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

187 lines
6.7 KiB
Go

//go:build integration
package main
import (
"github.com/nats-io/nats.go"
"reflect"
"strings"
"testing"
"time"
sdkdelivery "git.novox.be/novox/mesh-sdk/go/delivery"
"github.com/novox/mesh-controller/internal/inventory"
)
// A6, A7 whole against the store and a real bus (ADR 0503 §6, ADR 0498 check §7; issue 1851): a walk whose modules the
// builder built goes from asked to built with no build asked, its keys taken in off the plans' hold, then sends its tier
// to the first machine as before; a key still building holds the tier, said, and moves on when the builder's key is done.
func TestAWalkTakesItsTierFromTheBuildersKeysAndSendsIt(t *testing.T) {
withTheGate(t)
judgeLeavesAsTheCore(t)
tm := aTierMesh(t, "app1", "app2")
ctx := t.Context()
inv := tm.open.inventory
noBuildAsked(t)
b := aBuilderOnTheBus(t)
// The walk carries c3 of the catalogue; its tier is unasked.
p := tm.plan(t)
p.Commit = "c3c3c3c3c3c3c3c3"
for _, m := range []string{"app1", "app2"} {
p.Modules[m] = &inventory.PlanModule{}
}
if err := inv.SavePlan(ctx, &p); err != nil {
t.Fatal(err)
}
fp1, fp2 := fpOf('1'), fpOf('2')
m1 := b.indexed(t, "app1", "c3", "novox/mesh-catalog", "modules/app1", p.Commit, fp1)
m2 := b.indexed(t, "app2", "c3", "novox/mesh-catalog", "modules/app2", p.Commit, fp2)
b.built(t, "app1", "c3", fp1, m1, sdkdelivery.BuildDone)
b.built(t, "app2", "c3", fp2, m2, sdkdelivery.BuildBuilding)
advancePlans(ctx, tm.open)
becomes(t, 10*time.Second, "app1 taken from its key", func() bool {
advancePlans(ctx, tm.open)
return tm.plan(t).Modules["app1"].State == "built"
})
p = tm.plan(t)
if s := p.Modules["app2"]; s.State != "asked" || !s.Key || !strings.Contains(s.Why, "building on ace") {
t.Fatalf("A7: app2, whose key is building, is %+v", s)
}
if len(tm.sent) != 0 {
t.Fatalf("A7: the tier was sent while app2 waits on its key: %v", tm.sent)
}
if s := p.Modules["app1"]; !strings.Contains(s.Made, "taken from the builder's key") || s.Fingerprint != fp1 {
t.Fatalf("A6: plans does not say app1 was taken from the builder's key: %+v", s)
}
b.built(t, "app2", "c3", fp2, m2, sdkdelivery.BuildDone)
becomes(t, 10*time.Second, "the tier sent to the first machine", func() bool {
advancePlans(ctx, tm.open)
return len(tm.sent) > 0
})
p = tm.plan(t)
if !reflect.DeepEqual(tm.sent[0], []string{"anchor"}) {
t.Fatalf("A6: the tier taken from keys was sent %v, not to the first machine", tm.sent)
}
current, err := inv.CurrentBuilds(ctx)
if err != nil {
t.Fatal(err)
}
for _, m := range []string{"app1", "app2"} {
if current[m].Commit != p.Commit || !strings.HasPrefix(p.Modules[m].Build, "key-") {
t.Fatalf("A6: %s stands on %+v (%s), not the key's build at %s", m, current[m], p.Modules[m].Build, p.Commit)
}
}
}
// A7: a failed key fails the walk with the builder's words, and nothing is sent.
func TestAFailedKeyFailsTheWalkInTheBuildersWords(t *testing.T) {
tm := aTierMesh(t, "app1")
ctx := t.Context()
inv := tm.open.inventory
noBuildAsked(t)
b := aBuilderOnTheBus(t)
p := tm.plan(t)
p.Commit = "c4c4c4c4c4c4c4c4"
p.Modules["app1"] = &inventory.PlanModule{}
if err := inv.SavePlan(ctx, &p); err != nil {
t.Fatal(err)
}
fp := fpOf('4')
manifest := b.indexed(t, "app1", "c4", "novox/mesh-catalog", "modules/app1", p.Commit, fp)
b.built(t, "app1", "c4", fp, manifest, sdkdelivery.BuildFailed)
for i := 0; i < 3; i++ {
advancePlans(ctx, tm.open)
}
p = tm.plan(t)
if p.State != inventory.PlanFailed || !strings.Contains(p.Note, "digests differ: ace sha256:1, g14 sha256:2") {
t.Fatalf("A7: the walk is %s: %q", p.State, p.Note)
}
if len(tm.sent) != 0 {
t.Fatalf("A7: a failed walk sent %v", tm.sent)
}
}
// C2's criterion (issue 1851, R4g): a merge whose walk moves the builder, a build module and the controller takes every one
// from the builder's referenced done key, and no ask reaches any subject of the seat node-build-agent. A fake builder writes
// the keys on a real bus.
func TestTheBuilderABuildModuleAndTheControllerAreTakenFromKeysAndNothingIsAskedOfBuildAgent(t *testing.T) {
names := []string{sdkdelivery.BuilderModule, "mesh-build-go", "mesh-controller"}
tm := aTierMesh(t, names...)
ctx := t.Context()
inv := tm.open.inventory
b := aBuilderOnTheBus(t)
asked := make(chan string, 16)
sub, err := b.js.Conn().Subscribe("mesh.seat.node-build-agent.>", func(m *nats.Msg) { asked <- m.Subject })
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = sub.Unsubscribe() })
p := tm.plan(t)
p.Commit = "c5c5c5c5c5c5c5c5"
for _, m := range names {
p.Modules[m] = &inventory.PlanModule{}
}
if err := inv.SavePlan(ctx, &p); err != nil {
t.Fatal(err)
}
for i, m := range names {
fp := fpOf(byte('a' + i))
manifest := b.indexed(t, m, "c5", "novox/mesh-catalog", "modules/"+m, p.Commit, fp)
b.built(t, m, "c5", fp, manifest, sdkdelivery.BuildDone)
}
becomes(t, 10*time.Second, "the three taken from their keys", func() bool {
advancePlans(ctx, tm.open)
q := tm.plan(t)
for _, m := range names {
if s := q.Modules[m]; s == nil || s.State != "built" {
return false
}
}
return true
})
p = tm.plan(t)
for _, m := range names {
if s := p.Modules[m]; !s.Key || !strings.HasPrefix(s.Build, "key-") || !strings.Contains(s.Made, "taken from the builder's key") {
t.Errorf("%s was not taken from the builder's referenced key: %+v", m, s)
}
}
select {
case subject := <-asked:
t.Fatalf("an ask reached the retired build seat: %s", subject)
case <-time.After(300 * time.Millisecond):
}
}
// C5's criterion: after a plan that moves the builder, its record names the builder's key and its fingerprint.
func TestTheBuildersRecordNamesItsKeyAndFingerprint(t *testing.T) {
tm := aTierMesh(t, sdkdelivery.BuilderModule)
ctx := t.Context()
inv := tm.open.inventory
b := aBuilderOnTheBus(t)
p := tm.plan(t)
p.Commit = "c6c6c6c6c6c6c6c6"
p.Modules[sdkdelivery.BuilderModule] = &inventory.PlanModule{}
if err := inv.SavePlan(ctx, &p); err != nil {
t.Fatal(err)
}
fp := fpOf('9')
manifest := b.indexed(t, sdkdelivery.BuilderModule, "c6", "novox/mesh-catalog", "modules/"+sdkdelivery.BuilderModule, p.Commit, fp)
b.built(t, sdkdelivery.BuilderModule, "c6", fp, manifest, sdkdelivery.BuildDone)
becomes(t, 10*time.Second, "the builder taken from its key", func() bool {
advancePlans(ctx, tm.open)
return tm.plan(t).Modules[sdkdelivery.BuilderModule].State == "built"
})
id := tm.plan(t).Modules[sdkdelivery.BuilderModule].Build
record, found, err := inv.BuildByID(ctx, id)
if err != nil || !found {
t.Fatalf("the builder's record %q: %v %v", id, found, err)
}
if !strings.HasPrefix(record.ID, "key-") || record.SourceFingerprint != fp || record.Module != sdkdelivery.BuilderModule {
t.Fatalf("the builder's record names %+v, not its key and fingerprint %s", record, fp)
}
}