github.com/rish1988/moby@v25.0.2+incompatible/daemon/oci_linux_test.go (about) 1 package daemon // import "github.com/docker/docker/daemon" 2 3 import ( 4 "context" 5 "os" 6 "path/filepath" 7 "testing" 8 9 containertypes "github.com/docker/docker/api/types/container" 10 "github.com/docker/docker/container" 11 "github.com/docker/docker/daemon/config" 12 "github.com/docker/docker/daemon/network" 13 "github.com/docker/docker/libnetwork" 14 "github.com/google/go-cmp/cmp/cmpopts" 15 "github.com/opencontainers/runtime-spec/specs-go" 16 "golang.org/x/sys/unix" 17 "gotest.tools/v3/assert" 18 is "gotest.tools/v3/assert/cmp" 19 "gotest.tools/v3/skip" 20 ) 21 22 func setupFakeDaemon(t *testing.T, c *container.Container) *Daemon { 23 t.Helper() 24 root := t.TempDir() 25 26 rootfs := filepath.Join(root, "rootfs") 27 err := os.MkdirAll(rootfs, 0o755) 28 assert.NilError(t, err) 29 30 netController, err := libnetwork.New() 31 assert.NilError(t, err) 32 33 d := &Daemon{ 34 // some empty structs to avoid getting a panic 35 // caused by a null pointer dereference 36 linkIndex: newLinkIndex(), 37 netController: netController, 38 imageService: &fakeImageService{}, 39 } 40 41 c.Root = root 42 c.BaseFS = rootfs 43 44 if c.Config == nil { 45 c.Config = new(containertypes.Config) 46 } 47 if c.HostConfig == nil { 48 c.HostConfig = new(containertypes.HostConfig) 49 } 50 if c.NetworkSettings == nil { 51 c.NetworkSettings = &network.Settings{Networks: make(map[string]*network.EndpointSettings)} 52 } 53 54 // HORRIBLE HACK: clean up shm mounts leaked by some tests. Otherwise the 55 // offending tests would fail due to the mounts blocking the temporary 56 // directory from being cleaned up. 57 t.Cleanup(func() { 58 if c.ShmPath != "" { 59 var err error 60 for err == nil { // Some tests over-mount over the same path multiple times. 61 err = unix.Unmount(c.ShmPath, unix.MNT_DETACH) 62 } 63 } 64 }) 65 66 return d 67 } 68 69 type fakeImageService struct { 70 ImageService 71 } 72 73 func (i *fakeImageService) StorageDriver() string { 74 return "overlay" 75 } 76 77 // TestTmpfsDevShmNoDupMount checks that a user-specified /dev/shm tmpfs 78 // mount (as in "docker run --tmpfs /dev/shm:rw,size=NNN") does not result 79 // in "Duplicate mount point" error from the engine. 80 // https://github.com/moby/moby/issues/35455 81 func TestTmpfsDevShmNoDupMount(t *testing.T) { 82 skip.If(t, os.Getuid() != 0, "skipping test that requires root") 83 c := &container.Container{ 84 ShmPath: "foobar", // non-empty, for c.IpcMounts() to work 85 HostConfig: &containertypes.HostConfig{ 86 IpcMode: containertypes.IPCModeShareable, // default mode 87 // --tmpfs /dev/shm:rw,exec,size=NNN 88 Tmpfs: map[string]string{ 89 "/dev/shm": "rw,exec,size=1g", 90 }, 91 }, 92 } 93 d := setupFakeDaemon(t, c) 94 95 _, err := d.createSpec(context.TODO(), &configStore{}, c, nil) 96 assert.Check(t, err) 97 } 98 99 // TestIpcPrivateVsReadonly checks that in case of IpcMode: private 100 // and ReadonlyRootfs: true (as in "docker run --ipc private --read-only") 101 // the resulting /dev/shm mount is NOT made read-only. 102 // https://github.com/moby/moby/issues/36503 103 func TestIpcPrivateVsReadonly(t *testing.T) { 104 skip.If(t, os.Getuid() != 0, "skipping test that requires root") 105 c := &container.Container{ 106 HostConfig: &containertypes.HostConfig{ 107 IpcMode: containertypes.IPCModePrivate, 108 ReadonlyRootfs: true, 109 }, 110 } 111 d := setupFakeDaemon(t, c) 112 113 s, err := d.createSpec(context.TODO(), &configStore{}, c, nil) 114 assert.Check(t, err) 115 116 // Find the /dev/shm mount in ms, check it does not have ro 117 for _, m := range s.Mounts { 118 if m.Destination != "/dev/shm" { 119 continue 120 } 121 assert.Check(t, is.Equal(false, inSlice(m.Options, "ro"))) 122 } 123 } 124 125 // TestSysctlOverride ensures that any implicit sysctls (such as 126 // Config.Domainname) are overridden by an explicit sysctl in the HostConfig. 127 func TestSysctlOverride(t *testing.T) { 128 skip.If(t, os.Getuid() != 0, "skipping test that requires root") 129 c := &container.Container{ 130 Config: &containertypes.Config{ 131 Hostname: "foobar", 132 Domainname: "baz.cyphar.com", 133 }, 134 HostConfig: &containertypes.HostConfig{ 135 NetworkMode: "bridge", 136 Sysctls: map[string]string{}, 137 }, 138 } 139 d := setupFakeDaemon(t, c) 140 141 // Ensure that the implicit sysctl is set correctly. 142 s, err := d.createSpec(context.TODO(), &configStore{}, c, nil) 143 assert.NilError(t, err) 144 assert.Equal(t, s.Hostname, "foobar") 145 assert.Equal(t, s.Linux.Sysctl["kernel.domainname"], c.Config.Domainname) 146 if sysctlExists("net.ipv4.ip_unprivileged_port_start") { 147 assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], "0") 148 } 149 if sysctlExists("net.ipv4.ping_group_range") { 150 assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "0 2147483647") 151 } 152 153 // Set an explicit sysctl. 154 c.HostConfig.Sysctls["kernel.domainname"] = "foobar.net" 155 assert.Assert(t, c.HostConfig.Sysctls["kernel.domainname"] != c.Config.Domainname) 156 c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"] = "1024" 157 158 s, err = d.createSpec(context.TODO(), &configStore{}, c, nil) 159 assert.NilError(t, err) 160 assert.Equal(t, s.Hostname, "foobar") 161 assert.Equal(t, s.Linux.Sysctl["kernel.domainname"], c.HostConfig.Sysctls["kernel.domainname"]) 162 assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"]) 163 164 // Ensure the ping_group_range is not set on a daemon with user-namespaces enabled 165 s, err = d.createSpec(context.TODO(), &configStore{Config: config.Config{RemappedRoot: "dummy:dummy"}}, c, nil) 166 assert.NilError(t, err) 167 _, ok := s.Linux.Sysctl["net.ipv4.ping_group_range"] 168 assert.Assert(t, !ok) 169 170 // Ensure the ping_group_range is set on a container in "host" userns mode 171 // on a daemon with user-namespaces enabled 172 c.HostConfig.UsernsMode = "host" 173 s, err = d.createSpec(context.TODO(), &configStore{Config: config.Config{RemappedRoot: "dummy:dummy"}}, c, nil) 174 assert.NilError(t, err) 175 assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "0 2147483647") 176 } 177 178 // TestSysctlOverrideHost ensures that any implicit network sysctls are not set 179 // with host networking 180 func TestSysctlOverrideHost(t *testing.T) { 181 skip.If(t, os.Getuid() != 0, "skipping test that requires root") 182 c := &container.Container{ 183 Config: &containertypes.Config{}, 184 HostConfig: &containertypes.HostConfig{ 185 NetworkMode: "host", 186 Sysctls: map[string]string{}, 187 }, 188 } 189 d := setupFakeDaemon(t, c) 190 191 // Ensure that the implicit sysctl is not set 192 s, err := d.createSpec(context.TODO(), &configStore{}, c, nil) 193 assert.NilError(t, err) 194 assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], "") 195 assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "") 196 197 // Set an explicit sysctl. 198 c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"] = "1024" 199 200 s, err = d.createSpec(context.TODO(), &configStore{}, c, nil) 201 assert.NilError(t, err) 202 assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"]) 203 } 204 205 func TestGetSourceMount(t *testing.T) { 206 // must be able to find source mount for / 207 mnt, _, err := getSourceMount("/") 208 assert.NilError(t, err) 209 assert.Equal(t, mnt, "/") 210 211 // must be able to find source mount for current directory 212 cwd, err := os.Getwd() 213 assert.NilError(t, err) 214 _, _, err = getSourceMount(cwd) 215 assert.NilError(t, err) 216 } 217 218 func TestDefaultResources(t *testing.T) { 219 skip.If(t, os.Getuid() != 0, "skipping test that requires root") // TODO: is this actually true? I'm guilty of following the cargo cult here. 220 221 c := &container.Container{ 222 HostConfig: &containertypes.HostConfig{ 223 IpcMode: containertypes.IPCModeNone, 224 }, 225 } 226 d := setupFakeDaemon(t, c) 227 228 s, err := d.createSpec(context.Background(), &configStore{}, c, nil) 229 assert.NilError(t, err) 230 checkResourcesAreUnset(t, s.Linux.Resources) 231 } 232 233 func checkResourcesAreUnset(t *testing.T, r *specs.LinuxResources) { 234 t.Helper() 235 236 if r != nil { 237 if r.Memory != nil { 238 assert.Check(t, is.DeepEqual(r.Memory, &specs.LinuxMemory{})) 239 } 240 if r.CPU != nil { 241 assert.Check(t, is.DeepEqual(r.CPU, &specs.LinuxCPU{})) 242 } 243 assert.Check(t, is.Nil(r.Pids)) 244 if r.BlockIO != nil { 245 assert.Check(t, is.DeepEqual(r.BlockIO, &specs.LinuxBlockIO{}, cmpopts.EquateEmpty())) 246 } 247 if r.Network != nil { 248 assert.Check(t, is.DeepEqual(r.Network, &specs.LinuxNetwork{}, cmpopts.EquateEmpty())) 249 } 250 } 251 }