forked from TrueCloudLab/distribution
Remove old walk function
This removes the old global walk function, and changes all the code to use the per-driver walk functions. Signed-off-by: Sargun Dhillon <sargun@sargun.me>
This commit is contained in:
parent
c7b0da2622
commit
cbcbcb02c5
7 changed files with 13 additions and 207 deletions
|
@ -63,7 +63,7 @@ func (reg *registry) Enumerate(ctx context.Context, ingester func(string) error)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = Walk(ctx, reg.blobStore.driver, root, func(fileInfo driver.FileInfo) error {
|
err = reg.blobStore.driver.Walk(ctx, root, func(fileInfo driver.FileInfo) error {
|
||||||
return handleRepository(fileInfo, root, "", ingester)
|
return handleRepository(fileInfo, root, "", ingester)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -203,7 +203,7 @@ func (base *Base) Walk(ctx context.Context, path string, f storagedriver.WalkFn)
|
||||||
ctx, done := dcontext.WithTrace(ctx)
|
ctx, done := dcontext.WithTrace(ctx)
|
||||||
defer done("%s.Walk(%q)", base.Name(), path)
|
defer done("%s.Walk(%q)", base.Name(), path)
|
||||||
|
|
||||||
if !storagedriver.PathRegexp.MatchString(path) {
|
if !storagedriver.PathRegexp.MatchString(path) && path != "/" {
|
||||||
return storagedriver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
|
return storagedriver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
9
registry/storage/error.go
Normal file
9
registry/storage/error.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package storage
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// pushError formats an error type given a path and an error
|
||||||
|
// and pushes it to a slice of errors
|
||||||
|
func pushError(errors []error, path string, err error) []error {
|
||||||
|
return append(errors, fmt.Errorf("%s: %s", path, err))
|
||||||
|
}
|
|
@ -67,7 +67,7 @@ func getOutstandingUploads(ctx context.Context, driver storageDriver.StorageDriv
|
||||||
return uploads, append(errors, err)
|
return uploads, append(errors, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = Walk(ctx, driver, root, func(fileInfo storageDriver.FileInfo) error {
|
err = driver.Walk(ctx, root, func(fileInfo storageDriver.FileInfo) error {
|
||||||
filePath := fileInfo.Path()
|
filePath := fileInfo.Path()
|
||||||
_, file := path.Split(filePath)
|
_, file := path.Split(filePath)
|
||||||
if file[0] == '_' {
|
if file[0] == '_' {
|
||||||
|
|
|
@ -142,7 +142,7 @@ func TestPurgeMissingStartedAt(t *testing.T) {
|
||||||
oneHourAgo := time.Now().Add(-1 * time.Hour)
|
oneHourAgo := time.Now().Add(-1 * time.Hour)
|
||||||
fs, ctx := testUploadFS(t, 1, "test-repo", oneHourAgo)
|
fs, ctx := testUploadFS(t, 1, "test-repo", oneHourAgo)
|
||||||
|
|
||||||
err := Walk(ctx, fs, "/", func(fileInfo driver.FileInfo) error {
|
err := fs.Walk(ctx, "/", func(fileInfo driver.FileInfo) error {
|
||||||
filePath := fileInfo.Path()
|
filePath := fileInfo.Path()
|
||||||
_, file := path.Split(filePath)
|
_, file := path.Split(filePath)
|
||||||
|
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
package storage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
storageDriver "github.com/docker/distribution/registry/storage/driver"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Walk traverses a filesystem defined within driver, starting
|
|
||||||
// from the given path, calling f on each file
|
|
||||||
// If the returned error from the WalkFn is ErrSkipDir and fileInfo refers
|
|
||||||
// to a directory, the directory will not be entered and Walk
|
|
||||||
// will continue the traversal. Otherwise Walk will return
|
|
||||||
// the error
|
|
||||||
func Walk(ctx context.Context, driver storageDriver.StorageDriver, from string, f storageDriver.WalkFn) error {
|
|
||||||
children, err := driver.List(ctx, from)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
sort.Stable(sort.StringSlice(children))
|
|
||||||
for _, child := range children {
|
|
||||||
// TODO(stevvooe): Calling driver.Stat for every entry is quite
|
|
||||||
// expensive when running against backends with a slow Stat
|
|
||||||
// implementation, such as s3. This is very likely a serious
|
|
||||||
// performance bottleneck.
|
|
||||||
fileInfo, err := driver.Stat(ctx, child)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = f(fileInfo)
|
|
||||||
skipDir := (err == storageDriver.ErrSkipDir)
|
|
||||||
if err != nil && !skipDir {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if fileInfo.IsDir() && !skipDir {
|
|
||||||
if err := Walk(ctx, driver, child, f); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// pushError formats an error type given a path and an error
|
|
||||||
// and pushes it to a slice of errors
|
|
||||||
func pushError(errors []error, path string, err error) []error {
|
|
||||||
return append(errors, fmt.Errorf("%s: %s", path, err))
|
|
||||||
}
|
|
|
@ -1,152 +0,0 @@
|
||||||
package storage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/docker/distribution/registry/storage/driver"
|
|
||||||
"github.com/docker/distribution/registry/storage/driver/inmemory"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testFS(t *testing.T) (driver.StorageDriver, map[string]string, context.Context) {
|
|
||||||
d := inmemory.New()
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
expected := map[string]string{
|
|
||||||
"/a": "dir",
|
|
||||||
"/a/b": "dir",
|
|
||||||
"/a/b/c": "dir",
|
|
||||||
"/a/b/c/d": "file",
|
|
||||||
"/a/b/c/e": "file",
|
|
||||||
"/a/b/f": "dir",
|
|
||||||
"/a/b/f/g": "file",
|
|
||||||
"/a/b/f/h": "file",
|
|
||||||
"/a/b/f/i": "file",
|
|
||||||
"/z": "dir",
|
|
||||||
"/z/y": "file",
|
|
||||||
}
|
|
||||||
|
|
||||||
for p, typ := range expected {
|
|
||||||
if typ != "file" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := d.PutContent(ctx, p, []byte(p)); err != nil {
|
|
||||||
t.Fatalf("unable to put content into fixture: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return d, expected, ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWalkErrors(t *testing.T) {
|
|
||||||
d, expected, ctx := testFS(t)
|
|
||||||
fileCount := len(expected)
|
|
||||||
err := Walk(ctx, d, "", func(fileInfo driver.FileInfo) error {
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err == nil {
|
|
||||||
t.Error("Expected invalid root err")
|
|
||||||
}
|
|
||||||
|
|
||||||
errEarlyExpected := fmt.Errorf("Early termination")
|
|
||||||
|
|
||||||
err = Walk(ctx, d, "/", func(fileInfo driver.FileInfo) error {
|
|
||||||
// error on the 2nd file
|
|
||||||
if fileInfo.Path() == "/a/b" {
|
|
||||||
return errEarlyExpected
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(expected, fileInfo.Path())
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if len(expected) != fileCount-1 {
|
|
||||||
t.Error("Walk failed to terminate with error")
|
|
||||||
}
|
|
||||||
if err != errEarlyExpected {
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("expected an error due to early termination")
|
|
||||||
} else {
|
|
||||||
t.Error(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = Walk(ctx, d, "/nonexistent", func(fileInfo driver.FileInfo) error {
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Expected missing file err")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWalk(t *testing.T) {
|
|
||||||
d, expected, ctx := testFS(t)
|
|
||||||
var traversed []string
|
|
||||||
err := Walk(ctx, d, "/", func(fileInfo driver.FileInfo) error {
|
|
||||||
filePath := fileInfo.Path()
|
|
||||||
filetype, ok := expected[filePath]
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("Unexpected file in walk: %q", filePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
if fileInfo.IsDir() {
|
|
||||||
if filetype != "dir" {
|
|
||||||
t.Errorf("Unexpected file type: %q", filePath)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if filetype != "file" {
|
|
||||||
t.Errorf("Unexpected file type: %q", filePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// each file has its own path as the contents. If the length
|
|
||||||
// doesn't match the path length, fail.
|
|
||||||
if fileInfo.Size() != int64(len(fileInfo.Path())) {
|
|
||||||
t.Fatalf("unexpected size for %q: %v != %v",
|
|
||||||
fileInfo.Path(), fileInfo.Size(), len(fileInfo.Path()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete(expected, filePath)
|
|
||||||
traversed = append(traversed, filePath)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if len(expected) > 0 {
|
|
||||||
t.Errorf("Missed files in walk: %q", expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !sort.StringsAreSorted(traversed) {
|
|
||||||
t.Errorf("result should be sorted: %v", traversed)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWalkSkipDir(t *testing.T) {
|
|
||||||
d, expected, ctx := testFS(t)
|
|
||||||
err := Walk(ctx, d, "/", func(fileInfo driver.FileInfo) error {
|
|
||||||
filePath := fileInfo.Path()
|
|
||||||
if filePath == "/a/b" {
|
|
||||||
// skip processing /a/b/c and /a/b/c/d
|
|
||||||
return driver.ErrSkipDir
|
|
||||||
}
|
|
||||||
delete(expected, filePath)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf(err.Error())
|
|
||||||
}
|
|
||||||
if _, ok := expected["/a/b/c"]; !ok {
|
|
||||||
t.Errorf("/a/b/c not skipped")
|
|
||||||
}
|
|
||||||
if _, ok := expected["/a/b/c/d"]; !ok {
|
|
||||||
t.Errorf("/a/b/c/d not skipped")
|
|
||||||
}
|
|
||||||
if _, ok := expected["/a/b/c/e"]; !ok {
|
|
||||||
t.Errorf("/a/b/c/e not skipped")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in a new issue