Read the seat mesh-placement's state through the sdk's PlacementKeys in mesh-conditions, mesh-config, mesh-provisioner, mesh-vault and mesh-self-check, so they read its keys per module and node and the form before alike while it is delivered (issue 774, research 072 row 37)

This commit is contained in:
2026-10-31 09:27:15 +01:00
parent 21fddffc6e
commit 85c336f260
23 changed files with 235 additions and 150 deletions
@@ -216,26 +216,26 @@ func (k *Keeper) Have(key string, value []byte, deleted bool) error {
return k.judge(node, module, "")
}
// Placement takes one change of the placement: which module holds which seat on a node.
// Placement takes one change of the placement: which module holds which seat on a node. The keys are read in either
// form, a node key and its module keys or the form before (delivery.PlacementKeys; research 072 row 37).
func (k *Keeper) Placement(key string, value []byte, deleted bool) {
node, err := delivery.ParsePlacementKey(key)
if err != nil {
return
}
k.mu.Lock()
defer k.mu.Unlock()
k.init()
node, err := k.placement.Put(key, value, 0, deleted)
if node == "" {
return
}
if err != nil {
k.Logf("the placement of %s is not readable as it stands, so its seats' holders are kept as last read: %v", node, err)
}
for hk := range k.holders {
if strings.HasPrefix(hk, node+".") {
delete(k.holders, hk)
}
}
if deleted {
return
}
p, err := delivery.DecodePlacement(value)
if err != nil {
k.Logf("the placement of %s is not readable, so its seats' holders are not known: %v", node, err)
p, _, ok := k.placement.Of(node)
if !ok {
return
}
for _, h := range p.Holds {
@@ -113,12 +113,19 @@ func TestADeliveryHoldsItsOwnFindingsUntilItEnds(t *testing.T) {
// A seat's finding is about the module the placement says holds the seat on that node (the lead, 2026-10-29): held
// while the assigner is delivered on ace, though the seat is named mesh-placement.
r := newRig()
placed, err := delivery.EncodePlacement(delivery.Placement{Node: "ace", Modules: []delivery.PlacedModule{{Module: "mesh-delivery-assigner"}},
// Research 072 row 37: the placement in its keys per module and node; a stale legacy key beside them is not read.
stale, err := delivery.EncodePlacement(delivery.Placement{Node: "ace"})
if err != nil {
t.Fatal(err)
}
r.k.Placement("placement.ace", stale, false)
placed, err := delivery.PlacementValues(delivery.Placement{Node: "ace", Modules: []delivery.PlacedModule{{Module: "mesh-delivery-assigner"}},
Holds: []delivery.Holding{{Claim: "mesh-placement", Scope: "mesh", Module: "mesh-delivery-assigner"}}})
if err != nil {
t.Fatal(err)
}
r.k.Placement("placement.ace", placed, false)
r.k.Placement("ace.mesh-delivery-assigner", placed["ace.mesh-delivery-assigner"], false)
r.k.Placement("ace.node", placed["ace.node"], false)
_ = r.k.Want("want.ace.mesh-delivery-assigner", want(ctrlNext, ctrlNextComp), false)
_ = r.k.Have("ace.mesh-delivery-assigner", have(ctrlRunning, ctrlComposed, delivery.PhaseRunning, delivery.HealthOK), false)
seat, seatRaw := finding(t, selfCheck, health.ScopeSeat, "mesh-placement.ace", "holder-silent", "D3", health.Warning, r.now)
@@ -112,8 +112,10 @@ type Keeper struct {
haves map[string]delivery.Have
// holders are, by `<node>.<seat>`, the module the placement says holds the seat there.
holders map[string]string
stuck map[string]string
held map[string]held
// placement is the placement's keys as heard, assembled per node.
placement *delivery.PlacementKeys
stuck map[string]string
held map[string]held
// passed are the deliveries whose bound passed, by node and module, at the fingerprint wanted then; outOfHold the
// line a key raised out of a hold says (hold.go).
passed map[string]string
@@ -130,6 +132,7 @@ func (k *Keeper) init() {
map[string]string{}, map[string]held{}, map[string]clearedMemo{}
k.takeover = health.Takeover{V: health.TakeoverVersion, Follow: health.FollowController}
k.passed, k.outOfHold, k.holders = map[string]string{}, map[string]string{}, map[string]string{}
k.placement = delivery.NewPlacementKeys()
}
if k.Now == nil {
k.Now = time.Now
+1 -1
View File
@@ -3,7 +3,7 @@ module mesh-conditions
go 1.26.0
require (
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030001116-2da0de75c3f0
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261031082617-a320f8e2cd77
github.com/nats-io/nats.go v1.54.0
)
+2 -2
View File
@@ -1,5 +1,5 @@
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030001116-2da0de75c3f0 h1:lKU3HjNXGiVbLW2hUgFjU/TMTUYMlNnnmZxG5T7biQA=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030001116-2da0de75c3f0/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261031082617-a320f8e2cd77 h1:vMZJm3lVjq7V4SqlSMzteUokziozuoUxws8DakfbgNA=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261031082617-a320f8e2cd77/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
@@ -134,34 +134,58 @@ func (m *memStore) unassign(t *testing.T, node, module string) {
m.place(t, node, module, false)
}
// place writes a node's placement as the seat writes it: its node key and module keys (research 072 row 37), and for
// ace in the form before, `placement.ace`, which a reader still takes while the change is delivered.
func (m *memStore) place(t *testing.T, node, module string, on bool) {
t.Helper()
type placed struct {
Module string `json:"module"`
keys := map[string][]byte{}
for _, k := range m.keysOf(PlacementRead) {
e, _ := m.Get(PlacementRead, k)
keys[k] = e.Value
}
p := struct {
V int `json:"v"`
Node string `json:"node"`
Modules []placed `json:"modules"`
}{V: 1, Node: node}
if e, _ := m.Get(PlacementRead, "placement."+node); e != nil {
if err := json.Unmarshal(e.Value, &p); err != nil {
t.Fatal(err)
}
all, err := delivery.PlacementsOf(keys)
if err != nil {
t.Fatal(err)
}
var kept []placed
p := all[node]
p.Node = node
var kept []delivery.PlacedModule
for _, x := range p.Modules {
if x.Module != module {
kept = append(kept, x)
}
}
if on {
kept = append(kept, placed{Module: module})
kept = append(kept, delivery.PlacedModule{Module: module})
}
p.Modules = kept
if err := m.Put(PlacementRead, "placement."+node, p); err != nil {
for _, k := range m.keysOf(PlacementRead) {
if n, _, _, err := delivery.ParsePlacementKey(k); err == nil && n == node {
_ = m.Delete(PlacementRead, k)
}
}
if node == "ace" {
raw, err := delivery.EncodePlacement(p)
if err != nil {
t.Fatal(err)
}
_ = m.Put(PlacementRead, "placement.ace", json.RawMessage(raw))
return
}
values, err := delivery.PlacementValues(p)
if err != nil {
t.Fatal(err)
}
for k, raw := range values {
if err := m.Put(PlacementRead, k, json.RawMessage(raw)); err != nil {
t.Fatal(err)
}
}
}
func (m *memStore) keysOf(state string) []string {
keys, _ := m.Keys(state)
return keys
}
// manifest keeps a manifest in the indexer's manifests by its digest, as the indexer writes it, and answers the digest.
+24 -24
View File
@@ -70,32 +70,32 @@ func (c *Config) sourcesFrom(source string) (Sources, error) {
return s, nil
}
// readPlacement is every module the seat mesh-placement places on a node (ADR 0567 §1, §9): each node's key
// `placement.<node>`.
// readPlacement is every module the seat mesh-placement places on a node (ADR 0567 §1, §9), from the state's keys in
// either form: a node key and its module keys, or the form before, `placement.<node>` (delivery.PlacementsOf;
// research 072 row 37). By node in name order, each node's modules in the order its placement names them.
func (c *Config) readPlacement(s *Sources) error {
placed := map[string]bool{}
return c.each(PlacementRead, func(key string, raw []byte) error {
node, err := delivery.ParsePlacementKey(key)
if err != nil {
return nil // a key of another kind
}
var p struct {
Node string `json:"node"`
Modules []struct {
Module string `json:"module"`
} `json:"modules"`
}
if err := json.Unmarshal(raw, &p); err != nil || p.Node != node {
return fmt.Errorf("the placement %s is not one", key)
}
for _, m := range p.Modules {
if m.Module != "" && !placed[node+"."+m.Module] {
placed[node+"."+m.Module] = true
s.Assignments = append(s.Assignments, Assignment{Node: node, Module: m.Module})
}
}
keys := map[string][]byte{}
if err := c.each(PlacementRead, func(key string, raw []byte) error {
keys[key] = raw
return nil
})
}); err != nil {
return err
}
placements, err := delivery.PlacementsOf(keys)
if err != nil {
return fmt.Errorf("the placement is not read whole: %w", err)
}
nodes := make([]string, 0, len(placements))
for node := range placements {
nodes = append(nodes, node)
}
sort.Strings(nodes)
for _, node := range nodes {
for _, module := range placements[node].Assigned() {
s.Assignments = append(s.Assignments, Assignment{Node: node, Module: module})
}
}
return nil
}
// readDeclarations reads each placed module's declarations from its manifests as the delivery holds them: the trunk's
+1 -1
View File
@@ -2,7 +2,7 @@ module mesh-config
go 1.26.0
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030174137-a37181261c24
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261031082617-a320f8e2cd77
require (
github.com/klauspost/compress v1.20.0 // indirect
+2 -2
View File
@@ -1,5 +1,5 @@
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030174137-a37181261c24 h1:s0XXZtR7CFPBbnaDygWnE4/Q827iEFt0YfEzujk4Q9g=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030174137-a37181261c24/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261031082617-a320f8e2cd77 h1:vMZJm3lVjq7V4SqlSMzteUokziozuoUxws8DakfbgNA=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261031082617-a320f8e2cd77/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
@@ -903,9 +903,9 @@ func (h *Holder) put(key string, p provisions.Provision) {
// ---- what a change touches --------------------------------------------------------------------------------------
// NodesOfPlacement is the node a placement key names.
// NodesOfPlacement is the node a placement key names, in either form (research 072 row 37).
func NodesOfPlacement(key string) []string {
if n, err := delivery.ParsePlacementKey(key); err == nil {
if n, _, _, err := delivery.ParsePlacementKey(key); err == nil {
return []string{n}
}
return nil
@@ -938,25 +938,23 @@ func (h *Holder) NodesOfModule(module string) []string {
return sortedKeys(nodes)
}
// placedOn is every node a module is placed on, sorted.
// placedOn is every node a module is placed on, sorted, from the placement's keys in either form
// (delivery.PlacementsOf; research 072 row 37). A node whose keys do not read whole is read as they stand.
func (h *Holder) placedOn(module string) []string {
nodes := map[string]bool{}
raw := map[string][]byte{}
if keys, err := h.Store.Keys(PlacementRead); err == nil {
for _, k := range keys {
n, err := delivery.ParsePlacementKey(k)
if err != nil {
continue
if e, err := h.Store.Get(PlacementRead, k); err == nil && e != nil {
raw[k] = e.Value
}
e, err := h.Store.Get(PlacementRead, k)
if err != nil || e == nil {
continue
}
if p, err := delivery.DecodePlacement(e.Value); err == nil {
for _, m := range p.Modules {
if m.Module == module {
nodes[n] = true
}
}
}
}
placements, _ := delivery.PlacementsOf(raw)
for n, p := range placements {
for _, m := range p.Modules {
if m.Module == module {
nodes[n] = true
}
}
}
@@ -191,7 +191,17 @@ func TestAKeysStateFollowsItsProvidersOutcome(t *testing.T) {
// A provision's credential is the vault's own provision, on the consumer's node for a login of its own, and the
// provision depends on it (clauses 3, 6).
c := newMesh()
c.s.write(t, PlacementRead, "placement.novox", `{"v":1,"node":"novox","decided":{},"modules":[{"module":"mesh-vault"}]}`)
// The placement in its keys per module and node (research 072 row 37).
novox, err := delivery.PlacementValues(delivery.Placement{Node: "novox", Modules: []delivery.PlacedModule{{Module: "mesh-vault"}}})
if err != nil {
t.Fatal(err)
}
for k, v := range novox {
c.s.write(t, PlacementRead, k, string(v))
if n := NodesOfPlacement(k); !slices.Equal(n, []string{"novox"}) {
t.Errorf("NodesOfPlacement(%q) = %v", k, n)
}
}
pair := pgDB
pair.CredentialForm = "pair"
c.atWant["g14"] = map[string]catalogue.Plan{"gitea": planOf(pair)}
+1 -1
View File
@@ -3,7 +3,7 @@ module mesh-provisioner
go 1.26.0
require (
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030212912-6c5a8ef893b4
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261031082617-a320f8e2cd77
github.com/nats-io/nats.go v1.54.0
)
+2 -4
View File
@@ -1,7 +1,5 @@
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030182415-754edfae6924 h1:aIiKj066gYFEDokUOq0i2n4vsygu5g0XMKJRRaMI4fM=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030182415-754edfae6924/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030212912-6c5a8ef893b4 h1:uzXzKfjoWB8wCbAPgxUAWSArr+3DKwi8qrB3BUEOVOE=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030212912-6c5a8ef893b4/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261031082617-a320f8e2cd77 h1:vMZJm3lVjq7V4SqlSMzteUokziozuoUxws8DakfbgNA=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261031082617-a320f8e2cd77/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
@@ -31,29 +31,26 @@ const (
ScopeMachine = health.ScopeMachine
)
// placements is every node's placement, by node.
// placements is every node's placement, by node, from the state's keys in either form: a node key and its module keys,
// or the form before, `placement.<node>` (delivery.PlacementsOf; research 072 row 37).
func (c *Checker) placements() (map[string]delivery.Placement, error) {
keys, err := c.Store.Keys(PlacementRead)
if err != nil {
return nil, fmt.Errorf("the placement cannot be read: %w", err)
}
out := map[string]delivery.Placement{}
values := map[string][]byte{}
for _, k := range keys {
raw, ok, err := c.Store.Get(PlacementRead, k)
if err != nil {
return nil, fmt.Errorf("the placement of %s cannot be read: %w", k, err)
return nil, fmt.Errorf("the placement's key %s cannot be read: %w", k, err)
}
if !ok {
continue
if ok {
values[k] = raw
}
var p delivery.Placement
if err := json.Unmarshal(raw, &p); err != nil {
return nil, fmt.Errorf("the placement of %s is not readable: %w", k, err)
}
if p.Node == "" {
p.Node = k
}
out[p.Node] = p
}
out, err := delivery.PlacementsOf(values)
if err != nil {
return nil, fmt.Errorf("the placement is not readable: %w", err)
}
return out, nil
}
@@ -260,8 +260,12 @@ func machines() *memStore {
node, address, endpoint string
online bool
}{{"novox", "10.10.0.1/16", "203.0.113.5:51820", true}, {"ace", "10.10.0.2/16", "", true}, {"g14", "10.10.0.3/16", "", false}} {
_ = s.Put(PlacementRead, p.node, delivery.Placement{V: 1, Node: p.node,
Decided: delivery.NodeDecided{Address: p.address, Endpoint: p.endpoint, Hub: p.endpoint != ""}})
pl := delivery.Placement{V: 1, Node: p.node,
Decided: delivery.NodeDecided{Address: p.address, Endpoint: p.endpoint, Hub: p.endpoint != ""}}
if p.node == "ace" {
pl.Modules = []delivery.PlacedModule{{Module: "sshd"}} // a module key beside the node key
}
place(s, pl)
_ = s.Put(PresenceRead, p.node, delivery.Presence{V: 1, Online: p.online})
key, _ := delivery.SeatHoldersKey(BannedSeat, p.node)
_ = s.Put(SeatHoldersRead, key, delivery.SeatHolders{V: 1, Seat: BannedSeat, Scope: "node", Node: p.node,
@@ -270,6 +274,31 @@ func machines() *memStore {
return s
}
// place writes a node's placement as the seat does, its node key and a key per module (research 072 row 37); g14's in
// the form before, `placement.g14`, which the probes still read while the change is delivered.
func place(s *memStore, p delivery.Placement) {
if p.Node == "g14" {
raw, _ := delivery.EncodePlacement(p)
_ = s.Put(PlacementRead, "placement.g14", json.RawMessage(raw))
return
}
values, _ := delivery.PlacementValues(p)
for k, raw := range values {
_ = s.Put(PlacementRead, k, json.RawMessage(raw))
}
}
// placed is every node's placement the store holds.
func placed(s *memStore) map[string]delivery.Placement {
keys, _ := s.Keys(PlacementRead)
values := map[string][]byte{}
for _, k := range keys {
values[k], _, _ = s.Get(PlacementRead, k)
}
out, _ := delivery.PlacementsOf(values)
return out
}
// filling is a store holding the self-check's written fill finding of a stream: the condition open.
func filling(stream string) *memStore {
s := newStore()
@@ -285,11 +314,9 @@ func filling(stream string) *memStore {
func agents(verdicts map[string]string) *memStore {
s := machines()
for node, v := range verdicts {
raw, _, _ := s.Get(PlacementRead, node)
var p delivery.Placement
_ = json.Unmarshal(raw, &p)
p := placed(s)[node]
p.Decided.AgentAccount = "agent"
_ = s.Put(PlacementRead, node, p)
place(s, p)
h := delivery.NodeHealth{V: 1, Node: node, Contract: 13}
switch v {
case "":
@@ -303,7 +330,7 @@ func agents(verdicts map[string]string) *memStore {
h.Root = []delivery.RootVerdict{{Account: "agent", Module: "claude-code", Resource: "claude-code.agent", State: v,
Reason: "can become root without a person: a sudo rule"}}
}
raw, _ = delivery.EncodeNodeHealth(h)
raw, _ := delivery.EncodeNodeHealth(h)
_ = s.Put(HealthRead, node, json.RawMessage(raw))
}
return s
+1 -1
View File
@@ -3,7 +3,7 @@ module mesh-self-check
go 1.26.0
require (
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030001116-2da0de75c3f0
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261031082617-a320f8e2cd77
github.com/nats-io/nats.go v1.54.0
)
+2 -2
View File
@@ -1,5 +1,5 @@
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030001116-2da0de75c3f0 h1:lKU3HjNXGiVbLW2hUgFjU/TMTUYMlNnnmZxG5T7biQA=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030001116-2da0de75c3f0/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261031082617-a320f8e2cd77 h1:vMZJm3lVjq7V4SqlSMzteUokziozuoUxws8DakfbgNA=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261031082617-a320f8e2cd77/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
+48 -26
View File
@@ -25,7 +25,8 @@ type PlacementReader struct {
Read func(key string) ([]byte, error)
}
// placements is every node's placement, by node; none when the vault reads no placement.
// placements is every node's placement, by node, from the state's keys in either form (delivery.PlacementsOf; research
// 072 row 37); none when the vault reads no placement. A node whose keys do not read whole is read as they stand.
func (v *Vault) placements() (map[string]delivery.Placement, error) {
out := map[string]delivery.Placement{}
if v.Placement.Keys == nil || v.Placement.Read == nil {
@@ -35,52 +36,73 @@ func (v *Vault) placements() (map[string]delivery.Placement, error) {
if err != nil {
return nil, fmt.Errorf("the placement's keys could not be read: %w", err)
}
values := map[string][]byte{}
for _, k := range keys {
node, err := delivery.ParsePlacementKey(k)
if err != nil {
if _, _, _, err := delivery.ParsePlacementKey(k); err != nil {
continue
}
raw, err := v.Placement.Read(k)
if err != nil {
return nil, fmt.Errorf("the placement of %s could not be read: %w", node, err)
return nil, fmt.Errorf("the placement's key %s could not be read: %w", k, err)
}
if raw == nil {
continue
if raw != nil {
values[k] = raw
}
p, err := delivery.DecodePlacement(raw)
if err != nil || p.Node != node {
continue
}
out[node] = p
}
out, _ = delivery.PlacementsOf(values)
return out, nil
}
// nodePlaced is what node's own key of the placement says: the modules placed there and what a person decided of it,
// read from its node key `<node>.node` alone (which names every module placed, research 072 row 37), or, for a node not
// converted yet, from its key in the form before, `placement.<node>`. found is false when it has neither.
func (v *Vault) nodePlaced(node string) (modules []string, decided delivery.NodeDecided, found bool, err error) {
key, err := delivery.PlacementNodeKey(node)
if err != nil {
return nil, decided, false, err
}
raw, err := v.Placement.Read(key)
if err != nil {
return nil, decided, false, err
}
if raw != nil {
n, err := delivery.DecodePlacementNode(raw)
if err != nil {
return nil, decided, false, fmt.Errorf("the placement of %s is not readable: %w", node, err)
}
for _, r := range n.Modules {
modules = append(modules, r.Module)
}
return modules, n.Decided, true, nil
}
legacy, err := delivery.LegacyPlacementKey(node)
if err != nil {
return nil, decided, false, err
}
if raw, err = v.Placement.Read(legacy); err != nil || raw == nil {
return nil, decided, false, err
}
p, err := delivery.DecodePlacement(raw)
if err != nil {
return nil, decided, false, fmt.Errorf("the placement of %s is not readable: %w", node, err)
}
return p.Assigned(), p.Decided, true, nil
}
// placedOn says module is placed on node by the catalogue.
func (v *Vault) placedOn(node, module string) (bool, error) {
placed, err := v.placedModules(node)
return slices.Contains(placed, module), err
}
// placedModules is every module the catalogue places on node, read from the node's one key of the placement; none when
// the vault reads no placement or the node has no key.
// placedModules is every module the catalogue places on node, read from the node's own key of the placement
// (nodePlaced); none when the vault reads no placement or the node has no key.
func (v *Vault) placedModules(node string) ([]string, error) {
if v.Placement.Read == nil {
return nil, nil
}
key, err := delivery.PlacementKey(node)
if err != nil {
return nil, err
}
raw, err := v.Placement.Read(key)
if err != nil || raw == nil {
return nil, err
}
p, err := delivery.DecodePlacement(raw)
if err != nil {
return nil, fmt.Errorf("the placement of %s is not readable: %w", node, err)
}
return p.Assigned(), nil
modules, _, _, err := v.nodePlaced(node)
return modules, err
}
// standsOn says module stands on node: placed there, or assigned there while the assignments stand. A read that fails is
@@ -16,12 +16,14 @@ func placementsOf(t *testing.T, nodes map[string][]string) PlacementReader {
for _, m := range modules {
p.Modules = append(p.Modules, delivery.PlacedModule{Module: m})
}
raw, err := delivery.EncodePlacement(p)
// Research 072 row 37: the node key and a key per module.
kv, err := delivery.PlacementValues(p)
if err != nil {
t.Fatal(err)
}
key, _ := delivery.PlacementKey(node)
values[key] = raw
for k, raw := range kv {
values[k] = raw
}
}
return PlacementReader{
Keys: func() ([]string, error) {
+3 -11
View File
@@ -303,22 +303,14 @@ func (v *Vault) internalName(node string) (string, error) {
if v.Placement.Read == nil {
return "", errors.New("the vault reads no placement, so it cannot tell a node's internal name")
}
key, err := delivery.PlacementKey(node)
if err != nil {
return "", err
}
raw, err := v.Placement.Read(key)
_, decided, found, err := v.nodePlaced(node)
if err != nil {
return "", fmt.Errorf(notYetPrefix+"the placement of %s could not be read: %w", node, err)
}
if raw == nil {
if !found {
return "", fmt.Errorf("%s has no placement of the seat mesh-placement, so it has no name inside the mesh", node)
}
p, err := delivery.DecodePlacement(raw)
if err != nil {
return "", fmt.Errorf("the placement of %s is not readable: %w", node, err)
}
if p.Decided.Address == "" {
if decided.Address == "" {
return "", fmt.Errorf("%s is not on the private network (its placement names no address), so it has no name inside the mesh to be certified for", node)
}
return overlay.InternalName(node), nil
@@ -40,16 +40,21 @@ func TestTheVaultSigns(t *testing.T) {
"g14": {Node: "g14", Decided: delivery.NodeDecided{Address: "10.77.0.4"}},
"plain": {Node: "plain"},
}
// Research 072 row 37: ace and plain in their keys per module and node, g14 still in the form before.
v.Placement = PlacementReader{Read: func(key string) ([]byte, error) {
node, err := delivery.ParsePlacementKey(key)
node, _, kind, err := delivery.ParsePlacementKey(key)
if err != nil {
return nil, err
}
p, ok := placements[node]
if !ok {
if !ok || (node == "g14") != (kind == delivery.PlacementLegacyKind) {
return nil, nil
}
return delivery.EncodePlacement(p)
if kind == delivery.PlacementLegacyKind {
return delivery.EncodePlacement(p)
}
values, err := delivery.PlacementValues(p)
return values[key], err
}}
records := map[string]string{"ace": base64.StdEncoding.EncodeToString(servingPub), "plain": base64.StdEncoding.EncodeToString(servingPub)}
v.Haves = func(key string) ([]byte, error) {
+1 -1
View File
@@ -3,7 +3,7 @@ module mesh-vault
go 1.26.0
require (
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030225009-17fc9b9b8115
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261031082617-a320f8e2cd77
github.com/nats-io/nats-server/v2 v2.11.17
github.com/nats-io/nats.go v1.54.0
golang.org/x/crypto v0.57.0
+2 -2
View File
@@ -1,5 +1,5 @@
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030225009-17fc9b9b8115 h1:ezKg8aAKQoZ5XEAWQQVk4y/x0h2qC73GcUKBmbqY5jw=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030225009-17fc9b9b8115/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261031082617-a320f8e2cd77 h1:vMZJm3lVjq7V4SqlSMzteUokziozuoUxws8DakfbgNA=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261031082617-a320f8e2cd77/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=