Files
mesh-host/internal/bootstrap/rewrite_test.go
jschoubben 121367319d Rename mesh-control -> mesh-controller, substrate -> foundation
One name per thing, per the HQ glossary: the module/container/image/binary/repo
becomes mesh-controller, the seat the-controller, and the store+broker pair the
foundation (embedded base bundles, default template and example lock renamed with
their go:embed directives). No behaviour change — a pure vocabulary rename.

Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
2026-09-16 18:40:40 +02:00

322 lines
12 KiB
Go

package bootstrap
import (
"os"
"strings"
"testing"
"github.com/novox/mesh-host/internal/declaration"
)
// Each test names the decision it defends (novox/hq ADR 0017).
const (
held = "sha256:1111111111111111111111111111111111111111111111111111111111111111"
otherHeld = "sha256:2222222222222222222222222222222222222222222222222222222222222222"
)
// 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 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/foundation-first-node.lock")
if err != nil {
t.Fatalf("reading the foundation example: %v", err)
}
return raw
}
func TestTheControlPlaneIsNamedByTheImageThisMachineHolds(t *testing.T) {
out, err := Rewrite(theRealBundle(t), held)
if err != nil {
t.Fatal(err)
}
if !out.Changed {
t.Error("the rewrite reported nothing changed, and the template named a registry image")
}
control, err := controlPlaneIn(out.Declaration)
if err != nil {
t.Fatal(err)
}
if control.Image != held {
t.Errorf("the control plane is %q, want %q", control.Image, held)
}
}
// **Every place the bundle names that image, not only the container.**
//
// 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.
func TestEveryPlaceTheBundleNamesTheControlPlaneIsRewritten(t *testing.T) {
template := theRealBundle(t)
out, err := Rewrite(template, held)
if err != nil {
t.Fatal(err)
}
if out.Places < 2 {
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 {
t.Errorf("the produced bundle still names %q in %d place(s)", out.Was, remaining)
}
if got := strings.Count(string(out.Bundle), held); got != out.Places {
t.Errorf("the produced bundle names the held image %d time(s), and %d were replaced",
got, out.Places)
}
}
// Third-party images are somebody else's, at somebody else's registry, and the installer has no
// business touching them (novox/hq ADR 0006).
func TestPostgresAndTheBrokerAreLeftExactlyAsTheyWere(t *testing.T) {
template := theRealBundle(t)
out, err := Rewrite(template, held)
if err != nil {
t.Fatal(err)
}
produced := containerImages(out.Declaration)
for _, id := range []string{"store", "broker"} {
image, named := produced[id]
if !named {
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
// knows what it said.
if !strings.Contains(string(template), `"image": "`+image+`"`) {
t.Errorf("%s is now %q, which the template does not say", id, image)
}
if strings.HasPrefix(image, "sha256:") {
t.Errorf("%s was rewritten to an image this machine holds, and nothing holds it", id)
}
}
// And the claim in the report is the same claim, so a person reading it is reading evidence.
if len(out.Kept) != 2 {
t.Errorf("the rewrite reports %d untouched image(s): %v", len(out.Kept), out.Kept)
}
}
// A bundle with no control plane raises a store and a broker and no mesh. Refused, because
// applying it would succeed and leave a machine that looks bootstrapped.
func TestABundleThatNamesNoControlPlaneIsRefused(t *testing.T) {
template := []byte(`{"declaration":1,"resources":[
{"id":"store","type":"container","name":"mesh-store","image":"postgres@sha256:` +
strings.Repeat("7", 64) + `"}
]}`)
_, err := Rewrite(template, held)
if err == nil {
t.Fatal("a bundle with no control plane was rewritten and would have been applied")
}
// The refusal has to be actionable: it says what the bundle DID declare, so somebody can see
// they pointed it at the wrong file or misspelled the id.
if !strings.Contains(err.Error(), ControlPlaneID) || !strings.Contains(err.Error(), "store") {
t.Errorf("the refusal names neither what was wanted nor what was there: %v", err)
}
}
func TestAControlPlaneThatIsNotAContainerIsRefused(t *testing.T) {
template := []byte(`{"declaration":1,"resources":[
{"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")
}
}
// Idempotence. This program is run over and over while somebody gets a machine working, and the
// second run must be able to say the bundle already names this image rather than reporting a
// rewrite it did not perform.
func TestRewritingABundleThatAlreadyNamesTheImageChangesNothing(t *testing.T) {
first, err := Rewrite(theRealBundle(t), held)
if err != nil {
t.Fatal(err)
}
second, err := Rewrite(first.Bundle, held)
if err != nil {
t.Fatal(err)
}
if second.Changed {
t.Error("re-running the rewrite reported a change, and the image was already the one held")
}
if string(second.Bundle) != string(first.Bundle) {
t.Error("re-running the rewrite produced different bytes")
}
if second.Was != held {
t.Errorf("the second run reports it replaced %q; it replaced nothing", second.Was)
}
}
// A DIFFERENT image, though, must move — the ordinary case of a new control plane being installed
// over an old one. "Already correct" must not be the same code path as "already ran".
func TestANewImageReplacesAnOlderHeldOne(t *testing.T) {
first, err := Rewrite(theRealBundle(t), held)
if err != nil {
t.Fatal(err)
}
second, err := Rewrite(first.Bundle, otherHeld)
if err != nil {
t.Fatal(err)
}
if !second.Changed || second.Was != held || second.Now != otherHeld {
t.Errorf("a new image did not replace the old one: changed=%v was=%q now=%q",
second.Changed, second.Was, second.Now)
}
}
// The produced bundle is meant to be READ. Re-serialising a parsed declaration would drop every
// 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)
out, err := Rewrite(template, held)
if err != nil {
t.Fatal(err)
}
const remembered = "NAMED VOLUME"
if !strings.Contains(string(out.Bundle), remembered) {
t.Errorf("the produced bundle lost the template's comments; %q is gone", remembered)
}
}
// The installer must refuse its own bad input in its own words, rather than writing a bundle and
// letting the host refuse a declaration somebody did not write.
func TestSomethingThatIsNotAnImageIdIsRefused(t *testing.T) {
for _, bad := range []string{
"",
"mesh-controller:latest",
"sha256:abc",
"sha256:" + strings.Repeat("1", 63),
"sha256:" + strings.Repeat("g", 64),
"mesh-controller@sha256:" + strings.Repeat("1", 64),
} {
if _, err := Rewrite(theRealBundle(t), bad); err == nil {
t.Errorf("image id %q was accepted", bad)
}
}
}
// The address every enrolment token will carry is reported and never invented. It differs per
// machine and it is silently fatal when wrong: a node enrols against a dead address and nothing
// says so until it fails to come back.
func TestTheAddressNodesWillDialIsReportedAndNotRewritten(t *testing.T) {
template := theRealBundle(t)
out, err := Rewrite(template, held)
if err != nil {
t.Fatal(err)
}
if out.BrokerAddress == "" {
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 "+
"knows this machine's address", out.BrokerAddress)
}
}
// ---------------------------------------------------------------------------------------------
// 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 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 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-controller")
}
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 {
t.Fatal(err)
}
if control.Name != out.TempName {
t.Errorf("the produced bundle calls it %q, want %q", control.Name, out.TempName)
}
// And the plain name is free, which is the whole point: it belongs to the module now.
for _, name := range containerNames(out.Declaration) {
if name == ControlPlaneModule {
t.Errorf("the produced bundle still declares a container called %q, which the module "+
"will also declare", ControlPlaneModule)
}
}
}
// 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-controller@") ||
strings.Contains(string(out.Bundle), "/"+TempPrefix+"mesh-controller") {
t.Error("the rename reached inside an image reference")
}
}
// 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 {
t.Fatal(err)
}
out, err := Rewrite(theRealBundle(t), held)
if err != nil {
t.Fatal(err)
}
was, now := containerNames(before), containerNames(out.Declaration)
for id, name := range was {
if id == ControlPlaneID {
continue
}
if now[id] != name {
t.Errorf("%s was renamed from %q to %q", id, name, now[id])
}
}
}
// A re-run against a bundle this installer produced renames nothing and says so. The installer is
// run over and over while somebody gets a machine working, and a step that could not tell "already
// done" from "just done" makes the second run indistinguishable from the first.
func TestRewritingABundleThisAlreadyProducedRenamesNothing(t *testing.T) {
first, err := Rewrite(theRealBundle(t), held)
if err != nil {
t.Fatal(err)
}
second, err := Rewrite(first.Bundle, held)
if err != nil {
t.Fatal(err)
}
if second.Renamed {
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",
second.TempName, first.TempName)
}
if string(second.Bundle) != string(first.Bundle) {
t.Error("rewriting a produced bundle changed it")
}
}