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
2 changes: 0 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ jobs:
run: npm run test
- name: Package
run: |
jq --tab '.extensionDependencies += [ "ms-kubernetes-tools.vscode-kubernetes-tools" ]' package.json > package.json.new
mv package.json.new package.json
node ./out/build/update-readme.js
declare -A targets
targets["win32-x64"]=win32
Expand Down
1 change: 0 additions & 1 deletion README.commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ When installing the extension directly from the VSCode marketplace all the depen

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

### CLI Tools

Expand Down
29 changes: 16 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,6 @@
"onCommand:openshift.explorer.reportIssue",
"onCommand:openshift.explorer.switchContext",
"onCommand:clusters.openshift.project.openConsole",
"onCommand:clusters.openshift.useProject",
"onCommand:clusters.openshift.deploy",
"onCommand:clusters.openshift.build.start",
"onCommand:clusters.openshift.build.showLog",
Expand All @@ -255,7 +254,8 @@
"onCommand:openshift.component.deleteConfigurationFiles",
"onCommand:openshift.component.deleteSourceFolder",
"onWalkthrough:openshiftWalkthrough",
"onWalkthrough:serverlessFunctionWalkthrough"
"onWalkthrough:serverlessFunctionWalkthrough",
"onFileSystem:k8smsx"
],
"contributes": {
"configurationDefaults": {
Expand Down Expand Up @@ -607,11 +607,6 @@
"title": "Debug Component",
"category": "OpenShift"
},
{
"command": "clusters.openshift.useProject",
"title": "Use Project",
"category": "OpenShift"
},
{
"command": "clusters.openshift.project.openConsole",
"title": "Open in Console",
Expand Down Expand Up @@ -1872,10 +1867,6 @@
"when": "view == openshiftComponentsView && viewItem =~ /openshift\\.component.*\\.dev-run.*/",
"group": "c3@4"
},
{
"command": "clusters.openshift.useProject",
"when": "viewItem =~ /\\.openshift\\.inactiveProject/i"
},
{
"command": "clusters.openshift.project.openConsole",
"when": "view == extension.vsKubernetesExplorer && viewItem =~ /vsKubernetes\\.resource\\.project*/i"
Expand Down Expand Up @@ -1947,6 +1938,10 @@
"command": "openshift.deployment.scale",
"when": "view == openshiftProjectExplorer && viewItem =~ /^openshift\\.k8sObject\\.Deployment/"
},
{
"command": "openshift.resource.load",
"when": "view == openshiftProjectExplorer && viewItem == openshift.k8sObject.helm"
},
{
"command": "openshift.resource.unInstall",
"when": "view == openshiftProjectExplorer && viewItem == openshift.k8sObject.helm"
Expand Down Expand Up @@ -2243,6 +2238,15 @@
"default": 0,
"description": "Output verbosity level (value between 0 and 9) for OpenShift Create, and Dev commands in output channel and integrated terminal."
},
"openshiftToolkit.outputFormat": {
"enum": [
"json",
"yaml"
],
"type": "string",
"default": "yaml",
"description": "Output format for Kubernetes specs. One of 'json' or 'yaml' (default)."
},
"openshiftToolkit.searchForToolsInPath": {
"type": "boolean",
"default": false,
Expand Down Expand Up @@ -2311,8 +2315,7 @@
]
},
"extensionDependencies": [
"redhat.vscode-redhat-account",
"ms-kubernetes-tools.vscode-kubernetes-tools"
"redhat.vscode-redhat-account"
],
"__metadata": {
"id": "8fea1f1f-b45c-4eea-b479-3a92c6e697d3",
Expand Down
98 changes: 52 additions & 46 deletions src/explorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { FileContentChangeNotifier, WatchUtil } from './util/watch';
import { vsCommand } from './vscommand';
import { CustomResourceDefinitionStub } from './webview/common/createServiceTypes';
import { OpenShiftTerminalManager } from './webview/openshift-terminal/openShiftTerminal';
import { getOutputFormat, helmfsUri, kubefsUri } from './k8s/vfs/kuberesources.virtualfs';

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

Expand Down Expand Up @@ -196,6 +197,11 @@ export class OpenShiftExplorer implements TreeDataProvider<ExplorerItem>, Dispos
iconPath: element.status === 'failed' ? path.resolve(__dirname, '../../images/context/helmFailed.svg') :
path.resolve(__dirname, '../../images/context/helmDeployed.svg'),
tooltip: `Chart version: ${element.chart}\nApp version: ${element.app_version}\n`,
command: {
title: 'Load',
command: 'openshift.resource.load',
arguments: element.status === 'deployed' ? [element] : undefined
}
};
}

Expand Down Expand Up @@ -507,30 +513,56 @@ export class OpenShiftExplorer implements TreeDataProvider<ExplorerItem>, Dispos
@vsCommand('openshift.resource.load')
public static async loadResource(component: KubernetesObject) {
if (component) {
if (component.kind === 'Pod') {
const contextElement: DeploymentPodObject = component;
const pods = await OpenShiftExplorer.getInstance().getPods(contextElement);
if (pods.length === 0) {
contextElement.status.phase = 'Terminated'
void OpenShiftExplorer.getInstance().refresh(contextElement);
void window.showInformationMessage(`Pod ${contextElement.metadata.name} ${contextElement.status.phase.toLowerCase()}`);
void OpenShiftExplorer.getInstance().refresh();
return;
if ('chart' in component && 'name' in component && 'revision' in component
&& 'status' in component && component.chart !== 'noChart'
&& component.status === 'deployed') { // Deployed Helm Chart
const releaseName: string = typeof component.name === 'string' ? component.name : '';
const revisionString: string | undefined = typeof component.revision === 'string' ? component.revision : undefined;
const revision = revisionString ? parseInt(revisionString, 10) : undefined;
void OpenShiftExplorer.getInstance().loadKubernetesHelmChart(releaseName, revision);
} else {
if (component.kind === 'Pod') {
const contextElement: DeploymentPodObject = component;
const pods = await OpenShiftExplorer.getInstance().getPods(contextElement);
if (pods.length === 0) {
contextElement.status.phase = 'Terminated'
void OpenShiftExplorer.getInstance().refresh(contextElement);
void window.showInformationMessage(`Pod ${contextElement.metadata.name} ${contextElement.status.phase.toLowerCase()}`);
void OpenShiftExplorer.getInstance().refresh();
return;
}
}
void OpenShiftExplorer.getInstance().loadKubernetesCore(component.metadata.namespace, `${component.kind}/${component.metadata.name}`);
}
void OpenShiftExplorer.getInstance().loadKubernetesCore(component.metadata.namespace, `${component.kind}/${component.metadata.name}`);
}
}

/**
* loadind deployment config
* Loading deployment config
* @param namespace namespace
* @param value deployment name
*/
loadKubernetesCore(namespace: string | null, value: string) {
const outputFormat = this.getOutputFormat();
const uri = this.kubefsUri(namespace, value, outputFormat);
const outputFormat = getOutputFormat();
const uri = kubefsUri(namespace, value, outputFormat);
this.loadKubernetesDocument(uri);
}

/**
* Loading an installed Helm Chart config
* @param releaseName Installed Helm Chart release name
* @param revision Installed Helm Chart revision
*/
loadKubernetesHelmChart(releaseName: string, revision: number | undefined) {
const uri = helmfsUri(releaseName, revision);
this.loadKubernetesDocument(uri);
}

/**
* Loading a Kubernates document by its Uri
* @param uri A Kubernetes document Uri
*/
loadKubernetesDocument(uri: Uri) {
const query = this.getComparableQuery(uri);
const openUri = workspace.textDocuments.map((doc) => doc.uri)
.find((docUri) => {
Expand All @@ -543,41 +575,15 @@ export class OpenShiftExplorer implements TreeDataProvider<ExplorerItem>, Dispos

// If open document is found for the URI provided, we use its URI to bring its editor to the front
// instead of openning a new editor
workspace.openTextDocument(openUri ? openUri : uri).then((doc) => {
if (doc) {
void window.showTextDocument(doc);
}
},
workspace.openTextDocument(openUri ? openUri : uri).then(
(doc) => {
if (doc) {
void window.showTextDocument(doc);
}
},
(err) => window.showErrorMessage(`Error loading document: ${err}`));
}

/**
* get output format from vs-kubernetes.outputFormat
* default yaml
*
* @returns output format
*/
getOutputFormat(): string {
if (workspace.getConfiguration('vs-kubernetes').has('vs-kubernetes.outputFormat')) {
return workspace.getConfiguration('vs-kubernetes').get['vs-kubernetes.outputFormat'] as string;
}
return 'yaml'
}

kubefsUri(namespace: string | null | undefined, value: string, outputFormat: string, action?: string): Uri {
const K8S_RESOURCE_SCHEME = 'k8smsx';
const K8S_RESOURCE_SCHEME_READONLY = 'k8smsxro';
const KUBECTL_RESOURCE_AUTHORITY = 'loadkubernetescore';
const KUBECTL_DESCRIBE_AUTHORITY = 'kubernetesdescribe';
const docname = `${value.replace('/', '-')}${outputFormat && outputFormat !== '' ? `.${outputFormat}` : ''}`;
const nonce = new Date().getTime();
const nsquery = namespace ? `ns=${namespace}&` : '';
const scheme = action === 'describe' ? K8S_RESOURCE_SCHEME_READONLY : K8S_RESOURCE_SCHEME;
const authority = action === 'describe' ? KUBECTL_DESCRIBE_AUTHORITY : KUBECTL_RESOURCE_AUTHORITY;
const uri = `${scheme}://${authority}/${docname}?${nsquery}value=${value}&_=${nonce}`;
return Uri.parse(uri);
}

/*
* Returns the query string of the specified Uri without "nonce" param,
* so the query strings can be compared.
Expand Down Expand Up @@ -640,7 +646,7 @@ export class OpenShiftExplorer implements TreeDataProvider<ExplorerItem>, Dispos

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

@vsCommand('openshift.explorer.reportIssue')
Expand Down
17 changes: 14 additions & 3 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { registerYamlHandlers } from './yaml/yamlDocumentFeatures';

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

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

// Temporarily loaded resource providers
const resourceDocProvider = new KubernetesResourceVirtualFileSystemProvider();

// Link from resources to referenced resources
// const resourceLinkProvider = new KubernetesResourceLinkProvider();

// pick kube config in case multiple are configured
await setKubeConfig();

Expand Down Expand Up @@ -104,9 +111,6 @@ export async function activate(extensionContext: ExtensionContext): Promise<unkn
'./feedback',
'./deployment'
)),
commands.registerCommand('clusters.openshift.useProject', (context) =>
commands.executeCommand('extension.vsKubernetesUseNamespace', context),
),
crcStatusItem,
activeNamespaceStatusBarItem,
activeContextStatusBarItem,
Expand All @@ -119,6 +123,13 @@ export async function activate(extensionContext: ExtensionContext): Promise<unkn
setupWorkspaceDevfileContext(),
window.registerWebviewViewProvider('openShiftTerminalView', OpenShiftTerminalManager.getInstance(), { webviewOptions: { retainContextWhenHidden: true, } }),
...registerYamlHandlers(),
// Temporarily loaded resource providers
workspace.registerFileSystemProvider(K8S_RESOURCE_SCHEME, resourceDocProvider, { /* TODO: case sensitive? */ }),
workspace.registerFileSystemProvider(K8S_RESOURCE_SCHEME_READONLY, resourceDocProvider, { isReadonly: true }),

// Link from resources to referenced resources
// languages.registerDocumentLinkProvider({ scheme: K8S_RESOURCE_SCHEME }, resourceLinkProvider),

];
disposable.forEach((value) => extensionContext.subscriptions.push(value));

Expand Down
33 changes: 33 additions & 0 deletions src/helm/helm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Copyright (c) Red Hat, Inc. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*-----------------------------------------------------------------------------------------------*/
import { CommandText } from '../base/command';
import { CliChannel } from '../cli';
import { CliExitData } from '../util/childProcessUtil';
import { HelmRepo } from './helmChartType';
Expand Down Expand Up @@ -108,3 +109,35 @@ export function ascRepoName(oldRepo: HelmRepo, newRepo: HelmRepo) {
}
return oldRepo.name.localeCompare(newRepo.name);
}

// This file contains utilities for executing command line tools, notably Helm.

export enum HelmSyntaxVersion {
Unknown = 1,
V2 = 2,
V3 = 3,
}

let cachedVersion: HelmSyntaxVersion | undefined = undefined;

export async function helmSyntaxVersion(): Promise<HelmSyntaxVersion> {
if (cachedVersion === undefined) {
const srHelm2 = await CliChannel.getInstance().executeTool(new CommandText('helm version --short -c'));
if (CliExitData.failed(srHelm2)) {
// failed to run Helm; do not cache result
return HelmSyntaxVersion.Unknown;
}

if (srHelm2.stdout.indexOf('v2') >= 0) {
cachedVersion = HelmSyntaxVersion.V2;
} else {
const srHelm3 = await CliChannel.getInstance().executeTool(new CommandText('helm version --short'));
if (!CliExitData.failed(srHelm3) && srHelm3.stdout.indexOf('v3') >= 0) {
cachedVersion = HelmSyntaxVersion.V3;
} else {
return HelmSyntaxVersion.Unknown;
}
}
}
return cachedVersion;
}
2 changes: 1 addition & 1 deletion src/k8s/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class Node implements ClusterExplorerV1.Node, ClusterExplorerV1.ClusterEx
item.contextValue = `openShift.resource.${this.node}`;
item.command = {
arguments: [this],
command: 'extension.vsKubernetesLoad',
command: 'openshift.resource.load',
title: 'Load'
};
return item;
Expand Down
35 changes: 35 additions & 0 deletions src/k8s/vfs/errorable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*-----------------------------------------------------------------------------------------------
* Copyright (c) Red Hat, Inc. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*-----------------------------------------------------------------------------------------------*/

export interface Succeeded<T> {
readonly succeeded: true;
readonly result: T;
}

export interface Failed {
readonly succeeded: false;
readonly error: string[];
}

export type Errorable<T> = Succeeded<T> | Failed;

export function succeeded<T>(e: Errorable<T>): e is Succeeded<T> {
return e.succeeded;
}

export function failed<T>(e: Errorable<T>): e is Failed {
return !e.succeeded;
}

// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Errorable {
export function succeeded<T>(e: Errorable<T>): e is Succeeded<T> {
return e.succeeded;
}

export function failed<T>(e: Errorable<T>): e is Failed {
return !e.succeeded;
}
}
Loading