github.com/stchris/docker@v1.4.2-0.20150106053530-1510a324dbd5/registry/registry_test.go (about)

     1  package registry
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"net/url"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/docker/docker/utils"
    11  )
    12  
    13  var (
    14  	token = []string{"fake-token"}
    15  )
    16  
    17  const (
    18  	imageID = "42d718c941f5c532ac049bf0b0ab53f0062f09a03afd4aa4a02c098e46032b9d"
    19  	REPO    = "foo42/bar"
    20  )
    21  
    22  func spawnTestRegistrySession(t *testing.T) *Session {
    23  	authConfig := &AuthConfig{}
    24  	endpoint, err := NewEndpoint(makeURL("/v1/"), insecureRegistries)
    25  	if err != nil {
    26  		t.Fatal(err)
    27  	}
    28  	r, err := NewSession(authConfig, utils.NewHTTPRequestFactory(), endpoint, true)
    29  	if err != nil {
    30  		t.Fatal(err)
    31  	}
    32  	return r
    33  }
    34  
    35  func TestPingRegistryEndpoint(t *testing.T) {
    36  	ep, err := NewEndpoint(makeURL("/v1/"), insecureRegistries)
    37  	if err != nil {
    38  		t.Fatal(err)
    39  	}
    40  	regInfo, err := ep.Ping()
    41  	if err != nil {
    42  		t.Fatal(err)
    43  	}
    44  	assertEqual(t, regInfo.Standalone, true, "Expected standalone to be true (default)")
    45  }
    46  
    47  func TestGetRemoteHistory(t *testing.T) {
    48  	r := spawnTestRegistrySession(t)
    49  	hist, err := r.GetRemoteHistory(imageID, makeURL("/v1/"), token)
    50  	if err != nil {
    51  		t.Fatal(err)
    52  	}
    53  	assertEqual(t, len(hist), 2, "Expected 2 images in history")
    54  	assertEqual(t, hist[0], imageID, "Expected "+imageID+"as first ancestry")
    55  	assertEqual(t, hist[1], "77dbf71da1d00e3fbddc480176eac8994025630c6590d11cfc8fe1209c2a1d20",
    56  		"Unexpected second ancestry")
    57  }
    58  
    59  func TestLookupRemoteImage(t *testing.T) {
    60  	r := spawnTestRegistrySession(t)
    61  	err := r.LookupRemoteImage(imageID, makeURL("/v1/"), token)
    62  	assertEqual(t, err, nil, "Expected error of remote lookup to nil")
    63  	if err := r.LookupRemoteImage("abcdef", makeURL("/v1/"), token); err == nil {
    64  		t.Fatal("Expected error of remote lookup to not nil")
    65  	}
    66  }
    67  
    68  func TestGetRemoteImageJSON(t *testing.T) {
    69  	r := spawnTestRegistrySession(t)
    70  	json, size, err := r.GetRemoteImageJSON(imageID, makeURL("/v1/"), token)
    71  	if err != nil {
    72  		t.Fatal(err)
    73  	}
    74  	assertEqual(t, size, 154, "Expected size 154")
    75  	if len(json) <= 0 {
    76  		t.Fatal("Expected non-empty json")
    77  	}
    78  
    79  	_, _, err = r.GetRemoteImageJSON("abcdef", makeURL("/v1/"), token)
    80  	if err == nil {
    81  		t.Fatal("Expected image not found error")
    82  	}
    83  }
    84  
    85  func TestGetRemoteImageLayer(t *testing.T) {
    86  	r := spawnTestRegistrySession(t)
    87  	data, err := r.GetRemoteImageLayer(imageID, makeURL("/v1/"), token, 0)
    88  	if err != nil {
    89  		t.Fatal(err)
    90  	}
    91  	if data == nil {
    92  		t.Fatal("Expected non-nil data result")
    93  	}
    94  
    95  	_, err = r.GetRemoteImageLayer("abcdef", makeURL("/v1/"), token, 0)
    96  	if err == nil {
    97  		t.Fatal("Expected image not found error")
    98  	}
    99  }
   100  
   101  func TestGetRemoteTags(t *testing.T) {
   102  	r := spawnTestRegistrySession(t)
   103  	tags, err := r.GetRemoteTags([]string{makeURL("/v1/")}, REPO, token)
   104  	if err != nil {
   105  		t.Fatal(err)
   106  	}
   107  	assertEqual(t, len(tags), 1, "Expected one tag")
   108  	assertEqual(t, tags["latest"], imageID, "Expected tag latest to map to "+imageID)
   109  
   110  	_, err = r.GetRemoteTags([]string{makeURL("/v1/")}, "foo42/baz", token)
   111  	if err == nil {
   112  		t.Fatal("Expected error when fetching tags for bogus repo")
   113  	}
   114  }
   115  
   116  func TestGetRepositoryData(t *testing.T) {
   117  	r := spawnTestRegistrySession(t)
   118  	parsedURL, err := url.Parse(makeURL("/v1/"))
   119  	if err != nil {
   120  		t.Fatal(err)
   121  	}
   122  	host := "http://" + parsedURL.Host + "/v1/"
   123  	data, err := r.GetRepositoryData("foo42/bar")
   124  	if err != nil {
   125  		t.Fatal(err)
   126  	}
   127  	assertEqual(t, len(data.ImgList), 2, "Expected 2 images in ImgList")
   128  	assertEqual(t, len(data.Endpoints), 2,
   129  		fmt.Sprintf("Expected 2 endpoints in Endpoints, found %d instead", len(data.Endpoints)))
   130  	assertEqual(t, data.Endpoints[0], host,
   131  		fmt.Sprintf("Expected first endpoint to be %s but found %s instead", host, data.Endpoints[0]))
   132  	assertEqual(t, data.Endpoints[1], "http://test.example.com/v1/",
   133  		fmt.Sprintf("Expected first endpoint to be http://test.example.com/v1/ but found %s instead", data.Endpoints[1]))
   134  
   135  }
   136  
   137  func TestPushImageJSONRegistry(t *testing.T) {
   138  	r := spawnTestRegistrySession(t)
   139  	imgData := &ImgData{
   140  		ID:       "77dbf71da1d00e3fbddc480176eac8994025630c6590d11cfc8fe1209c2a1d20",
   141  		Checksum: "sha256:1ac330d56e05eef6d438586545ceff7550d3bdcb6b19961f12c5ba714ee1bb37",
   142  	}
   143  
   144  	err := r.PushImageJSONRegistry(imgData, []byte{0x42, 0xdf, 0x0}, makeURL("/v1/"), token)
   145  	if err != nil {
   146  		t.Fatal(err)
   147  	}
   148  }
   149  
   150  func TestPushImageLayerRegistry(t *testing.T) {
   151  	r := spawnTestRegistrySession(t)
   152  	layer := strings.NewReader("")
   153  	_, _, err := r.PushImageLayerRegistry(imageID, layer, makeURL("/v1/"), token, []byte{})
   154  	if err != nil {
   155  		t.Fatal(err)
   156  	}
   157  }
   158  
   159  func TestResolveRepositoryName(t *testing.T) {
   160  	_, _, err := ResolveRepositoryName("https://github.com/docker/docker")
   161  	assertEqual(t, err, ErrInvalidRepositoryName, "Expected error invalid repo name")
   162  	ep, repo, err := ResolveRepositoryName("fooo/bar")
   163  	if err != nil {
   164  		t.Fatal(err)
   165  	}
   166  	assertEqual(t, ep, IndexServerAddress(), "Expected endpoint to be index server address")
   167  	assertEqual(t, repo, "fooo/bar", "Expected resolved repo to be foo/bar")
   168  
   169  	u := makeURL("")[7:]
   170  	ep, repo, err = ResolveRepositoryName(u + "/private/moonbase")
   171  	if err != nil {
   172  		t.Fatal(err)
   173  	}
   174  	assertEqual(t, ep, u, "Expected endpoint to be "+u)
   175  	assertEqual(t, repo, "private/moonbase", "Expected endpoint to be private/moonbase")
   176  
   177  	ep, repo, err = ResolveRepositoryName("ubuntu-12.04-base")
   178  	if err != nil {
   179  		t.Fatal(err)
   180  	}
   181  	assertEqual(t, ep, IndexServerAddress(), "Expected endpoint to be "+IndexServerAddress())
   182  	assertEqual(t, repo, "ubuntu-12.04-base", "Expected endpoint to be ubuntu-12.04-base")
   183  }
   184  
   185  func TestPushRegistryTag(t *testing.T) {
   186  	r := spawnTestRegistrySession(t)
   187  	err := r.PushRegistryTag("foo42/bar", imageID, "stable", makeURL("/v1/"), token)
   188  	if err != nil {
   189  		t.Fatal(err)
   190  	}
   191  }
   192  
   193  func TestPushImageJSONIndex(t *testing.T) {
   194  	r := spawnTestRegistrySession(t)
   195  	imgData := []*ImgData{
   196  		{
   197  			ID:       "77dbf71da1d00e3fbddc480176eac8994025630c6590d11cfc8fe1209c2a1d20",
   198  			Checksum: "sha256:1ac330d56e05eef6d438586545ceff7550d3bdcb6b19961f12c5ba714ee1bb37",
   199  		},
   200  		{
   201  			ID:       "42d718c941f5c532ac049bf0b0ab53f0062f09a03afd4aa4a02c098e46032b9d",
   202  			Checksum: "sha256:bea7bf2e4bacd479344b737328db47b18880d09096e6674165533aa994f5e9f2",
   203  		},
   204  	}
   205  	repoData, err := r.PushImageJSONIndex("foo42/bar", imgData, false, nil)
   206  	if err != nil {
   207  		t.Fatal(err)
   208  	}
   209  	if repoData == nil {
   210  		t.Fatal("Expected RepositoryData object")
   211  	}
   212  	repoData, err = r.PushImageJSONIndex("foo42/bar", imgData, true, []string{r.indexEndpoint.String()})
   213  	if err != nil {
   214  		t.Fatal(err)
   215  	}
   216  	if repoData == nil {
   217  		t.Fatal("Expected RepositoryData object")
   218  	}
   219  }
   220  
   221  func TestSearchRepositories(t *testing.T) {
   222  	r := spawnTestRegistrySession(t)
   223  	results, err := r.SearchRepositories("fakequery")
   224  	if err != nil {
   225  		t.Fatal(err)
   226  	}
   227  	if results == nil {
   228  		t.Fatal("Expected non-nil SearchResults object")
   229  	}
   230  	assertEqual(t, results.NumResults, 1, "Expected 1 search results")
   231  	assertEqual(t, results.Query, "fakequery", "Expected 'fakequery' as query")
   232  	assertEqual(t, results.Results[0].StarCount, 42, "Expected 'fakeimage' a ot hae 42 stars")
   233  }
   234  
   235  func TestValidRepositoryName(t *testing.T) {
   236  	validRepositoryNames := []string{
   237  		// Sanity check.
   238  		"docker/docker",
   239  
   240  		// Allow 64-character non-hexadecimal names (hexadecimal names are forbidden).
   241  		"thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
   242  
   243  		// Allow embedded hyphens.
   244  		"docker-rules/docker",
   245  
   246  		// Allow underscores everywhere (as opposed to hyphens).
   247  		"____/____",
   248  	}
   249  	for _, repositoryName := range validRepositoryNames {
   250  		if err := validateRepositoryName(repositoryName); err != nil {
   251  			t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err)
   252  		}
   253  	}
   254  
   255  	invalidRepositoryNames := []string{
   256  		// Disallow capital letters.
   257  		"docker/Docker",
   258  
   259  		// Only allow one slash.
   260  		"docker///docker",
   261  
   262  		// Disallow 64-character hexadecimal.
   263  		"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
   264  
   265  		// Disallow leading and trailing hyphens in namespace.
   266  		"-docker/docker",
   267  		"docker-/docker",
   268  		"-docker-/docker",
   269  
   270  		// Disallow consecutive hyphens.
   271  		"dock--er/docker",
   272  
   273  		// Namespace too short.
   274  		"doc/docker",
   275  
   276  		// No repository.
   277  		"docker/",
   278  	}
   279  	for _, repositoryName := range invalidRepositoryNames {
   280  		if err := validateRepositoryName(repositoryName); err == nil {
   281  			t.Errorf("Repository name should be invalid: %v", repositoryName)
   282  		}
   283  	}
   284  }
   285  
   286  func TestTrustedLocation(t *testing.T) {
   287  	for _, url := range []string{"http://example.com", "https://example.com:7777", "http://docker.io", "http://test.docker.com", "https://fakedocker.com"} {
   288  		req, _ := http.NewRequest("GET", url, nil)
   289  		if trustedLocation(req) == true {
   290  			t.Fatalf("'%s' shouldn't be detected as a trusted location", url)
   291  		}
   292  	}
   293  
   294  	for _, url := range []string{"https://docker.io", "https://test.docker.com:80"} {
   295  		req, _ := http.NewRequest("GET", url, nil)
   296  		if trustedLocation(req) == false {
   297  			t.Fatalf("'%s' should be detected as a trusted location", url)
   298  		}
   299  	}
   300  }
   301  
   302  func TestAddRequiredHeadersToRedirectedRequests(t *testing.T) {
   303  	for _, urls := range [][]string{
   304  		{"http://docker.io", "https://docker.com"},
   305  		{"https://foo.docker.io:7777", "http://bar.docker.com"},
   306  		{"https://foo.docker.io", "https://example.com"},
   307  	} {
   308  		reqFrom, _ := http.NewRequest("GET", urls[0], nil)
   309  		reqFrom.Header.Add("Content-Type", "application/json")
   310  		reqFrom.Header.Add("Authorization", "super_secret")
   311  		reqTo, _ := http.NewRequest("GET", urls[1], nil)
   312  
   313  		AddRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom})
   314  
   315  		if len(reqTo.Header) != 1 {
   316  			t.Fatalf("Expected 1 headers, got %d", len(reqTo.Header))
   317  		}
   318  
   319  		if reqTo.Header.Get("Content-Type") != "application/json" {
   320  			t.Fatal("'Content-Type' should be 'application/json'")
   321  		}
   322  
   323  		if reqTo.Header.Get("Authorization") != "" {
   324  			t.Fatal("'Authorization' should be empty")
   325  		}
   326  	}
   327  
   328  	for _, urls := range [][]string{
   329  		{"https://docker.io", "https://docker.com"},
   330  		{"https://foo.docker.io:7777", "https://bar.docker.com"},
   331  	} {
   332  		reqFrom, _ := http.NewRequest("GET", urls[0], nil)
   333  		reqFrom.Header.Add("Content-Type", "application/json")
   334  		reqFrom.Header.Add("Authorization", "super_secret")
   335  		reqTo, _ := http.NewRequest("GET", urls[1], nil)
   336  
   337  		AddRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom})
   338  
   339  		if len(reqTo.Header) != 2 {
   340  			t.Fatalf("Expected 2 headers, got %d", len(reqTo.Header))
   341  		}
   342  
   343  		if reqTo.Header.Get("Content-Type") != "application/json" {
   344  			t.Fatal("'Content-Type' should be 'application/json'")
   345  		}
   346  
   347  		if reqTo.Header.Get("Authorization") != "super_secret" {
   348  			t.Fatal("'Authorization' should be 'super_secret'")
   349  		}
   350  	}
   351  }
   352  
   353  func TestIsSecure(t *testing.T) {
   354  	tests := []struct {
   355  		addr               string
   356  		insecureRegistries []string
   357  		expected           bool
   358  	}{
   359  		{IndexServerURL.Host, nil, true},
   360  		{"example.com", []string{}, true},
   361  		{"example.com", []string{"example.com"}, false},
   362  		{"localhost", []string{"localhost:5000"}, false},
   363  		{"localhost:5000", []string{"localhost:5000"}, false},
   364  		{"localhost", []string{"example.com"}, false},
   365  		{"127.0.0.1:5000", []string{"127.0.0.1:5000"}, false},
   366  		{"localhost", nil, false},
   367  		{"localhost:5000", nil, false},
   368  		{"127.0.0.1", nil, false},
   369  		{"localhost", []string{"example.com"}, false},
   370  		{"127.0.0.1", []string{"example.com"}, false},
   371  		{"example.com", nil, true},
   372  		{"example.com", []string{"example.com"}, false},
   373  		{"127.0.0.1", []string{"example.com"}, false},
   374  		{"127.0.0.1:5000", []string{"example.com"}, false},
   375  		{"example.com:5000", []string{"42.42.0.0/16"}, false},
   376  		{"example.com", []string{"42.42.0.0/16"}, false},
   377  		{"example.com:5000", []string{"42.42.42.42/8"}, false},
   378  		{"127.0.0.1:5000", []string{"127.0.0.0/8"}, false},
   379  		{"42.42.42.42:5000", []string{"42.1.1.1/8"}, false},
   380  		{"invalid.domain.com", []string{"42.42.0.0/16"}, true},
   381  		{"invalid.domain.com", []string{"invalid.domain.com"}, false},
   382  		{"invalid.domain.com:5000", []string{"invalid.domain.com"}, true},
   383  		{"invalid.domain.com:5000", []string{"invalid.domain.com:5000"}, false},
   384  	}
   385  	for _, tt := range tests {
   386  		// TODO: remove this once we remove localhost insecure by default
   387  		insecureRegistries := append(tt.insecureRegistries, "127.0.0.0/8")
   388  		if sec, err := isSecure(tt.addr, insecureRegistries); err != nil || sec != tt.expected {
   389  			t.Fatalf("isSecure failed for %q %v, expected %v got %v. Error: %v", tt.addr, insecureRegistries, tt.expected, sec, err)
   390  		}
   391  	}
   392  }