github.com/jogo/docker@v1.7.0-rc1/integration-cli/docker_api_containers_test.go (about) 1 package main 2 3 import ( 4 "archive/tar" 5 "bytes" 6 "encoding/json" 7 "io" 8 "net/http" 9 "net/http/httputil" 10 "os" 11 "os/exec" 12 "strings" 13 "time" 14 15 "github.com/docker/docker/api/types" 16 "github.com/docker/docker/pkg/stringid" 17 "github.com/docker/docker/runconfig" 18 "github.com/go-check/check" 19 ) 20 21 func (s *DockerSuite) TestContainerApiGetAll(c *check.C) { 22 startCount, err := getContainerCount() 23 if err != nil { 24 c.Fatalf("Cannot query container count: %v", err) 25 } 26 27 name := "getall" 28 runCmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "true") 29 out, _, err := runCommandWithOutput(runCmd) 30 if err != nil { 31 c.Fatalf("Error on container creation: %v, output: %q", err, out) 32 } 33 34 status, body, err := sockRequest("GET", "/containers/json?all=1", nil) 35 c.Assert(status, check.Equals, http.StatusOK) 36 c.Assert(err, check.IsNil) 37 38 var inspectJSON []struct { 39 Names []string 40 } 41 if err = json.Unmarshal(body, &inspectJSON); err != nil { 42 c.Fatalf("unable to unmarshal response body: %v", err) 43 } 44 45 if len(inspectJSON) != startCount+1 { 46 c.Fatalf("Expected %d container(s), %d found (started with: %d)", startCount+1, len(inspectJSON), startCount) 47 } 48 49 if actual := inspectJSON[0].Names[0]; actual != "/"+name { 50 c.Fatalf("Container Name mismatch. Expected: %q, received: %q\n", "/"+name, actual) 51 } 52 } 53 54 func (s *DockerSuite) TestContainerApiGetExport(c *check.C) { 55 name := "exportcontainer" 56 runCmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "touch", "/test") 57 out, _, err := runCommandWithOutput(runCmd) 58 if err != nil { 59 c.Fatalf("Error on container creation: %v, output: %q", err, out) 60 } 61 62 status, body, err := sockRequest("GET", "/containers/"+name+"/export", nil) 63 c.Assert(status, check.Equals, http.StatusOK) 64 c.Assert(err, check.IsNil) 65 66 found := false 67 for tarReader := tar.NewReader(bytes.NewReader(body)); ; { 68 h, err := tarReader.Next() 69 if err != nil { 70 if err == io.EOF { 71 break 72 } 73 c.Fatal(err) 74 } 75 if h.Name == "test" { 76 found = true 77 break 78 } 79 } 80 81 if !found { 82 c.Fatalf("The created test file has not been found in the exported image") 83 } 84 } 85 86 func (s *DockerSuite) TestContainerApiGetChanges(c *check.C) { 87 name := "changescontainer" 88 runCmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "rm", "/etc/passwd") 89 out, _, err := runCommandWithOutput(runCmd) 90 if err != nil { 91 c.Fatalf("Error on container creation: %v, output: %q", err, out) 92 } 93 94 status, body, err := sockRequest("GET", "/containers/"+name+"/changes", nil) 95 c.Assert(status, check.Equals, http.StatusOK) 96 c.Assert(err, check.IsNil) 97 98 changes := []struct { 99 Kind int 100 Path string 101 }{} 102 if err = json.Unmarshal(body, &changes); err != nil { 103 c.Fatalf("unable to unmarshal response body: %v", err) 104 } 105 106 // Check the changelog for removal of /etc/passwd 107 success := false 108 for _, elem := range changes { 109 if elem.Path == "/etc/passwd" && elem.Kind == 2 { 110 success = true 111 } 112 } 113 if !success { 114 c.Fatalf("/etc/passwd has been removed but is not present in the diff") 115 } 116 } 117 118 func (s *DockerSuite) TestContainerApiStartVolumeBinds(c *check.C) { 119 name := "testing" 120 config := map[string]interface{}{ 121 "Image": "busybox", 122 "Volumes": map[string]struct{}{"/tmp": {}}, 123 } 124 125 status, _, err := sockRequest("POST", "/containers/create?name="+name, config) 126 c.Assert(status, check.Equals, http.StatusCreated) 127 c.Assert(err, check.IsNil) 128 129 bindPath := randomUnixTmpDirPath("test") 130 config = map[string]interface{}{ 131 "Binds": []string{bindPath + ":/tmp"}, 132 } 133 status, _, err = sockRequest("POST", "/containers/"+name+"/start", config) 134 c.Assert(status, check.Equals, http.StatusNoContent) 135 c.Assert(err, check.IsNil) 136 137 pth, err := inspectFieldMap(name, "Volumes", "/tmp") 138 if err != nil { 139 c.Fatal(err) 140 } 141 142 if pth != bindPath { 143 c.Fatalf("expected volume host path to be %s, got %s", bindPath, pth) 144 } 145 } 146 147 // Test for GH#10618 148 func (s *DockerSuite) TestContainerApiStartDupVolumeBinds(c *check.C) { 149 name := "testdups" 150 config := map[string]interface{}{ 151 "Image": "busybox", 152 "Volumes": map[string]struct{}{"/tmp": {}}, 153 } 154 155 status, _, err := sockRequest("POST", "/containers/create?name="+name, config) 156 c.Assert(status, check.Equals, http.StatusCreated) 157 c.Assert(err, check.IsNil) 158 159 bindPath1 := randomUnixTmpDirPath("test1") 160 bindPath2 := randomUnixTmpDirPath("test2") 161 162 config = map[string]interface{}{ 163 "Binds": []string{bindPath1 + ":/tmp", bindPath2 + ":/tmp"}, 164 } 165 status, body, err := sockRequest("POST", "/containers/"+name+"/start", config) 166 c.Assert(status, check.Equals, http.StatusInternalServerError) 167 c.Assert(err, check.IsNil) 168 169 if !strings.Contains(string(body), "Duplicate bind") { 170 c.Fatalf("Expected failure due to duplicate bind mounts to same path, instead got: %q with error: %v", string(body), err) 171 } 172 } 173 174 func (s *DockerSuite) TestContainerApiStartVolumesFrom(c *check.C) { 175 volName := "voltst" 176 volPath := "/tmp" 177 178 if out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--name", volName, "-v", volPath, "busybox")); err != nil { 179 c.Fatal(out, err) 180 } 181 182 name := "TestContainerApiStartDupVolumeBinds" 183 config := map[string]interface{}{ 184 "Image": "busybox", 185 "Volumes": map[string]struct{}{volPath: {}}, 186 } 187 188 status, _, err := sockRequest("POST", "/containers/create?name="+name, config) 189 c.Assert(status, check.Equals, http.StatusCreated) 190 c.Assert(err, check.IsNil) 191 192 config = map[string]interface{}{ 193 "VolumesFrom": []string{volName}, 194 } 195 status, _, err = sockRequest("POST", "/containers/"+name+"/start", config) 196 c.Assert(status, check.Equals, http.StatusNoContent) 197 c.Assert(err, check.IsNil) 198 199 pth, err := inspectFieldMap(name, "Volumes", volPath) 200 if err != nil { 201 c.Fatal(err) 202 } 203 pth2, err := inspectFieldMap(volName, "Volumes", volPath) 204 if err != nil { 205 c.Fatal(err) 206 } 207 208 if pth != pth2 { 209 c.Fatalf("expected volume host path to be %s, got %s", pth, pth2) 210 } 211 } 212 213 func (s *DockerSuite) TestGetContainerStats(c *check.C) { 214 var ( 215 name = "statscontainer" 216 runCmd = exec.Command(dockerBinary, "run", "-d", "--name", name, "busybox", "top") 217 ) 218 out, _, err := runCommandWithOutput(runCmd) 219 if err != nil { 220 c.Fatalf("Error on container creation: %v, output: %q", err, out) 221 } 222 type b struct { 223 status int 224 body []byte 225 err error 226 } 227 bc := make(chan b, 1) 228 go func() { 229 status, body, err := sockRequest("GET", "/containers/"+name+"/stats", nil) 230 bc <- b{status, body, err} 231 }() 232 233 // allow some time to stream the stats from the container 234 time.Sleep(4 * time.Second) 235 if _, err := runCommand(exec.Command(dockerBinary, "rm", "-f", name)); err != nil { 236 c.Fatal(err) 237 } 238 239 // collect the results from the stats stream or timeout and fail 240 // if the stream was not disconnected. 241 select { 242 case <-time.After(2 * time.Second): 243 c.Fatal("stream was not closed after container was removed") 244 case sr := <-bc: 245 c.Assert(sr.err, check.IsNil) 246 c.Assert(sr.status, check.Equals, http.StatusOK) 247 248 dec := json.NewDecoder(bytes.NewBuffer(sr.body)) 249 var s *types.Stats 250 // decode only one object from the stream 251 if err := dec.Decode(&s); err != nil { 252 c.Fatal(err) 253 } 254 } 255 } 256 257 func (s *DockerSuite) TestContainerStatsRmRunning(c *check.C) { 258 out, _ := dockerCmd(c, "run", "-d", "busybox", "top") 259 id := strings.TrimSpace(out) 260 261 buf := &channelBuffer{make(chan []byte, 1)} 262 defer buf.Close() 263 chErr := make(chan error) 264 go func() { 265 _, body, err := sockRequestRaw("GET", "/containers/"+id+"/stats?stream=1", nil, "application/json") 266 if err != nil { 267 chErr <- err 268 } 269 defer body.Close() 270 _, err = io.Copy(buf, body) 271 chErr <- err 272 }() 273 defer func() { 274 c.Assert(<-chErr, check.IsNil) 275 }() 276 277 b := make([]byte, 32) 278 // make sure we've got some stats 279 _, err := buf.ReadTimeout(b, 2*time.Second) 280 c.Assert(err, check.IsNil) 281 282 // Now remove without `-f` and make sure we are still pulling stats 283 _, err = runCommand(exec.Command(dockerBinary, "rm", id)) 284 c.Assert(err, check.Not(check.IsNil), check.Commentf("rm should have failed but didn't")) 285 _, err = buf.ReadTimeout(b, 2*time.Second) 286 c.Assert(err, check.IsNil) 287 dockerCmd(c, "rm", "-f", id) 288 289 _, err = buf.ReadTimeout(b, 2*time.Second) 290 c.Assert(err, check.Not(check.IsNil)) 291 } 292 293 func (s *DockerSuite) TestGetStoppedContainerStats(c *check.C) { 294 // TODO: this test does nothing because we are c.Assert'ing in goroutine 295 var ( 296 name = "statscontainer" 297 runCmd = exec.Command(dockerBinary, "create", "--name", name, "busybox", "top") 298 ) 299 out, _, err := runCommandWithOutput(runCmd) 300 if err != nil { 301 c.Fatalf("Error on container creation: %v, output: %q", err, out) 302 } 303 304 go func() { 305 // We'll never get return for GET stats from sockRequest as of now, 306 // just send request and see if panic or error would happen on daemon side. 307 status, _, err := sockRequest("GET", "/containers/"+name+"/stats", nil) 308 c.Assert(status, check.Equals, http.StatusOK) 309 c.Assert(err, check.IsNil) 310 }() 311 312 // allow some time to send request and let daemon deal with it 313 time.Sleep(1 * time.Second) 314 } 315 316 func (s *DockerSuite) TestBuildApiDockerfilePath(c *check.C) { 317 // Test to make sure we stop people from trying to leave the 318 // build context when specifying the path to the dockerfile 319 buffer := new(bytes.Buffer) 320 tw := tar.NewWriter(buffer) 321 defer tw.Close() 322 323 dockerfile := []byte("FROM busybox") 324 if err := tw.WriteHeader(&tar.Header{ 325 Name: "Dockerfile", 326 Size: int64(len(dockerfile)), 327 }); err != nil { 328 c.Fatalf("failed to write tar file header: %v", err) 329 } 330 if _, err := tw.Write(dockerfile); err != nil { 331 c.Fatalf("failed to write tar file content: %v", err) 332 } 333 if err := tw.Close(); err != nil { 334 c.Fatalf("failed to close tar archive: %v", err) 335 } 336 337 res, body, err := sockRequestRaw("POST", "/build?dockerfile=../Dockerfile", buffer, "application/x-tar") 338 c.Assert(res.StatusCode, check.Equals, http.StatusInternalServerError) 339 c.Assert(err, check.IsNil) 340 341 out, err := readBody(body) 342 if err != nil { 343 c.Fatal(err) 344 } 345 346 if !strings.Contains(string(out), "must be within the build context") { 347 c.Fatalf("Didn't complain about leaving build context: %s", out) 348 } 349 } 350 351 func (s *DockerSuite) TestBuildApiDockerFileRemote(c *check.C) { 352 server, err := fakeStorage(map[string]string{ 353 "testD": `FROM busybox 354 COPY * /tmp/ 355 RUN find / -name ba* 356 RUN find /tmp/`, 357 }) 358 if err != nil { 359 c.Fatal(err) 360 } 361 defer server.Close() 362 363 res, body, err := sockRequestRaw("POST", "/build?dockerfile=baz&remote="+server.URL()+"/testD", nil, "application/json") 364 c.Assert(res.StatusCode, check.Equals, http.StatusOK) 365 c.Assert(err, check.IsNil) 366 367 buf, err := readBody(body) 368 if err != nil { 369 c.Fatal(err) 370 } 371 372 // Make sure Dockerfile exists. 373 // Make sure 'baz' doesn't exist ANYWHERE despite being mentioned in the URL 374 out := string(buf) 375 if !strings.Contains(out, "/tmp/Dockerfile") || 376 strings.Contains(out, "baz") { 377 c.Fatalf("Incorrect output: %s", out) 378 } 379 } 380 381 func (s *DockerSuite) TestBuildApiLowerDockerfile(c *check.C) { 382 git, err := fakeGIT("repo", map[string]string{ 383 "dockerfile": `FROM busybox 384 RUN echo from dockerfile`, 385 }, false) 386 if err != nil { 387 c.Fatal(err) 388 } 389 defer git.Close() 390 391 res, body, err := sockRequestRaw("POST", "/build?remote="+git.RepoURL, nil, "application/json") 392 c.Assert(res.StatusCode, check.Equals, http.StatusOK) 393 c.Assert(err, check.IsNil) 394 395 buf, err := readBody(body) 396 if err != nil { 397 c.Fatal(err) 398 } 399 400 out := string(buf) 401 if !strings.Contains(out, "from dockerfile") { 402 c.Fatalf("Incorrect output: %s", out) 403 } 404 } 405 406 func (s *DockerSuite) TestBuildApiBuildGitWithF(c *check.C) { 407 git, err := fakeGIT("repo", map[string]string{ 408 "baz": `FROM busybox 409 RUN echo from baz`, 410 "Dockerfile": `FROM busybox 411 RUN echo from Dockerfile`, 412 }, false) 413 if err != nil { 414 c.Fatal(err) 415 } 416 defer git.Close() 417 418 // Make sure it tries to 'dockerfile' query param value 419 res, body, err := sockRequestRaw("POST", "/build?dockerfile=baz&remote="+git.RepoURL, nil, "application/json") 420 c.Assert(res.StatusCode, check.Equals, http.StatusOK) 421 c.Assert(err, check.IsNil) 422 423 buf, err := readBody(body) 424 if err != nil { 425 c.Fatal(err) 426 } 427 428 out := string(buf) 429 if !strings.Contains(out, "from baz") { 430 c.Fatalf("Incorrect output: %s", out) 431 } 432 } 433 434 func (s *DockerSuite) TestBuildApiDoubleDockerfile(c *check.C) { 435 testRequires(c, UnixCli) // dockerfile overwrites Dockerfile on Windows 436 git, err := fakeGIT("repo", map[string]string{ 437 "Dockerfile": `FROM busybox 438 RUN echo from Dockerfile`, 439 "dockerfile": `FROM busybox 440 RUN echo from dockerfile`, 441 }, false) 442 if err != nil { 443 c.Fatal(err) 444 } 445 defer git.Close() 446 447 // Make sure it tries to 'dockerfile' query param value 448 res, body, err := sockRequestRaw("POST", "/build?remote="+git.RepoURL, nil, "application/json") 449 c.Assert(res.StatusCode, check.Equals, http.StatusOK) 450 c.Assert(err, check.IsNil) 451 452 buf, err := readBody(body) 453 if err != nil { 454 c.Fatal(err) 455 } 456 457 out := string(buf) 458 if !strings.Contains(out, "from Dockerfile") { 459 c.Fatalf("Incorrect output: %s", out) 460 } 461 } 462 463 func (s *DockerSuite) TestBuildApiDockerfileSymlink(c *check.C) { 464 // Test to make sure we stop people from trying to leave the 465 // build context when specifying a symlink as the path to the dockerfile 466 buffer := new(bytes.Buffer) 467 tw := tar.NewWriter(buffer) 468 defer tw.Close() 469 470 if err := tw.WriteHeader(&tar.Header{ 471 Name: "Dockerfile", 472 Typeflag: tar.TypeSymlink, 473 Linkname: "/etc/passwd", 474 }); err != nil { 475 c.Fatalf("failed to write tar file header: %v", err) 476 } 477 if err := tw.Close(); err != nil { 478 c.Fatalf("failed to close tar archive: %v", err) 479 } 480 481 res, body, err := sockRequestRaw("POST", "/build", buffer, "application/x-tar") 482 c.Assert(res.StatusCode, check.Equals, http.StatusInternalServerError) 483 c.Assert(err, check.IsNil) 484 485 out, err := readBody(body) 486 if err != nil { 487 c.Fatal(err) 488 } 489 490 // The reason the error is "Cannot locate specified Dockerfile" is because 491 // in the builder, the symlink is resolved within the context, therefore 492 // Dockerfile -> /etc/passwd becomes etc/passwd from the context which is 493 // a nonexistent file. 494 if !strings.Contains(string(out), "Cannot locate specified Dockerfile: Dockerfile") { 495 c.Fatalf("Didn't complain about leaving build context: %s", out) 496 } 497 } 498 499 // #9981 - Allow a docker created volume (ie, one in /var/lib/docker/volumes) to be used to overwrite (via passing in Binds on api start) an existing volume 500 func (s *DockerSuite) TestPostContainerBindNormalVolume(c *check.C) { 501 out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "create", "-v", "/foo", "--name=one", "busybox")) 502 if err != nil { 503 c.Fatal(err, out) 504 } 505 506 fooDir, err := inspectFieldMap("one", "Volumes", "/foo") 507 if err != nil { 508 c.Fatal(err) 509 } 510 511 out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "create", "-v", "/foo", "--name=two", "busybox")) 512 if err != nil { 513 c.Fatal(err, out) 514 } 515 516 bindSpec := map[string][]string{"Binds": {fooDir + ":/foo"}} 517 status, _, err := sockRequest("POST", "/containers/two/start", bindSpec) 518 c.Assert(status, check.Equals, http.StatusNoContent) 519 c.Assert(err, check.IsNil) 520 521 fooDir2, err := inspectFieldMap("two", "Volumes", "/foo") 522 if err != nil { 523 c.Fatal(err) 524 } 525 526 if fooDir2 != fooDir { 527 c.Fatalf("expected volume path to be %s, got: %s", fooDir, fooDir2) 528 } 529 } 530 531 func (s *DockerSuite) TestContainerApiPause(c *check.C) { 532 defer unpauseAllContainers() 533 runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "sleep", "30") 534 out, _, err := runCommandWithOutput(runCmd) 535 536 if err != nil { 537 c.Fatalf("failed to create a container: %s, %v", out, err) 538 } 539 ContainerID := strings.TrimSpace(out) 540 541 status, _, err := sockRequest("POST", "/containers/"+ContainerID+"/pause", nil) 542 c.Assert(status, check.Equals, http.StatusNoContent) 543 c.Assert(err, check.IsNil) 544 545 pausedContainers, err := getSliceOfPausedContainers() 546 547 if err != nil { 548 c.Fatalf("error thrown while checking if containers were paused: %v", err) 549 } 550 551 if len(pausedContainers) != 1 || stringid.TruncateID(ContainerID) != pausedContainers[0] { 552 c.Fatalf("there should be one paused container and not %d", len(pausedContainers)) 553 } 554 555 status, _, err = sockRequest("POST", "/containers/"+ContainerID+"/unpause", nil) 556 c.Assert(status, check.Equals, http.StatusNoContent) 557 c.Assert(err, check.IsNil) 558 559 pausedContainers, err = getSliceOfPausedContainers() 560 561 if err != nil { 562 c.Fatalf("error thrown while checking if containers were paused: %v", err) 563 } 564 565 if pausedContainers != nil { 566 c.Fatalf("There should be no paused container.") 567 } 568 } 569 570 func (s *DockerSuite) TestContainerApiTop(c *check.C) { 571 out, err := exec.Command(dockerBinary, "run", "-d", "busybox", "/bin/sh", "-c", "top").CombinedOutput() 572 if err != nil { 573 c.Fatal(err, out) 574 } 575 id := strings.TrimSpace(string(out)) 576 if err := waitRun(id); err != nil { 577 c.Fatal(err) 578 } 579 580 type topResp struct { 581 Titles []string 582 Processes [][]string 583 } 584 var top topResp 585 status, b, err := sockRequest("GET", "/containers/"+id+"/top?ps_args=aux", nil) 586 c.Assert(status, check.Equals, http.StatusOK) 587 c.Assert(err, check.IsNil) 588 589 if err := json.Unmarshal(b, &top); err != nil { 590 c.Fatal(err) 591 } 592 593 if len(top.Titles) != 11 { 594 c.Fatalf("expected 11 titles, found %d: %v", len(top.Titles), top.Titles) 595 } 596 597 if top.Titles[0] != "USER" || top.Titles[10] != "COMMAND" { 598 c.Fatalf("expected `USER` at `Titles[0]` and `COMMAND` at Titles[10]: %v", top.Titles) 599 } 600 if len(top.Processes) != 2 { 601 c.Fatalf("expected 2 processes, found %d: %v", len(top.Processes), top.Processes) 602 } 603 if top.Processes[0][10] != "/bin/sh -c top" { 604 c.Fatalf("expected `/bin/sh -c top`, found: %s", top.Processes[0][10]) 605 } 606 if top.Processes[1][10] != "top" { 607 c.Fatalf("expected `top`, found: %s", top.Processes[1][10]) 608 } 609 } 610 611 func (s *DockerSuite) TestContainerApiCommit(c *check.C) { 612 cName := "testapicommit" 613 out, err := exec.Command(dockerBinary, "run", "--name="+cName, "busybox", "/bin/sh", "-c", "touch /test").CombinedOutput() 614 if err != nil { 615 c.Fatal(err, out) 616 } 617 618 name := "TestContainerApiCommit" 619 status, b, err := sockRequest("POST", "/commit?repo="+name+"&testtag=tag&container="+cName, nil) 620 c.Assert(status, check.Equals, http.StatusCreated) 621 c.Assert(err, check.IsNil) 622 623 type resp struct { 624 Id string 625 } 626 var img resp 627 if err := json.Unmarshal(b, &img); err != nil { 628 c.Fatal(err) 629 } 630 631 cmd, err := inspectField(img.Id, "Config.Cmd") 632 if err != nil { 633 c.Fatal(err) 634 } 635 if cmd != "{[/bin/sh -c touch /test]}" { 636 c.Fatalf("got wrong Cmd from commit: %q", cmd) 637 } 638 // sanity check, make sure the image is what we think it is 639 out, err = exec.Command(dockerBinary, "run", img.Id, "ls", "/test").CombinedOutput() 640 if err != nil { 641 c.Fatalf("error checking committed image: %v - %q", err, string(out)) 642 } 643 } 644 645 func (s *DockerSuite) TestContainerApiCreate(c *check.C) { 646 config := map[string]interface{}{ 647 "Image": "busybox", 648 "Cmd": []string{"/bin/sh", "-c", "touch /test && ls /test"}, 649 } 650 651 status, b, err := sockRequest("POST", "/containers/create", config) 652 c.Assert(status, check.Equals, http.StatusCreated) 653 c.Assert(err, check.IsNil) 654 655 type createResp struct { 656 Id string 657 } 658 var container createResp 659 if err := json.Unmarshal(b, &container); err != nil { 660 c.Fatal(err) 661 } 662 663 out, err := exec.Command(dockerBinary, "start", "-a", container.Id).CombinedOutput() 664 if err != nil { 665 c.Fatal(out, err) 666 } 667 if strings.TrimSpace(string(out)) != "/test" { 668 c.Fatalf("expected output `/test`, got %q", out) 669 } 670 } 671 672 func (s *DockerSuite) TestContainerApiCreateWithHostName(c *check.C) { 673 hostName := "test-host" 674 config := map[string]interface{}{ 675 "Image": "busybox", 676 "Hostname": hostName, 677 } 678 679 status, body, err := sockRequest("POST", "/containers/create", config) 680 c.Assert(err, check.IsNil) 681 c.Assert(status, check.Equals, http.StatusCreated) 682 683 var container types.ContainerCreateResponse 684 if err := json.Unmarshal(body, &container); err != nil { 685 c.Fatal(err) 686 } 687 688 status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil) 689 c.Assert(err, check.IsNil) 690 c.Assert(status, check.Equals, http.StatusOK) 691 692 var containerJSON types.ContainerJSON 693 if err := json.Unmarshal(body, &containerJSON); err != nil { 694 c.Fatal(err) 695 } 696 697 if containerJSON.Config.Hostname != hostName { 698 c.Fatalf("Mismatched Hostname, Expected %s, Actual: %s ", hostName, containerJSON.Config.Hostname) 699 } 700 } 701 702 func (s *DockerSuite) TestContainerApiCreateWithDomainName(c *check.C) { 703 domainName := "test-domain" 704 config := map[string]interface{}{ 705 "Image": "busybox", 706 "Domainname": domainName, 707 } 708 709 status, body, err := sockRequest("POST", "/containers/create", config) 710 c.Assert(err, check.IsNil) 711 c.Assert(status, check.Equals, http.StatusCreated) 712 713 var container types.ContainerCreateResponse 714 if err := json.Unmarshal(body, &container); err != nil { 715 c.Fatal(err) 716 } 717 718 status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil) 719 c.Assert(err, check.IsNil) 720 c.Assert(status, check.Equals, http.StatusOK) 721 722 var containerJSON types.ContainerJSON 723 if err := json.Unmarshal(body, &containerJSON); err != nil { 724 c.Fatal(err) 725 } 726 727 if containerJSON.Config.Domainname != domainName { 728 c.Fatalf("Mismatched Domainname, Expected %s, Actual: %s ", domainName, containerJSON.Config.Domainname) 729 } 730 } 731 732 func (s *DockerSuite) TestContainerApiCreateNetworkMode(c *check.C) { 733 UtilCreateNetworkMode(c, "host") 734 UtilCreateNetworkMode(c, "bridge") 735 UtilCreateNetworkMode(c, "container:web1") 736 } 737 738 func UtilCreateNetworkMode(c *check.C, networkMode string) { 739 config := map[string]interface{}{ 740 "Image": "busybox", 741 "HostConfig": map[string]interface{}{"NetworkMode": networkMode}, 742 } 743 744 status, body, err := sockRequest("POST", "/containers/create", config) 745 c.Assert(err, check.IsNil) 746 c.Assert(status, check.Equals, http.StatusCreated) 747 748 var container types.ContainerCreateResponse 749 if err := json.Unmarshal(body, &container); err != nil { 750 c.Fatal(err) 751 } 752 753 status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil) 754 c.Assert(err, check.IsNil) 755 c.Assert(status, check.Equals, http.StatusOK) 756 757 var containerJSON types.ContainerJSON 758 if err := json.Unmarshal(body, &containerJSON); err != nil { 759 c.Fatal(err) 760 } 761 762 if containerJSON.HostConfig.NetworkMode != runconfig.NetworkMode(networkMode) { 763 c.Fatalf("Mismatched NetworkMode, Expected %s, Actual: %s ", networkMode, containerJSON.HostConfig.NetworkMode) 764 } 765 } 766 767 func (s *DockerSuite) TestContainerApiCreateWithCpuSharesCpuset(c *check.C) { 768 config := map[string]interface{}{ 769 "Image": "busybox", 770 "CpuShares": 512, 771 "CpusetCpus": "0,1", 772 } 773 774 status, body, err := sockRequest("POST", "/containers/create", config) 775 c.Assert(err, check.IsNil) 776 c.Assert(status, check.Equals, http.StatusCreated) 777 778 var container types.ContainerCreateResponse 779 if err := json.Unmarshal(body, &container); err != nil { 780 c.Fatal(err) 781 } 782 783 status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil) 784 c.Assert(err, check.IsNil) 785 c.Assert(status, check.Equals, http.StatusOK) 786 787 var containerJson types.ContainerJSON 788 789 c.Assert(json.Unmarshal(body, &containerJson), check.IsNil) 790 791 out, err := inspectField(containerJson.Id, "HostConfig.CpuShares") 792 c.Assert(err, check.IsNil) 793 c.Assert(out, check.Equals, "512") 794 795 outCpuset, errCpuset := inspectField(containerJson.Id, "HostConfig.CpusetCpus") 796 c.Assert(errCpuset, check.IsNil, check.Commentf("Output: %s", outCpuset)) 797 c.Assert(outCpuset, check.Equals, "0,1") 798 } 799 800 func (s *DockerSuite) TestContainerApiVerifyHeader(c *check.C) { 801 config := map[string]interface{}{ 802 "Image": "busybox", 803 } 804 805 create := func(ct string) (*http.Response, io.ReadCloser, error) { 806 jsonData := bytes.NewBuffer(nil) 807 if err := json.NewEncoder(jsonData).Encode(config); err != nil { 808 c.Fatal(err) 809 } 810 return sockRequestRaw("POST", "/containers/create", jsonData, ct) 811 } 812 813 // Try with no content-type 814 res, body, err := create("") 815 c.Assert(err, check.IsNil) 816 c.Assert(res.StatusCode, check.Equals, http.StatusInternalServerError) 817 body.Close() 818 819 // Try with wrong content-type 820 res, body, err = create("application/xml") 821 c.Assert(err, check.IsNil) 822 c.Assert(res.StatusCode, check.Equals, http.StatusInternalServerError) 823 body.Close() 824 825 // now application/json 826 res, body, err = create("application/json") 827 c.Assert(err, check.IsNil) 828 c.Assert(res.StatusCode, check.Equals, http.StatusCreated) 829 body.Close() 830 } 831 832 // Issue 7941 - test to make sure a "null" in JSON is just ignored. 833 // W/o this fix a null in JSON would be parsed into a string var as "null" 834 func (s *DockerSuite) TestContainerApiPostCreateNull(c *check.C) { 835 config := `{ 836 "Hostname":"", 837 "Domainname":"", 838 "Memory":0, 839 "MemorySwap":0, 840 "CpuShares":0, 841 "Cpuset":null, 842 "AttachStdin":true, 843 "AttachStdout":true, 844 "AttachStderr":true, 845 "PortSpecs":null, 846 "ExposedPorts":{}, 847 "Tty":true, 848 "OpenStdin":true, 849 "StdinOnce":true, 850 "Env":[], 851 "Cmd":"ls", 852 "Image":"busybox", 853 "Volumes":{}, 854 "WorkingDir":"", 855 "Entrypoint":null, 856 "NetworkDisabled":false, 857 "OnBuild":null}` 858 859 res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json") 860 c.Assert(res.StatusCode, check.Equals, http.StatusCreated) 861 c.Assert(err, check.IsNil) 862 863 b, err := readBody(body) 864 if err != nil { 865 c.Fatal(err) 866 } 867 type createResp struct { 868 Id string 869 } 870 var container createResp 871 if err := json.Unmarshal(b, &container); err != nil { 872 c.Fatal(err) 873 } 874 875 out, err := inspectField(container.Id, "HostConfig.CpusetCpus") 876 if err != nil { 877 c.Fatal(err, out) 878 } 879 if out != "" { 880 c.Fatalf("expected empty string, got %q", out) 881 } 882 883 outMemory, errMemory := inspectField(container.Id, "HostConfig.Memory") 884 c.Assert(outMemory, check.Equals, "0") 885 if errMemory != nil { 886 c.Fatal(errMemory, outMemory) 887 } 888 outMemorySwap, errMemorySwap := inspectField(container.Id, "HostConfig.MemorySwap") 889 c.Assert(outMemorySwap, check.Equals, "0") 890 if errMemorySwap != nil { 891 c.Fatal(errMemorySwap, outMemorySwap) 892 } 893 } 894 895 func (s *DockerSuite) TestCreateWithTooLowMemoryLimit(c *check.C) { 896 config := `{ 897 "Image": "busybox", 898 "Cmd": "ls", 899 "OpenStdin": true, 900 "CpuShares": 100, 901 "Memory": 524287 902 }` 903 904 res, body, _ := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json") 905 b, err2 := readBody(body) 906 if err2 != nil { 907 c.Fatal(err2) 908 } 909 910 c.Assert(res.StatusCode, check.Equals, http.StatusInternalServerError) 911 c.Assert(strings.Contains(string(b), "Minimum memory limit allowed is 4MB"), check.Equals, true) 912 } 913 914 func (s *DockerSuite) TestStartWithTooLowMemoryLimit(c *check.C) { 915 out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "create", "busybox")) 916 if err != nil { 917 c.Fatal(err, out) 918 } 919 920 containerID := strings.TrimSpace(out) 921 922 config := `{ 923 "CpuShares": 100, 924 "Memory": 524287 925 }` 926 927 res, body, _ := sockRequestRaw("POST", "/containers/"+containerID+"/start", strings.NewReader(config), "application/json") 928 b, err2 := readBody(body) 929 if err2 != nil { 930 c.Fatal(err2) 931 } 932 933 c.Assert(res.StatusCode, check.Equals, http.StatusInternalServerError) 934 c.Assert(strings.Contains(string(b), "Minimum memory limit allowed is 4MB"), check.Equals, true) 935 } 936 937 func (s *DockerSuite) TestContainerApiRename(c *check.C) { 938 runCmd := exec.Command(dockerBinary, "run", "--name", "TestContainerApiRename", "-d", "busybox", "sh") 939 out, _, err := runCommandWithOutput(runCmd) 940 c.Assert(err, check.IsNil) 941 942 containerID := strings.TrimSpace(out) 943 newName := "TestContainerApiRenameNew" 944 statusCode, _, err := sockRequest("POST", "/containers/"+containerID+"/rename?name="+newName, nil) 945 946 // 204 No Content is expected, not 200 947 c.Assert(statusCode, check.Equals, http.StatusNoContent) 948 c.Assert(err, check.IsNil) 949 950 name, err := inspectField(containerID, "Name") 951 if name != "/"+newName { 952 c.Fatalf("Failed to rename container, expected %v, got %v. Container rename API failed", newName, name) 953 } 954 } 955 956 func (s *DockerSuite) TestContainerApiKill(c *check.C) { 957 name := "test-api-kill" 958 runCmd := exec.Command(dockerBinary, "run", "-di", "--name", name, "busybox", "top") 959 out, _, err := runCommandWithOutput(runCmd) 960 if err != nil { 961 c.Fatalf("Error on container creation: %v, output: %q", err, out) 962 } 963 964 status, _, err := sockRequest("POST", "/containers/"+name+"/kill", nil) 965 c.Assert(status, check.Equals, http.StatusNoContent) 966 c.Assert(err, check.IsNil) 967 968 state, err := inspectField(name, "State.Running") 969 if err != nil { 970 c.Fatal(err) 971 } 972 if state != "false" { 973 c.Fatalf("got wrong State from container %s: %q", name, state) 974 } 975 } 976 977 func (s *DockerSuite) TestContainerApiRestart(c *check.C) { 978 name := "test-api-restart" 979 runCmd := exec.Command(dockerBinary, "run", "-di", "--name", name, "busybox", "top") 980 out, _, err := runCommandWithOutput(runCmd) 981 if err != nil { 982 c.Fatalf("Error on container creation: %v, output: %q", err, out) 983 } 984 985 status, _, err := sockRequest("POST", "/containers/"+name+"/restart?t=1", nil) 986 c.Assert(status, check.Equals, http.StatusNoContent) 987 c.Assert(err, check.IsNil) 988 989 if err := waitInspect(name, "{{ .State.Restarting }} {{ .State.Running }}", "false true", 5); err != nil { 990 c.Fatal(err) 991 } 992 } 993 994 func (s *DockerSuite) TestContainerApiRestartNotimeoutParam(c *check.C) { 995 name := "test-api-restart-no-timeout-param" 996 runCmd := exec.Command(dockerBinary, "run", "-di", "--name", name, "busybox", "top") 997 out, _, err := runCommandWithOutput(runCmd) 998 if err != nil { 999 c.Fatalf("Error on container creation: %v, output: %q", err, out) 1000 } 1001 id := strings.TrimSpace(out) 1002 c.Assert(waitRun(id), check.IsNil) 1003 1004 status, _, err := sockRequest("POST", "/containers/"+name+"/restart", nil) 1005 c.Assert(status, check.Equals, http.StatusNoContent) 1006 c.Assert(err, check.IsNil) 1007 1008 if err := waitInspect(name, "{{ .State.Restarting }} {{ .State.Running }}", "false true", 5); err != nil { 1009 c.Fatal(err) 1010 } 1011 } 1012 1013 func (s *DockerSuite) TestContainerApiStart(c *check.C) { 1014 name := "testing-start" 1015 config := map[string]interface{}{ 1016 "Image": "busybox", 1017 "Cmd": []string{"/bin/sh", "-c", "/bin/top"}, 1018 "OpenStdin": true, 1019 } 1020 1021 status, _, err := sockRequest("POST", "/containers/create?name="+name, config) 1022 c.Assert(status, check.Equals, http.StatusCreated) 1023 c.Assert(err, check.IsNil) 1024 1025 conf := make(map[string]interface{}) 1026 status, _, err = sockRequest("POST", "/containers/"+name+"/start", conf) 1027 c.Assert(status, check.Equals, http.StatusNoContent) 1028 c.Assert(err, check.IsNil) 1029 1030 // second call to start should give 304 1031 status, _, err = sockRequest("POST", "/containers/"+name+"/start", conf) 1032 c.Assert(status, check.Equals, http.StatusNotModified) 1033 c.Assert(err, check.IsNil) 1034 } 1035 1036 func (s *DockerSuite) TestContainerApiStop(c *check.C) { 1037 name := "test-api-stop" 1038 runCmd := exec.Command(dockerBinary, "run", "-di", "--name", name, "busybox", "top") 1039 out, _, err := runCommandWithOutput(runCmd) 1040 if err != nil { 1041 c.Fatalf("Error on container creation: %v, output: %q", err, out) 1042 } 1043 1044 status, _, err := sockRequest("POST", "/containers/"+name+"/stop?t=1", nil) 1045 c.Assert(status, check.Equals, http.StatusNoContent) 1046 c.Assert(err, check.IsNil) 1047 1048 if err := waitInspect(name, "{{ .State.Running }}", "false", 5); err != nil { 1049 c.Fatal(err) 1050 } 1051 1052 // second call to start should give 304 1053 status, _, err = sockRequest("POST", "/containers/"+name+"/stop?t=1", nil) 1054 c.Assert(status, check.Equals, http.StatusNotModified) 1055 c.Assert(err, check.IsNil) 1056 } 1057 1058 func (s *DockerSuite) TestContainerApiWait(c *check.C) { 1059 name := "test-api-wait" 1060 runCmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "sleep", "5") 1061 out, _, err := runCommandWithOutput(runCmd) 1062 if err != nil { 1063 c.Fatalf("Error on container creation: %v, output: %q", err, out) 1064 } 1065 1066 status, body, err := sockRequest("POST", "/containers/"+name+"/wait", nil) 1067 c.Assert(status, check.Equals, http.StatusOK) 1068 c.Assert(err, check.IsNil) 1069 1070 if err := waitInspect(name, "{{ .State.Running }}", "false", 5); err != nil { 1071 c.Fatal(err) 1072 } 1073 1074 var waitres types.ContainerWaitResponse 1075 if err := json.Unmarshal(body, &waitres); err != nil { 1076 c.Fatalf("unable to unmarshal response body: %v", err) 1077 } 1078 1079 if waitres.StatusCode != 0 { 1080 c.Fatalf("Expected wait response StatusCode to be 0, got %d", waitres.StatusCode) 1081 } 1082 } 1083 1084 func (s *DockerSuite) TestContainerApiCopy(c *check.C) { 1085 name := "test-container-api-copy" 1086 runCmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "touch", "/test.txt") 1087 _, err := runCommand(runCmd) 1088 c.Assert(err, check.IsNil) 1089 1090 postData := types.CopyConfig{ 1091 Resource: "/test.txt", 1092 } 1093 1094 status, body, err := sockRequest("POST", "/containers/"+name+"/copy", postData) 1095 c.Assert(err, check.IsNil) 1096 c.Assert(status, check.Equals, http.StatusOK) 1097 1098 found := false 1099 for tarReader := tar.NewReader(bytes.NewReader(body)); ; { 1100 h, err := tarReader.Next() 1101 if err != nil { 1102 if err == io.EOF { 1103 break 1104 } 1105 c.Fatal(err) 1106 } 1107 if h.Name == "test.txt" { 1108 found = true 1109 break 1110 } 1111 } 1112 c.Assert(found, check.Equals, true) 1113 } 1114 1115 func (s *DockerSuite) TestContainerApiCopyResourcePathEmpty(c *check.C) { 1116 name := "test-container-api-copy-resource-empty" 1117 runCmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "touch", "/test.txt") 1118 _, err := runCommand(runCmd) 1119 c.Assert(err, check.IsNil) 1120 1121 postData := types.CopyConfig{ 1122 Resource: "", 1123 } 1124 1125 status, body, err := sockRequest("POST", "/containers/"+name+"/copy", postData) 1126 c.Assert(err, check.IsNil) 1127 c.Assert(status, check.Equals, http.StatusInternalServerError) 1128 c.Assert(string(body), check.Matches, "Path cannot be empty\n") 1129 } 1130 1131 func (s *DockerSuite) TestContainerApiCopyResourcePathNotFound(c *check.C) { 1132 name := "test-container-api-copy-resource-not-found" 1133 runCmd := exec.Command(dockerBinary, "run", "--name", name, "busybox") 1134 _, err := runCommand(runCmd) 1135 c.Assert(err, check.IsNil) 1136 1137 postData := types.CopyConfig{ 1138 Resource: "/notexist", 1139 } 1140 1141 status, body, err := sockRequest("POST", "/containers/"+name+"/copy", postData) 1142 c.Assert(err, check.IsNil) 1143 c.Assert(status, check.Equals, http.StatusInternalServerError) 1144 c.Assert(string(body), check.Matches, "Could not find the file /notexist in container "+name+"\n") 1145 } 1146 1147 func (s *DockerSuite) TestContainerApiCopyContainerNotFound(c *check.C) { 1148 postData := types.CopyConfig{ 1149 Resource: "/something", 1150 } 1151 1152 status, _, err := sockRequest("POST", "/containers/notexists/copy", postData) 1153 c.Assert(err, check.IsNil) 1154 c.Assert(status, check.Equals, http.StatusNotFound) 1155 } 1156 1157 func (s *DockerSuite) TestContainerApiDelete(c *check.C) { 1158 runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "top") 1159 out, _, err := runCommandWithOutput(runCmd) 1160 c.Assert(err, check.IsNil) 1161 1162 id := strings.TrimSpace(out) 1163 c.Assert(waitRun(id), check.IsNil) 1164 1165 stopCmd := exec.Command(dockerBinary, "stop", id) 1166 _, err = runCommand(stopCmd) 1167 c.Assert(err, check.IsNil) 1168 1169 status, _, err := sockRequest("DELETE", "/containers/"+id, nil) 1170 c.Assert(err, check.IsNil) 1171 c.Assert(status, check.Equals, http.StatusNoContent) 1172 } 1173 1174 func (s *DockerSuite) TestContainerApiDeleteNotExist(c *check.C) { 1175 status, body, err := sockRequest("DELETE", "/containers/doesnotexist", nil) 1176 c.Assert(err, check.IsNil) 1177 c.Assert(status, check.Equals, http.StatusNotFound) 1178 c.Assert(string(body), check.Matches, "no such id: doesnotexist\n") 1179 } 1180 1181 func (s *DockerSuite) TestContainerApiDeleteForce(c *check.C) { 1182 runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "top") 1183 out, _, err := runCommandWithOutput(runCmd) 1184 c.Assert(err, check.IsNil) 1185 1186 id := strings.TrimSpace(out) 1187 c.Assert(waitRun(id), check.IsNil) 1188 1189 status, _, err := sockRequest("DELETE", "/containers/"+id+"?force=1", nil) 1190 c.Assert(err, check.IsNil) 1191 c.Assert(status, check.Equals, http.StatusNoContent) 1192 } 1193 1194 func (s *DockerSuite) TestContainerApiDeleteRemoveLinks(c *check.C) { 1195 runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "tlink1", "busybox", "top") 1196 out, _, err := runCommandWithOutput(runCmd) 1197 c.Assert(err, check.IsNil) 1198 1199 id := strings.TrimSpace(out) 1200 c.Assert(waitRun(id), check.IsNil) 1201 1202 runCmd = exec.Command(dockerBinary, "run", "--link", "tlink1:tlink1", "--name", "tlink2", "-d", "busybox", "top") 1203 out, _, err = runCommandWithOutput(runCmd) 1204 c.Assert(err, check.IsNil) 1205 1206 id2 := strings.TrimSpace(out) 1207 c.Assert(waitRun(id2), check.IsNil) 1208 1209 links, err := inspectFieldJSON(id2, "HostConfig.Links") 1210 c.Assert(err, check.IsNil) 1211 1212 if links != "[\"/tlink1:/tlink2/tlink1\"]" { 1213 c.Fatal("expected to have links between containers") 1214 } 1215 1216 status, _, err := sockRequest("DELETE", "/containers/tlink2/tlink1?link=1", nil) 1217 c.Assert(err, check.IsNil) 1218 c.Assert(status, check.Equals, http.StatusNoContent) 1219 1220 linksPostRm, err := inspectFieldJSON(id2, "HostConfig.Links") 1221 c.Assert(err, check.IsNil) 1222 1223 if linksPostRm != "null" { 1224 c.Fatal("call to api deleteContainer links should have removed the specified links") 1225 } 1226 } 1227 1228 func (s *DockerSuite) TestContainerApiDeleteConflict(c *check.C) { 1229 runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "top") 1230 out, _, err := runCommandWithOutput(runCmd) 1231 c.Assert(err, check.IsNil) 1232 1233 id := strings.TrimSpace(out) 1234 c.Assert(waitRun(id), check.IsNil) 1235 1236 status, _, err := sockRequest("DELETE", "/containers/"+id, nil) 1237 c.Assert(status, check.Equals, http.StatusConflict) 1238 c.Assert(err, check.IsNil) 1239 } 1240 1241 func (s *DockerSuite) TestContainerApiDeleteRemoveVolume(c *check.C) { 1242 testRequires(c, SameHostDaemon) 1243 1244 runCmd := exec.Command(dockerBinary, "run", "-d", "-v", "/testvolume", "busybox", "top") 1245 out, _, err := runCommandWithOutput(runCmd) 1246 c.Assert(err, check.IsNil) 1247 1248 id := strings.TrimSpace(out) 1249 c.Assert(waitRun(id), check.IsNil) 1250 1251 vol, err := inspectFieldMap(id, "Volumes", "/testvolume") 1252 c.Assert(err, check.IsNil) 1253 1254 _, err = os.Stat(vol) 1255 c.Assert(err, check.IsNil) 1256 1257 status, _, err := sockRequest("DELETE", "/containers/"+id+"?v=1&force=1", nil) 1258 c.Assert(status, check.Equals, http.StatusNoContent) 1259 c.Assert(err, check.IsNil) 1260 1261 if _, err := os.Stat(vol); !os.IsNotExist(err) { 1262 c.Fatalf("expected to get ErrNotExist error, got %v", err) 1263 } 1264 } 1265 1266 // Regression test for https://github.com/docker/docker/issues/6231 1267 func (s *DockerSuite) TestContainersApiChunkedEncoding(c *check.C) { 1268 out, _ := dockerCmd(c, "create", "-v", "/foo", "busybox", "true") 1269 id := strings.TrimSpace(out) 1270 1271 conn, err := sockConn(time.Duration(10 * time.Second)) 1272 if err != nil { 1273 c.Fatal(err) 1274 } 1275 client := httputil.NewClientConn(conn, nil) 1276 defer client.Close() 1277 1278 bindCfg := strings.NewReader(`{"Binds": ["/tmp:/foo"]}`) 1279 req, err := http.NewRequest("POST", "/containers/"+id+"/start", bindCfg) 1280 if err != nil { 1281 c.Fatal(err) 1282 } 1283 req.Header.Set("Content-Type", "application/json") 1284 // This is a cheat to make the http request do chunked encoding 1285 // Otherwise (just setting the Content-Encoding to chunked) net/http will overwrite 1286 // https://golang.org/src/pkg/net/http/request.go?s=11980:12172 1287 req.ContentLength = -1 1288 1289 resp, err := client.Do(req) 1290 if err != nil { 1291 c.Fatalf("error starting container with chunked encoding: %v", err) 1292 } 1293 resp.Body.Close() 1294 if resp.StatusCode != 204 { 1295 c.Fatalf("expected status code 204, got %d", resp.StatusCode) 1296 } 1297 1298 out, err = inspectFieldJSON(id, "HostConfig.Binds") 1299 if err != nil { 1300 c.Fatal(err) 1301 } 1302 1303 var binds []string 1304 if err := json.NewDecoder(strings.NewReader(out)).Decode(&binds); err != nil { 1305 c.Fatal(err) 1306 } 1307 if len(binds) != 1 { 1308 c.Fatalf("got unexpected binds: %v", binds) 1309 } 1310 1311 expected := "/tmp:/foo" 1312 if binds[0] != expected { 1313 c.Fatalf("got incorrect bind spec, wanted %s, got: %s", expected, binds[0]) 1314 } 1315 } 1316 1317 func (s *DockerSuite) TestPostContainerStop(c *check.C) { 1318 runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "top") 1319 out, _, err := runCommandWithOutput(runCmd) 1320 c.Assert(err, check.IsNil) 1321 1322 containerID := strings.TrimSpace(out) 1323 c.Assert(waitRun(containerID), check.IsNil) 1324 1325 statusCode, _, err := sockRequest("POST", "/containers/"+containerID+"/stop", nil) 1326 1327 // 204 No Content is expected, not 200 1328 c.Assert(statusCode, check.Equals, http.StatusNoContent) 1329 c.Assert(err, check.IsNil) 1330 1331 if err := waitInspect(containerID, "{{ .State.Running }}", "false", 5); err != nil { 1332 c.Fatal(err) 1333 } 1334 }