Skip to content

Commit

Permalink
Deploy to OpenShift command
Browse files Browse the repository at this point in the history
Uses the existing menu to select which Quarkus project to deploy.
Uses OpenShift Connector in order to deploy the Quarkus project to
OpenShift.
OpenShift Connector handles selecting/creating the application and
naming the component.

Closes redhat-developer#313

Signed-off-by: David Thompson <davthomp@redhat.com>
  • Loading branch information
datho7561 committed Dec 1, 2020
1 parent 4c4f56f commit e8bd39c
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 41 deletions.
8 changes: 8 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@
{
"command": "quarkusTools.welcome",
"title": "Quarkus: Welcome"
},
{
"command": "quarkusTools.deployToOpenShift",
"title": "Quarkus: Deploy current Quarkus Project to OpenShift"
}
],
"configuration": {
Expand Down Expand Up @@ -195,6 +199,10 @@
{
"command": "quarkusTools.debugQuarkusProject",
"when": "quarkusProjectExistsOrLightWeight"
},
{
"command": "quarkusTools.deployToOpenShift",
"when": "quarkusProjectExistsOrLightWeight"
}
]
},
Expand Down
1 change: 1 addition & 0 deletions src/definitions/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export namespace VSCodeCommands {
export const ADD_EXTENSIONS = 'quarkusTools.addExtension';
export const DEBUG_QUARKUS_PROJECT = 'quarkusTools.debugQuarkusProject';
export const QUARKUS_WELCOME = 'quarkusTools.welcome';
export const DEPLOY_TO_OPENSHIFT = 'quarkusTools.deployToOpenShift';
}

export namespace JavaVSCodeCommands {
Expand Down
65 changes: 25 additions & 40 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,20 @@
* limitations under the License.
*/
import * as path from 'path';
import { commands, ConfigurationChangeEvent, Disposable, ExtensionContext, languages, Terminal, TextDocument, window, workspace } from 'vscode';
import { VSCodeCommands } from './definitions/constants';
import { ExtensionContext, commands, window, workspace, Terminal, languages, TextDocument, ConfigurationChangeEvent, Disposable } from 'vscode';
import { ProjectLabelInfo } from './definitions/ProjectLabelInfo';
import { PropertiesLanguageMismatch, QuarkusConfig } from './QuarkusConfig';
import { QuarkusContext } from './QuarkusContext';
import quarkusProjectListener from './QuarkusProjectListener';
import { terminalCommandRunner } from './terminal/terminalCommandRunner';
import { requestStandardMode, runWithStandardMode } from './utils/requestStandardMode';
import { WelcomeWebview } from './webviews/WelcomeWebview';
import { addExtensionsWizard } from './wizards/addExtensions/addExtensionsWizard';
import { tryStartDebugging } from './wizards/debugging/startDebugging';
import { createTerminateDebugListener } from './wizards/debugging/terminateProcess';
import quarkusProjectListener from './QuarkusProjectListener';
import { deployToOpenShift } from './wizards/deployToOpenShift/deployToOpenShift';
import { generateProjectWizard } from './wizards/generateProject/generationWizard';
import { tryStartDebugging } from './wizards/debugging/startDebugging';
import { WelcomeWebview } from './webviews/WelcomeWebview';
import { QuarkusConfig, PropertiesLanguageMismatch } from './QuarkusConfig';
import { terminalCommandRunner } from './terminal/terminalCommandRunner';
import { ProjectLabelInfo } from './definitions/ProjectLabelInfo';
import { requestStandardMode } from './utils/requestStandardMode';

export function activate(context: ExtensionContext) {
QuarkusContext.setContext(context);
Expand Down Expand Up @@ -122,9 +123,9 @@ export function activate(context: ExtensionContext) {
const CONFIGURE_IN_SETTINGS = "Configure in Settings";
const DISABLE_IN_SETTINGS = "Disable Language Updating";
const response: Thenable<string> = window.showInformationMessage(
`Quarkus Tools for Visual Studio Code automatically switched the language ID of '${fileName}' `
+ `to be '${languageId}' in order to provide language support. `
+ `This behavior can be configured in settings.`, DISABLE_IN_SETTINGS, CONFIGURE_IN_SETTINGS);
`Quarkus Tools for Visual Studio Code automatically switched the language ID of '${fileName}' `
+ `to be '${languageId}' in order to provide language support. `
+ `This behavior can be configured in settings.`, DISABLE_IN_SETTINGS, CONFIGURE_IN_SETTINGS);
response.then(result => {
if (result === CONFIGURE_IN_SETTINGS) {
commands.executeCommand('workbench.action.openSettings', QuarkusConfig.PROPERTIES_LANGUAGE_MISMATCH);
Expand Down Expand Up @@ -154,55 +155,39 @@ function displayWelcomePageIfNeeded(context: ExtensionContext): void {

function registerVSCodeCommands(context: ExtensionContext) {

const notAQuarkusProjectWarning: (ignored: any) => PromiseLike<any> = (ignored: any): PromiseLike<any> => {
return window.showErrorMessage('No Quarkus projects were detected in this folder', 'Ok');
};

/**
* Command for creating a Quarkus Maven project
*/
context.subscriptions.push(commands.registerCommand(VSCodeCommands.CREATE_PROJECT, () => {
generateProjectWizard();
}));

/**
* Command for displaying welcome page
*/
context.subscriptions.push(commands.registerCommand(VSCodeCommands.QUARKUS_WELCOME, () => {
WelcomeWebview.createOrShow(context);
}));

/**
* Command for adding Quarkus extensions to current Quarkus Maven project
*/
context.subscriptions.push(commands.registerCommand(VSCodeCommands.ADD_EXTENSIONS, () => {
requestStandardMode("Adding extensions").then((isStandardMode) => {
if (isStandardMode) {
ProjectLabelInfo.getWorkspaceProjectLabelInfo().then((projectLabelInfo: ProjectLabelInfo[]) => {
if (projectLabelInfo.filter(info => info.isQuarkusProject()).length) {
addExtensionsWizard();
} else {
notAQuarkusProjectWarning(null);
}
}).catch(notAQuarkusProjectWarning);
}
});
runWithStandardMode(addExtensionsWizard, "Adding extensions");
}));

/**
* Command for debugging current Quarkus Maven project
*/
context.subscriptions.push(commands.registerCommand(VSCodeCommands.DEBUG_QUARKUS_PROJECT, () => {
requestStandardMode("Debugging the project").then((isStandardMode) => {
if (isStandardMode) {
ProjectLabelInfo.getWorkspaceProjectLabelInfo().then((projectLabelInfo: ProjectLabelInfo[]) => {
if (projectLabelInfo.filter(info => info.isQuarkusProject()).length) {
tryStartDebugging();
} else {
notAQuarkusProjectWarning(null);
}
}).catch(notAQuarkusProjectWarning);
}
});
runWithStandardMode(tryStartDebugging, "Debugging the project");
}));

/**
* Command for displaying welcome page
* Command for deploying a Quarkus project to OpenShift
*/
context.subscriptions.push(commands.registerCommand(VSCodeCommands.QUARKUS_WELCOME, () => {
WelcomeWebview.createOrShow(context);
context.subscriptions.push(commands.registerCommand(VSCodeCommands.DEPLOY_TO_OPENSHIFT, () => {
runWithStandardMode(deployToOpenShift, "Deploying to OpenShift");
}));

}
75 changes: 75 additions & 0 deletions src/utils/openShiftConnectorUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { commands, Disposable, Extension, extensions, ProgressLocation, window } from "vscode";

export const OPENSHIFT_CONNECTOR_EXTENSION_ID: string = 'redhat.vscode-openshift-connector';
export const OPENSHIFT_CONNECTOR = 'OpenShift Connector extension';
const DOWNLOAD_TIMEOUT: number = 60000; // Timeout for downloading VSCode OpenShift Connector, in milliseconds

/**
* Returns true if the OpenShift connector extension is installed, and false otherwise
*
* @returns true if the OpenShift connector extension is installed, and false otherwise
*/
export function isOpenShiftConnectorInstalled(): boolean {
return !!extensions.getExtension(OPENSHIFT_CONNECTOR_EXTENSION_ID);
}

/**
* Returns the OpenShift Connector extension API
*
* @throws Error if the extension is not installed
* @returns the OpenShift Connector extension API
*/
export async function getOpenShiftConnector(): Promise<any> {
if (!isOpenShiftConnectorInstalled()) {
throw new Error(`${OPENSHIFT_CONNECTOR} is not installed`);
}
const openShiftConnector: Extension<any> = extensions.getExtension(OPENSHIFT_CONNECTOR_EXTENSION_ID);
if (openShiftConnector.isActive) {
return openShiftConnector.exports;
}
return extensions.getExtension(OPENSHIFT_CONNECTOR_EXTENSION_ID).activate();
}

/**
* Install the OpenShift Connector extension
*
* @returns when the extension is installed
* @throws if the user refuses to install the extension, or if the extension does not get installed within a timeout period
*/
async function installOpenShiftConnector(): Promise<any> {
const installListenerDisposables: Disposable[] = [];
return Promise.race([
new Promise((resolve, reject) => {
extensions.onDidChange(() => {
if (isOpenShiftConnectorInstalled()) {
installListenerDisposables.forEach((d: Disposable) => { d.dispose(); });
resolve();
}
}, null, installListenerDisposables);
commands.executeCommand("workbench.extensions.installExtension", OPENSHIFT_CONNECTOR_EXTENSION_ID)
.then((_unused: any) => { }, reject);
}),
new Promise((_unused, reject) => {
// Fail install if it takes longer than the timeout period
setTimeout(reject, DOWNLOAD_TIMEOUT, new Error(`${OPENSHIFT_CONNECTOR} installation is taking a while. Cancelling 'Deploy to OpenShift'`));
})
]).catch(e => {
installListenerDisposables.forEach((d: Disposable) => { d.dispose(); });
return Promise.reject(e);
});
}

/**
* Install the OpenShift Connector extension and show the progress
*
* @returns when the extension is installed
* @throws if the extension installation fails or times out
*/
export async function installOpenShiftConnectorWithProgress(): Promise<void> {
return window.withProgress({ location: ProgressLocation.Notification, title: `Installing ${OPENSHIFT_CONNECTOR}...` }, _progress => {
return installOpenShiftConnector();
}).then(() => {
window.showInformationMessage(`Successfully installed ${OPENSHIFT_CONNECTOR}.`);
return Promise.resolve();
});
}
42 changes: 41 additions & 1 deletion src/utils/requestStandardMode.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import * as vscode from "vscode";
import { workspace } from "vscode";
import { ProjectLabelInfo } from "../definitions/ProjectLabelInfo";

const JAVA_EXTENSION_ID = "redhat.java";

Expand Down Expand Up @@ -65,7 +67,7 @@ export async function waitForStandardMode(): Promise<void> {
return new Promise((resolve) => {
const javaExt = vscode.extensions.getExtension(JAVA_EXTENSION_ID);
if (javaExt) {
javaExt.activate().then((javaExtApi)=> {
javaExt.activate().then((javaExtApi) => {
if (javaExtApi) {
javaExtApi.onDidServerModeChange((mode: string) => {
if (mode === ServerMode.STANDARD) {
Expand All @@ -77,3 +79,41 @@ export async function waitForStandardMode(): Promise<void> {
}
});
}

/**
* Request vscode-java standard mode, then try to run the given action in standard mode. Fail gracefully if no Quarkus projects exist.
*
* @param action A function to perform that requires standard mode
* @param actionDescription Human legible description of what is trying to be accomplished
*/
export function runWithStandardMode(action: () => void, actionDescription: string) {
requestStandardMode(actionDescription).then((isStandardMode) => {
if (isStandardMode) {
ProjectLabelInfo.getWorkspaceProjectLabelInfo().then((projectLabelInfo: ProjectLabelInfo[]) => {
if (projectLabelInfo.filter(info => info.isQuarkusProject()).length) {
action();
} else {
notAQuarkusProjectWarning();
}
}).catch(notAQuarkusProjectWarning);
}
});
}

/**
* Warns the user that no Quarkus projects were detected
*
* @param ignored Ignored
*/
function notAQuarkusProjectWarning(ignored?: any): PromiseLike<any> {
const numFolders: number = workspace.workspaceFolders.length;
let msg: string;
if (numFolders === 0) {
msg = 'No Quarkus projects were detected since no folders are open';
} else if (numFolders === 1) {
msg = 'No Quarkus projects were detected in this folder';
} else {
msg = 'No Quarkus projects were detected in any of the open folders';
}
return vscode.window.showErrorMessage(msg);
}
61 changes: 61 additions & 0 deletions src/wizards/deployToOpenShift/deployToOpenShift.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { commands, Uri, window } from "vscode";
import { ProjectLabelInfo } from "../../definitions/ProjectLabelInfo";
import { getOpenShiftConnector, installOpenShiftConnectorWithProgress, isOpenShiftConnectorInstalled, OPENSHIFT_CONNECTOR } from "../../utils/openShiftConnectorUtils";
import { getQuarkusProject } from "../getQuarkusProject";

/**
* Attempts to select a Quarkus project, then deploy it to OpenShift using OpenShift Connector
*/
export async function deployToOpenShift() {
try {
await installOpenShiftConnectorIfNeeded();
const quarkusProject: ProjectLabelInfo = await getQuarkusProject();
const result: string = await commands.executeCommand("openshift.component.createFromRootWorkspaceFolder", //
Uri.parse(quarkusProject.uri) as Uri, //
[] as Uri[], // unused in OpenShift Connector
undefined, // OpenShift context
"java-quarkus");
if (!result) {
throw new Error();
}
return window.showInformationMessage(result);
} catch (error) {
return window.showErrorMessage(`Failed to deploy to OpenShift`);
}
}

/**
* Installs the OpenShift Connector extension if its missing, then returns the extension API
*
* @throws Error if the extension is not installed and something prevents installation
* @returns the OpenShift Connector extension API
*/
async function installOpenShiftConnectorIfNeeded(): Promise<any> {
if (isOpenShiftConnectorInstalled()) {
return getOpenShiftConnector();
}
return askToInstallOpenShiftConnector();
}

/**
* Resolves to the OpenShift connector extension API, or rejects if the extension is missing and the user doesn't want to install it
*
* @throws Error if the extension is missing and the user can't or doesn't want to install it
* @returns the OpenShift connector extension API
*/
async function askToInstallOpenShiftConnector(): Promise<any> {
const YES: string = 'Yes';
const NO: string = 'No';
const response: string = await window.showInformationMessage(`${OPENSHIFT_CONNECTOR} is needed to deploy to OpenShift. Install it now?`, YES, NO);
if (response === YES) {
try {
await installOpenShiftConnectorWithProgress();
if (isOpenShiftConnectorInstalled()) {
return getOpenShiftConnector();
}
} catch (e) {
throw new Error(`${OPENSHIFT_CONNECTOR} installation failed`);
}
}
throw new Error(`${OPENSHIFT_CONNECTOR} needs to be installed to deploy to OpenShift`);
}

0 comments on commit e8bd39c

Please sign in to comment.