@kosko/helm
Version:
Load Helm charts in kosko.
198 lines (194 loc) • 6.81 kB
JavaScript
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