Repoint ADR references after HQ consolidated 65 records to 23
Comments naming records that no longer exist now point at the consolidated record holding their reasoning -- the four lab records are 0016, a test defends a decision is 0017.
This commit is contained in:
@@ -140,7 +140,7 @@ artifacts from* open, and guessing would harden into the answer by accident.
|
|||||||
| restore, to usable again | 10.5 s | 11.6 s | — |
|
| restore, to usable again | 10.5 s | 11.6 s | — |
|
||||||
|
|
||||||
A router adds seconds, not a boot: it is a container, because it is scenery rather than
|
A router adds seconds, not a boot: it is a container, because it is scenery rather than
|
||||||
something under test (`novox/hq` ADR 0033).
|
something under test (`novox/hq` ADR 0016).
|
||||||
|
|
||||||
**Verified by running**, not asserted — a machine at `192.168.1.135` behind a household
|
**Verified by running**, not asserted — a machine at `192.168.1.135` behind a household
|
||||||
gateway, reached from a machine on a routable address:
|
gateway, reached from a machine on a routable address:
|
||||||
@@ -242,19 +242,19 @@ npm run typecheck source and tests both — a test that does not compil
|
|||||||
npm run check typecheck + both suites — this is the gate
|
npm run check typecheck + both suites — this is the gate
|
||||||
```
|
```
|
||||||
|
|
||||||
**A test names the decision it defends** (`novox/hq` ADR 0034). A decision with no test is one
|
**A test names the decision it defends** (`novox/hq` ADR 0017). A decision with no test is one
|
||||||
that will quietly stop being true, and nobody learns that from a document:
|
that will quietly stop being true, and nobody learns that from a document:
|
||||||
|
|
||||||
| Test | Defends |
|
| Test | Defends |
|
||||||
|---|---|
|
|---|---|
|
||||||
| the lab provides the underlay and nothing of the overlay | ADR 0031 |
|
| the lab provides the underlay and nothing of the overlay | ADR 0016 |
|
||||||
| the workstation has no route into the scenario | ADR 0032 |
|
| the workstation has no route into the scenario | ADR 0016 |
|
||||||
| a router is a container while machines are virtual machines | ADR 0033 |
|
| a router is a container while machines are virtual machines | ADR 0016 |
|
||||||
| raise waits for *usable*, not for the call to return | the lifecycle design |
|
| raise waits for *usable*, not for the call to return | the lifecycle design |
|
||||||
| snapshots are whole-scenario | the lifecycle design |
|
| snapshots are whole-scenario | the lifecycle design |
|
||||||
| a public range that is not documentation space is refused | the declaration design |
|
| a public range that is not documentation space is refused | the declaration design |
|
||||||
| a scenario declaring what cannot be materialised is refused | the declaration design |
|
| a scenario declaring what cannot be materialised is refused | the declaration design |
|
||||||
| the live diagram distinguishes scenery from a node | ADR 0033 |
|
| the live diagram distinguishes scenery from a node | ADR 0016 |
|
||||||
| the live diagram draws what exists, never what was asked for | the diagram design |
|
| the live diagram draws what exists, never what was asked for | the diagram design |
|
||||||
| a picture nobody can open is not a picture | the diagram design |
|
| a picture nobody can open is not a picture | the diagram design |
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -93,7 +93,7 @@ async function main(): Promise<void> {
|
|||||||
|
|
||||||
case "base": {
|
case "base": {
|
||||||
// `base build` exists because a sealed scenario cannot install a container runtime, and
|
// `base build` exists because a sealed scenario cannot install a container runtime, and
|
||||||
// the runtime has to come from somewhere with a network (novox/hq ADR 0046).
|
// the runtime has to come from somewhere with a network (novox/hq ADR 0006).
|
||||||
if (rest[0] !== "build") fail("base needs a subcommand: build");
|
if (rest[0] !== "build") fail("base needs a subcommand: build");
|
||||||
const { buildBaseImage } = await import("./lifecycle/base.ts");
|
const { buildBaseImage } = await import("./lifecycle/base.ts");
|
||||||
const built = await buildBaseImage((line) => console.log(line));
|
const built = await buildBaseImage((line) => console.log(line));
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Building the base image a scenario's machines are raised from.
|
* Building the base image a scenario's machines are raised from.
|
||||||
*
|
*
|
||||||
* A sealed scenario cannot install a container runtime: its segments use documentation ranges
|
* A sealed scenario cannot install a container runtime: its segments use documentation ranges
|
||||||
* and there is no route out (novox/hq ADR 0032). ADR 0046 records the consequence — *the lab
|
* and there is no route out (novox/hq ADR 0016). ADR 0006 records the consequence — *the lab
|
||||||
* needs a way to place images, and the machine it places them into needs a container runtime,
|
* needs a way to place images, and the machine it places them into needs a container runtime,
|
||||||
* which a sealed scenario cannot install either.*
|
* which a sealed scenario cannot install either.*
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ export interface PlacedHost {
|
|||||||
* Put the host on a machine and ask it what the machine is.
|
* Put the host on a machine and ask it what the machine is.
|
||||||
*
|
*
|
||||||
* The result is read back from the running binary, never assumed from the fact that the copy
|
* The result is read back from the running binary, never assumed from the fact that the copy
|
||||||
* succeeded (novox/hq ADR 0035). A file arriving is not a host working, which is the same
|
* succeeded (novox/hq ADR 0018). A file arriving is not a host working, which is the same
|
||||||
* distinction the host itself makes about installed packages.
|
* distinction the host itself makes about installed packages.
|
||||||
*/
|
*/
|
||||||
export async function placeHost(
|
export async function placeHost(
|
||||||
@@ -192,7 +192,7 @@ export async function applyPlacements(
|
|||||||
* The base image a scenario's machines are built from, when they need a container runtime.
|
* The base image a scenario's machines are built from, when they need a container runtime.
|
||||||
*
|
*
|
||||||
* A sealed scenario cannot install one: its segments use documentation ranges and there is no
|
* A sealed scenario cannot install one: its segments use documentation ranges and there is no
|
||||||
* route out (novox/hq ADR 0032). So the runtime arrives the way research 012 says everything
|
* route out (novox/hq ADR 0016). So the runtime arrives the way research 012 says everything
|
||||||
* awkward should — **fetched at build time on a machine that has a network, applied on a target
|
* awkward should — **fetched at build time on a machine that has a network, applied on a target
|
||||||
* that then needs nothing.** Building this image is that build time.
|
* that then needs nothing.** Building this image is that build time.
|
||||||
*
|
*
|
||||||
@@ -262,7 +262,7 @@ export async function placeImage(
|
|||||||
// sealed machine cannot reach. Measured, not assumed: the load says `Loaded image ID:`
|
// sealed machine cannot reach. Measured, not assumed: the load says `Loaded image ID:`
|
||||||
// instead of `Loaded image:`, and `docker images` then lists nothing.
|
// instead of `Loaded image:`, and `docker images` then lists nothing.
|
||||||
//
|
//
|
||||||
// This collides with novox/hq ADR 0046, which pins bundle images BY DIGEST and has the host
|
// This collides with novox/hq ADR 0006, which pins bundle images BY DIGEST and has the host
|
||||||
// refuse anything else. Reconciling the two needs a registry inside the scenario, which is
|
// refuse anything else. Reconciling the two needs a registry inside the scenario, which is
|
||||||
// real design work — see 04-ISSUES/009.
|
// real design work — see 04-ISSUES/009.
|
||||||
if (reference.includes("@sha256:")) {
|
if (reference.includes("@sha256:")) {
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ export async function raise(
|
|||||||
const log = options.onProgress ?? (() => {});
|
const log = options.onProgress ?? (() => {});
|
||||||
|
|
||||||
// A scenario that places a runtime or an image needs machines built from the base image,
|
// A scenario that places a runtime or an image needs machines built from the base image,
|
||||||
// because a sealed machine cannot install one (novox/hq ADR 0046). Chosen here rather than
|
// because a sealed machine cannot install one (novox/hq ADR 0006). Chosen here rather than
|
||||||
// declared, so a scenario says WHAT it needs and not which image provides it.
|
// declared, so a scenario says WHAT it needs and not which image provides it.
|
||||||
const needsRuntime = planPlacements(scenario).some(({ artifacts }) =>
|
const needsRuntime = planPlacements(scenario).some(({ artifacts }) =>
|
||||||
artifacts.some((a) => a === "runtime" || a.startsWith(IMAGE_PREFIX))
|
artifacts.some((a) => a === "runtime" || a.startsWith(IMAGE_PREFIX))
|
||||||
|
|||||||
@@ -5,16 +5,16 @@
|
|||||||
* digest — `docker save` of a digest reference produces an archive with no repo tag, because a
|
* digest — `docker save` of a digest reference produces an archive with no repo tag, because a
|
||||||
* repo digest only exists for an image a registry served (novox/hq 04-ISSUES/009). So an image
|
* repo digest only exists for an image a registry served (novox/hq 04-ISSUES/009). So an image
|
||||||
* pinned by digest, which is the only kind the host accepts
|
* pinned by digest, which is the only kind the host accepts
|
||||||
* ([ADR 0046](../../02-DECISIONS/0046-the-installer-fetches-what-it-pins.md)), could not be
|
* ([ADR 0006](../../02-DECISIONS/0046-the-installer-fetches-what-it-pins.md)), could not be
|
||||||
* placed at all.
|
* placed at all.
|
||||||
*
|
*
|
||||||
* The answer is a registry, and it is not a workaround for the lab: ADR 0048 names an OCI
|
* The answer is a registry, and it is not a workaround for the lab: ADR 0006 names an OCI
|
||||||
* registry as substrate, and ADR 0046 says a first node fetches "upstream, wherever the image
|
* registry as substrate, and ADR 0006 says a first node fetches "upstream, wherever the image
|
||||||
* ordinarily lives". **This is that upstream** — scenery, like the transit router is the
|
* ordinarily lives". **This is that upstream** — scenery, like the transit router is the
|
||||||
* internet ([ADR 0033](../../02-DECISIONS/0033-a-router-is-scenery-not-a-node.md)).
|
* internet ([ADR 0016](../../02-DECISIONS/0033-a-router-is-scenery-not-a-node.md)).
|
||||||
*
|
*
|
||||||
* The digests it serves are its own, not Docker Hub's, and that is correct rather than a
|
* The digests it serves are its own, not Docker Hub's, and that is correct rather than a
|
||||||
* compromise. What ADR 0046 requires is a reference that is exact and cannot move. A digest
|
* compromise. What ADR 0006 requires is a reference that is exact and cannot move. A digest
|
||||||
* assigned by this registry is both.
|
* assigned by this registry is both.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
* nothing to say about it.
|
* nothing to say about it.
|
||||||
*
|
*
|
||||||
* A router is **scenery, not a node**, so it is a container rather than a virtual machine
|
* A router is **scenery, not a node**, so it is a container rather than a virtual machine
|
||||||
* (novox/hq ADR 0033). Nothing under test runs on it and no assertion is made about its
|
* (novox/hq ADR 0016). Nothing under test runs on it and no assertion is made about its
|
||||||
* internals; it exists so packets behave the way they behave in the world. What it has to
|
* internals; it exists so packets behave the way they behave in the world. What it has to
|
||||||
* reproduce is kernel behaviour, and a container has the same kernel.
|
* reproduce is kernel behaviour, and a container has the same kernel.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* Integration tests run against a real hypervisor. Mocking it is forbidden — a test that
|
* Integration tests run against a real hypervisor. Mocking it is forbidden — a test that
|
||||||
* fakes the system under integration asserts that the fake behaves as expected, which is
|
* fakes the system under integration asserts that the fake behaves as expected, which is
|
||||||
* the shape of test this project exists to stop shipping (novox/hq ADR 0034).
|
* the shape of test this project exists to stop shipping (novox/hq ADR 0017).
|
||||||
*
|
*
|
||||||
* Consequence, accepted: these are slow, and they need a machine that can raise scenarios.
|
* Consequence, accepted: these are slow, and they need a machine that can raise scenarios.
|
||||||
* They skip rather than fail where it cannot, so that a machine without a hypervisor gets
|
* They skip rather than fail where it cannot, so that a machine without a hypervisor gets
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ test("the host reports the MACHINE, not the workstation that placed it", { skip,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test("ADR 0031 — the host confirms the machine carries no overlay", { skip, timeout: 120_000 }, async () => {
|
test("ADR 0016 — the host confirms the machine carries no overlay", { skip, timeout: 120_000 }, async () => {
|
||||||
// The lab provides the underlay and NOTHING of the overlay. Asserted elsewhere by looking
|
// The lab provides the underlay and NOTHING of the overlay. Asserted elsewhere by looking
|
||||||
// for wireguard interfaces; here the placed host reports it independently, which is a
|
// for wireguard interfaces; here the placed host reports it independently, which is a
|
||||||
// second witness rather than the same check twice.
|
// second witness rather than the same check twice.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Each test names the decision it defends. A decision with no test is one that will quietly
|
* Each test names the decision it defends. A decision with no test is one that will quietly
|
||||||
* stop being true (novox/hq ADR 0034).
|
* stop being true (novox/hq ADR 0017).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { test, before, after } from "node:test";
|
import { test, before, after } from "node:test";
|
||||||
@@ -30,7 +30,7 @@ after(async () => {
|
|||||||
if (instanceId) await destroy(instanceId);
|
if (instanceId) await destroy(instanceId);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("ADR 0031 — the lab provides the underlay and NOTHING of the overlay", { skip, timeout: 120_000 }, async () => {
|
test("ADR 0016 — the lab provides the underlay and NOTHING of the overlay", { skip, timeout: 120_000 }, async () => {
|
||||||
// A scenario that pre-built peering would certify its own work. Whatever the mesh is
|
// A scenario that pre-built peering would certify its own work. Whatever the mesh is
|
||||||
// responsible for must be absent from a freshly raised machine.
|
// responsible for must be absent from a freshly raised machine.
|
||||||
const { stdout } = await exec(instanceId, "home-server", [
|
const { stdout } = await exec(instanceId, "home-server", [
|
||||||
@@ -43,7 +43,7 @@ test("ADR 0031 — the lab provides the underlay and NOTHING of the overlay", {
|
|||||||
assert.deepEqual(counts, [0, 0, 0], "a raised machine carries no overlay, no mesh config");
|
assert.deepEqual(counts, [0, 0, 0], "a raised machine carries no overlay, no mesh config");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("ADR 0031 — the declared address IS what the machine holds", { skip }, async () => {
|
test("ADR 0016 — the declared address IS what the machine holds", { skip }, async () => {
|
||||||
const { stdout } = await exec(instanceId, "home-server", ["ip", "-o", "-4", "addr", "show"]);
|
const { stdout } = await exec(instanceId, "home-server", ["ip", "-o", "-4", "addr", "show"]);
|
||||||
assert.match(stdout, /192\.168\.1\.135\/24/);
|
assert.match(stdout, /192\.168\.1\.135\/24/);
|
||||||
});
|
});
|
||||||
@@ -57,7 +57,7 @@ test("design — raise waits for USABLE, not for the call to return", { skip, ti
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test("ADR 0033 — a router is scenery: containers, while machines are virtual machines", { skip, timeout: 120_000 }, async () => {
|
test("ADR 0016 — a router is scenery: containers, while machines are virtual machines", { skip, timeout: 120_000 }, async () => {
|
||||||
const json = (await incusOk(["list", "--format", "json"], 30_000)) ?? "[]";
|
const json = (await incusOk(["list", "--format", "json"], 30_000)) ?? "[]";
|
||||||
const all = JSON.parse(json) as { name?: string; type?: string; config?: Record<string, string> }[];
|
const all = JSON.parse(json) as { name?: string; type?: string; config?: Record<string, string> }[];
|
||||||
const mine = all.filter((i) => i.config?.["user.mesh-lab.instance"] === instanceId);
|
const mine = all.filter((i) => i.config?.["user.mesh-lab.instance"] === instanceId);
|
||||||
@@ -114,7 +114,7 @@ test("design — restore leaves the scenario USABLE, not merely running", { skip
|
|||||||
assert.equal(stdout.trim(), "alive");
|
assert.equal(stdout.trim(), "alive");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("ADR 0032 — the workstation has no route into the scenario", { skip, timeout: 60_000 }, async () => {
|
test("ADR 0016 — the workstation has no route into the scenario", { skip, timeout: 60_000 }, async () => {
|
||||||
// Reachability is asked from INSIDE. If the workstation could reach a scenario address,
|
// Reachability is asked from INSIDE. If the workstation could reach a scenario address,
|
||||||
// two scenarios carrying the same prefix would put one's traffic in the other.
|
// two scenarios carrying the same prefix would put one's traffic in the other.
|
||||||
const { stdout } = await incus(["exec", `mlab-${instanceId}-anchor`, "--", "echo", "inside"]);
|
const { stdout } = await incus(["exec", `mlab-${instanceId}-anchor`, "--", "echo", "inside"]);
|
||||||
@@ -171,7 +171,7 @@ test("the live diagram draws what exists, never what was asked for", { skip, tim
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test("ADR 0033 — the live diagram distinguishes scenery from a node", { skip }, async () => {
|
test("ADR 0016 — the live diagram distinguishes scenery from a node", { skip }, async () => {
|
||||||
// The router is drawn as a router because the hypervisor says it is a container tagged as
|
// The router is drawn as a router because the hypervisor says it is a container tagged as
|
||||||
// a gateway — not because the diagram re-read the scenario and inferred it.
|
// a gateway — not because the diagram re-read the scenario and inferred it.
|
||||||
const drawn = await diagramFromLive(instanceId);
|
const drawn = await diagramFromLive(instanceId);
|
||||||
|
|||||||
+2
-2
@@ -6,7 +6,7 @@ import { assertSupported, UnsupportedError } from "../src/lifecycle/supported.ts
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* `place:` is the seam where the lab stops being infrastructure with no consumer. Each test
|
* `place:` is the seam where the lab stops being infrastructure with no consumer. Each test
|
||||||
* names what it defends, per novox/hq ADR 0034.
|
* names what it defends, per novox/hq ADR 0017.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function scenario(place: string): ReturnType<typeof parseScenario> {
|
function scenario(place: string): ReturnType<typeof parseScenario> {
|
||||||
@@ -88,7 +88,7 @@ test("the refusal says what CAN be placed", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- runtime and image placement (novox/hq ADR 0046: the lab places what a sealed scenario
|
// --- runtime and image placement (novox/hq ADR 0006: the lab places what a sealed scenario
|
||||||
// cannot fetch) ---
|
// cannot fetch) ---
|
||||||
|
|
||||||
test("an image reference is placeable, and a bare 'image:' is not", () => {
|
test("an image reference is placeable, and a bare 'image:' is not", () => {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { digestFrom, pinnedReference, registryAddress, repositoryFor } from "../
|
|||||||
*
|
*
|
||||||
* These test the pure parts. The parts that need a registry are exercised by raising a
|
* These test the pure parts. The parts that need a registry are exercised by raising a
|
||||||
* scenario, because a fake registry would assert that the fake behaves as expected
|
* scenario, because a fake registry would assert that the fake behaves as expected
|
||||||
* (novox/hq ADR 0034).
|
* (novox/hq ADR 0017).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
test("a digest is read from what the registry actually said", () => {
|
test("a digest is read from what the registry actually said", () => {
|
||||||
@@ -53,7 +53,7 @@ test("the registry's address is derived from its segment", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("what a declaration pins is the registry's own digest", () => {
|
test("what a declaration pins is the registry's own digest", () => {
|
||||||
// Not Docker Hub's. ADR 0046 requires a reference that is exact and cannot move, and a
|
// Not Docker Hub's. ADR 0006 requires a reference that is exact and cannot move, and a
|
||||||
// digest this registry assigned is both.
|
// digest this registry assigned is both.
|
||||||
const pinned = pinnedReference("192.0.2.250", {
|
const pinned = pinnedReference("192.0.2.250", {
|
||||||
requested: "alpine:3.20",
|
requested: "alpine:3.20",
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ machines: { a: { at: { segment: net, address: [192.0.2.1] }, inbound: deny } }`)
|
|||||||
test("the host is placeable — it used to be refused, and tier 0 now exists", () => {
|
test("the host is placeable — it used to be refused, and tier 0 now exists", () => {
|
||||||
// These two tests failed the moment placement worked, which is what they were for. They
|
// These two tests failed the moment placement worked, which is what they were for. They
|
||||||
// defended "there is nothing to place yet" while that was true; the decision changed, so
|
// defended "there is nothing to place yet" while that was true; the decision changed, so
|
||||||
// they change with it rather than being deleted (novox/hq ADR 0034).
|
// they change with it rather than being deleted (novox/hq ADR 0017).
|
||||||
const scenario = parseScenario(`scenario: x
|
const scenario = parseScenario(`scenario: x
|
||||||
segments: { net: { kind: public, cidr: [192.0.2.0/24] } }
|
segments: { net: { kind: public, cidr: [192.0.2.0/24] } }
|
||||||
machines: { a: { at: { segment: net, address: [192.0.2.1] } } }
|
machines: { a: { at: { segment: net, address: [192.0.2.1] } } }
|
||||||
|
|||||||
Reference in New Issue
Block a user