import * as _ from 'lodash' ; import * as filepath from 'path' ; import * as tmp from 'tmp' ; import * as vscode from 'vscode' ; import * as YAML from 'yamljs' ; import { Context , ExecResult , ExternalBinary , invokeForResult } from './binutilplusplus' ; import { NODE_TYPES } from './components/clusterexplorer/explorer' ; import { ClusterExplorerNode } from './components/clusterexplorer/node' ; import { HelmHistoryNode } from './components/clusterexplorer/node.helmrelease' ; import { refreshExplorer } from './components/clusterprovider/common/explorer' ; import { getToolPath } from './components/config/config' ; import { installDependencies } from './components/installer/installdependencies' ; import { Errorable , failed } from './errorable' ; import { fs as shellfs } from './fs' ; import * as helm from './helm' ; import * as helmrepoexplorer from './helm.repoExplorer' ; import { host , LongRunningUIOptions } from './host' ; import { showWorkspaceFolderPick } from './hostutils' ; import { Kubectl } from './kubectl' ; import { currentNamespace } from './kubectlUtils' ; import { HELM_RESOURCE_AUTHORITY , K8S_RESOURCE_SCHEME } from './kuberesources.virtualfs' ; import { helm as logger } from './logger' ; import { parseLineOutput } from './outputUtils' ; import { ExecCallback , shell as sh , ShellResult } from './shell' ; import { openHelmGeneratedValuesFile , preview } from './utils/preview' ; import * as fs from './wsl-fs' ; import * as shell from './shell' ; export interface PickChartUIOptions { readonly warnIfNoCharts : boolean ; } export enum EnsureMode { Alert , Silent } interface HelmRepositoriesFile { readonly repositories : ReadonlyArray < { readonly name : string ; readonly cache : string ; readonly url : string ; } > ; } export interface HelmRelease { readonly revision : number ; readonly updated : string ; readonly status : string ; readonly chart : string ; readonly appVersion : string ; readonly description : string ; } function helmReleaseFromJSON ( json ) { return { appVersion : json . app_version , ... json } ; } export async function helmVersion ( ) { const syntaxVersion = await helmSyntaxVersion ( ) ; const versionArgs = ( syntaxVersion === HelmSyntaxVersion . V3 ) ? '' : '-c' ; const sr = await helmExecAsync ( `version ${ versionArgs }` ) ; if ( ! sr ) { vscode . window . showErrorMessage ( 'Failed to run Helm' ) ; return ; } if ( sr . code !== 0 ) { vscode . window . showErrorMessage ( sr . stderr ) ; return ; } vscode . window . showInformationMessage ( sr . stdout ) ; } export enum HelmSyntaxVersion { Unknown = 1 , V2 = 2 , V3 = 3 } let cachedVersion = undefined ; export async function helmSyntaxVersion ( ) { if ( cachedVersion === undefined ) { const srHelm2 = await helmExecAsync ( `version --short -c` ) ; if ( ! srHelm2 ) { return HelmSyntaxVersion . Unknown ; } if ( srHelm2 . code === 0 && srHelm2 . stdout . indexOf ( 'v2' ) >= 0 ) { cachedVersion = HelmSyntaxVersion . V2 ; } else { const srHelm3 = await helmExecAsync ( `version --short` ) ; if ( srHelm3 && srHelm3 . code === 0 && srHelm3 . stdout . indexOf ( 'v3' ) >= 0 ) { cachedVersion = HelmSyntaxVersion . V3 ; } else { return HelmSyntaxVersion . Unknown ; } } } return cachedVersion ; } export function helmTemplate ( ) { pickChart ( ( path ) => { helmExec ( `template "${ path }"` , ( code , out , err ) => { if ( code !== 0 ) { vscode . window . showErrorMessage ( err ) ; return ; } vscode . window . showInformationMessage ( "chart rendered successfully" ) ; logger . log ( out ) ; } ) ; } ) ; } export function helmTemplatePreview ( ) { const editor = vscode . window . activeTextEditor ; if ( ! editor ) { vscode . window . showInformationMessage ( "No active editor." ) ; return ; } const filePath = editor . document . fileName ; if ( filePath . indexOf ( "templates" ) < 0 ) { vscode . window . showInformationMessage ( "Not a template: " + filePath ) ; return ; } if ( ! ensureHelm ( EnsureMode . Alert ) ) { return ; } const u = vscode . Uri . parse ( helm . PREVIEW_URI ) ; const f = filepath . basename ( filePath ) ; preview ( u , vscode . ViewColumn . Two , `Preview ${ f }` ) ; helm . recordPreviewHasBeenShown ( ) ; } export function helmDepUp ( arg ) { if ( ! arg ) { pickChart ( ( path ) => helmDepUpCore ( path ) ) ; return ; } const uri = arg . uri || arg ; if ( uri . scheme !== 'file' ) { vscode . window . showErrorMessage ( 'Chart is not on the filesystem' ) ; return ; } const path = filepath . dirname ( uri . fsPath ) ; helmDepUpCore ( path ) ; } function helmDepUpCore ( path ) { logger . log ( "⎈⎈⎈ Updating dependencies for " + path ) ; helmExec ( `dep up "${ path }"` , ( code , out , err ) => { logger . log ( out ) ; logger . log ( err ) ; if ( code !== 0 ) { logger . log ( "⎈⎈⎈ UPDATE FAILED" ) ; } } ) ; } export async function helmCreate ( ) { const createResult = await helmCreateCore ( "Chart name" , "mychart" ) ; if ( createResult && failed ( createResult ) ) { vscode . window . showErrorMessage ( createResult . error [ 0 ] ) ; } } export async function helmCreateCore ( prompt , sampleName ) { const folder = await showWorkspaceFolderPick ( ) ; if ( ! folder ) { return undefined ; } const name = await vscode . window . showInputBox ( { prompt : prompt , placeHolder : sampleName } ) ; if ( ! name ) { return undefined ; } const fullpath = filepath . join ( folder . uri . fsPath , name ) ; const sr = await helmExecAsync ( `create "${ fullpath }"` ) ; if ( ! sr || sr . code !== 0 ) { return { succeeded : false , error : [ sr ? sr . stderr : "Unable to run Helm" ] } ; } return { succeeded : true , result : { name : name , path : fullpath } } ; } export function helmLint ( ) { pickChart ( ( path ) => { logger . log ( "⎈⎈⎈ Linting " + path ) ; helmExec ( `lint "${ path }"` , ( code , out , err ) => { logger . log ( out ) ; logger . log ( err ) ; if ( code !== 0 ) { logger . log ( "⎈⎈⎈ LINTING FAILED" ) ; } } ) ; } ) ; } export function helmFetchValues ( arg ) { helmInspect ( arg , { noTargetMessage : "Helm generate values.yaml 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 . FETCH_VALUES_SCHEME , generateFile : true , } ) ; } export function helmInspectChart ( arg ) { 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 , generateFile : false } ) ; } interface InspectionStrategy { readonly noTargetMessage : string ; readonly inspectionScheme : string ; readonly generateFile : boolean ; } function helmInspect ( arg , s ) { if ( ! arg ) { vscode . window . showErrorMessage ( s . noTargetMessage ) ; return ; } if ( ! ensureHelm ( EnsureMode . Alert ) ) { return ; } if ( helmrepoexplorer . isHelmRepoChart ( arg ) || helmrepoexplorer . isHelmRepoChartVersion ( arg ) ) { const id = arg . id ; if ( s . generateFile ) { const versionQuery = helmrepoexplorer . isHelmRepoChartVersion ( arg ) ? `&version=${ arg . version }` : "" ; let valuesFileName = `${ id }-values.yaml` ; if ( versionQuery !== "" ) { valuesFileName = `${ id }-${ versionQuery . replace ( '&version=' , "" ) }-values.yaml` ; } const uri = vscode . Uri . parse ( `${ s . inspectionScheme }://${ helm . INSPECT_REPO_AUTHORITY }/${ valuesFileName }?chart=${ id }${ versionQuery }` ) ; openHelmGeneratedValuesFile ( uri ) ; } else { const versionQuery = helmrepoexplorer . isHelmRepoChartVersion ( arg ) ? `?version=${ arg . version }` : "" ; const uri = vscode . Uri . parse ( `${ s . inspectionScheme }://${ helm . INSPECT_REPO_AUTHORITY }/${ id }${ versionQuery }` ) ; preview ( uri , vscode . ViewColumn . Two , "Inspect" ) ; } } else { const u = arg as vscode . Uri ; const uri = vscode . Uri . parse ( `${ s . inspectionScheme }://${ helm . INSPECT_FILE_AUTHORITY }/?${ u . fsPath }` ) ; preview ( uri , vscode . ViewColumn . Two , "Inspect" ) ; } } export function helmDryRun ( ) { pickChart ( async ( path ) => { const syntaxVersion = await helmSyntaxVersion ( ) ; const generateNameArg = ( syntaxVersion === HelmSyntaxVersion . V3 ) ? '--generate-name' : '' ; logger . log ( "⎈⎈⎈ Installing (dry-run) " + path ) ; helmExec ( `install --dry-run ${ generateNameArg } --debug "${ path }"` , ( code , out , err ) => { logger . log ( out ) ; logger . log ( err ) ; if ( code !== 0 ) { logger . log ( "⎈⎈⎈ INSTALL FAILED" ) ; } } ) ; } ) ; } export function helmGet ( resourceNode ? ) { if ( ! resourceNode ) { return ; } if ( resourceNode . nodeType !== NODE_TYPES . helm . history && resourceNode . nodeType !== NODE_TYPES . helm . release ) { return ; } const releaseName = resourceNode . releaseName ; const revisionNumber = ( resourceNode . nodeType === NODE_TYPES . helm . history ? resourceNode . release . revision : undefined ) ; const uri = helmfsUri ( releaseName , revisionNumber ) ; vscode . workspace . openTextDocument ( uri ) . then ( ( doc ) => { if ( doc ) { vscode . window . showTextDocument ( doc ) ; } } ) ; } export function helmUninstall ( resourceNode ? ) { if ( ! resourceNode ) { return ; } if ( resourceNode . nodeType !== NODE_TYPES . helm . release ) { return ; } const releaseName = resourceNode . releaseName ; logger . log ( "⎈⎈⎈ Uninstalling " + releaseName ) ; vscode . window . showWarningMessage ( `You are about to uninstall ${ releaseName }. This action cannot be undone.` , 'Uninstall' ) . then ( ( opt ) => { if ( opt === "Uninstall" ) { helmExec ( `del ${ releaseName }` , ( code , out , err ) => { logger . log ( out ) ; logger . log ( err ) ; if ( code !== 0 ) { logger . log ( "⎈⎈⎈ UNINSTALL FAILED" ) ; vscode . window . showErrorMessage ( `Error uninstalling ${ releaseName } ${ err }` ) ; } else { vscode . window . showInformationMessage ( `Release ${ releaseName } successfully uninstalled.` ) ; refreshExplorer ( ) ; } } ) ; } } ) ; } export async function helmGetHistory ( release ) { if ( ! ensureHelm ( EnsureMode . Alert ) ) { return { succeeded : false , error : [ "Helm client is not installed" ] } ; } const sr = await helmExecAsync ( `history ${ release } --output json` ) ; if ( ! sr || sr . code !== 0 ) { const message = `Helm fetch history failed: ${ sr ? sr . stderr : "Unable to run Helm" }` ; await vscode . window . showErrorMessage ( message ) ; return { succeeded : false , error : [ message ] } ; } else { const releasesJSON = JSON . parse ( sr . stdout ) ; const releases = releasesJSON . map ( helmReleaseFromJSON ) ; return { succeeded : true , result : releases . reverse ( ) } ; } } export async function helmRollback ( resourceNode ? ) { if ( ! resourceNode ) { return ; } if ( resourceNode . release . status === "deployed" ) { vscode . window . showInformationMessage ( 'This is the currently deployed release' ) ; return ; } const releaseName = resourceNode . releaseName ; const release = resourceNode . release ; vscode . window . showWarningMessage ( `You are about to rollback ${ releaseName } to release version ${ release . revision }. Continue?` , 'Rollback' ) . then ( ( opt ) => { if ( opt === "Rollback" ) { helmExec ( `rollback ${ releaseName } ${ release . revision } --cleanup-on-fail` , async ( code , out , err ) => { logger . log ( out ) ; logger . log ( err ) ; if ( out !== "" ) { vscode . window . showInformationMessage ( `Release ${ releaseName } successfully rolled back to ${ release . revision }.` ) ; refreshExplorer ( ) ; } if ( code !== 0 ) { vscode . window . showErrorMessage ( `Error rolling back to ${ release . revision } for ${ releaseName } ${ err }` ) ; } } ) ; } } ) ; } export function helmfsUri ( releaseName , revision ) { const revisionSuffix = revision ? `-${ revision }` : '' ; const revisionQuery = revision ? `&revision=${ revision }` : '' ; const docname = `helmrelease-${ releaseName }${ revisionSuffix }.txt` ; const nonce = new Date ( ) . getTime ( ) ; const uri = `${ K8S_RESOURCE_SCHEME }://${ HELM_RESOURCE_AUTHORITY }/${ docname }?value=${ releaseName }${ revisionQuery }&_=${ nonce }` ; return vscode . Uri . parse ( uri ) ; } export function helmPackage ( ) { pickChart ( ( path ) => { const options = { openLabel : "Save Package" , canSelectFiles : false , canSelectFolders : true , canSelectMany : false } ; vscode . window . showOpenDialog ( options ) . then ( ( packagePath ) => { if ( packagePath && packagePath . length === 1 ) { if ( packagePath [ 0 ] . scheme !== 'file' ) { vscode . window . showErrorMessage ( 'Packaging folder must be a filesystem folder' ) ; return ; } logger . log ( "⎈⎈⎈ Packaging " + path ) ; helmExec ( `package "${ path }" -d "${ packagePath [ 0 ] . fsPath }"` , ( code , out , err ) => { logger . log ( out ) ; logger . log ( err ) ; if ( code !== 0 ) { vscode . window . showErrorMessage ( err ) ; } } ) ; } return ; } ) ; } ) ; } export async function helmFetch ( helmObject ) { 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 , version ) { if ( ! shell . isSafe ( chartId ) ) { vscode . window . showWarningMessage ( `Unexpected characters in chart name ${ chartId }. Use Helm CLI to fetch this chart.` ) ; return ; } if ( version && ! shell . isSafe ( version ) ) { vscode . window . showWarningMessage ( `Unexpected characters in chart version ${ version }. Use Helm CLI to fetch this chart.` ) ; return ; } 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 || sr . code !== 0 ) { await vscode . window . showErrorMessage ( `Helm fetch failed: ${ sr ? sr . stderr : "Unable to run Helm" }` ) ; return ; } await vscode . window . showInformationMessage ( `Fetched ${ chartId }` ) ; } export async function helmInstall ( kubectl , helmObject ) { 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 , chartId , version ) { if ( ! shell . isSafe ( chartId ) ) { vscode . window . showWarningMessage ( `Unexpected characters in chart name ${ chartId }. Use Helm CLI to install this chart.` ) ; return ; } if ( version && ! shell . isSafe ( version ) ) { vscode . window . showWarningMessage ( `Unexpected characters in chart version ${ version }. Use Helm CLI to install this chart.` ) ; return ; } const syntaxVersion = await helmSyntaxVersion ( ) ; const ns = await currentNamespace ( kubectl ) ; const nsArg = ns ? `--namespace ${ ns }` : '' ; const versionArg = version ? `--version ${ version }` : '' ; const generateNameArg = ( syntaxVersion === HelmSyntaxVersion . V3 ) ? '--generate-name' : '' ; const sr = await helmExecAsync ( `install ${ chartId } ${ versionArg } ${ nsArg } ${ generateNameArg }` ) ; if ( ! sr || sr . code !== 0 ) { const message = sr ? sr . stderr : "Unable to run Helm" ; logger . log ( message ) ; await vscode . window . showErrorMessage ( `Helm install failed: ${ message }` ) ; 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 ) { 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 ( ) ; } export async function helmDependencies ( helmObject ) { 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 , version ) { if ( ! shell . isSafe ( chartId ) ) { vscode . window . showWarningMessage ( `Unexpected characters in chart name ${ chartId }. Use Helm CLI to install this chart.` ) ; return ; } if ( version && ! shell . isSafe ( version ) ) { vscode . window . showWarningMessage ( `Unexpected characters in chart version ${ version }. Use Helm CLI to install this chart.` ) ; return ; } const versionQuery = version ? `?${ version }` : '' ; const uri = vscode . Uri . parse ( `${ helm . DEPENDENCIES_SCHEME }://${ helm . DEPENDENCIES_REPO_AUTHORITY }/${ chartId }${ versionQuery }` ) ; await preview ( uri , vscode . ViewColumn . Two , `${ chartId } Dependencies` ) ; } export async function helmDependenciesCore ( chartId , version ) { 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 || fsr . code !== 0 ) { tempDirObj . removeCallback ( ) ; return { succeeded : false , error : [ `Helm fetch failed: ${ fsr ? fsr . stderr : "Unable to run Helm" }` ] } ; } const tempDirFiles = sh . ls ( tempDirObj . name ) ; const chartPath = filepath . join ( tempDirObj . name , tempDirFiles [ 0 ] ) ; try { const dsr = await helmExecAsync ( `dep list "${ chartPath }"` ) ; if ( ! dsr || dsr . code !== 0 ) { return { succeeded : false , error : [ `Helm dependency list failed: ${ dsr ? dsr . stderr : "Unable to run Helm" }` ] } ; } 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` ] } ; } const dependencies = parseLineOutput ( lines , helm . HELM_OUTPUT_COLUMN_SEPARATOR ) ; return { succeeded : true , result : dependencies } ; } finally { fs . unlinkSync ( chartPath ) ; tempDirObj . removeCallback ( ) ; } } export function pickChart ( fn ) { if ( ! vscode . workspace . workspaceFolders || vscode . workspace . workspaceFolders . length === 0 ) { vscode . window . showErrorMessage ( "This command requires an open folder." ) ; return ; } vscode . workspace . findFiles ( "**/Chart.yaml" , "" , 1024 ) . then ( ( matches ) => { switch ( matches . length ) { case 0 : vscode . window . showErrorMessage ( "No charts found" ) ; return ; case 1 : const p = filepath . dirname ( matches [ 0 ] . fsPath ) ; fn ( p ) ; return ; default : const pathPicks = matches . map ( ( item ) => quickPickForChart ( item ) ) ; vscode . window . showQuickPick ( pathPicks ) . then ( ( picked ) => { if ( picked ) { fn ( picked . chartDir ) ; } } ) ; return ; } } ) ; } function quickPickForChart ( chartUri ) { const chartDir = filepath . dirname ( chartUri . fsPath ) ; const displayName = vscode . workspace . rootPath ? filepath . relative ( vscode . workspace . rootPath , chartDir ) : chartDir ; return { label : displayName || "." , chartDir : chartDir } ; } class Chart { public name : string ; public version : string ; public appVersion : string ; } export function loadChartMetadata ( chartDir ) { const f = filepath . join ( chartDir , "Chart.yaml" ) ; let c ; try { c = YAML . load ( f ) ; } catch ( err ) { vscode . window . showErrorMessage ( "Chart.yaml: " + err ) ; } return c ; } export function pickChartForFile ( file , options , fn ) { vscode . workspace . findFiles ( "**/Chart.yaml" , "" , 1024 ) . then ( ( matches ) => { switch ( matches . length ) { case 0 : if ( options . warnIfNoCharts ) { vscode . window . showErrorMessage ( "No charts found" ) ; } return ; case 1 : const p = filepath . dirname ( matches [ 0 ] . fsPath ) ; fn ( p ) ; return ; default : const paths = Array . of < string > ( ) ; matches . forEach ( ( item ) => { const dirname = filepath . dirname ( item . fsPath ) ; const rel = filepath . relative ( dirname , file ) ; if ( rel . indexOf ( ".." ) >= 0 ) { return ; } paths . push ( dirname ) ; } ) ; if ( paths . length === 0 ) { if ( options . warnIfNoCharts ) { vscode . window . showErrorMessage ( "Chart not found for " + file ) ; } return ; } if ( paths . length >= 1 ) { fn ( paths [ 0 ] ) ; return ; } return ; } } ) ; } export function helmExec ( args , fn ) { if ( ! ensureHelm ( EnsureMode . Alert ) ) { return ; } const configuredBin = getToolPath ( host , sh , 'helm' ) ; const bin = configuredBin ? `"${ configuredBin }"` : "helm" ; const cmd = `${ bin } ${ args }` ; const promise = sh . exec ( cmd ) ; promise . then ( ( res ) => { if ( res ) { fn ( res . code , res . stdout , res . stderr ) ; } else { console . log ( 'exec failed: unable to run Helm' ) ; } } , ( err ) => { console . log ( `exec failed! (${ err })` ) ; } ) ; } export async function helmExecAsync ( args ) { if ( ! ensureHelm ( EnsureMode . Alert ) ) { return { code : - 1 , stdout : "" , stderr : "" } ; } const configuredBin = getToolPath ( host , sh , 'helm' ) ; const bin = configuredBin ? `"${ configuredBin }"` : "helm" ; const cmd = `${ bin } ${ args }` ; return await sh . exec ( cmd ) ; } const HELM_BINARY = { binBaseName : 'helm' , configKeyName : 'helm' , displayName : 'Helm' , offersInstall : true , } ; const HELM_CONTEXT = { host : host , fs : shellfs , shell : sh , pathfinder : undefined , binary : HELM_BINARY , status : undefined , } ; export async function helmInvokeCommand ( command ) { return await invokeForResult ( HELM_CONTEXT , command , undefined ) ; } export async function helmInvokeCommandWithFeedback ( command , uiOptions ) { return await HELM_CONTEXT . host . longRunning ( uiOptions , ( ) => invokeForResult ( HELM_CONTEXT , command , undefined ) ) ; } const HELM_PAGING_PREFIX = "next:" ; export async function helmListAll ( namespace ? ) { if ( ! ensureHelm ( EnsureMode . Alert ) ) { return { succeeded : false , error : [ "Helm client is not installed" ] } ; } const releases = [ ] ; let offset = null ; do { const nsarg = namespace ? `--namespace ${ namespace }` : "" ; const offsetarg = offset ? `--offset ${ offset }` : "" ; const sr = await helmExecAsync ( `list --max 0 ${ nsarg } ${ offsetarg }` ) ; if ( ! sr || sr . code !== 0 ) { return { succeeded : false , error : [ sr ? sr . stderr : "Unable to run Helm" ] } ; } const lines = sr . stdout . split ( '\n' ) . map ( ( s ) => s . trim ( ) ) . filter ( ( l ) => l . length > 0 ) ; if ( lines . length > 0 ) { if ( lines [ 0 ] . startsWith ( HELM_PAGING_PREFIX ) ) { const pagingInfo = lines . shift ( ) ! ; offset = pagingInfo . substring ( HELM_PAGING_PREFIX . length ) . trim ( ) ; } else { offset = null ; } } if ( lines . length > 0 ) { const helmReleases = parseLineOutput ( lines , helm . HELM_OUTPUT_COLUMN_SEPARATOR ) ; releases . push ( ... helmReleases ) ; } } while ( offset !== null ) ; return { succeeded : true , result : releases } ; } export function ensureHelm ( mode ) { const configuredBin = getToolPath ( host , sh , 'helm' ) ; if ( configuredBin ) { if ( fs . existsSync ( configuredBin ) ) { return true ; } if ( mode === EnsureMode . Alert ) { vscode . window . showErrorMessage ( `${ configuredBin } does not exist!` , "Install dependencies" ) . then ( ( str ) => { if ( str === "Install dependencies" ) { installDependencies ( ) ; } } ) ; } return false ; } if ( sh . which ( "helm" ) ) { return true ; } if ( mode === EnsureMode . Alert ) { vscode . window . showErrorMessage ( `Could not find Helm binary.` , "Install dependencies" ) . then ( ( str ) => { if ( str === "Install dependencies" ) { installDependencies ( ) ; } } ) ; } return false ; } export class Requirement { public repository : string ; public name : string ; public version : string ; toString ( ) { return `- name: ${ this . name }
  version: ${ this . version }
  repository: ${ this . repository }
` ; } } export function insertRequirement ( ) { vscode . window . showInputBox ( { prompt : "Chart" , placeHolder : "stable/redis" , } ) . then ( ( val ) => { if ( ! val ) { return ; } const req = searchForChart ( val ) ; if ( ! req ) { vscode . window . showErrorMessage ( `Chart ${ val } not found` ) ; return ; } const ed = vscode . window . activeTextEditor ; if ( ! ed ) { logger . log ( YAML . stringify ( req ) ) ; return ; } ed . insertSnippet ( new vscode . SnippetString ( req . toString ( ) ) ) ; } ) ; } export function searchForChart ( name ) { const parts = name . split ( "/" , 2 ) ; if ( parts . length !== 2 ) { logger . log ( "Chart should be of the form REPO/CHARTNAME" ) ; return undefined ; } const hh = helmHome ( ) ; const reposFile = filepath . join ( hh , "repository" , "repositories.yaml" ) ; if ( ! fs . existsSync ( reposFile ) ) { vscode . window . showErrorMessage ( `Helm repositories file ${ reposFile } not found.` ) ; return undefined ; } const repos = YAML . load ( reposFile ) ; let req ; repos . repositories . forEach ( ( repo ) => { if ( repo . name === parts [ 0 ] ) { const cache = YAML . load ( repo . cache ) ; _ . each ( cache . entries , ( releases , name ) => { if ( name === parts [ 1 ] ) { req = new Requirement ( ) ; req . repository = repo . url ; req . name = name ; req . version = releases [ 0 ] . version ; return ; } } ) ; return ; } } ) ; return req ; } export function helmHome ( ) { const h = sh . home ( ) ; return process . env [ "HELM_HOME" ] || filepath . join ( h , '.helm' ) ; }