Files
mesh-host/internal/bootstrap/load_test.go
T
jschoubben b82ab95f74 mesh-bootstrap: the first-node procedure, as a program rather than a test
The only complete written-down copy of how a mesh is stood up was an integration
test in the lab. That is why every bootstrap gap kept being found late: an install
procedure that lives as a test fixture is exercised by whoever writes tests, never
by whoever installs. This is that procedure.

A separate binary, not a mesh-host subcommand. mesh-host says of itself that it
connects to nothing and listens on nothing and that what it applies comes from a
file, and that sentence is what makes an always-running root daemon auditable. An
installer loads images and interrogates a control plane. Same tier, different
program.

The control plane's image is carried, not built and not fetched. The forge that
holds its source runs on the mesh, so a bootstrap that had to fetch it would need
a mesh in order to raise one. Embedding breaks that cycle the way the carried
bundle breaks "copy it onto a machine and run it". The image id is read out of the
saved tar before the runtime is asked anything, which is what makes the load
idempotent: the installer can ask whether the machine already holds exactly this.

Five steps, each idempotent and each saying whether it found or changed something,
because this is run over and over by somebody getting a machine working. It stops
at a running substrate with a control plane that replies — enrolment, the module
catalogue and assignment are the next stage and are deliberately absent.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
2026-09-10 23:17:30 +02:00

212 lines
7.5 KiB
Go

package bootstrap
import (
"archive/tar"
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"strings"
"testing"
"github.com/novox/mesh-host/internal/image"
)
// Docker is never required here. What is being tested is which commands the installer issues and
// what it concludes from the answers, so the runtime is injected the way `internal/apply` injects
// its Runner (novox/hq ADR 0017). Behaviour against a real runtime is proved in the lab.
// asked records every command, so a test can assert that something was NOT run — which is the
// whole of what idempotence means here.
type asked struct {
commands []string
answer func(name string, args []string) (string, error)
}
func (a *asked) run(_ context.Context, name string, args ...string) (string, error) {
a.commands = append(a.commands, strings.TrimSpace(name+" "+strings.Join(args, " ")))
if a.answer == nil {
return "", errors.New("this test did not expect any command to be run")
}
return a.answer(name, args)
}
func (a *asked) ran(fragment string) bool {
for _, command := range a.commands {
if strings.Contains(command, fragment) {
return true
}
}
return false
}
func savedImageFixture(t *testing.T, digest string) []byte {
t.Helper()
entries, err := json.Marshal([]struct {
Config string
RepoTags []string
}{{Config: digest + ".json", RepoTags: []string{"mesh-control:test"}}})
if err != nil {
t.Fatal(err)
}
var buffer bytes.Buffer
writer := tar.NewWriter(&buffer)
if err := writer.WriteHeader(&tar.Header{
Name: "manifest.json", Mode: 0o644, Size: int64(len(entries)),
}); err != nil {
t.Fatal(err)
}
if _, err := writer.Write(entries); err != nil {
t.Fatal(err)
}
if err := writer.Close(); err != nil {
t.Fatal(err)
}
return buffer.Bytes()
}
const fixtureDigest = "3333333333333333333333333333333333333333333333333333333333333333"
// A machine that already holds the image is not loaded again, and says so.
//
// This is the idempotence the installer's usefulness rests on: it is run over and over while
// somebody gets a machine working, and a step that did its work again every time would be
// indistinguishable from one that had never run.
func TestAnImageThisMachineAlreadyHoldsIsNotLoadedAgain(t *testing.T) {
runtime := &asked{answer: func(_ string, args []string) (string, error) {
if len(args) > 1 && args[0] == "image" && args[1] == "inspect" {
return "sha256:" + fixtureDigest + "\n", nil
}
return "", fmt.Errorf("unexpected command: %v", args)
}}
var said []string
loaded, err := loadImage(context.Background(), runtime.run,
savedImageFixture(t, fixtureDigest), false, func(line string) { said = append(said, line) })
if err != nil {
t.Fatal(err)
}
if !loaded.Held {
t.Error("the machine already held the image and the load did not say so")
}
if runtime.ran("docker load") {
t.Errorf("the image was loaded again although the machine held it: %v", runtime.commands)
}
if !strings.Contains(strings.Join(said, "\n"), "already held") {
t.Errorf("nothing was said about finding the image already there: %v", said)
}
}
// The id comes out of the file, and the bundle is named by it.
//
// Not scraped from what `docker load` prints — that is a sentence for a person, which reads
// `Loaded image: name:tag` or `Loaded image ID: sha256:…` depending on how the image was saved.
// A program depending on which one a runtime chose would be depending on a runtime version.
func TestTheImageIdComesFromTheCarriedFileNotFromWhatTheRuntimeSays(t *testing.T) {
runtime := &asked{}
inspected := 0
runtime.answer = func(_ string, args []string) (string, error) {
switch {
case len(args) > 1 && args[0] == "image" && args[1] == "inspect":
inspected++
if inspected == 1 {
return "", errors.New("Error: No such image")
}
return "sha256:" + fixtureDigest + "\n", nil
case len(args) > 0 && args[0] == "load":
// Deliberately says something else entirely. The id must not come from here.
return "Loaded image: some-other-name:whatever\n", nil
}
return "", fmt.Errorf("unexpected command: %v", args)
}
loaded, err := loadImage(context.Background(), runtime.run,
savedImageFixture(t, fixtureDigest), false, func(string) {})
if err != nil {
t.Fatal(err)
}
if loaded.ID != "sha256:"+fixtureDigest {
t.Errorf("the image id is %q, want sha256:%s", loaded.ID, fixtureDigest)
}
if loaded.Held {
t.Error("an image that had to be loaded was reported as already held")
}
if !runtime.ran("docker load") {
t.Errorf("the image was never loaded: %v", runtime.commands)
}
}
// A load that reported success and left nothing there is a failure, not a convergence
// (novox/hq ADR 0018). Without the read-back it would surface later as the host refusing a bundle
// naming an image nothing serves — a true message about the wrong thing.
func TestALoadThatLeftNothingBehindIsAFailure(t *testing.T) {
runtime := &asked{answer: func(_ string, args []string) (string, error) {
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
}}
_, err := loadImage(context.Background(), runtime.run,
savedImageFixture(t, fixtureDigest), false, func(string) {})
if err == nil {
t.Fatal("a load that left nothing on the machine was reported as success")
}
if !strings.Contains(err.Error(), fixtureDigest) {
t.Errorf("the failure does not say which image is missing: %v", err)
}
}
// A dry run changes nothing, and still knows the id — because the id is a property of the carried
// file. That is what lets `--dry-run` produce and check the real bundle rather than a guess.
func TestADryRunLearnsTheIdAndLoadsNothing(t *testing.T) {
runtime := &asked{answer: func(_ string, args []string) (string, error) {
if len(args) > 1 && args[0] == "image" && args[1] == "inspect" {
return "", errors.New("Error: No such image")
}
return "", fmt.Errorf("a dry run ran %v", args)
}}
loaded, err := loadImage(context.Background(), runtime.run,
savedImageFixture(t, fixtureDigest), true, func(string) {})
if err != nil {
t.Fatal(err)
}
if loaded.ID != "sha256:"+fixtureDigest {
t.Errorf("a dry run did not work out the image id: %q", loaded.ID)
}
if runtime.ran("docker load") {
t.Errorf("a dry run loaded an image: %v", runtime.commands)
}
}
// An installer built from a plain checkout carries no image, and says which build step is missing.
// Discovered here, before a machine is touched, rather than after a bundle has been written.
func TestAnInstallerCarryingNoImageSaysSoRatherThanRaisingHalfAMesh(t *testing.T) {
if !image.IsEmpty() {
t.Skip("this checkout has a saved image embedded")
}
_, err := Load(context.Background(), (&asked{}).run, false, func(string) {})
if !errors.Is(err, image.ErrEmpty) {
t.Fatalf("an installer with no control-plane image gave %v, want ErrEmpty", err)
}
if !strings.Contains(err.Error(), "make bootstrap") {
t.Errorf("the refusal does not say how to build one that carries an image: %v", err)
}
}
// Two different images cannot share an id, so an answer that is not the id asked about means the
// runtime is talking about something else. Reported rather than believed.
func TestARuntimeAnsweringAboutADifferentImageIsRefused(t *testing.T) {
runtime := &asked{answer: func(_ string, _ []string) (string, error) {
return "sha256:" + strings.Repeat("9", 64) + "\n", nil
}}
if _, err := loadImage(context.Background(), runtime.run,
savedImageFixture(t, fixtureDigest), false, func(string) {}); err == nil {
t.Fatal("the runtime answered about a different image and it was accepted")
}
}