diff --git a/Makefile b/Makefile index 9ea72fc..e865709 100644 --- a/Makefile +++ b/Makefile @@ -39,17 +39,22 @@ test: build: CGO_ENABLED=0 go build -ldflags="$(LDFLAGS)" -o mesh-host ./cmd/mesh-host -# A host for a real machine, carrying a real bundle: make host BUNDLE=path/to/substrate.lock +# A host for a real machine, carrying a real bundle: +# make host SYSTEM=arch BUNDLE=path/to/substrate.lock +# +# The bundle replaces the one for SYSTEM, because its contents are per operating system — +# package names and unit names differ (novox/hq ADR 0005). host: @test -n "$(BUNDLE)" || { echo "BUNDLE= is required; a host with no bundle cannot raise a first node"; exit 1; } @test -f "$(BUNDLE)" || { echo "no such bundle: $(BUNDLE)"; exit 1; } - @cp internal/bundle/substrate.lock internal/bundle/substrate.lock.default - @cp "$(BUNDLE)" internal/bundle/substrate.lock + @test -f internal/bundle/substrate-$(SYSTEM).lock || { echo "no bundle slot for SYSTEM=$(SYSTEM)"; exit 1; } + @cp internal/bundle/substrate-$(SYSTEM).lock internal/bundle/substrate-$(SYSTEM).lock.default + @cp "$(BUNDLE)" internal/bundle/substrate-$(SYSTEM).lock @CGO_ENABLED=0 go build -ldflags="$(LDFLAGS)" -o mesh-host ./cmd/mesh-host; \ status=$$?; \ - mv internal/bundle/substrate.lock.default internal/bundle/substrate.lock; \ + mv internal/bundle/substrate-$(SYSTEM).lock.default internal/bundle/substrate-$(SYSTEM).lock; \ exit $$status - @echo "built carrying $(BUNDLE)" + @echo "built for $(SYSTEM) carrying $(BUNDLE)" clean: rm -f mesh-host diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..71be8fd --- /dev/null +++ b/examples/README.md @@ -0,0 +1,26 @@ +# Examples + +## `substrate-first-node.lock` + +What an Arch machine must be before a mesh exists — the first steps of the bootstrap in +[novox/hq `07-the-substrate.md`](https://git.novox.be/novox/hq): a container runtime, a store +running, and the control plane's database created inside it. + +**It stops there**, and the file says why: loading the control plane's schema and starting the +control plane are the next two steps, and there is no control plane yet. A bundle naming one +would be a bundle that cannot be applied. + +Build a host carrying it: + +``` +make host SYSTEM=arch BUNDLE=examples/substrate-first-node.lock +``` + +**The image reference has to be replaced before this is useful.** It is written as +`REGISTRY/postgres@DIGEST` because the digest belongs to whatever registry serves it — in the +lab, one the scenario raises, which reports its digests when it comes up. That is not a +placeholder to be tidied away: a bundle is built *for a target*, and which registry that target +pulls from is part of the target. + +Verified end to end in a lab machine with no route out: five resources applied, idempotent on a +second run, and `mesh` present in the store afterwards. diff --git a/examples/substrate-first-node.lock b/examples/substrate-first-node.lock new file mode 100644 index 0000000..a52c670 --- /dev/null +++ b/examples/substrate-first-node.lock @@ -0,0 +1,57 @@ +// substrate-arch.lock — what an Arch machine must be before a mesh exists. +// +// The first three steps of the bootstrap (novox/hq 03-DESIGN/01-to-be/07-the-substrate.md). +// Steps four and five — load the control plane's schema, start the control plane — are absent +// because the control plane does not exist yet. A bundle that named it would be a bundle that +// cannot be applied. +// +// PINNED BY DIGEST, and the digest is not decoration: a tag can be made to point at a different +// image, and this file is applied on a machine with no mesh to ask about anything. +// +// The store's data is a NAMED VOLUME rather than a directory on the machine. A directory the +// host creates is owned by root, and the database runs as somebody else inside the container — +// so it could not write, and the container crash-looped. A named volume lets the image set up +// its own ownership, which is what it is for. It also outlives the container, which is what you +// want for the thing holding the mesh's state. +{ + "declaration": 1, + "resources": [ + { + "id": "container-runtime", + "type": "package", + "package": "docker" + }, + { + "id": "container-runtime-running", + "type": "service", + "unit": "docker.service", + "state": "running", + "boot": "enabled" + }, + { + "id": "store", + "type": "container", + "name": "mesh-store", + "image": "REGISTRY/postgres@DIGEST", + "env": { + "POSTGRES_PASSWORD": "bootstrap", + "PGDATA": "/var/lib/postgresql/data/pgdata" + }, + "volumes": ["mesh-store-data:/var/lib/postgresql/data"] + }, + { + "id": "store-ready", + "type": "action", + "in": "mesh-store", + "command": ["sh", "-c", "for i in $(seq 1 60); do pg_isready -U postgres >/dev/null 2>&1 && exit 0; sleep 1; done; exit 1"], + "verify": ["pg_isready", "-U", "postgres"] + }, + { + "id": "control-plane-database", + "type": "action", + "in": "mesh-store", + "command": ["sh", "-c", "psql -U postgres -c 'CREATE DATABASE mesh'"], + "verify": ["sh", "-c", "psql -U postgres -lqt | cut -d'|' -f1 | grep -qw mesh"] + } + ] +} diff --git a/internal/bundle/substrate-alpine.lock b/internal/bundle/substrate-alpine.lock index 2e045fd..1d0f5cc 100644 --- a/internal/bundle/substrate-alpine.lock +++ b/internal/bundle/substrate-alpine.lock @@ -1,7 +1,7 @@ // substrate-alpine.lock — the pinned tier-1 descriptor the ALPINE host carries. // // Per system, because its CONTENTS are: this one names apk packages and OpenRC services where -// the arch bundle names pacman packages and systemd units (novox/hq ADR 0060). +// the arch bundle names pacman packages and systemd units (novox/hq ADR 0005). // // Empty on purpose. What belongs here is the closure for a one-node mesh, and that is not // yet known: novox/hq research 012 asks what the minimum actually is, and research 011 is diff --git a/internal/bundle/substrate-android.lock b/internal/bundle/substrate-android.lock index cae1625..6ee155f 100644 --- a/internal/bundle/substrate-android.lock +++ b/internal/bundle/substrate-android.lock @@ -5,7 +5,7 @@ // // The substrate is a container runtime, a store and the control plane (novox/hq // 07-the-substrate.md). An android host implements `file`, `directory` and `action` and refuses -// `package`, `container` and `service` (ADR 0060) — so every step of the bootstrap is a shape it +// `package`, `container` and `service` (ADR 0005) — so every step of the bootstrap is a shape it // does not have. No amount of filling this in changes that. // // **A partial host can JOIN a mesh and cannot BE the first node.** That is a real distinction diff --git a/internal/bundle/substrate-arch.lock b/internal/bundle/substrate-arch.lock index 0dfc53f..21cc7be 100644 --- a/internal/bundle/substrate-arch.lock +++ b/internal/bundle/substrate-arch.lock @@ -1,7 +1,7 @@ // substrate-arch.lock — the pinned tier-1 descriptor the ARCH host carries. // // Per system, because its CONTENTS are: package names, unit names and service names all differ -// (novox/hq ADR 0060). The mechanism is shared; what it names is not. +// (novox/hq ADR 0005). The mechanism is shared; what it names is not. // // Empty on purpose. What belongs here is the closure for a one-node mesh, and that is not // yet known: novox/hq research 012 asks what the minimum actually is, and research 011 is