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