fix: gate d601 v03 postgres mode by yaml switch (#972)

Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
Lyon
2026-06-26 14:01:49 +08:00
committed by GitHub
parent 0b923fc5ee
commit fc6d3bdaf9
8 changed files with 55 additions and 20 deletions
+19 -1
View File
@@ -144,7 +144,25 @@ lanes:
renderDir: runtime-v03
runtimeStore:
postgres:
mode: platform-service
mode: local-k3s
secretName: hwlab-v03-postgres
statefulSet: hwlab-v03-postgres
serviceName: hwlab-v03-postgres
adminUser: hwlab_v03
adminPasswordSourceRef: hwlab/d601-v03-postgres.env
adminPasswordSourceKey: HWLAB_V03_POSTGRES_PASSWORD
cloudApi:
secretName: hwlab-cloud-api-v03-db
secretKey: database-url
database: hwlab_v03
role: hwlab_v03
openfga:
secretName: hwlab-v03-openfga
secretKey: datastore-uri
authnKey: authn-preshared-key
postgresPasswordKey: postgres-password
database: hwlab_openfga
role: hwlab_openfga
poolMax: 16
connectionTimeoutMs: 5000
queryRetryMaxAttempts: 5
+4
View File
@@ -326,6 +326,10 @@ export interface HwlabRuntimeLaneSpec {
readonly downloadProfile: HwlabDownloadProfileSpec;
}
export function hwlabRuntimeActiveExternalPostgres(spec: HwlabRuntimeLaneSpec): HwlabRuntimeExternalPostgresSpec | undefined {
return spec.runtimeStore?.postgres?.mode === "platform-service" ? spec.externalPostgres : undefined;
}
export const HWLAB_NODE_LANE_CONFIG_PATH = "config/hwlab-node-lanes.yaml";
interface HwlabLaneConfig {
+3 -1
View File
@@ -358,7 +358,7 @@ export function withNodeRuntimeControlPlanePlanRendered(result: Record<string, u
result.ok === true ? "ok" : "failed",
webObserveText(result.mode),
webObserveText(checks.runtimeNamespace),
webObserveText(checks.externalPostgresDeclared),
webObserveText(checks.externalPostgresActive),
webObserveText(checks.publicExposureDeclared),
]],
),
@@ -367,6 +367,8 @@ export function withNodeRuntimeControlPlanePlanRendered(result: Record<string, u
["CHECK", "VALUE"],
[
["node-scoped-target", webObserveText(checks.nodeScopedTargetConfigured)],
["external-postgres-declared", webObserveText(checks.externalPostgresDeclared)],
["external-postgres-active", webObserveText(checks.externalPostgresActive)],
["local-postgres-absent", webObserveText(checks.localPostgresExpectedAbsent)],
["secret-values-printed", webObserveText(checks.secretValuesPrinted)],
],
+5 -2
View File
@@ -37,8 +37,10 @@ import { compactPrometheusLines, compactRuntimeCommand, isWorkbenchBackendEventM
import { compactCommandResultRedacted, record, shellQuote } from "./utils";
import { readBootstrapAdminPasswordMaterial } from "./web-probe";
import { webProbeCredential } from "./web-probe-observe";
import { hwlabRuntimeActiveExternalPostgres } from "../hwlab-node-lanes";
export function nodeRuntimeControlPlanePlan(scoped: ReturnType<typeof parseNodeScopedDelegatedOptions>): Record<string, unknown> {
const activeExternalPostgres = hwlabRuntimeActiveExternalPostgres(scoped.spec);
return {
ok: true,
command: `hwlab nodes control-plane plan --node ${scoped.node} --lane ${scoped.lane}`,
@@ -50,6 +52,7 @@ export function nodeRuntimeControlPlanePlan(scoped: ReturnType<typeof parseNodeS
checks: {
nodeScopedTargetConfigured: true,
externalPostgresDeclared: scoped.spec.externalPostgres !== undefined,
externalPostgresActive: activeExternalPostgres !== undefined,
secretValuesPrinted: false,
runtimeNamespace: scoped.spec.runtimeNamespace,
localPostgresExpectedAbsent: nodeRuntimeLocalPostgresExpectedAbsent(scoped.spec),
@@ -58,9 +61,9 @@ export function nodeRuntimeControlPlanePlan(scoped: ReturnType<typeof parseNodeS
next: {
infraStatus: `bun scripts/cli.ts hwlab nodes control-plane infra status --node ${scoped.node} --lane ${scoped.lane}`,
status: `bun scripts/cli.ts hwlab nodes control-plane status --node ${scoped.node} --lane ${scoped.lane}`,
platformDbStatus: scoped.spec.externalPostgres === undefined
platformDbStatus: activeExternalPostgres === undefined
? null
: `bun scripts/cli.ts platform-db postgres status --config ${scoped.spec.externalPostgres.configRef}`,
: `bun scripts/cli.ts platform-db postgres status --config ${activeExternalPostgres.configRef}`,
publicExposure: scoped.spec.publicExposure === null ? null : `bun scripts/cli.ts hwlab nodes control-plane public-exposure --node ${scoped.node} --lane ${scoped.lane} --confirm`,
},
};
+6 -3
View File
@@ -31,6 +31,7 @@ import type { RenderedCliResult } from "../output";
import { NODE_RUNTIME_CICD_WAIT_WARNING_SECONDS } from "./entry";
import { publicExposureSummary } from "./public-exposure";
import { assertNodeId, positiveIntegerOption, requiredOption } from "./utils";
import { hwlabRuntimeActiveExternalPostgres } from "../hwlab-node-lanes";
export function parseNodeScopedDelegatedOptions(domain: DelegatedNodeDomain, args: string[]): {
domain: DelegatedNodeDomain;
@@ -76,10 +77,11 @@ export function parseNodeScopedDelegatedOptions(domain: DelegatedNodeDomain, arg
}
export function nodeRuntimeLocalPostgresExpectedAbsent(spec: HwlabRuntimeLaneSpec): boolean {
return spec.externalPostgres !== undefined || spec.runtimeStore?.postgres?.mode === "platform-service";
return spec.runtimeStore?.postgres?.mode === "platform-service";
}
export function nodeRuntimeExpected(spec: HwlabRuntimeLaneSpec): Record<string, unknown> {
const activeExternalPostgres = hwlabRuntimeActiveExternalPostgres(spec);
return {
configPath: hwlabRuntimeLaneConfigPath(),
node: spec.nodeId,
@@ -151,6 +153,7 @@ export function nodeRuntimeExpected(spec: HwlabRuntimeLaneSpec): Record<string,
observability: spec.observability,
runtimeImageRewrites: spec.runtimeImageRewrites,
externalPostgres: spec.externalPostgres === undefined ? null : {
active: activeExternalPostgres !== undefined,
provider: spec.externalPostgres.provider,
configRef: spec.externalPostgres.configRef,
serviceName: spec.externalPostgres.serviceName,
@@ -178,8 +181,8 @@ export function nodeRuntimeExpected(spec: HwlabRuntimeLaneSpec): Record<string,
valuesPrinted: false,
},
localPostgres: {
shouldRender: spec.externalPostgres === undefined,
expectedAbsent: spec.externalPostgres !== undefined,
shouldRender: spec.runtimeStore?.postgres?.mode !== "platform-service",
expectedAbsent: spec.runtimeStore?.postgres?.mode === "platform-service",
},
};
}
+3 -2
View File
@@ -34,6 +34,7 @@ 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 { hwlabRuntimeActiveExternalPostgres } from "../hwlab-node-lanes";
export function isSafeWebProbeScriptRunDir(value: string | null): value is string {
return typeof value === "string"
@@ -191,10 +192,10 @@ export function parseSecretOptions(args: string[]): NodeSecretOptions {
export function runtimeSecretSpec(input: { node: string; lane: string }): RuntimeSecretSpec {
const namespace = `hwlab-${input.lane}`;
const runtimeLaneSpec = isHwlabRuntimeLane(input.lane) ? hwlabRuntimeLaneSpecForNode(input.lane, input.node) : undefined;
const externalPostgres = runtimeLaneSpec?.externalPostgres;
const externalPostgres = runtimeLaneSpec === undefined ? undefined : hwlabRuntimeActiveExternalPostgres(runtimeLaneSpec);
const postgresStore = runtimeLaneSpec?.runtimeStore?.postgres;
const bootstrapAdmin = runtimeLaneSpec?.bootstrapAdmin;
const platformDb = externalPostgres !== undefined || postgresStore?.mode === "platform-service";
const platformDb = postgresStore?.mode === "platform-service";
const localPostgresService = postgresStore?.serviceName ?? `${namespace}-postgres`;
const platformPostgresService = externalPostgres?.serviceName ?? postgresStore?.serviceName ?? "g14-platform-postgres";
const platformPostgresRuntimeAccess = externalPostgres?.runtimeAccess;
+3 -1
View File
@@ -38,6 +38,7 @@ import { compactNodeRuntimeGitMirrorStatus, nodeRuntimeGitMirrorStatus } from ".
import { keyValueLinesFromText, numericField, optionValue, record, shellQuote } from "./utils";
import { externalPostgresBridgeStatus, externalPostgresSecretStatus, getNodeRuntimePipelineRun, isLocalPostgresObject, nodeRuntimeRenderOverlay } from "./web-probe";
import { webObserveShort, webObserveText } from "./web-probe-observe";
import { hwlabRuntimeActiveExternalPostgres } from "../hwlab-node-lanes";
export function nodeRuntimeGitMirrorJobName(mirror: NodeRuntimeGitMirrorTargetSpec, action: "sync" | "flush"): string {
const prefix = action === "sync" ? mirror.syncJobPrefix : mirror.flushJobPrefix;
@@ -178,11 +179,12 @@ export function nodeRuntimeControlPlaneStatus(scoped: ReturnType<typeof parseNod
const publicProbes = nodeRuntimePublicProbeStatus(spec);
const gitMirror = nodeRuntimeGitMirrorStatus(scoped);
const gitMirrorCompact = compactNodeRuntimeGitMirrorStatus(gitMirror);
const activeExternalPostgres = hwlabRuntimeActiveExternalPostgres(spec);
const controlPlaneReady = serviceAccount.exitCode === 0 && pipeline.exitCode === 0 && argo.exitCode === 0;
const workloadsReady = workloadReadiness.length > 0 && workloadReadiness.every((item) => item.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 runtimeReady = namespaceExists && localPostgresReady && workloadsReady && (activeExternalPostgres === 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"
+12 -10
View File
@@ -37,10 +37,12 @@ import { compactRuntimeCommand, runNodeHostScript } from "./runtime-common";
import { assertLane, assertNodeId, keyValueLinesFromText, numericField, optionValue, optionalStringValue, positiveIntegerOption, positiveIntegerValue, record, requiredOption, shellQuote, statusText, stringValue, stripOptions } from "./utils";
import { discoverWebObserveIndexEntry, readWebObserveIndexEntry } from "./web-observe-render";
import { assertKnownOptions, nodeWebProbeAutoCommandTimeoutSeconds, nodeWebProbeDefaultUrl, normalizeNodeWebProbeObserveArgs, parseNodeWebProbeObserveOptions, parseNodeWebProbeSentinelOptions, parseWebProbeBrowserProxyMode } from "./web-probe-observe";
import { hwlabRuntimeActiveExternalPostgres } from "../hwlab-node-lanes";
export function nodeRuntimeRenderOverlay(spec: HwlabRuntimeLaneSpec): Record<string, unknown> {
const gitSshProxy = httpProxyEndpoint(spec.networkProfile.proxy.http);
const gitMirror = nodeRuntimeGitMirrorTarget(spec);
const activeExternalPostgres = hwlabRuntimeActiveExternalPostgres(spec);
const renderGitMirror = {
...gitMirror,
egressProxy: gitMirror.egressProxy.mode === "direct" ? {
@@ -109,13 +111,13 @@ export function nodeRuntimeRenderOverlay(spec: HwlabRuntimeLaneSpec): Record<str
webProxy: spec.publicExposure.webProxy,
apiProxy: spec.publicExposure.apiProxy,
},
externalPostgres: spec.externalPostgres === undefined ? undefined : {
externalPostgres: activeExternalPostgres === undefined ? undefined : {
enabled: true,
serviceName: spec.externalPostgres.serviceName,
endpointAddress: spec.externalPostgres.endpointAddress,
port: spec.externalPostgres.port,
runtimeAccess: spec.externalPostgres.runtimeAccess ?? null,
sslmode: spec.externalPostgres.sslmode,
serviceName: activeExternalPostgres.serviceName,
endpointAddress: activeExternalPostgres.endpointAddress,
port: activeExternalPostgres.port,
runtimeAccess: activeExternalPostgres.runtimeAccess ?? null,
sslmode: activeExternalPostgres.sslmode,
},
};
}
@@ -460,7 +462,7 @@ export function sleepSync(ms: number): void {
}
export function syncNodeExternalPostgresSecrets(spec: HwlabRuntimeLaneSpec, dryRun: boolean, timeoutSeconds: number): Record<string, unknown> | null {
const pg = spec.externalPostgres;
const pg = hwlabRuntimeActiveExternalPostgres(spec);
if (pg === undefined) return null;
const secretRoot = externalPostgresSecretSourceRoot(spec);
const cloudApi = readSecretSourceValue(secretRoot, pg.cloudApi.sourceRef, pg.cloudApi.envKey);
@@ -905,7 +907,7 @@ export function ensureNodeBaseImage(spec: HwlabRuntimeLaneSpec, dryRun: boolean,
}
export function externalPostgresSecretSetupScript(spec: HwlabRuntimeLaneSpec, dryRun: boolean): string {
const pg = spec.externalPostgres;
const pg = hwlabRuntimeActiveExternalPostgres(spec);
if (pg === undefined) return "true";
const authnKey = pg.openfga.authnKey ?? "authn-preshared-key";
return [
@@ -1068,7 +1070,7 @@ export function isLocalPostgresObject(name: string, spec: HwlabRuntimeLaneSpec):
}
export function externalPostgresBridgeStatus(spec: HwlabRuntimeLaneSpec, namespaceExists: boolean): Record<string, unknown> {
const pg = spec.externalPostgres;
const pg = hwlabRuntimeActiveExternalPostgres(spec);
if (pg === undefined) return { required: false, ready: true };
if (!namespaceExists) return { required: true, ready: false, degradedReason: "runtime-namespace-missing" };
const runtimeAccess = pg.runtimeAccess ?? { endpointAddress: pg.endpointAddress, port: pg.port };
@@ -1113,7 +1115,7 @@ export function externalPostgresBridgeStatus(spec: HwlabRuntimeLaneSpec, namespa
}
export function externalPostgresSecretStatus(spec: HwlabRuntimeLaneSpec, namespaceExists: boolean): Record<string, unknown> {
const pg = spec.externalPostgres;
const pg = hwlabRuntimeActiveExternalPostgres(spec);
if (pg === undefined) return { required: false, ready: true };
if (!namespaceExists) return { required: true, ready: false, degradedReason: "runtime-namespace-missing" };
const cloudApi = secretKeyStatus(spec, pg.cloudApi.secretName, pg.cloudApi.secretKey);