mirror of
https://github.com/octoleo/restic.git
synced 2024-06-10 04:42:21 +00:00
985722b102
Previously the global context was either accessed via gopts.ctx, stored in a local variable and then used within that function or sometimes both. This makes it very hard to follow which ctx or a wrapped version of it reaches which method. Thus just drop the context from the globalOptions struct and pass it explicitly to every command line handler method.
220 lines
5.9 KiB
Go
220 lines
5.9 KiB
Go
//go:build darwin || freebsd || linux
|
|
// +build darwin freebsd linux
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/restic/restic/internal/repository"
|
|
"github.com/restic/restic/internal/restic"
|
|
rtest "github.com/restic/restic/internal/test"
|
|
)
|
|
|
|
const (
|
|
mountWait = 20
|
|
mountSleep = 100 * time.Millisecond
|
|
mountTestSubdir = "snapshots"
|
|
)
|
|
|
|
func snapshotsDirExists(t testing.TB, dir string) bool {
|
|
f, err := os.Open(filepath.Join(dir, mountTestSubdir))
|
|
if err != nil && os.IsNotExist(err) {
|
|
return false
|
|
}
|
|
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if err := f.Close(); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// waitForMount blocks (max mountWait * mountSleep) until the subdir
|
|
// "snapshots" appears in the dir.
|
|
func waitForMount(t testing.TB, dir string) {
|
|
for i := 0; i < mountWait; i++ {
|
|
if snapshotsDirExists(t, dir) {
|
|
t.Log("mounted directory is ready")
|
|
return
|
|
}
|
|
|
|
time.Sleep(mountSleep)
|
|
}
|
|
|
|
t.Errorf("subdir %q of dir %s never appeared", mountTestSubdir, dir)
|
|
}
|
|
|
|
func testRunMount(t testing.TB, gopts GlobalOptions, dir string) {
|
|
opts := MountOptions{
|
|
TimeTemplate: time.RFC3339,
|
|
}
|
|
rtest.OK(t, runMount(context.TODO(), opts, gopts, []string{dir}))
|
|
}
|
|
|
|
func testRunUmount(t testing.TB, gopts GlobalOptions, dir string) {
|
|
var err error
|
|
for i := 0; i < mountWait; i++ {
|
|
if err = umount(dir); err == nil {
|
|
t.Logf("directory %v umounted", dir)
|
|
return
|
|
}
|
|
|
|
time.Sleep(mountSleep)
|
|
}
|
|
|
|
t.Errorf("unable to umount dir %v, last error was: %v", dir, err)
|
|
}
|
|
|
|
func listSnapshots(t testing.TB, dir string) []string {
|
|
snapshotsDir, err := os.Open(filepath.Join(dir, "snapshots"))
|
|
rtest.OK(t, err)
|
|
names, err := snapshotsDir.Readdirnames(-1)
|
|
rtest.OK(t, err)
|
|
rtest.OK(t, snapshotsDir.Close())
|
|
return names
|
|
}
|
|
|
|
func checkSnapshots(t testing.TB, global GlobalOptions, repo *repository.Repository, mountpoint, repodir string, snapshotIDs restic.IDs, expectedSnapshotsInFuseDir int) {
|
|
t.Logf("checking for %d snapshots: %v", len(snapshotIDs), snapshotIDs)
|
|
|
|
go testRunMount(t, global, mountpoint)
|
|
waitForMount(t, mountpoint)
|
|
defer testRunUmount(t, global, mountpoint)
|
|
|
|
if !snapshotsDirExists(t, mountpoint) {
|
|
t.Fatal(`virtual directory "snapshots" doesn't exist`)
|
|
}
|
|
|
|
ids := listSnapshots(t, repodir)
|
|
t.Logf("found %v snapshots in repo: %v", len(ids), ids)
|
|
|
|
namesInSnapshots := listSnapshots(t, mountpoint)
|
|
t.Logf("found %v snapshots in fuse mount: %v", len(namesInSnapshots), namesInSnapshots)
|
|
rtest.Assert(t,
|
|
expectedSnapshotsInFuseDir == len(namesInSnapshots),
|
|
"Invalid number of snapshots: expected %d, got %d", expectedSnapshotsInFuseDir, len(namesInSnapshots))
|
|
|
|
namesMap := make(map[string]bool)
|
|
for _, name := range namesInSnapshots {
|
|
namesMap[name] = false
|
|
}
|
|
|
|
// Is "latest" present?
|
|
if len(namesMap) != 0 {
|
|
_, ok := namesMap["latest"]
|
|
if !ok {
|
|
t.Errorf("Symlink latest isn't present in fuse dir")
|
|
} else {
|
|
namesMap["latest"] = true
|
|
}
|
|
}
|
|
|
|
for _, id := range snapshotIDs {
|
|
snapshot, err := restic.LoadSnapshot(context.TODO(), repo, id)
|
|
rtest.OK(t, err)
|
|
|
|
ts := snapshot.Time.Format(time.RFC3339)
|
|
present, ok := namesMap[ts]
|
|
if !ok {
|
|
t.Errorf("Snapshot %v (%q) isn't present in fuse dir", id.Str(), ts)
|
|
}
|
|
|
|
for i := 1; present; i++ {
|
|
ts = fmt.Sprintf("%s-%d", snapshot.Time.Format(time.RFC3339), i)
|
|
present, ok = namesMap[ts]
|
|
if !ok {
|
|
t.Errorf("Snapshot %v (%q) isn't present in fuse dir", id.Str(), ts)
|
|
}
|
|
|
|
if !present {
|
|
break
|
|
}
|
|
}
|
|
|
|
namesMap[ts] = true
|
|
}
|
|
|
|
for name, present := range namesMap {
|
|
rtest.Assert(t, present, "Directory %s is present in fuse dir but is not a snapshot", name)
|
|
}
|
|
}
|
|
|
|
func TestMount(t *testing.T) {
|
|
if !rtest.RunFuseTest {
|
|
t.Skip("Skipping fuse tests")
|
|
}
|
|
|
|
env, cleanup := withTestEnvironment(t)
|
|
// must list snapshots more than once
|
|
env.gopts.backendTestHook = nil
|
|
defer cleanup()
|
|
|
|
testRunInit(t, env.gopts)
|
|
|
|
repo, err := OpenRepository(context.TODO(), env.gopts)
|
|
rtest.OK(t, err)
|
|
|
|
checkSnapshots(t, env.gopts, repo, env.mountpoint, env.repo, []restic.ID{}, 0)
|
|
|
|
rtest.SetupTarTestFixture(t, env.testdata, filepath.Join("testdata", "backup-data.tar.gz"))
|
|
|
|
// first backup
|
|
testRunBackup(t, "", []string{env.testdata}, BackupOptions{}, env.gopts)
|
|
snapshotIDs := testRunList(t, "snapshots", env.gopts)
|
|
rtest.Assert(t, len(snapshotIDs) == 1,
|
|
"expected one snapshot, got %v", snapshotIDs)
|
|
|
|
checkSnapshots(t, env.gopts, repo, env.mountpoint, env.repo, snapshotIDs, 2)
|
|
|
|
// second backup, implicit incremental
|
|
testRunBackup(t, "", []string{env.testdata}, BackupOptions{}, env.gopts)
|
|
snapshotIDs = testRunList(t, "snapshots", env.gopts)
|
|
rtest.Assert(t, len(snapshotIDs) == 2,
|
|
"expected two snapshots, got %v", snapshotIDs)
|
|
|
|
checkSnapshots(t, env.gopts, repo, env.mountpoint, env.repo, snapshotIDs, 3)
|
|
|
|
// third backup, explicit incremental
|
|
bopts := BackupOptions{Parent: snapshotIDs[0].String()}
|
|
testRunBackup(t, "", []string{env.testdata}, bopts, env.gopts)
|
|
snapshotIDs = testRunList(t, "snapshots", env.gopts)
|
|
rtest.Assert(t, len(snapshotIDs) == 3,
|
|
"expected three snapshots, got %v", snapshotIDs)
|
|
|
|
checkSnapshots(t, env.gopts, repo, env.mountpoint, env.repo, snapshotIDs, 4)
|
|
}
|
|
|
|
func TestMountSameTimestamps(t *testing.T) {
|
|
if !rtest.RunFuseTest {
|
|
t.Skip("Skipping fuse tests")
|
|
}
|
|
|
|
env, cleanup := withTestEnvironment(t)
|
|
// must list snapshots more than once
|
|
env.gopts.backendTestHook = nil
|
|
defer cleanup()
|
|
|
|
rtest.SetupTarTestFixture(t, env.base, filepath.Join("testdata", "repo-same-timestamps.tar.gz"))
|
|
|
|
repo, err := OpenRepository(context.TODO(), env.gopts)
|
|
rtest.OK(t, err)
|
|
|
|
ids := []restic.ID{
|
|
restic.TestParseID("280303689e5027328889a06d718b729e96a1ce6ae9ef8290bff550459ae611ee"),
|
|
restic.TestParseID("75ad6cdc0868e082f2596d5ab8705e9f7d87316f5bf5690385eeff8dbe49d9f5"),
|
|
restic.TestParseID("5fd0d8b2ef0fa5d23e58f1e460188abb0f525c0f0c4af8365a1280c807a80a1b"),
|
|
}
|
|
|
|
checkSnapshots(t, env.gopts, repo, env.mountpoint, env.repo, ids, 4)
|
|
}
|