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