github.com/ttys3/engine@v17.12.1-ce-rc2+incompatible/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 "io/ioutil" 10 "net/http" 11 "os" 12 "path/filepath" 13 "regexp" 14 "runtime" 15 "strconv" 16 "strings" 17 "time" 18 19 "github.com/docker/docker/api/types" 20 containertypes "github.com/docker/docker/api/types/container" 21 mounttypes "github.com/docker/docker/api/types/mount" 22 networktypes "github.com/docker/docker/api/types/network" 23 "github.com/docker/docker/client" 24 "github.com/docker/docker/integration-cli/checker" 25 "github.com/docker/docker/integration-cli/cli" 26 "github.com/docker/docker/integration-cli/cli/build" 27 "github.com/docker/docker/integration-cli/request" 28 "github.com/docker/docker/pkg/ioutils" 29 "github.com/docker/docker/pkg/mount" 30 "github.com/docker/docker/pkg/stringid" 31 "github.com/docker/docker/volume" 32 "github.com/docker/go-connections/nat" 33 "github.com/go-check/check" 34 "github.com/gotestyourself/gotestyourself/poll" 35 "github.com/stretchr/testify/assert" 36 "github.com/stretchr/testify/require" 37 "golang.org/x/net/context" 38 ) 39 40 func (s *DockerSuite) TestContainerAPIGetAll(c *check.C) { 41 startCount := getContainerCount(c) 42 name := "getall" 43 dockerCmd(c, "run", "--name", name, "busybox", "true") 44 45 cli, err := client.NewEnvClient() 46 c.Assert(err, checker.IsNil) 47 defer cli.Close() 48 49 options := types.ContainerListOptions{ 50 All: true, 51 } 52 containers, err := cli.ContainerList(context.Background(), options) 53 c.Assert(err, checker.IsNil) 54 c.Assert(containers, checker.HasLen, startCount+1) 55 actual := containers[0].Names[0] 56 c.Assert(actual, checker.Equals, "/"+name) 57 } 58 59 // regression test for empty json field being omitted #13691 60 func (s *DockerSuite) TestContainerAPIGetJSONNoFieldsOmitted(c *check.C) { 61 startCount := getContainerCount(c) 62 dockerCmd(c, "run", "busybox", "true") 63 64 cli, err := client.NewEnvClient() 65 c.Assert(err, checker.IsNil) 66 defer cli.Close() 67 68 options := types.ContainerListOptions{ 69 All: true, 70 } 71 containers, err := cli.ContainerList(context.Background(), options) 72 c.Assert(err, checker.IsNil) 73 c.Assert(containers, checker.HasLen, startCount+1) 74 actual := fmt.Sprintf("%+v", containers[0]) 75 76 // empty Labels field triggered this bug, make sense to check for everything 77 // cause even Ports for instance can trigger this bug 78 // better safe than sorry.. 79 fields := []string{ 80 "ID", 81 "Names", 82 "Image", 83 "Command", 84 "Created", 85 "Ports", 86 "Labels", 87 "Status", 88 "NetworkSettings", 89 } 90 91 // decoding into types.Container do not work since it eventually unmarshal 92 // and empty field to an empty go map, so we just check for a string 93 for _, f := range fields { 94 if !strings.Contains(actual, f) { 95 c.Fatalf("Field %s is missing and it shouldn't", f) 96 } 97 } 98 } 99 100 type containerPs struct { 101 Names []string 102 Ports []types.Port 103 } 104 105 // regression test for non-empty fields from #13901 106 func (s *DockerSuite) TestContainerAPIPsOmitFields(c *check.C) { 107 // Problematic for Windows porting due to networking not yet being passed back 108 testRequires(c, DaemonIsLinux) 109 name := "pstest" 110 port := 80 111 runSleepingContainer(c, "--name", name, "--expose", strconv.Itoa(port)) 112 113 cli, err := client.NewEnvClient() 114 c.Assert(err, checker.IsNil) 115 defer cli.Close() 116 117 options := types.ContainerListOptions{ 118 All: true, 119 } 120 containers, err := cli.ContainerList(context.Background(), options) 121 c.Assert(err, checker.IsNil) 122 var foundContainer containerPs 123 for _, c := range containers { 124 for _, testName := range c.Names { 125 if "/"+name == testName { 126 foundContainer.Names = c.Names 127 foundContainer.Ports = c.Ports 128 break 129 } 130 } 131 } 132 133 c.Assert(foundContainer.Ports, checker.HasLen, 1) 134 c.Assert(foundContainer.Ports[0].PrivatePort, checker.Equals, uint16(port)) 135 c.Assert(foundContainer.Ports[0].PublicPort, checker.NotNil) 136 c.Assert(foundContainer.Ports[0].IP, checker.NotNil) 137 } 138 139 func (s *DockerSuite) TestContainerAPIGetExport(c *check.C) { 140 // Not supported on Windows as Windows does not support docker export 141 testRequires(c, DaemonIsLinux) 142 name := "exportcontainer" 143 dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test") 144 145 cli, err := client.NewEnvClient() 146 c.Assert(err, checker.IsNil) 147 defer cli.Close() 148 149 body, err := cli.ContainerExport(context.Background(), name) 150 c.Assert(err, checker.IsNil) 151 defer body.Close() 152 found := false 153 for tarReader := tar.NewReader(body); ; { 154 h, err := tarReader.Next() 155 if err != nil && err == io.EOF { 156 break 157 } 158 if h.Name == "test" { 159 found = true 160 break 161 } 162 } 163 c.Assert(found, checker.True, check.Commentf("The created test file has not been found in the exported image")) 164 } 165 166 func (s *DockerSuite) TestContainerAPIGetChanges(c *check.C) { 167 // Not supported on Windows as Windows does not support docker diff (/containers/name/changes) 168 testRequires(c, DaemonIsLinux) 169 name := "changescontainer" 170 dockerCmd(c, "run", "--name", name, "busybox", "rm", "/etc/passwd") 171 172 cli, err := client.NewEnvClient() 173 c.Assert(err, checker.IsNil) 174 defer cli.Close() 175 176 changes, err := cli.ContainerDiff(context.Background(), name) 177 c.Assert(err, checker.IsNil) 178 179 // Check the changelog for removal of /etc/passwd 180 success := false 181 for _, elem := range changes { 182 if elem.Path == "/etc/passwd" && elem.Kind == 2 { 183 success = true 184 } 185 } 186 c.Assert(success, checker.True, check.Commentf("/etc/passwd has been removed but is not present in the diff")) 187 } 188 189 func (s *DockerSuite) TestGetContainerStats(c *check.C) { 190 var ( 191 name = "statscontainer" 192 ) 193 runSleepingContainer(c, "--name", name) 194 195 type b struct { 196 stats types.ContainerStats 197 err error 198 } 199 200 bc := make(chan b, 1) 201 go func() { 202 cli, err := client.NewEnvClient() 203 c.Assert(err, checker.IsNil) 204 defer cli.Close() 205 206 stats, err := cli.ContainerStats(context.Background(), name, true) 207 c.Assert(err, checker.IsNil) 208 bc <- b{stats, err} 209 }() 210 211 // allow some time to stream the stats from the container 212 time.Sleep(4 * time.Second) 213 dockerCmd(c, "rm", "-f", name) 214 215 // collect the results from the stats stream or timeout and fail 216 // if the stream was not disconnected. 217 select { 218 case <-time.After(2 * time.Second): 219 c.Fatal("stream was not closed after container was removed") 220 case sr := <-bc: 221 dec := json.NewDecoder(sr.stats.Body) 222 defer sr.stats.Body.Close() 223 var s *types.Stats 224 // decode only one object from the stream 225 c.Assert(dec.Decode(&s), checker.IsNil) 226 } 227 } 228 229 func (s *DockerSuite) TestGetContainerStatsRmRunning(c *check.C) { 230 out := runSleepingContainer(c) 231 id := strings.TrimSpace(out) 232 233 buf := &ChannelBuffer{C: make(chan []byte, 1)} 234 defer buf.Close() 235 236 cli, err := client.NewEnvClient() 237 c.Assert(err, checker.IsNil) 238 defer cli.Close() 239 240 stats, err := cli.ContainerStats(context.Background(), id, true) 241 c.Assert(err, checker.IsNil) 242 defer stats.Body.Close() 243 244 chErr := make(chan error, 1) 245 go func() { 246 _, err = io.Copy(buf, stats.Body) 247 chErr <- err 248 }() 249 250 b := make([]byte, 32) 251 // make sure we've got some stats 252 _, err = buf.ReadTimeout(b, 2*time.Second) 253 c.Assert(err, checker.IsNil) 254 255 // Now remove without `-f` and make sure we are still pulling stats 256 _, _, err = dockerCmdWithError("rm", id) 257 c.Assert(err, checker.Not(checker.IsNil), check.Commentf("rm should have failed but didn't")) 258 _, err = buf.ReadTimeout(b, 2*time.Second) 259 c.Assert(err, checker.IsNil) 260 261 dockerCmd(c, "rm", "-f", id) 262 c.Assert(<-chErr, checker.IsNil) 263 } 264 265 // ChannelBuffer holds a chan of byte array that can be populate in a goroutine. 266 type ChannelBuffer struct { 267 C chan []byte 268 } 269 270 // Write implements Writer. 271 func (c *ChannelBuffer) Write(b []byte) (int, error) { 272 c.C <- b 273 return len(b), nil 274 } 275 276 // Close closes the go channel. 277 func (c *ChannelBuffer) Close() error { 278 close(c.C) 279 return nil 280 } 281 282 // ReadTimeout reads the content of the channel in the specified byte array with 283 // the specified duration as timeout. 284 func (c *ChannelBuffer) ReadTimeout(p []byte, n time.Duration) (int, error) { 285 select { 286 case b := <-c.C: 287 return copy(p[0:], b), nil 288 case <-time.After(n): 289 return -1, fmt.Errorf("timeout reading from channel") 290 } 291 } 292 293 // regression test for gh13421 294 // previous test was just checking one stat entry so it didn't fail (stats with 295 // stream false always return one stat) 296 func (s *DockerSuite) TestGetContainerStatsStream(c *check.C) { 297 name := "statscontainer" 298 runSleepingContainer(c, "--name", name) 299 300 type b struct { 301 stats types.ContainerStats 302 err error 303 } 304 305 bc := make(chan b, 1) 306 go func() { 307 cli, err := client.NewEnvClient() 308 c.Assert(err, checker.IsNil) 309 defer cli.Close() 310 311 stats, err := cli.ContainerStats(context.Background(), name, true) 312 c.Assert(err, checker.IsNil) 313 bc <- b{stats, err} 314 }() 315 316 // allow some time to stream the stats from the container 317 time.Sleep(4 * time.Second) 318 dockerCmd(c, "rm", "-f", name) 319 320 // collect the results from the stats stream or timeout and fail 321 // if the stream was not disconnected. 322 select { 323 case <-time.After(2 * time.Second): 324 c.Fatal("stream was not closed after container was removed") 325 case sr := <-bc: 326 b, err := ioutil.ReadAll(sr.stats.Body) 327 defer sr.stats.Body.Close() 328 c.Assert(err, checker.IsNil) 329 s := string(b) 330 // count occurrences of "read" of types.Stats 331 if l := strings.Count(s, "read"); l < 2 { 332 c.Fatalf("Expected more than one stat streamed, got %d", l) 333 } 334 } 335 } 336 337 func (s *DockerSuite) TestGetContainerStatsNoStream(c *check.C) { 338 name := "statscontainer" 339 runSleepingContainer(c, "--name", name) 340 341 type b struct { 342 stats types.ContainerStats 343 err error 344 } 345 346 bc := make(chan b, 1) 347 348 go func() { 349 cli, err := client.NewEnvClient() 350 c.Assert(err, checker.IsNil) 351 defer cli.Close() 352 353 stats, err := cli.ContainerStats(context.Background(), name, false) 354 c.Assert(err, checker.IsNil) 355 bc <- b{stats, err} 356 }() 357 358 // allow some time to stream the stats from the container 359 time.Sleep(4 * time.Second) 360 dockerCmd(c, "rm", "-f", name) 361 362 // collect the results from the stats stream or timeout and fail 363 // if the stream was not disconnected. 364 select { 365 case <-time.After(2 * time.Second): 366 c.Fatal("stream was not closed after container was removed") 367 case sr := <-bc: 368 b, err := ioutil.ReadAll(sr.stats.Body) 369 defer sr.stats.Body.Close() 370 c.Assert(err, checker.IsNil) 371 s := string(b) 372 // count occurrences of `"read"` of types.Stats 373 c.Assert(strings.Count(s, `"read"`), checker.Equals, 1, check.Commentf("Expected only one stat streamed, got %d", strings.Count(s, `"read"`))) 374 } 375 } 376 377 func (s *DockerSuite) TestGetStoppedContainerStats(c *check.C) { 378 name := "statscontainer" 379 dockerCmd(c, "create", "--name", name, "busybox", "ps") 380 381 chResp := make(chan error) 382 383 // We expect an immediate response, but if it's not immediate, the test would hang, so put it in a goroutine 384 // below we'll check this on a timeout. 385 go func() { 386 cli, err := client.NewEnvClient() 387 c.Assert(err, checker.IsNil) 388 defer cli.Close() 389 390 resp, err := cli.ContainerStats(context.Background(), name, false) 391 defer resp.Body.Close() 392 chResp <- err 393 }() 394 395 select { 396 case err := <-chResp: 397 c.Assert(err, checker.IsNil) 398 case <-time.After(10 * time.Second): 399 c.Fatal("timeout waiting for stats response for stopped container") 400 } 401 } 402 403 func (s *DockerSuite) TestContainerAPIPause(c *check.C) { 404 // Problematic on Windows as Windows does not support pause 405 testRequires(c, DaemonIsLinux) 406 407 getPaused := func(c *check.C) []string { 408 return strings.Fields(cli.DockerCmd(c, "ps", "-f", "status=paused", "-q", "-a").Combined()) 409 } 410 411 out := cli.DockerCmd(c, "run", "-d", "busybox", "sleep", "30").Combined() 412 ContainerID := strings.TrimSpace(out) 413 414 cli, err := client.NewEnvClient() 415 c.Assert(err, checker.IsNil) 416 defer cli.Close() 417 418 err = cli.ContainerPause(context.Background(), ContainerID) 419 c.Assert(err, checker.IsNil) 420 421 pausedContainers := getPaused(c) 422 423 if len(pausedContainers) != 1 || stringid.TruncateID(ContainerID) != pausedContainers[0] { 424 c.Fatalf("there should be one paused container and not %d", len(pausedContainers)) 425 } 426 427 err = cli.ContainerUnpause(context.Background(), ContainerID) 428 c.Assert(err, checker.IsNil) 429 430 pausedContainers = getPaused(c) 431 c.Assert(pausedContainers, checker.HasLen, 0, check.Commentf("There should be no paused container.")) 432 } 433 434 func (s *DockerSuite) TestContainerAPITop(c *check.C) { 435 testRequires(c, DaemonIsLinux) 436 out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "top") 437 id := strings.TrimSpace(string(out)) 438 c.Assert(waitRun(id), checker.IsNil) 439 440 cli, err := client.NewEnvClient() 441 c.Assert(err, checker.IsNil) 442 defer cli.Close() 443 444 top, err := cli.ContainerTop(context.Background(), id, []string{"aux"}) 445 c.Assert(err, checker.IsNil) 446 c.Assert(top.Titles, checker.HasLen, 11, check.Commentf("expected 11 titles, found %d: %v", len(top.Titles), top.Titles)) 447 448 if top.Titles[0] != "USER" || top.Titles[10] != "COMMAND" { 449 c.Fatalf("expected `USER` at `Titles[0]` and `COMMAND` at Titles[10]: %v", top.Titles) 450 } 451 c.Assert(top.Processes, checker.HasLen, 2, check.Commentf("expected 2 processes, found %d: %v", len(top.Processes), top.Processes)) 452 c.Assert(top.Processes[0][10], checker.Equals, "/bin/sh -c top") 453 c.Assert(top.Processes[1][10], checker.Equals, "top") 454 } 455 456 func (s *DockerSuite) TestContainerAPITopWindows(c *check.C) { 457 testRequires(c, DaemonIsWindows) 458 out := runSleepingContainer(c, "-d") 459 id := strings.TrimSpace(string(out)) 460 c.Assert(waitRun(id), checker.IsNil) 461 462 cli, err := client.NewEnvClient() 463 c.Assert(err, checker.IsNil) 464 defer cli.Close() 465 466 top, err := cli.ContainerTop(context.Background(), id, nil) 467 c.Assert(err, checker.IsNil) 468 c.Assert(top.Titles, checker.HasLen, 4, check.Commentf("expected 4 titles, found %d: %v", len(top.Titles), top.Titles)) 469 470 if top.Titles[0] != "Name" || top.Titles[3] != "Private Working Set" { 471 c.Fatalf("expected `Name` at `Titles[0]` and `Private Working Set` at Titles[3]: %v", top.Titles) 472 } 473 c.Assert(len(top.Processes), checker.GreaterOrEqualThan, 2, check.Commentf("expected at least 2 processes, found %d: %v", len(top.Processes), top.Processes)) 474 475 foundProcess := false 476 expectedProcess := "busybox.exe" 477 for _, process := range top.Processes { 478 if process[0] == expectedProcess { 479 foundProcess = true 480 break 481 } 482 } 483 484 c.Assert(foundProcess, checker.Equals, true, check.Commentf("expected to find %s: %v", expectedProcess, top.Processes)) 485 } 486 487 func (s *DockerSuite) TestContainerAPICommit(c *check.C) { 488 cName := "testapicommit" 489 dockerCmd(c, "run", "--name="+cName, "busybox", "/bin/sh", "-c", "touch /test") 490 491 cli, err := client.NewEnvClient() 492 c.Assert(err, checker.IsNil) 493 defer cli.Close() 494 495 options := types.ContainerCommitOptions{ 496 Reference: "testcontainerapicommit:testtag", 497 } 498 499 img, err := cli.ContainerCommit(context.Background(), cName, options) 500 c.Assert(err, checker.IsNil) 501 502 cmd := inspectField(c, img.ID, "Config.Cmd") 503 c.Assert(cmd, checker.Equals, "[/bin/sh -c touch /test]", check.Commentf("got wrong Cmd from commit: %q", cmd)) 504 505 // sanity check, make sure the image is what we think it is 506 dockerCmd(c, "run", img.ID, "ls", "/test") 507 } 508 509 func (s *DockerSuite) TestContainerAPICommitWithLabelInConfig(c *check.C) { 510 cName := "testapicommitwithconfig" 511 dockerCmd(c, "run", "--name="+cName, "busybox", "/bin/sh", "-c", "touch /test") 512 513 cli, err := client.NewEnvClient() 514 c.Assert(err, checker.IsNil) 515 defer cli.Close() 516 517 config := containertypes.Config{ 518 Labels: map[string]string{"key1": "value1", "key2": "value2"}} 519 520 options := types.ContainerCommitOptions{ 521 Reference: "testcontainerapicommitwithconfig", 522 Config: &config, 523 } 524 525 img, err := cli.ContainerCommit(context.Background(), cName, options) 526 c.Assert(err, checker.IsNil) 527 528 label1 := inspectFieldMap(c, img.ID, "Config.Labels", "key1") 529 c.Assert(label1, checker.Equals, "value1") 530 531 label2 := inspectFieldMap(c, img.ID, "Config.Labels", "key2") 532 c.Assert(label2, checker.Equals, "value2") 533 534 cmd := inspectField(c, img.ID, "Config.Cmd") 535 c.Assert(cmd, checker.Equals, "[/bin/sh -c touch /test]", check.Commentf("got wrong Cmd from commit: %q", cmd)) 536 537 // sanity check, make sure the image is what we think it is 538 dockerCmd(c, "run", img.ID, "ls", "/test") 539 } 540 541 func (s *DockerSuite) TestContainerAPIBadPort(c *check.C) { 542 // TODO Windows to Windows CI - Port this test 543 testRequires(c, DaemonIsLinux) 544 545 config := containertypes.Config{ 546 Image: "busybox", 547 Cmd: []string{"/bin/sh", "-c", "echo test"}, 548 } 549 550 hostConfig := containertypes.HostConfig{ 551 PortBindings: nat.PortMap{ 552 "8080/tcp": []nat.PortBinding{ 553 { 554 HostIP: "", 555 HostPort: "aa80"}, 556 }, 557 }, 558 } 559 560 cli, err := client.NewEnvClient() 561 c.Assert(err, checker.IsNil) 562 defer cli.Close() 563 564 _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "") 565 c.Assert(err.Error(), checker.Contains, `invalid port specification: "aa80"`) 566 } 567 568 func (s *DockerSuite) TestContainerAPICreate(c *check.C) { 569 config := containertypes.Config{ 570 Image: "busybox", 571 Cmd: []string{"/bin/sh", "-c", "touch /test && ls /test"}, 572 } 573 574 cli, err := client.NewEnvClient() 575 c.Assert(err, checker.IsNil) 576 defer cli.Close() 577 578 container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "") 579 c.Assert(err, checker.IsNil) 580 581 out, _ := dockerCmd(c, "start", "-a", container.ID) 582 c.Assert(strings.TrimSpace(out), checker.Equals, "/test") 583 } 584 585 func (s *DockerSuite) TestContainerAPICreateEmptyConfig(c *check.C) { 586 587 cli, err := client.NewEnvClient() 588 c.Assert(err, checker.IsNil) 589 defer cli.Close() 590 591 _, err = cli.ContainerCreate(context.Background(), &containertypes.Config{}, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "") 592 593 expected := "No command specified" 594 c.Assert(err.Error(), checker.Contains, expected) 595 } 596 597 func (s *DockerSuite) TestContainerAPICreateMultipleNetworksConfig(c *check.C) { 598 // Container creation must fail if client specified configurations for more than one network 599 config := containertypes.Config{ 600 Image: "busybox", 601 } 602 603 networkingConfig := networktypes.NetworkingConfig{ 604 EndpointsConfig: map[string]*networktypes.EndpointSettings{ 605 "net1": {}, 606 "net2": {}, 607 "net3": {}, 608 }, 609 } 610 611 cli, err := client.NewEnvClient() 612 c.Assert(err, checker.IsNil) 613 defer cli.Close() 614 615 _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networkingConfig, "") 616 msg := err.Error() 617 // network name order in error message is not deterministic 618 c.Assert(msg, checker.Contains, "Container cannot be connected to network endpoints") 619 c.Assert(msg, checker.Contains, "net1") 620 c.Assert(msg, checker.Contains, "net2") 621 c.Assert(msg, checker.Contains, "net3") 622 } 623 624 func (s *DockerSuite) TestContainerAPICreateWithHostName(c *check.C) { 625 domainName := "test-domain" 626 hostName := "test-hostname" 627 config := containertypes.Config{ 628 Image: "busybox", 629 Hostname: hostName, 630 Domainname: domainName, 631 } 632 633 cli, err := client.NewEnvClient() 634 c.Assert(err, checker.IsNil) 635 defer cli.Close() 636 637 container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "") 638 c.Assert(err, checker.IsNil) 639 640 containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) 641 c.Assert(err, checker.IsNil) 642 643 c.Assert(containerJSON.Config.Hostname, checker.Equals, hostName, check.Commentf("Mismatched Hostname")) 644 c.Assert(containerJSON.Config.Domainname, checker.Equals, domainName, check.Commentf("Mismatched Domainname")) 645 } 646 647 func (s *DockerSuite) TestContainerAPICreateBridgeNetworkMode(c *check.C) { 648 // Windows does not support bridge 649 testRequires(c, DaemonIsLinux) 650 UtilCreateNetworkMode(c, "bridge") 651 } 652 653 func (s *DockerSuite) TestContainerAPICreateOtherNetworkModes(c *check.C) { 654 // Windows does not support these network modes 655 testRequires(c, DaemonIsLinux, NotUserNamespace) 656 UtilCreateNetworkMode(c, "host") 657 UtilCreateNetworkMode(c, "container:web1") 658 } 659 660 func UtilCreateNetworkMode(c *check.C, networkMode containertypes.NetworkMode) { 661 config := containertypes.Config{ 662 Image: "busybox", 663 } 664 665 hostConfig := containertypes.HostConfig{ 666 NetworkMode: networkMode, 667 } 668 669 cli, err := client.NewEnvClient() 670 c.Assert(err, checker.IsNil) 671 defer cli.Close() 672 673 container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "") 674 c.Assert(err, checker.IsNil) 675 676 containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) 677 c.Assert(err, checker.IsNil) 678 679 c.Assert(containerJSON.HostConfig.NetworkMode, checker.Equals, containertypes.NetworkMode(networkMode), check.Commentf("Mismatched NetworkMode")) 680 } 681 682 func (s *DockerSuite) TestContainerAPICreateWithCpuSharesCpuset(c *check.C) { 683 // TODO Windows to Windows CI. The CpuShares part could be ported. 684 testRequires(c, DaemonIsLinux) 685 config := containertypes.Config{ 686 Image: "busybox", 687 } 688 689 hostConfig := containertypes.HostConfig{ 690 Resources: containertypes.Resources{ 691 CPUShares: 512, 692 CpusetCpus: "0", 693 }, 694 } 695 696 cli, err := client.NewEnvClient() 697 c.Assert(err, checker.IsNil) 698 defer cli.Close() 699 700 container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "") 701 c.Assert(err, checker.IsNil) 702 703 containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) 704 c.Assert(err, checker.IsNil) 705 706 out := inspectField(c, containerJSON.ID, "HostConfig.CpuShares") 707 c.Assert(out, checker.Equals, "512") 708 709 outCpuset := inspectField(c, containerJSON.ID, "HostConfig.CpusetCpus") 710 c.Assert(outCpuset, checker.Equals, "0") 711 } 712 713 func (s *DockerSuite) TestContainerAPIVerifyHeader(c *check.C) { 714 config := map[string]interface{}{ 715 "Image": "busybox", 716 } 717 718 create := func(ct string) (*http.Response, io.ReadCloser, error) { 719 jsonData := bytes.NewBuffer(nil) 720 c.Assert(json.NewEncoder(jsonData).Encode(config), checker.IsNil) 721 return request.Post("/containers/create", request.RawContent(ioutil.NopCloser(jsonData)), request.ContentType(ct)) 722 } 723 724 // Try with no content-type 725 res, body, err := create("") 726 c.Assert(err, checker.IsNil) 727 c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest) 728 body.Close() 729 730 // Try with wrong content-type 731 res, body, err = create("application/xml") 732 c.Assert(err, checker.IsNil) 733 c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest) 734 body.Close() 735 736 // now application/json 737 res, body, err = create("application/json") 738 c.Assert(err, checker.IsNil) 739 c.Assert(res.StatusCode, checker.Equals, http.StatusCreated) 740 body.Close() 741 } 742 743 //Issue 14230. daemon should return 500 for invalid port syntax 744 func (s *DockerSuite) TestContainerAPIInvalidPortSyntax(c *check.C) { 745 config := `{ 746 "Image": "busybox", 747 "HostConfig": { 748 "NetworkMode": "default", 749 "PortBindings": { 750 "19039;1230": [ 751 {} 752 ] 753 } 754 } 755 }` 756 757 res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON) 758 c.Assert(err, checker.IsNil) 759 c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest) 760 761 b, err := request.ReadBody(body) 762 c.Assert(err, checker.IsNil) 763 c.Assert(string(b[:]), checker.Contains, "invalid port") 764 } 765 766 func (s *DockerSuite) TestContainerAPIRestartPolicyInvalidPolicyName(c *check.C) { 767 config := `{ 768 "Image": "busybox", 769 "HostConfig": { 770 "RestartPolicy": { 771 "Name": "something", 772 "MaximumRetryCount": 0 773 } 774 } 775 }` 776 777 res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON) 778 c.Assert(err, checker.IsNil) 779 c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest) 780 781 b, err := request.ReadBody(body) 782 c.Assert(err, checker.IsNil) 783 c.Assert(string(b[:]), checker.Contains, "invalid restart policy") 784 } 785 786 func (s *DockerSuite) TestContainerAPIRestartPolicyRetryMismatch(c *check.C) { 787 config := `{ 788 "Image": "busybox", 789 "HostConfig": { 790 "RestartPolicy": { 791 "Name": "always", 792 "MaximumRetryCount": 2 793 } 794 } 795 }` 796 797 res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON) 798 c.Assert(err, checker.IsNil) 799 c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest) 800 801 b, err := request.ReadBody(body) 802 c.Assert(err, checker.IsNil) 803 c.Assert(string(b[:]), checker.Contains, "maximum retry count cannot be used with restart policy") 804 } 805 806 func (s *DockerSuite) TestContainerAPIRestartPolicyNegativeRetryCount(c *check.C) { 807 config := `{ 808 "Image": "busybox", 809 "HostConfig": { 810 "RestartPolicy": { 811 "Name": "on-failure", 812 "MaximumRetryCount": -2 813 } 814 } 815 }` 816 817 res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON) 818 c.Assert(err, checker.IsNil) 819 c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest) 820 821 b, err := request.ReadBody(body) 822 c.Assert(err, checker.IsNil) 823 c.Assert(string(b[:]), checker.Contains, "maximum retry count cannot be negative") 824 } 825 826 func (s *DockerSuite) TestContainerAPIRestartPolicyDefaultRetryCount(c *check.C) { 827 config := `{ 828 "Image": "busybox", 829 "HostConfig": { 830 "RestartPolicy": { 831 "Name": "on-failure", 832 "MaximumRetryCount": 0 833 } 834 } 835 }` 836 837 res, _, err := request.Post("/containers/create", request.RawString(config), request.JSON) 838 c.Assert(err, checker.IsNil) 839 c.Assert(res.StatusCode, checker.Equals, http.StatusCreated) 840 } 841 842 // Issue 7941 - test to make sure a "null" in JSON is just ignored. 843 // W/o this fix a null in JSON would be parsed into a string var as "null" 844 func (s *DockerSuite) TestContainerAPIPostCreateNull(c *check.C) { 845 config := `{ 846 "Hostname":"", 847 "Domainname":"", 848 "Memory":0, 849 "MemorySwap":0, 850 "CpuShares":0, 851 "Cpuset":null, 852 "AttachStdin":true, 853 "AttachStdout":true, 854 "AttachStderr":true, 855 "ExposedPorts":{}, 856 "Tty":true, 857 "OpenStdin":true, 858 "StdinOnce":true, 859 "Env":[], 860 "Cmd":"ls", 861 "Image":"busybox", 862 "Volumes":{}, 863 "WorkingDir":"", 864 "Entrypoint":null, 865 "NetworkDisabled":false, 866 "OnBuild":null}` 867 868 res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON) 869 c.Assert(err, checker.IsNil) 870 c.Assert(res.StatusCode, checker.Equals, http.StatusCreated) 871 872 b, err := request.ReadBody(body) 873 c.Assert(err, checker.IsNil) 874 type createResp struct { 875 ID string 876 } 877 var container createResp 878 c.Assert(json.Unmarshal(b, &container), checker.IsNil) 879 out := inspectField(c, container.ID, "HostConfig.CpusetCpus") 880 c.Assert(out, checker.Equals, "") 881 882 outMemory := inspectField(c, container.ID, "HostConfig.Memory") 883 c.Assert(outMemory, checker.Equals, "0") 884 outMemorySwap := inspectField(c, container.ID, "HostConfig.MemorySwap") 885 c.Assert(outMemorySwap, checker.Equals, "0") 886 } 887 888 func (s *DockerSuite) TestCreateWithTooLowMemoryLimit(c *check.C) { 889 // TODO Windows: Port once memory is supported 890 testRequires(c, DaemonIsLinux) 891 config := `{ 892 "Image": "busybox", 893 "Cmd": "ls", 894 "OpenStdin": true, 895 "CpuShares": 100, 896 "Memory": 524287 897 }` 898 899 res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON) 900 c.Assert(err, checker.IsNil) 901 b, err2 := request.ReadBody(body) 902 c.Assert(err2, checker.IsNil) 903 904 c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest) 905 c.Assert(string(b), checker.Contains, "Minimum memory limit allowed is 4MB") 906 } 907 908 func (s *DockerSuite) TestContainerAPIRename(c *check.C) { 909 out, _ := dockerCmd(c, "run", "--name", "TestContainerAPIRename", "-d", "busybox", "sh") 910 911 containerID := strings.TrimSpace(out) 912 newName := "TestContainerAPIRenameNew" 913 914 cli, err := client.NewEnvClient() 915 c.Assert(err, checker.IsNil) 916 defer cli.Close() 917 918 err = cli.ContainerRename(context.Background(), containerID, newName) 919 c.Assert(err, checker.IsNil) 920 921 name := inspectField(c, containerID, "Name") 922 c.Assert(name, checker.Equals, "/"+newName, check.Commentf("Failed to rename container")) 923 } 924 925 func (s *DockerSuite) TestContainerAPIKill(c *check.C) { 926 name := "test-api-kill" 927 runSleepingContainer(c, "-i", "--name", name) 928 929 cli, err := client.NewEnvClient() 930 c.Assert(err, checker.IsNil) 931 defer cli.Close() 932 933 err = cli.ContainerKill(context.Background(), name, "SIGKILL") 934 c.Assert(err, checker.IsNil) 935 936 state := inspectField(c, name, "State.Running") 937 c.Assert(state, checker.Equals, "false", check.Commentf("got wrong State from container %s: %q", name, state)) 938 } 939 940 func (s *DockerSuite) TestContainerAPIRestart(c *check.C) { 941 name := "test-api-restart" 942 runSleepingContainer(c, "-di", "--name", name) 943 cli, err := client.NewEnvClient() 944 c.Assert(err, checker.IsNil) 945 defer cli.Close() 946 947 timeout := 1 * time.Second 948 err = cli.ContainerRestart(context.Background(), name, &timeout) 949 c.Assert(err, checker.IsNil) 950 951 c.Assert(waitInspect(name, "{{ .State.Restarting }} {{ .State.Running }}", "false true", 15*time.Second), checker.IsNil) 952 } 953 954 func (s *DockerSuite) TestContainerAPIRestartNotimeoutParam(c *check.C) { 955 name := "test-api-restart-no-timeout-param" 956 out := runSleepingContainer(c, "-di", "--name", name) 957 id := strings.TrimSpace(out) 958 c.Assert(waitRun(id), checker.IsNil) 959 960 cli, err := client.NewEnvClient() 961 c.Assert(err, checker.IsNil) 962 defer cli.Close() 963 964 err = cli.ContainerRestart(context.Background(), name, nil) 965 c.Assert(err, checker.IsNil) 966 967 c.Assert(waitInspect(name, "{{ .State.Restarting }} {{ .State.Running }}", "false true", 15*time.Second), checker.IsNil) 968 } 969 970 func (s *DockerSuite) TestContainerAPIStart(c *check.C) { 971 name := "testing-start" 972 config := containertypes.Config{ 973 Image: "busybox", 974 Cmd: append([]string{"/bin/sh", "-c"}, sleepCommandForDaemonPlatform()...), 975 OpenStdin: true, 976 } 977 978 cli, err := client.NewEnvClient() 979 c.Assert(err, checker.IsNil) 980 defer cli.Close() 981 982 _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, name) 983 c.Assert(err, checker.IsNil) 984 985 err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{}) 986 c.Assert(err, checker.IsNil) 987 988 // second call to start should give 304 989 // maybe add ContainerStartWithRaw to test it 990 err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{}) 991 c.Assert(err, checker.IsNil) 992 993 // TODO(tibor): figure out why this doesn't work on windows 994 } 995 996 func (s *DockerSuite) TestContainerAPIStop(c *check.C) { 997 name := "test-api-stop" 998 runSleepingContainer(c, "-i", "--name", name) 999 timeout := 30 * time.Second 1000 1001 cli, err := client.NewEnvClient() 1002 c.Assert(err, checker.IsNil) 1003 defer cli.Close() 1004 1005 err = cli.ContainerStop(context.Background(), name, &timeout) 1006 c.Assert(err, checker.IsNil) 1007 c.Assert(waitInspect(name, "{{ .State.Running }}", "false", 60*time.Second), checker.IsNil) 1008 1009 // second call to start should give 304 1010 // maybe add ContainerStartWithRaw to test it 1011 err = cli.ContainerStop(context.Background(), name, &timeout) 1012 c.Assert(err, checker.IsNil) 1013 } 1014 1015 func (s *DockerSuite) TestContainerAPIWait(c *check.C) { 1016 name := "test-api-wait" 1017 1018 sleepCmd := "/bin/sleep" 1019 if testEnv.DaemonPlatform() == "windows" { 1020 sleepCmd = "sleep" 1021 } 1022 dockerCmd(c, "run", "--name", name, "busybox", sleepCmd, "2") 1023 1024 cli, err := client.NewEnvClient() 1025 c.Assert(err, checker.IsNil) 1026 defer cli.Close() 1027 1028 waitresC, errC := cli.ContainerWait(context.Background(), name, "") 1029 1030 select { 1031 case err = <-errC: 1032 c.Assert(err, checker.IsNil) 1033 case waitres := <-waitresC: 1034 c.Assert(waitres.StatusCode, checker.Equals, int64(0)) 1035 } 1036 } 1037 1038 func (s *DockerSuite) TestContainerAPICopyNotExistsAnyMore(c *check.C) { 1039 name := "test-container-api-copy" 1040 dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt") 1041 1042 postData := types.CopyConfig{ 1043 Resource: "/test.txt", 1044 } 1045 // no copy in client/ 1046 res, _, err := request.Post("/containers/"+name+"/copy", request.JSONBody(postData)) 1047 c.Assert(err, checker.IsNil) 1048 c.Assert(res.StatusCode, checker.Equals, http.StatusNotFound) 1049 } 1050 1051 func (s *DockerSuite) TestContainerAPICopyPre124(c *check.C) { 1052 testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later 1053 name := "test-container-api-copy" 1054 dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt") 1055 1056 postData := types.CopyConfig{ 1057 Resource: "/test.txt", 1058 } 1059 1060 res, body, err := request.Post("/v1.23/containers/"+name+"/copy", request.JSONBody(postData)) 1061 c.Assert(err, checker.IsNil) 1062 c.Assert(res.StatusCode, checker.Equals, http.StatusOK) 1063 1064 found := false 1065 for tarReader := tar.NewReader(body); ; { 1066 h, err := tarReader.Next() 1067 if err != nil { 1068 if err == io.EOF { 1069 break 1070 } 1071 c.Fatal(err) 1072 } 1073 if h.Name == "test.txt" { 1074 found = true 1075 break 1076 } 1077 } 1078 c.Assert(found, checker.True) 1079 } 1080 1081 func (s *DockerSuite) TestContainerAPICopyResourcePathEmptyPre124(c *check.C) { 1082 testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later 1083 name := "test-container-api-copy-resource-empty" 1084 dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt") 1085 1086 postData := types.CopyConfig{ 1087 Resource: "", 1088 } 1089 1090 res, body, err := request.Post("/v1.23/containers/"+name+"/copy", request.JSONBody(postData)) 1091 c.Assert(err, checker.IsNil) 1092 c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest) 1093 b, err := request.ReadBody(body) 1094 c.Assert(err, checker.IsNil) 1095 c.Assert(string(b), checker.Matches, "Path cannot be empty\n") 1096 } 1097 1098 func (s *DockerSuite) TestContainerAPICopyResourcePathNotFoundPre124(c *check.C) { 1099 testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later 1100 name := "test-container-api-copy-resource-not-found" 1101 dockerCmd(c, "run", "--name", name, "busybox") 1102 1103 postData := types.CopyConfig{ 1104 Resource: "/notexist", 1105 } 1106 1107 res, body, err := request.Post("/v1.23/containers/"+name+"/copy", request.JSONBody(postData)) 1108 c.Assert(err, checker.IsNil) 1109 c.Assert(res.StatusCode, checker.Equals, http.StatusNotFound) 1110 1111 b, err := request.ReadBody(body) 1112 c.Assert(err, checker.IsNil) 1113 c.Assert(string(b), checker.Matches, "Could not find the file /notexist in container "+name+"\n") 1114 } 1115 1116 func (s *DockerSuite) TestContainerAPICopyContainerNotFoundPr124(c *check.C) { 1117 testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later 1118 postData := types.CopyConfig{ 1119 Resource: "/something", 1120 } 1121 1122 res, _, err := request.Post("/v1.23/containers/notexists/copy", request.JSONBody(postData)) 1123 c.Assert(err, checker.IsNil) 1124 c.Assert(res.StatusCode, checker.Equals, http.StatusNotFound) 1125 } 1126 1127 func (s *DockerSuite) TestContainerAPIDelete(c *check.C) { 1128 out := runSleepingContainer(c) 1129 1130 id := strings.TrimSpace(out) 1131 c.Assert(waitRun(id), checker.IsNil) 1132 1133 dockerCmd(c, "stop", id) 1134 1135 cli, err := client.NewEnvClient() 1136 c.Assert(err, checker.IsNil) 1137 defer cli.Close() 1138 1139 err = cli.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{}) 1140 c.Assert(err, checker.IsNil) 1141 } 1142 1143 func (s *DockerSuite) TestContainerAPIDeleteNotExist(c *check.C) { 1144 cli, err := client.NewEnvClient() 1145 c.Assert(err, checker.IsNil) 1146 defer cli.Close() 1147 1148 err = cli.ContainerRemove(context.Background(), "doesnotexist", types.ContainerRemoveOptions{}) 1149 c.Assert(err.Error(), checker.Contains, "No such container: doesnotexist") 1150 } 1151 1152 func (s *DockerSuite) TestContainerAPIDeleteForce(c *check.C) { 1153 out := runSleepingContainer(c) 1154 id := strings.TrimSpace(out) 1155 c.Assert(waitRun(id), checker.IsNil) 1156 1157 removeOptions := types.ContainerRemoveOptions{ 1158 Force: true, 1159 } 1160 1161 cli, err := client.NewEnvClient() 1162 c.Assert(err, checker.IsNil) 1163 defer cli.Close() 1164 1165 err = cli.ContainerRemove(context.Background(), id, removeOptions) 1166 c.Assert(err, checker.IsNil) 1167 } 1168 1169 func (s *DockerSuite) TestContainerAPIDeleteRemoveLinks(c *check.C) { 1170 // Windows does not support links 1171 testRequires(c, DaemonIsLinux) 1172 out, _ := dockerCmd(c, "run", "-d", "--name", "tlink1", "busybox", "top") 1173 1174 id := strings.TrimSpace(out) 1175 c.Assert(waitRun(id), checker.IsNil) 1176 1177 out, _ = dockerCmd(c, "run", "--link", "tlink1:tlink1", "--name", "tlink2", "-d", "busybox", "top") 1178 1179 id2 := strings.TrimSpace(out) 1180 c.Assert(waitRun(id2), checker.IsNil) 1181 1182 links := inspectFieldJSON(c, id2, "HostConfig.Links") 1183 c.Assert(links, checker.Equals, "[\"/tlink1:/tlink2/tlink1\"]", check.Commentf("expected to have links between containers")) 1184 1185 removeOptions := types.ContainerRemoveOptions{ 1186 RemoveLinks: true, 1187 } 1188 1189 cli, err := client.NewEnvClient() 1190 c.Assert(err, checker.IsNil) 1191 defer cli.Close() 1192 1193 err = cli.ContainerRemove(context.Background(), "tlink2/tlink1", removeOptions) 1194 c.Assert(err, check.IsNil) 1195 1196 linksPostRm := inspectFieldJSON(c, id2, "HostConfig.Links") 1197 c.Assert(linksPostRm, checker.Equals, "null", check.Commentf("call to api deleteContainer links should have removed the specified links")) 1198 } 1199 1200 func (s *DockerSuite) TestContainerAPIDeleteConflict(c *check.C) { 1201 out := runSleepingContainer(c) 1202 1203 id := strings.TrimSpace(out) 1204 c.Assert(waitRun(id), checker.IsNil) 1205 1206 cli, err := client.NewEnvClient() 1207 c.Assert(err, checker.IsNil) 1208 defer cli.Close() 1209 1210 err = cli.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{}) 1211 expected := "cannot remove a running container" 1212 c.Assert(err.Error(), checker.Contains, expected) 1213 } 1214 1215 func (s *DockerSuite) TestContainerAPIDeleteRemoveVolume(c *check.C) { 1216 testRequires(c, SameHostDaemon) 1217 1218 vol := "/testvolume" 1219 if testEnv.DaemonPlatform() == "windows" { 1220 vol = `c:\testvolume` 1221 } 1222 1223 out := runSleepingContainer(c, "-v", vol) 1224 1225 id := strings.TrimSpace(out) 1226 c.Assert(waitRun(id), checker.IsNil) 1227 1228 source, err := inspectMountSourceField(id, vol) 1229 _, err = os.Stat(source) 1230 c.Assert(err, checker.IsNil) 1231 1232 removeOptions := types.ContainerRemoveOptions{ 1233 Force: true, 1234 RemoveVolumes: true, 1235 } 1236 1237 cli, err := client.NewEnvClient() 1238 c.Assert(err, checker.IsNil) 1239 defer cli.Close() 1240 1241 err = cli.ContainerRemove(context.Background(), id, removeOptions) 1242 c.Assert(err, check.IsNil) 1243 1244 _, err = os.Stat(source) 1245 c.Assert(os.IsNotExist(err), checker.True, check.Commentf("expected to get ErrNotExist error, got %v", err)) 1246 } 1247 1248 // Regression test for https://github.com/docker/docker/issues/6231 1249 func (s *DockerSuite) TestContainerAPIChunkedEncoding(c *check.C) { 1250 1251 config := map[string]interface{}{ 1252 "Image": "busybox", 1253 "Cmd": append([]string{"/bin/sh", "-c"}, sleepCommandForDaemonPlatform()...), 1254 "OpenStdin": true, 1255 } 1256 1257 resp, _, err := request.Post("/containers/create", request.JSONBody(config), func(req *http.Request) error { 1258 // This is a cheat to make the http request do chunked encoding 1259 // Otherwise (just setting the Content-Encoding to chunked) net/http will overwrite 1260 // https://golang.org/src/pkg/net/http/request.go?s=11980:12172 1261 req.ContentLength = -1 1262 return nil 1263 }) 1264 c.Assert(err, checker.IsNil, check.Commentf("error creating container with chunked encoding")) 1265 defer resp.Body.Close() 1266 c.Assert(resp.StatusCode, checker.Equals, http.StatusCreated) 1267 } 1268 1269 func (s *DockerSuite) TestContainerAPIPostContainerStop(c *check.C) { 1270 out := runSleepingContainer(c) 1271 1272 containerID := strings.TrimSpace(out) 1273 c.Assert(waitRun(containerID), checker.IsNil) 1274 1275 cli, err := client.NewEnvClient() 1276 c.Assert(err, checker.IsNil) 1277 defer cli.Close() 1278 1279 err = cli.ContainerStop(context.Background(), containerID, nil) 1280 c.Assert(err, checker.IsNil) 1281 c.Assert(waitInspect(containerID, "{{ .State.Running }}", "false", 60*time.Second), checker.IsNil) 1282 } 1283 1284 // #14170 1285 func (s *DockerSuite) TestPostContainerAPICreateWithStringOrSliceEntrypoint(c *check.C) { 1286 config := containertypes.Config{ 1287 Image: "busybox", 1288 Entrypoint: []string{"echo"}, 1289 Cmd: []string{"hello", "world"}, 1290 } 1291 1292 cli, err := client.NewEnvClient() 1293 c.Assert(err, checker.IsNil) 1294 defer cli.Close() 1295 1296 _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "echotest") 1297 c.Assert(err, checker.IsNil) 1298 out, _ := dockerCmd(c, "start", "-a", "echotest") 1299 c.Assert(strings.TrimSpace(out), checker.Equals, "hello world") 1300 1301 config2 := struct { 1302 Image string 1303 Entrypoint string 1304 Cmd []string 1305 }{"busybox", "echo", []string{"hello", "world"}} 1306 _, _, err = request.Post("/containers/create?name=echotest2", request.JSONBody(config2)) 1307 c.Assert(err, checker.IsNil) 1308 out, _ = dockerCmd(c, "start", "-a", "echotest2") 1309 c.Assert(strings.TrimSpace(out), checker.Equals, "hello world") 1310 } 1311 1312 // #14170 1313 func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCmd(c *check.C) { 1314 config := containertypes.Config{ 1315 Image: "busybox", 1316 Cmd: []string{"echo", "hello", "world"}, 1317 } 1318 1319 cli, err := client.NewEnvClient() 1320 c.Assert(err, checker.IsNil) 1321 defer cli.Close() 1322 1323 _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "echotest") 1324 c.Assert(err, checker.IsNil) 1325 out, _ := dockerCmd(c, "start", "-a", "echotest") 1326 c.Assert(strings.TrimSpace(out), checker.Equals, "hello world") 1327 1328 config2 := struct { 1329 Image string 1330 Entrypoint string 1331 Cmd string 1332 }{"busybox", "echo", "hello world"} 1333 _, _, err = request.Post("/containers/create?name=echotest2", request.JSONBody(config2)) 1334 c.Assert(err, checker.IsNil) 1335 out, _ = dockerCmd(c, "start", "-a", "echotest2") 1336 c.Assert(strings.TrimSpace(out), checker.Equals, "hello world") 1337 } 1338 1339 // regression #14318 1340 func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCapAddDrop(c *check.C) { 1341 // Windows doesn't support CapAdd/CapDrop 1342 testRequires(c, DaemonIsLinux) 1343 config := struct { 1344 Image string 1345 CapAdd string 1346 CapDrop string 1347 }{"busybox", "NET_ADMIN", "SYS_ADMIN"} 1348 res, _, err := request.Post("/containers/create?name=capaddtest0", request.JSONBody(config)) 1349 c.Assert(err, checker.IsNil) 1350 c.Assert(res.StatusCode, checker.Equals, http.StatusCreated) 1351 1352 config2 := containertypes.Config{ 1353 Image: "busybox", 1354 } 1355 hostConfig := containertypes.HostConfig{ 1356 CapAdd: []string{"NET_ADMIN", "SYS_ADMIN"}, 1357 CapDrop: []string{"SETGID"}, 1358 } 1359 1360 cli, err := client.NewEnvClient() 1361 c.Assert(err, checker.IsNil) 1362 defer cli.Close() 1363 1364 _, err = cli.ContainerCreate(context.Background(), &config2, &hostConfig, &networktypes.NetworkingConfig{}, "capaddtest1") 1365 c.Assert(err, checker.IsNil) 1366 } 1367 1368 // #14915 1369 func (s *DockerSuite) TestContainerAPICreateNoHostConfig118(c *check.C) { 1370 testRequires(c, DaemonIsLinux) // Windows only support 1.25 or later 1371 config := containertypes.Config{ 1372 Image: "busybox", 1373 } 1374 1375 cli, err := request.NewEnvClientWithVersion("v1.18") 1376 1377 _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "") 1378 c.Assert(err, checker.IsNil) 1379 } 1380 1381 // Ensure an error occurs when you have a container read-only rootfs but you 1382 // extract an archive to a symlink in a writable volume which points to a 1383 // directory outside of the volume. 1384 func (s *DockerSuite) TestPutContainerArchiveErrSymlinkInVolumeToReadOnlyRootfs(c *check.C) { 1385 // Windows does not support read-only rootfs 1386 // Requires local volume mount bind. 1387 // --read-only + userns has remount issues 1388 testRequires(c, SameHostDaemon, NotUserNamespace, DaemonIsLinux) 1389 1390 testVol := getTestDir(c, "test-put-container-archive-err-symlink-in-volume-to-read-only-rootfs-") 1391 defer os.RemoveAll(testVol) 1392 1393 makeTestContentInDir(c, testVol) 1394 1395 cID := makeTestContainer(c, testContainerOptions{ 1396 readOnly: true, 1397 volumes: defaultVolumes(testVol), // Our bind mount is at /vol2 1398 }) 1399 1400 // Attempt to extract to a symlink in the volume which points to a 1401 // directory outside the volume. This should cause an error because the 1402 // rootfs is read-only. 1403 var httpClient *http.Client 1404 cli, err := client.NewClient(daemonHost(), "v1.20", httpClient, map[string]string{}) 1405 c.Assert(err, checker.IsNil) 1406 1407 err = cli.CopyToContainer(context.Background(), cID, "/vol2/symlinkToAbsDir", nil, types.CopyToContainerOptions{}) 1408 c.Assert(err.Error(), checker.Contains, "container rootfs is marked read-only") 1409 } 1410 1411 func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *check.C) { 1412 // Not supported on Windows 1413 testRequires(c, DaemonIsLinux) 1414 1415 cli, err := client.NewEnvClient() 1416 c.Assert(err, checker.IsNil) 1417 defer cli.Close() 1418 1419 config := containertypes.Config{ 1420 Image: "busybox", 1421 } 1422 hostConfig1 := containertypes.HostConfig{ 1423 Resources: containertypes.Resources{ 1424 CpusetCpus: "1-42,,", 1425 }, 1426 } 1427 name := "wrong-cpuset-cpus" 1428 1429 _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig1, &networktypes.NetworkingConfig{}, name) 1430 expected := "Invalid value 1-42,, for cpuset cpus" 1431 c.Assert(err.Error(), checker.Contains, expected) 1432 1433 hostConfig2 := containertypes.HostConfig{ 1434 Resources: containertypes.Resources{ 1435 CpusetMems: "42-3,1--", 1436 }, 1437 } 1438 name = "wrong-cpuset-mems" 1439 _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig2, &networktypes.NetworkingConfig{}, name) 1440 expected = "Invalid value 42-3,1-- for cpuset mems" 1441 c.Assert(err.Error(), checker.Contains, expected) 1442 } 1443 1444 func (s *DockerSuite) TestPostContainersCreateShmSizeNegative(c *check.C) { 1445 // ShmSize is not supported on Windows 1446 testRequires(c, DaemonIsLinux) 1447 config := containertypes.Config{ 1448 Image: "busybox", 1449 } 1450 hostConfig := containertypes.HostConfig{ 1451 ShmSize: -1, 1452 } 1453 1454 cli, err := client.NewEnvClient() 1455 c.Assert(err, checker.IsNil) 1456 defer cli.Close() 1457 1458 _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "") 1459 c.Assert(err.Error(), checker.Contains, "SHM size can not be less than 0") 1460 } 1461 1462 func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *check.C) { 1463 // ShmSize is not supported on Windows 1464 testRequires(c, DaemonIsLinux) 1465 var defaultSHMSize int64 = 67108864 1466 config := containertypes.Config{ 1467 Image: "busybox", 1468 Cmd: []string{"mount"}, 1469 } 1470 1471 cli, err := client.NewEnvClient() 1472 c.Assert(err, checker.IsNil) 1473 defer cli.Close() 1474 1475 container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "") 1476 c.Assert(err, check.IsNil) 1477 1478 containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) 1479 c.Assert(err, check.IsNil) 1480 1481 c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, defaultSHMSize) 1482 1483 out, _ := dockerCmd(c, "start", "-i", containerJSON.ID) 1484 shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`) 1485 if !shmRegexp.MatchString(out) { 1486 c.Fatalf("Expected shm of 64MB in mount command, got %v", out) 1487 } 1488 } 1489 1490 func (s *DockerSuite) TestPostContainersCreateShmSizeOmitted(c *check.C) { 1491 // ShmSize is not supported on Windows 1492 testRequires(c, DaemonIsLinux) 1493 config := containertypes.Config{ 1494 Image: "busybox", 1495 Cmd: []string{"mount"}, 1496 } 1497 1498 cli, err := client.NewEnvClient() 1499 c.Assert(err, checker.IsNil) 1500 defer cli.Close() 1501 1502 container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "") 1503 c.Assert(err, check.IsNil) 1504 1505 containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) 1506 c.Assert(err, check.IsNil) 1507 1508 c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, int64(67108864)) 1509 1510 out, _ := dockerCmd(c, "start", "-i", containerJSON.ID) 1511 shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`) 1512 if !shmRegexp.MatchString(out) { 1513 c.Fatalf("Expected shm of 64MB in mount command, got %v", out) 1514 } 1515 } 1516 1517 func (s *DockerSuite) TestPostContainersCreateWithShmSize(c *check.C) { 1518 // ShmSize is not supported on Windows 1519 testRequires(c, DaemonIsLinux) 1520 config := containertypes.Config{ 1521 Image: "busybox", 1522 Cmd: []string{"mount"}, 1523 } 1524 1525 hostConfig := containertypes.HostConfig{ 1526 ShmSize: 1073741824, 1527 } 1528 1529 cli, err := client.NewEnvClient() 1530 c.Assert(err, checker.IsNil) 1531 defer cli.Close() 1532 1533 container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "") 1534 c.Assert(err, check.IsNil) 1535 1536 containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) 1537 c.Assert(err, check.IsNil) 1538 1539 c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, int64(1073741824)) 1540 1541 out, _ := dockerCmd(c, "start", "-i", containerJSON.ID) 1542 shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=1048576k`) 1543 if !shmRegex.MatchString(out) { 1544 c.Fatalf("Expected shm of 1GB in mount command, got %v", out) 1545 } 1546 } 1547 1548 func (s *DockerSuite) TestPostContainersCreateMemorySwappinessHostConfigOmitted(c *check.C) { 1549 // Swappiness is not supported on Windows 1550 testRequires(c, DaemonIsLinux) 1551 config := containertypes.Config{ 1552 Image: "busybox", 1553 } 1554 1555 cli, err := client.NewEnvClient() 1556 c.Assert(err, checker.IsNil) 1557 defer cli.Close() 1558 1559 container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "") 1560 c.Assert(err, check.IsNil) 1561 1562 containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) 1563 c.Assert(err, check.IsNil) 1564 1565 c.Assert(containerJSON.HostConfig.MemorySwappiness, check.IsNil) 1566 } 1567 1568 // check validation is done daemon side and not only in cli 1569 func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *check.C) { 1570 // OomScoreAdj is not supported on Windows 1571 testRequires(c, DaemonIsLinux) 1572 1573 config := containertypes.Config{ 1574 Image: "busybox", 1575 } 1576 1577 hostConfig := containertypes.HostConfig{ 1578 OomScoreAdj: 1001, 1579 } 1580 1581 cli, err := client.NewEnvClient() 1582 c.Assert(err, checker.IsNil) 1583 defer cli.Close() 1584 1585 name := "oomscoreadj-over" 1586 _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, name) 1587 1588 expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]" 1589 c.Assert(err.Error(), checker.Contains, expected) 1590 1591 hostConfig = containertypes.HostConfig{ 1592 OomScoreAdj: -1001, 1593 } 1594 1595 name = "oomscoreadj-low" 1596 _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, name) 1597 1598 expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]" 1599 c.Assert(err.Error(), checker.Contains, expected) 1600 } 1601 1602 // test case for #22210 where an empty container name caused panic. 1603 func (s *DockerSuite) TestContainerAPIDeleteWithEmptyName(c *check.C) { 1604 cli, err := client.NewEnvClient() 1605 c.Assert(err, checker.IsNil) 1606 defer cli.Close() 1607 1608 err = cli.ContainerRemove(context.Background(), "", types.ContainerRemoveOptions{}) 1609 c.Assert(err.Error(), checker.Contains, "No such container") 1610 } 1611 1612 func (s *DockerSuite) TestContainerAPIStatsWithNetworkDisabled(c *check.C) { 1613 // Problematic on Windows as Windows does not support stats 1614 testRequires(c, DaemonIsLinux) 1615 1616 name := "testing-network-disabled" 1617 1618 config := containertypes.Config{ 1619 Image: "busybox", 1620 Cmd: []string{"top"}, 1621 NetworkDisabled: true, 1622 } 1623 1624 cli, err := client.NewEnvClient() 1625 c.Assert(err, checker.IsNil) 1626 defer cli.Close() 1627 1628 _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, name) 1629 c.Assert(err, checker.IsNil) 1630 1631 err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{}) 1632 c.Assert(err, checker.IsNil) 1633 1634 c.Assert(waitRun(name), check.IsNil) 1635 1636 type b struct { 1637 stats types.ContainerStats 1638 err error 1639 } 1640 bc := make(chan b, 1) 1641 go func() { 1642 stats, err := cli.ContainerStats(context.Background(), name, false) 1643 bc <- b{stats, err} 1644 }() 1645 1646 // allow some time to stream the stats from the container 1647 time.Sleep(4 * time.Second) 1648 dockerCmd(c, "rm", "-f", name) 1649 1650 // collect the results from the stats stream or timeout and fail 1651 // if the stream was not disconnected. 1652 select { 1653 case <-time.After(2 * time.Second): 1654 c.Fatal("stream was not closed after container was removed") 1655 case sr := <-bc: 1656 c.Assert(sr.err, checker.IsNil) 1657 sr.stats.Body.Close() 1658 } 1659 } 1660 1661 func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *check.C) { 1662 type testCase struct { 1663 config containertypes.Config 1664 hostConfig containertypes.HostConfig 1665 msg string 1666 } 1667 1668 prefix, slash := getPrefixAndSlashFromDaemonPlatform() 1669 destPath := prefix + slash + "foo" 1670 notExistPath := prefix + slash + "notexist" 1671 1672 cases := []testCase{ 1673 { 1674 config: containertypes.Config{ 1675 Image: "busybox", 1676 }, 1677 hostConfig: containertypes.HostConfig{ 1678 Mounts: []mounttypes.Mount{{ 1679 Type: "notreal", 1680 Target: destPath, 1681 }, 1682 }, 1683 }, 1684 1685 msg: "mount type unknown", 1686 }, 1687 { 1688 config: containertypes.Config{ 1689 Image: "busybox", 1690 }, 1691 hostConfig: containertypes.HostConfig{ 1692 Mounts: []mounttypes.Mount{{ 1693 Type: "bind"}}}, 1694 msg: "Target must not be empty", 1695 }, 1696 { 1697 config: containertypes.Config{ 1698 Image: "busybox", 1699 }, 1700 hostConfig: containertypes.HostConfig{ 1701 Mounts: []mounttypes.Mount{{ 1702 Type: "bind", 1703 Target: destPath}}}, 1704 msg: "Source must not be empty", 1705 }, 1706 { 1707 config: containertypes.Config{ 1708 Image: "busybox", 1709 }, 1710 hostConfig: containertypes.HostConfig{ 1711 Mounts: []mounttypes.Mount{{ 1712 Type: "bind", 1713 Source: notExistPath, 1714 Target: destPath}}}, 1715 msg: "bind source path does not exist", 1716 }, 1717 { 1718 config: containertypes.Config{ 1719 Image: "busybox", 1720 }, 1721 hostConfig: containertypes.HostConfig{ 1722 Mounts: []mounttypes.Mount{{ 1723 Type: "volume"}}}, 1724 msg: "Target must not be empty", 1725 }, 1726 { 1727 config: containertypes.Config{ 1728 Image: "busybox", 1729 }, 1730 hostConfig: containertypes.HostConfig{ 1731 Mounts: []mounttypes.Mount{{ 1732 Type: "volume", 1733 Source: "hello", 1734 Target: destPath}}}, 1735 msg: "", 1736 }, 1737 { 1738 config: containertypes.Config{ 1739 Image: "busybox", 1740 }, 1741 hostConfig: containertypes.HostConfig{ 1742 Mounts: []mounttypes.Mount{{ 1743 Type: "volume", 1744 Source: "hello2", 1745 Target: destPath, 1746 VolumeOptions: &mounttypes.VolumeOptions{ 1747 DriverConfig: &mounttypes.Driver{ 1748 Name: "local"}}}}}, 1749 msg: "", 1750 }, 1751 } 1752 1753 if SameHostDaemon() { 1754 tmpDir, err := ioutils.TempDir("", "test-mounts-api") 1755 c.Assert(err, checker.IsNil) 1756 defer os.RemoveAll(tmpDir) 1757 cases = append(cases, []testCase{ 1758 { 1759 config: containertypes.Config{ 1760 Image: "busybox", 1761 }, 1762 hostConfig: containertypes.HostConfig{ 1763 Mounts: []mounttypes.Mount{{ 1764 Type: "bind", 1765 Source: tmpDir, 1766 Target: destPath}}}, 1767 msg: "", 1768 }, 1769 { 1770 config: containertypes.Config{ 1771 Image: "busybox", 1772 }, 1773 hostConfig: containertypes.HostConfig{ 1774 Mounts: []mounttypes.Mount{{ 1775 Type: "bind", 1776 Source: tmpDir, 1777 Target: destPath, 1778 VolumeOptions: &mounttypes.VolumeOptions{}}}}, 1779 msg: "VolumeOptions must not be specified", 1780 }, 1781 }...) 1782 } 1783 1784 if DaemonIsLinux() { 1785 cases = append(cases, []testCase{ 1786 { 1787 config: containertypes.Config{ 1788 Image: "busybox", 1789 }, 1790 hostConfig: containertypes.HostConfig{ 1791 Mounts: []mounttypes.Mount{{ 1792 Type: "volume", 1793 Source: "hello3", 1794 Target: destPath, 1795 VolumeOptions: &mounttypes.VolumeOptions{ 1796 DriverConfig: &mounttypes.Driver{ 1797 Name: "local", 1798 Options: map[string]string{"o": "size=1"}}}}}}, 1799 msg: "", 1800 }, 1801 { 1802 config: containertypes.Config{ 1803 Image: "busybox", 1804 }, 1805 hostConfig: containertypes.HostConfig{ 1806 Mounts: []mounttypes.Mount{{ 1807 Type: "tmpfs", 1808 Target: destPath}}}, 1809 msg: "", 1810 }, 1811 { 1812 config: containertypes.Config{ 1813 Image: "busybox", 1814 }, 1815 hostConfig: containertypes.HostConfig{ 1816 Mounts: []mounttypes.Mount{{ 1817 Type: "tmpfs", 1818 Target: destPath, 1819 TmpfsOptions: &mounttypes.TmpfsOptions{ 1820 SizeBytes: 4096 * 1024, 1821 Mode: 0700, 1822 }}}}, 1823 msg: "", 1824 }, 1825 1826 { 1827 config: containertypes.Config{ 1828 Image: "busybox", 1829 }, 1830 hostConfig: containertypes.HostConfig{ 1831 Mounts: []mounttypes.Mount{{ 1832 Type: "tmpfs", 1833 Source: "/shouldnotbespecified", 1834 Target: destPath}}}, 1835 msg: "Source must not be specified", 1836 }, 1837 }...) 1838 1839 } 1840 cli, err := client.NewEnvClient() 1841 c.Assert(err, checker.IsNil) 1842 defer cli.Close() 1843 1844 for i, x := range cases { 1845 c.Logf("case %d", i) 1846 _, err = cli.ContainerCreate(context.Background(), &x.config, &x.hostConfig, &networktypes.NetworkingConfig{}, "") 1847 if len(x.msg) > 0 { 1848 c.Assert(err.Error(), checker.Contains, x.msg, check.Commentf("%v", cases[i].config)) 1849 } else { 1850 c.Assert(err, checker.IsNil) 1851 } 1852 } 1853 } 1854 1855 func (s *DockerSuite) TestContainerAPICreateMountsBindRead(c *check.C) { 1856 testRequires(c, NotUserNamespace, SameHostDaemon) 1857 // also with data in the host side 1858 prefix, slash := getPrefixAndSlashFromDaemonPlatform() 1859 destPath := prefix + slash + "foo" 1860 tmpDir, err := ioutil.TempDir("", "test-mounts-api-bind") 1861 c.Assert(err, checker.IsNil) 1862 defer os.RemoveAll(tmpDir) 1863 err = ioutil.WriteFile(filepath.Join(tmpDir, "bar"), []byte("hello"), 666) 1864 c.Assert(err, checker.IsNil) 1865 config := containertypes.Config{ 1866 Image: "busybox", 1867 Cmd: []string{"/bin/sh", "-c", "cat /foo/bar"}, 1868 } 1869 hostConfig := containertypes.HostConfig{ 1870 Mounts: []mounttypes.Mount{ 1871 {Type: "bind", Source: tmpDir, Target: destPath}, 1872 }, 1873 } 1874 cli, err := client.NewEnvClient() 1875 c.Assert(err, checker.IsNil) 1876 defer cli.Close() 1877 1878 _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "test") 1879 c.Assert(err, checker.IsNil) 1880 1881 out, _ := dockerCmd(c, "start", "-a", "test") 1882 c.Assert(out, checker.Equals, "hello") 1883 } 1884 1885 // Test Mounts comes out as expected for the MountPoint 1886 func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *check.C) { 1887 prefix, slash := getPrefixAndSlashFromDaemonPlatform() 1888 destPath := prefix + slash + "foo" 1889 1890 var ( 1891 testImg string 1892 ) 1893 if testEnv.DaemonPlatform() != "windows" { 1894 testImg = "test-mount-config" 1895 buildImageSuccessfully(c, testImg, build.WithDockerfile(` 1896 FROM busybox 1897 RUN mkdir `+destPath+` && touch `+destPath+slash+`bar 1898 CMD cat `+destPath+slash+`bar 1899 `)) 1900 } else { 1901 testImg = "busybox" 1902 } 1903 1904 type testCase struct { 1905 spec mounttypes.Mount 1906 expected types.MountPoint 1907 } 1908 1909 var selinuxSharedLabel string 1910 if runtime.GOOS == "linux" { 1911 selinuxSharedLabel = "z" 1912 } 1913 1914 cases := []testCase{ 1915 // use literal strings here for `Type` instead of the defined constants in the volume package to keep this honest 1916 // Validation of the actual `Mount` struct is done in another test is not needed here 1917 { 1918 spec: mounttypes.Mount{Type: "volume", Target: destPath}, 1919 expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel}, 1920 }, 1921 { 1922 spec: mounttypes.Mount{Type: "volume", Target: destPath + slash}, 1923 expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel}, 1924 }, 1925 { 1926 spec: mounttypes.Mount{Type: "volume", Target: destPath, Source: "test1"}, 1927 expected: types.MountPoint{Type: "volume", Name: "test1", RW: true, Destination: destPath, Mode: selinuxSharedLabel}, 1928 }, 1929 { 1930 spec: mounttypes.Mount{Type: "volume", Target: destPath, ReadOnly: true, Source: "test2"}, 1931 expected: types.MountPoint{Type: "volume", Name: "test2", RW: false, Destination: destPath, Mode: selinuxSharedLabel}, 1932 }, 1933 { 1934 spec: mounttypes.Mount{Type: "volume", Target: destPath, Source: "test3", VolumeOptions: &mounttypes.VolumeOptions{DriverConfig: &mounttypes.Driver{Name: volume.DefaultDriverName}}}, 1935 expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", Name: "test3", RW: true, Destination: destPath, Mode: selinuxSharedLabel}, 1936 }, 1937 } 1938 1939 if SameHostDaemon() { 1940 // setup temp dir for testing binds 1941 tmpDir1, err := ioutil.TempDir("", "test-mounts-api-1") 1942 c.Assert(err, checker.IsNil) 1943 defer os.RemoveAll(tmpDir1) 1944 cases = append(cases, []testCase{ 1945 { 1946 spec: mounttypes.Mount{ 1947 Type: "bind", 1948 Source: tmpDir1, 1949 Target: destPath, 1950 }, 1951 expected: types.MountPoint{ 1952 Type: "bind", 1953 RW: true, 1954 Destination: destPath, 1955 Source: tmpDir1, 1956 }, 1957 }, 1958 { 1959 spec: mounttypes.Mount{Type: "bind", Source: tmpDir1, Target: destPath, ReadOnly: true}, 1960 expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir1}, 1961 }, 1962 }...) 1963 1964 // for modes only supported on Linux 1965 if DaemonIsLinux() { 1966 tmpDir3, err := ioutils.TempDir("", "test-mounts-api-3") 1967 c.Assert(err, checker.IsNil) 1968 defer os.RemoveAll(tmpDir3) 1969 1970 c.Assert(mount.Mount(tmpDir3, tmpDir3, "none", "bind,rw"), checker.IsNil) 1971 c.Assert(mount.ForceMount("", tmpDir3, "none", "shared"), checker.IsNil) 1972 1973 cases = append(cases, []testCase{ 1974 { 1975 spec: mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath}, 1976 expected: types.MountPoint{Type: "bind", RW: true, Destination: destPath, Source: tmpDir3}, 1977 }, 1978 { 1979 spec: mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true}, 1980 expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir3}, 1981 }, 1982 { 1983 spec: mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true, BindOptions: &mounttypes.BindOptions{Propagation: "shared"}}, 1984 expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir3, Propagation: "shared"}, 1985 }, 1986 }...) 1987 } 1988 } 1989 1990 if testEnv.DaemonPlatform() != "windows" { // Windows does not support volume populate 1991 cases = append(cases, []testCase{ 1992 { 1993 spec: mounttypes.Mount{Type: "volume", Target: destPath, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}}, 1994 expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel}, 1995 }, 1996 { 1997 spec: mounttypes.Mount{Type: "volume", Target: destPath + slash, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}}, 1998 expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel}, 1999 }, 2000 { 2001 spec: mounttypes.Mount{Type: "volume", Target: destPath, Source: "test4", VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}}, 2002 expected: types.MountPoint{Type: "volume", Name: "test4", RW: true, Destination: destPath, Mode: selinuxSharedLabel}, 2003 }, 2004 { 2005 spec: mounttypes.Mount{Type: "volume", Target: destPath, Source: "test5", ReadOnly: true, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}}, 2006 expected: types.MountPoint{Type: "volume", Name: "test5", RW: false, Destination: destPath, Mode: selinuxSharedLabel}, 2007 }, 2008 }...) 2009 } 2010 2011 type wrapper struct { 2012 containertypes.Config 2013 HostConfig containertypes.HostConfig 2014 } 2015 type createResp struct { 2016 ID string `json:"Id"` 2017 } 2018 2019 ctx := context.Background() 2020 apiclient := testEnv.APIClient() 2021 for i, x := range cases { 2022 c.Logf("case %d - config: %v", i, x.spec) 2023 container, err := apiclient.ContainerCreate( 2024 ctx, 2025 &containertypes.Config{Image: testImg}, 2026 &containertypes.HostConfig{Mounts: []mounttypes.Mount{x.spec}}, 2027 &networktypes.NetworkingConfig{}, 2028 "") 2029 require.NoError(c, err) 2030 2031 containerInspect, err := apiclient.ContainerInspect(ctx, container.ID) 2032 require.NoError(c, err) 2033 mps := containerInspect.Mounts 2034 require.Len(c, mps, 1) 2035 mountPoint := mps[0] 2036 2037 if x.expected.Source != "" { 2038 assert.Equal(c, x.expected.Source, mountPoint.Source) 2039 } 2040 if x.expected.Name != "" { 2041 assert.Equal(c, x.expected.Name, mountPoint.Name) 2042 } 2043 if x.expected.Driver != "" { 2044 assert.Equal(c, x.expected.Driver, mountPoint.Driver) 2045 } 2046 if x.expected.Propagation != "" { 2047 assert.Equal(c, x.expected.Propagation, mountPoint.Propagation) 2048 } 2049 assert.Equal(c, x.expected.RW, mountPoint.RW) 2050 assert.Equal(c, x.expected.Type, mountPoint.Type) 2051 assert.Equal(c, x.expected.Mode, mountPoint.Mode) 2052 assert.Equal(c, x.expected.Destination, mountPoint.Destination) 2053 2054 err = apiclient.ContainerStart(ctx, container.ID, types.ContainerStartOptions{}) 2055 require.NoError(c, err) 2056 poll.WaitOn(c, containerExit(apiclient, container.ID), poll.WithDelay(time.Second)) 2057 2058 err = apiclient.ContainerRemove(ctx, container.ID, types.ContainerRemoveOptions{ 2059 RemoveVolumes: true, 2060 Force: true, 2061 }) 2062 require.NoError(c, err) 2063 2064 switch { 2065 2066 // Named volumes still exist after the container is removed 2067 case x.spec.Type == "volume" && len(x.spec.Source) > 0: 2068 _, err := apiclient.VolumeInspect(ctx, mountPoint.Name) 2069 require.NoError(c, err) 2070 2071 // Bind mounts are never removed with the container 2072 case x.spec.Type == "bind": 2073 2074 // anonymous volumes are removed 2075 default: 2076 _, err := apiclient.VolumeInspect(ctx, mountPoint.Name) 2077 assert.True(c, client.IsErrNotFound(err)) 2078 } 2079 } 2080 } 2081 2082 func containerExit(apiclient client.APIClient, name string) func(poll.LogT) poll.Result { 2083 return func(logT poll.LogT) poll.Result { 2084 container, err := apiclient.ContainerInspect(context.Background(), name) 2085 if err != nil { 2086 return poll.Error(err) 2087 } 2088 switch container.State.Status { 2089 case "created", "running": 2090 return poll.Continue("container %s is %s, waiting for exit", name, container.State.Status) 2091 } 2092 return poll.Success() 2093 } 2094 } 2095 2096 func (s *DockerSuite) TestContainersAPICreateMountsTmpfs(c *check.C) { 2097 testRequires(c, DaemonIsLinux) 2098 type testCase struct { 2099 cfg mounttypes.Mount 2100 expectedOptions []string 2101 } 2102 target := "/foo" 2103 cases := []testCase{ 2104 { 2105 cfg: mounttypes.Mount{ 2106 Type: "tmpfs", 2107 Target: target}, 2108 expectedOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime"}, 2109 }, 2110 { 2111 cfg: mounttypes.Mount{ 2112 Type: "tmpfs", 2113 Target: target, 2114 TmpfsOptions: &mounttypes.TmpfsOptions{ 2115 SizeBytes: 4096 * 1024, Mode: 0700}}, 2116 expectedOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime", "size=4096k", "mode=700"}, 2117 }, 2118 } 2119 2120 cli, err := client.NewEnvClient() 2121 c.Assert(err, checker.IsNil) 2122 defer cli.Close() 2123 2124 config := containertypes.Config{ 2125 Image: "busybox", 2126 Cmd: []string{"/bin/sh", "-c", fmt.Sprintf("mount | grep 'tmpfs on %s'", target)}, 2127 } 2128 for i, x := range cases { 2129 cName := fmt.Sprintf("test-tmpfs-%d", i) 2130 hostConfig := containertypes.HostConfig{ 2131 Mounts: []mounttypes.Mount{x.cfg}, 2132 } 2133 2134 _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, cName) 2135 c.Assert(err, checker.IsNil) 2136 out, _ := dockerCmd(c, "start", "-a", cName) 2137 for _, option := range x.expectedOptions { 2138 c.Assert(out, checker.Contains, option) 2139 } 2140 } 2141 } 2142 2143 // Regression test for #33334 2144 // Makes sure that when a container which has a custom stop signal + restart=always 2145 // gets killed (with SIGKILL) by the kill API, that the restart policy is cancelled. 2146 func (s *DockerSuite) TestContainerKillCustomStopSignal(c *check.C) { 2147 id := strings.TrimSpace(runSleepingContainer(c, "--stop-signal=SIGTERM", "--restart=always")) 2148 res, _, err := request.Post("/containers/" + id + "/kill") 2149 c.Assert(err, checker.IsNil) 2150 defer res.Body.Close() 2151 2152 b, err := ioutil.ReadAll(res.Body) 2153 c.Assert(res.StatusCode, checker.Equals, http.StatusNoContent, check.Commentf(string(b))) 2154 err = waitInspect(id, "{{.State.Running}} {{.State.Restarting}}", "false false", 30*time.Second) 2155 c.Assert(err, checker.IsNil) 2156 }