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