daemon_linux_test.go 11 KB

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