clear_hold (D64 = B) + the box command through the running controller (R-1343), R-1340, R-1341 — tests red first; commit only
gates / gates (push) Successful in 31s
gates / gates (push) Successful in 31s
- clear_hold: a fifth verb in the closed operator-action list; lifts a hold of any reason in the RUNNING controller (memory + settings.json together), forgets the update's held flag, drops a waiting put-back from the app-stop marker (copy kept). Refused on a bad name or no hold. No new event type. - --clear-restore-hold asks the running controller over <data dir>/ctl/hold.sock (unix, 0600, peer uid checked, no TCP); edits settings.json only when no controller answers, and says so (in-guest restart). - R-1340: the operator's HELD STOPPED mail is built from the hold record (restore kind, mixed cause, failed put-back step, database replay or not, kept copy) — never the option-C database story by default. - R-1341: the OOM WARN line once per new kill (container, start time, kill count); event unchanged.
This commit is contained in:
1 parent
c7bb776e00
commit
5b3b541602
25 files changed
+1809
-83
No files matched your search
@@ -0,0 +1,46 @@
|
||||
package stacks
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/config"
|
||||
)
|
||||
|
||||
// D64 = B / R-1343: when the operator clears a hold, the ONE held flag kept outside the hold store — „the last update
|
||||
// ended held" — goes too. Left set, the crash-loop observer and the nightly update leg kept skipping an app whose hold
|
||||
// was gone, until the controller restarted.
|
||||
// RED-PROOF (2026-10-29): with ForgetUpdateHeld's body reduced to `return false`, this fails on „a held update must be forgotten".
|
||||
func TestD64_ForgetUpdateHeld(t *testing.T) {
|
||||
m := &Manager{cfg: &config.Config{}, logger: log.New(io.Discard, "", 0), stacks: map[string]*Stack{
|
||||
"kimai": {Name: "kimai", Deployed: true},
|
||||
"running": {Name: "running", Deployed: true, Updating: true},
|
||||
}}
|
||||
m.finishUpdateKey("kimai", UpdatePhaseFailed, UpdateErrorKeyHeld, "a kimai leállítva marad")
|
||||
m.markUpdateHeld("kimai")
|
||||
m.markUpdateHeld("running")
|
||||
|
||||
if !m.ForgetUpdateHeld("kimai") {
|
||||
t.Fatal("a held update must be forgotten")
|
||||
}
|
||||
st, _ := m.GetStack("kimai")
|
||||
if st.updateHeld || st.UpdateErrorKey != "" || st.UpdateError != "" || st.UpdatePhase != "" {
|
||||
t.Fatalf("held state left: held=%v key=%q err=%q phase=%q", st.updateHeld, st.UpdateErrorKey, st.UpdateError, st.UpdatePhase)
|
||||
}
|
||||
// The two readers of the flag (ObserveUnhealthy's skip, unhealthy.go; legCandidate's LegSkipHeld, unattended.go)
|
||||
// read the stored stack — not called here, because ObserveUnhealthy reaches docker (R-650).
|
||||
m.mu.RLock()
|
||||
skipped := m.stacks["kimai"].updateHeld || m.stacks["kimai"].UpdateErrorKey == UpdateErrorKeyHeld
|
||||
m.mu.RUnlock()
|
||||
if skipped {
|
||||
t.Fatal("the observer and the update leg still skip kimai")
|
||||
}
|
||||
// A running update is never touched; an app with nothing held reports false.
|
||||
if m.ForgetUpdateHeld("running") {
|
||||
t.Fatal("a running update must not be touched")
|
||||
}
|
||||
if m.ForgetUpdateHeld("kimai") || m.ForgetUpdateHeld("nosuch") {
|
||||
t.Fatal("nothing held → false")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user