Delivery 1 wrote no commit on the key of a pulled-in module, so after the upgrade those modules fell out of SentBuilds. At the start of the lease holder a copy fills every key that names no commit (creating a standing pulled-in key where none stands), overwrites nothing, empties each column it copied, and raises an urgent condition while it fails. Migration 0105 no longer drops node.sent_builds (issue 1112 does).
91 lines
4.1 KiB
Go
91 lines
4.1 KiB
Go
package inventory
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/novox/mesh-controller/internal/chain"
|
|
)
|
|
|
|
// **What delivery 1 recorded in node.sent_builds is copied onto the keys of the assignments, once** (issue 1109, review
|
|
// round 1): a module pulled in by an assignment had no commit on its key, so it would fall out of SentBuilds. The copy
|
|
// creates a standing pulled-in key where none stands, fills a standing key that names no commit (commit, and the build
|
|
// when one is known, no rollout), and overwrites nothing: a key with a commit, a hand send's unknown, a wait for a build.
|
|
func TestWhatTheColumnRecordedIsCopiedOntoTheKeysAndOverwritesNothing(t *testing.T) {
|
|
inv, c := anInventoryOnTheChain(t)
|
|
ctx := t.Context()
|
|
at := time.Date(2026, 10, 18, 4, 0, 0, 0, time.UTC)
|
|
if _, err := c.Builds.Create(ctx, "b-wg", chain.Build{ID: "b-wg", Module: "mesh-wireguard", Commit: "c-wg"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
put := func(module string, a chain.Assignment) {
|
|
a.Node, a.Module = "laptop", module
|
|
if _, err := c.Assignments.Create(ctx, assignmentKey("laptop", module), a); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
put("notes", chain.Assignment{Assigned: at}) // assigned, no commit yet
|
|
put("web", chain.Assignment{Assigned: at, Commit: "c-web-new", Build: "b1"}) // already has one
|
|
put("hand", chain.Assignment{Assigned: at, SentUnknown: at}) // a hand send since
|
|
put("waits", chain.Assignment{Assigned: at, State: chain.StatePending}) // a wait for a build
|
|
put("ended", chain.Assignment{Assigned: at, Ended: at}) // unassigned
|
|
sent := map[string]string{"notes": "c-notes", "web": "c-web-old", "mesh-wireguard": "c-wg", "hand": "c-hand",
|
|
"waits": "c-waits", "ended": "c-ended"}
|
|
|
|
for pass := 0; pass < 2; pass++ { // and the second pass changes nothing
|
|
if err := inv.copySentBuildsOf(ctx, c, "laptop", sent, at); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
get := func(module string) chain.Assignment {
|
|
t.Helper()
|
|
k, found, err := c.Assignments.Get(ctx, assignmentKey("laptop", module))
|
|
if err != nil || !found {
|
|
t.Fatalf("%s: %v %v", module, found, err)
|
|
}
|
|
return k.Value
|
|
}
|
|
if a := get("notes"); a.Commit != "c-notes" || a.Rollout != "" || !a.Stands() || a.PulledIn {
|
|
t.Errorf("notes: %+v", a)
|
|
}
|
|
if a := get("web"); a.Commit != "c-web-new" || a.Build != "b1" {
|
|
t.Errorf("a key with a commit was overwritten: %+v", a)
|
|
}
|
|
if a := get("hand"); a.Commit != "" || a.SentUnknown.IsZero() {
|
|
t.Errorf("a hand send's unknown was overwritten: %+v", a)
|
|
}
|
|
if a := get("waits"); a.Commit != "" || a.State != chain.StatePending {
|
|
t.Errorf("a wait was overwritten: %+v", a)
|
|
}
|
|
if a := get("mesh-wireguard"); a.Commit != "c-wg" || a.Build != "b-wg" || !a.PulledIn || !a.Stands() || a.Rollout != "" {
|
|
t.Errorf("the pulled-in module has no standing key of its own: %+v", a)
|
|
}
|
|
if a := get("ended"); a.Commit != "c-ended" || !a.Stands() || !a.PulledIn {
|
|
t.Errorf("an ended key is not made to stand again as pulled in: %+v", a)
|
|
}
|
|
}
|
|
}
|
|
|
|
// The copy reads the column of the store, writes the keys, and empties the column it has copied: SentBuilds then answers the
|
|
// pulled-in module, and a second copy finds nothing to do.
|
|
func TestTheColumnOfASentMachineIsCopiedOntoItsKeysAndEmptied(t *testing.T) {
|
|
inv := ForTest(t)
|
|
ctx := t.Context()
|
|
if _, err := inv.AddNode(ctx, "laptop"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
inv.ExecForTest(t, `update node set sent_builds = '{"mesh-wireguard": "c-wg"}'::jsonb, sent_at = now() where name = 'laptop'`)
|
|
if n, err := inv.CopySentBuilds(ctx); err != nil || n != 1 {
|
|
t.Fatalf("the copy did %d nodes (%v)", n, err)
|
|
}
|
|
got, known, err := inv.SentBuilds(ctx, "laptop")
|
|
if err != nil || !known || got["mesh-wireguard"] != "c-wg" {
|
|
t.Fatalf("SentBuilds answers %v, known %v (%v)", got, known, err)
|
|
}
|
|
if rows := inv.QueryForTest(t, `select count(*) from node where sent_builds is not null`); rows[0][0] != "0" {
|
|
t.Fatalf("the column was not emptied: %v", rows)
|
|
}
|
|
if n, err := inv.CopySentBuilds(ctx); err != nil || n != 0 {
|
|
t.Fatalf("a second copy did %d nodes (%v)", n, err)
|
|
}
|
|
}
|