UNPKG

@kosko/helm

Version:

Load Helm charts in kosko.

207 lines (200 loc) 7.7 kB
'use strict'; var yaml = require('@kosko/yaml'); var tmp = require('tmp-promise'); var promises = require('node:fs/promises'); var execUtils = require('@kosko/exec-utils'); var stringify = require('fast-safe-stringify'); var commonUtils = require('@kosko/common-utils'); var getCacheDir = require('cachedir'); var node_crypto = require('node:crypto'); var node_path = require('node:path'); var node_process = require('node:process'); var yaml$1 = require('js-yaml'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var tmp__default = /*#__PURE__*/_interopDefault(tmp); var stringify__default = /*#__PURE__*/_interopDefault(stringify); var getCacheDir__default = /*#__PURE__*/_interopDefault(getCacheDir); var yaml__default = /*#__PURE__*/_interopDefault(yaml$1); async function move(src, dest) { try { await promises.rename(src, dest); } catch (err) { if ("EXDEV" !== commonUtils.getErrorCode(err)) throw err; await promises.cp(src, dest, { recursive: !0, errorOnExist: !0, preserveTimestamps: !0 }), await promises.rm(src, { recursive: !0, force: !0 }); } } async function fileExists(path) { try { return (await promises.stat(path)).isFile(); } catch (err) { if ("ENOENT" !== commonUtils.getErrorCode(err)) throw err; return !1; } } let FILE_EXIST_ERROR_CODES = new Set([ "EEXIST", "ENOTEMPTY" ]), defaultCacheDir = getCacheDir__default.default("kosko-helm"); function genObjectHash(value) { return function(value) { let hash = node_crypto.createHash("sha1"); return hash.write(value), function(str) { let index = str.indexOf("="); return -1 === index ? str : str.substring(0, index); }(hash.digest("base64url")); }(stringify__default.default(value)); } async function runHelm(args) { try { return await execUtils.spawn("helm", args); } catch (err) { if ("ENOENT" !== commonUtils.getErrorCode(err)) throw err; throw Error('"loadChart" requires Helm CLI installed in your environment. More info: https://kosko.dev/docs/components/loading-helm-chart'); } } async function chartManifestExists(path) { try { return commonUtils.isRecord(await getChartMetadata(path)); } catch {} return !1; } async function isLocalChart(options) { return !(options.repo || options.chart.startsWith("oci://")) && chartManifestExists(options.chart); } async function getChartMetadata(chart) { let { stdout: content } = await runHelm([ "show", "chart", chart ]), metadata = yaml__default.default.load(content); if (commonUtils.isRecord(metadata)) return metadata; } function getPullArgs(options) { return [ options.chart, ...execUtils.stringArg("ca-file", options.caFile), ...execUtils.stringArg("cert-file", options.certFile), ...execUtils.booleanArg("devel", options.devel), ...execUtils.booleanArg("insecure-skip-tls-verify", options.insecureSkipTlsVerify), ...execUtils.stringArg("key-file", options.keyFile), ...execUtils.stringArg("keyring", options.keyring), ...execUtils.booleanArg("pass-credentials", options.passCredentials), ...execUtils.stringArg("password", options.password), ...execUtils.stringArg("repo", options.repo), ...execUtils.stringArg("username", options.username), ...execUtils.booleanArg("verify", options.verify), ...execUtils.stringArg("version", options.version) ]; } async function moveChartToCacheDir(src, dest) { if (!await fileExists(dest)) { await promises.mkdir(node_path.dirname(dest), { recursive: !0 }); try { await move(src, dest); } catch (err) { let code = commonUtils.getErrorCode(err); if (!code) throw err; if (FILE_EXIST_ERROR_CODES.has(code) || "EPERM" === code) return; throw err; } } } async function pullChart(options) { if (options.cache?.enabled === !1 || !options.version || await isLocalChart(options)) return; let cacheDir = options.cache?.dir || node_process.env.KOSKO_HELM_CACHE_DIR || defaultCacheDir, indexPath = node_path.join(cacheDir, "index-" + genObjectHash({ c: options.chart, d: options.devel, r: options.repo, v: options.version })); try { let content = await promises.readFile(indexPath, "utf8"); if (content) return node_path.join(cacheDir, content); } catch (err) { if ("ENOENT" !== commonUtils.getErrorCode(err)) throw err; } let tmpDir = await tmp__default.default.dir({ prefix: "kosko-helm", unsafeCleanup: !0 }); try { await runHelm([ "pull", ...getPullArgs(options), "--destination", tmpDir.path ]); let files = await promises.readdir(tmpDir.path); if (0 === files.length) return; let chartPath = node_path.join(tmpDir.path, files[0]), chartMeta = await getChartMetadata(chartPath), chartVersion = chartMeta?.version; if ("string" != typeof chartVersion) return; let chartHash = genObjectHash({ c: options.chart, r: options.repo, v: chartVersion }), dest = node_path.join(cacheDir, chartHash); return await moveChartToCacheDir(chartPath, dest), await promises.writeFile(indexPath, chartHash), dest; } finally{ await tmpDir.cleanup(); } } async function writeValues(values) { let file = await tmp__default.default.file(); return await promises.writeFile(file.path, stringify__default.default(values)), file; } async function renderChart(options) { let valueFile; let args = [ "template", ...options.name ? [ options.name ] : [], ...getPullArgs(options), ...execUtils.stringArrayArg("api-versions", options.apiVersions), ...execUtils.booleanArg("dependency-update", options.dependencyUpdate), ...execUtils.stringArg("description", options.description), ...execUtils.booleanArg("generate-name", options.generateName), ...execUtils.booleanArg("include-crds", options.includeCrds), ...execUtils.booleanArg("is-upgrade", options.isUpgrade), ...execUtils.stringArg("kube-version", options.kubeVersion), ...execUtils.stringArg("name-template", options.nameTemplate), ...execUtils.stringArg("namespace", options.namespace), ...execUtils.booleanArg("no-hooks", options.noHooks), ...execUtils.stringArg("post-renderer", options.postRenderer), ...execUtils.stringArrayArg("post-renderer-args", options.postRendererArgs), ...execUtils.booleanArg("skip-tests", options.skipTests), ...execUtils.stringArg("timeout", options.timeout) ]; options.values && (valueFile = await writeValues(options.values), args.push("--values", valueFile.path)); try { return await runHelm(args); } finally{ await valueFile?.cleanup(); } } function loadChart(options) { let { transform, ...opts } = options; return async ()=>{ let cachedChart = await pullChart(opts), { stdout } = await renderChart({ ...opts, ...cachedChart && { chart: cachedChart, repo: void 0 } }); return yaml.loadString(stdout, { transform }); }; } exports.loadChart = loadChart; //# sourceMappingURL=index.node.cjs.map