Browse code

Validate adding digests to tagstore with go types

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>

Tonis Tiigi authored on 2015/11/26 05:42:40
Showing 9 changed files
... ...
@@ -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
... ...
@@ -195,7 +195,7 @@ func restoreCustomImage(driver graphdriver.Driver, is image.Store, ls layer.Stor
195 195
 				return err
196 196
 			}
197 197
 
198
-			if err := ts.Add(ref, id, true); err != nil {
198
+			if err := ts.AddTag(ref, id, true); err != nil {
199 199
 				return err
200 200
 			}
201 201
 
... ...
@@ -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
+}