chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
#!/usr/bin/env node
// Runs a command as an image built by kaniko would run it, with no daemon.
//
// node scripts/run-image.mjs <oci layout> [NAME=value]... [-- command...]
//
// kaniko leaves the last stage's filesystem at / once a build ends. This
// reads the image's config from the OCI layout the build wrote, and starts
// its entrypoint with the command given, or the image's own, in that
// filesystem: with the image's environment, working directory and user. It
// exits with the command's status.
import { spawn } from 'node:child_process';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
const [layout, ...rest] = process.argv.slice(2);
if (!layout) {
console.error(
'usage: run-image.mjs <oci layout> [NAME=value]... [-- command...]',
);
process.exit(2);
}
const dash = rest.indexOf('--');
const vars = dash < 0 ? rest : rest.slice(0, dash);
const command = dash < 0 ? [] : rest.slice(dash + 1);
/** @param {string} digest */
const blob = (digest) =>
JSON.parse(readFileSync(join(layout, 'blobs', ...digest.split(':')), 'utf8'));
const index = blob(
JSON.parse(readFileSync(join(layout, 'index.json'), 'utf8')).manifests[0]
.digest,
);
const manifest = index.manifests ? blob(index.manifests[0].digest) : index;
const { config } = blob(manifest.config.digest);
const env = Object.fromEntries(
[...(config.Env ?? []), ...vars].map((pair) => {
const at = pair.indexOf('=');
return [pair.slice(0, at), pair.slice(at + 1)];
}),
);
/** @param {string} user */
function ids(user) {
if (!user) return {};
const [name, group] = user.split(':');
if (/^\d+$/.test(name)) {
return { uid: Number(name), gid: Number(group ?? name) };
}
const entry = readFileSync('/etc/passwd', 'utf8')
.split('\n')
.map((line) => line.split(':'))
.find((fields) => fields[0] === name);
if (!entry) throw new Error(`the image's user ${name} is not in /etc/passwd`);
return { uid: Number(entry[2]), gid: Number(entry[3]) };
}
const argv = [
...(config.Entrypoint ?? []),
...(command.length ? command : (config.Cmd ?? [])),
];
const child = spawn(argv[0], argv.slice(1), {
cwd: config.WorkingDir || '/',
env,
stdio: 'inherit',
...ids(config.User ?? ''),
});
child.on('exit', (code, signal) => process.exit(code ?? (signal ? 128 : 1)));