Twenty-eight integration tests each carried their own copy of the same two helpers, which pointed a manifest and the substrate bundle at whatever the lab's registry had assigned. They now share two in the harness, and the difference is the point: ours is rewritten to the ID the machine holds it under, and everything else is left exactly as written so the machine pulls it. **The substrate bundle is where the fiction was most load-bearing.** mesh-host's `examples/substrate-first-node.lock` pins all three of its images at `192.0.2.250:5000/…`, which is the address the lab's registry served from — it was written for a target, and the target was the lab. Two of those are ordinary third-party images and become the digests mesh-catalog's own postgres and lavinmq modules pin, so the substrate's store and broker are literally the images the mesh runs. mesh-control exists in no registry at all and becomes the ID the machine was handed. **The bundle itself should be fixed in mesh-host and this substitution deleted with it.** Beds that wrote a manifest by hand named an image by repository and let the rewrite supply a digest. There is nothing to supply one now, so `onTheMachine` refuses an unpinned reference and hands back the digest the catalogue pins — a bed runs the image the mesh ships, and a bed that drifts from the catalogue is testing a different postgres. Three beds took a third-party image out of the raised list, which no longer contains one: certificates (pebble), objectstore (minio and its client) and provisioner (postgres) now name theirs and pull it. builds and mesh publish into the MESH's own artifact store — the `registry` module's image, on the node, on 5000 — rather than into scenery the lab raised. That is a different claim, and only one of them exists in production. New unit tests cover what a full raise would otherwise be the only way to check: the routes an egress machine gets (that its gateway is still the path to the rest of the scenario, that a range with no path is unreachable rather than leaked to the uplink, that each family gets its own next hop), which machine is handed which of our images, and the `images:` rule that refuses a third-party entry. The "shipped scenarios are valid" test now loads every scenario rather than two of them. Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
203 lines
8.1 KiB
TypeScript
203 lines
8.1 KiB
TypeScript
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { parseScenario } from "../src/declaration/parse.ts";
|
|
import { planPlacements, planHeldImages, PLACEABLE, isPlaceable } from "../src/lifecycle/place.ts";
|
|
import { assertSupported, UnsupportedError } from "../src/lifecycle/supported.ts";
|
|
|
|
/**
|
|
* `place:` is the seam where the lab stops being infrastructure with no consumer. Each test
|
|
* names what it defends, per novox/hq ADR 0017.
|
|
*/
|
|
|
|
function scenario(place: string): ReturnType<typeof parseScenario> {
|
|
return parseScenario(`
|
|
scenario: placing
|
|
segments:
|
|
hosting:
|
|
kind: public
|
|
cidr: [192.0.2.0/24]
|
|
machines:
|
|
anchor:
|
|
at: { segment: hosting, address: [192.0.2.10] }
|
|
peer:
|
|
at: { segment: hosting, address: [192.0.2.20] }
|
|
${place}
|
|
`);
|
|
}
|
|
|
|
test("`all:` reaches every machine", () => {
|
|
const placements = planPlacements(scenario("place:\n all: [host]"));
|
|
assert.deepEqual(
|
|
placements.map((p) => p.machine).sort(),
|
|
["anchor", "peer"],
|
|
);
|
|
for (const p of placements) assert.deepEqual(p.artifacts, ["host"]);
|
|
});
|
|
|
|
test("a per-machine entry OVERRIDES `all:`, it does not add to it", () => {
|
|
// Worth being exact about: a scenario naming one artifact for one machine gets that
|
|
// artifact, not that artifact plus everything in `all:`. The opposite reading would place
|
|
// things nobody asked for, which is the shape of fault this lab exists to catch.
|
|
const placements = planPlacements(scenario("place:\n all: [host]\n anchor: [substrate]"));
|
|
const byMachine = new Map(placements.map((p) => [p.machine, p.artifacts]));
|
|
|
|
assert.deepEqual(byMachine.get("anchor"), ["substrate"], "anchor should have ONLY substrate");
|
|
assert.deepEqual(byMachine.get("peer"), ["host"]);
|
|
});
|
|
|
|
test("a machine placed with nothing is not a placement", () => {
|
|
const placements = planPlacements(scenario("place:\n all: [host]\n anchor: []"));
|
|
assert.deepEqual(placements.map((p) => p.machine), ["peer"]);
|
|
});
|
|
|
|
test("no `place:` at all is no placements, not an error", () => {
|
|
assert.deepEqual(planPlacements(scenario("")), []);
|
|
});
|
|
|
|
test("placing the host is supported", () => {
|
|
// The whole point of stage 1: this used to be refused.
|
|
assert.doesNotThrow(() => assertSupported(scenario("place:\n all: [host]")));
|
|
});
|
|
|
|
/**
|
|
* Which machine is handed which of the mesh's own images.
|
|
*
|
|
* **Not an economy — a fact.** An operator's workstation holds the images its own modules need,
|
|
* because somebody put them there, and a home server holds a different set. The lab used to serve
|
|
* everything to everyone from a registry it raised, which hid that entirely; handing every machine
|
|
* the union instead would put some thirty gigabytes of runtimes onto whole-mesh-full's
|
|
* thirty-gigabyte workstations, and the raise would die on disk with the topology looking fine.
|
|
*/
|
|
test("a machine that says nothing is handed everything the scenario has", () => {
|
|
const s = parseScenario(`
|
|
scenario: s
|
|
segments: { hosting: { kind: public, cidr: [192.0.2.0/24] } }
|
|
machines:
|
|
anchor: { at: { segment: hosting, address: [192.0.2.10] }, egress: true }
|
|
images: [mesh-control:development, mesh-runtime-redis:development]
|
|
place: { all: [host, runtime] }
|
|
`);
|
|
assert.deepEqual(planHeldImages(s), [
|
|
{ machine: "anchor", images: ["mesh-control:development", "mesh-runtime-redis:development"] },
|
|
]);
|
|
});
|
|
|
|
test("a machine that names some is handed those, and no others", () => {
|
|
const s = parseScenario(`
|
|
scenario: s
|
|
segments: { hosting: { kind: public, cidr: [192.0.2.0/24] } }
|
|
machines:
|
|
anchor:
|
|
at: { segment: hosting, address: [192.0.2.10] }
|
|
egress: true
|
|
images: [mesh-control:development]
|
|
laptop:
|
|
at: { segment: hosting, address: [192.0.2.20] }
|
|
egress: true
|
|
images: [mesh-runtime-redis:development]
|
|
images: [mesh-control:development, mesh-runtime-redis:development]
|
|
place: { all: [host, runtime] }
|
|
`);
|
|
assert.deepEqual(planHeldImages(s), [
|
|
{ machine: "anchor", images: ["mesh-control:development"] },
|
|
{ machine: "laptop", images: ["mesh-runtime-redis:development"] },
|
|
]);
|
|
});
|
|
|
|
test("a machine that names none is handed none, and is not a machine to visit", () => {
|
|
// Absent and empty are different, and a machine running nothing of ours should be able to say
|
|
// so without the lab deciding it must have meant everything.
|
|
const s = parseScenario(`
|
|
scenario: s
|
|
segments: { hosting: { kind: public, cidr: [192.0.2.0/24] } }
|
|
machines:
|
|
anchor: { at: { segment: hosting, address: [192.0.2.10] }, egress: true }
|
|
bare: { at: { segment: hosting, address: [192.0.2.20] }, egress: true, images: [] }
|
|
images: [mesh-control:development]
|
|
place: { all: [host, runtime] }
|
|
`);
|
|
assert.deepEqual(planHeldImages(s).map((p) => p.machine), ["anchor"]);
|
|
});
|
|
|
|
test("a scenario with none of our images loads nothing anywhere", () => {
|
|
const s = parseScenario(`
|
|
scenario: s
|
|
segments: { hosting: { kind: public, cidr: [192.0.2.0/24] } }
|
|
machines:
|
|
anchor: { at: { segment: hosting, address: [192.0.2.10] } }
|
|
place: { all: [host] }
|
|
`);
|
|
assert.deepEqual(planHeldImages(s), []);
|
|
});
|
|
|
|
test("a tier that does not exist is refused BY NAME", () => {
|
|
// Named individually rather than refused as a whole, so a scenario placing a host and a
|
|
// substrate is told exactly which half the lab cannot do — rather than being told `place:`
|
|
// is unsupported when half of it now works.
|
|
try {
|
|
assertSupported(scenario("place:\n all: [host, substrate]\n peer: [control]"));
|
|
assert.fail("expected a refusal");
|
|
} catch (err) {
|
|
assert.ok(err instanceof UnsupportedError);
|
|
const missing = err.missing.join("\n");
|
|
assert.match(missing, /substrate/, "the substrate was not named");
|
|
assert.match(missing, /control/, "the control plane was not named");
|
|
assert.doesNotMatch(missing, /place: host/, "the host is placeable and was refused anyway");
|
|
}
|
|
});
|
|
|
|
test("the refusal says what CAN be placed", () => {
|
|
// A refusal that does not say what is possible sends someone to the source to find out.
|
|
try {
|
|
assertSupported(scenario("place:\n all: [forge]"));
|
|
assert.fail("expected a refusal");
|
|
} catch (err) {
|
|
assert.ok(err instanceof UnsupportedError);
|
|
for (const placeable of PLACEABLE) {
|
|
assert.match(err.missing.join("\n"), new RegExp(placeable));
|
|
}
|
|
}
|
|
});
|
|
|
|
// --- runtime and image placement (novox/hq ADR 0006: the lab places what a sealed scenario
|
|
// cannot fetch) ---
|
|
|
|
test("an image reference is placeable, and a bare 'image:' is not", () => {
|
|
assert.ok(isPlaceable("image:alpine@sha256:abc"), "a reference should be placeable");
|
|
assert.ok(isPlaceable("image:postgres:17"), "a tag is the scenario's business, not the lab's");
|
|
assert.ok(!isPlaceable("image:"), "there is nothing to place");
|
|
assert.ok(!isPlaceable("image: "), "whitespace is not a reference");
|
|
});
|
|
|
|
test("the placeables are host, runtime and an image", () => {
|
|
assert.ok(isPlaceable("host"));
|
|
assert.ok(isPlaceable("runtime"));
|
|
assert.ok(!isPlaceable("substrate"), "the tiers above tier 0 do not exist yet");
|
|
assert.ok(!isPlaceable("control-plane"));
|
|
});
|
|
|
|
test("an unplaceable artifact is named, not refused as a whole", () => {
|
|
// A scenario placing a host and a substrate is told which half the lab cannot do — refusing
|
|
// wholesale would send somebody looking for the wrong problem.
|
|
const scenario = {
|
|
name: "s",
|
|
segments: {},
|
|
machines: { a: {} as never },
|
|
place: { a: ["host", "runtime", "image:alpine@sha256:x", "substrate"] },
|
|
} as unknown as Parameters<typeof assertSupported>[0];
|
|
|
|
assert.throws(
|
|
() => assertSupported(scenario),
|
|
(err: Error) => {
|
|
// Checked as `place: <artifact> —`, which is how an artifact is REPORTED as
|
|
// unplaceable. Searching for the bare word matched the message's own list of what CAN
|
|
// be placed, which mentions runtime — so the test failed on a correct message.
|
|
assert.ok(err.message.includes("place: substrate —"), "the unplaceable one is named");
|
|
assert.ok(!err.message.includes("place: image:alpine"), "a placeable one is not");
|
|
assert.ok(!err.message.includes("place: runtime —"), "nor is runtime");
|
|
assert.ok(!err.message.includes("place: host —"), "nor is host");
|
|
return true;
|
|
},
|
|
);
|
|
});
|