Merge pull request #2748 from manishtomar/tag-digests
API to retrive tag's digests
This commit is contained in:
commit
ae2e973db9
3 changed files with 152 additions and 1 deletions
|
@ -177,3 +177,37 @@ func (ts *tagStore) Lookup(ctx context.Context, desc distribution.Descriptor) ([
|
||||||
|
|
||||||
return tags, nil
|
return tags, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ts *tagStore) ManifestDigests(ctx context.Context, tag string) ([]digest.Digest, error) {
|
||||||
|
var tagLinkPath = func(name string, dgst digest.Digest) (string, error) {
|
||||||
|
return pathFor(manifestTagIndexEntryLinkPathSpec{
|
||||||
|
name: name,
|
||||||
|
tag: tag,
|
||||||
|
revision: dgst,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
lbs := &linkedBlobStore{
|
||||||
|
blobStore: ts.blobStore,
|
||||||
|
blobAccessController: &linkedBlobStatter{
|
||||||
|
blobStore: ts.blobStore,
|
||||||
|
repository: ts.repository,
|
||||||
|
linkPathFns: []linkPathFunc{manifestRevisionLinkPath},
|
||||||
|
},
|
||||||
|
repository: ts.repository,
|
||||||
|
ctx: ctx,
|
||||||
|
linkPathFns: []linkPathFunc{tagLinkPath},
|
||||||
|
linkDirectoryPathSpec: manifestTagIndexPathSpec{
|
||||||
|
name: ts.repository.Named().Name(),
|
||||||
|
tag: tag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var dgsts []digest.Digest
|
||||||
|
err := lbs.Enumerate(ctx, func(dgst digest.Digest) error {
|
||||||
|
dgsts = append(dgsts, dgst)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dgsts, nil
|
||||||
|
}
|
||||||
|
|
|
@ -2,15 +2,22 @@ package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/distribution"
|
"github.com/docker/distribution"
|
||||||
|
"github.com/docker/distribution/manifest"
|
||||||
|
"github.com/docker/distribution/manifest/schema2"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
"github.com/docker/distribution/registry/storage/driver/inmemory"
|
"github.com/docker/distribution/registry/storage/driver/inmemory"
|
||||||
|
digest "github.com/opencontainers/go-digest"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tagsTestEnv struct {
|
type tagsTestEnv struct {
|
||||||
ts distribution.TagService
|
ts distribution.TagService
|
||||||
|
bs distribution.BlobStore
|
||||||
|
ms distribution.ManifestService
|
||||||
|
gbs distribution.BlobStatter
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,10 +34,17 @@ func testTagStore(t *testing.T) *tagsTestEnv {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
ms, err := repo.Manifests(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
return &tagsTestEnv{
|
return &tagsTestEnv{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
ts: repo.Tags(ctx),
|
ts: repo.Tags(ctx),
|
||||||
|
bs: repo.Blobs(ctx),
|
||||||
|
gbs: reg.BlobStatter(),
|
||||||
|
ms: ms,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,5 +219,98 @@ func TestTagLookup(t *testing.T) {
|
||||||
if len(tags) != 2 {
|
if len(tags) != 2 {
|
||||||
t.Errorf("Lookup of descB returned %d tags, expected 2", len(tags))
|
t.Errorf("Lookup of descB returned %d tags, expected 2", len(tags))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTagIndexes(t *testing.T) {
|
||||||
|
env := testTagStore(t)
|
||||||
|
tagStore := env.ts
|
||||||
|
ctx := env.ctx
|
||||||
|
|
||||||
|
md, ok := tagStore.(distribution.TagManifestsProvider)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("tagStore does not implement TagManifestDigests interface")
|
||||||
|
}
|
||||||
|
|
||||||
|
conf, err := env.bs.Put(ctx, "application/octet-stream", []byte{0})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t1Dgsts := make(map[digest.Digest]struct{})
|
||||||
|
t2Dgsts := make(map[digest.Digest]struct{})
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
layer, err := env.bs.Put(ctx, "application/octet-stream", []byte{byte(i + 1)})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
m := schema2.Manifest{
|
||||||
|
Versioned: manifest.Versioned{
|
||||||
|
SchemaVersion: 2,
|
||||||
|
MediaType: schema2.MediaTypeManifest,
|
||||||
|
},
|
||||||
|
Config: distribution.Descriptor{
|
||||||
|
Digest: conf.Digest,
|
||||||
|
Size: 1,
|
||||||
|
MediaType: schema2.MediaTypeImageConfig,
|
||||||
|
},
|
||||||
|
Layers: []distribution.Descriptor{
|
||||||
|
{
|
||||||
|
Digest: layer.Digest,
|
||||||
|
Size: 1,
|
||||||
|
MediaType: schema2.MediaTypeLayer,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
dm, err := schema2.FromStruct(m)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
dgst, err := env.ms.Put(ctx, dm)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
desc, err := env.gbs.Stat(ctx, dgst)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if i < 3 {
|
||||||
|
// tag first 3 manifests as "t1"
|
||||||
|
err = tagStore.Tag(ctx, "t1", desc)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t1Dgsts[dgst] = struct{}{}
|
||||||
|
} else {
|
||||||
|
// the last two under "t2"
|
||||||
|
err = tagStore.Tag(ctx, "t2", desc)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t2Dgsts[dgst] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gotT1Dgsts, err := md.ManifestDigests(ctx, "t1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(t1Dgsts, digestMap(gotT1Dgsts)) {
|
||||||
|
t.Fatalf("Expected digests: %v but got digests: %v", t1Dgsts, digestMap(gotT1Dgsts))
|
||||||
|
}
|
||||||
|
|
||||||
|
gotT2Dgsts, err := md.ManifestDigests(ctx, "t2")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(t2Dgsts, digestMap(gotT2Dgsts)) {
|
||||||
|
t.Fatalf("Expected digests: %v but got digests: %v", t2Dgsts, digestMap(gotT2Dgsts))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func digestMap(dgsts []digest.Digest) map[digest.Digest]struct{} {
|
||||||
|
set := make(map[digest.Digest]struct{})
|
||||||
|
for _, dgst := range dgsts {
|
||||||
|
set[dgst] = struct{}{}
|
||||||
|
}
|
||||||
|
return set
|
||||||
}
|
}
|
||||||
|
|
10
tags.go
10
tags.go
|
@ -2,6 +2,8 @@ package distribution
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
digest "github.com/opencontainers/go-digest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TagService provides access to information about tagged objects.
|
// TagService provides access to information about tagged objects.
|
||||||
|
@ -25,3 +27,11 @@ type TagService interface {
|
||||||
// Lookup returns the set of tags referencing the given digest.
|
// Lookup returns the set of tags referencing the given digest.
|
||||||
Lookup(ctx context.Context, digest Descriptor) ([]string, error)
|
Lookup(ctx context.Context, digest Descriptor) ([]string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TagManifestsProvider provides method to retreive the digests of manifests that a tag historically
|
||||||
|
// pointed to
|
||||||
|
type TagManifestsProvider interface {
|
||||||
|
// ManifestDigests returns set of digests that this tag historically pointed to. This also
|
||||||
|
// includes currently linked digest. There is no ordering guaranteed
|
||||||
|
ManifestDigests(ctx context.Context, tag string) ([]digest.Digest, error)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue