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.
The component is created if it does not exist yet (i.e. no .odo folder
is present), and it's pushed if it already exists.
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 Mar 15, 2021
1 parent 75682e0 commit 0dec4fe
Show file tree
Hide file tree
Showing 8 changed files with 2,304 additions and 2,572 deletions.
4,558 changes: 2,035 additions & 2,523 deletions package-lock.json

Large diffs are not rendered by default.

13 changes: 9 additions & 4 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 Expand Up @@ -246,9 +254,6 @@
"test-ui-run": "extest setup-and-run 'out/test/vscodeUiTest/suite/*.js' -u -e 'out/test/vscodeUiTest/extensions'",
"test-all": "npm test && npm run test-ui-run"
},
"resolutions": {
"minimist": "^1.2.5"
},
"devDependencies": {
"@types/chai": "^4.2.3",
"@types/chai-fs": "^2.0.2",
Expand All @@ -266,7 +271,7 @@
"gradle-to-js": "^2.0.0",
"gulp": "^4.0.2",
"gulp-rename": "^1.4.0",
"minimist": ">=1.2.5",
"minimist": "^1.2.5",
"mocha": "^6.2.1",
"pom-parser": "^1.2.0",
"ts-loader": "^6.0.1",
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");
}));

}
128 changes: 128 additions & 0 deletions src/utils/openShiftConnectorUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import * as fs from 'fs-extra';
import * as path from 'path';
import { commands, Disposable, Extension, extensions, ProgressLocation, Uri, 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<void> {
const installListenerDisposables: Disposable[] = [];
return Promise.race([
new Promise<void>((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<void>((_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> {
await window.withProgress({ location: ProgressLocation.Notification, title: `Installing ${OPENSHIFT_CONNECTOR}...` }, progress => {
return installOpenShiftConnector();
});
window.showInformationMessage(`Successfully installed ${OPENSHIFT_CONNECTOR}.`);
}

/**
* Represents an OpenShift component
*/
export interface OpenShiftComponentInformation {
name: string;
uri: Uri;
}

export namespace OpenShiftConnector {

/**
* Attempts to push the component to OpenShift, and resolves once it has succeeded or failed
*
* @param component the component to push to OpenShift
* @returns once the component has been pushed to OpenShift, or when the push to OpenShift fails
*/
export async function push(component: OpenShiftComponentInformation): Promise<void> {
await commands.executeCommand("openshift.component.push", //
// This is the bare minimum info that is needed to push the component out of the following interface:
// https://github.com/redhat-developer/vscode-openshift-tools/blob/66210684eb4bd739ae062af6a47335ea33d36b07/src/odo.ts#L36
{
contextPath: component.uri,
getName: () => { return component.name; }
}
);
}

/**
* Attempts to create a component from the given component information, and resolves once it has succeeded or failed
*
* @param component The OpenShiftComponent to create
* @returns once the component has been created, or when the component creation fails
*/
export async function create(component: OpenShiftComponentInformation): Promise<void> {
await commands.executeCommand("openshift.component.createFromRootWorkspaceFolder", //
component.uri, // uri to the project folder
[] as Uri[], // unused in OpenShift Connector
undefined, // OpenShift context
"java-quarkus"); // use java quarkus devfile
}

/**
* Returns true if the given component has been created and false otherwise
*
* @param component the component to check if it exists
* @returns true if the given component has been created and false otherwise
*/
export async function isExistingComponent(component: OpenShiftComponentInformation): Promise<boolean> {
return fs.pathExists(path.join(component.uri.fsPath, '.odo'));
}

}
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);
}
9 changes: 5 additions & 4 deletions src/utils/tasksUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/
import * as _ from 'lodash';

import { tasks, ProcessExecution, ShellExecution, Task, TaskExecution, WorkspaceFolder } from 'vscode';
import { tasks, ProcessExecution, ShellExecution, Task, TaskExecution, WorkspaceFolder, CustomExecution } from 'vscode';
import { BuildSupport } from '../buildSupport/BuildSupport';

export async function getQuarkusDevTaskNames(workspaceFolder: WorkspaceFolder, projectBuildSupport: BuildSupport) {
Expand Down Expand Up @@ -49,9 +49,10 @@ export async function getRunningQuarkusDevTasks(workspaceFolder: WorkspaceFolder
}

export function getTaskExecutionWorkingDir(task: Task): string|undefined {
if (!task.execution) return undefined;
if (!task.execution || task.execution instanceof CustomExecution) return undefined;

if (!task.execution.options || !task.execution.options.cwd) {
const taskExecution: ProcessExecution | ShellExecution = task.execution;
if (!taskExecution.options || !taskExecution.options.cwd) {
return './';
}

Expand All @@ -67,7 +68,7 @@ async function getTasksFromWorkspace(workspaceFolder: WorkspaceFolder): Promise<

function isQuarkusDevTask(task: Task, projectBuildSupport: BuildSupport): boolean {

const execution: ProcessExecution | ShellExecution = task.execution;
const execution: ProcessExecution | ShellExecution | CustomExecution = task.execution;
return execution &&
'commandLine' in execution &&
(execution.commandLine.includes(projectBuildSupport.getDefaultExecutable()) ||
Expand Down

0 comments on commit 0dec4fe

Please sign in to comment.