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 substrate 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 // 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") if err != nil { t.Fatalf("reading the substrate 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 substrate 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 substrate 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 substrate 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-control"} ]}`) 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 substrate 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-control:latest", "sha256:abc", "sha256:" + strings.Repeat("1", 63), "sha256:" + strings.Repeat("g", 64), "mesh-control@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 substrate 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 substrate raises a control plane and a // module later declares one; if both are called `mesh-control` 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) { 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") } if out.TempName != "temp-mesh-control" { t.Errorf("the substrate'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-control` too, and it is not a container name. A // substitution that caught it would produce `…/temp-mesh-control@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") { 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. 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-control 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") } }