diff --git a/storage/manifest.go b/storage/manifest.go index daeaa39b3..6c5062446 100644 --- a/storage/manifest.go +++ b/storage/manifest.go @@ -13,6 +13,16 @@ import ( "github.com/docker/docker-registry/digest" ) +// ErrUnknownRepository is returned if the named repository is not known by +// the registry. +type ErrUnknownRepository struct { + Name string +} + +func (err ErrUnknownRepository) Error() string { + return fmt.Sprintf("unknown respository name=%s", err.Name) +} + // ErrUnknownManifest is returned if the manifest is not known by the // registry. type ErrUnknownManifest struct { diff --git a/storage/manifest_test.go b/storage/manifest_test.go index e45179437..ea634df8d 100644 --- a/storage/manifest_test.go +++ b/storage/manifest_test.go @@ -99,6 +99,20 @@ func TestManifestStorage(t *testing.T) { if !reflect.DeepEqual(fetchedManifest, sm) { t.Fatalf("fetched manifest not equal: %#v != %#v", fetchedManifest, sm) } + + // Grabs the tags and check that this tagged manifest is present + tags, err := ms.Tags(name) + if err != nil { + t.Fatalf("unexpected error fetching tags: %v", err) + } + + if len(tags) != 1 { + t.Fatalf("unexpected tags returned: %v", tags) + } + + if tags[0] != tag { + t.Fatalf("unexpected tag found in tags: %v != %v", tags, []string{tag}) + } } type layerKey struct { diff --git a/storage/manifeststore.go b/storage/manifeststore.go index ebbc6b3c7..33b16390e 100644 --- a/storage/manifeststore.go +++ b/storage/manifeststore.go @@ -3,6 +3,7 @@ package storage import ( "encoding/json" "fmt" + "path" "github.com/docker/docker-registry/storagedriver" "github.com/docker/libtrust" @@ -16,6 +17,35 @@ type manifestStore struct { var _ ManifestService = &manifestStore{} +func (ms *manifestStore) Tags(name string) ([]string, error) { + p, err := ms.pathMapper.path(manifestTagsPath{ + name: name, + }) + if err != nil { + return nil, err + } + + var tags []string + entries, err := ms.driver.List(p) + if err != nil { + logrus.Infof("%#v", err) + switch err := err.(type) { + case storagedriver.PathNotFoundError: + return nil, ErrUnknownRepository{Name: name} + default: + return nil, err + } + } + + for _, entry := range entries { + _, filename := path.Split(entry) + + tags = append(tags, filename) + } + + return tags, nil +} + func (ms *manifestStore) Exists(name, tag string) (bool, error) { p, err := ms.path(name, tag) if err != nil { diff --git a/storage/paths.go b/storage/paths.go index ecc3dd32a..a3538b855 100644 --- a/storage/paths.go +++ b/storage/paths.go @@ -64,6 +64,8 @@ func (pm *pathMapper) path(spec pathSpec) (string, error) { repoPrefix := append(rootPrefix, "repositories") switch v := spec.(type) { + case manifestTagsPath: + return path.Join(append(repoPrefix, v.name, "manifests")...), nil case manifestPathSpec: // TODO(sday): May need to store manifest by architecture. return path.Join(append(repoPrefix, v.name, "manifests", v.tag)...), nil @@ -109,6 +111,14 @@ type pathSpec interface { pathSpec() } +// manifestTagsPath describes the path elements required to point to the +// directory with all manifest tags under the repository. +type manifestTagsPath struct { + name string +} + +func (manifestTagsPath) pathSpec() {} + // manifestPathSpec describes the path elements used to build a manifest path. // The contents should be a signed manifest json file. type manifestPathSpec struct { diff --git a/storage/services.go b/storage/services.go index 1f6d5e51a..da6d88c5d 100644 --- a/storage/services.go +++ b/storage/services.go @@ -52,6 +52,9 @@ func (ss *Services) Manifests() ManifestService { // ManifestService provides operations on image manifests. type ManifestService interface { + // Tags lists the tags under the named repository. + Tags(name string) ([]string, error) + // Exists returns true if the layer exists. Exists(name, tag string) (bool, error)