From 0ffb24ff5d317b1cf43b2f2e94f92daf44e666b2 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 1 Sep 2026 02:44:32 +0200 Subject: [PATCH] Write down what a run has to be pointed at MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reconstructed from the source twice now, which is 04-ISSUES/005 in its own README: a test whose artifact was not pointed at skips rather than fails, so an unset variable is a green run that proved nothing. The first attempt today reported "skipped 24" and left a receipt claiming zero of everything — working exactly as designed, and indistinguishable at a glance from a suite that had nothing to do. Also records the two things that cost time either side of it: `check` says which variables are missing before a long run rather than skipping quietly, and a heredoc into `newgrp` runs the suite as a child of a shell that immediately exits, so it needs `setsid nohup … &` or it dies with the shell that launched it. --- README.md | 34 ++++++++++++++++ test/integration/mesh.test.ts | 75 +++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/README.md b/README.md index 77fb1eb..27119ea 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,40 @@ Placing needs a built host binary — set `MESH_LAB_HOST_BINARY` to one. It is a rather than a search on purpose: the declaration design leaves *where `place:` gets its artifacts from* open, and guessing would harden into the answer by accident. +## Pointing a run at the repositories + +**Every variable is an explicit path, and none of them has a default.** A test whose artifact was +not pointed at *skips* — it does not fail — so an unset variable is a green run that proved +nothing. That is `novox/hq` 04-ISSUES/005 exactly, and it has now been rediscovered twice, so it +is written down here rather than reconstructed a third time. + +```sh +export MESH_LAB_HOST_BINARY=/mesh-host +export MESH_LAB_BUNDLE=/examples/substrate-first-node.lock +export MESH_LAB_MODULES=/examples/modules +export MESH_LAB_BUILDER=/mesh-builder + +# Built with `go build -o ./examples/` in mesh-control. +export MESH_LAB_PROVISIONER=/postgres-provisioner +export MESH_LAB_OBJECTSTORE_PROVISIONER=/objectstore-provisioner +export MESH_LAB_ROUTE_PROXY=/route-proxy +``` + +`MESH_LAB_HOST_BINARY` and `MESH_LAB_MODULES` do double duty: the repository each sits in is what +`suite` rebuilds and what the receipt claims. Point the run at a repository and it is built and +claimed; leave it out and it is neither. + +Check before running a long suite — it says which of these are missing rather than skipping +quietly: + +```sh +node --experimental-strip-types src/cli.ts check +``` + +If it says the daemon is not reachable, the group grant postdates the shell. `newgrp` fixes it, +but a heredoc into `newgrp` runs the suite as a child of a shell that then exits — start it with +`setsid nohup … &` inside the heredoc, or the run dies with the shell that launched it. + ## Measured on a workstation | | one machine | two machines | two machines + a router | diff --git a/test/integration/mesh.test.ts b/test/integration/mesh.test.ts index 057e6be..e62cac4 100644 --- a/test/integration/mesh.test.ts +++ b/test/integration/mesh.test.ts @@ -1658,3 +1658,78 @@ test("a third-party workload is adopted, with the credential it already had", { assert.doesNotMatch(reached, /unreachable/, "a container could not reach the other by name, so the module's network did nothing"); }); + +// The real modules, resolved together on one machine. +// +// **What this proves without pulling a gigabyte of images**: that five manifests written from the +// running system resolve as a graph — keycloak's requirement met by postgres's provision, +// capabilities checked, nothing claiming the same singular thing — and that the declaration the +// control plane composes is one the host accepts. `plan --json` exists for exactly this: it is +// the only way to know that what the control plane emits is what the host takes. +// +// Running them needs their images stocked and two provisioners built, which is a separate and +// larger job. This is the half that can be known now, and it is the half where a design fault +// would live. +test("the real modules resolve together, and compose a declaration a host accepts", { + skip, timeout: 300_000, +}, async () => { + const modules = ["postgres", "keycloak", "gitea", "minio", "mailu"]; + for (const name of modules) { + const raw = readFileSync( + `${process.env["MESH_LAB_MODULES"]}/${name}.json`, "utf8"); + await must("anchor", `printf %s ${quote(raw)} > /${name}.json`); + await must("anchor", `docker cp /${name}.json mesh-control:/${name}.json`); + await mesh(`module add /${name}.json`); + } + + // Assigned one at a time, because assignment resolves the whole set and says so immediately. + // A refusal here is the graph rejecting something, which is the point of asking. + for (const name of modules) { + await mesh(`assign anchor ${name}`); + } + + const plan = await mesh("plan anchor --json", 120_000); + const declaration = JSON.parse(plan.slice(plan.indexOf("{"))); + const byId = new Map( + (declaration.resources as any[]).map((r) => [r.id, r])); + const ids = [...byId.keys()]; + + // Every module's own network, which only exists because more than one container needs to reach + // another by name. + for (const id of ["postgres.net", "keycloak.net", "minio.net", "mailu.net"]) { + assert.ok(byId.has(id), `${id} is missing; ${ids.length} resources: ${ids.join(", ")}`); + assert.equal(byId.get(id).type, "network"); + } + + // The cross-module edge: keycloak asked for a database and was told where it is and given a + // credential. Neither file is anything keycloak's manifest could have written. + const bound = [...byId.values()].find((r) => + r.type === "file" && r.path === "/var/lib/keycloak/database.json"); + assert.ok(bound, `keycloak was never told where its database is: ${ids.join(", ")}`); + assert.match(JSON.stringify(bound), /postgres/, + "keycloak's binding does not name what answered its requirement"); + + const credential = [...byId.values()].find((r) => + r.type === "file" && r.path === "/var/lib/keycloak/database.env"); + assert.ok(credential, "keycloak was given no credential for its database"); + assert.ok(credential.sealed, "keycloak's credential is not sealed, so the mesh can read it"); + assert.ok(!credential.content, "a credential arrived as content rather than sealed"); + + // And the provider was told who asked, which is what its provisioner reconciles against. + const grants = [...byId.values()].find((r) => + r.type === "file" && String(r.path).startsWith("/var/lib/postgres/grants")); + assert.ok(grants, "postgres was never told which modules were granted a database"); + assert.match(JSON.stringify(grants), /keycloak|gitea/, + "the grants file names neither module that asked for a database"); + + // Secrets reach containers as files, never as environment in the declaration. + const containers = [...byId.values()].filter((r) => r.type === "container"); + assert.ok(containers.length >= 12, + `only ${containers.length} containers; mailu alone is nine`); + for (const c of containers) { + for (const [key, value] of Object.entries(c.env ?? {})) { + assert.doesNotMatch(String(value), /^[A-Za-z0-9+/]{24,}={0,2}$/, + `${c.name} carries something secret-shaped in env.${key}, which the broker would see`); + } + } +});