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  }