github.com/rita33cool1/iot-system-gateway@v0.0.0-20200911033302-e65bde238cc5/docker-engine/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 "strings" 11 "sync" 12 13 "github.com/docker/distribution/reference" 14 "github.com/docker/docker/integration-cli/checker" 15 "github.com/docker/docker/integration-cli/cli/build" 16 "github.com/go-check/check" 17 "github.com/gotestyourself/gotestyourself/icmd" 18 ) 19 20 // Pushing an image to a private registry. 21 func testPushBusyboxImage(c *check.C) { 22 repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) 23 // tag the image to upload it to the private registry 24 dockerCmd(c, "tag", "busybox", repoName) 25 // push the image to the registry 26 dockerCmd(c, "push", repoName) 27 } 28 29 func (s *DockerRegistrySuite) TestPushBusyboxImage(c *check.C) { 30 testPushBusyboxImage(c) 31 } 32 33 func (s *DockerSchema1RegistrySuite) TestPushBusyboxImage(c *check.C) { 34 testPushBusyboxImage(c) 35 } 36 37 // pushing an image without a prefix should throw an error 38 func (s *DockerSuite) TestPushUnprefixedRepo(c *check.C) { 39 out, _, err := dockerCmdWithError("push", "busybox") 40 c.Assert(err, check.NotNil, check.Commentf("pushing an unprefixed repo didn't result in a non-zero exit status: %s", out)) 41 } 42 43 func testPushUntagged(c *check.C) { 44 repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) 45 expected := "An image does not exist locally with the tag" 46 47 out, _, err := dockerCmdWithError("push", repoName) 48 c.Assert(err, check.NotNil, check.Commentf("pushing the image to the private registry should have failed: output %q", out)) 49 c.Assert(out, checker.Contains, expected, check.Commentf("pushing the image failed")) 50 } 51 52 func (s *DockerRegistrySuite) TestPushUntagged(c *check.C) { 53 testPushUntagged(c) 54 } 55 56 func (s *DockerSchema1RegistrySuite) TestPushUntagged(c *check.C) { 57 testPushUntagged(c) 58 } 59 60 func testPushBadTag(c *check.C) { 61 repoName := fmt.Sprintf("%v/dockercli/busybox:latest", privateRegistryURL) 62 expected := "does not exist" 63 64 out, _, err := dockerCmdWithError("push", repoName) 65 c.Assert(err, check.NotNil, check.Commentf("pushing the image to the private registry should have failed: output %q", out)) 66 c.Assert(out, checker.Contains, expected, check.Commentf("pushing the image failed")) 67 } 68 69 func (s *DockerRegistrySuite) TestPushBadTag(c *check.C) { 70 testPushBadTag(c) 71 } 72 73 func (s *DockerSchema1RegistrySuite) TestPushBadTag(c *check.C) { 74 testPushBadTag(c) 75 } 76 77 func testPushMultipleTags(c *check.C) { 78 repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) 79 repoTag1 := fmt.Sprintf("%v/dockercli/busybox:t1", privateRegistryURL) 80 repoTag2 := fmt.Sprintf("%v/dockercli/busybox:t2", privateRegistryURL) 81 // tag the image and upload it to the private registry 82 dockerCmd(c, "tag", "busybox", repoTag1) 83 84 dockerCmd(c, "tag", "busybox", repoTag2) 85 86 dockerCmd(c, "push", repoName) 87 88 // Ensure layer list is equivalent for repoTag1 and repoTag2 89 out1, _ := dockerCmd(c, "pull", repoTag1) 90 91 imageAlreadyExists := ": Image already exists" 92 var out1Lines []string 93 for _, outputLine := range strings.Split(out1, "\n") { 94 if strings.Contains(outputLine, imageAlreadyExists) { 95 out1Lines = append(out1Lines, outputLine) 96 } 97 } 98 99 out2, _ := dockerCmd(c, "pull", repoTag2) 100 101 var out2Lines []string 102 for _, outputLine := range strings.Split(out2, "\n") { 103 if strings.Contains(outputLine, imageAlreadyExists) { 104 out1Lines = append(out1Lines, outputLine) 105 } 106 } 107 c.Assert(out2Lines, checker.HasLen, len(out1Lines)) 108 109 for i := range out1Lines { 110 c.Assert(out1Lines[i], checker.Equals, out2Lines[i]) 111 } 112 } 113 114 func (s *DockerRegistrySuite) TestPushMultipleTags(c *check.C) { 115 testPushMultipleTags(c) 116 } 117 118 func (s *DockerSchema1RegistrySuite) TestPushMultipleTags(c *check.C) { 119 testPushMultipleTags(c) 120 } 121 122 func testPushEmptyLayer(c *check.C) { 123 repoName := fmt.Sprintf("%v/dockercli/emptylayer", privateRegistryURL) 124 emptyTarball, err := ioutil.TempFile("", "empty_tarball") 125 c.Assert(err, check.IsNil, check.Commentf("Unable to create test file")) 126 127 tw := tar.NewWriter(emptyTarball) 128 err = tw.Close() 129 c.Assert(err, check.IsNil, check.Commentf("Error creating empty tarball")) 130 131 freader, err := os.Open(emptyTarball.Name()) 132 c.Assert(err, check.IsNil, check.Commentf("Could not open test tarball")) 133 defer freader.Close() 134 135 icmd.RunCmd(icmd.Cmd{ 136 Command: []string{dockerBinary, "import", "-", repoName}, 137 Stdin: freader, 138 }).Assert(c, icmd.Success) 139 140 // Now verify we can push it 141 out, _, err := dockerCmdWithError("push", repoName) 142 c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out)) 143 } 144 145 func (s *DockerRegistrySuite) TestPushEmptyLayer(c *check.C) { 146 testPushEmptyLayer(c) 147 } 148 149 func (s *DockerSchema1RegistrySuite) TestPushEmptyLayer(c *check.C) { 150 testPushEmptyLayer(c) 151 } 152 153 // testConcurrentPush pushes multiple tags to the same repo 154 // concurrently. 155 func testConcurrentPush(c *check.C) { 156 repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) 157 158 repos := []string{} 159 for _, tag := range []string{"push1", "push2", "push3"} { 160 repo := fmt.Sprintf("%v:%v", repoName, tag) 161 buildImageSuccessfully(c, repo, build.WithDockerfile(fmt.Sprintf(` 162 FROM busybox 163 ENTRYPOINT ["/bin/echo"] 164 ENV FOO foo 165 ENV BAR bar 166 CMD echo %s 167 `, repo))) 168 repos = append(repos, repo) 169 } 170 171 // Push tags, in parallel 172 results := make(chan error) 173 174 for _, repo := range repos { 175 go func(repo string) { 176 result := icmd.RunCommand(dockerBinary, "push", repo) 177 results <- result.Error 178 }(repo) 179 } 180 181 for range repos { 182 err := <-results 183 c.Assert(err, checker.IsNil, check.Commentf("concurrent push failed with error: %v", err)) 184 } 185 186 // Clear local images store. 187 args := append([]string{"rmi"}, repos...) 188 dockerCmd(c, args...) 189 190 // Re-pull and run individual tags, to make sure pushes succeeded 191 for _, repo := range repos { 192 dockerCmd(c, "pull", repo) 193 dockerCmd(c, "inspect", repo) 194 out, _ := dockerCmd(c, "run", "--rm", repo) 195 c.Assert(strings.TrimSpace(out), checker.Equals, "/bin/sh -c echo "+repo) 196 } 197 } 198 199 func (s *DockerRegistrySuite) TestConcurrentPush(c *check.C) { 200 testConcurrentPush(c) 201 } 202 203 func (s *DockerSchema1RegistrySuite) TestConcurrentPush(c *check.C) { 204 testConcurrentPush(c) 205 } 206 207 func (s *DockerRegistrySuite) TestCrossRepositoryLayerPush(c *check.C) { 208 sourceRepoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) 209 // tag the image to upload it to the private registry 210 dockerCmd(c, "tag", "busybox", sourceRepoName) 211 // push the image to the registry 212 out1, _, err := dockerCmdWithError("push", sourceRepoName) 213 c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out1)) 214 // ensure that none of the layers were mounted from another repository during push 215 c.Assert(strings.Contains(out1, "Mounted from"), check.Equals, false) 216 217 digest1 := reference.DigestRegexp.FindString(out1) 218 c.Assert(len(digest1), checker.GreaterThan, 0, check.Commentf("no digest found for pushed manifest")) 219 220 destRepoName := fmt.Sprintf("%v/dockercli/crossrepopush", privateRegistryURL) 221 // retag the image to upload the same layers to another repo in the same registry 222 dockerCmd(c, "tag", "busybox", destRepoName) 223 // push the image to the registry 224 out2, _, err := dockerCmdWithError("push", destRepoName) 225 c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out2)) 226 // ensure that layers were mounted from the first repo during push 227 c.Assert(strings.Contains(out2, "Mounted from dockercli/busybox"), check.Equals, true) 228 229 digest2 := reference.DigestRegexp.FindString(out2) 230 c.Assert(len(digest2), checker.GreaterThan, 0, check.Commentf("no digest found for pushed manifest")) 231 c.Assert(digest1, check.Equals, digest2) 232 233 // ensure that pushing again produces the same digest 234 out3, _, err := dockerCmdWithError("push", destRepoName) 235 c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out2)) 236 237 digest3 := reference.DigestRegexp.FindString(out3) 238 c.Assert(len(digest2), checker.GreaterThan, 0, check.Commentf("no digest found for pushed manifest")) 239 c.Assert(digest3, check.Equals, digest2) 240 241 // ensure that we can pull and run the cross-repo-pushed repository 242 dockerCmd(c, "rmi", destRepoName) 243 dockerCmd(c, "pull", destRepoName) 244 out4, _ := dockerCmd(c, "run", destRepoName, "echo", "-n", "hello world") 245 c.Assert(out4, check.Equals, "hello world") 246 } 247 248 func (s *DockerSchema1RegistrySuite) TestCrossRepositoryLayerPushNotSupported(c *check.C) { 249 sourceRepoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) 250 // tag the image to upload it to the private registry 251 dockerCmd(c, "tag", "busybox", sourceRepoName) 252 // push the image to the registry 253 out1, _, err := dockerCmdWithError("push", sourceRepoName) 254 c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out1)) 255 // ensure that none of the layers were mounted from another repository during push 256 c.Assert(strings.Contains(out1, "Mounted from"), check.Equals, false) 257 258 digest1 := reference.DigestRegexp.FindString(out1) 259 c.Assert(len(digest1), checker.GreaterThan, 0, check.Commentf("no digest found for pushed manifest")) 260 261 destRepoName := fmt.Sprintf("%v/dockercli/crossrepopush", privateRegistryURL) 262 // retag the image to upload the same layers to another repo in the same registry 263 dockerCmd(c, "tag", "busybox", destRepoName) 264 // push the image to the registry 265 out2, _, err := dockerCmdWithError("push", destRepoName) 266 c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out2)) 267 // schema1 registry should not support cross-repo layer mounts, so ensure that this does not happen 268 c.Assert(strings.Contains(out2, "Mounted from"), check.Equals, false) 269 270 digest2 := reference.DigestRegexp.FindString(out2) 271 c.Assert(len(digest2), checker.GreaterThan, 0, check.Commentf("no digest found for pushed manifest")) 272 c.Assert(digest1, check.Not(check.Equals), digest2) 273 274 // ensure that we can pull and run the second pushed repository 275 dockerCmd(c, "rmi", destRepoName) 276 dockerCmd(c, "pull", destRepoName) 277 out3, _ := dockerCmd(c, "run", destRepoName, "echo", "-n", "hello world") 278 c.Assert(out3, check.Equals, "hello world") 279 } 280 281 func (s *DockerRegistryAuthHtpasswdSuite) TestPushNoCredentialsNoRetry(c *check.C) { 282 repoName := fmt.Sprintf("%s/busybox", privateRegistryURL) 283 dockerCmd(c, "tag", "busybox", repoName) 284 out, _, err := dockerCmdWithError("push", repoName) 285 c.Assert(err, check.NotNil, check.Commentf(out)) 286 c.Assert(out, check.Not(checker.Contains), "Retrying") 287 c.Assert(out, checker.Contains, "no basic auth credentials") 288 } 289 290 // This may be flaky but it's needed not to regress on unauthorized push, see #21054 291 func (s *DockerSuite) TestPushToCentralRegistryUnauthorized(c *check.C) { 292 testRequires(c, Network) 293 repoName := "test/busybox" 294 dockerCmd(c, "tag", "busybox", repoName) 295 out, _, err := dockerCmdWithError("push", repoName) 296 c.Assert(err, check.NotNil, check.Commentf(out)) 297 c.Assert(out, check.Not(checker.Contains), "Retrying") 298 } 299 300 func getTestTokenService(status int, body string, retries int) *httptest.Server { 301 var mu sync.Mutex 302 return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 303 mu.Lock() 304 if retries > 0 { 305 w.WriteHeader(http.StatusServiceUnavailable) 306 w.Header().Set("Content-Type", "application/json") 307 w.Write([]byte(`{"errors":[{"code":"UNAVAILABLE","message":"cannot create token at this time"}]}`)) 308 retries-- 309 } else { 310 w.WriteHeader(status) 311 w.Header().Set("Content-Type", "application/json") 312 w.Write([]byte(body)) 313 } 314 mu.Unlock() 315 })) 316 } 317 318 func (s *DockerRegistryAuthTokenSuite) TestPushTokenServiceUnauthResponse(c *check.C) { 319 ts := getTestTokenService(http.StatusUnauthorized, `{"errors": [{"Code":"UNAUTHORIZED", "message": "a message", "detail": null}]}`, 0) 320 defer ts.Close() 321 s.setupRegistryWithTokenService(c, ts.URL) 322 repoName := fmt.Sprintf("%s/busybox", privateRegistryURL) 323 dockerCmd(c, "tag", "busybox", repoName) 324 out, _, err := dockerCmdWithError("push", repoName) 325 c.Assert(err, check.NotNil, check.Commentf(out)) 326 c.Assert(out, checker.Not(checker.Contains), "Retrying") 327 c.Assert(out, checker.Contains, "unauthorized: a message") 328 } 329 330 func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseUnauthorized(c *check.C) { 331 ts := getTestTokenService(http.StatusUnauthorized, `{"error": "unauthorized"}`, 0) 332 defer ts.Close() 333 s.setupRegistryWithTokenService(c, ts.URL) 334 repoName := fmt.Sprintf("%s/busybox", privateRegistryURL) 335 dockerCmd(c, "tag", "busybox", repoName) 336 out, _, err := dockerCmdWithError("push", repoName) 337 c.Assert(err, check.NotNil, check.Commentf(out)) 338 c.Assert(out, checker.Not(checker.Contains), "Retrying") 339 split := strings.Split(out, "\n") 340 c.Assert(split[len(split)-2], check.Equals, "unauthorized: authentication required") 341 } 342 343 func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseError(c *check.C) { 344 ts := getTestTokenService(http.StatusTooManyRequests, `{"errors": [{"code":"TOOMANYREQUESTS","message":"out of tokens"}]}`, 3) 345 defer ts.Close() 346 s.setupRegistryWithTokenService(c, ts.URL) 347 repoName := fmt.Sprintf("%s/busybox", privateRegistryURL) 348 dockerCmd(c, "tag", "busybox", repoName) 349 out, _, err := dockerCmdWithError("push", repoName) 350 c.Assert(err, check.NotNil, check.Commentf(out)) 351 // TODO: isolate test so that it can be guaranteed that the 503 will trigger xfer retries 352 //c.Assert(out, checker.Contains, "Retrying") 353 //c.Assert(out, checker.Not(checker.Contains), "Retrying in 15") 354 split := strings.Split(out, "\n") 355 c.Assert(split[len(split)-2], check.Equals, "toomanyrequests: out of tokens") 356 } 357 358 func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseUnparsable(c *check.C) { 359 ts := getTestTokenService(http.StatusForbidden, `no way`, 0) 360 defer ts.Close() 361 s.setupRegistryWithTokenService(c, ts.URL) 362 repoName := fmt.Sprintf("%s/busybox", privateRegistryURL) 363 dockerCmd(c, "tag", "busybox", repoName) 364 out, _, err := dockerCmdWithError("push", repoName) 365 c.Assert(err, check.NotNil, check.Commentf(out)) 366 c.Assert(out, checker.Not(checker.Contains), "Retrying") 367 split := strings.Split(out, "\n") 368 c.Assert(split[len(split)-2], checker.Contains, "error parsing HTTP 403 response body: ") 369 } 370 371 func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseNoToken(c *check.C) { 372 ts := getTestTokenService(http.StatusOK, `{"something": "wrong"}`, 0) 373 defer ts.Close() 374 s.setupRegistryWithTokenService(c, ts.URL) 375 repoName := fmt.Sprintf("%s/busybox", privateRegistryURL) 376 dockerCmd(c, "tag", "busybox", repoName) 377 out, _, err := dockerCmdWithError("push", repoName) 378 c.Assert(err, check.NotNil, check.Commentf(out)) 379 c.Assert(out, checker.Not(checker.Contains), "Retrying") 380 split := strings.Split(out, "\n") 381 c.Assert(split[len(split)-2], check.Equals, "authorization server did not include a token in the response") 382 }