github.com/rumpl/bof@v23.0.0-rc.2+incompatible/registry/registry_test.go (about)

     1  package registry // import "github.com/docker/docker/registry"
     2  
     3  import (
     4  	"net/http"
     5  	"net/http/httputil"
     6  	"os"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/docker/distribution/reference"
    11  	"github.com/docker/distribution/registry/client/transport"
    12  	"github.com/docker/docker/api/types"
    13  	"github.com/docker/docker/api/types/registry"
    14  	"gotest.tools/v3/assert"
    15  	is "gotest.tools/v3/assert/cmp"
    16  	"gotest.tools/v3/skip"
    17  )
    18  
    19  func spawnTestRegistrySession(t *testing.T) *session {
    20  	authConfig := &types.AuthConfig{}
    21  	endpoint, err := newV1Endpoint(makeIndex("/v1/"), "", nil)
    22  	if err != nil {
    23  		t.Fatal(err)
    24  	}
    25  	userAgent := "docker test client"
    26  	var tr http.RoundTripper = debugTransport{newTransport(nil), t.Log}
    27  	tr = transport.NewTransport(newAuthTransport(tr, authConfig, false), Headers(userAgent, nil)...)
    28  	client := httpClient(tr)
    29  
    30  	if err := authorizeClient(client, authConfig, endpoint); err != nil {
    31  		t.Fatal(err)
    32  	}
    33  	r := newSession(client, endpoint)
    34  
    35  	// In a normal scenario for the v1 registry, the client should send a `X-Docker-Token: true`
    36  	// header while authenticating, in order to retrieve a token that can be later used to
    37  	// perform authenticated actions.
    38  	//
    39  	// The mock v1 registry does not support that, (TODO(tiborvass): support it), instead,
    40  	// it will consider authenticated any request with the header `X-Docker-Token: fake-token`.
    41  	//
    42  	// Because we know that the client's transport is an `*authTransport` we simply cast it,
    43  	// in order to set the internal cached token to the fake token, and thus send that fake token
    44  	// upon every subsequent requests.
    45  	r.client.Transport.(*authTransport).token = []string{"fake-token"}
    46  	return r
    47  }
    48  
    49  func TestPingRegistryEndpoint(t *testing.T) {
    50  	skip.If(t, os.Getuid() != 0, "skipping test that requires root")
    51  	testPing := func(index *registry.IndexInfo, expectedStandalone bool, assertMessage string) {
    52  		ep, err := newV1Endpoint(index, "", nil)
    53  		if err != nil {
    54  			t.Fatal(err)
    55  		}
    56  		regInfo, err := ep.ping()
    57  		if err != nil {
    58  			t.Fatal(err)
    59  		}
    60  
    61  		assert.Equal(t, regInfo.Standalone, expectedStandalone, assertMessage)
    62  	}
    63  
    64  	testPing(makeIndex("/v1/"), true, "Expected standalone to be true (default)")
    65  	testPing(makeHTTPSIndex("/v1/"), true, "Expected standalone to be true (default)")
    66  	testPing(makePublicIndex(), false, "Expected standalone to be false for public index")
    67  }
    68  
    69  func TestEndpoint(t *testing.T) {
    70  	skip.If(t, os.Getuid() != 0, "skipping test that requires root")
    71  	// Simple wrapper to fail test if err != nil
    72  	expandEndpoint := func(index *registry.IndexInfo) *v1Endpoint {
    73  		endpoint, err := newV1Endpoint(index, "", nil)
    74  		if err != nil {
    75  			t.Fatal(err)
    76  		}
    77  		return endpoint
    78  	}
    79  
    80  	assertInsecureIndex := func(index *registry.IndexInfo) {
    81  		index.Secure = true
    82  		_, err := newV1Endpoint(index, "", nil)
    83  		assert.ErrorContains(t, err, "insecure-registry", index.Name+": Expected insecure-registry  error for insecure index")
    84  		index.Secure = false
    85  	}
    86  
    87  	assertSecureIndex := func(index *registry.IndexInfo) {
    88  		index.Secure = true
    89  		_, err := newV1Endpoint(index, "", nil)
    90  		assert.ErrorContains(t, err, "certificate signed by unknown authority", index.Name+": Expected cert error for secure index")
    91  		index.Secure = false
    92  	}
    93  
    94  	index := &registry.IndexInfo{}
    95  	index.Name = makeURL("/v1/")
    96  	endpoint := expandEndpoint(index)
    97  	assert.Equal(t, endpoint.String(), index.Name, "Expected endpoint to be "+index.Name)
    98  	assertInsecureIndex(index)
    99  
   100  	index.Name = makeURL("")
   101  	endpoint = expandEndpoint(index)
   102  	assert.Equal(t, endpoint.String(), index.Name+"/v1/", index.Name+": Expected endpoint to be "+index.Name+"/v1/")
   103  	assertInsecureIndex(index)
   104  
   105  	httpURL := makeURL("")
   106  	index.Name = strings.SplitN(httpURL, "://", 2)[1]
   107  	endpoint = expandEndpoint(index)
   108  	assert.Equal(t, endpoint.String(), httpURL+"/v1/", index.Name+": Expected endpoint to be "+httpURL+"/v1/")
   109  	assertInsecureIndex(index)
   110  
   111  	index.Name = makeHTTPSURL("/v1/")
   112  	endpoint = expandEndpoint(index)
   113  	assert.Equal(t, endpoint.String(), index.Name, "Expected endpoint to be "+index.Name)
   114  	assertSecureIndex(index)
   115  
   116  	index.Name = makeHTTPSURL("")
   117  	endpoint = expandEndpoint(index)
   118  	assert.Equal(t, endpoint.String(), index.Name+"/v1/", index.Name+": Expected endpoint to be "+index.Name+"/v1/")
   119  	assertSecureIndex(index)
   120  
   121  	httpsURL := makeHTTPSURL("")
   122  	index.Name = strings.SplitN(httpsURL, "://", 2)[1]
   123  	endpoint = expandEndpoint(index)
   124  	assert.Equal(t, endpoint.String(), httpsURL+"/v1/", index.Name+": Expected endpoint to be "+httpsURL+"/v1/")
   125  	assertSecureIndex(index)
   126  
   127  	badEndpoints := []string{
   128  		"http://127.0.0.1/v1/",
   129  		"https://127.0.0.1/v1/",
   130  		"http://127.0.0.1",
   131  		"https://127.0.0.1",
   132  		"127.0.0.1",
   133  	}
   134  	for _, address := range badEndpoints {
   135  		index.Name = address
   136  		_, err := newV1Endpoint(index, "", nil)
   137  		assert.Check(t, err != nil, "Expected error while expanding bad endpoint: %s", address)
   138  	}
   139  }
   140  
   141  func TestParseRepositoryInfo(t *testing.T) {
   142  	type staticRepositoryInfo struct {
   143  		Index         *registry.IndexInfo
   144  		RemoteName    string
   145  		CanonicalName string
   146  		LocalName     string
   147  		Official      bool
   148  	}
   149  
   150  	expectedRepoInfos := map[string]staticRepositoryInfo{
   151  		"fooo/bar": {
   152  			Index: &registry.IndexInfo{
   153  				Name:     IndexName,
   154  				Official: true,
   155  			},
   156  			RemoteName:    "fooo/bar",
   157  			LocalName:     "fooo/bar",
   158  			CanonicalName: "docker.io/fooo/bar",
   159  			Official:      false,
   160  		},
   161  		"library/ubuntu": {
   162  			Index: &registry.IndexInfo{
   163  				Name:     IndexName,
   164  				Official: true,
   165  			},
   166  			RemoteName:    "library/ubuntu",
   167  			LocalName:     "ubuntu",
   168  			CanonicalName: "docker.io/library/ubuntu",
   169  			Official:      true,
   170  		},
   171  		"nonlibrary/ubuntu": {
   172  			Index: &registry.IndexInfo{
   173  				Name:     IndexName,
   174  				Official: true,
   175  			},
   176  			RemoteName:    "nonlibrary/ubuntu",
   177  			LocalName:     "nonlibrary/ubuntu",
   178  			CanonicalName: "docker.io/nonlibrary/ubuntu",
   179  			Official:      false,
   180  		},
   181  		"ubuntu": {
   182  			Index: &registry.IndexInfo{
   183  				Name:     IndexName,
   184  				Official: true,
   185  			},
   186  			RemoteName:    "library/ubuntu",
   187  			LocalName:     "ubuntu",
   188  			CanonicalName: "docker.io/library/ubuntu",
   189  			Official:      true,
   190  		},
   191  		"other/library": {
   192  			Index: &registry.IndexInfo{
   193  				Name:     IndexName,
   194  				Official: true,
   195  			},
   196  			RemoteName:    "other/library",
   197  			LocalName:     "other/library",
   198  			CanonicalName: "docker.io/other/library",
   199  			Official:      false,
   200  		},
   201  		"127.0.0.1:8000/private/moonbase": {
   202  			Index: &registry.IndexInfo{
   203  				Name:     "127.0.0.1:8000",
   204  				Official: false,
   205  			},
   206  			RemoteName:    "private/moonbase",
   207  			LocalName:     "127.0.0.1:8000/private/moonbase",
   208  			CanonicalName: "127.0.0.1:8000/private/moonbase",
   209  			Official:      false,
   210  		},
   211  		"127.0.0.1:8000/privatebase": {
   212  			Index: &registry.IndexInfo{
   213  				Name:     "127.0.0.1:8000",
   214  				Official: false,
   215  			},
   216  			RemoteName:    "privatebase",
   217  			LocalName:     "127.0.0.1:8000/privatebase",
   218  			CanonicalName: "127.0.0.1:8000/privatebase",
   219  			Official:      false,
   220  		},
   221  		"localhost:8000/private/moonbase": {
   222  			Index: &registry.IndexInfo{
   223  				Name:     "localhost:8000",
   224  				Official: false,
   225  			},
   226  			RemoteName:    "private/moonbase",
   227  			LocalName:     "localhost:8000/private/moonbase",
   228  			CanonicalName: "localhost:8000/private/moonbase",
   229  			Official:      false,
   230  		},
   231  		"localhost:8000/privatebase": {
   232  			Index: &registry.IndexInfo{
   233  				Name:     "localhost:8000",
   234  				Official: false,
   235  			},
   236  			RemoteName:    "privatebase",
   237  			LocalName:     "localhost:8000/privatebase",
   238  			CanonicalName: "localhost:8000/privatebase",
   239  			Official:      false,
   240  		},
   241  		"example.com/private/moonbase": {
   242  			Index: &registry.IndexInfo{
   243  				Name:     "example.com",
   244  				Official: false,
   245  			},
   246  			RemoteName:    "private/moonbase",
   247  			LocalName:     "example.com/private/moonbase",
   248  			CanonicalName: "example.com/private/moonbase",
   249  			Official:      false,
   250  		},
   251  		"example.com/privatebase": {
   252  			Index: &registry.IndexInfo{
   253  				Name:     "example.com",
   254  				Official: false,
   255  			},
   256  			RemoteName:    "privatebase",
   257  			LocalName:     "example.com/privatebase",
   258  			CanonicalName: "example.com/privatebase",
   259  			Official:      false,
   260  		},
   261  		"example.com:8000/private/moonbase": {
   262  			Index: &registry.IndexInfo{
   263  				Name:     "example.com:8000",
   264  				Official: false,
   265  			},
   266  			RemoteName:    "private/moonbase",
   267  			LocalName:     "example.com:8000/private/moonbase",
   268  			CanonicalName: "example.com:8000/private/moonbase",
   269  			Official:      false,
   270  		},
   271  		"example.com:8000/privatebase": {
   272  			Index: &registry.IndexInfo{
   273  				Name:     "example.com:8000",
   274  				Official: false,
   275  			},
   276  			RemoteName:    "privatebase",
   277  			LocalName:     "example.com:8000/privatebase",
   278  			CanonicalName: "example.com:8000/privatebase",
   279  			Official:      false,
   280  		},
   281  		"localhost/private/moonbase": {
   282  			Index: &registry.IndexInfo{
   283  				Name:     "localhost",
   284  				Official: false,
   285  			},
   286  			RemoteName:    "private/moonbase",
   287  			LocalName:     "localhost/private/moonbase",
   288  			CanonicalName: "localhost/private/moonbase",
   289  			Official:      false,
   290  		},
   291  		"localhost/privatebase": {
   292  			Index: &registry.IndexInfo{
   293  				Name:     "localhost",
   294  				Official: false,
   295  			},
   296  			RemoteName:    "privatebase",
   297  			LocalName:     "localhost/privatebase",
   298  			CanonicalName: "localhost/privatebase",
   299  			Official:      false,
   300  		},
   301  		IndexName + "/public/moonbase": {
   302  			Index: &registry.IndexInfo{
   303  				Name:     IndexName,
   304  				Official: true,
   305  			},
   306  			RemoteName:    "public/moonbase",
   307  			LocalName:     "public/moonbase",
   308  			CanonicalName: "docker.io/public/moonbase",
   309  			Official:      false,
   310  		},
   311  		"index." + IndexName + "/public/moonbase": {
   312  			Index: &registry.IndexInfo{
   313  				Name:     IndexName,
   314  				Official: true,
   315  			},
   316  			RemoteName:    "public/moonbase",
   317  			LocalName:     "public/moonbase",
   318  			CanonicalName: "docker.io/public/moonbase",
   319  			Official:      false,
   320  		},
   321  		"ubuntu-12.04-base": {
   322  			Index: &registry.IndexInfo{
   323  				Name:     IndexName,
   324  				Official: true,
   325  			},
   326  			RemoteName:    "library/ubuntu-12.04-base",
   327  			LocalName:     "ubuntu-12.04-base",
   328  			CanonicalName: "docker.io/library/ubuntu-12.04-base",
   329  			Official:      true,
   330  		},
   331  		IndexName + "/ubuntu-12.04-base": {
   332  			Index: &registry.IndexInfo{
   333  				Name:     IndexName,
   334  				Official: true,
   335  			},
   336  			RemoteName:    "library/ubuntu-12.04-base",
   337  			LocalName:     "ubuntu-12.04-base",
   338  			CanonicalName: "docker.io/library/ubuntu-12.04-base",
   339  			Official:      true,
   340  		},
   341  		"index." + IndexName + "/ubuntu-12.04-base": {
   342  			Index: &registry.IndexInfo{
   343  				Name:     IndexName,
   344  				Official: true,
   345  			},
   346  			RemoteName:    "library/ubuntu-12.04-base",
   347  			LocalName:     "ubuntu-12.04-base",
   348  			CanonicalName: "docker.io/library/ubuntu-12.04-base",
   349  			Official:      true,
   350  		},
   351  	}
   352  
   353  	for reposName, expectedRepoInfo := range expectedRepoInfos {
   354  		named, err := reference.ParseNormalizedNamed(reposName)
   355  		if err != nil {
   356  			t.Error(err)
   357  		}
   358  
   359  		repoInfo, err := ParseRepositoryInfo(named)
   360  		if err != nil {
   361  			t.Error(err)
   362  		} else {
   363  			assert.Check(t, is.Equal(repoInfo.Index.Name, expectedRepoInfo.Index.Name), reposName)
   364  			assert.Check(t, is.Equal(reference.Path(repoInfo.Name), expectedRepoInfo.RemoteName), reposName)
   365  			assert.Check(t, is.Equal(reference.FamiliarName(repoInfo.Name), expectedRepoInfo.LocalName), reposName)
   366  			assert.Check(t, is.Equal(repoInfo.Name.Name(), expectedRepoInfo.CanonicalName), reposName)
   367  			assert.Check(t, is.Equal(repoInfo.Index.Official, expectedRepoInfo.Index.Official), reposName)
   368  			assert.Check(t, is.Equal(repoInfo.Official, expectedRepoInfo.Official), reposName)
   369  		}
   370  	}
   371  }
   372  
   373  func TestNewIndexInfo(t *testing.T) {
   374  	testIndexInfo := func(config *serviceConfig, expectedIndexInfos map[string]*registry.IndexInfo) {
   375  		for indexName, expectedIndexInfo := range expectedIndexInfos {
   376  			index, err := newIndexInfo(config, indexName)
   377  			if err != nil {
   378  				t.Fatal(err)
   379  			} else {
   380  				assert.Check(t, is.Equal(index.Name, expectedIndexInfo.Name), indexName+" name")
   381  				assert.Check(t, is.Equal(index.Official, expectedIndexInfo.Official), indexName+" is official")
   382  				assert.Check(t, is.Equal(index.Secure, expectedIndexInfo.Secure), indexName+" is secure")
   383  				assert.Check(t, is.Equal(len(index.Mirrors), len(expectedIndexInfo.Mirrors)), indexName+" mirrors")
   384  			}
   385  		}
   386  	}
   387  
   388  	config := emptyServiceConfig
   389  	var noMirrors []string
   390  	expectedIndexInfos := map[string]*registry.IndexInfo{
   391  		IndexName: {
   392  			Name:     IndexName,
   393  			Official: true,
   394  			Secure:   true,
   395  			Mirrors:  noMirrors,
   396  		},
   397  		"index." + IndexName: {
   398  			Name:     IndexName,
   399  			Official: true,
   400  			Secure:   true,
   401  			Mirrors:  noMirrors,
   402  		},
   403  		"example.com": {
   404  			Name:     "example.com",
   405  			Official: false,
   406  			Secure:   true,
   407  			Mirrors:  noMirrors,
   408  		},
   409  		"127.0.0.1:5000": {
   410  			Name:     "127.0.0.1:5000",
   411  			Official: false,
   412  			Secure:   false,
   413  			Mirrors:  noMirrors,
   414  		},
   415  	}
   416  	testIndexInfo(config, expectedIndexInfos)
   417  
   418  	publicMirrors := []string{"http://mirror1.local", "http://mirror2.local"}
   419  	var err error
   420  	config, err = makeServiceConfig(publicMirrors, []string{"example.com"})
   421  	if err != nil {
   422  		t.Fatal(err)
   423  	}
   424  
   425  	expectedIndexInfos = map[string]*registry.IndexInfo{
   426  		IndexName: {
   427  			Name:     IndexName,
   428  			Official: true,
   429  			Secure:   true,
   430  			Mirrors:  publicMirrors,
   431  		},
   432  		"index." + IndexName: {
   433  			Name:     IndexName,
   434  			Official: true,
   435  			Secure:   true,
   436  			Mirrors:  publicMirrors,
   437  		},
   438  		"example.com": {
   439  			Name:     "example.com",
   440  			Official: false,
   441  			Secure:   false,
   442  			Mirrors:  noMirrors,
   443  		},
   444  		"example.com:5000": {
   445  			Name:     "example.com:5000",
   446  			Official: false,
   447  			Secure:   true,
   448  			Mirrors:  noMirrors,
   449  		},
   450  		"127.0.0.1": {
   451  			Name:     "127.0.0.1",
   452  			Official: false,
   453  			Secure:   false,
   454  			Mirrors:  noMirrors,
   455  		},
   456  		"127.0.0.1:5000": {
   457  			Name:     "127.0.0.1:5000",
   458  			Official: false,
   459  			Secure:   false,
   460  			Mirrors:  noMirrors,
   461  		},
   462  		"other.com": {
   463  			Name:     "other.com",
   464  			Official: false,
   465  			Secure:   true,
   466  			Mirrors:  noMirrors,
   467  		},
   468  	}
   469  	testIndexInfo(config, expectedIndexInfos)
   470  
   471  	config, err = makeServiceConfig(nil, []string{"42.42.0.0/16"})
   472  	if err != nil {
   473  		t.Fatal(err)
   474  	}
   475  	expectedIndexInfos = map[string]*registry.IndexInfo{
   476  		"example.com": {
   477  			Name:     "example.com",
   478  			Official: false,
   479  			Secure:   false,
   480  			Mirrors:  noMirrors,
   481  		},
   482  		"example.com:5000": {
   483  			Name:     "example.com:5000",
   484  			Official: false,
   485  			Secure:   false,
   486  			Mirrors:  noMirrors,
   487  		},
   488  		"127.0.0.1": {
   489  			Name:     "127.0.0.1",
   490  			Official: false,
   491  			Secure:   false,
   492  			Mirrors:  noMirrors,
   493  		},
   494  		"127.0.0.1:5000": {
   495  			Name:     "127.0.0.1:5000",
   496  			Official: false,
   497  			Secure:   false,
   498  			Mirrors:  noMirrors,
   499  		},
   500  		"other.com": {
   501  			Name:     "other.com",
   502  			Official: false,
   503  			Secure:   true,
   504  			Mirrors:  noMirrors,
   505  		},
   506  	}
   507  	testIndexInfo(config, expectedIndexInfos)
   508  }
   509  
   510  func TestMirrorEndpointLookup(t *testing.T) {
   511  	skip.If(t, os.Getuid() != 0, "skipping test that requires root")
   512  	containsMirror := func(endpoints []APIEndpoint) bool {
   513  		for _, pe := range endpoints {
   514  			if pe.URL.Host == "my.mirror" {
   515  				return true
   516  			}
   517  		}
   518  		return false
   519  	}
   520  	cfg, err := makeServiceConfig([]string{"https://my.mirror"}, nil)
   521  	if err != nil {
   522  		t.Fatal(err)
   523  	}
   524  	s := defaultService{config: cfg}
   525  
   526  	imageName, err := reference.WithName(IndexName + "/test/image")
   527  	if err != nil {
   528  		t.Error(err)
   529  	}
   530  	pushAPIEndpoints, err := s.LookupPushEndpoints(reference.Domain(imageName))
   531  	if err != nil {
   532  		t.Fatal(err)
   533  	}
   534  	if containsMirror(pushAPIEndpoints) {
   535  		t.Fatal("Push endpoint should not contain mirror")
   536  	}
   537  
   538  	pullAPIEndpoints, err := s.LookupPullEndpoints(reference.Domain(imageName))
   539  	if err != nil {
   540  		t.Fatal(err)
   541  	}
   542  	if !containsMirror(pullAPIEndpoints) {
   543  		t.Fatal("Pull endpoint should contain mirror")
   544  	}
   545  }
   546  
   547  func TestSearchRepositories(t *testing.T) {
   548  	r := spawnTestRegistrySession(t)
   549  	results, err := r.searchRepositories("fakequery", 25)
   550  	if err != nil {
   551  		t.Fatal(err)
   552  	}
   553  	if results == nil {
   554  		t.Fatal("Expected non-nil SearchResults object")
   555  	}
   556  	assert.Equal(t, results.NumResults, 1, "Expected 1 search results")
   557  	assert.Equal(t, results.Query, "fakequery", "Expected 'fakequery' as query")
   558  	assert.Equal(t, results.Results[0].StarCount, 42, "Expected 'fakeimage' to have 42 stars")
   559  }
   560  
   561  func TestTrustedLocation(t *testing.T) {
   562  	for _, url := range []string{"http://example.com", "https://example.com:7777", "http://docker.io", "http://test.docker.com", "https://fakedocker.com"} {
   563  		req, _ := http.NewRequest(http.MethodGet, url, nil)
   564  		assert.Check(t, !trustedLocation(req))
   565  	}
   566  
   567  	for _, url := range []string{"https://docker.io", "https://test.docker.com:80"} {
   568  		req, _ := http.NewRequest(http.MethodGet, url, nil)
   569  		assert.Check(t, trustedLocation(req))
   570  	}
   571  }
   572  
   573  func TestAddRequiredHeadersToRedirectedRequests(t *testing.T) {
   574  	for _, urls := range [][]string{
   575  		{"http://docker.io", "https://docker.com"},
   576  		{"https://foo.docker.io:7777", "http://bar.docker.com"},
   577  		{"https://foo.docker.io", "https://example.com"},
   578  	} {
   579  		reqFrom, _ := http.NewRequest(http.MethodGet, urls[0], nil)
   580  		reqFrom.Header.Add("Content-Type", "application/json")
   581  		reqFrom.Header.Add("Authorization", "super_secret")
   582  		reqTo, _ := http.NewRequest(http.MethodGet, urls[1], nil)
   583  
   584  		_ = addRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom})
   585  
   586  		if len(reqTo.Header) != 1 {
   587  			t.Fatalf("Expected 1 headers, got %d", len(reqTo.Header))
   588  		}
   589  
   590  		if reqTo.Header.Get("Content-Type") != "application/json" {
   591  			t.Fatal("'Content-Type' should be 'application/json'")
   592  		}
   593  
   594  		if reqTo.Header.Get("Authorization") != "" {
   595  			t.Fatal("'Authorization' should be empty")
   596  		}
   597  	}
   598  
   599  	for _, urls := range [][]string{
   600  		{"https://docker.io", "https://docker.com"},
   601  		{"https://foo.docker.io:7777", "https://bar.docker.com"},
   602  	} {
   603  		reqFrom, _ := http.NewRequest(http.MethodGet, urls[0], nil)
   604  		reqFrom.Header.Add("Content-Type", "application/json")
   605  		reqFrom.Header.Add("Authorization", "super_secret")
   606  		reqTo, _ := http.NewRequest(http.MethodGet, urls[1], nil)
   607  
   608  		_ = addRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom})
   609  
   610  		if len(reqTo.Header) != 2 {
   611  			t.Fatalf("Expected 2 headers, got %d", len(reqTo.Header))
   612  		}
   613  
   614  		if reqTo.Header.Get("Content-Type") != "application/json" {
   615  			t.Fatal("'Content-Type' should be 'application/json'")
   616  		}
   617  
   618  		if reqTo.Header.Get("Authorization") != "super_secret" {
   619  			t.Fatal("'Authorization' should be 'super_secret'")
   620  		}
   621  	}
   622  }
   623  
   624  func TestAllowNondistributableArtifacts(t *testing.T) {
   625  	tests := []struct {
   626  		addr       string
   627  		registries []string
   628  		expected   bool
   629  	}{
   630  		{IndexName, nil, false},
   631  		{"example.com", []string{}, false},
   632  		{"example.com", []string{"example.com"}, true},
   633  		{"localhost", []string{"localhost:5000"}, false},
   634  		{"localhost:5000", []string{"localhost:5000"}, true},
   635  		{"localhost", []string{"example.com"}, false},
   636  		{"127.0.0.1:5000", []string{"127.0.0.1:5000"}, true},
   637  		{"localhost", nil, false},
   638  		{"localhost:5000", nil, false},
   639  		{"127.0.0.1", nil, false},
   640  		{"localhost", []string{"example.com"}, false},
   641  		{"127.0.0.1", []string{"example.com"}, false},
   642  		{"example.com", nil, false},
   643  		{"example.com", []string{"example.com"}, true},
   644  		{"127.0.0.1", []string{"example.com"}, false},
   645  		{"127.0.0.1:5000", []string{"example.com"}, false},
   646  		{"example.com:5000", []string{"42.42.0.0/16"}, true},
   647  		{"example.com", []string{"42.42.0.0/16"}, true},
   648  		{"example.com:5000", []string{"42.42.42.42/8"}, true},
   649  		{"127.0.0.1:5000", []string{"127.0.0.0/8"}, true},
   650  		{"42.42.42.42:5000", []string{"42.1.1.1/8"}, true},
   651  		{"invalid.example.com", []string{"42.42.0.0/16"}, false},
   652  		{"invalid.example.com", []string{"invalid.example.com"}, true},
   653  		{"invalid.example.com:5000", []string{"invalid.example.com"}, false},
   654  		{"invalid.example.com:5000", []string{"invalid.example.com:5000"}, true},
   655  	}
   656  	for _, tt := range tests {
   657  		config, err := newServiceConfig(ServiceOptions{
   658  			AllowNondistributableArtifacts: tt.registries,
   659  		})
   660  		if err != nil {
   661  			t.Error(err)
   662  		}
   663  		if v := config.allowNondistributableArtifacts(tt.addr); v != tt.expected {
   664  			t.Errorf("allowNondistributableArtifacts failed for %q %v, expected %v got %v", tt.addr, tt.registries, tt.expected, v)
   665  		}
   666  	}
   667  }
   668  
   669  func TestIsSecureIndex(t *testing.T) {
   670  	tests := []struct {
   671  		addr               string
   672  		insecureRegistries []string
   673  		expected           bool
   674  	}{
   675  		{IndexName, nil, true},
   676  		{"example.com", []string{}, true},
   677  		{"example.com", []string{"example.com"}, false},
   678  		{"localhost", []string{"localhost:5000"}, false},
   679  		{"localhost:5000", []string{"localhost:5000"}, false},
   680  		{"localhost", []string{"example.com"}, false},
   681  		{"127.0.0.1:5000", []string{"127.0.0.1:5000"}, false},
   682  		{"localhost", nil, false},
   683  		{"localhost:5000", nil, false},
   684  		{"127.0.0.1", nil, false},
   685  		{"localhost", []string{"example.com"}, false},
   686  		{"127.0.0.1", []string{"example.com"}, false},
   687  		{"example.com", nil, true},
   688  		{"example.com", []string{"example.com"}, false},
   689  		{"127.0.0.1", []string{"example.com"}, false},
   690  		{"127.0.0.1:5000", []string{"example.com"}, false},
   691  		{"example.com:5000", []string{"42.42.0.0/16"}, false},
   692  		{"example.com", []string{"42.42.0.0/16"}, false},
   693  		{"example.com:5000", []string{"42.42.42.42/8"}, false},
   694  		{"127.0.0.1:5000", []string{"127.0.0.0/8"}, false},
   695  		{"42.42.42.42:5000", []string{"42.1.1.1/8"}, false},
   696  		{"invalid.example.com", []string{"42.42.0.0/16"}, true},
   697  		{"invalid.example.com", []string{"invalid.example.com"}, false},
   698  		{"invalid.example.com:5000", []string{"invalid.example.com"}, true},
   699  		{"invalid.example.com:5000", []string{"invalid.example.com:5000"}, false},
   700  	}
   701  	for _, tt := range tests {
   702  		config, err := makeServiceConfig(nil, tt.insecureRegistries)
   703  		if err != nil {
   704  			t.Error(err)
   705  		}
   706  		if sec := config.isSecureIndex(tt.addr); sec != tt.expected {
   707  			t.Errorf("isSecureIndex failed for %q %v, expected %v got %v", tt.addr, tt.insecureRegistries, tt.expected, sec)
   708  		}
   709  	}
   710  }
   711  
   712  type debugTransport struct {
   713  	http.RoundTripper
   714  	log func(...interface{})
   715  }
   716  
   717  func (tr debugTransport) RoundTrip(req *http.Request) (*http.Response, error) {
   718  	dump, err := httputil.DumpRequestOut(req, false)
   719  	if err != nil {
   720  		tr.log("could not dump request")
   721  	}
   722  	tr.log(string(dump))
   723  	resp, err := tr.RoundTripper.RoundTrip(req)
   724  	if err != nil {
   725  		return nil, err
   726  	}
   727  	dump, err = httputil.DumpResponse(resp, false)
   728  	if err != nil {
   729  		tr.log("could not dump response")
   730  	}
   731  	tr.log(string(dump))
   732  	return resp, err
   733  }