|
@@ -0,0 +1,222 @@
|
|
|
|
+// build +linux
|
|
|
|
+package main
|
|
|
|
+
|
|
|
|
+import (
|
|
|
|
+ "bufio"
|
|
|
|
+ "fmt"
|
|
|
|
+ "io/ioutil"
|
|
|
|
+ "os"
|
|
|
|
+ "strings"
|
|
|
|
+
|
|
|
|
+ "github.com/docker/docker/api/types"
|
|
|
|
+ "github.com/docker/docker/api/types/container"
|
|
|
|
+ "github.com/docker/docker/integration-cli/checker"
|
|
|
|
+ "github.com/docker/docker/integration-cli/cli"
|
|
|
|
+ "github.com/docker/docker/integration-cli/request"
|
|
|
|
+ "github.com/go-check/check"
|
|
|
|
+ "golang.org/x/net/context"
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
+/* testIpcCheckDevExists checks whether a given mount (identified by its
|
|
|
|
+ * major:minor pair from /proc/self/mountinfo) exists on the host system.
|
|
|
|
+ *
|
|
|
|
+ * The format of /proc/self/mountinfo is like:
|
|
|
|
+ *
|
|
|
|
+ * 29 23 0:24 / /dev/shm rw,nosuid,nodev shared:4 - tmpfs tmpfs rw
|
|
|
|
+ * ^^^^\
|
|
|
|
+ * - this is the minor:major we look for
|
|
|
|
+ */
|
|
|
|
+func testIpcCheckDevExists(mm string) (bool, error) {
|
|
|
|
+ f, err := os.Open("/proc/self/mountinfo")
|
|
|
|
+ if err != nil {
|
|
|
|
+ return false, err
|
|
|
|
+ }
|
|
|
|
+ defer f.Close()
|
|
|
|
+
|
|
|
|
+ s := bufio.NewScanner(f)
|
|
|
|
+ for s.Scan() {
|
|
|
|
+ fields := strings.Fields(s.Text())
|
|
|
|
+ if len(fields) < 7 {
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+ if fields[2] == mm {
|
|
|
|
+ return true, nil
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if err := s.Err(); err != nil {
|
|
|
|
+ return false, err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return false, nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// testIpcNonePrivateShareable is a helper function to test "none",
|
|
|
|
+// "private" and "shareable" modes.
|
|
|
|
+func testIpcNonePrivateShareable(c *check.C, mode string, mustBeMounted bool, mustBeShared bool) {
|
|
|
|
+ cfg := container.Config{
|
|
|
|
+ Image: "busybox",
|
|
|
|
+ Cmd: []string{"top"},
|
|
|
|
+ }
|
|
|
|
+ hostCfg := container.HostConfig{
|
|
|
|
+ IpcMode: container.IpcMode(mode),
|
|
|
|
+ }
|
|
|
|
+ ctx := context.Background()
|
|
|
|
+
|
|
|
|
+ client, err := request.NewClient()
|
|
|
|
+ c.Assert(err, checker.IsNil)
|
|
|
|
+
|
|
|
|
+ resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "")
|
|
|
|
+ c.Assert(err, checker.IsNil)
|
|
|
|
+ c.Assert(len(resp.Warnings), checker.Equals, 0)
|
|
|
|
+
|
|
|
|
+ err = client.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})
|
|
|
|
+ c.Assert(err, checker.IsNil)
|
|
|
|
+
|
|
|
|
+ // get major:minor pair for /dev/shm from container's /proc/self/mountinfo
|
|
|
|
+ cmd := "awk '($5 == \"/dev/shm\") {printf $3}' /proc/self/mountinfo"
|
|
|
|
+ mm := cli.DockerCmd(c, "exec", "-i", resp.ID, "sh", "-c", cmd).Combined()
|
|
|
|
+ if !mustBeMounted {
|
|
|
|
+ c.Assert(mm, checker.Equals, "")
|
|
|
|
+ // no more checks to perform
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ c.Assert(mm, checker.Matches, "^[0-9]+:[0-9]+$")
|
|
|
|
+
|
|
|
|
+ shared, err := testIpcCheckDevExists(mm)
|
|
|
|
+ c.Assert(err, checker.IsNil)
|
|
|
|
+ c.Logf("[testIpcPrivateShareable] ipcmode: %v, ipcdev: %v, shared: %v, mustBeShared: %v\n", mode, mm, shared, mustBeShared)
|
|
|
|
+ c.Assert(shared, checker.Equals, mustBeShared)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* TestAPIIpcModeNone checks the container "none" IPC mode
|
|
|
|
+ * (--ipc none) works as expected. It makes sure there is no
|
|
|
|
+ * /dev/shm mount inside the container.
|
|
|
|
+ */
|
|
|
|
+func (s *DockerSuite) TestAPIIpcModeNone(c *check.C) {
|
|
|
|
+ testRequires(c, DaemonIsLinux)
|
|
|
|
+ testIpcNonePrivateShareable(c, "none", false, false)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* TestAPIIpcModePrivate checks the container private IPC mode
|
|
|
|
+ * (--ipc private) works as expected. It gets the minor:major pair
|
|
|
|
+ * of /dev/shm mount from the container, and makes sure there is no
|
|
|
|
+ * such pair on the host.
|
|
|
|
+ */
|
|
|
|
+func (s *DockerSuite) TestAPIIpcModePrivate(c *check.C) {
|
|
|
|
+ testRequires(c, DaemonIsLinux)
|
|
|
|
+ testIpcNonePrivateShareable(c, "private", true, false)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* TestAPIIpcModeShareable checks the container shareable IPC mode
|
|
|
|
+ * (--ipc shareable) works as expected. It gets the minor:major pair
|
|
|
|
+ * of /dev/shm mount from the container, and makes sure such pair
|
|
|
|
+ * also exists on the host.
|
|
|
|
+ */
|
|
|
|
+func (s *DockerSuite) TestAPIIpcModeShareable(c *check.C) {
|
|
|
|
+ testRequires(c, DaemonIsLinux)
|
|
|
|
+ testIpcNonePrivateShareable(c, "shareable", true, true)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// testIpcContainer is a helper function to test --ipc container:NNN mode in various scenarios
|
|
|
|
+func testIpcContainer(s *DockerSuite, c *check.C, donorMode string, mustWork bool) {
|
|
|
|
+ cfg := container.Config{
|
|
|
|
+ Image: "busybox",
|
|
|
|
+ Cmd: []string{"top"},
|
|
|
|
+ }
|
|
|
|
+ hostCfg := container.HostConfig{
|
|
|
|
+ IpcMode: container.IpcMode(donorMode),
|
|
|
|
+ }
|
|
|
|
+ ctx := context.Background()
|
|
|
|
+
|
|
|
|
+ client, err := request.NewClient()
|
|
|
|
+ c.Assert(err, checker.IsNil)
|
|
|
|
+
|
|
|
|
+ // create and start the "donor" container
|
|
|
|
+ resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "")
|
|
|
|
+ c.Assert(err, checker.IsNil)
|
|
|
|
+ c.Assert(len(resp.Warnings), checker.Equals, 0)
|
|
|
|
+ name1 := resp.ID
|
|
|
|
+
|
|
|
|
+ err = client.ContainerStart(ctx, name1, types.ContainerStartOptions{})
|
|
|
|
+ c.Assert(err, checker.IsNil)
|
|
|
|
+
|
|
|
|
+ // create and start the second container
|
|
|
|
+ hostCfg.IpcMode = container.IpcMode("container:" + name1)
|
|
|
|
+ resp, err = client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "")
|
|
|
|
+ c.Assert(err, checker.IsNil)
|
|
|
|
+ c.Assert(len(resp.Warnings), checker.Equals, 0)
|
|
|
|
+ name2 := resp.ID
|
|
|
|
+
|
|
|
|
+ err = client.ContainerStart(ctx, name2, types.ContainerStartOptions{})
|
|
|
|
+ if !mustWork {
|
|
|
|
+ // start should fail with a specific error
|
|
|
|
+ c.Assert(err, checker.NotNil)
|
|
|
|
+ c.Assert(fmt.Sprintf("%v", err), checker.Contains, "non-shareable IPC")
|
|
|
|
+ // no more checks to perform here
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // start should succeed
|
|
|
|
+ c.Assert(err, checker.IsNil)
|
|
|
|
+
|
|
|
|
+ // check that IPC is shared
|
|
|
|
+ // 1. create a file in the first container
|
|
|
|
+ cli.DockerCmd(c, "exec", name1, "sh", "-c", "printf covfefe > /dev/shm/bar")
|
|
|
|
+ // 2. check it's the same file in the second one
|
|
|
|
+ out := cli.DockerCmd(c, "exec", "-i", name2, "cat", "/dev/shm/bar").Combined()
|
|
|
|
+ c.Assert(out, checker.Matches, "^covfefe$")
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* TestAPIIpcModeShareableAndContainer checks that a container created with
|
|
|
|
+ * --ipc container:ID can use IPC of another shareable container.
|
|
|
|
+ */
|
|
|
|
+func (s *DockerSuite) TestAPIIpcModeShareableAndContainer(c *check.C) {
|
|
|
|
+ testRequires(c, DaemonIsLinux)
|
|
|
|
+ testIpcContainer(s, c, "shareable", true)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* TestAPIIpcModePrivateAndContainer checks that a container created with
|
|
|
|
+ * --ipc container:ID can NOT use IPC of another private container.
|
|
|
|
+ */
|
|
|
|
+func (s *DockerSuite) TestAPIIpcModePrivateAndContainer(c *check.C) {
|
|
|
|
+ testRequires(c, DaemonIsLinux)
|
|
|
|
+ testIpcContainer(s, c, "private", false)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* TestAPIIpcModeHost checks that a container created with --ipc host
|
|
|
|
+ * can use IPC of the host system.
|
|
|
|
+ */
|
|
|
|
+func (s *DockerSuite) TestAPIIpcModeHost(c *check.C) {
|
|
|
|
+ testRequires(c, DaemonIsLinux)
|
|
|
|
+
|
|
|
|
+ cfg := container.Config{
|
|
|
|
+ Image: "busybox",
|
|
|
|
+ Cmd: []string{"top"},
|
|
|
|
+ }
|
|
|
|
+ hostCfg := container.HostConfig{
|
|
|
|
+ IpcMode: container.IpcMode("host"),
|
|
|
|
+ }
|
|
|
|
+ ctx := context.Background()
|
|
|
|
+
|
|
|
|
+ client, err := request.NewClient()
|
|
|
|
+ c.Assert(err, checker.IsNil)
|
|
|
|
+
|
|
|
|
+ resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "")
|
|
|
|
+ c.Assert(err, checker.IsNil)
|
|
|
|
+ c.Assert(len(resp.Warnings), checker.Equals, 0)
|
|
|
|
+ name := resp.ID
|
|
|
|
+
|
|
|
|
+ err = client.ContainerStart(ctx, name, types.ContainerStartOptions{})
|
|
|
|
+ c.Assert(err, checker.IsNil)
|
|
|
|
+
|
|
|
|
+ // check that IPC is shared
|
|
|
|
+ // 1. create a file inside container
|
|
|
|
+ cli.DockerCmd(c, "exec", name, "sh", "-c", "printf covfefe > /dev/shm/."+name)
|
|
|
|
+ // 2. check it's the same on the host
|
|
|
|
+ bytes, err := ioutil.ReadFile("/dev/shm/." + name)
|
|
|
|
+ c.Assert(err, checker.IsNil)
|
|
|
|
+ c.Assert(string(bytes), checker.Matches, "^covfefe$")
|
|
|
|
+ // 3. clean up
|
|
|
|
+ cli.DockerCmd(c, "exec", name, "rm", "-f", "/dev/shm/."+name)
|
|
|
|
+}
|