This changeset implements immutable manifest references via the HTTP API. Most of the changes follow from modifications to ManifestService. Once updates were made across the repo to implement these changes, the http handlers were change accordingly. The new methods on ManifestService will be broken out into a tagging service in a later PR. Unfortunately, due to complexities around managing the manifest tag index in an eventually consistent manner, direct deletes of manifests have been disabled. Signed-off-by: Stephen J Day <stephen.day@docker.com>
158 lines
3.4 KiB
Go
158 lines
3.4 KiB
Go
package storage
|
|
|
|
import (
|
|
"path"
|
|
|
|
"github.com/docker/distribution"
|
|
"github.com/docker/distribution/digest"
|
|
storagedriver "github.com/docker/distribution/registry/storage/driver"
|
|
)
|
|
|
|
// tagStore provides methods to manage manifest tags in a backend storage driver.
|
|
type tagStore struct {
|
|
*repository
|
|
}
|
|
|
|
// tags lists the manifest tags for the specified repository.
|
|
func (ts *tagStore) tags() ([]string, error) {
|
|
p, err := ts.pm.path(manifestTagPathSpec{
|
|
name: ts.name,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var tags []string
|
|
entries, err := ts.driver.List(p)
|
|
if err != nil {
|
|
switch err := err.(type) {
|
|
case storagedriver.PathNotFoundError:
|
|
return nil, distribution.ErrRepositoryUnknown{Name: ts.name}
|
|
default:
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
for _, entry := range entries {
|
|
_, filename := path.Split(entry)
|
|
|
|
tags = append(tags, filename)
|
|
}
|
|
|
|
return tags, nil
|
|
}
|
|
|
|
// exists returns true if the specified manifest tag exists in the repository.
|
|
func (ts *tagStore) exists(tag string) (bool, error) {
|
|
tagPath, err := ts.pm.path(manifestTagCurrentPathSpec{
|
|
name: ts.Name(),
|
|
tag: tag,
|
|
})
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
exists, err := exists(ts.driver, tagPath)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return exists, 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(tag string, revision digest.Digest) error {
|
|
indexEntryPath, err := ts.pm.path(manifestTagIndexEntryLinkPathSpec{
|
|
name: ts.Name(),
|
|
tag: tag,
|
|
revision: revision,
|
|
})
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
currentPath, err := ts.pm.path(manifestTagCurrentPathSpec{
|
|
name: ts.Name(),
|
|
tag: tag,
|
|
})
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Link into the index
|
|
if err := ts.blobStore.link(indexEntryPath, revision); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Overwrite the current link
|
|
return ts.blobStore.link(currentPath, revision)
|
|
}
|
|
|
|
// resolve the current revision for name and tag.
|
|
func (ts *tagStore) resolve(tag string) (digest.Digest, error) {
|
|
currentPath, err := ts.pm.path(manifestTagCurrentPathSpec{
|
|
name: ts.Name(),
|
|
tag: tag,
|
|
})
|
|
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if exists, err := exists(ts.driver, currentPath); err != nil {
|
|
return "", err
|
|
} else if !exists {
|
|
return "", distribution.ErrManifestUnknown{Name: ts.Name(), Tag: tag}
|
|
}
|
|
|
|
revision, err := ts.blobStore.readlink(currentPath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return revision, nil
|
|
}
|
|
|
|
// revisions returns all revisions with the specified name and tag.
|
|
func (ts *tagStore) revisions(tag string) ([]digest.Digest, error) {
|
|
manifestTagIndexPath, err := ts.pm.path(manifestTagIndexPathSpec{
|
|
name: ts.Name(),
|
|
tag: tag,
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// TODO(stevvooe): Need to append digest alg to get listing of revisions.
|
|
manifestTagIndexPath = path.Join(manifestTagIndexPath, "sha256")
|
|
|
|
entries, err := ts.driver.List(manifestTagIndexPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var revisions []digest.Digest
|
|
for _, entry := range entries {
|
|
revisions = append(revisions, digest.NewDigestFromHex("sha256", path.Base(entry)))
|
|
}
|
|
|
|
return revisions, nil
|
|
}
|
|
|
|
// delete removes the tag from repository, including the history of all
|
|
// revisions that have the specified tag.
|
|
func (ts *tagStore) delete(tag string) error {
|
|
tagPath, err := ts.pm.path(manifestTagPathSpec{
|
|
name: ts.Name(),
|
|
tag: tag,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return ts.driver.Delete(tagPath)
|
|
}
|