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