Files
mesh-controller/internal/inventory/provisioned_test.go
T
mesh-tools 97a2d841c7 Land feature 57's controller work on its release branch
The controller plans config, templates and moved providers' provisions,
hands bus accounts to the vault and judges the new manifest words, on the
feature's SDK and node-engine validator (feature 57, config and provisioning).
2026-10-15 21:27:33 +00:00

125 lines
5.6 KiB
Go

package inventory
import (
"strings"
"testing"
"time"
)
// A walk counts a provisioned module applied on a machine only once its files are placed (novox/hq ADR 0362, design
// 57 P6, issue 672). The walk of issue 582 (one module, three machines of the rest), with g14's send having handed
// dunst to the provisioner: g14's own report comes 5 s after its send, shanks's, the last of the others, 15 s
// after its own.
const dunstTemplates = "sha256:" + "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
func walkHandingDunst(t *testing.T) sendRest582 {
t.Helper()
c := walk582(t)
s := c.walk.Modules["build-agent"].Rest["g14"]
s.Provisioned = map[string]string{"build-agent": dunstTemplates}
c.walk.Modules["build-agent"].Rest["g14"] = s
return c
}
// provisioning is a walk's lookup with the machines' clients saying reports.
func (w sendRest582) provisioning(reports map[string]ProvReport) AppliedLookup {
return func(node string, send SentDeclaration, walkSent time.Time) (AppliedReport, bool) {
r, ok := w.lookup(node, send, walkSent)
return ProvisionedSend(node, send, r, ok, func(node, module string) (ProvReport, bool) {
p, said := reports[node+"."+module]
return p, said
})
}
}
func restOf(w *WalkPhases, node string) RestReport {
for _, r := range w.Rest {
if r.Node == node {
return r
}
}
return RestReport{}
}
// The node-engine's report alone does not end the walk: it waits for prov.applied; with it, the walk ends, and
// its delivery time runs to the later of the two reports.
func TestAWalkWaitsForTheProvisionersReportAndEndsAtTheLater(t *testing.T) {
c := walkHandingDunst(t)
g14Sent := c.walk.Modules["build-agent"].Rest["g14"].At
w := c.walk.WalkPhases(g14Sent.Add(time.Minute), c.provisioning(nil))
if w.End != nil || restOf(w, "g14").State != RestWaiting ||
!strings.Contains(phase(w, PhaseApply, -1).Said, "waiting for g14") {
t.Fatalf("the walk did not wait for g14's prov.applied: end %v, g14 %+v, apply %+v", w.End, restOf(w, "g14"),
phase(w, PhaseApply, -1))
}
// A report on an older build of the templates is not this send's.
older := map[string]ProvReport{"g14.build-agent": {Status: ProvApplied, Templates: "sha256:" + strings.Repeat("0", 64),
At: g14Sent.Add(2 * time.Second)}}
if w := c.walk.WalkPhases(g14Sent.Add(time.Minute), c.provisioning(older)); w.End != nil {
t.Fatalf("a report on an older build ended the walk at %v", w.End)
}
placed := g14Sent.Add(50 * time.Second) // after shanks's report, 35 s after the judgement
w = c.walk.WalkPhases(g14Sent.Add(time.Hour), c.provisioning(map[string]ProvReport{
"g14.build-agent": {Status: ProvApplied, Templates: dunstTemplates, At: placed}}))
sumsUp(t, w)
if w.End == nil || !w.End.Equal(placed) {
t.Fatalf("the walk ends at %v, want the provisioner's report at %s: %+v", w.End, placed, w.Rest)
}
if g := restOf(w, "g14"); g.State != RestApplied || !g.Reported.Equal(placed) {
t.Fatalf("g14 is not applied at its later report: %+v", g)
}
}
// A failed prov.applied is the machine's failed apply, named in the walk's end with its reason.
func TestAFailedProvisioningIsAFailedApply(t *testing.T) {
c := walkHandingDunst(t)
g14Sent := c.walk.Modules["build-agent"].Rest["g14"].At
w := c.walk.WalkPhases(g14Sent.Add(time.Hour), c.provisioning(map[string]ProvReport{
"g14.build-agent": {Status: ProvFailed, Reason: "the templates cannot be fetched", At: g14Sent.Add(3 * time.Second)}}))
if w.End != nil {
t.Fatalf("a failed provisioning ended the walk: %v", w.End)
}
if g := restOf(w, "g14"); g.State != RestFailed || !strings.Contains(g.Outcome, "the templates cannot be fetched") {
t.Fatalf("g14 is not named failed with the reason: %+v", g)
}
if said := phase(w, PhaseApply, -1).Said; !strings.Contains(said, "not applied on g14 (provisioning failed (build-agent: "+
"the templates cannot be fetched))") {
t.Fatalf("the walk's end does not name g14's failure: %q", said)
}
// A failure said before the send, of no build, is not this send's.
stale := map[string]ProvReport{"g14.build-agent": {Status: ProvFailed, Reason: "old", At: g14Sent.Add(-time.Hour)}}
if g := restOf(c.walk.WalkPhases(g14Sent.Add(time.Minute), c.provisioning(stale)), "g14"); g.State != RestWaiting {
t.Fatalf("a failure from before the send was read as this send's: %+v", g)
}
}
// None within ApplySilentAfter names the machine silent, never counted applied; the others end the walk.
func TestNoProvisioningReportWithinTheBoundIsSilence(t *testing.T) {
c := walkHandingDunst(t)
g14Sent := c.walk.Modules["build-agent"].Rest["g14"].At
waiting := map[string]ProvReport{"g14.build-agent": {Status: ProvWaiting, WaitsFor: []string{"config:font-size"},
At: g14Sent.Add(3 * time.Second)}}
w := c.walk.WalkPhases(g14Sent.Add(ApplySilentAfter+time.Minute), c.provisioning(waiting))
sumsUp(t, w)
if g := restOf(w, "g14"); g.State != RestSilent {
t.Fatalf("g14 is not silent past ApplySilentAfter: %+v", g)
}
if len(w.Silent) != 1 || w.Silent[0] != "g14" || w.End == nil || !w.End.Equal(c.judged.Add(35*time.Second)) {
t.Fatalf("the walk does not end on the others, naming g14 silent: end %v, silent %v", w.End, w.Silent)
}
}
// A send that handed nothing ends on the node-engine's report alone, as before.
func TestASendThatHandedNothingEndsOnTheEnginesReport(t *testing.T) {
c := walk582(t)
before := c.walk.WalkPhases(c.judged.Add(time.Hour), c.lookup)
now := c.walk.WalkPhases(c.judged.Add(time.Hour), c.provisioning(nil))
if before.End == nil || now.End == nil || !now.End.Equal(*before.End) {
t.Fatalf("a walk handing nothing ends differently: before %v, now %v", before.End, now.End)
}
}