Rename mesh-control -> mesh-controller, substrate -> foundation
One name per thing, per the HQ glossary: the module/container/image/binary/repo becomes mesh-controller, the seat the-controller, and the store+broker pair the foundation (embedded base bundles, default template and example lock renamed with their go:embed directives). No behaviour change — a pure vocabulary rename. Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
/**
|
||||
* A lavinmq PROVIDER and a consumer of it ride one node while the substrate's own broker owns 5672 on
|
||||
* A lavinmq PROVIDER and a consumer of it ride one node while the foundation's own broker owns 5672 on
|
||||
* another — the end-to-end proof that a module which needs a message queue gets its OWN broker.
|
||||
*
|
||||
* lavinmq is the mesh's control-plane broker AND a user-facing capability: a consumer that requires
|
||||
* `amqp` is given a scoped vhost + user on a lavinmq PROVIDER, not an account on the control broker
|
||||
* (novox/hq ADR 0048). That makes it the two-node case, the twin of two-node-db: the substrate's
|
||||
* (novox/hq ADR 0048). That makes it the two-node case, the twin of two-node-db: the foundation's
|
||||
* broker publishes 5672 on anchor, and a lavinmq provider must publish 5672 for its consumers to
|
||||
* reach it — so the two cannot share a machine. `anchor` runs the substrate and nothing else; `laptop`
|
||||
* reach it — so the two cannot share a machine. `anchor` runs the foundation and nothing else; `laptop`
|
||||
* runs the provider AND the amqp-ping consumer, co-located, and owns laptop's 5672 uncontested.
|
||||
*
|
||||
* The proof is layered:
|
||||
@@ -14,7 +14,7 @@
|
||||
* config BEFORE the broker started (ADR 0052) — proven because the broker came up at all and is
|
||||
* gone from `docker ps -a` (a step, not a service);
|
||||
* - the lavinmq service and BOTH runtimes (bootstrap done, provisioner running) are up and stable;
|
||||
* - each module got its own scoped broker account on the substrate broker (anchor), named for the
|
||||
* - each module got its own scoped broker account on the foundation broker (anchor), named for the
|
||||
* node that runs it;
|
||||
* - the provisioner (in mesh-lavinmq on laptop) created the consumer's vhost AND user, both named
|
||||
* for the login the mesh derived — checked with `lavinmqctl` inside the broker;
|
||||
@@ -25,12 +25,12 @@
|
||||
* its vhost — the provider named the vhost after the login — and nothing is hardcoded; the provider's
|
||||
* `serves` carries the port so the consumer references `${bound:amqp:port}`.
|
||||
*
|
||||
* MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock
|
||||
* MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock
|
||||
*
|
||||
* HELPER — stock the two runtimes into the local daemon before the run (some may already be there):
|
||||
* scripts/build-module-runtime.sh lavinmq /tmp/lavinmq.tar
|
||||
* scripts/build-module-runtime.sh amqp-ping /tmp/amqp-ping.tar
|
||||
* cloudamqp/lavinmq:latest must be in the local daemon too (it is the substrate's own broker image);
|
||||
* cloudamqp/lavinmq:latest must be in the local daemon too (it is the foundation's own broker image);
|
||||
* scenarios/lavinmq-bed.yml stocks all of them, and each node pulls what it runs by digest.
|
||||
*/
|
||||
|
||||
@@ -41,7 +41,7 @@ import { loadScenario } from "../../src/declaration/parse.ts";
|
||||
import { raise } from "../../src/lifecycle/raise.ts";
|
||||
import { destroy, exec } from "../../src/lifecycle/operate.ts";
|
||||
import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts";
|
||||
import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts";
|
||||
import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts";
|
||||
import type { HeldImage } from "../../src/pinning.ts";
|
||||
|
||||
const capability = await labIsUsable();
|
||||
@@ -53,11 +53,11 @@ const skip = !capability.usable
|
||||
: !binary || !existsSync(binary)
|
||||
? "MESH_LAB_HOST_BINARY is not set to a built mesh-host"
|
||||
: !bundle || !existsSync(bundle)
|
||||
? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)"
|
||||
? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)"
|
||||
: false;
|
||||
|
||||
const SCENARIO = "lavinmq-bed";
|
||||
/** The node that carries the provider and its consumer. anchor carries only the substrate. */
|
||||
/** The node that carries the provider and its consumer. anchor carries only the foundation. */
|
||||
const NODE = "laptop";
|
||||
/** The login the mesh derives for the consumer: mesh_<node>_<slug> (slug "ping"; ADR 0049). */
|
||||
const CONSUMER_LOGIN = "mesh_laptop_ping";
|
||||
@@ -86,7 +86,7 @@ async function must(machine: string, command: string, timeoutMs?: number): Promi
|
||||
|
||||
/** The control plane, a container on the first node. */
|
||||
async function mesh(command: string, timeoutMs?: number): Promise<string> {
|
||||
return must("anchor", `docker exec mesh-control /mesh-control ${command}`, timeoutMs);
|
||||
return must("anchor", `docker exec mesh-controller /mesh-controller ${command}`, timeoutMs);
|
||||
}
|
||||
|
||||
/** The reference a manifest should carry, once this scenario has been raised. */
|
||||
@@ -95,9 +95,9 @@ function pinned(reference: string): string {
|
||||
return onTheMachine(reference, held);
|
||||
}
|
||||
|
||||
/** The substrate bundle: ours by the ID the machine holds, everything else upstream. */
|
||||
/** The foundation bundle: ours by the ID the machine holds, everything else upstream. */
|
||||
function bundleFor(images: HeldImage[]): string {
|
||||
return substrateBundle(bundle, images);
|
||||
return foundationBundle(bundle, images);
|
||||
}
|
||||
|
||||
function tokenFrom(said: string): string {
|
||||
@@ -118,7 +118,7 @@ async function settled(node: string, withinMs = 1_200_000): Promise<void> {
|
||||
} | undefined;
|
||||
let said = "";
|
||||
try {
|
||||
const asked = await on("anchor", `docker exec mesh-control /mesh-control status --json`);
|
||||
const asked = await on("anchor", `docker exec mesh-controller /mesh-controller status --json`);
|
||||
said = asked.out;
|
||||
if (asked.ok) state = JSON.parse(said);
|
||||
} catch (err) {
|
||||
@@ -157,11 +157,11 @@ before(async () => {
|
||||
instanceId = raised.instanceId;
|
||||
held = raised.images;
|
||||
|
||||
await must("anchor", `cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`);
|
||||
await must("anchor", `${HOST_PATH} apply /tmp/substrate.lock`, 600_000);
|
||||
await must("anchor", `cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`);
|
||||
await must("anchor", `${HOST_PATH} apply /tmp/foundation.lock`, 600_000);
|
||||
const up = await must("anchor", `docker ps --format '{{.Names}}'`);
|
||||
for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) {
|
||||
assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`);
|
||||
for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) {
|
||||
assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`);
|
||||
}
|
||||
|
||||
for (const [machine, node] of [["anchor", "anchor"], ["laptop", "laptop"]] as const) {
|
||||
@@ -178,7 +178,7 @@ after(async () => {
|
||||
await destroyAll(`${SCENARIO}-`);
|
||||
}, { timeout: 600_000 });
|
||||
|
||||
test("the lavinmq provider and its consumer ride laptop while the substrate broker owns 5672 on anchor", {
|
||||
test("the lavinmq provider and its consumer ride laptop while the foundation broker owns 5672 on anchor", {
|
||||
skip, timeout: 1_500_000,
|
||||
}, async () => {
|
||||
// ================================================================================================
|
||||
@@ -287,7 +287,7 @@ test("the lavinmq provider and its consumer ride laptop while the substrate brok
|
||||
|
||||
// --- add, issue a scoped broker account, assign to laptop --------------------------------------
|
||||
async function addIssueAssign(name: string, manifest: string): Promise<void> {
|
||||
await must("anchor", `printf %s ${quote(manifest)} > /tmp/${name}.json && docker cp /tmp/${name}.json mesh-control:/${name}.json`);
|
||||
await must("anchor", `printf %s ${quote(manifest)} > /tmp/${name}.json && docker cp /tmp/${name}.json mesh-controller:/${name}.json`);
|
||||
await mesh(`module add /${name}.json`);
|
||||
const issued = await mesh(`module issue ${name} --node ${NODE}`);
|
||||
assert.match(issued, /broker account/, issued);
|
||||
@@ -311,14 +311,14 @@ test("the lavinmq provider and its consumer ride laptop while the substrate brok
|
||||
await settled(NODE);
|
||||
|
||||
// ================================================================================================
|
||||
// THE two-node split — the substrate broker owns 5672 on anchor, the provider owns it on laptop.
|
||||
// THE two-node split — the foundation broker owns 5672 on anchor, the provider owns it on laptop.
|
||||
// ================================================================================================
|
||||
const onAnchor = await must("anchor", `docker ps --format '{{.Names}}'`);
|
||||
const onLaptop = await must(NODE, `docker ps --format '{{.Names}}'`);
|
||||
assert.match(onAnchor, /(^|\n)mesh-broker(\n|$)/, "the substrate broker is not on the first node");
|
||||
assert.match(onAnchor, /(^|\n)mesh-broker(\n|$)/, "the foundation broker is not on the first node");
|
||||
assert.doesNotMatch(onAnchor, /(^|\n)lavinmq(\n|$)/,
|
||||
"the lavinmq provider landed on the substrate node — the 5672 collision this bed exists to avoid");
|
||||
assert.doesNotMatch(onLaptop, /(^|\n)mesh-store(\n|$)/, "the substrate store leaked onto the second node");
|
||||
"the lavinmq provider landed on the foundation node — the 5672 collision this bed exists to avoid");
|
||||
assert.doesNotMatch(onLaptop, /(^|\n)mesh-store(\n|$)/, "the foundation store leaked onto the second node");
|
||||
assert.match(onLaptop, /(^|\n)lavinmq(\n|$)/, "the lavinmq provider is not on the second node");
|
||||
|
||||
// ================================================================================================
|
||||
@@ -359,7 +359,7 @@ test("the lavinmq provider and its consumer ride laptop while the substrate brok
|
||||
}
|
||||
|
||||
// ================================================================================================
|
||||
// Each module got its own scoped broker account on the substrate broker (anchor), named for the
|
||||
// Each module got its own scoped broker account on the foundation broker (anchor), named for the
|
||||
// node that runs it and the module.
|
||||
// ================================================================================================
|
||||
const users = await must("anchor", `docker exec mesh-broker lavinmqctl list_users 2>&1`);
|
||||
|
||||
Reference in New Issue
Block a user