Skip to content

Commit

Permalink
deploy and un deploy functionality (#3073)
Browse files Browse the repository at this point in the history
* deploy and undeploy functionality

Signed-off-by: msivasubramaniaan <msivasub@redhat.com>

* shown response in the UI

Signed-off-by: msivasubramaniaan <msivasub@redhat.com>

---------

Signed-off-by: msivasubramaniaan <msivasub@redhat.com>
  • Loading branch information
msivasubramaniaan authored Aug 9, 2023
1 parent 48c7457 commit b847944
Show file tree
Hide file tree
Showing 8 changed files with 464 additions and 96 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 29 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,11 @@
"title": "Deploy",
"category": "OpenShift"
},
{
"command": "openshift.Serverless.undeploy",
"title": "UnDeploy",
"category": "OpenShift"
},
{
"command": "openshift.Serverless.buildAndRun",
"title": "Build and Run",
Expand Down Expand Up @@ -1115,6 +1120,10 @@
"command": "openshift.Serverless.deploy",
"when": "false"
},
{
"command": "openshift.Serverless.undeploy",
"when": "false"
},
{
"command": "openshift.Serverless.buildAndRun",
"when": "false"
Expand Down Expand Up @@ -1456,15 +1465,23 @@
},
{
"command": "openshift.Serverless.build",
"when": "view == openshiftServerlessFunctionsView && viewItem =~ /^(localFunctions|localFunctionsWithBuild)$/"
"when": "view == openshiftServerlessFunctionsView && viewItem =~ /^(localFunctions|localFunctionsWithBuild|localDeployFunctions)$/"
},
{
"command": "openshift.Serverless.buildAndRun",
"when": "view == openshiftServerlessFunctionsView && viewItem =~ /^(localFunctions|localFunctionsWithBuild)$/"
"when": "view == openshiftServerlessFunctionsView && viewItem =~ /^(localFunctions|localFunctionsWithBuild|localDeployFunctions)$/"
},
{
"command": "openshift.Serverless.run",
"when": "view == openshiftServerlessFunctionsView && viewItem =~ /^(localFunctionsWithBuild)$/"
"when": "view == openshiftServerlessFunctionsView && viewItem =~ /^(localFunctionsWithBuild|localDeployFunctions)$/"
},
{
"command": "openshift.Serverless.deploy",
"when": "view == openshiftServerlessFunctionsView && viewItem =~ /^(localFunctions|localFunctionsWithBuild|localDeployFunctions|deployFunctions)$/"
},
{
"command": "openshift.Serverless.undeploy",
"when": "view == openshiftServerlessFunctionsView && viewItem =~ /^(localDeployFunctions|deployFunctions)$/"
},
{
"command": "openshift.Serverless.stopRun",
Expand Down Expand Up @@ -1580,6 +1597,15 @@
"image": "https://raw.githubusercontent.com/redhat-developer/vscode-openshift-tools/main/images/gif/walkthrough/serverless-function/run.gif",
"altText": "Run function"
}
},
{
"id": "deployFunction",
"title": "Deploy the Function",
"description": "Deploys a function to the currently configured Knative-enabled cluster from your IDE.\nOnly a function which has the source code opened in the IDE can be pushed. Right-click on the function you want to deploy (look for its node in the Functions tree), open the context menu (right-click on the node) and click on \"Deploy\". The Output Channel will show up with the deploy command where you can see the logs.",
"media": {
"image": "https://raw.githubusercontent.com/redhat-developer/vscode-openshift-tools/main/images/gif/walkthrough/serverless-function/deploy.gif",
"altText": "Deploy function"
}
}
]
}
Expand Down
218 changes: 181 additions & 37 deletions src/serveressFunction/build-run-deploy.ts

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions src/serveressFunction/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,29 @@ export class ServerlessCommand {
return commandText;
}

static deployFunction(location: string,
image: string,
namespace: string,
clusterVersion: ClusterVersion | null): CommandText {
const commandText = new CommandText('func', 'deploy', [
new CommandOption('-p', location),
new CommandOption('-i', image),
new CommandOption('-v')
]);
if (namespace){
commandText.addOption(new CommandOption('-n', namespace))
}
if (clusterVersion) {
commandText.addOption(new CommandOption('-r', ''))
}
return commandText;
}

static undeployFunction(name: string): CommandText {
const commandText = new CommandText('func', `delete ${name}`);
return commandText
}

static getClusterVersion(): CommandText {
return new CommandText('oc get clusterversion', undefined, [
new CommandOption('-o', 'josn')
Expand Down
93 changes: 52 additions & 41 deletions src/serveressFunction/functionImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ import * as path from 'path';
import * as fs from 'fs-extra';
import { Uri, window, workspace } from 'vscode';
import { FunctionContent, FunctionObject, FunctionStatus } from './types';
import { ServerlessFunctionView } from './view';
import { ServerlessCommand, Utils } from './commands';
import { OdoImpl } from '../odo';
import { BuildAndDeploy } from './build-run-deploy';
import { stringify } from 'yaml';
import { CliExitData } from '../cli';
import { CliChannel, CliExitData } from '../cli';
import * as cp from 'child_process';
import { VsCommandError } from '../vscommand';
import { CommandText } from '../base/command';
import { DeploymentConfig } from '../k8s/deploymentConfig';
import { ServerlessFunctionView } from './view';

export interface ServerlessFunction {
getLocalFunctions(): Promise<FunctionObject[]>;
Expand All @@ -32,16 +34,18 @@ export class ServerlessFunctionImpl implements ServerlessFunction {
return ServerlessFunctionImpl.instance;
}

/*private async getListItems<T>(command: CommandText, fail = false) {
private async getListItems(command: CommandText, fail = false) {
const listCliExitData = await CliChannel.getInstance().executeTool(command, undefined, fail);
const result = loadItems<T>(listCliExitData.stdout);
return result;
try {
return JSON.parse(listCliExitData.stdout) as FunctionObject[];
} catch(err) {
return [];
}
}

private async getDeployedFunctions(): Promise<FunctionObject[]> {
//set context value to deploy
return this.getListItems<FunctionObject>(DeploymentConfig.command.getDeploymentFunctions());
}*/
return this.getListItems(DeploymentConfig.command.getDeploymentFunctions());
}

async createFunction(language: string, template: string, location: string, image: string): Promise<CliExitData> {
let funnctionResponse: CliExitData;
Expand Down Expand Up @@ -78,9 +82,8 @@ export class ServerlessFunctionImpl implements ServerlessFunction {
}

async getLocalFunctions(): Promise<FunctionObject[]> {
//const deployedFunctions = await this.getDeployedFunctions();
const folders: Uri[] = [];
const functionList: FunctionObject[] = [];
const folders: Uri[] = [];
if (workspace.workspaceFolders) {
// eslint-disable-next-line no-restricted-syntax
for (const wf of workspace.workspaceFolders) {
Expand All @@ -89,42 +92,50 @@ export class ServerlessFunctionImpl implements ServerlessFunction {
}
}
}
const currentNamespace: string = ServerlessFunctionView.getInstance().getCurrentNameSpace();
// eslint-disable-next-line no-console
console.log(currentNamespace);
for (const folderUri of folders) {
const funcStatus = FunctionStatus.LOCALONLY;
const funcData: FunctionContent = await Utils.getFuncYamlContent(folderUri.fsPath);
/*if (
functionTreeView.has(funcData?.name) &&
(!funcData?.deploy?.namespace || getCurrentNamespace === funcData?.deploy?.namespace)
) {
funcStatus = FunctionStatus.CLUSTERLOCALBOTH;
}
fs.watch(folderUri.fsPath, (eventName, filename) => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
//functionExplorer.refresh();
});
if (funcData?.name) {
const url =
func.contextValue !== FunctionContextType.FAILNAMESPACENODE && funcData?.image.trim()
? functionTreeView.get(funcData?.name)?.url
: undefined;
functionTreeView.set(funcData?.name, this.createFunctionNodeImpl(func, funcData, folderUri, funcStatus, url));
}*/
const functionNode: FunctionObject = {
name: funcData.name,
runtime: funcData.runtime,
folderURI: folderUri,
context: funcStatus,
hasImage: await this.checkImage(folderUri),
isRunning: BuildAndDeploy.getInstance().checkRunning(folderUri.fsPath)
const deployedFunctions = await this.getDeployedFunctions();
if (folders.length > 0) {
for (const folderUri of folders) {
const funcData: FunctionContent = await Utils.getFuncYamlContent(folderUri.fsPath);
const funcStatus = this.getFunctionStatus(funcData, deployedFunctions);
const functionNode: FunctionObject = {
name: funcData.name,
runtime: funcData.runtime,
folderURI: folderUri,
context: funcStatus,
hasImage: await this.checkImage(folderUri),
isRunning: BuildAndDeploy.getInstance().checkRunning(folderUri.fsPath)
}
functionList.push(functionNode);
fs.watchFile(path.join(folderUri.fsPath, 'func.yaml'), (_eventName, _filename) => {
ServerlessFunctionView.getInstance().refresh();
});
}
}
if (deployedFunctions.length > 0) {
for (const deployedFunction of deployedFunctions) {
if (functionList.filter((functionParam) => functionParam.name === deployedFunction.name).length === 0) {
const functionNode: FunctionObject = {
name: deployedFunction.name,
runtime: deployedFunction.runtime,
context: FunctionStatus.CLUSTERONLY
}
functionList.push(functionNode);
}
}
functionList.push(functionNode);
}
return functionList;
}

getFunctionStatus(funcData: FunctionContent, deployedFunctions: FunctionObject[]): FunctionStatus {
if (deployedFunctions.length > 0) {
const func = deployedFunctions.find((deployedFunction) => deployedFunction.name === funcData.name && deployedFunction.namespace === funcData.deploy?.namespace)
if (func) {
return FunctionStatus.CLUSTERLOCALBOTH;
}
}
return FunctionStatus.LOCALONLY;
}

async checkImage(folderUri: Uri): Promise<boolean> {
const yamlContent = await Utils.getFuncYamlContent(folderUri.fsPath);
return BuildAndDeploy.imageRegex.test(yamlContent?.image);
Expand Down
127 changes: 127 additions & 0 deletions src/serveressFunction/multiStepInput.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*-----------------------------------------------------------------------------------------------
* Copyright (c) Red Hat, Inc. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*-----------------------------------------------------------------------------------------------*/

/* eslint-disable max-classes-per-file */
import { QuickPickItem, window, Disposable, QuickInput, QuickInputButton } from 'vscode';

interface QuickPickParameters<T extends QuickPickItem> {
title: string;
items: T[];
activeItem?: T;
placeholder: string;
buttons?: QuickInputButton[];
}

interface InputBoxParameters {
title: string;
value?: string;
prompt: string;
password: boolean;
validate: (value: string) => string;
buttons?: QuickInputButton[];
placeholder?: string;
reattemptForLogin?: boolean;
}

class MultiStepInput {
public current?: QuickInput;

async showQuickPick<T extends QuickPickItem, P extends QuickPickParameters<T>>({ title, items, activeItem, placeholder }: P) {
const disposables: Disposable[] = [];
try {
return await new Promise<T | (P extends { buttons: (infer I)[] } ? I : never)>((resolve) => {
const input = window.createQuickPick<T>();
input.title = title;
input.placeholder = placeholder;
input.items = items;
input.ignoreFocusOut = true;
if (activeItem) {
input.activeItems = [activeItem];
}
disposables.push(
// eslint-disable-next-line @typescript-eslint/no-shadow
input.onDidChangeSelection((items) => {
resolve(items[0]);
this.current.dispose();
}),
input.onDidHide(() => {
resolve(null);
}),
);
if (this.current) {
this.current.dispose();
}
this.current = input;
this.current.show();
});
} finally {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
disposables.forEach((d) => d.dispose());
}
}

async showInputBox<P extends InputBoxParameters>({
title,
value,
prompt,
password,
validate,
placeholder,
reattemptForLogin,
}: P) {
const disposables: Disposable[] = [];
try {
return await new Promise<string | (P extends { buttons: (infer I)[] } ? I : never)>((resolve) => {
const input = window.createInputBox();
input.title = title;
input.value = value || '';
input.prompt = prompt;
input.password = password;
input.ignoreFocusOut = true;
input.placeholder = placeholder || '';
let validating = validate('');
if (reattemptForLogin) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
input.validationMessage = 'Incorrect credentials, please try again';
}
disposables.push(
input.onDidAccept(() => {
// eslint-disable-next-line @typescript-eslint/no-shadow
const { value } = input;
input.enabled = false;
input.busy = true;
if (!validate(value)) {
resolve(value);
this.current.dispose();
}
input.enabled = true;
input.busy = false;
}),
input.onDidChangeValue((text) => {
const current = validate(text);
validating = current;
const validationMessage = current;
if (current === validating) {
input.validationMessage = validationMessage;
}
}),
input.onDidHide(() => {
resolve(null);
}),
);
if (this.current) {
this.current.dispose();
}
this.current = input;
this.current.show();
});
} finally {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
disposables.forEach((d) => d.dispose());
}
}
}

export const multiStep = new MultiStepInput();
Loading

0 comments on commit b847944

Please sign in to comment.