Files
mesh-controller/internal/inventory/provisioned_test.go
T

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