controller v0.265.0: R-634 cause fixed, held apps say so, OOM storm alarm, R-647 leftovers
gates / gates (push) Successful in 27s

R-634: a whole-box backup no longer stops/restarts a DEPLOYING app (the
measured cause of containers running under 'not deployed'); StopStack
and StartStack refuse a deploying stack for every caller.
R-625: held badge 'Stopped - restore needed', no Update button.
R-636: kernel oom_kill counter; 20+ in 30 min -> one app_oom_storm.
R-647: held error per reader, copy_holds key, two log wordings.

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-09-23 17:16:57 +02:00
parent 0a3026180a
commit 0054d4bd69
28 changed files with 832 additions and 48 deletions
+47 -11
View File
@@ -322,17 +322,6 @@ func (s *Server) localeFuncs(lang string) template.FuncMap {
}
return stk.UpdatePhaseLabel // no translation for this phase: the stored label, never an empty line
},
"updateErrorText": func(st interface{}) string { return stackOf(st).UpdateErrorIn(lang) },
"holdText": func(st interface{}) string {
stk := stackOf(st)
if stk.HoldReason == "" || s.backupMgr == nil {
return stk.HoldReason
}
if held, why := s.backupMgr.RestoreHoldForLang(stk.Name, lang); held && why != "" {
return why
}
return stk.HoldReason
},
"timeAgo": func(t time.Time) string {
if t.IsZero() {
return "–"
@@ -387,6 +376,13 @@ func (s *Server) localeFuncs(lang string) template.FuncMap {
// Found live on 2026-09-20: on an English app page the only Felhom-authored Hungarian left
// was this badge, „Naprakész", with a Hungarian tooltip under it.
"updateBadge": func(st stacks.Stack) *MetaBadge {
if st.HoldReason != "" { // R-625's twin — see updatebadge.go
return &MetaBadge{
Label: b.Msg(lang, "badge.update.held"),
Class: "tag-error",
Title: firstSentence(st.HoldReason),
}
}
switch compareInstalledToTemplate(st) {
case updateCurrent:
return &MetaBadge{
@@ -554,3 +550,43 @@ func (s *Server) note(key string, args ...interface{}) string {
// noteErr renders an error into a saved note in the box's language: its bundle message when it
// carries one (release B), its own text otherwise.
func (s *Server) noteErr(err error) string { return util.ErrText(s.boxLang(), err) }
// readerFuncs are the funcs that need the SERVER to answer in the reader's language, and — unlike
// localeFuncs — they apply to EVERY language, Hungarian included (R-647, v0.265.0). A hold sentence is
// composed by the backup side in one language at a time; before this a Hungarian reader of an English
// box (the `?lang=` switch, an operator) read it in English. On a Hungarian box a Hungarian reader gets
// exactly the bytes templateFuncMap gave (the parity fixtures are rendered through here).
// `prev` is the func set they wrap (templateFuncMap + localeFuncs for this language).
func (s *Server) readerFuncs(lang string, prev template.FuncMap) template.FuncMap {
holdFor := func(st stacks.Stack) string {
if st.HoldReason == "" || s.backupMgr == nil {
return st.HoldReason
}
if held, why := s.backupMgr.RestoreHoldForLang(st.Name, lang); held && why != "" {
return why
}
return st.HoldReason
}
out := template.FuncMap{
"holdText": func(st interface{}) string { return holdFor(stackOf(st)) },
"updateErrorText": func(st interface{}) string {
stk := stackOf(st)
if stk.UpdateErrorKey == stacks.UpdateErrorKeyHeld && s.backupMgr != nil {
if held, why := s.backupMgr.RestoreHoldForLang(stk.Name, lang); held && why != "" {
return why
}
}
return stk.UpdateErrorIn(lang)
},
}
if base, ok := prev["updateBadge"].(func(stacks.Stack) *MetaBadge); ok {
out["updateBadge"] = func(st stacks.Stack) *MetaBadge {
bd := base(st)
if bd != nil && st.HoldReason != "" { // R-625: the title is the hold's opening, for THIS reader
bd.Title = firstSentence(holdFor(st))
}
return bd
}
}
return out
}