Files
felhom-agent/REPORT.md
T

4.1 KiB

REPORT — agent v0.84.0: ReassertNetworkMounts — NAS automount survives guest reboots (RCA fix 1)

Date: 2026-07-11 · Commit: 474b858 · Deployed: felhom-pve (backup .bak-0.83.0) · NOT published (Peti stays 0.81.0; publish-train queue now carries 0.84.0) Source: felhom.eu/documentation/audits/AUDIT-nas-cwa-rca-2026-07-11.md (fix 1); pairs with controller v0.117.0 (fixes 2+4).

Baseline (fresh-verified at session start)

Agent 0.83.0 live on felhom-pve; controller 0.116.1 on 9201; nas-media active-mounted; CWA healthy.

What shipped

  • internal/storage/netreassert.go ReassertNetworkAutomounts — the §8 decision table: real nfs/nfs4/cifs mounted → skip (fresh namespaces inherit real mounts); autofs trigger → systemctl stop -- <unit>.automount + enable --now -- <unit>.automount (existing FELHOM_NETMOUNT verbs — the §3 sudoers fallback was NOT needed, see equivalence below); neither → skip (orphan states owned by add/remove). Idempotent; per-share errors never stop the pass. networkUnitEntries() factored out of ListNetworkMounts (behavior unchanged).
  • internal/localapi/netreassert.go Server.ReassertNetworkMounts — daemon leg: host-global pass once, then per-RUNNING-guest GuestSeesMount verify (WARN-only). Type-asserted capability (lean NetworkStorageOps + fakes untouched). Wired at startup after ReassertGuestBinds; deliberately NOT in the 20 s ticker (idle trigger = healthy, never churned).
  • internal/guesthook/netreassert.go + PhasePostStart — hook leg: PVE runs the hookscript as root → direct systemctl; GuestSeesPath verify (hook-process mirror of GuestSeesMount); non-fatal by contract (stderr → PVE task log, exit 0 always, 30 s bound). The installed wrapper already forwards all phases — no snippet re-install was needed.

Tests + red-proofs

Test Result Red-proof
§8 decision table (TestNetReassertAction_Table) PASS always-rearm shape → FAIL netReassertAction("nfs") = "rearmed", want "skip-active" (the live-mount churn)
rearm emits EXACTLY stop+enable-now on the right unit PASS (linux) covered by the table red-proof (same predicate)
active mount → ZERO systemctl calls PASS (linux) the always-rearm shape fails it with 2 recorded calls
idempotent double-pass; zero-units no-op PASS (linux)
hook wiring (TestRunGuestHook_PostStartInvokesNetworkReassert) PASS PhasePostStart case removed → FAIL got []
hook core verifies rearmed+active rows only; verify-failure non-fatal PASS
daemon leg verifies RUNNING guests only; invisible share = WARN-only PASS

Full linux suite on 180: clean. Windows suite: clean (unit-name tests skip by design).

Deploy + live validation

  • Installed on felhom-pve 21:16; felhom-agent 0.84.0; journal clean.
  • Equivalence (spike gate §3, live): the startup sweep found nas-media idle → re-armed via stop+enable-now → guest visibility retained (no WARN) → stop+enable-now ≡ restart proven; no sudoers widening.
  • Hook leg, real entry: felhom-agent guest-hook 9201 post-start as root → exit 0, network share nas-media visible in guest (skip-active).
  • 🛑 Q1c reboot (operator GO, 21:20 pct reboot 9201): the start-task transcript itself carried netreassert: automount trigger re-armed + guest-hook: vmid 9201 post-start — network share nas-media visible in guest (rearmed). Post-boot, NO manual action: guest had trigger + nfs4; calibre-web auto-started healthy on the NAS library (the RCA's 4-hour silent death is gone); uid-1000 write-through WRITE-OK; controller health status=ok first cycle; no storage badge. Q1c GREEN.

Observations / follow-ups

  • Sudoers fallback not needed (record per §3: stop+enable-now re-propagates identically).
  • RCA fixes 3 (boot-failure alerting), 5 (label mojibake), 6 (ring spam) remain queued as separate tasks.
  • Peti publish-train queue: agent 0.84.0 + controller 0.117.0 + journal-group one-liner + temp Git Sync creds deletion.