github.com/0xfoo/docker@v1.8.2/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, 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 TestPushRegistryTag(t *testing.T) { 681 r := spawnTestRegistrySession(t) 682 err := r.PushRegistryTag("foo42/bar", imageID, "stable", makeURL("/v1/")) 683 if err != nil { 684 t.Fatal(err) 685 } 686 } 687 688 func TestPushImageJSONIndex(t *testing.T) { 689 r := spawnTestRegistrySession(t) 690 imgData := []*ImgData{ 691 { 692 ID: "77dbf71da1d00e3fbddc480176eac8994025630c6590d11cfc8fe1209c2a1d20", 693 Checksum: "sha256:1ac330d56e05eef6d438586545ceff7550d3bdcb6b19961f12c5ba714ee1bb37", 694 }, 695 { 696 ID: "42d718c941f5c532ac049bf0b0ab53f0062f09a03afd4aa4a02c098e46032b9d", 697 Checksum: "sha256:bea7bf2e4bacd479344b737328db47b18880d09096e6674165533aa994f5e9f2", 698 }, 699 } 700 repoData, err := r.PushImageJSONIndex("foo42/bar", imgData, false, nil) 701 if err != nil { 702 t.Fatal(err) 703 } 704 if repoData == nil { 705 t.Fatal("Expected RepositoryData object") 706 } 707 repoData, err = r.PushImageJSONIndex("foo42/bar", imgData, true, []string{r.indexEndpoint.String()}) 708 if err != nil { 709 t.Fatal(err) 710 } 711 if repoData == nil { 712 t.Fatal("Expected RepositoryData object") 713 } 714 } 715 716 func TestSearchRepositories(t *testing.T) { 717 r := spawnTestRegistrySession(t) 718 results, err := r.SearchRepositories("fakequery") 719 if err != nil { 720 t.Fatal(err) 721 } 722 if results == nil { 723 t.Fatal("Expected non-nil SearchResults object") 724 } 725 assertEqual(t, results.NumResults, 1, "Expected 1 search results") 726 assertEqual(t, results.Query, "fakequery", "Expected 'fakequery' as query") 727 assertEqual(t, results.Results[0].StarCount, 42, "Expected 'fakeimage' to have 42 stars") 728 } 729 730 func TestValidRemoteName(t *testing.T) { 731 validRepositoryNames := []string{ 732 // Sanity check. 733 "docker/docker", 734 735 // Allow 64-character non-hexadecimal names (hexadecimal names are forbidden). 736 "thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev", 737 738 // Allow embedded hyphens. 739 "docker-rules/docker", 740 741 //Username doc and image name docker being tested. 742 "doc/docker", 743 744 // single character names are now allowed. 745 "d/docker", 746 "jess/t", 747 } 748 for _, repositoryName := range validRepositoryNames { 749 if err := validateRemoteName(repositoryName); err != nil { 750 t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err) 751 } 752 } 753 754 invalidRepositoryNames := []string{ 755 // Disallow capital letters. 756 "docker/Docker", 757 758 // Only allow one slash. 759 "docker///docker", 760 761 // Disallow 64-character hexadecimal. 762 "1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a", 763 764 // Disallow leading and trailing hyphens in namespace. 765 "-docker/docker", 766 "docker-/docker", 767 "-docker-/docker", 768 769 // Don't allow underscores everywhere (as opposed to hyphens). 770 "____/____", 771 772 "_docker/_docker", 773 774 // Disallow consecutive hyphens. 775 "dock--er/docker", 776 777 // No repository. 778 "docker/", 779 780 //namespace too long 781 "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", 782 } 783 for _, repositoryName := range invalidRepositoryNames { 784 if err := validateRemoteName(repositoryName); err == nil { 785 t.Errorf("Repository name should be invalid: %v", repositoryName) 786 } 787 } 788 } 789 790 func TestTrustedLocation(t *testing.T) { 791 for _, url := range []string{"http://example.com", "https://example.com:7777", "http://docker.io", "http://test.docker.com", "https://fakedocker.com"} { 792 req, _ := http.NewRequest("GET", url, nil) 793 if trustedLocation(req) == true { 794 t.Fatalf("'%s' shouldn't be detected as a trusted location", url) 795 } 796 } 797 798 for _, url := range []string{"https://docker.io", "https://test.docker.com:80"} { 799 req, _ := http.NewRequest("GET", url, nil) 800 if trustedLocation(req) == false { 801 t.Fatalf("'%s' should be detected as a trusted location", url) 802 } 803 } 804 } 805 806 func TestAddRequiredHeadersToRedirectedRequests(t *testing.T) { 807 for _, urls := range [][]string{ 808 {"http://docker.io", "https://docker.com"}, 809 {"https://foo.docker.io:7777", "http://bar.docker.com"}, 810 {"https://foo.docker.io", "https://example.com"}, 811 } { 812 reqFrom, _ := http.NewRequest("GET", urls[0], nil) 813 reqFrom.Header.Add("Content-Type", "application/json") 814 reqFrom.Header.Add("Authorization", "super_secret") 815 reqTo, _ := http.NewRequest("GET", urls[1], nil) 816 817 addRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom}) 818 819 if len(reqTo.Header) != 1 { 820 t.Fatalf("Expected 1 headers, got %d", len(reqTo.Header)) 821 } 822 823 if reqTo.Header.Get("Content-Type") != "application/json" { 824 t.Fatal("'Content-Type' should be 'application/json'") 825 } 826 827 if reqTo.Header.Get("Authorization") != "" { 828 t.Fatal("'Authorization' should be empty") 829 } 830 } 831 832 for _, urls := range [][]string{ 833 {"https://docker.io", "https://docker.com"}, 834 {"https://foo.docker.io:7777", "https://bar.docker.com"}, 835 } { 836 reqFrom, _ := http.NewRequest("GET", urls[0], nil) 837 reqFrom.Header.Add("Content-Type", "application/json") 838 reqFrom.Header.Add("Authorization", "super_secret") 839 reqTo, _ := http.NewRequest("GET", urls[1], nil) 840 841 addRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom}) 842 843 if len(reqTo.Header) != 2 { 844 t.Fatalf("Expected 2 headers, got %d", len(reqTo.Header)) 845 } 846 847 if reqTo.Header.Get("Content-Type") != "application/json" { 848 t.Fatal("'Content-Type' should be 'application/json'") 849 } 850 851 if reqTo.Header.Get("Authorization") != "super_secret" { 852 t.Fatal("'Authorization' should be 'super_secret'") 853 } 854 } 855 } 856 857 func TestIsSecureIndex(t *testing.T) { 858 tests := []struct { 859 addr string 860 insecureRegistries []string 861 expected bool 862 }{ 863 {IndexName, nil, true}, 864 {"example.com", []string{}, true}, 865 {"example.com", []string{"example.com"}, false}, 866 {"localhost", []string{"localhost:5000"}, false}, 867 {"localhost:5000", []string{"localhost:5000"}, false}, 868 {"localhost", []string{"example.com"}, false}, 869 {"127.0.0.1:5000", []string{"127.0.0.1:5000"}, false}, 870 {"localhost", nil, false}, 871 {"localhost:5000", nil, false}, 872 {"127.0.0.1", nil, false}, 873 {"localhost", []string{"example.com"}, false}, 874 {"127.0.0.1", []string{"example.com"}, false}, 875 {"example.com", nil, true}, 876 {"example.com", []string{"example.com"}, false}, 877 {"127.0.0.1", []string{"example.com"}, false}, 878 {"127.0.0.1:5000", []string{"example.com"}, false}, 879 {"example.com:5000", []string{"42.42.0.0/16"}, false}, 880 {"example.com", []string{"42.42.0.0/16"}, false}, 881 {"example.com:5000", []string{"42.42.42.42/8"}, false}, 882 {"127.0.0.1:5000", []string{"127.0.0.0/8"}, false}, 883 {"42.42.42.42:5000", []string{"42.1.1.1/8"}, false}, 884 {"invalid.domain.com", []string{"42.42.0.0/16"}, true}, 885 {"invalid.domain.com", []string{"invalid.domain.com"}, false}, 886 {"invalid.domain.com:5000", []string{"invalid.domain.com"}, true}, 887 {"invalid.domain.com:5000", []string{"invalid.domain.com:5000"}, false}, 888 } 889 for _, tt := range tests { 890 config := makeServiceConfig(nil, tt.insecureRegistries) 891 if sec := config.isSecureIndex(tt.addr); sec != tt.expected { 892 t.Errorf("isSecureIndex failed for %q %v, expected %v got %v", tt.addr, tt.insecureRegistries, tt.expected, sec) 893 } 894 } 895 } 896 897 type debugTransport struct { 898 http.RoundTripper 899 log func(...interface{}) 900 } 901 902 func (tr debugTransport) RoundTrip(req *http.Request) (*http.Response, error) { 903 dump, err := httputil.DumpRequestOut(req, false) 904 if err != nil { 905 tr.log("could not dump request") 906 } 907 tr.log(string(dump)) 908 resp, err := tr.RoundTripper.RoundTrip(req) 909 if err != nil { 910 return nil, err 911 } 912 dump, err = httputil.DumpResponse(resp, false) 913 if err != nil { 914 tr.log("could not dump response") 915 } 916 tr.log(string(dump)) 917 return resp, err 918 }