Remove tags referencing deleted manifests.

When a manifest is deleted by digest, look up the referenced tags in the tag
store and remove all associations.

Signed-off-by: Richard Scothern <richard.scothern@gmail.com>
This commit is contained in:
Richard Scothern 2016-01-05 11:22:40 -08:00
parent 1c6c0c8a2d
commit fea0a7ed49
4 changed files with 181 additions and 7 deletions

View file

@ -116,15 +116,19 @@ func (ts *tagStore) Get(ctx context.Context, tag string) (distribution.Descripto
return distribution.Descriptor{Digest: revision}, nil
}
// delete removes the tag from repository, including the history of all
// revisions that have the specified tag.
// Untag removes the tag association
func (ts *tagStore) Untag(ctx context.Context, tag string) error {
tagPath, err := pathFor(manifestTagPathSpec{
name: ts.repository.Name(),
tag: tag,
})
if err != nil {
switch err.(type) {
case storagedriver.PathNotFoundError:
return distribution.ErrTagUnknown{Tag: tag}
case nil:
break
default:
return err
}
@ -153,7 +157,35 @@ func (ts *tagStore) linkedBlobStore(ctx context.Context, tag string) *linkedBlob
// Lookup recovers a list of tags which refer to this digest. When a manifest is deleted by
// digest, tag entries which point to it need to be recovered to avoid dangling tags.
func (ts *tagStore) Lookup(ctx context.Context, digest distribution.Descriptor) ([]string, error) {
// An efficient implementation of this will require changes to the S3 driver.
return make([]string, 0), nil
func (ts *tagStore) Lookup(ctx context.Context, desc distribution.Descriptor) ([]string, error) {
allTags, err := ts.All(ctx)
switch err.(type) {
case distribution.ErrRepositoryUnknown:
// This tag store has been initialized but not yet populated
break
case nil:
break
default:
return nil, err
}
var tags []string
for _, tag := range allTags {
tagLinkPathSpec := manifestTagCurrentPathSpec{
name: ts.repository.Name(),
tag: tag,
}
tagLinkPath, err := pathFor(tagLinkPathSpec)
tagDigest, err := ts.blobStore.readlink(ctx, tagLinkPath)
if err != nil {
return nil, err
}
if tagDigest == desc.Digest {
tags = append(tags, tag)
}
}
return tags, nil
}

View file

@ -102,7 +102,7 @@ func TestTagStoreUnTag(t *testing.T) {
}
}
func TestTagAll(t *testing.T) {
func TestTagStoreAll(t *testing.T) {
env := testTagStore(t)
tagStore := env.ts
ctx := env.ctx
@ -148,3 +148,59 @@ func TestTagAll(t *testing.T) {
}
}
func TestTagLookup(t *testing.T) {
env := testTagStore(t)
tagStore := env.ts
ctx := env.ctx
descA := distribution.Descriptor{Digest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}
desc0 := distribution.Descriptor{Digest: "sha256:0000000000000000000000000000000000000000000000000000000000000000"}
tags, err := tagStore.Lookup(ctx, descA)
if err != nil {
t.Fatal(err)
}
if len(tags) != 0 {
t.Fatalf("Lookup returned > 0 tags from empty store")
}
err = tagStore.Tag(ctx, "a", descA)
if err != nil {
t.Fatal(err)
}
err = tagStore.Tag(ctx, "b", descA)
if err != nil {
t.Fatal(err)
}
err = tagStore.Tag(ctx, "0", desc0)
if err != nil {
t.Fatal(err)
}
err = tagStore.Tag(ctx, "1", desc0)
if err != nil {
t.Fatal(err)
}
tags, err = tagStore.Lookup(ctx, descA)
if err != nil {
t.Fatal(err)
}
if len(tags) != 2 {
t.Errorf("Lookup of descA returned %d tags, expected 2", len(tags))
}
tags, err = tagStore.Lookup(ctx, desc0)
if err != nil {
t.Fatal(err)
}
if len(tags) != 2 {
t.Errorf("Lookup of descB returned %d tags, expected 2", len(tags))
}
}