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