diff --git a/Makefile b/Makefile index 4b92c97..c295f39 100644 --- a/Makefile +++ b/Makefile @@ -36,24 +36,24 @@ test: go test ./... -count=1 # A default build carries no bundle and refuses to reconcile, which is the honest state for a -# host nobody has told what a substrate is. +# host nobody has told what a foundation is. 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 SYSTEM=arch BUNDLE=path/to/substrate.lock +# make host SYSTEM=arch BUNDLE=path/to/foundation.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; } - @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 + @test -f internal/bundle/foundation-$(SYSTEM).lock || { echo "no bundle slot for SYSTEM=$(SYSTEM)"; exit 1; } + @cp internal/bundle/foundation-$(SYSTEM).lock internal/bundle/foundation-$(SYSTEM).lock.default + @cp "$(BUNDLE)" internal/bundle/foundation-$(SYSTEM).lock @CGO_ENABLED=0 go build -ldflags="$(LDFLAGS)" -o mesh-host ./cmd/mesh-host; \ status=$$?; \ - mv internal/bundle/substrate-$(SYSTEM).lock.default internal/bundle/substrate-$(SYSTEM).lock; \ + mv internal/bundle/foundation-$(SYSTEM).lock.default internal/bundle/foundation-$(SYSTEM).lock; \ exit $$status @echo "built for $(SYSTEM) carrying $(BUNDLE)" @@ -66,7 +66,7 @@ host: # answered by ADR 0071: the source comes from a mesh that already exists, which is not the one being # raised. What cannot be fetched is the thing that does the fetching, and that is what is carried. # -# The image is BUILT ELSEWHERE and handed over — mesh-control's own `make builder-image` — and +# The image is BUILT ELSEWHERE and handed over — mesh-controller's own `make builder-image` — and # embedded here at release time, the same way carrying the bundle breaks the "copy it onto a machine # and run it" cycle (novox/hq ADR 0005). # @@ -90,7 +90,7 @@ BOOTSTRAP_OUT ?= mesh-bootstrap bootstrap: @test -n "$(IMAGE)" || { echo "IMAGE= is required; an installer carrying no builder image cannot raise a mesh"; exit 1; } @case "$(IMAGE)" in sha256:*) echo "IMAGE=$(IMAGE) is an image id. The installer identifies the carried image by its tag, because an id is the digest of a configuration that a runtime rewrites as it loads. Pass a name:tag"; exit 1;; esac - @docker image inspect "$(IMAGE)" >/dev/null 2>&1 || { echo "this machine does not hold $(IMAGE) — build it in mesh-control with 'make image'"; exit 1; } + @docker image inspect "$(IMAGE)" >/dev/null 2>&1 || { echo "this machine does not hold $(IMAGE) — build it in mesh-controller with 'make image'"; exit 1; } @test -n "$$(docker image inspect --format '{{len .RepoTags}}' "$(IMAGE)" | grep -v '^0$$')" || { echo "$(IMAGE) has no repository tag, so the saved archive would carry no name the installer can ask a runtime about. Tag it first: docker tag $(IMAGE) mesh-builder:"; exit 1; } @cp internal/image/builder.tar internal/image/builder.tar.placeholder @docker save --output internal/image/builder.tar "$(IMAGE)" diff --git a/README.md b/README.md index d440a3b..0b051a4 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ the fault this exists to prevent. A host built for a machine carries its declaration **inside the binary**: ``` -make host BUNDLE=path/to/substrate.lock +make host BUNDLE=path/to/foundation.lock ``` `mesh-host reconcile` then applies it. That is the first node's path — no mesh present, nothing @@ -126,21 +126,21 @@ Stages 3 and 4 — the link, and enrolment — are designed and not built. ## What stage 2 does not yet prove -The design defines stage 2 as *the host applies `substrate.lock` with no mesh present*, and -calls out the claim underneath it: **that one host can raise the substrate alone**. +The design defines stage 2 as *the host applies `foundation.lock` with no mesh present*, and +calls out the claim underneath it: **that one host can raise the foundation alone**. The mechanism is proved — a sealed machine, one binary, and it configures itself from what it -carries. The claim is not. The substrate is four container services, and: +carries. The claim is not. The foundation is four container services, and: - the vocabulary has no container type, because a container needs an image and where images come from is open ([`novox/hq` research 012](https://git.novox.be/novox/hq)); -- what belongs in a substrate is not known — the closure for a one-node mesh is what +- what belongs in a foundation is not known — the closure for a one-node mesh is what research 011 and 012 exist to answer; - and the machine used to test this has no container runtime, because a sealed network cannot install one. -So `substrate.lock` here is a real bundle with a placeholder's content. Saying that plainly -beats shipping a host that claims a substrate it has never raised. +So `foundation.lock` here is a real bundle with a placeholder's content. Saying that plainly +beats shipping a host that claims a foundation it has never raised. ## A capability is detected, never assumed @@ -198,7 +198,7 @@ repository carries implementation and does not carry decisions. ## Checks that cross into the control plane's repository -Two things are agreed between this repository and `novox/mesh-control`, and each is a separate +Two things are agreed between this repository and `novox/mesh-controller`, and each is a separate struct on each side. A field renamed on one of them fails **silently** — the crossing succeeds and something is simply absent — so both are checked by handing one side's real output to the other's real parser. Neither runs by default; each skips with a reason, because a repository that fails @@ -207,7 +207,7 @@ without its neighbour checked out is a repository nobody can build. **What the mesh sends, read by this host:** ``` -mesh-control: ./build/mesh-control plan --json > /tmp/d.json +mesh-controller: ./build/mesh-controller plan --json > /tmp/d.json mesh-host: MESH_EMITTED=/tmp/d.json go test ./internal/declaration/ -v ``` @@ -215,7 +215,7 @@ mesh-host: MESH_EMITTED=/tmp/d.json go test ./internal/declaration/ -v ``` mesh-host: MESH_ENROL_OUT=/tmp/enrol.json go test ./internal/link/ -mesh-control: MESH_ENROL=/tmp/enrol.json make check +mesh-controller: MESH_ENROL=/tmp/enrol.json make check ``` The second writes the private half of the sealing key beside the request, so the mesh's suite can diff --git a/cmd/mesh-bootstrap/main.go b/cmd/mesh-bootstrap/main.go index 1c19326..5f28e15 100644 --- a/cmd/mesh-bootstrap/main.go +++ b/cmd/mesh-bootstrap/main.go @@ -8,11 +8,11 @@ // cannot be folded into it without making that sentence false. Same tier, same repository, // different program. // -// Genesis is a pivot (novox/hq ADR 0067). It raises a substrate whose control plane is named by the +// Genesis is a pivot (novox/hq ADR 0067). It raises a foundation whose control plane is named by the // digest of its own configuration — legal exactly where nothing could have served an image — then // enrols this machine, installs the registry module, pushes that image into it to get the manifest // digest it has never had, reinstalls the control plane as an ordinary module pinned to it, and -// drops the temporary one. Without --catalog it stops after the substrate and says why. +// drops the temporary one. Without --catalog it stops after the foundation and says why. package main import ( @@ -40,8 +40,8 @@ import ( var version = "development build" const ( - defaultTemplate = "substrate.lock" - defaultOut = "/var/lib/mesh-host/substrate.lock" + defaultTemplate = "foundation.lock" + defaultOut = "/var/lib/mesh-host/foundation.lock" defaultRegistry = "127.0.0.1:5000" defaultHost = "/usr/local/bin/mesh-host" // The unit this project actually packages, in `packaging/`. It said `mesh-host.service`, which @@ -58,7 +58,7 @@ const usage = `mesh-bootstrap — make a bare machine into a mesh 1 preflight what has to be true before anything is changed 2 load the builder's image, carried in this installer 3 build the control plane, from its own repository and a commit - 4 bundle the substrate, named for this machine + 4 bundle the foundation, named for this machine 5 apply raise it 6 verify it is up, and the control plane replies 7 enrol this machine becomes the mesh's first node @@ -75,7 +75,7 @@ const usage = `mesh-bootstrap — make a bare machine into a mesh 13 base build the shared toolchain and runtime everything with code stands on 14 store build and install postgres — a database provider, which the - substrate's own store is not + foundation's own store is not 15 catalogue build and install the module graph 16 network choose the private network (--private-network), place this machine as its hub (--endpoint, --site) @@ -83,7 +83,7 @@ const usage = `mesh-bootstrap — make a bare machine into a mesh question is which, not whether 18 extras anything beyond the floor (--extras) - --bundle the substrate template to build this machine's bundle from + --bundle the foundation template to build this machine's bundle from (default ` + defaultTemplate + `) --out where the produced bundle is written, for a person to read (default ` + defaultOut + `) @@ -131,8 +131,8 @@ the same thing that will maintain it, and the control plane a mesh ends up runni one it built itself, from a repository and a commit it can name and build again. Genesis is a pivot: a temporary control plane installs the registry that makes it -permanent. The temporary one is called temp-mesh-control and the permanent one is -called mesh-control, so they are two containers with two owners and there is nothing +permanent. The temporary one is called temp-mesh-controller and the permanent one is +called mesh-controller, so they are two containers with two owners and there is nothing to hand over. Every step is idempotent: run it again after fixing whatever it named, and the steps @@ -217,11 +217,11 @@ func newFlagSet(opts *bootstrap.Options, jsonOut *bool) *flag.FlagSet { set := flag.NewFlagSet("mesh-bootstrap", flag.ContinueOnError) set.SetOutput(os.Stderr) set.Usage = func() { fmt.Fprint(os.Stderr, usage) } - set.StringVar(&opts.Template, "bundle", opts.Template, "the substrate template to build from") + set.StringVar(&opts.Template, "bundle", opts.Template, "the foundation template to build from") set.StringVar(&opts.Out, "out", opts.Out, "where the produced bundle is written") set.StringVar(&opts.State, "state", opts.State, "where this node records what it has applied") set.StringVar(&opts.Catalogue, "catalog", opts.Catalogue, - "a checkout of the mesh's catalogue; without it this stops after the substrate") + "a checkout of the mesh's catalogue; without it this stops after the foundation") set.StringVar(&opts.Source.Repository, "source", opts.Source.Repository, "the repository the control plane is built from, on a mesh that already exists") set.StringVar(&opts.Source.Ref, "source-ref", opts.Source.Ref, @@ -367,7 +367,7 @@ func hostname() string { // // Plain HTTP, and only at the mesh's own registry: it is reached over the mesh's private network, // which is already the encrypted and authenticated thing, and a second layer inside it would be -// certificates to issue and rotate for no property the first does not have (mesh-control's +// certificates to issue and rotate for no property the first does not have (mesh-controller's // `internal/builder` pushes to it on the same reasoning). // // The body is read with a limit. What is asked for is a status and a short JSON answer, and a diff --git a/cmd/mesh-bootstrap/main_test.go b/cmd/mesh-bootstrap/main_test.go index 896522e..2fadbcb 100644 --- a/cmd/mesh-bootstrap/main_test.go +++ b/cmd/mesh-bootstrap/main_test.go @@ -57,7 +57,7 @@ func TestAMistypedFlagIsRefusedNotIgnored(t *testing.T) { } func TestAnUnexpectedArgumentIsRefused(t *testing.T) { - if _, _, _, err := parseArgs([]string{"bootstrap", "substrate.lock"}); err == nil { + if _, _, _, err := parseArgs([]string{"bootstrap", "foundation.lock"}); err == nil { t.Fatal("a stray argument was ignored rather than refused — the bundle is --bundle") } } @@ -141,7 +141,7 @@ func TestThePivotsDefaultsAreTheDocumentedOnes(t *testing.T) { // be a checkout somebody else made, at whatever commit they left it on — and it decides which // image the mesh's control plane is pinned to for ever after. if opts.Catalogue != "" { - t.Errorf("--catalog defaults to %q; without one the installer stops at the substrate", + t.Errorf("--catalog defaults to %q; without one the installer stops at the foundation", opts.Catalogue) } // The machine's own name, because that is what a person already calls it. diff --git a/cmd/mesh-host/main.go b/cmd/mesh-host/main.go index 2095c26..73157a0 100644 --- a/cmd/mesh-host/main.go +++ b/cmd/mesh-host/main.go @@ -823,7 +823,7 @@ func sealOpener(statePath string) apply.Unseal { // carriedPorts is every machine port held by what this host raised from its own bundle. // -// **What the mesh must assign around** (novox/hq ADR 0038). The substrate is not a module: a node +// **What the mesh must assign around** (novox/hq ADR 0038). The foundation is not a module: a node // raises it before any mesh exists, so the control plane has never heard of the store or the // broker. Told this, it can put a module somewhere else; not told, it hands out a port one of them // holds and finds out from a container runtime. diff --git a/examples/README.md b/examples/README.md index 687430a..c3a0258 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,17 +1,17 @@ # Examples -## `substrate-first-node.lock` +## `foundation-first-node.lock` What a machine must be before a mesh exists — the bootstrap in -[novox/hq `07-the-substrate.md`](https://git.novox.be/novox/hq), whole: +[novox/hq `07-the-foundation.md`](https://git.novox.be/novox/hq), whole: ``` 0 a container runtime 1 the store runs 2 a database per context `inventory` and `identity` -3 those contexts' schemas mesh-control migrate +3 those contexts' schemas mesh-controller migrate 4 the broker runs with a certificate it generated itself -5 the control plane runs mesh-control serve +5 the control plane runs mesh-controller serve ``` **A machine that applies this is a mesh** — one node, with nothing joined to it yet, which is @@ -24,7 +24,7 @@ a comment about what something does not do is a comment nobody updates. Build a host carrying it: ``` -make host SYSTEM=arch BUNDLE=examples/substrate-first-node.lock +make host SYSTEM=arch BUNDLE=examples/foundation-first-node.lock ``` **The registry address and digests have to be replaced before this is useful.** They are written diff --git a/examples/bundle_test.go b/examples/bundle_test.go index 47ce39d..07e75da 100644 --- a/examples/bundle_test.go +++ b/examples/bundle_test.go @@ -1,6 +1,6 @@ // Package examples checks the bundles shipped in this directory. // -// **Nothing checked them before.** `substrate-first-node.lock` is what a machine becomes when +// **Nothing checked them before.** `foundation-first-node.lock` is what a machine becomes when // there is no mesh to ask — the one declaration applied with nothing to verify it against — and // it was edited by hand and read by nobody but a running host. package examples @@ -16,7 +16,7 @@ import ( func bundle(t *testing.T) *declaration.Declaration { t.Helper() - raw, err := os.ReadFile("substrate-first-node.lock") + raw, err := os.ReadFile("foundation-first-node.lock") if err != nil { t.Fatal(err) } @@ -27,12 +27,12 @@ func bundle(t *testing.T) *declaration.Declaration { return d } -// Defends novox/hq ADR 0028: the substrate supplies the control plane and nothing else. +// Defends novox/hq ADR 0028: the foundation supplies the control plane and nothing else. // -// The object store was substrate for months on the strength of "it cannot grant itself a bucket", +// The object store was foundation for months on the strength of "it cannot grant itself a bucket", // which answers half the test. The control plane never needed one, and nothing noticed because // nothing counted what the bundle holds. -func TestTheBundleCarriesTheSubstrateAndTheControlPlaneAndNothingElse(t *testing.T) { +func TestTheBundleCarriesTheFoundationAndTheControlPlaneAndNothingElse(t *testing.T) { var images []string for _, r := range bundle(t).Resources { c, ok := r.(*declaration.Container) @@ -48,7 +48,7 @@ func TestTheBundleCarriesTheSubstrateAndTheControlPlaneAndNothingElse(t *testing } sort.Strings(images) - want := []string{"lavinmq", "mesh-control", "postgres"} + want := []string{"lavinmq", "mesh-controller", "postgres"} if strings.Join(images, ",") != strings.Join(want, ",") { t.Fatalf("the bundle carries %v; expected exactly %v.\n\n"+ "Adding one is a change to what every first node becomes, and to ADR 0006's "+ @@ -57,13 +57,13 @@ func TestTheBundleCarriesTheSubstrateAndTheControlPlaneAndNothingElse(t *testing } // The control plane is in the bundle, and the design overlooked it once by reasoning about -// substrate services rather than counting containers (novox/hq 03-DESIGN/01-to-be/07). +// foundation services rather than counting containers (novox/hq 03-DESIGN/01-to-be/07). func TestTheControlPlaneIsCarriedToo(t *testing.T) { for _, r := range bundle(t).Resources { - if c, ok := r.(*declaration.Container); ok && strings.Contains(c.Image, "mesh-control") { + if c, ok := r.(*declaration.Container); ok && strings.Contains(c.Image, "mesh-controller") { return } } t.Fatal("nothing in the bundle starts the control plane, so the machine would raise a " + - "substrate and stop") + "foundation and stop") } diff --git a/examples/substrate-first-node.lock b/examples/foundation-first-node.lock similarity index 94% rename from examples/substrate-first-node.lock rename to examples/foundation-first-node.lock index 2fcefe6..3f516b1 100644 --- a/examples/substrate-first-node.lock +++ b/examples/foundation-first-node.lock @@ -1,6 +1,6 @@ -// substrate-first-node.lock — what a machine must be before a mesh exists. +// foundation-first-node.lock — what a machine must be before a mesh exists. // -// The whole bootstrap (novox/hq 03-DESIGN/01-to-be/07-the-substrate.md): a container runtime, a +// The whole bootstrap (novox/hq 03-DESIGN/01-to-be/07-the-foundation.md): a container runtime, a // store, a database per context, those contexts' schemas, the broker, and the control plane // running on top of them. // @@ -85,7 +85,7 @@ }, // Each context owns its own database (novox/hq ADR 0008). A third one is a third database, // created the same way and named the same way — which is the whole of adding a context to the - // bootstrap, and is why the count is not something the substrate has an opinion about. + // bootstrap, and is why the count is not something the foundation has an opinion about. { "id": "licences-database", "type": "action", @@ -100,7 +100,7 @@ "-e", "MESH_STORE_INVENTORY=postgres://postgres:bootstrap@127.0.0.1:5432/inventory?sslmode=disable", "-e", "MESH_STORE_IDENTITY=postgres://postgres:bootstrap@127.0.0.1:5432/identity?sslmode=disable", "-e", "MESH_STORE_LICENCES=postgres://postgres:bootstrap@127.0.0.1:5432/licences?sslmode=disable", - "192.0.2.250:5000/mesh-control@sha256:c67db38439ff0aee242b467486765467bb95801f52175fc5727cc4e437338ace", + "192.0.2.250:5000/mesh-controller@sha256:c67db38439ff0aee242b467486765467bb95801f52175fc5727cc4e437338ace", "migrate"], "verify": ["sh", "-c", "docker exec mesh-store psql -U postgres -d inventory -tAc \"select to_regclass('public.node')\" | grep -qx node && docker exec mesh-store psql -U postgres -d identity -tAc \"select to_regclass('public.signing_key')\" | grep -qx signing_key && docker exec mesh-store psql -U postgres -d licences -tAc \"select to_regclass('public.licence')\" | grep -qx licence"] }, @@ -133,8 +133,8 @@ { "id": "control-plane", "type": "container", - "name": "mesh-control", - "image": "192.0.2.250:5000/mesh-control@sha256:c67db38439ff0aee242b467486765467bb95801f52175fc5727cc4e437338ace", + "name": "mesh-controller", + "image": "192.0.2.250:5000/mesh-controller@sha256:c67db38439ff0aee242b467486765467bb95801f52175fc5727cc4e437338ace", "network": "host", "args": ["serve"], "volumes": ["mesh-broker-tls:/broker-tls:ro"], diff --git a/internal/apply/apply_test.go b/internal/apply/apply_test.go index 0086be6..3dfc524 100644 --- a/internal/apply/apply_test.go +++ b/internal/apply/apply_test.go @@ -459,7 +459,7 @@ func TestForgettingAUnitThatIsGoneDoesNotStrandTheNode(t *testing.T) { } } -// --- package, container and action (novox/hq 07-the-substrate.md, ADR 0006, ADR 0005) --- +// --- package, container and action (novox/hq 07-the-foundation.md, ADR 0006, ADR 0005) --- func parseTrusted(t *testing.T, raw string) *declaration.Declaration { t.Helper() diff --git a/internal/bootstrap/apply.go b/internal/bootstrap/apply.go index 6f8e7c0..372793d 100644 --- a/internal/bootstrap/apply.go +++ b/internal/bootstrap/apply.go @@ -15,7 +15,7 @@ import ( // Runner is the same runner every applier in this repository takes. type Runner = apply.Runner -// ApplyBundle raises the substrate, through the host's own apply. +// ApplyBundle raises the foundation, through the host's own apply. // // **This calls `internal/apply` rather than running the `mesh-host` binary**, and that is worth // stating because shelling out would have been easier. The installer and the host must apply a @@ -25,7 +25,7 @@ type Runner = apply.Runner // raising, which would make the installer depend on the thing it installs. // // It applies under `store.OriginCarried`, which is the same origin `mesh-host reconcile` uses and -// is not a detail: what the substrate raised must be invisible to the removal pass of a +// is not a detail: what the foundation raised must be invisible to the removal pass of a // declaration that later arrives from the control plane, or the first thing the mesh tells this // node would tear down the mesh (novox/hq 04-ISSUES/010). // @@ -71,12 +71,12 @@ func ApplyBundle(ctx context.Context, o Options, sys system.System, d *declarati // // It cannot happen and it is refused with a sentence rather than a nil dereference. A sealing key // is generated at enrolment (`internal/identity`), and enrolment is something that happens on a -// mesh — which is the thing this program is raising. A substrate bundle carrying a sealed file +// mesh — which is the thing this program is raising. A foundation bundle carrying a sealed file // would be a bundle written for a node that has already joined. func refuseSealed(string) ([]byte, error) { return nil, errors.New( "this bundle contains a file sealed to a node's key, and a machine that has not enrolled " + - "has no such key. A substrate is applied before any mesh exists, so it can carry no " + + "has no such key. A foundation is applied before any mesh exists, so it can carry no " + "secret the mesh sealed") } diff --git a/internal/bootstrap/apply_test.go b/internal/bootstrap/apply_test.go index 7a3c8ea..e3f45e6 100644 --- a/internal/bootstrap/apply_test.go +++ b/internal/bootstrap/apply_test.go @@ -78,12 +78,12 @@ func TestASystemNobodyHasBuiltIsRefusedByName(t *testing.T) { } } -// A substrate is applied before any mesh exists, so it can carry no secret the mesh sealed — there +// A foundation is applied before any mesh exists, so it can carry no secret the mesh sealed — there // is no key to open one with. Refused with a sentence rather than a nil dereference. -func TestASealedFileInASubstrateIsRefusedWithAReason(t *testing.T) { +func TestASealedFileInAFoundationIsRefusedWithAReason(t *testing.T) { _, err := refuseSealed("anything") if err == nil { - t.Fatal("a sealed file in a substrate bundle was accepted") + t.Fatal("a sealed file in a foundation bundle was accepted") } if !strings.Contains(err.Error(), "has not enrolled") { t.Errorf("the refusal does not say why there is no key: %v", err) diff --git a/internal/bootstrap/bootstrap.go b/internal/bootstrap/bootstrap.go index b633144..e65496b 100644 --- a/internal/bootstrap/bootstrap.go +++ b/internal/bootstrap/bootstrap.go @@ -111,7 +111,7 @@ func failed(step Step, err error) error { // Options are the things that differ between machines. type Options struct { - // Template is the substrate bundle this machine's own bundle is made from. + // Template is the foundation bundle this machine's own bundle is made from. Template string // Out is where the produced bundle is written, so a person can read what was applied. Out string @@ -128,12 +128,12 @@ type Options struct { // runtime, a control plane opening its stores. Wait time.Duration - // Node is the name this machine is known by in the mesh. Everything after the substrate names + // Node is the name this machine is known by in the mesh. Everything after the foundation names // it: the record, the token, the assignment, the push. Node string // Catalogue is a checkout of the mesh's catalogue repository, which is where the registry's and - // the control plane's manifests are read from. Empty stops the installer after the substrate: + // the control plane's manifests are read from. Empty stops the installer after the foundation: // there is no pivot without manifests, and pretending otherwise would leave a machine that // looks installed and cannot upgrade itself. Catalogue string @@ -181,7 +181,7 @@ type Options struct { Extras []string } -// pivots reports whether this run goes past the substrate. +// pivots reports whether this run goes past the foundation. func (o Options) pivots() bool { return strings.TrimSpace(o.Catalogue) != "" } // Deps are the ways this program reaches outside itself. Injected so the whole of it can be @@ -245,11 +245,11 @@ type Result struct { Applied int `json:"applied,omitempty"` Changed bool `json:"changed,omitempty"` - // Running is the substrate's containers, confirmed up. + // Running is the foundation's containers, confirmed up. Running []string `json:"running,omitempty"` // Answered is what the temporary control plane said back — not merely that it is up. Answered string `json:"temporary-control-plane,omitempty"` - // Temporary is what the substrate's control plane is called, which is not what the module's is. + // Temporary is what the foundation's control plane is called, which is not what the module's is. Temporary string `json:"temporary-container,omitempty"` // Node is this machine's name in the mesh, and how it came to be enrolled and heard from. @@ -289,15 +289,15 @@ type Result struct { // answer to it is to run this again: re-running is the retry, and it is one a person chooses after // reading which step failed and why. // -// **Genesis is a pivot** (novox/hq ADR 0067). Steps 1 to 5 raise a substrate whose control plane is +// **Genesis is a pivot** (novox/hq ADR 0067). Steps 1 to 5 raise a foundation whose control plane is // named by the digest of its own configuration, because nothing has ever served that image and // nothing could have. Steps 6 to 10 turn that into a mesh that can maintain itself: this machine // enrols, the registry module is installed, the carried image is pushed INTO that registry — which // gives it a manifest digest, its first — and the control plane is reinstalled as an ordinary // module pinned to it. The temporary one is then dropped from the bundle and the host removes it. // -// **What makes the last part expressible is a name.** The substrate's control plane is called -// `temp-mesh-control` and the module's is called `mesh-control`. Two containers, two owners: +// **What makes the last part expressible is a name.** The foundation's control plane is called +// `temp-mesh-controller` and the module's is called `mesh-controller`. Two containers, two owners: // nothing is handed over, nothing has to stop being owned without being destroyed, and destruction // by omission is the right end for something named "temp". // @@ -309,7 +309,7 @@ type Result struct { // be fixed remotely — so no step may leave one: // // 1–3 nothing on the machine but a written file. Re-run: the bundle is produced again. -// 4 a partly-raised substrate, recorded in the state file. Re-run: apply converges the rest. +// 4 a partly-raised foundation, recorded in the state file. Re-run: apply converges the rest. // 5 everything up; something did not answer yet. Re-run: it is asked again. // 6 a node record and possibly a spent token. Re-run: `node list` finds the record, the // identity file says whether this machine enrolled, and a fresh token is issued if not. @@ -458,7 +458,7 @@ func Run(ctx context.Context, o Options, d Deps, say func(string)) (Result, erro o.Out, rewritten.Resources)) // ---- 4. apply ----------------------------------------------------------------------- - say("apply — raising the substrate") + say("apply — raising the foundation") report, err := ApplyBundle(ctx, o, sys, rewritten.Declaration, d.Run, say) result.Applied, result.Changed = len(report.Outcomes), report.Changed() if err != nil { @@ -472,7 +472,7 @@ func Run(ctx context.Context, o Options, d Deps, say func(string)) (Result, erro } // ---- 5. verify ---------------------------------------------------------------------- - say("verify — the substrate is up, and the control plane replies") + say("verify — the foundation is up, and the control plane replies") verified, err := Verify(ctx, rewritten.Declaration, d.Run, o.Timeout, o.Wait, say) result.Running, result.Answered = verified.Running, verified.Answered if err != nil { @@ -484,7 +484,7 @@ func Run(ctx context.Context, o Options, d Deps, say func(string)) (Result, erro // control plane is named by an image id, which no registry serves, so nothing can ever // replace it with a newer one. That is the whole of what the pivot fixes, and it needs // manifests, and manifests come from a checkout somebody has to point this at. - result.Stopped = "no --catalog was given, so this stopped at the substrate. " + + result.Stopped = "no --catalog was given, so this stopped at the foundation. " + "The control plane is named by the digest of its own configuration and no registry " + "serves it, so this mesh cannot yet upgrade itself. Run again with " + "--catalog to finish the pivot; every step " + @@ -497,7 +497,7 @@ func Run(ctx context.Context, o Options, d Deps, say func(string)) (Result, erro // ---- 6. enrol ------------------------------------------------------------------------- // // From here on the mesh is being told things, and the way to tell it anything is to run its - // own binary inside its own container. `temporary` is the substrate's control plane; the + // own binary inside its own container. `temporary` is the foundation's control plane; the // module's is a different container with a different name and does not exist yet. temporary := controlPlane{container: rewritten.TempName, run: d.Run, timeout: o.Timeout} @@ -588,9 +588,9 @@ func Run(ctx context.Context, o Options, d Deps, say func(string)) (Result, erro } // ---- 14. store ------------------------------------------------------------------------ - // A database PROVIDER. The substrate's store is the control plane's own memory and offers + // A database PROVIDER. The foundation's store is the control plane's own memory and offers // nothing to anything; the first thing that wants a database is the catalogue, next. - say("store — a database provider, which the substrate's own store is not") + say("store — a database provider, which the foundation's own store is not") if err := InstallFromCatalogue(ctx, o, permanentControl, "postgres", say); err != nil { return result, failed(StepStore, err) } diff --git a/internal/bootstrap/build_test.go b/internal/bootstrap/build_test.go index a62ecf2..9abd359 100644 --- a/internal/bootstrap/build_test.go +++ b/internal/bootstrap/build_test.go @@ -24,7 +24,7 @@ func TestAnInstallerWithNothingToBuildRefuses(t *testing.T) { // A repository without a commit refuses too, because a branch is somebody else's moving target. func TestABranchIsNotACommit(t *testing.T) { - err := Source{Repository: "https://example.invalid/mesh-control.git"}.Check() + err := Source{Repository: "https://example.invalid/mesh-controller.git"}.Check() if err == nil { t.Fatal("a source with no ref was accepted; genesis would have built whatever a branch pointed at") } @@ -35,7 +35,7 @@ func TestABranchIsNotACommit(t *testing.T) { // And a repository with a commit is enough. func TestARepositoryAndACommitIsEnough(t *testing.T) { - if err := (Source{Repository: "https://example.invalid/mesh-control.git", Ref: "a1b2c3d4"}).Check(); err != nil { + if err := (Source{Repository: "https://example.invalid/mesh-controller.git", Ref: "a1b2c3d4"}).Check(); err != nil { t.Fatalf("a repository and a commit were refused: %v", err) } } diff --git a/internal/bootstrap/control.go b/internal/bootstrap/control.go index 6f6d937..e7a655a 100644 --- a/internal/bootstrap/control.go +++ b/internal/bootstrap/control.go @@ -14,7 +14,7 @@ import ( // storeFileSuffix is how a manifest asks for a store connection in a file rather than in the // environment. // -// `MESH_STORE_` is what the control plane reads (mesh-control's `internal/store`.Variable) +// `MESH_STORE_` is what the control plane reads (mesh-controller's `internal/store`.Variable) // and putting a password in a container's environment puts it in `docker inspect` for ever. So a // module manifest names a file per context and points at it with `…_FILE`; the mesh seals the value // into that file on the machine, and nothing but the process reads it. @@ -41,20 +41,20 @@ type Permanent struct { // **The host performs the replacement, not the control plane** (novox/hq ADR 0067). The temporary // control plane composes a declaration naming the registry-pinned image, publishes it, and this // node's host creates the container. Nothing is asked to replace itself while running, which is -// what makes the whole thing expressible: the container being created is called `mesh-control` and -// the one composing it is called `temp-mesh-control`, so there are two of them and neither is in +// what makes the whole thing expressible: the container being created is called `mesh-controller` and +// the one composing it is called `temp-mesh-controller`, so there are two of them and neither is in // the other's way. // -// **The store connections are the substrate's, made at genesis, and the mesh cannot invent them.** +// **The store connections are the foundation's, made at genesis, and the mesh cannot invent them.** // Every other secret in a mesh is one the mesh made; these existed before the mesh did — they are -// the credentials the substrate bundle created the databases with. Generating replacements would +// the credentials the foundation bundle created the databases with. Generating replacements would // put thirty-two random bytes where a working connection string has to be, and the control plane // would come up unable to open a single context. So they go in through `secret accept`, which is // exactly the path for a value the mesh must carry and could not have invented — and they are read // out of the bundle this installer produced rather than reconstructed, because the bundle is what // created them and a second opinion about what a DSN should say is a second chance to be wrong. func InstallControlPlane(ctx context.Context, o Options, d Deps, control controlPlane, - substrate *declaration.Declaration, image string, say func(string)) (Permanent, error) { + foundation *declaration.Declaration, image string, say func(string)) (Permanent, error) { out := Permanent{Image: image} @@ -63,7 +63,7 @@ func InstallControlPlane(ctx context.Context, o Options, d Deps, control control return out, fmt.Errorf( "%w\n"+ "This is the manifest that makes the control plane an ordinary module. Without it "+ - "the machine keeps the temporary control plane the substrate raised, which works "+ + "the machine keeps the temporary control plane the foundation raised, which works "+ "and cannot be upgraded — so the install stops here rather than pretending to "+ "have pivoted", err) } @@ -92,7 +92,7 @@ func InstallControlPlane(ctx context.Context, o Options, d Deps, control control } // The connections, before the push that would otherwise deliver random bytes for them. - delivered, err := deliverStores(ctx, o, control, pinned, substrate, say) + delivered, err := deliverStores(ctx, o, control, pinned, foundation, say) out.Delivered = delivered if err != nil { return out, err @@ -107,7 +107,7 @@ func InstallControlPlane(ctx context.Context, o Options, d Deps, control control } // And it answers, which is the same question step 5 asked of the temporary one and for the // same reason: `status` opens all three stores, so a reply proves the sealed connections it - // was given are the ones the substrate made. Asked of the NEW container — this is the only + // was given are the ones the foundation made. Asked of the NEW container — this is the only // moment in the program where two control planes are running, and asking the wrong one would // report the temporary one's health as the permanent one's. answered, err := waitForTheControlPlane(ctx, control.run, o.Timeout, o.Wait, container, say) @@ -142,7 +142,7 @@ func pinImage(manifest []byte, reference string) ([]byte, int, error) { } // The reference the registry gave back is `/@sha256:…`, and what the // manifest holds is `@sha256:0…0`. Replacing only the digest would leave the - // manifest's own repository name in front of it — which may be `mesh-control` with no + // manifest's own repository name in front of it — which may be `mesh-controller` with no // registry, and a runtime would then pull it from the internet. The whole reference moves. var out bytes.Buffer rest := manifest @@ -215,21 +215,21 @@ func controlPlaneResourceIn(manifest []byte) string { return "" } -// deliverStores carries the substrate's own database connections into the module. +// deliverStores carries the foundation's own database connections into the module. // // The pairing is read from the manifest rather than assumed, so that whatever the catalogue calls // these secrets is what is delivered. The installer does not guess that the secret holding the // inventory connection is called `inventory`; it follows the manifest from the variable to the // secret, and a manifest whose two ends do not meet is refused rather than half-delivered. // -// **What is delivered is what the substrate already has, and only that.** The mesh generates an +// **What is delivered is what the foundation already has, and only that.** The mesh generates an // own-secret nobody supplied, which is right for something coming into existence and wrong for // something that already exists. So every variable the module fills from a secret is looked up in -// the substrate's control plane: what it names is accepted, what it does not is left for the mesh -// to make. A store connection missing from the substrate is the one exception and is an error — +// the foundation's control plane: what it names is accepted, what it does not is left for the mesh +// to make. A store connection missing from the foundation is the one exception and is an error — // a control plane that cannot open a context is not a control plane. func deliverStores(ctx context.Context, o Options, control controlPlane, manifest []byte, - substrate *declaration.Declaration, say func(string)) ([]string, error) { + foundation *declaration.Declaration, say func(string)) ([]string, error) { wanted, err := secretsByVariableIn(manifest) if err != nil { @@ -246,7 +246,7 @@ func deliverStores(ctx context.Context, o Options, control controlPlane, manifes ControlPlaneModule, storeVariablePrefix, storeVariablePrefix, storeFileSuffix) } - temporary, err := controlPlaneIn(substrate) + temporary, err := controlPlaneIn(foundation) if err != nil { return nil, err } @@ -260,13 +260,13 @@ func deliverStores(ctx context.Context, o Options, control controlPlane, manifes return delivered, fmt.Errorf( "the %s module wants %s and the bundle this installer produced does not name "+ "one.\n"+ - "That connection is the substrate's, created at genesis — the mesh cannot "+ + "That connection is the foundation's, created at genesis — the mesh cannot "+ "invent it and the installer will not guess at one", ControlPlaneModule, variable) } - // Not something the substrate made. The mesh generates its own, which is exactly what + // Not something the foundation made. The mesh generates its own, which is exactly what // an own-secret is for; said so that nothing about the delivery is silent. - say(" the mesh will make " + secret + " — the substrate names no " + variable) + say(" the mesh will make " + secret + " — the foundation names no " + variable) continue } @@ -282,7 +282,7 @@ func deliverStores(ctx context.Context, o Options, control controlPlane, manifes return delivered, err } delivered = append(delivered, secret) - say(" accepted " + secret + " — " + variable + ", as the substrate made it") + say(" accepted " + secret + " — " + variable + ", as the foundation made it") } return delivered, nil } diff --git a/internal/bootstrap/control_test.go b/internal/bootstrap/control_test.go index 9a6a5e1..e4b0d6f 100644 --- a/internal/bootstrap/control_test.go +++ b/internal/bootstrap/control_test.go @@ -9,37 +9,37 @@ import ( // Step 9 is where the control plane stops being a special case. These tests defend the two things // that could go wrong quietly: pinning it to the wrong image, and delivering it store connections -// the mesh invented rather than the ones the substrate actually made. +// the mesh invented rather than the ones the foundation actually made. // theControlPlaneModule is the catalogue's manifest, trimmed to what this installer reads. // -// A fixture rather than the file itself, unlike the substrate example the rewrite tests use: the +// A fixture rather than the file itself, unlike the foundation example the rewrite tests use: the // catalogue is a different repository on a different branch, and a test that read it would pass or // fail according to what somebody else had checked out. What it must stay faithful to is the // SHAPE — the placeholder digest, the own-secret per context, the mount from the machine's path to // the container's, and the environment file that fills what is not a path. const theControlPlaneModule = `{ - "module": "mesh-control", + "module": "mesh-controller", "version": "1", "slug": "control", "capabilities": ["container-runtime"], - "claims": [{"name": "the-control-plane", "scope": "mesh"}], + "claims": [{"name": "the-controller", "scope": "mesh"}], "own-secrets": { - "inventory": "/var/lib/mesh/mesh-control/inventory", - "identity": "/var/lib/mesh/mesh-control/identity", - "licences": "/var/lib/mesh/mesh-control/licences", - "broker": "/var/lib/mesh/mesh-control/broker", - "broker-management": "/var/lib/mesh/mesh-control/broker-management" + "inventory": "/var/lib/mesh/mesh-controller/inventory", + "identity": "/var/lib/mesh/mesh-controller/identity", + "licences": "/var/lib/mesh/mesh-controller/licences", + "broker": "/var/lib/mesh/mesh-controller/broker", + "broker-management": "/var/lib/mesh/mesh-controller/broker-management" }, "resources": [ - {"id": "mesh-state", "type": "directory", "path": "/var/lib/mesh/mesh-control", "mode": "0700"}, - {"id": "broker-env", "type": "file", "path": "/var/lib/mesh/mesh-control/broker.env", + {"id": "mesh-state", "type": "directory", "path": "/var/lib/mesh/mesh-controller", "mode": "0700"}, + {"id": "broker-env", "type": "file", "path": "/var/lib/mesh/mesh-controller/broker.env", "mode": "0600", "content": "MESH_BROKER_AMQP=${secret:broker}\nMESH_BROKER_MANAGEMENT=${secret:broker-management}\nMESH_BROKER_ADDRESS=${machine:at}:5671\n"}, - {"id": "server", "type": "container", "name": "mesh-control", - "image": "mesh-control@` + placeholderDigest + `", + {"id": "server", "type": "container", "name": "mesh-controller", + "image": "mesh-controller@` + placeholderDigest + `", "network": "host", "args": ["serve"], - "env-file": ["/var/lib/mesh/mesh-control/broker.env"], + "env-file": ["/var/lib/mesh/mesh-controller/broker.env"], "env": { "MESH_STORE_INVENTORY_FILE": "/run/secrets/inventory", "MESH_STORE_IDENTITY_FILE": "/run/secrets/identity", @@ -48,18 +48,18 @@ const theControlPlaneModule = `{ }, "volumes": [ "mesh-broker-tls:/broker-tls:ro", - "/var/lib/mesh/mesh-control/inventory:/run/secrets/inventory:ro", - "/var/lib/mesh/mesh-control/identity:/run/secrets/identity:ro", - "/var/lib/mesh/mesh-control/licences:/run/secrets/licences:ro" + "/var/lib/mesh/mesh-controller/inventory:/run/secrets/inventory:ro", + "/var/lib/mesh/mesh-controller/identity:/run/secrets/identity:ro", + "/var/lib/mesh/mesh-controller/licences:/run/secrets/licences:ro" ]} ] }` -const pushedReference = "127.0.0.1:5000/mesh-control@sha256:" + +const pushedReference = "127.0.0.1:5000/mesh-controller@sha256:" + "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" // **The whole reference moves, not only the digest.** The manifest's placeholder names a -// repository too, and replacing sixty-four zeros inside it would leave `mesh-control@sha256:…` +// repository too, and replacing sixty-four zeros inside it would leave `mesh-controller@sha256:…` // with no registry in front — which a runtime would go to the internet for, and this mesh's // control plane exists in no public registry by design. func TestTheControlPlaneIsPinnedToWhatThisMeshsRegistryAssigned(t *testing.T) { @@ -73,7 +73,7 @@ func TestTheControlPlaneIsPinnedToWhatThisMeshsRegistryAssigned(t *testing.T) { if !strings.Contains(string(pinned), `"image": "`+pushedReference+`"`) { t.Errorf("the manifest does not name the pushed image:\n%s", pinned) } - if strings.Contains(string(pinned), `"mesh-control@sha256:`) { + if strings.Contains(string(pinned), `"mesh-controller@sha256:`) { t.Errorf("the digest was replaced and the manifest's own repository name was left in "+ "front of it, so nothing says which registry serves it:\n%s", pinned) } @@ -95,10 +95,10 @@ func TestAManifestAlreadyPinnedByHandIsRefused(t *testing.T) { // otherwise be left half pinned, and fail inside an apply rather than here. func TestEveryPlaceTheManifestNamesTheImageIsPinned(t *testing.T) { twice := strings.Replace(theControlPlaneModule, - `{"id": "mesh-state", "type": "directory", "path": "/var/lib/mesh/mesh-control", "mode": "0700"},`, - `{"id": "mesh-state", "type": "directory", "path": "/var/lib/mesh/mesh-control", "mode": "0700"}, - {"id": "migrate", "type": "container", "name": "mesh-control-migrate", "run-once": true, - "image": "mesh-control@`+placeholderDigest+`", "args": ["migrate"]},`, 1) + `{"id": "mesh-state", "type": "directory", "path": "/var/lib/mesh/mesh-controller", "mode": "0700"},`, + `{"id": "mesh-state", "type": "directory", "path": "/var/lib/mesh/mesh-controller", "mode": "0700"}, + {"id": "migrate", "type": "container", "name": "mesh-controller-migrate", "run-once": true, + "image": "mesh-controller@`+placeholderDigest+`", "args": ["migrate"]},`, 1) pinned, places, err := pinImage([]byte(twice), pushedReference) if err != nil { @@ -112,8 +112,8 @@ func TestEveryPlaceTheManifestNamesTheImageIsPinned(t *testing.T) { } } -// **The connections are the substrate's, and they are read out of the bundle that made them.** -// The mesh cannot invent them: they are the credentials the substrate created the databases with, +// **The connections are the foundation's, and they are read out of the bundle that made them.** +// The mesh cannot invent them: they are the credentials the foundation created the databases with, // and thirty-two random bytes in their place would leave the control plane unable to open a single // context. The pairing is read from the manifest so that whatever the catalogue calls these // secrets is what is delivered. @@ -141,38 +141,38 @@ func TestTheStoreConnectionsComeFromTheBundleThatMadeThem(t *testing.T) { t.Error("the address the mesh composes from the machine was treated as a secret") } - // The values are the substrate's own, taken from the produced bundle rather than composed. + // The values are the foundation's own, taken from the produced bundle rather than composed. rewritten, err := Rewrite(theRealBundle(t), held) if err != nil { t.Fatal(err) } runtime := &asked{answer: aMeshThatAgrees(nil)} - control := controlPlane{container: "temp-mesh-control", run: runtime.run, timeout: time.Second} + control := controlPlane{container: "temp-mesh-controller", run: runtime.run, timeout: time.Second} delivered, err := deliverStores(context.Background(), Options{Node: "anchor"}, control, []byte(theControlPlaneModule), rewritten.Declaration, func(string) {}) if err != nil { t.Fatal(err) } - // Three stores and both halves of the broker: everything the substrate made and nothing else. + // Three stores and both halves of the broker: everything the foundation made and nothing else. if len(delivered) != 5 { - t.Fatalf("%d values were delivered, and the substrate names five: %v", + t.Fatalf("%d values were delivered, and the foundation names five: %v", len(delivered), delivered) } for _, secret := range delivered { - if !runtime.ran("secret accept anchor mesh-control " + secret + " --from") { + if !runtime.ran("secret accept anchor mesh-controller " + secret + " --from") { t.Errorf("%s was not accepted through `secret accept`: %v", secret, runtime.commands) } } } -// A secret the substrate did not make is left for the mesh to make, and said so. Every other +// A secret the foundation did not make is left for the mesh to make, and said so. Every other // secret in a mesh is one the mesh made; `secret accept` is only for what predates the mesh. -func TestASecretTheSubstrateNeverMadeIsLeftToTheMesh(t *testing.T) { +func TestASecretTheFoundationNeverMadeIsLeftToTheMesh(t *testing.T) { extra := strings.Replace(theControlPlaneModule, - `"broker": "/var/lib/mesh/mesh-control/broker",`, - `"broker": "/var/lib/mesh/mesh-control/broker", - "something-new": "/var/lib/mesh/mesh-control/something-new",`, 1) + `"broker": "/var/lib/mesh/mesh-controller/broker",`, + `"broker": "/var/lib/mesh/mesh-controller/broker", + "something-new": "/var/lib/mesh/mesh-controller/something-new",`, 1) extra = strings.Replace(extra, `"content": "MESH_BROKER_AMQP=${secret:broker}\n`, `"content": "MESH_SOMETHING_NEW=${secret:something-new}\nMESH_BROKER_AMQP=${secret:broker}\n`, 1) @@ -182,7 +182,7 @@ func TestASecretTheSubstrateNeverMadeIsLeftToTheMesh(t *testing.T) { t.Fatal(err) } runtime := &asked{answer: aMeshThatAgrees(nil)} - control := controlPlane{container: "temp-mesh-control", run: runtime.run, timeout: time.Second} + control := controlPlane{container: "temp-mesh-controller", run: runtime.run, timeout: time.Second} var said []string delivered, err := deliverStores(context.Background(), Options{Node: "anchor"}, control, @@ -192,7 +192,7 @@ func TestASecretTheSubstrateNeverMadeIsLeftToTheMesh(t *testing.T) { } for _, secret := range delivered { if secret == "something-new" { - t.Error("a value the substrate never made was accepted as though it had") + t.Error("a value the foundation never made was accepted as though it had") } } if !strings.Contains(strings.Join(said, "\n"), "the mesh will make something-new") { @@ -205,8 +205,8 @@ func TestASecretTheSubstrateNeverMadeIsLeftToTheMesh(t *testing.T) { // be — which presents as a control plane that will not start, three steps from the cause. func TestAConnectionFileNothingWritesIsRefused(t *testing.T) { mismatched := strings.Replace(theControlPlaneModule, - `"inventory": "/var/lib/mesh/mesh-control/inventory",`, - `"inventory": "/var/lib/mesh/mesh-control/somewhere-else",`, 1) + `"inventory": "/var/lib/mesh/mesh-controller/inventory",`, + `"inventory": "/var/lib/mesh/mesh-controller/somewhere-else",`, 1) _, err := secretsByVariableIn([]byte(mismatched)) if err == nil { @@ -226,11 +226,11 @@ func TestAManifestWantingNoStoresIsRefusedWithTheShapeItShouldHave(t *testing.T) t.Fatal(err) } runtime := &asked{answer: aMeshThatAgrees(nil)} - control := controlPlane{container: "temp-mesh-control", run: runtime.run, timeout: time.Second} + control := controlPlane{container: "temp-mesh-controller", run: runtime.run, timeout: time.Second} - bare := `{"module":"mesh-control","version":"1","resources":[ - {"id":"container","type":"container","name":"mesh-control", - "image":"mesh-control@` + placeholderDigest + `"}]}` + bare := `{"module":"mesh-controller","version":"1","resources":[ + {"id":"container","type":"container","name":"mesh-controller", + "image":"mesh-controller@` + placeholderDigest + `"}]}` _, err = deliverStores(context.Background(), Options{Node: "anchor"}, control, []byte(bare), rewritten.Declaration, func(string) {}) @@ -244,13 +244,13 @@ func TestAManifestWantingNoStoresIsRefusedWithTheShapeItShouldHave(t *testing.T) // The permanent control plane is asked a question, not merely looked at — the same question the // temporary one was asked at step 5, and for the same reason: `status` opens all three stores, so -// a reply proves the sealed connections it was given are the ones the substrate made. +// a reply proves the sealed connections it was given are the ones the foundation made. func TestThePermanentControlPlaneIsAskedTheSameQuestion(t *testing.T) { runtime := &asked{answer: aMeshThatAgrees(map[string]string{ "module list": "", - "exec mesh-control /mesh-control": "1 node, 0 waiting\n", + "exec mesh-controller /mesh-controller": "1 node, 0 waiting\n", })} - control := controlPlane{container: "temp-mesh-control", run: runtime.run, timeout: time.Second} + control := controlPlane{container: "temp-mesh-controller", run: runtime.run, timeout: time.Second} rewritten, err := Rewrite(theRealBundle(t), held) if err != nil { t.Fatal(err) @@ -265,11 +265,11 @@ func TestThePermanentControlPlaneIsAskedTheSameQuestion(t *testing.T) { if out.Answered != "1 node, 0 waiting" { t.Errorf("the permanent control plane's reply is reported as %q", out.Answered) } - if !runtime.ran("docker exec mesh-control " + controlPlaneBinary + " status") { + if !runtime.ran("docker exec mesh-controller " + controlPlaneBinary + " status") { t.Errorf("the permanent control plane was never asked anything: %v", runtime.commands) } // And the module was registered with the digest, not with the placeholder. - if !runtime.ran("module add /mesh-control-module.json") { + if !runtime.ran("module add /mesh-controller-module.json") { t.Errorf("the module was never registered: %v", runtime.commands) } } diff --git a/internal/bootstrap/enrol.go b/internal/bootstrap/enrol.go index facbda5..10c4646 100644 --- a/internal/bootstrap/enrol.go +++ b/internal/bootstrap/enrol.go @@ -13,7 +13,7 @@ import ( // hereIs what `node list` says about a machine the mesh has heard from recently. // -// mesh-control prints one of three words per node: "here", "never spoken", or "out of touch ". +// mesh-controller prints one of three words per node: "here", "never spoken", or "out of touch ". // The installer waits for the first, and it is the only honest proof that the host agent is // running: an enrolled machine whose host is not running looks exactly like an enrolled machine // whose host has crashed, and both look exactly like a successful install until the first push diff --git a/internal/bootstrap/enrol_test.go b/internal/bootstrap/enrol_test.go index cad2fa9..48e71cb 100644 --- a/internal/bootstrap/enrol_test.go +++ b/internal/bootstrap/enrol_test.go @@ -70,7 +70,7 @@ func TestAMachineThatHasAlreadyEnrolledIsNotEnrolledAgain(t *testing.T) { out, err := Enrol(context.Background(), Options{ Node: "anchor", State: alreadyEnrolled(t, "anchor"), Timeout: time.Second, Host: "/usr/local/bin/mesh-host", HostInBackground: true, - }, arch(t), controlPlane{container: "temp-mesh-control", run: runtime.run, timeout: time.Second}, + }, arch(t), controlPlane{container: "temp-mesh-controller", run: runtime.run, timeout: time.Second}, func(string) {}) if err != nil { t.Fatal(err) @@ -109,7 +109,7 @@ func TestAMeshThatHasHeardFromAMachineWithNoAgentStartsOne(t *testing.T) { out, err := Enrol(context.Background(), Options{ Node: "anchor", State: alreadyEnrolled(t, "anchor"), Timeout: time.Second, Host: "/usr/local/bin/mesh-host", HostInBackground: true, - }, arch(t), controlPlane{container: "temp-mesh-control", run: runtime.run, timeout: time.Second}, + }, arch(t), controlPlane{container: "temp-mesh-controller", run: runtime.run, timeout: time.Second}, func(string) {}) if err != nil { t.Fatal(err) @@ -135,7 +135,7 @@ func TestAMachineEnrolledUnderAnotherNameIsRefused(t *testing.T) { _, err := Enrol(context.Background(), Options{ Node: "anchor", State: alreadyEnrolled(t, "somewhere-else"), Timeout: time.Second, - }, arch(t), controlPlane{container: "temp-mesh-control", run: runtime.run, timeout: time.Second}, + }, arch(t), controlPlane{container: "temp-mesh-controller", run: runtime.run, timeout: time.Second}, func(string) {}) if err == nil { t.Fatal("a machine already enrolled as something else was enrolled again") @@ -173,7 +173,7 @@ func TestAHostThatIsRunningAndUnheardOfIsNotAnInstall(t *testing.T) { _, err := Enrol(context.Background(), Options{ Node: "anchor", State: alreadyEnrolled(t, "anchor"), HostService: "mesh-host.service", Timeout: time.Second, Wait: 0, - }, arch(t), controlPlane{container: "temp-mesh-control", run: runtime.run, timeout: time.Second}, + }, arch(t), controlPlane{container: "temp-mesh-controller", run: runtime.run, timeout: time.Second}, func(string) {}) if err == nil { t.Fatal("a node the mesh has never heard from was reported enrolled and running") @@ -202,7 +202,7 @@ func TestAMachineWithNoHostServiceIsRefusedRatherThanGivenOne(t *testing.T) { _, err := Enrol(context.Background(), Options{ Node: "anchor", State: alreadyEnrolled(t, "anchor"), HostService: "mesh-host.service", Timeout: time.Second, Wait: 0, - }, arch(t), controlPlane{container: "temp-mesh-control", run: runtime.run, timeout: time.Second}, + }, arch(t), controlPlane{container: "temp-mesh-controller", run: runtime.run, timeout: time.Second}, func(string) {}) if err == nil { t.Fatal("a machine with no host service was reported as having a running host") @@ -222,7 +222,7 @@ func TestAMachineWithNoNameIsRefusedBeforeAnythingIsAsked(t *testing.T) { return "", fmt.Errorf("nothing should have been asked") }} _, err := Enrol(context.Background(), Options{Timeout: time.Second}, arch(t), - controlPlane{container: "temp-mesh-control", run: runtime.run}, func(string) {}) + controlPlane{container: "temp-mesh-controller", run: runtime.run}, func(string) {}) if err == nil { t.Fatal("a machine with no name was enrolled") } diff --git a/internal/bootstrap/load.go b/internal/bootstrap/load.go index ef6ca17..90f131f 100644 --- a/internal/bootstrap/load.go +++ b/internal/bootstrap/load.go @@ -120,7 +120,7 @@ func loadImage(ctx context.Context, run Runner, saved []byte, dryRun bool, say f // Through a file rather than through stdin: the runner this repository shares runs a command // and captures its output, and giving it a second mouth for one caller would change every // applier's contract for the sake of one step (internal/apply's Runner). - tarball, err := os.CreateTemp("", "mesh-control-*.tar") + tarball, err := os.CreateTemp("", "mesh-controller-*.tar") if err != nil { return loaded, fmt.Errorf("nowhere to put the carried image while loading it: %w", err) } diff --git a/internal/bootstrap/load_test.go b/internal/bootstrap/load_test.go index b37fdc2..2707603 100644 --- a/internal/bootstrap/load_test.go +++ b/internal/bootstrap/load_test.go @@ -41,12 +41,12 @@ func (a *asked) ran(fragment string) bool { return false } -// savedImageFixture builds what `docker save` produces, tagged `mesh-control:test` unless a test +// savedImageFixture builds what `docker save` produces, tagged `mesh-controller:test` unless a test // asks for something else. Pass no tags for an archive saved without one. func savedImageFixture(t *testing.T, digest string, tags ...string) []byte { t.Helper() if tags == nil { - tags = []string{"mesh-control:test"} + tags = []string{"mesh-controller:test"} } entries, err := json.Marshal([]struct { Config string @@ -75,7 +75,7 @@ func savedImageFixture(t *testing.T, digest string, tags ...string) []byte { // fixtureDigest is what the ARCHIVE calls the image, and runtimeDigest is what a runtime calls it // after loading the same bytes. They differ on purpose, because they differ in reality: an image // id is the digest of the image's configuration, and a runtime rewrites that configuration as it -// loads. Measured on a live raise, `mesh-control:development` was `sha256:b86bb81c…` on the +// loads. Measured on a live raise, `mesh-controller:development` was `sha256:b86bb81c…` on the // workstation that saved it and `sha256:2dc21904…` on the machine that loaded it. const ( fixtureDigest = "3333333333333333333333333333333333333333333333333333333333333333" @@ -107,7 +107,7 @@ func TestTheIdComesFromTheRuntimeAndNotFromTheArchive(t *testing.T) { // Loaded — and stored under a configuration of the runtime's own making. return "sha256:" + runtimeDigest + "\n", nil case len(args) > 0 && args[0] == "load": - return "Loaded image: mesh-control:test\n", nil + return "Loaded image: mesh-controller:test\n", nil } return "", fmt.Errorf("unexpected command: %v", args) } @@ -128,7 +128,7 @@ func TestTheIdComesFromTheRuntimeAndNotFromTheArchive(t *testing.T) { t.Error("a real run reported its id as a prediction") } // The runtime was asked BY THE TAG, which is the only name that survives the transfer. - if !runtime.ran("docker image inspect --format {{.Id}} mesh-control:test") { + if !runtime.ran("docker image inspect --format {{.Id}} mesh-controller:test") { t.Errorf("the runtime was never asked what the tag resolves to: %v", runtime.commands) } // And the difference is said out loud, or somebody comparing this against `docker images` on @@ -183,7 +183,7 @@ func TestALoadThatLeftNothingBehindIsAFailure(t *testing.T) { if len(args) > 1 && args[0] == "image" && args[1] == "inspect" { return "", errors.New("Error: No such image") } - return "Loaded image: mesh-control:test\n", nil + return "Loaded image: mesh-controller:test\n", nil }} _, err := loadImage(context.Background(), runtime.run, @@ -192,7 +192,7 @@ func TestALoadThatLeftNothingBehindIsAFailure(t *testing.T) { t.Fatal("a load that left nothing on the machine was reported as success") } // Named by the tag, because that is what was asked about and what is missing. - if !strings.Contains(err.Error(), "mesh-control:test") { + if !strings.Contains(err.Error(), "mesh-controller:test") { t.Errorf("the failure does not say what this machine holds nothing of: %v", err) } } @@ -298,7 +298,7 @@ func TestAnInstallerCarryingNoImageSaysSoRatherThanRaisingHalfAMesh(t *testing.T // check anything against, so it is checked where the refusal can say whose mistake it is. func TestARuntimeAnsweringSomethingThatIsNotAnImageIdIsRefused(t *testing.T) { for _, nonsense := range []string{ - "mesh-control:test", + "mesh-controller:test", "sha256:" + strings.Repeat("9", 63), "", } { diff --git a/internal/bootstrap/module.go b/internal/bootstrap/module.go index 2c6eb43..0802e90 100644 --- a/internal/bootstrap/module.go +++ b/internal/bootstrap/module.go @@ -80,7 +80,7 @@ func installModule(ctx context.Context, o Options, control controlPlane, module // Split out because one module needs something in between: the control plane's own store // connections have to be accepted before its declaration is composed, or the mesh would seal // thirty-two random bytes into the file it expects a connection string in and the container would -// come up unable to open anything (mesh-control's `secret accept`, and what it exists for). +// come up unable to open anything (mesh-controller's `secret accept`, and what it exists for). // // **`module add` is run every time and is not skipped when the module is already known.** It is an // upsert on the manifest, and the manifest is exactly what changes between runs — step 9 registers diff --git a/internal/bootstrap/phase_packages.go b/internal/bootstrap/phase_packages.go index 10be2b7..d7f24ca 100644 --- a/internal/bootstrap/phase_packages.go +++ b/internal/bootstrap/phase_packages.go @@ -14,7 +14,7 @@ import ( // The base (mesh-tools) resolves the SDK by version from the mesh's package registry rather than // cloning it from a git URL (novox/hq ADR 0076, issue 053). So the registry has to answer, and the // SDK has to be in it, before the base build runs. That is a pivot like the control plane's: gitea's -// SERVER is raised directly here, on the substrate's own postgres, and adopted as an ordinary module +// SERVER is raised directly here, on the foundation's own postgres, and adopted as an ordinary module // only after the base exists (which is what lets its provisioner image — built on the base — run). // // Nothing here is the steady state. It is the smallest set of acts that puts a working npm registry @@ -22,9 +22,9 @@ import ( // and the SDK published under it. The gitea MODULE, installed after the base, takes all of this over. const ( - // substrateStore is the substrate's postgres container — the mesh's own memory, raised from the + // foundationStore is the foundation's postgres container — the mesh's own memory, raised from the // bundle. gitea's bootstrap database lives here too, so a mesh runs one postgres (issue 051). - substrateStore = "mesh-store" + foundationStore = "mesh-store" // giteaBootstrap is the gitea server raised directly at genesis, before gitea is a module. giteaBootstrap = "mesh-gitea-server" // giteaImage is the same upstream image the gitea module runs, pinned identically so the module @@ -39,7 +39,7 @@ const ( // builderGiteaUser is the gitea account the builder publishes and pulls with at genesis. It is // the `as` the builder's static package binding names. builderGiteaUser = "mesh-builder" - // giteaDBRole/giteaDBName is gitea's own database in the substrate store. + // giteaDBRole/giteaDBName is gitea's own database in the foundation store. giteaDBRole = "mesh_gitea" giteaDBName = "mesh_gitea" // giteaPort is where the raised server answers on the machine. @@ -59,7 +59,7 @@ func RaisePackageRegistry(ctx context.Context, o Options, d Deps, control contro return err } - say(" seeding gitea's database in the substrate store") + say(" seeding gitea's database in the foundation store") if err := seedGiteaDatabase(ctx, run, o.Timeout, dbPassword, say); err != nil { return err } @@ -106,8 +106,8 @@ func RaisePackageRegistry(ctx context.Context, o Options, d Deps, control contro return nil } -// seedGiteaDatabase creates gitea's role and database inside the substrate postgres, the same way -// the substrate creates its own — psql run through the store container (the map's Route B). The role +// seedGiteaDatabase creates gitea's role and database inside the foundation postgres, the same way +// the foundation creates its own — psql run through the store container (the map's Route B). The role // is created before the database because the database is owned by it. Both are tolerant of already // existing, so a re-run changes nothing. func seedGiteaDatabase(ctx context.Context, run Runner, timeout time.Duration, password string, @@ -119,7 +119,7 @@ func seedGiteaDatabase(ctx context.Context, run Runner, timeout time.Duration, p // transaction and \gexec does not parse through -c. The password is base64url, so it carries no // quote or backslash to escape inside a SQL literal. psql := func(sql string) (string, error) { - return run(asking, "docker", "exec", substrateStore, "psql", "-U", "postgres", "-tAc", sql) + return run(asking, "docker", "exec", foundationStore, "psql", "-U", "postgres", "-tAc", sql) } // The role: create it, and if it is already there (create fails) reset its password so a re-run @@ -128,7 +128,7 @@ func seedGiteaDatabase(ctx context.Context, run Runner, timeout time.Duration, p if _, err := psql(create); err != nil { alter := fmt.Sprintf("ALTER ROLE %s LOGIN PASSWORD '%s'", giteaDBRole, password) if _, err := psql(alter); err != nil { - return fmt.Errorf("could not create gitea's role in %s: %w", substrateStore, err) + return fmt.Errorf("could not create gitea's role in %s: %w", foundationStore, err) } } @@ -136,17 +136,17 @@ func seedGiteaDatabase(ctx context.Context, run Runner, timeout time.Duration, p // second create is an error rather than a no-op. present, err := psql(fmt.Sprintf("SELECT 1 FROM pg_database WHERE datname='%s'", giteaDBName)) if err != nil { - return fmt.Errorf("could not check for gitea's database in %s: %w", substrateStore, err) + return fmt.Errorf("could not check for gitea's database in %s: %w", foundationStore, err) } if strings.TrimSpace(present) != "1" { if _, err := psql(fmt.Sprintf("CREATE DATABASE %s OWNER %s", giteaDBName, giteaDBRole)); err != nil { - return fmt.Errorf("could not create gitea's database in %s: %w", substrateStore, err) + return fmt.Errorf("could not create gitea's database in %s: %w", foundationStore, err) } } return nil } -// raiseGiteaServer starts the gitea server container against the substrate store. It joins the +// raiseGiteaServer starts the gitea server container against the foundation store. It joins the // store's network namespace so `127.0.0.1:5432` reaches postgres, and publishes its own port on the // machine so the builder and this installer can reach it. Started if absent, left alone if present. func raiseGiteaServer(ctx context.Context, run Runner, timeout time.Duration, dbPassword string, @@ -179,7 +179,7 @@ func raiseGiteaServer(ctx context.Context, run Runner, timeout time.Duration, db } args := append([]string{ "run", "-d", "--name", giteaBootstrap, - // Host network, like the control plane: it reaches the substrate store on the machine's + // Host network, like the control plane: it reaches the foundation store on the machine's // loopback (where the store publishes 5432) and answers on the machine's own 3000, which is // where mesh-bootstrap and the builder's build containers look for it. "--network", "host", diff --git a/internal/bootstrap/preflight.go b/internal/bootstrap/preflight.go index f6008a6..5275aeb 100644 --- a/internal/bootstrap/preflight.go +++ b/internal/bootstrap/preflight.go @@ -26,7 +26,7 @@ func Preflight(ctx context.Context, o Options, d Deps, say func(string)) ([]byte // 1. Does this installer carry what it claims to? // // Asked before the machine is touched, for the same reason `mesh-host bundle` exists: a host - // that carries no substrate must say so when somebody asks, not on a first node + // that carries no foundation must say so when somebody asks, not on a first node // (internal/bundle). An installer built without an image would otherwise get a machine as far // as a running store and a running broker and stop. if image.IsEmpty() { @@ -67,13 +67,13 @@ func Preflight(ctx context.Context, o Options, d Deps, say func(string)) ([]byte } say(fmt.Sprintf(" builder %s carried (the archive calls it %s)", tag, carriedID)) - // 2. Is the template there, and is it a substrate? + // 2. Is the template there, and is it a foundation? template, err := os.ReadFile(o.Template) if err != nil { return nil, fmt.Errorf( "the bundle template could not be read: %w\n"+ "It is what this machine will be asked to be, so there is nothing to do without "+ - "it. Point --bundle at one; mesh-host's examples/substrate-first-node.lock is "+ + "it. Point --bundle at one; mesh-host's examples/foundation-first-node.lock is "+ "the shape", err) } // Parsed here as well as at the rewrite, because a template that is not a declaration should @@ -120,7 +120,7 @@ func Preflight(ctx context.Context, o Options, d Deps, say func(string)) ([]byte // with the id of the image this installer carries. Whatever the slot held is therefore never // pulled, never fetched, and never reached; requiring it to be reachable refuses a correct // install because of a string that is about to be thrown away. Found on the first real run: the - // lab's template still carried `192.0.2.250:5000/mesh-control@…`, the address of a registry that + // lab's template still carried `192.0.2.250:5000/mesh-controller@…`, the address of a registry that // no longer exists, and preflight timed out dialling it. for _, host := range registriesIn(parsed) { dialing, cancel := context.WithTimeout(ctx, o.Timeout) diff --git a/internal/bootstrap/preflight_test.go b/internal/bootstrap/preflight_test.go index 1664da1..3321ef5 100644 --- a/internal/bootstrap/preflight_test.go +++ b/internal/bootstrap/preflight_test.go @@ -82,7 +82,7 @@ func TestOnlyTheRegistriesTheBundleNamesAreAskedAbout(t *testing.T) { strings.Repeat("7", 64) + `"}, {"id":"broker","type":"container","name":"mesh-broker","image":"192.0.2.250:5000/lavinmq@sha256:` + strings.Repeat("8", 64) + `"}, - {"id":"control-plane","type":"container","name":"mesh-control","image":"` + held + `"} + {"id":"control-plane","type":"container","name":"mesh-controller","image":"` + held + `"} ]}`)) if err != nil { t.Fatal(err) @@ -111,7 +111,7 @@ func TestTheControlPlanesOwnRegistryIsNeverAskedAbout(t *testing.T) { parsed, err := declaration.ParseFileTrusted([]byte(`{"declaration":1,"resources":[ {"id":"store","type":"container","name":"mesh-store","image":"postgres@sha256:` + strings.Repeat("7", 64) + `"}, - {"id":"control-plane","type":"container","name":"mesh-control","image":"192.0.2.250:5000/mesh-control@sha256:` + + {"id":"control-plane","type":"container","name":"mesh-controller","image":"192.0.2.250:5000/mesh-controller@sha256:` + strings.Repeat("8", 64) + `"} ]}`)) if err != nil { @@ -137,7 +137,7 @@ func TestWhereAnImageWouldBeFetchedFrom(t *testing.T) { {"postgres@sha256:" + strings.Repeat("a", 64), DefaultRegistry, true}, {"cloudamqp/lavinmq@sha256:" + strings.Repeat("a", 64), DefaultRegistry, true}, {"192.0.2.250:5000/postgres@sha256:" + strings.Repeat("a", 64), "192.0.2.250:5000", true}, - {"localhost/mesh-control@sha256:" + strings.Repeat("a", 64), "localhost:443", true}, + {"localhost/mesh-controller@sha256:" + strings.Repeat("a", 64), "localhost:443", true}, {"registry.example.com/a/b@sha256:" + strings.Repeat("a", 64), "registry.example.com:443", true}, // Held by this machine. Nothing serves it, and nothing can. {"sha256:" + strings.Repeat("a", 64), "", false}, diff --git a/internal/bootstrap/publish.go b/internal/bootstrap/publish.go index 5ad5af9..0b01ca7 100644 --- a/internal/bootstrap/publish.go +++ b/internal/bootstrap/publish.go @@ -9,19 +9,19 @@ import ( ) // ControlPlaneRepository is what the control plane's image is called in the mesh's own registry. -const ControlPlaneRepository = "mesh-control" +const ControlPlaneRepository = "mesh-controller" // genesisTag is the tag the first push uses. // // A tag is not a pin and is never what anything is deployed from — the digest the registry assigns -// is (novox/hq ADR 0006). This exists so a person reading `/v2/mesh-control/tags/list` can see +// is (novox/hq ADR 0006). This exists so a person reading `/v2/mesh-controller/tags/list` can see // which image this mesh started from, and so the push has something to name. Everything downstream // uses the digest that comes back. const genesisTag = "genesis" // Published is what step 8 did. type Published struct { - // Reference is `/mesh-control@sha256:…` — the first manifest digest this image has + // Reference is `/mesh-controller@sha256:…` — the first manifest digest this image has // ever had, and the thing that makes the control plane an ordinary module. Reference string // Tagged is where it was pushed, tag and all. @@ -34,7 +34,7 @@ type Published struct { // // **This is the pivot's hinge.** Every image must be pinned by digest, and a digest a pin can mean // is one a REGISTRY assigned when something was pushed to it. The control plane's image is built -// from source and pushed nowhere, so it has none — which is why the substrate names it by the +// from source and pushed nowhere, so it has none — which is why the foundation names it by the // digest of its own configuration, and why that is legal exactly where nothing could have served // one. The moment this push completes, that stops being true: the image has a manifest digest, so // the control plane can be named the way every other module is named, so the mesh can build and @@ -42,7 +42,7 @@ type Published struct { // upgrade itself, which is the check novox/hq ADR 0067 states: after installing, the running // control plane must be pinned by a digest the mesh's own registry assigned, not by an image id. // -// **It mirrors mesh-control's `internal/builder`.PublishImage rather than importing it.** Tag, +// **It mirrors mesh-controller's `internal/builder`.PublishImage rather than importing it.** Tag, // push, read back `RepoDigests`, refuse anything without `@sha256:` — the same four steps, because // there is exactly one right way to learn what a registry will serve something as, and it is to // ask the registry. It is not imported because that code is tier 2: the host and its installer diff --git a/internal/bootstrap/publish_test.go b/internal/bootstrap/publish_test.go index 759414d..c1b526d 100644 --- a/internal/bootstrap/publish_test.go +++ b/internal/bootstrap/publish_test.go @@ -41,7 +41,7 @@ func TestAnImageNoRegistryHasEverHeldIsPushed(t *testing.T) { if !pushed { return http.StatusNotFound, "", nil } - return http.StatusOK, `{"name":"mesh-control","tags":["genesis"]}`, nil + return http.StatusOK, `{"name":"mesh-controller","tags":["genesis"]}`, nil }, func(_ string, args []string) (string, error) { switch args[0] { @@ -51,7 +51,7 @@ func TestAnImageNoRegistryHasEverHeldIsPushed(t *testing.T) { pushed = true return "", nil case "inspect": - return `["127.0.0.1:5000/mesh-control@sha256:` + strings.Repeat("a", 64) + `"]`, nil + return `["127.0.0.1:5000/mesh-controller@sha256:` + strings.Repeat("a", 64) + `"]`, nil } return "", fmt.Errorf("unexpected: %v", args) }) @@ -63,10 +63,10 @@ func TestAnImageNoRegistryHasEverHeldIsPushed(t *testing.T) { if out.Already { t.Error("an image no registry held was reported as already published") } - if !strings.HasPrefix(out.Reference, "127.0.0.1:5000/mesh-control@sha256:") { + if !strings.HasPrefix(out.Reference, "127.0.0.1:5000/mesh-controller@sha256:") { t.Errorf("the control plane is pinned as %q", out.Reference) } - if !runtime.ran("docker push 127.0.0.1:5000/mesh-control:genesis") { + if !runtime.ran("docker push 127.0.0.1:5000/mesh-controller:genesis") { t.Errorf("nothing was pushed: %v", runtime.commands) } } @@ -77,11 +77,11 @@ func TestAnImageNoRegistryHasEverHeldIsPushed(t *testing.T) { func TestAnImageTheRegistryAlreadyServesIsNotPushedAgain(t *testing.T) { o, d, runtime := publishing(t, func(string) (int, string, error) { - return http.StatusOK, `{"name":"mesh-control","tags":["genesis"]}`, nil + return http.StatusOK, `{"name":"mesh-controller","tags":["genesis"]}`, nil }, func(_ string, args []string) (string, error) { if args[0] == "inspect" { - return `["127.0.0.1:5000/mesh-control@sha256:` + strings.Repeat("b", 64) + `"]`, nil + return `["127.0.0.1:5000/mesh-controller@sha256:` + strings.Repeat("b", 64) + `"]`, nil } return "", fmt.Errorf("unexpected: %v", args) }) @@ -103,8 +103,8 @@ func TestAnImageTheRegistryAlreadyServesIsNotPushedAgain(t *testing.T) { // listed first — which would pin this mesh's control plane to somebody else's registry, silently, // which is the dependency the whole pivot exists to remove. func TestTheDigestComesFromThisMeshsOwnRegistry(t *testing.T) { - elsewhere := "some.other.registry/mesh-control@sha256:" + strings.Repeat("c", 64) - ours := "127.0.0.1:5000/mesh-control@sha256:" + strings.Repeat("d", 64) + elsewhere := "some.other.registry/mesh-controller@sha256:" + strings.Repeat("c", 64) + ours := "127.0.0.1:5000/mesh-controller@sha256:" + strings.Repeat("d", 64) o, d, _ := publishing(t, func(string) (int, string, error) { @@ -167,7 +167,7 @@ func TestATagIsNotAPin(t *testing.T) { }, func(_ string, args []string) (string, error) { if args[0] == "inspect" { - return `["127.0.0.1:5000/mesh-control:genesis"]`, nil + return `["127.0.0.1:5000/mesh-controller:genesis"]`, nil } return "", nil }) diff --git a/internal/bootstrap/registry.go b/internal/bootstrap/registry.go index 2578473..359007f 100644 --- a/internal/bootstrap/registry.go +++ b/internal/bootstrap/registry.go @@ -44,7 +44,7 @@ type Registry struct { // // **No credentials, and that is deliberate.** The registry is reached over the mesh's own private // network, which is already the encrypted and authenticated thing; a second layer inside it would -// be certificates to issue and rotate for no property the first does not have (mesh-control's +// be certificates to issue and rotate for no property the first does not have (mesh-controller's // `internal/builder`, which pushes to it the same way). So there is nothing here to configure and // nothing to seal — which is also why step 8 can push without the mesh having issued anything. // @@ -136,7 +136,7 @@ func waitForTheRegistry(ctx context.Context, d Deps, o Options, say func(string) // waitForContainer waits for a container the mesh was asked to create to be running. // -// Unlike the substrate's own verify, this one waits: the mesh applies through a node's host, over +// Unlike the foundation's own verify, this one waits: the mesh applies through a node's host, over // the broker, asynchronously. A push that the control plane accepted has not yet happened on the // machine, and refusing on the first look would refuse every correct install. func waitForContainer(ctx context.Context, run Runner, probe, wait time.Duration, name string, diff --git a/internal/bootstrap/registry_test.go b/internal/bootstrap/registry_test.go index effae95..4d86d71 100644 --- a/internal/bootstrap/registry_test.go +++ b/internal/bootstrap/registry_test.go @@ -18,7 +18,7 @@ import ( // catalogueWith writes a fake catalogue checkout holding one module's manifest. // -// A fixture here rather than the real catalogue, unlike the substrate example the rewrite tests +// A fixture here rather than the real catalogue, unlike the foundation example the rewrite tests // use: the catalogue is a different repository on a different branch, and a test that read it // would pass or fail according to what somebody else had checked out. func catalogueWith(t *testing.T, module, manifest string) string { @@ -100,7 +100,7 @@ func TestARegistryContainerThatIsUpIsNotARegistryThatServes(t *testing.T) { _, err := InstallRegistry(context.Background(), installing(t, catalogueWith(t, RegistryModule, upstreamRegistryManifest)), - deps, controlPlane{container: "temp-mesh-control", run: runtime.run, timeout: time.Second}, + deps, controlPlane{container: "temp-mesh-controller", run: runtime.run, timeout: time.Second}, func(string) {}) if err == nil { t.Fatal("a registry whose container is up and which answers 500 was accepted") @@ -124,7 +124,7 @@ func TestARegistryThatAnswersIsAccepted(t *testing.T) { out, err := InstallRegistry(context.Background(), installing(t, catalogueWith(t, RegistryModule, upstreamRegistryManifest)), - deps, controlPlane{container: "temp-mesh-control", run: runtime.run, timeout: time.Second}, + deps, controlPlane{container: "temp-mesh-controller", run: runtime.run, timeout: time.Second}, func(string) {}) if err != nil { t.Fatal(err) @@ -159,7 +159,7 @@ func TestARegistryManifestThatWantsBuildingIsRefused(t *testing.T) { runtime := &asked{answer: aMeshThatAgrees(nil)} _, err := InstallRegistry(context.Background(), installing(t, catalogueWith(t, RegistryModule, wants)), - Deps{Run: runtime.run}, controlPlane{container: "temp-mesh-control", run: runtime.run}, + Deps{Run: runtime.run}, controlPlane{container: "temp-mesh-controller", run: runtime.run}, func(string) {}) if err == nil { t.Fatal("a registry manifest naming an image the mesh would have to build was accepted") @@ -178,7 +178,7 @@ func TestACatalogueThatIsNotThereIsSaidPlainly(t *testing.T) { runtime := &asked{answer: aMeshThatAgrees(nil)} _, err := InstallRegistry(context.Background(), installing(t, filepath.Join(t.TempDir(), "nowhere")), - Deps{Run: runtime.run}, controlPlane{container: "temp-mesh-control", run: runtime.run}, + Deps{Run: runtime.run}, controlPlane{container: "temp-mesh-controller", run: runtime.run}, func(string) {}) if err == nil { t.Fatal("a catalogue that does not exist was accepted") @@ -188,7 +188,7 @@ func TestACatalogueThatIsNotThereIsSaidPlainly(t *testing.T) { } } -// A refusal from the control plane is repeated verbatim. mesh-control refuses in paragraphs — +// A refusal from the control plane is repeated verbatim. mesh-controller refuses in paragraphs — // "nothing provides route, wanted by registry" — and an installer that reported "exit status 1" // would throw away the only thing a person can act on. func TestWhatTheMeshRefusedIsRepeated(t *testing.T) { @@ -202,7 +202,7 @@ func TestWhatTheMeshRefusedIsRepeated(t *testing.T) { _, err := InstallRegistry(context.Background(), installing(t, catalogueWith(t, RegistryModule, upstreamRegistryManifest)), - Deps{Run: runtime.run}, controlPlane{container: "temp-mesh-control", run: runtime.run, + Deps{Run: runtime.run}, controlPlane{container: "temp-mesh-controller", run: runtime.run, timeout: time.Second}, func(string) {}) if err == nil { t.Fatal("a push the mesh refused was reported as successful") diff --git a/internal/bootstrap/retire.go b/internal/bootstrap/retire.go index a84055d..2ed13f8 100644 --- a/internal/bootstrap/retire.go +++ b/internal/bootstrap/retire.go @@ -32,8 +32,8 @@ type Retired struct { // bundle is applied again, and the removal pass does what it does for every other resource that // leaves a declaration. // -// That is the whole of why the rename at step 3 mattered. Had the substrate and the module both -// called their container `mesh-control`, this apply would have removed the module's container — +// That is the whole of why the rename at step 3 mattered. Had the foundation and the module both +// called their container `mesh-controller`, this apply would have removed the module's container — // the host would have been asked to take away something it believed it owned, and it would have // been right. Two names, two owners, and the removal is unambiguous. // @@ -63,7 +63,7 @@ func RetireTheTemporaryControlPlane(ctx context.Context, o Options, sys system.S // Textual, for the reason the rewrite at step 3 is textual: the produced bundle is meant to be // READ, and a person coming to a machine after a pivot should be able to open the file the - // installer applied and see the substrate they recognise with the control plane gone from it. + // installer applied and see the foundation they recognise with the control plane gone from it. // Re-serialising a parsed declaration would drop every comment in it. bundle, err := removeResource(produced, ControlPlaneID) if err != nil { @@ -124,7 +124,7 @@ func RetireTheTemporaryControlPlane(ctx context.Context, o Options, sys system.S // removeResource takes one resource out of a bundle's text, comments and all. // // It walks the `resources` array counting braces, skipping over strings and comments so that a -// `//` inside a connection string is not read as the start of one — the substrate's own bundle +// `//` inside a connection string is not read as the start of one — the foundation's own bundle // contains `postgres://…` several times, and a scanner that did not know the difference would // treat the rest of the line as a comment and lose a brace. // diff --git a/internal/bootstrap/retire_test.go b/internal/bootstrap/retire_test.go index efdec44..2a86992 100644 --- a/internal/bootstrap/retire_test.go +++ b/internal/bootstrap/retire_test.go @@ -46,7 +46,7 @@ func TestTheTemporaryControlPlaneLeavesTheBundleAndNothingElseDoes(t *testing.T) t.Error("the bundle still declares a control plane") } // The store and the broker are still exactly what they were. A retirement that took the - // substrate with it would leave the machine with a module and nothing under it. + // foundation with it would leave the machine with a module and nothing under it. for id, name := range containerNames(before) { if id == ControlPlaneID { continue @@ -57,7 +57,7 @@ func TestTheTemporaryControlPlaneLeavesTheBundleAndNothingElseDoes(t *testing.T) } } -// **A `//` inside a string is not a comment.** The substrate's own bundle carries +// **A `//` inside a string is not a comment.** The foundation's own bundle carries // `postgres://…` several times, and a scanner that read the rest of those lines as a comment // would lose braces and cut the wrong thing out — silently, because what it produced would still // look like a file. @@ -144,7 +144,7 @@ func TestAContainerStillThereAfterRemovalIsNotGone(t *testing.T) { stillThere := &asked{answer: func(_ string, _ []string) (string, error) { return "true running\n", nil }} - gone, err := isGone(context.Background(), stillThere.run, time.Second, 0, "temp-mesh-control") + gone, err := isGone(context.Background(), stillThere.run, time.Second, 0, "temp-mesh-controller") if err != nil { t.Fatal(err) } @@ -153,9 +153,9 @@ func TestAContainerStillThereAfterRemovalIsNotGone(t *testing.T) { } removed := &asked{answer: func(_ string, _ []string) (string, error) { - return "", errors.New("No such object: temp-mesh-control") + return "", errors.New("No such object: temp-mesh-controller") }} - gone, err = isGone(context.Background(), removed.run, time.Second, 0, "temp-mesh-control") + gone, err = isGone(context.Background(), removed.run, time.Second, 0, "temp-mesh-controller") if err != nil { t.Fatal(err) } diff --git a/internal/bootstrap/rewrite.go b/internal/bootstrap/rewrite.go index dc040e4..45eb470 100644 --- a/internal/bootstrap/rewrite.go +++ b/internal/bootstrap/rewrite.go @@ -19,27 +19,27 @@ import ( // broker and no mesh. const ControlPlaneID = "control-plane" -// TempPrefix is what the substrate's control plane is renamed with. +// TempPrefix is what the foundation's control plane is renamed with. // -// **This is the whole of how a carried resource becomes a declared one.** The substrate raises a +// **This is the whole of how a carried resource becomes a declared one.** The foundation raises a // control plane and a module later declares one, and for a moment both exist — which looked like a // handover problem needing a way for the host to stop owning something without destroying it. It is -// not one. The temporary control plane is called `temp-mesh-control` and the permanent one is -// called `mesh-control`: two containers, two owners, nothing shared and nothing to hand over. At +// not one. The temporary control plane is called `temp-mesh-controller` and the permanent one is +// called `mesh-controller`: two containers, two owners, nothing shared and nothing to hand over. At // the end the temporary one is dropped from the bundle and the host removes it, which is exactly // what should happen to something named "temp" (novox/hq ADR 0067). // -// The name is also the audit. After the pivot, a machine running `mesh-control` and not -// `temp-mesh-control` has completed it; one running both stopped in the middle; one running only +// The name is also the audit. After the pivot, a machine running `mesh-controller` and not +// `temp-mesh-controller` has completed it; one running both stopped in the middle; one running only // the temp has not started. That is readable from `docker ps` by somebody who knows nothing else. const TempPrefix = "temp-" // ControlPlaneModule is the module the permanent control plane is installed as, and the name its -// container takes — the name the substrate's own control plane gives up here so that it can. +// container takes — the name the foundation's own control plane gives up here so that it can. // // Declared beside the rename rather than beside the step that uses it, because this is where the // two names are decided together and where the reason for both of them is written down. -const ControlPlaneModule = "mesh-control" +const ControlPlaneModule = "mesh-controller" // brokerAddressVar is what a token tells an enrolling node to dial. // @@ -85,11 +85,11 @@ type Rewritten struct { // Rewrite produces the bundle this machine will apply from the template it was given. // // **Two substitutions, and both are textual.** The control plane's image becomes the id of the image -// this machine now holds, and its container is renamed `temp-mesh-control`; nothing else changes. +// this machine now holds, and its container is renamed `temp-mesh-controller`; nothing else changes. // The rename is what makes the pivot expressible at all — see TempPrefix. Textual rather than // parse-and-re-serialise because // the produced file has to be *read* — a person getting a machine working must be able to open it, -// see the substrate they recognise, and see exactly one thing different. Re-serialising a parsed +// see the foundation they recognise, and see exactly one thing different. Re-serialising a parsed // declaration would drop every comment in the template, and those comments are where the reasons // live. // @@ -182,7 +182,7 @@ func Rewrite(template []byte, imageID string) (Rewritten, error) { if produced.Name != out.TempName { return Rewritten{}, fmt.Errorf( "the produced bundle still calls the control plane's container %q, not %q. The "+ - "permanent one is a module and takes the plain name, so a substrate that kept it "+ + "permanent one is a module and takes the plain name, so a foundation that kept it "+ "would put two owners on one container", produced.Name, out.TempName) } @@ -209,7 +209,7 @@ func Rewrite(template []byte, imageID string) (Rewritten, error) { return Rewritten{}, fmt.Errorf( "renaming the control plane's container also renamed %q, from %q to %q. Only the "+ "control plane moves out of the way; every other container keeps the name the "+ - "substrate gave it", id, wasName[id], name) + "foundation gave it", id, wasName[id], name) } sortStrings(out.Kept) return out, nil @@ -217,15 +217,15 @@ func Rewrite(template []byte, imageID string) (Rewritten, error) { // renameContainer changes one container's name in the bundle's text. // -// **The quoted name, not the bare word.** `mesh-control` also appears inside the image reference -// the template carries (`…/mesh-control@sha256:…`) and could appear inside a command line; a bare +// **The quoted name, not the bare word.** `mesh-controller` also appears inside the image reference +// the template carries (`…/mesh-controller@sha256:…`) and could appear inside a command line; a bare // substitution would catch those too. What is wanted is a JSON string that IS the name, so the -// quotes are part of what is matched — `"mesh-control"` matches the container's `name` and an +// quotes are part of what is matched — `"mesh-controller"` matches the container's `name` and an // action's `in`, which are exactly the places the name means the container, and nothing else. // // It refuses when the text does not contain what the parse says is there, for the same reason the // image substitution does: the two would then be reading different things, and a rename that -// replaced nothing and reported success would leave the module and the substrate fighting over one +// replaced nothing and reported success would leave the module and the foundation fighting over one // container three steps later. func renameContainer(bundle []byte, from, to string) ([]byte, error) { if from == to { @@ -235,7 +235,7 @@ func renameContainer(bundle []byte, from, to string) ([]byte, error) { if bytes.Count(bundle, quoted) == 0 { return nil, fmt.Errorf( "the control plane's container is called %q according to the parsed template, and %s "+ - "is not in the file. Nothing was renamed, and the substrate would raise a "+ + "is not in the file. Nothing was renamed, and the foundation would raise a "+ "container the module also wants", from, quoted) } return bytes.ReplaceAll(bundle, quoted, []byte(`"`+to+`"`)), nil @@ -257,7 +257,7 @@ func controlPlaneIn(d *declaration.Declaration) (*declaration.Container, error) } return nil, fmt.Errorf( "this bundle names no %q, so there is no control plane to give this machine's image to. "+ - "A substrate without one raises a store and a broker and no mesh. It declares: %s", + "A foundation without one raises a store and a broker and no mesh. It declares: %s", ControlPlaneID, strings.Join(identities(d), ", ")) } @@ -316,7 +316,7 @@ func sortStrings(values []string) { // writeBundleFile puts the produced bundle where a person can read it, creating the directory it // lives in. // -// 0644, and that is deliberate: this file names an image and describes a substrate, and it holds +// 0644, and that is deliberate: this file names an image and describes a foundation, and it holds // the bootstrap credentials the template happens to carry — which are the same ones anybody can // read in the template itself. It is meant to be read. What must not be world-readable is the // node's identity, and that lives elsewhere and is written elsewhere (`internal/identity`). diff --git a/internal/bootstrap/rewrite_test.go b/internal/bootstrap/rewrite_test.go index 7902f1c..0ad169c 100644 --- a/internal/bootstrap/rewrite_test.go +++ b/internal/bootstrap/rewrite_test.go @@ -15,16 +15,16 @@ const ( otherHeld = "sha256:2222222222222222222222222222222222222222222222222222222222222222" ) -// theRealBundle is this repository's own substrate example, used rather than a fixture. +// theRealBundle is this repository's own foundation example, used rather than a fixture. // // A fixture would agree with whatever this code does. The example is what an installer is actually -// pointed at, it names the control plane twice, and it is the file that changes when the substrate +// pointed at, it names the control plane twice, and it is the file that changes when the foundation // changes — so a rewrite that stops working on it is a rewrite that has stopped working. func theRealBundle(t *testing.T) []byte { t.Helper() - raw, err := os.ReadFile("../../examples/substrate-first-node.lock") + raw, err := os.ReadFile("../../examples/foundation-first-node.lock") if err != nil { - t.Fatalf("reading the substrate example: %v", err) + t.Fatalf("reading the foundation example: %v", err) } return raw } @@ -48,7 +48,7 @@ func TestTheControlPlaneIsNamedByTheImageThisMachineHolds(t *testing.T) { // **Every place the bundle names that image, not only the container.** // -// The substrate names the control plane's image twice: the container that runs `serve`, and the +// The foundation names the control plane's image twice: the container that runs `serve`, and the // action that runs `migrate` to create the contexts' schemas. Rewriting only the container leaves // the migration pointing at an image no registry serves, and the apply dies in the middle — after // the store is up and before the broker. This is the test that would have caught that. @@ -60,7 +60,7 @@ func TestEveryPlaceTheBundleNamesTheControlPlaneIsRewritten(t *testing.T) { t.Fatal(err) } if out.Places < 2 { - t.Fatalf("the control plane's image was found in %d place(s); the substrate names it in "+ + t.Fatalf("the control plane's image was found in %d place(s); the foundation names it in "+ "the container AND in the migration action", out.Places) } if remaining := strings.Count(string(out.Bundle), out.Was); remaining != 0 { @@ -86,7 +86,7 @@ func TestPostgresAndTheBrokerAreLeftExactlyAsTheyWere(t *testing.T) { for _, id := range []string{"store", "broker"} { image, named := produced[id] if !named { - t.Fatalf("the substrate example no longer declares a %q container", id) + t.Fatalf("the foundation example no longer declares a %q container", id) } // Compared against the template's own text rather than against an expectation written // here: what is being defended is "unchanged", and the template is the only thing that @@ -126,7 +126,7 @@ func TestABundleThatNamesNoControlPlaneIsRefused(t *testing.T) { func TestAControlPlaneThatIsNotAContainerIsRefused(t *testing.T) { template := []byte(`{"declaration":1,"resources":[ - {"id":"control-plane","type":"package","package":"mesh-control"} + {"id":"control-plane","type":"package","package":"mesh-controller"} ]}`) if _, err := Rewrite(template, held); err == nil { t.Fatal("a control plane declared as a package was accepted, and a package has no image") @@ -175,7 +175,7 @@ func TestANewImageReplacesAnOlderHeldOne(t *testing.T) { } // The produced bundle is meant to be READ. Re-serialising a parsed declaration would drop every -// comment in the template, and the substrate example is mostly comments — each one recording why a +// comment in the template, and the foundation example is mostly comments — each one recording why a // resource is the way it is, several of them paid for in the lab. func TestTheProducedBundleKeepsTheTemplatesComments(t *testing.T) { template := theRealBundle(t) @@ -194,11 +194,11 @@ func TestTheProducedBundleKeepsTheTemplatesComments(t *testing.T) { func TestSomethingThatIsNotAnImageIdIsRefused(t *testing.T) { for _, bad := range []string{ "", - "mesh-control:latest", + "mesh-controller:latest", "sha256:abc", "sha256:" + strings.Repeat("1", 63), "sha256:" + strings.Repeat("g", 64), - "mesh-control@sha256:" + strings.Repeat("1", 64), + "mesh-controller@sha256:" + strings.Repeat("1", 64), } { if _, err := Rewrite(theRealBundle(t), bad); err == nil { t.Errorf("image id %q was accepted", bad) @@ -216,7 +216,7 @@ func TestTheAddressNodesWillDialIsReportedAndNotRewritten(t *testing.T) { t.Fatal(err) } if out.BrokerAddress == "" { - t.Fatal("the substrate example no longer says what address enrolling nodes will dial") + t.Fatal("the foundation example no longer says what address enrolling nodes will dial") } if !strings.Contains(string(out.Bundle), out.BrokerAddress) { t.Errorf("the produced bundle no longer carries %q — it was rewritten, and nothing here "+ @@ -228,21 +228,21 @@ func TestTheAddressNodesWillDialIsReportedAndNotRewritten(t *testing.T) { // The rename, which is what makes genesis a pivot rather than a handover (novox/hq ADR 0067). // --------------------------------------------------------------------------------------------- -// **This is the test that dissolves the blocker.** The substrate raises a control plane and a -// module later declares one; if both are called `mesh-control` then for one moment two owners hold +// **This is the test that dissolves the blocker.** The foundation raises a control plane and a +// module later declares one; if both are called `mesh-controller` then for one moment two owners hold // one container, and the host — which tracks what it owns — has no way to stop owning something -// without destroying it. Nothing here invents such a mechanism. The substrate's container is -// called `temp-mesh-control` instead, and there are simply two containers. -func TestTheSubstratesControlPlaneMovesOutOfTheModulesWay(t *testing.T) { +// without destroying it. Nothing here invents such a mechanism. The foundation's container is +// called `temp-mesh-controller` instead, and there are simply two containers. +func TestTheFoundationsControlPlaneMovesOutOfTheModulesWay(t *testing.T) { out, err := Rewrite(theRealBundle(t), held) if err != nil { t.Fatal(err) } if !out.Renamed { - t.Error("the rewrite reported nothing renamed, and the template named it mesh-control") + t.Error("the rewrite reported nothing renamed, and the template named it mesh-controller") } - if out.TempName != "temp-mesh-control" { - t.Errorf("the substrate's control plane is called %q", out.TempName) + if out.TempName != "temp-mesh-controller" { + t.Errorf("the foundation's control plane is called %q", out.TempName) } control, err := controlPlaneIn(out.Declaration) if err != nil { @@ -260,22 +260,22 @@ func TestTheSubstratesControlPlaneMovesOutOfTheModulesWay(t *testing.T) { } } -// The image reference contains the string `mesh-control` too, and it is not a container name. A -// substitution that caught it would produce `…/temp-mesh-control@sha256:…`, which no registry +// The image reference contains the string `mesh-controller` too, and it is not a container name. A +// substitution that caught it would produce `…/temp-mesh-controller@sha256:…`, which no registry // serves — and it would be found inside a pull rather than here. func TestTheImageReferenceIsNotMistakenForTheContainerName(t *testing.T) { out, err := Rewrite(theRealBundle(t), held) if err != nil { t.Fatal(err) } - if strings.Contains(string(out.Bundle), TempPrefix+"mesh-control@") || - strings.Contains(string(out.Bundle), "/"+TempPrefix+"mesh-control") { + if strings.Contains(string(out.Bundle), TempPrefix+"mesh-controller@") || + strings.Contains(string(out.Bundle), "/"+TempPrefix+"mesh-controller") { t.Error("the rename reached inside an image reference") } } -// Everything else keeps the name the substrate gave it. The store and the broker are containers -// too, and a rename that moved them would leave a machine whose substrate the host cannot find. +// Everything else keeps the name the foundation gave it. The store and the broker are containers +// too, and a rename that moved them would leave a machine whose foundation the host cannot find. func TestRenamingTheControlPlaneLeavesEveryOtherContainerAlone(t *testing.T) { before, err := declaration.ParseFileTrusted(theRealBundle(t)) if err != nil { @@ -309,7 +309,7 @@ func TestRewritingABundleThisAlreadyProducedRenamesNothing(t *testing.T) { t.Fatal(err) } if second.Renamed { - t.Error("a bundle already naming temp-mesh-control was renamed again") + t.Error("a bundle already naming temp-mesh-controller was renamed again") } if second.TempName != first.TempName { t.Errorf("the second pass calls it %q and the first called it %q", diff --git a/internal/bootstrap/talk.go b/internal/bootstrap/talk.go index eab5a14..3b5b029 100644 --- a/internal/bootstrap/talk.go +++ b/internal/bootstrap/talk.go @@ -13,13 +13,13 @@ import ( // // **Through `docker exec`, not over a network.** The control plane listens on nothing — `serve` is // a broker consumer, and every administrative verb is a subcommand of the same binary that opens -// the stores directly (mesh-control's own usage). So the way to tell a mesh anything, from the +// the stores directly (mesh-controller's own usage). So the way to tell a mesh anything, from the // machine the mesh is on, is to run its binary inside its own container. That is also what the lab // does, and having the installer and the lab drive the mesh identically is the point: the lab is // meant to exercise the installer, not a second procedure that resembles it. // // It carries which container, because the whole pivot turns on there being two of them: the -// substrate's `temp-mesh-control` for steps 6 to 9, and the module's `mesh-control` afterwards. +// foundation's `temp-mesh-controller` for steps 6 to 9, and the module's `mesh-controller` afterwards. type controlPlane struct { container string run Runner @@ -35,9 +35,9 @@ func (c controlPlane) within(timeout time.Duration) controlPlane { return c } -// tell runs a mesh-control subcommand and gives back what it said. +// tell runs a mesh-controller subcommand and gives back what it said. // -// The failure carries the command AND the output. A mesh-control refusal is a paragraph explaining +// The failure carries the command AND the output. A mesh-controller refusal is a paragraph explaining // what is wrong — "nothing provides route, wanted by registry" — and an installer that reported // only "exit status 1" would throw away the one thing a person needs. func (c controlPlane) tell(ctx context.Context, args ...string) (string, error) { @@ -83,7 +83,7 @@ func (c controlPlane) carry(ctx context.Context, local, remote string) error { // crash-looped on material it never received. There is no shell in the image to chown it with. // // What goes through here is a module manifest and a store connection string. The connection is the -// same value the produced bundle already holds in the clear — a substrate names its own bootstrap +// same value the produced bundle already holds in the clear — a foundation names its own bootstrap // credentials, and at genesis there is nowhere else for them to be — so this widens nothing. The // file on the machine is removed at once, and the copy inside the container goes when the // container does, which for the temporary control plane is step 10. @@ -107,7 +107,7 @@ func indent(s string) string { // // Line-and-word rather than a substring search, because these listings are columns and a // substring match would find `registry` inside `registry-mirror` and report a module installed -// that is not. Every one of mesh-control's `list` verbs prints the name first on the line. +// that is not. Every one of mesh-controller's `list` verbs prints the name first on the line. func mentions(listing, name string) bool { for _, line := range strings.Split(listing, "\n") { first, _, _ := strings.Cut(strings.TrimSpace(line), " ") diff --git a/internal/bootstrap/verify.go b/internal/bootstrap/verify.go index 4bed6fe..6285257 100644 --- a/internal/bootstrap/verify.go +++ b/internal/bootstrap/verify.go @@ -13,14 +13,14 @@ import ( // // A path rather than a shell command, because the image is `FROM scratch` and holds one static // binary and nothing else — no shell to invoke, nothing to interpret a command line -// (mesh-control's Dockerfile, novox/hq ADR 0006). That is a property of the image this installer +// (mesh-controller's Dockerfile, novox/hq ADR 0006). That is a property of the image this installer // carries, which is why the path can be written down here. -const controlPlaneBinary = "/mesh-control" +const controlPlaneBinary = "/mesh-controller" // answerEvery is how often the control plane is asked again while it is starting. var answerEvery = 2 * time.Second -// Verified is what the substrate was found to be. +// Verified is what the foundation was found to be. type Verified struct { // Running is every long-running container the bundle declares, confirmed up. Running []string @@ -29,7 +29,7 @@ type Verified struct { Answered string } -// Verify proves the substrate is up and the control plane replies. +// Verify proves the foundation is up and the control plane replies. // // **A container that is up is not a control plane that replies**, and this project has paid for // that distinction more than once: a runtime reports a container running from the moment the @@ -146,7 +146,7 @@ func containerRunning(ctx context.Context, run Runner, probe time.Duration, name // // A run-once step has exited by design and a scheduled step has deliberately never been started // (novox/hq ADR 0052, ADR 0053), so asking either of them to be running would be asking the -// substrate to be something other than what it declared. +// foundation to be something other than what it declared. func longRunning(d *declaration.Declaration) []string { var names []string for _, r := range d.Resources { diff --git a/internal/bootstrap/verify_test.go b/internal/bootstrap/verify_test.go index 3711d7d..c8decc6 100644 --- a/internal/bootstrap/verify_test.go +++ b/internal/bootstrap/verify_test.go @@ -11,7 +11,7 @@ import ( "github.com/novox/mesh-host/internal/declaration" ) -func substrate(t *testing.T) *declaration.Declaration { +func foundation(t *testing.T) *declaration.Declaration { t.Helper() out, err := Rewrite(theRealBundle(t), held) if err != nil { @@ -39,12 +39,12 @@ func TestAContainerThatIsUpIsNotAControlPlaneThatReplies(t *testing.T) { return "", fmt.Errorf("unexpected command: %v", args) }} - _, err := Verify(context.Background(), substrate(t), runtime.run, + _, err := Verify(context.Background(), foundation(t), runtime.run, time.Second, 0, func(string) {}) if err == nil { - t.Fatal("every container was running, nothing answered, and the substrate was reported up") + t.Fatal("every container was running, nothing answered, and the foundation was reported up") } - for _, wanted := range []string{"mesh-control", "Running is not replying", "docker logs"} { + for _, wanted := range []string{"mesh-controller", "Running is not replying", "docker logs"} { if !strings.Contains(err.Error(), wanted) { t.Errorf("the failure does not mention %q:\n%v", wanted, err) } @@ -65,14 +65,14 @@ func TestAControlPlaneThatSaysNothingHasNotAnswered(t *testing.T) { return " \n", nil }} - if _, err := Verify(context.Background(), substrate(t), runtime.run, + if _, err := Verify(context.Background(), foundation(t), runtime.run, time.Second, 0, func(string) {}); err == nil { t.Fatal("a control plane that exited zero without saying anything was accepted") } } -// The substrate answering is the whole point, and what it said is reported rather than asserted. -func TestASubstrateThatIsUpAndAnsweringIsAccepted(t *testing.T) { +// The foundation answering is the whole point, and what it said is reported rather than asserted. +func TestAFoundationThatIsUpAndAnsweringIsAccepted(t *testing.T) { runtime := &asked{answer: func(_ string, args []string) (string, error) { if args[0] == "inspect" { return "true running\n", nil @@ -80,16 +80,16 @@ func TestASubstrateThatIsUpAndAnsweringIsAccepted(t *testing.T) { return "1 node, 0 waiting\n", nil }} - verified, err := Verify(context.Background(), substrate(t), runtime.run, + verified, err := Verify(context.Background(), foundation(t), runtime.run, time.Second, 0, func(string) {}) if err != nil { t.Fatal(err) } // Three long-running containers: the store, the broker and the TEMPORARY control plane. The // run-once and scheduled shapes are excluded on purpose — a step that has exited is not a - // fault. The name is `temp-mesh-control` because the permanent one is a module and takes the + // fault. The name is `temp-mesh-controller` because the permanent one is a module and takes the // plain name (novox/hq ADR 0067), which is what makes the two of them coexist at all. - want := []string{"mesh-store", "mesh-broker", "temp-mesh-control"} + want := []string{"mesh-store", "mesh-broker", "temp-mesh-controller"} if len(verified.Running) != len(want) { t.Fatalf("confirmed %v running, want %v", verified.Running, want) } @@ -122,7 +122,7 @@ func TestAControlPlaneThatIsStillStartingIsWaitedFor(t *testing.T) { return "1 node\n", nil }} - if _, err := Verify(context.Background(), substrate(t), runtime.run, + if _, err := Verify(context.Background(), foundation(t), runtime.run, time.Second, time.Second, func(string) {}); err != nil { t.Fatalf("a control plane that answered on the third ask was refused: %v", err) } @@ -141,10 +141,10 @@ func TestAContainerThatExitedIsNamedWithItsState(t *testing.T) { return "", fmt.Errorf("unexpected command: %v", args) }} - _, err := Verify(context.Background(), substrate(t), runtime.run, + _, err := Verify(context.Background(), foundation(t), runtime.run, time.Second, 0, func(string) {}) if err == nil { - t.Fatal("a container that had exited was reported as part of a running substrate") + t.Fatal("a container that had exited was reported as part of a running foundation") } if !strings.Contains(err.Error(), "mesh-broker") || !strings.Contains(err.Error(), "exited") { t.Errorf("the failure does not say which container is in what state: %v", err) @@ -160,11 +160,11 @@ func TestTheControlPlaneIsAskedByRunningItsOwnBinary(t *testing.T) { } return "1 node\n", nil }} - if _, err := Verify(context.Background(), substrate(t), runtime.run, + if _, err := Verify(context.Background(), foundation(t), runtime.run, time.Second, 0, func(string) {}); err != nil { t.Fatal(err) } - if !runtime.ran("docker exec " + TempPrefix + "mesh-control " + controlPlaneBinary + " status") { + if !runtime.ran("docker exec " + TempPrefix + "mesh-controller " + controlPlaneBinary + " status") { t.Errorf("the control plane was never asked anything: %v", runtime.commands) } } diff --git a/internal/bundle/bundle.go b/internal/bundle/bundle.go index f9f3c6b..329f10f 100644 --- a/internal/bundle/bundle.go +++ b/internal/bundle/bundle.go @@ -27,13 +27,13 @@ import ( // nothing and reporting success — a host that silently did nothing on a first node would look // exactly like one that worked. // -//go:embed substrate-arch.lock +//go:embed foundation-arch.lock var archLock []byte -//go:embed substrate-alpine.lock +//go:embed foundation-alpine.lock var alpineLock []byte -//go:embed substrate-android.lock +//go:embed foundation-android.lock var androidLock []byte var locks = map[string][]byte{ @@ -52,7 +52,7 @@ func Raw(system string) []byte { return locks[system] } // IsEmpty reports whether anything was built in. A bundle of only comments and whitespace is // empty for this purpose: a placeholder is a comment, and treating it as content would mean a -// host claims to carry a substrate it does not. +// host claims to carry a foundation it does not. func IsEmpty(system string) bool { for _, line := range strings.Split(string(locks[system]), "\n") { line = strings.TrimSpace(line) diff --git a/internal/bundle/bundle_test.go b/internal/bundle/bundle_test.go index b7fb1ea..232971d 100644 --- a/internal/bundle/bundle_test.go +++ b/internal/bundle/bundle_test.go @@ -13,7 +13,7 @@ func TestADefaultBuildCarriesNothingAndSaysSo(t *testing.T) { // success would look exactly like a host that raised a first node — and the difference // would surface as a mesh that never came up, with nothing to point at. if !IsEmpty("arch") { - t.Fatal("the default build claims to carry a substrate") + t.Fatal("the default build claims to carry a foundation") } _, err := Load("arch") if !errors.Is(err, ErrEmpty) { diff --git a/internal/bundle/substrate-alpine.lock b/internal/bundle/foundation-alpine.lock similarity index 87% rename from internal/bundle/substrate-alpine.lock rename to internal/bundle/foundation-alpine.lock index 1d0f5cc..4630a96 100644 --- a/internal/bundle/substrate-alpine.lock +++ b/internal/bundle/foundation-alpine.lock @@ -1,4 +1,4 @@ -// substrate-alpine.lock — the pinned tier-1 descriptor the ALPINE host carries. +// foundation-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 0005). diff --git a/internal/bundle/substrate-android.lock b/internal/bundle/foundation-android.lock similarity index 66% rename from internal/bundle/substrate-android.lock rename to internal/bundle/foundation-android.lock index 6ee155f..20c3711 100644 --- a/internal/bundle/substrate-android.lock +++ b/internal/bundle/foundation-android.lock @@ -1,10 +1,10 @@ -// substrate-android.lock — deliberately not a bundle. +// foundation-android.lock — deliberately not a bundle. // // An android host cannot raise a mesh, and this file says so rather than being an empty // placeholder waiting to be filled in. // -// 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 +// The foundation is a container runtime, a store and the control plane (novox/hq +// 07-the-foundation.md). An android host implements `file`, `directory` and `action` and refuses // `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. // diff --git a/internal/bundle/substrate-arch.lock b/internal/bundle/foundation-arch.lock similarity index 88% rename from internal/bundle/substrate-arch.lock rename to internal/bundle/foundation-arch.lock index 21cc7be..762792e 100644 --- a/internal/bundle/substrate-arch.lock +++ b/internal/bundle/foundation-arch.lock @@ -1,4 +1,4 @@ -// substrate-arch.lock — the pinned tier-1 descriptor the ARCH host carries. +// foundation-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 0005). The mechanism is shared; what it names is not. diff --git a/internal/declaration/declaration.go b/internal/declaration/declaration.go index 909e540..d1ffc9c 100644 --- a/internal/declaration/declaration.go +++ b/internal/declaration/declaration.go @@ -1076,7 +1076,7 @@ func vocabulary() string { // ParseFileTrusted reads a declaration from a file somebody handed this host. // // The same as ParseTrusted, and it allows whole-line `//` comments first. A pinned, hand-authored -// artefact that nobody can annotate is one nobody can review — the substrate bundle is mostly +// artefact that nobody can annotate is one nobody can review — the foundation bundle is mostly // explanation of why each digest is what it is. // // **Only for a file, never for the link.** Over the link the format stays exactly JSON, because diff --git a/internal/declaration/declaration_test.go b/internal/declaration/declaration_test.go index 9a0c27a..b2fd73e 100644 --- a/internal/declaration/declaration_test.go +++ b/internal/declaration/declaration_test.go @@ -157,7 +157,7 @@ func TestAnEmptyDeclarationIsAMistake(t *testing.T) { refusalFor(t, `{"declaration":1,"resources":[]}`) } -// --- the vocabulary the substrate bootstrap needs (novox/hq 07-the-substrate.md) --- +// --- the vocabulary the foundation bootstrap needs (novox/hq 07-the-foundation.md) --- func TestAnActionOverTheLinkIsRefused(t *testing.T) { // novox/hq ADR 0005. The link may push declarations of known shape and never a command to @@ -229,7 +229,7 @@ func TestAnImageMustBePinnedByDigest(t *testing.T) { func TestAnImageTheMachineHoldsIsNamedByItsOwnDigest(t *testing.T) { held := "sha256:" + strings.Repeat("b", 64) if _, err := ParseTrusted([]byte(`{"declaration":1,"resources":[ - {"id":"control","type":"container","name":"mesh-control","image":"` + held + `"} + {"id":"control","type":"container","name":"mesh-controller","image":"` + held + `"} ]}`)); err != nil { t.Errorf("an image named by its own digest was refused: %v", err) } @@ -238,7 +238,7 @@ func TestAnImageTheMachineHoldsIsNamedByItsOwnDigest(t *testing.T) { // whichever the runtime happened to match first. for _, bad := range []string{"sha256:abc", "sha256:", "sha256:" + strings.Repeat("b", 63)} { if _, err := ParseTrusted([]byte(`{"declaration":1,"resources":[ - {"id":"control","type":"container","name":"mesh-control","image":"` + bad + `"} + {"id":"control","type":"container","name":"mesh-controller","image":"` + bad + `"} ]}`)); err == nil { t.Errorf("image id %q was accepted and is not a digest", bad) } @@ -267,7 +267,7 @@ func TestAFieldTheNewTypesDoNotUseIsRefused(t *testing.T) { } func TestTheVocabularyIsTheElevenShapesTheMeshNeeds(t *testing.T) { - // Six of them the bootstrap uses (novox/hq 07-the-substrate.md), and removing one is a + // Six of them the bootstrap uses (novox/hq 07-the-foundation.md), and removing one is a // failing test rather than a discovery during a first-node install. // // Two were added on 2026-08-30 and the count is asserted precisely because adding one is a @@ -364,7 +364,7 @@ func TestSomethingInsideAValueIsNotAComment(t *testing.T) { // parses as the declaration and the rest is never looked at. The machine applies something, // reports success, and what it applied is not what the file says. // -// Not hypothetical: a test harness appended a line to the substrate bundle by accident, every +// Not hypothetical: a test harness appended a line to the foundation bundle by accident, every // apply kept working, and nothing said so for the entire time it was wrong. func TestSomethingAfterTheDeclarationIsRefused(t *testing.T) { good := `{"declaration":1,"resources":[{"id":"a","type":"file","path":"/tmp/a",` + diff --git a/internal/image/builder.tar b/internal/image/builder.tar index e26e4f9..d1d3e70 100644 --- a/internal/image/builder.tar +++ b/internal/image/builder.tar @@ -2,7 +2,7 @@ This is not a saved image. It is the placeholder that keeps this repository buil A release build replaces this file with the output of `docker save` and puts it back afterwards: - make bootstrap IMAGE=mesh-control: + make bootstrap IMAGE=mesh-controller: An installer built with this file present carries no control plane, and says so in preflight rather than getting a machine part-way to being a mesh and stopping. diff --git a/internal/image/image.go b/internal/image/image.go index 70d2386..ef068be 100644 --- a/internal/image/image.go +++ b/internal/image/image.go @@ -58,7 +58,7 @@ var saved []byte var ErrEmpty = errors.New( "this mesh-bootstrap carries no builder image, so it cannot raise a mesh. A release build " + "embeds one: `make bootstrap IMAGE=` in the mesh-host repository, where " + - "is a mesh-builder image already built from the mesh-control source") + "is a mesh-builder image already built from the mesh-controller source") // IsEmpty reports whether anything was built in. // diff --git a/internal/image/image_test.go b/internal/image/image_test.go index ef93e6a..ef4d0dd 100644 --- a/internal/image/image_test.go +++ b/internal/image/image_test.go @@ -72,11 +72,11 @@ func TestTheArchivesOwnIdIsReadFromTheSavedFile(t *testing.T) { // does not. func TestTheSavedTagsAreRead(t *testing.T) { saved := savedImage(t, map[string]string{ - "manifest.json": manifest(t, strings.Repeat("b", 64)+".json", "mesh-control:v1"), + "manifest.json": manifest(t, strings.Repeat("b", 64)+".json", "mesh-controller:v1"), }) got := Tags(saved) - if len(got) != 1 || got[0] != "mesh-control:v1" { - t.Errorf("tags = %v, want [mesh-control:v1]", got) + if len(got) != 1 || got[0] != "mesh-controller:v1" { + t.Errorf("tags = %v, want [mesh-controller:v1]", got) } } diff --git a/internal/link/messages.go b/internal/link/messages.go index becc2ef..67b63c5 100644 --- a/internal/link/messages.go +++ b/internal/link/messages.go @@ -60,7 +60,7 @@ type Report struct { // Carried are the machine's ports held by what this host raised from its own bundle. // // **So the mesh can assign around what it did not put here** (novox/hq ADR 0038). A node - // raises its substrate before any mesh exists, so the control plane has never heard of the + // raises its foundation before any mesh exists, so the control plane has never heard of the // store or the broker — and would hand a module a port one of them holds, discovering it only // when a container runtime refused to start. // diff --git a/internal/store/store.go b/internal/store/store.go index e0669d7..d9c097f 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -35,7 +35,7 @@ type Applied struct { Type string `json:"type"` // Origin is who asked for this: the bundle this host carries, or the mesh. // - // Recorded because the two must not remove each other. A node raises its own substrate from + // Recorded because the two must not remove each other. A node raises its own foundation from // the bundle before any mesh exists, then enrols and is sent declarations — and a // declaration naming two resources would otherwise remove the store, the broker and the // control plane, which is 04-ISSUES/010 and happened on the first end-to-end run. @@ -47,7 +47,7 @@ type Applied struct { // Holds are the machine's own ports this resource occupies. // // **So the mesh can assign around what it did not put here** (novox/hq ADR 0038). A node - // raises its substrate from the bundle before any mesh exists, so the control plane has never + // raises its foundation from the bundle before any mesh exists, so the control plane has never // heard of the store, the broker or the control plane's own container — and a module assigned // afterwards would be given a port one of them already holds, and would be told so by a // container runtime rather than by anything that could have prevented it. @@ -226,7 +226,7 @@ const ( // // State written before origins existed was all bundle-applied: a host had no other way to be // told anything. Guessing wrong in the other direction would have a first upgrade remove the -// substrate, which is the fault this field exists to prevent. +// foundation, which is the fault this field exists to prevent. func originOf(r Applied) string { if r.Origin == "" { return OriginCarried diff --git a/internal/store/store_test.go b/internal/store/store_test.go index 3867be9..712c3bd 100644 --- a/internal/store/store_test.go +++ b/internal/store/store_test.go @@ -136,7 +136,7 @@ func TestNothingIsAnOrphanWhenEverythingIsDeclared(t *testing.T) { } func TestADeclarationDoesNotOrphanWhatTheBundleRaised(t *testing.T) { - // 04-ISSUES/010. A first node raises its substrate from the bundle it carries, then enrols + // 04-ISSUES/010. A first node raises its foundation from the bundle it carries, then enrols // and is sent a declaration naming two resources. Before origins, that removed the store, the // broker and the control plane that had sent it — the mesh deleting itself over the link the // message arrived on, in under a second, on the first end-to-end run. @@ -175,7 +175,7 @@ func TestTheBundleDoesNotOrphanWhatTheMeshDeclared(t *testing.T) { func TestStateWrittenBeforeOriginsExistedIsTreatedAsCarried(t *testing.T) { // Every resource a host had applied before this field existed came from its bundle, because // there was no other way to tell it anything. Guessing the other way would have the first - // declaration remove the substrate — which is the fault this exists to prevent, arriving + // declaration remove the foundation — which is the fault this exists to prevent, arriving // through the upgrade that fixes it. s := State{Resources: []Applied{{ID: "store", Type: "container", Target: "mesh-store"}}} diff --git a/internal/system/android.go b/internal/system/android.go index b74e10b..f46396b 100644 --- a/internal/system/android.go +++ b/internal/system/android.go @@ -35,7 +35,7 @@ import ( // while disconnected, reconcile already happens on start, and the mesh already reports *last // heard from* rather than alarming on silence. // -// It also **cannot be the first node** — every step of raising a substrate is a shape it +// It also **cannot be the first node** — every step of raising a foundation is a shape it // refuses — and its bundle says so rather than being an empty placeholder. type android struct{}