v0.243.0: FileBrowser generated admin password (R-513); per-tier whole-guest backup truth (R-517); skip absent-storage tiers (R-518); OOM-killed worker visible (R-514)
gates / gates (push) Successful in 14s

MinAgent: 0.131.0

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-09-15 10:12:08 +02:00
parent 406755fa8f
commit 843b319f35
23 changed files with 1095 additions and 8 deletions
+45
View File
@@ -35,6 +35,43 @@ var ErrTiersUnsupported = errors.New("quiesce: agent does not serve /backup/tier
type BackupTier struct {
Target string
Primary bool
// StorageAbsent (R-518, agent >= v0.131.0): the agent POSITIVELY determined the tier's storage
// does not exist. Only then is the tier skipped; unknown/legacy stay false (fail toward backing up).
StorageAbsent bool
}
// TierSkipNotifier is the OPTIONAL notifier extension for a tier skipped because its storage does not
// exist (R-518). Optional so every existing TierNotifier keeps compiling.
type TierSkipNotifier interface {
BackupTierSkipped(tier, message string)
}
// skipAbsentTiers drops the tiers whose storage the agent reports absent (R-518's cheap half,
// BIGNIGHT: the apps stayed stopped through a PBS tier whose storage did not exist, then it failed).
// It logs every skip and notifies ONCE per tier per absence (a scheduled loop re-resolves every poll;
// the note re-arms when the storage reappears).
func (l *Loop) skipAbsentTiers(tiers []BackupTier) []BackupTier {
out := make([]BackupTier, 0, len(tiers))
l.skipMu.Lock()
defer l.skipMu.Unlock()
if l.skipNoted == nil {
l.skipNoted = map[string]bool{}
}
for _, t := range tiers {
if !t.StorageAbsent {
delete(l.skipNoted, t.Target)
out = append(out, t)
continue
}
l.logger.Printf("[WARN] [quiesce] tier %s skipped: its storage does not exist on the host — no app is stopped for it", tierLabel(t.Target))
if !l.skipNoted[t.Target] {
l.skipNoted[t.Target] = true
if n, ok := l.tierNotify.(TierSkipNotifier); ok && n != nil {
n.BackupTierSkipped(t.Target, "Whole-guest backup tier "+t.Target+" skipped: its storage does not exist on the host (never provisioned or removed). No app was stopped for it.")
}
}
}
return out
}
// TieredBackend is the OPTIONAL R-82 extension to Backend. A backend that does not implement it
@@ -120,6 +157,14 @@ func (l *Loop) resolveDueTiers(ctx context.Context) (due []dueTier, degraded boo
if terr != nil {
return nil, false, terr
}
if len(tiers) > 0 {
if kept := l.skipAbsentTiers(tiers); len(kept) == 0 {
l.logger.Printf("[WARN] [quiesce] every advertised tier's storage is absent — nothing to back up this cycle")
return nil, false, nil
} else {
tiers = kept
}
}
if len(tiers) == 0 {
// An agent that advertises no tiers cannot be backed up per-tier, but it can still be
// backed up untargeted. Fail toward DOING the backup, never toward skipping it.