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