github.com/walkingsparrow/docker@v1.4.2-0.20151218153551-b708a2249bfa/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/api/types" 19 "github.com/docker/docker/pkg/integration" 20 "github.com/docker/docker/pkg/integration/checker" 21 "github.com/docker/docker/pkg/stringid" 22 "github.com/docker/docker/runconfig" 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) TestContainerApiCreateNetworkMode(c *check.C) { 656 testRequires(c, DaemonIsLinux) 657 UtilCreateNetworkMode(c, "host") 658 UtilCreateNetworkMode(c, "bridge") 659 UtilCreateNetworkMode(c, "container:web1") 660 } 661 662 func UtilCreateNetworkMode(c *check.C, networkMode string) { 663 config := map[string]interface{}{ 664 "Image": "busybox", 665 "HostConfig": map[string]interface{}{"NetworkMode": networkMode}, 666 } 667 668 status, body, err := sockRequest("POST", "/containers/create", config) 669 c.Assert(err, checker.IsNil) 670 c.Assert(status, checker.Equals, http.StatusCreated) 671 672 var container types.ContainerCreateResponse 673 c.Assert(json.Unmarshal(body, &container), checker.IsNil) 674 675 status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil) 676 c.Assert(err, checker.IsNil) 677 c.Assert(status, checker.Equals, http.StatusOK) 678 679 var containerJSON types.ContainerJSON 680 c.Assert(json.Unmarshal(body, &containerJSON), checker.IsNil) 681 c.Assert(containerJSON.HostConfig.NetworkMode, checker.Equals, runconfig.NetworkMode(networkMode), check.Commentf("Mismatched NetworkMode")) 682 } 683 684 func (s *DockerSuite) TestContainerApiCreateWithCpuSharesCpuset(c *check.C) { 685 testRequires(c, DaemonIsLinux) 686 config := map[string]interface{}{ 687 "Image": "busybox", 688 "CpuShares": 512, 689 "CpusetCpus": "0", 690 } 691 692 status, body, err := sockRequest("POST", "/containers/create", config) 693 c.Assert(err, checker.IsNil) 694 c.Assert(status, checker.Equals, http.StatusCreated) 695 696 var container types.ContainerCreateResponse 697 c.Assert(json.Unmarshal(body, &container), checker.IsNil) 698 699 status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil) 700 c.Assert(err, checker.IsNil) 701 c.Assert(status, checker.Equals, http.StatusOK) 702 703 var containerJSON types.ContainerJSON 704 705 c.Assert(json.Unmarshal(body, &containerJSON), checker.IsNil) 706 707 out, err := inspectField(containerJSON.ID, "HostConfig.CpuShares") 708 c.Assert(err, checker.IsNil) 709 c.Assert(out, checker.Equals, "512") 710 711 outCpuset, errCpuset := inspectField(containerJSON.ID, "HostConfig.CpusetCpus") 712 c.Assert(errCpuset, checker.IsNil, check.Commentf("Output: %s", outCpuset)) 713 c.Assert(outCpuset, checker.Equals, "0") 714 } 715 716 func (s *DockerSuite) TestContainerApiVerifyHeader(c *check.C) { 717 testRequires(c, DaemonIsLinux) 718 config := map[string]interface{}{ 719 "Image": "busybox", 720 } 721 722 create := func(ct string) (*http.Response, io.ReadCloser, error) { 723 jsonData := bytes.NewBuffer(nil) 724 c.Assert(json.NewEncoder(jsonData).Encode(config), checker.IsNil) 725 return sockRequestRaw("POST", "/containers/create", jsonData, ct) 726 } 727 728 // Try with no content-type 729 res, body, err := create("") 730 c.Assert(err, checker.IsNil) 731 c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError) 732 body.Close() 733 734 // Try with wrong content-type 735 res, body, err = create("application/xml") 736 c.Assert(err, checker.IsNil) 737 c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError) 738 body.Close() 739 740 // now application/json 741 res, body, err = create("application/json") 742 c.Assert(err, checker.IsNil) 743 c.Assert(res.StatusCode, checker.Equals, http.StatusCreated) 744 body.Close() 745 } 746 747 //Issue 14230. daemon should return 500 for invalid port syntax 748 func (s *DockerSuite) TestContainerApiInvalidPortSyntax(c *check.C) { 749 testRequires(c, DaemonIsLinux) 750 config := `{ 751 "Image": "busybox", 752 "HostConfig": { 753 "PortBindings": { 754 "19039;1230": [ 755 {} 756 ] 757 } 758 } 759 }` 760 761 res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json") 762 c.Assert(err, checker.IsNil) 763 c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError) 764 765 b, err := readBody(body) 766 c.Assert(err, checker.IsNil) 767 c.Assert(string(b[:]), checker.Contains, "Invalid port") 768 } 769 770 // Issue 7941 - test to make sure a "null" in JSON is just ignored. 771 // W/o this fix a null in JSON would be parsed into a string var as "null" 772 func (s *DockerSuite) TestContainerApiPostCreateNull(c *check.C) { 773 testRequires(c, DaemonIsLinux) 774 config := `{ 775 "Hostname":"", 776 "Domainname":"", 777 "Memory":0, 778 "MemorySwap":0, 779 "CpuShares":0, 780 "Cpuset":null, 781 "AttachStdin":true, 782 "AttachStdout":true, 783 "AttachStderr":true, 784 "ExposedPorts":{}, 785 "Tty":true, 786 "OpenStdin":true, 787 "StdinOnce":true, 788 "Env":[], 789 "Cmd":"ls", 790 "Image":"busybox", 791 "Volumes":{}, 792 "WorkingDir":"", 793 "Entrypoint":null, 794 "NetworkDisabled":false, 795 "OnBuild":null}` 796 797 res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json") 798 c.Assert(err, checker.IsNil) 799 c.Assert(res.StatusCode, checker.Equals, http.StatusCreated) 800 801 b, err := readBody(body) 802 c.Assert(err, checker.IsNil) 803 type createResp struct { 804 ID string 805 } 806 var container createResp 807 c.Assert(json.Unmarshal(b, &container), checker.IsNil) 808 809 out, err := inspectField(container.ID, "HostConfig.CpusetCpus") 810 c.Assert(err, checker.IsNil) 811 c.Assert(out, checker.Equals, "") 812 813 outMemory, errMemory := inspectField(container.ID, "HostConfig.Memory") 814 c.Assert(outMemory, checker.Equals, "0") 815 c.Assert(errMemory, checker.IsNil) 816 outMemorySwap, errMemorySwap := inspectField(container.ID, "HostConfig.MemorySwap") 817 c.Assert(outMemorySwap, checker.Equals, "0") 818 c.Assert(errMemorySwap, checker.IsNil) 819 } 820 821 func (s *DockerSuite) TestCreateWithTooLowMemoryLimit(c *check.C) { 822 testRequires(c, DaemonIsLinux) 823 config := `{ 824 "Image": "busybox", 825 "Cmd": "ls", 826 "OpenStdin": true, 827 "CpuShares": 100, 828 "Memory": 524287 829 }` 830 831 res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json") 832 c.Assert(err, checker.IsNil) 833 b, err2 := readBody(body) 834 c.Assert(err2, checker.IsNil) 835 836 c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError) 837 c.Assert(string(b), checker.Contains, "Minimum memory limit allowed is 4MB") 838 } 839 840 func (s *DockerSuite) TestStartWithTooLowMemoryLimit(c *check.C) { 841 testRequires(c, DaemonIsLinux) 842 out, _ := dockerCmd(c, "create", "busybox") 843 844 containerID := strings.TrimSpace(out) 845 846 config := `{ 847 "CpuShares": 100, 848 "Memory": 524287 849 }` 850 851 res, body, err := sockRequestRaw("POST", "/containers/"+containerID+"/start", strings.NewReader(config), "application/json") 852 c.Assert(err, checker.IsNil) 853 b, err2 := readBody(body) 854 c.Assert(err2, checker.IsNil) 855 c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError) 856 c.Assert(string(b), checker.Contains, "Minimum memory limit allowed is 4MB") 857 } 858 859 func (s *DockerSuite) TestContainerApiRename(c *check.C) { 860 testRequires(c, DaemonIsLinux) 861 out, _ := dockerCmd(c, "run", "--name", "TestContainerApiRename", "-d", "busybox", "sh") 862 863 containerID := strings.TrimSpace(out) 864 newName := "TestContainerApiRenameNew" 865 statusCode, _, err := sockRequest("POST", "/containers/"+containerID+"/rename?name="+newName, nil) 866 c.Assert(err, checker.IsNil) 867 // 204 No Content is expected, not 200 868 c.Assert(statusCode, checker.Equals, http.StatusNoContent) 869 870 name, err := inspectField(containerID, "Name") 871 c.Assert(name, checker.Equals, "/"+newName, check.Commentf("Failed to rename container")) 872 } 873 874 func (s *DockerSuite) TestContainerApiKill(c *check.C) { 875 testRequires(c, DaemonIsLinux) 876 name := "test-api-kill" 877 dockerCmd(c, "run", "-di", "--name", name, "busybox", "top") 878 879 status, _, err := sockRequest("POST", "/containers/"+name+"/kill", nil) 880 c.Assert(err, checker.IsNil) 881 c.Assert(status, checker.Equals, http.StatusNoContent) 882 883 state, err := inspectField(name, "State.Running") 884 c.Assert(err, checker.IsNil) 885 c.Assert(state, checker.Equals, "false", check.Commentf("got wrong State from container %s: %q", name, state)) 886 } 887 888 func (s *DockerSuite) TestContainerApiRestart(c *check.C) { 889 testRequires(c, DaemonIsLinux) 890 name := "test-api-restart" 891 dockerCmd(c, "run", "-di", "--name", name, "busybox", "top") 892 893 status, _, err := sockRequest("POST", "/containers/"+name+"/restart?t=1", nil) 894 c.Assert(err, checker.IsNil) 895 c.Assert(status, checker.Equals, http.StatusNoContent) 896 c.Assert(waitInspect(name, "{{ .State.Restarting }} {{ .State.Running }}", "false true", 5*time.Second), checker.IsNil) 897 } 898 899 func (s *DockerSuite) TestContainerApiRestartNotimeoutParam(c *check.C) { 900 testRequires(c, DaemonIsLinux) 901 name := "test-api-restart-no-timeout-param" 902 out, _ := dockerCmd(c, "run", "-di", "--name", name, "busybox", "top") 903 id := strings.TrimSpace(out) 904 c.Assert(waitRun(id), checker.IsNil) 905 906 status, _, err := sockRequest("POST", "/containers/"+name+"/restart", nil) 907 c.Assert(err, checker.IsNil) 908 c.Assert(status, checker.Equals, http.StatusNoContent) 909 c.Assert(waitInspect(name, "{{ .State.Restarting }} {{ .State.Running }}", "false true", 5*time.Second), checker.IsNil) 910 } 911 912 func (s *DockerSuite) TestContainerApiStart(c *check.C) { 913 testRequires(c, DaemonIsLinux) 914 name := "testing-start" 915 config := map[string]interface{}{ 916 "Image": "busybox", 917 "Cmd": []string{"/bin/sh", "-c", "/bin/top"}, 918 "OpenStdin": true, 919 } 920 921 status, _, err := sockRequest("POST", "/containers/create?name="+name, config) 922 c.Assert(err, checker.IsNil) 923 c.Assert(status, checker.Equals, http.StatusCreated) 924 925 conf := make(map[string]interface{}) 926 status, _, err = sockRequest("POST", "/containers/"+name+"/start", conf) 927 c.Assert(err, checker.IsNil) 928 c.Assert(status, checker.Equals, http.StatusNoContent) 929 930 // second call to start should give 304 931 status, _, err = sockRequest("POST", "/containers/"+name+"/start", conf) 932 c.Assert(err, checker.IsNil) 933 c.Assert(status, checker.Equals, http.StatusNotModified) 934 } 935 936 func (s *DockerSuite) TestContainerApiStop(c *check.C) { 937 testRequires(c, DaemonIsLinux) 938 name := "test-api-stop" 939 dockerCmd(c, "run", "-di", "--name", name, "busybox", "top") 940 941 status, _, err := sockRequest("POST", "/containers/"+name+"/stop?t=1", nil) 942 c.Assert(err, checker.IsNil) 943 c.Assert(status, checker.Equals, http.StatusNoContent) 944 c.Assert(waitInspect(name, "{{ .State.Running }}", "false", 5*time.Second), checker.IsNil) 945 946 // second call to start should give 304 947 status, _, err = sockRequest("POST", "/containers/"+name+"/stop?t=1", nil) 948 c.Assert(err, checker.IsNil) 949 c.Assert(status, checker.Equals, http.StatusNotModified) 950 } 951 952 func (s *DockerSuite) TestContainerApiWait(c *check.C) { 953 testRequires(c, DaemonIsLinux) 954 name := "test-api-wait" 955 dockerCmd(c, "run", "--name", name, "busybox", "sleep", "5") 956 957 status, body, err := sockRequest("POST", "/containers/"+name+"/wait", nil) 958 c.Assert(err, checker.IsNil) 959 c.Assert(status, checker.Equals, http.StatusOK) 960 c.Assert(waitInspect(name, "{{ .State.Running }}", "false", 5*time.Second), checker.IsNil) 961 962 var waitres types.ContainerWaitResponse 963 c.Assert(json.Unmarshal(body, &waitres), checker.IsNil) 964 c.Assert(waitres.StatusCode, checker.Equals, 0) 965 } 966 967 func (s *DockerSuite) TestContainerApiCopy(c *check.C) { 968 testRequires(c, DaemonIsLinux) 969 name := "test-container-api-copy" 970 dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt") 971 972 postData := types.CopyConfig{ 973 Resource: "/test.txt", 974 } 975 976 status, body, err := sockRequest("POST", "/containers/"+name+"/copy", postData) 977 c.Assert(err, checker.IsNil) 978 c.Assert(status, checker.Equals, http.StatusOK) 979 980 found := false 981 for tarReader := tar.NewReader(bytes.NewReader(body)); ; { 982 h, err := tarReader.Next() 983 if err != nil { 984 if err == io.EOF { 985 break 986 } 987 c.Fatal(err) 988 } 989 if h.Name == "test.txt" { 990 found = true 991 break 992 } 993 } 994 c.Assert(found, checker.True) 995 } 996 997 func (s *DockerSuite) TestContainerApiCopyResourcePathEmpty(c *check.C) { 998 testRequires(c, DaemonIsLinux) 999 name := "test-container-api-copy-resource-empty" 1000 dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt") 1001 1002 postData := types.CopyConfig{ 1003 Resource: "", 1004 } 1005 1006 status, body, err := sockRequest("POST", "/containers/"+name+"/copy", postData) 1007 c.Assert(err, checker.IsNil) 1008 c.Assert(status, checker.Equals, http.StatusInternalServerError) 1009 c.Assert(string(body), checker.Matches, "Path cannot be empty\n") 1010 } 1011 1012 func (s *DockerSuite) TestContainerApiCopyResourcePathNotFound(c *check.C) { 1013 testRequires(c, DaemonIsLinux) 1014 name := "test-container-api-copy-resource-not-found" 1015 dockerCmd(c, "run", "--name", name, "busybox") 1016 1017 postData := types.CopyConfig{ 1018 Resource: "/notexist", 1019 } 1020 1021 status, body, err := sockRequest("POST", "/containers/"+name+"/copy", postData) 1022 c.Assert(err, checker.IsNil) 1023 c.Assert(status, checker.Equals, http.StatusInternalServerError) 1024 c.Assert(string(body), checker.Matches, "Could not find the file /notexist in container "+name+"\n") 1025 } 1026 1027 func (s *DockerSuite) TestContainerApiCopyContainerNotFound(c *check.C) { 1028 postData := types.CopyConfig{ 1029 Resource: "/something", 1030 } 1031 1032 status, _, err := sockRequest("POST", "/containers/notexists/copy", postData) 1033 c.Assert(err, checker.IsNil) 1034 c.Assert(status, checker.Equals, http.StatusNotFound) 1035 } 1036 1037 func (s *DockerSuite) TestContainerApiDelete(c *check.C) { 1038 testRequires(c, DaemonIsLinux) 1039 out, _ := dockerCmd(c, "run", "-d", "busybox", "top") 1040 1041 id := strings.TrimSpace(out) 1042 c.Assert(waitRun(id), checker.IsNil) 1043 1044 dockerCmd(c, "stop", id) 1045 1046 status, _, err := sockRequest("DELETE", "/containers/"+id, nil) 1047 c.Assert(err, checker.IsNil) 1048 c.Assert(status, checker.Equals, http.StatusNoContent) 1049 } 1050 1051 func (s *DockerSuite) TestContainerApiDeleteNotExist(c *check.C) { 1052 status, body, err := sockRequest("DELETE", "/containers/doesnotexist", nil) 1053 c.Assert(err, checker.IsNil) 1054 c.Assert(status, checker.Equals, http.StatusNotFound) 1055 c.Assert(string(body), checker.Matches, "No such container: doesnotexist\n") 1056 } 1057 1058 func (s *DockerSuite) TestContainerApiDeleteForce(c *check.C) { 1059 testRequires(c, DaemonIsLinux) 1060 out, _ := dockerCmd(c, "run", "-d", "busybox", "top") 1061 1062 id := strings.TrimSpace(out) 1063 c.Assert(waitRun(id), checker.IsNil) 1064 1065 status, _, err := sockRequest("DELETE", "/containers/"+id+"?force=1", nil) 1066 c.Assert(err, checker.IsNil) 1067 c.Assert(status, checker.Equals, http.StatusNoContent) 1068 } 1069 1070 func (s *DockerSuite) TestContainerApiDeleteRemoveLinks(c *check.C) { 1071 testRequires(c, DaemonIsLinux) 1072 out, _ := dockerCmd(c, "run", "-d", "--name", "tlink1", "busybox", "top") 1073 1074 id := strings.TrimSpace(out) 1075 c.Assert(waitRun(id), checker.IsNil) 1076 1077 out, _ = dockerCmd(c, "run", "--link", "tlink1:tlink1", "--name", "tlink2", "-d", "busybox", "top") 1078 1079 id2 := strings.TrimSpace(out) 1080 c.Assert(waitRun(id2), checker.IsNil) 1081 1082 links, err := inspectFieldJSON(id2, "HostConfig.Links") 1083 c.Assert(err, checker.IsNil) 1084 c.Assert(links, checker.Equals, "[\"/tlink1:/tlink2/tlink1\"]", check.Commentf("expected to have links between containers")) 1085 1086 status, _, err := sockRequest("DELETE", "/containers/tlink2/tlink1?link=1", nil) 1087 c.Assert(err, checker.IsNil) 1088 c.Assert(status, checker.Equals, http.StatusNoContent) 1089 1090 linksPostRm, err := inspectFieldJSON(id2, "HostConfig.Links") 1091 c.Assert(err, checker.IsNil) 1092 c.Assert(linksPostRm, checker.Equals, "null", check.Commentf("call to api deleteContainer links should have removed the specified links")) 1093 } 1094 1095 func (s *DockerSuite) TestContainerApiDeleteConflict(c *check.C) { 1096 testRequires(c, DaemonIsLinux) 1097 out, _ := dockerCmd(c, "run", "-d", "busybox", "top") 1098 1099 id := strings.TrimSpace(out) 1100 c.Assert(waitRun(id), checker.IsNil) 1101 1102 status, _, err := sockRequest("DELETE", "/containers/"+id, nil) 1103 c.Assert(err, checker.IsNil) 1104 c.Assert(status, checker.Equals, http.StatusConflict) 1105 } 1106 1107 func (s *DockerSuite) TestContainerApiDeleteRemoveVolume(c *check.C) { 1108 testRequires(c, DaemonIsLinux) 1109 testRequires(c, SameHostDaemon) 1110 1111 out, _ := dockerCmd(c, "run", "-d", "-v", "/testvolume", "busybox", "top") 1112 1113 id := strings.TrimSpace(out) 1114 c.Assert(waitRun(id), checker.IsNil) 1115 1116 source, err := inspectMountSourceField(id, "/testvolume") 1117 _, err = os.Stat(source) 1118 c.Assert(err, checker.IsNil) 1119 1120 status, _, err := sockRequest("DELETE", "/containers/"+id+"?v=1&force=1", nil) 1121 c.Assert(err, checker.IsNil) 1122 c.Assert(status, checker.Equals, http.StatusNoContent) 1123 _, err = os.Stat(source) 1124 c.Assert(os.IsNotExist(err), checker.True, check.Commentf("expected to get ErrNotExist error, got %v", err)) 1125 } 1126 1127 // Regression test for https://github.com/docker/docker/issues/6231 1128 func (s *DockerSuite) TestContainersApiChunkedEncoding(c *check.C) { 1129 testRequires(c, DaemonIsLinux) 1130 out, _ := dockerCmd(c, "create", "-v", "/foo", "busybox", "true") 1131 id := strings.TrimSpace(out) 1132 1133 conn, err := sockConn(time.Duration(10 * time.Second)) 1134 c.Assert(err, checker.IsNil) 1135 client := httputil.NewClientConn(conn, nil) 1136 defer client.Close() 1137 1138 bindCfg := strings.NewReader(`{"Binds": ["/tmp:/foo"]}`) 1139 req, err := http.NewRequest("POST", "/containers/"+id+"/start", bindCfg) 1140 c.Assert(err, checker.IsNil) 1141 req.Header.Set("Content-Type", "application/json") 1142 // This is a cheat to make the http request do chunked encoding 1143 // Otherwise (just setting the Content-Encoding to chunked) net/http will overwrite 1144 // https://golang.org/src/pkg/net/http/request.go?s=11980:12172 1145 req.ContentLength = -1 1146 1147 resp, err := client.Do(req) 1148 c.Assert(err, checker.IsNil, check.Commentf("error starting container with chunked encoding")) 1149 resp.Body.Close() 1150 c.Assert(resp.StatusCode, checker.Equals, 204) 1151 1152 out, err = inspectFieldJSON(id, "HostConfig.Binds") 1153 c.Assert(err, checker.IsNil) 1154 1155 var binds []string 1156 c.Assert(json.NewDecoder(strings.NewReader(out)).Decode(&binds), checker.IsNil) 1157 c.Assert(binds, checker.HasLen, 1, check.Commentf("Got unexpected binds: %v", binds)) 1158 1159 expected := "/tmp:/foo" 1160 c.Assert(binds[0], checker.Equals, expected, check.Commentf("got incorrect bind spec")) 1161 } 1162 1163 func (s *DockerSuite) TestPostContainerStop(c *check.C) { 1164 testRequires(c, DaemonIsLinux) 1165 out, _ := dockerCmd(c, "run", "-d", "busybox", "top") 1166 1167 containerID := strings.TrimSpace(out) 1168 c.Assert(waitRun(containerID), checker.IsNil) 1169 1170 statusCode, _, err := sockRequest("POST", "/containers/"+containerID+"/stop", nil) 1171 c.Assert(err, checker.IsNil) 1172 // 204 No Content is expected, not 200 1173 c.Assert(statusCode, checker.Equals, http.StatusNoContent) 1174 c.Assert(waitInspect(containerID, "{{ .State.Running }}", "false", 5*time.Second), checker.IsNil) 1175 } 1176 1177 // #14170 1178 func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceEntrypoint(c *check.C) { 1179 testRequires(c, DaemonIsLinux) 1180 config := struct { 1181 Image string 1182 Entrypoint string 1183 Cmd []string 1184 }{"busybox", "echo", []string{"hello", "world"}} 1185 _, _, err := sockRequest("POST", "/containers/create?name=echotest", config) 1186 c.Assert(err, checker.IsNil) 1187 out, _ := dockerCmd(c, "start", "-a", "echotest") 1188 c.Assert(strings.TrimSpace(out), checker.Equals, "hello world") 1189 1190 config2 := struct { 1191 Image string 1192 Entrypoint []string 1193 Cmd []string 1194 }{"busybox", []string{"echo"}, []string{"hello", "world"}} 1195 _, _, err = sockRequest("POST", "/containers/create?name=echotest2", config2) 1196 c.Assert(err, checker.IsNil) 1197 out, _ = dockerCmd(c, "start", "-a", "echotest2") 1198 c.Assert(strings.TrimSpace(out), checker.Equals, "hello world") 1199 } 1200 1201 // #14170 1202 func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCmd(c *check.C) { 1203 testRequires(c, DaemonIsLinux) 1204 config := struct { 1205 Image string 1206 Entrypoint string 1207 Cmd string 1208 }{"busybox", "echo", "hello world"} 1209 _, _, err := sockRequest("POST", "/containers/create?name=echotest", config) 1210 c.Assert(err, checker.IsNil) 1211 out, _ := dockerCmd(c, "start", "-a", "echotest") 1212 c.Assert(strings.TrimSpace(out), checker.Equals, "hello world") 1213 1214 config2 := struct { 1215 Image string 1216 Cmd []string 1217 }{"busybox", []string{"echo", "hello", "world"}} 1218 _, _, err = sockRequest("POST", "/containers/create?name=echotest2", config2) 1219 c.Assert(err, checker.IsNil) 1220 out, _ = dockerCmd(c, "start", "-a", "echotest2") 1221 c.Assert(strings.TrimSpace(out), checker.Equals, "hello world") 1222 } 1223 1224 // regression #14318 1225 func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCapAddDrop(c *check.C) { 1226 testRequires(c, DaemonIsLinux) 1227 config := struct { 1228 Image string 1229 CapAdd string 1230 CapDrop string 1231 }{"busybox", "NET_ADMIN", "SYS_ADMIN"} 1232 status, _, err := sockRequest("POST", "/containers/create?name=capaddtest0", config) 1233 c.Assert(err, checker.IsNil) 1234 c.Assert(status, checker.Equals, http.StatusCreated) 1235 1236 config2 := struct { 1237 Image string 1238 CapAdd []string 1239 CapDrop []string 1240 }{"busybox", []string{"NET_ADMIN", "SYS_ADMIN"}, []string{"SETGID"}} 1241 status, _, err = sockRequest("POST", "/containers/create?name=capaddtest1", config2) 1242 c.Assert(err, checker.IsNil) 1243 c.Assert(status, checker.Equals, http.StatusCreated) 1244 } 1245 1246 // #14640 1247 func (s *DockerSuite) TestPostContainersStartWithoutLinksInHostConfig(c *check.C) { 1248 testRequires(c, DaemonIsLinux) 1249 name := "test-host-config-links" 1250 dockerCmd(c, "create", "--name", name, "busybox", "top") 1251 1252 hc, err := inspectFieldJSON(name, "HostConfig") 1253 c.Assert(err, checker.IsNil) 1254 config := `{"HostConfig":` + hc + `}` 1255 1256 res, b, err := sockRequestRaw("POST", "/containers/"+name+"/start", strings.NewReader(config), "application/json") 1257 c.Assert(err, checker.IsNil) 1258 c.Assert(res.StatusCode, checker.Equals, http.StatusNoContent) 1259 b.Close() 1260 } 1261 1262 // #14640 1263 func (s *DockerSuite) TestPostContainersStartWithLinksInHostConfig(c *check.C) { 1264 testRequires(c, DaemonIsLinux) 1265 name := "test-host-config-links" 1266 dockerCmd(c, "run", "--name", "foo", "-d", "busybox", "top") 1267 dockerCmd(c, "create", "--name", name, "--link", "foo:bar", "busybox", "top") 1268 1269 hc, err := inspectFieldJSON(name, "HostConfig") 1270 c.Assert(err, checker.IsNil) 1271 config := `{"HostConfig":` + hc + `}` 1272 1273 res, b, err := sockRequestRaw("POST", "/containers/"+name+"/start", strings.NewReader(config), "application/json") 1274 c.Assert(err, checker.IsNil) 1275 c.Assert(res.StatusCode, checker.Equals, http.StatusNoContent) 1276 b.Close() 1277 } 1278 1279 // #14640 1280 func (s *DockerSuite) TestPostContainersStartWithLinksInHostConfigIdLinked(c *check.C) { 1281 testRequires(c, DaemonIsLinux) 1282 name := "test-host-config-links" 1283 out, _ := dockerCmd(c, "run", "--name", "link0", "-d", "busybox", "top") 1284 id := strings.TrimSpace(out) 1285 dockerCmd(c, "create", "--name", name, "--link", id, "busybox", "top") 1286 1287 hc, err := inspectFieldJSON(name, "HostConfig") 1288 c.Assert(err, checker.IsNil) 1289 config := `{"HostConfig":` + hc + `}` 1290 1291 res, b, err := sockRequestRaw("POST", "/containers/"+name+"/start", strings.NewReader(config), "application/json") 1292 c.Assert(err, checker.IsNil) 1293 c.Assert(res.StatusCode, checker.Equals, http.StatusNoContent) 1294 b.Close() 1295 } 1296 1297 // #14915 1298 func (s *DockerSuite) TestContainersApiCreateNoHostConfig118(c *check.C) { 1299 testRequires(c, DaemonIsLinux) 1300 config := struct { 1301 Image string 1302 }{"busybox"} 1303 status, _, err := sockRequest("POST", "/v1.18/containers/create", config) 1304 c.Assert(err, checker.IsNil) 1305 c.Assert(status, checker.Equals, http.StatusCreated) 1306 } 1307 1308 // Ensure an error occurs when you have a container read-only rootfs but you 1309 // extract an archive to a symlink in a writable volume which points to a 1310 // directory outside of the volume. 1311 func (s *DockerSuite) TestPutContainerArchiveErrSymlinkInVolumeToReadOnlyRootfs(c *check.C) { 1312 // Requires local volume mount bind. 1313 // --read-only + userns has remount issues 1314 testRequires(c, SameHostDaemon, NotUserNamespace) 1315 1316 testVol := getTestDir(c, "test-put-container-archive-err-symlink-in-volume-to-read-only-rootfs-") 1317 defer os.RemoveAll(testVol) 1318 1319 makeTestContentInDir(c, testVol) 1320 1321 cID := makeTestContainer(c, testContainerOptions{ 1322 readOnly: true, 1323 volumes: defaultVolumes(testVol), // Our bind mount is at /vol2 1324 }) 1325 defer deleteContainer(cID) 1326 1327 // Attempt to extract to a symlink in the volume which points to a 1328 // directory outside the volume. This should cause an error because the 1329 // rootfs is read-only. 1330 query := make(url.Values, 1) 1331 query.Set("path", "/vol2/symlinkToAbsDir") 1332 urlPath := fmt.Sprintf("/v1.20/containers/%s/archive?%s", cID, query.Encode()) 1333 1334 statusCode, body, err := sockRequest("PUT", urlPath, nil) 1335 c.Assert(err, checker.IsNil) 1336 1337 if !isCpCannotCopyReadOnly(fmt.Errorf(string(body))) { 1338 c.Fatalf("expected ErrContainerRootfsReadonly error, but got %d: %s", statusCode, string(body)) 1339 } 1340 } 1341 1342 func (s *DockerSuite) TestContainersApiGetContainersJSONEmpty(c *check.C) { 1343 testRequires(c, DaemonIsLinux) 1344 1345 status, body, err := sockRequest("GET", "/containers/json?all=1", nil) 1346 c.Assert(err, checker.IsNil) 1347 c.Assert(status, checker.Equals, http.StatusOK) 1348 c.Assert(string(body), checker.Equals, "[]\n") 1349 } 1350 1351 func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *check.C) { 1352 testRequires(c, DaemonIsLinux) 1353 1354 c1 := struct { 1355 Image string 1356 CpusetCpus string 1357 }{"busybox", "1-42,,"} 1358 name := "wrong-cpuset-cpus" 1359 status, body, err := sockRequest("POST", "/containers/create?name="+name, c1) 1360 c.Assert(err, checker.IsNil) 1361 c.Assert(status, checker.Equals, http.StatusInternalServerError) 1362 expected := "Invalid value 1-42,, for cpuset cpus.\n" 1363 c.Assert(string(body), checker.Equals, expected) 1364 1365 c2 := struct { 1366 Image string 1367 CpusetMems string 1368 }{"busybox", "42-3,1--"} 1369 name = "wrong-cpuset-mems" 1370 status, body, err = sockRequest("POST", "/containers/create?name="+name, c2) 1371 c.Assert(err, checker.IsNil) 1372 c.Assert(status, checker.Equals, http.StatusInternalServerError) 1373 expected = "Invalid value 42-3,1-- for cpuset mems.\n" 1374 c.Assert(string(body), checker.Equals, expected) 1375 } 1376 1377 func (s *DockerSuite) TestStartWithNilDNS(c *check.C) { 1378 testRequires(c, DaemonIsLinux) 1379 out, _ := dockerCmd(c, "create", "busybox") 1380 containerID := strings.TrimSpace(out) 1381 1382 config := `{"HostConfig": {"Dns": null}}` 1383 1384 res, b, err := sockRequestRaw("POST", "/containers/"+containerID+"/start", strings.NewReader(config), "application/json") 1385 c.Assert(err, checker.IsNil) 1386 c.Assert(res.StatusCode, checker.Equals, http.StatusNoContent) 1387 b.Close() 1388 1389 dns, err := inspectFieldJSON(containerID, "HostConfig.Dns") 1390 c.Assert(err, checker.IsNil) 1391 c.Assert(dns, checker.Equals, "[]") 1392 } 1393 1394 func (s *DockerSuite) TestPostContainersCreateShmSizeNegative(c *check.C) { 1395 config := map[string]interface{}{ 1396 "Image": "busybox", 1397 "HostConfig": map[string]interface{}{"ShmSize": -1}, 1398 } 1399 1400 status, body, err := sockRequest("POST", "/containers/create", config) 1401 c.Assert(err, check.IsNil) 1402 c.Assert(status, check.Equals, http.StatusInternalServerError) 1403 c.Assert(string(body), checker.Contains, "SHM size must be greater then 0") 1404 } 1405 1406 func (s *DockerSuite) TestPostContainersCreateShmSizeZero(c *check.C) { 1407 config := map[string]interface{}{ 1408 "Image": "busybox", 1409 "HostConfig": map[string]interface{}{"ShmSize": 0}, 1410 } 1411 1412 status, body, err := sockRequest("POST", "/containers/create", config) 1413 c.Assert(err, check.IsNil) 1414 c.Assert(status, check.Equals, http.StatusInternalServerError) 1415 c.Assert(string(body), checker.Contains, "SHM size must be greater then 0") 1416 } 1417 1418 func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *check.C) { 1419 var defaultSHMSize int64 = 67108864 1420 config := map[string]interface{}{ 1421 "Image": "busybox", 1422 "Cmd": "mount", 1423 } 1424 1425 status, body, err := sockRequest("POST", "/containers/create", config) 1426 c.Assert(err, check.IsNil) 1427 c.Assert(status, check.Equals, http.StatusCreated) 1428 1429 var container types.ContainerCreateResponse 1430 c.Assert(json.Unmarshal(body, &container), check.IsNil) 1431 1432 status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil) 1433 c.Assert(err, check.IsNil) 1434 c.Assert(status, check.Equals, http.StatusOK) 1435 1436 var containerJSON types.ContainerJSON 1437 c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil) 1438 1439 c.Assert(*containerJSON.HostConfig.ShmSize, check.Equals, defaultSHMSize) 1440 1441 out, _ := dockerCmd(c, "start", "-i", containerJSON.ID) 1442 shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`) 1443 if !shmRegexp.MatchString(out) { 1444 c.Fatalf("Expected shm of 64MB in mount command, got %v", out) 1445 } 1446 } 1447 1448 func (s *DockerSuite) TestPostContainersCreateShmSizeOmitted(c *check.C) { 1449 config := map[string]interface{}{ 1450 "Image": "busybox", 1451 "HostConfig": map[string]interface{}{}, 1452 "Cmd": "mount", 1453 } 1454 1455 status, body, err := sockRequest("POST", "/containers/create", config) 1456 c.Assert(err, check.IsNil) 1457 c.Assert(status, check.Equals, http.StatusCreated) 1458 1459 var container types.ContainerCreateResponse 1460 c.Assert(json.Unmarshal(body, &container), check.IsNil) 1461 1462 status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil) 1463 c.Assert(err, check.IsNil) 1464 c.Assert(status, check.Equals, http.StatusOK) 1465 1466 var containerJSON types.ContainerJSON 1467 c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil) 1468 1469 c.Assert(*containerJSON.HostConfig.ShmSize, check.Equals, int64(67108864)) 1470 1471 out, _ := dockerCmd(c, "start", "-i", containerJSON.ID) 1472 shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`) 1473 if !shmRegexp.MatchString(out) { 1474 c.Fatalf("Expected shm of 64MB in mount command, got %v", out) 1475 } 1476 } 1477 1478 func (s *DockerSuite) TestPostContainersCreateWithShmSize(c *check.C) { 1479 config := map[string]interface{}{ 1480 "Image": "busybox", 1481 "Cmd": "mount", 1482 "HostConfig": map[string]interface{}{"ShmSize": 1073741824}, 1483 } 1484 1485 status, body, err := sockRequest("POST", "/containers/create", config) 1486 c.Assert(err, check.IsNil) 1487 c.Assert(status, check.Equals, http.StatusCreated) 1488 1489 var container types.ContainerCreateResponse 1490 c.Assert(json.Unmarshal(body, &container), check.IsNil) 1491 1492 status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil) 1493 c.Assert(err, check.IsNil) 1494 c.Assert(status, check.Equals, http.StatusOK) 1495 1496 var containerJSON types.ContainerJSON 1497 c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil) 1498 1499 c.Assert(*containerJSON.HostConfig.ShmSize, check.Equals, int64(1073741824)) 1500 1501 out, _ := dockerCmd(c, "start", "-i", containerJSON.ID) 1502 shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=1048576k`) 1503 if !shmRegex.MatchString(out) { 1504 c.Fatalf("Expected shm of 1GB in mount command, got %v", out) 1505 } 1506 } 1507 1508 func (s *DockerSuite) TestPostContainersCreateMemorySwappinessHostConfigOmitted(c *check.C) { 1509 config := map[string]interface{}{ 1510 "Image": "busybox", 1511 } 1512 1513 status, body, err := sockRequest("POST", "/containers/create", config) 1514 c.Assert(err, check.IsNil) 1515 c.Assert(status, check.Equals, http.StatusCreated) 1516 1517 var container types.ContainerCreateResponse 1518 c.Assert(json.Unmarshal(body, &container), check.IsNil) 1519 1520 status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil) 1521 c.Assert(err, check.IsNil) 1522 c.Assert(status, check.Equals, http.StatusOK) 1523 1524 var containerJSON types.ContainerJSON 1525 c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil) 1526 1527 c.Assert(*containerJSON.HostConfig.MemorySwappiness, check.Equals, int64(-1)) 1528 } 1529 1530 // check validation is done daemon side and not only in cli 1531 func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *check.C) { 1532 testRequires(c, DaemonIsLinux) 1533 1534 config := struct { 1535 Image string 1536 OomScoreAdj int 1537 }{"busybox", 1001} 1538 name := "oomscoreadj-over" 1539 status, b, err := sockRequest("POST", "/containers/create?name="+name, config) 1540 c.Assert(err, check.IsNil) 1541 c.Assert(status, check.Equals, http.StatusInternalServerError) 1542 expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]." 1543 if !strings.Contains(string(b), expected) { 1544 c.Fatalf("Expected output to contain %q, got %q", expected, string(b)) 1545 } 1546 1547 config = struct { 1548 Image string 1549 OomScoreAdj int 1550 }{"busybox", -1001} 1551 name = "oomscoreadj-low" 1552 status, b, err = sockRequest("POST", "/containers/create?name="+name, config) 1553 c.Assert(err, check.IsNil) 1554 c.Assert(status, check.Equals, http.StatusInternalServerError) 1555 expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]." 1556 if !strings.Contains(string(b), expected) { 1557 c.Fatalf("Expected output to contain %q, got %q", expected, string(b)) 1558 } 1559 }