github.com/wozhu6104/docker@v20.10.10+incompatible/reference/store_test.go (about) 1 package reference // import "github.com/docker/docker/reference" 2 3 import ( 4 "bytes" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "strings" 9 "testing" 10 11 "github.com/docker/distribution/reference" 12 digest "github.com/opencontainers/go-digest" 13 "gotest.tools/v3/assert" 14 is "gotest.tools/v3/assert/cmp" 15 ) 16 17 var ( 18 saveLoadTestCases = map[string]digest.Digest{ 19 "registry:5000/foobar:HEAD": "sha256:470022b8af682154f57a2163d030eb369549549cba00edc69e1b99b46bb924d6", 20 "registry:5000/foobar:alternate": "sha256:ae300ebc4a4f00693702cfb0a5e0b7bc527b353828dc86ad09fb95c8a681b793", 21 "registry:5000/foobar:latest": "sha256:6153498b9ac00968d71b66cca4eac37e990b5f9eb50c26877eb8799c8847451b", 22 "registry:5000/foobar:master": "sha256:6c9917af4c4e05001b346421959d7ea81b6dc9d25718466a37a6add865dfd7fc", 23 "jess/hollywood:latest": "sha256:ae7a5519a0a55a2d4ef20ddcbd5d0ca0888a1f7ab806acc8e2a27baf46f529fe", 24 "registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6": "sha256:24126a56805beb9711be5f4590cc2eb55ab8d4a85ebd618eed72bb19fc50631c", 25 "busybox:latest": "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c", 26 } 27 28 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"}}}`) 29 ) 30 31 func TestLoad(t *testing.T) { 32 jsonFile, err := ioutil.TempFile("", "tag-store-test") 33 if err != nil { 34 t.Fatalf("error creating temp file: %v", err) 35 } 36 defer os.RemoveAll(jsonFile.Name()) 37 38 // Write canned json to the temp file 39 _, err = jsonFile.Write(marshalledSaveLoadTestCases) 40 if err != nil { 41 t.Fatalf("error writing to temp file: %v", err) 42 } 43 jsonFile.Close() 44 45 store, err := NewReferenceStore(jsonFile.Name()) 46 if err != nil { 47 t.Fatalf("error creating tag store: %v", err) 48 } 49 50 for refStr, expectedID := range saveLoadTestCases { 51 ref, err := reference.ParseNormalizedNamed(refStr) 52 if err != nil { 53 t.Fatalf("failed to parse reference: %v", err) 54 } 55 id, err := store.Get(ref) 56 if err != nil { 57 t.Fatalf("could not find reference %s: %v", refStr, err) 58 } 59 if id != expectedID { 60 t.Fatalf("expected %s - got %s", expectedID, id) 61 } 62 } 63 } 64 65 func TestSave(t *testing.T) { 66 jsonFile, err := ioutil.TempFile("", "tag-store-test") 67 assert.NilError(t, err) 68 69 _, err = jsonFile.Write([]byte(`{}`)) 70 assert.NilError(t, err) 71 jsonFile.Close() 72 defer os.RemoveAll(jsonFile.Name()) 73 74 store, err := NewReferenceStore(jsonFile.Name()) 75 if err != nil { 76 t.Fatalf("error creating tag store: %v", err) 77 } 78 79 for refStr, id := range saveLoadTestCases { 80 ref, err := reference.ParseNormalizedNamed(refStr) 81 if err != nil { 82 t.Fatalf("failed to parse reference: %v", err) 83 } 84 if canonical, ok := ref.(reference.Canonical); ok { 85 err = store.AddDigest(canonical, id, false) 86 if err != nil { 87 t.Fatalf("could not add digest reference %s: %v", refStr, err) 88 } 89 } else { 90 err = store.AddTag(ref, id, false) 91 if err != nil { 92 t.Fatalf("could not add reference %s: %v", refStr, err) 93 } 94 } 95 } 96 97 jsonBytes, err := ioutil.ReadFile(jsonFile.Name()) 98 if err != nil { 99 t.Fatalf("could not read json file: %v", err) 100 } 101 102 if !bytes.Equal(jsonBytes, marshalledSaveLoadTestCases) { 103 t.Fatalf("save output did not match expectations\nexpected:\n%s\ngot:\n%s", marshalledSaveLoadTestCases, jsonBytes) 104 } 105 } 106 107 func TestAddDeleteGet(t *testing.T) { 108 jsonFile, err := ioutil.TempFile("", "tag-store-test") 109 if err != nil { 110 t.Fatalf("error creating temp file: %v", err) 111 } 112 _, err = jsonFile.Write([]byte(`{}`)) 113 assert.NilError(t, err) 114 _ = jsonFile.Close() 115 defer func() { _ = os.RemoveAll(jsonFile.Name()) }() 116 117 store, err := NewReferenceStore(jsonFile.Name()) 118 if err != nil { 119 t.Fatalf("error creating tag store: %v", err) 120 } 121 122 testImageID1 := digest.Digest("sha256:9655aef5fd742a1b4e1b7b163aa9f1c76c186304bf39102283d80927c916ca9c") 123 testImageID2 := digest.Digest("sha256:9655aef5fd742a1b4e1b7b163aa9f1c76c186304bf39102283d80927c916ca9d") 124 testImageID3 := digest.Digest("sha256:9655aef5fd742a1b4e1b7b163aa9f1c76c186304bf39102283d80927c916ca9e") 125 126 // Try adding a reference with no tag or digest 127 nameOnly, err := reference.ParseNormalizedNamed("username/repo") 128 if err != nil { 129 t.Fatalf("could not parse reference: %v", err) 130 } 131 if err = store.AddTag(nameOnly, testImageID1, false); err != nil { 132 t.Fatalf("error adding to store: %v", err) 133 } 134 135 // Add a few references 136 ref1, err := reference.ParseNormalizedNamed("username/repo1:latest") 137 if err != nil { 138 t.Fatalf("could not parse reference: %v", err) 139 } 140 if err = store.AddTag(ref1, testImageID1, false); err != nil { 141 t.Fatalf("error adding to store: %v", err) 142 } 143 144 ref2, err := reference.ParseNormalizedNamed("username/repo1:old") 145 if err != nil { 146 t.Fatalf("could not parse reference: %v", err) 147 } 148 if err = store.AddTag(ref2, testImageID2, false); err != nil { 149 t.Fatalf("error adding to store: %v", err) 150 } 151 152 ref3, err := reference.ParseNormalizedNamed("username/repo1:alias") 153 if err != nil { 154 t.Fatalf("could not parse reference: %v", err) 155 } 156 if err = store.AddTag(ref3, testImageID1, false); err != nil { 157 t.Fatalf("error adding to store: %v", err) 158 } 159 160 ref4, err := reference.ParseNormalizedNamed("username/repo2:latest") 161 if err != nil { 162 t.Fatalf("could not parse reference: %v", err) 163 } 164 if err = store.AddTag(ref4, testImageID2, false); err != nil { 165 t.Fatalf("error adding to store: %v", err) 166 } 167 // Write the same values again; should silently succeed 168 if err = store.AddTag(ref4, testImageID2, false); err != nil { 169 t.Fatalf("error redundantly adding to store: %v", err) 170 } 171 172 ref5, err := reference.ParseNormalizedNamed("username/repo3@sha256:58153dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c") 173 if err != nil { 174 t.Fatalf("could not parse reference: %v", err) 175 } 176 if err = store.AddDigest(ref5.(reference.Canonical), testImageID2, false); err != nil { 177 t.Fatalf("error adding to store: %v", err) 178 } 179 // Write the same values again; should silently succeed 180 if err = store.AddDigest(ref5.(reference.Canonical), testImageID2, false); err != nil { 181 t.Fatalf("error redundantly adding to store: %v", err) 182 } 183 184 // Attempt to overwrite with force == false 185 if err = store.AddTag(ref4, testImageID3, false); err == nil || !strings.HasPrefix(err.Error(), "Conflict:") { 186 t.Fatalf("did not get expected error on overwrite attempt - got %v", err) 187 } 188 // Repeat to overwrite with force == true 189 if err = store.AddTag(ref4, testImageID3, true); err != nil { 190 t.Fatalf("failed to force tag overwrite: %v", err) 191 } 192 193 // Check references so far 194 id, err := store.Get(nameOnly) 195 if err != nil { 196 t.Fatalf("Get returned error: %v", err) 197 } 198 if id != testImageID1 { 199 t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID1.String()) 200 } 201 202 id, err = store.Get(ref1) 203 if err != nil { 204 t.Fatalf("Get returned error: %v", err) 205 } 206 if id != testImageID1 { 207 t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID1.String()) 208 } 209 210 id, err = store.Get(ref2) 211 if err != nil { 212 t.Fatalf("Get returned error: %v", err) 213 } 214 if id != testImageID2 { 215 t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID2.String()) 216 } 217 218 id, err = store.Get(ref3) 219 if err != nil { 220 t.Fatalf("Get returned error: %v", err) 221 } 222 if id != testImageID1 { 223 t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID1.String()) 224 } 225 226 id, err = store.Get(ref4) 227 if err != nil { 228 t.Fatalf("Get returned error: %v", err) 229 } 230 if id != testImageID3 { 231 t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID3.String()) 232 } 233 234 id, err = store.Get(ref5) 235 if err != nil { 236 t.Fatalf("Get returned error: %v", err) 237 } 238 if id != testImageID2 { 239 t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID3.String()) 240 } 241 242 // Get should return ErrDoesNotExist for a nonexistent repo 243 nonExistRepo, err := reference.ParseNormalizedNamed("username/nonexistrepo:latest") 244 if err != nil { 245 t.Fatalf("could not parse reference: %v", err) 246 } 247 if _, err = store.Get(nonExistRepo); err != ErrDoesNotExist { 248 t.Fatal("Expected ErrDoesNotExist from Get") 249 } 250 251 // Get should return ErrDoesNotExist for a nonexistent tag 252 nonExistTag, err := reference.ParseNormalizedNamed("username/repo1:nonexist") 253 if err != nil { 254 t.Fatalf("could not parse reference: %v", err) 255 } 256 if _, err = store.Get(nonExistTag); err != ErrDoesNotExist { 257 t.Fatal("Expected ErrDoesNotExist from Get") 258 } 259 260 // Check References 261 refs := store.References(testImageID1) 262 if len(refs) != 3 { 263 t.Fatal("unexpected number of references") 264 } 265 // Looking for the references in this order verifies that they are 266 // returned lexically sorted. 267 if refs[0].String() != ref3.String() { 268 t.Fatalf("unexpected reference: %v", refs[0].String()) 269 } 270 if refs[1].String() != ref1.String() { 271 t.Fatalf("unexpected reference: %v", refs[1].String()) 272 } 273 if refs[2].String() != nameOnly.String()+":latest" { 274 t.Fatalf("unexpected reference: %v", refs[2].String()) 275 } 276 277 // Check ReferencesByName 278 repoName, err := reference.ParseNormalizedNamed("username/repo1") 279 if err != nil { 280 t.Fatalf("could not parse reference: %v", err) 281 } 282 associations := store.ReferencesByName(repoName) 283 if len(associations) != 3 { 284 t.Fatal("unexpected number of associations") 285 } 286 // Looking for the associations in this order verifies that they are 287 // returned lexically sorted. 288 if associations[0].Ref.String() != ref3.String() { 289 t.Fatalf("unexpected reference: %v", associations[0].Ref.String()) 290 } 291 if associations[0].ID != testImageID1 { 292 t.Fatalf("unexpected reference: %v", associations[0].Ref.String()) 293 } 294 if associations[1].Ref.String() != ref1.String() { 295 t.Fatalf("unexpected reference: %v", associations[1].Ref.String()) 296 } 297 if associations[1].ID != testImageID1 { 298 t.Fatalf("unexpected reference: %v", associations[1].Ref.String()) 299 } 300 if associations[2].Ref.String() != ref2.String() { 301 t.Fatalf("unexpected reference: %v", associations[2].Ref.String()) 302 } 303 if associations[2].ID != testImageID2 { 304 t.Fatalf("unexpected reference: %v", associations[2].Ref.String()) 305 } 306 307 // Delete should return ErrDoesNotExist for a nonexistent repo 308 if _, err = store.Delete(nonExistRepo); err != ErrDoesNotExist { 309 t.Fatal("Expected ErrDoesNotExist from Delete") 310 } 311 312 // Delete should return ErrDoesNotExist for a nonexistent tag 313 if _, err = store.Delete(nonExistTag); err != ErrDoesNotExist { 314 t.Fatal("Expected ErrDoesNotExist from Delete") 315 } 316 317 // Delete a few references 318 if deleted, err := store.Delete(ref1); err != nil || !deleted { 319 t.Fatal("Delete failed") 320 } 321 if _, err := store.Get(ref1); err != ErrDoesNotExist { 322 t.Fatal("Expected ErrDoesNotExist from Get") 323 } 324 if deleted, err := store.Delete(ref5); err != nil || !deleted { 325 t.Fatal("Delete failed") 326 } 327 if _, err := store.Get(ref5); err != ErrDoesNotExist { 328 t.Fatal("Expected ErrDoesNotExist from Get") 329 } 330 if deleted, err := store.Delete(nameOnly); err != nil || !deleted { 331 t.Fatal("Delete failed") 332 } 333 if _, err := store.Get(nameOnly); err != ErrDoesNotExist { 334 t.Fatal("Expected ErrDoesNotExist from Get") 335 } 336 } 337 338 func TestInvalidTags(t *testing.T) { 339 tmpDir, err := ioutil.TempDir("", "tag-store-test") 340 assert.NilError(t, err) 341 defer os.RemoveAll(tmpDir) 342 343 store, err := NewReferenceStore(filepath.Join(tmpDir, "repositories.json")) 344 assert.NilError(t, err) 345 id := digest.Digest("sha256:470022b8af682154f57a2163d030eb369549549cba00edc69e1b99b46bb924d6") 346 347 // sha256 as repo name 348 ref, err := reference.ParseNormalizedNamed("sha256:abc") 349 assert.NilError(t, err) 350 err = store.AddTag(ref, id, true) 351 assert.Check(t, is.ErrorContains(err, "")) 352 353 // setting digest as a tag 354 ref, err = reference.ParseNormalizedNamed("registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6") 355 assert.NilError(t, err) 356 357 err = store.AddTag(ref, id, true) 358 assert.Check(t, is.ErrorContains(err, "")) 359 }