github.com/kelda-inc/moby@v1.13.1/registry/registry_test.go (about)

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