feat(D53): the hub learns which apps are held — app_held / app_hold_cleared, daily repeat
gates / gates (push) Successful in 54s
gates / gates (push) Successful in 54s
internal/holdreport: one observer of the hold store, ticked every minute; app_held for a new or changed hold (stack_name, reason, since, why), app_hold_cleared when gone (also while the controller was down: settings.reported_holds), every hold again daily and at start. Severity info. RestoreHold.Why/WireReason (holdWhy calls it). 8 tests, red-proved (inert Tick; unwired scheduler line). Commit only (felhom.eu 08 §10).
This commit is contained in:
@@ -40,6 +40,7 @@ import (
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/crashboot"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/crypto"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/fillwatch"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/holdreport"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/infra"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/integrations"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/mailrelay"
|
||||
@@ -1063,6 +1064,14 @@ func main() {
|
||||
go func() { defer aubBusy.Unlock(); aubRunner.Tick(time.Now()) }() // a run can take minutes; never block the scheduler
|
||||
return nil
|
||||
})
|
||||
// D53 (felhom.eu `08` §10): tell the hub which apps are held — app_held for a new or changed hold, app_hold_cleared
|
||||
// when one is gone, every hold again once a day and after each start. One observer of the hold store, so every
|
||||
// writer is covered. Pinned by internal/holdreport.
|
||||
holdReporter := holdreport.New(sett, notifier.PushEvent, logger)
|
||||
sched.Every("hold-report", time.Minute, func(ctx context.Context) error {
|
||||
holdReporter.Tick(time.Now())
|
||||
return nil
|
||||
})
|
||||
sched.Every("deadapp-check", 30*time.Second, func(ctx context.Context) error {
|
||||
if bootGrace.Within(time.Now()) {
|
||||
return nil // still inside the startup settle window (90 s, or ~15 min after a crash boot)
|
||||
@@ -2562,22 +2571,7 @@ type driveStartGate struct {
|
||||
// stop read as „held after a failed restore whose rollback also failed" for 11 days on demo-hp, so the operator could
|
||||
// not tell a crash loop from a broken restore). Pinned by TestR1286_EveryHoldReasonSaysWhatItIs.
|
||||
func holdWhy(h settings.RestoreHold) string {
|
||||
switch h.Reason {
|
||||
case settings.HoldReasonUpdateFailed:
|
||||
return "held after a failed update (" + h.At + ") — restore it from its backup to start it"
|
||||
case settings.HoldReasonUnhealthyStop:
|
||||
kind := h.UnhealthyKind
|
||||
if kind == "" {
|
||||
kind = "unhealthy"
|
||||
}
|
||||
return "stopped by the box for a " + strings.ReplaceAll(kind, "_", " ") + " (" + h.At + ") — the household's Start gives it one more try"
|
||||
case settings.HoldReasonRestoreMixed:
|
||||
return "held after an off-site restore failed its database replay (" + h.At + ") — clear the hold to start it"
|
||||
case settings.HoldReasonPutBackPending:
|
||||
return "held until the pre-restore copy is poured back (" + h.At + ") — the put-back starts it"
|
||||
default:
|
||||
return "held after a failed restore whose rollback also failed (" + h.At + ") — clear the hold to start it"
|
||||
}
|
||||
return h.Why() // one sentence per reason, shared with the hub's „Held apps" list (D53)
|
||||
}
|
||||
|
||||
func (g driveStartGate) MayStart(stackName string) (bool, string) {
|
||||
|
||||
Reference in New Issue
Block a user