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