github.com/sijibomii/docker@v0.0.0-20231230191044-5cf6ca554647/registry/registry_test.go (about)

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