Files
mesh-controller/cmd/mesh-controller/recorded_kept_integration_test.go
T

145 lines
5.4 KiB
Go

//go:build integration
package main
import (
"strings"
"testing"
"time"
"github.com/novox/mesh-controller/internal/catalogue"
"github.com/novox/mesh-controller/internal/inventory"
"github.com/novox/mesh-controller/internal/link"
)
func TestARecordedBuildIsCarriedOnlyByAPersonsPush(t *testing.T) {
open := aMesh(t)
ctx := t.Context()
inv := open.inventory
start := time.Now().Add(-time.Hour)
pgImage := "registry.invalid:5000/postgres/server@sha256:" + strings.Repeat("a", 64)
mailImage := "registry.invalid:5000/mailu/smtp@sha256:" + strings.Repeat("c", 64)
for _, b := range []link.BuildResult{
aContainerBuild(t, "postgres", "c1111111", catalogue.PolicyRecord, start,
map[string][2]string{"server": {pgImage, ""}}),
aContainerBuild(t, "mailu", "c1111111", "", start.Add(time.Second),
map[string][2]string{"smtp": {mailImage, ""}, "imap": {mailImage, ""}}),
} {
if _, _, err := takeIn(ctx, inv, asTheOperator(t, inv, b)); err != nil {
t.Fatal(err)
}
}
for _, m := range []string{"postgres", "mailu"} {
if _, err := inv.Assign(ctx, "anchor", m); err != nil {
t.Fatal(err)
}
}
if err := inv.RecordSent(ctx, nodeID(t, open, "anchor"), "d-anchor",
map[string]string{"postgres": "c1111111", "mailu": "c1111111"}); err != nil {
t.Fatal(err)
}
// The merge: both adopt a health check; the images are the same.
for _, b := range []link.BuildResult{
aContainerBuild(t, "postgres", "c2222222", catalogue.PolicyRecord, start.Add(time.Minute),
map[string][2]string{"server": {pgImage, "runtime"}}),
aContainerBuild(t, "mailu", "c2222222", "", start.Add(time.Minute+time.Second),
map[string][2]string{"smtp": {mailImage, "runtime"}, "imap": {mailImage, "runtime"}}),
} {
if _, _, err := takeIn(ctx, inv, b); err != nil {
t.Fatal(err)
}
}
healthOf := func(plan catalogue.Resolution, module, id string) any {
for _, m := range plan.Modules {
for _, r := range m.Resources {
if m.Module == module && r["id"] == id {
return r["health"]
}
}
}
return nil
}
// A plan's, a release plan's, a healer's send: the database is kept as it runs, mail moves.
kept := keepingRecorded(ctx)
plan, settings, err := planFor(kept, open, "anchor")
if err != nil {
t.Fatal(err)
}
if healthOf(plan, "postgres", "server") != nil {
t.Fatal("a send that is not a person's push composed the recorded module's new build")
}
if healthOf(plan, "mailu", "smtp") == nil {
t.Fatal("the module that rolls out was not composed at its new build")
}
if imageOf(t, plan, "postgres", "server") != pgImage {
t.Fatal("the recorded module's container lost its image")
}
gens, err := generators(kept, open)
if err != nil {
t.Fatal(err)
}
declared, err := declarationWith(kept, open, "anchor", plan, settings, gens, Allocating)
if err != nil {
t.Fatal(err)
}
if declared.Builds["postgres"] != "c1111111" || declared.Builds["mailu"] != "c2222222" {
t.Fatalf("the send records it carries %v; want postgres still at c1111111 and mailu at c2222222", declared.Builds)
}
// A person's push: the recorded module's new build.
plan, _, err = planFor(ctx, open, "anchor")
if err != nil {
t.Fatal(err)
}
if healthOf(plan, "postgres", "server") == nil {
t.Fatal("a person's push did not compose the recorded module's new build")
}
// The bus step moves the bus alone: a recorded module it is told it may move moves, the others stay.
plan, _, err = planFor(keepingRecorded(ctx, "postgres"), open, "anchor")
if err != nil {
t.Fatal(err)
}
if healthOf(plan, "postgres", "server") == nil {
t.Fatal("the module a send is let move was kept")
}
// What a send composes under (sendToEach): every recorded module kept; on the bus step, the bus moves.
if under, err := sendKeeps(ctx, inv); err != nil {
t.Fatal(err)
} else if skip, on := keptExcept(under); !on || len(skip) != 0 {
t.Fatalf("an ordinary send keeps %v %v", on, skip)
}
if err := inv.RegisterModule(ctx, catalogue.Manifest{Module: "nats", Version: "2",
Provides: []catalogue.Offer{{Name: "mesh-bus"}}}, inventory.Source{Repository: "novox/mesh-catalog",
Seat: "git", Path: "modules/nats", BuiltFrom: "n2", Head: "n2"}); err != nil {
t.Fatal(err)
}
if under, err := sendKeeps(withBusStep(ctx), inv); err != nil {
t.Fatal(err)
} else if skip, on := keptExcept(under); !on || !skip["nats"] || len(skip) != 1 {
t.Fatalf("the bus step keeps %v %v; want everything recorded but the bus", on, skip)
}
// And a recorded module whose kept build the records no longer hold refuses the send, said.
if err := inv.RecordSent(ctx, nodeID(t, open, "anchor"), "d-anchor",
map[string]string{"postgres": "c0000000", "mailu": "c2222222"}); err != nil {
t.Fatal(err)
}
if _, _, err := planFor(kept, open, "anchor"); err == nil || !strings.Contains(err.Error(), "push anchor") {
t.Fatalf("a recorded build that cannot be kept did not refuse the send with its remedy: %v", err)
}
// And the gated send for mail says what it recreates: both containers, no new image, at once.
moves := []inventory.CarriedMove{{Module: "mailu", Node: "anchor", From: "c1111111", To: "c2222222"}}
sayRecreations(ctx, open, moves)
if !strings.Contains(moves[0].Recreates, "recreates 2 of mailu's 2") || !strings.Contains(moves[0].Recreates, "no new image") {
t.Fatalf("the send says %q of mail's containers", moves[0].Recreates)
}
if said := recreationsSaid(moves); !strings.HasPrefix(said, "on anchor recreates") {
t.Fatalf("the plan's note says %q", said)
}
}