One-node bed: SDK-by-version, rename, and the store/broker upgrade proofs #27

Merged
jschoubben merged 26 commits from feat/a-bed-that-hands-over-nothing into main 2026-09-16 21:25:34 +00:00
Showing only changes of commit 2f470f27b8 - Show all commits
+78 -2
View File
@@ -103,6 +103,12 @@ const MUST_RUN = ["mesh-control", "mesh-registry", "mesh-broker", "mesh-store",
/** Named once, because the step title is also how later steps say what they waited on. */
const NEEDS = "the mesh runs a broker for that module to talk to";
const GENESIS = "a bare machine becomes a mesh of one, raised by the installer";
const SUBSTRATE = "the substrate is up — a store and a broker of the mesh's own";
const BUILT_CP = "the control plane is one this mesh built, not one it was handed";
const PIVOTED = "the pivot finished — what raised the mesh is gone";
const HAS_REGISTRY = "the registry serves this mesh its own images";
const ENROLLED = "the machine is enrolled, and an agent is running on it";
const HAS_BUILDER = "the builder is installed as a module, with an account";
const BASE_BUILT = "the mesh builds the shared base from source";
const STORE_RUNS = "the mesh builds and runs a store of its own";
const CATALOGUE_RUNS = "the mesh builds and runs its own catalogue";
@@ -365,6 +371,69 @@ before(async () => {
return raised.report.join("\n");
});
// ---- WHAT GENESIS CLAIMED, ASKED OF THE MACHINE ----------------------------------------------
//
// **Genesis is twelve steps and was reported as one line.** All the work of raising a mesh
// happens inside it, so "genesis failed" said nothing about which of its claims broke, and
// "genesis passed" was one tick standing in for six separate things being true.
//
// Each is asked of the machine rather than read from the installer's own output — the installer
// saying it published an image and the registry serving one are different facts, and it is the
// second that matters.
await step(SUBSTRATE, GENESIS, async () => {
await waitForContainer(CONTROL, "mesh-store", 120);
await waitForContainer(CONTROL, "mesh-broker", 120);
return (await on(CONTROL, `docker ps --format '{{.Names}}\t{{.Status}}'`)).out;
});
// **The whole reason the installer carries a builder.** A carried control-plane image would
// satisfy "a control plane is running" equally well, which is what makes this worth asserting:
// the image has to be one this mesh's own registry serves.
await step(BUILT_CP, GENESIS, async () => {
const image = (await on(CONTROL,
`docker inspect -f '{{.Config.Image}}' mesh-control 2>&1`)).out.trim();
assert.match(image, /@sha256:[0-9a-f]{64}/,
`the control plane names its image by tag, not by digest: ${image}`);
assert.ok(image.includes(":5000/"),
`the control plane runs an image no registry of this mesh served: ${image}`);
return image;
});
await step(PIVOTED, BUILT_CP, async () => {
const ps = (await on(CONTROL, `docker ps -a --format '{{.Names}}'`)).out;
assert.doesNotMatch(ps, /^temp-mesh-control$/m,
`the temporary control plane is still here, so the pivot did not finish:\n${ps}`);
return ps;
});
await step(HAS_REGISTRY, SUBSTRATE, async () => {
await waitForContainer(CONTROL, "mesh-registry", 120);
const held = (await on(CONTROL,
`curl -sS --max-time 15 http://127.0.0.1:5000/v2/_catalog`)).out;
assert.match(held, /mesh-control/,
`the registry serves no mesh-control, so nothing was published into it:\n${held}`);
return held.trim();
});
await step(ENROLLED, GENESIS, async () => {
const nodes = await mesh("node list");
assert.match(nodes, new RegExp(`^${CONTROL}\\b`, "m"),
`the mesh has not heard from the machine it is running on:\n${nodes}`);
// Heard from once is not an agent running, and the difference is the whole of joining.
const agent = (await on(CONTROL, `pgrep -af '[m]esh-host run' | head -3`)).out.trim();
assert.ok(agent, `no host agent is running, so nothing would apply what the mesh sends`);
return `${nodes.trim()}\n${agent}`;
});
await step(HAS_BUILDER, HAS_REGISTRY, async () => {
await waitForContainer(CONTROL, "mesh-builder", 120);
const modules = await mesh("module list");
assert.match(modules, /^builder\b/m,
`the builder is running but the mesh holds no record of it as a module:\n${modules}`);
return modules;
});
// ---- 2..6. THE MESH BECOMES ONE --------------------------------------------------------------
//
// **Joining used to be here, second, and that was the wrong order.** Three machines were enrolled
@@ -380,7 +449,7 @@ before(async () => {
// The toolchain and runtime every module with code of its own stands on. It is a module, and it
// is built like one — cloned from the forge by the builder installing put here, compiled on the
// machine, published into the mesh's own registry.
await step(BASE_BUILT, GENESIS, async () => {
await step(BASE_BUILT, HAS_BUILDER, async () => {
// Registered from the manifest the builder will also read, so what the mesh holds and what it
// builds are the same description of the same module.
//
@@ -507,7 +576,8 @@ before(async () => {
// And the catalogue, ASKED rather than observed. These five questions are what it exists for.
const ask = async (tool: string, args = "{}") =>
must(CONTROL, `docker exec mesh-catalog mesh-tools invoke mesh-catalog ${tool} ${quote(args)}`,
must(CONTROL, `docker exec mesh-catalog node /app/dist/main.js invoke mesh-catalog ` +
`${tool} ${quote(args)}`,
120_000);
const held = await ask("catalog_modules");
@@ -631,6 +701,12 @@ after(async () => {
for (const name of [
GENESIS,
SUBSTRATE,
BUILT_CP,
PIVOTED,
HAS_REGISTRY,
ENROLLED,
HAS_BUILDER,
BASE_BUILT,
STORE_RUNS,
CATALOGUE_RUNS,