@controlplane/cli
Version:
Control Plane Corporation CLI
285 lines • 12.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.decodeReleaseFromSecret = exports.encodeRelease = exports.getReleaseSecretNameLegacy = exports.getReleaseSecretNameWithoutRevision = exports.getReleaseSecretName = exports.extractHelmConfig = exports.extractHelmDefaultValues = exports.extractHelmCustomValues = exports.extractHelmChartMetadata = exports.getTemplateResources = exports.generateHelmTemplate = exports.processHelmArgs = void 0;
const path = require("path");
const jsyaml = require("js-yaml");
const child_process_1 = require("child_process");
const names_1 = require("../util/names");
const fs_1 = require("fs");
const io_1 = require("../util/io");
const pako_1 = require("pako");
const constants_1 = require("./constants");
const objects_1 = require("../util/objects");
// Exported //
function processHelmArgs(args) {
// A user cannot specify a generate release name and a release name at the same time
if (args.release && args.chart && args.generateName) {
throw new Error('ERROR: Cannot set --generate-name and also specify a release name');
}
// Generate a random name
if (args.generateName || !args.release) {
// If the release positional is defined with a value, then it must be the path to the
// chart since generate name and release cannot be specified together
if (args.release && !args.chart) {
args.chart = args.release;
}
// Release will now have a random name
args.release = (0, names_1.nextName)(); // Will produce an 8 character long random string
}
else {
// If we got here, then the release name has been specified by the user, in
// that case, let's validate the length of the specified relase name
if (args.release.length > constants_1.CPLN_HELM_RELEASE_MAX_LENGTH) {
throw new Error(`ERROR: Release name "${args.release}" exceeds max length of ${constants_1.CPLN_HELM_RELEASE_MAX_LENGTH}.`);
}
}
// Use current directory for the chart path if it is not specified
if (!args.chart) {
args.chart = '.';
}
}
exports.processHelmArgs = processHelmArgs;
function generateHelmTemplate(args, org, gvc, debug) {
var _a, _b, _c, _d, _e, _f;
let options = [];
const customOptions = [
// Placeholder comment to ensure each item is on a new line
`--set cpln.org=${org}`,
`--set globals.cpln.org=${org}`,
`--set global.cpln.org=${org}`,
];
if (gvc) {
customOptions.push(`--set cpln.gvc=${gvc}`);
customOptions.push(`--set globals.cpln.gvc=${gvc}`);
customOptions.push(`--set global.cpln.gvc=${gvc}`);
}
// Handle helm template options
if (args.set && args.set.length > 0) {
if (typeof args.set === 'string') {
args.set = [args.set];
}
options.push(args.set.map((set) => `--set ${safeSet(set)}`).join(' '));
}
if (args.setString) {
if (typeof args.setString === 'string') {
args.setString = [args.setString];
}
options.push(args.setString.map((setString) => `--set-string ${safeSet(setString)}`).join(' '));
}
if (args.setFile) {
if (typeof args.setFile === 'string') {
args.setFile = [args.setFile];
}
options.push(args.setFile.map((setFile) => `--set-file ${safeSet(setFile)}`).join(' '));
}
if (args.dependencyUpdate) {
options.push(`--dependency-update`);
}
if (args.description) {
options.push(`--description "${args.description}"`);
}
if (args.postRenderer) {
options.push(`--post-renderer ${args.postRenderer}`);
}
if (args.postRendererArgs && args.postRendererArgs.length > 0) {
if (typeof args.postRendererArgs === 'string') {
args.postRendererArgs = [args.postRendererArgs];
}
options.push(args.postRendererArgs.map((postRendererArgs) => `--post-renderer-args ${safeSet(postRendererArgs)}`).join(' '));
}
if (args.repo) {
options.push(`--repo ${args.repo}`);
}
if (args.values && args.values.length > 0) {
if (typeof args.values === 'string') {
args.values = [args.values];
}
options.push(args.values.map((values) => `--values ${values}`).join(' '));
}
if (args.verify) {
options.push(`--verify`);
}
if (debug) {
options.push(`--debug`);
}
const helmTemplateCommand = `helm template ${args.release} ${args.chart} ${options.join(' ')} ${customOptions.join(' ')}`;
try {
// Make sure output is NOT inherited by the parent process
return (0, child_process_1.execSync)(helmTemplateCommand, {
stdio: 'pipe',
encoding: 'utf8',
maxBuffer: 10 * 1024 * 1024,
});
}
catch (e) {
const stderr = (_c = (_b = (_a = e === null || e === void 0 ? void 0 : e.stderr) === null || _a === void 0 ? void 0 : _a.toString) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : '';
const stdout = (_f = (_e = (_d = e === null || e === void 0 ? void 0 : e.stdout) === null || _d === void 0 ? void 0 : _d.toString) === null || _e === void 0 ? void 0 : _e.call(_d)) !== null && _f !== void 0 ? _f : '';
// Helm sometimes mixes channels
const combined = `${stderr}${stdout}`.trim();
// Check for the specific "missing dependencies" failure
if (combined.includes(constants_1.HELM_DEPENDENCY_MISSING_ERROR)) {
throw new Error('ERROR: Helm chart dependencies are missing. You may need to re-run the command with the `--dependency-update` option to fetch missing dependencies.');
}
// Otherwise, surface only the tool's message (not the Node stack)
throw new Error(combined || e.message || 'ERROR: helm template command failed for unknown reasons.');
}
}
exports.generateHelmTemplate = generateHelmTemplate;
function getTemplateResources(session, args, org, gvc) {
// Print verbose
if (args.debug || args.verbose) {
session.out(generateHelmTemplate(args, org, gvc, (args.debug || args.verbose)));
}
// Generate a Helm template using the Helm template CLI command
const template = generateHelmTemplate(args, org, gvc);
// Load template output into cpln resources
const templateResources = jsyaml.safeLoadAll(template).filter((item) => item !== null && typeof item === 'object');
// Sort by kind priority
(0, objects_1.sortIHasKindByPriority)(templateResources, false);
// Template resources cannot be empty
if (templateResources.length === 0) {
session.abort({
message: 'ERROR: There were no resources found in the generated template. Make sure you have templates in your chart.',
});
}
// Return the template resources
return templateResources;
}
exports.getTemplateResources = getTemplateResources;
function extractHelmChartMetadata(chartDirPath) {
const filePath = path.join(chartDirPath, 'Chart.yaml');
return (0, io_1.loadObject)((0, fs_1.readFileSync)(filePath, 'utf-8'));
}
exports.extractHelmChartMetadata = extractHelmChartMetadata;
function extractHelmCustomValues(pathsToValues) {
let values = [];
// Read and add custom values if specified
if (pathsToValues) {
if (typeof pathsToValues === 'string') {
pathsToValues = [pathsToValues];
}
// Iterate over each custom values path and add it
for (const path of pathsToValues) {
const customValues = (0, fs_1.readFileSync)(path, 'utf-8');
if (customValues) {
values.push(customValues.trim());
}
}
}
return values;
}
exports.extractHelmCustomValues = extractHelmCustomValues;
function extractHelmDefaultValues(chartDirPath) {
return jsyaml.load((0, child_process_1.execSync)(`helm show values ${chartDirPath}`).toString());
}
exports.extractHelmDefaultValues = extractHelmDefaultValues;
function extractHelmConfig(args, valuesFiles) {
let config = {};
// Apply "values"
for (const valuesFile of valuesFiles) {
Object.assign(config, jsyaml.safeLoad(valuesFile));
}
// Apply "set"
applySetProperty(config, args.set);
// Apply "set-string"
applySetProperty(config, args.setString);
// Apply "set-file"
applySetFileProperty(config, args.setFile);
return config;
}
exports.extractHelmConfig = extractHelmConfig;
function getReleaseSecretName(release, revision) {
// Construct the name of the secret
const name = `${constants_1.CPLN_RELEASE_NAME_PREFIX}-${release}`;
// Make the name safe
const safeName = (0, names_1.safeCplnName)(59, name);
// Append the version number to the safe name and return it
return `${safeName}-v${revision}`;
}
exports.getReleaseSecretName = getReleaseSecretName;
function getReleaseSecretNameWithoutRevision(release) {
// Get the secret name of the release using 0 as the version number
const name = getReleaseSecretName(release, 0);
// The length of the version part is 3 ('-v0'.length === 3)
const versionPartLength = 3;
// Trim out the verion part and return the secret name
return name.substring(0, name.length - versionPartLength);
}
exports.getReleaseSecretNameWithoutRevision = getReleaseSecretNameWithoutRevision;
function getReleaseSecretNameLegacy(release) {
return `${constants_1.CPLN_RELEASE_NAME_PREFIX_LEGACY}-${release}`;
}
exports.getReleaseSecretNameLegacy = getReleaseSecretNameLegacy;
async function encodeRelease(release) {
// Gzip the release object
const gzipped = (0, pako_1.gzip)(JSON.stringify(release));
// Base64-encode and return
return Buffer.from(gzipped).toString('base64');
}
exports.encodeRelease = encodeRelease;
function decodeReleaseFromSecret(secret) {
// If the secret is not valid, throw an error
if (!secret.data || !secret.data.payload) {
throw new Error('ERROR: Invalid release revision secret data.');
}
// Decode the base64-encoded payload
const decoded = Buffer.from(secret.data.payload, 'base64');
// Gunzip the decoded data
const gunzipped = (0, pako_1.ungzip)(new Uint8Array(decoded), { to: 'string' });
// Parse the JSON and return the release object
return JSON.parse(gunzipped);
}
exports.decodeReleaseFromSecret = decodeReleaseFromSecret;
// Local //
function safeSet(input) {
const index = input.indexOf('=');
if (index === -1) {
return input;
}
const key = input.substring(0, index);
const value = input.substring(index + 1);
return `"${key}"="${value}"`;
}
function applySetProperty(config, input) {
if (!input) {
return;
}
// Make sure we have an array
const entries = Array.isArray(input) ? input : [input];
// Iterate over each entry and update config
for (const entry of entries) {
const index = entry.indexOf('=');
// Skip entry if it doesn't have an equal sign, it will be caught later by helm template command
if (index === -1) {
continue;
}
const key = entry.substring(0, index);
const value = entry.substring(index + 1);
// Update helm config
config[key] = value;
}
}
function applySetFileProperty(config, input) {
if (!input) {
return;
}
// Make sure we have an array
const entries = Array.isArray(input) ? input : [input];
// Iterate over each entry and update config
for (const entry of entries) {
const index = entry.indexOf('=');
// Skip entry if it doesn't have an equal sign, it will be caught later by helm template command
if (index === -1) {
continue;
}
const key = entry.substring(0, index);
const value = entry.substring(index + 1);
// Skip if the path in value doesn't exists, it will be caught later by helm template command
if (!(0, fs_1.existsSync)(value)) {
continue;
}
// Update helm config
config[key] = (0, fs_1.readFileSync)(value, 'utf-8');
}
}
//# sourceMappingURL=functions.js.map