mesh-self-check reads a node's presence through the watchdogs client: a part's key is no node, a refused version is an error (ADR 0634 section 4; issue 1966)

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
2026-11-05 16:44:52 +01:00
parent 75959dfa25
commit 73eb3bd11c
3 changed files with 36 additions and 9 deletions
@@ -10,7 +10,7 @@ import (
"context"
"encoding/json"
"fmt"
watchdogs "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshwatchdogs"
watchdogsclient "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient"
assignerclient "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient"
placing "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner"
"net"
@@ -65,16 +65,22 @@ func (c *Checker) heard() (map[string]bool, error) {
}
out := map[string]bool{}
for _, k := range keys {
node, ok := watchdogs.ParsePresenceByNodeKey(k)
if !ok || node == "" {
node, _, _, err := watchdogsclient.ParsePresenceKey(k)
if err != nil || node == "" { // a part's key is no node's, and a key of no form is not read
continue
}
raw, ok, err := c.Store.Get(PresenceRead, k)
if err != nil {
return nil, fmt.Errorf("the presence of %s cannot be read: %w", node, err)
}
var p watchdogs.PresenceByNodeValue
if ok && json.Unmarshal(raw, &p) == nil && p.Online {
if !ok {
continue
}
p, err := watchdogsclient.DecodeNodePresence(raw)
if err != nil {
return nil, fmt.Errorf("the presence of %s is not read: %w", node, err)
}
if p.Online {
out[node] = true
}
}
@@ -6,7 +6,7 @@ import (
"errors"
engineclient "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient"
nodeseat "git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
watchdogs "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshwatchdogs"
watchdogsclient "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient"
assignerclient "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient"
placing "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner"
catalogclient "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient"
@@ -269,7 +269,7 @@ func machines() *memStore {
pl.Modules = []placing.PlacedModule{{Module: "sshd"}} // a module key beside the node key
}
place(s, pl)
_ = s.Put(PresenceRead, p.node, watchdogs.PresenceByNodeValue{V: 1, Online: p.online})
_ = s.Put(PresenceRead, p.node, watchdogsclient.NodePresence{V: 1, Online: p.online})
key, _ := assignerclient.SeatHoldersKey(BannedSeat, p.node)
_ = s.Put(SeatHoldersRead, key, placing.SeatHolders{V: 1, Seat: BannedSeat, Scope: "node", Node: p.node,
Holders: []placing.SeatHoldersHoldersItem{{Module: "fail2ban", Node: p.node}}})
@@ -438,7 +438,7 @@ func (r *dataRig) holder(node string) {
key, _ := assignerclient.SeatHoldersKey("node-backup", node)
_ = r.s.Put(SeatHoldersRead, key, placing.SeatHolders{V: 1, Seat: "node-backup", Scope: "node", Node: node,
Holders: []placing.SeatHoldersHoldersItem{{Module: "restic", Node: node}}})
_ = r.s.Put(PresenceRead, node, watchdogs.PresenceByNodeValue{V: 1, Online: true})
_ = r.s.Put(PresenceRead, node, watchdogsclient.NodePresence{V: 1, Online: true})
}
// dataMesh builds the mesh: pg declares data (backed up within 48 h, written all the time within 24 h) and dumps; plex
@@ -485,3 +485,24 @@ func dataMesh(change func(*dataRig)) *memStore {
}
return r.s
}
// TestPresenceIsReadThroughTheWatchdogsClient holds the node's presence to the client's codec (ADR 0634 section 4): a
// part's key (`part.<part>.<instance>`) is no node, and a value of a version the client refuses is a refusal, not a
// node that is offline.
func TestPresenceIsReadThroughTheWatchdogsClient(t *testing.T) {
s := newStore()
_ = s.Put(PresenceRead, "ace", watchdogsclient.NodePresence{V: 1, Online: true})
_ = s.Put(PresenceRead, "part.watchman.1", watchdogsclient.PartPresence{V: 1, Online: true})
c := &Checker{Store: s}
heard, err := c.heard()
if err != nil {
t.Fatal(err)
}
if len(heard) != 1 || !heard["ace"] {
t.Errorf("heard %v, want only ace: a part's presence is no node's", heard)
}
_ = s.Put(PresenceRead, "g14", watchdogsclient.NodePresence{V: 99, Online: true})
if _, err := c.heard(); err == nil {
t.Error("a presence of a version the client refuses was read as a node's")
}
}
+1 -1
View File
@@ -5,7 +5,7 @@ go 1.26.0
require (
git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshwatchdogs v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-contracts/go/seats/nodeengine v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000