82 lines
2.8 KiB
Go
82 lines
2.8 KiB
Go
package migration
|
|
|
|
import (
|
|
"github.com/docker/distribution"
|
|
"github.com/docker/distribution/context"
|
|
"github.com/docker/distribution/reference"
|
|
"github.com/palantir/stacktrace"
|
|
|
|
log "github.com/Sirupsen/logrus"
|
|
)
|
|
|
|
type Enumerator interface {
|
|
EnumerateRepo(ctx context.Context, reg distribution.Namespace, repoName string) error
|
|
}
|
|
|
|
// NewEnumerator returns an enumerator which provides functions to iterate over
|
|
// a repository's tags, calling the given tagEnumerator function for each tag.
|
|
func NewEnumerator(onGetTag tagEnumerator) Enumerator {
|
|
return &enumerator{onGetTag}
|
|
}
|
|
|
|
// tagEnumerator is a function signature for handling a specific repository's tag
|
|
// on each tieration
|
|
type tagEnumerator func(ctx context.Context, repo distribution.Repository, tagName string, tag distribution.Descriptor) error
|
|
|
|
// enumerator handles iterating over a repository's tags, calling `onGetTag` on
|
|
// each tag
|
|
type enumerator struct {
|
|
onGetTag tagEnumerator
|
|
}
|
|
|
|
// EnumerateRepo iterates over a given repository's tags, calling `EnumerateTag`
|
|
// on each tag. The repository is specified as a string via the `repoName`
|
|
// argument.
|
|
// A context and registry (distribution.Namespace) must be supplied with valid,
|
|
// instantiated drivers.
|
|
func (e *enumerator) EnumerateRepo(ctx context.Context, reg distribution.Namespace, repoName string) error {
|
|
named, err := reference.ParseNamed(repoName)
|
|
if err != nil {
|
|
log.WithField("error", err).Errorf("failed to parse repo name %s", repoName)
|
|
return nil
|
|
}
|
|
|
|
repo, err := reg.Repository(ctx, named)
|
|
if err != nil {
|
|
log.WithField("error", err).Errorf("failed to construct repository %s", repoName)
|
|
return nil
|
|
}
|
|
|
|
// enumerate all repository tags
|
|
tags, err := repo.Tags(ctx).All(ctx)
|
|
if err != nil {
|
|
log.WithField("error", err).Errorf("failed to return all tags for repository %s", repoName)
|
|
return nil
|
|
}
|
|
|
|
for _, t := range tags {
|
|
if err = e.EnumerateTags(ctx, repo, t); err != nil {
|
|
log.WithField("error", err).Errorf("error processing tag during enumeration %s", t)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// EnumerateTags is called with a tag name as a string, loads the tag's
|
|
// descriptor and delegates to `enumerator.onGetTag` with the tag name
|
|
// and descriptor for further processing.
|
|
//
|
|
// This allows us to pass custom functions for migration and consistency
|
|
// checking whilst leveraging the same enumeration code.
|
|
func (e *enumerator) EnumerateTags(ctx context.Context, repo distribution.Repository, tagName string) error {
|
|
// TagService.All returns a slice of strings instead of a concrete
|
|
// distribution.Descriptor. Here we transform the tag name into a
|
|
// descriptor and call the supplied onGetTag function.
|
|
desc, err := repo.Tags(ctx).Get(ctx, tagName)
|
|
if err != nil {
|
|
return stacktrace.NewError("failed retrieving tag descriptor for tag %s: %s", tagName, err)
|
|
}
|
|
|
|
return e.onGetTag(ctx, repo, tagName, desc)
|
|
}
|