github.com/rsampaio/docker@v0.7.2-0.20150827203920-fdc73cc3fc31/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("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 // #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 439 func (s *DockerSuite) TestPostContainerBindNormalVolume(c *check.C) { 440 dockerCmd(c, "create", "-v", "/foo", "--name=one", "busybox") 441 442 fooDir, err := inspectMountSourceField("one", "/foo") 443 if err != nil { 444 c.Fatal(err) 445 } 446 447 dockerCmd(c, "create", "-v", "/foo", "--name=two", "busybox") 448 449 bindSpec := map[string][]string{"Binds": {fooDir + ":/foo"}} 450 status, _, err := sockRequest("POST", "/containers/two/start", bindSpec) 451 c.Assert(err, check.IsNil) 452 c.Assert(status, check.Equals, http.StatusNoContent) 453 454 fooDir2, err := inspectMountSourceField("two", "/foo") 455 if err != nil { 456 c.Fatal(err) 457 } 458 459 if fooDir2 != fooDir { 460 c.Fatalf("expected volume path to be %s, got: %s", fooDir, fooDir2) 461 } 462 } 463 464 func (s *DockerSuite) TestContainerApiPause(c *check.C) { 465 defer unpauseAllContainers() 466 out, _ := dockerCmd(c, "run", "-d", "busybox", "sleep", "30") 467 ContainerID := strings.TrimSpace(out) 468 469 status, _, err := sockRequest("POST", "/containers/"+ContainerID+"/pause", nil) 470 c.Assert(err, check.IsNil) 471 c.Assert(status, check.Equals, http.StatusNoContent) 472 473 pausedContainers, err := getSliceOfPausedContainers() 474 475 if err != nil { 476 c.Fatalf("error thrown while checking if containers were paused: %v", err) 477 } 478 479 if len(pausedContainers) != 1 || stringid.TruncateID(ContainerID) != pausedContainers[0] { 480 c.Fatalf("there should be one paused container and not %d", len(pausedContainers)) 481 } 482 483 status, _, err = sockRequest("POST", "/containers/"+ContainerID+"/unpause", nil) 484 c.Assert(err, check.IsNil) 485 c.Assert(status, check.Equals, http.StatusNoContent) 486 487 pausedContainers, err = getSliceOfPausedContainers() 488 489 if err != nil { 490 c.Fatalf("error thrown while checking if containers were paused: %v", err) 491 } 492 493 if pausedContainers != nil { 494 c.Fatalf("There should be no paused container.") 495 } 496 } 497 498 func (s *DockerSuite) TestContainerApiTop(c *check.C) { 499 out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "top") 500 id := strings.TrimSpace(string(out)) 501 c.Assert(waitRun(id), check.IsNil) 502 503 type topResp struct { 504 Titles []string 505 Processes [][]string 506 } 507 var top topResp 508 status, b, err := sockRequest("GET", "/containers/"+id+"/top?ps_args=aux", nil) 509 c.Assert(err, check.IsNil) 510 c.Assert(status, check.Equals, http.StatusOK) 511 512 if err := json.Unmarshal(b, &top); err != nil { 513 c.Fatal(err) 514 } 515 516 if len(top.Titles) != 11 { 517 c.Fatalf("expected 11 titles, found %d: %v", len(top.Titles), top.Titles) 518 } 519 520 if top.Titles[0] != "USER" || top.Titles[10] != "COMMAND" { 521 c.Fatalf("expected `USER` at `Titles[0]` and `COMMAND` at Titles[10]: %v", top.Titles) 522 } 523 if len(top.Processes) != 2 { 524 c.Fatalf("expected 2 processes, found %d: %v", len(top.Processes), top.Processes) 525 } 526 if top.Processes[0][10] != "/bin/sh -c top" { 527 c.Fatalf("expected `/bin/sh -c top`, found: %s", top.Processes[0][10]) 528 } 529 if top.Processes[1][10] != "top" { 530 c.Fatalf("expected `top`, found: %s", top.Processes[1][10]) 531 } 532 } 533 534 func (s *DockerSuite) TestContainerApiCommit(c *check.C) { 535 cName := "testapicommit" 536 dockerCmd(c, "run", "--name="+cName, "busybox", "/bin/sh", "-c", "touch /test") 537 538 name := "TestContainerApiCommit" 539 status, b, err := sockRequest("POST", "/commit?repo="+name+"&testtag=tag&container="+cName, nil) 540 c.Assert(err, check.IsNil) 541 c.Assert(status, check.Equals, http.StatusCreated) 542 543 type resp struct { 544 ID string 545 } 546 var img resp 547 if err := json.Unmarshal(b, &img); err != nil { 548 c.Fatal(err) 549 } 550 551 cmd, err := inspectField(img.ID, "Config.Cmd") 552 if err != nil { 553 c.Fatal(err) 554 } 555 if cmd != "{[/bin/sh -c touch /test]}" { 556 c.Fatalf("got wrong Cmd from commit: %q", cmd) 557 } 558 // sanity check, make sure the image is what we think it is 559 dockerCmd(c, "run", img.ID, "ls", "/test") 560 } 561 562 func (s *DockerSuite) TestContainerApiCommitWithLabelInConfig(c *check.C) { 563 cName := "testapicommitwithconfig" 564 dockerCmd(c, "run", "--name="+cName, "busybox", "/bin/sh", "-c", "touch /test") 565 566 config := map[string]interface{}{ 567 "Labels": map[string]string{"key1": "value1", "key2": "value2"}, 568 } 569 570 name := "TestContainerApiCommitWithConfig" 571 status, b, err := sockRequest("POST", "/commit?repo="+name+"&container="+cName, config) 572 c.Assert(err, check.IsNil) 573 c.Assert(status, check.Equals, http.StatusCreated) 574 575 type resp struct { 576 ID string 577 } 578 var img resp 579 if err := json.Unmarshal(b, &img); err != nil { 580 c.Fatal(err) 581 } 582 583 label1, err := inspectFieldMap(img.ID, "Config.Labels", "key1") 584 if err != nil { 585 c.Fatal(err) 586 } 587 c.Assert(label1, check.Equals, "value1") 588 589 label2, err := inspectFieldMap(img.ID, "Config.Labels", "key2") 590 if err != nil { 591 c.Fatal(err) 592 } 593 c.Assert(label2, check.Equals, "value2") 594 595 cmd, err := inspectField(img.ID, "Config.Cmd") 596 if err != nil { 597 c.Fatal(err) 598 } 599 if cmd != "{[/bin/sh -c touch /test]}" { 600 c.Fatalf("got wrong Cmd from commit: %q", cmd) 601 } 602 603 // sanity check, make sure the image is what we think it is 604 dockerCmd(c, "run", img.ID, "ls", "/test") 605 } 606 607 func (s *DockerSuite) TestContainerApiBadPort(c *check.C) { 608 config := map[string]interface{}{ 609 "Image": "busybox", 610 "Cmd": []string{"/bin/sh", "-c", "echo test"}, 611 "PortBindings": map[string]interface{}{ 612 "8080/tcp": []map[string]interface{}{ 613 { 614 "HostIP": "", 615 "HostPort": "aa80", 616 }, 617 }, 618 }, 619 } 620 621 jsonData := bytes.NewBuffer(nil) 622 json.NewEncoder(jsonData).Encode(config) 623 624 status, b, err := sockRequest("POST", "/containers/create", config) 625 c.Assert(err, check.IsNil) 626 c.Assert(status, check.Equals, http.StatusInternalServerError) 627 628 if strings.TrimSpace(string(b)) != `Invalid port specification: "aa80"` { 629 c.Fatalf("Incorrect error msg: %s", string(b)) 630 } 631 } 632 633 func (s *DockerSuite) TestContainerApiCreate(c *check.C) { 634 config := map[string]interface{}{ 635 "Image": "busybox", 636 "Cmd": []string{"/bin/sh", "-c", "touch /test && ls /test"}, 637 } 638 639 status, b, err := sockRequest("POST", "/containers/create", config) 640 c.Assert(err, check.IsNil) 641 c.Assert(status, check.Equals, http.StatusCreated) 642 643 type createResp struct { 644 ID string 645 } 646 var container createResp 647 if err := json.Unmarshal(b, &container); err != nil { 648 c.Fatal(err) 649 } 650 651 out, _ := dockerCmd(c, "start", "-a", container.ID) 652 if strings.TrimSpace(out) != "/test" { 653 c.Fatalf("expected output `/test`, got %q", out) 654 } 655 } 656 657 func (s *DockerSuite) TestContainerApiCreateEmptyConfig(c *check.C) { 658 config := map[string]interface{}{} 659 660 status, b, err := sockRequest("POST", "/containers/create", config) 661 c.Assert(err, check.IsNil) 662 c.Assert(status, check.Equals, http.StatusInternalServerError) 663 664 expected := "Config cannot be empty in order to create a container\n" 665 if body := string(b); body != expected { 666 c.Fatalf("Expected to get %q, got %q", expected, body) 667 } 668 } 669 670 func (s *DockerSuite) TestContainerApiCreateWithHostName(c *check.C) { 671 hostName := "test-host" 672 config := map[string]interface{}{ 673 "Image": "busybox", 674 "Hostname": hostName, 675 } 676 677 status, body, err := sockRequest("POST", "/containers/create", config) 678 c.Assert(err, check.IsNil) 679 c.Assert(status, check.Equals, http.StatusCreated) 680 681 var container types.ContainerCreateResponse 682 if err := json.Unmarshal(body, &container); err != nil { 683 c.Fatal(err) 684 } 685 686 status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil) 687 c.Assert(err, check.IsNil) 688 c.Assert(status, check.Equals, http.StatusOK) 689 690 var containerJSON types.ContainerJSON 691 if err := json.Unmarshal(body, &containerJSON); err != nil { 692 c.Fatal(err) 693 } 694 695 if containerJSON.Config.Hostname != hostName { 696 c.Fatalf("Mismatched Hostname, Expected %s, Actual: %s ", hostName, containerJSON.Config.Hostname) 697 } 698 } 699 700 func (s *DockerSuite) TestContainerApiCreateWithDomainName(c *check.C) { 701 domainName := "test-domain" 702 config := map[string]interface{}{ 703 "Image": "busybox", 704 "Domainname": domainName, 705 } 706 707 status, body, err := sockRequest("POST", "/containers/create", config) 708 c.Assert(err, check.IsNil) 709 c.Assert(status, check.Equals, http.StatusCreated) 710 711 var container types.ContainerCreateResponse 712 if err := json.Unmarshal(body, &container); err != nil { 713 c.Fatal(err) 714 } 715 716 status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil) 717 c.Assert(err, check.IsNil) 718 c.Assert(status, check.Equals, http.StatusOK) 719 720 var containerJSON types.ContainerJSON 721 if err := json.Unmarshal(body, &containerJSON); err != nil { 722 c.Fatal(err) 723 } 724 725 if containerJSON.Config.Domainname != domainName { 726 c.Fatalf("Mismatched Domainname, Expected %s, Actual: %s ", domainName, containerJSON.Config.Domainname) 727 } 728 } 729 730 func (s *DockerSuite) TestContainerApiCreateNetworkMode(c *check.C) { 731 UtilCreateNetworkMode(c, "host") 732 UtilCreateNetworkMode(c, "bridge") 733 UtilCreateNetworkMode(c, "container:web1") 734 } 735 736 func UtilCreateNetworkMode(c *check.C, networkMode string) { 737 config := map[string]interface{}{ 738 "Image": "busybox", 739 "HostConfig": map[string]interface{}{"NetworkMode": networkMode}, 740 } 741 742 status, body, err := sockRequest("POST", "/containers/create", config) 743 c.Assert(err, check.IsNil) 744 c.Assert(status, check.Equals, http.StatusCreated) 745 746 var container types.ContainerCreateResponse 747 if err := json.Unmarshal(body, &container); err != nil { 748 c.Fatal(err) 749 } 750 751 status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil) 752 c.Assert(err, check.IsNil) 753 c.Assert(status, check.Equals, http.StatusOK) 754 755 var containerJSON types.ContainerJSON 756 if err := json.Unmarshal(body, &containerJSON); err != nil { 757 c.Fatal(err) 758 } 759 760 if containerJSON.HostConfig.NetworkMode != runconfig.NetworkMode(networkMode) { 761 c.Fatalf("Mismatched NetworkMode, Expected %s, Actual: %s ", networkMode, containerJSON.HostConfig.NetworkMode) 762 } 763 } 764 765 func (s *DockerSuite) TestContainerApiCreateWithCpuSharesCpuset(c *check.C) { 766 config := map[string]interface{}{ 767 "Image": "busybox", 768 "CpuShares": 512, 769 "CpusetCpus": "0,1", 770 } 771 772 status, body, err := sockRequest("POST", "/containers/create", config) 773 c.Assert(err, check.IsNil) 774 c.Assert(status, check.Equals, http.StatusCreated) 775 776 var container types.ContainerCreateResponse 777 if err := json.Unmarshal(body, &container); err != nil { 778 c.Fatal(err) 779 } 780 781 status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil) 782 c.Assert(err, check.IsNil) 783 c.Assert(status, check.Equals, http.StatusOK) 784 785 var containerJSON types.ContainerJSON 786 787 c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil) 788 789 out, err := inspectField(containerJSON.ID, "HostConfig.CpuShares") 790 c.Assert(err, check.IsNil) 791 c.Assert(out, check.Equals, "512") 792 793 outCpuset, errCpuset := inspectField(containerJSON.ID, "HostConfig.CpusetCpus") 794 c.Assert(errCpuset, check.IsNil, check.Commentf("Output: %s", outCpuset)) 795 c.Assert(outCpuset, check.Equals, "0,1") 796 } 797 798 func (s *DockerSuite) TestContainerApiVerifyHeader(c *check.C) { 799 config := map[string]interface{}{ 800 "Image": "busybox", 801 } 802 803 create := func(ct string) (*http.Response, io.ReadCloser, error) { 804 jsonData := bytes.NewBuffer(nil) 805 if err := json.NewEncoder(jsonData).Encode(config); err != nil { 806 c.Fatal(err) 807 } 808 return sockRequestRaw("POST", "/containers/create", jsonData, ct) 809 } 810 811 // Try with no content-type 812 res, body, err := create("") 813 c.Assert(err, check.IsNil) 814 c.Assert(res.StatusCode, check.Equals, http.StatusInternalServerError) 815 body.Close() 816 817 // Try with wrong content-type 818 res, body, err = create("application/xml") 819 c.Assert(err, check.IsNil) 820 c.Assert(res.StatusCode, check.Equals, http.StatusInternalServerError) 821 body.Close() 822 823 // now application/json 824 res, body, err = create("application/json") 825 c.Assert(err, check.IsNil) 826 c.Assert(res.StatusCode, check.Equals, http.StatusCreated) 827 body.Close() 828 } 829 830 //Issue 14230. daemon should return 500 for invalid port syntax 831 func (s *DockerSuite) TestContainerApiInvalidPortSyntax(c *check.C) { 832 config := `{ 833 "Image": "busybox", 834 "HostConfig": { 835 "PortBindings": { 836 "19039;1230": [ 837 {} 838 ] 839 } 840 } 841 }` 842 843 res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json") 844 c.Assert(err, check.IsNil) 845 c.Assert(res.StatusCode, check.Equals, http.StatusInternalServerError) 846 847 b, err := readBody(body) 848 if err != nil { 849 c.Fatal(err) 850 } 851 c.Assert(strings.Contains(string(b[:]), "Invalid port"), check.Equals, true) 852 } 853 854 // Issue 7941 - test to make sure a "null" in JSON is just ignored. 855 // W/o this fix a null in JSON would be parsed into a string var as "null" 856 func (s *DockerSuite) TestContainerApiPostCreateNull(c *check.C) { 857 config := `{ 858 "Hostname":"", 859 "Domainname":"", 860 "Memory":0, 861 "MemorySwap":0, 862 "CpuShares":0, 863 "Cpuset":null, 864 "AttachStdin":true, 865 "AttachStdout":true, 866 "AttachStderr":true, 867 "ExposedPorts":{}, 868 "Tty":true, 869 "OpenStdin":true, 870 "StdinOnce":true, 871 "Env":[], 872 "Cmd":"ls", 873 "Image":"busybox", 874 "Volumes":{}, 875 "WorkingDir":"", 876 "Entrypoint":null, 877 "NetworkDisabled":false, 878 "OnBuild":null}` 879 880 res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json") 881 c.Assert(err, check.IsNil) 882 c.Assert(res.StatusCode, check.Equals, http.StatusCreated) 883 884 b, err := readBody(body) 885 if err != nil { 886 c.Fatal(err) 887 } 888 type createResp struct { 889 ID string 890 } 891 var container createResp 892 if err := json.Unmarshal(b, &container); err != nil { 893 c.Fatal(err) 894 } 895 896 out, err := inspectField(container.ID, "HostConfig.CpusetCpus") 897 if err != nil { 898 c.Fatal(err, out) 899 } 900 if out != "" { 901 c.Fatalf("expected empty string, got %q", out) 902 } 903 904 outMemory, errMemory := inspectField(container.ID, "HostConfig.Memory") 905 c.Assert(outMemory, check.Equals, "0") 906 if errMemory != nil { 907 c.Fatal(errMemory, outMemory) 908 } 909 outMemorySwap, errMemorySwap := inspectField(container.ID, "HostConfig.MemorySwap") 910 c.Assert(outMemorySwap, check.Equals, "0") 911 if errMemorySwap != nil { 912 c.Fatal(errMemorySwap, outMemorySwap) 913 } 914 } 915 916 func (s *DockerSuite) TestCreateWithTooLowMemoryLimit(c *check.C) { 917 config := `{ 918 "Image": "busybox", 919 "Cmd": "ls", 920 "OpenStdin": true, 921 "CpuShares": 100, 922 "Memory": 524287 923 }` 924 925 res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json") 926 c.Assert(err, check.IsNil) 927 b, err2 := readBody(body) 928 if err2 != nil { 929 c.Fatal(err2) 930 } 931 932 c.Assert(res.StatusCode, check.Equals, http.StatusInternalServerError) 933 c.Assert(strings.Contains(string(b), "Minimum memory limit allowed is 4MB"), check.Equals, true) 934 } 935 936 func (s *DockerSuite) TestStartWithTooLowMemoryLimit(c *check.C) { 937 out, _ := dockerCmd(c, "create", "busybox") 938 939 containerID := strings.TrimSpace(out) 940 941 config := `{ 942 "CpuShares": 100, 943 "Memory": 524287 944 }` 945 946 res, body, err := sockRequestRaw("POST", "/containers/"+containerID+"/start", strings.NewReader(config), "application/json") 947 c.Assert(err, check.IsNil) 948 b, err2 := readBody(body) 949 if err2 != nil { 950 c.Fatal(err2) 951 } 952 953 c.Assert(res.StatusCode, check.Equals, http.StatusInternalServerError) 954 c.Assert(strings.Contains(string(b), "Minimum memory limit allowed is 4MB"), check.Equals, true) 955 } 956 957 func (s *DockerSuite) TestContainerApiRename(c *check.C) { 958 out, _ := dockerCmd(c, "run", "--name", "TestContainerApiRename", "-d", "busybox", "sh") 959 960 containerID := strings.TrimSpace(out) 961 newName := "TestContainerApiRenameNew" 962 statusCode, _, err := sockRequest("POST", "/containers/"+containerID+"/rename?name="+newName, nil) 963 c.Assert(err, check.IsNil) 964 // 204 No Content is expected, not 200 965 c.Assert(statusCode, check.Equals, http.StatusNoContent) 966 967 name, err := inspectField(containerID, "Name") 968 if name != "/"+newName { 969 c.Fatalf("Failed to rename container, expected %v, got %v. Container rename API failed", newName, name) 970 } 971 } 972 973 func (s *DockerSuite) TestContainerApiKill(c *check.C) { 974 name := "test-api-kill" 975 dockerCmd(c, "run", "-di", "--name", name, "busybox", "top") 976 977 status, _, err := sockRequest("POST", "/containers/"+name+"/kill", nil) 978 c.Assert(err, check.IsNil) 979 c.Assert(status, check.Equals, http.StatusNoContent) 980 981 state, err := inspectField(name, "State.Running") 982 if err != nil { 983 c.Fatal(err) 984 } 985 if state != "false" { 986 c.Fatalf("got wrong State from container %s: %q", name, state) 987 } 988 } 989 990 func (s *DockerSuite) TestContainerApiRestart(c *check.C) { 991 name := "test-api-restart" 992 dockerCmd(c, "run", "-di", "--name", name, "busybox", "top") 993 994 status, _, err := sockRequest("POST", "/containers/"+name+"/restart?t=1", nil) 995 c.Assert(err, check.IsNil) 996 c.Assert(status, check.Equals, http.StatusNoContent) 997 998 if err := waitInspect(name, "{{ .State.Restarting }} {{ .State.Running }}", "false true", 5); err != nil { 999 c.Fatal(err) 1000 } 1001 } 1002 1003 func (s *DockerSuite) TestContainerApiRestartNotimeoutParam(c *check.C) { 1004 name := "test-api-restart-no-timeout-param" 1005 out, _ := dockerCmd(c, "run", "-di", "--name", name, "busybox", "top") 1006 id := strings.TrimSpace(out) 1007 c.Assert(waitRun(id), check.IsNil) 1008 1009 status, _, err := sockRequest("POST", "/containers/"+name+"/restart", nil) 1010 c.Assert(err, check.IsNil) 1011 c.Assert(status, check.Equals, http.StatusNoContent) 1012 1013 if err := waitInspect(name, "{{ .State.Restarting }} {{ .State.Running }}", "false true", 5); err != nil { 1014 c.Fatal(err) 1015 } 1016 } 1017 1018 func (s *DockerSuite) TestContainerApiStart(c *check.C) { 1019 name := "testing-start" 1020 config := map[string]interface{}{ 1021 "Image": "busybox", 1022 "Cmd": []string{"/bin/sh", "-c", "/bin/top"}, 1023 "OpenStdin": true, 1024 } 1025 1026 status, _, err := sockRequest("POST", "/containers/create?name="+name, config) 1027 c.Assert(err, check.IsNil) 1028 c.Assert(status, check.Equals, http.StatusCreated) 1029 1030 conf := make(map[string]interface{}) 1031 status, _, err = sockRequest("POST", "/containers/"+name+"/start", conf) 1032 c.Assert(err, check.IsNil) 1033 c.Assert(status, check.Equals, http.StatusNoContent) 1034 1035 // second call to start should give 304 1036 status, _, err = sockRequest("POST", "/containers/"+name+"/start", conf) 1037 c.Assert(err, check.IsNil) 1038 c.Assert(status, check.Equals, http.StatusNotModified) 1039 } 1040 1041 func (s *DockerSuite) TestContainerApiStop(c *check.C) { 1042 name := "test-api-stop" 1043 dockerCmd(c, "run", "-di", "--name", name, "busybox", "top") 1044 1045 status, _, err := sockRequest("POST", "/containers/"+name+"/stop?t=1", nil) 1046 c.Assert(err, check.IsNil) 1047 c.Assert(status, check.Equals, http.StatusNoContent) 1048 1049 if err := waitInspect(name, "{{ .State.Running }}", "false", 5); err != nil { 1050 c.Fatal(err) 1051 } 1052 1053 // second call to start should give 304 1054 status, _, err = sockRequest("POST", "/containers/"+name+"/stop?t=1", nil) 1055 c.Assert(err, check.IsNil) 1056 c.Assert(status, check.Equals, http.StatusNotModified) 1057 } 1058 1059 func (s *DockerSuite) TestContainerApiWait(c *check.C) { 1060 name := "test-api-wait" 1061 dockerCmd(c, "run", "--name", name, "busybox", "sleep", "5") 1062 1063 status, body, err := sockRequest("POST", "/containers/"+name+"/wait", nil) 1064 c.Assert(err, check.IsNil) 1065 c.Assert(status, check.Equals, http.StatusOK) 1066 1067 if err := waitInspect(name, "{{ .State.Running }}", "false", 5); err != nil { 1068 c.Fatal(err) 1069 } 1070 1071 var waitres types.ContainerWaitResponse 1072 if err := json.Unmarshal(body, &waitres); err != nil { 1073 c.Fatalf("unable to unmarshal response body: %v", err) 1074 } 1075 1076 if waitres.StatusCode != 0 { 1077 c.Fatalf("Expected wait response StatusCode to be 0, got %d", waitres.StatusCode) 1078 } 1079 } 1080 1081 func (s *DockerSuite) TestContainerApiCopy(c *check.C) { 1082 name := "test-container-api-copy" 1083 dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt") 1084 1085 postData := types.CopyConfig{ 1086 Resource: "/test.txt", 1087 } 1088 1089 status, body, err := sockRequest("POST", "/containers/"+name+"/copy", postData) 1090 c.Assert(err, check.IsNil) 1091 c.Assert(status, check.Equals, http.StatusOK) 1092 1093 found := false 1094 for tarReader := tar.NewReader(bytes.NewReader(body)); ; { 1095 h, err := tarReader.Next() 1096 if err != nil { 1097 if err == io.EOF { 1098 break 1099 } 1100 c.Fatal(err) 1101 } 1102 if h.Name == "test.txt" { 1103 found = true 1104 break 1105 } 1106 } 1107 c.Assert(found, check.Equals, true) 1108 } 1109 1110 func (s *DockerSuite) TestContainerApiCopyResourcePathEmpty(c *check.C) { 1111 name := "test-container-api-copy-resource-empty" 1112 dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt") 1113 1114 postData := types.CopyConfig{ 1115 Resource: "", 1116 } 1117 1118 status, body, err := sockRequest("POST", "/containers/"+name+"/copy", postData) 1119 c.Assert(err, check.IsNil) 1120 c.Assert(status, check.Equals, http.StatusInternalServerError) 1121 c.Assert(string(body), check.Matches, "Path cannot be empty\n") 1122 } 1123 1124 func (s *DockerSuite) TestContainerApiCopyResourcePathNotFound(c *check.C) { 1125 name := "test-container-api-copy-resource-not-found" 1126 dockerCmd(c, "run", "--name", name, "busybox") 1127 1128 postData := types.CopyConfig{ 1129 Resource: "/notexist", 1130 } 1131 1132 status, body, err := sockRequest("POST", "/containers/"+name+"/copy", postData) 1133 c.Assert(err, check.IsNil) 1134 c.Assert(status, check.Equals, http.StatusInternalServerError) 1135 c.Assert(string(body), check.Matches, "Could not find the file /notexist in container "+name+"\n") 1136 } 1137 1138 func (s *DockerSuite) TestContainerApiCopyContainerNotFound(c *check.C) { 1139 postData := types.CopyConfig{ 1140 Resource: "/something", 1141 } 1142 1143 status, _, err := sockRequest("POST", "/containers/notexists/copy", postData) 1144 c.Assert(err, check.IsNil) 1145 c.Assert(status, check.Equals, http.StatusNotFound) 1146 } 1147 1148 func (s *DockerSuite) TestContainerApiDelete(c *check.C) { 1149 out, _ := dockerCmd(c, "run", "-d", "busybox", "top") 1150 1151 id := strings.TrimSpace(out) 1152 c.Assert(waitRun(id), check.IsNil) 1153 1154 dockerCmd(c, "stop", id) 1155 1156 status, _, err := sockRequest("DELETE", "/containers/"+id, nil) 1157 c.Assert(err, check.IsNil) 1158 c.Assert(status, check.Equals, http.StatusNoContent) 1159 } 1160 1161 func (s *DockerSuite) TestContainerApiDeleteNotExist(c *check.C) { 1162 status, body, err := sockRequest("DELETE", "/containers/doesnotexist", nil) 1163 c.Assert(err, check.IsNil) 1164 c.Assert(status, check.Equals, http.StatusNotFound) 1165 c.Assert(string(body), check.Matches, "no such id: doesnotexist\n") 1166 } 1167 1168 func (s *DockerSuite) TestContainerApiDeleteForce(c *check.C) { 1169 out, _ := dockerCmd(c, "run", "-d", "busybox", "top") 1170 1171 id := strings.TrimSpace(out) 1172 c.Assert(waitRun(id), check.IsNil) 1173 1174 status, _, err := sockRequest("DELETE", "/containers/"+id+"?force=1", nil) 1175 c.Assert(err, check.IsNil) 1176 c.Assert(status, check.Equals, http.StatusNoContent) 1177 } 1178 1179 func (s *DockerSuite) TestContainerApiDeleteRemoveLinks(c *check.C) { 1180 out, _ := dockerCmd(c, "run", "-d", "--name", "tlink1", "busybox", "top") 1181 1182 id := strings.TrimSpace(out) 1183 c.Assert(waitRun(id), check.IsNil) 1184 1185 out, _ = dockerCmd(c, "run", "--link", "tlink1:tlink1", "--name", "tlink2", "-d", "busybox", "top") 1186 1187 id2 := strings.TrimSpace(out) 1188 c.Assert(waitRun(id2), check.IsNil) 1189 1190 links, err := inspectFieldJSON(id2, "HostConfig.Links") 1191 c.Assert(err, check.IsNil) 1192 1193 if links != "[\"/tlink1:/tlink2/tlink1\"]" { 1194 c.Fatal("expected to have links between containers") 1195 } 1196 1197 status, _, err := sockRequest("DELETE", "/containers/tlink2/tlink1?link=1", nil) 1198 c.Assert(err, check.IsNil) 1199 c.Assert(status, check.Equals, http.StatusNoContent) 1200 1201 linksPostRm, err := inspectFieldJSON(id2, "HostConfig.Links") 1202 c.Assert(err, check.IsNil) 1203 1204 if linksPostRm != "null" { 1205 c.Fatal("call to api deleteContainer links should have removed the specified links") 1206 } 1207 } 1208 1209 func (s *DockerSuite) TestContainerApiDeleteConflict(c *check.C) { 1210 out, _ := dockerCmd(c, "run", "-d", "busybox", "top") 1211 1212 id := strings.TrimSpace(out) 1213 c.Assert(waitRun(id), check.IsNil) 1214 1215 status, _, err := sockRequest("DELETE", "/containers/"+id, nil) 1216 c.Assert(err, check.IsNil) 1217 c.Assert(status, check.Equals, http.StatusConflict) 1218 } 1219 1220 func (s *DockerSuite) TestContainerApiDeleteRemoveVolume(c *check.C) { 1221 testRequires(c, SameHostDaemon) 1222 1223 out, _ := dockerCmd(c, "run", "-d", "-v", "/testvolume", "busybox", "top") 1224 1225 id := strings.TrimSpace(out) 1226 c.Assert(waitRun(id), check.IsNil) 1227 1228 source, err := inspectMountSourceField(id, "/testvolume") 1229 _, err = os.Stat(source) 1230 c.Assert(err, check.IsNil) 1231 1232 status, _, err := sockRequest("DELETE", "/containers/"+id+"?v=1&force=1", nil) 1233 c.Assert(err, check.IsNil) 1234 c.Assert(status, check.Equals, http.StatusNoContent) 1235 1236 if _, err := os.Stat(source); !os.IsNotExist(err) { 1237 c.Fatalf("expected to get ErrNotExist error, got %v", err) 1238 } 1239 } 1240 1241 // Regression test for https://github.com/docker/docker/issues/6231 1242 func (s *DockerSuite) TestContainersApiChunkedEncoding(c *check.C) { 1243 out, _ := dockerCmd(c, "create", "-v", "/foo", "busybox", "true") 1244 id := strings.TrimSpace(out) 1245 1246 conn, err := sockConn(time.Duration(10 * time.Second)) 1247 if err != nil { 1248 c.Fatal(err) 1249 } 1250 client := httputil.NewClientConn(conn, nil) 1251 defer client.Close() 1252 1253 bindCfg := strings.NewReader(`{"Binds": ["/tmp:/foo"]}`) 1254 req, err := http.NewRequest("POST", "/containers/"+id+"/start", bindCfg) 1255 if err != nil { 1256 c.Fatal(err) 1257 } 1258 req.Header.Set("Content-Type", "application/json") 1259 // This is a cheat to make the http request do chunked encoding 1260 // Otherwise (just setting the Content-Encoding to chunked) net/http will overwrite 1261 // https://golang.org/src/pkg/net/http/request.go?s=11980:12172 1262 req.ContentLength = -1 1263 1264 resp, err := client.Do(req) 1265 if err != nil { 1266 c.Fatalf("error starting container with chunked encoding: %v", err) 1267 } 1268 resp.Body.Close() 1269 if resp.StatusCode != 204 { 1270 c.Fatalf("expected status code 204, got %d", resp.StatusCode) 1271 } 1272 1273 out, err = inspectFieldJSON(id, "HostConfig.Binds") 1274 if err != nil { 1275 c.Fatal(err) 1276 } 1277 1278 var binds []string 1279 if err := json.NewDecoder(strings.NewReader(out)).Decode(&binds); err != nil { 1280 c.Fatal(err) 1281 } 1282 if len(binds) != 1 { 1283 c.Fatalf("got unexpected binds: %v", binds) 1284 } 1285 1286 expected := "/tmp:/foo" 1287 if binds[0] != expected { 1288 c.Fatalf("got incorrect bind spec, wanted %s, got: %s", expected, binds[0]) 1289 } 1290 } 1291 1292 func (s *DockerSuite) TestPostContainerStop(c *check.C) { 1293 out, _ := dockerCmd(c, "run", "-d", "busybox", "top") 1294 1295 containerID := strings.TrimSpace(out) 1296 c.Assert(waitRun(containerID), check.IsNil) 1297 1298 statusCode, _, err := sockRequest("POST", "/containers/"+containerID+"/stop", nil) 1299 c.Assert(err, check.IsNil) 1300 // 204 No Content is expected, not 200 1301 c.Assert(statusCode, check.Equals, http.StatusNoContent) 1302 1303 if err := waitInspect(containerID, "{{ .State.Running }}", "false", 5); err != nil { 1304 c.Fatal(err) 1305 } 1306 } 1307 1308 // #14170 1309 func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceEntrypoint(c *check.C) { 1310 config := struct { 1311 Image string 1312 Entrypoint string 1313 Cmd []string 1314 }{"busybox", "echo", []string{"hello", "world"}} 1315 _, _, err := sockRequest("POST", "/containers/create?name=echotest", config) 1316 c.Assert(err, check.IsNil) 1317 out, _ := dockerCmd(c, "start", "-a", "echotest") 1318 c.Assert(strings.TrimSpace(out), check.Equals, "hello world") 1319 1320 config2 := struct { 1321 Image string 1322 Entrypoint []string 1323 Cmd []string 1324 }{"busybox", []string{"echo"}, []string{"hello", "world"}} 1325 _, _, err = sockRequest("POST", "/containers/create?name=echotest2", config2) 1326 c.Assert(err, check.IsNil) 1327 out, _ = dockerCmd(c, "start", "-a", "echotest2") 1328 c.Assert(strings.TrimSpace(out), check.Equals, "hello world") 1329 } 1330 1331 // #14170 1332 func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCmd(c *check.C) { 1333 config := struct { 1334 Image string 1335 Entrypoint string 1336 Cmd string 1337 }{"busybox", "echo", "hello world"} 1338 _, _, err := sockRequest("POST", "/containers/create?name=echotest", config) 1339 c.Assert(err, check.IsNil) 1340 out, _ := dockerCmd(c, "start", "-a", "echotest") 1341 c.Assert(strings.TrimSpace(out), check.Equals, "hello world") 1342 1343 config2 := struct { 1344 Image string 1345 Cmd []string 1346 }{"busybox", []string{"echo", "hello", "world"}} 1347 _, _, err = sockRequest("POST", "/containers/create?name=echotest2", config2) 1348 c.Assert(err, check.IsNil) 1349 out, _ = dockerCmd(c, "start", "-a", "echotest2") 1350 c.Assert(strings.TrimSpace(out), check.Equals, "hello world") 1351 } 1352 1353 // regression #14318 1354 func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCapAddDrop(c *check.C) { 1355 config := struct { 1356 Image string 1357 CapAdd string 1358 CapDrop string 1359 }{"busybox", "NET_ADMIN", "SYS_ADMIN"} 1360 status, _, err := sockRequest("POST", "/containers/create?name=capaddtest0", config) 1361 c.Assert(err, check.IsNil) 1362 c.Assert(status, check.Equals, http.StatusCreated) 1363 1364 config2 := struct { 1365 Image string 1366 CapAdd []string 1367 CapDrop []string 1368 }{"busybox", []string{"NET_ADMIN", "SYS_ADMIN"}, []string{"SETGID"}} 1369 status, _, err = sockRequest("POST", "/containers/create?name=capaddtest1", config2) 1370 c.Assert(err, check.IsNil) 1371 c.Assert(status, check.Equals, http.StatusCreated) 1372 } 1373 1374 // #14640 1375 func (s *DockerSuite) TestPostContainersStartWithoutLinksInHostConfig(c *check.C) { 1376 name := "test-host-config-links" 1377 dockerCmd(c, "create", "--name", name, "busybox", "top") 1378 1379 hc, err := inspectFieldJSON(name, "HostConfig") 1380 c.Assert(err, check.IsNil) 1381 config := `{"HostConfig":` + hc + `}` 1382 1383 res, b, err := sockRequestRaw("POST", "/containers/"+name+"/start", strings.NewReader(config), "application/json") 1384 c.Assert(err, check.IsNil) 1385 c.Assert(res.StatusCode, check.Equals, http.StatusNoContent) 1386 b.Close() 1387 } 1388 1389 // #14640 1390 func (s *DockerSuite) TestPostContainersStartWithLinksInHostConfig(c *check.C) { 1391 name := "test-host-config-links" 1392 dockerCmd(c, "run", "--name", "foo", "-d", "busybox", "top") 1393 dockerCmd(c, "create", "--name", name, "--link", "foo:bar", "busybox", "top") 1394 1395 hc, err := inspectFieldJSON(name, "HostConfig") 1396 c.Assert(err, check.IsNil) 1397 config := `{"HostConfig":` + hc + `}` 1398 1399 res, b, err := sockRequestRaw("POST", "/containers/"+name+"/start", strings.NewReader(config), "application/json") 1400 c.Assert(err, check.IsNil) 1401 c.Assert(res.StatusCode, check.Equals, http.StatusNoContent) 1402 b.Close() 1403 } 1404 1405 // #14640 1406 func (s *DockerSuite) TestPostContainersStartWithLinksInHostConfigIdLinked(c *check.C) { 1407 name := "test-host-config-links" 1408 out, _ := dockerCmd(c, "run", "--name", "link0", "-d", "busybox", "top") 1409 id := strings.TrimSpace(out) 1410 dockerCmd(c, "create", "--name", name, "--link", id, "busybox", "top") 1411 1412 hc, err := inspectFieldJSON(name, "HostConfig") 1413 c.Assert(err, check.IsNil) 1414 config := `{"HostConfig":` + hc + `}` 1415 1416 res, b, err := sockRequestRaw("POST", "/containers/"+name+"/start", strings.NewReader(config), "application/json") 1417 c.Assert(err, check.IsNil) 1418 c.Assert(res.StatusCode, check.Equals, http.StatusNoContent) 1419 b.Close() 1420 } 1421 1422 // #14915 1423 func (s *DockerSuite) TestContainersApiCreateNoHostConfig118(c *check.C) { 1424 config := struct { 1425 Image string 1426 }{"busybox"} 1427 status, _, err := sockRequest("POST", "/v1.18/containers/create", config) 1428 c.Assert(err, check.IsNil) 1429 c.Assert(status, check.Equals, http.StatusCreated) 1430 } 1431 1432 // Ensure an error occurs when you have a container read-only rootfs but you 1433 // extract an archive to a symlink in a writable volume which points to a 1434 // directory outside of the volume. 1435 func (s *DockerSuite) TestPutContainerArchiveErrSymlinkInVolumeToReadOnlyRootfs(c *check.C) { 1436 testRequires(c, SameHostDaemon) // Requires local volume mount bind. 1437 1438 testVol := getTestDir(c, "test-put-container-archive-err-symlink-in-volume-to-read-only-rootfs-") 1439 defer os.RemoveAll(testVol) 1440 1441 makeTestContentInDir(c, testVol) 1442 1443 cID := makeTestContainer(c, testContainerOptions{ 1444 readOnly: true, 1445 volumes: defaultVolumes(testVol), // Our bind mount is at /vol2 1446 }) 1447 defer deleteContainer(cID) 1448 1449 // Attempt to extract to a symlink in the volume which points to a 1450 // directory outside the volume. This should cause an error because the 1451 // rootfs is read-only. 1452 query := make(url.Values, 1) 1453 query.Set("path", "/vol2/symlinkToAbsDir") 1454 urlPath := fmt.Sprintf("/v1.20/containers/%s/archive?%s", cID, query.Encode()) 1455 1456 statusCode, body, err := sockRequest("PUT", urlPath, nil) 1457 c.Assert(err, check.IsNil) 1458 1459 if !isCpCannotCopyReadOnly(fmt.Errorf(string(body))) { 1460 c.Fatalf("expected ErrContainerRootfsReadonly error, but got %d: %s", statusCode, string(body)) 1461 } 1462 }