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-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) } }