feat: add HWLAB v02 secret bootstrap command

This commit is contained in:
Codex
2026-06-02 10:22:52 +00:00
parent d82ea72a3f
commit db3092ad29
2 changed files with 169 additions and 3 deletions
+167 -2
View File
@@ -22,12 +22,16 @@ const V02_POLLER = "hwlab-v02-branch-poller";
const V02_RECONCILER = "hwlab-v02-control-plane-reconciler";
const V02_PIPELINERUN_PREFIX = "hwlab-v02-ci-poll";
const V02_CONTROL_PLANE_FIELD_MANAGER = "unidesk-hwlab-v02-control-plane";
const V02_SECRET_FIELD_MANAGER = "unidesk-hwlab-v02-secret";
const V02_GIT_URL = "git@github.com:pikasTech/HWLAB.git";
const V02_GIT_READ_URL = "http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git";
const V02_GIT_WRITE_URL = "http://git-mirror-write.devops-infra.svc.cluster.local/pikasTech/HWLAB.git";
const V02_GITOPS_BRANCH = "v0.2-gitops";
const V02_CATALOG_PATH = "deploy/artifact-catalog.v02.json";
const V02_RUNTIME_PATH = "deploy/gitops/g14/runtime-v02";
const V02_RUNTIME_NAMESPACE = "hwlab-v02";
const V02_DEVICE_POD_API_KEY_SECRET = "hwlab-v02-device-pod-api-key";
const V02_DEVICE_POD_API_KEY_SECRET_KEY = "api-key";
const V02_REGISTRY_PREFIX = "127.0.0.1:5000/hwlab";
const V02_BASE_IMAGE = "127.0.0.1:5000/hwlab/hwlab-node20-base:20-bookworm-slim";
const GIT_MIRROR_NAMESPACE = "devops-infra";
@@ -121,6 +125,16 @@ interface G14GitMirrorOptions {
timeoutSeconds: number;
}
interface G14SecretOptions {
action: "status" | "ensure";
lane: "v02";
dryRun: boolean;
confirm: boolean;
name: typeof V02_DEVICE_POD_API_KEY_SECRET;
key: typeof V02_DEVICE_POD_API_KEY_SECRET_KEY;
timeoutSeconds: number;
}
interface CommandJsonResult {
ok: boolean;
command: string[];
@@ -319,6 +333,31 @@ function parseGitMirrorOptions(args: string[]): G14GitMirrorOptions {
};
}
function parseSecretOptions(args: string[]): G14SecretOptions {
const [actionRaw] = args;
if (actionRaw !== "status" && actionRaw !== "ensure") {
throw new Error("secret usage: status|ensure --lane v02 --name hwlab-v02-device-pod-api-key --key api-key [--dry-run|--confirm]");
}
const lane = optionValue(args, "--lane") ?? "v02";
if (lane !== "v02") throw new Error("secret currently supports --lane v02");
const name = optionValue(args, "--name") ?? V02_DEVICE_POD_API_KEY_SECRET;
if (name !== V02_DEVICE_POD_API_KEY_SECRET) throw new Error(`secret currently supports --name ${V02_DEVICE_POD_API_KEY_SECRET}`);
const key = optionValue(args, "--key") ?? V02_DEVICE_POD_API_KEY_SECRET_KEY;
if (key !== V02_DEVICE_POD_API_KEY_SECRET_KEY) throw new Error(`secret currently supports --key ${V02_DEVICE_POD_API_KEY_SECRET_KEY}`);
const confirm = args.includes("--confirm");
const explicitDryRun = args.includes("--dry-run");
if (confirm && explicitDryRun) throw new Error("secret accepts only one of --confirm or --dry-run");
return {
action: actionRaw,
lane,
confirm,
dryRun: actionRaw === "status" ? true : explicitDryRun || !confirm,
name,
key,
timeoutSeconds: positiveIntegerOption(args, "--timeout-seconds", 120, 600),
};
}
function positiveIntegerOption(args: string[], name: string, defaultValue: number, maxValue: number): number {
const index = args.indexOf(name);
if (index === -1) return defaultValue;
@@ -2302,6 +2341,125 @@ function runV02ControlPlane(options: G14ControlPlaneOptions): Record<string, unk
};
}
function v02SecretScript(options: G14SecretOptions): string {
const emitAfterStatus = [
"after_exists=$(secret_exists_flag)",
"after_b64=$(secret_b64)",
"after_key_present=$([ -n \"$after_b64\" ] && printf yes || printf no)",
"after_value_bytes=$(decoded_length \"$after_b64\")",
"printf 'namespace\\t%s\\n' \"$namespace\"",
"printf 'secret\\t%s\\n' \"$name\"",
"printf 'key\\t%s\\n' \"$key\"",
"printf 'action\\t%s\\n' \"$action\"",
"printf 'dryRun\\t%s\\n' \"$dry_run\"",
"printf 'mutation\\t%s\\n' \"$mutation\"",
"printf 'beforeExists\\t%s\\n' \"$before_exists\"",
"printf 'beforeKeyPresent\\t%s\\n' \"$before_key_present\"",
"printf 'beforeValueBytes\\t%s\\n' \"$before_value_bytes\"",
"printf 'afterExists\\t%s\\n' \"$after_exists\"",
"printf 'afterKeyPresent\\t%s\\n' \"$after_key_present\"",
"printf 'afterValueBytes\\t%s\\n' \"$after_value_bytes\"",
"printf 'applyExitCode\\t%s\\n' \"$apply_exit\"",
].join("\n");
return [
"set +e",
`namespace=${shellQuote(V02_RUNTIME_NAMESPACE)}`,
`name=${shellQuote(options.name)}`,
`key=${shellQuote(options.key)}`,
`action_request=${shellQuote(options.action)}`,
`dry_run=${shellQuote(options.dryRun ? "true" : "false")}`,
`field_manager=${shellQuote(V02_SECRET_FIELD_MANAGER)}`,
"secret_exists_flag() { kubectl -n \"$namespace\" get secret \"$name\" >/dev/null 2>&1 && printf yes || printf no; }",
"secret_b64() { kubectl -n \"$namespace\" get secret \"$name\" -o \"go-template={{ index .data \\\"$key\\\" }}\" 2>/dev/null || true; }",
"decoded_length() { if [ -n \"$1\" ]; then printf '%s' \"$1\" | base64 -d 2>/dev/null | wc -c | tr -d ' '; else printf '0'; fi; }",
"before_exists=$(secret_exists_flag)",
"before_b64=$(secret_b64)",
"before_key_present=$([ -n \"$before_b64\" ] && printf yes || printf no)",
"before_value_bytes=$(decoded_length \"$before_b64\")",
"action=observed",
"mutation=false",
"apply_exit=",
"if [ \"$action_request\" = ensure ]; then",
" if [ \"$dry_run\" = true ]; then",
" if [ \"$before_key_present\" = yes ] && [ \"$before_value_bytes\" -gt 0 ]; then action=kept; else action=would-create; fi",
" elif [ \"$before_key_present\" = yes ] && [ \"$before_value_bytes\" -gt 0 ]; then",
" action=kept",
" else",
" if ! command -v openssl >/dev/null 2>&1; then",
" action=openssl-missing",
" apply_exit=127",
" else",
" generated_key=$(openssl rand -base64 48)",
" kubectl -n \"$namespace\" create secret generic \"$name\" --from-literal=\"$key=$generated_key\" --dry-run=client -o yaml | kubectl apply --server-side --force-conflicts --field-manager=\"$field_manager\" -f -",
" apply_exit=$?",
" generated_key=",
" if [ \"$apply_exit\" -eq 0 ]; then action=ensured; mutation=true; else action=apply-failed; fi",
" fi",
" fi",
"fi",
emitAfterStatus,
"if [ -n \"$apply_exit\" ] && [ \"$apply_exit\" != 0 ]; then exit \"$apply_exit\"; fi",
].join("\n");
}
function v02SecretStatusFromText(text: string, commandOk: boolean, exitCode: number | null, stderr: string): Record<string, unknown> {
const fields = keyValueLinesFromText(text);
const afterExists = fields.afterExists === "yes";
const afterKeyPresent = fields.afterKeyPresent === "yes";
const afterValueBytes = numericField(fields.afterValueBytes);
const healthy = afterExists && afterKeyPresent && typeof afterValueBytes === "number" && afterValueBytes > 0;
return {
ok: commandOk && healthy,
namespace: fields.namespace || V02_RUNTIME_NAMESPACE,
secret: fields.secret || V02_DEVICE_POD_API_KEY_SECRET,
key: fields.key || V02_DEVICE_POD_API_KEY_SECRET_KEY,
action: fields.action || null,
dryRun: fields.dryRun === "true",
mutation: fields.mutation === "true",
before: {
exists: fields.beforeExists === "yes",
keyPresent: fields.beforeKeyPresent === "yes",
valueBytes: numericField(fields.beforeValueBytes),
},
after: {
exists: afterExists,
keyPresent: afterKeyPresent,
valueBytes: afterValueBytes,
},
applyExitCode: numericField(fields.applyExitCode),
exitCode,
stderr: commandOk ? "" : stderr.trim().slice(0, 2000),
valuesRedacted: true,
summary: healthy
? `${fields.secret || V02_DEVICE_POD_API_KEY_SECRET}/${fields.key || V02_DEVICE_POD_API_KEY_SECRET_KEY} exists`
: `${fields.secret || V02_DEVICE_POD_API_KEY_SECRET}/${fields.key || V02_DEVICE_POD_API_KEY_SECRET_KEY} missing or empty`,
};
}
function runG14Secret(options: G14SecretOptions): Record<string, unknown> {
const script = v02SecretScript(options);
const result = g14K3s(["script", "--", script], options.timeoutSeconds * 1000);
const status = v02SecretStatusFromText(statusText(result), isCommandSuccess(result), result.exitCode, result.stderr);
const dryRunOk = options.action === "ensure" && options.dryRun && isCommandSuccess(result);
const ok = dryRunOk ? true : status.ok === true;
return {
ok,
command: `hwlab g14 secret ${options.action} --lane v02`,
lane: options.lane,
namespace: V02_RUNTIME_NAMESPACE,
secret: options.name,
key: options.key,
mode: options.action === "status" ? "status" : options.dryRun ? "dry-run" : "confirmed-ensure",
status,
mutation: status.mutation === true,
result: compactCommandResult(result),
valuesRedacted: true,
next: ok && options.action === "status"
? undefined
: { ensure: `bun scripts/cli.ts hwlab g14 secret ensure --lane v02 --name ${options.name} --key ${options.key} --confirm` },
};
}
function deleteLegacyGitMirrorCronJob(dryRun: boolean): CommandJsonResult {
return g14K3s([
"kubectl",
@@ -3800,6 +3958,9 @@ export function hwlabG14Help(): Record<string, unknown> {
"bun scripts/cli.ts hwlab g14 control-plane runtime-migration --lane v02 --dry-run",
"bun scripts/cli.ts hwlab g14 control-plane runtime-migration --lane v02 --allow-live-db-read --dry-run",
"bun scripts/cli.ts hwlab g14 control-plane runtime-migration --lane v02 --confirm",
"bun scripts/cli.ts hwlab g14 secret status --lane v02 --name hwlab-v02-device-pod-api-key --key api-key",
"bun scripts/cli.ts hwlab g14 secret ensure --lane v02 --name hwlab-v02-device-pod-api-key --key api-key --dry-run",
"bun scripts/cli.ts hwlab g14 secret ensure --lane v02 --name hwlab-v02-device-pod-api-key --key api-key --confirm",
"bun scripts/cli.ts hwlab g14 git-mirror status",
"bun scripts/cli.ts hwlab g14 git-mirror apply --confirm",
"bun scripts/cli.ts hwlab g14 git-mirror sync --confirm",
@@ -3810,7 +3971,7 @@ export function hwlabG14Help(): Record<string, unknown> {
"bun scripts/cli.ts hwlab g14 tools-image build --name ci-node-tools --tag node22-alpine-bun-v1 --confirm",
"bun scripts/cli.ts job status <jobId> --tail-bytes 30000",
],
description: "G14 HWLAB PR monitor, DEV rollout command, bounded v0.2 control-plane bootstrap/cleanup/runtime-migration helper, devops-infra git mirror maintenance, and controlled CI tools image build/status entry. The public monitor starts a fire-and-forget job; confirmed control-plane trigger-current and git-mirror sync/flush also return async jobs by default, with --wait reserved for explicit synchronous debugging. control-plane status/apply/cleanup-runs/cleanup-released-pvs/runtime-migration uses UniDesk G14:k3s routes for v0.2 Tekton/Argo control resources, runtime migration, and completed CI workspace retention only. git-mirror status/apply/sync/flush is the manual devops-infra mirror/relay control path and does not install a CronJob.",
description: "G14 HWLAB PR monitor, DEV rollout command, bounded v0.2 control-plane bootstrap/cleanup/runtime-migration helper, v0.2 runtime SecretRef bootstrap, devops-infra git mirror maintenance, and controlled CI tools image build/status entry. The public monitor starts a fire-and-forget job; confirmed control-plane trigger-current and git-mirror sync/flush also return async jobs by default, with --wait reserved for explicit synchronous debugging. control-plane status/apply/cleanup-runs/cleanup-released-pvs/runtime-migration uses UniDesk G14:k3s routes for v0.2 Tekton/Argo control resources, runtime migration, and completed CI workspace retention only. secret status/ensure is the standard v0.2 runtime SecretRef bootstrap path; it never reads or prints secret values. git-mirror status/apply/sync/flush is the manual devops-infra mirror/relay control path and does not install a CronJob.",
defaults: {
repo: HWLAB_REPO,
base: G14_SOURCE_BRANCH,
@@ -3846,6 +4007,10 @@ export async function runHwlabG14Command(_config: Config, args: string[]): Promi
}
return runV02ControlPlane(options);
}
if (action === "secret") {
const options = parseSecretOptions(args.slice(1));
return runG14Secret(options);
}
if (action === "tools-image") {
const options = parseToolsImageOptions(args.slice(1));
return runG14ToolsImage(options);
@@ -3858,7 +4023,7 @@ export async function runHwlabG14Command(_config: Config, args: string[]): Promi
return runG14GitMirror(options);
}
if (action !== "monitor-prs") {
return { ok: false, command: `hwlab g14 ${action ?? ""}`.trim(), degradedReason: "unsupported-command", message: "supported commands: hwlab g14 monitor-prs, hwlab g14 record-rollout, hwlab g14 control-plane, hwlab g14 git-mirror, hwlab g14 tools-image" };
return { ok: false, command: `hwlab g14 ${action ?? ""}`.trim(), degradedReason: "unsupported-command", message: "supported commands: hwlab g14 monitor-prs, hwlab g14 record-rollout, hwlab g14 control-plane, hwlab g14 secret, hwlab g14 git-mirror, hwlab g14 tools-image" };
}
const options = parseOptions(args.slice(1));
if (options.worker) return runMonitorWorker(options);