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
gates / gates (push) Successful in 1m50s
This commit is contained in:
1 parent
cbc944c3c0
commit
6a17c95815
10 files changed
+246
-1
No files matched your search
@@ -0,0 +1,66 @@
|
||||
package backup
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/config"
|
||||
)
|
||||
|
||||
// exclFake is volDumpFakeProvider plus the production adapter's exclusion answer.
|
||||
type exclFake struct {
|
||||
volDumpFakeProvider
|
||||
excluded []string
|
||||
}
|
||||
|
||||
func (f *exclFake) GetExcludedDockerVolumes(string) []string { return f.excluded }
|
||||
|
||||
// R-49 (D21 = B) — the consequence across a whole dump run, through the F7 tar seam (no Docker): the excluded cache
|
||||
// volume is not dumped, the other volume is, and the cache's OLD tar (from before the template marked it) is gone
|
||||
// from the unit, so no later copy carries it.
|
||||
// RED-PROOF: with DumpAppVolumes listing GetDockerVolumes again (no exclusion), the cache is dumped and its old tar
|
||||
// replaced, not removed — seen failing 2026-10-22.
|
||||
func TestR49_AnExcludedVolumeLeavesTheUnit(t *testing.T) {
|
||||
drive := t.TempDir()
|
||||
cfg := &config.Config{}
|
||||
cfg.Paths.SystemDataPath = drive
|
||||
fake := &exclFake{volDumpFakeProvider: volDumpFakeProvider{
|
||||
stacks: []StackSummary{{Name: "immich"}},
|
||||
volumes: map[string][]string{"immich": {"immich_immich_ml_cache", "immich_immich_postgres_data"}},
|
||||
}, excluded: []string{"immich_immich_ml_cache"}}
|
||||
var tarred []string
|
||||
m := &Manager{cfg: cfg, logger: log.New(io.Discard, "", 0), systemDataPath: drive, stackProvider: fake,
|
||||
tarVolume: func(vol, dir string) ([]byte, error) {
|
||||
tarred = append(tarred, vol)
|
||||
return nil, os.WriteFile(filepath.Join(dir, vol+".tar.tmp"), []byte("TAR "+vol), 0o644)
|
||||
}}
|
||||
dumpDir := AppVolumeDumpPath(m.namespaceRoot(drive), "immich")
|
||||
os.MkdirAll(dumpDir, 0o755)
|
||||
old := filepath.Join(dumpDir, "immich_immich_ml_cache.tar")
|
||||
os.WriteFile(old, []byte("824 MB OF MODELS"), 0o644)
|
||||
|
||||
if err := m.DumpAppVolumes("immich"); err != nil {
|
||||
t.Fatalf("DumpAppVolumes: %v", err)
|
||||
}
|
||||
if len(tarred) != 1 || tarred[0] != "immich_immich_postgres_data" {
|
||||
t.Fatalf("dumped %v, want only the database volume", tarred)
|
||||
}
|
||||
if _, err := os.Stat(old); !os.IsNotExist(err) {
|
||||
t.Fatal("the excluded cache's old tar is still in the unit — every later copy would carry it")
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(dumpDir, "immich_immich_postgres_data.tar")); err != nil {
|
||||
t.Fatalf("the database volume's tar is missing: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// A provider without the exclusion answer (every test fake, an older wiring) dumps every volume, as before.
|
||||
func TestR49_NoExclusionProviderDumpsEverything(t *testing.T) {
|
||||
m := &Manager{logger: log.New(io.Discard, "", 0), stackProvider: &volDumpFakeProvider{
|
||||
volumes: map[string][]string{"a": {"a_x", "a_y"}}}}
|
||||
if got := m.dumpableVolumes("a"); len(got) != 2 {
|
||||
t.Fatalf("dumpable = %v, want both", got)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user