Merge pull request #5183 from wplapper/cmd_prune

restic prune: selection of packs to repack based on size
This commit is contained in:
Michael Eischer
2025-03-22 15:43:32 +01:00
committed by GitHub
6 changed files with 158 additions and 11 deletions

View File

@@ -67,6 +67,9 @@ type PruneOptions struct {
RepackCacheableOnly bool
RepackSmall bool
RepackUncompressed bool
SmallPackSize string
SmallPackBytes uint64
}
func (opts *PruneOptions) AddFlags(f *pflag.FlagSet) {
@@ -81,6 +84,7 @@ func (opts *PruneOptions) AddLimitedFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.RepackCacheableOnly, "repack-cacheable-only", false, "only repack packs which are cacheable")
f.BoolVar(&opts.RepackSmall, "repack-small", false, "repack pack files below 80% of target pack size")
f.BoolVar(&opts.RepackUncompressed, "repack-uncompressed", false, "repack all uncompressed data")
f.StringVar(&opts.SmallPackSize, "repack-smaller-than", "", "pack `below-limit` packfiles (allowed suffixes: k/K, m/M)")
}
func verifyPruneOptions(opts *PruneOptions) error {
@@ -139,6 +143,15 @@ func verifyPruneOptions(opts *PruneOptions) error {
}
}
if opts.SmallPackSize != "" {
size, err := ui.ParseBytes(opts.SmallPackSize)
if err != nil {
return errors.Fatalf("invalid number of bytes %q for --repack-smaller-than: %v", opts.SmallPackSize, err)
}
opts.SmallPackBytes = uint64(size)
opts.RepackSmall = true
}
return nil
}
@@ -194,6 +207,7 @@ func runPruneWithRepo(ctx context.Context, opts PruneOptions, gopts GlobalOption
MaxUnusedBytes: opts.maxUnusedBytes,
MaxRepackBytes: opts.MaxRepackBytes,
SmallPackBytes: opts.SmallPackBytes,
RepackCacheableOnly: opts.RepackCacheableOnly,
RepackSmall: opts.RepackSmall,

View File

@@ -13,14 +13,25 @@ import (
)
func testRunPrune(t testing.TB, gopts GlobalOptions, opts PruneOptions) {
t.Helper()
rtest.OK(t, testRunPruneOutput(gopts, opts))
}
func testRunPruneMustFail(t testing.TB, gopts GlobalOptions, opts PruneOptions) {
t.Helper()
err := testRunPruneOutput(gopts, opts)
rtest.Assert(t, err != nil, "expected non nil error")
}
func testRunPruneOutput(gopts GlobalOptions, opts PruneOptions) error {
oldHook := gopts.backendTestHook
gopts.backendTestHook = func(r backend.Backend) (backend.Backend, error) { return newListOnceBackend(r), nil }
defer func() {
gopts.backendTestHook = oldHook
}()
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, term *termstatus.Terminal) error {
return withTermStatus(gopts, func(ctx context.Context, term *termstatus.Terminal) error {
return runPrune(context.TODO(), opts, gopts, term)
}))
})
}
func TestPrune(t *testing.T) {
@@ -237,3 +248,20 @@ func testEdgeCaseRepo(t *testing.T, tarfile string, optionsCheck CheckOptions, o
"prune should have reported an error")
}
}
func TestPruneRepackSmallerThanSmoke(t *testing.T) {
env, cleanup := withTestEnvironment(t)
defer cleanup()
// the implementation is already unit tested, so just check that
// the setting reaches its goal
createPrunableRepo(t, env)
testRunPrune(t, env.gopts, PruneOptions{
SmallPackSize: "4M",
MaxUnused: "5%",
})
testRunPruneMustFail(t, env.gopts, PruneOptions{
SmallPackSize: "500M",
MaxUnused: "5%",
})
}