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

97 lines
4.0 KiB
Go

package inventory
import (
"encoding/json"
"reflect"
"strings"
"testing"
)
// A send keeps the provisioner's plans it handed (design 57 P6, issue 672, migration 0100): the assignments mirror
// carries the plan of the build that machine was sent, in the module's own assignment and no other, and a walk
// reads which templates the send handed.
func TestASendKeepsThePlansItHandedAndTheMirrorCarriesThem(t *testing.T) {
inv, node := aNodeWithModules(t, "dunst", "web")
ctx := t.Context()
for _, m := range []string{"dunst", "web"} {
if _, err := inv.Assign(ctx, node, m); err != nil {
t.Fatal(err)
}
}
record, err := inv.NodeByName(ctx, node)
if err != nil {
t.Fatal(err)
}
digest := "sha256:" + strings.Repeat("d", 64)
plan := Provisioning{Templates: TemplatesArchive{Source: "http://store:5000/v2/dunst/files/blobs/" + digest,
Digest: digest}, Files: []ProvisionedFile{{ID: "configuration", Path: "/home/op/.config/dunst/dunstrc",
Template: "dunstrc", OwnerAtRuntime: "owned", Takes: "reload-on"}}}
// A plan for a module the machine no longer has assigned is kept with the send and mirrored nowhere; one for a
// module only pulled in by another is mirrored under its own key, saying what requires it (issue 765).
raw := []byte(`{"dunst":` + mustJSON(t, plan) + `,"gone":` + mustJSON(t, plan) + `,"notify":` +
strings.Replace(mustJSON(t, plan), `{`, `{"required-by":"desk",`, 1) + `}`)
if err := inv.RecordSentWith(ctx, record.ID, "first", nil, 0, 0, Send{Digest: "first", Provisioning: raw}); err != nil {
t.Fatal(err)
}
sent, _, err := inv.SentTo(ctx, node)
if err != nil || !reflect.DeepEqual(sent.Provisioned, map[string]string{"dunst": digest, "gone": digest,
"notify": digest}) {
t.Fatalf("the send says it handed %v (%v)", sent.Provisioned, err)
}
plans, err := inv.SentProvisioningPlans(ctx)
required := plan
required.RequiredBy = "desk"
if err != nil || !reflect.DeepEqual(plans, map[string]Provisioning{node + ".dunst": plan, node + ".notify": required}) {
t.Fatalf("the plans mirrored are %+v (%v)", plans, err)
}
inv.SetProvisioningPlans(inv.SentProvisioningPlans)
mirror, err := inv.AssignmentsMirror(ctx)
if err != nil {
t.Fatal(err)
}
var dunst, web AssignmentMirrored
if err := json.Unmarshal(mirror[node+".dunst"], &dunst); err != nil {
t.Fatal(err)
}
if err := json.Unmarshal(mirror[node+".web"], &web); err != nil {
t.Fatal(err)
}
if dunst.Provisioning == nil || !reflect.DeepEqual(*dunst.Provisioning, plan) || web.Provisioning != nil {
t.Fatalf("the mirror carries %+v for dunst and %+v for web", dunst.Provisioning, web.Provisioning)
}
var notify AssignmentMirrored
if err := json.Unmarshal(mirror[node+".notify"], &notify); err != nil {
t.Fatalf("no value for the pulled-in module: %s (%v)", mirror[node+".notify"], err)
}
if notify.RequiredBy != "desk" || !notify.Assigned.IsZero() || notify.Provisioning == nil ||
!reflect.DeepEqual(*notify.Provisioning, plan) {
t.Fatalf("the mirror carries %s for the pulled-in module", mirror[node+".notify"])
}
if _, has := mirror[node+".gone"]; has {
t.Fatalf("a module no longer assigned is mirrored: %s", mirror[node+".gone"])
}
// The next send hands nothing: nothing is kept, and the assignment's value is the bytes it was.
if err := inv.RecordSentWith(ctx, record.ID, "second", nil, 0, 0, Send{Digest: "second"}); err != nil {
t.Fatal(err)
}
if sent, _, err := inv.SentTo(ctx, node); err != nil || sent.Provisioned != nil {
t.Fatalf("a send handing nothing says it handed %v (%v)", sent.Provisioned, err)
}
if mirror, err := inv.AssignmentsMirror(ctx); err != nil || strings.Contains(string(mirror[node+".dunst"]), "provisioning") {
t.Fatalf("dunst's assignment still carries a plan: %s (%v)", mirror[node+".dunst"], err)
} else if _, has := mirror[node+".notify"]; has {
t.Fatalf("a module the machine no longer runs is mirrored: %s", mirror[node+".notify"])
}
}
func mustJSON(t *testing.T, v any) string {
t.Helper()
raw, err := json.Marshal(v)
if err != nil {
t.Fatal(err)
}
return string(raw)
}