Those methods now only allow modifying snapshots. Internal data types used by the repository are now read-only. The repository-internal code can bypass the restrictions by wrapping the repository in an `unpackedRepository` type. The restriction itself is implemented by using a new datatype WriteableFileType in the SaveUnpacked and RemoveUnpacked methods. This statically ensures that code cannot bypass the access restrictions. The test changes are somewhat noisy as some of them modify repository internals and therefore require some way to bypass the access restrictions. This works by capturing an `unpackedRepository` or `Backend` when creating the Repository using a test helper function.
114 lines
3.1 KiB
Go
114 lines
3.1 KiB
Go
package repository_test
|
|
|
|
import (
|
|
"context"
|
|
"math"
|
|
"math/rand"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/restic/restic/internal/checker"
|
|
"github.com/restic/restic/internal/repository"
|
|
"github.com/restic/restic/internal/restic"
|
|
rtest "github.com/restic/restic/internal/test"
|
|
"github.com/restic/restic/internal/ui/progress"
|
|
"golang.org/x/sync/errgroup"
|
|
)
|
|
|
|
func testPrune(t *testing.T, opts repository.PruneOptions, errOnUnused bool) {
|
|
seed := time.Now().UnixNano()
|
|
random := rand.New(rand.NewSource(seed))
|
|
t.Logf("rand initialized with seed %d", seed)
|
|
|
|
repo, _, be := repository.TestRepositoryWithVersion(t, 0)
|
|
createRandomBlobs(t, random, repo, 4, 0.5, true)
|
|
createRandomBlobs(t, random, repo, 5, 0.5, true)
|
|
keep, _ := selectBlobs(t, random, repo, 0.5)
|
|
|
|
var wg errgroup.Group
|
|
repo.StartPackUploader(context.TODO(), &wg)
|
|
// duplicate a few blobs to exercise those code paths
|
|
for blob := range keep {
|
|
buf, err := repo.LoadBlob(context.TODO(), blob.Type, blob.ID, nil)
|
|
rtest.OK(t, err)
|
|
_, _, _, err = repo.SaveBlob(context.TODO(), blob.Type, buf, blob.ID, true)
|
|
rtest.OK(t, err)
|
|
}
|
|
rtest.OK(t, repo.Flush(context.TODO()))
|
|
|
|
plan, err := repository.PlanPrune(context.TODO(), opts, repo, func(ctx context.Context, repo restic.Repository, usedBlobs restic.FindBlobSet) error {
|
|
for blob := range keep {
|
|
usedBlobs.Insert(blob)
|
|
}
|
|
return nil
|
|
}, &progress.NoopPrinter{})
|
|
rtest.OK(t, err)
|
|
|
|
rtest.OK(t, plan.Execute(context.TODO(), &progress.NoopPrinter{}))
|
|
|
|
repo = repository.TestOpenBackend(t, be)
|
|
checker.TestCheckRepo(t, repo, true)
|
|
|
|
if errOnUnused {
|
|
existing := listBlobs(repo)
|
|
rtest.Assert(t, existing.Equals(keep), "unexpected blobs, wanted %v got %v", keep, existing)
|
|
}
|
|
}
|
|
|
|
func TestPrune(t *testing.T) {
|
|
for _, test := range []struct {
|
|
name string
|
|
opts repository.PruneOptions
|
|
errOnUnused bool
|
|
}{
|
|
{
|
|
name: "0",
|
|
opts: repository.PruneOptions{
|
|
MaxRepackBytes: math.MaxUint64,
|
|
MaxUnusedBytes: func(used uint64) (unused uint64) { return 0 },
|
|
},
|
|
errOnUnused: true,
|
|
},
|
|
{
|
|
name: "50",
|
|
opts: repository.PruneOptions{
|
|
MaxRepackBytes: math.MaxUint64,
|
|
MaxUnusedBytes: func(used uint64) (unused uint64) { return used / 2 },
|
|
},
|
|
},
|
|
{
|
|
name: "unlimited",
|
|
opts: repository.PruneOptions{
|
|
MaxRepackBytes: math.MaxUint64,
|
|
MaxUnusedBytes: func(used uint64) (unused uint64) { return math.MaxUint64 },
|
|
},
|
|
},
|
|
{
|
|
name: "cachableonly",
|
|
opts: repository.PruneOptions{
|
|
MaxRepackBytes: math.MaxUint64,
|
|
MaxUnusedBytes: func(used uint64) (unused uint64) { return used / 20 },
|
|
RepackCacheableOnly: true,
|
|
},
|
|
},
|
|
{
|
|
name: "small",
|
|
opts: repository.PruneOptions{
|
|
MaxRepackBytes: math.MaxUint64,
|
|
MaxUnusedBytes: func(used uint64) (unused uint64) { return math.MaxUint64 },
|
|
RepackSmall: true,
|
|
},
|
|
errOnUnused: true,
|
|
},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
testPrune(t, test.opts, test.errOnUnused)
|
|
})
|
|
t.Run(test.name+"-recovery", func(t *testing.T) {
|
|
opts := test.opts
|
|
opts.UnsafeRecovery = true
|
|
// unsafeNoSpaceRecovery does not repack partially used pack files
|
|
testPrune(t, opts, false)
|
|
})
|
|
}
|
|
}
|