From 2530ca762e22baa249cc1afc268f9586618036c0 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 21 Sep 2026 22:33:44 +0200 Subject: [PATCH] The lab stands in for the builder on an upstream artifact too: the reference the manifest pins hello-web's server is somebody else's image the mesh would copy in (ADR 0096); the loader refused it as an artifact nobody stocked. --- test/catalogue-module.test.ts | 14 ++++++++++++++ test/integration/harness.ts | 14 ++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/test/catalogue-module.test.ts b/test/catalogue-module.test.ts index 45195e3..6ebab3a 100644 --- a/test/catalogue-module.test.ts +++ b/test/catalogue-module.test.ts @@ -85,3 +85,17 @@ test("a manifest the catalogue does not have is refused by name", () => { assert.throws(() => catalogueModule("nothing", held), /no manifest for nothing/); } finally { restore(); } }); + +test("an upstream artifact resolves to the reference the manifest pins, as the machine pulls it", () => { + const web = { + module: "thing", version: "1", + resources: [{ id: "server", type: "container", name: "web", artifact: "server" }], + build: { artifacts: [{ name: "server", kind: "upstream", from: "alpine@" + digest("e") }] }, + }; + const restore = aCatalogueWith(web); + try { + const m = JSON.parse(catalogueModule("thing", held)) as { resources: Record[] }; + assert.equal(m.resources[0]!["image"], "alpine@" + digest("e")); + assert.equal(m.resources[0]!["artifact"], undefined); + } finally { restore(); } +}); diff --git a/test/integration/harness.ts b/test/integration/harness.ts index a85422c..6c3cded 100644 --- a/test/integration/harness.ts +++ b/test/integration/harness.ts @@ -306,12 +306,22 @@ export interface ForTheLab { export function catalogueModule(module: string, held: HeldImage[], lab: ForTheLab = {}): string { const m = JSON.parse(readFileSync(catalogueManifest(module), "utf8")) as { resources?: { id: string; type: string; image?: string; artifact?: string; ports?: string[]; env?: Record }[]; - build?: unknown; + build?: { artifacts?: { name: string; kind: string; from?: string }[] }; }; const artifacts: Record = { runtime: `mesh-runtime-${module}`, ...(lab.artifacts ?? {}) }; + // An upstream artifact is somebody else's image, which the mesh's builder copies into its own + // registry (ADR 0096). The lab stands in for the builder by using the reference the manifest + // pins, which the machine pulls over its uplink — the same bytes, without the copy. + const upstream = new Map(); + for (const a of m.build?.artifacts ?? []) { + if (a.kind === "upstream" && a.from) upstream.set(a.name, a.from); + } for (const r of m.resources ?? []) { if (r.type !== "container") continue; - if (typeof r.artifact === "string") { + if (typeof r.artifact === "string" && upstream.has(r.artifact) && !lab.artifacts?.[r.artifact]) { + r.image = onTheMachine(upstream.get(r.artifact)!, held); + delete r.artifact; + } else if (typeof r.artifact === "string") { const repository = artifacts[r.artifact]; assert.ok(repository, `${module}'s container '${r.id}' names the "${r.artifact}" artifact, which the mesh would ` +