Files
mesh-host/internal/bootstrap/preflight_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

127 lines
4.8 KiB
Go

package bootstrap
import (
"context"
"errors"
"strings"
"testing"
"time"
"github.com/novox/mesh-host/internal/declaration"
)
// An installed package is not a capability (novox/hq 04-ISSUES/007). The daemon is asked, and a
// machine where it does not answer is refused before anything is loaded, written or applied.
//
// The refusal has to be plain, because the person reading it is standing in front of a machine
// that will not work: it says what was asked, what came back, that re-running is safe, and names
// the record that explains why an installed docker is not enough.
func TestPreflightRefusesPlainlyWhenTheRuntimeDoesNotAnswer(t *testing.T) {
silent := func(context.Context, string, ...string) (string, error) {
return "", errors.New("Cannot connect to the Docker daemon at unix:///var/run/docker.sock")
}
// No wait, so this is one attempt: what is being tested is the refusal, not the patience.
err := waitForRuntime(context.Background(), silent, time.Second, 0, func(string) {})
if err == nil {
t.Fatal("a machine whose container runtime does not answer was accepted")
}
for _, wanted := range []string{
"no container runtime that answers",
"Cannot connect to the Docker daemon",
"04-ISSUES/007",
"idempotent",
} {
if !strings.Contains(err.Error(), wanted) {
t.Errorf("the refusal does not mention %q:\n%v", wanted, err)
}
}
}
// And a runtime that is merely slow to start is waited for rather than refused.
//
// A socket-activated daemon queued behind the network is not absent, it is a few seconds away.
// Refusing on the first attempt would make a correct bootstrap fail for being observed too early —
// and `docker load` against such a daemon blocks silently rather than failing, which is how one
// became a 35-minute silence (04-ISSUES/024).
func TestARuntimeThatIsStillStartingIsWaitedFor(t *testing.T) {
previous := runtimeAskEvery
runtimeAskEvery = time.Millisecond
defer func() { runtimeAskEvery = previous }()
attempts := 0
slow := func(context.Context, string, ...string) (string, error) {
attempts++
if attempts < 3 {
return "", errors.New("Cannot connect to the Docker daemon")
}
return "27.0.3\n", nil
}
var said []string
if err := waitForRuntime(context.Background(), slow, time.Second, time.Second,
func(line string) { said = append(said, line) }); err != nil {
t.Fatalf("a runtime that answered on the third ask was refused: %v", err)
}
if attempts != 3 {
t.Errorf("the runtime was asked %d time(s)", attempts)
}
if !strings.Contains(strings.Join(said, "\n"), "27.0.3") {
t.Errorf("the version the daemon reported was not said back: %v", said)
}
}
// What has to be reachable is what the bundle actually names, not "the internet".
//
// The mesh's own image is carried and nothing serves it, so asking a registry about it would be
// asking a question with no answer — which is the whole point of naming an image by the digest of
// its own configuration.
func TestOnlyTheRegistriesTheBundleNamesAreAskedAbout(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":"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 + `"}
]}`))
if err != nil {
t.Fatal(err)
}
got := registriesIn(parsed)
want := []string{DefaultRegistry, "192.0.2.250:5000"}
if len(got) != len(want) {
t.Fatalf("asked about %v, want %v", got, want)
}
for i := range want {
if got[i] != want[i] {
t.Errorf("asked about %v, want %v", got, want)
}
}
}
func TestWhereAnImageWouldBeFetchedFrom(t *testing.T) {
// The container runtime's own rule: the part before the first slash is a registry host if it
// has a dot, a port, or is localhost. Getting this wrong means dialling a hostname that is
// really the first half of a repository name, and refusing a machine that is fine.
for _, c := range []struct {
reference string
host string
served bool
}{
{"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},
{"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},
{"", "", false},
} {
host, served := registryOf(c.reference)
if host != c.host || served != c.served {
t.Errorf("%q → (%q, %v), want (%q, %v)", c.reference, host, served, c.host, c.served)
}
}
}