Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ test-resources
src/webview/log
src/webview/describe
src/webview/cluster
src/webview/webpack.config.js
out
src/webview/common
src/webview/create-service
Expand Down
7 changes: 7 additions & 0 deletions USAGE_DATA.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ In addition to generic command's usage data (see above) `Login` command also rep
* openshift_version - cluster's OpenShift version (if can be accessed by the current user)
* kubernetes_version - cluster's Kubernetes version

#### Bind Service

In addition to the generic command's usage data (see above), the `Bind Service` context menu option reports events when:

* the wizard to select the service to bind to is opened
* the wizard to select the service to bind to is submitted

### Add Cluster Editor

The editor reports selection made on first page:
Expand Down
3 changes: 2 additions & 1 deletion build/esbuild.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ const webviews = [
'helm-chart',
'log',
'welcome',
'feedback'
'feedback',
'add-service-binding',
];

function kebabToCamel(text) {
Expand Down
Binary file added images/walkthrough/addServiceBinding.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,11 @@
"title": "Start Dev",
"category": "OpenShift"
},
{
"command": "openshift.component.binding.add",
"title": "Bind Service",
"category": "OpenShift"
},
{
"command": "openshift.component.exitDevMode",
"title": "Stop Dev",
Expand Down Expand Up @@ -510,6 +515,10 @@
"title": "Open Console Dashboard for Current Cluster",
"category": "OpenShift"
},
{
"command": "openshift.open.operatorBackedServiceCatalog",
"title": "Open Operator Backed Service Catalog"
},
{
"command": "openshift.component.debug",
"title": "Debug",
Expand Down Expand Up @@ -910,6 +919,10 @@
"command": "openshift.open.developerConsole",
"when": "view == openshiftProjectExplorer"
},
{
"command": "openshift.open.operatorBackedServiceCatalog",
"when": "false"
},
{
"command": "openshift.component.debug",
"when": "view == openshiftProjectExplorer"
Expand Down Expand Up @@ -1069,6 +1082,10 @@
{
"command": "openshift.component.openInBrowser",
"when": "false"
},
{
"command": "openshift.component.binding.add",
"when": "false"
}
],
"view/title": [
Expand Down Expand Up @@ -1309,6 +1326,11 @@
"when": "view == openshiftComponentsView && viewItem =~ /openshift\\.component.*\\.dep-nrn.*/ || viewItem =~ /openshift\\.component.*\\.dep-run.*/",
"group": "c2@1"
},
{
"command": "openshift.component.binding.add",
"when": "view == openshiftComponentsView && viewItem =~ /openshift\\.component.*\\.dev-nrn.*/",
"group": "c2@2"
},
{
"command": "openshift.component.showDevTerminal",
"when": "view == openshiftComponentsView && viewItem =~ /openshift\\.component.*\\.dev-run.*/",
Expand Down
2 changes: 1 addition & 1 deletion src/odo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export interface Odo {
*/
getActiveProject(): Promise<string>;

/*
/**
* Deletes all the odo configuration files associated with the component (`.odo`, `devfile.yaml`) located at the given path.
*
* @param componentPath the path to the component
Expand Down
16 changes: 16 additions & 0 deletions src/odo/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,4 +320,20 @@ export class Command {
return new CommandText('oc api-resources | grep openshift');
}

static addBinding(serviceNamespace: string, serviceName: string, bindingName: string): CommandText {
return new CommandText('odo add binding',
undefined,
[
new CommandOption('--service-namespace', serviceNamespace, false),
new CommandOption('--service', serviceName, false),
new CommandOption('--name', bindingName, false),
]
)
}

static getBindableServices(): CommandText {
return new CommandText('odo list service',
undefined,
[new CommandOption('-o json')]);
}
}
69 changes: 68 additions & 1 deletion src/odo3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { KubernetesObject } from '@kubernetes/client-node';
import { CommandText } from './base/command';
import { CliChannel } from './cli';
import { CliChannel, CliExitData } from './cli';
import { Command as CommonCommand, loadItems } from './k8s/common';
import { Command as DeploymentCommand } from './k8s/deployment';
import { DeploymentConfig } from './k8s/deploymentConfig';
Expand All @@ -20,6 +20,30 @@ export interface Odo3 {
setNamespace(newNamespace: string): Promise<void>;

describeComponent(contextPath: string): Promise<ComponentDescription | undefined>;

/**
* Bind a component to a bindable service by modifying the devfile
*
* Resolves when the binding it created.
*
* @param contextPath the path to the component
* @param serviceName the name of the service to bind to
* @param serviceNamespace the namespace the the service is in
* @param bindingName the name of the service binding
*/
addBinding(
contextPath: string,
serviceName: string,
serviceNamespace: string,
bindingName: string,
): Promise<void>;

/**
* Returns a list of all the bindable services on the cluster.
*
* @returns a list of all the bindable services on the cluster
*/
getBindableServices(): Promise<KubernetesObject[]>;
}

export class Odo3Impl implements Odo3 {
Expand Down Expand Up @@ -69,6 +93,49 @@ export class Odo3Impl implements Odo3 {
// ignore and return undefined
}
}

async addBinding(contextPath: string, serviceNamespace: string, serviceName: string, bindingName: string) {
const myCommand = Command.addBinding(serviceNamespace, serviceName, bindingName);
await CliChannel.getInstance().executeTool(
myCommand,
{cwd: contextPath},
true
);
}

async getBindableServices(): Promise<KubernetesObject[]> {
const data: CliExitData = await CliChannel.getInstance().executeTool(
Command.getBindableServices()
);
let responseObj;
try {
responseObj = JSON.parse(data.stdout);
} catch {
throw new Error(JSON.parse(data.stderr).message);
}
if (!responseObj.bindableServices) {
return [];
}
return (responseObj.bindableServices as BindableService[]) //
.map(obj => {
return {
kind: obj.kind,
apiVersion: obj.apiVersion,
metadata: {
namespace: obj.namespace,
name: obj.name,
}
} as KubernetesObject;
});
}
}

interface BindableService {
name: string;
namespace: string;
kind: string;
apiVersion: string;
service: string;
}

export function newInstance(): Odo3 {
Expand Down
15 changes: 15 additions & 0 deletions src/openshift/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { KubernetesObject } from '@kubernetes/client-node';
import { ExtensionContext, QuickInputButton, QuickPickItem, QuickPickItemButtonEvent, Terminal, ThemeIcon, Uri, Progress as VProgress, WebviewPanel, commands, env, window, workspace } from 'vscode';
import { CliChannel, CliExitData } from '../cli';
import { getInstance } from '../odo';
import { Command } from '../odo/command';
import { TokenStore } from '../util/credentialManager';
import { Filters } from '../util/filters';
Expand Down Expand Up @@ -96,6 +97,20 @@ export class Cluster extends OpenShiftItem {
});
}

@vsCommand('openshift.open.operatorBackedServiceCatalog')
@clusterRequired()
static async openOpenshiftConsoleTopography(): Promise<void> {
return Progress.execFunctionWithProgress('Opening Operator Backed Service Catalog', async (progress) => {
const [consoleUrl, namespace] = await Promise.all([
Cluster.getConsoleUrl(progress),
getInstance().getActiveProject()
]);
progress.report({increment: 100, message: 'Starting default browser'});
// eg. https://console-openshift-console.apps-crc.testing/catalog/ns/default?catalogType=OperatorBackedService
return commands.executeCommand('vscode.open', Uri.parse(`${consoleUrl}/catalog/ns/${namespace}?catalogType=OperatorBackedService`));
});
}

@vsCommand('openshift.resource.openInDeveloperConsole')
@clusterRequired()
static async openInDeveloperConsole(resource: KubernetesObject): Promise<void> {
Expand Down
76 changes: 74 additions & 2 deletions src/openshift/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@ import { Command } from '../odo/command';
import { ComponentTypeAdapter, ComponentTypeDescription, DevfileComponentType, ascDevfileFirst, isDevfileComponent } from '../odo/componentType';
import { StarterProject, isStarterProject } from '../odo/componentTypeDescription';
import { ComponentWorkspaceFolder } from '../odo/workspace';
import { NewComponentCommandProps } from '../telemetry';
import * as odo3 from '../odo3';
import sendTelemetry, { NewComponentCommandProps } from '../telemetry';
import { Progress } from '../util/progress';
import { selectWorkspaceFolder } from '../util/workspace';
import { VsCommandError, vsCommand } from '../vscommand';
import AddServiceBindingViewLoader, { ServiceBindingFormResponse } from '../webview/add-service-binding/addServiceBindingViewLoader';
import DescribeViewLoader from '../webview/describe/describeViewLoader';
import GitImportLoader from '../webview/git-import/gitImportLoader';
import LogViewLoader from '../webview/log/LogViewLoader';
import OpenShiftItem, { clusterRequired } from './openshiftItem';
import DescribeViewLoader from '../webview/describe/describeViewLoader';

function createCancelledResult(stepName: string): any {
const cancelledResult: any = new String('');
Expand Down Expand Up @@ -203,6 +205,76 @@ export class Component extends OpenShiftItem {
return Component.dev(component, 'podman');
}

@vsCommand('openshift.component.binding.add')
static async addBinding(component: ComponentWorkspaceFolder) {
Comment thread
datho7561 marked this conversation as resolved.
const odo: odo3.Odo3 = odo3.newInstance();

const services = await Progress.execFunctionWithProgress('Looking for bindable services', (progress) => {
return odo.getBindableServices();
});

if (!services || services.length === 0) {
void window.showErrorMessage('No bindable services are available', 'Open Service Catalog in OpenShift Console')
.then((result) => {
if (result === 'Open Service Catalog in OpenShift Console') {
void commands.executeCommand('openshift.open.operatorBackedServiceCatalog')
}
});
return;
}

void sendTelemetry('startAddBindingWizard');

let formResponse: ServiceBindingFormResponse = undefined;
try {
formResponse = await new Promise<ServiceBindingFormResponse>(
(resolve, reject) => {
void AddServiceBindingViewLoader.loadView(
component.contextPath,
services.map(
(service) => `${service.metadata.namespace}/${service.metadata.name}`,
),
(panel) => {
panel.onDidDispose((_e) => {
reject(new Error('The \'Add Service Binding\' wizard was closed'));
});
return async (eventData) => {
if (eventData.action === 'addServiceBinding') {
resolve(eventData.params);
await panel.dispose();
}
};
},
).then(view => {
if (!view) {
// the view was already created
reject();
}
});
},
);
} catch (e) {
// The form was closed without submitting,
// or the form already exists for this component.
// stop the command.
return;
}

const selectedServiceObject = services.filter(
(service) =>
`${service.metadata.namespace}/${service.metadata.name}` === formResponse.selectedService,
)[0];

void sendTelemetry('finishAddBindingWizard');

await odo.addBinding(
component.contextPath,
selectedServiceObject.metadata.namespace,
selectedServiceObject.metadata.name,
formResponse.bindingName,
);
}

@vsCommand('openshift.component.dev')
@clusterRequired()
static async dev(component: ComponentWorkspaceFolder, runOn?: undefined | 'podman') {
Expand Down
Loading