UNPKG

@kosko/helm

Version:

Load Helm charts in kosko.

198 lines (194 loc) 6.81 kB
import { loadString } from '@kosko/yaml'; import tmp from 'tmp-promise'; import { rename, cp, rm, stat, readFile, readdir, writeFile, mkdir } from 'node:fs/promises'; import { stringArrayArg, booleanArg, stringArg, spawn } from '@kosko/exec-utils'; import stringify from 'fast-safe-stringify'; import { getErrorCode, isRecord } from '@kosko/common-utils'; import getCacheDir from 'cachedir'; import { createHash } from 'node:crypto'; import { join, dirname } from 'node:path'; import { env } from 'node:process'; import yaml from 'js-yaml'; async function move(src, dest) { try { await rename(src, dest); } catch (err) { if ("EXDEV" !== getErrorCode(err)) throw err; await cp(src, dest, { recursive: !0, errorOnExist: !0, preserveTimestamps: !0 }), await rm(src, { recursive: !0, force: !0 }); } } async function fileExists(path) { try { return (await stat(path)).isFile(); } catch (err) { if ("ENOENT" !== getErrorCode(err)) throw err; return !1; } } let FILE_EXIST_ERROR_CODES = new Set([ "EEXIST", "ENOTEMPTY" ]), defaultCacheDir = getCacheDir("kosko-helm"); function genObjectHash(value) { return function(value) { let hash = createHash("sha1"); return hash.write(value), function(str) { let index = str.indexOf("="); return -1 === index ? str : str.substring(0, index); }(hash.digest("base64url")); }(stringify(value)); } async function runHelm(args) { try { return await spawn("helm", args); } catch (err) { if ("ENOENT" !== 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 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.load(content); if (isRecord(metadata)) return metadata; } function getPullArgs(options) { return [ options.chart, ...stringArg("ca-file", options.caFile), ...stringArg("cert-file", options.certFile), ...booleanArg("devel", options.devel), ...booleanArg("insecure-skip-tls-verify", options.insecureSkipTlsVerify), ...stringArg("key-file", options.keyFile), ...stringArg("keyring", options.keyring), ...booleanArg("pass-credentials", options.passCredentials), ...stringArg("password", options.password), ...stringArg("repo", options.repo), ...stringArg("username", options.username), ...booleanArg("verify", options.verify), ...stringArg("version", options.version) ]; } async function moveChartToCacheDir(src, dest) { if (!await fileExists(dest)) { await mkdir(dirname(dest), { recursive: !0 }); try { await move(src, dest); } catch (err) { let code = 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 || env.KOSKO_HELM_CACHE_DIR || defaultCacheDir, indexPath = join(cacheDir, "index-" + genObjectHash({ c: options.chart, d: options.devel, r: options.repo, v: options.version })); try { let content = await readFile(indexPath, "utf8"); if (content) return join(cacheDir, content); } catch (err) { if ("ENOENT" !== getErrorCode(err)) throw err; } let tmpDir = await tmp.dir({ prefix: "kosko-helm", unsafeCleanup: !0 }); try { await runHelm([ "pull", ...getPullArgs(options), "--destination", tmpDir.path ]); let files = await readdir(tmpDir.path); if (0 === files.length) return; let chartPath = 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 = join(cacheDir, chartHash); return await moveChartToCacheDir(chartPath, dest), await writeFile(indexPath, chartHash), dest; } finally{ await tmpDir.cleanup(); } } async function writeValues(values) { let file = await tmp.file(); return await writeFile(file.path, stringify(values)), file; } async function renderChart(options) { let valueFile; let args = [ "template", ...options.name ? [ options.name ] : [], ...getPullArgs(options), ...stringArrayArg("api-versions", options.apiVersions), ...booleanArg("dependency-update", options.dependencyUpdate), ...stringArg("description", options.description), ...booleanArg("generate-name", options.generateName), ...booleanArg("include-crds", options.includeCrds), ...booleanArg("is-upgrade", options.isUpgrade), ...stringArg("kube-version", options.kubeVersion), ...stringArg("name-template", options.nameTemplate), ...stringArg("namespace", options.namespace), ...booleanArg("no-hooks", options.noHooks), ...stringArg("post-renderer", options.postRenderer), ...stringArrayArg("post-renderer-args", options.postRendererArgs), ...booleanArg("skip-tests", options.skipTests), ...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 loadString(stdout, { transform }); }; } export { loadChart }; //# sourceMappingURL=index.base.mjs.map