controller v0.265.0: R-634 cause fixed, held apps say so, OOM storm alarm, R-647 leftovers
gates / gates (push) Successful in 27s
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:
@@ -564,7 +564,7 @@ func (m *Manager) finishUpdate(name, phase, msg string) {
|
||||
// beside it for the page. key "" = `plain` is a finished sentence and is stored as it is.
|
||||
func (m *Manager) finishUpdateKey(name, phase, key, plain string, args ...interface{}) {
|
||||
msg := plain
|
||||
if key != "" {
|
||||
if key != "" && key != UpdateErrorKeyHeld { // the held key names no bundle entry: plain IS the sentence
|
||||
msg = util.Text(i18n.Default, key, args...)
|
||||
}
|
||||
m.mu.Lock()
|
||||
@@ -590,12 +590,57 @@ func UpdatePhaseLabelIn(lang, phase string) string {
|
||||
|
||||
// UpdateErrorIn is the stack's update sentence in lang (v0.264.0, R-606).
|
||||
func (s Stack) UpdateErrorIn(lang string) string {
|
||||
if s.UpdateErrorKey == "" || lang == i18n.Default {
|
||||
if s.UpdateErrorKey == "" || s.UpdateErrorKey == UpdateErrorKeyHeld || lang == i18n.Default {
|
||||
return s.UpdateError
|
||||
}
|
||||
return util.Text(lang, s.UpdateErrorKey, s.UpdateErrorArgs...)
|
||||
}
|
||||
|
||||
// UpdateErrorKeyHeld marks an UpdateError that IS the hold sentence (R-647, v0.265.0). It names no
|
||||
// bundle entry: the sentence is composed by the backup side, so UpdateErrorFor asks it for the
|
||||
// reader's language, and UpdateErrorIn (no manager to ask) returns the stored Hungarian.
|
||||
const UpdateErrorKeyHeld = "update.error.held"
|
||||
|
||||
// holdLanguage is the OPTIONAL half of UpdateGuards that renders the hold sentence in a given
|
||||
// language (the production adapter implements it; the test fakes need not).
|
||||
type holdLanguage interface {
|
||||
HoldForLang(name, lang string) (bool, string)
|
||||
}
|
||||
|
||||
func holdForLang(g UpdateGuards, name, lang string) (bool, string) {
|
||||
if g == nil {
|
||||
return false, ""
|
||||
}
|
||||
if hl, ok := g.(holdLanguage); ok {
|
||||
return hl.HoldForLang(name, lang)
|
||||
}
|
||||
return g.HoldFor(name)
|
||||
}
|
||||
|
||||
// HoldReasonFor is a stack's hold sentence in the READER's language (R-647): "" when nothing holds it.
|
||||
// A Hungarian reader on a Hungarian box gets exactly Stack.HoldReason.
|
||||
func (m *Manager) HoldReasonFor(st Stack, lang string) string {
|
||||
if st.HoldReason == "" {
|
||||
return ""
|
||||
}
|
||||
if held, why := holdForLang(m.guards(), st.Name, lang); held && why != "" {
|
||||
return why
|
||||
}
|
||||
return st.HoldReason
|
||||
}
|
||||
|
||||
// UpdateErrorFor is a stack's update error in the READER's language (R-606 + R-647). A held update's
|
||||
// error is the hold sentence, rendered by the backup side for this reader.
|
||||
func (m *Manager) UpdateErrorFor(st Stack, lang string) string {
|
||||
if st.UpdateErrorKey == UpdateErrorKeyHeld {
|
||||
if held, why := holdForLang(m.guards(), st.Name, lang); held && why != "" {
|
||||
return why
|
||||
}
|
||||
return st.UpdateError
|
||||
}
|
||||
return st.UpdateErrorIn(lang)
|
||||
}
|
||||
|
||||
func (m *Manager) updateCompose(dir string, env []string, args ...string) (string, error) {
|
||||
if m.updateComposeFn != nil {
|
||||
return m.updateComposeFn(dir, env, args...)
|
||||
@@ -880,7 +925,7 @@ func (m *Manager) failAndHold(ctx context.Context, name, dir string, env []strin
|
||||
m.logger.Printf("[ERROR] [stacks] update %s: no UpdateGuards — the hold CANNOT be recorded", name)
|
||||
} else if err := g.HoldAfterFailedUpdate(name, m.now(), rp, undoState); err != nil {
|
||||
m.logger.Printf("[ERROR] [stacks] update %s: %v", name, err)
|
||||
} else if _, w := g.HoldFor(name); w != "" {
|
||||
} else if _, w := holdForLang(g, name, i18n.Default); w != "" {
|
||||
holdWhy = w
|
||||
m.markUpdateHeld(name)
|
||||
}
|
||||
@@ -890,8 +935,10 @@ func (m *Manager) failAndHold(ctx context.Context, name, dir string, env []strin
|
||||
if holdWhy == "" {
|
||||
m.finishUpdateKey(name, UpdatePhaseFailed, "update.error.hold_unsaved", "")
|
||||
} else {
|
||||
// The hold's own sentence (the page renders it per reader through RestoreHoldForLang).
|
||||
m.finishUpdate(name, UpdatePhaseFailed, holdWhy)
|
||||
// R-647 (v0.265.0): stored as the HELD key + the Hungarian sentence, so every reader — the
|
||||
// `?lang=` switch, an operator reading a box in the other language — gets the hold sentence in
|
||||
// THEIR language (UpdateErrorFor), and the stored text is Hungarian as for every other key.
|
||||
m.finishUpdateKey(name, UpdatePhaseFailed, UpdateErrorKeyHeld, holdWhy)
|
||||
}
|
||||
m.emitUpdateEvent(UpdateEventHeld, name, entry, rp, holdWhy != "")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user