github.com/docker/Engine@v17.12.1-ce-rc2+incompatible/integration-cli/docker_api_ipcmode_test.go (about)

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