Tizen_4.0 base
[platform/upstream/docker-engine.git] / integration-cli / docker_cli_push_test.go
1 package main
2
3 import (
4         "archive/tar"
5         "fmt"
6         "io/ioutil"
7         "net/http"
8         "net/http/httptest"
9         "os"
10         "path/filepath"
11         "strings"
12         "sync"
13
14         "github.com/docker/distribution/reference"
15         "github.com/docker/docker/cli/config"
16         "github.com/docker/docker/integration-cli/checker"
17         "github.com/docker/docker/integration-cli/cli"
18         "github.com/docker/docker/integration-cli/cli/build"
19         icmd "github.com/docker/docker/pkg/testutil/cmd"
20         "github.com/go-check/check"
21 )
22
23 // Pushing an image to a private registry.
24 func testPushBusyboxImage(c *check.C) {
25         repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
26         // tag the image to upload it to the private registry
27         dockerCmd(c, "tag", "busybox", repoName)
28         // push the image to the registry
29         dockerCmd(c, "push", repoName)
30 }
31
32 func (s *DockerRegistrySuite) TestPushBusyboxImage(c *check.C) {
33         testPushBusyboxImage(c)
34 }
35
36 func (s *DockerSchema1RegistrySuite) TestPushBusyboxImage(c *check.C) {
37         testPushBusyboxImage(c)
38 }
39
40 // pushing an image without a prefix should throw an error
41 func (s *DockerSuite) TestPushUnprefixedRepo(c *check.C) {
42         out, _, err := dockerCmdWithError("push", "busybox")
43         c.Assert(err, check.NotNil, check.Commentf("pushing an unprefixed repo didn't result in a non-zero exit status: %s", out))
44 }
45
46 func testPushUntagged(c *check.C) {
47         repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
48         expected := "An image does not exist locally with the tag"
49
50         out, _, err := dockerCmdWithError("push", repoName)
51         c.Assert(err, check.NotNil, check.Commentf("pushing the image to the private registry should have failed: output %q", out))
52         c.Assert(out, checker.Contains, expected, check.Commentf("pushing the image failed"))
53 }
54
55 func (s *DockerRegistrySuite) TestPushUntagged(c *check.C) {
56         testPushUntagged(c)
57 }
58
59 func (s *DockerSchema1RegistrySuite) TestPushUntagged(c *check.C) {
60         testPushUntagged(c)
61 }
62
63 func testPushBadTag(c *check.C) {
64         repoName := fmt.Sprintf("%v/dockercli/busybox:latest", privateRegistryURL)
65         expected := "does not exist"
66
67         out, _, err := dockerCmdWithError("push", repoName)
68         c.Assert(err, check.NotNil, check.Commentf("pushing the image to the private registry should have failed: output %q", out))
69         c.Assert(out, checker.Contains, expected, check.Commentf("pushing the image failed"))
70 }
71
72 func (s *DockerRegistrySuite) TestPushBadTag(c *check.C) {
73         testPushBadTag(c)
74 }
75
76 func (s *DockerSchema1RegistrySuite) TestPushBadTag(c *check.C) {
77         testPushBadTag(c)
78 }
79
80 func testPushMultipleTags(c *check.C) {
81         repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
82         repoTag1 := fmt.Sprintf("%v/dockercli/busybox:t1", privateRegistryURL)
83         repoTag2 := fmt.Sprintf("%v/dockercli/busybox:t2", privateRegistryURL)
84         // tag the image and upload it to the private registry
85         dockerCmd(c, "tag", "busybox", repoTag1)
86
87         dockerCmd(c, "tag", "busybox", repoTag2)
88
89         dockerCmd(c, "push", repoName)
90
91         // Ensure layer list is equivalent for repoTag1 and repoTag2
92         out1, _ := dockerCmd(c, "pull", repoTag1)
93
94         imageAlreadyExists := ": Image already exists"
95         var out1Lines []string
96         for _, outputLine := range strings.Split(out1, "\n") {
97                 if strings.Contains(outputLine, imageAlreadyExists) {
98                         out1Lines = append(out1Lines, outputLine)
99                 }
100         }
101
102         out2, _ := dockerCmd(c, "pull", repoTag2)
103
104         var out2Lines []string
105         for _, outputLine := range strings.Split(out2, "\n") {
106                 if strings.Contains(outputLine, imageAlreadyExists) {
107                         out1Lines = append(out1Lines, outputLine)
108                 }
109         }
110         c.Assert(out2Lines, checker.HasLen, len(out1Lines))
111
112         for i := range out1Lines {
113                 c.Assert(out1Lines[i], checker.Equals, out2Lines[i])
114         }
115 }
116
117 func (s *DockerRegistrySuite) TestPushMultipleTags(c *check.C) {
118         testPushMultipleTags(c)
119 }
120
121 func (s *DockerSchema1RegistrySuite) TestPushMultipleTags(c *check.C) {
122         testPushMultipleTags(c)
123 }
124
125 func testPushEmptyLayer(c *check.C) {
126         repoName := fmt.Sprintf("%v/dockercli/emptylayer", privateRegistryURL)
127         emptyTarball, err := ioutil.TempFile("", "empty_tarball")
128         c.Assert(err, check.IsNil, check.Commentf("Unable to create test file"))
129
130         tw := tar.NewWriter(emptyTarball)
131         err = tw.Close()
132         c.Assert(err, check.IsNil, check.Commentf("Error creating empty tarball"))
133
134         freader, err := os.Open(emptyTarball.Name())
135         c.Assert(err, check.IsNil, check.Commentf("Could not open test tarball"))
136         defer freader.Close()
137
138         icmd.RunCmd(icmd.Cmd{
139                 Command: []string{dockerBinary, "import", "-", repoName},
140                 Stdin:   freader,
141         }).Assert(c, icmd.Success)
142
143         // Now verify we can push it
144         out, _, err := dockerCmdWithError("push", repoName)
145         c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out))
146 }
147
148 func (s *DockerRegistrySuite) TestPushEmptyLayer(c *check.C) {
149         testPushEmptyLayer(c)
150 }
151
152 func (s *DockerSchema1RegistrySuite) TestPushEmptyLayer(c *check.C) {
153         testPushEmptyLayer(c)
154 }
155
156 // testConcurrentPush pushes multiple tags to the same repo
157 // concurrently.
158 func testConcurrentPush(c *check.C) {
159         repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
160
161         repos := []string{}
162         for _, tag := range []string{"push1", "push2", "push3"} {
163                 repo := fmt.Sprintf("%v:%v", repoName, tag)
164                 buildImageSuccessfully(c, repo, build.WithDockerfile(fmt.Sprintf(`
165         FROM busybox
166         ENTRYPOINT ["/bin/echo"]
167         ENV FOO foo
168         ENV BAR bar
169         CMD echo %s
170 `, repo)))
171                 repos = append(repos, repo)
172         }
173
174         // Push tags, in parallel
175         results := make(chan error)
176
177         for _, repo := range repos {
178                 go func(repo string) {
179                         result := icmd.RunCommand(dockerBinary, "push", repo)
180                         results <- result.Error
181                 }(repo)
182         }
183
184         for range repos {
185                 err := <-results
186                 c.Assert(err, checker.IsNil, check.Commentf("concurrent push failed with error: %v", err))
187         }
188
189         // Clear local images store.
190         args := append([]string{"rmi"}, repos...)
191         dockerCmd(c, args...)
192
193         // Re-pull and run individual tags, to make sure pushes succeeded
194         for _, repo := range repos {
195                 dockerCmd(c, "pull", repo)
196                 dockerCmd(c, "inspect", repo)
197                 out, _ := dockerCmd(c, "run", "--rm", repo)
198                 c.Assert(strings.TrimSpace(out), checker.Equals, "/bin/sh -c echo "+repo)
199         }
200 }
201
202 func (s *DockerRegistrySuite) TestConcurrentPush(c *check.C) {
203         testConcurrentPush(c)
204 }
205
206 func (s *DockerSchema1RegistrySuite) TestConcurrentPush(c *check.C) {
207         testConcurrentPush(c)
208 }
209
210 func (s *DockerRegistrySuite) TestCrossRepositoryLayerPush(c *check.C) {
211         sourceRepoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
212         // tag the image to upload it to the private registry
213         dockerCmd(c, "tag", "busybox", sourceRepoName)
214         // push the image to the registry
215         out1, _, err := dockerCmdWithError("push", sourceRepoName)
216         c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out1))
217         // ensure that none of the layers were mounted from another repository during push
218         c.Assert(strings.Contains(out1, "Mounted from"), check.Equals, false)
219
220         digest1 := reference.DigestRegexp.FindString(out1)
221         c.Assert(len(digest1), checker.GreaterThan, 0, check.Commentf("no digest found for pushed manifest"))
222
223         destRepoName := fmt.Sprintf("%v/dockercli/crossrepopush", privateRegistryURL)
224         // retag the image to upload the same layers to another repo in the same registry
225         dockerCmd(c, "tag", "busybox", destRepoName)
226         // push the image to the registry
227         out2, _, err := dockerCmdWithError("push", destRepoName)
228         c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out2))
229         // ensure that layers were mounted from the first repo during push
230         c.Assert(strings.Contains(out2, "Mounted from dockercli/busybox"), check.Equals, true)
231
232         digest2 := reference.DigestRegexp.FindString(out2)
233         c.Assert(len(digest2), checker.GreaterThan, 0, check.Commentf("no digest found for pushed manifest"))
234         c.Assert(digest1, check.Equals, digest2)
235
236         // ensure that pushing again produces the same digest
237         out3, _, err := dockerCmdWithError("push", destRepoName)
238         c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out2))
239
240         digest3 := reference.DigestRegexp.FindString(out3)
241         c.Assert(len(digest2), checker.GreaterThan, 0, check.Commentf("no digest found for pushed manifest"))
242         c.Assert(digest3, check.Equals, digest2)
243
244         // ensure that we can pull and run the cross-repo-pushed repository
245         dockerCmd(c, "rmi", destRepoName)
246         dockerCmd(c, "pull", destRepoName)
247         out4, _ := dockerCmd(c, "run", destRepoName, "echo", "-n", "hello world")
248         c.Assert(out4, check.Equals, "hello world")
249 }
250
251 func (s *DockerSchema1RegistrySuite) TestCrossRepositoryLayerPushNotSupported(c *check.C) {
252         sourceRepoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
253         // tag the image to upload it to the private registry
254         dockerCmd(c, "tag", "busybox", sourceRepoName)
255         // push the image to the registry
256         out1, _, err := dockerCmdWithError("push", sourceRepoName)
257         c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out1))
258         // ensure that none of the layers were mounted from another repository during push
259         c.Assert(strings.Contains(out1, "Mounted from"), check.Equals, false)
260
261         digest1 := reference.DigestRegexp.FindString(out1)
262         c.Assert(len(digest1), checker.GreaterThan, 0, check.Commentf("no digest found for pushed manifest"))
263
264         destRepoName := fmt.Sprintf("%v/dockercli/crossrepopush", privateRegistryURL)
265         // retag the image to upload the same layers to another repo in the same registry
266         dockerCmd(c, "tag", "busybox", destRepoName)
267         // push the image to the registry
268         out2, _, err := dockerCmdWithError("push", destRepoName)
269         c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out2))
270         // schema1 registry should not support cross-repo layer mounts, so ensure that this does not happen
271         c.Assert(strings.Contains(out2, "Mounted from"), check.Equals, false)
272
273         digest2 := reference.DigestRegexp.FindString(out2)
274         c.Assert(len(digest2), checker.GreaterThan, 0, check.Commentf("no digest found for pushed manifest"))
275         c.Assert(digest1, check.Not(check.Equals), digest2)
276
277         // ensure that we can pull and run the second pushed repository
278         dockerCmd(c, "rmi", destRepoName)
279         dockerCmd(c, "pull", destRepoName)
280         out3, _ := dockerCmd(c, "run", destRepoName, "echo", "-n", "hello world")
281         c.Assert(out3, check.Equals, "hello world")
282 }
283
284 func (s *DockerTrustSuite) TestTrustedPush(c *check.C) {
285         repoName := fmt.Sprintf("%v/dockerclitrusted/pushtest:latest", privateRegistryURL)
286         // tag the image and upload it to the private registry
287         cli.DockerCmd(c, "tag", "busybox", repoName)
288
289         cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing)
290
291         // Try pull after push
292         cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, icmd.Expected{
293                 Out: "Status: Image is up to date",
294         })
295
296         // Assert that we rotated the snapshot key to the server by checking our local keystore
297         contents, err := ioutil.ReadDir(filepath.Join(config.Dir(), "trust/private/tuf_keys", privateRegistryURL, "dockerclitrusted/pushtest"))
298         c.Assert(err, check.IsNil, check.Commentf("Unable to read local tuf key files"))
299         // Check that we only have 1 key (targets key)
300         c.Assert(contents, checker.HasLen, 1)
301 }
302
303 func (s *DockerTrustSuite) TestTrustedPushWithEnvPasswords(c *check.C) {
304         repoName := fmt.Sprintf("%v/dockerclienv/trusted:latest", privateRegistryURL)
305         // tag the image and upload it to the private registry
306         cli.DockerCmd(c, "tag", "busybox", repoName)
307
308         cli.Docker(cli.Args("push", repoName), trustedCmdWithPassphrases("12345678", "12345678")).Assert(c, SuccessSigningAndPushing)
309
310         // Try pull after push
311         cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, icmd.Expected{
312                 Out: "Status: Image is up to date",
313         })
314 }
315
316 func (s *DockerTrustSuite) TestTrustedPushWithFailingServer(c *check.C) {
317         repoName := fmt.Sprintf("%v/dockerclitrusted/failingserver:latest", privateRegistryURL)
318         // tag the image and upload it to the private registry
319         cli.DockerCmd(c, "tag", "busybox", repoName)
320
321         // Using a name that doesn't resolve to an address makes this test faster
322         cli.Docker(cli.Args("push", repoName), trustedCmdWithServer("https://server.invalid:81/")).Assert(c, icmd.Expected{
323                 ExitCode: 1,
324                 Err:      "error contacting notary server",
325         })
326 }
327
328 func (s *DockerTrustSuite) TestTrustedPushWithoutServerAndUntrusted(c *check.C) {
329         repoName := fmt.Sprintf("%v/dockerclitrusted/trustedandnot:latest", privateRegistryURL)
330         // tag the image and upload it to the private registry
331         cli.DockerCmd(c, "tag", "busybox", repoName)
332
333         result := cli.Docker(cli.Args("push", "--disable-content-trust", repoName), trustedCmdWithServer("https://server.invalid:81/"))
334         result.Assert(c, icmd.Success)
335         c.Assert(result.Combined(), check.Not(checker.Contains), "Error establishing connection to notary repository", check.Commentf("Missing expected output on trusted push with --disable-content-trust:"))
336 }
337
338 func (s *DockerTrustSuite) TestTrustedPushWithExistingTag(c *check.C) {
339         repoName := fmt.Sprintf("%v/dockerclitag/trusted:latest", privateRegistryURL)
340         // tag the image and upload it to the private registry
341         cli.DockerCmd(c, "tag", "busybox", repoName)
342         cli.DockerCmd(c, "push", repoName)
343
344         cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing)
345
346         // Try pull after push
347         cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, icmd.Expected{
348                 Out: "Status: Image is up to date",
349         })
350 }
351
352 func (s *DockerTrustSuite) TestTrustedPushWithExistingSignedTag(c *check.C) {
353         repoName := fmt.Sprintf("%v/dockerclipushpush/trusted:latest", privateRegistryURL)
354         // tag the image and upload it to the private registry
355         cli.DockerCmd(c, "tag", "busybox", repoName)
356
357         // Do a trusted push
358         cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing)
359
360         // Do another trusted push
361         cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing)
362         cli.DockerCmd(c, "rmi", repoName)
363
364         // Try pull to ensure the double push did not break our ability to pull
365         cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, SuccessDownloaded)
366 }
367
368 func (s *DockerTrustSuite) TestTrustedPushWithIncorrectPassphraseForNonRoot(c *check.C) {
369         repoName := fmt.Sprintf("%v/dockercliincorretpwd/trusted:latest", privateRegistryURL)
370         // tag the image and upload it to the private registry
371         cli.DockerCmd(c, "tag", "busybox", repoName)
372
373         // Push with default passphrases
374         cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing)
375
376         // Push with wrong passphrases
377         cli.Docker(cli.Args("push", repoName), trustedCmdWithPassphrases("12345678", "87654321")).Assert(c, icmd.Expected{
378                 ExitCode: 1,
379                 Err:      "could not find necessary signing keys",
380         })
381 }
382
383 func (s *DockerTrustSuite) TestTrustedPushWithReleasesDelegationOnly(c *check.C) {
384         testRequires(c, NotaryHosting)
385         repoName := fmt.Sprintf("%v/dockerclireleasedelegationinitfirst/trusted", privateRegistryURL)
386         targetName := fmt.Sprintf("%s:latest", repoName)
387         s.notaryInitRepo(c, repoName)
388         s.notaryCreateDelegation(c, repoName, "targets/releases", s.not.keys[0].Public)
389         s.notaryPublish(c, repoName)
390
391         s.notaryImportKey(c, repoName, "targets/releases", s.not.keys[0].Private)
392
393         // tag the image and upload it to the private registry
394         cli.DockerCmd(c, "tag", "busybox", targetName)
395
396         cli.Docker(cli.Args("push", targetName), trustedCmd).Assert(c, SuccessSigningAndPushing)
397         // check to make sure that the target has been added to targets/releases and not targets
398         s.assertTargetInRoles(c, repoName, "latest", "targets/releases")
399         s.assertTargetNotInRoles(c, repoName, "latest", "targets")
400
401         // Try pull after push
402         os.RemoveAll(filepath.Join(config.Dir(), "trust"))
403
404         cli.Docker(cli.Args("pull", targetName), trustedCmd).Assert(c, icmd.Expected{
405                 Out: "Status: Image is up to date",
406         })
407 }
408
409 func (s *DockerTrustSuite) TestTrustedPushSignsAllFirstLevelRolesWeHaveKeysFor(c *check.C) {
410         testRequires(c, NotaryHosting)
411         repoName := fmt.Sprintf("%v/dockerclimanyroles/trusted", privateRegistryURL)
412         targetName := fmt.Sprintf("%s:latest", repoName)
413         s.notaryInitRepo(c, repoName)
414         s.notaryCreateDelegation(c, repoName, "targets/role1", s.not.keys[0].Public)
415         s.notaryCreateDelegation(c, repoName, "targets/role2", s.not.keys[1].Public)
416         s.notaryCreateDelegation(c, repoName, "targets/role3", s.not.keys[2].Public)
417
418         // import everything except the third key
419         s.notaryImportKey(c, repoName, "targets/role1", s.not.keys[0].Private)
420         s.notaryImportKey(c, repoName, "targets/role2", s.not.keys[1].Private)
421
422         s.notaryCreateDelegation(c, repoName, "targets/role1/subrole", s.not.keys[3].Public)
423         s.notaryImportKey(c, repoName, "targets/role1/subrole", s.not.keys[3].Private)
424
425         s.notaryPublish(c, repoName)
426
427         // tag the image and upload it to the private registry
428         cli.DockerCmd(c, "tag", "busybox", targetName)
429
430         cli.Docker(cli.Args("push", targetName), trustedCmd).Assert(c, SuccessSigningAndPushing)
431
432         // check to make sure that the target has been added to targets/role1 and targets/role2, and
433         // not targets (because there are delegations) or targets/role3 (due to missing key) or
434         // targets/role1/subrole (due to it being a second level delegation)
435         s.assertTargetInRoles(c, repoName, "latest", "targets/role1", "targets/role2")
436         s.assertTargetNotInRoles(c, repoName, "latest", "targets")
437
438         // Try pull after push
439         os.RemoveAll(filepath.Join(config.Dir(), "trust"))
440
441         // pull should fail because none of these are the releases role
442         cli.Docker(cli.Args("pull", targetName), trustedCmd).Assert(c, icmd.Expected{
443                 ExitCode: 1,
444         })
445 }
446
447 func (s *DockerTrustSuite) TestTrustedPushSignsForRolesWithKeysAndValidPaths(c *check.C) {
448         repoName := fmt.Sprintf("%v/dockerclirolesbykeysandpaths/trusted", privateRegistryURL)
449         targetName := fmt.Sprintf("%s:latest", repoName)
450         s.notaryInitRepo(c, repoName)
451         s.notaryCreateDelegation(c, repoName, "targets/role1", s.not.keys[0].Public, "l", "z")
452         s.notaryCreateDelegation(c, repoName, "targets/role2", s.not.keys[1].Public, "x", "y")
453         s.notaryCreateDelegation(c, repoName, "targets/role3", s.not.keys[2].Public, "latest")
454         s.notaryCreateDelegation(c, repoName, "targets/role4", s.not.keys[3].Public, "latest")
455
456         // import everything except the third key
457         s.notaryImportKey(c, repoName, "targets/role1", s.not.keys[0].Private)
458         s.notaryImportKey(c, repoName, "targets/role2", s.not.keys[1].Private)
459         s.notaryImportKey(c, repoName, "targets/role4", s.not.keys[3].Private)
460
461         s.notaryPublish(c, repoName)
462
463         // tag the image and upload it to the private registry
464         cli.DockerCmd(c, "tag", "busybox", targetName)
465
466         cli.Docker(cli.Args("push", targetName), trustedCmd).Assert(c, SuccessSigningAndPushing)
467
468         // check to make sure that the target has been added to targets/role1 and targets/role4, and
469         // not targets (because there are delegations) or targets/role2 (due to path restrictions) or
470         // targets/role3 (due to missing key)
471         s.assertTargetInRoles(c, repoName, "latest", "targets/role1", "targets/role4")
472         s.assertTargetNotInRoles(c, repoName, "latest", "targets")
473
474         // Try pull after push
475         os.RemoveAll(filepath.Join(config.Dir(), "trust"))
476
477         // pull should fail because none of these are the releases role
478         cli.Docker(cli.Args("pull", targetName), trustedCmd).Assert(c, icmd.Expected{
479                 ExitCode: 1,
480         })
481 }
482
483 func (s *DockerTrustSuite) TestTrustedPushDoesntSignTargetsIfDelegationsExist(c *check.C) {
484         testRequires(c, NotaryHosting)
485         repoName := fmt.Sprintf("%v/dockerclireleasedelegationnotsignable/trusted", privateRegistryURL)
486         targetName := fmt.Sprintf("%s:latest", repoName)
487         s.notaryInitRepo(c, repoName)
488         s.notaryCreateDelegation(c, repoName, "targets/role1", s.not.keys[0].Public)
489         s.notaryPublish(c, repoName)
490
491         // do not import any delegations key
492
493         // tag the image and upload it to the private registry
494         cli.DockerCmd(c, "tag", "busybox", targetName)
495
496         cli.Docker(cli.Args("push", targetName), trustedCmd).Assert(c, icmd.Expected{
497                 ExitCode: 1,
498                 Err:      "no valid signing keys",
499         })
500         s.assertTargetNotInRoles(c, repoName, "latest", "targets", "targets/role1")
501 }
502
503 func (s *DockerRegistryAuthHtpasswdSuite) TestPushNoCredentialsNoRetry(c *check.C) {
504         repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
505         dockerCmd(c, "tag", "busybox", repoName)
506         out, _, err := dockerCmdWithError("push", repoName)
507         c.Assert(err, check.NotNil, check.Commentf(out))
508         c.Assert(out, check.Not(checker.Contains), "Retrying")
509         c.Assert(out, checker.Contains, "no basic auth credentials")
510 }
511
512 // This may be flaky but it's needed not to regress on unauthorized push, see #21054
513 func (s *DockerSuite) TestPushToCentralRegistryUnauthorized(c *check.C) {
514         testRequires(c, Network)
515         repoName := "test/busybox"
516         dockerCmd(c, "tag", "busybox", repoName)
517         out, _, err := dockerCmdWithError("push", repoName)
518         c.Assert(err, check.NotNil, check.Commentf(out))
519         c.Assert(out, check.Not(checker.Contains), "Retrying")
520 }
521
522 func getTestTokenService(status int, body string, retries int) *httptest.Server {
523         var mu sync.Mutex
524         return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
525                 mu.Lock()
526                 if retries > 0 {
527                         w.WriteHeader(http.StatusServiceUnavailable)
528                         w.Header().Set("Content-Type", "application/json")
529                         w.Write([]byte(`{"errors":[{"code":"UNAVAILABLE","message":"cannot create token at this time"}]}`))
530                         retries--
531                 } else {
532                         w.WriteHeader(status)
533                         w.Header().Set("Content-Type", "application/json")
534                         w.Write([]byte(body))
535                 }
536                 mu.Unlock()
537         }))
538 }
539
540 func (s *DockerRegistryAuthTokenSuite) TestPushTokenServiceUnauthResponse(c *check.C) {
541         ts := getTestTokenService(http.StatusUnauthorized, `{"errors": [{"Code":"UNAUTHORIZED", "message": "a message", "detail": null}]}`, 0)
542         defer ts.Close()
543         s.setupRegistryWithTokenService(c, ts.URL)
544         repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
545         dockerCmd(c, "tag", "busybox", repoName)
546         out, _, err := dockerCmdWithError("push", repoName)
547         c.Assert(err, check.NotNil, check.Commentf(out))
548         c.Assert(out, checker.Not(checker.Contains), "Retrying")
549         c.Assert(out, checker.Contains, "unauthorized: a message")
550 }
551
552 func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseUnauthorized(c *check.C) {
553         ts := getTestTokenService(http.StatusUnauthorized, `{"error": "unauthorized"}`, 0)
554         defer ts.Close()
555         s.setupRegistryWithTokenService(c, ts.URL)
556         repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
557         dockerCmd(c, "tag", "busybox", repoName)
558         out, _, err := dockerCmdWithError("push", repoName)
559         c.Assert(err, check.NotNil, check.Commentf(out))
560         c.Assert(out, checker.Not(checker.Contains), "Retrying")
561         split := strings.Split(out, "\n")
562         c.Assert(split[len(split)-2], check.Equals, "unauthorized: authentication required")
563 }
564
565 func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseError(c *check.C) {
566         ts := getTestTokenService(http.StatusTooManyRequests, `{"errors": [{"code":"TOOMANYREQUESTS","message":"out of tokens"}]}`, 3)
567         defer ts.Close()
568         s.setupRegistryWithTokenService(c, ts.URL)
569         repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
570         dockerCmd(c, "tag", "busybox", repoName)
571         out, _, err := dockerCmdWithError("push", repoName)
572         c.Assert(err, check.NotNil, check.Commentf(out))
573         // TODO: isolate test so that it can be guaranteed that the 503 will trigger xfer retries
574         //c.Assert(out, checker.Contains, "Retrying")
575         //c.Assert(out, checker.Not(checker.Contains), "Retrying in 15")
576         split := strings.Split(out, "\n")
577         c.Assert(split[len(split)-2], check.Equals, "toomanyrequests: out of tokens")
578 }
579
580 func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseUnparsable(c *check.C) {
581         ts := getTestTokenService(http.StatusForbidden, `no way`, 0)
582         defer ts.Close()
583         s.setupRegistryWithTokenService(c, ts.URL)
584         repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
585         dockerCmd(c, "tag", "busybox", repoName)
586         out, _, err := dockerCmdWithError("push", repoName)
587         c.Assert(err, check.NotNil, check.Commentf(out))
588         c.Assert(out, checker.Not(checker.Contains), "Retrying")
589         split := strings.Split(out, "\n")
590         c.Assert(split[len(split)-2], checker.Contains, "error parsing HTTP 403 response body: ")
591 }
592
593 func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseNoToken(c *check.C) {
594         ts := getTestTokenService(http.StatusOK, `{"something": "wrong"}`, 0)
595         defer ts.Close()
596         s.setupRegistryWithTokenService(c, ts.URL)
597         repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
598         dockerCmd(c, "tag", "busybox", repoName)
599         out, _, err := dockerCmdWithError("push", repoName)
600         c.Assert(err, check.NotNil, check.Commentf(out))
601         c.Assert(out, checker.Not(checker.Contains), "Retrying")
602         split := strings.Split(out, "\n")
603         c.Assert(split[len(split)-2], check.Equals, "authorization server did not include a token in the response")
604 }