R-1214: the second-drive copy runs in a 512 MB memory-capped container for /mnt paths (its page cache no longer evicts the apps) — measured on 9202; guarded against a path the daemon sees differently; unreleased
gates / gates (push) Successful in 1m40s
gates / gates (push) Successful in 1m40s
This commit is contained in:
1 parent
d5f88a2182
commit
03c0a693b8
4 files changed
+177
-1
No files matched your search
@@ -0,0 +1,93 @@
|
||||
package backup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// R-1214: the second-drive copy runs in a memory-capped container ONLY for paths the controller and the Docker daemon
|
||||
// see identically (/mnt/…); every other path, a box with no readable image, and a container that cannot start keep the
|
||||
// plain in-process rsync; an rsync failure inside the container is an error, never a silent second copy.
|
||||
// RED-PROOF: with the tier2CappedPath guard removed, TestR1214_APathOutsideMntNeverGoesToDocker fails (the copy of a
|
||||
// controller-volume path would go to the container, where it is an EMPTY folder and `--delete` empties the copy);
|
||||
// with the capped branch removed, TestR1214_MntPathsAreCopiedCapped fails — both seen failing 2026-10-22.
|
||||
type cappedRec struct{ calls [][]string }
|
||||
|
||||
func withSeams(t *testing.T, root string, img string, run func(args []string) ([]byte, int, error)) *cappedRec {
|
||||
t.Helper()
|
||||
rec := &cappedRec{}
|
||||
oldRoot, oldImg, oldRun, oldLog := tier2CappedRoot, tier2SelfImage, tier2RunCapped, tier2Logf
|
||||
tier2CappedRoot = root
|
||||
tier2SelfImage = func() string { return img }
|
||||
tier2RunCapped = func(_ context.Context, args []string) ([]byte, int, error) {
|
||||
rec.calls = append(rec.calls, args)
|
||||
return run(args)
|
||||
}
|
||||
tier2Logf = func(string, ...any) {}
|
||||
t.Cleanup(func() { tier2CappedRoot, tier2SelfImage, tier2RunCapped, tier2Logf = oldRoot, oldImg, oldRun, oldLog })
|
||||
return rec
|
||||
}
|
||||
|
||||
func TestR1214_MntPathsAreCopiedCapped(t *testing.T) {
|
||||
root := t.TempDir() + "/"
|
||||
src, dst := filepath.Join(root, "drive1", "unit"), filepath.Join(root, "drive2", "unit")
|
||||
os.MkdirAll(src, 0755)
|
||||
rec := withSeams(t, root, "gitea.dooplex.hu/admin/felhom-controller:9.9.9", func([]string) ([]byte, int, error) { return nil, 0, nil })
|
||||
if err := rsyncMirror(src, dst); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(rec.calls) != 1 {
|
||||
t.Fatalf("docker calls = %d, want 1", len(rec.calls))
|
||||
}
|
||||
got := strings.Join(rec.calls[0], " ")
|
||||
for _, want := range []string{"run --rm", "--memory 512m", "--memory-swap 512m", "--network none",
|
||||
"-v " + src + ":" + src + ":ro", "-v " + dst + ":" + dst + " ", "--entrypoint rsync gitea.dooplex.hu/admin/felhom-controller:9.9.9 -a --delete " + src + "/ " + dst + "/"} {
|
||||
if !strings.Contains(got, want) {
|
||||
t.Fatalf("the capped copy lacks %q:\n%s", want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestR1214_APathOutsideMntNeverGoesToDocker(t *testing.T) {
|
||||
root := t.TempDir() + "/"
|
||||
other := t.TempDir() // a path the daemon would see differently (the controller's own data volume)
|
||||
src, dst := filepath.Join(other, "unit"), filepath.Join(root, "drive2", "unit")
|
||||
os.MkdirAll(src, 0755)
|
||||
os.WriteFile(filepath.Join(src, "photo.jpg"), []byte("x"), 0644)
|
||||
rec := withSeams(t, root, "img", func([]string) ([]byte, int, error) { return nil, 0, nil })
|
||||
if err := rsyncMirror(src, dst); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(rec.calls) != 0 {
|
||||
t.Fatalf("a path outside %s went to the container: %v", root, rec.calls)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(dst, "photo.jpg")); err != nil {
|
||||
t.Fatal("the plain copy did not run")
|
||||
}
|
||||
}
|
||||
|
||||
func TestR1214_ANotStartedContainerFallsBackAnRsyncErrorDoesNot(t *testing.T) {
|
||||
root := t.TempDir() + "/"
|
||||
src, dst := filepath.Join(root, "a", "unit"), filepath.Join(root, "b", "unit")
|
||||
os.MkdirAll(src, 0755)
|
||||
os.WriteFile(filepath.Join(src, "f"), []byte("x"), 0644)
|
||||
withSeams(t, root, "img", func([]string) ([]byte, int, error) { return []byte("no such image"), 125, errors.New("exit 125") })
|
||||
if err := rsyncMirror(src, dst); err != nil {
|
||||
t.Fatalf("a container that cannot start must fall back to the plain copy: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(dst, "f")); err != nil {
|
||||
t.Fatal("the fallback copy did not run")
|
||||
}
|
||||
withSeams(t, root, "img", func([]string) ([]byte, int, error) { return []byte("rsync error"), 23, errors.New("exit 23") })
|
||||
if err := rsyncMirror(src, dst); err == nil {
|
||||
t.Fatal("an rsync failure inside the container must be an error")
|
||||
}
|
||||
withSeams(t, root, "", func([]string) ([]byte, int, error) { t.Fatal("no image → no container"); return nil, 0, nil })
|
||||
if err := rsyncMirror(src, dst); err != nil {
|
||||
t.Fatalf("no readable image → the plain copy: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user