diff --git a/package.json b/package.json index 54c91894e..4acf1b58a 100644 --- a/package.json +++ b/package.json @@ -61,9 +61,13 @@ "onCommand:extension.helmCreate", "onCommand:extension.helmInsertReq", "onCommand:extension.helmDepUp", + "onCommand:extension.helmInspectChart", "onCommand:extension.helmInspectValues", "onCommand:extension.helmGet", "onCommand:extension.helmPackage", + "onCommand:extension.helmFetch", + "onCommand:extension.helmInstall", + "onCommand:extension.helmDependencies", "onCommand:extension.draftVersion", "onCommand:extension.draftCreate", "onCommand:extension.draftUp", @@ -433,6 +437,56 @@ "command": "extension.vsKubernetesDeleteFile", "group": "3", "when": "view == extension.vsKubernetesExplorer && viewItem == vsKubernetes.file" + }, + { + "command": "extension.helmFetch", + "group": "1", + "when": "view == extension.vsKubernetesHelmRepoExplorer && viewItem == vsKubernetes.chart" + }, + { + "command": "extension.helmFetch", + "group": "1", + "when": "view == extension.vsKubernetesHelmRepoExplorer && viewItem == vsKubernetes.chartversion" + }, + { + "command": "extension.helmInstall", + "group": "2", + "when": "view == extension.vsKubernetesHelmRepoExplorer && viewItem == vsKubernetes.chart" + }, + { + "command": "extension.helmInstall", + "group": "2", + "when": "view == extension.vsKubernetesHelmRepoExplorer && viewItem == vsKubernetes.chartversion" + }, + { + "command": "extension.helmDependencies", + "group": "0@3", + "when": "view == extension.vsKubernetesHelmRepoExplorer && viewItem == vsKubernetes.chart" + }, + { + "command": "extension.helmDependencies", + "group": "0@3", + "when": "view == extension.vsKubernetesHelmRepoExplorer && viewItem == vsKubernetes.chartversion" + }, + { + "command": "extension.helmInspectChart", + "group": "0@1", + "when": "view == extension.vsKubernetesHelmRepoExplorer && viewItem == vsKubernetes.chart" + }, + { + "command": "extension.helmInspectChart", + "group": "0@1", + "when": "view == extension.vsKubernetesHelmRepoExplorer && viewItem == vsKubernetes.chartversion" + }, + { + "command": "extension.helmInspectValues", + "group": "0@2", + "when": "view == extension.vsKubernetesHelmRepoExplorer && viewItem == vsKubernetes.chart" + }, + { + "command": "extension.helmInspectValues", + "group": "0@2", + "when": "view == extension.vsKubernetesHelmRepoExplorer && viewItem == vsKubernetes.chartversion" } ], "commandPalette": [ @@ -479,6 +533,14 @@ { "command": "extension.helmInspectValues", "when": "filesExplorerFocus" + }, + { + "command": "extension.helmInspectValues", + "when": "view === extension.vsKubernetesHelmRepoExplorer" + }, + { + "command": "extension.helmInspectChart", + "when": "view === extension.vsKubernetesHelmRepoExplorer" } ] }, @@ -738,6 +800,12 @@ }, { "command": "extension.helmInspectValues", + "title": "Inspect Values", + "description": "Inspect a Helm Chart", + "category": "Helm" + }, + { + "command": "extension.helmInspectChart", "title": "Inspect Chart", "description": "Inspect a Helm Chart", "category": "Helm" @@ -759,6 +827,24 @@ "title": "Package", "description": "Package a chart directory into a versioned chart archive file.", "category": "Helm" + }, + { + "command": "extension.helmFetch", + "title": "Fetch", + "description": "Fetch a Helm chart into the current project", + "category": "Helm" + }, + { + "command": "extension.helmInstall", + "title": "Install", + "description": "Install a Helm chart into the cluster", + "category": "Helm" + }, + { + "command": "extension.helmDependencies", + "title": "Show Dependencies", + "description": "List the dependencies of a Helm chart", + "category": "Helm" } ], "languages": [ diff --git a/src/extension.ts b/src/extension.ts index 1bfb918bf..1f20caf42 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -41,7 +41,7 @@ import * as helm from './helm'; import * as helmexec from './helm.exec'; import { HelmRequirementsCodeLensProvider } from './helm.requirementsCodeLens'; import { HelmTemplateHoverProvider } from './helm.hoverProvider'; -import { HelmTemplatePreviewDocumentProvider, HelmInspectDocumentProvider } from './helm.documentProvider'; +import { HelmTemplatePreviewDocumentProvider, HelmInspectDocumentProvider, HelmDependencyDocumentProvider } from './helm.documentProvider'; import { HelmTemplateCompletionProvider } from './helm.completionProvider'; import { Reporter } from './telemetry'; import * as telemetry from './telemetry-helper'; @@ -115,6 +115,7 @@ export async function activate(context): Promise { const resourceDocProvider = new KubernetesResourceVirtualFileSystemProvider(kubectl, host, vscode.workspace.rootPath); const previewProvider = new HelmTemplatePreviewDocumentProvider(); const inspectProvider = new HelmInspectDocumentProvider(); + const dependenciesProvider = new HelmDependencyDocumentProvider(); const completionProvider = new HelmTemplateCompletionProvider(); const completionFilter = [ "helm", @@ -173,12 +174,16 @@ export async function activate(context): Promise { registerCommand('extension.helmTemplatePreview', helmexec.helmTemplatePreview), registerCommand('extension.helmLint', helmexec.helmLint), registerCommand('extension.helmInspectValues', helmexec.helmInspectValues), + registerCommand('extension.helmInspectChart', helmexec.helmInspectChart), registerCommand('extension.helmDryRun', helmexec.helmDryRun), registerCommand('extension.helmDepUp', helmexec.helmDepUp), registerCommand('extension.helmInsertReq', helmexec.insertRequirement), registerCommand('extension.helmCreate', helmexec.helmCreate), registerCommand('extension.helmGet', helmexec.helmGet), registerCommand('extension.helmPackage', helmexec.helmPackage), + registerCommand('extension.helmFetch', helmexec.helmFetch), + registerCommand('extension.helmInstall', (o) => helmexec.helmInstall(kubectl, o)), + registerCommand('extension.helmDependencies', helmexec.helmDependencies), // Commands - Draft registerCommand('extension.draftVersion', execDraftVersion), @@ -192,8 +197,9 @@ export async function activate(context): Promise { vscode.workspace.registerTextDocumentContentProvider(configureFromCluster.uriScheme, configureFromClusterUI), vscode.workspace.registerTextDocumentContentProvider(createCluster.uriScheme, createClusterUI), vscode.workspace.registerTextDocumentContentProvider(helm.PREVIEW_SCHEME, previewProvider), - vscode.workspace.registerTextDocumentContentProvider(helm.INSPECT_SCHEME, inspectProvider), + vscode.workspace.registerTextDocumentContentProvider(helm.INSPECT_VALUES_SCHEME, inspectProvider), vscode.workspace.registerTextDocumentContentProvider(helm.INSPECT_CHART_SCHEME, inspectProvider), + vscode.workspace.registerTextDocumentContentProvider(helm.DEPENDENCIES_SCHEME, dependenciesProvider), // Completion providers vscode.languages.registerCompletionItemProvider(completionFilter, completionProvider), diff --git a/src/helm.documentProvider.ts b/src/helm.documentProvider.ts index d2ce52132..aa20e7502 100644 --- a/src/helm.documentProvider.ts +++ b/src/helm.documentProvider.ts @@ -7,6 +7,7 @@ import { escape as htmlEscape } from 'lodash'; import * as helm from './helm'; import * as logger from './logger'; +import { failed } from './errorable'; interface HelmDocumentResult { readonly title: string; @@ -32,13 +33,16 @@ function render(document: HelmDocumentResult): string { } function extractChartName(uri: vscode.Uri): string { - if (uri.scheme === helm.INSPECT_CHART_SCHEME && uri.authority === helm.INSPECT_CHART_REPO_AUTHORITY) { + if (uri.authority === helm.INSPECT_REPO_AUTHORITY) { const id = uri.path.substring(1); const version = uri.query; return `${id} ${version}`; } - if (filepath.extname(uri.fsPath) === ".tgz") { - return filepath.basename(uri.fsPath); + if (uri.authority === helm.INSPECT_FILE_AUTHORITY) { + const fsPath = uri.query; + if (filepath.extname(fsPath) === ".tgz") { + return filepath.basename(fsPath); + } } return "Chart"; } @@ -57,8 +61,9 @@ export class HelmInspectDocumentProvider implements vscode.TextDocumentContentPr reject(err); }; - if (uri.scheme === helm.INSPECT_SCHEME) { - const file = uri.fsPath || uri.authority; + if (uri.authority === helm.INSPECT_FILE_AUTHORITY) { + // currently always INSPECT_VALUES_SCHEME + const file = uri.query; const fi = fs.statSync(file); if (!fi.isDirectory() && filepath.extname(file) === ".tgz") { exec.helmExec(`inspect values "${file}"`, printer); @@ -70,10 +75,15 @@ export class HelmInspectDocumentProvider implements vscode.TextDocumentContentPr exec.pickChartForFile(file, { warnIfNoCharts: true }, (path) => { exec.helmExec(`inspect values "${path}"`, printer); }); - } else if (uri.scheme === helm.INSPECT_CHART_SCHEME && uri.authority === helm.INSPECT_CHART_REPO_AUTHORITY) { + } else if (uri.authority === helm.INSPECT_REPO_AUTHORITY) { const id = uri.path.substring(1); const version = uri.query; - exec.helmExec(`inspect ${id} --version ${version}`, printer); + const versionArg = version ? `--version ${version}` : ''; + if (uri.scheme === helm.INSPECT_CHART_SCHEME) { + exec.helmExec(`inspect ${id} ${versionArg}`, printer); + } else if (uri.scheme === helm.INSPECT_VALUES_SCHEME) { + exec.helmExec(`inspect values ${id} ${versionArg}`, printer); + } } }); @@ -134,4 +144,33 @@ export class HelmTemplatePreviewDocumentProvider implements vscode.TextDocumentC }); } -} \ No newline at end of file +} + +export class HelmDependencyDocumentProvider implements vscode.TextDocumentContentProvider { + public provideTextDocumentContent(uri: vscode.Uri, tok: vscode.CancellationToken): vscode.ProviderResult { + return this.provideTextDocumentContentImpl(uri); + } + + async provideTextDocumentContentImpl(uri: vscode.Uri): Promise { + const chartId = uri.path.substring(1); + const version = uri.query; + const dependencies = await exec.helmDependenciesCore(chartId, version); + if (failed(dependencies)) { + return `

${dependencies.error[0]}

`; + } + + const list = dependencies.result + .map(this.formatDependency) + .join('
'); + return `

${chartId} depends on:

    ${list}
`; + } + + formatDependency(d: { [key: string]: string }): string { + const name = d.name; + const version = d.version === '*' ? '' : ` (v${d.version})`; + const repoPrefix = d.repository.startsWith('alias:') ? d.repository.substring('alias:'.length) + '/' : ''; + const repoSuffix = d.repository.startsWith('alias:') ? '' : ` from ${d.repository}`; + const status = ` - ${d.status}`; + return `
  • ${repoPrefix}${name}${version}${repoSuffix}${status}
  • `; + } +} diff --git a/src/helm.exec.ts b/src/helm.exec.ts index 7c3b892b9..d1b7cc666 100644 --- a/src/helm.exec.ts +++ b/src/helm.exec.ts @@ -6,8 +6,10 @@ import { helm as logger } from './logger'; import * as YAML from 'yamljs'; import * as _ from 'lodash'; import * as fs from "fs"; +import * as tmp from 'tmp'; import * as extension from './extension'; import * as explorer from './explorer'; +import * as helmrepoexplorer from './helm.repoExplorer'; import * as helm from './helm'; import { showWorkspaceFolderPick } from './hostutils'; import { shell as sh, ShellResult } from './shell'; @@ -15,6 +17,8 @@ import { K8S_RESOURCE_SCHEME, HELM_RESOURCE_AUTHORITY } from './kuberesources.vi import { Errorable } from './errorable'; import { parseLineOutput } from './outputUtils'; import { sleep } from './sleep'; +import { currentNamespace } from './kubectlUtils'; +import { Kubectl } from './kubectl'; export interface PickChartUIOptions { readonly warnIfNoCharts: boolean; @@ -121,25 +125,42 @@ export function helmLint() { }); } -// helmInspect inspects a packaged chart or a chart dir and returns the values. -// If a non-tgz, non-directory file is passed, this tries to find a parent chart. export function helmInspectValues(arg: any) { + helmInspect(arg, { + noTargetMessage: "Helm Inspect Values is for packaged charts and directories. Launch the command from a file or directory in the file explorer. or a chart or version in the Helm Repos explorer.", + inspectionScheme: helm.INSPECT_VALUES_SCHEME + }); +} + +export function helmInspectChart(arg: any) { + helmInspect(arg, { + noTargetMessage: "Helm Inspect Chart is for packaged charts and directories. Launch the command from a chart or version in the Helm Repos explorer.", + inspectionScheme: helm.INSPECT_CHART_SCHEME + }); +} + +interface InspectionStrategy { + readonly noTargetMessage: string; + readonly inspectionScheme: string; +} + +function helmInspect(arg: any, s: InspectionStrategy) { if (!arg) { - vscode.window.showErrorMessage("Helm Inspect Values is primarily for inspecting packaged charts and directories. Launch the command from a file or directory in the Explorer pane."); + vscode.window.showErrorMessage(s.noTargetMessage); return; } if (!ensureHelm(EnsureMode.Alert)) { return; } - if (arg.kind && arg.kind === helm.INSPECT_CHART_REPO_AUTHORITY) { - const id: string = arg.id; - const version: string = arg.version; - const uri = vscode.Uri.parse(`${helm.INSPECT_CHART_SCHEME}://${helm.INSPECT_CHART_REPO_AUTHORITY}/${id}?${version}`); + if (helmrepoexplorer.isHelmRepoChart(arg) || helmrepoexplorer.isHelmRepoChartVersion(arg)) { + const id = arg.id; + const versionQuery = helmrepoexplorer.isHelmRepoChartVersion(arg) ? `?${arg.version}` : ''; + const uri = vscode.Uri.parse(`${s.inspectionScheme}://${helm.INSPECT_REPO_AUTHORITY}/${id}${versionQuery}`); vscode.commands.executeCommand("vscode.previewHtml", uri, vscode.ViewColumn.Two, "Inspect"); } else { const u = arg as vscode.Uri; - const uri = vscode.Uri.parse("helm-inspect-values://" + u.fsPath); + const uri = vscode.Uri.parse(`${s.inspectionScheme}://${helm.INSPECT_FILE_AUTHORITY}/?${u.fsPath}`); vscode.commands.executeCommand("vscode.previewHtml", uri, vscode.ViewColumn.Two, "Inspect"); } } @@ -193,6 +214,131 @@ export function helmPackage() { }); } +export async function helmFetch(helmObject: helmrepoexplorer.HelmObject | undefined): Promise { + if (!helmObject) { + const id = await vscode.window.showInputBox({ prompt: "Chart to fetch", placeHolder: "stable/mychart" }); + if (id) { + helmFetchCore(id, undefined); + } + } + if (helmrepoexplorer.isHelmRepoChart(helmObject)) { + await helmFetchCore(helmObject.id, undefined); + } else if (helmrepoexplorer.isHelmRepoChartVersion(helmObject)) { + await helmFetchCore(helmObject.id, helmObject.version); + } +} + +async function helmFetchCore(chartId: string, version: string | undefined): Promise { + const projectFolder = await showWorkspaceFolderPick(); + if (!projectFolder) { + return; + } + + const versionArg = version ? `--version ${version}` : ''; + const sr = await helmExecAsync(`fetch ${chartId} --untar ${versionArg} -d "${projectFolder.uri.fsPath}"`); + if (sr.code !== 0) { + await vscode.window.showErrorMessage(`Helm fetch failed: ${sr.stderr}`); + return; + } + await vscode.window.showInformationMessage(`Fetched ${chartId}`); +} + +export async function helmInstall(kubectl: Kubectl, helmObject: helmrepoexplorer.HelmObject | undefined): Promise { + if (!helmObject) { + const id = await vscode.window.showInputBox({ prompt: "Chart to install", placeHolder: "stable/mychart" }); + if (id) { + helmInstallCore(kubectl, id, undefined); + } + } + if (helmrepoexplorer.isHelmRepoChart(helmObject)) { + await helmInstallCore(kubectl, helmObject.id, undefined); + } else if (helmrepoexplorer.isHelmRepoChartVersion(helmObject)) { + await helmInstallCore(kubectl, helmObject.id, helmObject.version); + } +} + +async function helmInstallCore(kubectl: Kubectl, chartId: string, version: string | undefined): Promise { + const ns = await currentNamespace(kubectl); + const nsArg = ns ? `--namespace ${ns}` : ''; + const versionArg = version ? `--version ${version}` : ''; + const sr = await helmExecAsync(`install ${chartId} ${versionArg} ${nsArg}`); + if (sr.code !== 0) { + logger.log(sr.stderr); + await vscode.window.showErrorMessage(`Helm install failed: ${sr.stderr}`); + return; + } + const releaseName = extractReleaseName(sr.stdout); + logger.log(sr.stdout); + await vscode.window.showInformationMessage(`Installed ${chartId} as release ${releaseName}`); +} + +const HELM_INSTALL_NAME_HEADER = "NAME:"; + +function extractReleaseName(helmOutput: string): string { + const lines = helmOutput.split('\n').map((l) => l.trim()); + const nameLine = lines.find((l) => l.startsWith(HELM_INSTALL_NAME_HEADER)); + if (!nameLine) { + return '(unknown)'; + } + return nameLine.substring(HELM_INSTALL_NAME_HEADER.length + 1).trim(); +} + +interface Dependency { + readonly name: string; + readonly version: string; + readonly repository: string; + readonly status: string; +} + +export async function helmDependencies(helmObject: helmrepoexplorer.HelmObject | undefined): Promise { + if (!helmObject) { + const id = await vscode.window.showInputBox({ prompt: "Chart to show dependencies for", placeHolder: "stable/mychart" }); + if (id) { + helmDependenciesLaunchViewer(id, undefined); + } + } + if (helmrepoexplorer.isHelmRepoChart(helmObject)) { + await helmDependenciesLaunchViewer(helmObject.id, undefined); + } else if (helmrepoexplorer.isHelmRepoChartVersion(helmObject)) { + await helmDependenciesLaunchViewer(helmObject.id, helmObject.version); + } +} + +async function helmDependenciesLaunchViewer(chartId: string, version: string | undefined): Promise { + // Boing it back through a HTML preview window + const versionQuery = version ? `?${version}` : ''; + const uri = vscode.Uri.parse(`${helm.DEPENDENCIES_SCHEME}://${helm.DEPENDENCIES_REPO_AUTHORITY}/${chartId}${versionQuery}`); + await vscode.commands.executeCommand("vscode.previewHtml", uri, vscode.ViewColumn.Two, `${chartId} Dependencies`); +} + +export async function helmDependenciesCore(chartId: string, version: string | undefined): Promise> { + const tempDirObj = tmp.dirSync({ prefix: "vsk-fetchfordeps-", unsafeCleanup: true }); + const versionArg = version ? `--version ${version}` : ''; + const fsr = await helmExecAsync(`fetch ${chartId} ${versionArg} -d "${tempDirObj.name}"`); + if (fsr.code !== 0) { + tempDirObj.removeCallback(); + return { succeeded: false, error: [`Helm fetch failed: ${fsr.stderr}`] }; + } + + const tempDirFiles = fs.readdirSync(tempDirObj.name); + const chartPath = filepath.join(tempDirObj.name, tempDirFiles[0]); // should be the only thing in the directory + try { + const dsr = await helmExecAsync(`dep list "${chartPath}"`); + if (dsr.code !== 0) { + return { succeeded: false, error: [`Helm dependency list failed: ${dsr.stderr}`] }; + } + const lines = dsr.stdout.split('\n').map((l) => l.trim()).filter((l) => l.length > 0); + if (lines.length === 1) { + return { succeeded: false, error: [`${chartId} has no dependencies`] }; // I don't feel good about using an error for this but life is short + } + const dependencies = parseLineOutput(lines, helm.HELM_OUTPUT_COLUMN_SEPARATOR); + return { succeeded: true, result: dependencies }; + } finally { + fs.unlinkSync(chartPath); + tempDirObj.removeCallback(); + } +} + // pickChart tries to find charts in this repo. If one is found, fn() is executed with that // chart's path. If more than one are found, the user is prompted to choose one, and then // the fn is executed with that chart. diff --git a/src/helm.repoExplorer.ts b/src/helm.repoExplorer.ts index 15be44257..5aaca285a 100644 --- a/src/helm.repoExplorer.ts +++ b/src/helm.repoExplorer.ts @@ -4,7 +4,7 @@ import * as _ from 'lodash'; import { Host } from './host'; import * as helm from './helm.exec'; -import { INSPECT_CHART_REPO_AUTHORITY, HELM_OUTPUT_COLUMN_SEPARATOR } from './helm'; +import { HELM_OUTPUT_COLUMN_SEPARATOR } from './helm'; import { Errorable, failed } from './errorable'; import { parseLineOutput } from './outputUtils'; @@ -12,11 +12,44 @@ export function create(host: Host): HelmRepoExplorer { return new HelmRepoExplorer(host); } -interface HelmObject { +export enum RepoExplorerObjectKind { + Repo, + Chart, + ChartVersion, + Error, +} + +export interface HelmObject { + readonly kind: RepoExplorerObjectKind; getChildren(): Promise; getTreeItem(): vscode.TreeItem; } +export interface HelmRepo extends HelmObject { + readonly name: string; +} + +export interface HelmRepoChart extends HelmObject { + readonly id: string; +} + +export interface HelmRepoChartVersion extends HelmObject { + readonly id: string; + readonly version: string; +} + +export function isHelmRepo(o: HelmObject): o is HelmRepo { + return o && o.kind === RepoExplorerObjectKind.Repo; +} + +export function isHelmRepoChart(o: HelmObject): o is HelmRepoChart { + return o && o.kind === RepoExplorerObjectKind.Chart; +} + +export function isHelmRepoChartVersion(o: HelmObject): o is HelmRepoChartVersion { + return o && o.kind === RepoExplorerObjectKind.ChartVersion; +} + export class HelmRepoExplorer implements vscode.TreeDataProvider { private onDidChangeTreeDataEmitter: vscode.EventEmitter = new vscode.EventEmitter(); readonly onDidChangeTreeData: vscode.Event = this.onDidChangeTreeDataEmitter.event; @@ -49,7 +82,8 @@ export class HelmRepoExplorer implements vscode.TreeDataProvider { return repos.result; } - refresh(): void { + async refresh(): Promise { + await helm.helmExecAsync('repo update'); this.onDidChangeTreeDataEmitter.fire(); } } @@ -57,6 +91,8 @@ export class HelmRepoExplorer implements vscode.TreeDataProvider { class HelmError implements HelmObject { constructor(private readonly text: string, private readonly detail: string) {} + get kind() { return RepoExplorerObjectKind.Error; } + getTreeItem(): vscode.TreeItem { const treeItem = new vscode.TreeItem(this.text); treeItem.tooltip = this.detail; @@ -68,8 +104,10 @@ class HelmError implements HelmObject { } } -class HelmRepo implements HelmObject { - constructor(private readonly name: string, private readonly url: string) {} +class HelmRepoImpl implements HelmRepo { + constructor(readonly name: string, private readonly url: string) {} + + get kind() { return RepoExplorerObjectKind.Repo; } getTreeItem(): vscode.TreeItem { const treeItem = new vscode.TreeItem(this.name, vscode.TreeItemCollapsibleState.Collapsed); @@ -77,6 +115,7 @@ class HelmRepo implements HelmObject { light: vscode.Uri.file(path.join(__dirname, "../../images/light/helm-blue-vector.svg")), dark: vscode.Uri.file(path.join(__dirname, "../../images/dark/helm-white-vector.svg")), }; + treeItem.contextValue = 'vsKubernetes.repo'; return treeItem; } @@ -89,12 +128,12 @@ class HelmRepo implements HelmObject { } } -class HelmRepoChart implements HelmObject { - private readonly versions: HelmRepoChartVersion[]; +class HelmRepoChartImpl implements HelmRepoChart { + private readonly versions: HelmRepoChartVersionImpl[]; private readonly name: string; - constructor(private readonly repoName, private readonly id: string, private readonly content: { [key: string]: string }[]) { - this.versions = content.map((e) => new HelmRepoChartVersion( + constructor(private readonly repoName, readonly id: string, private readonly content: { [key: string]: string }[]) { + this.versions = content.map((e) => new HelmRepoChartVersionImpl( id, e['chart version'], e['app version'], @@ -103,8 +142,12 @@ class HelmRepoChart implements HelmObject { this.name = id.substring(repoName.length + 1); } + get kind() { return RepoExplorerObjectKind.Chart; } + getTreeItem(): vscode.TreeItem { - return new vscode.TreeItem(this.name, vscode.TreeItemCollapsibleState.Collapsed); + const treeItem = new vscode.TreeItem(this.name, vscode.TreeItemCollapsibleState.Collapsed); + treeItem.contextValue = 'vsKubernetes.chart'; + return treeItem; } async getChildren(): Promise { @@ -112,22 +155,25 @@ class HelmRepoChart implements HelmObject { } } -class HelmRepoChartVersion implements HelmObject { +class HelmRepoChartVersionImpl implements HelmRepoChartVersion { constructor( - private readonly id: string, - private readonly version: string, + readonly id: string, + readonly version: string, private readonly appVersion: string | undefined, private readonly description: string | undefined ) {} + get kind() { return RepoExplorerObjectKind.ChartVersion; } + getTreeItem(): vscode.TreeItem { const treeItem = new vscode.TreeItem(this.version); treeItem.tooltip = this.tooltip(); treeItem.command = { - command: "extension.helmInspectValues", + command: "extension.helmInspectChart", title: "Inspect", - arguments: [{ kind: INSPECT_CHART_REPO_AUTHORITY, id: this.id, version: this.version }] + arguments: [this] }; + treeItem.contextValue = 'vsKubernetes.chartversion'; return treeItem; } @@ -144,7 +190,7 @@ class HelmRepoChartVersion implements HelmObject { } } -async function listHelmRepos(): Promise> { +async function listHelmRepos(): Promise> { const sr = await helm.helmExecAsync("repo list"); if (sr.code !== 0) { return { succeeded: false, error: [sr.stderr] }; @@ -155,11 +201,11 @@ async function listHelmRepos(): Promise> { .map((l) => l.trim()) .filter((l) => l.length > 0) .map((l) => l.split('\t').map((bit) => bit.trim())) - .map((bits) => new HelmRepo(bits[0], bits[1])); + .map((bits) => new HelmRepoImpl(bits[0], bits[1])); return { succeeded: true, result: repos }; } -async function listHelmRepoCharts(repoName: string, repoUrl: string): Promise> { +async function listHelmRepoCharts(repoName: string, repoUrl: string): Promise> { const sr = await helm.helmExecAsync(`search ${repoName}/ -l`); if (sr.code !== 0) { return { succeeded: false, error: [ sr.stderr ]}; @@ -172,7 +218,7 @@ async function listHelmRepoCharts(repoName: string, repoUrl: string): Promise e.name) .toPairs() - .map((p) => new HelmRepoChart(repoName, p[0], p[1])) + .map((p) => new HelmRepoChartImpl(repoName, p[0], p[1])) .value(); return { succeeded: true, result: charts }; } diff --git a/src/helm.ts b/src/helm.ts index 88e385258..657d44b48 100644 --- a/src/helm.ts +++ b/src/helm.ts @@ -1,9 +1,12 @@ export const PREVIEW_SCHEME = 'helm-template-preview'; export const PREVIEW_URI = PREVIEW_SCHEME + '://preview'; -export const INSPECT_SCHEME = 'helm-inspect-values'; -export const HELM_OUTPUT_COLUMN_SEPARATOR = /\t+/g; +export const INSPECT_VALUES_SCHEME = 'helm-inspect-values'; export const INSPECT_CHART_SCHEME = 'helm-inspect-chart'; -export const INSPECT_CHART_REPO_AUTHORITY = 'repo-chart'; +export const INSPECT_REPO_AUTHORITY = 'repo-chart'; +export const INSPECT_FILE_AUTHORITY = 'chart-file'; +export const DEPENDENCIES_SCHEME = 'helm-dependencies'; +export const DEPENDENCIES_REPO_AUTHORITY = 'repo-chart'; +export const HELM_OUTPUT_COLUMN_SEPARATOR = /\t+/g; let previewShown = false;