Skip to content

Commit 7fbc8c5

Browse files
committed
Migrate Kubernetes extension commands #3990
Fixes: #3990 Signed-off-by: Victor Rubezhny <vrubezhny@redhat.com>
1 parent 78dd780 commit 7fbc8c5

File tree

13 files changed

+366
-68
lines changed

13 files changed

+366
-68
lines changed

.github/workflows/release.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ jobs:
4949
run: npm run test
5050
- name: Package
5151
run: |
52-
jq --tab '.extensionDependencies += [ "ms-kubernetes-tools.vscode-kubernetes-tools" ]' package.json > package.json.new
53-
mv package.json.new package.json
5452
node ./out/build/update-readme.js
5553
declare -A targets
5654
targets["win32-x64"]=win32

README.commands.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ When installing the extension directly from the VSCode marketplace all the depen
8080

8181
* [Red Hat Authentication](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-redhat-account)
8282
* [YAML Extension](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml)
83-
* [Kubernetes Extension](https://marketplace.visualstudio.com/items?itemName=ms-kubernetes-tools.vscode-kubernetes-tools)
8483

8584
### CLI Tools
8685

package.json

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,6 @@
232232
"onCommand:openshift.explorer.reportIssue",
233233
"onCommand:openshift.explorer.switchContext",
234234
"onCommand:clusters.openshift.project.openConsole",
235-
"onCommand:clusters.openshift.useProject",
236235
"onCommand:clusters.openshift.deploy",
237236
"onCommand:clusters.openshift.build.start",
238237
"onCommand:clusters.openshift.build.showLog",
@@ -255,7 +254,8 @@
255254
"onCommand:openshift.component.deleteConfigurationFiles",
256255
"onCommand:openshift.component.deleteSourceFolder",
257256
"onWalkthrough:openshiftWalkthrough",
258-
"onWalkthrough:serverlessFunctionWalkthrough"
257+
"onWalkthrough:serverlessFunctionWalkthrough",
258+
"onFileSystem:k8smsx"
259259
],
260260
"contributes": {
261261
"configurationDefaults": {
@@ -607,11 +607,6 @@
607607
"title": "Debug Component",
608608
"category": "OpenShift"
609609
},
610-
{
611-
"command": "clusters.openshift.useProject",
612-
"title": "Use Project",
613-
"category": "OpenShift"
614-
},
615610
{
616611
"command": "clusters.openshift.project.openConsole",
617612
"title": "Open in Console",
@@ -1872,10 +1867,6 @@
18721867
"when": "view == openshiftComponentsView && viewItem =~ /openshift\\.component.*\\.dev-run.*/",
18731868
"group": "c3@4"
18741869
},
1875-
{
1876-
"command": "clusters.openshift.useProject",
1877-
"when": "viewItem =~ /\\.openshift\\.inactiveProject/i"
1878-
},
18791870
{
18801871
"command": "clusters.openshift.project.openConsole",
18811872
"when": "view == extension.vsKubernetesExplorer && viewItem =~ /vsKubernetes\\.resource\\.project*/i"
@@ -1947,6 +1938,10 @@
19471938
"command": "openshift.deployment.scale",
19481939
"when": "view == openshiftProjectExplorer && viewItem =~ /^openshift\\.k8sObject\\.Deployment/"
19491940
},
1941+
{
1942+
"command": "openshift.resource.load",
1943+
"when": "view == openshiftProjectExplorer && viewItem == openshift.k8sObject.helm"
1944+
},
19501945
{
19511946
"command": "openshift.resource.unInstall",
19521947
"when": "view == openshiftProjectExplorer && viewItem == openshift.k8sObject.helm"
@@ -2243,6 +2238,15 @@
22432238
"default": 0,
22442239
"description": "Output verbosity level (value between 0 and 9) for OpenShift Create, and Dev commands in output channel and integrated terminal."
22452240
},
2241+
"openshiftToolkit.outputFormat": {
2242+
"enum": [
2243+
"json",
2244+
"yaml"
2245+
],
2246+
"type": "string",
2247+
"default": "yaml",
2248+
"description": "Output format for Kubernetes specs. One of 'json' or 'yaml' (default)."
2249+
},
22462250
"openshiftToolkit.searchForToolsInPath": {
22472251
"type": "boolean",
22482252
"default": false,
@@ -2311,8 +2315,7 @@
23112315
]
23122316
},
23132317
"extensionDependencies": [
2314-
"redhat.vscode-redhat-account",
2315-
"ms-kubernetes-tools.vscode-kubernetes-tools"
2318+
"redhat.vscode-redhat-account"
23162319
],
23172320
"__metadata": {
23182321
"id": "8fea1f1f-b45c-4eea-b479-3a92c6e697d3",

src/explorer.ts

Lines changed: 46 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import { FileContentChangeNotifier, WatchUtil } from './util/watch';
3737
import { vsCommand } from './vscommand';
3838
import { CustomResourceDefinitionStub } from './webview/common/createServiceTypes';
3939
import { OpenShiftTerminalManager } from './webview/openshift-terminal/openShiftTerminal';
40+
import { getOutputFormat, helmfsUri, kubefsUri } from './k8s/vfs/kuberesources.virtualfs';
4041

4142
type ExplorerItem = KubernetesObject | Helm.HelmRelease | Context | TreeItem | OpenShiftObject | HelmRepo;
4243

@@ -507,30 +508,55 @@ export class OpenShiftExplorer implements TreeDataProvider<ExplorerItem>, Dispos
507508
@vsCommand('openshift.resource.load')
508509
public static async loadResource(component: KubernetesObject) {
509510
if (component) {
510-
if (component.kind === 'Pod') {
511-
const contextElement: DeploymentPodObject = component;
512-
const pods = await OpenShiftExplorer.getInstance().getPods(contextElement);
513-
if (pods.length === 0) {
514-
contextElement.status.phase = 'Terminated'
515-
void OpenShiftExplorer.getInstance().refresh(contextElement);
516-
void window.showInformationMessage(`Pod ${contextElement.metadata.name} ${contextElement.status.phase.toLowerCase()}`);
517-
void OpenShiftExplorer.getInstance().refresh();
518-
return;
511+
if ('chart' in component && 'name' in component && 'revision' in component
512+
&& component.chart !== 'noChart') { // Deployed Helm Chart
513+
const releaseName: string = typeof component.name === 'string' ? component.name : '';
514+
const revisionString: string | undefined = typeof component.revision === 'string' ? component.revision : undefined;
515+
const revision = revisionString ? parseInt(revisionString, 10) : undefined;
516+
void OpenShiftExplorer.getInstance().loadKubernetesHelmChart(releaseName, revision);
517+
} else {
518+
if (component.kind === 'Pod') {
519+
const contextElement: DeploymentPodObject = component;
520+
const pods = await OpenShiftExplorer.getInstance().getPods(contextElement);
521+
if (pods.length === 0) {
522+
contextElement.status.phase = 'Terminated'
523+
void OpenShiftExplorer.getInstance().refresh(contextElement);
524+
void window.showInformationMessage(`Pod ${contextElement.metadata.name} ${contextElement.status.phase.toLowerCase()}`);
525+
void OpenShiftExplorer.getInstance().refresh();
526+
return;
527+
}
519528
}
529+
void OpenShiftExplorer.getInstance().loadKubernetesCore(component.metadata.namespace, `${component.kind}/${component.metadata.name}`);
520530
}
521-
void OpenShiftExplorer.getInstance().loadKubernetesCore(component.metadata.namespace, `${component.kind}/${component.metadata.name}`);
522531
}
523532
}
524533

525534
/**
526-
* loadind deployment config
535+
* Loading deployment config
527536
* @param namespace namespace
528537
* @param value deployment name
529538
*/
530539
loadKubernetesCore(namespace: string | null, value: string) {
531-
const outputFormat = this.getOutputFormat();
532-
const uri = this.kubefsUri(namespace, value, outputFormat);
540+
const outputFormat = getOutputFormat();
541+
const uri = kubefsUri(namespace, value, outputFormat);
542+
this.loadKubernetesDocument(uri);
543+
}
533544

545+
/**
546+
* Loading an installed Helm Chart config
547+
* @param releaseName Installed Helm Chart release name
548+
* @param revision Installed Helm Chart revision
549+
*/
550+
loadKubernetesHelmChart(releaseName: string, revision: number | undefined) {
551+
const uri = helmfsUri(releaseName, revision);
552+
this.loadKubernetesDocument(uri);
553+
}
554+
555+
/**
556+
* Loading a Kubernates document by its Uri
557+
* @param uri A Kubernetes document Uri
558+
*/
559+
loadKubernetesDocument(uri: Uri) {
534560
const query = this.getComparableQuery(uri);
535561
const openUri = workspace.textDocuments.map((doc) => doc.uri)
536562
.find((docUri) => {
@@ -543,41 +569,15 @@ export class OpenShiftExplorer implements TreeDataProvider<ExplorerItem>, Dispos
543569

544570
// If open document is found for the URI provided, we use its URI to bring its editor to the front
545571
// instead of openning a new editor
546-
workspace.openTextDocument(openUri ? openUri : uri).then((doc) => {
547-
if (doc) {
548-
void window.showTextDocument(doc);
549-
}
550-
},
572+
workspace.openTextDocument(openUri ? openUri : uri).then(
573+
(doc) => {
574+
if (doc) {
575+
void window.showTextDocument(doc);
576+
}
577+
},
551578
(err) => window.showErrorMessage(`Error loading document: ${err}`));
552579
}
553580

554-
/**
555-
* get output format from vs-kubernetes.outputFormat
556-
* default yaml
557-
*
558-
* @returns output format
559-
*/
560-
getOutputFormat(): string {
561-
if (workspace.getConfiguration('vs-kubernetes').has('vs-kubernetes.outputFormat')) {
562-
return workspace.getConfiguration('vs-kubernetes').get['vs-kubernetes.outputFormat'] as string;
563-
}
564-
return 'yaml'
565-
}
566-
567-
kubefsUri(namespace: string | null | undefined, value: string, outputFormat: string, action?: string): Uri {
568-
const K8S_RESOURCE_SCHEME = 'k8smsx';
569-
const K8S_RESOURCE_SCHEME_READONLY = 'k8smsxro';
570-
const KUBECTL_RESOURCE_AUTHORITY = 'loadkubernetescore';
571-
const KUBECTL_DESCRIBE_AUTHORITY = 'kubernetesdescribe';
572-
const docname = `${value.replace('/', '-')}${outputFormat && outputFormat !== '' ? `.${outputFormat}` : ''}`;
573-
const nonce = new Date().getTime();
574-
const nsquery = namespace ? `ns=${namespace}&` : '';
575-
const scheme = action === 'describe' ? K8S_RESOURCE_SCHEME_READONLY : K8S_RESOURCE_SCHEME;
576-
const authority = action === 'describe' ? KUBECTL_DESCRIBE_AUTHORITY : KUBECTL_RESOURCE_AUTHORITY;
577-
const uri = `${scheme}://${authority}/${docname}?${nsquery}value=${value}&_=${nonce}`;
578-
return Uri.parse(uri);
579-
}
580-
581581
/*
582582
* Returns the query string of the specified Uri without "nonce" param,
583583
* so the query strings can be compared.
@@ -640,7 +640,7 @@ export class OpenShiftExplorer implements TreeDataProvider<ExplorerItem>, Dispos
640640

641641
@vsCommand('openshift.resource.openInConsole')
642642
public static openInConsole(component: KubernetesObject) {
643-
void commands.executeCommand('extension.vsKubernetesLoad', { namespace: component.metadata.namespace, kindName: `${component.kind}/${component.metadata.name}` });
643+
void commands.executeCommand('openshift.resource.load', { namespace: component.metadata.namespace, kindName: `${component.kind}/${component.metadata.name}` });
644644
}
645645

646646
@vsCommand('openshift.explorer.reportIssue')

src/extension.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { registerYamlHandlers } from './yaml/yamlDocumentFeatures';
3636

3737
import fsx = require('fs-extra');
3838
import { Oc } from './oc/ocWrapper';
39+
import { K8S_RESOURCE_SCHEME, K8S_RESOURCE_SCHEME_READONLY, KubernetesResourceVirtualFileSystemProvider } from './k8s/vfs/kuberesources.virtualfs';
3940

4041
// eslint-disable-next-line @typescript-eslint/no-empty-function
4142
// this method is called when your extension is deactivated
@@ -76,6 +77,12 @@ export async function activate(extensionContext: ExtensionContext): Promise<unkn
7677
Cluster.extensionContext = extensionContext;
7778
TokenStore.extensionContext = extensionContext;
7879

80+
// Temporarily loaded resource providers
81+
const resourceDocProvider = new KubernetesResourceVirtualFileSystemProvider();
82+
83+
// Link from resources to referenced resources
84+
// const resourceLinkProvider = new KubernetesResourceLinkProvider();
85+
7986
// pick kube config in case multiple are configured
8087
await setKubeConfig();
8188

@@ -104,9 +111,6 @@ export async function activate(extensionContext: ExtensionContext): Promise<unkn
104111
'./feedback',
105112
'./deployment'
106113
)),
107-
commands.registerCommand('clusters.openshift.useProject', (context) =>
108-
commands.executeCommand('extension.vsKubernetesUseNamespace', context),
109-
),
110114
crcStatusItem,
111115
activeNamespaceStatusBarItem,
112116
activeContextStatusBarItem,
@@ -119,6 +123,13 @@ export async function activate(extensionContext: ExtensionContext): Promise<unkn
119123
setupWorkspaceDevfileContext(),
120124
window.registerWebviewViewProvider('openShiftTerminalView', OpenShiftTerminalManager.getInstance(), { webviewOptions: { retainContextWhenHidden: true, } }),
121125
...registerYamlHandlers(),
126+
// Temporarily loaded resource providers
127+
workspace.registerFileSystemProvider(K8S_RESOURCE_SCHEME, resourceDocProvider, { /* TODO: case sensitive? */ }),
128+
workspace.registerFileSystemProvider(K8S_RESOURCE_SCHEME_READONLY, resourceDocProvider, { isReadonly: true }),
129+
130+
// Link from resources to referenced resources
131+
// languages.registerDocumentLinkProvider({ scheme: K8S_RESOURCE_SCHEME }, resourceLinkProvider),
132+
122133
];
123134
disposable.forEach((value) => extensionContext.subscriptions.push(value));
124135

src/helm/helm.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* Copyright (c) Red Hat, Inc. All rights reserved.
33
* Licensed under the MIT License. See LICENSE file in the project root for license information.
44
*-----------------------------------------------------------------------------------------------*/
5+
import { CommandText } from '../base/command';
56
import { CliChannel } from '../cli';
67
import { CliExitData } from '../util/childProcessUtil';
78
import { HelmRepo } from './helmChartType';
@@ -108,3 +109,35 @@ export function ascRepoName(oldRepo: HelmRepo, newRepo: HelmRepo) {
108109
}
109110
return oldRepo.name.localeCompare(newRepo.name);
110111
}
112+
113+
// This file contains utilities for executing command line tools, notably Helm.
114+
115+
export enum HelmSyntaxVersion {
116+
Unknown = 1,
117+
V2 = 2,
118+
V3 = 3,
119+
}
120+
121+
let cachedVersion: HelmSyntaxVersion | undefined = undefined;
122+
123+
export async function helmSyntaxVersion(): Promise<HelmSyntaxVersion> {
124+
if (cachedVersion === undefined) {
125+
const srHelm2 = await CliChannel.getInstance().executeTool(new CommandText('helm version --short -c'));
126+
if (CliExitData.failed(srHelm2)) {
127+
// failed to run Helm; do not cache result
128+
return HelmSyntaxVersion.Unknown;
129+
}
130+
131+
if (srHelm2.stdout.indexOf('v2') >= 0) {
132+
cachedVersion = HelmSyntaxVersion.V2;
133+
} else {
134+
const srHelm3 = await CliChannel.getInstance().executeTool(new CommandText('helm version --short'));
135+
if (!CliExitData.failed(srHelm3) && srHelm3.stdout.indexOf('v3') >= 0) {
136+
cachedVersion = HelmSyntaxVersion.V3;
137+
} else {
138+
return HelmSyntaxVersion.Unknown;
139+
}
140+
}
141+
}
142+
return cachedVersion;
143+
}

src/k8s/node.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export class Node implements ClusterExplorerV1.Node, ClusterExplorerV1.ClusterEx
3737
item.contextValue = `openShift.resource.${this.node}`;
3838
item.command = {
3939
arguments: [this],
40-
command: 'extension.vsKubernetesLoad',
40+
command: 'openshift.resource.load',
4141
title: 'Load'
4242
};
4343
return item;

src/k8s/vfs/errorable.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*-----------------------------------------------------------------------------------------------
2+
* Copyright (c) Red Hat, Inc. All rights reserved.
3+
* Licensed under the MIT License. See LICENSE file in the project root for license information.
4+
*-----------------------------------------------------------------------------------------------*/
5+
6+
export interface Succeeded<T> {
7+
readonly succeeded: true;
8+
readonly result: T;
9+
}
10+
11+
export interface Failed {
12+
readonly succeeded: false;
13+
readonly error: string[];
14+
}
15+
16+
export type Errorable<T> = Succeeded<T> | Failed;
17+
18+
export function succeeded<T>(e: Errorable<T>): e is Succeeded<T> {
19+
return e.succeeded;
20+
}
21+
22+
export function failed<T>(e: Errorable<T>): e is Failed {
23+
return !e.succeeded;
24+
}
25+
26+
// eslint-disable-next-line @typescript-eslint/no-namespace
27+
export namespace Errorable {
28+
export function succeeded<T>(e: Errorable<T>): e is Succeeded<T> {
29+
return e.succeeded;
30+
}
31+
32+
export function failed<T>(e: Errorable<T>): e is Failed {
33+
return !e.succeeded;
34+
}
35+
}

0 commit comments

Comments
 (0)