github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/registry/registry_test.go (about)

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