github.com/endophage/docker@v1.4.2-0.20161027011718-242853499895/integration-cli/docker_cli_push_test.go (about) 1 package main 2 3 import ( 4 "archive/tar" 5 "fmt" 6 "io/ioutil" 7 "net/http" 8 "net/http/httptest" 9 "os" 10 "os/exec" 11 "path/filepath" 12 "strings" 13 "sync" 14 "time" 15 16 "github.com/docker/distribution/reference" 17 "github.com/docker/docker/cliconfig" 18 "github.com/docker/docker/pkg/integration/checker" 19 "github.com/go-check/check" 20 ) 21 22 // Pushing an image to a private registry. 23 func testPushBusyboxImage(c *check.C) { 24 repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) 25 // tag the image to upload it to the private registry 26 dockerCmd(c, "tag", "busybox", repoName) 27 // push the image to the registry 28 dockerCmd(c, "push", repoName) 29 } 30 31 func (s *DockerRegistrySuite) TestPushBusyboxImage(c *check.C) { 32 testPushBusyboxImage(c) 33 } 34 35 func (s *DockerSchema1RegistrySuite) TestPushBusyboxImage(c *check.C) { 36 testPushBusyboxImage(c) 37 } 38 39 // pushing an image without a prefix should throw an error 40 func (s *DockerSuite) TestPushUnprefixedRepo(c *check.C) { 41 out, _, err := dockerCmdWithError("push", "busybox") 42 c.Assert(err, check.NotNil, check.Commentf("pushing an unprefixed repo didn't result in a non-zero exit status: %s", out)) 43 } 44 45 func testPushUntagged(c *check.C) { 46 repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) 47 expected := "An image does not exist locally with the tag" 48 49 out, _, err := dockerCmdWithError("push", repoName) 50 c.Assert(err, check.NotNil, check.Commentf("pushing the image to the private registry should have failed: output %q", out)) 51 c.Assert(out, checker.Contains, expected, check.Commentf("pushing the image failed")) 52 } 53 54 func (s *DockerRegistrySuite) TestPushUntagged(c *check.C) { 55 testPushUntagged(c) 56 } 57 58 func (s *DockerSchema1RegistrySuite) TestPushUntagged(c *check.C) { 59 testPushUntagged(c) 60 } 61 62 func testPushBadTag(c *check.C) { 63 repoName := fmt.Sprintf("%v/dockercli/busybox:latest", privateRegistryURL) 64 expected := "does not exist" 65 66 out, _, err := dockerCmdWithError("push", repoName) 67 c.Assert(err, check.NotNil, check.Commentf("pushing the image to the private registry should have failed: output %q", out)) 68 c.Assert(out, checker.Contains, expected, check.Commentf("pushing the image failed")) 69 } 70 71 func (s *DockerRegistrySuite) TestPushBadTag(c *check.C) { 72 testPushBadTag(c) 73 } 74 75 func (s *DockerSchema1RegistrySuite) TestPushBadTag(c *check.C) { 76 testPushBadTag(c) 77 } 78 79 func testPushMultipleTags(c *check.C) { 80 repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) 81 repoTag1 := fmt.Sprintf("%v/dockercli/busybox:t1", privateRegistryURL) 82 repoTag2 := fmt.Sprintf("%v/dockercli/busybox:t2", privateRegistryURL) 83 // tag the image and upload it to the private registry 84 dockerCmd(c, "tag", "busybox", repoTag1) 85 86 dockerCmd(c, "tag", "busybox", repoTag2) 87 88 dockerCmd(c, "push", repoName) 89 90 // Ensure layer list is equivalent for repoTag1 and repoTag2 91 out1, _ := dockerCmd(c, "pull", repoTag1) 92 93 imageAlreadyExists := ": Image already exists" 94 var out1Lines []string 95 for _, outputLine := range strings.Split(out1, "\n") { 96 if strings.Contains(outputLine, imageAlreadyExists) { 97 out1Lines = append(out1Lines, outputLine) 98 } 99 } 100 101 out2, _ := dockerCmd(c, "pull", repoTag2) 102 103 var out2Lines []string 104 for _, outputLine := range strings.Split(out2, "\n") { 105 if strings.Contains(outputLine, imageAlreadyExists) { 106 out1Lines = append(out1Lines, outputLine) 107 } 108 } 109 c.Assert(out2Lines, checker.HasLen, len(out1Lines)) 110 111 for i := range out1Lines { 112 c.Assert(out1Lines[i], checker.Equals, out2Lines[i]) 113 } 114 } 115 116 func (s *DockerRegistrySuite) TestPushMultipleTags(c *check.C) { 117 testPushMultipleTags(c) 118 } 119 120 func (s *DockerSchema1RegistrySuite) TestPushMultipleTags(c *check.C) { 121 testPushMultipleTags(c) 122 } 123 124 func testPushEmptyLayer(c *check.C) { 125 repoName := fmt.Sprintf("%v/dockercli/emptylayer", privateRegistryURL) 126 emptyTarball, err := ioutil.TempFile("", "empty_tarball") 127 c.Assert(err, check.IsNil, check.Commentf("Unable to create test file")) 128 129 tw := tar.NewWriter(emptyTarball) 130 err = tw.Close() 131 c.Assert(err, check.IsNil, check.Commentf("Error creating empty tarball")) 132 133 freader, err := os.Open(emptyTarball.Name()) 134 c.Assert(err, check.IsNil, check.Commentf("Could not open test tarball")) 135 defer freader.Close() 136 137 importCmd := exec.Command(dockerBinary, "import", "-", repoName) 138 importCmd.Stdin = freader 139 out, _, err := runCommandWithOutput(importCmd) 140 c.Assert(err, check.IsNil, check.Commentf("import failed: %q", out)) 141 142 // Now verify we can push it 143 out, _, err = dockerCmdWithError("push", repoName) 144 c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out)) 145 } 146 147 func (s *DockerRegistrySuite) TestPushEmptyLayer(c *check.C) { 148 testPushEmptyLayer(c) 149 } 150 151 func (s *DockerSchema1RegistrySuite) TestPushEmptyLayer(c *check.C) { 152 testPushEmptyLayer(c) 153 } 154 155 // testConcurrentPush pushes multiple tags to the same repo 156 // concurrently. 157 func testConcurrentPush(c *check.C) { 158 repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) 159 160 repos := []string{} 161 for _, tag := range []string{"push1", "push2", "push3"} { 162 repo := fmt.Sprintf("%v:%v", repoName, tag) 163 _, err := buildImage(repo, fmt.Sprintf(` 164 FROM busybox 165 ENTRYPOINT ["/bin/echo"] 166 ENV FOO foo 167 ENV BAR bar 168 CMD echo %s 169 `, repo), true) 170 c.Assert(err, checker.IsNil) 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 _, _, err := runCommandWithOutput(exec.Command(dockerBinary, "push", repo)) 180 results <- err 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 dockerCmd(c, "tag", "busybox", repoName) 288 289 pushCmd := exec.Command(dockerBinary, "push", repoName) 290 s.trustedCmd(pushCmd) 291 out, _, err := runCommandWithOutput(pushCmd) 292 c.Assert(err, check.IsNil, check.Commentf("Error running trusted push: %s\n%s", err, out)) 293 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push")) 294 295 // Try pull after push 296 pullCmd := exec.Command(dockerBinary, "pull", repoName) 297 s.trustedCmd(pullCmd) 298 out, _, err = runCommandWithOutput(pullCmd) 299 c.Assert(err, check.IsNil, check.Commentf(out)) 300 c.Assert(string(out), checker.Contains, "Status: Image is up to date", check.Commentf(out)) 301 302 // Assert that we rotated the snapshot key to the server by checking our local keystore 303 contents, err := ioutil.ReadDir(filepath.Join(cliconfig.ConfigDir(), "trust/private/tuf_keys", privateRegistryURL, "dockerclitrusted/pushtest")) 304 c.Assert(err, check.IsNil, check.Commentf("Unable to read local tuf key files")) 305 // Check that we only have 1 key (targets key) 306 c.Assert(contents, checker.HasLen, 1) 307 } 308 309 func (s *DockerTrustSuite) TestTrustedPushWithEnvPasswords(c *check.C) { 310 repoName := fmt.Sprintf("%v/dockerclienv/trusted:latest", privateRegistryURL) 311 // tag the image and upload it to the private registry 312 dockerCmd(c, "tag", "busybox", repoName) 313 314 pushCmd := exec.Command(dockerBinary, "push", repoName) 315 s.trustedCmdWithPassphrases(pushCmd, "12345678", "12345678") 316 out, _, err := runCommandWithOutput(pushCmd) 317 c.Assert(err, check.IsNil, check.Commentf("Error running trusted push: %s\n%s", err, out)) 318 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push")) 319 320 // Try pull after push 321 pullCmd := exec.Command(dockerBinary, "pull", repoName) 322 s.trustedCmd(pullCmd) 323 out, _, err = runCommandWithOutput(pullCmd) 324 c.Assert(err, check.IsNil, check.Commentf(out)) 325 c.Assert(string(out), checker.Contains, "Status: Image is up to date", check.Commentf(out)) 326 } 327 328 func (s *DockerTrustSuite) TestTrustedPushWithFailingServer(c *check.C) { 329 repoName := fmt.Sprintf("%v/dockerclitrusted/failingserver:latest", privateRegistryURL) 330 // tag the image and upload it to the private registry 331 dockerCmd(c, "tag", "busybox", repoName) 332 333 pushCmd := exec.Command(dockerBinary, "push", repoName) 334 // Using a name that doesn't resolve to an address makes this test faster 335 s.trustedCmdWithServer(pushCmd, "https://server.invalid:81/") 336 out, _, err := runCommandWithOutput(pushCmd) 337 c.Assert(err, check.NotNil, check.Commentf("Missing error while running trusted push w/ no server")) 338 c.Assert(out, checker.Contains, "error contacting notary server", check.Commentf("Missing expected output on trusted push")) 339 } 340 341 func (s *DockerTrustSuite) TestTrustedPushWithoutServerAndUntrusted(c *check.C) { 342 repoName := fmt.Sprintf("%v/dockerclitrusted/trustedandnot:latest", privateRegistryURL) 343 // tag the image and upload it to the private registry 344 dockerCmd(c, "tag", "busybox", repoName) 345 346 pushCmd := exec.Command(dockerBinary, "push", "--disable-content-trust", repoName) 347 // Using a name that doesn't resolve to an address makes this test faster 348 s.trustedCmdWithServer(pushCmd, "https://server.invalid") 349 out, _, err := runCommandWithOutput(pushCmd) 350 c.Assert(err, check.IsNil, check.Commentf("trusted push with no server and --disable-content-trust failed: %s\n%s", err, out)) 351 c.Assert(out, check.Not(checker.Contains), "Error establishing connection to notary repository", check.Commentf("Missing expected output on trusted push with --disable-content-trust:")) 352 } 353 354 func (s *DockerTrustSuite) TestTrustedPushWithExistingTag(c *check.C) { 355 repoName := fmt.Sprintf("%v/dockerclitag/trusted:latest", privateRegistryURL) 356 // tag the image and upload it to the private registry 357 dockerCmd(c, "tag", "busybox", repoName) 358 dockerCmd(c, "push", repoName) 359 360 pushCmd := exec.Command(dockerBinary, "push", repoName) 361 s.trustedCmd(pushCmd) 362 out, _, err := runCommandWithOutput(pushCmd) 363 c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out)) 364 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with existing tag")) 365 366 // Try pull after push 367 pullCmd := exec.Command(dockerBinary, "pull", repoName) 368 s.trustedCmd(pullCmd) 369 out, _, err = runCommandWithOutput(pullCmd) 370 c.Assert(err, check.IsNil, check.Commentf(out)) 371 c.Assert(string(out), checker.Contains, "Status: Image is up to date", check.Commentf(out)) 372 } 373 374 func (s *DockerTrustSuite) TestTrustedPushWithExistingSignedTag(c *check.C) { 375 repoName := fmt.Sprintf("%v/dockerclipushpush/trusted:latest", privateRegistryURL) 376 // tag the image and upload it to the private registry 377 dockerCmd(c, "tag", "busybox", repoName) 378 379 // Do a trusted push 380 pushCmd := exec.Command(dockerBinary, "push", repoName) 381 s.trustedCmd(pushCmd) 382 out, _, err := runCommandWithOutput(pushCmd) 383 c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out)) 384 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with existing tag")) 385 386 // Do another trusted push 387 pushCmd = exec.Command(dockerBinary, "push", repoName) 388 s.trustedCmd(pushCmd) 389 out, _, err = runCommandWithOutput(pushCmd) 390 c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out)) 391 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with existing tag")) 392 393 dockerCmd(c, "rmi", repoName) 394 395 // Try pull to ensure the double push did not break our ability to pull 396 pullCmd := exec.Command(dockerBinary, "pull", repoName) 397 s.trustedCmd(pullCmd) 398 out, _, err = runCommandWithOutput(pullCmd) 399 c.Assert(err, check.IsNil, check.Commentf("Error running trusted pull: %s\n%s", err, out)) 400 c.Assert(out, checker.Contains, "Status: Downloaded", check.Commentf("Missing expected output on trusted pull with --disable-content-trust")) 401 402 } 403 404 func (s *DockerTrustSuite) TestTrustedPushWithIncorrectPassphraseForNonRoot(c *check.C) { 405 repoName := fmt.Sprintf("%v/dockercliincorretpwd/trusted:latest", privateRegistryURL) 406 // tag the image and upload it to the private registry 407 dockerCmd(c, "tag", "busybox", repoName) 408 409 // Push with default passphrases 410 pushCmd := exec.Command(dockerBinary, "push", repoName) 411 s.trustedCmd(pushCmd) 412 out, _, err := runCommandWithOutput(pushCmd) 413 c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out)) 414 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push:\n%s", out)) 415 416 // Push with wrong passphrases 417 pushCmd = exec.Command(dockerBinary, "push", repoName) 418 s.trustedCmdWithPassphrases(pushCmd, "12345678", "87654321") 419 out, _, err = runCommandWithOutput(pushCmd) 420 c.Assert(err, check.NotNil, check.Commentf("Error missing from trusted push with short targets passphrase: \n%s", out)) 421 c.Assert(out, checker.Contains, "could not find necessary signing keys", check.Commentf("Missing expected output on trusted push with short targets/snapsnot passphrase")) 422 } 423 424 func (s *DockerTrustSuite) TestTrustedPushWithExpiredSnapshot(c *check.C) { 425 c.Skip("Currently changes system time, causing instability") 426 repoName := fmt.Sprintf("%v/dockercliexpiredsnapshot/trusted:latest", privateRegistryURL) 427 // tag the image and upload it to the private registry 428 dockerCmd(c, "tag", "busybox", repoName) 429 430 // Push with default passphrases 431 pushCmd := exec.Command(dockerBinary, "push", repoName) 432 s.trustedCmd(pushCmd) 433 out, _, err := runCommandWithOutput(pushCmd) 434 c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out)) 435 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push")) 436 437 // Snapshots last for three years. This should be expired 438 fourYearsLater := time.Now().Add(time.Hour * 24 * 365 * 4) 439 440 runAtDifferentDate(fourYearsLater, func() { 441 // Push with wrong passphrases 442 pushCmd = exec.Command(dockerBinary, "push", repoName) 443 s.trustedCmd(pushCmd) 444 out, _, err = runCommandWithOutput(pushCmd) 445 c.Assert(err, check.NotNil, check.Commentf("Error missing from trusted push with expired snapshot: \n%s", out)) 446 c.Assert(out, checker.Contains, "repository out-of-date", check.Commentf("Missing expected output on trusted push with expired snapshot")) 447 }) 448 } 449 450 func (s *DockerTrustSuite) TestTrustedPushWithExpiredTimestamp(c *check.C) { 451 c.Skip("Currently changes system time, causing instability") 452 repoName := fmt.Sprintf("%v/dockercliexpiredtimestamppush/trusted:latest", privateRegistryURL) 453 // tag the image and upload it to the private registry 454 dockerCmd(c, "tag", "busybox", repoName) 455 456 // Push with default passphrases 457 pushCmd := exec.Command(dockerBinary, "push", repoName) 458 s.trustedCmd(pushCmd) 459 out, _, err := runCommandWithOutput(pushCmd) 460 c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out)) 461 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push")) 462 463 // The timestamps expire in two weeks. Lets check three 464 threeWeeksLater := time.Now().Add(time.Hour * 24 * 21) 465 466 // Should succeed because the server transparently re-signs one 467 runAtDifferentDate(threeWeeksLater, func() { 468 pushCmd := exec.Command(dockerBinary, "push", repoName) 469 s.trustedCmd(pushCmd) 470 out, _, err := runCommandWithOutput(pushCmd) 471 c.Assert(err, check.IsNil, check.Commentf("Error running trusted push: %s\n%s", err, out)) 472 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with expired timestamp")) 473 }) 474 } 475 476 func (s *DockerTrustSuite) TestTrustedPushWithReleasesDelegationOnly(c *check.C) { 477 testRequires(c, NotaryHosting) 478 repoName := fmt.Sprintf("%v/dockerclireleasedelegationinitfirst/trusted", privateRegistryURL) 479 targetName := fmt.Sprintf("%s:latest", repoName) 480 s.notaryInitRepo(c, repoName) 481 s.notaryCreateDelegation(c, repoName, "targets/releases", s.not.keys[0].Public) 482 s.notaryPublish(c, repoName) 483 484 s.notaryImportKey(c, repoName, "targets/releases", s.not.keys[0].Private) 485 486 // tag the image and upload it to the private registry 487 dockerCmd(c, "tag", "busybox", targetName) 488 489 pushCmd := exec.Command(dockerBinary, "push", targetName) 490 s.trustedCmd(pushCmd) 491 out, _, err := runCommandWithOutput(pushCmd) 492 c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out)) 493 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with existing tag")) 494 // check to make sure that the target has been added to targets/releases and not targets 495 s.assertTargetInRoles(c, repoName, "latest", "targets/releases") 496 s.assertTargetNotInRoles(c, repoName, "latest", "targets") 497 498 // Try pull after push 499 os.RemoveAll(filepath.Join(cliconfig.ConfigDir(), "trust")) 500 501 pullCmd := exec.Command(dockerBinary, "pull", targetName) 502 s.trustedCmd(pullCmd) 503 out, _, err = runCommandWithOutput(pullCmd) 504 c.Assert(err, check.IsNil, check.Commentf(out)) 505 c.Assert(string(out), checker.Contains, "Status: Image is up to date", check.Commentf(out)) 506 } 507 508 func (s *DockerTrustSuite) TestTrustedPushSignsAllFirstLevelRolesWeHaveKeysFor(c *check.C) { 509 testRequires(c, NotaryHosting) 510 repoName := fmt.Sprintf("%v/dockerclimanyroles/trusted", privateRegistryURL) 511 targetName := fmt.Sprintf("%s:latest", repoName) 512 s.notaryInitRepo(c, repoName) 513 s.notaryCreateDelegation(c, repoName, "targets/role1", s.not.keys[0].Public) 514 s.notaryCreateDelegation(c, repoName, "targets/role2", s.not.keys[1].Public) 515 s.notaryCreateDelegation(c, repoName, "targets/role3", s.not.keys[2].Public) 516 517 // import everything except the third key 518 s.notaryImportKey(c, repoName, "targets/role1", s.not.keys[0].Private) 519 s.notaryImportKey(c, repoName, "targets/role2", s.not.keys[1].Private) 520 521 s.notaryCreateDelegation(c, repoName, "targets/role1/subrole", s.not.keys[3].Public) 522 s.notaryImportKey(c, repoName, "targets/role1/subrole", s.not.keys[3].Private) 523 524 s.notaryPublish(c, repoName) 525 526 // tag the image and upload it to the private registry 527 dockerCmd(c, "tag", "busybox", targetName) 528 529 pushCmd := exec.Command(dockerBinary, "push", targetName) 530 s.trustedCmd(pushCmd) 531 out, _, err := runCommandWithOutput(pushCmd) 532 c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out)) 533 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with existing tag")) 534 535 // check to make sure that the target has been added to targets/role1 and targets/role2, and 536 // not targets (because there are delegations) or targets/role3 (due to missing key) or 537 // targets/role1/subrole (due to it being a second level delegation) 538 s.assertTargetInRoles(c, repoName, "latest", "targets/role1", "targets/role2") 539 s.assertTargetNotInRoles(c, repoName, "latest", "targets") 540 541 // Try pull after push 542 os.RemoveAll(filepath.Join(cliconfig.ConfigDir(), "trust")) 543 544 // pull should fail because none of these are the releases role 545 pullCmd := exec.Command(dockerBinary, "pull", targetName) 546 s.trustedCmd(pullCmd) 547 out, _, err = runCommandWithOutput(pullCmd) 548 c.Assert(err, check.NotNil, check.Commentf(out)) 549 } 550 551 func (s *DockerTrustSuite) TestTrustedPushSignsForRolesWithKeysAndValidPaths(c *check.C) { 552 repoName := fmt.Sprintf("%v/dockerclirolesbykeysandpaths/trusted", privateRegistryURL) 553 targetName := fmt.Sprintf("%s:latest", repoName) 554 s.notaryInitRepo(c, repoName) 555 s.notaryCreateDelegation(c, repoName, "targets/role1", s.not.keys[0].Public, "l", "z") 556 s.notaryCreateDelegation(c, repoName, "targets/role2", s.not.keys[1].Public, "x", "y") 557 s.notaryCreateDelegation(c, repoName, "targets/role3", s.not.keys[2].Public, "latest") 558 s.notaryCreateDelegation(c, repoName, "targets/role4", s.not.keys[3].Public, "latest") 559 560 // import everything except the third key 561 s.notaryImportKey(c, repoName, "targets/role1", s.not.keys[0].Private) 562 s.notaryImportKey(c, repoName, "targets/role2", s.not.keys[1].Private) 563 s.notaryImportKey(c, repoName, "targets/role4", s.not.keys[3].Private) 564 565 s.notaryPublish(c, repoName) 566 567 // tag the image and upload it to the private registry 568 dockerCmd(c, "tag", "busybox", targetName) 569 570 pushCmd := exec.Command(dockerBinary, "push", targetName) 571 s.trustedCmd(pushCmd) 572 out, _, err := runCommandWithOutput(pushCmd) 573 c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out)) 574 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with existing tag")) 575 576 // check to make sure that the target has been added to targets/role1 and targets/role4, and 577 // not targets (because there are delegations) or targets/role2 (due to path restrictions) or 578 // targets/role3 (due to missing key) 579 s.assertTargetInRoles(c, repoName, "latest", "targets/role1", "targets/role4") 580 s.assertTargetNotInRoles(c, repoName, "latest", "targets") 581 582 // Try pull after push 583 os.RemoveAll(filepath.Join(cliconfig.ConfigDir(), "trust")) 584 585 // pull should fail because none of these are the releases role 586 pullCmd := exec.Command(dockerBinary, "pull", targetName) 587 s.trustedCmd(pullCmd) 588 out, _, err = runCommandWithOutput(pullCmd) 589 c.Assert(err, check.NotNil, check.Commentf(out)) 590 } 591 592 func (s *DockerTrustSuite) TestTrustedPushDoesntSignTargetsIfDelegationsExist(c *check.C) { 593 testRequires(c, NotaryHosting) 594 repoName := fmt.Sprintf("%v/dockerclireleasedelegationnotsignable/trusted", privateRegistryURL) 595 targetName := fmt.Sprintf("%s:latest", repoName) 596 s.notaryInitRepo(c, repoName) 597 s.notaryCreateDelegation(c, repoName, "targets/role1", s.not.keys[0].Public) 598 s.notaryPublish(c, repoName) 599 600 // do not import any delegations key 601 602 // tag the image and upload it to the private registry 603 dockerCmd(c, "tag", "busybox", targetName) 604 605 pushCmd := exec.Command(dockerBinary, "push", targetName) 606 s.trustedCmd(pushCmd) 607 out, _, err := runCommandWithOutput(pushCmd) 608 c.Assert(err, check.NotNil, check.Commentf("trusted push succeeded but should have failed:\n%s", out)) 609 c.Assert(out, checker.Contains, "no valid signing keys", 610 check.Commentf("Missing expected output on trusted push without keys")) 611 612 s.assertTargetNotInRoles(c, repoName, "latest", "targets", "targets/role1") 613 } 614 615 func (s *DockerRegistryAuthHtpasswdSuite) TestPushNoCredentialsNoRetry(c *check.C) { 616 repoName := fmt.Sprintf("%s/busybox", privateRegistryURL) 617 dockerCmd(c, "tag", "busybox", repoName) 618 out, _, err := dockerCmdWithError("push", repoName) 619 c.Assert(err, check.NotNil, check.Commentf(out)) 620 c.Assert(out, check.Not(checker.Contains), "Retrying") 621 c.Assert(out, checker.Contains, "no basic auth credentials") 622 } 623 624 // This may be flaky but it's needed not to regress on unauthorized push, see #21054 625 func (s *DockerSuite) TestPushToCentralRegistryUnauthorized(c *check.C) { 626 testRequires(c, Network) 627 repoName := "test/busybox" 628 dockerCmd(c, "tag", "busybox", repoName) 629 out, _, err := dockerCmdWithError("push", repoName) 630 c.Assert(err, check.NotNil, check.Commentf(out)) 631 c.Assert(out, check.Not(checker.Contains), "Retrying") 632 } 633 634 func getTestTokenService(status int, body string, retries int) *httptest.Server { 635 var mu sync.Mutex 636 return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 637 mu.Lock() 638 if retries > 0 { 639 w.WriteHeader(http.StatusServiceUnavailable) 640 w.Header().Set("Content-Type", "application/json") 641 w.Write([]byte(`{"errors":[{"code":"UNAVAILABLE","message":"cannot create token at this time"}]}`)) 642 retries-- 643 } else { 644 w.WriteHeader(status) 645 w.Header().Set("Content-Type", "application/json") 646 w.Write([]byte(body)) 647 } 648 mu.Unlock() 649 })) 650 } 651 652 func (s *DockerRegistryAuthTokenSuite) TestPushTokenServiceUnauthResponse(c *check.C) { 653 ts := getTestTokenService(http.StatusUnauthorized, `{"errors": [{"Code":"UNAUTHORIZED", "message": "a message", "detail": null}]}`, 0) 654 defer ts.Close() 655 s.setupRegistryWithTokenService(c, ts.URL) 656 repoName := fmt.Sprintf("%s/busybox", privateRegistryURL) 657 dockerCmd(c, "tag", "busybox", repoName) 658 out, _, err := dockerCmdWithError("push", repoName) 659 c.Assert(err, check.NotNil, check.Commentf(out)) 660 c.Assert(out, checker.Not(checker.Contains), "Retrying") 661 c.Assert(out, checker.Contains, "unauthorized: a message") 662 } 663 664 func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseUnauthorized(c *check.C) { 665 ts := getTestTokenService(http.StatusUnauthorized, `{"error": "unauthorized"}`, 0) 666 defer ts.Close() 667 s.setupRegistryWithTokenService(c, ts.URL) 668 repoName := fmt.Sprintf("%s/busybox", privateRegistryURL) 669 dockerCmd(c, "tag", "busybox", repoName) 670 out, _, err := dockerCmdWithError("push", repoName) 671 c.Assert(err, check.NotNil, check.Commentf(out)) 672 c.Assert(out, checker.Not(checker.Contains), "Retrying") 673 split := strings.Split(out, "\n") 674 c.Assert(split[len(split)-2], check.Equals, "unauthorized: authentication required") 675 } 676 677 func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseError(c *check.C) { 678 ts := getTestTokenService(http.StatusTooManyRequests, `{"errors": [{"code":"TOOMANYREQUESTS","message":"out of tokens"}]}`, 4) 679 defer ts.Close() 680 s.setupRegistryWithTokenService(c, ts.URL) 681 repoName := fmt.Sprintf("%s/busybox", privateRegistryURL) 682 dockerCmd(c, "tag", "busybox", repoName) 683 out, _, err := dockerCmdWithError("push", repoName) 684 c.Assert(err, check.NotNil, check.Commentf(out)) 685 c.Assert(out, checker.Contains, "Retrying") 686 c.Assert(out, checker.Not(checker.Contains), "Retrying in 15") 687 split := strings.Split(out, "\n") 688 c.Assert(split[len(split)-2], check.Equals, "toomanyrequests: out of tokens") 689 } 690 691 func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseUnparsable(c *check.C) { 692 ts := getTestTokenService(http.StatusForbidden, `no way`, 0) 693 defer ts.Close() 694 s.setupRegistryWithTokenService(c, ts.URL) 695 repoName := fmt.Sprintf("%s/busybox", privateRegistryURL) 696 dockerCmd(c, "tag", "busybox", repoName) 697 out, _, err := dockerCmdWithError("push", repoName) 698 c.Assert(err, check.NotNil, check.Commentf(out)) 699 c.Assert(out, checker.Not(checker.Contains), "Retrying") 700 split := strings.Split(out, "\n") 701 c.Assert(split[len(split)-2], checker.Contains, "error parsing HTTP 403 response body: ") 702 } 703 704 func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseNoToken(c *check.C) { 705 ts := getTestTokenService(http.StatusOK, `{"something": "wrong"}`, 0) 706 defer ts.Close() 707 s.setupRegistryWithTokenService(c, ts.URL) 708 repoName := fmt.Sprintf("%s/busybox", privateRegistryURL) 709 dockerCmd(c, "tag", "busybox", repoName) 710 out, _, err := dockerCmdWithError("push", repoName) 711 c.Assert(err, check.NotNil, check.Commentf(out)) 712 c.Assert(out, checker.Not(checker.Contains), "Retrying") 713 split := strings.Split(out, "\n") 714 c.Assert(split[len(split)-2], check.Equals, "authorization server did not include a token in the response") 715 }