The catalogue is named or absent, the scanner reads both key orders, the loader has unit tests
Review findings: a sibling-path fallback read a catalogue the receipt never claimed; a manifest literal naming its version first slipped the fence; the shared loader thirteen beds install through had no test short of a lab run.
This commit is contained in:
@@ -16,8 +16,9 @@ import { catalogueIsPresent, catalogueDir } from "./integration/harness.ts";
|
|||||||
* So: a manifest literal in a bed that names a catalogue module is refused, unless the bed is
|
* So: a manifest literal in a bed that names a catalogue module is refused, unless the bed is
|
||||||
* listed below with the reason it still carries one. The list is the debt, and it only shrinks.
|
* listed below with the reason it still carries one. The list is the debt, and it only shrinks.
|
||||||
*
|
*
|
||||||
* What this reads: `module: "<name>"` and `"module": "<name>"` with a `version` close behind, in
|
* What this reads: `module: "<name>"` and `"module": "<name>"` with a `version` close by, either
|
||||||
* test/integration/*.test.ts, against the catalogue's directory names. A bed that hid the name
|
* order, in test/integration/*.test.ts, against the catalogue's directory names. Skipped aloud
|
||||||
|
* where MESH_LAB_CATALOG is unset — a skip is reported, never silent. A bed that hid the name
|
||||||
* behind a computed string would pass — this is a fence, not a proof, and the reviewer of a bed
|
* behind a computed string would pass — this is a fence, not a proof, and the reviewer of a bed
|
||||||
* that builds a manifest inline is the proof.
|
* that builds a manifest inline is the proof.
|
||||||
*/
|
*/
|
||||||
@@ -88,6 +89,10 @@ test("a bed that installs a catalogue module reads the catalogue", (t) => {
|
|||||||
for (const m of text.matchAll(/(?:^|[\s{,])(?:"module"|module)\s*:\s*"([a-z0-9.-]+)"[^}]{0,160}?(?:"version"|version)\s*:/g)) {
|
for (const m of text.matchAll(/(?:^|[\s{,])(?:"module"|module)\s*:\s*"([a-z0-9.-]+)"[^}]{0,160}?(?:"version"|version)\s*:/g)) {
|
||||||
if (names.has(m[1]!)) found.add(m[1]!);
|
if (names.has(m[1]!)) found.add(m[1]!);
|
||||||
}
|
}
|
||||||
|
// And the other order — a literal that names its version first.
|
||||||
|
for (const m of text.matchAll(/(?:^|[\s{,])(?:"version"|version)\s*:\s*"[^"]*"[^}]{0,160}?(?:"module"|module)\s*:\s*"([a-z0-9.-]+)"/g)) {
|
||||||
|
if (names.has(m[1]!)) found.add(m[1]!);
|
||||||
|
}
|
||||||
const declared = STILL_CARRIED[file];
|
const declared = STILL_CARRIED[file];
|
||||||
for (const name of [...found].sort()) {
|
for (const name of [...found].sort()) {
|
||||||
if (declared?.modules.includes(name)) continue;
|
if (declared?.modules.includes(name)) continue;
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import { test } from "node:test";
|
||||||
|
import assert from "node:assert/strict";
|
||||||
|
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import { join } from "node:path";
|
||||||
|
|
||||||
|
import { catalogueModule } from "./integration/harness.ts";
|
||||||
|
import type { HeldImage } from "../src/pinning.ts";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The shared loader thirteen beds install through (novox/hq 04-ISSUES/073, ADR 0089): it reads
|
||||||
|
* the catalogue's manifest and rewrites only what the lab must. Checked here against a catalogue
|
||||||
|
* written by the test, so the rules hold without a lab run.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const digest = (c: string) => "sha256:" + c.repeat(64);
|
||||||
|
const held: HeldImage[] = [
|
||||||
|
{ requested: "mesh-runtime-thing:development", repository: "mesh-runtime-thing", reference: digest("a") },
|
||||||
|
{ requested: "mesh-helper:development", repository: "mesh-helper", reference: digest("b") },
|
||||||
|
];
|
||||||
|
|
||||||
|
function aCatalogueWith(manifest: object): () => void {
|
||||||
|
const root = mkdtempSync(join(tmpdir(), "mesh-lab-catalogue-"));
|
||||||
|
mkdirSync(join(root, "modules", "distribution"), { recursive: true });
|
||||||
|
writeFileSync(join(root, "modules", "distribution", "module.json"), "{}");
|
||||||
|
mkdirSync(join(root, "modules", "thing"));
|
||||||
|
writeFileSync(join(root, "modules", "thing", "module.json"), JSON.stringify(manifest));
|
||||||
|
const before = process.env["MESH_LAB_CATALOG"];
|
||||||
|
process.env["MESH_LAB_CATALOG"] = root;
|
||||||
|
return () => {
|
||||||
|
if (before === undefined) delete process.env["MESH_LAB_CATALOG"]; else process.env["MESH_LAB_CATALOG"] = before;
|
||||||
|
rmSync(root, { recursive: true, force: true });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const thing = {
|
||||||
|
module: "thing", version: "1",
|
||||||
|
resources: [
|
||||||
|
{ id: "server", type: "container", name: "thing", image: "postgres@" + digest("c"), ports: ["8080", "9090:9090"], env: { A: "1" } },
|
||||||
|
{ id: "runtime", type: "container", name: "mesh-thing", artifact: "runtime" },
|
||||||
|
],
|
||||||
|
build: { artifacts: [{ name: "runtime", kind: "image", from: "Dockerfile" }] },
|
||||||
|
};
|
||||||
|
|
||||||
|
test("the runtime artifact becomes the image the machine holds, and the build section goes", () => {
|
||||||
|
const restore = aCatalogueWith(thing);
|
||||||
|
try {
|
||||||
|
const m = JSON.parse(catalogueModule("thing", held)) as { build?: unknown; resources: Record<string, unknown>[] };
|
||||||
|
assert.equal(m.build, undefined);
|
||||||
|
const runtime = m.resources.find((r) => r["id"] === "runtime")!;
|
||||||
|
assert.equal(runtime["image"], digest("a"));
|
||||||
|
assert.equal(runtime["artifact"], undefined);
|
||||||
|
// An image already pinned to a digest passes through as written.
|
||||||
|
assert.equal(m.resources.find((r) => r["id"] === "server")!["image"], "postgres@" + digest("c"));
|
||||||
|
} finally { restore(); }
|
||||||
|
});
|
||||||
|
|
||||||
|
test("an artifact the bed did not name is refused, and a named one resolves to the stocked image", () => {
|
||||||
|
const helper = { ...thing, resources: [{ id: "helper", type: "container", name: "h", artifact: "helper" }] };
|
||||||
|
const restore = aCatalogueWith(helper);
|
||||||
|
try {
|
||||||
|
assert.throws(() => catalogueModule("thing", held), /names the "helper" artifact/);
|
||||||
|
const m = JSON.parse(catalogueModule("thing", held, { artifacts: { helper: "mesh-helper" } })) as { resources: Record<string, unknown>[] };
|
||||||
|
assert.equal(m.resources[0]!["image"], digest("b"));
|
||||||
|
assert.throws(() => catalogueModule("thing", held, { artifacts: { helper: "mesh-nothing" } }), /stocked no such image/);
|
||||||
|
} finally { restore(); }
|
||||||
|
});
|
||||||
|
|
||||||
|
test("a host-port remap and a lab address are the only other things that change", () => {
|
||||||
|
const restore = aCatalogueWith(thing);
|
||||||
|
try {
|
||||||
|
const m = JSON.parse(catalogueModule("thing", held, {
|
||||||
|
ports: { "8080": "8090:8080" },
|
||||||
|
env: { server: { B: "2" } },
|
||||||
|
})) as { resources: Record<string, unknown>[] };
|
||||||
|
const server = m.resources.find((r) => r["id"] === "server")!;
|
||||||
|
assert.deepEqual(server["ports"], ["8090:8080", "9090:9090"]);
|
||||||
|
assert.deepEqual(server["env"], { A: "1", B: "2" });
|
||||||
|
} finally { restore(); }
|
||||||
|
});
|
||||||
|
|
||||||
|
test("a manifest the catalogue does not have is refused by name", () => {
|
||||||
|
const restore = aCatalogueWith(thing);
|
||||||
|
try {
|
||||||
|
assert.throws(() => catalogueModule("nothing", held), /no manifest for nothing/);
|
||||||
|
} finally { restore(); }
|
||||||
|
});
|
||||||
@@ -241,7 +241,9 @@ export const FILTER_MODULE = "nftables";
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The catalogue's `modules/` directory: MESH_LAB_CATALOG under either spelling (the checkout, or
|
* The catalogue's `modules/` directory: MESH_LAB_CATALOG under either spelling (the checkout, or
|
||||||
* its modules directory), else the checkout beside this one, the way the main layout has it.
|
* its modules directory). Named, or absent — never guessed from a sibling path: the receipt claims
|
||||||
|
* the catalogue the run was pointed at (src/repos.ts), and a catalogue read from somewhere the
|
||||||
|
* receipt does not name is the drift this exists to close.
|
||||||
*
|
*
|
||||||
* Beds used to build the manifests they install inline, as literals copied from the catalogue when
|
* Beds used to build the manifests they install inline, as literals copied from the catalogue when
|
||||||
* each bed was written. The copies did not move when the catalogue did, so a catalogue change was
|
* each bed was written. The copies did not move when the catalogue did, so a catalogue change was
|
||||||
@@ -251,16 +253,14 @@ export const FILTER_MODULE = "nftables";
|
|||||||
*/
|
*/
|
||||||
export function catalogueDir(): string {
|
export function catalogueDir(): string {
|
||||||
const named = process.env["MESH_LAB_CATALOG"];
|
const named = process.env["MESH_LAB_CATALOG"];
|
||||||
const candidates = named
|
if (!named) throw new Error("MESH_LAB_CATALOG is not set to a checkout of mesh-catalog (or its modules directory)");
|
||||||
? [resolve(named, "modules"), resolve(named)]
|
const candidates = [resolve(named, "modules"), resolve(named)];
|
||||||
: [resolve(process.cwd(), "..", "mesh-catalog", "modules")];
|
|
||||||
for (const dir of candidates) {
|
for (const dir of candidates) {
|
||||||
// Known by the registry's manifest, which genesis reads from the catalogue and always will —
|
// Known by the registry's manifest, which genesis reads from the catalogue and always will —
|
||||||
// not the control plane's, which lives in the control plane's own repository (ADR 0069).
|
// not the control plane's, which lives in the control plane's own repository (ADR 0069).
|
||||||
if (existsSync(resolve(dir, "distribution", "module.json"))) return dir;
|
if (existsSync(resolve(dir, "distribution", "module.json"))) return dir;
|
||||||
}
|
}
|
||||||
throw new Error(
|
throw new Error(`no catalogue: MESH_LAB_CATALOG=${named} and no distribution/module.json under ${candidates.join(" or ")}`);
|
||||||
`no catalogue: MESH_LAB_CATALOG=${named ?? "(unset)"} and nothing at ${candidates.join(", ")}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Whether a catalogue is where a bed will look — for a skip guard, which says so instead of failing. */
|
/** Whether a catalogue is where a bed will look — for a skip guard, which says so instead of failing. */
|
||||||
|
|||||||
Reference in New Issue
Block a user