github.com/squaremo/docker@v1.3.2-0.20150516120342-42cfc9554972/registry/registry_test.go (about)

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