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