Raise a mesh of one by the installer, in a bed of its own
The four-node bed proves genesis entangled with three machines joining across a gateway, so the cheapest check of the install path costs a four-machine raise. This is genesis alone: one machine, the installer, and the question asked of the machine rather than inferred from an exit code. Genesis moves into a shared routine both beds call, rather than being described a second time here — a second description kept in step with the first is what put the whole procedure inside a fixture to begin with. The scenario needs two things the first draft missed, and both cost a full raise to discover: a way out to the internet, because the installer's first act is to pull the substrate; and a container runtime, because the installer's first refusal is a machine that has none. Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* A MESH OF ONE, raised by the installer and nothing else.
|
||||
*
|
||||
* The four-node bed proves genesis too, but it proves it entangled with three machines joining
|
||||
* afterwards across a gateway. This bed is genesis alone: one machine, the installer, and the
|
||||
* question "is this a working mesh?" asked of the machine.
|
||||
*
|
||||
* It shares its genesis with the four-node bed (`./genesis.ts`) rather than describing installing
|
||||
* a second time — a second description kept in step with the first is the arrangement that put
|
||||
* the whole procedure inside a fixture in the first place (novox/hq ADR 0067).
|
||||
*
|
||||
* MESH_LAB_INCUS='sudo -n incus'
|
||||
* MESH_LAB_HOST_BINARY=.../mesh-host/mesh-host
|
||||
* MESH_LAB_BOOTSTRAP_BINARY=.../mesh-host/mesh-bootstrap
|
||||
* MESH_LAB_BUNDLE=.../mesh-host/examples/substrate-first-node.lock
|
||||
* MESH_LAB_CATALOG=.../mesh-catalog/modules
|
||||
* MESH_LAB_KEEP=1 to leave it standing afterwards
|
||||
*/
|
||||
import { test, before, after } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { existsSync } from "node:fs";
|
||||
import { loadScenario } from "../../src/declaration/parse.ts";
|
||||
import { raise } from "../../src/lifecycle/raise.ts";
|
||||
import { destroy } from "../../src/lifecycle/operate.ts";
|
||||
import { bootstrapBinaryPath, hostBinaryPath } from "../../src/lifecycle/place.ts";
|
||||
import { labIsUsable, destroyAll, substrateBundle } from "./harness.ts";
|
||||
import { genesis, type GenesisResult } from "./genesis.ts";
|
||||
|
||||
const SCENARIO = "genesis-single";
|
||||
const NODE = "anchor";
|
||||
|
||||
const capability = await labIsUsable();
|
||||
const binary = hostBinaryPath();
|
||||
const installer = bootstrapBinaryPath();
|
||||
const bundle = process.env["MESH_LAB_BUNDLE"] ?? "";
|
||||
const catalogDir = process.env["MESH_LAB_CATALOG"] ?? "";
|
||||
const KEEP = !!process.env["MESH_LAB_KEEP"];
|
||||
const FIXED_ID = process.env["MESH_LAB_INSTANCE_ID"] ?? (KEEP ? "genesis-single-live" : undefined);
|
||||
|
||||
const skip =
|
||||
!capability.usable ? capability.why :
|
||||
!binary ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" :
|
||||
!installer ? "MESH_LAB_BOOTSTRAP_BINARY is not set to a built mesh-bootstrap (mesh-host `make " +
|
||||
"bootstrap IMAGE=mesh-control:development`)" :
|
||||
!bundle || !existsSync(bundle) ? "MESH_LAB_BUNDLE is not set to a substrate template" :
|
||||
!catalogDir || !existsSync(catalogDir) ? "MESH_LAB_CATALOG is not set to mesh-catalog/modules" :
|
||||
false;
|
||||
|
||||
let instanceId = "";
|
||||
let result: GenesisResult;
|
||||
|
||||
before(async () => {
|
||||
if (skip) return;
|
||||
|
||||
const raised = await raise(loadScenario(`scenarios/${SCENARIO}.yml`), {
|
||||
onProgress: (m) => console.log(`raise: ${m}`),
|
||||
...(FIXED_ID ? { instanceId: FIXED_ID } : {}),
|
||||
});
|
||||
instanceId = raised.instanceId;
|
||||
console.log(`INSTANCE ${instanceId}${KEEP ? " (KEEP — will be left standing)" : ""}`);
|
||||
|
||||
// Caught rather than allowed to propagate, so a failure BEFORE the installer ran is reported in
|
||||
// the same shape as one the installer itself named, instead of a bare stack trace.
|
||||
try {
|
||||
result = await genesis({
|
||||
instanceId,
|
||||
node: NODE,
|
||||
installer: installer as string,
|
||||
catalogDir,
|
||||
// The template, not a bundle. The store and broker become the references mesh-catalog pins,
|
||||
// and the machine pulls them over its uplink like any first node. mesh-control is left
|
||||
// naming a registry that does not exist — the installer overwrites it, and leaving it proves
|
||||
// that it does.
|
||||
bundleTemplate: substrateBundle(bundle, []),
|
||||
log: (m) => console.log(m),
|
||||
});
|
||||
} catch (err) {
|
||||
result = {
|
||||
ok: false,
|
||||
step: "getting the machine ready to be bootstrapped — the installer never ran",
|
||||
why: (err as Error).message,
|
||||
report: [],
|
||||
};
|
||||
}
|
||||
console.log(result.report.join("\n"));
|
||||
}, { timeout: 3_600_000 });
|
||||
|
||||
after(async () => {
|
||||
if (KEEP) {
|
||||
console.log(`\nLEFT STANDING: ${instanceId} — not destroyed (MESH_LAB_KEEP).`);
|
||||
return;
|
||||
}
|
||||
if (instanceId) await destroy(instanceId);
|
||||
await destroyAll(`${SCENARIO}-`);
|
||||
}, { timeout: 900_000 });
|
||||
|
||||
test("one machine becomes a working mesh of one, raised by the installer", {
|
||||
skip, timeout: 3_600_000,
|
||||
}, () => {
|
||||
assert.ok(result.ok,
|
||||
`GENESIS FAILED — ${result.step || "no step named"}.\n\n${result.why}\n\n` +
|
||||
result.report.join("\n"));
|
||||
});
|
||||
Reference in New Issue
Block a user