fix: disable dev cloud api db ssl mode

This commit is contained in:
Code Queue Review
2026-05-23 02:25:17 +00:00
parent 0a7311c3c2
commit 6de765c9dc
17 changed files with 231 additions and 28 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ for future compatibility, but PROD deployment is not part of MVP acceptance.
- `hwlab-cloud-api` declares the DEV DB env contract with
`HWLAB_CLOUD_DB_URL` from Secret reference
`hwlab-cloud-api-dev-db/database-url` and non-secret
`HWLAB_CLOUD_DB_SSL_MODE=require`. Runtime DB readiness dials the redacted
`HWLAB_CLOUD_DB_SSL_MODE=disable`. Runtime DB readiness dials the redacted
host parsed from that Secret URL. `cloud-api-db` is only an optional desired
alias until Service plus Endpoint/EndpointSlice ownership and rollout/apply
contract exist; see
+1 -1
View File
@@ -194,7 +194,7 @@
"HWLAB_IMAGE_TAG": "7de6edd",
"HWLAB_RUNTIME_SUBSTITUTE_FORBIDDEN": "unidesk-backend,provider-gateway,microservice-proxy",
"HWLAB_CLOUD_DB_URL": "secretRef:hwlab-cloud-api-dev-db/database-url",
"HWLAB_CLOUD_DB_SSL_MODE": "require",
"HWLAB_CLOUD_DB_SSL_MODE": "disable",
"HWLAB_CLOUD_DB_CONTRACT": "dev-redacted-presence-only",
"HWLAB_CLOUD_RUNTIME_ADAPTER": "postgres",
"HWLAB_CLOUD_RUNTIME_DURABLE": "true",
+1 -1
View File
@@ -365,7 +365,7 @@
},
"HWLAB_CLOUD_DB_SSL_MODE": {
"type": "string",
"const": "require"
"const": "disable"
},
"HWLAB_CLOUD_DB_CONTRACT": {
"type": "string",
+1 -1
View File
@@ -75,7 +75,7 @@
},
{
"name": "HWLAB_CLOUD_DB_SSL_MODE",
"value": "require"
"value": "disable"
},
{
"name": "HWLAB_CLOUD_DB_CONTRACT",
+1 -1
View File
@@ -12,7 +12,7 @@
"data": {
"endpoint": "http://74.48.78.17:16667",
"cloud-api": "GET /health/live via hwlab-edge-proxy then hwlab-cloud-api:6667",
"cloud-api-db": "DEV DB config gate requires HWLAB_CLOUD_DB_URL from Secret hwlab-cloud-api-dev-db/database-url and HWLAB_CLOUD_DB_SSL_MODE=require. Runtime readiness dials the redacted host parsed from the Secret URL and reports endpointSource=secret-url-host. cloud-api-db is an optional desired alias only until this repo owns Service plus Endpoint or EndpointSlice manifests and rollout/apply contract; health reports env presence, env injection, redacted connection attempt/result, and liveConnected without exposing secret values",
"cloud-api-db": "DEV DB config gate requires HWLAB_CLOUD_DB_URL from Secret hwlab-cloud-api-dev-db/database-url and HWLAB_CLOUD_DB_SSL_MODE=disable. Runtime readiness dials the redacted host parsed from the Secret URL and reports endpointSource=secret-url-host. cloud-api-db is an optional desired alias only until this repo owns Service plus Endpoint or EndpointSlice manifests and rollout/apply contract; health reports env presence, env injection, redacted connection attempt/result, and liveConnected without exposing secret values",
"cloud-web": "GET /health/live on hwlab-cloud-web:8080; consumes cloud-api only",
"agent": "hwlab-agent-mgr readiness gates worker template creation; hwlab-agent-worker is suspended until a session is scheduled",
"sim": "hwlab-gateway-simu:7101 and hwlab-box-simu:7201 expose /health/live for DEV smoke",
+1 -1
View File
@@ -289,7 +289,7 @@ unchanged.
`hwlab-cloud-api` has a DEV DB env contract placeholder:
`HWLAB_CLOUD_DB_URL` must come from Secret reference
`hwlab-cloud-api-dev-db/database-url`, and
`HWLAB_CLOUD_DB_SSL_MODE=require`. Runtime readiness dials the redacted host
`HWLAB_CLOUD_DB_SSL_MODE=disable`. Runtime readiness dials the redacted host
parsed from that Secret URL. `cloud-api-db` remains an optional desired alias,
not a readiness gate or TCP probe target, until this repo owns its
Service/Endpoint rollout contract. The local health gate reports DB runtime
+1 -1
View File
@@ -35,7 +35,7 @@ Required DEV DB config names:
- `HWLAB_CLOUD_DB_URL` from Secret reference
`hwlab-cloud-api-dev-db/database-url`.
- `HWLAB_CLOUD_DB_SSL_MODE=require`.
- `HWLAB_CLOUD_DB_SSL_MODE=disable`.
- Runtime TCP dial target: the redacted host parsed from
`HWLAB_CLOUD_DB_URL`, reported as `endpointSource="secret-url-host"`.
+1 -1
View File
@@ -70,7 +70,7 @@ The preflight checks:
- `hwlab-cloud-api` declares the DEV DB env contract:
`HWLAB_CLOUD_DB_URL` from Secret reference
`hwlab-cloud-api-dev-db/database-url` and
`HWLAB_CLOUD_DB_SSL_MODE=require`. Runtime readiness dials the redacted host
`HWLAB_CLOUD_DB_SSL_MODE=disable`. Runtime readiness dials the redacted host
parsed from that Secret URL. `cloud-api-db` is an optional desired alias only
until this repo owns Service plus Endpoint or EndpointSlice manifests and
rollout/apply contract.
+1 -1
View File
@@ -40,7 +40,7 @@ export const DEV_DB_ENV_CONTRACT = Object.freeze({
"schema"
]),
nonSecretDefaults: Object.freeze({
HWLAB_CLOUD_DB_SSL_MODE: "require"
HWLAB_CLOUD_DB_SSL_MODE: "disable"
}),
forbidden: Object.freeze({
prodAllowed: false,
+4 -4
View File
@@ -122,7 +122,7 @@ test("cloud api health reports DB env presence and live connection classificatio
await new Promise((resolve) => fakeDb.listen(0, "127.0.0.1", resolve));
const dbPort = fakeDb.address().port;
process.env.HWLAB_CLOUD_DB_URL = `postgres://hwlab_test@127.0.0.1:${dbPort}/hwlab`;
process.env.HWLAB_CLOUD_DB_SSL_MODE = "require";
process.env.HWLAB_CLOUD_DB_SSL_MODE = "disable";
delete process.env.HWLAB_CLOUD_DB_SERVICE_NAME;
delete process.env.HWLAB_CLOUD_DB_SERVICE_NAMESPACE;
delete process.env.HWLAB_CLOUD_DB_HOST;
@@ -213,7 +213,7 @@ test("cloud api health separates DB connected from durable runtime schema readin
await new Promise((resolve) => fakeDb.listen(0, "127.0.0.1", resolve));
const dbPort = fakeDb.address().port;
process.env.HWLAB_CLOUD_DB_URL = `postgres://hwlab_test@127.0.0.1:${dbPort}/hwlab`;
process.env.HWLAB_CLOUD_DB_SSL_MODE = "require";
process.env.HWLAB_CLOUD_DB_SSL_MODE = "disable";
const server = createCloudApiServer({
runtimeStore: {
@@ -302,7 +302,7 @@ test("cloud api health keeps DB live evidence separate when durable adapter quer
await new Promise((resolve) => fakeDb.listen(0, "127.0.0.1", resolve));
const dbPort = fakeDb.address().port;
process.env.HWLAB_CLOUD_DB_URL = `postgres://hwlab_test:password@127.0.0.1:${dbPort}/hwlab`;
process.env.HWLAB_CLOUD_DB_SSL_MODE = "require";
process.env.HWLAB_CLOUD_DB_SSL_MODE = "disable";
const server = createCloudApiServer({
runtimeStore: {
@@ -404,7 +404,7 @@ test("cloud api health does not treat DB env presence-only as live readiness", a
const originalSslMode = process.env.HWLAB_CLOUD_DB_SSL_MODE;
const originalProbeDisabled = process.env.HWLAB_CLOUD_DB_PROBE_DISABLED;
process.env.HWLAB_CLOUD_DB_URL = "postgres://user:password@db.example.invalid:5432/hwlab";
process.env.HWLAB_CLOUD_DB_SSL_MODE = "require";
process.env.HWLAB_CLOUD_DB_SSL_MODE = "disable";
delete process.env.HWLAB_CLOUD_DB_PROBE_DISABLED;
const server = createCloudApiServer();
+47 -1
View File
@@ -18,13 +18,17 @@ async function makeFixture({
deployEnvMirrors = true,
deploySkillsCommitId = null,
deployCodeAgentProviderEnv = true,
deployDbEnv = true,
deployDbSslMode = "disable",
workloadTag = commitId,
workloadEnvCommitId = commitId,
workloadEnvImageTag = commitId,
workloadEnvImage = null,
workloadEnvMirrors = true,
workloadSkillsCommitId = null,
workloadCodeAgentProviderEnv = true
workloadCodeAgentProviderEnv = true,
workloadDbEnv = true,
workloadDbSslMode = "disable"
} = {}) {
const root = await mkdtemp(path.join(os.tmpdir(), "hwlab-desired-state-"));
await mkdir(path.join(root, "deploy/k8s/base"), { recursive: true });
@@ -59,6 +63,28 @@ async function makeFixture({
}
]
: [];
const dbEnv =
serviceId === "hwlab-cloud-api" && deployDbEnv
? {
HWLAB_CLOUD_DB_URL: "secretRef:hwlab-cloud-api-dev-db/database-url",
HWLAB_CLOUD_DB_SSL_MODE: deployDbSslMode
}
: {};
const workloadDbEntries =
serviceId === "hwlab-cloud-api" && workloadDbEnv
? [
{
name: "HWLAB_CLOUD_DB_URL",
valueFrom: {
secretKeyRef: {
name: "hwlab-cloud-api-dev-db",
key: "database-url"
}
}
},
{ name: "HWLAB_CLOUD_DB_SSL_MODE", value: workloadDbSslMode }
]
: [];
const deploy = {
manifestVersion: "v1",
environment: "dev",
@@ -77,6 +103,7 @@ async function makeFixture({
HWLAB_IMAGE: deployEnvImage ?? image,
HWLAB_IMAGE_TAG: deployEnvImageTag,
...(deploySkillsCommitId ? { HWLAB_SKILLS_COMMIT_ID: deploySkillsCommitId } : {}),
...dbEnv,
...providerEnv
}
: {}
@@ -136,6 +163,7 @@ async function makeFixture({
{ name: "HWLAB_IMAGE", value: workloadEnvImage ?? image },
{ name: "HWLAB_IMAGE_TAG", value: workloadEnvImageTag },
...(workloadSkillsCommitId ? [{ name: "HWLAB_SKILLS_COMMIT_ID", value: workloadSkillsCommitId }] : []),
...workloadDbEntries,
...workloadProviderEnv
]
: []
@@ -174,12 +202,30 @@ test("passes when cloud-api preserves provider env and DEV egress contract", asy
const repoRoot = await makeFixture();
const plan = await buildDesiredStatePlan({ repoRoot });
assert.equal(plan.status, "pass");
assert.equal(plan.cloudApiDb.ready, true);
assert.equal(plan.cloudApiDb.sslMode.expected, "disable");
assert.equal(plan.cloudApiDb.sslMode.deployMatches, true);
assert.equal(plan.cloudApiDb.sslMode.workloadMatches, true);
assert.equal(plan.codeAgentProvider.ready, true);
assert.equal(plan.codeAgentProvider.secretRef.present, true);
assert.equal(plan.codeAgentProvider.egress.deployMatchesDevProxy, true);
assert.deepEqual(plan.diagnostics, []);
});
test("blocks when cloud-api DB SSL mode drifts back to require", async () => {
const repoRoot = await makeFixture({
deployDbSslMode: "require",
workloadDbSslMode: "require"
});
const plan = await buildDesiredStatePlan({ repoRoot });
assert.equal(plan.status, "blocked");
assert.equal(plan.cloudApiDb.ready, false);
assert.equal(plan.cloudApiDb.sslMode.expected, "disable");
assert.equal(plan.cloudApiDb.sslMode.deployMatches, false);
assert.equal(plan.cloudApiDb.sslMode.workloadMatches, false);
assert.ok(plan.diagnostics.some((diagnostic) => diagnostic.code === "cloud_api_db_contract_mismatch"));
});
test("blocks when cloud-web runtime identity mirrors are missing", async () => {
const repoRoot = await makeFixture({
serviceId: "hwlab-cloud-web",
+14 -1
View File
@@ -192,7 +192,13 @@ function validateSource(ctx, manifest) {
function validateCloudApiDbSource(ctx, env) {
const alias = DEV_DB_ENV_CONTRACT.dns;
expectEqual(ctx, env.HWLAB_CLOUD_DB_URL, "secretRef:hwlab-cloud-api-dev-db/database-url", "$.services.hwlab-cloud-api.env.HWLAB_CLOUD_DB_URL", "cloud API DB URL Secret reference");
expectEqual(ctx, env.HWLAB_CLOUD_DB_SSL_MODE, "require", "$.services.hwlab-cloud-api.env.HWLAB_CLOUD_DB_SSL_MODE", "cloud API DB SSL mode");
expectEqual(
ctx,
env.HWLAB_CLOUD_DB_SSL_MODE,
DEV_DB_ENV_CONTRACT.nonSecretDefaults.HWLAB_CLOUD_DB_SSL_MODE,
"$.services.hwlab-cloud-api.env.HWLAB_CLOUD_DB_SSL_MODE",
"cloud API DEV DB SSL mode"
);
expectEqual(ctx, env.HWLAB_CLOUD_DB_CONTRACT, "dev-redacted-presence-only", "$.services.hwlab-cloud-api.env.HWLAB_CLOUD_DB_CONTRACT", "cloud API DB redacted contract marker");
expectEqual(ctx, DEV_DB_ENV_CONTRACT.endpointAuthority.source, "secret-url-host", "internal/cloud/db-contract.mjs.endpointAuthority.source", "cloud API DB runtime authority source");
expectEqual(ctx, DEV_DB_ENV_CONTRACT.endpointAuthority.env, "HWLAB_CLOUD_DB_URL", "internal/cloud/db-contract.mjs.endpointAuthority.env", "cloud API DB runtime authority env");
@@ -343,6 +349,13 @@ function validateCloudApiDbOptionalAliasArtifacts(ctx, services, workloads) {
expectEqual(ctx, service, undefined, "deploy/k8s/base/services.yaml.cloud-api-db", "cloud-api-db remains optional until Service/Endpoint ownership and rollout contract exist");
const container = mapByServiceId(listItems(workloads)).get("hwlab-cloud-api")?.spec?.template?.spec?.containers?.[0];
const env = new Map((container?.env ?? []).map((entry) => [entry.name, entry.value]));
expectEqual(
ctx,
env.get("HWLAB_CLOUD_DB_SSL_MODE"),
DEV_DB_ENV_CONTRACT.nonSecretDefaults.HWLAB_CLOUD_DB_SSL_MODE,
"deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.HWLAB_CLOUD_DB_SSL_MODE",
"cloud API workload DEV DB SSL mode"
);
for (const name of [
"HWLAB_CLOUD_DB_SERVICE_NAME",
"HWLAB_CLOUD_DB_SERVICE_NAMESPACE",
+104
View File
@@ -8,6 +8,7 @@ import {
DEV_CODE_AGENT_PROVIDER_CONTRACT,
inspectCodeAgentProviderManifestRefs
} from "../../internal/cloud/code-agent-contract.mjs";
import { DEV_DB_ENV_CONTRACT } from "../../internal/cloud/db-contract.mjs";
const execFileAsync = promisify(execFile);
const defaultRepoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
@@ -329,6 +330,100 @@ function inspectCodeAgentProviderDesiredState(ctx, deployService, workloadRecord
return inspection;
}
function dbSecretRefPlaceholder() {
const ref = DEV_DB_ENV_CONTRACT.secretRefs[0];
return `secretRef:${ref.secretName}/${ref.secretKey}`;
}
function secretRefMatches(entry, ref) {
return (
entry?.valueFrom?.secretKeyRef?.name === ref.secretName &&
entry?.valueFrom?.secretKeyRef?.key === ref.secretKey
);
}
function inspectCloudApiDbDesiredState(ctx, deployService, workloadRecords) {
const workloadRecord = workloadRecords.find((workload) => workload.containerName === "hwlab-cloud-api") ?? workloadRecords[0] ?? null;
const deployEnv = deployService?.env ?? {};
const workloadEnv = workloadRecord?.env ?? {};
const secretRef = DEV_DB_ENV_CONTRACT.secretRefs[0];
const expectedSecretRef = dbSecretRefPlaceholder();
const expectedSslMode = DEV_DB_ENV_CONTRACT.nonSecretDefaults.HWLAB_CLOUD_DB_SSL_MODE;
const deploySecretMatches = deployEnv.HWLAB_CLOUD_DB_URL === expectedSecretRef;
const workloadSecretMatches = secretRefMatches(workloadEnv.HWLAB_CLOUD_DB_URL, secretRef);
const deploySslMatches = deployEnv.HWLAB_CLOUD_DB_SSL_MODE === expectedSslMode;
const workloadSslMatches = workloadEnv.HWLAB_CLOUD_DB_SSL_MODE === expectedSslMode;
const ready = deploySecretMatches && workloadSecretMatches && deploySslMatches && workloadSslMatches;
if (!ready) {
addDiagnostic(ctx, {
code: "cloud_api_db_contract_mismatch",
path: "deploy.cloudApiDb",
message: "hwlab-cloud-api desired state must preserve DEV DB Secret ref and non-SSL runtime mode",
expected: {
secretRef: `${secretRef.secretName}/${secretRef.secretKey}`,
sslMode: expectedSslMode
},
actual: {
deploySecretRef: deploySecretMatches ? "secretRef:declared" : "missing_or_mismatched",
workloadSecretRef: workloadSecretMatches ? "secretRef:declared" : "missing_or_mismatched",
deploySslMode: deployEnv.HWLAB_CLOUD_DB_SSL_MODE ?? null,
workloadSslMode: workloadEnv.HWLAB_CLOUD_DB_SSL_MODE ?? null
},
secretMaterialRead: false,
valuesRedacted: true
});
}
recordObservation(ctx, {
source: "deploy",
serviceId: "hwlab-cloud-api",
path: `${deployPath}.services.hwlab-cloud-api.env.HWLAB_CLOUD_DB_SSL_MODE`,
field: "HWLAB_CLOUD_DB_SSL_MODE",
kind: "runtime-config",
value: deployEnv.HWLAB_CLOUD_DB_SSL_MODE ?? null,
expected: expectedSslMode
});
recordObservation(ctx, {
source: "workload",
serviceId: "hwlab-cloud-api",
path: `${workloadsPath}.hwlab-cloud-api.env.HWLAB_CLOUD_DB_SSL_MODE`,
field: "HWLAB_CLOUD_DB_SSL_MODE",
kind: "runtime-config",
value: workloadEnv.HWLAB_CLOUD_DB_SSL_MODE ?? null,
expected: expectedSslMode
});
return {
contractVersion: DEV_DB_ENV_CONTRACT.contractVersion,
environment: DEV_DB_ENV_CONTRACT.environment,
status: ready ? "pass" : "blocked",
ready,
secretRef: {
env: secretRef.env,
secretName: secretRef.secretName,
secretKey: secretRef.secretKey,
deployPlaceholderPresent: deploySecretMatches,
workloadSecretRefPresent: workloadSecretMatches,
present: deploySecretMatches && workloadSecretMatches,
redacted: true
},
sslMode: {
env: "HWLAB_CLOUD_DB_SSL_MODE",
value: deployEnv.HWLAB_CLOUD_DB_SSL_MODE ?? null,
workloadValue: workloadEnv.HWLAB_CLOUD_DB_SSL_MODE ?? null,
expected: expectedSslMode,
deployMatches: deploySslMatches,
workloadMatches: workloadSslMatches,
secret: false
},
secretMaterialRead: false,
valuesRedacted: true,
liveDbEvidence: false,
fixtureEvidence: false
};
}
function indexWorkloads(workloads) {
const byService = new Map();
for (const item of listItems(workloads)) {
@@ -616,6 +711,7 @@ export async function buildDesiredStatePlan(options = {}) {
const services = [];
let codeAgentProvider = null;
let cloudApiDb = null;
for (const serviceId of serviceIds) {
const deployService = deployByService.get(serviceId);
const catalogService = catalogByService.get(serviceId);
@@ -774,6 +870,7 @@ export async function buildDesiredStatePlan(options = {}) {
services.push(service);
if (serviceId === "hwlab-cloud-api") {
cloudApiDb = inspectCloudApiDbDesiredState(ctx, deployService, workloadsForService);
codeAgentProvider = inspectCodeAgentProviderDesiredState(ctx, deployService, workloadsForService);
}
}
@@ -838,6 +935,13 @@ export async function buildDesiredStatePlan(options = {}) {
nonAuthoritativeEvidence: [artifactReportPath],
note: "This planner reviews source desired-state only. It does not prove image existence, registry reachability, a real DEV apply, or M3 DEV-LIVE hardware-loop evidence."
},
cloudApiDb: cloudApiDb ?? {
status: "blocked",
ready: false,
secretMaterialRead: false,
valuesRedacted: true,
liveDbEvidence: false
},
codeAgentProvider: codeAgentProvider ?? {
status: "blocked",
ready: false,
+23 -2
View File
@@ -1133,14 +1133,33 @@ async function checkCloudApiDb(deploy, workloads, services, blockers) {
const missingSecretRefs = requiredEnv
.filter((env) => env.secretRef && !env.secretRef.present)
.map((env) => `${env.secretRef.secretName}/${env.secretRef.secretKey}`);
const expectedSslMode = DEV_DB_ENV_CONTRACT.nonSecretDefaults.HWLAB_CLOUD_DB_SSL_MODE;
const sslModeMatches =
deployEnv.HWLAB_CLOUD_DB_SSL_MODE === expectedSslMode &&
workloadEnv.get("HWLAB_CLOUD_DB_SSL_MODE")?.value === expectedSslMode;
const mismatchedNonSecretEnv = sslModeMatches
? []
: [`HWLAB_CLOUD_DB_SSL_MODE must be ${expectedSslMode}`];
const dnsContract = inspectCloudApiDbOptionalAliasContract(deployEnv, workloadEnv, services);
const missingDnsContract = dnsContract.missing;
if (missingManifest.length > 0 || missingK8s.length > 0 || missingSecretRefs.length > 0 || missingDnsContract.length > 0) {
if (
missingManifest.length > 0 ||
missingK8s.length > 0 ||
missingSecretRefs.length > 0 ||
mismatchedNonSecretEnv.length > 0 ||
missingDnsContract.length > 0
) {
addBlocker(
blockers,
"runtime_blocker",
"cloud-api-db-config",
`cloud-api DEV DB env/authority contract is incomplete; missing ${[...missingManifest, ...missingK8s, ...missingSecretRefs, ...missingDnsContract].join(", ")}`
`cloud-api DEV DB env/authority contract is incomplete; missing ${[
...missingManifest,
...missingK8s,
...missingSecretRefs,
...mismatchedNonSecretEnv,
...missingDnsContract
].join(", ")}`
);
}
@@ -1148,6 +1167,7 @@ async function checkCloudApiDb(deploy, workloads, services, blockers) {
missingManifest.length === 0 &&
missingK8s.length === 0 &&
missingSecretRefs.length === 0 &&
mismatchedNonSecretEnv.length === 0 &&
missingDnsContract.length === 0;
const health = {
status: configReady ? "degraded" : "blocked",
@@ -1181,6 +1201,7 @@ async function checkCloudApiDb(deploy, workloads, services, blockers) {
missingManifest,
missingK8s,
missingSecretRefs,
mismatchedNonSecretEnv,
missingDnsContract,
dns: dnsContract,
runtimeHealth: summarizeDbContract(health),
+14 -4
View File
@@ -834,20 +834,21 @@ function validateCloudApiDbContract(reporter, deploy, workloads, services) {
...staticContract.missingDeployEnv,
...staticContract.missingK8sEnv,
...staticContract.missingSecretRefs,
...staticContract.mismatchedNonSecretEnv,
...staticContract.missingDnsContract
];
reporter.check(
"cloud-api-db-env-contract",
"db",
"blocked",
`cloud-api DEV DB manifest is missing ${missing.join(", ")}.`,
`cloud-api DEV DB manifest contract is blocked by ${missing.join(", ")}.`,
evidence
);
reporter.block({
type: "contract_blocker",
scope: "cloud-api-db-env-contract",
summary: `cloud-api DEV DB contract is incomplete; missing ${missing.join(", ")}.`,
nextTask: "Repair deploy/deploy.json and deploy/k8s/base/workloads.yaml so they expose the required DB env names and redacted Secret reference. Do not make cloud-api-db a hard readiness gate unless this repo also owns its Service/Endpoint rollout contract."
summary: `cloud-api DEV DB contract is incomplete; blocked by ${missing.join(", ")}.`,
nextTask: "Repair deploy/deploy.json and deploy/k8s/base/workloads.yaml so they expose the required DB env names, redacted Secret reference, and HWLAB_CLOUD_DB_SSL_MODE=disable. Do not make cloud-api-db a hard readiness gate unless this repo also owns its Service/Endpoint rollout contract."
});
}
@@ -873,7 +874,7 @@ function validateCloudApiDbContract(reporter, deploy, workloads, services) {
type: "runtime_blocker",
scope: "cloud-api-db-health-gate",
summary: `cloud-api DB runtime env is not ready; missing ${healthContract.missingEnv.join(", ")}.`,
nextTask: "Configure DEV hwlab-cloud-api with Secret hwlab-cloud-api-dev-db/database-url and HWLAB_CLOUD_DB_SSL_MODE=require, then rerun health/preflight without printing the secret value."
nextTask: "Configure DEV hwlab-cloud-api with Secret hwlab-cloud-api-dev-db/database-url and HWLAB_CLOUD_DB_SSL_MODE=disable, then rerun health/preflight without printing the secret value."
});
}
@@ -914,6 +915,13 @@ function inspectCloudApiDbStaticContract(deploy, workloads, services) {
const missingSecretRefs = fields
.filter((field) => field.secretRef && !field.secretRef.present)
.map((field) => `${field.secretRef.secretName}/${field.secretRef.secretKey}`);
const expectedSslMode = DEV_DB_ENV_CONTRACT.nonSecretDefaults.HWLAB_CLOUD_DB_SSL_MODE;
const sslModeMatches =
deployEnv.HWLAB_CLOUD_DB_SSL_MODE === expectedSslMode &&
workloadEnv.get("HWLAB_CLOUD_DB_SSL_MODE")?.value === expectedSslMode;
const mismatchedNonSecretEnv = sslModeMatches
? []
: [`HWLAB_CLOUD_DB_SSL_MODE must be ${expectedSslMode}`];
const dnsContract = inspectCloudApiDbOptionalAliasContract(deployEnv, workloadEnv, services);
return {
@@ -923,12 +931,14 @@ function inspectCloudApiDbStaticContract(deploy, workloads, services) {
missingDeployEnv.length === 0 &&
missingK8sEnv.length === 0 &&
missingSecretRefs.length === 0 &&
mismatchedNonSecretEnv.length === 0 &&
dnsContract.ready,
requiredEnv: fields,
dns: dnsContract,
missingDeployEnv,
missingK8sEnv,
missingSecretRefs,
mismatchedNonSecretEnv,
missingDnsContract: dnsContract.missing,
secretMaterialRead: false,
valuesRedacted: true,
+6 -6
View File
@@ -15,7 +15,7 @@ test("source check is non-secret and does not attempt live DB access", async ()
const report = await buildDevRuntimeMigrationReport(parseArgs(["--check"]), {
env: {
HWLAB_CLOUD_DB_URL: "postgresql://hwlab:secret-password@db.example.test:5432/hwlab",
HWLAB_CLOUD_DB_SSL_MODE: "require"
HWLAB_CLOUD_DB_SSL_MODE: "disable"
},
now: () => "2026-05-22T00:00:00.000Z"
});
@@ -38,7 +38,7 @@ test("apply mode refuses missing DEV confirmation before touching query client",
const report = await buildDevRuntimeMigrationReport(parseArgs(["--apply"]), {
env: {
HWLAB_CLOUD_DB_URL: "postgresql://hwlab:redacted@example.invalid:5432/hwlab",
HWLAB_CLOUD_DB_SSL_MODE: "require"
HWLAB_CLOUD_DB_SSL_MODE: "disable"
},
queryClient,
now: () => "2026-05-22T00:00:00.000Z"
@@ -56,7 +56,7 @@ test("live dry-run separates migration ledger blocker from auth and schema", asy
{
env: {
HWLAB_CLOUD_DB_URL: "postgresql://hwlab:redacted@example.invalid:5432/hwlab",
HWLAB_CLOUD_DB_SSL_MODE: "require"
HWLAB_CLOUD_DB_SSL_MODE: "disable"
},
queryClient: createFakeQueryClient({ migrationReady: false }),
now: () => "2026-05-22T00:00:00.000Z"
@@ -81,7 +81,7 @@ test("apply mode records ledger and verifies durable runtime readiness", async (
{
env: {
HWLAB_CLOUD_DB_URL: "postgresql://hwlab:redacted@example.invalid:5432/hwlab",
HWLAB_CLOUD_DB_SSL_MODE: "require"
HWLAB_CLOUD_DB_SSL_MODE: "disable"
},
queryClient,
now: () => "2026-05-22T00:00:00.000Z"
@@ -112,7 +112,7 @@ test("live dry-run closes pg pool after readiness verification", async () => {
{
env: {
HWLAB_CLOUD_DB_URL: "postgresql://hwlab:redacted@example.invalid:5432/hwlab",
HWLAB_CLOUD_DB_SSL_MODE: "require"
HWLAB_CLOUD_DB_SSL_MODE: "disable"
},
pgModuleLoader: async () => createFakePgModule({ backingClient, pools }),
now: () => "2026-05-22T00:00:00.000Z"
@@ -133,7 +133,7 @@ test("apply mode uses a dedicated pg client transaction", async () => {
{
env: {
HWLAB_CLOUD_DB_URL: "postgresql://hwlab:redacted@example.invalid:5432/hwlab",
HWLAB_CLOUD_DB_SSL_MODE: "require"
HWLAB_CLOUD_DB_SSL_MODE: "disable"
},
pgModuleLoader: async () => createFakePgModule({ backingClient, pools }),
now: () => "2026-05-22T00:00:00.000Z"
+10 -1
View File
@@ -170,10 +170,19 @@ assert.equal(cloudApi.env.HWLAB_COMMIT_ID, deployManifest.commitId, "cloud-api h
assert.equal(cloudApi.env.HWLAB_IMAGE, cloudApi.image, "cloud-api health image evidence");
assert.equal(cloudApi.env.HWLAB_IMAGE_TAG, deployManifest.commitId.slice(0, 7), "cloud-api health image tag evidence");
assert.equal(cloudApi.env.HWLAB_CLOUD_DB_URL, "secretRef:hwlab-cloud-api-dev-db/database-url", "cloud-api DB URL must be a Secret reference placeholder");
assert.equal(cloudApi.env.HWLAB_CLOUD_DB_SSL_MODE, "require", "cloud-api DB SSL mode");
assert.equal(
cloudApi.env.HWLAB_CLOUD_DB_SSL_MODE,
DEV_DB_ENV_CONTRACT.nonSecretDefaults.HWLAB_CLOUD_DB_SSL_MODE,
"cloud-api DEV DB SSL mode"
);
assert.equal(cloudApi.env.HWLAB_CLOUD_DB_CONTRACT, "dev-redacted-presence-only", "cloud-api DB contract marker");
assert.equal(cloudApi.env.HWLAB_CLOUD_RUNTIME_ADAPTER, "postgres", "cloud-api runtime durable adapter");
assert.equal(cloudApi.env.HWLAB_CLOUD_RUNTIME_DURABLE, "true", "cloud-api runtime durable flag");
assert.equal(
cloudApiWorkloadEnv.HWLAB_CLOUD_DB_SSL_MODE?.value,
DEV_DB_ENV_CONTRACT.nonSecretDefaults.HWLAB_CLOUD_DB_SSL_MODE,
"cloud-api workload DEV DB SSL mode"
);
assert.equal(cloudApiWorkloadEnv.HWLAB_CLOUD_RUNTIME_ADAPTER?.value, "postgres", "cloud-api workload runtime durable adapter");
assert.equal(cloudApiWorkloadEnv.HWLAB_CLOUD_RUNTIME_DURABLE?.value, "true", "cloud-api workload runtime durable flag");
assert.equal(cloudApi.env.HWLAB_CLOUD_DB_SERVICE_NAME, undefined, "cloud-api DB alias service name is optional");