docker_cli_run_unix_test.go 32 KB

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