Files
mesh-host/internal/bootstrap/publish_test.go
T
jschoubben f534cf8b42 bootstrap: the rest of the pivot — enrol, registry, publish, reinstall, retire
Steps 6 to 10, which turn a substrate into a mesh that can maintain itself
(novox/hq ADR 0067).

 6 enrol      a node record, a token, `mesh-host enrol`, and the host agent
              running. Proved by the mesh having HEARD from the node, not by a
              process existing: a host that cannot reach the broker looks exactly
              like a successful install until the first push applies nothing.
 7 registry   the module that gives this mesh an image store, registered from a
              --catalog checkout, assigned and pushed. Its image is upstream and
              never built (04-ISSUES/029) — a placeholder digest there is refused.
              Verified by asking `/v2/`, because a container that is up is not a
              registry that serves.
 8 publish    the carried image pushed into that registry, which assigns it the
              first manifest digest it has ever had. This is the hinge: without
              it the mesh works and can never upgrade itself.
 9 control    the control plane registered as an ordinary module pinned to that
              digest, with the substrate's own store connections delivered
              through `secret accept` — read out of the bundle that made them,
              because the mesh cannot invent a credential that predates it.
10 retire     the temporary control plane dropped from the bundle and removed by
              the host's ordinary removal pass.

Every step asks before it acts and reports "already done". No step leaves the
machine without a control plane: steps 9 and 10 overlap deliberately, and two
stateless control planes are untidy rather than broken.

mesh-control's `internal/builder`.PublishImage is mirrored rather than imported —
tier 0 depends on nothing that must be installed first — with one correction: the
digest is chosen from RepoDigests by repository instead of taken as element zero,
so an image pushed to two registries cannot silently pin this mesh to the wrong
one.

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

198 lines
6.7 KiB
Go

package bootstrap
import (
"context"
"errors"
"fmt"
"net/http"
"strings"
"testing"
"time"
)
// Step 8 is the pivot's hinge (novox/hq ADR 0067): the carried image gets a manifest digest, which
// is the first one it has ever had, and that is what lets the control plane be named the way every
// other module is named. These tests defend how that digest is learned, because a wrong one pins
// the mesh to an image nothing on this machine serves.
func publishing(t *testing.T, fetch func(string) (int, string, error),
run func(name string, args []string) (string, error)) (Options, Deps, *asked) {
t.Helper()
runtime := &asked{answer: run}
return Options{
Registry: "127.0.0.1:5000",
Timeout: time.Second,
Wait: 0,
}, Deps{
Run: runtime.run,
Fetch: func(_ context.Context, url string) (int, string, error) {
return fetch(url)
},
}, runtime
}
// Nothing has ever been pushed under this name, so the registry says 404 — and that is an answer,
// not a failure. An installer that treated it as one would refuse on the first run of the step it
// exists to perform.
func TestAnImageNoRegistryHasEverHeldIsPushed(t *testing.T) {
pushed := false
o, d, runtime := publishing(t,
func(string) (int, string, error) {
if !pushed {
return http.StatusNotFound, "", nil
}
return http.StatusOK, `{"name":"mesh-control","tags":["genesis"]}`, nil
},
func(_ string, args []string) (string, error) {
switch args[0] {
case "tag":
return "", nil
case "push":
pushed = true
return "", nil
case "inspect":
return `["127.0.0.1:5000/mesh-control@sha256:` + strings.Repeat("a", 64) + `"]`, nil
}
return "", fmt.Errorf("unexpected: %v", args)
})
out, err := PublishControlPlane(context.Background(), o, d, held, func(string) {})
if err != nil {
t.Fatal(err)
}
if out.Already {
t.Error("an image no registry held was reported as already published")
}
if !strings.HasPrefix(out.Reference, "127.0.0.1:5000/mesh-control@sha256:") {
t.Errorf("the control plane is pinned as %q", out.Reference)
}
if !runtime.ran("docker push 127.0.0.1:5000/mesh-control:genesis") {
t.Errorf("nothing was pushed: %v", runtime.commands)
}
}
// An image the registry already serves is not pushed again, and says so. Blobs are named by their
// content, so re-pushing is asking a registry to store what it already has under the name it
// already has — and the installer is run over and over.
func TestAnImageTheRegistryAlreadyServesIsNotPushedAgain(t *testing.T) {
o, d, runtime := publishing(t,
func(string) (int, string, error) {
return http.StatusOK, `{"name":"mesh-control","tags":["genesis"]}`, nil
},
func(_ string, args []string) (string, error) {
if args[0] == "inspect" {
return `["127.0.0.1:5000/mesh-control@sha256:` + strings.Repeat("b", 64) + `"]`, nil
}
return "", fmt.Errorf("unexpected: %v", args)
})
out, err := PublishControlPlane(context.Background(), o, d, held, func(string) {})
if err != nil {
t.Fatal(err)
}
if !out.Already {
t.Error("an image the registry already serves was not reported as already published")
}
if runtime.ran("docker push") {
t.Errorf("it was pushed again: %v", runtime.commands)
}
}
// **The digest is chosen by repository, not taken as element zero.** An image that has been pushed
// to more than one registry has more than one entry, and element zero is whichever the runtime
// listed first — which would pin this mesh's control plane to somebody else's registry, silently,
// which is the dependency the whole pivot exists to remove.
func TestTheDigestComesFromThisMeshsOwnRegistry(t *testing.T) {
elsewhere := "some.other.registry/mesh-control@sha256:" + strings.Repeat("c", 64)
ours := "127.0.0.1:5000/mesh-control@sha256:" + strings.Repeat("d", 64)
o, d, _ := publishing(t,
func(string) (int, string, error) {
return http.StatusOK, `{"tags":["genesis"]}`, nil
},
func(_ string, args []string) (string, error) {
if args[0] == "inspect" {
return `["` + elsewhere + `","` + ours + `"]`, nil
}
return "", fmt.Errorf("unexpected: %v", args)
})
out, err := PublishControlPlane(context.Background(), o, d, held, func(string) {})
if err != nil {
t.Fatal(err)
}
if out.Reference != ours {
t.Errorf("the control plane is pinned as %q, and this mesh's registry serves %q",
out.Reference, ours)
}
}
// A push that produced no digest this mesh's registry serves is refused, and the refusal says what
// depends on it. The next step names the control plane's module by that digest, so there would be
// nothing to name — and finding that out one step later would mean registering a module pinned to
// an empty string.
func TestAPushThatProducedNoDigestIsRefused(t *testing.T) {
pushed := false
o, d, _ := publishing(t,
func(string) (int, string, error) {
if !pushed {
return http.StatusNotFound, "", nil
}
// Pushed, and the registry still does not list it.
return http.StatusOK, `{"tags":[]}`, nil
},
func(_ string, args []string) (string, error) {
if args[0] == "push" {
pushed = true
}
return "", nil
})
_, err := PublishControlPlane(context.Background(), o, d, held, func(string) {})
if err == nil {
t.Fatal("a push that produced no digest was accepted")
}
if !strings.Contains(err.Error(), "does not serve it") {
t.Errorf("the refusal does not say what is missing: %v", err)
}
}
// A tag is not a pin. If the runtime answers with something that is not pinned by digest, it is
// not used — a tag can be made to point at a different image, and this reference is applied on
// machines with no mesh to ask about anything (novox/hq ADR 0006).
func TestATagIsNotAPin(t *testing.T) {
o, d, _ := publishing(t,
func(string) (int, string, error) {
return http.StatusOK, `{"tags":["genesis"]}`, nil
},
func(_ string, args []string) (string, error) {
if args[0] == "inspect" {
return `["127.0.0.1:5000/mesh-control:genesis"]`, nil
}
return "", nil
})
out, err := PublishControlPlane(context.Background(), o, d, held, func(string) {})
if err == nil {
t.Fatalf("a tag was accepted as a pin: %q", out.Reference)
}
}
// A registry that cannot be reached at all is said so plainly rather than becoming a push that
// fails for a reason nobody can read.
func TestARegistryThatCannotBeAskedIsSaidSo(t *testing.T) {
o, d, _ := publishing(t,
func(string) (int, string, error) {
return 0, "", errors.New("connection refused")
},
func(string, []string) (string, error) { return "", nil })
_, err := PublishControlPlane(context.Background(), o, d, held, func(string) {})
if err == nil {
t.Fatal("a registry that refused the connection was treated as empty")
}
if !strings.Contains(err.Error(), "cannot ask the registry") {
t.Errorf("the refusal does not say the registry could not be asked: %v", err)
}
}