github.com/sijibomii/docker@v0.0.0-20231230191044-5cf6ca554647/registry/registry_test.go (about) 1 package registry 2 3 import ( 4 "fmt" 5 "net/http" 6 "net/http/httputil" 7 "net/url" 8 "strings" 9 "testing" 10 11 "github.com/docker/distribution/registry/client/transport" 12 "github.com/docker/docker/reference" 13 "github.com/docker/engine-api/types" 14 registrytypes "github.com/docker/engine-api/types/registry" 15 ) 16 17 var ( 18 token = []string{"fake-token"} 19 ) 20 21 const ( 22 imageID = "42d718c941f5c532ac049bf0b0ab53f0062f09a03afd4aa4a02c098e46032b9d" 23 REPO = "foo42/bar" 24 ) 25 26 func spawnTestRegistrySession(t *testing.T) *Session { 27 authConfig := &types.AuthConfig{} 28 endpoint, err := NewV1Endpoint(makeIndex("/v1/"), "", nil) 29 if err != nil { 30 t.Fatal(err) 31 } 32 userAgent := "docker test client" 33 var tr http.RoundTripper = debugTransport{NewTransport(nil), t.Log} 34 tr = transport.NewTransport(AuthTransport(tr, authConfig, false), DockerHeaders(userAgent, nil)...) 35 client := HTTPClient(tr) 36 r, err := NewSession(client, authConfig, endpoint) 37 if err != nil { 38 t.Fatal(err) 39 } 40 // In a normal scenario for the v1 registry, the client should send a `X-Docker-Token: true` 41 // header while authenticating, in order to retrieve a token that can be later used to 42 // perform authenticated actions. 43 // 44 // The mock v1 registry does not support that, (TODO(tiborvass): support it), instead, 45 // it will consider authenticated any request with the header `X-Docker-Token: fake-token`. 46 // 47 // Because we know that the client's transport is an `*authTransport` we simply cast it, 48 // in order to set the internal cached token to the fake token, and thus send that fake token 49 // upon every subsequent requests. 50 r.client.Transport.(*authTransport).token = token 51 return r 52 } 53 54 func TestPingRegistryEndpoint(t *testing.T) { 55 testPing := func(index *registrytypes.IndexInfo, expectedStandalone bool, assertMessage string) { 56 ep, err := NewV1Endpoint(index, "", nil) 57 if err != nil { 58 t.Fatal(err) 59 } 60 regInfo, err := ep.Ping() 61 if err != nil { 62 t.Fatal(err) 63 } 64 65 assertEqual(t, regInfo.Standalone, expectedStandalone, assertMessage) 66 } 67 68 testPing(makeIndex("/v1/"), true, "Expected standalone to be true (default)") 69 testPing(makeHTTPSIndex("/v1/"), true, "Expected standalone to be true (default)") 70 testPing(makePublicIndex(), false, "Expected standalone to be false for public index") 71 } 72 73 func TestEndpoint(t *testing.T) { 74 // Simple wrapper to fail test if err != nil 75 expandEndpoint := func(index *registrytypes.IndexInfo) *V1Endpoint { 76 endpoint, err := NewV1Endpoint(index, "", nil) 77 if err != nil { 78 t.Fatal(err) 79 } 80 return endpoint 81 } 82 83 assertInsecureIndex := func(index *registrytypes.IndexInfo) { 84 index.Secure = true 85 _, err := NewV1Endpoint(index, "", nil) 86 assertNotEqual(t, err, nil, index.Name+": Expected error for insecure index") 87 assertEqual(t, strings.Contains(err.Error(), "insecure-registry"), true, index.Name+": Expected insecure-registry error for insecure index") 88 index.Secure = false 89 } 90 91 assertSecureIndex := func(index *registrytypes.IndexInfo) { 92 index.Secure = true 93 _, err := NewV1Endpoint(index, "", nil) 94 assertNotEqual(t, err, nil, index.Name+": Expected cert error for secure index") 95 assertEqual(t, strings.Contains(err.Error(), "certificate signed by unknown authority"), true, index.Name+": Expected cert error for secure index") 96 index.Secure = false 97 } 98 99 index := ®istrytypes.IndexInfo{} 100 index.Name = makeURL("/v1/") 101 endpoint := expandEndpoint(index) 102 assertEqual(t, endpoint.String(), index.Name, "Expected endpoint to be "+index.Name) 103 assertInsecureIndex(index) 104 105 index.Name = makeURL("") 106 endpoint = expandEndpoint(index) 107 assertEqual(t, endpoint.String(), index.Name+"/v1/", index.Name+": Expected endpoint to be "+index.Name+"/v1/") 108 assertInsecureIndex(index) 109 110 httpURL := makeURL("") 111 index.Name = strings.SplitN(httpURL, "://", 2)[1] 112 endpoint = expandEndpoint(index) 113 assertEqual(t, endpoint.String(), httpURL+"/v1/", index.Name+": Expected endpoint to be "+httpURL+"/v1/") 114 assertInsecureIndex(index) 115 116 index.Name = makeHTTPSURL("/v1/") 117 endpoint = expandEndpoint(index) 118 assertEqual(t, endpoint.String(), index.Name, "Expected endpoint to be "+index.Name) 119 assertSecureIndex(index) 120 121 index.Name = makeHTTPSURL("") 122 endpoint = expandEndpoint(index) 123 assertEqual(t, endpoint.String(), index.Name+"/v1/", index.Name+": Expected endpoint to be "+index.Name+"/v1/") 124 assertSecureIndex(index) 125 126 httpsURL := makeHTTPSURL("") 127 index.Name = strings.SplitN(httpsURL, "://", 2)[1] 128 endpoint = expandEndpoint(index) 129 assertEqual(t, endpoint.String(), httpsURL+"/v1/", index.Name+": Expected endpoint to be "+httpsURL+"/v1/") 130 assertSecureIndex(index) 131 132 badEndpoints := []string{ 133 "http://127.0.0.1/v1/", 134 "https://127.0.0.1/v1/", 135 "http://127.0.0.1", 136 "https://127.0.0.1", 137 "127.0.0.1", 138 } 139 for _, address := range badEndpoints { 140 index.Name = address 141 _, err := NewV1Endpoint(index, "", nil) 142 checkNotEqual(t, err, nil, "Expected error while expanding bad endpoint") 143 } 144 } 145 146 func TestGetRemoteHistory(t *testing.T) { 147 r := spawnTestRegistrySession(t) 148 hist, err := r.GetRemoteHistory(imageID, makeURL("/v1/")) 149 if err != nil { 150 t.Fatal(err) 151 } 152 assertEqual(t, len(hist), 2, "Expected 2 images in history") 153 assertEqual(t, hist[0], imageID, "Expected "+imageID+"as first ancestry") 154 assertEqual(t, hist[1], "77dbf71da1d00e3fbddc480176eac8994025630c6590d11cfc8fe1209c2a1d20", 155 "Unexpected second ancestry") 156 } 157 158 func TestLookupRemoteImage(t *testing.T) { 159 r := spawnTestRegistrySession(t) 160 err := r.LookupRemoteImage(imageID, makeURL("/v1/")) 161 assertEqual(t, err, nil, "Expected error of remote lookup to nil") 162 if err := r.LookupRemoteImage("abcdef", makeURL("/v1/")); err == nil { 163 t.Fatal("Expected error of remote lookup to not nil") 164 } 165 } 166 167 func TestGetRemoteImageJSON(t *testing.T) { 168 r := spawnTestRegistrySession(t) 169 json, size, err := r.GetRemoteImageJSON(imageID, makeURL("/v1/")) 170 if err != nil { 171 t.Fatal(err) 172 } 173 assertEqual(t, size, int64(154), "Expected size 154") 174 if len(json) == 0 { 175 t.Fatal("Expected non-empty json") 176 } 177 178 _, _, err = r.GetRemoteImageJSON("abcdef", makeURL("/v1/")) 179 if err == nil { 180 t.Fatal("Expected image not found error") 181 } 182 } 183 184 func TestGetRemoteImageLayer(t *testing.T) { 185 r := spawnTestRegistrySession(t) 186 data, err := r.GetRemoteImageLayer(imageID, makeURL("/v1/"), 0) 187 if err != nil { 188 t.Fatal(err) 189 } 190 if data == nil { 191 t.Fatal("Expected non-nil data result") 192 } 193 194 _, err = r.GetRemoteImageLayer("abcdef", makeURL("/v1/"), 0) 195 if err == nil { 196 t.Fatal("Expected image not found error") 197 } 198 } 199 200 func TestGetRemoteTag(t *testing.T) { 201 r := spawnTestRegistrySession(t) 202 repoRef, err := reference.ParseNamed(REPO) 203 if err != nil { 204 t.Fatal(err) 205 } 206 tag, err := r.GetRemoteTag([]string{makeURL("/v1/")}, repoRef, "test") 207 if err != nil { 208 t.Fatal(err) 209 } 210 assertEqual(t, tag, imageID, "Expected tag test to map to "+imageID) 211 212 bazRef, err := reference.ParseNamed("foo42/baz") 213 if err != nil { 214 t.Fatal(err) 215 } 216 _, err = r.GetRemoteTag([]string{makeURL("/v1/")}, bazRef, "foo") 217 if err != ErrRepoNotFound { 218 t.Fatal("Expected ErrRepoNotFound error when fetching tag for bogus repo") 219 } 220 } 221 222 func TestGetRemoteTags(t *testing.T) { 223 r := spawnTestRegistrySession(t) 224 repoRef, err := reference.ParseNamed(REPO) 225 if err != nil { 226 t.Fatal(err) 227 } 228 tags, err := r.GetRemoteTags([]string{makeURL("/v1/")}, repoRef) 229 if err != nil { 230 t.Fatal(err) 231 } 232 assertEqual(t, len(tags), 2, "Expected two tags") 233 assertEqual(t, tags["latest"], imageID, "Expected tag latest to map to "+imageID) 234 assertEqual(t, tags["test"], imageID, "Expected tag test to map to "+imageID) 235 236 bazRef, err := reference.ParseNamed("foo42/baz") 237 if err != nil { 238 t.Fatal(err) 239 } 240 _, err = r.GetRemoteTags([]string{makeURL("/v1/")}, bazRef) 241 if err != ErrRepoNotFound { 242 t.Fatal("Expected ErrRepoNotFound error when fetching tags for bogus repo") 243 } 244 } 245 246 func TestGetRepositoryData(t *testing.T) { 247 r := spawnTestRegistrySession(t) 248 parsedURL, err := url.Parse(makeURL("/v1/")) 249 if err != nil { 250 t.Fatal(err) 251 } 252 host := "http://" + parsedURL.Host + "/v1/" 253 repoRef, err := reference.ParseNamed(REPO) 254 if err != nil { 255 t.Fatal(err) 256 } 257 data, err := r.GetRepositoryData(repoRef) 258 if err != nil { 259 t.Fatal(err) 260 } 261 assertEqual(t, len(data.ImgList), 2, "Expected 2 images in ImgList") 262 assertEqual(t, len(data.Endpoints), 2, 263 fmt.Sprintf("Expected 2 endpoints in Endpoints, found %d instead", len(data.Endpoints))) 264 assertEqual(t, data.Endpoints[0], host, 265 fmt.Sprintf("Expected first endpoint to be %s but found %s instead", host, data.Endpoints[0])) 266 assertEqual(t, data.Endpoints[1], "http://test.example.com/v1/", 267 fmt.Sprintf("Expected first endpoint to be http://test.example.com/v1/ but found %s instead", data.Endpoints[1])) 268 269 } 270 271 func TestPushImageJSONRegistry(t *testing.T) { 272 r := spawnTestRegistrySession(t) 273 imgData := &ImgData{ 274 ID: "77dbf71da1d00e3fbddc480176eac8994025630c6590d11cfc8fe1209c2a1d20", 275 Checksum: "sha256:1ac330d56e05eef6d438586545ceff7550d3bdcb6b19961f12c5ba714ee1bb37", 276 } 277 278 err := r.PushImageJSONRegistry(imgData, []byte{0x42, 0xdf, 0x0}, makeURL("/v1/")) 279 if err != nil { 280 t.Fatal(err) 281 } 282 } 283 284 func TestPushImageLayerRegistry(t *testing.T) { 285 r := spawnTestRegistrySession(t) 286 layer := strings.NewReader("") 287 _, _, err := r.PushImageLayerRegistry(imageID, layer, makeURL("/v1/"), []byte{}) 288 if err != nil { 289 t.Fatal(err) 290 } 291 } 292 293 func TestParseRepositoryInfo(t *testing.T) { 294 type staticRepositoryInfo struct { 295 Index *registrytypes.IndexInfo 296 RemoteName string 297 CanonicalName string 298 LocalName string 299 Official bool 300 } 301 302 expectedRepoInfos := map[string]staticRepositoryInfo{ 303 "fooo/bar": { 304 Index: ®istrytypes.IndexInfo{ 305 Name: IndexName, 306 Official: true, 307 }, 308 RemoteName: "fooo/bar", 309 LocalName: "fooo/bar", 310 CanonicalName: "docker.io/fooo/bar", 311 Official: false, 312 }, 313 "library/ubuntu": { 314 Index: ®istrytypes.IndexInfo{ 315 Name: IndexName, 316 Official: true, 317 }, 318 RemoteName: "library/ubuntu", 319 LocalName: "ubuntu", 320 CanonicalName: "docker.io/library/ubuntu", 321 Official: true, 322 }, 323 "nonlibrary/ubuntu": { 324 Index: ®istrytypes.IndexInfo{ 325 Name: IndexName, 326 Official: true, 327 }, 328 RemoteName: "nonlibrary/ubuntu", 329 LocalName: "nonlibrary/ubuntu", 330 CanonicalName: "docker.io/nonlibrary/ubuntu", 331 Official: false, 332 }, 333 "ubuntu": { 334 Index: ®istrytypes.IndexInfo{ 335 Name: IndexName, 336 Official: true, 337 }, 338 RemoteName: "library/ubuntu", 339 LocalName: "ubuntu", 340 CanonicalName: "docker.io/library/ubuntu", 341 Official: true, 342 }, 343 "other/library": { 344 Index: ®istrytypes.IndexInfo{ 345 Name: IndexName, 346 Official: true, 347 }, 348 RemoteName: "other/library", 349 LocalName: "other/library", 350 CanonicalName: "docker.io/other/library", 351 Official: false, 352 }, 353 "127.0.0.1:8000/private/moonbase": { 354 Index: ®istrytypes.IndexInfo{ 355 Name: "127.0.0.1:8000", 356 Official: false, 357 }, 358 RemoteName: "private/moonbase", 359 LocalName: "127.0.0.1:8000/private/moonbase", 360 CanonicalName: "127.0.0.1:8000/private/moonbase", 361 Official: false, 362 }, 363 "127.0.0.1:8000/privatebase": { 364 Index: ®istrytypes.IndexInfo{ 365 Name: "127.0.0.1:8000", 366 Official: false, 367 }, 368 RemoteName: "privatebase", 369 LocalName: "127.0.0.1:8000/privatebase", 370 CanonicalName: "127.0.0.1:8000/privatebase", 371 Official: false, 372 }, 373 "localhost:8000/private/moonbase": { 374 Index: ®istrytypes.IndexInfo{ 375 Name: "localhost:8000", 376 Official: false, 377 }, 378 RemoteName: "private/moonbase", 379 LocalName: "localhost:8000/private/moonbase", 380 CanonicalName: "localhost:8000/private/moonbase", 381 Official: false, 382 }, 383 "localhost:8000/privatebase": { 384 Index: ®istrytypes.IndexInfo{ 385 Name: "localhost:8000", 386 Official: false, 387 }, 388 RemoteName: "privatebase", 389 LocalName: "localhost:8000/privatebase", 390 CanonicalName: "localhost:8000/privatebase", 391 Official: false, 392 }, 393 "example.com/private/moonbase": { 394 Index: ®istrytypes.IndexInfo{ 395 Name: "example.com", 396 Official: false, 397 }, 398 RemoteName: "private/moonbase", 399 LocalName: "example.com/private/moonbase", 400 CanonicalName: "example.com/private/moonbase", 401 Official: false, 402 }, 403 "example.com/privatebase": { 404 Index: ®istrytypes.IndexInfo{ 405 Name: "example.com", 406 Official: false, 407 }, 408 RemoteName: "privatebase", 409 LocalName: "example.com/privatebase", 410 CanonicalName: "example.com/privatebase", 411 Official: false, 412 }, 413 "example.com:8000/private/moonbase": { 414 Index: ®istrytypes.IndexInfo{ 415 Name: "example.com:8000", 416 Official: false, 417 }, 418 RemoteName: "private/moonbase", 419 LocalName: "example.com:8000/private/moonbase", 420 CanonicalName: "example.com:8000/private/moonbase", 421 Official: false, 422 }, 423 "example.com:8000/privatebase": { 424 Index: ®istrytypes.IndexInfo{ 425 Name: "example.com:8000", 426 Official: false, 427 }, 428 RemoteName: "privatebase", 429 LocalName: "example.com:8000/privatebase", 430 CanonicalName: "example.com:8000/privatebase", 431 Official: false, 432 }, 433 "localhost/private/moonbase": { 434 Index: ®istrytypes.IndexInfo{ 435 Name: "localhost", 436 Official: false, 437 }, 438 RemoteName: "private/moonbase", 439 LocalName: "localhost/private/moonbase", 440 CanonicalName: "localhost/private/moonbase", 441 Official: false, 442 }, 443 "localhost/privatebase": { 444 Index: ®istrytypes.IndexInfo{ 445 Name: "localhost", 446 Official: false, 447 }, 448 RemoteName: "privatebase", 449 LocalName: "localhost/privatebase", 450 CanonicalName: "localhost/privatebase", 451 Official: false, 452 }, 453 IndexName + "/public/moonbase": { 454 Index: ®istrytypes.IndexInfo{ 455 Name: IndexName, 456 Official: true, 457 }, 458 RemoteName: "public/moonbase", 459 LocalName: "public/moonbase", 460 CanonicalName: "docker.io/public/moonbase", 461 Official: false, 462 }, 463 "index." + IndexName + "/public/moonbase": { 464 Index: ®istrytypes.IndexInfo{ 465 Name: IndexName, 466 Official: true, 467 }, 468 RemoteName: "public/moonbase", 469 LocalName: "public/moonbase", 470 CanonicalName: "docker.io/public/moonbase", 471 Official: false, 472 }, 473 "ubuntu-12.04-base": { 474 Index: ®istrytypes.IndexInfo{ 475 Name: IndexName, 476 Official: true, 477 }, 478 RemoteName: "library/ubuntu-12.04-base", 479 LocalName: "ubuntu-12.04-base", 480 CanonicalName: "docker.io/library/ubuntu-12.04-base", 481 Official: true, 482 }, 483 IndexName + "/ubuntu-12.04-base": { 484 Index: ®istrytypes.IndexInfo{ 485 Name: IndexName, 486 Official: true, 487 }, 488 RemoteName: "library/ubuntu-12.04-base", 489 LocalName: "ubuntu-12.04-base", 490 CanonicalName: "docker.io/library/ubuntu-12.04-base", 491 Official: true, 492 }, 493 "index." + IndexName + "/ubuntu-12.04-base": { 494 Index: ®istrytypes.IndexInfo{ 495 Name: IndexName, 496 Official: true, 497 }, 498 RemoteName: "library/ubuntu-12.04-base", 499 LocalName: "ubuntu-12.04-base", 500 CanonicalName: "docker.io/library/ubuntu-12.04-base", 501 Official: true, 502 }, 503 } 504 505 for reposName, expectedRepoInfo := range expectedRepoInfos { 506 named, err := reference.WithName(reposName) 507 if err != nil { 508 t.Error(err) 509 } 510 511 repoInfo, err := ParseRepositoryInfo(named) 512 if err != nil { 513 t.Error(err) 514 } else { 515 checkEqual(t, repoInfo.Index.Name, expectedRepoInfo.Index.Name, reposName) 516 checkEqual(t, repoInfo.RemoteName(), expectedRepoInfo.RemoteName, reposName) 517 checkEqual(t, repoInfo.Name(), expectedRepoInfo.LocalName, reposName) 518 checkEqual(t, repoInfo.FullName(), expectedRepoInfo.CanonicalName, reposName) 519 checkEqual(t, repoInfo.Index.Official, expectedRepoInfo.Index.Official, reposName) 520 checkEqual(t, repoInfo.Official, expectedRepoInfo.Official, reposName) 521 } 522 } 523 } 524 525 func TestNewIndexInfo(t *testing.T) { 526 testIndexInfo := func(config *serviceConfig, expectedIndexInfos map[string]*registrytypes.IndexInfo) { 527 for indexName, expectedIndexInfo := range expectedIndexInfos { 528 index, err := newIndexInfo(config, indexName) 529 if err != nil { 530 t.Fatal(err) 531 } else { 532 checkEqual(t, index.Name, expectedIndexInfo.Name, indexName+" name") 533 checkEqual(t, index.Official, expectedIndexInfo.Official, indexName+" is official") 534 checkEqual(t, index.Secure, expectedIndexInfo.Secure, indexName+" is secure") 535 checkEqual(t, len(index.Mirrors), len(expectedIndexInfo.Mirrors), indexName+" mirrors") 536 } 537 } 538 } 539 540 config := newServiceConfig(ServiceOptions{}) 541 noMirrors := []string{} 542 expectedIndexInfos := map[string]*registrytypes.IndexInfo{ 543 IndexName: { 544 Name: IndexName, 545 Official: true, 546 Secure: true, 547 Mirrors: noMirrors, 548 }, 549 "index." + IndexName: { 550 Name: IndexName, 551 Official: true, 552 Secure: true, 553 Mirrors: noMirrors, 554 }, 555 "example.com": { 556 Name: "example.com", 557 Official: false, 558 Secure: true, 559 Mirrors: noMirrors, 560 }, 561 "127.0.0.1:5000": { 562 Name: "127.0.0.1:5000", 563 Official: false, 564 Secure: false, 565 Mirrors: noMirrors, 566 }, 567 } 568 testIndexInfo(config, expectedIndexInfos) 569 570 publicMirrors := []string{"http://mirror1.local", "http://mirror2.local"} 571 config = makeServiceConfig(publicMirrors, []string{"example.com"}) 572 573 expectedIndexInfos = map[string]*registrytypes.IndexInfo{ 574 IndexName: { 575 Name: IndexName, 576 Official: true, 577 Secure: true, 578 Mirrors: publicMirrors, 579 }, 580 "index." + IndexName: { 581 Name: IndexName, 582 Official: true, 583 Secure: true, 584 Mirrors: publicMirrors, 585 }, 586 "example.com": { 587 Name: "example.com", 588 Official: false, 589 Secure: false, 590 Mirrors: noMirrors, 591 }, 592 "example.com:5000": { 593 Name: "example.com:5000", 594 Official: false, 595 Secure: true, 596 Mirrors: noMirrors, 597 }, 598 "127.0.0.1": { 599 Name: "127.0.0.1", 600 Official: false, 601 Secure: false, 602 Mirrors: noMirrors, 603 }, 604 "127.0.0.1:5000": { 605 Name: "127.0.0.1:5000", 606 Official: false, 607 Secure: false, 608 Mirrors: noMirrors, 609 }, 610 "other.com": { 611 Name: "other.com", 612 Official: false, 613 Secure: true, 614 Mirrors: noMirrors, 615 }, 616 } 617 testIndexInfo(config, expectedIndexInfos) 618 619 config = makeServiceConfig(nil, []string{"42.42.0.0/16"}) 620 expectedIndexInfos = map[string]*registrytypes.IndexInfo{ 621 "example.com": { 622 Name: "example.com", 623 Official: false, 624 Secure: false, 625 Mirrors: noMirrors, 626 }, 627 "example.com:5000": { 628 Name: "example.com:5000", 629 Official: false, 630 Secure: false, 631 Mirrors: noMirrors, 632 }, 633 "127.0.0.1": { 634 Name: "127.0.0.1", 635 Official: false, 636 Secure: false, 637 Mirrors: noMirrors, 638 }, 639 "127.0.0.1:5000": { 640 Name: "127.0.0.1:5000", 641 Official: false, 642 Secure: false, 643 Mirrors: noMirrors, 644 }, 645 "other.com": { 646 Name: "other.com", 647 Official: false, 648 Secure: true, 649 Mirrors: noMirrors, 650 }, 651 } 652 testIndexInfo(config, expectedIndexInfos) 653 } 654 655 func TestMirrorEndpointLookup(t *testing.T) { 656 containsMirror := func(endpoints []APIEndpoint) bool { 657 for _, pe := range endpoints { 658 if pe.URL.Host == "my.mirror" { 659 return true 660 } 661 } 662 return false 663 } 664 s := Service{config: makeServiceConfig([]string{"my.mirror"}, nil)} 665 666 imageName, err := reference.WithName(IndexName + "/test/image") 667 if err != nil { 668 t.Error(err) 669 } 670 pushAPIEndpoints, err := s.LookupPushEndpoints(imageName.Hostname()) 671 if err != nil { 672 t.Fatal(err) 673 } 674 if containsMirror(pushAPIEndpoints) { 675 t.Fatal("Push endpoint should not contain mirror") 676 } 677 678 pullAPIEndpoints, err := s.LookupPullEndpoints(imageName.Hostname()) 679 if err != nil { 680 t.Fatal(err) 681 } 682 if !containsMirror(pullAPIEndpoints) { 683 t.Fatal("Pull endpoint should contain mirror") 684 } 685 } 686 687 func TestPushRegistryTag(t *testing.T) { 688 r := spawnTestRegistrySession(t) 689 repoRef, err := reference.ParseNamed(REPO) 690 if err != nil { 691 t.Fatal(err) 692 } 693 err = r.PushRegistryTag(repoRef, imageID, "stable", makeURL("/v1/")) 694 if err != nil { 695 t.Fatal(err) 696 } 697 } 698 699 func TestPushImageJSONIndex(t *testing.T) { 700 r := spawnTestRegistrySession(t) 701 imgData := []*ImgData{ 702 { 703 ID: "77dbf71da1d00e3fbddc480176eac8994025630c6590d11cfc8fe1209c2a1d20", 704 Checksum: "sha256:1ac330d56e05eef6d438586545ceff7550d3bdcb6b19961f12c5ba714ee1bb37", 705 }, 706 { 707 ID: "42d718c941f5c532ac049bf0b0ab53f0062f09a03afd4aa4a02c098e46032b9d", 708 Checksum: "sha256:bea7bf2e4bacd479344b737328db47b18880d09096e6674165533aa994f5e9f2", 709 }, 710 } 711 repoRef, err := reference.ParseNamed(REPO) 712 if err != nil { 713 t.Fatal(err) 714 } 715 repoData, err := r.PushImageJSONIndex(repoRef, imgData, false, nil) 716 if err != nil { 717 t.Fatal(err) 718 } 719 if repoData == nil { 720 t.Fatal("Expected RepositoryData object") 721 } 722 repoData, err = r.PushImageJSONIndex(repoRef, imgData, true, []string{r.indexEndpoint.String()}) 723 if err != nil { 724 t.Fatal(err) 725 } 726 if repoData == nil { 727 t.Fatal("Expected RepositoryData object") 728 } 729 } 730 731 func TestSearchRepositories(t *testing.T) { 732 r := spawnTestRegistrySession(t) 733 results, err := r.SearchRepositories("fakequery") 734 if err != nil { 735 t.Fatal(err) 736 } 737 if results == nil { 738 t.Fatal("Expected non-nil SearchResults object") 739 } 740 assertEqual(t, results.NumResults, 1, "Expected 1 search results") 741 assertEqual(t, results.Query, "fakequery", "Expected 'fakequery' as query") 742 assertEqual(t, results.Results[0].StarCount, 42, "Expected 'fakeimage' to have 42 stars") 743 } 744 745 func TestTrustedLocation(t *testing.T) { 746 for _, url := range []string{"http://example.com", "https://example.com:7777", "http://docker.io", "http://test.docker.com", "https://fakedocker.com"} { 747 req, _ := http.NewRequest("GET", url, nil) 748 if trustedLocation(req) == true { 749 t.Fatalf("'%s' shouldn't be detected as a trusted location", url) 750 } 751 } 752 753 for _, url := range []string{"https://docker.io", "https://test.docker.com:80"} { 754 req, _ := http.NewRequest("GET", url, nil) 755 if trustedLocation(req) == false { 756 t.Fatalf("'%s' should be detected as a trusted location", url) 757 } 758 } 759 } 760 761 func TestAddRequiredHeadersToRedirectedRequests(t *testing.T) { 762 for _, urls := range [][]string{ 763 {"http://docker.io", "https://docker.com"}, 764 {"https://foo.docker.io:7777", "http://bar.docker.com"}, 765 {"https://foo.docker.io", "https://example.com"}, 766 } { 767 reqFrom, _ := http.NewRequest("GET", urls[0], nil) 768 reqFrom.Header.Add("Content-Type", "application/json") 769 reqFrom.Header.Add("Authorization", "super_secret") 770 reqTo, _ := http.NewRequest("GET", urls[1], nil) 771 772 addRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom}) 773 774 if len(reqTo.Header) != 1 { 775 t.Fatalf("Expected 1 headers, got %d", len(reqTo.Header)) 776 } 777 778 if reqTo.Header.Get("Content-Type") != "application/json" { 779 t.Fatal("'Content-Type' should be 'application/json'") 780 } 781 782 if reqTo.Header.Get("Authorization") != "" { 783 t.Fatal("'Authorization' should be empty") 784 } 785 } 786 787 for _, urls := range [][]string{ 788 {"https://docker.io", "https://docker.com"}, 789 {"https://foo.docker.io:7777", "https://bar.docker.com"}, 790 } { 791 reqFrom, _ := http.NewRequest("GET", urls[0], nil) 792 reqFrom.Header.Add("Content-Type", "application/json") 793 reqFrom.Header.Add("Authorization", "super_secret") 794 reqTo, _ := http.NewRequest("GET", urls[1], nil) 795 796 addRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom}) 797 798 if len(reqTo.Header) != 2 { 799 t.Fatalf("Expected 2 headers, got %d", len(reqTo.Header)) 800 } 801 802 if reqTo.Header.Get("Content-Type") != "application/json" { 803 t.Fatal("'Content-Type' should be 'application/json'") 804 } 805 806 if reqTo.Header.Get("Authorization") != "super_secret" { 807 t.Fatal("'Authorization' should be 'super_secret'") 808 } 809 } 810 } 811 812 func TestIsSecureIndex(t *testing.T) { 813 tests := []struct { 814 addr string 815 insecureRegistries []string 816 expected bool 817 }{ 818 {IndexName, nil, true}, 819 {"example.com", []string{}, true}, 820 {"example.com", []string{"example.com"}, false}, 821 {"localhost", []string{"localhost:5000"}, false}, 822 {"localhost:5000", []string{"localhost:5000"}, false}, 823 {"localhost", []string{"example.com"}, false}, 824 {"127.0.0.1:5000", []string{"127.0.0.1:5000"}, false}, 825 {"localhost", nil, false}, 826 {"localhost:5000", nil, false}, 827 {"127.0.0.1", nil, false}, 828 {"localhost", []string{"example.com"}, false}, 829 {"127.0.0.1", []string{"example.com"}, false}, 830 {"example.com", nil, true}, 831 {"example.com", []string{"example.com"}, false}, 832 {"127.0.0.1", []string{"example.com"}, false}, 833 {"127.0.0.1:5000", []string{"example.com"}, false}, 834 {"example.com:5000", []string{"42.42.0.0/16"}, false}, 835 {"example.com", []string{"42.42.0.0/16"}, false}, 836 {"example.com:5000", []string{"42.42.42.42/8"}, false}, 837 {"127.0.0.1:5000", []string{"127.0.0.0/8"}, false}, 838 {"42.42.42.42:5000", []string{"42.1.1.1/8"}, false}, 839 {"invalid.domain.com", []string{"42.42.0.0/16"}, true}, 840 {"invalid.domain.com", []string{"invalid.domain.com"}, false}, 841 {"invalid.domain.com:5000", []string{"invalid.domain.com"}, true}, 842 {"invalid.domain.com:5000", []string{"invalid.domain.com:5000"}, false}, 843 } 844 for _, tt := range tests { 845 config := makeServiceConfig(nil, tt.insecureRegistries) 846 if sec := isSecureIndex(config, tt.addr); sec != tt.expected { 847 t.Errorf("isSecureIndex failed for %q %v, expected %v got %v", tt.addr, tt.insecureRegistries, tt.expected, sec) 848 } 849 } 850 } 851 852 type debugTransport struct { 853 http.RoundTripper 854 log func(...interface{}) 855 } 856 857 func (tr debugTransport) RoundTrip(req *http.Request) (*http.Response, error) { 858 dump, err := httputil.DumpRequestOut(req, false) 859 if err != nil { 860 tr.log("could not dump request") 861 } 862 tr.log(string(dump)) 863 resp, err := tr.RoundTripper.RoundTrip(req) 864 if err != nil { 865 return nil, err 866 } 867 dump, err = httputil.DumpResponse(resp, false) 868 if err != nil { 869 tr.log("could not dump response") 870 } 871 tr.log(string(dump)) 872 return resp, err 873 }