mirror of
https://github.com/kopia/kopia.git
synced 2026-03-25 01:21:16 -04:00
fix(snapshots): ErrorEntry policy resolution to use child policy (#5234)
Fix ErrorEntry child policy resolution, includes tests. - Fixes kopia/kopia#5232
This commit is contained in:
@@ -204,7 +204,7 @@ func (imd *Directory) AddDir(name string, permissions os.FileMode) *Directory {
|
||||
return subdir
|
||||
}
|
||||
|
||||
// AddErrorEntry adds a fake directory with a given name and permissions.
|
||||
// AddErrorEntry adds a fake directory-typed error entry with a given name and permissions.
|
||||
func (imd *Directory) AddErrorEntry(name string, permissions os.FileMode, err error) *ErrorEntry {
|
||||
imd, name = imd.resolveSubdir(name)
|
||||
|
||||
@@ -222,6 +222,24 @@ func (imd *Directory) AddErrorEntry(name string, permissions os.FileMode, err er
|
||||
return ee
|
||||
}
|
||||
|
||||
// AddFileErrorEntry adds a fake file-typed error entry with a given name and permissions.
|
||||
func (imd *Directory) AddFileErrorEntry(name string, permissions os.FileMode, err error) *ErrorEntry {
|
||||
imd, name = imd.resolveSubdir(name)
|
||||
|
||||
ee := &ErrorEntry{
|
||||
entry: entry{
|
||||
name: name,
|
||||
mode: permissions &^ os.ModeDir,
|
||||
modTime: DefaultModTime,
|
||||
},
|
||||
err: err,
|
||||
}
|
||||
|
||||
imd.addChild(ee)
|
||||
|
||||
return ee
|
||||
}
|
||||
|
||||
// AddDirDevice adds a fake directory with a given name and permissions.
|
||||
func (imd *Directory) AddDirDevice(name string, permissions os.FileMode, deviceInfo fs.DeviceInfo) *Directory {
|
||||
imd, name = imd.resolveSubdir(name)
|
||||
|
||||
@@ -928,8 +928,13 @@ func (u *Uploader) processSingle(
|
||||
prefix string
|
||||
)
|
||||
|
||||
// Use the child policy for the specific entry path, not the parent directory policy.
|
||||
// This ensures per-entry error handling rules are respected, consistent with how
|
||||
// directory processing derives childTree via policyTree.Child().
|
||||
childPolicy := policyTree.Child(entry.Name()).EffectivePolicy()
|
||||
|
||||
if errors.Is(entry.ErrorInfo(), fs.ErrUnknown) {
|
||||
isIgnoredError = policyTree.EffectivePolicy().ErrorHandlingPolicy.IgnoreUnknownTypes.OrDefault(true)
|
||||
isIgnoredError = childPolicy.ErrorHandlingPolicy.IgnoreUnknownTypes.OrDefault(true)
|
||||
|
||||
// If unknown types are configured to be ignored, skip them completely without any error reporting
|
||||
if isIgnoredError {
|
||||
@@ -939,7 +944,7 @@ func (u *Uploader) processSingle(
|
||||
prefix = "unknown entry"
|
||||
} else {
|
||||
prefix = "error"
|
||||
ehp := policyTree.EffectivePolicy().ErrorHandlingPolicy
|
||||
ehp := childPolicy.ErrorHandlingPolicy
|
||||
|
||||
if entry.IsDir() {
|
||||
isIgnoredError = ehp.IgnoreDirectoryErrors.OrDefault(false)
|
||||
|
||||
@@ -520,6 +520,117 @@ func TestUpload_ErrorEntries(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpload_ErrorEntryChildPolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testlogging.Context(t)
|
||||
th := newUploadTestHarness(ctx, t)
|
||||
|
||||
t.Cleanup(th.cleanup)
|
||||
|
||||
// Add a dir-typed error entry, a file-typed error entry, and an unknown-typed error entry under d1.
|
||||
th.sourceDir.Subdir("d1").AddErrorEntry("dir-err", 0, errors.New("dir-error"))
|
||||
th.sourceDir.Subdir("d1").AddFileErrorEntry("file-err", 0, errors.New("file-error"))
|
||||
th.sourceDir.Subdir("d1").AddErrorEntry("unknown-err", os.ModeIrregular, fs.ErrUnknown)
|
||||
|
||||
trueValue := policy.OptionalBool(true)
|
||||
falseValue := policy.OptionalBool(false)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
defined map[string]*policy.Policy
|
||||
defaultPolicy *policy.Policy
|
||||
wantFatalErrors int
|
||||
wantIgnoredErrors int
|
||||
wantErrors entryPathToError
|
||||
}{
|
||||
{
|
||||
desc: "child policy ignores dir errors only",
|
||||
defined: map[string]*policy.Policy{
|
||||
"./d1/dir-err": {
|
||||
ErrorHandlingPolicy: policy.ErrorHandlingPolicy{
|
||||
IgnoreDirectoryErrors: &trueValue,
|
||||
IgnoreFileErrors: &falseValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultPolicy: &policy.Policy{
|
||||
ErrorHandlingPolicy: policy.ErrorHandlingPolicy{
|
||||
IgnoreDirectoryErrors: &falseValue,
|
||||
IgnoreFileErrors: &falseValue,
|
||||
},
|
||||
},
|
||||
wantFatalErrors: 1, // file-err is fatal (uses default policy)
|
||||
wantIgnoredErrors: 1, // dir-err is ignored (uses child policy)
|
||||
// unknown-err is silently skipped (IgnoreUnknownTypes defaults to true)
|
||||
wantErrors: entryPathToError{
|
||||
"d1/dir-err": errors.New("dir-error"),
|
||||
"d1/file-err": errors.New("file-error"),
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "child policy ignores file errors only",
|
||||
defined: map[string]*policy.Policy{
|
||||
"./d1/file-err": {
|
||||
ErrorHandlingPolicy: policy.ErrorHandlingPolicy{
|
||||
IgnoreDirectoryErrors: &falseValue,
|
||||
IgnoreFileErrors: &trueValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultPolicy: &policy.Policy{
|
||||
ErrorHandlingPolicy: policy.ErrorHandlingPolicy{
|
||||
IgnoreDirectoryErrors: &falseValue,
|
||||
IgnoreFileErrors: &falseValue,
|
||||
},
|
||||
},
|
||||
wantFatalErrors: 1, // dir-err is fatal (uses default policy)
|
||||
wantIgnoredErrors: 1, // file-err is ignored (uses child policy)
|
||||
// unknown-err is silently skipped (IgnoreUnknownTypes defaults to true)
|
||||
wantErrors: entryPathToError{
|
||||
"d1/dir-err": errors.New("dir-error"),
|
||||
"d1/file-err": errors.New("file-error"),
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "child policy disables unknown type ignore",
|
||||
defined: map[string]*policy.Policy{
|
||||
"./d1/unknown-err": {
|
||||
ErrorHandlingPolicy: policy.ErrorHandlingPolicy{
|
||||
IgnoreUnknownTypes: &falseValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultPolicy: &policy.Policy{
|
||||
ErrorHandlingPolicy: policy.ErrorHandlingPolicy{
|
||||
IgnoreDirectoryErrors: &trueValue,
|
||||
IgnoreFileErrors: &trueValue,
|
||||
},
|
||||
},
|
||||
wantFatalErrors: 1, // unknown-err is fatal (child policy overrides default)
|
||||
wantIgnoredErrors: 2, // dir-err and file-err are ignored (default policy)
|
||||
wantErrors: entryPathToError{
|
||||
"d1/dir-err": errors.New("dir-error"),
|
||||
"d1/file-err": errors.New("file-error"),
|
||||
"d1/unknown-err": fs.ErrUnknown,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
u := NewUploader(th.repo)
|
||||
|
||||
policyTree := policy.BuildTree(tc.defined, tc.defaultPolicy)
|
||||
|
||||
man, err := u.Upload(ctx, th.sourceDir, policyTree, snapshot.SourceInfo{})
|
||||
require.NoError(t, err)
|
||||
|
||||
verifyErrors(t, man, tc.wantFatalErrors, tc.wantIgnoredErrors, tc.wantErrors)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func verifyErrors(t *testing.T, man *snapshot.Manifest, wantFatalErrors, wantIgnoredErrors int, wantErrors entryPathToError) {
|
||||
t.Helper()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user