Files
mesh-host/internal/image/image_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

153 lines
5.9 KiB
Go

package image
import (
"archive/tar"
"bytes"
"encoding/json"
"strings"
"testing"
)
// Each test names the decision it defends (novox/hq ADR 0017).
// savedImage builds what `docker save` produces, as far as this package reads it.
func savedImage(t *testing.T, files map[string]string) []byte {
t.Helper()
var buffer bytes.Buffer
writer := tar.NewWriter(&buffer)
for name, content := range files {
header := &tar.Header{Name: name, Mode: 0o644, Size: int64(len(content))}
if err := writer.WriteHeader(header); err != nil {
t.Fatalf("building the fixture: %v", err)
}
if _, err := writer.Write([]byte(content)); err != nil {
t.Fatalf("building the fixture: %v", err)
}
}
if err := writer.Close(); err != nil {
t.Fatalf("building the fixture: %v", err)
}
return buffer.Bytes()
}
func manifest(t *testing.T, config string, tags ...string) string {
t.Helper()
raw, err := json.Marshal([]manifestEntry{{Config: config, RepoTags: tags}})
if err != nil {
t.Fatalf("building the fixture: %v", err)
}
return string(raw)
}
// The image id is read from the FILE, before any runtime is asked anything.
//
// That is what makes the load idempotent: knowing the id in advance lets the installer ask "do you
// already hold exactly this" instead of loading and then finding out. Scraping it from what
// `docker load` prints would only be possible after loading, so the second run of an installer
// would load again every time and be unable to say it had not.
func TestTheImageIdIsReadFromTheSavedFile(t *testing.T) {
digest := strings.Repeat("a", 64)
// Both layouts `docker save` has used. The older one names the config `<digest>.json`; the OCI
// one names it `blobs/sha256/<digest>`. They carry the same sixty-four characters, and a
// reader that understood only one would work until somebody upgraded their runtime.
for _, config := range []string{digest + ".json", "blobs/sha256/" + digest} {
saved := savedImage(t, map[string]string{"manifest.json": manifest(t, config)})
id, err := ID(saved)
if err != nil {
t.Fatalf("config %q: %v", config, err)
}
if id != "sha256:"+digest {
t.Errorf("config %q gave id %q, want sha256:%s", config, id, digest)
}
}
}
func TestTheSavedTagsAreReadForAPersonToRecognise(t *testing.T) {
saved := savedImage(t, map[string]string{
"manifest.json": manifest(t, strings.Repeat("b", 64)+".json", "mesh-control:v1"),
})
got := Tags(saved)
if len(got) != 1 || got[0] != "mesh-control:v1" {
t.Errorf("tags = %v, want [mesh-control:v1]", got)
}
}
// A tar that is not a saved image is refused with what is wrong, not with a nil id.
//
// The installer names the control plane by this id in the bundle it writes. An id it could not
// read, treated as empty, would produce a bundle naming nothing — refused by the host two steps
// later, with a message about a declaration rather than about what somebody embedded.
func TestSomethingThatIsNotASavedImageIsRefused(t *testing.T) {
notAnImage := savedImage(t, map[string]string{"hello": "world"})
if _, err := ID(notAnImage); err == nil {
t.Error("a tar with no manifest.json was accepted as a saved image")
} else if !strings.Contains(err.Error(), "docker save") {
t.Errorf("the refusal does not say what to embed instead: %v", err)
}
if _, err := ID([]byte("this is not a tar at all")); err == nil {
t.Error("bytes that are not a tar were accepted")
}
}
// Exactly one image. A bootstrap that chose between several would be the thing that guesses which
// one is the control plane, and it would guess right until the day somebody saved two.
func TestATarHoldingSeveralImagesIsRefused(t *testing.T) {
entries, err := json.Marshal([]manifestEntry{
{Config: strings.Repeat("a", 64) + ".json"},
{Config: strings.Repeat("b", 64) + ".json"},
})
if err != nil {
t.Fatal(err)
}
saved := savedImage(t, map[string]string{"manifest.json": string(entries)})
if _, err := ID(saved); err == nil {
t.Error("a tar holding two images was accepted")
}
}
// An id is a digest or it is nothing. A truncated one names several images, and which one ran
// would be whichever the runtime matched first — the same reasoning `internal/declaration` gives
// for refusing a short image reference.
func TestAConfigThatIsNotADigestIsRefused(t *testing.T) {
for _, config := range []string{"config.json", "abc.json", "blobs/sha256/" + strings.Repeat("a", 63)} {
saved := savedImage(t, map[string]string{"manifest.json": manifest(t, config)})
if _, err := ID(saved); err == nil {
t.Errorf("config %q was accepted and is not a digest", config)
}
}
}
// The committed placeholder must read as "carries nothing", so an installer built from a plain
// checkout says so in preflight rather than getting a machine as far as a running store and
// stopping. This is the same guarantee `internal/bundle` makes about a lock file of only comments.
func TestAnInstallerBuiltFromAPlainCheckoutCarriesNothing(t *testing.T) {
if !IsEmpty() {
// Not a failure of this checkout: `make bootstrap` embeds a real image and puts the
// placeholder back, so a real image here means a build was interrupted.
t.Skip("this checkout has a saved image embedded, so there is no placeholder to check")
}
if _, err := Saved(); err == nil {
t.Fatal("an installer carrying only the placeholder reported it carries an image")
}
}
// And "empty" is decided by whether the bytes could be loaded, not by matching the placeholder's
// text. A truncated or corrupted embed is equally unloadable and equally worth refusing early.
func TestEmptyMeansUnloadableRatherThanEqualToThePlaceholder(t *testing.T) {
restore := saved
defer func() { saved = restore }()
saved = []byte("half a tar, cut off")
if !IsEmpty() {
t.Error("bytes that are not a tar were reported as a carried image")
}
saved = savedImage(t, map[string]string{
"manifest.json": manifest(t, strings.Repeat("c", 64)+".json"),
})
if IsEmpty() {
t.Error("a real saved image was reported as no image at all")
}
}