2
2
mirror of https://github.com/octoleo/restic.git synced 2024-11-27 15:26:37 +00:00

fix: restore inclusion logic and restore tests

doc: update exclude and include docs
This commit is contained in:
Srigovind Nayak 2024-06-10 01:55:39 +05:30
parent 188684ee9e
commit fe412e2553
No known key found for this signature in database
GPG Key ID: 3C4A72A34ABD4C43
4 changed files with 35 additions and 55 deletions

View File

@ -178,12 +178,12 @@ func runRestore(ctx context.Context, opts RestoreOptions, gopts GlobalOptions,
matched, childMayMatch := includeFn(item) matched, childMayMatch := includeFn(item)
selectedForRestore = selectedForRestore || matched selectedForRestore = selectedForRestore || matched
childMayBeSelected = childMayBeSelected || childMayMatch childMayBeSelected = childMayBeSelected || childMayMatch
childMayBeSelected = childMayBeSelected && node.Type == "dir"
if selectedForRestore || childMayBeSelected { if selectedForRestore && childMayBeSelected {
break break
} }
} }
childMayBeSelected = childMayBeSelected && node.Type == "dir"
return selectedForRestore, childMayBeSelected return selectedForRestore, childMayBeSelected
} }

View File

@ -13,7 +13,6 @@ import (
"time" "time"
"github.com/restic/restic/internal/feature" "github.com/restic/restic/internal/feature"
"github.com/restic/restic/internal/filter"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
"github.com/restic/restic/internal/ui/termstatus" "github.com/restic/restic/internal/ui/termstatus"
@ -78,32 +77,11 @@ func testRunRestoreExcludesFromFile(t testing.TB, gopts GlobalOptions, dir strin
} }
func TestRestoreMustFailWhenUsingBothIncludesAndExcludes(t *testing.T) { func TestRestoreMustFailWhenUsingBothIncludesAndExcludes(t *testing.T) {
testfiles := []struct {
path string
size uint
}{
{"dir1/include_me.txt", 100},
}
env, cleanup := withTestEnvironment(t) env, cleanup := withTestEnvironment(t)
defer cleanup() defer cleanup()
testRunInit(t, env.gopts) testRunInit(t, env.gopts)
// Create test files and directories
for _, testFile := range testfiles {
fullPath := filepath.Join(env.testdata, testFile.path)
rtest.OK(t, os.MkdirAll(filepath.Dir(fullPath), 0755))
rtest.OK(t, appendRandomData(fullPath, testFile.size))
}
opts := BackupOptions{}
// Perform backup
testRunBackup(t, filepath.Dir(env.testdata), []string{filepath.Base(env.testdata)}, opts, env.gopts)
testRunCheck(t, env.gopts)
snapshotID := testListSnapshots(t, env.gopts, 1)[0]
// Add both include and exclude patterns // Add both include and exclude patterns
includePatterns := []string{"dir1/*include_me.txt", "dir2/**", "dir4/**/*_me.txt"} includePatterns := []string{"dir1/*include_me.txt", "dir2/**", "dir4/**/*_me.txt"}
excludePatterns := []string{"dir1/*include_me.txt", "dir2/**", "dir4/**/*_me.txt"} excludePatterns := []string{"dir1/*include_me.txt", "dir2/**", "dir4/**/*_me.txt"}
@ -116,8 +94,9 @@ func TestRestoreMustFailWhenUsingBothIncludesAndExcludes(t *testing.T) {
restoreOpts.Includes = includePatterns restoreOpts.Includes = includePatterns
restoreOpts.Excludes = excludePatterns restoreOpts.Excludes = excludePatterns
err := testRunRestoreAssumeFailure(snapshotID.String(), restoreOpts, env.gopts) err := testRunRestoreAssumeFailure("latest", restoreOpts, env.gopts)
rtest.Assert(t, err != nil, "restore must fail if include and exclude patterns are provided") rtest.Assert(t, err != nil && strings.Contains(err.Error(), "exclude and include patterns are mutually exclusive"),
"expected: %s error, got %v", "exclude and include patterns are mutually exclusive", err)
} }
func TestRestoreIncludes(t *testing.T) { func TestRestoreIncludes(t *testing.T) {
@ -159,7 +138,7 @@ func TestRestoreIncludes(t *testing.T) {
restoredir := filepath.Join(env.base, "restore") restoredir := filepath.Join(env.base, "restore")
testRunRestoreIncludes(t, env.gopts, restoredir, snapshotID, includePatterns) testRunRestoreIncludes(t, env.gopts, restoredir, snapshotID, includePatterns)
testRestoreFileInclusions := func(t *testing.T, env *testEnvironment, includePatterns []string) { testRestoreFileInclusions := func(t *testing.T) {
// Check that only the included files are restored // Check that only the included files are restored
for _, testFile := range testfiles { for _, testFile := range testfiles {
restoredFilePath := filepath.Join(restoredir, "testdata", testFile.path) restoredFilePath := filepath.Join(restoredir, "testdata", testFile.path)
@ -172,7 +151,7 @@ func TestRestoreIncludes(t *testing.T) {
} }
} }
testRestoreFileInclusions(t, env, includePatterns) testRestoreFileInclusions(t)
// Create an include file with some patterns // Create an include file with some patterns
patternsFile := env.base + "/patternsFile" patternsFile := env.base + "/patternsFile"
@ -185,18 +164,19 @@ func TestRestoreIncludes(t *testing.T) {
testRunRestoreIncludesFromFile(t, env.gopts, restoredir, snapshotID, patternsFile) testRunRestoreIncludesFromFile(t, env.gopts, restoredir, snapshotID, patternsFile)
testRestoreFileInclusions(t, env, includePatterns) testRestoreFileInclusions(t)
} }
func TestRestoreFilter(t *testing.T) { func TestRestoreFilter(t *testing.T) {
testfiles := []struct { testfiles := []struct {
name string name string
size uint size uint
exclude bool
}{ }{
{"testfile1.c", 100}, {"testfile1.c", 100, true},
{"testfile2.exe", 101}, {"testfile2.exe", 101, true},
{"subdir1/subdir2/testfile3.docx", 102}, {"subdir1/subdir2/testfile3.docx", 102, true},
{"subdir1/subdir2/testfile4.c", 102}, {"subdir1/subdir2/testfile4.c", 102, false},
} }
env, cleanup := withTestEnvironment(t) env, cleanup := withTestEnvironment(t)
@ -223,38 +203,38 @@ func TestRestoreFilter(t *testing.T) {
rtest.OK(t, testFileSize(filepath.Join(env.base, "restore0", "testdata", testFile.name), int64(testFile.size))) rtest.OK(t, testFileSize(filepath.Join(env.base, "restore0", "testdata", testFile.name), int64(testFile.size)))
} }
excludePatterns := []string{"*.c", "*.exe", "*", "*file3*"} excludePatterns := []string{"testfile1.c", "*.exe", "*file3*"}
testRestoreFileExclusions := func(t *testing.T, env *testEnvironment, excludePatterns []string) { // checks if the files are excluded correctly
for i, pat := range excludePatterns { testRestoredFileExclusions := func(t *testing.T, restoredir string) {
base := filepath.Join(env.base, fmt.Sprintf("restore%d", i+1)) for _, testFile := range testfiles {
testRunRestoreExcludes(t, env.gopts, base, snapshotID, []string{pat}) restoredFilePath := filepath.Join(restoredir, "testdata", testFile.name)
for _, testFile := range testfiles { _, err := os.Stat(restoredFilePath)
err := testFileSize(filepath.Join(base, "testdata", testFile.name), int64(testFile.size)) if testFile.exclude {
if ok, _ := filter.Match(pat, filepath.Base(testFile.name)); !ok { rtest.Assert(t, os.IsNotExist(err), "File %s should not have been restored", testFile.name)
rtest.OK(t, err) } else {
} else { rtest.OK(t, testFileSize(restoredFilePath, int64(testFile.size)))
rtest.Assert(t, os.IsNotExist(err),
"expected %v to not exist in restore step %v, but it exists, err %v", testFile.name, i+1, err)
}
} }
} }
} }
testRestoreFileExclusions(t, env, excludePatterns) // restore with excludes
restoredir := filepath.Join(env.base, "restore-with-excludes")
testRunRestoreExcludes(t, env.gopts, restoredir, snapshotID, excludePatterns)
testRestoredFileExclusions(t, restoredir)
// Create an include file with some patterns // Create an exclude file with some patterns
patternsFile := env.base + "/patternsFile" patternsFile := env.base + "/patternsFile"
fileErr := os.WriteFile(patternsFile, []byte(strings.Join(excludePatterns, "\n")), 0644) fileErr := os.WriteFile(patternsFile, []byte(strings.Join(excludePatterns, "\n")), 0644)
if fileErr != nil { if fileErr != nil {
t.Fatalf("Could not write include file: %v", fileErr) t.Fatalf("Could not write include file: %v", fileErr)
} }
restoredir := filepath.Join(env.base, "restore-exclude-from-file") // restore with excludes from file
restoredir = filepath.Join(env.base, "restore-with-exclude-from-file")
testRunRestoreExcludesFromFile(t, env.gopts, restoredir, snapshotID, patternsFile) testRunRestoreExcludesFromFile(t, env.gopts, restoredir, snapshotID, patternsFile)
testRestoreFileExclusions(t, env, excludePatterns) testRestoredFileExclusions(t, restoredir)
} }
func TestRestore(t *testing.T) { func TestRestore(t *testing.T) {

View File

@ -443,7 +443,7 @@ func initExcludePatternOptions(f *pflag.FlagSet, opts *excludePatternOptions) {
f.StringArrayVarP(&opts.Excludes, "exclude", "e", nil, "exclude a `pattern` (can be specified multiple times)") f.StringArrayVarP(&opts.Excludes, "exclude", "e", nil, "exclude a `pattern` (can be specified multiple times)")
f.StringArrayVar(&opts.InsensitiveExcludes, "iexclude", nil, "same as --exclude `pattern` but ignores the casing of filenames") f.StringArrayVar(&opts.InsensitiveExcludes, "iexclude", nil, "same as --exclude `pattern` but ignores the casing of filenames")
f.StringArrayVar(&opts.ExcludeFiles, "exclude-file", nil, "read exclude patterns from a `file` (can be specified multiple times)") f.StringArrayVar(&opts.ExcludeFiles, "exclude-file", nil, "read exclude patterns from a `file` (can be specified multiple times)")
f.StringArrayVar(&opts.InsensitiveExcludeFiles, "iexclude-file", nil, "same as --exclude-file but ignores casing of filenames in patterns") f.StringArrayVar(&opts.InsensitiveExcludeFiles, "iexclude-file", nil, "same as --exclude-file but ignores casing of `file`names in patterns")
} }
func (opts *excludePatternOptions) Empty() bool { func (opts *excludePatternOptions) Empty() bool {

View File

@ -23,7 +23,7 @@ func initIncludePatternOptions(f *pflag.FlagSet, opts *includePatternOptions) {
f.StringArrayVarP(&opts.Includes, "include", "i", nil, "include a `pattern` (can be specified multiple times)") f.StringArrayVarP(&opts.Includes, "include", "i", nil, "include a `pattern` (can be specified multiple times)")
f.StringArrayVar(&opts.InsensitiveIncludes, "iinclude", nil, "same as --include `pattern` but ignores the casing of filenames") f.StringArrayVar(&opts.InsensitiveIncludes, "iinclude", nil, "same as --include `pattern` but ignores the casing of filenames")
f.StringArrayVar(&opts.IncludeFiles, "include-file", nil, "read include patterns from a `file` (can be specified multiple times)") f.StringArrayVar(&opts.IncludeFiles, "include-file", nil, "read include patterns from a `file` (can be specified multiple times)")
f.StringArrayVar(&opts.InsensitiveIncludeFiles, "iinclude-file", nil, "same as --include-file but ignores casing of filenames in patterns") f.StringArrayVar(&opts.InsensitiveIncludeFiles, "iinclude-file", nil, "same as --include-file but ignores casing of `file`names in patterns")
} }
func (opts includePatternOptions) CollectPatterns() ([]IncludeByNameFunc, error) { func (opts includePatternOptions) CollectPatterns() ([]IncludeByNameFunc, error) {