controllerswap: F1 verify hardening — reject crash-looping no-healthcheck image v0.47.0

controllerHealthy reads RestartCount (running&&rc>0 -> not ok) + signals needsDwell for no-healthcheck;
verify requires verifyDwell(=3) consecutive ok polls for a no-healthcheck image (real healthcheck
trusted immediately). Closes the F1 hole (alpine crash-loop passed the point-in-time check). Red-proof
+ dwell + real-image tests. No sudoers/orchestration change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pg8ANF97SEeKYSN5Jxw3qJ
This commit is contained in:
2026-06-29 22:45:41 +02:00
parent bb548e3c5a
commit 3844df7c23
8 changed files with 218 additions and 57 deletions
+7 -7
View File
@@ -22,13 +22,13 @@ func TestAntiRetargetResolve(t *testing.T) {
okInspect := func(string) (storage.DeviceProbe, error) { return dataBearing, nil }
cases := []struct {
name string
durable string
resolve func(string) (string, error)
derive func(string) (string, error)
inspect func(string) (storage.DeviceProbe, error)
wantDev string
wantErr string // substring; "" = expect success
name string
durable string
resolve func(string) (string, error)
derive func(string) (string, error)
inspect func(string) (storage.DeviceProbe, error)
wantDev string
wantErr string // substring; "" = expect success
}{
{
name: "happy-path-matches",