hub v0.179.1: a host boot ends "the host needs a restart" at once (demo-felhom 2026-11-08)
gates / gates (push) Successful in 2m3s
gates / gates (push) Successful in 2m3s
The host layer's reboot-needed run is cut at the host's last boot (crash guard last_boot_at, one minute skew margin): the kernel step's reboot is reported on the kernel layer, so the flag outlived it until the next night. Feeds the System page and the 14-day alarm. Test TestRebootNeeded_ClearedByAHostBootAfterTheClaim, red on two mutants. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
1 parent
a0d8057634
commit
ddbcabebb4
3 files changed
+135
-6
No files matched your search
@@ -328,3 +328,60 @@ func TestRebootNeeded_ClearedByAScannedPass(t *testing.T) {
|
||||
t.Fatal("reboot alarm after the reboot")
|
||||
}
|
||||
}
|
||||
|
||||
// bootReport stores a host report whose crash guard says the host last booted at `boot` (the agent's
|
||||
// system.facts.host.crash_guard.last_boot_at, UTC ISO).
|
||||
func bootReport(t *testing.T, f *fix, host string, boot time.Time) {
|
||||
t.Helper()
|
||||
h, err := f.s.Store.GetHost(host)
|
||||
if err != nil || h == nil {
|
||||
t.Fatalf("no host %s", host)
|
||||
}
|
||||
body := `{"system":{"pve_version":"pve-manager/9.2.2/x","facts":{"host":{"crash_guard":{"last_boot_at":"` +
|
||||
boot.UTC().Format("2006-01-02T15:04:05Z") + `"}}}}}`
|
||||
if err := f.s.Store.SaveHostReport(host, h.CustomerID, []byte(body), store.HostReportDenorm{AgentVersion: "0.172.1"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// 2026-11-08, demo-felhom: the night's host pass said reboot-needed at 05:31, the kernel step rebooted the box at
|
||||
// 05:35, and the System page kept „the host needs a restart" until the next night's host pass — the reboot is
|
||||
// reported on the KERNEL layer, never the host layer. A host boot AFTER the newest claim ends the need, on the page
|
||||
// AND for the 14-day alarm. A boot BEFORE the claim (or within the margin) does not. Red-proof: drop the boot check
|
||||
// in layerLine and the first assertion fails.
|
||||
func TestRebootNeeded_ClearedByAHostBootAfterTheClaim(t *testing.T) {
|
||||
f := newFix(t)
|
||||
if _, err := f.s.Store.GetHost("hp"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f.ingest(t, "hp", Report{Layer: LayerHost, Trigger: "night", Mode: "apply", Outcome: "applied", Healthy: true,
|
||||
Upgraded: []Package{pk("libc6", "2")}, RebootNeeded: true, RebootScanned: true})
|
||||
claim := f.now
|
||||
// A boot BEFORE the claim says nothing: the need stays.
|
||||
bootReport(t, f, "hp", claim.Add(-time.Hour))
|
||||
if l := f.s.layerLine("hp", LayerHost, 0); l.RebootNeededSince.IsZero() {
|
||||
t.Fatal("a boot before the claim cleared the need")
|
||||
}
|
||||
// A boot within the margin after the claim (clock skew) says nothing either.
|
||||
bootReport(t, f, "hp", claim.Add(30*time.Second))
|
||||
if l := f.s.layerLine("hp", LayerHost, 0); l.RebootNeededSince.IsZero() {
|
||||
t.Fatal("a boot inside the skew margin cleared the need")
|
||||
}
|
||||
// The kernel step's reboot, 4 minutes after the claim: the need is over.
|
||||
f.now = f.now.Add(10 * time.Minute)
|
||||
bootReport(t, f, "hp", claim.Add(4*time.Minute))
|
||||
if l := f.s.layerLine("hp", LayerHost, 0); !l.RebootNeededSince.IsZero() {
|
||||
t.Fatalf("a host boot after the claim did not clear the need: since %v", l.RebootNeededSince)
|
||||
}
|
||||
f.now = f.now.Add(20 * 24 * time.Hour)
|
||||
f.s.Alarms()
|
||||
if f.count(EventRebootNeeded) != 0 {
|
||||
t.Fatal("reboot alarm after the host rebooted")
|
||||
}
|
||||
// A NEW claim after that boot (the next night installs more) starts the need again.
|
||||
f.ingest(t, "hp", Report{Layer: LayerHost, Trigger: "night", Mode: "apply", Outcome: "applied", Healthy: true,
|
||||
Upgraded: []Package{pk("libc6", "3")}, RebootNeeded: true, RebootScanned: true})
|
||||
if l := f.s.layerLine("hp", LayerHost, 0); !l.RebootNeededSince.Equal(f.now) {
|
||||
t.Fatalf("a claim after the boot: since = %v, want %v", l.RebootNeededSince, f.now)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user