v0.119.0 — the host report carries the box's addresses
A managed box's IP was invisible in every operator surface because nothing reported one: HostMetrics carried node/cpu/mem/disk/load/uptime/temp/wrapper-sha and no address of any kind. The hub could not show a host's LAN IP anywhere. Two things that looked like the answer are traps, both checked before writing code: lan_resolver.host_ip is an OPTIONAL config value absent unless that feature is configured, and DeriveHostIP(local_api.listen_addr) returns 169.254.253.1 — since R-50 the local API binds a link-local address identical on every box. Both would have produced a confident wrong answer. New wire field addresses[], one entry per (interface, address). Deliberately iface+cidr rather than a single lan_ip: a Proxmox host legitimately holds several (management bridge, tailnet, WG tunnel) and picking one to call "the" LAN IP is a guess the agent is not entitled to make — silently wrong on a box whose bridge is not vmbr0. The agent reports what exists; the hub does the labelling. The filter is one predicate, chosen by MEASURING both demo hosts rather than by reasoning about interface names. IsGlobalUnicast() alone drops loopback, IPv6 link-local (one per bridge, pure noise) and IPv4 link-local (169.254/16 — exactly the island address above). It needs no veth/fwbr/tap denylist: that per-guest plumbing carries no IP at all and self-excludes, verified on both boxes. No new privilege and no block I/O — net.Interfaces() is a netlink/procfs read, so the sudoers fence is untouched and the health-check rule is honoured. The seam DEFAULTS to the real enumerator, inverting the nil-reporter-means-off convention: this stanza has no config gate, so a forgotten wiring call would have shipped it silently empty — the inert-seam failure recorded four times here. Cross-repo: the golden is duplicated byte-identically in felhom.eu and the contract test fails on top-level key drift, so both goldens moved together and addresses[0]'s key set is asserted bidirectionally. The field marshals as [], never null — the repo's own no-nulls invariant caught that on the first run. Tests +9; three red-proofs (global-unicast filter, down-interface guard, inert collectAddresses) each run, observed failing, and reverted.
This commit is contained in:
@@ -80,6 +80,9 @@ func TestHostReport_ContractMatchesGolden(t *testing.T) {
|
||||
},
|
||||
AuditTail: []AuditEntry{},
|
||||
Cloudflared: Cloudflared{Status: "active"},
|
||||
// v0.119.0: host addresses. Populated so the bidirectional key-set guard exercises the new
|
||||
// element keys, not just the presence of the array.
|
||||
Addresses: []HostAddress{{Iface: "vmbr0", CIDR: "192.168.0.162/24"}},
|
||||
}
|
||||
// dr_recipe host-half: built from the same guest/storage/pbs facts (the production path).
|
||||
report.DRRecipe = BuildDRRecipeHostHalf(report.Guests, report.StorageTargets, report.PBSSnapshots,
|
||||
@@ -106,6 +109,9 @@ func TestHostReport_ContractMatchesGolden(t *testing.T) {
|
||||
assertSameKeys(t, "restore_tests[0]", firstElem(golden["restore_tests"]), firstElem(got["restore_tests"]))
|
||||
// slice-6-Phase-B addition — pbs_snapshots[0] key set.
|
||||
assertSameKeys(t, "pbs_snapshots[0]", firstElem(golden["pbs_snapshots"]), firstElem(got["pbs_snapshots"]))
|
||||
// v0.119.0 addition — addresses[0] key set (iface/cidr), the cross-repo wire for the hub's
|
||||
// Network card.
|
||||
assertSameKeys(t, "addresses[0]", firstElem(golden["addresses"]), firstElem(got["addresses"]))
|
||||
|
||||
// DR-recipe host-half — the agent's secret-free reconstruction-scaffolding section. Assert the
|
||||
// dr_recipe key set + each sub-array's element key set (the cross-repo wire pinned in the golden).
|
||||
|
||||
Reference in New Issue
Block a user