diff --git a/internal/link/have_test.go b/internal/link/have_test.go index caaa39fc..041bc536 100644 --- a/internal/link/have_test.go +++ b/internal/link/have_test.go @@ -334,7 +334,7 @@ func TestTheNodeKeyCarriesTheNodesFacts(t *testing.T) { t.Fatalf("the node key holds no record: %+v, %v", got, err) } wantRecord := nodeengine.NodeRecord{V: sdkkeys.Version, Node: "n1", TunnelKey: "tk", SealingKey: "sk", ServingKey: "pk", - Certificate: "cert", Trusts: keys.Trusts, Capabilities: []string{"apt", "bus-state-calls", "ledger-calls", "systemd"}, OutwardLinks: []string{"eth0"}, NetworksSaid: true, + Certificate: "cert", Trusts: keys.Trusts, Capabilities: []string{"apt", "bus-state-calls", "ledger-calls", "systemd", CapabilityWantParts}, OutwardLinks: []string{"eth0"}, NetworksSaid: true, Networks: []nodeengine.AttachedNetwork{{Link: "eth0", Network: "192.0.2.0/24"}}, Contract: NodeKeyContract, ManifestReads: catalogue.ManifestReads()} // what it reads of a manifest: its contract's (ADR 0614 §4) if !nodeengineclient.SameNodeRecord(*got.Record, wantRecord) { @@ -350,7 +350,7 @@ func TestTheNodeKeyCarriesTheNodesFacts(t *testing.T) { t.Errorf("a report changing nothing wrote the node key again") } // Design 47: the node's capabilities, said when they change, on capabilities-changed.. - if got := l.saidOn("mesh.seat.node-engine.event.capabilities-changed.n1 "); len(got) != 1 || !strings.Contains(got[0], `"capabilities":["apt","bus-state-calls","ledger-calls","systemd"]`) { + if got := l.saidOn("mesh.seat.node-engine.event.capabilities-changed.n1 "); len(got) != 1 || !strings.Contains(got[0], `"capabilities":["apt","bus-state-calls","ledger-calls","systemd","want-parts"]`) { t.Errorf("capabilities-changed said %v, want once for the record's capabilities", got) } // A runner that does not answer states what it stated before; one answering without any states none (an older runner). @@ -362,7 +362,7 @@ func TestTheNodeKeyCarriesTheNodesFacts(t *testing.T) { } l.statedErr, l.stated = nil, map[string]string{} q.SayFacts(context.Background(), rep) - if got, _ = nodeengineclient.DecodeHaveNode(l.kv["n1.node"].value); l.kv["n1.node"].rev != at+1 || !slices.Equal(got.Record.Capabilities, []string{"apt", "systemd"}) { + if got, _ = nodeengineclient.DecodeHaveNode(l.kv["n1.node"].value); l.kv["n1.node"].rev != at+1 || !slices.Equal(got.Record.Capabilities, []string{"apt", "systemd", "want-parts"}) { t.Errorf("a runner stating no calls: revision %d (was %d), capabilities %v", l.kv["n1.node"].rev, at, got.Record.Capabilities) } at++ diff --git a/internal/link/record.go b/internal/link/record.go index 6ce89833..bd93ea82 100644 --- a/internal/link/record.go +++ b/internal/link/record.go @@ -37,6 +37,10 @@ type RecordKeys struct { // buildReads are the words and forms this engine reads of a manifest (novox/hq ADR 0614 §4): the reader capabilities of // the mesh-sdk it is built with, its seats left out. Computed once: they are the build's, and never change while it runs. +// CapabilityWantParts is the capability a node-engine states in its node record when it reads the want's version, parts and +// kept-on (design 47 G §525); the assigner writes them into a node's want only while the record states it. +const CapabilityWantParts = "want-parts" + var buildReads = sync.OnceValue(func() []string { return slices.DeleteFunc(catalogue.ManifestReads(), func(r string) bool { return strings.HasPrefix(r, catalogue.ReadSeat) }) }) @@ -125,6 +129,8 @@ func recordOf(node string, r Report, keys RecordKeys) *nodeengine.NodeRecord { rec.Capabilities = append(rec.Capabilities, c.Name) } } + // A node-engine of G states that it reads the want's new fields: version, parts and where a kept build is kept (design 47 G §525). + rec.Capabilities = append(rec.Capabilities, CapabilityWantParts) for _, c := range keys.Stated { if c != "" && !slices.Contains(rec.Capabilities, c) { rec.Capabilities = append(rec.Capabilities, c)