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