github.com/hustcat/docker@v1.3.3-0.20160314103604-901c67a8eeab/integration-cli/docker_cli_run_unix_test.go (about) 1 // +build !windows 2 3 package main 4 5 import ( 6 "bufio" 7 "fmt" 8 "io/ioutil" 9 "os" 10 "os/exec" 11 "path/filepath" 12 "regexp" 13 "strconv" 14 "strings" 15 "sync" 16 "time" 17 18 "github.com/docker/docker/pkg/homedir" 19 "github.com/docker/docker/pkg/integration/checker" 20 "github.com/docker/docker/pkg/mount" 21 "github.com/docker/docker/pkg/parsers" 22 "github.com/docker/docker/pkg/sysinfo" 23 "github.com/go-check/check" 24 "github.com/kr/pty" 25 ) 26 27 // #6509 28 func (s *DockerSuite) TestRunRedirectStdout(c *check.C) { 29 checkRedirect := func(command string) { 30 _, tty, err := pty.Open() 31 c.Assert(err, checker.IsNil, check.Commentf("Could not open pty")) 32 cmd := exec.Command("sh", "-c", command) 33 cmd.Stdin = tty 34 cmd.Stdout = tty 35 cmd.Stderr = tty 36 c.Assert(cmd.Start(), checker.IsNil) 37 ch := make(chan error) 38 go func() { 39 ch <- cmd.Wait() 40 close(ch) 41 }() 42 43 select { 44 case <-time.After(10 * time.Second): 45 c.Fatal("command timeout") 46 case err := <-ch: 47 c.Assert(err, checker.IsNil, check.Commentf("wait err")) 48 } 49 } 50 51 checkRedirect(dockerBinary + " run -i busybox cat /etc/passwd | grep -q root") 52 checkRedirect(dockerBinary + " run busybox cat /etc/passwd | grep -q root") 53 } 54 55 // Test recursive bind mount works by default 56 func (s *DockerSuite) TestRunWithVolumesIsRecursive(c *check.C) { 57 // /tmp gets permission denied 58 testRequires(c, NotUserNamespace, SameHostDaemon) 59 tmpDir, err := ioutil.TempDir("", "docker_recursive_mount_test") 60 c.Assert(err, checker.IsNil) 61 62 defer os.RemoveAll(tmpDir) 63 64 // Create a temporary tmpfs mount. 65 tmpfsDir := filepath.Join(tmpDir, "tmpfs") 66 c.Assert(os.MkdirAll(tmpfsDir, 0777), checker.IsNil, check.Commentf("failed to mkdir at %s", tmpfsDir)) 67 c.Assert(mount.Mount("tmpfs", tmpfsDir, "tmpfs", ""), checker.IsNil, check.Commentf("failed to create a tmpfs mount at %s", tmpfsDir)) 68 69 f, err := ioutil.TempFile(tmpfsDir, "touch-me") 70 c.Assert(err, checker.IsNil) 71 defer f.Close() 72 73 runCmd := exec.Command(dockerBinary, "run", "--name", "test-data", "--volume", fmt.Sprintf("%s:/tmp:ro", tmpDir), "busybox:latest", "ls", "/tmp/tmpfs") 74 out, _, _, err := runCommandWithStdoutStderr(runCmd) 75 c.Assert(err, checker.IsNil) 76 c.Assert(out, checker.Contains, filepath.Base(f.Name()), check.Commentf("Recursive bind mount test failed. Expected file not found")) 77 } 78 79 func (s *DockerSuite) TestRunDeviceDirectory(c *check.C) { 80 testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm) 81 if _, err := os.Stat("/dev/snd"); err != nil { 82 c.Skip("Host does not have /dev/snd") 83 } 84 85 out, _ := dockerCmd(c, "run", "--device", "/dev/snd:/dev/snd", "busybox", "sh", "-c", "ls /dev/snd/") 86 c.Assert(strings.Trim(out, "\r\n"), checker.Contains, "timer", check.Commentf("expected output /dev/snd/timer")) 87 88 out, _ = dockerCmd(c, "run", "--device", "/dev/snd:/dev/othersnd", "busybox", "sh", "-c", "ls /dev/othersnd/") 89 c.Assert(strings.Trim(out, "\r\n"), checker.Contains, "seq", check.Commentf("expected output /dev/othersnd/seq")) 90 } 91 92 // TestRunDetach checks attaching and detaching with the default escape sequence. 93 func (s *DockerSuite) TestRunAttachDetach(c *check.C) { 94 name := "attach-detach" 95 96 dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat") 97 98 cmd := exec.Command(dockerBinary, "attach", name) 99 stdout, err := cmd.StdoutPipe() 100 c.Assert(err, checker.IsNil) 101 cpty, tty, err := pty.Open() 102 c.Assert(err, checker.IsNil) 103 defer cpty.Close() 104 cmd.Stdin = tty 105 c.Assert(cmd.Start(), checker.IsNil) 106 c.Assert(waitRun(name), check.IsNil) 107 108 _, err = cpty.Write([]byte("hello\n")) 109 c.Assert(err, checker.IsNil) 110 111 out, err := bufio.NewReader(stdout).ReadString('\n') 112 c.Assert(err, checker.IsNil) 113 c.Assert(strings.TrimSpace(out), checker.Equals, "hello") 114 115 // escape sequence 116 _, err = cpty.Write([]byte{16}) 117 c.Assert(err, checker.IsNil) 118 time.Sleep(100 * time.Millisecond) 119 _, err = cpty.Write([]byte{17}) 120 c.Assert(err, checker.IsNil) 121 122 ch := make(chan struct{}) 123 go func() { 124 cmd.Wait() 125 ch <- struct{}{} 126 }() 127 128 select { 129 case <-ch: 130 case <-time.After(10 * time.Second): 131 c.Fatal("timed out waiting for container to exit") 132 } 133 134 running := inspectField(c, name, "State.Running") 135 c.Assert(running, checker.Equals, "true", check.Commentf("expected container to still be running")) 136 } 137 138 // TestRunDetach checks attaching and detaching with the escape sequence specified via flags. 139 func (s *DockerSuite) TestRunAttachDetachFromFlag(c *check.C) { 140 name := "attach-detach" 141 keyCtrlA := []byte{1} 142 keyA := []byte{97} 143 144 dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat") 145 146 cmd := exec.Command(dockerBinary, "attach", "--detach-keys='ctrl-a,a'", name) 147 stdout, err := cmd.StdoutPipe() 148 if err != nil { 149 c.Fatal(err) 150 } 151 cpty, tty, err := pty.Open() 152 if err != nil { 153 c.Fatal(err) 154 } 155 defer cpty.Close() 156 cmd.Stdin = tty 157 if err := cmd.Start(); err != nil { 158 c.Fatal(err) 159 } 160 c.Assert(waitRun(name), check.IsNil) 161 162 if _, err := cpty.Write([]byte("hello\n")); err != nil { 163 c.Fatal(err) 164 } 165 166 out, err := bufio.NewReader(stdout).ReadString('\n') 167 if err != nil { 168 c.Fatal(err) 169 } 170 if strings.TrimSpace(out) != "hello" { 171 c.Fatalf("expected 'hello', got %q", out) 172 } 173 174 // escape sequence 175 if _, err := cpty.Write(keyCtrlA); err != nil { 176 c.Fatal(err) 177 } 178 time.Sleep(100 * time.Millisecond) 179 if _, err := cpty.Write(keyA); err != nil { 180 c.Fatal(err) 181 } 182 183 ch := make(chan struct{}) 184 go func() { 185 cmd.Wait() 186 ch <- struct{}{} 187 }() 188 189 select { 190 case <-ch: 191 case <-time.After(10 * time.Second): 192 c.Fatal("timed out waiting for container to exit") 193 } 194 195 running := inspectField(c, name, "State.Running") 196 c.Assert(running, checker.Equals, "true", check.Commentf("expected container to still be running")) 197 } 198 199 // TestRunDetach checks attaching and detaching with the escape sequence specified via config file. 200 func (s *DockerSuite) TestRunAttachDetachFromConfig(c *check.C) { 201 keyCtrlA := []byte{1} 202 keyA := []byte{97} 203 204 // Setup config 205 homeKey := homedir.Key() 206 homeVal := homedir.Get() 207 tmpDir, err := ioutil.TempDir("", "fake-home") 208 c.Assert(err, checker.IsNil) 209 defer os.RemoveAll(tmpDir) 210 211 dotDocker := filepath.Join(tmpDir, ".docker") 212 os.Mkdir(dotDocker, 0600) 213 tmpCfg := filepath.Join(dotDocker, "config.json") 214 215 defer func() { os.Setenv(homeKey, homeVal) }() 216 os.Setenv(homeKey, tmpDir) 217 218 data := `{ 219 "detachKeys": "ctrl-a,a" 220 }` 221 222 err = ioutil.WriteFile(tmpCfg, []byte(data), 0600) 223 c.Assert(err, checker.IsNil) 224 225 // Then do the work 226 name := "attach-detach" 227 dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat") 228 229 cmd := exec.Command(dockerBinary, "attach", name) 230 stdout, err := cmd.StdoutPipe() 231 if err != nil { 232 c.Fatal(err) 233 } 234 cpty, tty, err := pty.Open() 235 if err != nil { 236 c.Fatal(err) 237 } 238 defer cpty.Close() 239 cmd.Stdin = tty 240 if err := cmd.Start(); err != nil { 241 c.Fatal(err) 242 } 243 c.Assert(waitRun(name), check.IsNil) 244 245 if _, err := cpty.Write([]byte("hello\n")); err != nil { 246 c.Fatal(err) 247 } 248 249 out, err := bufio.NewReader(stdout).ReadString('\n') 250 if err != nil { 251 c.Fatal(err) 252 } 253 if strings.TrimSpace(out) != "hello" { 254 c.Fatalf("expected 'hello', got %q", out) 255 } 256 257 // escape sequence 258 if _, err := cpty.Write(keyCtrlA); err != nil { 259 c.Fatal(err) 260 } 261 time.Sleep(100 * time.Millisecond) 262 if _, err := cpty.Write(keyA); err != nil { 263 c.Fatal(err) 264 } 265 266 ch := make(chan struct{}) 267 go func() { 268 cmd.Wait() 269 ch <- struct{}{} 270 }() 271 272 select { 273 case <-ch: 274 case <-time.After(10 * time.Second): 275 c.Fatal("timed out waiting for container to exit") 276 } 277 278 running := inspectField(c, name, "State.Running") 279 c.Assert(running, checker.Equals, "true", check.Commentf("expected container to still be running")) 280 } 281 282 // TestRunDetach checks attaching and detaching with the detach flags, making sure it overrides config file 283 func (s *DockerSuite) TestRunAttachDetachKeysOverrideConfig(c *check.C) { 284 keyCtrlA := []byte{1} 285 keyA := []byte{97} 286 287 // Setup config 288 homeKey := homedir.Key() 289 homeVal := homedir.Get() 290 tmpDir, err := ioutil.TempDir("", "fake-home") 291 c.Assert(err, checker.IsNil) 292 defer os.RemoveAll(tmpDir) 293 294 dotDocker := filepath.Join(tmpDir, ".docker") 295 os.Mkdir(dotDocker, 0600) 296 tmpCfg := filepath.Join(dotDocker, "config.json") 297 298 defer func() { os.Setenv(homeKey, homeVal) }() 299 os.Setenv(homeKey, tmpDir) 300 301 data := `{ 302 "detachKeys": "ctrl-e,e" 303 }` 304 305 err = ioutil.WriteFile(tmpCfg, []byte(data), 0600) 306 c.Assert(err, checker.IsNil) 307 308 // Then do the work 309 name := "attach-detach" 310 dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat") 311 312 cmd := exec.Command(dockerBinary, "attach", "--detach-keys='ctrl-a,a'", name) 313 stdout, err := cmd.StdoutPipe() 314 if err != nil { 315 c.Fatal(err) 316 } 317 cpty, tty, err := pty.Open() 318 if err != nil { 319 c.Fatal(err) 320 } 321 defer cpty.Close() 322 cmd.Stdin = tty 323 if err := cmd.Start(); err != nil { 324 c.Fatal(err) 325 } 326 c.Assert(waitRun(name), check.IsNil) 327 328 if _, err := cpty.Write([]byte("hello\n")); err != nil { 329 c.Fatal(err) 330 } 331 332 out, err := bufio.NewReader(stdout).ReadString('\n') 333 if err != nil { 334 c.Fatal(err) 335 } 336 if strings.TrimSpace(out) != "hello" { 337 c.Fatalf("expected 'hello', got %q", out) 338 } 339 340 // escape sequence 341 if _, err := cpty.Write(keyCtrlA); err != nil { 342 c.Fatal(err) 343 } 344 time.Sleep(100 * time.Millisecond) 345 if _, err := cpty.Write(keyA); err != nil { 346 c.Fatal(err) 347 } 348 349 ch := make(chan struct{}) 350 go func() { 351 cmd.Wait() 352 ch <- struct{}{} 353 }() 354 355 select { 356 case <-ch: 357 case <-time.After(10 * time.Second): 358 c.Fatal("timed out waiting for container to exit") 359 } 360 361 running := inspectField(c, name, "State.Running") 362 c.Assert(running, checker.Equals, "true", check.Commentf("expected container to still be running")) 363 } 364 365 // "test" should be printed 366 func (s *DockerSuite) TestRunWithCPUQuota(c *check.C) { 367 testRequires(c, cpuCfsQuota) 368 369 file := "/sys/fs/cgroup/cpu/cpu.cfs_quota_us" 370 out, _ := dockerCmd(c, "run", "--cpu-quota", "8000", "--name", "test", "busybox", "cat", file) 371 c.Assert(strings.TrimSpace(out), checker.Equals, "8000") 372 373 out = inspectField(c, "test", "HostConfig.CpuQuota") 374 c.Assert(out, checker.Equals, "8000", check.Commentf("setting the CPU CFS quota failed")) 375 } 376 377 func (s *DockerSuite) TestRunWithCpuPeriod(c *check.C) { 378 testRequires(c, cpuCfsPeriod) 379 380 file := "/sys/fs/cgroup/cpu/cpu.cfs_period_us" 381 out, _ := dockerCmd(c, "run", "--cpu-period", "50000", "--name", "test", "busybox", "cat", file) 382 c.Assert(strings.TrimSpace(out), checker.Equals, "50000") 383 384 out = inspectField(c, "test", "HostConfig.CpuPeriod") 385 c.Assert(out, checker.Equals, "50000", check.Commentf("setting the CPU CFS period failed")) 386 } 387 388 func (s *DockerSuite) TestRunWithKernelMemory(c *check.C) { 389 testRequires(c, kernelMemorySupport) 390 391 file := "/sys/fs/cgroup/memory/memory.kmem.limit_in_bytes" 392 stdout, _, _ := dockerCmdWithStdoutStderr(c, "run", "--kernel-memory", "50M", "--name", "test1", "busybox", "cat", file) 393 c.Assert(strings.TrimSpace(stdout), checker.Equals, "52428800") 394 395 out := inspectField(c, "test1", "HostConfig.KernelMemory") 396 c.Assert(out, check.Equals, "52428800") 397 } 398 399 func (s *DockerSuite) TestRunWithInvalidKernelMemory(c *check.C) { 400 testRequires(c, kernelMemorySupport) 401 402 out, _, err := dockerCmdWithError("run", "--kernel-memory", "2M", "busybox", "true") 403 c.Assert(err, check.NotNil) 404 expected := "Minimum kernel memory limit allowed is 4MB" 405 c.Assert(out, checker.Contains, expected) 406 407 out, _, err = dockerCmdWithError("run", "--kernel-memory", "-16m", "--name", "test2", "busybox", "echo", "test") 408 c.Assert(err, check.NotNil) 409 expected = "invalid size" 410 c.Assert(out, checker.Contains, expected) 411 } 412 413 func (s *DockerSuite) TestRunWithCPUShares(c *check.C) { 414 testRequires(c, cpuShare) 415 416 file := "/sys/fs/cgroup/cpu/cpu.shares" 417 out, _ := dockerCmd(c, "run", "--cpu-shares", "1000", "--name", "test", "busybox", "cat", file) 418 c.Assert(strings.TrimSpace(out), checker.Equals, "1000") 419 420 out = inspectField(c, "test", "HostConfig.CPUShares") 421 c.Assert(out, check.Equals, "1000") 422 } 423 424 // "test" should be printed 425 func (s *DockerSuite) TestRunEchoStdoutWithCPUSharesAndMemoryLimit(c *check.C) { 426 testRequires(c, cpuShare) 427 testRequires(c, memoryLimitSupport) 428 out, _, _ := dockerCmdWithStdoutStderr(c, "run", "--cpu-shares", "1000", "-m", "32m", "busybox", "echo", "test") 429 c.Assert(out, checker.Equals, "test\n", check.Commentf("container should've printed 'test'")) 430 } 431 432 func (s *DockerSuite) TestRunWithCpusetCpus(c *check.C) { 433 testRequires(c, cgroupCpuset) 434 435 file := "/sys/fs/cgroup/cpuset/cpuset.cpus" 436 out, _ := dockerCmd(c, "run", "--cpuset-cpus", "0", "--name", "test", "busybox", "cat", file) 437 c.Assert(strings.TrimSpace(out), checker.Equals, "0") 438 439 out = inspectField(c, "test", "HostConfig.CpusetCpus") 440 c.Assert(out, check.Equals, "0") 441 } 442 443 func (s *DockerSuite) TestRunWithCpusetMems(c *check.C) { 444 testRequires(c, cgroupCpuset) 445 446 file := "/sys/fs/cgroup/cpuset/cpuset.mems" 447 out, _ := dockerCmd(c, "run", "--cpuset-mems", "0", "--name", "test", "busybox", "cat", file) 448 c.Assert(strings.TrimSpace(out), checker.Equals, "0") 449 450 out = inspectField(c, "test", "HostConfig.CpusetMems") 451 c.Assert(out, check.Equals, "0") 452 } 453 454 func (s *DockerSuite) TestRunWithBlkioWeight(c *check.C) { 455 testRequires(c, blkioWeight) 456 457 file := "/sys/fs/cgroup/blkio/blkio.weight" 458 out, _ := dockerCmd(c, "run", "--blkio-weight", "300", "--name", "test", "busybox", "cat", file) 459 c.Assert(strings.TrimSpace(out), checker.Equals, "300") 460 461 out = inspectField(c, "test", "HostConfig.BlkioWeight") 462 c.Assert(out, check.Equals, "300") 463 } 464 465 func (s *DockerSuite) TestRunWithInvalidBlkioWeight(c *check.C) { 466 testRequires(c, blkioWeight) 467 out, _, err := dockerCmdWithError("run", "--blkio-weight", "5", "busybox", "true") 468 c.Assert(err, check.NotNil, check.Commentf(out)) 469 expected := "Range of blkio weight is from 10 to 1000" 470 c.Assert(out, checker.Contains, expected) 471 } 472 473 func (s *DockerSuite) TestRunWithInvalidPathforBlkioWeightDevice(c *check.C) { 474 testRequires(c, blkioWeight) 475 out, _, err := dockerCmdWithError("run", "--blkio-weight-device", "/dev/sdX:100", "busybox", "true") 476 c.Assert(err, check.NotNil, check.Commentf(out)) 477 } 478 479 func (s *DockerSuite) TestRunWithInvalidPathforBlkioDeviceReadBps(c *check.C) { 480 testRequires(c, blkioWeight) 481 out, _, err := dockerCmdWithError("run", "--device-read-bps", "/dev/sdX:500", "busybox", "true") 482 c.Assert(err, check.NotNil, check.Commentf(out)) 483 } 484 485 func (s *DockerSuite) TestRunWithInvalidPathforBlkioDeviceWriteBps(c *check.C) { 486 testRequires(c, blkioWeight) 487 out, _, err := dockerCmdWithError("run", "--device-write-bps", "/dev/sdX:500", "busybox", "true") 488 c.Assert(err, check.NotNil, check.Commentf(out)) 489 } 490 491 func (s *DockerSuite) TestRunWithInvalidPathforBlkioDeviceReadIOps(c *check.C) { 492 testRequires(c, blkioWeight) 493 out, _, err := dockerCmdWithError("run", "--device-read-iops", "/dev/sdX:500", "busybox", "true") 494 c.Assert(err, check.NotNil, check.Commentf(out)) 495 } 496 497 func (s *DockerSuite) TestRunWithInvalidPathforBlkioDeviceWriteIOps(c *check.C) { 498 testRequires(c, blkioWeight) 499 out, _, err := dockerCmdWithError("run", "--device-write-iops", "/dev/sdX:500", "busybox", "true") 500 c.Assert(err, check.NotNil, check.Commentf(out)) 501 } 502 503 func (s *DockerSuite) TestRunOOMExitCode(c *check.C) { 504 testRequires(c, oomControl) 505 errChan := make(chan error) 506 go func() { 507 defer close(errChan) 508 //changing memory to 40MB from 4MB due to an issue with GCCGO that test fails to start the container. 509 out, exitCode, _ := dockerCmdWithError("run", "-m", "40MB", "busybox", "sh", "-c", "x=a; while true; do x=$x$x$x$x; done") 510 if expected := 137; exitCode != expected { 511 errChan <- fmt.Errorf("wrong exit code for OOM container: expected %d, got %d (output: %q)", expected, exitCode, out) 512 } 513 }() 514 515 select { 516 case err := <-errChan: 517 c.Assert(err, check.IsNil) 518 case <-time.After(600 * time.Second): 519 c.Fatal("Timeout waiting for container to die on OOM") 520 } 521 } 522 523 func (s *DockerSuite) TestRunWithMemoryLimit(c *check.C) { 524 testRequires(c, memoryLimitSupport) 525 526 file := "/sys/fs/cgroup/memory/memory.limit_in_bytes" 527 stdout, _, _ := dockerCmdWithStdoutStderr(c, "run", "-m", "32M", "--name", "test", "busybox", "cat", file) 528 c.Assert(strings.TrimSpace(stdout), checker.Equals, "33554432") 529 530 out := inspectField(c, "test", "HostConfig.Memory") 531 c.Assert(out, check.Equals, "33554432") 532 } 533 534 // TestRunWithoutMemoryswapLimit sets memory limit and disables swap 535 // memory limit, this means the processes in the container can use 536 // 16M memory and as much swap memory as they need (if the host 537 // supports swap memory). 538 func (s *DockerSuite) TestRunWithoutMemoryswapLimit(c *check.C) { 539 testRequires(c, DaemonIsLinux) 540 testRequires(c, memoryLimitSupport) 541 testRequires(c, swapMemorySupport) 542 dockerCmd(c, "run", "-m", "32m", "--memory-swap", "-1", "busybox", "true") 543 } 544 545 func (s *DockerSuite) TestRunWithSwappiness(c *check.C) { 546 testRequires(c, memorySwappinessSupport) 547 file := "/sys/fs/cgroup/memory/memory.swappiness" 548 out, _ := dockerCmd(c, "run", "--memory-swappiness", "0", "--name", "test", "busybox", "cat", file) 549 c.Assert(strings.TrimSpace(out), checker.Equals, "0") 550 551 out = inspectField(c, "test", "HostConfig.MemorySwappiness") 552 c.Assert(out, check.Equals, "0") 553 } 554 555 func (s *DockerSuite) TestRunWithSwappinessInvalid(c *check.C) { 556 testRequires(c, memorySwappinessSupport) 557 out, _, err := dockerCmdWithError("run", "--memory-swappiness", "101", "busybox", "true") 558 c.Assert(err, check.NotNil) 559 expected := "Valid memory swappiness range is 0-100" 560 c.Assert(out, checker.Contains, expected, check.Commentf("Expected output to contain %q, not %q", out, expected)) 561 562 out, _, err = dockerCmdWithError("run", "--memory-swappiness", "-10", "busybox", "true") 563 c.Assert(err, check.NotNil) 564 c.Assert(out, checker.Contains, expected, check.Commentf("Expected output to contain %q, not %q", out, expected)) 565 } 566 567 func (s *DockerSuite) TestRunWithMemoryReservation(c *check.C) { 568 testRequires(c, memoryReservationSupport) 569 570 file := "/sys/fs/cgroup/memory/memory.soft_limit_in_bytes" 571 out, _ := dockerCmd(c, "run", "--memory-reservation", "200M", "--name", "test", "busybox", "cat", file) 572 c.Assert(strings.TrimSpace(out), checker.Equals, "209715200") 573 574 out = inspectField(c, "test", "HostConfig.MemoryReservation") 575 c.Assert(out, check.Equals, "209715200") 576 } 577 578 func (s *DockerSuite) TestRunWithMemoryReservationInvalid(c *check.C) { 579 testRequires(c, memoryLimitSupport) 580 testRequires(c, memoryReservationSupport) 581 out, _, err := dockerCmdWithError("run", "-m", "500M", "--memory-reservation", "800M", "busybox", "true") 582 c.Assert(err, check.NotNil) 583 expected := "Minimum memory limit should be larger than memory reservation limit" 584 c.Assert(strings.TrimSpace(out), checker.Contains, expected, check.Commentf("run container should fail with invalid memory reservation")) 585 } 586 587 func (s *DockerSuite) TestStopContainerSignal(c *check.C) { 588 out, _ := dockerCmd(c, "run", "--stop-signal", "SIGUSR1", "-d", "busybox", "/bin/sh", "-c", `trap 'echo "exit trapped"; exit 0' USR1; while true; do sleep 1; done`) 589 containerID := strings.TrimSpace(out) 590 591 c.Assert(waitRun(containerID), checker.IsNil) 592 593 dockerCmd(c, "stop", containerID) 594 out, _ = dockerCmd(c, "logs", containerID) 595 596 c.Assert(out, checker.Contains, "exit trapped", check.Commentf("Expected `exit trapped` in the log")) 597 } 598 599 func (s *DockerSuite) TestRunSwapLessThanMemoryLimit(c *check.C) { 600 testRequires(c, memoryLimitSupport) 601 testRequires(c, swapMemorySupport) 602 out, _, err := dockerCmdWithError("run", "-m", "16m", "--memory-swap", "15m", "busybox", "echo", "test") 603 expected := "Minimum memoryswap limit should be larger than memory limit" 604 c.Assert(err, check.NotNil) 605 606 c.Assert(out, checker.Contains, expected) 607 } 608 609 func (s *DockerSuite) TestRunInvalidCpusetCpusFlagValue(c *check.C) { 610 testRequires(c, cgroupCpuset, SameHostDaemon) 611 612 sysInfo := sysinfo.New(true) 613 cpus, err := parsers.ParseUintList(sysInfo.Cpus) 614 c.Assert(err, check.IsNil) 615 var invalid int 616 for i := 0; i <= len(cpus)+1; i++ { 617 if !cpus[i] { 618 invalid = i 619 break 620 } 621 } 622 out, _, err := dockerCmdWithError("run", "--cpuset-cpus", strconv.Itoa(invalid), "busybox", "true") 623 c.Assert(err, check.NotNil) 624 expected := fmt.Sprintf("Error response from daemon: Requested CPUs are not available - requested %s, available: %s", strconv.Itoa(invalid), sysInfo.Cpus) 625 c.Assert(out, checker.Contains, expected) 626 } 627 628 func (s *DockerSuite) TestRunInvalidCpusetMemsFlagValue(c *check.C) { 629 testRequires(c, cgroupCpuset) 630 631 sysInfo := sysinfo.New(true) 632 mems, err := parsers.ParseUintList(sysInfo.Mems) 633 c.Assert(err, check.IsNil) 634 var invalid int 635 for i := 0; i <= len(mems)+1; i++ { 636 if !mems[i] { 637 invalid = i 638 break 639 } 640 } 641 out, _, err := dockerCmdWithError("run", "--cpuset-mems", strconv.Itoa(invalid), "busybox", "true") 642 c.Assert(err, check.NotNil) 643 expected := fmt.Sprintf("Error response from daemon: Requested memory nodes are not available - requested %s, available: %s", strconv.Itoa(invalid), sysInfo.Mems) 644 c.Assert(out, checker.Contains, expected) 645 } 646 647 func (s *DockerSuite) TestRunInvalidCPUShares(c *check.C) { 648 testRequires(c, cpuShare, DaemonIsLinux) 649 out, _, err := dockerCmdWithError("run", "--cpu-shares", "1", "busybox", "echo", "test") 650 c.Assert(err, check.NotNil, check.Commentf(out)) 651 expected := "The minimum allowed cpu-shares is 2" 652 c.Assert(out, checker.Contains, expected) 653 654 out, _, err = dockerCmdWithError("run", "--cpu-shares", "-1", "busybox", "echo", "test") 655 c.Assert(err, check.NotNil, check.Commentf(out)) 656 expected = "shares: invalid argument" 657 c.Assert(out, checker.Contains, expected) 658 659 out, _, err = dockerCmdWithError("run", "--cpu-shares", "99999999", "busybox", "echo", "test") 660 c.Assert(err, check.NotNil, check.Commentf(out)) 661 expected = "The maximum allowed cpu-shares is" 662 c.Assert(out, checker.Contains, expected) 663 } 664 665 func (s *DockerSuite) TestRunWithDefaultShmSize(c *check.C) { 666 testRequires(c, DaemonIsLinux) 667 668 name := "shm-default" 669 out, _ := dockerCmd(c, "run", "--name", name, "busybox", "mount") 670 shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`) 671 if !shmRegex.MatchString(out) { 672 c.Fatalf("Expected shm of 64MB in mount command, got %v", out) 673 } 674 shmSize := inspectField(c, name, "HostConfig.ShmSize") 675 c.Assert(shmSize, check.Equals, "67108864") 676 } 677 678 func (s *DockerSuite) TestRunWithShmSize(c *check.C) { 679 testRequires(c, DaemonIsLinux) 680 681 name := "shm" 682 out, _ := dockerCmd(c, "run", "--name", name, "--shm-size=1G", "busybox", "mount") 683 shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=1048576k`) 684 if !shmRegex.MatchString(out) { 685 c.Fatalf("Expected shm of 1GB in mount command, got %v", out) 686 } 687 shmSize := inspectField(c, name, "HostConfig.ShmSize") 688 c.Assert(shmSize, check.Equals, "1073741824") 689 } 690 691 func (s *DockerSuite) TestRunTmpfsMounts(c *check.C) { 692 // TODO Windows (Post TP4): This test cannot run on a Windows daemon as 693 // Windows does not support tmpfs mounts. 694 testRequires(c, DaemonIsLinux) 695 if out, _, err := dockerCmdWithError("run", "--tmpfs", "/run", "busybox", "touch", "/run/somefile"); err != nil { 696 c.Fatalf("/run directory not mounted on tmpfs %q %s", err, out) 697 } 698 if out, _, err := dockerCmdWithError("run", "--tmpfs", "/run:noexec", "busybox", "touch", "/run/somefile"); err != nil { 699 c.Fatalf("/run directory not mounted on tmpfs %q %s", err, out) 700 } 701 if out, _, err := dockerCmdWithError("run", "--tmpfs", "/run:noexec,nosuid,rw,size=5k,mode=700", "busybox", "touch", "/run/somefile"); err != nil { 702 c.Fatalf("/run failed to mount on tmpfs with valid options %q %s", err, out) 703 } 704 if _, _, err := dockerCmdWithError("run", "--tmpfs", "/run:foobar", "busybox", "touch", "/run/somefile"); err == nil { 705 c.Fatalf("/run mounted on tmpfs when it should have vailed within invalid mount option") 706 } 707 if _, _, err := dockerCmdWithError("run", "--tmpfs", "/run", "-v", "/run:/run", "busybox", "touch", "/run/somefile"); err == nil { 708 c.Fatalf("Should have generated an error saying Duplicate mount points") 709 } 710 } 711 712 // TestRunSeccompProfileDenyUnshare checks that 'docker run --security-opt seccomp:/tmp/profile.json debian:jessie unshare' exits with operation not permitted. 713 func (s *DockerSuite) TestRunSeccompProfileDenyUnshare(c *check.C) { 714 testRequires(c, SameHostDaemon, seccompEnabled, NotArm, Apparmor) 715 jsonData := `{ 716 "defaultAction": "SCMP_ACT_ALLOW", 717 "syscalls": [ 718 { 719 "name": "unshare", 720 "action": "SCMP_ACT_ERRNO" 721 } 722 ] 723 }` 724 tmpFile, err := ioutil.TempFile("", "profile.json") 725 defer tmpFile.Close() 726 if err != nil { 727 c.Fatal(err) 728 } 729 730 if _, err := tmpFile.Write([]byte(jsonData)); err != nil { 731 c.Fatal(err) 732 } 733 runCmd := exec.Command(dockerBinary, "run", "--security-opt", "apparmor:unconfined", "--security-opt", "seccomp:"+tmpFile.Name(), "debian:jessie", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc") 734 out, _, _ := runCommandWithOutput(runCmd) 735 if !strings.Contains(out, "Operation not permitted") { 736 c.Fatalf("expected unshare with seccomp profile denied to fail, got %s", out) 737 } 738 } 739 740 // TestRunSeccompProfileDenyChmod checks that 'docker run --security-opt seccomp:/tmp/profile.json busybox chmod 400 /etc/hostname' exits with operation not permitted. 741 func (s *DockerSuite) TestRunSeccompProfileDenyChmod(c *check.C) { 742 testRequires(c, SameHostDaemon, seccompEnabled) 743 jsonData := `{ 744 "defaultAction": "SCMP_ACT_ALLOW", 745 "syscalls": [ 746 { 747 "name": "chmod", 748 "action": "SCMP_ACT_ERRNO" 749 } 750 ] 751 }` 752 tmpFile, err := ioutil.TempFile("", "profile.json") 753 defer tmpFile.Close() 754 if err != nil { 755 c.Fatal(err) 756 } 757 758 if _, err := tmpFile.Write([]byte(jsonData)); err != nil { 759 c.Fatal(err) 760 } 761 runCmd := exec.Command(dockerBinary, "run", "--security-opt", "seccomp:"+tmpFile.Name(), "busybox", "chmod", "400", "/etc/hostname") 762 out, _, _ := runCommandWithOutput(runCmd) 763 if !strings.Contains(out, "Operation not permitted") { 764 c.Fatalf("expected chmod with seccomp profile denied to fail, got %s", out) 765 } 766 } 767 768 // TestRunSeccompProfileDenyUnshareUserns checks that 'docker run debian:jessie unshare --map-root-user --user sh -c whoami' with a specific profile to 769 // deny unhare of a userns exits with operation not permitted. 770 func (s *DockerSuite) TestRunSeccompProfileDenyUnshareUserns(c *check.C) { 771 testRequires(c, SameHostDaemon, seccompEnabled, NotArm, Apparmor) 772 // from sched.h 773 jsonData := fmt.Sprintf(`{ 774 "defaultAction": "SCMP_ACT_ALLOW", 775 "syscalls": [ 776 { 777 "name": "unshare", 778 "action": "SCMP_ACT_ERRNO", 779 "args": [ 780 { 781 "index": 0, 782 "value": %d, 783 "op": "SCMP_CMP_EQ" 784 } 785 ] 786 } 787 ] 788 }`, uint64(0x10000000)) 789 tmpFile, err := ioutil.TempFile("", "profile.json") 790 defer tmpFile.Close() 791 if err != nil { 792 c.Fatal(err) 793 } 794 795 if _, err := tmpFile.Write([]byte(jsonData)); err != nil { 796 c.Fatal(err) 797 } 798 runCmd := exec.Command(dockerBinary, "run", "--security-opt", "apparmor:unconfined", "--security-opt", "seccomp:"+tmpFile.Name(), "debian:jessie", "unshare", "--map-root-user", "--user", "sh", "-c", "whoami") 799 out, _, _ := runCommandWithOutput(runCmd) 800 if !strings.Contains(out, "Operation not permitted") { 801 c.Fatalf("expected unshare userns with seccomp profile denied to fail, got %s", out) 802 } 803 } 804 805 // TestRunSeccompProfileDenyCloneUserns checks that 'docker run syscall-test' 806 // with a the default seccomp profile exits with operation not permitted. 807 func (s *DockerSuite) TestRunSeccompProfileDenyCloneUserns(c *check.C) { 808 testRequires(c, SameHostDaemon, seccompEnabled) 809 810 runCmd := exec.Command(dockerBinary, "run", "syscall-test", "userns-test", "id") 811 out, _, err := runCommandWithOutput(runCmd) 812 if err == nil || !strings.Contains(out, "clone failed: Operation not permitted") { 813 c.Fatalf("expected clone userns with default seccomp profile denied to fail, got %s: %v", out, err) 814 } 815 } 816 817 // TestRunSeccompUnconfinedCloneUserns checks that 818 // 'docker run --security-opt seccomp:unconfined syscall-test' allows creating a userns. 819 func (s *DockerSuite) TestRunSeccompUnconfinedCloneUserns(c *check.C) { 820 testRequires(c, SameHostDaemon, seccompEnabled, UserNamespaceInKernel, NotUserNamespace) 821 822 // make sure running w privileged is ok 823 runCmd := exec.Command(dockerBinary, "run", "--security-opt", "seccomp:unconfined", "syscall-test", "userns-test", "id") 824 if out, _, err := runCommandWithOutput(runCmd); err != nil || !strings.Contains(out, "nobody") { 825 c.Fatalf("expected clone userns with --security-opt seccomp:unconfined to succeed, got %s: %v", out, err) 826 } 827 } 828 829 // TestRunSeccompAllowPrivCloneUserns checks that 'docker run --privileged syscall-test' 830 // allows creating a userns. 831 func (s *DockerSuite) TestRunSeccompAllowPrivCloneUserns(c *check.C) { 832 testRequires(c, SameHostDaemon, seccompEnabled, UserNamespaceInKernel, NotUserNamespace) 833 834 // make sure running w privileged is ok 835 runCmd := exec.Command(dockerBinary, "run", "--privileged", "syscall-test", "userns-test", "id") 836 if out, _, err := runCommandWithOutput(runCmd); err != nil || !strings.Contains(out, "nobody") { 837 c.Fatalf("expected clone userns with --privileged to succeed, got %s: %v", out, err) 838 } 839 } 840 841 // TestRunSeccompAllowSetrlimit checks that 'docker run debian:jessie ulimit -v 1048510' succeeds. 842 func (s *DockerSuite) TestRunSeccompAllowSetrlimit(c *check.C) { 843 testRequires(c, SameHostDaemon, seccompEnabled) 844 845 // ulimit uses setrlimit, so we want to make sure we don't break it 846 runCmd := exec.Command(dockerBinary, "run", "debian:jessie", "bash", "-c", "ulimit -v 1048510") 847 if out, _, err := runCommandWithOutput(runCmd); err != nil { 848 c.Fatalf("expected ulimit with seccomp to succeed, got %s: %v", out, err) 849 } 850 } 851 852 func (s *DockerSuite) TestRunSeccompDefaultProfile(c *check.C) { 853 testRequires(c, SameHostDaemon, seccompEnabled, NotUserNamespace) 854 855 var group sync.WaitGroup 856 group.Add(4) 857 errChan := make(chan error, 4) 858 go func() { 859 out, _, err := dockerCmdWithError("run", "--cap-add", "ALL", "syscall-test", "acct-test") 860 if err == nil || !strings.Contains(out, "Operation not permitted") { 861 errChan <- fmt.Errorf("expected Operation not permitted, got: %s", out) 862 } 863 group.Done() 864 }() 865 866 go func() { 867 out, _, err := dockerCmdWithError("run", "--cap-add", "ALL", "syscall-test", "ns-test", "echo", "hello") 868 if err == nil || !strings.Contains(out, "Operation not permitted") { 869 errChan <- fmt.Errorf("expected Operation not permitted, got: %s", out) 870 } 871 group.Done() 872 }() 873 874 go func() { 875 out, _, err := dockerCmdWithError("run", "--cap-add", "ALL", "--security-opt", "seccomp:unconfined", "syscall-test", "acct-test") 876 if err == nil || !strings.Contains(out, "No such file or directory") { 877 errChan <- fmt.Errorf("expected No such file or directory, got: %s", out) 878 } 879 group.Done() 880 }() 881 882 go func() { 883 out, _, err := dockerCmdWithError("run", "--cap-add", "ALL", "--security-opt", "seccomp:unconfined", "syscall-test", "ns-test", "echo", "hello") 884 if err != nil || !strings.Contains(out, "hello") { 885 errChan <- fmt.Errorf("expected hello, got: %s, %v", out, err) 886 } 887 group.Done() 888 }() 889 890 group.Wait() 891 close(errChan) 892 893 for err := range errChan { 894 c.Assert(err, checker.IsNil) 895 } 896 } 897 898 // TestRunNoNewPrivSetuid checks that --security-opt=no-new-privileges prevents 899 // effective uid transtions on executing setuid binaries. 900 func (s *DockerSuite) TestRunNoNewPrivSetuid(c *check.C) { 901 testRequires(c, DaemonIsLinux, NotUserNamespace, SameHostDaemon) 902 903 // test that running a setuid binary results in no effective uid transition 904 runCmd := exec.Command(dockerBinary, "run", "--security-opt", "no-new-privileges", "--user", "1000", "nnp-test", "/usr/bin/nnp-test") 905 if out, _, err := runCommandWithOutput(runCmd); err != nil || !strings.Contains(out, "EUID=1000") { 906 c.Fatalf("expected output to contain EUID=1000, got %s: %v", out, err) 907 } 908 } 909 910 func (s *DockerSuite) TestRunApparmorProcDirectory(c *check.C) { 911 testRequires(c, SameHostDaemon, Apparmor) 912 913 // running w seccomp unconfined tests the apparmor profile 914 runCmd := exec.Command(dockerBinary, "run", "--security-opt", "seccomp:unconfined", "debian:jessie", "chmod", "777", "/proc/1/cgroup") 915 if out, _, err := runCommandWithOutput(runCmd); err == nil || !(strings.Contains(out, "Permission denied") || strings.Contains(out, "Operation not permitted")) { 916 c.Fatalf("expected chmod 777 /proc/1/cgroup to fail, got %s: %v", out, err) 917 } 918 919 runCmd = exec.Command(dockerBinary, "run", "--security-opt", "seccomp:unconfined", "debian:jessie", "chmod", "777", "/proc/1/attr/current") 920 if out, _, err := runCommandWithOutput(runCmd); err == nil || !(strings.Contains(out, "Permission denied") || strings.Contains(out, "Operation not permitted")) { 921 c.Fatalf("expected chmod 777 /proc/1/attr/current to fail, got %s: %v", out, err) 922 } 923 } 924 925 // make sure the default profile can be successfully parsed (using unshare as it is 926 // something which we know is blocked in the default profile) 927 func (s *DockerSuite) TestRunSeccompWithDefaultProfile(c *check.C) { 928 testRequires(c, SameHostDaemon, seccompEnabled) 929 930 out, _, err := dockerCmdWithError("run", "--security-opt", "seccomp:../profiles/seccomp/default.json", "debian:jessie", "unshare", "--map-root-user", "--user", "sh", "-c", "whoami") 931 c.Assert(err, checker.NotNil, check.Commentf(out)) 932 c.Assert(strings.TrimSpace(out), checker.Equals, "unshare: unshare failed: Operation not permitted") 933 } 934 935 // TestRunDeviceSymlink checks run with device that follows symlink (#13840) 936 func (s *DockerSuite) TestRunDeviceSymlink(c *check.C) { 937 testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm, SameHostDaemon) 938 if _, err := os.Stat("/dev/zero"); err != nil { 939 c.Skip("Host does not have /dev/zero") 940 } 941 942 // Create a temporary directory to create symlink 943 tmpDir, err := ioutil.TempDir("", "docker_device_follow_symlink_tests") 944 c.Assert(err, checker.IsNil) 945 946 defer os.RemoveAll(tmpDir) 947 948 // Create a symbolic link to /dev/zero 949 symZero := filepath.Join(tmpDir, "zero") 950 err = os.Symlink("/dev/zero", symZero) 951 c.Assert(err, checker.IsNil) 952 953 // Create a temporary file "temp" inside tmpDir, write some data to "tmpDir/temp", 954 // then create a symlink "tmpDir/file" to the temporary file "tmpDir/temp". 955 tmpFile := filepath.Join(tmpDir, "temp") 956 err = ioutil.WriteFile(tmpFile, []byte("temp"), 0666) 957 c.Assert(err, checker.IsNil) 958 symFile := filepath.Join(tmpDir, "file") 959 err = os.Symlink(tmpFile, symFile) 960 c.Assert(err, checker.IsNil) 961 962 // md5sum of 'dd if=/dev/zero bs=4K count=8' is bb7df04e1b0a2570657527a7e108ae23 963 out, _ := dockerCmd(c, "run", "--device", symZero+":/dev/symzero", "busybox", "sh", "-c", "dd if=/dev/symzero bs=4K count=8 | md5sum") 964 c.Assert(strings.Trim(out, "\r\n"), checker.Contains, "bb7df04e1b0a2570657527a7e108ae23", check.Commentf("expected output bb7df04e1b0a2570657527a7e108ae23")) 965 966 // symlink "tmpDir/file" to a file "tmpDir/temp" will result in an error as it is not a device. 967 out, _, err = dockerCmdWithError("run", "--device", symFile+":/dev/symzero", "busybox", "sh", "-c", "dd if=/dev/symzero bs=4K count=8 | md5sum") 968 c.Assert(err, check.NotNil) 969 c.Assert(strings.Trim(out, "\r\n"), checker.Contains, "not a device node", check.Commentf("expected output 'not a device node'")) 970 } 971 972 // TestRunPidsLimit makes sure the pids cgroup is set with --pids-limit 973 func (s *DockerSuite) TestRunPidsLimit(c *check.C) { 974 testRequires(c, pidsLimit) 975 976 file := "/sys/fs/cgroup/pids/pids.max" 977 out, _ := dockerCmd(c, "run", "--name", "skittles", "--pids-limit", "2", "busybox", "cat", file) 978 c.Assert(strings.TrimSpace(out), checker.Equals, "2") 979 980 out = inspectField(c, "skittles", "HostConfig.PidsLimit") 981 c.Assert(out, checker.Equals, "2", check.Commentf("setting the pids limit failed")) 982 }