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