github.com/mheon/docker@v0.11.2-0.20150922122814-44f47903a831/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) 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) 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) 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) 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) 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) 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 //Username doc and image name docker being tested. 771 "doc/docker", 772 773 // single character names are now allowed. 774 "d/docker", 775 "jess/t", 776 } 777 for _, repositoryName := range validRepositoryNames { 778 if err := validateRemoteName(repositoryName); err != nil { 779 t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err) 780 } 781 } 782 783 invalidRepositoryNames := []string{ 784 // Disallow capital letters. 785 "docker/Docker", 786 787 // Only allow one slash. 788 "docker///docker", 789 790 // Disallow 64-character hexadecimal. 791 "1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a", 792 793 // Disallow leading and trailing hyphens in namespace. 794 "-docker/docker", 795 "docker-/docker", 796 "-docker-/docker", 797 798 // Don't allow underscores everywhere (as opposed to hyphens). 799 "____/____", 800 801 "_docker/_docker", 802 803 // Disallow consecutive hyphens. 804 "dock--er/docker", 805 806 // No repository. 807 "docker/", 808 809 //namespace too long 810 "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", 811 } 812 for _, repositoryName := range invalidRepositoryNames { 813 if err := validateRemoteName(repositoryName); err == nil { 814 t.Errorf("Repository name should be invalid: %v", repositoryName) 815 } 816 } 817 } 818 819 func TestTrustedLocation(t *testing.T) { 820 for _, url := range []string{"http://example.com", "https://example.com:7777", "http://docker.io", "http://test.docker.com", "https://fakedocker.com"} { 821 req, _ := http.NewRequest("GET", url, nil) 822 if trustedLocation(req) == true { 823 t.Fatalf("'%s' shouldn't be detected as a trusted location", url) 824 } 825 } 826 827 for _, url := range []string{"https://docker.io", "https://test.docker.com:80"} { 828 req, _ := http.NewRequest("GET", url, nil) 829 if trustedLocation(req) == false { 830 t.Fatalf("'%s' should be detected as a trusted location", url) 831 } 832 } 833 } 834 835 func TestAddRequiredHeadersToRedirectedRequests(t *testing.T) { 836 for _, urls := range [][]string{ 837 {"http://docker.io", "https://docker.com"}, 838 {"https://foo.docker.io:7777", "http://bar.docker.com"}, 839 {"https://foo.docker.io", "https://example.com"}, 840 } { 841 reqFrom, _ := http.NewRequest("GET", urls[0], nil) 842 reqFrom.Header.Add("Content-Type", "application/json") 843 reqFrom.Header.Add("Authorization", "super_secret") 844 reqTo, _ := http.NewRequest("GET", urls[1], nil) 845 846 addRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom}) 847 848 if len(reqTo.Header) != 1 { 849 t.Fatalf("Expected 1 headers, got %d", len(reqTo.Header)) 850 } 851 852 if reqTo.Header.Get("Content-Type") != "application/json" { 853 t.Fatal("'Content-Type' should be 'application/json'") 854 } 855 856 if reqTo.Header.Get("Authorization") != "" { 857 t.Fatal("'Authorization' should be empty") 858 } 859 } 860 861 for _, urls := range [][]string{ 862 {"https://docker.io", "https://docker.com"}, 863 {"https://foo.docker.io:7777", "https://bar.docker.com"}, 864 } { 865 reqFrom, _ := http.NewRequest("GET", urls[0], nil) 866 reqFrom.Header.Add("Content-Type", "application/json") 867 reqFrom.Header.Add("Authorization", "super_secret") 868 reqTo, _ := http.NewRequest("GET", urls[1], nil) 869 870 addRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom}) 871 872 if len(reqTo.Header) != 2 { 873 t.Fatalf("Expected 2 headers, got %d", len(reqTo.Header)) 874 } 875 876 if reqTo.Header.Get("Content-Type") != "application/json" { 877 t.Fatal("'Content-Type' should be 'application/json'") 878 } 879 880 if reqTo.Header.Get("Authorization") != "super_secret" { 881 t.Fatal("'Authorization' should be 'super_secret'") 882 } 883 } 884 } 885 886 func TestIsSecureIndex(t *testing.T) { 887 tests := []struct { 888 addr string 889 insecureRegistries []string 890 expected bool 891 }{ 892 {IndexName, nil, true}, 893 {"example.com", []string{}, true}, 894 {"example.com", []string{"example.com"}, false}, 895 {"localhost", []string{"localhost:5000"}, false}, 896 {"localhost:5000", []string{"localhost:5000"}, false}, 897 {"localhost", []string{"example.com"}, false}, 898 {"127.0.0.1:5000", []string{"127.0.0.1:5000"}, false}, 899 {"localhost", nil, false}, 900 {"localhost:5000", nil, false}, 901 {"127.0.0.1", nil, false}, 902 {"localhost", []string{"example.com"}, false}, 903 {"127.0.0.1", []string{"example.com"}, false}, 904 {"example.com", nil, true}, 905 {"example.com", []string{"example.com"}, false}, 906 {"127.0.0.1", []string{"example.com"}, false}, 907 {"127.0.0.1:5000", []string{"example.com"}, false}, 908 {"example.com:5000", []string{"42.42.0.0/16"}, false}, 909 {"example.com", []string{"42.42.0.0/16"}, false}, 910 {"example.com:5000", []string{"42.42.42.42/8"}, false}, 911 {"127.0.0.1:5000", []string{"127.0.0.0/8"}, false}, 912 {"42.42.42.42:5000", []string{"42.1.1.1/8"}, false}, 913 {"invalid.domain.com", []string{"42.42.0.0/16"}, true}, 914 {"invalid.domain.com", []string{"invalid.domain.com"}, false}, 915 {"invalid.domain.com:5000", []string{"invalid.domain.com"}, true}, 916 {"invalid.domain.com:5000", []string{"invalid.domain.com:5000"}, false}, 917 } 918 for _, tt := range tests { 919 config := makeServiceConfig(nil, tt.insecureRegistries) 920 if sec := config.isSecureIndex(tt.addr); sec != tt.expected { 921 t.Errorf("isSecureIndex failed for %q %v, expected %v got %v", tt.addr, tt.insecureRegistries, tt.expected, sec) 922 } 923 } 924 } 925 926 type debugTransport struct { 927 http.RoundTripper 928 log func(...interface{}) 929 } 930 931 func (tr debugTransport) RoundTrip(req *http.Request) (*http.Response, error) { 932 dump, err := httputil.DumpRequestOut(req, false) 933 if err != nil { 934 tr.log("could not dump request") 935 } 936 tr.log(string(dump)) 937 resp, err := tr.RoundTripper.RoundTrip(req) 938 if err != nil { 939 return nil, err 940 } 941 dump, err = httputil.DumpResponse(resp, false) 942 if err != nil { 943 tr.log("could not dump response") 944 } 945 tr.log(string(dump)) 946 return resp, err 947 }