Files
mesh-host/internal/bootstrap/publish_test.go
T
jschoubben d3ee49d6f3 F-host 2, ENG-5: on a fresh mesh the bootstrap pushes the holders' own images to the store on its loopback with no credential, and names them under the seat's reach (lead's design limit, review of issue 1966)
The store's front is distribution's program, a bundle the mesh builds, so nothing serves it before the builder: the
bootstrap's /v2/ wait and its pushes now go to the loopback port the store's manifest publishes, as the publish contract
allows on the store's own node. The one-off credential and its file go: the runtime could not carry it on a push, and no
push needs it. The tags list is asked for the repository being published, not always the control plane's.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 16:29:56 +01:00

200 lines
7.1 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}
// Each value named before it is returned: a return of several composite literals spanning lines is laid
// out differently by the gofmt of one Go release and the next, and the build seat's is the one that
// judges (novox/hq issue 286).
options := Options{Registry: "127.0.0.1:5000", Timeout: time.Second, Wait: 0,
Catalogue: catalogueWith(t, RegistryModule, upstreamRegistryManifest)}
deps := Deps{
Run: runtime.run,
Fetch: func(_ context.Context, url string) (int, string, error) {
return fetch(url)
},
}
return options, deps, 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-controller","tags":["genesis"]}`, nil
},
func(_ string, args []string) (string, error) {
switch args[0] {
case "tag":
return "", nil
case "push":
pushed = true
return "", nil
case "image":
return `["127.0.0.1:5000/mesh-controller@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-controller@sha256:") {
t.Errorf("the control plane is pinned as %q", out.Reference)
}
if !runtime.ran("docker push 127.0.0.1:5000/mesh-controller: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-controller","tags":["genesis"]}`, nil
},
func(_ string, args []string) (string, error) {
if args[0] == "image" {
return `["127.0.0.1:5000/mesh-controller@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-controller@sha256:" + strings.Repeat("c", 64)
ours := "127.0.0.1:5000/mesh-controller@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] == "image" {
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] == "image" {
return `["127.0.0.1:5000/mesh-controller: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)
}
}