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