123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- // 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)
- }
|