6ae6df7d75
Signed-off-by: João Pereira <484633+joaodrp@users.noreply.github.com>
209 lines
5.4 KiB
Go
209 lines
5.4 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"path"
|
|
"sort"
|
|
|
|
"github.com/distribution/distribution/v3"
|
|
storagedriver "github.com/distribution/distribution/v3/registry/storage/driver"
|
|
"github.com/opencontainers/go-digest"
|
|
)
|
|
|
|
var _ distribution.TagService = &tagStore{}
|
|
|
|
// tagStore provides methods to manage manifest tags in a backend storage driver.
|
|
// This implementation uses the same on-disk layout as the (now deleted) tag
|
|
// store. This provides backward compatibility with current registry deployments
|
|
// which only makes use of the Digest field of the returned distribution.Descriptor
|
|
// but does not enable full roundtripping of Descriptor objects
|
|
type tagStore struct {
|
|
repository *repository
|
|
blobStore *blobStore
|
|
}
|
|
|
|
// All returns all tags
|
|
func (ts *tagStore) All(ctx context.Context) ([]string, error) {
|
|
var tags []string
|
|
|
|
pathSpec, err := pathFor(manifestTagPathSpec{
|
|
name: ts.repository.Named().Name(),
|
|
})
|
|
if err != nil {
|
|
return tags, err
|
|
}
|
|
|
|
entries, err := ts.blobStore.driver.List(ctx, pathSpec)
|
|
if err != nil {
|
|
switch err := err.(type) {
|
|
case storagedriver.PathNotFoundError:
|
|
return tags, distribution.ErrRepositoryUnknown{Name: ts.repository.Named().Name()}
|
|
default:
|
|
return tags, err
|
|
}
|
|
}
|
|
|
|
for _, entry := range entries {
|
|
_, filename := path.Split(entry)
|
|
tags = append(tags, filename)
|
|
}
|
|
|
|
// there is no guarantee for the order,
|
|
// therefore sort before return.
|
|
sort.Strings(tags)
|
|
|
|
return tags, nil
|
|
}
|
|
|
|
// Tag tags the digest with the given tag, updating the the store to point at
|
|
// the current tag. The digest must point to a manifest.
|
|
func (ts *tagStore) Tag(ctx context.Context, tag string, desc distribution.Descriptor) error {
|
|
currentPath, err := pathFor(manifestTagCurrentPathSpec{
|
|
name: ts.repository.Named().Name(),
|
|
tag: tag,
|
|
})
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
lbs := ts.linkedBlobStore(ctx, tag)
|
|
|
|
// Link into the index
|
|
if err := lbs.linkBlob(ctx, desc); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Overwrite the current link
|
|
return ts.blobStore.link(ctx, currentPath, desc.Digest)
|
|
}
|
|
|
|
// resolve the current revision for name and tag.
|
|
func (ts *tagStore) Get(ctx context.Context, tag string) (distribution.Descriptor, error) {
|
|
currentPath, err := pathFor(manifestTagCurrentPathSpec{
|
|
name: ts.repository.Named().Name(),
|
|
tag: tag,
|
|
})
|
|
|
|
if err != nil {
|
|
return distribution.Descriptor{}, err
|
|
}
|
|
|
|
revision, err := ts.blobStore.readlink(ctx, currentPath)
|
|
if err != nil {
|
|
switch err.(type) {
|
|
case storagedriver.PathNotFoundError:
|
|
return distribution.Descriptor{}, distribution.ErrTagUnknown{Tag: tag}
|
|
}
|
|
|
|
return distribution.Descriptor{}, err
|
|
}
|
|
|
|
return distribution.Descriptor{Digest: revision}, nil
|
|
}
|
|
|
|
// Untag removes the tag association
|
|
func (ts *tagStore) Untag(ctx context.Context, tag string) error {
|
|
tagPath, err := pathFor(manifestTagPathSpec{
|
|
name: ts.repository.Named().Name(),
|
|
tag: tag,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return ts.blobStore.driver.Delete(ctx, tagPath)
|
|
}
|
|
|
|
// linkedBlobStore returns the linkedBlobStore for the named tag, allowing one
|
|
// to index manifest blobs by tag name. While the tag store doesn't map
|
|
// precisely to the linked blob store, using this ensures the links are
|
|
// managed via the same code path.
|
|
func (ts *tagStore) linkedBlobStore(ctx context.Context, tag string) *linkedBlobStore {
|
|
return &linkedBlobStore{
|
|
blobStore: ts.blobStore,
|
|
repository: ts.repository,
|
|
ctx: ctx,
|
|
linkPathFns: []linkPathFunc{func(name string, dgst digest.Digest) (string, error) {
|
|
return pathFor(manifestTagIndexEntryLinkPathSpec{
|
|
name: name,
|
|
tag: tag,
|
|
revision: dgst,
|
|
})
|
|
|
|
}},
|
|
}
|
|
}
|
|
|
|
// 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, 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.Named().Name(),
|
|
tag: tag,
|
|
}
|
|
|
|
tagLinkPath, _ := pathFor(tagLinkPathSpec)
|
|
tagDigest, err := ts.blobStore.readlink(ctx, tagLinkPath)
|
|
if err != nil {
|
|
switch err.(type) {
|
|
case storagedriver.PathNotFoundError:
|
|
continue
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
if tagDigest == desc.Digest {
|
|
tags = append(tags, tag)
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|