From 6eae0765f4d3eca42fd04d74a2dbed6e2f7e3fc9 Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 29 Oct 2026 23:02:54 +0100 Subject: [PATCH] Name the machines a failing network part points at, so the watchman holds it there as the controller does without any machine's address (issue 774) NetworkPart.Targets: the hub's machine for "hub", the machine whose tunnel address is each resolver's; empty where the part is the machine's own. Filled by the node-engine from its tunnel's peers. Test: a row in TestTheControlRecordsReadTheirWritersValues, read back byte-equal. --- go/delivery/control_records_test.go | 7 ++++++- go/delivery/nodehealth.go | 12 +++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/go/delivery/control_records_test.go b/go/delivery/control_records_test.go index 1d21efa..c43aeef 100644 --- a/go/delivery/control_records_test.go +++ b/go/delivery/control_records_test.go @@ -2,6 +2,7 @@ package delivery import ( "encoding/json" + "strings" "testing" "time" ) @@ -31,6 +32,10 @@ func TestTheControlRecordsReadTheirWritersValues(t *testing.T) { `"relogin":[{"account":"jochen","module":"brother","resource":"brother.scanner","reason":"relogin needed: the session began before scanner","since":"2026-10-29T20:00:00Z"}],` + `"found":[{"module":"photos","resource":"photos.library","path":"/srv/photos","reason":"used as found: owned by root","since":"2026-10-29T20:00:00Z"}],` + `"at":"2026-10-29T20:00:00Z"}`, &NodeHealth{}}, + // A part pointing at another machine names it (issue 774): the hub's tunnel, held at the hub's machine. + {"a node's health report, a part pointing at the hub", `{"v":1,"node":"anchor","network":{"state":"unhealthy",` + + `"since":"2026-10-29T20:00:00Z","parts":[{"part":"tunnel","state":"unhealthy","reason":"the tunnel to the hub has not handshaken for over five minutes",` + + `"toward":["hub"],"targets":["novox"],"since":"2026-10-29T20:00:00Z"}]},"at":"2026-10-29T20:00:00Z"}`, &NodeHealth{}}, {"the predefined list", `{"v":1,"commit":"abc","at":"2026-10-29T12:00:00Z","list":{"domain":{"type":"string"}}}`, &PredefinedKept{}}, } { if err := json.Unmarshal([]byte(c.raw), c.into); err != nil { @@ -40,7 +45,7 @@ func TestTheControlRecordsReadTheirWritersValues(t *testing.T) { var want, got any _ = json.Unmarshal([]byte(c.raw), &want) _ = json.Unmarshal(again, &got) - if string(jsonOf(want)) != string(jsonOf(got)) || ((c.name == "declared packages" || c.name == "a node's health report") && string(again) != c.raw) { + if string(jsonOf(want)) != string(jsonOf(got)) || ((c.name == "declared packages" || strings.HasPrefix(c.name, "a node's health report")) && string(again) != c.raw) { t.Errorf("%s reads back as %s", c.name, again) } } diff --git a/go/delivery/nodehealth.go b/go/delivery/nodehealth.go index f41486a..b9928a3 100644 --- a/go/delivery/nodehealth.go +++ b/go/delivery/nodehealth.go @@ -63,9 +63,15 @@ type NetworkPart struct { Writer string `json:"writer,omitempty"` Owner string `json:"owner,omitempty"` // Toward is what the failure points at: "hub", or each resolver's address that failed. - Toward []string `json:"toward,omitempty"` - Since time.Time `json:"since"` - Streak int `json:"streak,omitempty"` + Toward []string `json:"toward,omitempty"` + // Targets are the mesh machines the failure points at, by node name, as the controller holds a part at the machine + // it points at: the hub's machine for "hub", and the machine whose tunnel address is each resolver's. Empty where the + // part is the machine's own: it points at nothing, at this machine, or at an address no other mesh machine has (the + // resolver a VPN client wrote in). Filled by the node-engine from its tunnel's peers, so the watchman needs no + // address of any machine (issue 774). + Targets []string `json:"targets,omitempty"` + Since time.Time `json:"since"` + Streak int `json:"streak,omitempty"` } // UnitsHealth is the machine's service managers (issue 315).