Files
pikasTech-HWLAB/internal/cloud/db-contract.mjs
T
Lyon 357a68bb95 fix: layer dev DB readiness evidence
Refs #49

Merged by commander after reviewing Code Queue task codex_1779422762669_1. Adds redacted DB readiness layering and refreshes DEV endpoint/report evidence. Follow-up: deploy/verify the new readiness probe in DEV via standard image CI/CD.
2026-05-22 12:27:01 +08:00

415 lines
12 KiB
JavaScript

import net from "node:net";
import { ENVIRONMENT_DEV } from "../protocol/index.mjs";
export const DEV_DB_ENV_CONTRACT = Object.freeze({
contractVersion: "v1",
environment: ENVIRONMENT_DEV,
requiredEnv: Object.freeze([
"HWLAB_CLOUD_DB_URL",
"HWLAB_CLOUD_DB_SSL_MODE"
]),
secretRefs: Object.freeze([
Object.freeze({
env: "HWLAB_CLOUD_DB_URL",
secretName: "hwlab-cloud-api-dev-db",
secretKey: "database-url"
})
]),
nonSecretDefaults: Object.freeze({
HWLAB_CLOUD_DB_SSL_MODE: "require"
}),
forbidden: Object.freeze({
prodAllowed: false,
secretPlaintextAllowed: false,
liveDbEvidenceFromFixtures: false
})
});
const secretEnvNames = new Set(DEV_DB_ENV_CONTRACT.secretRefs.map((item) => item.env));
const supportedDbProtocols = new Set(["postgres:", "postgresql:"]);
const defaultProbeTimeoutMs = 1200;
export function buildDbHealthContract(env = process.env) {
const fields = DEV_DB_ENV_CONTRACT.requiredEnv.map((name) => {
const present = hasEnvValue(env, name);
return {
name,
present,
redacted: secretEnvNames.has(name),
source: secretEnvNames.has(name) ? "k8s-secret-ref" : "runtime-env",
required: true
};
});
const missingEnv = fields.filter((field) => !field.present).map((field) => field.name);
const configReady = missingEnv.length === 0;
const connection = buildNotAttemptedConnection(configReady, missingEnv);
const connected = false;
const connectionChecked = false;
const status = configReady ? "degraded" : "blocked";
return {
contractVersion: DEV_DB_ENV_CONTRACT.contractVersion,
environment: DEV_DB_ENV_CONTRACT.environment,
connected,
liveConnected: connected,
connectionChecked,
connectionAttempted: connection.attempted,
connectionResult: connection.result,
configReady,
ready: connected,
status,
mode: configReady ? "configured_without_live_connection_attempt" : "not_configured",
fields,
missingEnv,
secretRefs: DEV_DB_ENV_CONTRACT.secretRefs.map(({ env: envName, secretName, secretKey }) => ({
env: envName,
secretName,
secretKey,
present: hasEnvValue(env, envName),
envInjected: hasEnvValue(env, envName),
secretPresent: "not_observed_by_runtime",
secretKeyPresent: "not_observed_by_runtime",
redacted: true
})),
connection,
safety: {
devOnly: true,
prodAllowed: DEV_DB_ENV_CONTRACT.forbidden.prodAllowed,
secretsRead: false,
valuesRedacted: true,
liveDbEvidence: false
},
blocker: configReady ? "live DB connection has not been attempted by this health contract" : missingEnvBlocker(missingEnv),
evidence: configReady ? "env_presence_only_no_live_db" : "env_contract_blocked"
};
}
export async function buildDbRuntimeReadiness(env = process.env, options = {}) {
const base = buildDbHealthContract(env);
if (!base.configReady) {
return base;
}
if (probeDisabled(env, options)) {
return {
...base,
blocker: "live DB probe is disabled for this runtime",
evidence: "env_presence_only_probe_disabled"
};
}
const connection = await probeDbConnection(env, options);
return applyConnectionResult(base, connection);
}
export function buildDbEnvManifestPlaceholder() {
return {
requiredEnv: [...DEV_DB_ENV_CONTRACT.requiredEnv],
secretRefs: DEV_DB_ENV_CONTRACT.secretRefs.map((item) => ({ ...item })),
nonSecretDefaults: { ...DEV_DB_ENV_CONTRACT.nonSecretDefaults },
fixtureEvidence: {
liveDbEvidence: false,
summary: "This manifest contract records only env and Secret reference names. It is not live DB evidence."
}
};
}
export function summarizeDbContract(db = buildDbHealthContract()) {
return {
status: db.status,
ready: db.ready,
configReady: db.configReady,
connected: db.connected,
liveConnected: db.liveConnected ?? db.connected,
connectionChecked: db.connectionChecked,
connectionAttempted: db.connectionAttempted ?? db.connectionChecked,
connectionResult: db.connectionResult ?? db.connection?.result ?? "unknown",
requiredEnv: db.fields.map((field) => ({
name: field.name,
present: field.present,
redacted: field.redacted,
source: field.source
})),
missingEnv: [...db.missingEnv],
secretRefs: db.secretRefs.map(
({
env: envName,
secretName,
secretKey,
present,
envInjected,
secretPresent,
secretKeyPresent,
redacted
}) => ({
env: envName,
secretName,
secretKey,
present,
envInjected: envInjected ?? present,
secretPresent: secretPresent ?? "not_observed",
secretKeyPresent: secretKeyPresent ?? "not_observed",
redacted
})
),
connection: summarizeConnection(db.connection),
blocker: db.blocker ?? null,
liveDbEvidence: db.safety.liveDbEvidence,
fixtureEvidence: false
};
}
function hasEnvValue(env, name) {
return typeof env?.[name] === "string" && env[name].trim().length > 0;
}
function probeDisabled(env, options) {
return options.probe === false || env?.HWLAB_CLOUD_DB_PROBE_DISABLED === "1";
}
function buildNotAttemptedConnection(configReady, missingEnv) {
return {
attempted: false,
networkAttempted: false,
result: configReady ? "not_attempted" : "not_attempted_missing_env",
classification: configReady ? "configured_no_live_attempt" : "missing_runtime_env",
probeType: "tcp-connect",
endpointRedacted: true,
valueRedacted: true,
timeoutMs: null,
durationMs: 0,
errorCode: null,
missingEnv: [...missingEnv]
};
}
async function probeDbConnection(env, options) {
const timeoutMs = normalizeTimeoutMs(options.timeoutMs ?? env?.HWLAB_CLOUD_DB_PROBE_TIMEOUT_MS);
const parsed = parseDbTarget(env?.HWLAB_CLOUD_DB_URL);
if (!parsed.ok) {
return {
attempted: true,
networkAttempted: false,
result: parsed.result,
classification: parsed.classification,
probeType: "tcp-connect",
endpointRedacted: true,
valueRedacted: true,
timeoutMs,
durationMs: 0,
errorCode: parsed.errorCode,
missingEnv: []
};
}
return tcpConnect({
host: parsed.host,
port: parsed.port,
timeoutMs
});
}
function parseDbTarget(rawUrl) {
try {
const url = new URL(rawUrl);
if (!supportedDbProtocols.has(url.protocol)) {
return {
ok: false,
result: "unsupported_protocol",
classification: "db_url_unsupported_protocol",
errorCode: "UNSUPPORTED_PROTOCOL"
};
}
if (!url.hostname) {
return {
ok: false,
result: "invalid_url",
classification: "db_url_missing_host",
errorCode: "MISSING_HOST"
};
}
const port = url.port ? Number.parseInt(url.port, 10) : 5432;
if (!Number.isInteger(port) || port <= 0 || port > 65535) {
return {
ok: false,
result: "invalid_url",
classification: "db_url_invalid_port",
errorCode: "INVALID_PORT"
};
}
return {
ok: true,
host: url.hostname,
port
};
} catch {
return {
ok: false,
result: "invalid_url",
classification: "db_url_parse_error",
errorCode: "INVALID_URL"
};
}
}
function tcpConnect({ host, port, timeoutMs }) {
return new Promise((resolve) => {
const startedAt = Date.now();
const socket = net.connect({ host, port, timeout: timeoutMs });
let settled = false;
function finish(result) {
if (settled) return;
settled = true;
socket.destroy();
resolve({
attempted: true,
networkAttempted: true,
probeType: "tcp-connect",
endpointRedacted: true,
valueRedacted: true,
timeoutMs,
durationMs: Date.now() - startedAt,
missingEnv: [],
...result
});
}
socket.on("connect", () => {
finish({
result: "connected",
classification: "tcp_connected",
errorCode: null
});
});
socket.on("timeout", () => {
finish({
result: "timeout",
classification: "tcp_timeout",
errorCode: "ETIMEDOUT"
});
});
socket.on("error", (error) => {
finish(classifySocketError(error));
});
});
}
function classifySocketError(error) {
const code = typeof error?.code === "string" ? error.code : "UNKNOWN";
if (code === "ECONNREFUSED") {
return {
result: "refused",
classification: "tcp_refused",
errorCode: code
};
}
if (code === "ENOTFOUND" || code === "EAI_AGAIN") {
return {
result: "dns_error",
classification: "dns_resolution_failed",
errorCode: code
};
}
if (code === "ETIMEDOUT") {
return {
result: "timeout",
classification: "tcp_timeout",
errorCode: code
};
}
if (code === "EHOSTUNREACH" || code === "ENETUNREACH") {
return {
result: "network_unreachable",
classification: "network_unreachable",
errorCode: code
};
}
return {
result: "error",
classification: "tcp_error",
errorCode: code
};
}
function applyConnectionResult(base, connection) {
const liveConnected = connection.result === "connected";
return {
...base,
connected: liveConnected,
liveConnected,
connectionChecked: connection.attempted,
connectionAttempted: connection.attempted,
connectionResult: connection.result,
ready: liveConnected,
status: liveConnected ? "ready" : "degraded",
mode: liveConnected ? "live_connection_ready" : "live_connection_blocked",
connection,
safety: {
...base.safety,
liveDbEvidence: connection.attempted,
liveDbConnectedEvidence: liveConnected
},
blocker: liveConnected ? null : blockerForConnection(connection),
evidence: liveConnected ? "live_db_tcp_connection_ready" : "live_db_tcp_connection_blocked"
};
}
function blockerForConnection(connection) {
if (connection.result === "invalid_url") {
return "DB URL is injected but cannot be parsed as a supported postgres URL";
}
if (connection.result === "unsupported_protocol") {
return "DB URL is injected but does not use a supported postgres protocol";
}
if (connection.result === "refused") {
return "DB endpoint refused a TCP connection from the cloud-api runtime";
}
if (connection.result === "timeout") {
return "DB endpoint TCP connection timed out from the cloud-api runtime";
}
if (connection.result === "dns_error") {
return "DB endpoint DNS resolution failed from the cloud-api runtime";
}
if (connection.result === "network_unreachable") {
return "DB endpoint network is unreachable from the cloud-api runtime";
}
return "DB endpoint TCP connection did not complete from the cloud-api runtime";
}
function missingEnvBlocker(missingEnv) {
return `DB runtime config is missing ${missingEnv.join(", ")}`;
}
function normalizeTimeoutMs(value) {
const parsed = Number.parseInt(String(value ?? ""), 10);
if (!Number.isInteger(parsed) || parsed <= 0) {
return defaultProbeTimeoutMs;
}
return Math.min(parsed, 5000);
}
function summarizeConnection(connection) {
if (!connection) {
return {
attempted: false,
result: "unknown",
endpointRedacted: true,
valueRedacted: true
};
}
return {
attempted: Boolean(connection.attempted),
networkAttempted: Boolean(connection.networkAttempted),
result: connection.result,
classification: connection.classification,
probeType: connection.probeType,
endpointRedacted: true,
valueRedacted: true,
timeoutMs: connection.timeoutMs,
durationMs: connection.durationMs,
errorCode: connection.errorCode ?? null
};
}