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