Draw a scenario, from the declaration and from the hypervisor
`mesh-lab diagram` renders a scenario as draw.io, from either source, through one layout — so a difference between what was asked for and what exists is a difference you can see. The shape says what a resource is and is fixed per kind. The badges say what is true about that particular one and come entirely from metadata: translation, forwardability, mapping expiry, refuses-inbound, container-or-VM, running. The interesting properties of a network are exactly the ones with no visual consequence — a translated address looks identical to an untranslated one. For the live picture to be a record rather than a restatement, raise now writes down what it applied: a segment's kind, ranges and MTU on the link; a gateway's translation, forwardability and expiry on the gateway; inbound: deny on the machine. Every behavioural tag is written AFTER the thing works, never at creation — a failed raise leaves wreckage standing on purpose, and a picture of that wreckage must not badge translation the router never got. The pairing earned itself immediately: drawn side by side, every virtual machine held no addresses. A container's interface carries the device's name and a VM names its own, so joining them by name silently dropped one whole class of machine. Fixed by joining on MAC. Also brings tests under the typecheck gate, which caught integration timeouts being passed as a 4th argument and therefore ignored entirely.
This commit is contained in:
@@ -37,6 +37,8 @@ mesh-lab exec <instance> <machine> -- <cmd...>
|
||||
mesh-lab snapshot <instance> <label>
|
||||
mesh-lab restore <instance> <label>
|
||||
mesh-lab destroy <instance>
|
||||
mesh-lab diagram scenarios/x.yml draw what the scenario asks for
|
||||
mesh-lab diagram --live <instance> draw what is actually standing
|
||||
```
|
||||
|
||||
`check` refuses rather than warns. A machine without copy-on-write storage runs scenarios
|
||||
@@ -121,8 +123,9 @@ raise a mesh that silently lacks what it declared — that is the fault this lab
|
||||
(`novox/hq` 04-ISSUES/003: a firewall key declared in five manifests and read by no code, so a
|
||||
manifest appears to restrict a port and restricts nothing).
|
||||
|
||||
`the-ordinary-shape.yml` therefore validates and does not raise. That is the intended state:
|
||||
it is the topology being built toward, and the tool says exactly what is missing.
|
||||
No scenario in `scenarios/` declares `place:` yet, so all of them raise. What they raise is
|
||||
an underlay holding empty machines — correct, and not yet useful for anything, because the
|
||||
node host that would be placed on them does not exist.
|
||||
|
||||
## Measured on a workstation
|
||||
|
||||
@@ -170,6 +173,48 @@ These numbers depend entirely on a copy-on-write pool. On `dir` the same snapsho
|
||||
and a full copy of the disk, and a second one did not finish in two minutes — which is why
|
||||
`check` refuses rather than warns.
|
||||
|
||||
## Drawing one
|
||||
|
||||
```
|
||||
mesh-lab diagram scenarios/the-ordinary-shape.yml what the declaration asks for
|
||||
mesh-lab diagram --live <instance> what the hypervisor actually holds
|
||||
```
|
||||
|
||||
Both produce draw.io files, laid out the same way — public networks at the top, each private
|
||||
one below the network it sits behind. Drawing both sources through one layout is the point: a
|
||||
difference between what was asked for and what exists becomes a difference you can *see*.
|
||||
|
||||
Two kinds of symbol, and the split matters:
|
||||
|
||||
- the **shape** says what a resource is, and is fixed per kind — a server is always the server
|
||||
shape, a gateway always the router shape, whatever else is true about it;
|
||||
- the **badges** say what is true about that particular one, and come entirely from metadata:
|
||||
`N` translated, `F` forwarding (green yes, red no), `T` mappings expire, `D` refuses inbound,
|
||||
`C` container, `VM` virtual machine, `▶` running. Each carries the full sentence as a
|
||||
tooltip, because a one-letter code with no explanation is a private language.
|
||||
|
||||
Badges exist because the interesting properties of a network are exactly the ones with no
|
||||
visual consequence. An address that is translated looks identical to one that is not, until
|
||||
traffic proves otherwise.
|
||||
|
||||
The live drawing reads **only** the hypervisor — the same tags `destroy` uses — and never
|
||||
re-opens the scenario file. A picture built from the declaration and labelled *as raised*
|
||||
would report the request as though it were the result, which is the whole failure the pairing
|
||||
exists to expose. So `raise` records what it applied: a segment's kind and ranges on the link,
|
||||
a gateway's translation, forwardability and mapping expiry on the gateway, and `inbound: deny`
|
||||
on the machine.
|
||||
|
||||
**Every behavioural tag is written after the thing works, never before.** A tag written when
|
||||
the resource is created would restate the request; a failed raise leaves its wreckage standing
|
||||
on purpose, so a picture of that wreckage would badge translation the gateway was never
|
||||
configured to do. The gateway is tagged after its ruleset is applied, and the machine after
|
||||
the read-back proves its firewall loaded.
|
||||
|
||||
That pairing has already earned itself. Drawn side by side, the live picture showed every
|
||||
virtual machine holding no addresses at all: a container's interface carries the device's
|
||||
name, a virtual machine names its own, and joining them by name silently dropped one whole
|
||||
class of machine. The two pictures disagreed, so the bug was visible in seconds.
|
||||
|
||||
## Where the reasoning lives
|
||||
|
||||
Design and decisions are in [`novox/hq`](https://git.novox.be/novox/hq), not here:
|
||||
@@ -186,14 +231,11 @@ This repository carries implementation. It does not carry decisions.
|
||||
No build step — Node strips the types.
|
||||
|
||||
```
|
||||
npm test the declaration layer, offline
|
||||
npm run typecheck
|
||||
```
|
||||
|
||||
```
|
||||
npm test the declaration layer, offline, 40 tests
|
||||
npm run test:integration real scenarios against a real hypervisor, 10 tests
|
||||
npm run check typecheck + both — this is the gate
|
||||
npm test the declaration layer and the diagram, offline, 49 tests
|
||||
npm run test:integration real scenarios against a real hypervisor, 14 tests
|
||||
npm run typecheck source and tests both — a test that does not compile is a test
|
||||
that silently never ran
|
||||
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
|
||||
@@ -208,6 +250,9 @@ that will quietly stop being true, and nobody learns that from a document:
|
||||
| snapshots are whole-scenario | the lifecycle 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 |
|
||||
| the live diagram distinguishes scenery from a node | ADR 0033 |
|
||||
| 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 |
|
||||
|
||||
**Mocking the hypervisor is forbidden.** A fake would assert that the fake behaves as expected,
|
||||
which is the shape of test this project exists to stop shipping. Integration tests skip with a
|
||||
|
||||
Reference in New Issue
Block a user