R-49 (D21 = B): a template may exclude a named volume from every backup (backup.volumes, class excluded) — Immich's model cache; guarded, red-proved; unreleased
gates / gates (push) Successful in 1m50s

This commit is contained in:
admin committed 2026-10-22 16:21:38 +02:00
1 parent cbc944c3c0
commit 6a17c95815
10 files changed
+246 -1

No files matched your search

@@ -0,0 +1,37 @@
package backup
// R-49, operator ruling D21 = B (2026-10-22): a template may mark a named volume `excluded` (a pure cache the app
// rebuilds by itself — Immich's machine-learning model cache, 62 % of its off-site copy). Such a volume is never
// dumped, so it is in no recovery unit: not on the drive's copy, not on the second drive, not off-site. Its last tar
// is removed by DumpAppVolumes' stale-tar clean-up. A restore then finds no tar for it and leaves the volume as the
// app makes it (Immich downloads its models again). The rule and its guards: appbackup.ExcludedDockerVolumes.
// volumeExclusionProvider is implemented by the production stack adapter (cmd/controller); test fakes need not.
type volumeExclusionProvider interface {
GetExcludedDockerVolumes(name string) []string
}
// dumpableVolumes is the stack's named Docker volumes minus the ones its template excludes.
func (m *Manager) dumpableVolumes(stackName string) []string {
all := m.stackProvider.GetDockerVolumes(stackName)
ex, ok := m.stackProvider.(volumeExclusionProvider)
if !ok || len(all) == 0 {
return all
}
skip := map[string]bool{}
for _, v := range ex.GetExcludedDockerVolumes(stackName) {
skip[v] = true
}
if len(skip) == 0 {
return all
}
out := make([]string, 0, len(all))
for _, v := range all {
if skip[v] {
m.logger.Printf("[INFO] [backup] Volume %s/%s is excluded by the template (a cache the app rebuilds) — not dumped (R-49)", stackName, v)
continue
}
out = append(out, v)
}
return out
}