Merge pull request #653 from pdevine/catalog-api
Catalog for V2 API Implementation
This commit is contained in:
commit
a1ce8d81f7
10 changed files with 835 additions and 1 deletions
65
docs/storage/catalog.go
Normal file
65
docs/storage/catalog.go
Normal file
|
@ -0,0 +1,65 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/registry/storage/driver"
|
||||
)
|
||||
|
||||
// Returns a list, or partial list, of repositories in the registry.
|
||||
// Because it's a quite expensive operation, it should only be used when building up
|
||||
// an initial set of repositories.
|
||||
func (reg *registry) Repositories(ctx context.Context, repos []string, last string) (n int, err error) {
|
||||
var foundRepos []string
|
||||
var errVal error
|
||||
|
||||
if len(repos) == 0 {
|
||||
return 0, errors.New("no space in slice")
|
||||
}
|
||||
|
||||
root, err := defaultPathMapper.path(repositoriesRootPathSpec{})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Walk each of the directories in our storage. Unfortunately since there's no
|
||||
// guarantee that storage will return files in lexigraphical order, we have
|
||||
// to store everything another slice, sort it and then copy it back to our
|
||||
// passed in slice.
|
||||
|
||||
Walk(ctx, reg.blobStore.driver, root, func(fileInfo driver.FileInfo) error {
|
||||
filePath := fileInfo.Path()
|
||||
|
||||
// lop the base path off
|
||||
repoPath := filePath[len(root)+1:]
|
||||
|
||||
_, file := path.Split(repoPath)
|
||||
if file == "_layers" {
|
||||
repoPath = strings.TrimSuffix(repoPath, "/_layers")
|
||||
if repoPath > last {
|
||||
foundRepos = append(foundRepos, repoPath)
|
||||
}
|
||||
return ErrSkipDir
|
||||
} else if strings.HasPrefix(file, "_") {
|
||||
return ErrSkipDir
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
sort.Strings(foundRepos)
|
||||
n = copy(repos, foundRepos)
|
||||
|
||||
// Signal that we have no more entries by setting EOF
|
||||
if len(foundRepos) <= len(repos) {
|
||||
errVal = io.EOF
|
||||
}
|
||||
|
||||
return n, errVal
|
||||
|
||||
}
|
122
docs/storage/catalog_test.go
Normal file
122
docs/storage/catalog_test.go
Normal file
|
@ -0,0 +1,122 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/registry/storage/cache/memory"
|
||||
"github.com/docker/distribution/registry/storage/driver"
|
||||
"github.com/docker/distribution/registry/storage/driver/inmemory"
|
||||
)
|
||||
|
||||
type setupEnv struct {
|
||||
ctx context.Context
|
||||
driver driver.StorageDriver
|
||||
expected []string
|
||||
registry distribution.Namespace
|
||||
}
|
||||
|
||||
func setupFS(t *testing.T) *setupEnv {
|
||||
d := inmemory.New()
|
||||
c := []byte("")
|
||||
ctx := context.Background()
|
||||
registry := NewRegistryWithDriver(ctx, d, memory.NewInMemoryBlobDescriptorCacheProvider())
|
||||
rootpath, _ := defaultPathMapper.path(repositoriesRootPathSpec{})
|
||||
|
||||
repos := []string{
|
||||
"/foo/a/_layers/1",
|
||||
"/foo/b/_layers/2",
|
||||
"/bar/c/_layers/3",
|
||||
"/bar/d/_layers/4",
|
||||
"/foo/d/in/_layers/5",
|
||||
"/an/invalid/repo",
|
||||
"/bar/d/_layers/ignored/dir/6",
|
||||
}
|
||||
|
||||
for _, repo := range repos {
|
||||
if err := d.PutContent(ctx, rootpath+repo, c); err != nil {
|
||||
t.Fatalf("Unable to put to inmemory fs")
|
||||
}
|
||||
}
|
||||
|
||||
expected := []string{
|
||||
"bar/c",
|
||||
"bar/d",
|
||||
"foo/a",
|
||||
"foo/b",
|
||||
"foo/d/in",
|
||||
}
|
||||
|
||||
return &setupEnv{
|
||||
ctx: ctx,
|
||||
driver: d,
|
||||
expected: expected,
|
||||
registry: registry,
|
||||
}
|
||||
}
|
||||
|
||||
func TestCatalog(t *testing.T) {
|
||||
env := setupFS(t)
|
||||
|
||||
p := make([]string, 50)
|
||||
|
||||
numFilled, err := env.registry.Repositories(env.ctx, p, "")
|
||||
|
||||
if !testEq(p, env.expected, numFilled) {
|
||||
t.Errorf("Expected catalog repos err")
|
||||
}
|
||||
|
||||
if err != io.EOF {
|
||||
t.Errorf("Catalog has more values which we aren't expecting")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCatalogInParts(t *testing.T) {
|
||||
env := setupFS(t)
|
||||
|
||||
chunkLen := 2
|
||||
p := make([]string, chunkLen)
|
||||
|
||||
numFilled, err := env.registry.Repositories(env.ctx, p, "")
|
||||
if err == io.EOF || numFilled != len(p) {
|
||||
t.Errorf("Expected more values in catalog")
|
||||
}
|
||||
|
||||
if !testEq(p, env.expected[0:chunkLen], numFilled) {
|
||||
t.Errorf("Expected catalog first chunk err")
|
||||
}
|
||||
|
||||
lastRepo := p[len(p)-1]
|
||||
numFilled, err = env.registry.Repositories(env.ctx, p, lastRepo)
|
||||
|
||||
if err == io.EOF || numFilled != len(p) {
|
||||
t.Errorf("Expected more values in catalog")
|
||||
}
|
||||
|
||||
if !testEq(p, env.expected[chunkLen:chunkLen*2], numFilled) {
|
||||
t.Errorf("Expected catalog second chunk err")
|
||||
}
|
||||
|
||||
lastRepo = p[len(p)-1]
|
||||
numFilled, err = env.registry.Repositories(env.ctx, p, lastRepo)
|
||||
|
||||
if err != io.EOF {
|
||||
t.Errorf("Catalog has more values which we aren't expecting")
|
||||
}
|
||||
|
||||
if !testEq(p, env.expected[chunkLen*2:chunkLen*3-1], numFilled) {
|
||||
t.Errorf("Expected catalog third chunk err")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func testEq(a, b []string, size int) bool {
|
||||
for cnt := 0; cnt < size-1; cnt++ {
|
||||
if a[cnt] != b[cnt] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue