v0.285.0 — a box keeps two controller versions (decision 56, R-745); the update clean-up's nil-stack crash (R-751)
gates / gates (push) Successful in 25s

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-01 07:36:26 +02:00
parent a70c398dee
commit ac8ea72025
11 changed files with 473 additions and 4 deletions
@@ -287,13 +287,18 @@ func (m *Manager) RetainImagesAfterUpdate(name string, previous map[string]Insta
m.logger.Printf("[WARN] [stacks] image retention %s: rescan failed: %v", name, err)
}
}
st, _ = m.GetStack(name)
// R-751: the app can be gone by now (removed right after the update, or the rescan no longer finds it) — a nil
// stack here panicked in this goroutine and took the controller down. Its images are then the remove's to judge.
if st, ok = m.GetStack(name); !ok || st == nil {
m.logger.Printf("[INFO] [stacks] image retention after the update of %s: the app is gone — nothing to do here", name)
return
}
if _, err := m.deleteUnkeptImages("update of "+name, appImageRepos(dir, st.AppConfig), ""); err != nil {
m.logger.Printf("[WARN] [stacks] image retention after the update of %s: %v", name, err)
}
}
// retainAfterUpdateFn runs the retention after an update ends (a seam: the update tests do not exercise it).
// retainAfterUpdateFn runs the retention after an update ends (a seam: a no-op in this package's tests — TestMain, R-751).
var retainAfterUpdateFn = func(m *Manager, name string, previous map[string]InstalledImage) {
go m.RetainImagesAfterUpdate(name, previous)
}