UNPKG

@controlplane/cli

Version:

Control Plane Corporation CLI

957 lines 43.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HelmGetValues = exports.HelmGetManifest = exports.HelmGet = exports.HelmHistory = exports.HelmList = exports.HelmUninstall = exports.HelmRollback = exports.HelmUpgrade = exports.HelmInstall = exports.HelmTemplate = exports.HelmCmd = void 0; const tmp = require("tmp"); const path = require("path"); const fs = require("fs"); const jsyaml = require("js-yaml"); const helm_1 = require("../helm/helm"); const command_1 = require("../cli/command"); const functions_1 = require("../util/functions"); const resolver_1 = require("./resolver"); const objects_1 = require("../util/objects"); const io_1 = require("../util/io"); const resultFetcher_1 = require("../rest/resultFetcher"); const generic_1 = require("./generic"); const constants_1 = require("../helm/constants"); const helmDeploymentManager_1 = require("../helm/helmDeploymentManager"); const options_1 = require("./options"); const functions_2 = require("../helm/functions"); const k8s_crd_exporter_1 = require("../k8s-crd-exporter"); const helpers_1 = require("../k8s-crd-exporter/helpers"); function withSingleRelease(yargs) { return yargs.positional('release', { type: 'string', describe: 'The release name', demandOption: false, }); } function withSingleChart(yargs) { return yargs.positional('chart', { type: 'string', describe: 'Path to chart', demandOption: true, }); } function withSingleRevision(yargs) { return yargs.positional('revision', { type: 'string', describe: `Revision (version) number. If this argument is omitted or set to 0, it will roll back to the previous release. To see revision numbers, run 'cpln helm history RELEASE'.`, }); } function withHelmTemplateOptions(yargs) { return yargs.options({ set: { multiple: true, describe: 'Set values on the command line (can specify multiple or separate values: --set key1=val1 --set key2=val2)', }, 'set-string': { multiple: true, describe: 'Set STRING values on the command line (can specify multiple or separate values: --set-string key1=val1 --set-string key2=val2)', }, 'set-file': { multiple: true, describe: 'Set values from respective files specified via the command line (can specify multiple or separate values: --set-file key1=path1 --set-file key2=path2)', }, description: { type: 'string', describe: 'Add a custom description', alias: 'desc', }, 'generate-name': { type: 'boolean', describe: 'Generate the name (and omit the NAME parameter)', alias: 'g', }, 'post-renderer': { type: 'string', describe: 'The path to an executable to be used for post rendering. If it exists in $PATH, the binary will be used, otherwise it will try to look for the executable at the given path', }, 'post-renderer-args': { default: [], multiple: true, describe: 'An argument to the post-renderer (can specify multiple or separate values: --post-renderer-args arg1 --post-renderer-args arg2) (default [])', }, repo: { type: 'string', describe: 'Chart repository url where to locate the requested chart', }, values: { type: 'string', multiple: true, describe: 'Specify values in a YAML file or a URL (can specify multiple or separate values: --values value1.yaml --values values2.yaml)', alias: 'f', }, verify: { type: 'boolean', describe: 'Verify the package before using it', }, }); } function withHelmGetOptions(yargs) { return yargs.options({ revision: { type: 'number', describe: 'get the named release with revision', }, }); } function withHelmGetValuesOptions(yargs) { return yargs.options({ all: { type: 'boolean', describe: 'dump all (computed) values', alias: 'a', }, }); } function withWaitOptions(yargs) { return yargs.options({ wait: { type: 'boolean', describe: `If set, will wait until all Workloads are in a ready state before marking the release as successful. It will wait for as long as --timeout`, }, timeout: { type: 'number', default: 5 * 60, describe: 'The amount of seconds to wait for workloads to be ready before timing out. Works only if the "wait" option is set to true.', }, }); } function withCleanupOnFail(yargs) { return yargs.options({ 'cleanup-on-fail': { type: 'boolean', default: false, describe: `allow deletion of new resources created in this rollback when rollback fails`, }, }); } // Commands // class HelmCmd extends command_1.Command { constructor() { super(...arguments); this.command = 'helm'; this.describe = 'Manage helm releases on cpln'; } builder(yargs) { return yargs .demandCommand() .version(false) .help() .command(new HelmGet().toYargs()) .command(new HelmHistory().toYargs()) .command(new HelmInstall().toYargs()) .command(new HelmList().toYargs()) .command(new HelmRollback().toYargs()) .command(new HelmTemplate().toYargs()) .command(new HelmUninstall().toYargs()) .command(new HelmUpgrade().toYargs()); } handle() { } } exports.HelmCmd = HelmCmd; class HelmTemplate extends command_1.Command { constructor() { super(...arguments); this.command = 'template [release] [chart]'; this.describe = 'Generate cpln resources from a template'; } builder(yargs) { return (0, functions_1.pipe)( // withSingleRelease, withSingleChart, withHelmTemplateOptions, helpers_1.withK8sCrdExporterOptions, options_1.withAllOptions)(yargs); } async handle(args) { // Validate Arguments (0, functions_2.validateHelmArgs)(this.session, args); // Process arguments (0, functions_2.processHelmArgs)(args); // Generate a template const template = (0, functions_2.generateHelmTemplate)(args, this.session.context.org, this.session.context.gvc, args.debug || args.verbose); // Output the template in different formats switch (args.output) { case 'crd': const templateResources = this.loadTemplate(template); // Create a new CRD exporter const exporter = new k8s_crd_exporter_1.K8sCrdExporter(this.session.profile, this.session.context.org, this.session.request); // Construct the batch request const batchRequest = { resources: templateResources, }; // Perform the batch request const batchResponse = await exporter.batch(batchRequest, args.namespace, args.keep); // Determine output format let outputFormat; // Extract and show resources in the default format (defaults to JSON if no default is specified) switch (this.session.profile.format.output) { case 'json': case 'json-slim': case 'yaml': case 'yaml-slim': outputFormat = this.session.profile.format.output || 'yaml'; break; default: outputFormat = 'yaml'; break; } // Output the items in the determined format this.session.outFormat(batchResponse.items, { output: outputFormat, color: this.session.profile.format.color }); break; case 'json': case 'json-slim': case 'names': this.session.outFormat(this.loadTemplate(template)); break; default: this.session.out(template); break; } } loadTemplate(template) { return jsyaml.safeLoadAll(template).filter((item) => item !== null); } } exports.HelmTemplate = HelmTemplate; class HelmInstall extends command_1.Command { constructor() { super(...arguments); this.command = 'install [release] [chart]'; this.aliases = ['apply']; this.describe = 'Install a release'; } builder(yargs) { return (0, functions_1.pipe)( // withSingleRelease, withSingleChart, withWaitOptions, withHelmTemplateOptions, generic_1.withTagRemovableOptions, options_1.withAllOptions)(yargs); } async handle(args) { var _a, _b, _c, _d, _e; // Validate Arguments (0, functions_2.validateHelmArgs)(this.session, args); // Process arguments (0, functions_2.processHelmArgs)(args); // Initiate a new Helm instance const helm = new helm_1.Helm(this.client, this.session, args, this.ensureDeletion); const org = this.session.context.org; const gvc = this.session.context.gvc || ''; const description = args.description || ''; const deployedResourceLinks = []; const appliedWorkloadLinks = []; const customTags = { 'cpln/release': helm.stateName, }; // Read default values const defaultValues = (0, functions_2.extractHelmDefaultValues)(args.chart); // Read custom values const valuesFiles = (0, functions_2.extractHelmCustomValues)(args.values); // Create helm config const helmConfig = (0, functions_2.extractHelmConfig)(args, valuesFiles); // Print verbose if (args.debug || args.verbose) { this.session.out((0, functions_2.generateHelmTemplate)(args, org, this.session.context.gvc, args.debug || args.verbose)); } // Generate a template const template = (0, functions_2.generateHelmTemplate)(args, org, this.session.context.gvc); // Load template output into cpln resources const templateResources = jsyaml.safeLoadAll(template).filter((item) => item !== null); // Sort by kind priority (0, objects_1.sortIHasKindByPriority)(templateResources, false); if (templateResources.length === 0) { this.session.abort({ message: 'ERROR: There were no resources found in the generated template. Make sure you have templates in your chart.', }); } // Prepare the necessary variables let state; let release; try { // Attempt to fetch state state = await helm.fetchState(); // Get release release = await helm.getRelease(state); // Validate release helm.validateRelease(release); } catch (e) { // Create a new state if it doesn't exist if (((_a = e.response) === null || _a === void 0 ? void 0 : _a.status) === 404) { try { // Initialize an empty release release = helm.initializeRelease(); // Create new state state = await helm.createState(release); } catch (e) { this.session.out(`ERROR: Unable to create the release ${args.release}.`); this.session.abort({ error: e }); } } else { this.session.out(`ERROR: Unable to fetch the release ${args.release}.`); this.session.abort({ error: e }); } } // Create new deployment let deployment; const deploymentManager = new helmDeploymentManager_1.HelmDeploymentManager(this.session, release); if (release.deployments.length == 0) { deployment = deploymentManager.createFirstDeployment(helm.chart, description, helmConfig, defaultValues, valuesFiles, gvc); release.deployments.push(deployment); } else if (release.deployments.length == 1 && deploymentManager.latestDeployment.status != 'deployed') { // If there is only one deployment, and it is not deployed, return it so we can attempt to deploy it again deployment = deploymentManager.latestDeployment; } else { deployment = deploymentManager.createNextDeployment('pending-upgrade', helm.chart, description, helmConfig, defaultValues, valuesFiles, gvc); release.deployments.push(deployment); } // Get the latest 'deployed' deployment to determine which resources to update or skip const lastDeployedDeployment = deploymentManager.findLatestDeployedDeployment(); // Prepare before apply await helm.prepareBeforeApply(templateResources, deploymentManager, deployment, state, release); // Apply resources individually and update the state accordingly for (const templateResource of templateResources) { try { const ctx = this.session.context; // Modify context if it has been specified in the resource if (templateResource.gvc) { ctx.gvc = templateResource.gvc; } // Avoid applying GVC related resources if the GVC itself does not exist await helm.ensureGvcExists(templateResource, ctx); // Prepare relation links const templateResourceSelfLink = (0, resolver_1.resolveToLink)(templateResource.kind, templateResource.name, ctx); const templateResourceParentLink = (0, resolver_1.kindResolver)(templateResource.kind).parentLink(ctx); // Check if this resource belongs to a different release and throw error accordingly await helm.validateResourceReleaseTag(templateResourceSelfLink); // Update the tags templateResource.tags = { ...templateResource.tags, ...(0, generic_1.fromTagOptions)(args), ...(0, generic_1.fromRemoveTagOptions)(args), ...customTags, }; // Collect workload links so we can wait for them to be ready if requested if (templateResource.kind === 'workload') { appliedWorkloadLinks.push(templateResourceSelfLink); } // A variable to determine whether we should apply template resource or not let shouldApplyResource = await helm.shouldApplyResource(lastDeployedDeployment, templateResourceSelfLink, templateResource); // Apply template resource if needed if (shouldApplyResource) { const copyTemplateResource = JSON.parse(JSON.stringify(templateResource)); // Remove the GVC property before applying if it exists if (copyTemplateResource.gvc) { delete copyTemplateResource.gvc; } // Perform apply const axiosResponse = await this.client.axios.put(templateResourceParentLink, copyTemplateResource); if (axiosResponse.status == 201) { this.session.out(`Created ${axiosResponse.headers.location}`); } else { this.session.out(`Updated ${templateResourceSelfLink}`); } } else { this.session.out(`Unchanged ${templateResourceSelfLink}`); } // Confirm that the resource has been deployed deployedResourceLinks.push(templateResourceSelfLink); // Prepare for updating state const fetchedResource = await this.client.get(templateResourceSelfLink); // Attempt to find resource if the template resource already exists in the deployment let foundResource = deployment.resources.find((resource) => resource.link === templateResourceSelfLink); // If the template resource is already in the state, then just update the version if (foundResource) { foundResource.id = fetchedResource.id; foundResource.version = fetchedResource.version; foundResource.template = templateResource; } else { // Push a new resource into the deployment deployment.resources.push({ id: fetchedResource.id, kind: fetchedResource.kind, version: fetchedResource.version, link: templateResourceSelfLink, template: templateResource, }); } // Update the state await helm.updateState(state, release); } catch (e) { deploymentManager.updateDeploymentStatus(deployment, 'failed', ((_c = (_b = e.response) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.message) || e.message); await helm.updateState(state, release); this.session.err(`ERROR: Unable to install ${templateResource.kind} '${templateResource.name}'.`); this.session.abort({ error: e }); } } // Figure out the resources that are not/no longer part of the new deployment let resourcesToDelete = []; // Sort the deployments in descending order based on the "revision" property const sortedDeployments = release.deployments.sort((a, b) => b.revision - a.revision); // Iterate over the sorted deployments and collect resource that are no longer part of the new state for (const deployment of sortedDeployments) { for (const resource of deployment.resources) { // Skip if the resource has been applied if (deployedResourceLinks.includes(resource.link)) { continue; } // Add to the delete collection resourcesToDelete.push(resource); } } // Delete resources accumulated that are not/no longer part of the deployment if (resourcesToDelete.length > 0) { this.session.out('\nPerforming cleanup...\n'); try { await helm.deleteResources(resourcesToDelete); } catch (e) { deploymentManager.updateDeploymentStatus(deployment, 'failed', ((_e = (_d = e.response) === null || _d === void 0 ? void 0 : _d.data) === null || _e === void 0 ? void 0 : _e.message) || e.message); await helm.updateState(state, release); this.session.abort({ error: e }); } } // Supersede active deployment if (deploymentManager.activeDeployment && release.deployments.length > 1) { deploymentManager.updateDeploymentStatus(deploymentManager.activeDeployment, 'superseded'); } // Mark current deployment as deployed deploymentManager.updateDeploymentStatus(deployment, 'deployed'); await helm.updateState(state, release); // Wait for workloads to be ready if the "wait" option is set to true await helm.awaitWorkloadsReadiness(appliedWorkloadLinks); // Notify user that the release is successful this.session.out(`\nRelease '${args.release}' has been installed successfully!`); // Print notes this.printNotes(args, org); } printNotes(args, org) { // Ignore printing if NOTES.txt does not exist if (!fs.existsSync(path.join(args.chart, 'templates', 'NOTES.txt'))) { return; } // Handle temporary directory for the chart tmp.dir({ unsafeCleanup: true }, (err, tempDirPath, cleanupCallback) => { if (err) { this.session.err(`ERROR: Unable to print notes.`); this.session.abort({ error: err }); } // Set chart path to the temporary directory const templateArgs = { ...args, chart: tempDirPath, }; try { // Copy chart into the temporary directory path (0, io_1.copyDirectorySync)(args.chart, tempDirPath); // Delete all YAML templates from the temporary chart const templatesDir = path.join(tempDirPath, 'templates'); fs.readdirSync(templatesDir).forEach((file) => { if (path.extname(file) === '.yaml') { fs.unlinkSync(path.join(templatesDir, file)); } }); // Read notes content into an object const notesPath = path.join(templatesDir, 'NOTES.txt'); const notesContent = fs.readFileSync(notesPath, 'utf8'); const notesObject = { notes: notesContent }; // Create notes.yaml and dump the notes object into it const notesYamlPath = path.join(templatesDir, 'notes.yaml'); fs.writeFileSync(notesYamlPath, jsyaml.dump(notesObject)); const template = (0, functions_2.generateHelmTemplate)(templateArgs, org, this.session.context.gvc); const notesObj = jsyaml.safeLoad(template); // Finally, print notes this.session.out(`\n${notesObj.notes}`); // Delete tmp directory cleanupCallback(); } catch (e) { // Make sure the directory is deleted on exception cleanupCallback(); // Abort session this.session.err(`ERROR: Unable to print notes.`); this.session.abort({ error: e }); } }); } } exports.HelmInstall = HelmInstall; class HelmUpgrade extends HelmInstall { constructor() { super(...arguments); this.command = 'upgrade [release] [chart]'; this.describe = 'Upgrade a release'; } } exports.HelmUpgrade = HelmUpgrade; class HelmRollback extends command_1.Command { constructor() { super(...arguments); this.command = 'rollback <release> [revision]'; this.describe = 'Roll back a release to a previous revision'; } builder(yargs) { return (0, functions_1.pipe)( // withSingleRelease, withSingleRevision, withCleanupOnFail, withWaitOptions, options_1.withAllOptions)(yargs); } async handle(args) { var _a, _b, _c, _d, _e; // Initiate a new Helm instance const helm = new helm_1.Helm(this.client, this.session, args, this.ensureDeletion); const org = this.session.context.org; const gvc = this.session.context.gvc || ''; const deployedResourceLinks = []; const appliedWorkloadLinks = []; const createdResources = []; let state; let release; try { // Attempt to fetch state state = await helm.fetchState(); // Get release release = await helm.getRelease(state); // Validate release helm.validateRelease(release); } catch (e) { // Create a new state if it doesn't exist if (((_a = e.response) === null || _a === void 0 ? void 0 : _a.status) === 404) { this.session.abort({ message: `ERROR: Release '${args.release}' does not exit.` }); } else { this.session.err(`ERROR: Unable to fetch the release '${args.release}'.`); this.session.abort({ error: e }); } } // Throw an error if deployments were empty if (release.deployments.length == 0) { this.session.abort({ message: `ERROR: Release '${release.release}' has no deployments.` }); } // Figure out rollback target let rollbackTarget; const deploymentManager = new helmDeploymentManager_1.HelmDeploymentManager(this.session, release); // Find the deployment with the specified revision if (args.revision) { const foundDeployment = deploymentManager.getDeployment(args.revision); if (!foundDeployment) { this.session.abort({ message: `ERROR: Deployment with revision '${args.revision}' does not exit.` }); } rollbackTarget = foundDeployment; } else { // Find latest superseded deployment const foundDeployment = deploymentManager.getRollbackTargetDeployment(); if (!foundDeployment) { this.session.abort({ message: `ERROR: There are no deployments to rollback to.`, }); } rollbackTarget = foundDeployment; } // Validate rollback target if (rollbackTarget.status !== 'superseded' && rollbackTarget.status !== 'deployed') { this.session.abort({ message: `ERROR: Deployment with revision '${rollbackTarget.revision}' is invalid, you can only rollback to superseded or deployed deployments.`, }); } // Prepare new deployment properties const chart = { name: rollbackTarget.chart, appVersion: rollbackTarget.appVersion }; const description = `Rollback to ${rollbackTarget.revision}`; // Create new deployment const deployment = deploymentManager.createNextDeployment('pending-rollback', chart, description, rollbackTarget.config, rollbackTarget.values, rollbackTarget.valuesFiles, gvc); release.deployments.push(deployment); // Get the latest 'deployed' deployment to determine which resources to update or skip const lastDeployedDeployment = deploymentManager.findLatestDeployedDeployment(); // Extract resources from rollback target const resources = rollbackTarget.resources.map((helmResource) => helmResource.template); // Prepare before apply await helm.prepareBeforeApply(resources, deploymentManager, deployment, state, release); // Initiate rollback for (const resource of rollbackTarget.resources) { try { const obj = (0, resolver_1.dismantleSelfLink)(org, resource.link); // Modify context if it has been specified in the resource if (resource.template.gvc) { obj.ctx.gvc = resource.template.gvc; } // Avoid applying GVC related resources if the GVC itself does not exist await helm.ensureGvcExists(resource.template, obj.ctx); // Prepare a relation link const resourceSelfLink = (0, resolver_1.resolveToLink)(resource.kind, resource.template.name, obj.ctx); const resourceParentLink = (0, resolver_1.kindResolver)(resource.kind).parentLink(obj.ctx); // Check if this resource belongs to a different release and throw error accordingly await helm.validateResourceReleaseTag(resourceSelfLink); // Collect workload links so we can wait for them to be ready if requested if (resource.kind === 'workload') { appliedWorkloadLinks.push(resource.link); } // A variable to determine whether we should apply this resource or not let shouldApplyResource = await helm.shouldApplyResource(lastDeployedDeployment, resourceSelfLink, resource.template); // Apply template resource if needed if (shouldApplyResource) { const copyResource = JSON.parse(JSON.stringify(resource.template)); // Remove the GVC property before applying if it exists if (copyResource.gvc) { delete copyResource.gvc; } // Perform apply const axiosResponse = await this.client.axios.put(resourceParentLink, copyResource); if (axiosResponse.status == 201) { this.session.out(`Created ${axiosResponse.headers.location}`); } else { this.session.out(`Updated ${resourceSelfLink}`); } } else { this.session.out(`Unchanged ${resourceSelfLink}`); } // Confirm that the resource has been deployed deployedResourceLinks.push(resourceSelfLink); // Prepare for updating deployment const fetchedResource = await this.client.get(resource.link); // Push a new resource into the deployment deployment.resources.push({ id: fetchedResource.id, kind: fetchedResource.kind, version: fetchedResource.version, link: resource.link, template: resource.template, }); // Update the state await helm.updateState(state, release); } catch (e) { deploymentManager.updateDeploymentStatus(deployment, 'failed', ((_c = (_b = e.response) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.message) || e.message); await helm.updateState(state, release); // Attempt to clean up on fail await helm.cleanupOnFail(createdResources); this.session.err(`ERROR: Unable to rollback to deployment revision '${rollbackTarget.revision}'.`); this.session.abort({ error: e }); } } // Figure out the resources that are not/no longer part of the new deployment let resourcesToDelete = []; // Sort the deployments in descending order based on the "revision" property const sortedDeployments = release.deployments.sort((a, b) => b.revision - a.revision); for (const deployment of sortedDeployments) { for (const resource of deployment.resources) { // Skip if the resource has been applied if (deployedResourceLinks.includes(resource.link)) { continue; } // Add to the delete collection resourcesToDelete.push(resource); } } // Delete resources accumulated that are not/no longer part of the deployment if (resourcesToDelete.length > 0) { this.session.out('\nPerforming cleanup...\n'); try { await helm.deleteResources(resourcesToDelete); } catch (e) { deploymentManager.updateDeploymentStatus(deployment, 'failed', ((_e = (_d = e.response) === null || _d === void 0 ? void 0 : _d.data) === null || _e === void 0 ? void 0 : _e.message) || e.message); await helm.updateState(state, release); // Attempt to clean up on fail await helm.cleanupOnFail(createdResources); this.session.abort({ error: e }); } } // Supersede active deployment if (deploymentManager.activeDeployment) { deploymentManager.updateDeploymentStatus(deploymentManager.activeDeployment, 'superseded'); } try { // Mark current deployment as deployed and update state deploymentManager.updateDeploymentStatus(deployment, 'deployed'); await helm.updateState(state, release); // Wait for workloads to be ready if the "wait" option is set to true await helm.awaitWorkloadsReadiness(appliedWorkloadLinks); } catch (e) { // Attempt to clean up on fail await helm.cleanupOnFail(createdResources); this.session.abort({ error: e }); } // Notify user that the rollback is successful this.session.out(`\nRollback to deployment revision '${rollbackTarget.revision}' has been successfully!`); } } exports.HelmRollback = HelmRollback; class HelmUninstall extends command_1.Command { constructor() { super(...arguments); this.command = 'uninstall <release>'; this.aliases = ['destroy', 'del', 'delete', 'un']; this.describe = 'Uninstall a release'; } builder(yargs) { return (0, functions_1.pipe)( // withSingleRelease, options_1.withRequestOptions, options_1.withAllOptions)(yargs); } async handle(args) { var _a; let state; let release; const helm = new helm_1.Helm(this.client, this.session, args, this.ensureDeletion); try { // Attempt to fetch state state = await helm.fetchState(); // Get release release = await helm.getRelease(state); // Validate release helm.validateRelease(release); } catch (e) { if (((_a = e.response) === null || _a === void 0 ? void 0 : _a.status) === 404) { this.session.abort({ message: `ERROR: Release '${args.release}' does not exist` }); } this.session.abort({ error: e }); } // Delete resources of each deployment in case there are resources of failed deployments that are still running if (release.deployments.length > 0) { // Sort the deployments in descending order based on the "revision" property const sortedDeployments = release.deployments.sort((a, b) => b.revision - a.revision); // Iterate over each deployment and delete its resources for (const deployment of sortedDeployments) { // Delete deployment resources await helm.deleteResources(deployment.resources); } } // Delete state await helm.deleteState(release); this.session.out(`\nRelease '${args.release}' has been uninstalled successfully!`); } } exports.HelmUninstall = HelmUninstall; class HelmList extends command_1.Command { constructor() { super(...arguments); this.command = 'list'; this.describe = 'List releases'; } builder(yargs) { return (0, functions_1.pipe)( // options_1.withOrgOptions, options_1.withStandardOptions)(yargs); } async handle(args) { const statePromises = []; const items = []; // Initiate a new Helm instance const helm = new helm_1.Helm(this.client, this.session, args, this.ensureDeletion); // Fetch helm states const stateList = await this.client.post(`${helm.stateParentLink}/-query`, { kind: 'secret', spec: { match: 'any', terms: [ { op: '~', property: 'name', value: 'cpln-release-', }, ], }, }); // Fetch more helm states await (0, resultFetcher_1.fetchPages)(this.client, this.session.format.max, stateList); // Iterate over items and prepare releases for (const state of stateList.items) { const selfLink = (0, resolver_1.resolveToLink)('secret', state.name, this.session.context); statePromises.push(helm.fetchState(selfLink)); } // Fetch states in parallel const states = await Promise.all(statePromises); // Iterate over states and get the latest deployment for (const state of states) { const release = await helm.getRelease(state); const deploymentManager = new helmDeploymentManager_1.HelmDeploymentManager(this.session, release); let item; // Get latest deployment if (deploymentManager.latestDeployment) { item = { release: release.release, ...deploymentManager.latestDeployment, }; } else { // Create a virtual deployment (this shouldn't happen, but could happen) item = { release: release.release, gvc: '', revision: 0, updated: '', status: 'corrupt', chart: '', appVersion: '', }; } items.push(item); } // Prepare accumulator const accumulator = { kind: 'list', itemKind: constants_1.helmListKind, items: items, links: [], }; if (this.session.format.max) { accumulator.items = accumulator.items.slice(0, this.session.format.max); } // Output the deployment list with format await this.session.outFormat(accumulator); } } exports.HelmList = HelmList; class HelmHistory extends command_1.Command { constructor() { super(...arguments); this.command = 'history <release>'; this.describe = 'Fetch release history'; } builder(yargs) { return (0, functions_1.pipe)( // withSingleRelease, options_1.withAllOptions)(yargs); } async handle(args) { var _a; const helm = new helm_1.Helm(this.client, this.session, args, this.ensureDeletion); try { // Attempt to fetch state const state = await helm.fetchState(); // Get release const release = await helm.getRelease(state); // Throw an error if deployments were empty if (release.deployments.length == 0) { this.session.abort({ message: `ERROR: Release '${release.release}' has no deployments.` }); } // Prepare accumulator const accumulator = { kind: 'list', itemKind: constants_1.helmDeploymentKind, items: release.deployments, links: [], }; if (this.session.format.max) { accumulator.items = accumulator.items.slice(0, this.session.format.max); } // Output the state with format await this.session.outFormat(accumulator); } catch (e) { if (((_a = e.response) === null || _a === void 0 ? void 0 : _a.status) === 404) { this.session.abort({ message: `ERROR: Release '${args.release}' does not exist` }); } this.session.abort({ error: e }); } } } exports.HelmHistory = HelmHistory; class HelmGet extends command_1.Command { constructor() { super(); this.command = 'get'; this.describe = 'Download extended information of a named release'; } builder(yargs) { return (yargs .demandCommand() .version(false) .help() // specific .command(new HelmGetManifest().toYargs()) .command(new HelmGetValues().toYargs())); } handle() { } } exports.HelmGet = HelmGet; class HelmGetManifest extends command_1.Command { constructor() { super(...arguments); this.command = 'manifest <release>'; this.describe = 'Download the manifest for a named release'; } builder(yargs) { return (0, functions_1.pipe)( // withSingleRelease, withHelmGetOptions, options_1.withOrgOptions, options_1.withStandardOptions)(yargs); } async handle(args) { var _a; const helm = new helm_1.Helm(this.client, this.session, args, this.ensureDeletion); try { // Attempt to fetch state const state = await helm.fetchState(); // Get release const release = await helm.getRelease(state); // Figure out deployment const deploymentManager = new helmDeploymentManager_1.HelmDeploymentManager(this.session, release); const deployment = deploymentManager.findDeployment(args.revision); // Output the state with format await this.session.outFormat(deployment.resources); } catch (e) { if (((_a = e.response) === null || _a === void 0 ? void 0 : _a.status) === 404) { this.session.abort({ message: `ERROR: Release '${args.release}' does not exist` }); } this.session.abort({ error: e }); } } } exports.HelmGetManifest = HelmGetManifest; class HelmGetValues extends command_1.Command { constructor() { super(...arguments); this.command = 'values <release>'; this.describe = 'Download the values file for a named release'; } builder(yargs) { return (0, functions_1.pipe)( // withSingleRelease, withHelmGetValuesOptions, withHelmGetOptions, options_1.withOrgOptions, options_1.withStandardOptions)(yargs); } async handle(args) { var _a; const helm = new helm_1.Helm(this.client, this.session, args, this.ensureDeletion); try { // Attempt to fetch state const state = await helm.fetchState(); // Get release const release = await helm.getRelease(state); // Figure out deployment const deploymentManager = new helmDeploymentManager_1.HelmDeploymentManager(this.session, release); const deployment = deploymentManager.findDeployment(args.revision); let config = {}; // Handle all if (args.all) { config = { ...deployment.values }; } // Handle existing config config = { ...config, ...deployment.config, }; // Output the state with format await this.session.outFormat(config); } catch (e) { if (((_a = e.response) === null || _a === void 0 ? void 0 : _a.status) === 404) { this.session.abort({ message: `ERROR: Release '${args.release}' does not exist` }); } this.session.abort({ error: e }); } } } exports.HelmGetValues = HelmGetValues; //# sourceMappingURL=helm.js.map