hub v0.131.1: a scanned host pass (reboot_scanned, agent 0.141.1) clears 'reboot needed', so the 14-day alarm does not fire after a reboot; live evidence partB/partD/partG
gates / gates (push) Successful in 30s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-04 13:39:48 +02:00
parent 693f3b45aa
commit fd1f98547e
11 changed files with 835 additions and 2 deletions
@@ -0,0 +1,5 @@
11:34:43 container=running/healthy hub=running alarm=none
11:35:45 container=running/healthy hub=running alarm=none
11:36:47 container=running/unhealthy hub=running alarm=none
11:37:49 container=running/unhealthy hub=not_running alarm=none
11:38:51 container=running/unhealthy hub=not_running alarm=none
@@ -0,0 +1,5 @@
block at 2026-10-04T11:34:33Z: DOCKER-USER DROP tcp+udp dport 7844 in guest 9201 on demo-hp
Chain DOCKER-USER (1 references)
target prot opt source destination
DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:7844
DROP udp -- 0.0.0.0/0 0.0.0.0/0 udp dpt:7844
@@ -0,0 +1,22 @@
11:23:25 running
11:23:56 running
11:24:26 running
11:24:57 running
11:25:27 running
11:25:58 running
11:26:28 running
11:26:59 running
11:27:29 running
11:28:00 running
11:28:31 running
11:29:01 running
11:29:32 running
11:30:02 running
11:30:33 running
11:31:03 running
11:31:34 running
11:32:04 running
11:32:35 running
11:33:05 running
11:33:36 running
11:34:06 running
@@ -0,0 +1,3 @@
stop at 2026-10-04T11:23:17Z
cloudflared
exited 0
@@ -0,0 +1,36 @@
# Live-found defects in agent 0.141.0 / hub 0.131.0 and their red-proofs, 2026-10-04T11:39:07Z
## 1. the host restart scan hid lxc-start (cgroup 0::/lxc.monitor/9201 matched the skip pattern "lxc")
live: demo-felhom after the 108-package host pass: lxc-start had 20 deleted maps, report said reboot_needed=false
mutation: RESTART_SKIP_CGROUP host ':/lxc/' -> 'lxc'
self.assertFalse(self.grep(host, "0::/lxc.monitor/9201"), "lxc-start must NOT be skipped (it runs the guest)")
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: True is not false : lxc-start must NOT be skipped (it runs the guest)
----------------------------------------------------------------------
Ran 1 test in 0.005s
FAILED (failures=1)
## 2. the host scanned only after an install, so a reboot never cleared 'reboot needed'
mutation: wrapper 'if self.layer == "host" and ...' -> 'if False and ...'
----------------------------------------------------------------------
Ran 1 test in 0.002s
FAILED (failures=1)
mutation: agent leg drops rep.RebootScanned copy
=== RUN TestHostReport_CarriesRebootScanned
leg_test.go:367: hub got [{RunID:20261004T040000Z Layer:guest Trigger:night Mode:apply Ring:0 ReleaseID:ring0-20261004T040000Z Outcome:nothing Healthy:true HealthReason: VMID:9201 Upgraded:[] Inst
--- FAIL: TestHostReport_CarriesRebootScanned (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-agent/internal/osupdate 0.008s
FAIL
mutation: hub layerLine predicate ignores RebootScanned
=== RUN TestRebootNeeded_ClearedByAScannedPass
alarms_test.go:323: a scanned pass after the reboot did not clear it: 2026-10-04 12:00:00 +0000 UTC
--- FAIL: TestRebootNeeded_ClearedByAScannedPass (0.03s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-hub/internal/osupdates 0.037s
FAIL
@@ -0,0 +1,716 @@
=== felhom-agent 0.141.0 selftest=os-update vmid=9201 ring=0 enabled=true guest-release=false host-release=false appliance=true ===
time=2026-10-04T13:34:48.263+02:00 level=INFO msg="osupdate: START" run=20261004T113448Z layer=guest vmid=9201 ring=0 trigger=debug enabled=true release=ring0-20261004T113448Z
time=2026-10-04T13:34:59.917+02:00 level=INFO msg="osupdate: wrapper" line="os-apply: START release=ring0-20261004T113448Z layer=guest:9201 lane=fast mode=apply select=pending-fast packages=0"
time=2026-10-04T13:34:59.917+02:00 level=INFO msg="osupdate: wrapper" line="os-apply: REPAIR configured=0 fixed=0"
time=2026-10-04T13:34:59.917+02:00 level=INFO msg="osupdate: wrapper" line="os-apply: PLAN upgrade=0 already=0 not-installed=0 from-snapshot=0"
time=2026-10-04T13:34:59.917+02:00 level=INFO msg="osupdate: wrapper" line="os-apply: DONE rc=0 seconds=0 upgraded=0 (nothing to do)"
time=2026-10-04T13:34:59.917+02:00 level=INFO msg="osupdate: DONE" run=20261004T113448Z layer=guest vmid=9201 ring=0 trigger=debug outcome=nothing healthy=true reason="" upgraded=0 pending=6 not_covered=6 restart_needed=0 reboot_needed=false wrapper_seconds=11.6
time=2026-10-04T13:34:59.927+02:00 level=INFO msg="osupdate: START" run=20261004T113448Z layer=host vmid=9201 ring=0 trigger=debug enabled=true release=ring0-20261004T113448Z
time=2026-10-04T13:35:57.810+02:00 level=INFO msg="osupdate: wrapper" line="os-apply: START release=ring0-20261004T113448Z layer=host lane=fast mode=apply select=pending-fast packages=0"
time=2026-10-04T13:35:57.810+02:00 level=INFO msg="osupdate: wrapper" line="os-apply: REPAIR configured=0 fixed=0"
time=2026-10-04T13:35:57.810+02:00 level=INFO msg="osupdate: wrapper" line="os-apply: PLAN upgrade=108 already=0 not-installed=0 from-snapshot=0"
time=2026-10-04T13:35:57.810+02:00 level=INFO msg="osupdate: wrapper" line="os-apply: CONFFILE updated /etc/debian_version (it was not changed locally)"
time=2026-10-04T13:35:57.810+02:00 level=INFO msg="osupdate: wrapper" line="os-apply: DONE rc=0 seconds=41.1 upgraded=108 restart-needed=(sd-pam),agetty,bash,blkmapd,chronyd,cron,dbus-daemon,dmeventd,felhom-agent,felhom-os-apply,ksmtuned,pmxcfs,proxmox-firewal,pve-firewall,pve-ha-crm,pve-ha-lrm,pvedaemon,pvedaemon worke,pvefw-logger,pveproxy,pveproxy worker,pvescheduler,pvestatd,qmeventd,rpcbind,rrdcached,smartd,spiceproxy,spiceproxy work,sshd,sshd-session,sudo,systemd-logind,systemd-udevd,watchdog-mux,zed reboot-needed=no"
time=2026-10-04T13:35:58.613+02:00 level=INFO msg="osupdate: DONE" run=20261004T113448Z layer=host vmid=9201 ring=0 trigger=debug outcome=applied healthy=true reason="" upgraded=108 pending=80 not_covered=80 restart_needed=36 reboot_needed=false wrapper_seconds=57.8
--- os-update report (guest) ---
{
"health_reason": "",
"healthy": true,
"mode": "apply",
"not_covered": [
"docker-ce-cli",
"containerd.io",
"docker-ce",
"docker-buildx-plugin",
"docker-ce-rootless-extras",
"docker-compose-plugin"
],
"outcome": "nothing",
"pending": 6,
"reboot_needed": false,
"refused": null,
"release_id": "ring0-20261004T113448Z",
"restart_needed": null,
"ring": 0,
"run_id": "20261004T113448Z",
"upgraded": [],
"wrapper_seconds": 11.6
}
--- os-update report (host) ---
{
"health_reason": "",
"healthy": true,
"mode": "apply",
"not_covered": [
"frr",
"shim-signed-common",
"shim-unsigned",
"shim-helpers-amd64-signed",
"shim-signed",
"libradosstriper1",
"librgw2",
"ceph-common",
"librbd1",
"librados2",
"python3-cephfs",
"libcephfs2",
"python3-rgw",
"python3-rados",
"python3-ceph-argparse",
"python3-ceph-common",
"python3-rbd",
"ceph-fuse",
"chrony",
"libcorosync-common4",
"libcfg7",
"libcmap4",
"libcpg4",
"libknet1t64",
"libnozzle1t64",
"libquorum5",
"libvotequorum8",
"corosync",
"frr-pythontools",
"libjs-extjs",
"libnvpair3linux",
"libproxmox-acme-plugins",
"libproxmox-backup-qemu0",
"pve-qemu-kvm",
"libpve-notify-perl",
"libpve-cluster-api-perl",
"libpve-cluster-perl",
"pve-cluster",
"libpve-access-control",
"libpve-apiclient-perl",
"librados2-perl",
"proxmox-backup-client",
"proxmox-backup-file-restore",
"pve-manager",
"libproxmox-acme-perl",
"libpve-common-perl",
"libpve-guest-common-perl",
"qemu-server",
"libpve-storage-perl",
"pve-edk2-firmware-legacy",
"pve-edk2-firmware-ovmf",
"libpve-network-api-perl",
"libpve-network-perl",
"proxmox-firewall-data",
"pve-firewall",
"pve-container",
"pve-ha-manager",
"novnc-pve",
"proxmox-enterprise-support-keyring",
"proxmox-mini-journalreader",
"proxmox-widget-toolkit",
"pve-docs",
"pve-i18n",
"pve-xtermjs",
"pve-yew-mobile-i18n",
"pve-yew-mobile-gui",
"libuutil3linux",
"libzfs7linux",
"libzpool7linux",
"proxmox-first-boot",
"pve-firmware",
"proxmox-kernel-7.0.14-20-pve-signed",
"proxmox-kernel-7.0",
"proxmox-kernel-helper",
"pve-edk2-firmware-aarch64",
"pve-edk2-firmware",
"zfs-initramfs",
"zfsutils-linux",
"zfs-zed",
"tailscale"
],
"outcome": "applied",
"pending": 80,
"reboot_needed": false,
"refused": null,
"release_id": "ring0-20261004T113448Z",
"restart_needed": [
"(sd-pam)",
"agetty",
"bash",
"blkmapd",
"chronyd",
"cron",
"dbus-daemon",
"dmeventd",
"felhom-agent",
"felhom-os-apply",
"ksmtuned",
"pmxcfs",
"proxmox-firewal",
"pve-firewall",
"pve-ha-crm",
"pve-ha-lrm",
"pvedaemon",
"pvedaemon worke",
"pvefw-logger",
"pveproxy",
"pveproxy worker",
"pvescheduler",
"pvestatd",
"qmeventd",
"rpcbind",
"rrdcached",
"smartd",
"spiceproxy",
"spiceproxy work",
"sshd",
"sshd-session",
"sudo",
"systemd-logind",
"systemd-udevd",
"watchdog-mux",
"zed"
],
"ring": 0,
"run_id": "20261004T113448Z",
"upgraded": [
{
"name": "libc6",
"version": "2.41-12+deb13u4",
"origin": ""
},
{
"name": "base-files",
"version": "13.8+deb13u7",
"origin": ""
},
{
"name": "bash",
"version": "5.2.37-2+b10",
"origin": ""
},
{
"name": "bsdutils",
"version": "1:2.41.5-0+deb13u1",
"origin": ""
},
{
"name": "gzip",
"version": "1.13-1+deb13u1",
"origin": ""
},
{
"name": "libperl5.40",
"version": "5.40.1-6+deb13u1",
"origin": ""
},
{
"name": "perl",
"version": "5.40.1-6+deb13u1",
"origin": ""
},
{
"name": "perl-base",
"version": "5.40.1-6+deb13u1",
"origin": ""
},
{
"name": "perl-modules-5.40",
"version": "5.40.1-6+deb13u1",
"origin": ""
},
{
"name": "liblastlog2-2",
"version": "2.41.5-0+deb13u1",
"origin": ""
},
{
"name": "eject",
"version": "2.41.5-0+deb13u1",
"origin": ""
},
{
"name": "bsdextrautils",
"version": "2.41.5-0+deb13u1",
"origin": ""
},
{
"name": "util-linux-extra",
"version": "2.41.5-0+deb13u1",
"origin": ""
},
{
"name": "fdisk",
"version": "2.41.5-0+deb13u1",
"origin": ""
},
{
"name": "libsmartcols1",
"version": "2.41.5-0+deb13u1",
"origin": ""
},
{
"name": "libblkid1",
"version": "2.41.5-0+deb13u1",
"origin": ""
},
{
"name": "libmount1",
"version": "2.41.5-0+deb13u1",
"origin": ""
},
{
"name": "mount",
"version": "2.41.5-0+deb13u1",
"origin": ""
},
{
"name": "libuuid1",
"version": "2.41.5-0+deb13u1",
"origin": ""
},
{
"name": "util-linux",
"version": "2.41.5-0+deb13u1",
"origin": ""
},
{
"name": "libfdisk1",
"version": "2.41.5-0+deb13u1",
"origin": ""
},
{
"name": "libaudit-common",
"version": "1:4.0.2-2+deb13u1",
"origin": ""
},
{
"name": "libaudit1",
"version": "1:4.0.2-2+deb13u1",
"origin": ""
},
{
"name": "sqlite3",
"version": "3.46.1-7+deb13u2",
"origin": ""
},
{
"name": "libsqlite3-0",
"version": "3.46.1-7+deb13u2",
"origin": ""
},
{
"name": "libc-bin",
"version": "2.41-12+deb13u4",
"origin": ""
},
{
"name": "login",
"version": "1:4.16.0-2+really2.41.5-0+deb13u1",
"origin": ""
},
{
"name": "logsave",
"version": "1.47.2-3+b12",
"origin": ""
},
{
"name": "libext2fs2t64",
"version": "1.47.2-3+b12",
"origin": ""
},
{
"name": "e2fsprogs",
"version": "1.47.2-3+b12",
"origin": ""
},
{
"name": "dnsmasq-base",
"version": "2.91-1+deb13u2",
"origin": ""
},
{
"name": "dnsmasq",
"version": "2.91-1+deb13u2",
"origin": ""
},
{
"name": "libcap2",
"version": "1:2.75-10+deb13u1+b3",
"origin": ""
},
{
"name": "libpcre2-8-0",
"version": "10.46-1~deb13u3",
"origin": ""
},
{
"name": "libpcre2-posix3",
"version": "10.46-1~deb13u3",
"origin": ""
},
{
"name": "libexpat1",
"version": "2.8.3-1~deb13u1",
"origin": ""
},
{
"name": "openssl-provider-legacy",
"version": "3.5.7-1~deb13u3",
"origin": ""
},
{
"name": "libssl3t64",
"version": "3.5.7-1~deb13u3",
"origin": ""
},
{
"name": "postfix",
"version": "3.10.13-0+deb13u1",
"origin": ""
},
{
"name": "python3.13",
"version": "3.13.5-2+deb13u5",
"origin": ""
},
{
"name": "libpython3.13-stdlib",
"version": "3.13.5-2+deb13u5",
"origin": ""
},
{
"name": "python3.13-minimal",
"version": "3.13.5-2+deb13u5",
"origin": ""
},
{
"name": "libpython3.13-minimal",
"version": "3.13.5-2+deb13u5",
"origin": ""
},
{
"name": "tzdata",
"version": "2026c-0+deb13u1",
"origin": ""
},
{
"name": "liblzma5",
"version": "5.8.1-1+deb13u1",
"origin": ""
},
{
"name": "rsync",
"version": "3.5.0+ds1-0+deb13u1",
"origin": ""
},
{
"name": "openssl",
"version": "3.5.7-1~deb13u3",
"origin": ""
},
{
"name": "dhcpcd-base",
"version": "1:10.1.0-11+deb13u4",
"origin": ""
},
{
"name": "libgssapi-krb5-2",
"version": "1.21.3-5+deb13u1",
"origin": ""
},
{
"name": "libkrb5-3",
"version": "1.21.3-5+deb13u1",
"origin": ""
},
{
"name": "libkrb5support0",
"version": "1.21.3-5+deb13u1",
"origin": ""
},
{
"name": "libk5crypto3",
"version": "1.21.3-5+deb13u1",
"origin": ""
},
{
"name": "libcom-err2",
"version": "1.47.2-3+b12",
"origin": ""
},
{
"name": "libxml2",
"version": "2.12.7+dfsg+really2.9.14-2.1+deb13u3",
"origin": ""
},
{
"name": "bind9-dnsutils",
"version": "1:9.20.29-1~deb13u1",
"origin": ""
},
{
"name": "bind9-host",
"version": "1:9.20.29-1~deb13u1",
"origin": ""
},
{
"name": "bind9-libs",
"version": "1:9.20.29-1~deb13u1",
"origin": ""
},
{
"name": "krb5-locales",
"version": "1.21.3-5+deb13u1",
"origin": ""
},
{
"name": "libc-l10n",
"version": "2.41-12+deb13u4",
"origin": ""
},
{
"name": "locales",
"version": "2.41-12+deb13u4",
"origin": ""
},
{
"name": "xz-utils",
"version": "5.8.1-1+deb13u1",
"origin": ""
},
{
"name": "busybox",
"version": "1:1.37.0-6+b9",
"origin": ""
},
{
"name": "curl",
"version": "8.14.1-2+deb13u5",
"origin": ""
},
{
"name": "libssh2-1t64",
"version": "1.11.1-1+deb13u2",
"origin": ""
},
{
"name": "libcurl4t64",
"version": "8.14.1-2+deb13u5",
"origin": ""
},
{
"name": "librabbitmq4",
"version": "0.15.0-1+deb13u2",
"origin": ""
},
{
"name": "libnss3",
"version": "2:3.110-1+deb13u4",
"origin": ""
},
{
"name": "libgcrypt20",
"version": "1.11.0-7+deb13u1",
"origin": ""
},
{
"name": "gpgsm",
"version": "2.4.7-21+deb13u1+b5",
"origin": ""
},
{
"name": "dirmngr",
"version": "2.4.7-21+deb13u1+b5",
"origin": ""
},
{
"name": "gpg",
"version": "2.4.7-21+deb13u1+b5",
"origin": ""
},
{
"name": "gpgconf",
"version": "2.4.7-21+deb13u1+b5",
"origin": ""
},
{
"name": "gpg-agent",
"version": "2.4.7-21+deb13u1+b5",
"origin": ""
},
{
"name": "libasound2t64",
"version": "1.2.14-1+deb13u1",
"origin": ""
},
{
"name": "libasound2-data",
"version": "1.2.14-1+deb13u1",
"origin": ""
},
{
"name": "libbytes-random-secure-perl",
"version": "0.29-4~deb13u1",
"origin": ""
},
{
"name": "libcap2-bin",
"version": "1:2.75-10+deb13u1+b3",
"origin": ""
},
{
"name": "libcurl3t64-gnutls",
"version": "8.14.1-2+deb13u5",
"origin": ""
},
{
"name": "libevent-2.1-7t64",
"version": "2.1.13-stable-1~deb13u1",
"origin": ""
},
{
"name": "libevent-core-2.1-7t64",
"version": "2.1.13-stable-1~deb13u1",
"origin": ""
},
{
"name": "libgbm1",
"version": "25.0.7-2+deb13u1",
"origin": ""
},
{
"name": "mesa-libgallium",
"version": "25.0.7-2+deb13u1",
"origin": ""
},
{
"name": "libglib2.0-0t64",
"version": "2.84.4-3~deb13u5",
"origin": ""
},
{
"name": "libgraphite2-3",
"version": "1.3.14-2+deb13u1",
"origin": ""
},
{
"name": "libgstreamer-plugins-base1.0-0",
"version": "1.26.2-1+deb13u2",
"origin": ""
},
{
"name": "libhtml-parser-perl",
"version": "3.83-2~deb13u1",
"origin": ""
},
{
"name": "libhttp-daemon-perl",
"version": "6.16-1+deb13u1",
"origin": ""
},
{
"name": "libtalloc2",
"version": "2:2.4.3+samba4.22.11+dfsg-0+deb13u1",
"origin": ""
},
{
"name": "libtevent0t64",
"version": "2:0.16.2+samba4.22.11+dfsg-0+deb13u1",
"origin": ""
},
{
"name": "libsmbclient0",
"version": "2:4.22.11+dfsg-0+deb13u1",
"origin": ""
},
{
"name": "samba-common",
"version": "2:4.22.11+dfsg-0+deb13u1",
"origin": ""
},
{
"name": "smbclient",
"version": "2:4.22.11+dfsg-0+deb13u1",
"origin": ""
},
{
"name": "libtdb1",
"version": "2:1.4.13+samba4.22.11+dfsg-0+deb13u1",
"origin": ""
},
{
"name": "libldb2",
"version": "2:2.11.0+samba4.22.11+dfsg-0+deb13u1",
"origin": ""
},
{
"name": "samba-libs",
"version": "2:4.22.11+dfsg-0+deb13u1",
"origin": ""
},
{
"name": "libwbclient0",
"version": "2:4.22.11+dfsg-0+deb13u1",
"origin": ""
},
{
"name": "libnet-dns-perl",
"version": "1.56-0+deb13u1",
"origin": ""
},
{
"name": "libpcre2-16-0",
"version": "10.46-1~deb13u3",
"origin": ""
},
{
"name": "libpng16-16t64",
"version": "1.6.48-1+deb13u6",
"origin": ""
},
{
"name": "libslirp0",
"version": "4.8.0-1+deb13u1",
"origin": ""
},
{
"name": "libxml-libxml-perl",
"version": "2.0207+dfsg+really+2.0134-5+deb13u1",
"origin": ""
},
{
"name": "socat",
"version": "1.8.0.3-1+deb13u1",
"origin": ""
},
{
"name": "libss2",
"version": "1.47.2-3+b12",
"origin": ""
},
{
"name": "libtasn1-6",
"version": "4.20.0-2+deb13u1",
"origin": ""
},
{
"name": "libunbound8",
"version": "1.26.1-0+deb13u1",
"origin": ""
},
{
"name": "python3-idna",
"version": "3.10-1+deb13u1",
"origin": ""
},
{
"name": "python3-urllib3",
"version": "2.3.0-3+deb13u2",
"origin": ""
},
{
"name": "xfsprogs",
"version": "6.13.0-2+deb13u1",
"origin": ""
}
],
"wrapper_seconds": 57.8
}
pass took 1m10.4s
WALL_SECONDS=70.462688397
@@ -0,0 +1,8 @@
=== felhom-agent 0.140.0 selftest=os-update vmid=9201 ring=0 enabled=true release=false ===
time=2026-10-04T13:19:32.683+02:00 level=INFO msg="osupdate: START" run=20261004T111932Z vmid=9201 ring=0 trigger=debug enabled=true release=ring0-20261004T111932Z
time=2026-10-04T13:19:46.617+02:00 level=INFO msg="osupdate: wrapper" line="os-apply: START release=ring0-20261004T111932Z layer=guest:9201 lane=fast mode=inventory packages=0"
time=2026-10-04T13:19:46.618+02:00 level=INFO msg="osupdate: DONE" run=20261004T111932Z vmid=9201 ring=0 trigger=debug outcome=nothing healthy=true reason="" upgraded=0 pending=6 not_covered=6 restart_needed=10
"outcome": "nothing",
"pending": 6,
"upgraded": null
WALL_SECONDS=14.029320302
@@ -0,0 +1,2 @@
demo-felhom wrapper 4729769ce32e25e6 755 root; sudoers 02df92d751f1780a
demo-hp wrapper 4729769ce32e25e6 755 root; sudoers 02df92d751f1780a
+11
View File
@@ -1,3 +1,14 @@
## v0.131.1 — a reboot clears "reboot needed" (2026-10-04)
**Pairs with agent v0.141.1.** A patch release in the same session as v0.131.0 — a deliberate exception to "one
release per repo": without it the 14-day `os_reboot_needed` alarm fires on a host the operator rebooted long ago.
- The fleet line's "reboot needed since" now also counts a pass that installed nothing when the agent says it looked
(`reboot_scanned`, agent ≥ 0.141.1 scans the host on every pass). A looked-and-clean pass ends the run, so the date
and the alarm clear after a reboot. Reports from older agents behave as before (only passes with an install count).
- Test `TestRebootNeeded_ClearedByAScannedPass`; red-proof in
`documentation/audits/os-host-lane-2026-10-04/partB/live-defects-redproofs.txt`.
## v0.131.0 — the tunnel status is true (R-841); OS updates: the host fast lane's own approved set, the fleet view and four alarms (`11` §8 steps 3+4) (2026-10-04) ## v0.131.0 — the tunnel status is true (R-841); OS updates: the host fast lane's own approved set, the fleet view and four alarms (`11` §8 steps 3+4) (2026-10-04)
**Needs agent v0.141.0** for the tunnel's three states and the host layer. An older agent still reports **Needs agent v0.141.0** for the tunnel's three states and the host layer. An older agent still reports
+24
View File
@@ -304,3 +304,27 @@ func TestFleet_OneLinePerBoxBothLayers(t *testing.T) {
} }
t.Fatal("no hp line") t.Fatal("no hp line")
} }
// A reboot clears "reboot needed": the agent scans the host on every pass (reboot_scanned) and a scanned pass that
// finds nothing ends the run, so no alarm fires 14 days later. Red-proof: ignore RebootScanned in layerLine's predicate
// and the cleared step still shows a date.
func TestRebootNeeded_ClearedByAScannedPass(t *testing.T) {
f := newFix(t)
f.ingest(t, "hp", Report{Layer: LayerHost, Trigger: "night", Mode: "apply", Outcome: "applied", Healthy: true,
Upgraded: []Package{pk("libc6", "2")}, RebootNeeded: true, RebootScanned: true})
f.now = f.now.Add(24 * time.Hour)
f.ingest(t, "hp", Report{Layer: LayerHost, Trigger: "night", Mode: "apply", Outcome: "nothing", Healthy: true, RebootNeeded: true, RebootScanned: true})
if l := f.s.layerLine("hp", LayerHost, 0); f.now.Sub(l.RebootNeededSince) != 24*time.Hour {
t.Fatalf("since = %v", l.RebootNeededSince)
}
f.now = f.now.Add(24 * time.Hour) // the operator rebooted
f.ingest(t, "hp", Report{Layer: LayerHost, Trigger: "night", Mode: "apply", Outcome: "nothing", Healthy: true, RebootScanned: true})
if l := f.s.layerLine("hp", LayerHost, 0); !l.RebootNeededSince.IsZero() {
t.Fatalf("a scanned pass after the reboot did not clear it: %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 reboot")
}
}
+3 -2
View File
@@ -88,6 +88,7 @@ type Report struct {
RestartNeeded []string `json:"restart_needed,omitempty"` RestartNeeded []string `json:"restart_needed,omitempty"`
DockerRestartNeeded bool `json:"docker_restart_needed,omitempty"` DockerRestartNeeded bool `json:"docker_restart_needed,omitempty"`
RebootNeeded bool `json:"reboot_needed,omitempty"` RebootNeeded bool `json:"reboot_needed,omitempty"`
RebootScanned bool `json:"reboot_scanned,omitempty"` // agent ≥ 0.141.1: the pass looked (host: every pass)
Refused json.RawMessage `json:"refused,omitempty"` Refused json.RawMessage `json:"refused,omitempty"`
PassSeconds float64 `json:"pass_seconds,omitempty"` PassSeconds float64 `json:"pass_seconds,omitempty"`
} }
@@ -534,8 +535,8 @@ func (s *Service) layerLine(hostID, layer string, ring int) LayerLine {
} }
l.NotCoveredFast = len(s.notCoveredFast(layer, ring, r)) l.NotCoveredFast = len(s.notCoveredFast(layer, ring, r))
l.RebootNeededSince = s.sinceContinuously(hostID, layer, func(x Report) (bool, bool) { l.RebootNeededSince = s.sinceContinuously(hostID, layer, func(x Report) (bool, bool) {
if len(x.Upgraded) == 0 && x.Outcome != "applied" { if !x.RebootScanned && len(x.Upgraded) == 0 && x.Outcome != "applied" {
return false, false // a report without an install says nothing about reboot-needed: skip it return false, false // a pass that did not look says nothing about reboot-needed: skip it
} }
return x.RebootNeeded, true return x.RebootNeeded, true
}) })