Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/scripts/coolify-portal-domain-controller.test.mjs
T

978 lines
38 KiB
JavaScript

// @vitest-environment node
import assert from "node:assert/strict";
import { spawn } from "node:child_process";
import http from "node:http";
import path from "node:path";
import { describe, it } from "vitest";
const scriptPath = path.resolve("scripts/coolify-portal-domain-controller.mjs");
const bearerToken = "controller-coolify-token-sentinel";
const controllerUuid = "c".repeat(24);
const workerGeneralUuid = "g".repeat(24);
const workerMailUuid = "m".repeat(24);
const webCoreUuid = "w".repeat(24);
const webPortalUuid = "p".repeat(24);
const deploymentUuid = "d".repeat(24);
const revision = "a".repeat(40);
const controllerImage = "registry.example.test/toolctrl-labs/hivegrid/portal-domain-controller";
const workerImage = "registry.example.test/toolctrl-labs/hivegrid/worker";
const webImage = "registry.example.test/toolctrl-labs/hivegrid/web";
const commandSecret = "controller-command-secret-sentinel-value";
const otherCommandSecret = "different-controller-command-secret-value";
const metricsToken = "controller-metrics-token-sentinel-value";
const configDirectory = "/data/coolify/proxy/dynamic/hivegrid-portal-domains";
const reloadTriggerFile = "/data/coolify/proxy/dynamic/.hivegrid-portal-domains.reload";
const stagingCname = "staging-domains.portal.hivegrid.de";
const productionCname = "domains.portal.hivegrid.de";
const stagingMetricsBinding = "10.42.0.3:19120:8080";
const productionMetricsBinding = "10.42.0.8:19120:8080";
function expectedControllerHealthConfig(expectedRevision = revision) {
return {
health_check_enabled: true,
health_check_type: "cmd",
health_check_command: `node apps/portal-domain-controller/dist/deploy-health.js ${expectedRevision}`,
health_check_interval: 30,
health_check_timeout: 10,
health_check_retries: 3,
health_check_start_period: 20,
};
}
function controllerEnv(overrides = {}) {
return envEntries({
PORTAL_DOMAIN_CONTROLLER_COMMAND_SECRET: commandSecret,
PORTAL_DOMAIN_CONTROLLER_METRICS_TOKEN: metricsToken,
PORTAL_DOMAIN_CONTROLLER_REDIS_URL: "rediss://controller:secret@redis.example.test:6380/7",
PORTAL_DOMAIN_CONTROLLER_EXPECTED_CNAME: stagingCname,
PORTAL_DOMAIN_CONTROLLER_BACKEND_SERVICE: `https-0-${webPortalUuid}@docker`,
PORTAL_DOMAIN_CONTROLLER_ENTRYPOINT: "https",
PORTAL_DOMAIN_CONTROLLER_CONFIG_DIR: configDirectory,
PORTAL_DOMAIN_CONTROLLER_RELOAD_TRIGGER_FILE: reloadTriggerFile,
...overrides,
});
}
function workerEnv(group, overrides = {}) {
return envEntries({
WORKER_QUEUE_GROUP: group,
BULLMQ_REDIS_URL: "rediss://worker:other-secret@redis.example.test:6380/7",
...(group === "general"
? {
PORTAL_DOMAIN_CONTROLLER_COMMAND_SECRET: commandSecret,
PORTAL_DOMAIN_ORCHESTRATION_REQUIRED: "true",
}
: {}),
...overrides,
});
}
function envEntries(values) {
return Object.entries(values)
.filter(([, value]) => value !== undefined)
.map(([key, value]) => ({ key, value, is_preview: false }));
}
function exactStorages() {
return {
file_storages: [
{
uuid: "storage-config",
is_directory: true,
fs_path: configDirectory,
mount_path: configDirectory,
},
],
persistent_storages: [
{
uuid: "storage-reload",
host_path: reloadTriggerFile,
mount_path: reloadTriggerFile,
},
],
};
}
function fixture(overrides = {}) {
const controller = {
id: 101,
uuid: controllerUuid,
name: "hivegrid-portal-domain-controller",
build_pack: "dockerimage",
docker_registry_image_name: controllerImage,
docker_registry_image_tag: "previous",
pre_deployment_command: "",
post_deployment_command: "",
fqdn: "",
ports_mappings: stagingMetricsBinding,
status: "stopped",
custom_healthcheck_found: false,
health_check_enabled: true,
health_check_type: "http",
health_check_command: "",
health_check_interval: 30,
health_check_timeout: 10,
health_check_retries: 3,
health_check_start_period: 20,
...overrides.controller,
};
const workers = [
{ uuid: workerGeneralUuid, name: "worker-general", docker_registry_image_name: workerImage },
{ uuid: workerMailUuid, name: "worker-mail", docker_registry_image_name: workerImage },
];
const webCore = {
uuid: webCoreUuid,
name: "hivegrid-web",
build_pack: "dockerimage",
docker_registry_image_name: webImage,
};
const webPortal = {
uuid: webPortalUuid,
name: "hivegrid-portal",
build_pack: "dockerimage",
docker_registry_image_name: webImage,
};
return {
controller,
apps: [controller, webCore, webPortal, ...workers],
envByUuid: new Map([
[controllerUuid, controllerEnv(overrides.controllerEnv)],
[
webCoreUuid,
envEntries({
WEB_RUNTIME_SURFACE: "core",
PORTAL_CUSTOM_DOMAIN_CNAME_TARGET: overrides.webCnameTarget ?? stagingCname,
PORTAL_CUSTOM_DOMAIN_RUNTIME_ENABLED: overrides.webCoreRuntimeEnabled ?? "true",
...overrides.webCoreEnv,
}),
],
[
webPortalUuid,
envEntries({
WEB_RUNTIME_SURFACE: "portal",
PORTAL_CUSTOM_DOMAIN_RUNTIME_ENABLED: overrides.webPortalRuntimeEnabled ?? "true",
...overrides.webPortalEnv,
}),
],
[workerGeneralUuid, workerEnv("general", overrides.workerGeneralEnv)],
[workerMailUuid, workerEnv("mail")],
]),
storages: overrides.storages ?? exactStorages(),
stopDoesNotTransition: overrides.stopDoesNotTransition ?? false,
controllerProcessRunning: overrides.controllerProcessRunning ?? true,
controllerHealthRevision: overrides.controllerHealthRevision ?? controller.docker_registry_image_tag,
postStopHealthSequence: overrides.postStopHealthSequence ? [...overrides.postStopHealthSequence] : null,
stopRequested: false,
releaseMutationDrifts: overrides.releaseMutationDrifts ?? false,
compensationDoesNotRestore: overrides.compensationDoesNotRestore ?? false,
releasePatchCount: 0,
statusSequence: overrides.statusSequence ?? null,
allowedCsv: [controllerUuid, webCoreUuid, webPortalUuid, workerGeneralUuid, workerMailUuid].join(","),
};
}
function startFixtureServer(state) {
return startServer((request) => {
if (request.url === "/controller-healthz") {
const reachable = state.stopRequested && state.postStopHealthSequence?.length
? state.postStopHealthSequence.shift()
: state.controllerProcessRunning;
if (!reachable) return { destroy: true };
return {
payload: {
status: "ok",
service: "hivegrid-portal-domain-controller",
revision: state.controllerHealthRevision,
},
};
}
if (request.url === "/applications") return { payload: state.apps };
if (request.url === `/applications/${controllerUuid}/storages`) return { payload: state.storages };
const envMatch = /^\/applications\/([a-z0-9]{24})\/envs$/u.exec(request.url);
if (envMatch) return { payload: state.envByUuid.get(envMatch[1]) ?? [] };
if (request.url === `/deployments/${deploymentUuid}`) {
return {
payload: {
deployment_uuid: deploymentUuid,
application_id: state.controller.id,
application_name: "hivegrid-portal-domain-controller",
status: "finished",
},
};
}
if (request.url === `/applications/${controllerUuid}` && request.method === "PATCH") {
state.releasePatchCount += 1;
const patch = JSON.parse(request.body);
if (state.releaseMutationDrifts && state.releasePatchCount === 1) {
Object.assign(state.controller, patch, { health_check_command: "drifted-command" });
} else if (!(state.compensationDoesNotRestore && state.releasePatchCount > 1)) {
Object.assign(state.controller, patch);
}
return { payload: state.controller };
}
if (request.url === `/applications/${controllerUuid}/stop` && request.method === "POST") {
state.stopRequested = true;
if (!state.stopDoesNotTransition) {
state.controller.status = "exited:unhealthy";
state.controllerProcessRunning = false;
}
return { payload: { message: "Application stopping request queued." } };
}
if (request.url === `/applications/${controllerUuid}`) {
if (state.statusSequence?.length) state.controller.status = state.statusSequence.shift();
return { payload: state.controller };
}
return { status: 404, payload: {} };
});
}
function runHelper(serverUrl, ...args) {
const envOverrides = typeof args[0] === "object" && args[0] !== null ? args.shift() : {};
return new Promise((resolve) => {
const child = spawn(process.execPath, [scriptPath, ...args], {
env: {
PATH: process.env.PATH || "",
NODE_ENV: "test",
SELECTED_COOLIFY_API_URL: serverUrl,
SELECTED_COOLIFY_TOKEN: bearerToken,
COOLIFY_DEPLOYMENT_POLL_MS: "1",
COOLIFY_DEPLOYMENT_WAIT_ATTEMPTS: "2",
COOLIFY_CONTROLLER_DRAIN_HEALTH_URL: `${serverUrl}/controller-healthz`,
COOLIFY_CONTROLLER_DRAIN_FENCE_MS: "1",
...envOverrides,
},
stdio: ["ignore", "pipe", "pipe"],
});
let stdout = "";
let stderr = "";
child.stdout.setEncoding("utf8");
child.stderr.setEncoding("utf8");
child.stdout.on("data", (chunk) => {
stdout += chunk;
});
child.stderr.on("data", (chunk) => {
stderr += chunk;
});
child.on("close", (status) => resolve({ status, stdout, stderr }));
});
}
async function readBody(request) {
let body = "";
request.setEncoding("utf8");
for await (const chunk of request) body += chunk;
return body;
}
function startServer(responder) {
const requests = [];
const serverErrors = [];
const server = http.createServer(async (request, response) => {
const recorded = {
method: request.method,
url: request.url,
authorization: request.headers.authorization,
body: await readBody(request),
};
requests.push(recorded);
try {
const result = await responder(recorded);
if (result?.destroy) {
response.destroy();
return;
}
response.writeHead(result?.status || 200, { "content-type": "application/json" });
response.end(JSON.stringify(result?.payload ?? {}));
} catch (error) {
serverErrors.push(error);
response.writeHead(500, { "content-type": "application/json" });
response.end("{}");
}
});
return new Promise((resolve, reject) => {
server.once("error", reject);
server.listen(0, "127.0.0.1", () =>
resolve({
url: `http://127.0.0.1:${server.address().port}`,
requests,
serverErrors,
close: () => new Promise((done, failed) => server.close((error) => (error ? failed(error) : done()))),
})
);
});
}
function assertSecretSafe(result) {
for (const secret of [bearerToken, commandSecret, otherCommandSecret, metricsToken, "controller:secret", "worker:other-secret"]) {
assert.doesNotMatch(`${result.stdout}${result.stderr}`, new RegExp(secret.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")));
}
}
describe("portal-domain controller Coolify deployment helper", () => {
it("accepts only the private two-mount runtime with matched worker-general HMAC wiring", async () => {
const state = fixture();
const server = await startFixtureServer(state);
try {
const result = await runHelper(server.url, "assert-runtime", "staging", controllerUuid, state.allowedCsv, controllerImage, workerImage);
assert.equal(result.status, 0, result.stderr);
assert.match(result.stdout, /runtime, mounts, and worker-general HMAC wiring verified/);
assert.equal(result.stderr, "");
assertSecretSafe(result);
assert.deepEqual(server.serverErrors, []);
assert.ok(server.requests.some(({ url }) => url === `/applications/${controllerUuid}/storages`));
assert.ok(server.requests.some(({ url }) => url === `/applications/${workerGeneralUuid}/envs`));
assert.ok(server.requests.some(({ url }) => url === `/applications/${webPortalUuid}/envs`));
assert.ok(server.requests.every(({ authorization }) => authorization === `Bearer ${bearerToken}`));
} finally {
await server.close();
}
});
it("accepts only Coolify's exact automatic metadata keys", async () => {
const accepted = fixture({
controllerEnv: {
COOLIFY_BRANCH: "main",
COOLIFY_CONTAINER_NAME: "controller-container",
COOLIFY_FQDN: "",
COOLIFY_RESOURCE_UUID: controllerUuid,
COOLIFY_URL: "",
},
});
const acceptedServer = await startFixtureServer(accepted);
try {
const result = await runHelper(acceptedServer.url, "assert-runtime", "staging", controllerUuid, accepted.allowedCsv, controllerImage, workerImage);
assert.equal(result.status, 0, result.stderr);
assertSecretSafe(result);
} finally {
await acceptedServer.close();
}
const rejected = fixture({ controllerEnv: { COOLIFY_DEPLOYMENT_TOKEN: "not-allowed" } });
const rejectedServer = await startFixtureServer(rejected);
try {
const result = await runHelper(rejectedServer.url, "assert-runtime", "staging", controllerUuid, rejected.allowedCsv, controllerImage, workerImage);
assert.equal(result.status, 1);
assert.match(result.stderr, /forbidden privileged environment keys/);
assertSecretSafe(result);
} finally {
await rejectedServer.close();
}
});
it("keeps the controller probe on the exact internal port behind the mesh metrics binding", async () => {
const accepted = fixture({ controllerEnv: { PORTAL_DOMAIN_CONTROLLER_PORT: "8080" } });
const acceptedServer = await startFixtureServer(accepted);
try {
const result = await runHelper(acceptedServer.url, "assert-runtime", "staging", controllerUuid, accepted.allowedCsv, controllerImage, workerImage);
assert.equal(result.status, 0, result.stderr);
assertSecretSafe(result);
} finally {
await acceptedServer.close();
}
const rejected = fixture({ controllerEnv: { PORTAL_DOMAIN_CONTROLLER_PORT: "8081" } });
const rejectedServer = await startFixtureServer(rejected);
try {
const result = await runHelper(rejectedServer.url, "assert-runtime", "staging", controllerUuid, rejected.allowedCsv, controllerImage, workerImage);
assert.equal(result.status, 1);
assert.match(result.stderr, /must be exactly 8080/);
assertSecretSafe(result);
} finally {
await rejectedServer.close();
}
});
it("rejects a worker-general HMAC mismatch before any mutation", async () => {
const state = fixture({ workerGeneralEnv: { PORTAL_DOMAIN_CONTROLLER_COMMAND_SECRET: otherCommandSecret } });
const server = await startFixtureServer(state);
try {
const result = await runHelper(server.url, "set-release", "staging", controllerUuid, state.allowedCsv, controllerImage, workerImage, revision);
assert.equal(result.status, 1);
assert.match(result.stderr, /HMAC secrets must match/);
assert.ok(server.requests.every(({ method }) => method === "GET"));
assertSecretSafe(result);
} finally {
await server.close();
}
});
it("rejects a broad Traefik root mount or missing reload sentinel", async () => {
for (const storages of [
[{ type: "file", is_directory: true, fs_path: "/data/coolify/proxy/dynamic", mount_path: "/data/coolify/proxy/dynamic" }],
{
file_storages: exactStorages().file_storages,
persistent_storages: [
{
...exactStorages().persistent_storages[0],
host_path: "/data/coolify/proxy/dynamic/not-the-reload-sentinel",
},
],
},
]) {
const state = fixture({ storages });
const server = await startFixtureServer(state);
try {
const result = await runHelper(server.url, "assert-runtime", "staging", controllerUuid, state.allowedCsv, controllerImage, workerImage);
assert.equal(result.status, 1);
assert.match(result.stderr, /exactly two narrow bind mounts|config subdirectory and RW reload sentinel/);
assertSecretSafe(result);
} finally {
await server.close();
}
}
});
it("rejects missing or malformed environment-specific Traefik routing identities", async () => {
for (const controllerEnvOverride of [
{ PORTAL_DOMAIN_CONTROLLER_BACKEND_SERVICE: undefined },
{ PORTAL_DOMAIN_CONTROLLER_BACKEND_SERVICE: "http://hivegrid-portal:8080" },
{ PORTAL_DOMAIN_CONTROLLER_ENTRYPOINT: "https;malformed" },
{ PORTAL_DOMAIN_CONTROLLER_ENTRYPOINT: "websecure" },
]) {
const state = fixture({ controllerEnv: controllerEnvOverride });
const server = await startFixtureServer(state);
try {
const result = await runHelper(server.url, "assert-runtime", "staging", controllerUuid, state.allowedCsv, controllerImage, workerImage);
assert.equal(result.status, 1);
assert.match(result.stderr, /BACKEND_SERVICE|ENTRYPOINT/);
assertSecretSafe(result);
} finally {
await server.close();
}
}
});
it("rejects a missing or ambiguous allowlisted portal surface before any mutation", async () => {
const duplicatePortalUuid = "q".repeat(24);
const missing = fixture({ webPortalEnv: { WEB_RUNTIME_SURFACE: undefined } });
const ambiguous = fixture();
ambiguous.apps.push({
uuid: duplicatePortalUuid,
name: "hivegrid-portal-shadow",
build_pack: "dockerimage",
docker_registry_image_name: webImage,
});
ambiguous.envByUuid.set(duplicatePortalUuid, envEntries({ WEB_RUNTIME_SURFACE: "portal" }));
ambiguous.allowedCsv = `${ambiguous.allowedCsv},${duplicatePortalUuid}`;
for (const state of [missing, ambiguous]) {
const server = await startFixtureServer(state);
try {
const result = await runHelper(server.url, "set-release", "staging", controllerUuid, state.allowedCsv, controllerImage, workerImage, revision);
assert.equal(result.status, 1);
assert.match(result.stderr, /exactly one allowlisted hivegrid-portal surface/);
assert.ok(server.requests.every(({ method }) => method === "GET"));
assertSecretSafe(result);
} finally {
await server.close();
}
}
});
it("rejects a valid Docker service name that does not identify the resolved portal resource", async () => {
const state = fixture({
controllerEnv: {
PORTAL_DOMAIN_CONTROLLER_BACKEND_SERVICE: `https-0-${webCoreUuid}@docker`,
},
});
const server = await startFixtureServer(state);
try {
const result = await runHelper(server.url, "set-release", "staging", controllerUuid, state.allowedCsv, controllerImage, workerImage, revision);
assert.equal(result.status, 1);
assert.match(result.stderr, /must target the exact allowlisted hivegrid-portal Docker service/);
assert.ok(server.requests.every(({ method }) => method === "GET"));
assertSecretSafe(result);
} finally {
await server.close();
}
});
it("rejects CNAME drift and unexpected controller credentials before mutation", async () => {
for (const state of [
fixture({ webCnameTarget: "production-ingress.example.test" }),
fixture({ controllerEnv: { STORAGE_SECRET_ACCESS_KEY: "must-never-reach-controller" } }),
]) {
const server = await startFixtureServer(state);
try {
const result = await runHelper(server.url, "set-release", "staging", controllerUuid, state.allowedCsv, controllerImage, workerImage, revision);
assert.equal(result.status, 1);
assert.match(result.stderr, /hivegrid-web must use exactly|least-privilege environment allowlist/);
assert.ok(server.requests.every(({ method }) => method === "GET"));
assertSecretSafe(result);
} finally {
await server.close();
}
}
});
it("requires the exact custom-domain runtime opt-in on both staging web surfaces", async () => {
for (const state of [
fixture({ webCoreEnv: { PORTAL_CUSTOM_DOMAIN_RUNTIME_ENABLED: undefined } }),
fixture({ webCoreRuntimeEnabled: "false" }),
fixture({ webCoreRuntimeEnabled: "1" }),
fixture({ webPortalEnv: { PORTAL_CUSTOM_DOMAIN_RUNTIME_ENABLED: undefined } }),
fixture({ webPortalRuntimeEnabled: "false" }),
fixture({ webPortalRuntimeEnabled: "TRUE" }),
]) {
const server = await startFixtureServer(state);
try {
const result = await runHelper(server.url, "set-release", "staging", controllerUuid, state.allowedCsv, controllerImage, workerImage, revision);
assert.equal(result.status, 1);
assert.match(result.stderr, /PORTAL_CUSTOM_DOMAIN_RUNTIME_ENABLED exactly to true in staging/);
assert.ok(server.requests.every(({ method }) => method === "GET"));
assertSecretSafe(result);
} finally {
await server.close();
}
}
});
it("pins each deployment environment to its exact CNAME target before mutation", async () => {
const cases = [
{
environment: "production",
state: fixture({ controller: { ports_mappings: productionMetricsBinding } }),
expected: new RegExp(`EXPECTED_CNAME must be exactly ${productionCname}`),
},
{
environment: "staging",
state: fixture({
controllerEnv: { PORTAL_DOMAIN_CONTROLLER_EXPECTED_CNAME: productionCname },
webCnameTarget: productionCname,
}),
expected: new RegExp(`EXPECTED_CNAME must be exactly ${stagingCname}`),
},
{
environment: "preview",
state: fixture(),
expected: /deployment environment must be exactly staging or production/,
},
];
for (const { environment, state, expected } of cases) {
const server = await startFixtureServer(state);
try {
const result = await runHelper(server.url, "set-release", environment, controllerUuid, state.allowedCsv, controllerImage, workerImage, revision);
assert.equal(result.status, 1);
assert.match(result.stderr, expected);
assert.ok(server.requests.every(({ method }) => method === "GET"));
assertSecretSafe(result);
} finally {
await server.close();
}
}
});
it("accepts the production environment only with the production CNAME target", async () => {
const state = fixture({
controller: { ports_mappings: productionMetricsBinding },
controllerEnv: { PORTAL_DOMAIN_CONTROLLER_EXPECTED_CNAME: productionCname },
webCnameTarget: productionCname,
webCoreRuntimeEnabled: "false",
webPortalRuntimeEnabled: "false",
});
const server = await startFixtureServer(state);
try {
const result = await runHelper(server.url, "assert-runtime", "production", controllerUuid, state.allowedCsv, controllerImage, workerImage);
assert.equal(result.status, 0, result.stderr);
assertSecretSafe(result);
} finally {
await server.close();
}
});
it("pins the explicit resource to the full SHA and clears deployment hooks", async () => {
const state = fixture();
const server = await startFixtureServer(state);
try {
const result = await runHelper(server.url, "set-release", "staging", controllerUuid, state.allowedCsv, controllerImage, workerImage, revision);
assert.equal(result.status, 0, result.stderr);
assert.match(result.stdout, /immutable release configured/);
const patches = server.requests.filter(({ method }) => method === "PATCH");
assert.equal(patches.length, 1);
assert.deepEqual(JSON.parse(patches[0].body), {
pre_deployment_command: "",
post_deployment_command: "",
docker_registry_image_tag: revision,
...expectedControllerHealthConfig(),
});
assertSecretSafe(result);
} finally {
await server.close();
}
});
it("verifies compensation after a rejected release mutation", async () => {
for (const compensationDoesNotRestore of [false, true]) {
const state = fixture({
controller: { health_check_interval: 0, health_check_start_period: 0 },
releaseMutationDrifts: true,
compensationDoesNotRestore,
});
const previousTag = state.controller.docker_registry_image_tag;
const previousHealthType = state.controller.health_check_type;
const server = await startFixtureServer(state);
try {
const result = await runHelper(server.url, "set-release", "staging", controllerUuid, state.allowedCsv, controllerImage, workerImage, revision);
assert.equal(result.status, 1);
assert.match(
result.stderr,
compensationDoesNotRestore ? /compensation was incomplete/ : /previous configuration was restored/
);
assert.equal(server.requests.filter(({ method }) => method === "PATCH").length, 2);
if (!compensationDoesNotRestore) {
assert.equal(state.controller.docker_registry_image_tag, previousTag);
assert.equal(state.controller.health_check_type, previousHealthType);
assert.equal(state.controller.health_check_interval, 0);
assert.equal(state.controller.health_check_start_period, 0);
}
assertSecretSafe(result);
} finally {
await server.close();
}
}
});
it("stops and drains the old controller before a paired web rollout", async () => {
const state = fixture({ controller: { docker_registry_image_tag: revision, status: "running:healthy" } });
const server = await startFixtureServer(state);
try {
const result = await runHelper(server.url, "stop-and-drain", "staging", controllerUuid, state.allowedCsv, controllerImage, workerImage);
assert.equal(result.status, 0, result.stderr);
assert.match(result.stdout, /stopped and command worker drained/);
assert.deepEqual(
server.requests.filter(({ method, url }) => method === "POST" && url.endsWith("/stop")).map(({ url }) => url),
[`/applications/${controllerUuid}/stop`]
);
assertSecretSafe(result);
} finally {
await server.close();
}
});
it("fails closed for operator recovery when pre-stop health is unreachable despite exited status", async () => {
const state = fixture({
controller: { docker_registry_image_tag: revision, status: "exited:unhealthy" },
controllerProcessRunning: false,
});
const server = await startFixtureServer(state);
try {
const result = await runHelper(server.url, "stop-and-drain", "staging", controllerUuid, state.allowedCsv, controllerImage, workerImage);
assert.equal(result.status, 1);
assert.match(result.stderr, /pre-stop health is unreachable or not revision-bound; operator recovery is required/);
assert.equal(server.requests.some(({ method, url }) => method === "POST" && url.endsWith("/stop")), false);
assertSecretSafe(result);
} finally {
await server.close();
}
});
it("does not trust stale exited status while the queued stop and live health lag", async () => {
const state = fixture({
controller: { docker_registry_image_tag: revision, status: "exited:unhealthy" },
statusSequence: ["exited:unhealthy", "exited:unhealthy", "running:healthy", "exited:unhealthy", "exited:unhealthy"],
postStopHealthSequence: [true, true, false, false],
});
const server = await startFixtureServer(state);
try {
const result = await runHelper(
server.url,
{ COOLIFY_DEPLOYMENT_WAIT_ATTEMPTS: "4" },
"stop-and-drain",
"staging",
controllerUuid,
state.allowedCsv,
controllerImage,
workerImage
);
assert.equal(result.status, 0, result.stderr);
assert.match(result.stdout, /stopped and command worker drained/);
assert.deepEqual(
server.requests.filter(({ method, url }) => method === "POST" && url.endsWith("/stop")).map(({ url }) => url),
[`/applications/${controllerUuid}/stop`]
);
assert.equal(server.requests.filter(({ method, url }) => method === "GET" && url === `/applications/${controllerUuid}`).length, 5);
assert.equal(server.requests.filter(({ url }) => url === "/controller-healthz").length, 5);
assertSecretSafe(result);
} finally {
await server.close();
}
});
it("fails closed when Coolify cannot prove the controller stopped", async () => {
const state = fixture({
controller: { docker_registry_image_tag: revision, status: "running:healthy" },
stopDoesNotTransition: true,
});
const server = await startFixtureServer(state);
try {
const result = await runHelper(server.url, "stop-and-drain", "staging", controllerUuid, state.allowedCsv, controllerImage, workerImage);
assert.equal(result.status, 1);
assert.match(result.stderr, /did not stop and drain/);
assertSecretSafe(result);
} finally {
await server.close();
}
});
it("fails closed before stop when mesh health belongs to another revision", async () => {
const state = fixture({
controller: { docker_registry_image_tag: revision, status: "running:healthy" },
controllerHealthRevision: "b".repeat(40),
});
const server = await startFixtureServer(state);
try {
const result = await runHelper(server.url, "stop-and-drain", "staging", controllerUuid, state.allowedCsv, controllerImage, workerImage);
assert.equal(result.status, 1);
assert.match(result.stderr, /pre-stop health is unreachable or not revision-bound/);
assert.equal(server.requests.some(({ method, url }) => method === "POST" && url.endsWith("/stop")), false);
assertSecretSafe(result);
} finally {
await server.close();
}
});
it("snapshots only a validated immutable controller release without mutation", async () => {
const valid = fixture({
controller: {
docker_registry_image_tag: revision,
custom_healthcheck_found: false,
...expectedControllerHealthConfig(),
},
});
const validServer = await startFixtureServer(valid);
try {
const result = await runHelper(validServer.url, "snapshot-release", "staging", controllerUuid, valid.allowedCsv, controllerImage, workerImage);
assert.equal(result.status, 0, result.stderr);
assert.equal(result.stdout, revision);
assert.equal(result.stderr, "");
assert.ok(validServer.requests.every(({ method }) => method === "GET"));
assertSecretSafe(result);
} finally {
await validServer.close();
}
for (const controller of [
{ docker_registry_image_tag: "staging" },
{ docker_registry_image_tag: revision, custom_healthcheck_found: false, ...expectedControllerHealthConfig(), health_check_command: "drifted" },
]) {
const invalid = fixture({ controller });
const invalidServer = await startFixtureServer(invalid);
try {
const result = await runHelper(
invalidServer.url,
"snapshot-release",
"staging",
controllerUuid,
invalid.allowedCsv,
controllerImage,
workerImage,
);
assert.equal(result.status, 1);
assert.match(result.stderr, /full lowercase Git SHA|revision-bound command health configuration drifted/);
assert.ok(invalidServer.requests.every(({ method }) => method === "GET"));
assertSecretSafe(result);
} finally {
await invalidServer.close();
}
}
});
it("rejects a mutable release tag before contacting Coolify", async () => {
const state = fixture();
const server = await startFixtureServer(state);
try {
const result = await runHelper(server.url, "set-release", "staging", controllerUuid, state.allowedCsv, controllerImage, workerImage, "staging");
assert.equal(result.status, 1);
assert.match(result.stderr, /full lowercase Git SHA/);
assert.deepEqual(server.requests, []);
assertSecretSafe(result);
} finally {
await server.close();
}
});
it("binds the completed deployment to a revision-bound command healthz and readyz contract", async () => {
const state = fixture({
controller: {
docker_registry_image_tag: revision,
status: "running:healthy",
custom_healthcheck_found: false,
...expectedControllerHealthConfig(),
},
});
const server = await startFixtureServer(state);
try {
const result = await runHelper(
server.url,
"assert-release",
"staging",
controllerUuid,
state.allowedCsv,
controllerImage,
workerImage,
revision,
deploymentUuid,
);
assert.equal(result.status, 0, result.stderr);
assert.match(result.stdout, /healthz, readyz, and immutable revision verified/);
assert.ok(server.requests.some(({ url }) => url === `/deployments/${deploymentUuid}`));
assertSecretSafe(result);
} finally {
await server.close();
}
});
it("tolerates at least eight stale exited statuses within the freshly deployed controller grace period", async () => {
const state = fixture({
controller: {
docker_registry_image_tag: revision,
status: "exited:unhealthy",
custom_healthcheck_found: false,
...expectedControllerHealthConfig(),
},
// First entry feeds the runtime-contract lookup, the rest the poll loop.
statusSequence: [
"exited:unhealthy",
"exited:unhealthy",
"exited:unhealthy",
"exited:unhealthy",
"exited:unhealthy",
"exited:unhealthy",
"exited:unhealthy",
"exited:unhealthy",
"exited:unhealthy",
"running:healthy",
],
});
const server = await startFixtureServer(state);
try {
const result = await runHelper(
server.url,
{ COOLIFY_DEPLOYMENT_WAIT_ATTEMPTS: "10", COOLIFY_FAILED_STATUS_GRACE_MS: "60000" },
"assert-release",
"staging",
controllerUuid,
state.allowedCsv,
controllerImage,
workerImage,
revision,
deploymentUuid
);
assert.equal(result.status, 0, result.stderr);
assert.match(result.stdout, /healthz, readyz, and immutable revision verified/);
assertSecretSafe(result);
} finally {
await server.close();
}
});
it("still fails closed when the controller stays in a failed runtime state", async () => {
const state = fixture({
controller: {
docker_registry_image_tag: revision,
status: "exited:unhealthy",
custom_healthcheck_found: false,
...expectedControllerHealthConfig(),
},
});
const server = await startFixtureServer(state);
try {
const result = await runHelper(
server.url,
{
COOLIFY_DEPLOYMENT_POLL_MS: "10",
COOLIFY_DEPLOYMENT_WAIT_ATTEMPTS: "6",
COOLIFY_FAILED_STATUS_GRACE_MS: "5",
},
"assert-release",
"staging",
controllerUuid,
state.allowedCsv,
controllerImage,
workerImage,
revision,
deploymentUuid
);
assert.equal(result.status, 1);
assert.match(result.stderr, /entered a terminal runtime state/);
assertSecretSafe(result);
} finally {
await server.close();
}
});
it("rejects an invalid failed-status grace before contacting Coolify", async () => {
const state = fixture();
const server = await startFixtureServer(state);
try {
const result = await runHelper(
server.url,
{ COOLIFY_FAILED_STATUS_GRACE_MS: "-1" },
"assert-release",
"staging",
controllerUuid,
state.allowedCsv,
controllerImage,
workerImage,
revision,
deploymentUuid
);
assert.equal(result.status, 1);
assert.match(result.stderr, /COOLIFY_FAILED_STATUS_GRACE_MS must be a non-negative safe integer/);
assert.deepEqual(server.requests, []);
assertSecretSafe(result);
} finally {
await server.close();
}
});
it("rejects a public controller route, an image-healthcheck override, or command-health drift", async () => {
for (const controller of [
{ fqdn: "https://controller.example.test" },
{
docker_registry_image_tag: revision,
status: "running:healthy",
custom_healthcheck_found: true,
...expectedControllerHealthConfig(),
},
{
docker_registry_image_tag: revision,
status: "running:healthy",
custom_healthcheck_found: false,
...expectedControllerHealthConfig(),
health_check_command: "curl http://127.0.0.1:8080/readyz",
},
]) {
const state = fixture({ controller });
const server = await startFixtureServer(state);
try {
const args = controller.fqdn
? ["assert-runtime", "staging", controllerUuid, state.allowedCsv, controllerImage, workerImage]
: ["assert-release", "staging", controllerUuid, state.allowedCsv, controllerImage, workerImage, revision, deploymentUuid];
const result = await runHelper(server.url, ...args);
assert.equal(result.status, 1);
assert.match(result.stderr, /must remain private|command health configuration drifted/);
assertSecretSafe(result);
} finally {
await server.close();
}
}
});
it("rejects a public or missing metrics port instead of exposing the controller beyond the mesh", async () => {
for (const ports_mappings of [null, "0.0.0.0:19120:8080", "10.42.0.3:19120:8080,10.42.0.3:19121:8080"]) {
const state = fixture({ controller: { ports_mappings } });
const server = await startFixtureServer(state);
try {
const result = await runHelper(server.url, "assert-runtime", "staging", controllerUuid, state.allowedCsv, controllerImage, workerImage);
assert.equal(result.status, 1);
assert.match(result.stderr, /metrics only through the exact mesh binding/);
assertSecretSafe(result);
} finally {
await server.close();
}
}
});
});