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