fix(hwlab): configure d601 internal probe and local postgres (#725)
Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
@@ -73,9 +73,9 @@ export function hwlabNodeWebProbeHelp(): Record<string, unknown> {
|
||||
description: "Run target node/lane HWLAB Cloud Web DOM probes with Web login credentials resolved from YAML-declared bootstrap admin sourceRef and injected only as one-shot stdin/env.",
|
||||
examples: [
|
||||
"bun scripts/cli.ts hwlab nodes web-probe run --node D601 --lane v03 --wait-messages-ms 1000",
|
||||
"bun scripts/cli.ts hwlab nodes web-probe run --node D601 --lane v03 --url https://hwlab.pikapython.com --fresh-session --message 'ping'",
|
||||
"bun scripts/cli.ts hwlab nodes web-probe script --node D601 --lane v03 --browser-proxy-mode direct --script-file .state/probes/workbench.mjs",
|
||||
"bun scripts/cli.ts hwlab nodes web-probe observe start --node D601 --lane v03 --target-path /workbench --sample-interval-ms 5000 --browser-proxy-mode direct",
|
||||
"bun scripts/cli.ts hwlab nodes web-probe run --node D601 --lane v03 --fresh-session --message 'ping'",
|
||||
"bun scripts/cli.ts hwlab nodes web-probe script --node D601 --lane v03 --script-file .state/probes/workbench.mjs",
|
||||
"bun scripts/cli.ts hwlab nodes web-probe observe start --node D601 --lane v03 --target-path /workbench --sample-interval-ms 5000",
|
||||
"bun scripts/cli.ts hwlab nodes web-probe observe command webobs-xxxx --type newSession",
|
||||
"bun scripts/cli.ts hwlab nodes web-probe observe command webobs-xxxx --type selectProvider --provider codex-api",
|
||||
"bun scripts/cli.ts hwlab nodes web-probe observe command webobs-xxxx --type sendPrompt --text 'ping'",
|
||||
@@ -90,6 +90,7 @@ export function hwlabNodeWebProbeHelp(): Record<string, unknown> {
|
||||
observe: "Start, inspect, control, stop, collect, and analyze a pure-client long-running Workbench observer on the target host. The observer runs a control page plus a passive observer page in a shared-auth browser context, receives commands through stateDir/commands files, writes JSONL artifacts, and does not expose any inbound service API.",
|
||||
},
|
||||
notes: [
|
||||
"The default probe URL and browser proxy mode come from config/hwlab-node-lanes.yaml webProbe; pass --url only when intentionally overriding the YAML-selected origin.",
|
||||
"Prefer --script-file for reusable probes; stdin heredocs remain supported for one-off probes.",
|
||||
"Issue-ready evidence is available under issueEvidence and summary.issueEvidence; full script report is persisted under probe.reportPath with a SHA-256 fingerprint.",
|
||||
"observe sampling is passive by default: it records DOM summaries and natural page request/response/requestfailed events with observerInitiated=false; it does not actively fetch Workbench APIs, reload, switch sessions, route/intercept, or call repair helpers.",
|
||||
|
||||
@@ -461,6 +461,10 @@ function parseNodeScopedDelegatedOptions(domain: DelegatedNodeDomain, args: stri
|
||||
};
|
||||
}
|
||||
|
||||
function nodeRuntimeLocalPostgresExpectedAbsent(spec: HwlabRuntimeLaneSpec): boolean {
|
||||
return spec.externalPostgres !== undefined || spec.runtimeStore?.postgres?.mode === "platform-service";
|
||||
}
|
||||
|
||||
function nodeRuntimeExpected(spec: HwlabRuntimeLaneSpec): Record<string, unknown> {
|
||||
return {
|
||||
configPath: hwlabRuntimeLaneConfigPath(),
|
||||
@@ -508,6 +512,10 @@ function nodeRuntimeExpected(spec: HwlabRuntimeLaneSpec): Record<string, unknown
|
||||
webUrl: spec.publicWebUrl,
|
||||
apiUrl: spec.publicApiUrl,
|
||||
},
|
||||
webProbe: spec.webProbe === undefined ? null : {
|
||||
browserProxyMode: spec.webProbe.browserProxyMode ?? null,
|
||||
defaultOrigin: spec.webProbe.defaultOrigin ?? null,
|
||||
},
|
||||
bootstrapAdmin: spec.bootstrapAdmin === undefined ? null : {
|
||||
username: spec.bootstrapAdmin.username,
|
||||
displayName: spec.bootstrapAdmin.displayName,
|
||||
@@ -575,7 +583,7 @@ function nodeRuntimeControlPlanePlan(scoped: ReturnType<typeof parseNodeScopedDe
|
||||
externalPostgresDeclared: scoped.spec.externalPostgres !== undefined,
|
||||
secretValuesPrinted: false,
|
||||
runtimeNamespace: scoped.spec.runtimeNamespace,
|
||||
localPostgresExpectedAbsent: scoped.spec.externalPostgres !== undefined,
|
||||
localPostgresExpectedAbsent: nodeRuntimeLocalPostgresExpectedAbsent(scoped.spec),
|
||||
publicExposureDeclared: scoped.spec.publicExposure !== null,
|
||||
},
|
||||
next: {
|
||||
@@ -3013,7 +3021,7 @@ function withNodeRuntimeControlPlaneStatusRendered(result: Record<string, unknow
|
||||
[
|
||||
["pipeline", pipelineRun.ready === true ? "ok" : pipelineRun.exists === true ? webObserveText(pipelineRun.status) : "missing", webObserveShort(webObserveText(pipelineRun.reason ?? pipelineRun.message), 80)],
|
||||
["argo", argo.ready === true ? "ok" : "failed", `${webObserveText(argo.syncStatus)}/${webObserveText(argo.health)} rev=${shortValue(argo.syncRevision)}`],
|
||||
["runtime", runtime.ready === true ? "ok" : "failed", `workloads=${webObserveText(runtime.workloadReady)} pg=${webObserveText(runtime.externalPostgresReady)}`],
|
||||
["runtime", runtime.ready === true ? "ok" : "failed", `workloads=${webObserveText(runtime.workloadReady)} pg=${webObserveText(runtime.externalPostgresReady ?? runtime.localPostgresReady)}`],
|
||||
["public", publicProbe.ready === true ? "ok" : "failed", webObserveShort(webObserveText(diagnostic.kind ?? diagnostic.message), 80)],
|
||||
["git-mirror", gitMirror.ready === true ? "ok" : "failed", `pending=${webObserveText(gitMirror.pendingFlush)} inSync=${webObserveText(gitMirror.githubInSync)}`],
|
||||
],
|
||||
@@ -3831,7 +3839,9 @@ function nodeRuntimeControlPlaneStatus(scoped: ReturnType<typeof parseNodeScoped
|
||||
const gitMirrorCompact = compactNodeRuntimeGitMirrorStatus(gitMirror);
|
||||
const controlPlaneReady = serviceAccount.exitCode === 0 && pipeline.exitCode === 0 && argo.exitCode === 0;
|
||||
const workloadsReady = workloadReadiness.length > 0 && workloadReadiness.every((item) => item.ready);
|
||||
const runtimeReady = namespaceExists && localPostgresObjects.length === 0 && workloadsReady && (spec.externalPostgres === undefined || (bridge.ready && secrets.ready));
|
||||
const localPostgresExpectedAbsent = nodeRuntimeLocalPostgresExpectedAbsent(spec);
|
||||
const localPostgresReady = localPostgresExpectedAbsent ? localPostgresObjects.length === 0 : localPostgresObjects.length > 0;
|
||||
const runtimeReady = namespaceExists && localPostgresReady && workloadsReady && (spec.externalPostgres === undefined || (bridge.ready && secrets.ready));
|
||||
const argoReady = argo.exitCode === 0 && repoURL === spec.argoRepoUrl && targetRevision === spec.gitopsBranch && path === spec.runtimePath && syncStatus === "Synced" && health === "Healthy";
|
||||
const pipelineRunReady = pipelineRunProbe !== null && pipelineRunProbe.status === "True";
|
||||
const pipelineRunDegradedReason = typeof pipelineRunDiagnostics?.degradedReason === "string"
|
||||
@@ -3877,6 +3887,8 @@ function nodeRuntimeControlPlaneStatus(scoped: ReturnType<typeof parseNodeScoped
|
||||
namespaceExists,
|
||||
localPostgresObjects,
|
||||
localPostgresAbsent: namespaceExists && localPostgresObjects.length === 0,
|
||||
localPostgresExpectedAbsent,
|
||||
localPostgresReady,
|
||||
workloadNames,
|
||||
workloadCount: workloadNames.length,
|
||||
workloadReadiness,
|
||||
@@ -4131,6 +4143,8 @@ function summarizeNodeRuntimeControlPlaneStatus(status: Record<string, unknown>,
|
||||
workloadCount,
|
||||
workloadReady: `${readyWorkloads}/${workloadReadiness.length}`,
|
||||
localPostgresAbsent: runtime.localPostgresAbsent === true,
|
||||
localPostgresExpectedAbsent: runtime.localPostgresExpectedAbsent === true,
|
||||
localPostgresReady: runtime.localPostgresReady === true,
|
||||
externalPostgresReady: runtime.externalPostgresBridge === undefined && runtime.externalPostgresSecrets === undefined
|
||||
? null
|
||||
: record(runtime.externalPostgresBridge).ready === true && record(runtime.externalPostgresSecrets).ready === true,
|
||||
@@ -6793,10 +6807,10 @@ function parseNodeWebProbeOptions(args: string[]): NodeWebProbeOptions {
|
||||
action: "script",
|
||||
node,
|
||||
lane,
|
||||
url: optionValue(args, "--url") ?? spec.publicWebUrl,
|
||||
url: optionValue(args, "--url") ?? nodeWebProbeDefaultUrl(spec),
|
||||
timeoutMs,
|
||||
viewport: optionValue(args, "--viewport") ?? "1440x900",
|
||||
browserProxyMode: parseWebProbeBrowserProxyMode(optionValue(args, "--browser-proxy-mode")),
|
||||
browserProxyMode: parseWebProbeBrowserProxyMode(optionValue(args, "--browser-proxy-mode") ?? spec.webProbe?.browserProxyMode),
|
||||
commandTimeoutSeconds,
|
||||
scriptText,
|
||||
scriptSource: {
|
||||
@@ -6853,7 +6867,7 @@ function parseNodeWebProbeOptions(args: string[]): NodeWebProbeOptions {
|
||||
action: "run",
|
||||
node,
|
||||
lane,
|
||||
url: optionValue(args, "--url") ?? spec.publicWebUrl,
|
||||
url: optionValue(args, "--url") ?? nodeWebProbeDefaultUrl(spec),
|
||||
timeoutMs,
|
||||
waitAfterSubmitMs,
|
||||
waitMessagesMs,
|
||||
@@ -6952,10 +6966,10 @@ function parseNodeWebProbeObserveOptions(
|
||||
id: observeId ?? jobId,
|
||||
node,
|
||||
lane,
|
||||
url: optionValue(args, "--url") ?? spec.publicWebUrl,
|
||||
url: optionValue(args, "--url") ?? (observeActionRaw === "start" ? nodeWebProbeDefaultUrl(spec) : indexed?.url ?? spec.publicWebUrl),
|
||||
targetPath: optionValue(args, "--target-path") ?? "/workbench",
|
||||
viewport: optionValue(args, "--viewport") ?? "1440x900",
|
||||
browserProxyMode: parseWebProbeBrowserProxyMode(optionValue(args, "--browser-proxy-mode")),
|
||||
browserProxyMode: parseWebProbeBrowserProxyMode(optionValue(args, "--browser-proxy-mode") ?? spec.webProbe?.browserProxyMode),
|
||||
sampleIntervalMs: positiveIntegerOption(args, "--sample-interval-ms", 5000, 600000),
|
||||
screenshotIntervalMs: positiveIntegerOption(args, "--screenshot-interval-ms", 300000, 86_400_000),
|
||||
observerRefreshIntervalMs: positiveIntegerOption(args, "--observer-refresh-interval-ms", 180000, 86_400_000),
|
||||
@@ -7001,6 +7015,13 @@ function parseWebProbeBrowserProxyMode(value: string | undefined): WebProbeBrows
|
||||
throw new Error(`web-probe --browser-proxy-mode must be auto or direct, got ${value}`);
|
||||
}
|
||||
|
||||
function nodeWebProbeDefaultUrl(spec: HwlabRuntimeLaneSpec): string {
|
||||
const origin = spec.webProbe?.defaultOrigin;
|
||||
if (origin === undefined || origin.mode === "public") return origin?.baseUrl ?? spec.publicWebUrl;
|
||||
const clusterIp = resolveKubernetesServiceClusterIp(spec, origin.namespace, origin.serviceName, new Map());
|
||||
return `${origin.scheme}://${clusterIp}:${origin.port}`;
|
||||
}
|
||||
|
||||
function nodeWebProbeAutoCommandTimeoutSeconds(input: {
|
||||
timeoutMs: number;
|
||||
waitAfterSubmitMs: number;
|
||||
@@ -10132,16 +10153,18 @@ function runtimeSecretSpec(input: { node: string; lane: string }): RuntimeSecret
|
||||
const namespace = `hwlab-${input.lane}`;
|
||||
const runtimeLaneSpec = isHwlabRuntimeLane(input.lane) ? hwlabRuntimeLaneSpecForNode(input.lane, input.node) : undefined;
|
||||
const externalPostgres = runtimeLaneSpec?.externalPostgres;
|
||||
const postgresStore = runtimeLaneSpec?.runtimeStore?.postgres;
|
||||
const bootstrapAdmin = runtimeLaneSpec?.bootstrapAdmin;
|
||||
const platformDb = externalPostgres !== undefined || /^v0*[3-9]\d*$/.test(input.lane);
|
||||
const platformPostgresService = externalPostgres?.serviceName ?? "g14-platform-postgres";
|
||||
const legacyPostgresHost = `${namespace}-postgres.${namespace}.svc.cluster.local`;
|
||||
const platformDb = externalPostgres !== undefined || postgresStore?.mode === "platform-service" || (postgresStore?.mode === undefined && /^v0*[3-9]\d*$/.test(input.lane));
|
||||
const localPostgresService = postgresStore?.serviceName ?? `${namespace}-postgres`;
|
||||
const platformPostgresService = externalPostgres?.serviceName ?? postgresStore?.serviceName ?? "g14-platform-postgres";
|
||||
const legacyPostgresHost = `${localPostgresService}.${namespace}.svc.cluster.local`;
|
||||
const platformPostgresHost = `${platformPostgresService}.${namespace}.svc.cluster.local`;
|
||||
const platformPostgresEndpointSlice = `${platformPostgresService}-host`;
|
||||
const openFgaDbName = externalPostgres?.database ?? (platformDb ? `openfga_${input.lane}` : "hwlab_openfga");
|
||||
const openFgaDbUser = externalPostgres?.openfga.role ?? (platformDb ? `openfga_${input.lane}_app` : "hwlab_openfga");
|
||||
const cloudApiDbName = externalPostgres?.database ?? `hwlab_${input.lane}`;
|
||||
const cloudApiDbUser = externalPostgres?.cloudApi.role ?? (platformDb ? `hwlab_${input.lane}_app` : `hwlab_${input.lane}`);
|
||||
const openFgaDbName = externalPostgres?.database ?? postgresStore?.openfga?.database ?? (platformDb ? `openfga_${input.lane}` : "hwlab_openfga");
|
||||
const openFgaDbUser = externalPostgres?.openfga.role ?? postgresStore?.openfga?.role ?? (platformDb ? `openfga_${input.lane}_app` : "hwlab_openfga");
|
||||
const cloudApiDbName = externalPostgres?.database ?? postgresStore?.cloudApi?.database ?? `hwlab_${input.lane}`;
|
||||
const cloudApiDbUser = externalPostgres?.cloudApi.role ?? postgresStore?.cloudApi?.role ?? (platformDb ? `hwlab_${input.lane}_app` : `hwlab_${input.lane}`);
|
||||
return {
|
||||
node: input.node,
|
||||
lane: input.lane,
|
||||
@@ -10152,10 +10175,10 @@ function runtimeSecretSpec(input: { node: string; lane: string }): RuntimeSecret
|
||||
platformPostgresService,
|
||||
platformPostgresEndpointAddress: externalPostgres?.endpointAddress,
|
||||
platformPostgresEndpointSlice,
|
||||
postgresSecret: `${namespace}-postgres`,
|
||||
postgresStatefulSet: `${namespace}-postgres`,
|
||||
postgresAdminUser: `hwlab_${input.lane}`,
|
||||
openFgaSecret: externalPostgres?.openfga.secretName ?? `${namespace}-openfga`,
|
||||
postgresSecret: postgresStore?.secretName ?? `${namespace}-postgres`,
|
||||
postgresStatefulSet: postgresStore?.statefulSet ?? postgresStore?.secretName ?? `${namespace}-postgres`,
|
||||
postgresAdminUser: postgresStore?.adminUser ?? `hwlab_${input.lane}`,
|
||||
openFgaSecret: externalPostgres?.openfga.secretName ?? postgresStore?.openfga?.secretName ?? `${namespace}-openfga`,
|
||||
openFgaDbName,
|
||||
openFgaDbUser,
|
||||
openFgaDbHost: platformDb ? platformPostgresHost : legacyPostgresHost,
|
||||
@@ -10169,8 +10192,8 @@ function runtimeSecretSpec(input: { node: string; lane: string }): RuntimeSecret
|
||||
...(bootstrapAdmin?.passwordHashTransform === undefined ? {} : { bootstrapAdminPasswordHashTransform: bootstrapAdmin.passwordHashTransform }),
|
||||
bootstrapAdminSourceNamespace: BOOTSTRAP_ADMIN_SOURCE_NAMESPACE,
|
||||
bootstrapAdminSourceSecret: BOOTSTRAP_ADMIN_SOURCE_SECRET,
|
||||
cloudApiDbSecret: externalPostgres?.cloudApi.secretName ?? `hwlab-cloud-api-${input.lane}-db`,
|
||||
cloudApiDbKey: externalPostgres?.cloudApi.secretKey ?? CLOUD_API_DB_KEY,
|
||||
cloudApiDbSecret: externalPostgres?.cloudApi.secretName ?? postgresStore?.cloudApi?.secretName ?? `hwlab-cloud-api-${input.lane}-db`,
|
||||
cloudApiDbKey: externalPostgres?.cloudApi.secretKey ?? postgresStore?.cloudApi?.secretKey ?? CLOUD_API_DB_KEY,
|
||||
cloudApiDbName,
|
||||
cloudApiDbUser,
|
||||
cloudApiDbHost: platformDb ? platformPostgresHost : legacyPostgresHost,
|
||||
|
||||
@@ -80,6 +80,13 @@ export interface HwlabRuntimeExternalPostgresSpec {
|
||||
}
|
||||
|
||||
export interface HwlabRuntimePostgresStoreSpec {
|
||||
readonly mode?: "local-k3s" | "platform-service";
|
||||
readonly secretName?: string;
|
||||
readonly statefulSet?: string;
|
||||
readonly serviceName?: string;
|
||||
readonly adminUser?: string;
|
||||
readonly cloudApi?: HwlabRuntimePostgresStoreComponentSpec;
|
||||
readonly openfga?: HwlabRuntimePostgresStoreComponentSpec;
|
||||
readonly poolMax: number;
|
||||
readonly connectionTimeoutMs?: number;
|
||||
readonly queryRetryMaxAttempts?: number;
|
||||
@@ -87,10 +94,39 @@ export interface HwlabRuntimePostgresStoreSpec {
|
||||
readonly queryRetryMaxDelayMs?: number;
|
||||
}
|
||||
|
||||
export interface HwlabRuntimePostgresStoreComponentSpec {
|
||||
readonly secretName: string;
|
||||
readonly secretKey: string;
|
||||
readonly database: string;
|
||||
readonly role: string;
|
||||
readonly authnKey?: string;
|
||||
readonly postgresPasswordKey?: string;
|
||||
}
|
||||
|
||||
export interface HwlabRuntimeStoreSpec {
|
||||
readonly postgres?: HwlabRuntimePostgresStoreSpec;
|
||||
}
|
||||
|
||||
export interface HwlabRuntimeWebProbeServiceOriginSpec {
|
||||
readonly mode: "k8s-service-cluster-ip";
|
||||
readonly serviceName: string;
|
||||
readonly namespace: string;
|
||||
readonly port: number;
|
||||
readonly scheme: "http" | "https";
|
||||
}
|
||||
|
||||
export interface HwlabRuntimeWebProbePublicOriginSpec {
|
||||
readonly mode: "public";
|
||||
readonly baseUrl?: string;
|
||||
}
|
||||
|
||||
export type HwlabRuntimeWebProbeOriginSpec = HwlabRuntimeWebProbeServiceOriginSpec | HwlabRuntimeWebProbePublicOriginSpec;
|
||||
|
||||
export interface HwlabRuntimeWebProbeSpec {
|
||||
readonly browserProxyMode?: "auto" | "direct";
|
||||
readonly defaultOrigin?: HwlabRuntimeWebProbeOriginSpec;
|
||||
}
|
||||
|
||||
export interface HwlabRuntimeBuildkitSpec {
|
||||
readonly sidecarImage: string;
|
||||
}
|
||||
@@ -230,6 +266,7 @@ export interface HwlabRuntimeLaneSpec {
|
||||
readonly bootstrapAdmin?: HwlabRuntimeBootstrapAdminSpec;
|
||||
readonly externalPostgres?: HwlabRuntimeExternalPostgresSpec;
|
||||
readonly runtimeStore?: HwlabRuntimeStoreSpec;
|
||||
readonly webProbe?: HwlabRuntimeWebProbeSpec;
|
||||
readonly publicExposure: HwlabRuntimePublicExposureSpec | null;
|
||||
readonly observability: HwlabRuntimeObservabilitySpec;
|
||||
readonly runtimeImageRewrites: readonly HwlabRuntimeImageRewriteSpec[];
|
||||
@@ -272,6 +309,7 @@ interface HwlabLaneConfig {
|
||||
readonly bootstrapAdmin?: HwlabRuntimeBootstrapAdminSpec;
|
||||
readonly externalPostgres?: HwlabRuntimeExternalPostgresSpec;
|
||||
readonly runtimeStore?: HwlabRuntimeStoreSpec;
|
||||
readonly webProbe?: HwlabRuntimeWebProbeSpec;
|
||||
readonly publicExposure: HwlabRuntimePublicExposureSpec | null;
|
||||
readonly observability: HwlabRuntimeObservabilitySpec;
|
||||
readonly runtimeImageRewrites: readonly HwlabRuntimeImageRewriteSpec[];
|
||||
@@ -490,6 +528,7 @@ function laneConfig(id: HwlabRuntimeLane, raw: Record<string, unknown>): HwlabLa
|
||||
bootstrapAdmin: bootstrapAdminConfig(raw.bootstrapAdmin, `lanes.${id}.bootstrapAdmin`),
|
||||
externalPostgres: externalPostgresConfig(raw.externalPostgres, `lanes.${id}.externalPostgres`),
|
||||
runtimeStore: runtimeStoreConfig(raw.runtimeStore, `lanes.${id}.runtimeStore`),
|
||||
webProbe: webProbeConfig(raw.webProbe, `lanes.${id}.webProbe`),
|
||||
publicExposure: publicExposureConfig(raw.publicExposure, `lanes.${id}.publicExposure`),
|
||||
observability: observabilityConfig(raw.observability, `lanes.${id}.observability`),
|
||||
runtimeImageRewrites: runtimeImageRewritesConfig(raw.runtimeImageRewrites, `lanes.${id}.runtimeImageRewrites`),
|
||||
@@ -510,6 +549,7 @@ function laneTargetConfig(id: HwlabRuntimeLane, nodeId: string, baseRaw: Record<
|
||||
bootstrapAdmin: mergeOptionalRecord(baseRaw.bootstrapAdmin, targetRaw.bootstrapAdmin),
|
||||
externalPostgres: mergeOptionalRecord(baseRaw.externalPostgres, targetRaw.externalPostgres),
|
||||
runtimeStore: mergeOptionalRecord(baseRaw.runtimeStore, targetRaw.runtimeStore),
|
||||
webProbe: mergeOptionalRecord(baseRaw.webProbe, targetRaw.webProbe),
|
||||
publicExposure: mergeOptionalRecord(baseRaw.publicExposure, targetRaw.publicExposure),
|
||||
observability: mergeOptionalRecord(baseRaw.observability, targetRaw.observability),
|
||||
};
|
||||
@@ -595,6 +635,13 @@ function runtimeStoreConfig(value: unknown, path: string): HwlabRuntimeStoreSpec
|
||||
? {}
|
||||
: {
|
||||
postgres: {
|
||||
...(postgres.mode === undefined ? {} : { mode: postgresModeField(postgres, `${path}.postgres`) }),
|
||||
...(postgres.secretName === undefined ? {} : { secretName: stringField(postgres, "secretName", `${path}.postgres`) }),
|
||||
...(postgres.statefulSet === undefined ? {} : { statefulSet: stringField(postgres, "statefulSet", `${path}.postgres`) }),
|
||||
...(postgres.serviceName === undefined ? {} : { serviceName: stringField(postgres, "serviceName", `${path}.postgres`) }),
|
||||
...(postgres.adminUser === undefined ? {} : { adminUser: stringField(postgres, "adminUser", `${path}.postgres`) }),
|
||||
...(postgres.cloudApi === undefined ? {} : { cloudApi: runtimeStorePostgresComponentConfig(postgres.cloudApi, `${path}.postgres.cloudApi`) }),
|
||||
...(postgres.openfga === undefined ? {} : { openfga: runtimeStorePostgresComponentConfig(postgres.openfga, `${path}.postgres.openfga`) }),
|
||||
poolMax: numberField(postgres, "poolMax", `${path}.postgres`),
|
||||
...(postgres.connectionTimeoutMs === undefined
|
||||
? {}
|
||||
@@ -613,6 +660,62 @@ function runtimeStoreConfig(value: unknown, path: string): HwlabRuntimeStoreSpec
|
||||
};
|
||||
}
|
||||
|
||||
function postgresModeField(raw: Record<string, unknown>, path: string): "local-k3s" | "platform-service" {
|
||||
const mode = stringField(raw, "mode", path);
|
||||
if (mode !== "local-k3s" && mode !== "platform-service") throw new Error(`${path}.mode must be local-k3s or platform-service`);
|
||||
return mode;
|
||||
}
|
||||
|
||||
function runtimeStorePostgresComponentConfig(value: unknown, path: string): HwlabRuntimePostgresStoreComponentSpec {
|
||||
const raw = asRecord(value, path);
|
||||
return {
|
||||
secretName: stringField(raw, "secretName", path),
|
||||
secretKey: secretKeyField(raw, "secretKey", path),
|
||||
database: stringField(raw, "database", path),
|
||||
role: stringField(raw, "role", path),
|
||||
authnKey: optionalStringField(raw, "authnKey", path),
|
||||
postgresPasswordKey: optionalStringField(raw, "postgresPasswordKey", path),
|
||||
};
|
||||
}
|
||||
|
||||
function webProbeConfig(value: unknown, path: string): HwlabRuntimeWebProbeSpec | undefined {
|
||||
if (value === undefined) return undefined;
|
||||
const raw = asRecord(value, path);
|
||||
const rawBrowserProxyMode = optionalStringField(raw, "browserProxyMode", path);
|
||||
let browserProxyMode: "auto" | "direct" | undefined;
|
||||
if (rawBrowserProxyMode !== undefined) {
|
||||
if (rawBrowserProxyMode !== "auto" && rawBrowserProxyMode !== "direct") {
|
||||
throw new Error(`${path}.browserProxyMode must be auto or direct`);
|
||||
}
|
||||
browserProxyMode = rawBrowserProxyMode;
|
||||
}
|
||||
return {
|
||||
...(browserProxyMode === undefined ? {} : { browserProxyMode }),
|
||||
...(raw.defaultOrigin === undefined ? {} : { defaultOrigin: webProbeOriginConfig(raw.defaultOrigin, `${path}.defaultOrigin`) }),
|
||||
};
|
||||
}
|
||||
|
||||
function webProbeOriginConfig(value: unknown, path: string): HwlabRuntimeWebProbeOriginSpec {
|
||||
const raw = asRecord(value, path);
|
||||
const mode = stringField(raw, "mode", path);
|
||||
if (mode === "public") {
|
||||
return {
|
||||
mode,
|
||||
...(raw.baseUrl === undefined ? {} : { baseUrl: stringField(raw, "baseUrl", path).replace(/\/+$/u, "") }),
|
||||
};
|
||||
}
|
||||
if (mode !== "k8s-service-cluster-ip") throw new Error(`${path}.mode must be public or k8s-service-cluster-ip`);
|
||||
const scheme = stringField(raw, "scheme", path);
|
||||
if (scheme !== "http" && scheme !== "https") throw new Error(`${path}.scheme must be http or https`);
|
||||
return {
|
||||
mode,
|
||||
serviceName: stringField(raw, "serviceName", path),
|
||||
namespace: stringField(raw, "namespace", path),
|
||||
port: numberField(raw, "port", path),
|
||||
scheme,
|
||||
};
|
||||
}
|
||||
|
||||
function publicExposureProxyConfig(value: unknown, path: string): HwlabRuntimePublicExposureFrpcProxySpec {
|
||||
const raw = asRecord(value, path);
|
||||
return {
|
||||
@@ -887,6 +990,7 @@ function buildRuntimeLaneSpec(config: HwlabLaneConfig): HwlabRuntimeLaneSpec {
|
||||
...(config.bootstrapAdmin === undefined ? {} : { bootstrapAdmin: config.bootstrapAdmin }),
|
||||
...(config.externalPostgres === undefined ? {} : { externalPostgres: config.externalPostgres }),
|
||||
...(config.runtimeStore === undefined ? {} : { runtimeStore: config.runtimeStore }),
|
||||
...(config.webProbe === undefined ? {} : { webProbe: config.webProbe }),
|
||||
publicExposure: config.publicExposure,
|
||||
observability: config.observability,
|
||||
runtimeImageRewrites: config.runtimeImageRewrites,
|
||||
|
||||
Reference in New Issue
Block a user