@kosko/helm
Version:
Load Helm charts in kosko.
207 lines (200 loc) • 7.7 kB
JavaScript
;
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