fix: route artifact registry health via remote control plane
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
artifactRegistryReadonlyAutoRemotePlan,
|
||||
artifactRegistryReadonlyResultFromCommand,
|
||||
buildArtifactRegistryReadonlyProbe,
|
||||
parseArtifactRegistryOptions,
|
||||
runArtifactRegistryCommand,
|
||||
} from "./src/artifact-registry";
|
||||
import type { CommandResult } from "./src/command";
|
||||
|
||||
@@ -114,18 +116,173 @@ assertCondition(asStringArray(success.failedScopes, "success.failedScopes").leng
|
||||
assertCondition(success.recommendedAction === "none", "healthy registry recommendedAction should be none", success);
|
||||
assertCondition(success.remoteCommandShape === probe.remoteCommandShape, "healthy registry should echo remote command shape", success);
|
||||
|
||||
process.stdout.write(`${JSON.stringify({
|
||||
ok: true,
|
||||
checks: [
|
||||
"provider-ssh-command missing is classified distinctly",
|
||||
"oversized host.ssh command shape is classified distinctly",
|
||||
"remote host.ssh timeout is classified distinctly",
|
||||
"successful registry readonly probe has no failed scopes",
|
||||
],
|
||||
classifications: {
|
||||
missing: missing.failureClassification,
|
||||
commandShape: commandShape.failureClassification,
|
||||
timeout: timeout.failureClassification,
|
||||
success: success.failureClassification,
|
||||
},
|
||||
}, null, 2)}\n`);
|
||||
const driftStdout = [
|
||||
"readonly=true",
|
||||
"unit_exists=true",
|
||||
"compose_exists=true",
|
||||
"config_exists=true",
|
||||
"storage_exists=true",
|
||||
"systemctl_available=true",
|
||||
"unit_active=active",
|
||||
"unit_enabled=enabled",
|
||||
"docker_available=true",
|
||||
"container_running=true",
|
||||
"container_status=running",
|
||||
"container_image=registry:2.8.2",
|
||||
"container_restart_policy=unless-stopped",
|
||||
"listener_count=1",
|
||||
"bad_listener_count=0",
|
||||
"loopback_only=true",
|
||||
"curl_available=true",
|
||||
"v2_http_code=200",
|
||||
"config_hash=old-config",
|
||||
"compose_hash=old-compose",
|
||||
"unit_hash=old-unit",
|
||||
"config_hash_matches=false",
|
||||
"compose_hash_matches=false",
|
||||
"unit_hash_matches=false",
|
||||
"image_matches=false",
|
||||
"",
|
||||
].join("\n");
|
||||
|
||||
const drift = asRecord(artifactRegistryReadonlyResultFromCommand(probe, command({
|
||||
stdout: driftStdout,
|
||||
})), "registry drift result");
|
||||
assertCondition(drift.ok === false, "health should fail when rendered config/image drift exists", drift);
|
||||
assertCondition(drift.failureClassification === "registry-unhealthy", "registry drift should classify as registry-unhealthy", drift);
|
||||
const driftScopes = asStringArray(drift.failedScopes, "drift.failedScopes");
|
||||
assertCondition(driftScopes.includes("rendered-config"), "registry drift should include rendered-config scope", drift);
|
||||
assertCondition(driftScopes.includes("registry-image"), "registry drift should include registry-image scope", drift);
|
||||
assertCondition(!driftScopes.includes("control-plane-missing"), "registry drift must not be classified as control-plane missing", drift);
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const localMissingWithNoRemote = await runArtifactRegistryCommand(["health", "--provider-id", "D601"], {
|
||||
env: {
|
||||
CODE_QUEUE_DEV_CONTAINER_MASTER_HOST: "203.0.113.10",
|
||||
CODE_QUEUE_SERVICE_ROLE: "scheduler",
|
||||
},
|
||||
runRemoteScriptForTest: () => command({
|
||||
exitCode: 1,
|
||||
stderr: "Error response from daemon: No such container: unidesk-backend-core\n",
|
||||
}),
|
||||
runCliForTest: () => command({
|
||||
exitCode: 1,
|
||||
stdout: JSON.stringify({
|
||||
ok: false,
|
||||
command: "artifact-registry health --provider-id D601",
|
||||
data: {
|
||||
transport: "frontend",
|
||||
readonly: true,
|
||||
dispatch: { ok: false, status: 502, body: { ok: false, error: "backend-core proxy unavailable" } },
|
||||
wait: null,
|
||||
result: {
|
||||
ok: false,
|
||||
readonly: true,
|
||||
installed: false,
|
||||
healthy: false,
|
||||
decision: "infra-blocked",
|
||||
retryable: true,
|
||||
runnerDisposition: "infra-blocked",
|
||||
failureClassification: "control-plane-missing",
|
||||
failedScopes: ["control-plane-missing", "backend-core-api"],
|
||||
runtimeApiHealthy: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}),
|
||||
});
|
||||
const controlPlaneMissing = asRecord(localMissingWithNoRemote, "control-plane missing result");
|
||||
assertCondition(controlPlaneMissing.ok === false, "missing local and remote control planes should fail", controlPlaneMissing);
|
||||
assertCondition(controlPlaneMissing.failureClassification === "control-plane-missing", "missing remote control plane should classify control-plane-missing", controlPlaneMissing);
|
||||
assertCondition(asStringArray(controlPlaneMissing.failedScopes, "controlPlaneMissing.failedScopes").includes("control-plane-missing"), "control-plane missing scope should be reported", controlPlaneMissing);
|
||||
assertCondition(asRecord(controlPlaneMissing.controlPlane, "controlPlane").localBackendCoreMissing === true, "local backend-core absence should remain evidence", controlPlaneMissing);
|
||||
|
||||
const remoteFallback = await runArtifactRegistryCommand(["health", "--provider-id", "D601"], {
|
||||
env: {
|
||||
CODE_QUEUE_DEV_CONTAINER_MASTER_HOST: "74.48.78.17",
|
||||
},
|
||||
runRemoteScriptForTest: () => command({
|
||||
exitCode: 1,
|
||||
stderr: "Error response from daemon: No such container: unidesk-backend-core\n",
|
||||
}),
|
||||
runCliForTest: () => command({
|
||||
stdout: JSON.stringify({
|
||||
ok: true,
|
||||
command: "artifact-registry health --provider-id D601",
|
||||
data: {
|
||||
transport: "frontend",
|
||||
readonly: true,
|
||||
result: success,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
});
|
||||
const remoteFallbackRecord = asRecord(remoteFallback, "remote fallback result");
|
||||
assertCondition(remoteFallbackRecord.ok === true, "remote fallback should return the remote registry result", remoteFallbackRecord);
|
||||
const fallbackControlPlane = asRecord(remoteFallbackRecord.controlPlane, "remote fallback controlPlane");
|
||||
assertCondition(fallbackControlPlane.remoteFallbackUsed === true, "remote fallback should be marked", fallbackControlPlane);
|
||||
assertCondition(fallbackControlPlane.localBackendCoreMissing === true, "local backend-core absence should remain evidence only", fallbackControlPlane);
|
||||
assertCondition(asStringArray(remoteFallbackRecord.failedScopes, "remoteFallback.failedScopes").length === 0, "remote fallback should preserve registry scopes", remoteFallbackRecord);
|
||||
|
||||
let remoteFirstLocalSshCalls = 0;
|
||||
const remoteFirst = await runArtifactRegistryCommand(["health", "--provider-id", "D601"], {
|
||||
env: {
|
||||
CODE_QUEUE_SERVICE_ROLE: "scheduler",
|
||||
CODE_QUEUE_DEV_CONTAINER_MASTER_HOST: "74.48.78.17",
|
||||
},
|
||||
runRemoteScriptForTest: () => {
|
||||
remoteFirstLocalSshCalls += 1;
|
||||
return command({ exitCode: 1, stderr: "unexpected local ssh path" });
|
||||
},
|
||||
runCliForTest: () => command({
|
||||
stdout: JSON.stringify({
|
||||
ok: true,
|
||||
command: "artifact-registry health --provider-id D601",
|
||||
data: {
|
||||
transport: "frontend",
|
||||
readonly: true,
|
||||
result: success,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
});
|
||||
const remoteFirstRecord = asRecord(remoteFirst, "remote first result");
|
||||
assertCondition(remoteFirstRecord.ok === true, "runner-like env should succeed through remote frontend first", remoteFirstRecord);
|
||||
assertCondition(remoteFirstLocalSshCalls === 0, "runner-like env should not require local backend-core before remote frontend", { remoteFirstLocalSshCalls });
|
||||
assertCondition(asRecord(remoteFirstRecord.controlPlane, "remoteFirst.controlPlane").remoteFirst === true, "remote-first controlPlane should be marked", remoteFirstRecord.controlPlane);
|
||||
|
||||
const autoPlan = artifactRegistryReadonlyAutoRemotePlan("health", options, {
|
||||
CODE_QUEUE_SERVICE_ROLE: "scheduler",
|
||||
CODE_QUEUE_DEV_CONTAINER_MASTER_HOST: "74.48.78.17",
|
||||
});
|
||||
assertCondition(autoPlan.enabled === true, "runner-like env should auto-select remote frontend for readonly registry health", autoPlan);
|
||||
assertCondition(String(autoPlan.command ?? "").includes("--main-server-ip 74.48.78.17"), "auto remote plan should expose command shape", autoPlan);
|
||||
|
||||
process.stdout.write(`${JSON.stringify({
|
||||
ok: true,
|
||||
checks: [
|
||||
"provider-ssh-command missing is classified distinctly",
|
||||
"oversized host.ssh command shape is classified distinctly",
|
||||
"remote host.ssh timeout is classified distinctly",
|
||||
"successful registry readonly probe has no failed scopes",
|
||||
"runner-like env uses remote frontend before local backend-core",
|
||||
"local backend-core absence can fall back to remote frontend control plane",
|
||||
"missing local and remote control planes classify as control-plane-missing",
|
||||
"rendered-config and registry-image drift classify as registry-unhealthy",
|
||||
],
|
||||
classifications: {
|
||||
missing: missing.failureClassification,
|
||||
commandShape: commandShape.failureClassification,
|
||||
timeout: timeout.failureClassification,
|
||||
success: success.failureClassification,
|
||||
drift: drift.failureClassification,
|
||||
controlPlaneMissing: controlPlaneMissing.failureClassification,
|
||||
remoteFallback: remoteFallbackRecord.failureClassification,
|
||||
remoteFirst: remoteFirstRecord.failureClassification,
|
||||
},
|
||||
}, null, 2)}\n`);
|
||||
}
|
||||
|
||||
if (import.meta.main) {
|
||||
await main();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user