github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/daemon/daemon_linux_test.go (about)

     1  // +build linux
     2  
     3  package daemon // import "github.com/docker/docker/daemon"
     4  
     5  import (
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  	"testing"
    11  
    12  	containertypes "github.com/docker/docker/api/types/container"
    13  	"github.com/docker/docker/daemon/config"
    14  	"github.com/moby/sys/mount"
    15  	"github.com/moby/sys/mountinfo"
    16  	"gotest.tools/v3/assert"
    17  	is "gotest.tools/v3/assert/cmp"
    18  )
    19  
    20  const mountsFixture = `142 78 0:38 / / rw,relatime - aufs none rw,si=573b861da0b3a05b,dio
    21  143 142 0:60 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw
    22  144 142 0:67 / /dev rw,nosuid - tmpfs tmpfs rw,mode=755
    23  145 144 0:78 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666
    24  146 144 0:49 / /dev/mqueue rw,nosuid,nodev,noexec,relatime - mqueue mqueue rw
    25  147 142 0:84 / /sys rw,nosuid,nodev,noexec,relatime - sysfs sysfs rw
    26  148 147 0:86 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755
    27  149 148 0:22 /docker/5425782a95e643181d8a485a2bab3c0bb21f51d7dfc03511f0e6fbf3f3aa356a /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,cpuset
    28  150 148 0:25 /docker/5425782a95e643181d8a485a2bab3c0bb21f51d7dfc03511f0e6fbf3f3aa356a /sys/fs/cgroup/cpu rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,cpu
    29  151 148 0:27 /docker/5425782a95e643181d8a485a2bab3c0bb21f51d7dfc03511f0e6fbf3f3aa356a /sys/fs/cgroup/cpuacct rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,cpuacct
    30  152 148 0:28 /docker/5425782a95e643181d8a485a2bab3c0bb21f51d7dfc03511f0e6fbf3f3aa356a /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,memory
    31  153 148 0:29 /docker/5425782a95e643181d8a485a2bab3c0bb21f51d7dfc03511f0e6fbf3f3aa356a /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,devices
    32  154 148 0:30 /docker/5425782a95e643181d8a485a2bab3c0bb21f51d7dfc03511f0e6fbf3f3aa356a /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,freezer
    33  155 148 0:31 /docker/5425782a95e643181d8a485a2bab3c0bb21f51d7dfc03511f0e6fbf3f3aa356a /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,blkio
    34  156 148 0:32 /docker/5425782a95e643181d8a485a2bab3c0bb21f51d7dfc03511f0e6fbf3f3aa356a /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,perf_event
    35  157 148 0:33 /docker/5425782a95e643181d8a485a2bab3c0bb21f51d7dfc03511f0e6fbf3f3aa356a /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,hugetlb
    36  158 148 0:35 /docker/5425782a95e643181d8a485a2bab3c0bb21f51d7dfc03511f0e6fbf3f3aa356a /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime - cgroup systemd rw,name=systemd
    37  159 142 8:4 /home/mlaventure/gopath /home/mlaventure/gopath rw,relatime - ext4 /dev/disk/by-uuid/d99e196c-1fc4-4b4f-bab9-9962b2b34e99 rw,errors=remount-ro,data=ordered
    38  160 142 8:4 /var/lib/docker/volumes/9a428b651ee4c538130143cad8d87f603a4bf31b928afe7ff3ecd65480692b35/_data /var/lib/docker rw,relatime - ext4 /dev/disk/by-uuid/d99e196c-1fc4-4b4f-bab9-9962b2b34e99 rw,errors=remount-ro,data=ordered
    39  164 142 8:4 /home/mlaventure/gopath/src/github.com/docker/docker /go/src/github.com/docker/docker rw,relatime - ext4 /dev/disk/by-uuid/d99e196c-1fc4-4b4f-bab9-9962b2b34e99 rw,errors=remount-ro,data=ordered
    40  165 142 8:4 /var/lib/docker/containers/5425782a95e643181d8a485a2bab3c0bb21f51d7dfc03511f0e6fbf3f3aa356a/resolv.conf /etc/resolv.conf rw,relatime - ext4 /dev/disk/by-uuid/d99e196c-1fc4-4b4f-bab9-9962b2b34e99 rw,errors=remount-ro,data=ordered
    41  166 142 8:4 /var/lib/docker/containers/5425782a95e643181d8a485a2bab3c0bb21f51d7dfc03511f0e6fbf3f3aa356a/hostname /etc/hostname rw,relatime - ext4 /dev/disk/by-uuid/d99e196c-1fc4-4b4f-bab9-9962b2b34e99 rw,errors=remount-ro,data=ordered
    42  167 142 8:4 /var/lib/docker/containers/5425782a95e643181d8a485a2bab3c0bb21f51d7dfc03511f0e6fbf3f3aa356a/hosts /etc/hosts rw,relatime - ext4 /dev/disk/by-uuid/d99e196c-1fc4-4b4f-bab9-9962b2b34e99 rw,errors=remount-ro,data=ordered
    43  168 144 0:39 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,size=65536k
    44  169 144 0:12 /14 /dev/console rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=000
    45  83 147 0:10 / /sys/kernel/security rw,relatime - securityfs none rw
    46  89 142 0:87 / /tmp rw,relatime - tmpfs none rw
    47  97 142 0:60 / /run/docker/netns/default rw,nosuid,nodev,noexec,relatime - proc proc rw
    48  100 160 8:4 /var/lib/docker/volumes/9a428b651ee4c538130143cad8d87f603a4bf31b928afe7ff3ecd65480692b35/_data/aufs /var/lib/docker/aufs rw,relatime - ext4 /dev/disk/by-uuid/d99e196c-1fc4-4b4f-bab9-9962b2b34e99 rw,errors=remount-ro,data=ordered
    49  115 100 0:102 / /var/lib/docker/aufs/mnt/0ecda1c63e5b58b3d89ff380bf646c95cc980252cf0b52466d43619aec7c8432 rw,relatime - aufs none rw,si=573b861dbc01905b,dio
    50  116 160 0:107 / /var/lib/docker/containers/d045dc441d2e2e1d5b3e328d47e5943811a40819fb47497c5f5a5df2d6d13c37/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,size=65536k
    51  118 142 0:102 / /run/docker/libcontainerd/d045dc441d2e2e1d5b3e328d47e5943811a40819fb47497c5f5a5df2d6d13c37/rootfs rw,relatime - aufs none rw,si=573b861dbc01905b,dio
    52  242 142 0:60 / /run/docker/netns/c3664df2a0f7 rw,nosuid,nodev,noexec,relatime - proc proc rw
    53  120 100 0:122 / /var/lib/docker/aufs/mnt/03ca4b49e71f1e49a41108829f4d5c70ac95934526e2af8984a1f65f1de0715d rw,relatime - aufs none rw,si=573b861eb147805b,dio
    54  171 142 0:122 / /run/docker/libcontainerd/e406ff6f3e18516d50e03dbca4de54767a69a403a6f7ec1edc2762812824521e/rootfs rw,relatime - aufs none rw,si=573b861eb147805b,dio
    55  310 142 0:60 / /run/docker/netns/71a18572176b rw,nosuid,nodev,noexec,relatime - proc proc rw
    56  `
    57  
    58  func TestCleanupMounts(t *testing.T) {
    59  	d := &Daemon{
    60  		root: "/var/lib/docker/",
    61  	}
    62  
    63  	expected := "/var/lib/docker/containers/d045dc441d2e2e1d5b3e328d47e5943811a40819fb47497c5f5a5df2d6d13c37/shm"
    64  	var unmounted int
    65  	unmount := func(target string) error {
    66  		if target == expected {
    67  			unmounted++
    68  		}
    69  		return nil
    70  	}
    71  
    72  	d.cleanupMountsFromReaderByID(strings.NewReader(mountsFixture), "", unmount)
    73  
    74  	if unmounted != 1 {
    75  		t.Fatal("Expected to unmount the shm (and the shm only)")
    76  	}
    77  }
    78  
    79  func TestCleanupMountsByID(t *testing.T) {
    80  	d := &Daemon{
    81  		root: "/var/lib/docker/",
    82  	}
    83  
    84  	expected := "/var/lib/docker/aufs/mnt/03ca4b49e71f1e49a41108829f4d5c70ac95934526e2af8984a1f65f1de0715d"
    85  	var unmounted int
    86  	unmount := func(target string) error {
    87  		if target == expected {
    88  			unmounted++
    89  		}
    90  		return nil
    91  	}
    92  
    93  	d.cleanupMountsFromReaderByID(strings.NewReader(mountsFixture), "03ca4b49e71f1e49a41108829f4d5c70ac95934526e2af8984a1f65f1de0715d", unmount)
    94  
    95  	if unmounted != 1 {
    96  		t.Fatal("Expected to unmount the auf root (and that only)")
    97  	}
    98  }
    99  
   100  func TestNotCleanupMounts(t *testing.T) {
   101  	d := &Daemon{
   102  		repository: "",
   103  	}
   104  	var unmounted bool
   105  	unmount := func(target string) error {
   106  		unmounted = true
   107  		return nil
   108  	}
   109  	mountInfo := `234 232 0:59 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,size=65536k`
   110  	d.cleanupMountsFromReaderByID(strings.NewReader(mountInfo), "", unmount)
   111  	if unmounted {
   112  		t.Fatal("Expected not to clean up /dev/shm")
   113  	}
   114  }
   115  
   116  func TestValidateContainerIsolationLinux(t *testing.T) {
   117  	d := Daemon{}
   118  
   119  	_, err := d.verifyContainerSettings("linux", &containertypes.HostConfig{Isolation: containertypes.IsolationHyperV}, nil, false)
   120  	assert.Check(t, is.Error(err, "invalid isolation 'hyperv' on linux"))
   121  }
   122  
   123  func TestShouldUnmountRoot(t *testing.T) {
   124  	for _, test := range []struct {
   125  		desc   string
   126  		root   string
   127  		info   *mountinfo.Info
   128  		expect bool
   129  	}{
   130  		{
   131  			desc:   "root is at /",
   132  			root:   "/docker",
   133  			info:   &mountinfo.Info{Root: "/docker", Mountpoint: "/docker"},
   134  			expect: true,
   135  		},
   136  		{
   137  			desc:   "root is at in a submount from `/`",
   138  			root:   "/foo/docker",
   139  			info:   &mountinfo.Info{Root: "/docker", Mountpoint: "/foo/docker"},
   140  			expect: true,
   141  		},
   142  		{
   143  			desc:   "root is mounted in from a parent mount namespace same root dir", // dind is an example of this
   144  			root:   "/docker",
   145  			info:   &mountinfo.Info{Root: "/docker/volumes/1234657/_data", Mountpoint: "/docker"},
   146  			expect: false,
   147  		},
   148  	} {
   149  		t.Run(test.desc, func(t *testing.T) {
   150  			for _, options := range []struct {
   151  				desc     string
   152  				Optional string
   153  				expect   bool
   154  			}{
   155  				{desc: "shared", Optional: "shared:", expect: true},
   156  				{desc: "slave", Optional: "slave:", expect: false},
   157  				{desc: "private", Optional: "private:", expect: false},
   158  			} {
   159  				t.Run(options.desc, func(t *testing.T) {
   160  					expect := options.expect
   161  					if expect {
   162  						expect = test.expect
   163  					}
   164  					if test.info != nil {
   165  						test.info.Optional = options.Optional
   166  					}
   167  					assert.Check(t, is.Equal(expect, shouldUnmountRoot(test.root, test.info)))
   168  				})
   169  			}
   170  		})
   171  	}
   172  }
   173  
   174  func checkMounted(t *testing.T, p string, expect bool) {
   175  	t.Helper()
   176  	mounted, err := mountinfo.Mounted(p)
   177  	assert.Check(t, err)
   178  	assert.Check(t, mounted == expect, "expected %v, actual %v", expect, mounted)
   179  }
   180  
   181  func TestRootMountCleanup(t *testing.T) {
   182  	if os.Getuid() != 0 {
   183  		t.Skip("root required")
   184  	}
   185  
   186  	t.Parallel()
   187  
   188  	testRoot, err := ioutil.TempDir("", t.Name())
   189  	assert.NilError(t, err)
   190  	defer os.RemoveAll(testRoot)
   191  	cfg := &config.Config{}
   192  
   193  	err = mount.MakePrivate(testRoot)
   194  	assert.NilError(t, err)
   195  	defer mount.Unmount(testRoot)
   196  
   197  	cfg.ExecRoot = filepath.Join(testRoot, "exec")
   198  	cfg.Root = filepath.Join(testRoot, "daemon")
   199  
   200  	err = os.Mkdir(cfg.ExecRoot, 0755)
   201  	assert.NilError(t, err)
   202  	err = os.Mkdir(cfg.Root, 0755)
   203  	assert.NilError(t, err)
   204  
   205  	d := &Daemon{configStore: cfg, root: cfg.Root}
   206  	unmountFile := getUnmountOnShutdownPath(cfg)
   207  
   208  	t.Run("regular dir no mountpoint", func(t *testing.T) {
   209  		err = setupDaemonRootPropagation(cfg)
   210  		assert.NilError(t, err)
   211  		_, err = os.Stat(unmountFile)
   212  		assert.NilError(t, err)
   213  		checkMounted(t, cfg.Root, true)
   214  
   215  		assert.Assert(t, d.cleanupMounts())
   216  		checkMounted(t, cfg.Root, false)
   217  
   218  		_, err = os.Stat(unmountFile)
   219  		assert.Assert(t, os.IsNotExist(err))
   220  	})
   221  
   222  	t.Run("root is a private mountpoint", func(t *testing.T) {
   223  		err = mount.MakePrivate(cfg.Root)
   224  		assert.NilError(t, err)
   225  		defer mount.Unmount(cfg.Root)
   226  
   227  		err = setupDaemonRootPropagation(cfg)
   228  		assert.NilError(t, err)
   229  		assert.Check(t, ensureShared(cfg.Root))
   230  
   231  		_, err = os.Stat(unmountFile)
   232  		assert.Assert(t, os.IsNotExist(err))
   233  		assert.Assert(t, d.cleanupMounts())
   234  		checkMounted(t, cfg.Root, true)
   235  	})
   236  
   237  	// mount is pre-configured with a shared mount
   238  	t.Run("root is a shared mountpoint", func(t *testing.T) {
   239  		err = mount.MakeShared(cfg.Root)
   240  		assert.NilError(t, err)
   241  		defer mount.Unmount(cfg.Root)
   242  
   243  		err = setupDaemonRootPropagation(cfg)
   244  		assert.NilError(t, err)
   245  
   246  		if _, err := os.Stat(unmountFile); err == nil {
   247  			t.Fatal("unmount file should not exist")
   248  		}
   249  
   250  		assert.Assert(t, d.cleanupMounts())
   251  		checkMounted(t, cfg.Root, true)
   252  		assert.Assert(t, mount.Unmount(cfg.Root))
   253  	})
   254  
   255  	// does not need mount but unmount file exists from previous run
   256  	t.Run("old mount file is cleaned up on setup if not needed", func(t *testing.T) {
   257  		err = mount.MakeShared(testRoot)
   258  		assert.NilError(t, err)
   259  		defer mount.MakePrivate(testRoot)
   260  		err = ioutil.WriteFile(unmountFile, nil, 0644)
   261  		assert.NilError(t, err)
   262  
   263  		err = setupDaemonRootPropagation(cfg)
   264  		assert.NilError(t, err)
   265  
   266  		_, err = os.Stat(unmountFile)
   267  		assert.Check(t, os.IsNotExist(err), err)
   268  		checkMounted(t, cfg.Root, false)
   269  		assert.Assert(t, d.cleanupMounts())
   270  	})
   271  
   272  }