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