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