Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
| ... | ... |
@@ -1024,20 +1024,16 @@ func (daemon *Daemon) changes(container *Container) ([]archive.Change, error) {
|
| 1024 | 1024 |
// imageName. If force is true, an existing tag with the same name may be |
| 1025 | 1025 |
// overwritten. |
| 1026 | 1026 |
func (daemon *Daemon) TagImage(newTag reference.Named, imageName string, force bool) error {
|
| 1027 |
- if _, isDigested := newTag.(reference.Digested); isDigested {
|
|
| 1028 |
- return errors.New("refusing to create a tag with a digest reference")
|
|
| 1029 |
- } |
|
| 1030 |
- if newTag.Name() == string(digest.Canonical) {
|
|
| 1031 |
- return errors.New("refusing to create an ambiguous tag using digest algorithm as name")
|
|
| 1032 |
- } |
|
| 1033 |
- |
|
| 1034 |
- newTag = registry.NormalizeLocalReference(newTag) |
|
| 1035 | 1027 |
imageID, err := daemon.GetImageID(imageName) |
| 1036 | 1028 |
if err != nil {
|
| 1037 | 1029 |
return err |
| 1038 | 1030 |
} |
| 1031 |
+ newTag = registry.NormalizeLocalReference(newTag) |
|
| 1032 |
+ if err := daemon.tagStore.AddTag(newTag, imageID, force); err != nil {
|
|
| 1033 |
+ return err |
|
| 1034 |
+ } |
|
| 1039 | 1035 |
daemon.EventsService.Log("tag", newTag.String(), "")
|
| 1040 |
- return daemon.tagStore.Add(newTag, imageID, force) |
|
| 1036 |
+ return nil |
|
| 1041 | 1037 |
} |
| 1042 | 1038 |
|
| 1043 | 1039 |
// PullImage initiates a pull operation. image is the repository name to pull, and |
| ... | ... |
@@ -332,7 +332,7 @@ func (p *v1Puller) pullImage(out io.Writer, v1ID, endpoint string, localNameRef |
| 332 | 332 |
return layersDownloaded, err |
| 333 | 333 |
} |
| 334 | 334 |
|
| 335 |
- if err := p.config.TagStore.Add(localNameRef, imageID, true); err != nil {
|
|
| 335 |
+ if err := p.config.TagStore.AddTag(localNameRef, imageID, true); err != nil {
|
|
| 336 | 336 |
return layersDownloaded, err |
| 337 | 337 |
} |
| 338 | 338 |
|
| ... | ... |
@@ -406,7 +406,11 @@ func (p *v2Puller) pullV2Tag(out io.Writer, ref reference.Named) (tagUpdated boo |
| 406 | 406 |
} |
| 407 | 407 |
|
| 408 | 408 |
if tagUpdated {
|
| 409 |
- if err = p.config.TagStore.Add(ref, imageID, true); err != nil {
|
|
| 409 |
+ if canonical, ok := ref.(reference.Canonical); ok {
|
|
| 410 |
+ if err = p.config.TagStore.AddDigest(canonical, imageID, true); err != nil {
|
|
| 411 |
+ return false, err |
|
| 412 |
+ } |
|
| 413 |
+ } else if err = p.config.TagStore.AddTag(ref, imageID, true); err != nil {
|
|
| 410 | 414 |
return false, err |
| 411 | 415 |
} |
| 412 | 416 |
} |
| ... | ... |
@@ -128,7 +128,7 @@ func (l *tarexporter) setLoadedTag(ref reference.NamedTagged, imgID image.ID, ou |
| 128 | 128 |
fmt.Fprintf(outStream, "The image %s already exists, renaming the old one with ID %s to empty string\n", ref.String(), string(prevID)) // todo: this message is wrong in case of multiple tags |
| 129 | 129 |
} |
| 130 | 130 |
|
| 131 |
- if err := l.ts.Add(ref, imgID, true); err != nil {
|
|
| 131 |
+ if err := l.ts.AddTag(ref, imgID, true); err != nil {
|
|
| 132 | 132 |
return err |
| 133 | 133 |
} |
| 134 | 134 |
return nil |
| ... | ... |
@@ -190,7 +190,8 @@ func migrateContainers(root string, ls graphIDMounter, is image.Store, imageMapp |
| 190 | 190 |
} |
| 191 | 191 |
|
| 192 | 192 |
type tagAdder interface {
|
| 193 |
- Add(ref reference.Named, id image.ID, force bool) error |
|
| 193 |
+ AddTag(ref reference.Named, id image.ID, force bool) error |
|
| 194 |
+ AddDigest(ref reference.Canonical, id image.ID, force bool) error |
|
| 194 | 195 |
} |
| 195 | 196 |
|
| 196 | 197 |
func migrateTags(root, driverName string, ts tagAdder, mappings map[string]image.ID) error {
|
| ... | ... |
@@ -226,20 +227,23 @@ func migrateTags(root, driverName string, ts tagAdder, mappings map[string]image |
| 226 | 226 |
continue |
| 227 | 227 |
} |
| 228 | 228 |
if dgst, err := digest.ParseDigest(tag); err == nil {
|
| 229 |
- ref, err = reference.WithDigest(ref, dgst) |
|
| 229 |
+ canonical, err := reference.WithDigest(ref, dgst) |
|
| 230 | 230 |
if err != nil {
|
| 231 | 231 |
logrus.Errorf("migrate tags: invalid digest %q, %q", dgst, err)
|
| 232 | 232 |
continue |
| 233 | 233 |
} |
| 234 |
+ if err := ts.AddDigest(canonical, strongID, false); err != nil {
|
|
| 235 |
+ logrus.Errorf("can't migrate digest %q for %q, err: %q", ref.String(), strongID, err)
|
|
| 236 |
+ } |
|
| 234 | 237 |
} else {
|
| 235 |
- ref, err = reference.WithTag(ref, tag) |
|
| 238 |
+ tagRef, err := reference.WithTag(ref, tag) |
|
| 236 | 239 |
if err != nil {
|
| 237 | 240 |
logrus.Errorf("migrate tags: invalid tag %q, %q", tag, err)
|
| 238 | 241 |
continue |
| 239 | 242 |
} |
| 240 |
- } |
|
| 241 |
- if err := ts.Add(ref, strongID, false); err != nil {
|
|
| 242 |
- logrus.Errorf("can't migrate tag %q for %q, err: %q", ref.String(), strongID, err)
|
|
| 243 |
+ if err := ts.AddTag(tagRef, strongID, false); err != nil {
|
|
| 244 |
+ logrus.Errorf("can't migrate tag %q for %q, err: %q", ref.String(), strongID, err)
|
|
| 245 |
+ } |
|
| 243 | 246 |
} |
| 244 | 247 |
logrus.Infof("migrated tag %s:%s to point to %s", name, tag, strongID)
|
| 245 | 248 |
} |
| ... | ... |
@@ -289,13 +289,16 @@ type mockTagAdder struct {
|
| 289 | 289 |
refs map[string]string |
| 290 | 290 |
} |
| 291 | 291 |
|
| 292 |
-func (t *mockTagAdder) Add(ref reference.Named, id image.ID, force bool) error {
|
|
| 292 |
+func (t *mockTagAdder) AddTag(ref reference.Named, id image.ID, force bool) error {
|
|
| 293 | 293 |
if t.refs == nil {
|
| 294 | 294 |
t.refs = make(map[string]string) |
| 295 | 295 |
} |
| 296 | 296 |
t.refs[ref.String()] = id.String() |
| 297 | 297 |
return nil |
| 298 | 298 |
} |
| 299 |
+func (t *mockTagAdder) AddDigest(ref reference.Canonical, id image.ID, force bool) error {
|
|
| 300 |
+ return t.AddTag(ref, id, force) |
|
| 301 |
+} |
|
| 299 | 302 |
|
| 300 | 303 |
type mockRegistrar struct {
|
| 301 | 304 |
layers map[layer.ChainID]*mockLayer |
| ... | ... |
@@ -9,6 +9,7 @@ import ( |
| 9 | 9 |
"path/filepath" |
| 10 | 10 |
"sync" |
| 11 | 11 |
|
| 12 |
+ "github.com/docker/distribution/digest" |
|
| 12 | 13 |
"github.com/docker/distribution/reference" |
| 13 | 14 |
"github.com/docker/docker/image" |
| 14 | 15 |
) |
| ... | ... |
@@ -32,7 +33,8 @@ type Association struct {
|
| 32 | 32 |
type Store interface {
|
| 33 | 33 |
References(id image.ID) []reference.Named |
| 34 | 34 |
ReferencesByName(ref reference.Named) []Association |
| 35 |
- Add(ref reference.Named, id image.ID, force bool) error |
|
| 35 |
+ AddTag(ref reference.Named, id image.ID, force bool) error |
|
| 36 |
+ AddDigest(ref reference.Canonical, id image.ID, force bool) error |
|
| 36 | 37 |
Delete(ref reference.Named) (bool, error) |
| 37 | 38 |
Get(ref reference.Named) (image.ID, error) |
| 38 | 39 |
} |
| ... | ... |
@@ -90,10 +92,24 @@ func NewTagStore(jsonPath string) (Store, error) {
|
| 90 | 90 |
return store, nil |
| 91 | 91 |
} |
| 92 | 92 |
|
| 93 |
-// Add adds a tag or digest to the store. If force is set to true, existing |
|
| 93 |
+// Add adds a tag to the store. If force is set to true, existing |
|
| 94 | 94 |
// references can be overwritten. This only works for tags, not digests. |
| 95 |
-func (store *store) Add(ref reference.Named, id image.ID, force bool) error {
|
|
| 96 |
- ref = defaultTagIfNameOnly(ref) |
|
| 95 |
+func (store *store) AddTag(ref reference.Named, id image.ID, force bool) error {
|
|
| 96 |
+ if _, isDigested := ref.(reference.Digested); isDigested {
|
|
| 97 |
+ return errors.New("refusing to create a tag with a digest reference")
|
|
| 98 |
+ } |
|
| 99 |
+ return store.addReference(defaultTagIfNameOnly(ref), id, force) |
|
| 100 |
+} |
|
| 101 |
+ |
|
| 102 |
+// Add adds a digest reference to the store. |
|
| 103 |
+func (store *store) AddDigest(ref reference.Canonical, id image.ID, force bool) error {
|
|
| 104 |
+ return store.addReference(ref, id, force) |
|
| 105 |
+} |
|
| 106 |
+ |
|
| 107 |
+func (store *store) addReference(ref reference.Named, id image.ID, force bool) error {
|
|
| 108 |
+ if ref.Name() == string(digest.Canonical) {
|
|
| 109 |
+ return errors.New("refusing to create an ambiguous tag using digest algorithm as name")
|
|
| 110 |
+ } |
|
| 97 | 111 |
|
| 98 | 112 |
store.mu.Lock() |
| 99 | 113 |
defer store.mu.Unlock() |
| ... | ... |
@@ -4,6 +4,7 @@ import ( |
| 4 | 4 |
"bytes" |
| 5 | 5 |
"io/ioutil" |
| 6 | 6 |
"os" |
| 7 |
+ "path/filepath" |
|
| 7 | 8 |
"sort" |
| 8 | 9 |
"strings" |
| 9 | 10 |
"testing" |
| ... | ... |
@@ -79,9 +80,16 @@ func TestSave(t *testing.T) {
|
| 79 | 79 |
if err != nil {
|
| 80 | 80 |
t.Fatalf("failed to parse reference: %v", err)
|
| 81 | 81 |
} |
| 82 |
- err = store.Add(ref, id, false) |
|
| 83 |
- if err != nil {
|
|
| 84 |
- t.Fatalf("could not add reference %s: %v", refStr, err)
|
|
| 82 |
+ if canonical, ok := ref.(reference.Canonical); ok {
|
|
| 83 |
+ err = store.AddDigest(canonical, id, false) |
|
| 84 |
+ if err != nil {
|
|
| 85 |
+ t.Fatalf("could not add digest reference %s: %v", refStr, err)
|
|
| 86 |
+ } |
|
| 87 |
+ } else {
|
|
| 88 |
+ err = store.AddTag(ref, id, false) |
|
| 89 |
+ if err != nil {
|
|
| 90 |
+ t.Fatalf("could not add reference %s: %v", refStr, err)
|
|
| 91 |
+ } |
|
| 85 | 92 |
} |
| 86 | 93 |
} |
| 87 | 94 |
|
| ... | ... |
@@ -130,7 +138,7 @@ func TestAddDeleteGet(t *testing.T) {
|
| 130 | 130 |
if err != nil {
|
| 131 | 131 |
t.Fatalf("could not parse reference: %v", err)
|
| 132 | 132 |
} |
| 133 |
- if err = store.Add(nameOnly, testImageID1, false); err != nil {
|
|
| 133 |
+ if err = store.AddTag(nameOnly, testImageID1, false); err != nil {
|
|
| 134 | 134 |
t.Fatalf("error adding to store: %v", err)
|
| 135 | 135 |
} |
| 136 | 136 |
|
| ... | ... |
@@ -139,7 +147,7 @@ func TestAddDeleteGet(t *testing.T) {
|
| 139 | 139 |
if err != nil {
|
| 140 | 140 |
t.Fatalf("could not parse reference: %v", err)
|
| 141 | 141 |
} |
| 142 |
- if err = store.Add(ref1, testImageID1, false); err != nil {
|
|
| 142 |
+ if err = store.AddTag(ref1, testImageID1, false); err != nil {
|
|
| 143 | 143 |
t.Fatalf("error adding to store: %v", err)
|
| 144 | 144 |
} |
| 145 | 145 |
|
| ... | ... |
@@ -147,7 +155,7 @@ func TestAddDeleteGet(t *testing.T) {
|
| 147 | 147 |
if err != nil {
|
| 148 | 148 |
t.Fatalf("could not parse reference: %v", err)
|
| 149 | 149 |
} |
| 150 |
- if err = store.Add(ref2, testImageID2, false); err != nil {
|
|
| 150 |
+ if err = store.AddTag(ref2, testImageID2, false); err != nil {
|
|
| 151 | 151 |
t.Fatalf("error adding to store: %v", err)
|
| 152 | 152 |
} |
| 153 | 153 |
|
| ... | ... |
@@ -155,7 +163,7 @@ func TestAddDeleteGet(t *testing.T) {
|
| 155 | 155 |
if err != nil {
|
| 156 | 156 |
t.Fatalf("could not parse reference: %v", err)
|
| 157 | 157 |
} |
| 158 |
- if err = store.Add(ref3, testImageID1, false); err != nil {
|
|
| 158 |
+ if err = store.AddTag(ref3, testImageID1, false); err != nil {
|
|
| 159 | 159 |
t.Fatalf("error adding to store: %v", err)
|
| 160 | 160 |
} |
| 161 | 161 |
|
| ... | ... |
@@ -163,7 +171,7 @@ func TestAddDeleteGet(t *testing.T) {
|
| 163 | 163 |
if err != nil {
|
| 164 | 164 |
t.Fatalf("could not parse reference: %v", err)
|
| 165 | 165 |
} |
| 166 |
- if err = store.Add(ref4, testImageID2, false); err != nil {
|
|
| 166 |
+ if err = store.AddTag(ref4, testImageID2, false); err != nil {
|
|
| 167 | 167 |
t.Fatalf("error adding to store: %v", err)
|
| 168 | 168 |
} |
| 169 | 169 |
|
| ... | ... |
@@ -171,16 +179,16 @@ func TestAddDeleteGet(t *testing.T) {
|
| 171 | 171 |
if err != nil {
|
| 172 | 172 |
t.Fatalf("could not parse reference: %v", err)
|
| 173 | 173 |
} |
| 174 |
- if err = store.Add(ref5, testImageID2, false); err != nil {
|
|
| 174 |
+ if err = store.AddDigest(ref5.(reference.Canonical), testImageID2, false); err != nil {
|
|
| 175 | 175 |
t.Fatalf("error adding to store: %v", err)
|
| 176 | 176 |
} |
| 177 | 177 |
|
| 178 | 178 |
// Attempt to overwrite with force == false |
| 179 |
- if err = store.Add(ref4, testImageID3, false); err == nil || !strings.HasPrefix(err.Error(), "Conflict:") {
|
|
| 179 |
+ if err = store.AddTag(ref4, testImageID3, false); err == nil || !strings.HasPrefix(err.Error(), "Conflict:") {
|
|
| 180 | 180 |
t.Fatalf("did not get expected error on overwrite attempt - got %v", err)
|
| 181 | 181 |
} |
| 182 | 182 |
// Repeat to overwrite with force == true |
| 183 |
- if err = store.Add(ref4, testImageID3, true); err != nil {
|
|
| 183 |
+ if err = store.AddTag(ref4, testImageID3, true); err != nil {
|
|
| 184 | 184 |
t.Fatalf("failed to force tag overwrite: %v", err)
|
| 185 | 185 |
} |
| 186 | 186 |
|
| ... | ... |
@@ -326,3 +334,35 @@ func TestAddDeleteGet(t *testing.T) {
|
| 326 | 326 |
t.Fatal("Expected ErrDoesNotExist from Get")
|
| 327 | 327 |
} |
| 328 | 328 |
} |
| 329 |
+ |
|
| 330 |
+func TestInvalidTags(t *testing.T) {
|
|
| 331 |
+ tmpDir, err := ioutil.TempDir("", "tag-store-test")
|
|
| 332 |
+ defer os.RemoveAll(tmpDir) |
|
| 333 |
+ |
|
| 334 |
+ store, err := NewTagStore(filepath.Join(tmpDir, "repositories.json")) |
|
| 335 |
+ if err != nil {
|
|
| 336 |
+ t.Fatalf("error creating tag store: %v", err)
|
|
| 337 |
+ } |
|
| 338 |
+ id := image.ID("sha256:470022b8af682154f57a2163d030eb369549549cba00edc69e1b99b46bb924d6")
|
|
| 339 |
+ |
|
| 340 |
+ // sha256 as repo name |
|
| 341 |
+ ref, err := reference.ParseNamed("sha256:abc")
|
|
| 342 |
+ if err != nil {
|
|
| 343 |
+ t.Fatal(err) |
|
| 344 |
+ } |
|
| 345 |
+ err = store.AddTag(ref, id, true) |
|
| 346 |
+ if err == nil {
|
|
| 347 |
+ t.Fatalf("expected setting tag %q to fail", ref)
|
|
| 348 |
+ } |
|
| 349 |
+ |
|
| 350 |
+ // setting digest as a tag |
|
| 351 |
+ ref, err = reference.ParseNamed("registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6")
|
|
| 352 |
+ if err != nil {
|
|
| 353 |
+ t.Fatal(err) |
|
| 354 |
+ } |
|
| 355 |
+ err = store.AddTag(ref, id, true) |
|
| 356 |
+ if err == nil {
|
|
| 357 |
+ t.Fatalf("expected setting digest %q to fail", ref)
|
|
| 358 |
+ } |
|
| 359 |
+ |
|
| 360 |
+} |