fix: prepare D518 runtime secret sources

This commit is contained in:
Codex
2026-06-27 14:07:19 +00:00
parent e56cfe2b73
commit 414bbba866
5 changed files with 236 additions and 44 deletions
+5
View File
@@ -641,6 +641,11 @@ lanes:
secretKey: password-hash
rollout:
deployment: hwlab-cloud-api
codeAgentProvider:
secretName: hwlab-v03-code-agent-provider
sourceRef: hwlab/d518-v03-code-agent-provider.env
openaiSourceKey: OPENAI_API_KEY
opencodeSourceKey: OPENCODE_API_KEY
publicExposure:
mode: pk01-caddy-frp
publicBaseUrl: https://hwlab.pikapython.com
+28
View File
@@ -295,6 +295,13 @@ export interface HwlabRuntimeBootstrapAdminSpec {
readonly rolloutDeployment: string;
}
export interface HwlabRuntimeCodeAgentProviderSpec {
readonly secretName: string;
readonly sourceRef: string;
readonly openaiSourceKey?: string;
readonly opencodeSourceKey?: string;
}
export interface HwlabRuntimeLaneSpec {
readonly lane: HwlabRuntimeLane;
readonly nodeId: string;
@@ -332,6 +339,7 @@ export interface HwlabRuntimeLaneSpec {
readonly stepEnv: Record<string, string>;
readonly buildkit?: HwlabRuntimeBuildkitSpec;
readonly bootstrapAdmin?: HwlabRuntimeBootstrapAdminSpec;
readonly codeAgentProvider?: HwlabRuntimeCodeAgentProviderSpec;
readonly externalPostgres?: HwlabRuntimeExternalPostgresSpec;
readonly runtimeStore?: HwlabRuntimeStoreSpec;
readonly webProbe?: HwlabRuntimeWebProbeSpec;
@@ -380,6 +388,7 @@ interface HwlabLaneConfig {
readonly stepEnv: Record<string, string>;
readonly buildkit?: HwlabRuntimeBuildkitSpec;
readonly bootstrapAdmin?: HwlabRuntimeBootstrapAdminSpec;
readonly codeAgentProvider?: HwlabRuntimeCodeAgentProviderSpec;
readonly externalPostgres?: HwlabRuntimeExternalPostgresSpec;
readonly runtimeStore?: HwlabRuntimeStoreSpec;
readonly webProbe?: HwlabRuntimeWebProbeSpec;
@@ -614,6 +623,7 @@ function laneConfig(id: HwlabRuntimeLane, raw: Record<string, unknown>): HwlabLa
stepEnv: optionalStringRecord(raw.stepEnv, `lanes.${id}.stepEnv`),
buildkit: buildkitConfig(raw.buildkit, `lanes.${id}.buildkit`),
bootstrapAdmin: bootstrapAdminConfig(raw.bootstrapAdmin, `lanes.${id}.bootstrapAdmin`),
codeAgentProvider: codeAgentProviderConfig(raw.codeAgentProvider, `lanes.${id}.codeAgentProvider`),
externalPostgres: externalPostgresConfig(raw.externalPostgres, `lanes.${id}.externalPostgres`),
runtimeStore: runtimeStoreConfig(raw.runtimeStore, `lanes.${id}.runtimeStore`),
webProbe: webProbeConfig(raw.webProbe, `lanes.${id}.webProbe`),
@@ -635,6 +645,7 @@ function laneTargetConfig(id: HwlabRuntimeLane, nodeId: string, baseRaw: Record<
stepEnv: mergeOptionalRecord(baseRaw.stepEnv, targetRaw.stepEnv) ?? {},
buildkit: mergeOptionalRecord(baseRaw.buildkit, targetRaw.buildkit),
bootstrapAdmin: mergeOptionalRecord(baseRaw.bootstrapAdmin, targetRaw.bootstrapAdmin),
codeAgentProvider: mergeOptionalRecord(baseRaw.codeAgentProvider, targetRaw.codeAgentProvider),
externalPostgres: mergeOptionalRecord(baseRaw.externalPostgres, targetRaw.externalPostgres),
runtimeStore: mergeOptionalRecord(baseRaw.runtimeStore, targetRaw.runtimeStore),
webProbe: mergeOptionalRecord(baseRaw.webProbe, targetRaw.webProbe),
@@ -685,6 +696,22 @@ function bootstrapAdminConfig(value: unknown, path: string): HwlabRuntimeBootstr
};
}
function codeAgentProviderConfig(value: unknown, path: string): HwlabRuntimeCodeAgentProviderSpec | undefined {
if (value === undefined) return undefined;
const raw = asRecord(value, path);
const openaiSourceKey = optionalStringField(raw, "openaiSourceKey", path);
const opencodeSourceKey = optionalStringField(raw, "opencodeSourceKey", path);
if (openaiSourceKey === undefined && opencodeSourceKey === undefined) {
throw new Error(`${path} must declare at least one of openaiSourceKey or opencodeSourceKey`);
}
return {
secretName: stringField(raw, "secretName", path),
sourceRef: sourceRefField(raw, "sourceRef", path),
...(openaiSourceKey === undefined ? {} : { openaiSourceKey: secretKeyField(raw, "openaiSourceKey", path) }),
...(opencodeSourceKey === undefined ? {} : { opencodeSourceKey: secretKeyField(raw, "opencodeSourceKey", path) }),
};
}
function externalPostgresComponentConfig(value: unknown, path: string): HwlabRuntimeExternalPostgresComponentSpec {
const raw = asRecord(value, path);
return {
@@ -1239,6 +1266,7 @@ function buildRuntimeLaneSpec(config: HwlabLaneConfig): HwlabRuntimeLaneSpec {
stepEnv: config.stepEnv,
...(config.buildkit === undefined ? {} : { buildkit: config.buildkit }),
...(config.bootstrapAdmin === undefined ? {} : { bootstrapAdmin: config.bootstrapAdmin }),
...(config.codeAgentProvider === undefined ? {} : { codeAgentProvider: config.codeAgentProvider }),
...(config.externalPostgres === undefined ? {} : { externalPostgres: config.externalPostgres }),
...(config.runtimeStore === undefined ? {} : { runtimeStore: config.runtimeStore }),
...(config.webProbe === undefined ? {} : { webProbe: config.webProbe }),
+17
View File
@@ -324,6 +324,20 @@ export interface BootstrapAdminPasswordMaterial {
error: string | null;
}
export interface CodeAgentProviderSecretMaterial {
ok: boolean;
sourceRef: string | null;
sourcePath: string | null;
sourcePresent: boolean;
openaiSourceKey: string | null;
opencodeSourceKey: string | null;
openaiValue: string | null;
opencodeValue: string | null;
openaiFingerprint: string | null;
opencodeFingerprint: string | null;
error: string | null;
}
export interface NodePublicExposureOptions {
action: "public-exposure";
node: string;
@@ -373,6 +387,9 @@ export interface RuntimeSecretSpec {
codeAgentProviderSecret: string;
codeAgentProviderSourceNamespace: string;
codeAgentProviderSourceSecret: string;
codeAgentProviderSourceRef?: string;
codeAgentProviderOpenaiSourceKey?: string;
codeAgentProviderOpencodeSourceKey?: string;
fieldManager: string;
}
+80 -4
View File
@@ -28,12 +28,12 @@ import { nodeObservabilityRecordingRuleExpression, nodeObservabilityRecordingRul
import { runDelegatedHwlabNodeCommand, type DelegatedNodeDomain } from "../hwlab-node-transport";
import type { RenderedCliResult } from "../output";
import type { NodePublicExposureOptions, NodeSecretOptions, RuntimeSecretSpec } from "./entry";
import type { CodeAgentProviderSecretMaterial, NodePublicExposureOptions, NodeSecretOptions, RuntimeSecretSpec } from "./entry";
import { BOOTSTRAP_ADMIN_PASSWORD_HASH_KEY, BOOTSTRAP_ADMIN_SOURCE_NAMESPACE, BOOTSTRAP_ADMIN_SOURCE_SECRET, CLOUD_API_DB_KEY, CODE_AGENT_PROVIDER_OPENAI_KEY, CODE_AGENT_PROVIDER_OPENCODE_KEY, CODE_AGENT_PROVIDER_SOURCE_NAMESPACE, CODE_AGENT_PROVIDER_SOURCE_SECRET, MASTER_ADMIN_API_KEY_KEY, OPENFGA_AUTHN_KEY, OPENFGA_DATASTORE_URI_KEY, OPENFGA_POSTGRES_PASSWORD_KEY } from "./entry";
import { transPath } from "./runtime-common";
import { bootstrapAdminSecretScript, cloudApiDbSecretScript, codeAgentProviderSecretScript, masterAdminApiKeySecretScript, obsoletePlatformDbCleanupScript, obsoletePlatformDbStatusFromText, obsoleteSecretCleanupScript, openFgaSecretScript, ownedPostgresCleanupScript, platformDbSecretStatusScript, secretStatusFromText } from "./secret-scripts";
import { assertLane, assertNodeId, compactCommandResult, keyValueLinesFromText, numericField, optionValue, positiveIntegerOption, readMasterAdminApiKey, requiredOption, shellQuote, statusText } from "./utils";
import { parseEnvFile, readBootstrapAdminSecretMaterial, syncNodeExternalPostgresSecrets } from "./web-probe";
import { displayRepoPath, localSecretSourcePaths, parseEnvFile, readBootstrapAdminSecretMaterial, shortSecretFingerprint, syncNodeExternalPostgresSecrets } from "./web-probe";
import { hwlabRuntimeActiveExternalPostgres } from "../hwlab-node-lanes";
export function isSafeWebProbeScriptRunDir(value: string | null): value is string {
@@ -242,9 +242,12 @@ export function runtimeSecretSpec(input: { node: string; lane: string }): Runtim
obsoleteHwpodDbSecret: `hwpod-${input.lane}-db`,
obsoleteHwpodDbName: `hwpod_${input.lane}`,
obsoleteHwpodDbUser: `hwpod_${input.lane}_app`,
codeAgentProviderSecret: `${namespace}-code-agent-provider`,
codeAgentProviderSecret: runtimeLaneSpec?.codeAgentProvider?.secretName ?? `${namespace}-code-agent-provider`,
codeAgentProviderSourceNamespace: CODE_AGENT_PROVIDER_SOURCE_NAMESPACE,
codeAgentProviderSourceSecret: CODE_AGENT_PROVIDER_SOURCE_SECRET,
...(runtimeLaneSpec?.codeAgentProvider?.sourceRef === undefined ? {} : { codeAgentProviderSourceRef: runtimeLaneSpec.codeAgentProvider.sourceRef }),
...(runtimeLaneSpec?.codeAgentProvider?.openaiSourceKey === undefined ? {} : { codeAgentProviderOpenaiSourceKey: runtimeLaneSpec.codeAgentProvider.openaiSourceKey }),
...(runtimeLaneSpec?.codeAgentProvider?.opencodeSourceKey === undefined ? {} : { codeAgentProviderOpencodeSourceKey: runtimeLaneSpec.codeAgentProvider.opencodeSourceKey }),
fieldManager: `unidesk-hwlab-node-${input.lane}-secret`,
};
}
@@ -256,6 +259,9 @@ export function runNodeSecret(options: NodeSecretOptions): Record<string, unknow
return runExternalPostgresSecretEnsure(options, spec);
}
const bootstrapAdminMaterial = options.preset === "bootstrap-admin" ? readBootstrapAdminSecretMaterial(spec) : null;
const codeAgentProviderMaterial = options.preset === "code-agent-provider" && spec.codeAgentProviderSourceRef !== undefined
? readCodeAgentProviderSecretMaterial(spec)
: null;
const input = options.preset === "master-server-admin-api-key" && options.action === "ensure" && !options.dryRun
? readMasterAdminApiKey(spec).key
: options.preset === "bootstrap-admin" && options.action === "ensure" && !options.dryRun && bootstrapAdminMaterial?.ok === true
@@ -271,7 +277,7 @@ export function runNodeSecret(options: NodeSecretOptions): Record<string, unknow
? spec.platformDb ? platformDbSecretStatusScript(options, spec) : cloudApiDbSecretScript(options, spec)
: options.preset === "owned-postgres-cleanup"
? ownedPostgresCleanupScript(options, spec)
: codeAgentProviderSecretScript(options, spec);
: codeAgentProviderSecretScript(options, spec, codeAgentProviderMaterial);
const result = runTransScript(options.node, script, input, options.timeoutSeconds);
const status = secretStatusFromText(statusText(result), result.exitCode === 0, result.exitCode, result.stderr, spec);
const dryRunOk = options.action === "ensure" && options.dryRun && result.exitCode === 0;
@@ -296,6 +302,76 @@ export function runNodeSecret(options: NodeSecretOptions): Record<string, unknow
};
}
export function readCodeAgentProviderSecretMaterial(spec: RuntimeSecretSpec): CodeAgentProviderSecretMaterial {
const sourceRef = spec.codeAgentProviderSourceRef ?? null;
if (sourceRef === null) {
return {
ok: false,
sourceRef,
sourcePath: null,
sourcePresent: false,
openaiSourceKey: spec.codeAgentProviderOpenaiSourceKey ?? null,
opencodeSourceKey: spec.codeAgentProviderOpencodeSourceKey ?? null,
openaiValue: null,
opencodeValue: null,
openaiFingerprint: null,
opencodeFingerprint: null,
error: "code-agent-provider-source-ref-missing",
};
}
const paths = localSecretSourcePaths(sourceRef);
const sourcePath = paths.find((candidate) => existsSync(candidate)) ?? paths[0] ?? null;
const openaiSourceKey = spec.codeAgentProviderOpenaiSourceKey ?? null;
const opencodeSourceKey = spec.codeAgentProviderOpencodeSourceKey ?? null;
if (sourcePath === null) {
return {
ok: false,
sourceRef,
sourcePath,
sourcePresent: false,
openaiSourceKey,
opencodeSourceKey,
openaiValue: null,
opencodeValue: null,
openaiFingerprint: null,
opencodeFingerprint: null,
error: "code-agent-provider-source-path-unresolved",
};
}
if (!existsSync(sourcePath)) {
return {
ok: false,
sourceRef,
sourcePath: displayRepoPath(sourcePath),
sourcePresent: false,
openaiSourceKey,
opencodeSourceKey,
openaiValue: null,
opencodeValue: null,
openaiFingerprint: null,
opencodeFingerprint: null,
error: "code-agent-provider-source-missing",
};
}
const values = parseEnvFile(readFileSync(sourcePath, "utf8"));
const openaiValue = openaiSourceKey === null ? null : values[openaiSourceKey] ?? null;
const opencodeValue = opencodeSourceKey === null ? null : values[opencodeSourceKey] ?? null;
const ok = (openaiValue !== null && openaiValue.length > 0) || (opencodeValue !== null && opencodeValue.length > 0);
return {
ok,
sourceRef,
sourcePath: displayRepoPath(sourcePath),
sourcePresent: true,
openaiSourceKey,
opencodeSourceKey,
openaiValue,
opencodeValue,
openaiFingerprint: openaiValue === null || openaiValue.length === 0 ? null : shortSecretFingerprint(openaiValue),
opencodeFingerprint: opencodeValue === null || opencodeValue.length === 0 ? null : shortSecretFingerprint(opencodeValue),
error: ok ? null : "code-agent-provider-source-key-missing",
};
}
export function nextSecretCommand(options: NodeSecretOptions, spec: RuntimeSecretSpec): Record<string, string> {
if (options.action === "cleanup-owned-postgres") {
return { ensure: `bun scripts/cli.ts hwlab nodes secret cleanup-owned-postgres --node ${options.node} --lane ${options.lane} --confirm` };
+106 -40
View File
@@ -28,13 +28,37 @@ import { nodeObservabilityRecordingRuleExpression, nodeObservabilityRecordingRul
import { runDelegatedHwlabNodeCommand, type DelegatedNodeDomain } from "../hwlab-node-transport";
import type { RenderedCliResult } from "../output";
import type { BootstrapAdminSecretMaterial, NodeSecretOptions, RuntimeSecretSpec } from "./entry";
import type { BootstrapAdminSecretMaterial, CodeAgentProviderSecretMaterial, NodeSecretOptions, RuntimeSecretSpec } from "./entry";
import { CODE_AGENT_PROVIDER_OPENAI_KEY, CODE_AGENT_PROVIDER_OPENCODE_KEY, MASTER_ADMIN_API_KEY_KEY, OPENFGA_AUTHN_KEY, OPENFGA_DATASTORE_URI_KEY, OPENFGA_POSTGRES_PASSWORD_KEY } from "./entry";
import { parseNodeScopedDelegatedOptions } from "./plan";
import { runTransScript, runtimeSecretSpec } from "./public-exposure";
import { compactCommandResult, keyValueLinesFromText, numericField, shellQuote, splitWhitespaceField, statusText } from "./utils";
import { displayRepoPath } from "./web-probe";
function base64Value(value: string | null | undefined): string {
return Buffer.from(value ?? "", "utf8").toString("base64");
}
function shellUrlEncodeFunction(): string[] {
return [
"urlencode() {",
" value=$1",
" encoded=",
" i=1",
" len=${#value}",
" while [ \"$i\" -le \"$len\" ]; do",
" c=$(printf '%s' \"$value\" | cut -c \"$i\")",
" case \"$c\" in",
" [abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.~_-]) encoded=\"$encoded$c\" ;;",
" *) hex=$(printf '%s' \"$c\" | od -An -tx1 | tr -d ' \\n' | tr '[:lower:]' '[:upper:]'); encoded=\"$encoded%$hex\" ;;",
" esac",
" i=$((i + 1))",
" done",
" printf '%s' \"$encoded\"",
"}",
];
}
export function runNodeEndpointBridge(options: ReturnType<typeof parseNodeScopedDelegatedOptions>): Record<string, unknown> {
if (options.dryRun && options.confirm) throw new Error("control-plane allow-endpoint-bridge accepts only one of --dry-run or --confirm");
const dryRun = options.dryRun || !options.confirm;
@@ -682,6 +706,7 @@ export function openFgaSecretScript(options: NodeSecretOptions, spec: RuntimeSec
"secret_b64_key() { kubectl -n \"$namespace\" get secret \"$1\" -o \"go-template={{ index .data \\\"$2\\\" }}\" 2>/dev/null || true; }",
"decoded_value() { if [ -n \"$1\" ]; then printf '%s' \"$1\" | base64 -d 2>/dev/null || true; fi; }",
"decoded_length() { if [ -n \"$1\" ]; then printf '%s' \"$1\" | base64 -d 2>/dev/null | wc -c | tr -d ' '; else printf '0'; fi; }",
...shellUrlEncodeFunction(),
"parse_database_url() {",
" uri=$1",
" uri_host= uri_user= uri_database= uri_sslmode= uri_password_present=no",
@@ -741,6 +766,8 @@ export function openFgaSecretScript(options: NodeSecretOptions, spec: RuntimeSec
"before_uri_password_present=$uri_password_present",
"pg_password=$(decoded_value \"$before_pg_password_b64\")",
"postgres_admin_password=$(decoded_value \"$postgres_admin_b64\")",
"expected_datastore_uri_full=",
"if [ -n \"$pg_password\" ]; then expected_datastore_uri_full=\"postgres://$(urlencode \"$db_user\"):$(urlencode \"$pg_password\")@$db_host:5432/$db_name?sslmode=disable\"; fi",
"probe_db",
"db_role_exists_before=$role_result",
"db_database_exists_before=$database_result",
@@ -763,11 +790,7 @@ export function openFgaSecretScript(options: NodeSecretOptions, spec: RuntimeSec
" [ \"$before_authn_present\" = yes ] && [ \"$before_authn_bytes\" -gt 0 ] || missing_secret=true",
" [ \"$before_uri_present\" = yes ] && [ \"$before_uri_bytes\" -gt 0 ] || missing_secret=true",
" [ \"$before_pg_password_present\" = yes ] && [ \"$before_pg_password_bytes\" -gt 0 ] || missing_secret=true",
" expected_datastore_uri=",
" if [ -n \"$pg_password\" ]; then",
" expected_datastore_uri=\"postgres://$db_user:$pg_password@$db_host:5432/$db_name?sslmode=disable\"",
" [ \"$datastore_uri\" = \"$expected_datastore_uri\" ] || missing_secret=true",
" fi",
" if [ -n \"$expected_datastore_uri_full\" ]; then [ \"$datastore_uri\" = \"$expected_datastore_uri_full\" ] || missing_secret=true; fi",
" missing_db=false",
" [ \"$db_role_exists_before\" = t ] || missing_db=true",
" [ \"$db_database_exists_before\" = t ] || missing_db=true",
@@ -788,7 +811,7 @@ export function openFgaSecretScript(options: NodeSecretOptions, spec: RuntimeSec
" else",
" [ -n \"$authn_value\" ] || authn_value=$(openssl rand -base64 48)",
" [ -n \"$pg_password\" ] || pg_password=$(openssl rand -hex 24)",
" datastore_uri=\"postgres://$db_user:$pg_password@$db_host:5432/$db_name?sslmode=disable\"",
" datastore_uri=\"postgres://$(urlencode \"$db_user\"):$(urlencode \"$pg_password\")@$db_host:5432/$db_name?sslmode=disable\"",
" kubectl -n \"$namespace\" create secret generic \"$openfga_secret\" --from-literal=\"$authn_key=$authn_value\" --from-literal=\"$datastore_uri_key=$datastore_uri\" --from-literal=\"$postgres_password_key=$pg_password\" --dry-run=client -o yaml | kubectl apply --server-side --force-conflicts --field-manager=\"$field_manager\" -f -",
" apply_exit=$?",
" if [ \"$apply_exit\" -eq 0 ]; then",
@@ -804,6 +827,11 @@ export function openFgaSecretScript(options: NodeSecretOptions, spec: RuntimeSec
"SQL",
" db_ensure_exit=$?",
" if [ \"$db_ensure_exit\" -eq 0 ]; then",
" if ! kubectl -n \"$namespace\" get deployment \"$openfga_deployment\" >/dev/null 2>&1; then",
" openfga_image=deployment-not-present",
" action=ensured",
" mutation=true",
" else",
" openfga_image=$(kubectl -n \"$namespace\" get deployment \"$openfga_deployment\" -o 'jsonpath={.spec.template.spec.containers[0].image}' 2>/tmp/hwlab-openfga-image.err)",
" migrate_job=\"$openfga_deployment-migrate-unidesk-$(date +%s)\"",
" tmp=$(mktemp /tmp/hwlab-openfga-migrate.XXXXXX.yaml)",
@@ -856,6 +884,7 @@ export function openFgaSecretScript(options: NodeSecretOptions, spec: RuntimeSec
" elif [ -n \"$rollout_status_exit\" ] && [ \"$rollout_status_exit\" != 0 ]; then action=rollout-status-failed",
" else action=ensured; mutation=true; fi",
" fi",
" fi",
" else action=db-ensure-failed; fi",
" else action=apply-failed; fi",
" fi",
@@ -879,10 +908,8 @@ export function openFgaSecretScript(options: NodeSecretOptions, spec: RuntimeSec
"after_uri_database=$uri_database",
"after_uri_sslmode=$uri_sslmode",
"after_uri_password_present=$uri_password_present",
"expected_uri_prefix=\"postgres://$db_user:\"",
"expected_uri_suffix=\"@$db_host:5432/$db_name?sslmode=disable\"",
"case \"$datastore_uri\" in \"$expected_uri_prefix\"*\"$expected_uri_suffix\") before_uri_matches_expected=yes ;; *) before_uri_matches_expected=no ;; esac",
"case \"$after_datastore_uri\" in \"$expected_uri_prefix\"*\"$expected_uri_suffix\") after_uri_matches_expected=yes ;; *) after_uri_matches_expected=no ;; esac",
"case \"$datastore_uri\" in \"$expected_datastore_uri_full\") before_uri_matches_expected=yes ;; *) before_uri_matches_expected=no ;; esac",
"case \"$after_datastore_uri\" in \"$expected_datastore_uri_full\") after_uri_matches_expected=yes ;; *) after_uri_matches_expected=no ;; esac",
"probe_db",
"db_role_exists_after=$role_result",
"db_database_exists_after=$database_result",
@@ -1258,13 +1285,25 @@ export function legacyBootstrapAdminSecretScript(options: NodeSecretOptions, spe
].join("\n");
}
export function codeAgentProviderSecretScript(options: NodeSecretOptions, spec: RuntimeSecretSpec): string {
export function codeAgentProviderSecretScript(options: NodeSecretOptions, spec: RuntimeSecretSpec, material: CodeAgentProviderSecretMaterial | null): string {
const sourceMode = material === null ? "cluster-secret" : "local-source-ref";
return [
"set +e",
`namespace=${shellQuote(spec.namespace)}`,
`name=${shellQuote(spec.codeAgentProviderSecret)}`,
`source_namespace=${shellQuote(spec.codeAgentProviderSourceNamespace)}`,
`source_name=${shellQuote(spec.codeAgentProviderSourceSecret)}`,
`source_mode=${shellQuote(sourceMode)}`,
`source_ref=${shellQuote(material?.sourceRef ?? "")}`,
`source_path=${shellQuote(material?.sourcePath ?? "")}`,
`source_present_config=${shellQuote(material?.sourcePresent === true ? "yes" : "no")}`,
`source_error=${shellQuote(material?.error ?? "")}`,
`openai_source_key=${shellQuote(material?.openaiSourceKey ?? "")}`,
`opencode_source_key=${shellQuote(material?.opencodeSourceKey ?? "")}`,
`source_openai_b64_config=${shellQuote(base64Value(material?.openaiValue))}`,
`source_opencode_b64_config=${shellQuote(base64Value(material?.opencodeValue))}`,
`source_openai_fingerprint=${shellQuote(material?.openaiFingerprint ?? "")}`,
`source_opencode_fingerprint=${shellQuote(material?.opencodeFingerprint ?? "")}`,
`openai_key=${shellQuote(CODE_AGENT_PROVIDER_OPENAI_KEY)}`,
`opencode_key=${shellQuote(CODE_AGENT_PROVIDER_OPENCODE_KEY)}`,
`selected_key=${shellQuote(options.key ?? "")}`,
@@ -1278,9 +1317,15 @@ export function codeAgentProviderSecretScript(options: NodeSecretOptions, spec:
"before_exists=$(secret_exists_flag \"$namespace\" \"$name\")",
"before_openai_b64=$(secret_b64_key \"$namespace\" \"$name\" \"$openai_key\")",
"before_opencode_b64=$(secret_b64_key \"$namespace\" \"$name\" \"$opencode_key\")",
"source_exists=$(secret_exists_flag \"$source_namespace\" \"$source_name\")",
"source_openai_b64=$(secret_b64_key \"$source_namespace\" \"$source_name\" \"$openai_key\")",
"source_opencode_b64=$(secret_b64_key \"$source_namespace\" \"$source_name\" \"$opencode_key\")",
"if [ \"$source_mode\" = local-source-ref ]; then",
" source_exists=$source_present_config",
" source_openai_b64=$source_openai_b64_config",
" source_opencode_b64=$source_opencode_b64_config",
"else",
" source_exists=$(secret_exists_flag \"$source_namespace\" \"$source_name\")",
" source_openai_b64=$(secret_b64_key \"$source_namespace\" \"$source_name\" \"$openai_key\")",
" source_opencode_b64=$(secret_b64_key \"$source_namespace\" \"$source_name\" \"$opencode_key\")",
"fi",
"before_openai_present=$([ -n \"$before_openai_b64\" ] && printf yes || printf no)",
"before_opencode_present=$([ -n \"$before_opencode_b64\" ] && printf yes || printf no)",
"source_openai_present=$([ -n \"$source_openai_b64\" ] && printf yes || printf no)",
@@ -1333,8 +1378,14 @@ export function codeAgentProviderSecretScript(options: NodeSecretOptions, spec:
"printf 'namespace\\t%s\\n' \"$namespace\"",
"printf 'secret\\t%s\\n' \"$name\"",
"printf 'preset\\t%s\\n' \"$preset\"",
"printf 'sourceMode\\t%s\\n' \"$source_mode\"",
"printf 'sourceNamespace\\t%s\\n' \"$source_namespace\"",
"printf 'sourceSecret\\t%s\\n' \"$source_name\"",
"printf 'sourceRef\\t%s\\n' \"$source_ref\"",
"printf 'sourcePath\\t%s\\n' \"$source_path\"",
"printf 'sourceError\\t%s\\n' \"$source_error\"",
"printf 'openaiSourceKey\\t%s\\n' \"$openai_source_key\"",
"printf 'opencodeSourceKey\\t%s\\n' \"$opencode_source_key\"",
"printf 'selectedKey\\t%s\\n' \"$selected_key\"",
"printf 'action\\t%s\\n' \"$action\"",
"printf 'dryRun\\t%s\\n' \"$dry_run\"",
@@ -1347,8 +1398,10 @@ export function codeAgentProviderSecretScript(options: NodeSecretOptions, spec:
"printf 'sourceExists\\t%s\\n' \"$source_exists\"",
"printf 'sourceOpenaiPresent\\t%s\\n' \"$source_openai_present\"",
"printf 'sourceOpenaiBytes\\t%s\\n' \"$source_openai_bytes\"",
"printf 'sourceOpenaiFingerprint\\t%s\\n' \"$source_openai_fingerprint\"",
"printf 'sourceOpencodePresent\\t%s\\n' \"$source_opencode_present\"",
"printf 'sourceOpencodeBytes\\t%s\\n' \"$source_opencode_bytes\"",
"printf 'sourceOpencodeFingerprint\\t%s\\n' \"$source_opencode_fingerprint\"",
"printf 'afterExists\\t%s\\n' \"$after_exists\"",
"printf 'afterOpenaiPresent\\t%s\\n' \"$after_openai_present\"",
"printf 'afterOpenaiBytes\\t%s\\n' \"$after_openai_bytes\"",
@@ -1381,6 +1434,7 @@ export function cloudApiDbSecretScript(options: NodeSecretOptions, spec: Runtime
"secret_b64_key() { kubectl -n \"$namespace\" get secret \"$1\" -o \"go-template={{ index .data \\\"$2\\\" }}\" 2>/dev/null || true; }",
"decoded_value() { if [ -n \"$1\" ]; then printf '%s' \"$1\" | base64 -d 2>/dev/null || true; fi; }",
"decoded_length() { if [ -n \"$1\" ]; then printf '%s' \"$1\" | base64 -d 2>/dev/null | wc -c | tr -d ' '; else printf '0'; fi; }",
...shellUrlEncodeFunction(),
"parse_database_url() {",
" uri=$1",
" uri_host= uri_user= uri_database= uri_sslmode= uri_password_present=no",
@@ -1443,6 +1497,8 @@ export function cloudApiDbSecretScript(options: NodeSecretOptions, spec: Runtime
"postgres_admin_b64=$(secret_b64_key \"$postgres_secret\" POSTGRES_PASSWORD)",
"postgres_admin_present=$([ -n \"$postgres_admin_b64\" ] && printf yes || printf no)",
"postgres_admin_password=$(decoded_value \"$postgres_admin_b64\")",
"expected_database_url_full=",
"if [ -n \"$postgres_admin_password\" ]; then expected_database_url_full=\"postgres://$(urlencode \"$db_user\"):$(urlencode \"$postgres_admin_password\")@$db_host:5432/$db_name?sslmode=disable\"; fi",
"probe_db",
"db_role_exists_before=$role_result",
"db_database_exists_before=$database_result",
@@ -1465,11 +1521,7 @@ export function cloudApiDbSecretScript(options: NodeSecretOptions, spec: Runtime
"if [ \"$action_request\" = ensure ]; then",
" missing_secret=false",
" [ \"$before_url_present\" = yes ] && [ \"$before_url_bytes\" -gt 0 ] || missing_secret=true",
" expected_database_url=",
" if [ -n \"$postgres_admin_password\" ]; then",
" expected_database_url=\"postgres://$db_user:$postgres_admin_password@$db_host:5432/$db_name?sslmode=disable\"",
" [ \"$database_url\" = \"$expected_database_url\" ] || missing_secret=true",
" fi",
" if [ -n \"$expected_database_url_full\" ]; then [ \"$database_url\" = \"$expected_database_url_full\" ] || missing_secret=true; fi",
" missing_db=false",
" [ \"$db_role_exists_before\" = t ] || missing_db=true",
" [ \"$db_database_exists_before\" = t ] || missing_db=true",
@@ -1481,7 +1533,7 @@ export function cloudApiDbSecretScript(options: NodeSecretOptions, spec: Runtime
" elif [ \"$missing_secret\" = false ] && [ \"$missing_db\" = false ] && [ \"$consumer_not_ready\" = false ]; then",
" action=kept",
" else",
" database_url=\"postgres://$db_user:$postgres_admin_password@$db_host:5432/$db_name?sslmode=disable\"",
" database_url=\"postgres://$(urlencode \"$db_user\"):$(urlencode \"$postgres_admin_password\")@$db_host:5432/$db_name?sslmode=disable\"",
" kubectl -n \"$namespace\" create secret generic \"$name\" --from-literal=\"$database_url_key=$database_url\" --dry-run=client -o yaml | kubectl apply --server-side --force-conflicts --field-manager=\"$field_manager\" -f -",
" apply_exit=$?",
" if [ \"$apply_exit\" -eq 0 ]; then",
@@ -1504,14 +1556,7 @@ export function cloudApiDbSecretScript(options: NodeSecretOptions, spec: Runtime
" rc=$?",
" if [ \"$rc\" -ne 0 ]; then rollout_restart_exit=$rc; break; fi",
" done",
" if [ \"$rollout_restart_exit\" -eq 0 ]; then",
" rollout_status_exit=0",
" for deployment in $db_consumer_deployments; do",
" kubectl -n \"$namespace\" rollout status \"deployment/$deployment\" --timeout=180s >/tmp/hwlab-db-consumer-rollout-status-$deployment.out 2>/tmp/hwlab-db-consumer-rollout-status-$deployment.err",
" rc=$?",
" if [ \"$rc\" -ne 0 ]; then rollout_status_exit=$rc; break; fi",
" done",
" fi",
" if [ \"$rollout_restart_exit\" -eq 0 ]; then rollout_status_exit=0; fi",
" fi",
" if [ -n \"$rollout_restart_exit\" ] && [ \"$rollout_restart_exit\" != 0 ]; then action=rollout-restart-failed",
" elif [ -n \"$rollout_status_exit\" ] && [ \"$rollout_status_exit\" != 0 ]; then action=rollout-status-failed",
@@ -1531,10 +1576,8 @@ export function cloudApiDbSecretScript(options: NodeSecretOptions, spec: Runtime
"after_url_database=$uri_database",
"after_url_sslmode=$uri_sslmode",
"after_url_password_present=$uri_password_present",
"expected_url_prefix=\"postgres://$db_user:\"",
"expected_url_suffix=\"@$db_host:5432/$db_name?sslmode=disable\"",
"case \"$database_url\" in \"$expected_url_prefix\"*\"$expected_url_suffix\") before_url_matches_expected=yes ;; *) before_url_matches_expected=no ;; esac",
"case \"$after_database_url\" in \"$expected_url_prefix\"*\"$expected_url_suffix\") after_url_matches_expected=yes ;; *) after_url_matches_expected=no ;; esac",
"case \"$database_url\" in \"$expected_database_url_full\") before_url_matches_expected=yes ;; *) before_url_matches_expected=no ;; esac",
"case \"$after_database_url\" in \"$expected_database_url_full\") after_url_matches_expected=yes ;; *) after_url_matches_expected=no ;; esac",
"probe_db",
"db_role_exists_after=$role_result",
"db_database_exists_after=$database_result",
@@ -1790,18 +1833,41 @@ export function secretStatusFromText(text: string, commandOk: boolean, exitCode:
const openaiReady = fields.afterOpenaiPresent === "yes" && typeof afterOpenaiBytes === "number" && afterOpenaiBytes > 0;
const opencodeReady = fields.afterOpencodePresent === "yes" && typeof afterOpencodeBytes === "number" && afterOpencodeBytes > 0;
const healthy = fields.afterExists === "yes" && (openaiReady || opencodeReady);
const localSourceMode = fields.sourceMode === "local-source-ref";
return {
ok: commandOk && healthy,
namespace: fields.namespace || spec.namespace,
secret: fields.secret || spec.codeAgentProviderSecret,
preset: "code-agent-provider",
source: {
namespace: fields.sourceNamespace || spec.codeAgentProviderSourceNamespace,
secret: fields.sourceSecret || spec.codeAgentProviderSourceSecret,
exists: fields.sourceExists === "yes",
openaiApiKey: { keyPresent: fields.sourceOpenaiPresent === "yes", valueBytes: sourceOpenaiBytes },
opencodeApiKey: { keyPresent: fields.sourceOpencodePresent === "yes", valueBytes: sourceOpencodeBytes },
},
source: localSourceMode
? {
mode: "local-source-ref",
sourceRef: fields.sourceRef || spec.codeAgentProviderSourceRef || null,
sourcePath: fields.sourcePath || null,
exists: fields.sourceExists === "yes",
error: fields.sourceError || null,
openaiApiKey: {
sourceKey: fields.openaiSourceKey || spec.codeAgentProviderOpenaiSourceKey || null,
keyPresent: fields.sourceOpenaiPresent === "yes",
valueBytes: sourceOpenaiBytes,
fingerprint: fields.sourceOpenaiFingerprint || null,
},
opencodeApiKey: {
sourceKey: fields.opencodeSourceKey || spec.codeAgentProviderOpencodeSourceKey || null,
keyPresent: fields.sourceOpencodePresent === "yes",
valueBytes: sourceOpencodeBytes,
fingerprint: fields.sourceOpencodeFingerprint || null,
},
valuesRedacted: true,
}
: {
mode: "cluster-secret",
namespace: fields.sourceNamespace || spec.codeAgentProviderSourceNamespace,
secret: fields.sourceSecret || spec.codeAgentProviderSourceSecret,
exists: fields.sourceExists === "yes",
openaiApiKey: { keyPresent: fields.sourceOpenaiPresent === "yes", valueBytes: sourceOpenaiBytes },
opencodeApiKey: { keyPresent: fields.sourceOpencodePresent === "yes", valueBytes: sourceOpencodeBytes },
},
selectedKey: fields.selectedKey || null,
action: fields.action || null,
dryRun: fields.dryRun === "true",