Browse code

distribution: errors: do not access the errors slice if it's empty

- cherry-pick from 1.10.3 branch: 0186f4d4223a094a050d06f456355da3ae431468
- add token service test suite
- add integration test (missing in 1.10.3 branch)

Signed-off-by: Antonio Murdaca <runcom@redhat.com>

Antonio Murdaca authored on 2016/03/15 05:11:35
Showing 11 changed files
... ...
@@ -84,7 +84,9 @@ func continueOnError(err error) bool {
84 84
 func retryOnError(err error) error {
85 85
 	switch v := err.(type) {
86 86
 	case errcode.Errors:
87
-		return retryOnError(v[0])
87
+		if len(v) != 0 {
88
+			return retryOnError(v[0])
89
+		}
88 90
 	case errcode.Error:
89 91
 		switch v.Code {
90 92
 		case errcode.ErrorCodeUnauthorized, errcode.ErrorCodeUnsupported, errcode.ErrorCodeDenied:
... ...
@@ -49,7 +49,7 @@ type DockerRegistrySuite struct {
49 49
 
50 50
 func (s *DockerRegistrySuite) SetUpTest(c *check.C) {
51 51
 	testRequires(c, DaemonIsLinux, RegistryHosting)
52
-	s.reg = setupRegistry(c, false, false)
52
+	s.reg = setupRegistry(c, false, "", "")
53 53
 	s.d = NewDaemon(c)
54 54
 }
55 55
 
... ...
@@ -77,7 +77,7 @@ type DockerSchema1RegistrySuite struct {
77 77
 
78 78
 func (s *DockerSchema1RegistrySuite) SetUpTest(c *check.C) {
79 79
 	testRequires(c, DaemonIsLinux, RegistryHosting)
80
-	s.reg = setupRegistry(c, true, false)
80
+	s.reg = setupRegistry(c, true, "", "")
81 81
 	s.d = NewDaemon(c)
82 82
 }
83 83
 
... ...
@@ -92,24 +92,24 @@ func (s *DockerSchema1RegistrySuite) TearDownTest(c *check.C) {
92 92
 }
93 93
 
94 94
 func init() {
95
-	check.Suite(&DockerRegistryAuthSuite{
95
+	check.Suite(&DockerRegistryAuthHtpasswdSuite{
96 96
 		ds: &DockerSuite{},
97 97
 	})
98 98
 }
99 99
 
100
-type DockerRegistryAuthSuite struct {
100
+type DockerRegistryAuthHtpasswdSuite struct {
101 101
 	ds  *DockerSuite
102 102
 	reg *testRegistryV2
103 103
 	d   *Daemon
104 104
 }
105 105
 
106
-func (s *DockerRegistryAuthSuite) SetUpTest(c *check.C) {
106
+func (s *DockerRegistryAuthHtpasswdSuite) SetUpTest(c *check.C) {
107 107
 	testRequires(c, DaemonIsLinux, RegistryHosting)
108
-	s.reg = setupRegistry(c, false, true)
108
+	s.reg = setupRegistry(c, false, "htpasswd", "")
109 109
 	s.d = NewDaemon(c)
110 110
 }
111 111
 
112
-func (s *DockerRegistryAuthSuite) TearDownTest(c *check.C) {
112
+func (s *DockerRegistryAuthHtpasswdSuite) TearDownTest(c *check.C) {
113 113
 	if s.reg != nil {
114 114
 		out, err := s.d.Cmd("logout", privateRegistryURL)
115 115
 		c.Assert(err, check.IsNil, check.Commentf(out))
... ...
@@ -122,6 +122,42 @@ func (s *DockerRegistryAuthSuite) TearDownTest(c *check.C) {
122 122
 }
123 123
 
124 124
 func init() {
125
+	check.Suite(&DockerRegistryAuthTokenSuite{
126
+		ds: &DockerSuite{},
127
+	})
128
+}
129
+
130
+type DockerRegistryAuthTokenSuite struct {
131
+	ds  *DockerSuite
132
+	reg *testRegistryV2
133
+	d   *Daemon
134
+}
135
+
136
+func (s *DockerRegistryAuthTokenSuite) SetUpTest(c *check.C) {
137
+	testRequires(c, DaemonIsLinux, RegistryHosting)
138
+	s.d = NewDaemon(c)
139
+}
140
+
141
+func (s *DockerRegistryAuthTokenSuite) TearDownTest(c *check.C) {
142
+	if s.reg != nil {
143
+		out, err := s.d.Cmd("logout", privateRegistryURL)
144
+		c.Assert(err, check.IsNil, check.Commentf(out))
145
+		s.reg.Close()
146
+	}
147
+	if s.d != nil {
148
+		s.d.Stop()
149
+	}
150
+	s.ds.TearDownTest(c)
151
+}
152
+
153
+func (s *DockerRegistryAuthTokenSuite) setupRegistryWithTokenService(c *check.C, tokenURL string) {
154
+	if s == nil {
155
+		c.Fatal("registry suite isn't initialized")
156
+	}
157
+	s.reg = setupRegistry(c, false, "token", tokenURL)
158
+}
159
+
160
+func init() {
125 161
 	check.Suite(&DockerDaemonSuite{
126 162
 		ds: &DockerSuite{},
127 163
 	})
... ...
@@ -159,7 +195,7 @@ type DockerTrustSuite struct {
159 159
 
160 160
 func (s *DockerTrustSuite) SetUpTest(c *check.C) {
161 161
 	testRequires(c, RegistryHosting, NotaryServerHosting)
162
-	s.reg = setupRegistry(c, false, false)
162
+	s.reg = setupRegistry(c, false, "", "")
163 163
 	s.not = setupNotary(c)
164 164
 }
165 165
 
... ...
@@ -6596,7 +6596,7 @@ func (s *DockerSuite) TestBuildWorkdirWindowsPath(c *check.C) {
6596 6596
 	}
6597 6597
 }
6598 6598
 
6599
-func (s *DockerRegistryAuthSuite) TestBuildFromAuthenticatedRegistry(c *check.C) {
6599
+func (s *DockerRegistryAuthHtpasswdSuite) TestBuildFromAuthenticatedRegistry(c *check.C) {
6600 6600
 	dockerCmd(c, "login", "-u", s.reg.username, "-p", s.reg.password, privateRegistryURL)
6601 6601
 
6602 6602
 	baseImage := privateRegistryURL + "/baseimage"
... ...
@@ -6619,7 +6619,7 @@ func (s *DockerRegistryAuthSuite) TestBuildFromAuthenticatedRegistry(c *check.C)
6619 6619
 	c.Assert(err, checker.IsNil)
6620 6620
 }
6621 6621
 
6622
-func (s *DockerRegistryAuthSuite) TestBuildWithExternalAuth(c *check.C) {
6622
+func (s *DockerRegistryAuthHtpasswdSuite) TestBuildWithExternalAuth(c *check.C) {
6623 6623
 	osPath := os.Getenv("PATH")
6624 6624
 	defer os.Setenv("PATH", osPath)
6625 6625
 
... ...
@@ -19,7 +19,7 @@ func (s *DockerSuite) TestLoginWithoutTTY(c *check.C) {
19 19
 	c.Assert(err, checker.NotNil) //"Expected non nil err when loginning in & TTY not available"
20 20
 }
21 21
 
22
-func (s *DockerRegistryAuthSuite) TestLoginToPrivateRegistry(c *check.C) {
22
+func (s *DockerRegistryAuthHtpasswdSuite) TestLoginToPrivateRegistry(c *check.C) {
23 23
 	// wrong credentials
24 24
 	out, _, err := dockerCmdWithError("login", "-u", s.reg.username, "-p", "WRONGPASSWORD", privateRegistryURL)
25 25
 	c.Assert(err, checker.NotNil, check.Commentf(out))
... ...
@@ -29,7 +29,7 @@ func (s *DockerRegistryAuthSuite) TestLoginToPrivateRegistry(c *check.C) {
29 29
 	dockerCmd(c, "login", "-u", s.reg.username, "-p", s.reg.password, privateRegistryURL)
30 30
 }
31 31
 
32
-func (s *DockerRegistryAuthSuite) TestLoginToPrivateRegistryDeprecatedEmailFlag(c *check.C) {
32
+func (s *DockerRegistryAuthHtpasswdSuite) TestLoginToPrivateRegistryDeprecatedEmailFlag(c *check.C) {
33 33
 	// Test to make sure login still works with the deprecated -e and --email flags
34 34
 	// wrong credentials
35 35
 	out, _, err := dockerCmdWithError("login", "-u", s.reg.username, "-p", "WRONGPASSWORD", "-e", s.reg.email, privateRegistryURL)
... ...
@@ -10,7 +10,7 @@ import (
10 10
 	"github.com/go-check/check"
11 11
 )
12 12
 
13
-func (s *DockerRegistryAuthSuite) TestLogoutWithExternalAuth(c *check.C) {
13
+func (s *DockerRegistryAuthHtpasswdSuite) TestLogoutWithExternalAuth(c *check.C) {
14 14
 	osPath := os.Getenv("PATH")
15 15
 	defer os.Setenv("PATH", osPath)
16 16
 
... ...
@@ -387,7 +387,7 @@ func (s *DockerRegistrySuite) TestPullManifestList(c *check.C) {
387 387
 	dockerCmd(c, "rmi", repoName)
388 388
 }
389 389
 
390
-func (s *DockerRegistryAuthSuite) TestPullWithExternalAuth(c *check.C) {
390
+func (s *DockerRegistryAuthHtpasswdSuite) TestPullWithExternalAuth(c *check.C) {
391 391
 	osPath := os.Getenv("PATH")
392 392
 	defer os.Setenv("PATH", osPath)
393 393
 
... ...
@@ -255,7 +255,7 @@ func (s *DockerHubPullSuite) TestPullClientDisconnect(c *check.C) {
255 255
 	c.Assert(err, checker.NotNil, check.Commentf("image was pulled after client disconnected"))
256 256
 }
257 257
 
258
-func (s *DockerRegistryAuthSuite) TestPullNoCredentialsNotFound(c *check.C) {
258
+func (s *DockerRegistryAuthHtpasswdSuite) TestPullNoCredentialsNotFound(c *check.C) {
259 259
 	// we don't care about the actual image, we just want to see image not found
260 260
 	// because that means v2 call returned 401 and we fell back to v1 which usually
261 261
 	// gives a 404 (in this case the test registry doesn't handle v1 at all)
... ...
@@ -4,6 +4,8 @@ import (
4 4
 	"archive/tar"
5 5
 	"fmt"
6 6
 	"io/ioutil"
7
+	"net/http"
8
+	"net/http/httptest"
7 9
 	"os"
8 10
 	"os/exec"
9 11
 	"path/filepath"
... ...
@@ -528,7 +530,7 @@ func (s *DockerTrustSuite) TestTrustedPushWithReleasesDelegation(c *check.C) {
528 528
 	c.Assert(string(contents), checker.Contains, `"latest"`, check.Commentf(string(contents)))
529 529
 }
530 530
 
531
-func (s *DockerRegistryAuthSuite) TestPushNoCredentialsNoRetry(c *check.C) {
531
+func (s *DockerRegistryAuthHtpasswdSuite) TestPushNoCredentialsNoRetry(c *check.C) {
532 532
 	repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
533 533
 	dockerCmd(c, "tag", "busybox", repoName)
534 534
 	out, _, err := dockerCmdWithError("push", repoName)
... ...
@@ -546,3 +548,33 @@ func (s *DockerSuite) TestPushToCentralRegistryUnauthorized(c *check.C) {
546 546
 	c.Assert(err, check.NotNil, check.Commentf(out))
547 547
 	c.Assert(out, checker.Contains, "unauthorized: access to the requested resource is not authorized")
548 548
 }
549
+
550
+func (s *DockerRegistryAuthTokenSuite) TestPushTokenServiceUnauthResponse(c *check.C) {
551
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
552
+		w.WriteHeader(http.StatusUnauthorized)
553
+		w.Header().Set("Content-Type", "application/json")
554
+		w.Write([]byte(`{"errors": [{"Code":"UNAUTHORIZED", "message": "a message", "detail": null}]}`))
555
+	}))
556
+	defer ts.Close()
557
+	s.setupRegistryWithTokenService(c, ts.URL)
558
+	repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
559
+	dockerCmd(c, "tag", "busybox", repoName)
560
+	out, _, err := dockerCmdWithError("push", repoName)
561
+	c.Assert(err, check.NotNil, check.Commentf(out))
562
+	c.Assert(out, checker.Contains, "unauthorized: a message")
563
+}
564
+
565
+func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponse(c *check.C) {
566
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
567
+		w.WriteHeader(http.StatusUnauthorized)
568
+		w.Header().Set("Content-Type", "application/json")
569
+		// this will make the daemon panics if no check is performed in retryOnError
570
+		w.Write([]byte(`{"error": "unauthorized"}`))
571
+	}))
572
+	defer ts.Close()
573
+	s.setupRegistryWithTokenService(c, ts.URL)
574
+	repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
575
+	dockerCmd(c, "tag", "busybox", repoName)
576
+	out, _, err := dockerCmdWithError("push", repoName)
577
+	c.Assert(err, check.NotNil, check.Commentf(out))
578
+}
... ...
@@ -1237,8 +1237,8 @@ func daemonTime(c *check.C) time.Time {
1237 1237
 	return dt
1238 1238
 }
1239 1239
 
1240
-func setupRegistry(c *check.C, schema1, auth bool) *testRegistryV2 {
1241
-	reg, err := newTestRegistryV2(c, schema1, auth)
1240
+func setupRegistry(c *check.C, schema1 bool, auth, tokenURL string) *testRegistryV2 {
1241
+	reg, err := newTestRegistryV2(c, schema1, auth, tokenURL)
1242 1242
 	c.Assert(err, check.IsNil)
1243 1243
 
1244 1244
 	// Wait for registry to be ready to serve requests.
1245 1245
new file mode 100644
... ...
@@ -0,0 +1,21 @@
0
+-----BEGIN CERTIFICATE-----
1
+MIIDfzCCAmegAwIBAgIJAKZjzF7N4zFJMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV
2
+BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg
3
+Q29tcGFueSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNjAzMTQxOTAzMDZa
4
+Fw0xNzAzMTQxOTAzMDZaMFYxCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0
5
+IENpdHkxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQxEjAQBgNVBAMMCWxv
6
+Y2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMAVEPA6tSNy
7
+MoExHvT8CWvbe0MyYqZjMmUUdGVYyAaoZgmj9HvtGKaUWY/hCtgTond3OKhPq69u
8
+fQSDlHQA/scq4KZovKQJhvBaRb2DqD31KcbcDyh5KUAL1aalbjTLbKmAYSFSoY93
9
+57KiBei2BmvS55HLhOiO8ccQOq3feH/J/XcszAdAaiGXW3woDOIumYzur6Q8Suyn
10
+cIUEX5Ik7mxS7oGYN1IM++Y+B6aAFT7htAZEvF7RF7sjG7QBfxNPOFg9lBWXzVSv
11
+0vRbVme9OCDD2QOpj8O7XAPuLDwW5b2A8Iex3CJRngBI9vAK5h1Wssst8117bur9
12
+AiubOrF6cxUCAwEAAaNQME4wHQYDVR0OBBYEFNTGYK7uX19yjCPeGXhmel98amoA
13
+MB8GA1UdIwQYMBaAFNTGYK7uX19yjCPeGXhmel98amoAMAwGA1UdEwQFMAMBAf8w
14
+DQYJKoZIhvcNAQELBQADggEBACW/oF6RgLbTPxb8oPI9424Uv/erYYdxdqIaO3Mz
15
+fQfBEvGu62A0ZLH+av4BTeqBM6iVhN6/Y3hUb8UzbbZAIo/dVJSglW7PXAfUITMM
16
+ca9U2r2cFqgXELZkhde6mTFTYwM3swMCP0HUEo+Hu62NX5gunKr4QMNfTlE3vHEj
17
+jitnkTR0ZVEKHvmdTJC9S92j+NuaJVcwe5UNP1Nj/Ksd/iUUCa2DBnw2N7YwHTDB
18
+jb9cQb8aNVNSrjKP3sknMslVy1JVbUB1LXsth/h+kkVFNP4dsk+dZHn20uIA/VeJ
19
+mJ3Wo54CeTAa3DysiWbIIYsFSASCPvki08ZKI373tCf2RvE=
20
+-----END CERTIFICATE-----
... ...
@@ -20,12 +20,13 @@ const (
20 20
 type testRegistryV2 struct {
21 21
 	cmd      *exec.Cmd
22 22
 	dir      string
23
+	auth     string
23 24
 	username string
24 25
 	password string
25 26
 	email    string
26 27
 }
27 28
 
28
-func newTestRegistryV2(c *check.C, schema1, auth bool) (*testRegistryV2, error) {
29
+func newTestRegistryV2(c *check.C, schema1 bool, auth, tokenURL string) (*testRegistryV2, error) {
29 30
 	tmp, err := ioutil.TempDir("", "registry-test-")
30 31
 	if err != nil {
31 32
 		return nil, err
... ...
@@ -39,12 +40,13 @@ http:
39 39
     addr: %s
40 40
 %s`
41 41
 	var (
42
-		htpasswd string
43
-		username string
44
-		password string
45
-		email    string
42
+		authTemplate string
43
+		username     string
44
+		password     string
45
+		email        string
46 46
 	)
47
-	if auth {
47
+	switch auth {
48
+	case "htpasswd":
48 49
 		htpasswdPath := filepath.Join(tmp, "htpasswd")
49 50
 		// generated with: htpasswd -Bbn testuser testpassword
50 51
 		userpasswd := "testuser:$2y$05$sBsSqk0OpSD1uTZkHXc4FeJ0Z70wLQdAX/82UiHuQOKbNbBrzs63m"
... ...
@@ -54,11 +56,19 @@ http:
54 54
 		if err := ioutil.WriteFile(htpasswdPath, []byte(userpasswd), os.FileMode(0644)); err != nil {
55 55
 			return nil, err
56 56
 		}
57
-		htpasswd = fmt.Sprintf(`auth:
57
+		authTemplate = fmt.Sprintf(`auth:
58 58
     htpasswd:
59 59
         realm: basic-realm
60 60
         path: %s
61 61
 `, htpasswdPath)
62
+	case "token":
63
+		authTemplate = fmt.Sprintf(`auth:
64
+    token:
65
+        realm: %s
66
+        service: "registry"
67
+        issuer: "auth-registry"
68
+        rootcertbundle: "fixtures/registry/cert.pem"
69
+`, tokenURL)
62 70
 	}
63 71
 
64 72
 	confPath := filepath.Join(tmp, "config.yaml")
... ...
@@ -66,7 +76,7 @@ http:
66 66
 	if err != nil {
67 67
 		return nil, err
68 68
 	}
69
-	if _, err := fmt.Fprintf(config, template, tmp, privateRegistryURL, htpasswd); err != nil {
69
+	if _, err := fmt.Fprintf(config, template, tmp, privateRegistryURL, authTemplate); err != nil {
70 70
 		os.RemoveAll(tmp)
71 71
 		return nil, err
72 72
 	}
... ...
@@ -86,6 +96,7 @@ http:
86 86
 	return &testRegistryV2{
87 87
 		cmd:      cmd,
88 88
 		dir:      tmp,
89
+		auth:     auth,
89 90
 		username: username,
90 91
 		password: password,
91 92
 		email:    email,
... ...
@@ -101,7 +112,7 @@ func (t *testRegistryV2) Ping() error {
101 101
 	resp.Body.Close()
102 102
 
103 103
 	fail := resp.StatusCode != http.StatusOK
104
-	if t.username != "" {
104
+	if t.auth != "" {
105 105
 		// unauthorized is a _good_ status when pinging v2/ and it needs auth
106 106
 		fail = fail && resp.StatusCode != http.StatusUnauthorized
107 107
 	}