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