fix: surface DB runtime readiness layers

This commit is contained in:
Code Queue Review
2026-05-23 06:08:35 +00:00
parent b3fe1d5ebe
commit 40984789ff
6 changed files with 237 additions and 86 deletions
+40 -40
View File
@@ -5,7 +5,7 @@
"profile": "dev",
"namespace": "hwlab-dev",
"endpoint": "http://74.48.78.17:16667",
"commitId": "42338c0",
"commitId": "b3fe1d5",
"artifactState": "contract-skeleton",
"publish": {
"ciPublished": false,
@@ -66,9 +66,9 @@
"services": [
{
"serviceId": "hwlab-cloud-api",
"commitId": "42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:42338c0",
"imageTag": "42338c0",
"commitId": "b3fe1d5",
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:b3fe1d5",
"imageTag": "b3fe1d5",
"digest": "not_published",
"publishState": "skeleton-only",
"profile": "dev",
@@ -82,9 +82,9 @@
},
{
"serviceId": "hwlab-cloud-web",
"commitId": "42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:42338c0",
"imageTag": "42338c0",
"commitId": "b3fe1d5",
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:b3fe1d5",
"imageTag": "b3fe1d5",
"digest": "not_published",
"publishState": "skeleton-only",
"profile": "dev",
@@ -98,9 +98,9 @@
},
{
"serviceId": "hwlab-agent-mgr",
"commitId": "42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-agent-mgr:42338c0",
"imageTag": "42338c0",
"commitId": "b3fe1d5",
"image": "127.0.0.1:5000/hwlab/hwlab-agent-mgr:b3fe1d5",
"imageTag": "b3fe1d5",
"digest": "not_published",
"publishState": "skeleton-only",
"profile": "dev",
@@ -114,9 +114,9 @@
},
{
"serviceId": "hwlab-agent-worker",
"commitId": "42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-agent-worker:42338c0",
"imageTag": "42338c0",
"commitId": "b3fe1d5",
"image": "127.0.0.1:5000/hwlab/hwlab-agent-worker:b3fe1d5",
"imageTag": "b3fe1d5",
"digest": "not_published",
"publishState": "skeleton-only",
"profile": "dev",
@@ -130,9 +130,9 @@
},
{
"serviceId": "hwlab-gateway",
"commitId": "42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-gateway:42338c0",
"imageTag": "42338c0",
"commitId": "b3fe1d5",
"image": "127.0.0.1:5000/hwlab/hwlab-gateway:b3fe1d5",
"imageTag": "b3fe1d5",
"digest": "not_published",
"publishState": "skeleton-only",
"profile": "dev",
@@ -146,9 +146,9 @@
},
{
"serviceId": "hwlab-gateway-simu",
"commitId": "42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-gateway-simu:42338c0",
"imageTag": "42338c0",
"commitId": "b3fe1d5",
"image": "127.0.0.1:5000/hwlab/hwlab-gateway-simu:b3fe1d5",
"imageTag": "b3fe1d5",
"digest": "not_published",
"publishState": "skeleton-only",
"profile": "dev",
@@ -162,9 +162,9 @@
},
{
"serviceId": "hwlab-box-simu",
"commitId": "42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-box-simu:42338c0",
"imageTag": "42338c0",
"commitId": "b3fe1d5",
"image": "127.0.0.1:5000/hwlab/hwlab-box-simu:b3fe1d5",
"imageTag": "b3fe1d5",
"digest": "not_published",
"publishState": "skeleton-only",
"profile": "dev",
@@ -178,9 +178,9 @@
},
{
"serviceId": "hwlab-patch-panel",
"commitId": "42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-patch-panel:42338c0",
"imageTag": "42338c0",
"commitId": "b3fe1d5",
"image": "127.0.0.1:5000/hwlab/hwlab-patch-panel:b3fe1d5",
"imageTag": "b3fe1d5",
"digest": "not_published",
"publishState": "skeleton-only",
"profile": "dev",
@@ -194,9 +194,9 @@
},
{
"serviceId": "hwlab-router",
"commitId": "42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-router:42338c0",
"imageTag": "42338c0",
"commitId": "b3fe1d5",
"image": "127.0.0.1:5000/hwlab/hwlab-router:b3fe1d5",
"imageTag": "b3fe1d5",
"digest": "not_published",
"publishState": "skeleton-only",
"profile": "dev",
@@ -210,9 +210,9 @@
},
{
"serviceId": "hwlab-tunnel-client",
"commitId": "42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-tunnel-client:42338c0",
"imageTag": "42338c0",
"commitId": "b3fe1d5",
"image": "127.0.0.1:5000/hwlab/hwlab-tunnel-client:b3fe1d5",
"imageTag": "b3fe1d5",
"digest": "not_published",
"publishState": "skeleton-only",
"profile": "dev",
@@ -226,9 +226,9 @@
},
{
"serviceId": "hwlab-edge-proxy",
"commitId": "42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-edge-proxy:42338c0",
"imageTag": "42338c0",
"commitId": "b3fe1d5",
"image": "127.0.0.1:5000/hwlab/hwlab-edge-proxy:b3fe1d5",
"imageTag": "b3fe1d5",
"digest": "not_published",
"publishState": "skeleton-only",
"profile": "dev",
@@ -242,9 +242,9 @@
},
{
"serviceId": "hwlab-cli",
"commitId": "42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-cli:42338c0",
"imageTag": "42338c0",
"commitId": "b3fe1d5",
"image": "127.0.0.1:5000/hwlab/hwlab-cli:b3fe1d5",
"imageTag": "b3fe1d5",
"digest": "not_published",
"publishState": "skeleton-only",
"profile": "dev",
@@ -258,9 +258,9 @@
},
{
"serviceId": "hwlab-agent-skills",
"commitId": "42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-agent-skills:42338c0",
"imageTag": "42338c0",
"commitId": "b3fe1d5",
"image": "127.0.0.1:5000/hwlab/hwlab-agent-skills:b3fe1d5",
"imageTag": "b3fe1d5",
"digest": "not_published",
"publishState": "skeleton-only",
"profile": "dev",
+21 -21
View File
@@ -1,7 +1,7 @@
{
"manifestVersion": "v1",
"environment": "dev",
"commitId": "42338c0",
"commitId": "b3fe1d5",
"namespace": "hwlab-dev",
"endpoint": "http://74.48.78.17:16667",
"health": {
@@ -181,7 +181,7 @@
"services": [
{
"serviceId": "hwlab-cloud-api",
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:b3fe1d5",
"namespace": "hwlab-dev",
"healthPath": "/health/live",
"profile": "dev",
@@ -189,9 +189,9 @@
"env": {
"HWLAB_ENVIRONMENT": "dev",
"HWLAB_PUBLIC_ENDPOINT": "http://74.48.78.17:16667",
"HWLAB_COMMIT_ID": "42338c0",
"HWLAB_IMAGE": "127.0.0.1:5000/hwlab/hwlab-cloud-api:42338c0",
"HWLAB_IMAGE_TAG": "42338c0",
"HWLAB_COMMIT_ID": "b3fe1d5",
"HWLAB_IMAGE": "127.0.0.1:5000/hwlab/hwlab-cloud-api:b3fe1d5",
"HWLAB_IMAGE_TAG": "b3fe1d5",
"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": "disable",
@@ -210,7 +210,7 @@
},
{
"serviceId": "hwlab-cloud-web",
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:b3fe1d5",
"namespace": "hwlab-dev",
"healthPath": "/health/live",
"profile": "dev",
@@ -218,14 +218,14 @@
"env": {
"HWLAB_ENVIRONMENT": "dev",
"HWLAB_API_BASE_URL": "http://hwlab-cloud-api.hwlab-dev.svc.cluster.local:6667",
"HWLAB_COMMIT_ID": "42338c0",
"HWLAB_IMAGE": "127.0.0.1:5000/hwlab/hwlab-cloud-web:42338c0",
"HWLAB_IMAGE_TAG": "42338c0"
"HWLAB_COMMIT_ID": "b3fe1d5",
"HWLAB_IMAGE": "127.0.0.1:5000/hwlab/hwlab-cloud-web:b3fe1d5",
"HWLAB_IMAGE_TAG": "b3fe1d5"
}
},
{
"serviceId": "hwlab-agent-mgr",
"image": "127.0.0.1:5000/hwlab/hwlab-agent-mgr:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-agent-mgr:b3fe1d5",
"namespace": "hwlab-dev",
"healthPath": "/health/live",
"profile": "dev",
@@ -237,7 +237,7 @@
},
{
"serviceId": "hwlab-agent-worker",
"image": "127.0.0.1:5000/hwlab/hwlab-agent-worker:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-agent-worker:b3fe1d5",
"namespace": "hwlab-dev",
"healthPath": "/health/live",
"profile": "dev",
@@ -249,7 +249,7 @@
},
{
"serviceId": "hwlab-gateway",
"image": "127.0.0.1:5000/hwlab/hwlab-gateway:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-gateway:b3fe1d5",
"namespace": "hwlab-dev",
"healthPath": "/health/live",
"profile": "dev",
@@ -261,7 +261,7 @@
},
{
"serviceId": "hwlab-gateway-simu",
"image": "127.0.0.1:5000/hwlab/hwlab-gateway-simu:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-gateway-simu:b3fe1d5",
"namespace": "hwlab-dev",
"healthPath": "/health/live",
"profile": "dev",
@@ -281,7 +281,7 @@
},
{
"serviceId": "hwlab-box-simu",
"image": "127.0.0.1:5000/hwlab/hwlab-box-simu:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-box-simu:b3fe1d5",
"namespace": "hwlab-dev",
"healthPath": "/health/live",
"profile": "dev",
@@ -300,7 +300,7 @@
},
{
"serviceId": "hwlab-patch-panel",
"image": "127.0.0.1:5000/hwlab/hwlab-patch-panel:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-patch-panel:b3fe1d5",
"namespace": "hwlab-dev",
"healthPath": "/health/live",
"profile": "dev",
@@ -321,7 +321,7 @@
},
{
"serviceId": "hwlab-router",
"image": "127.0.0.1:5000/hwlab/hwlab-router:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-router:b3fe1d5",
"namespace": "hwlab-dev",
"healthPath": "/health/live",
"profile": "dev",
@@ -333,7 +333,7 @@
},
{
"serviceId": "hwlab-tunnel-client",
"image": "127.0.0.1:5000/hwlab/hwlab-tunnel-client:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-tunnel-client:b3fe1d5",
"namespace": "hwlab-dev",
"healthPath": "/health/live",
"profile": "dev",
@@ -347,7 +347,7 @@
},
{
"serviceId": "hwlab-edge-proxy",
"image": "127.0.0.1:5000/hwlab/hwlab-edge-proxy:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-edge-proxy:b3fe1d5",
"namespace": "hwlab-dev",
"healthPath": "/health/live",
"profile": "dev",
@@ -359,7 +359,7 @@
},
{
"serviceId": "hwlab-cli",
"image": "127.0.0.1:5000/hwlab/hwlab-cli:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-cli:b3fe1d5",
"namespace": "hwlab-dev",
"healthPath": "/health/live",
"profile": "dev",
@@ -370,13 +370,13 @@
},
{
"serviceId": "hwlab-agent-skills",
"image": "127.0.0.1:5000/hwlab/hwlab-agent-skills:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-agent-skills:b3fe1d5",
"namespace": "hwlab-dev",
"healthPath": "/health/live",
"profile": "dev",
"replicas": 1,
"env": {
"HWLAB_SKILLS_COMMIT_ID": "42338c0"
"HWLAB_SKILLS_COMMIT_ID": "b3fe1d5"
}
}
],
+20 -20
View File
@@ -31,7 +31,7 @@
"containers": [
{
"name": "hwlab-cloud-api",
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:b3fe1d5",
"ports": [
{
"name": "http",
@@ -49,15 +49,15 @@
},
{
"name": "HWLAB_COMMIT_ID",
"value": "42338c0"
"value": "b3fe1d5"
},
{
"name": "HWLAB_IMAGE",
"value": "127.0.0.1:5000/hwlab/hwlab-cloud-api:42338c0"
"value": "127.0.0.1:5000/hwlab/hwlab-cloud-api:b3fe1d5"
},
{
"name": "HWLAB_IMAGE_TAG",
"value": "42338c0"
"value": "b3fe1d5"
},
{
"name": "HWLAB_RUNTIME_SUBSTITUTE_FORBIDDEN",
@@ -175,7 +175,7 @@
"containers": [
{
"name": "hwlab-cloud-web",
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:b3fe1d5",
"ports": [
{
"name": "http",
@@ -189,15 +189,15 @@
},
{
"name": "HWLAB_COMMIT_ID",
"value": "42338c0"
"value": "b3fe1d5"
},
{
"name": "HWLAB_IMAGE",
"value": "127.0.0.1:5000/hwlab/hwlab-cloud-web:42338c0"
"value": "127.0.0.1:5000/hwlab/hwlab-cloud-web:b3fe1d5"
},
{
"name": "HWLAB_IMAGE_TAG",
"value": "42338c0"
"value": "b3fe1d5"
}
],
"readinessProbe": {
@@ -247,7 +247,7 @@
"containers": [
{
"name": "hwlab-agent-mgr",
"image": "127.0.0.1:5000/hwlab/hwlab-agent-mgr:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-agent-mgr:b3fe1d5",
"ports": [
{
"name": "http",
@@ -303,7 +303,7 @@
"containers": [
{
"name": "hwlab-agent-worker",
"image": "127.0.0.1:5000/hwlab/hwlab-agent-worker:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-agent-worker:b3fe1d5",
"env": [
{
"name": "HWLAB_AGENT_SESSION_MODE",
@@ -346,7 +346,7 @@
"containers": [
{
"name": "hwlab-gateway",
"image": "127.0.0.1:5000/hwlab/hwlab-gateway:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-gateway:b3fe1d5",
"ports": [
{
"name": "http",
@@ -407,7 +407,7 @@
"containers": [
{
"name": "hwlab-gateway-simu",
"image": "127.0.0.1:5000/hwlab/hwlab-gateway-simu:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-gateway-simu:b3fe1d5",
"ports": [
{
"name": "http",
@@ -480,7 +480,7 @@
"containers": [
{
"name": "hwlab-box-simu",
"image": "127.0.0.1:5000/hwlab/hwlab-box-simu:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-box-simu:b3fe1d5",
"ports": [
{
"name": "http",
@@ -548,7 +548,7 @@
"containers": [
{
"name": "hwlab-patch-panel",
"image": "127.0.0.1:5000/hwlab/hwlab-patch-panel:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-patch-panel:b3fe1d5",
"ports": [
{
"name": "http",
@@ -612,7 +612,7 @@
"containers": [
{
"name": "hwlab-router",
"image": "127.0.0.1:5000/hwlab/hwlab-router:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-router:b3fe1d5",
"ports": [
{
"name": "http",
@@ -672,7 +672,7 @@
"containers": [
{
"name": "hwlab-tunnel-client",
"image": "127.0.0.1:5000/hwlab/hwlab-tunnel-client:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-tunnel-client:b3fe1d5",
"ports": [
{
"name": "http",
@@ -744,7 +744,7 @@
"containers": [
{
"name": "hwlab-edge-proxy",
"image": "127.0.0.1:5000/hwlab/hwlab-edge-proxy:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-edge-proxy:b3fe1d5",
"ports": [
{
"name": "http",
@@ -805,7 +805,7 @@
"containers": [
{
"name": "hwlab-cli",
"image": "127.0.0.1:5000/hwlab/hwlab-cli:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-cli:b3fe1d5",
"env": [
{
"name": "HWLAB_CLI_ENDPOINT",
@@ -847,7 +847,7 @@
"containers": [
{
"name": "hwlab-agent-skills",
"image": "127.0.0.1:5000/hwlab/hwlab-agent-skills:42338c0",
"image": "127.0.0.1:5000/hwlab/hwlab-agent-skills:b3fe1d5",
"ports": [
{
"name": "http",
@@ -857,7 +857,7 @@
"env": [
{
"name": "HWLAB_SKILLS_COMMIT_ID",
"value": "42338c0"
"value": "b3fe1d5"
}
],
"readinessProbe": {
+128 -2
View File
@@ -1,5 +1,14 @@
import net from "node:net";
import {
RUNTIME_DURABLE_ADAPTER_AUTH_BLOCKED,
RUNTIME_DURABLE_ADAPTER_MIGRATION_BLOCKED,
RUNTIME_DURABLE_ADAPTER_QUERY_BLOCKED,
RUNTIME_DURABLE_ADAPTER_SCHEMA_BLOCKED,
RUNTIME_DURABLE_ADAPTER_SSL_BLOCKED,
RUNTIME_DURABILITY_REQUIRED_EVIDENCE,
RUNTIME_STORE_KIND_POSTGRES
} from "../db/runtime-store.mjs";
import { ENVIRONMENT_DEV } from "../protocol/index.mjs";
export const DEV_DB_ENV_CONTRACT = Object.freeze({
@@ -36,8 +45,11 @@ export const DEV_DB_ENV_CONTRACT = Object.freeze({
readinessLayers: Object.freeze([
"dns",
"tcp",
"ssl",
"auth",
"schema"
"schema",
"migration",
"durability"
]),
nonSecretDefaults: Object.freeze({
HWLAB_CLOUD_DB_SSL_MODE: "disable"
@@ -199,12 +211,36 @@ export function summarizeDbContract(db = buildDbHealthContract()) {
optionalPublicDnsAlias: summarizeOptionalPublicDnsAlias(db.optionalPublicDnsAlias ?? db.endpoint?.optionalPublicDnsAlias),
connection: summarizeConnection(db.connection),
redaction: summarizeRedaction(db.redaction),
readinessLayers: buildReadinessLayers(db.connection),
readinessLayers: db.readinessLayers ?? buildReadinessLayers(db.connection),
blocker: db.blocker ?? null,
fixtureEvidence: false
};
}
export function applyRuntimeDbReadinessLayers(db = buildDbHealthContract(), runtime = {}) {
const baseLayers = buildReadinessLayers(db.connection);
if (!db?.ready || db?.connectionResult !== "connected") {
return {
...db,
readinessLayers: baseLayers,
runtimeReadiness: buildDbRuntimeReadinessSummary(runtime, { dbLiveReady: false })
};
}
return {
...db,
readinessLayers: {
...baseLayers,
ssl: layerFromRuntimeGate(runtime, "ssl"),
auth: layerFromRuntimeGate(runtime, "auth"),
schema: layerFromRuntimeGate(runtime, "schema"),
migration: layerFromRuntimeGate(runtime, "migration"),
durability: layerFromRuntimeGate(runtime, "durability")
},
runtimeReadiness: buildDbRuntimeReadinessSummary(runtime, { dbLiveReady: true })
};
}
function hasEnvValue(env, name) {
return typeof env?.[name] === "string" && env[name].trim().length > 0;
}
@@ -494,6 +530,10 @@ function buildReadinessLayers(connection = {}) {
status: tcpReady ? "pass" : "not_proven",
result
},
ssl: {
status: "not_proven",
result: connected ? "tcp_connected_ssl_not_checked" : result
},
auth: {
status: "not_proven",
result: connected ? "tcp_connected_auth_not_checked" : result
@@ -501,10 +541,96 @@ function buildReadinessLayers(connection = {}) {
schema: {
status: "not_proven",
result: connected ? "tcp_connected_schema_not_checked" : result
},
migration: {
status: "not_proven",
result: connected ? "tcp_connected_migration_not_checked" : result
},
durability: {
status: "not_proven",
result: connected ? "tcp_connected_durability_not_checked" : result
}
};
}
function layerFromRuntimeGate(runtime = {}, gateName) {
if (!isPostgresRuntime(runtime)) {
return {
status: "not_proven",
result: "runtime_adapter_not_postgres"
};
}
const gate = runtime.gates?.[gateName];
if (gate?.ready === true || gate?.status === "ready") {
return {
status: "pass",
result: `runtime_${gateName}_ready`,
blocker: null
};
}
if (gate?.status === "blocked" || runtimeGateBlockedAt(runtime, gateName)) {
return {
status: "blocked",
result: runtimeLayerResult(runtime, gateName),
blocker: gate?.blocker ?? runtime.blocker ?? null
};
}
return {
status: "not_proven",
result: `runtime_${gateName}_not_checked`,
blocker: null
};
}
function isPostgresRuntime(runtime = {}) {
return runtime?.adapter === RUNTIME_STORE_KIND_POSTGRES || runtime?.durableRequested === true;
}
function runtimeGateBlockedAt(runtime = {}, gateName) {
const blocker = runtime?.blocker;
if (gateName === "ssl") return blocker === RUNTIME_DURABLE_ADAPTER_SSL_BLOCKED;
if (gateName === "auth") return blocker === RUNTIME_DURABLE_ADAPTER_AUTH_BLOCKED;
if (gateName === "schema") return blocker === RUNTIME_DURABLE_ADAPTER_SCHEMA_BLOCKED;
if (gateName === "migration") return blocker === RUNTIME_DURABLE_ADAPTER_MIGRATION_BLOCKED;
if (gateName === "durability") return blocker === RUNTIME_DURABLE_ADAPTER_QUERY_BLOCKED;
return false;
}
function runtimeLayerResult(runtime = {}, gateName) {
const queryResult = runtime?.connection?.queryResult;
if (typeof queryResult === "string" && queryResult.length > 0) return queryResult;
if (gateName === "ssl") return "ssl_negotiation_blocked";
if (gateName === "auth") return "auth_blocked";
if (gateName === "schema") return "schema_blocked";
if (gateName === "migration") return "migration_blocked";
if (gateName === "durability") return "query_blocked";
return "runtime_query_blocked";
}
function buildDbRuntimeReadinessSummary(runtime = {}, { dbLiveReady } = {}) {
const contract = runtime?.durabilityContract ?? {};
return {
adapter: runtime?.adapter ?? "unknown",
durable: Boolean(runtime?.durable),
durableRequested: Boolean(runtime?.durableRequested || runtime?.adapter === RUNTIME_STORE_KIND_POSTGRES),
ready: runtime?.ready === true,
status: runtime?.status ?? "unknown",
blocker: runtime?.blocker ?? null,
blockedLayer: contract.blockedLayer ?? null,
queryAttempted: Boolean(runtime?.connection?.queryAttempted),
queryResult: runtime?.connection?.queryResult ?? (dbLiveReady ? "not_checked" : "db_live_not_ready"),
dbLiveEvidenceObserved: Boolean(dbLiveReady),
dbLiveEvidenceIsDurabilityEvidence: false,
requiredEvidence: contract.requiredEvidence ?? RUNTIME_DURABILITY_REQUIRED_EVIDENCE,
secretMaterialRead: false,
valuesRedacted: true,
endpointRedacted: true
};
}
function summarizeConnection(connection) {
if (!connection) {
return {
+8 -3
View File
@@ -13,7 +13,10 @@ import {
handleJsonRpcRequest
} from "./json-rpc.mjs";
import { describeCodeAgentAvailability, handleCodeAgentChat } from "./code-agent-chat.mjs";
import { buildDbRuntimeReadiness } from "./db-contract.mjs";
import {
applyRuntimeDbReadinessLayers,
buildDbRuntimeReadiness
} from "./db-contract.mjs";
import { buildCloudApiReadiness } from "./health-contract.mjs";
import {
M3_IO_CONTROL_ROUTE,
@@ -48,9 +51,10 @@ export async function buildHealthPayload(options = {}) {
const commitId = process.env.HWLAB_COMMIT_ID || process.env.HWLAB_GIT_SHA || "unknown";
const imageReference = process.env.HWLAB_IMAGE || "unknown";
const imageTag = process.env.HWLAB_IMAGE_TAG || commitId.slice(0, 7) || "unknown";
const db = await buildDbRuntimeReadiness(env, options.dbProbe);
const dbProbe = await buildDbRuntimeReadiness(env, options.dbProbe);
const codeAgent = describeCodeAgentAvailability(env, options);
const runtime = await runtimeReadiness(options.runtimeStore ?? createConfiguredCloudRuntimeStore({ ...options, env }));
const db = applyRuntimeDbReadinessLayers(dbProbe, runtime);
const readiness = buildCloudApiReadiness({ db, codeAgent, runtime });
return {
@@ -160,9 +164,10 @@ async function handleRpcHttpRequest(request, response, options) {
async function handleRestAdapter(request, response, url, options) {
if (request.method === "GET" && url.pathname === "/v1") {
const db = await buildDbRuntimeReadiness(options.env ?? process.env, options.dbProbe);
const dbProbe = await buildDbRuntimeReadiness(options.env ?? process.env, options.dbProbe);
const codeAgent = describeCodeAgentAvailability(options.env ?? process.env, options);
const runtime = await runtimeReadiness(options.runtimeStore);
const db = applyRuntimeDbReadinessLayers(dbProbe, runtime);
const readiness = buildCloudApiReadiness({ db, codeAgent, runtime });
sendJson(response, 200, {
serviceId: CLOUD_API_SERVICE_ID,
+20
View File
@@ -515,6 +515,15 @@ test("cloud api health contract distinguishes durable SSL, auth, schema, migrati
assert.equal(payload.runtime.gates.migration.status, testCase.gates.migration);
assert.equal(payload.runtime.gates.durability.status, testCase.gates.durability);
assert.equal(payload.runtime.durabilityContract.blockedLayer, testCase.blockedLayer);
assert.equal(payload.db.runtimeReadiness.blockedLayer, testCase.blockedLayer);
assert.equal(payload.db.runtimeReadiness.queryResult, testCase.queryResult);
assert.equal(payload.db.runtimeReadiness.dbLiveEvidenceIsDurabilityEvidence, false);
assert.equal(payload.db.readinessLayers.ssl.status, layerStatus(testCase.gates.ssl));
assert.equal(payload.db.readinessLayers.auth.status, layerStatus(testCase.gates.auth));
assert.equal(payload.db.readinessLayers.schema.status, layerStatus(testCase.gates.schema));
assert.equal(payload.db.readinessLayers.migration.status, layerStatus(testCase.gates.migration));
assert.equal(payload.db.readinessLayers.durability.status, layerStatus(testCase.gates.durability));
assert.equal(payload.db.readinessLayers[layerForBlockedCase(testCase.blockedLayer)].result, testCase.queryResult);
assert.equal(payload.readiness.durability.blockedLayer, testCase.blockedLayer);
assert.equal(payload.readiness.durability.queryResult, testCase.queryResult);
assert.equal(payload.readiness.durability.secretMaterialRead, false);
@@ -652,6 +661,17 @@ function durableBlockedRuntime({ blocker, blockedLayer, queryResult, gates }) {
};
}
function layerStatus(gateStatus) {
if (gateStatus === "ready") return "pass";
if (gateStatus === "blocked") return "blocked";
return "not_proven";
}
function layerForBlockedCase(blockedLayer) {
if (blockedLayer === "durability_query") return "durability";
return blockedLayer;
}
test("cloud api /v1 describes Code Agent provider blocker without leaking secret values", async () => {
const server = createCloudApiServer({
env: {