feat: make hwlab v03 admin secret yaml driven

This commit is contained in:
Codex
2026-06-13 11:01:04 +00:00
parent cfedce2702
commit 48c1a2c297
4 changed files with 345 additions and 12 deletions
+46
View File
@@ -120,6 +120,17 @@ export interface HwlabRuntimePublicExposureSpec {
readonly apiProxy: HwlabRuntimePublicExposureFrpcProxySpec;
}
export interface HwlabRuntimeBootstrapAdminSpec {
readonly username: string;
readonly displayName: string;
readonly passwordSourceRef: string;
readonly passwordSourceKey: string;
readonly passwordHashTransform: "hwlab-sha256";
readonly secretName: string;
readonly secretKey: string;
readonly rolloutDeployment: string;
}
export interface HwlabRuntimeLaneSpec {
readonly lane: HwlabRuntimeLane;
readonly nodeId: string;
@@ -156,6 +167,7 @@ export interface HwlabRuntimeLaneSpec {
readonly publicApiUrl: string;
readonly stepEnv: Record<string, string>;
readonly buildkit?: HwlabRuntimeBuildkitSpec;
readonly bootstrapAdmin?: HwlabRuntimeBootstrapAdminSpec;
readonly externalPostgres?: HwlabRuntimeExternalPostgresSpec;
readonly publicExposure: HwlabRuntimePublicExposureSpec | null;
readonly observability: HwlabRuntimeObservabilitySpec;
@@ -196,6 +208,7 @@ interface HwlabLaneConfig {
readonly public: { readonly webUrl: string; readonly apiUrl: string };
readonly stepEnv: Record<string, string>;
readonly buildkit?: HwlabRuntimeBuildkitSpec;
readonly bootstrapAdmin?: HwlabRuntimeBootstrapAdminSpec;
readonly externalPostgres?: HwlabRuntimeExternalPostgresSpec;
readonly publicExposure: HwlabRuntimePublicExposureSpec | null;
readonly observability: HwlabRuntimeObservabilitySpec;
@@ -396,6 +409,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`),
externalPostgres: externalPostgresConfig(raw.externalPostgres, `lanes.${id}.externalPostgres`),
publicExposure: publicExposureConfig(raw.publicExposure, `lanes.${id}.publicExposure`),
observability: observabilityConfig(raw.observability, `lanes.${id}.observability`),
@@ -414,6 +428,7 @@ function laneTargetConfig(id: HwlabRuntimeLane, nodeId: string, baseRaw: Record<
public: mergeOptionalRecord(baseRaw.public, targetRaw.public),
stepEnv: mergeOptionalRecord(baseRaw.stepEnv, targetRaw.stepEnv) ?? {},
buildkit: mergeOptionalRecord(baseRaw.buildkit, targetRaw.buildkit),
bootstrapAdmin: mergeOptionalRecord(baseRaw.bootstrapAdmin, targetRaw.bootstrapAdmin),
externalPostgres: mergeOptionalRecord(baseRaw.externalPostgres, targetRaw.externalPostgres),
publicExposure: mergeOptionalRecord(baseRaw.publicExposure, targetRaw.publicExposure),
observability: mergeOptionalRecord(baseRaw.observability, targetRaw.observability),
@@ -430,6 +445,36 @@ function buildkitConfig(value: unknown, path: string): HwlabRuntimeBuildkitSpec
};
}
function sourceRefField(obj: Record<string, unknown>, key: string, path: string): string {
const value = stringField(obj, key, path);
if (!/^[A-Za-z0-9_./-]+$/u.test(value)) throw new Error(`${path}.${key} has an unsupported format`);
return value;
}
function secretKeyField(obj: Record<string, unknown>, key: string, path: string): string {
const value = stringField(obj, key, path);
if (!/^[A-Za-z0-9_.-]+$/u.test(value)) throw new Error(`${path}.${key} has an unsupported format`);
return value;
}
function bootstrapAdminConfig(value: unknown, path: string): HwlabRuntimeBootstrapAdminSpec | undefined {
if (value === undefined) return undefined;
const raw = asRecord(value, path);
const transform = stringField(raw, "passwordHashTransform", path);
if (transform !== "hwlab-sha256") throw new Error(`${path}.passwordHashTransform must be hwlab-sha256`);
const rollout = asRecord(raw.rollout, `${path}.rollout`);
return {
username: stringField(raw, "username", path),
displayName: stringField(raw, "displayName", path),
passwordSourceRef: sourceRefField(raw, "passwordSourceRef", path),
passwordSourceKey: secretKeyField(raw, "passwordSourceKey", path),
passwordHashTransform: transform,
secretName: stringField(raw, "secretName", path),
secretKey: secretKeyField(raw, "secretKey", path),
rolloutDeployment: stringField(rollout, "deployment", `${path}.rollout`),
};
}
function externalPostgresComponentConfig(value: unknown, path: string): HwlabRuntimeExternalPostgresComponentSpec {
const raw = asRecord(value, path);
return {
@@ -603,6 +648,7 @@ function buildRuntimeLaneSpec(config: HwlabLaneConfig): HwlabRuntimeLaneSpec {
publicApiUrl: config.public.apiUrl,
stepEnv: config.stepEnv,
...(config.buildkit === undefined ? {} : { buildkit: config.buildkit }),
...(config.bootstrapAdmin === undefined ? {} : { bootstrapAdmin: config.bootstrapAdmin }),
...(config.externalPostgres === undefined ? {} : { externalPostgres: config.externalPostgres }),
publicExposure: config.publicExposure,
observability: config.observability,