118 lines
4.3 KiB
Bash
Executable File
118 lines
4.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
root="$(cd "$(dirname "$0")/.." && pwd -P)"
|
|
tmp_base="${TMPDIR:-/tmp}"
|
|
tmp="$(mktemp -d "${tmp_base%/}/thoth-auth-runtime-compose.XXXXXX")"
|
|
trap 'rm -rf "$tmp"' EXIT HUP INT TERM
|
|
|
|
canonical="$tmp/canonical-auth"
|
|
runtime="$tmp/runtime-auth"
|
|
mkdir -p "$canonical" "$runtime" "$tmp/data" "$tmp/pi-state" "$tmp/workspace-registry"
|
|
chmod 0700 "$canonical" "$runtime"
|
|
printf '%s\n' '{}' >"$tmp/pi-auth.json"
|
|
printf '%s\n' 'fixture-secret-sentinel' >"$tmp/thothii.secrets"
|
|
chmod 0600 "$tmp/pi-auth.json" "$tmp/thothii.secrets"
|
|
|
|
write_env() {
|
|
local path="$1"
|
|
{
|
|
printf '%s\n' \
|
|
'THT_WORKSPACE_GIT_REMOTE=https://git.example.invalid/platform/workspaces.git' \
|
|
"PI_AUTH_FILE=$tmp/pi-auth.json" \
|
|
"THT_SECRETS_FILE=$tmp/thothii.secrets" \
|
|
"THT_AUTH_CONFIG_ROOT=$canonical" \
|
|
"THT_DATA_ROOT=$tmp/data" \
|
|
"THT_PI_STATE_ROOT=$tmp/pi-state" \
|
|
"THT_WORKSPACE_REGISTRY_ROOT=$tmp/workspace-registry"
|
|
} >"$path"
|
|
}
|
|
|
|
write_env "$tmp/nonprojected.env"
|
|
cp "$tmp/nonprojected.env" "$tmp/projected.env"
|
|
printf 'THT_AUTH_RUNTIME_ROOT=%s\n' "$runtime" >>"$tmp/projected.env"
|
|
|
|
cat >"$tmp/operator.yaml" <<'YAML'
|
|
services:
|
|
core:
|
|
environment:
|
|
THT_AUTH_RUNTIME_PROJECTION_ROOT: operator-marker
|
|
YAML
|
|
|
|
cat >"$tmp/current-image.yaml" <<'YAML'
|
|
services:
|
|
core:
|
|
environment:
|
|
THT_AUTH_RUNTIME_PROJECTION_ROOT: current-marker
|
|
YAML
|
|
|
|
render() {
|
|
local output="$1"
|
|
local env_file="$2"
|
|
shift 2
|
|
local -a files=(-f "$root/compose.yaml" -f "$root/deploy/compose.server.yaml")
|
|
local file
|
|
for file in "$@"; do
|
|
files+=(-f "$file")
|
|
done
|
|
docker compose --project-directory "$root" --env-file "$env_file" "${files[@]}" \
|
|
config --format json >"$output"
|
|
}
|
|
|
|
render "$tmp/nonprojected.json" "$tmp/nonprojected.env"
|
|
render "$tmp/projected.json" "$tmp/projected.env" \
|
|
"$tmp/operator.yaml" "$root/deploy/compose.auth-runtime-projection.yaml"
|
|
render "$tmp/current.json" "$tmp/projected.env" \
|
|
"$tmp/operator.yaml" "$root/deploy/compose.auth-runtime-projection.yaml" \
|
|
"$tmp/current-image.yaml"
|
|
|
|
for mode in nonprojected projected current; do
|
|
node - "$tmp/$mode.json" "$mode" "$canonical" "$runtime" <<'NODE'
|
|
const fs = require("fs");
|
|
const [path, mode, canonical, runtime] = process.argv.slice(2);
|
|
const config = JSON.parse(fs.readFileSync(path, "utf8"));
|
|
const core = config.services?.core;
|
|
if (!core) throw new Error(`${mode}: missing core service`);
|
|
|
|
const mounts = (core.volumes || []).filter((mount) => mount.target === "/run/thothii-auth");
|
|
if (mounts.length !== 1 || mounts[0].type !== "bind" || !mounts[0].read_only) {
|
|
throw new Error(`${mode}: expected exactly one read-only auth bind`);
|
|
}
|
|
if (mode === "nonprojected") {
|
|
if (mounts[0].source !== canonical) throw new Error("nonprojected: canonical auth source changed");
|
|
if (Object.hasOwn(core.environment || {}, "THT_AUTH_RUNTIME_PROJECTION_ROOT")) {
|
|
throw new Error("nonprojected: projected environment unexpectedly present");
|
|
}
|
|
} else {
|
|
if (mounts[0].source !== runtime) throw new Error(`${mode}: runtime source did not replace canonical source`);
|
|
if ((core.volumes || []).some((mount) => mount.source === canonical)) {
|
|
throw new Error(`${mode}: canonical source is still mounted`);
|
|
}
|
|
const expected = mode === "projected" ? "/run/thothii-auth" : "current-marker";
|
|
if (core.environment?.THT_AUTH_RUNTIME_PROJECTION_ROOT !== expected) {
|
|
throw new Error(`${mode}: override ordering failed`);
|
|
}
|
|
}
|
|
|
|
for (const [name, service] of Object.entries(config.services || {})) {
|
|
if (name !== "core" && Object.hasOwn(service.environment || {}, "THT_AUTH_RUNTIME_PROJECTION_ROOT")) {
|
|
throw new Error(`${mode}: ${name} received projected auth environment`);
|
|
}
|
|
}
|
|
const maintenance = config.services?.["workspace-maintenance"];
|
|
if ((maintenance?.volumes || []).some(
|
|
(mount) => mount.target === "/run/thothii-auth" || mount.target === "/data/auth",
|
|
)) {
|
|
throw new Error(`${mode}: workspace-maintenance received auth mount`);
|
|
}
|
|
if (Object.keys(maintenance?.environment || {}).some((key) => key.startsWith("THT_AUTH_"))) {
|
|
throw new Error(`${mode}: workspace-maintenance received auth environment`);
|
|
}
|
|
if (JSON.stringify(config).includes("fixture-secret-sentinel")) {
|
|
throw new Error(`${mode}: rendered Compose leaked a secret sentinel`);
|
|
}
|
|
NODE
|
|
done
|
|
|
|
echo "runtime auth projection Compose contract passed."
|