12 "github.com/docker/distribution/reference"
13 "github.com/opencontainers/go-digest"
17 saveLoadTestCases = map[string]digest.Digest{
18 "registry:5000/foobar:HEAD": "sha256:470022b8af682154f57a2163d030eb369549549cba00edc69e1b99b46bb924d6",
19 "registry:5000/foobar:alternate": "sha256:ae300ebc4a4f00693702cfb0a5e0b7bc527b353828dc86ad09fb95c8a681b793",
20 "registry:5000/foobar:latest": "sha256:6153498b9ac00968d71b66cca4eac37e990b5f9eb50c26877eb8799c8847451b",
21 "registry:5000/foobar:master": "sha256:6c9917af4c4e05001b346421959d7ea81b6dc9d25718466a37a6add865dfd7fc",
22 "jess/hollywood:latest": "sha256:ae7a5519a0a55a2d4ef20ddcbd5d0ca0888a1f7ab806acc8e2a27baf46f529fe",
23 "registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6": "sha256:24126a56805beb9711be5f4590cc2eb55ab8d4a85ebd618eed72bb19fc50631c",
24 "busybox:latest": "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c",
27 marshalledSaveLoadTestCases = []byte(`{"Repositories":{"busybox":{"busybox:latest":"sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c"},"jess/hollywood":{"jess/hollywood:latest":"sha256:ae7a5519a0a55a2d4ef20ddcbd5d0ca0888a1f7ab806acc8e2a27baf46f529fe"},"registry":{"registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6":"sha256:24126a56805beb9711be5f4590cc2eb55ab8d4a85ebd618eed72bb19fc50631c"},"registry:5000/foobar":{"registry:5000/foobar:HEAD":"sha256:470022b8af682154f57a2163d030eb369549549cba00edc69e1b99b46bb924d6","registry:5000/foobar:alternate":"sha256:ae300ebc4a4f00693702cfb0a5e0b7bc527b353828dc86ad09fb95c8a681b793","registry:5000/foobar:latest":"sha256:6153498b9ac00968d71b66cca4eac37e990b5f9eb50c26877eb8799c8847451b","registry:5000/foobar:master":"sha256:6c9917af4c4e05001b346421959d7ea81b6dc9d25718466a37a6add865dfd7fc"}}}`)
30 func TestLoad(t *testing.T) {
31 jsonFile, err := ioutil.TempFile("", "tag-store-test")
33 t.Fatalf("error creating temp file: %v", err)
35 defer os.RemoveAll(jsonFile.Name())
37 // Write canned json to the temp file
38 _, err = jsonFile.Write(marshalledSaveLoadTestCases)
40 t.Fatalf("error writing to temp file: %v", err)
44 store, err := NewReferenceStore(jsonFile.Name(), runtime.GOOS)
46 t.Fatalf("error creating tag store: %v", err)
49 for refStr, expectedID := range saveLoadTestCases {
50 ref, err := reference.ParseNormalizedNamed(refStr)
52 t.Fatalf("failed to parse reference: %v", err)
54 id, err := store.Get(ref)
56 t.Fatalf("could not find reference %s: %v", refStr, err)
59 t.Fatalf("expected %s - got %s", expectedID, id)
64 func TestSave(t *testing.T) {
65 jsonFile, err := ioutil.TempFile("", "tag-store-test")
67 t.Fatalf("error creating temp file: %v", err)
69 _, err = jsonFile.Write([]byte(`{}`))
71 defer os.RemoveAll(jsonFile.Name())
73 store, err := NewReferenceStore(jsonFile.Name(), runtime.GOOS)
75 t.Fatalf("error creating tag store: %v", err)
78 for refStr, id := range saveLoadTestCases {
79 ref, err := reference.ParseNormalizedNamed(refStr)
81 t.Fatalf("failed to parse reference: %v", err)
83 if canonical, ok := ref.(reference.Canonical); ok {
84 err = store.AddDigest(canonical, id, false)
86 t.Fatalf("could not add digest reference %s: %v", refStr, err)
89 err = store.AddTag(ref, id, false)
91 t.Fatalf("could not add reference %s: %v", refStr, err)
96 jsonBytes, err := ioutil.ReadFile(jsonFile.Name())
98 t.Fatalf("could not read json file: %v", err)
101 if !bytes.Equal(jsonBytes, marshalledSaveLoadTestCases) {
102 t.Fatalf("save output did not match expectations\nexpected:\n%s\ngot:\n%s", marshalledSaveLoadTestCases, jsonBytes)
106 func TestAddDeleteGet(t *testing.T) {
107 jsonFile, err := ioutil.TempFile("", "tag-store-test")
109 t.Fatalf("error creating temp file: %v", err)
111 _, err = jsonFile.Write([]byte(`{}`))
113 defer os.RemoveAll(jsonFile.Name())
115 store, err := NewReferenceStore(jsonFile.Name(), runtime.GOOS)
117 t.Fatalf("error creating tag store: %v", err)
120 testImageID1 := digest.Digest("sha256:9655aef5fd742a1b4e1b7b163aa9f1c76c186304bf39102283d80927c916ca9c")
121 testImageID2 := digest.Digest("sha256:9655aef5fd742a1b4e1b7b163aa9f1c76c186304bf39102283d80927c916ca9d")
122 testImageID3 := digest.Digest("sha256:9655aef5fd742a1b4e1b7b163aa9f1c76c186304bf39102283d80927c916ca9e")
124 // Try adding a reference with no tag or digest
125 nameOnly, err := reference.ParseNormalizedNamed("username/repo")
127 t.Fatalf("could not parse reference: %v", err)
129 if err = store.AddTag(nameOnly, testImageID1, false); err != nil {
130 t.Fatalf("error adding to store: %v", err)
133 // Add a few references
134 ref1, err := reference.ParseNormalizedNamed("username/repo1:latest")
136 t.Fatalf("could not parse reference: %v", err)
138 if err = store.AddTag(ref1, testImageID1, false); err != nil {
139 t.Fatalf("error adding to store: %v", err)
142 ref2, err := reference.ParseNormalizedNamed("username/repo1:old")
144 t.Fatalf("could not parse reference: %v", err)
146 if err = store.AddTag(ref2, testImageID2, false); err != nil {
147 t.Fatalf("error adding to store: %v", err)
150 ref3, err := reference.ParseNormalizedNamed("username/repo1:alias")
152 t.Fatalf("could not parse reference: %v", err)
154 if err = store.AddTag(ref3, testImageID1, false); err != nil {
155 t.Fatalf("error adding to store: %v", err)
158 ref4, err := reference.ParseNormalizedNamed("username/repo2:latest")
160 t.Fatalf("could not parse reference: %v", err)
162 if err = store.AddTag(ref4, testImageID2, false); err != nil {
163 t.Fatalf("error adding to store: %v", err)
166 ref5, err := reference.ParseNormalizedNamed("username/repo3@sha256:58153dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c")
168 t.Fatalf("could not parse reference: %v", err)
170 if err = store.AddDigest(ref5.(reference.Canonical), testImageID2, false); err != nil {
171 t.Fatalf("error adding to store: %v", err)
174 // Attempt to overwrite with force == false
175 if err = store.AddTag(ref4, testImageID3, false); err == nil || !strings.HasPrefix(err.Error(), "Conflict:") {
176 t.Fatalf("did not get expected error on overwrite attempt - got %v", err)
178 // Repeat to overwrite with force == true
179 if err = store.AddTag(ref4, testImageID3, true); err != nil {
180 t.Fatalf("failed to force tag overwrite: %v", err)
183 // Check references so far
184 id, err := store.Get(nameOnly)
186 t.Fatalf("Get returned error: %v", err)
188 if id != testImageID1 {
189 t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID1.String())
192 id, err = store.Get(ref1)
194 t.Fatalf("Get returned error: %v", err)
196 if id != testImageID1 {
197 t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID1.String())
200 id, err = store.Get(ref2)
202 t.Fatalf("Get returned error: %v", err)
204 if id != testImageID2 {
205 t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID2.String())
208 id, err = store.Get(ref3)
210 t.Fatalf("Get returned error: %v", err)
212 if id != testImageID1 {
213 t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID1.String())
216 id, err = store.Get(ref4)
218 t.Fatalf("Get returned error: %v", err)
220 if id != testImageID3 {
221 t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID3.String())
224 id, err = store.Get(ref5)
226 t.Fatalf("Get returned error: %v", err)
228 if id != testImageID2 {
229 t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID3.String())
232 // Get should return ErrDoesNotExist for a nonexistent repo
233 nonExistRepo, err := reference.ParseNormalizedNamed("username/nonexistrepo:latest")
235 t.Fatalf("could not parse reference: %v", err)
237 if _, err = store.Get(nonExistRepo); err != ErrDoesNotExist {
238 t.Fatal("Expected ErrDoesNotExist from Get")
241 // Get should return ErrDoesNotExist for a nonexistent tag
242 nonExistTag, err := reference.ParseNormalizedNamed("username/repo1:nonexist")
244 t.Fatalf("could not parse reference: %v", err)
246 if _, err = store.Get(nonExistTag); err != ErrDoesNotExist {
247 t.Fatal("Expected ErrDoesNotExist from Get")
251 refs := store.References(testImageID1)
253 t.Fatal("unexpected number of references")
255 // Looking for the references in this order verifies that they are
256 // returned lexically sorted.
257 if refs[0].String() != ref3.String() {
258 t.Fatalf("unexpected reference: %v", refs[0].String())
260 if refs[1].String() != ref1.String() {
261 t.Fatalf("unexpected reference: %v", refs[1].String())
263 if refs[2].String() != nameOnly.String()+":latest" {
264 t.Fatalf("unexpected reference: %v", refs[2].String())
267 // Check ReferencesByName
268 repoName, err := reference.ParseNormalizedNamed("username/repo1")
270 t.Fatalf("could not parse reference: %v", err)
272 associations := store.ReferencesByName(repoName)
273 if len(associations) != 3 {
274 t.Fatal("unexpected number of associations")
276 // Looking for the associations in this order verifies that they are
277 // returned lexically sorted.
278 if associations[0].Ref.String() != ref3.String() {
279 t.Fatalf("unexpected reference: %v", associations[0].Ref.String())
281 if associations[0].ID != testImageID1 {
282 t.Fatalf("unexpected reference: %v", associations[0].Ref.String())
284 if associations[1].Ref.String() != ref1.String() {
285 t.Fatalf("unexpected reference: %v", associations[1].Ref.String())
287 if associations[1].ID != testImageID1 {
288 t.Fatalf("unexpected reference: %v", associations[1].Ref.String())
290 if associations[2].Ref.String() != ref2.String() {
291 t.Fatalf("unexpected reference: %v", associations[2].Ref.String())
293 if associations[2].ID != testImageID2 {
294 t.Fatalf("unexpected reference: %v", associations[2].Ref.String())
297 // Delete should return ErrDoesNotExist for a nonexistent repo
298 if _, err = store.Delete(nonExistRepo); err != ErrDoesNotExist {
299 t.Fatal("Expected ErrDoesNotExist from Delete")
302 // Delete should return ErrDoesNotExist for a nonexistent tag
303 if _, err = store.Delete(nonExistTag); err != ErrDoesNotExist {
304 t.Fatal("Expected ErrDoesNotExist from Delete")
307 // Delete a few references
308 if deleted, err := store.Delete(ref1); err != nil || deleted != true {
309 t.Fatal("Delete failed")
311 if _, err := store.Get(ref1); err != ErrDoesNotExist {
312 t.Fatal("Expected ErrDoesNotExist from Get")
314 if deleted, err := store.Delete(ref5); err != nil || deleted != true {
315 t.Fatal("Delete failed")
317 if _, err := store.Get(ref5); err != ErrDoesNotExist {
318 t.Fatal("Expected ErrDoesNotExist from Get")
320 if deleted, err := store.Delete(nameOnly); err != nil || deleted != true {
321 t.Fatal("Delete failed")
323 if _, err := store.Get(nameOnly); err != ErrDoesNotExist {
324 t.Fatal("Expected ErrDoesNotExist from Get")
328 func TestInvalidTags(t *testing.T) {
329 tmpDir, err := ioutil.TempDir("", "tag-store-test")
330 defer os.RemoveAll(tmpDir)
332 store, err := NewReferenceStore(filepath.Join(tmpDir, "repositories.json"), runtime.GOOS)
334 t.Fatalf("error creating tag store: %v", err)
336 id := digest.Digest("sha256:470022b8af682154f57a2163d030eb369549549cba00edc69e1b99b46bb924d6")
338 // sha256 as repo name
339 ref, err := reference.ParseNormalizedNamed("sha256:abc")
343 err = store.AddTag(ref, id, true)
345 t.Fatalf("expected setting tag %q to fail", ref)
348 // setting digest as a tag
349 ref, err = reference.ParseNormalizedNamed("registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6")
353 err = store.AddTag(ref, id, true)
355 t.Fatalf("expected setting tag %q to fail", ref)