Ignore missing paths during enumeration
It's possible to run into a race condition in which the enumerator lists lots of repositories and then starts the long process of enumerating through them. In that time if someone deletes a repo, the enumerator may error out. Signed-off-by: Ryan Abrams <rdabrams@gmail.com>
This commit is contained in:
parent
9930542dc5
commit
6b73a9ab89
2 changed files with 57 additions and 1 deletions
|
@ -4,6 +4,8 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrSkipDir is used as a return value from onFileFunc to indicate that
|
// ErrSkipDir is used as a return value from onFileFunc to indicate that
|
||||||
|
@ -32,7 +34,14 @@ func WalkFallback(ctx context.Context, driver StorageDriver, from string, f Walk
|
||||||
// performance bottleneck.
|
// performance bottleneck.
|
||||||
fileInfo, err := driver.Stat(ctx, child)
|
fileInfo, err := driver.Stat(ctx, child)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
switch err.(type) {
|
||||||
|
case PathNotFoundError:
|
||||||
|
// repository was removed in between listing and enumeration. Ignore it.
|
||||||
|
logrus.WithField("path", child).Infof("ignoring deleted path")
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
err = f(fileInfo)
|
err = f(fileInfo)
|
||||||
if err == nil && fileInfo.IsDir() {
|
if err == nil && fileInfo.IsDir() {
|
||||||
|
|
47
registry/storage/driver/walk_test.go
Normal file
47
registry/storage/driver/walk_test.go
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type changingFileSystem struct {
|
||||||
|
StorageDriver
|
||||||
|
fileset []string
|
||||||
|
keptFiles map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfs *changingFileSystem) List(ctx context.Context, path string) ([]string, error) {
|
||||||
|
return cfs.fileset, nil
|
||||||
|
}
|
||||||
|
func (cfs *changingFileSystem) Stat(ctx context.Context, path string) (FileInfo, error) {
|
||||||
|
kept, ok := cfs.keptFiles[path]
|
||||||
|
if ok && kept {
|
||||||
|
return &FileInfoInternal{
|
||||||
|
FileInfoFields: FileInfoFields{
|
||||||
|
Path: path,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return nil, PathNotFoundError{}
|
||||||
|
}
|
||||||
|
func TestWalkFileRemoved(t *testing.T) {
|
||||||
|
d := &changingFileSystem{
|
||||||
|
fileset: []string{"zoidberg", "bender"},
|
||||||
|
keptFiles: map[string]bool{
|
||||||
|
"zoidberg": true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
infos := []FileInfo{}
|
||||||
|
err := WalkFallback(context.Background(), d, "", func(fileInfo FileInfo) error {
|
||||||
|
infos = append(infos, fileInfo)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if len(infos) != 1 || infos[0].Path() != "zoidberg" {
|
||||||
|
t.Errorf(fmt.Sprintf("unexpected path set during walk: %s", infos))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(err.Error())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue