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