fix(R-1298): held-app reports only for installed apps (a restore hold always)

Red-proved. Commit only.
This commit is contained in:
2026-10-26 20:26:44 +01:00
parent dfd0f8ec53
commit b156e4526a
4 changed files with 75 additions and 3 deletions
+34 -2
View File
@@ -60,6 +60,9 @@ type Reporter struct {
push Push
logger *log.Logger
lastFull time.Time
// installed (R-1298) — nil = every app counts as installed.
installed func(stack string) bool
skipLogged map[string]bool
}
// New builds a reporter. The first Tick re-sends every hold (a controller start).
@@ -69,13 +72,42 @@ func New(store Store, push Push, logger *log.Logger) *Reporter {
func key(h settings.RestoreHold) string { return h.WireReason() + "|" + h.At }
// SetInstalled wires the „is this app installed?" check (R-1298). nil = every app counts as installed.
func (r *Reporter) SetInstalled(f func(stack string) bool) { r.installed = f }
// installHold is a hold that belongs to an app's INSTALL (R-704): a removal ends it. A restore hold (restore_failed,
// restore_mixed) does not — a person clears it, so it stays reported whatever the app's state (R-379).
func installHold(h settings.RestoreHold) bool {
switch h.Reason {
case settings.HoldReasonUnhealthyStop, settings.HoldReasonUpdateFailed, settings.HoldReasonPutBackPending:
return true
}
return false
}
// Tick sends what changed since the last tick, and every hold again when a day has passed.
func (r *Reporter) Tick(now time.Time) {
if r.store == nil || r.push == nil {
return
}
holds := r.store.ListRestoreHolds()
sort.Slice(holds, func(i, j int) bool { return holds[i].Stack < holds[j].Stack })
all := r.store.ListRestoreHolds()
sort.Slice(all, func(i, j int) bool { return all[i].Stack < all[j].Stack })
// R-1298: an install's hold of an app that is not installed any more (removed outside the product) is not a held
// app — it is skipped, and reported cleared if it was reported before. Logged once per stack per process.
holds := all[:0:0]
for _, h := range all {
if r.installed != nil && installHold(h) && !r.installed(h.Stack) {
if r.skipLogged == nil {
r.skipLogged = map[string]bool{}
}
if !r.skipLogged[h.Stack] {
r.skipLogged[h.Stack] = true
r.logf("[INFO] [holdreport] %s: a %s hold of an app that is not installed — not reported (R-1298)", h.Stack, h.WireReason())
}
continue
}
holds = append(holds, h)
}
prev := r.store.GetReportedHolds()
full := r.lastFull.IsZero() || now.Sub(r.lastFull) >= ResendEvery