diff options
Diffstat (limited to 'reference/store.go')
-rw-r--r-- | reference/store.go | 117 |
1 files changed, 71 insertions, 46 deletions
diff --git a/reference/store.go b/reference/store.go index 35ae33660a..844ebf5319 100644 --- a/reference/store.go +++ b/reference/store.go @@ -9,6 +9,7 @@ import ( "sort" "sync" + "github.com/docker/distribution/reference" "github.com/docker/docker/pkg/ioutils" "github.com/opencontainers/go-digest" ) @@ -21,18 +22,18 @@ var ( // An Association is a tuple associating a reference with an image ID. type Association struct { - Ref Named + Ref reference.Named ID digest.Digest } // Store provides the set of methods which can operate on a tag store. type Store interface { - References(id digest.Digest) []Named - ReferencesByName(ref Named) []Association - AddTag(ref Named, id digest.Digest, force bool) error - AddDigest(ref Canonical, id digest.Digest, force bool) error - Delete(ref Named) (bool, error) - Get(ref Named) (digest.Digest, error) + References(id digest.Digest) []reference.Named + ReferencesByName(ref reference.Named) []Association + AddTag(ref reference.Named, id digest.Digest, force bool) error + AddDigest(ref reference.Canonical, id digest.Digest, force bool) error + Delete(ref reference.Named) (bool, error) + Get(ref reference.Named) (digest.Digest, error) } type store struct { @@ -44,24 +45,28 @@ type store struct { Repositories map[string]repository // referencesByIDCache is a cache of references indexed by ID, to speed // up References. - referencesByIDCache map[digest.Digest]map[string]Named + referencesByIDCache map[digest.Digest]map[string]reference.Named } // Repository maps tags to digests. The key is a stringified Reference, // including the repository name. type repository map[string]digest.Digest -type lexicalRefs []Named +type lexicalRefs []reference.Named -func (a lexicalRefs) Len() int { return len(a) } -func (a lexicalRefs) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a lexicalRefs) Less(i, j int) bool { return a[i].String() < a[j].String() } +func (a lexicalRefs) Len() int { return len(a) } +func (a lexicalRefs) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a lexicalRefs) Less(i, j int) bool { + return a[i].String() < a[j].String() +} type lexicalAssociations []Association -func (a lexicalAssociations) Len() int { return len(a) } -func (a lexicalAssociations) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a lexicalAssociations) Less(i, j int) bool { return a[i].Ref.String() < a[j].Ref.String() } +func (a lexicalAssociations) Len() int { return len(a) } +func (a lexicalAssociations) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a lexicalAssociations) Less(i, j int) bool { + return a[i].Ref.String() < a[j].Ref.String() +} // NewReferenceStore creates a new reference store, tied to a file path where // the set of references are serialized in JSON format. @@ -74,7 +79,7 @@ func NewReferenceStore(jsonPath string) (Store, error) { store := &store{ jsonPath: abspath, Repositories: make(map[string]repository), - referencesByIDCache: make(map[digest.Digest]map[string]Named), + referencesByIDCache: make(map[digest.Digest]map[string]reference.Named), } // Load the json file if it exists, otherwise create it. if err := store.reload(); os.IsNotExist(err) { @@ -89,43 +94,45 @@ func NewReferenceStore(jsonPath string) (Store, error) { // AddTag adds a tag reference to the store. If force is set to true, existing // references can be overwritten. This only works for tags, not digests. -func (store *store) AddTag(ref Named, id digest.Digest, force bool) error { - if _, isCanonical := ref.(Canonical); isCanonical { +func (store *store) AddTag(ref reference.Named, id digest.Digest, force bool) error { + if _, isCanonical := ref.(reference.Canonical); isCanonical { return errors.New("refusing to create a tag with a digest reference") } - return store.addReference(WithDefaultTag(ref), id, force) + return store.addReference(reference.TagNameOnly(ref), id, force) } // AddDigest adds a digest reference to the store. -func (store *store) AddDigest(ref Canonical, id digest.Digest, force bool) error { +func (store *store) AddDigest(ref reference.Canonical, id digest.Digest, force bool) error { return store.addReference(ref, id, force) } -func (store *store) addReference(ref Named, id digest.Digest, force bool) error { - if ref.Name() == string(digest.Canonical) { +func (store *store) addReference(ref reference.Named, id digest.Digest, force bool) error { + refName := reference.FamiliarName(ref) + refStr := reference.FamiliarString(ref) + + if refName == string(digest.Canonical) { return errors.New("refusing to create an ambiguous tag using digest algorithm as name") } store.mu.Lock() defer store.mu.Unlock() - repository, exists := store.Repositories[ref.Name()] + repository, exists := store.Repositories[refName] if !exists || repository == nil { repository = make(map[string]digest.Digest) - store.Repositories[ref.Name()] = repository + store.Repositories[refName] = repository } - refStr := ref.String() oldID, exists := repository[refStr] if exists { // force only works for tags - if digested, isDigest := ref.(Canonical); isDigest { + if digested, isDigest := ref.(reference.Canonical); isDigest { return fmt.Errorf("Cannot overwrite digest %s", digested.Digest().String()) } if !force { - return fmt.Errorf("Conflict: Tag %s is already set to image %s, if you want to replace it, please use -f option", ref.String(), oldID.String()) + return fmt.Errorf("Conflict: Tag %s is already set to image %s, if you want to replace it, please use -f option", refStr, oldID.String()) } if store.referencesByIDCache[oldID] != nil { @@ -138,7 +145,7 @@ func (store *store) addReference(ref Named, id digest.Digest, force bool) error repository[refStr] = id if store.referencesByIDCache[id] == nil { - store.referencesByIDCache[id] = make(map[string]Named) + store.referencesByIDCache[id] = make(map[string]reference.Named) } store.referencesByIDCache[id][refStr] = ref @@ -147,24 +154,24 @@ func (store *store) addReference(ref Named, id digest.Digest, force bool) error // Delete deletes a reference from the store. It returns true if a deletion // happened, or false otherwise. -func (store *store) Delete(ref Named) (bool, error) { - ref = WithDefaultTag(ref) +func (store *store) Delete(ref reference.Named) (bool, error) { + ref = reference.TagNameOnly(ref) + + refName := reference.FamiliarName(ref) + refStr := reference.FamiliarString(ref) store.mu.Lock() defer store.mu.Unlock() - repoName := ref.Name() - - repository, exists := store.Repositories[repoName] + repository, exists := store.Repositories[refName] if !exists { return false, ErrDoesNotExist } - refStr := ref.String() if id, exists := repository[refStr]; exists { delete(repository, refStr) if len(repository) == 0 { - delete(store.Repositories, repoName) + delete(store.Repositories, refName) } if store.referencesByIDCache[id] != nil { delete(store.referencesByIDCache[id], refStr) @@ -179,18 +186,34 @@ func (store *store) Delete(ref Named) (bool, error) { } // Get retrieves an item from the store by reference -func (store *store) Get(ref Named) (digest.Digest, error) { - ref = WithDefaultTag(ref) +func (store *store) Get(ref reference.Named) (digest.Digest, error) { + if canonical, ok := ref.(reference.Canonical); ok { + // If reference contains both tag and digest, only + // lookup by digest as it takes precendent over + // tag, until tag/digest combos are stored. + if _, ok := ref.(reference.Tagged); ok { + var err error + ref, err = reference.WithDigest(reference.TrimNamed(canonical), canonical.Digest()) + if err != nil { + return "", err + } + } + } else { + ref = reference.TagNameOnly(ref) + } + + refName := reference.FamiliarName(ref) + refStr := reference.FamiliarString(ref) store.mu.RLock() defer store.mu.RUnlock() - repository, exists := store.Repositories[ref.Name()] + repository, exists := store.Repositories[refName] if !exists || repository == nil { return "", ErrDoesNotExist } - id, exists := repository[ref.String()] + id, exists := repository[refStr] if !exists { return "", ErrDoesNotExist } @@ -200,7 +223,7 @@ func (store *store) Get(ref Named) (digest.Digest, error) { // References returns a slice of references to the given ID. The slice // will be nil if there are no references to this ID. -func (store *store) References(id digest.Digest) []Named { +func (store *store) References(id digest.Digest) []reference.Named { store.mu.RLock() defer store.mu.RUnlock() @@ -208,7 +231,7 @@ func (store *store) References(id digest.Digest) []Named { // 1) We must not return a mutable // 2) It would be ugly to expose the extraneous map keys to callers. - var references []Named + var references []reference.Named for _, ref := range store.referencesByIDCache[id] { references = append(references, ref) } @@ -221,18 +244,20 @@ func (store *store) References(id digest.Digest) []Named { // ReferencesByName returns the references for a given repository name. // If there are no references known for this repository name, // ReferencesByName returns nil. -func (store *store) ReferencesByName(ref Named) []Association { +func (store *store) ReferencesByName(ref reference.Named) []Association { + refName := reference.FamiliarName(ref) + store.mu.RLock() defer store.mu.RUnlock() - repository, exists := store.Repositories[ref.Name()] + repository, exists := store.Repositories[refName] if !exists { return nil } var associations []Association for refStr, refID := range repository { - ref, err := ParseNamed(refStr) + ref, err := reference.ParseNormalizedNamed(refStr) if err != nil { // Should never happen return nil @@ -270,13 +295,13 @@ func (store *store) reload() error { for _, repository := range store.Repositories { for refStr, refID := range repository { - ref, err := ParseNamed(refStr) + ref, err := reference.ParseNormalizedNamed(refStr) if err != nil { // Should never happen continue } if store.referencesByIDCache[refID] == nil { - store.referencesByIDCache[refID] = make(map[string]Named) + store.referencesByIDCache[refID] = make(map[string]reference.Named) } store.referencesByIDCache[refID][refStr] = ref } |