Both beds now pass a repository and a commit, and refuse to run without them rather than raising a machine the installer cannot finish.
113 lines
4.7 KiB
TypeScript
113 lines
4.7 KiB
TypeScript
/**
|
|
* 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"] ?? "";
|
|
// What the installer is told to build. It carries a builder rather than a finished control plane
|
|
// (novox/hq ADR 0073), so genesis needs a repository and a commit — and a commit rather than a
|
|
// branch, because what is cloned is the trust anchor for everything this mesh will ever run.
|
|
const source = process.env["MESH_LAB_SOURCE"] ?? "";
|
|
const sourceRef = process.env["MESH_LAB_SOURCE_REF"] ?? "";
|
|
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-builder:development`)" :
|
|
!source ? "MESH_LAB_SOURCE is not set to the repository the control plane is built from" :
|
|
!sourceRef ? "MESH_LAB_SOURCE_REF is not set to the commit to build" :
|
|
!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, []),
|
|
source,
|
|
sourceRef,
|
|
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"));
|
|
});
|