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