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