docker_api_ipcmode_test.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. // build +linux
  2. package main
  3. import (
  4. "bufio"
  5. "fmt"
  6. "io/ioutil"
  7. "os"
  8. "strings"
  9. "github.com/docker/docker/api/types"
  10. "github.com/docker/docker/api/types/container"
  11. "github.com/docker/docker/integration-cli/checker"
  12. "github.com/docker/docker/integration-cli/cli"
  13. "github.com/docker/docker/integration-cli/request"
  14. "github.com/go-check/check"
  15. "golang.org/x/net/context"
  16. )
  17. /* testIpcCheckDevExists checks whether a given mount (identified by its
  18. * major:minor pair from /proc/self/mountinfo) exists on the host system.
  19. *
  20. * The format of /proc/self/mountinfo is like:
  21. *
  22. * 29 23 0:24 / /dev/shm rw,nosuid,nodev shared:4 - tmpfs tmpfs rw
  23. * ^^^^\
  24. * - this is the minor:major we look for
  25. */
  26. func testIpcCheckDevExists(mm string) (bool, error) {
  27. f, err := os.Open("/proc/self/mountinfo")
  28. if err != nil {
  29. return false, err
  30. }
  31. defer f.Close()
  32. s := bufio.NewScanner(f)
  33. for s.Scan() {
  34. fields := strings.Fields(s.Text())
  35. if len(fields) < 7 {
  36. continue
  37. }
  38. if fields[2] == mm {
  39. return true, nil
  40. }
  41. }
  42. if err := s.Err(); err != nil {
  43. return false, err
  44. }
  45. return false, nil
  46. }
  47. // testIpcNonePrivateShareable is a helper function to test "none",
  48. // "private" and "shareable" modes.
  49. func testIpcNonePrivateShareable(c *check.C, mode string, mustBeMounted bool, mustBeShared bool) {
  50. cfg := container.Config{
  51. Image: "busybox",
  52. Cmd: []string{"top"},
  53. }
  54. hostCfg := container.HostConfig{
  55. IpcMode: container.IpcMode(mode),
  56. }
  57. ctx := context.Background()
  58. client, err := request.NewClient()
  59. c.Assert(err, checker.IsNil)
  60. resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "")
  61. c.Assert(err, checker.IsNil)
  62. c.Assert(len(resp.Warnings), checker.Equals, 0)
  63. err = client.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})
  64. c.Assert(err, checker.IsNil)
  65. // get major:minor pair for /dev/shm from container's /proc/self/mountinfo
  66. cmd := "awk '($5 == \"/dev/shm\") {printf $3}' /proc/self/mountinfo"
  67. mm := cli.DockerCmd(c, "exec", "-i", resp.ID, "sh", "-c", cmd).Combined()
  68. if !mustBeMounted {
  69. c.Assert(mm, checker.Equals, "")
  70. // no more checks to perform
  71. return
  72. }
  73. c.Assert(mm, checker.Matches, "^[0-9]+:[0-9]+$")
  74. shared, err := testIpcCheckDevExists(mm)
  75. c.Assert(err, checker.IsNil)
  76. c.Logf("[testIpcPrivateShareable] ipcmode: %v, ipcdev: %v, shared: %v, mustBeShared: %v\n", mode, mm, shared, mustBeShared)
  77. c.Assert(shared, checker.Equals, mustBeShared)
  78. }
  79. /* TestAPIIpcModeNone checks the container "none" IPC mode
  80. * (--ipc none) works as expected. It makes sure there is no
  81. * /dev/shm mount inside the container.
  82. */
  83. func (s *DockerSuite) TestAPIIpcModeNone(c *check.C) {
  84. testRequires(c, DaemonIsLinux)
  85. testIpcNonePrivateShareable(c, "none", false, false)
  86. }
  87. /* TestAPIIpcModePrivate checks the container private IPC mode
  88. * (--ipc private) works as expected. It gets the minor:major pair
  89. * of /dev/shm mount from the container, and makes sure there is no
  90. * such pair on the host.
  91. */
  92. func (s *DockerSuite) TestAPIIpcModePrivate(c *check.C) {
  93. testRequires(c, DaemonIsLinux)
  94. testIpcNonePrivateShareable(c, "private", true, false)
  95. }
  96. /* TestAPIIpcModeShareable checks the container shareable IPC mode
  97. * (--ipc shareable) works as expected. It gets the minor:major pair
  98. * of /dev/shm mount from the container, and makes sure such pair
  99. * also exists on the host.
  100. */
  101. func (s *DockerSuite) TestAPIIpcModeShareable(c *check.C) {
  102. testRequires(c, DaemonIsLinux)
  103. testIpcNonePrivateShareable(c, "shareable", true, true)
  104. }
  105. // testIpcContainer is a helper function to test --ipc container:NNN mode in various scenarios
  106. func testIpcContainer(s *DockerSuite, c *check.C, donorMode string, mustWork bool) {
  107. cfg := container.Config{
  108. Image: "busybox",
  109. Cmd: []string{"top"},
  110. }
  111. hostCfg := container.HostConfig{
  112. IpcMode: container.IpcMode(donorMode),
  113. }
  114. ctx := context.Background()
  115. client, err := request.NewClient()
  116. c.Assert(err, checker.IsNil)
  117. // create and start the "donor" container
  118. resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "")
  119. c.Assert(err, checker.IsNil)
  120. c.Assert(len(resp.Warnings), checker.Equals, 0)
  121. name1 := resp.ID
  122. err = client.ContainerStart(ctx, name1, types.ContainerStartOptions{})
  123. c.Assert(err, checker.IsNil)
  124. // create and start the second container
  125. hostCfg.IpcMode = container.IpcMode("container:" + name1)
  126. resp, err = client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "")
  127. c.Assert(err, checker.IsNil)
  128. c.Assert(len(resp.Warnings), checker.Equals, 0)
  129. name2 := resp.ID
  130. err = client.ContainerStart(ctx, name2, types.ContainerStartOptions{})
  131. if !mustWork {
  132. // start should fail with a specific error
  133. c.Assert(err, checker.NotNil)
  134. c.Assert(fmt.Sprintf("%v", err), checker.Contains, "non-shareable IPC")
  135. // no more checks to perform here
  136. return
  137. }
  138. // start should succeed
  139. c.Assert(err, checker.IsNil)
  140. // check that IPC is shared
  141. // 1. create a file in the first container
  142. cli.DockerCmd(c, "exec", name1, "sh", "-c", "printf covfefe > /dev/shm/bar")
  143. // 2. check it's the same file in the second one
  144. out := cli.DockerCmd(c, "exec", "-i", name2, "cat", "/dev/shm/bar").Combined()
  145. c.Assert(out, checker.Matches, "^covfefe$")
  146. }
  147. /* TestAPIIpcModeShareableAndContainer checks that a container created with
  148. * --ipc container:ID can use IPC of another shareable container.
  149. */
  150. func (s *DockerSuite) TestAPIIpcModeShareableAndContainer(c *check.C) {
  151. testRequires(c, DaemonIsLinux)
  152. testIpcContainer(s, c, "shareable", true)
  153. }
  154. /* TestAPIIpcModePrivateAndContainer checks that a container created with
  155. * --ipc container:ID can NOT use IPC of another private container.
  156. */
  157. func (s *DockerSuite) TestAPIIpcModePrivateAndContainer(c *check.C) {
  158. testRequires(c, DaemonIsLinux)
  159. testIpcContainer(s, c, "private", false)
  160. }
  161. /* TestAPIIpcModeHost checks that a container created with --ipc host
  162. * can use IPC of the host system.
  163. */
  164. func (s *DockerSuite) TestAPIIpcModeHost(c *check.C) {
  165. testRequires(c, DaemonIsLinux)
  166. cfg := container.Config{
  167. Image: "busybox",
  168. Cmd: []string{"top"},
  169. }
  170. hostCfg := container.HostConfig{
  171. IpcMode: container.IpcMode("host"),
  172. }
  173. ctx := context.Background()
  174. client, err := request.NewClient()
  175. c.Assert(err, checker.IsNil)
  176. resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "")
  177. c.Assert(err, checker.IsNil)
  178. c.Assert(len(resp.Warnings), checker.Equals, 0)
  179. name := resp.ID
  180. err = client.ContainerStart(ctx, name, types.ContainerStartOptions{})
  181. c.Assert(err, checker.IsNil)
  182. // check that IPC is shared
  183. // 1. create a file inside container
  184. cli.DockerCmd(c, "exec", name, "sh", "-c", "printf covfefe > /dev/shm/."+name)
  185. // 2. check it's the same on the host
  186. bytes, err := ioutil.ReadFile("/dev/shm/." + name)
  187. c.Assert(err, checker.IsNil)
  188. c.Assert(string(bytes), checker.Matches, "^covfefe$")
  189. // 3. clean up
  190. cli.DockerCmd(c, "exec", name, "rm", "-f", "/dev/shm/."+name)
  191. }