github.com/tonistiigi/docker@v0.10.1-0.20240229224939-974013b0dc6a/integration/container/run_linux_test.go (about) 1 package container // import "github.com/docker/docker/integration/container" 2 3 import ( 4 "bytes" 5 "io" 6 "os" 7 "os/exec" 8 "path/filepath" 9 "strings" 10 "testing" 11 "time" 12 13 containertypes "github.com/docker/docker/api/types/container" 14 "github.com/docker/docker/api/types/versions" 15 "github.com/docker/docker/client" 16 "github.com/docker/docker/integration/internal/container" 17 net "github.com/docker/docker/integration/internal/network" 18 "github.com/docker/docker/pkg/stdcopy" 19 "github.com/docker/docker/testutil" 20 "github.com/docker/docker/testutil/daemon" 21 "golang.org/x/sys/unix" 22 "gotest.tools/v3/assert" 23 is "gotest.tools/v3/assert/cmp" 24 "gotest.tools/v3/poll" 25 "gotest.tools/v3/skip" 26 ) 27 28 func TestNISDomainname(t *testing.T) { 29 skip.If(t, testEnv.DaemonInfo.OSType != "linux") 30 31 // Rootless supports custom Hostname but doesn't support custom Domainname 32 // OCI runtime create failed: container_linux.go:349: starting container process caused "process_linux.go:449: container init caused \ 33 // "write sysctl key kernel.domainname: open /proc/sys/kernel/domainname: permission denied\"": unknown. 34 skip.If(t, testEnv.IsRootless, "rootless mode doesn't support setting Domainname (TODO: https://github.com/moby/moby/issues/40632)") 35 36 ctx := setupTest(t) 37 apiClient := testEnv.APIClient() 38 39 const ( 40 hostname = "foobar" 41 domainname = "baz.cyphar.com" 42 ) 43 44 cID := container.Run(ctx, t, apiClient, func(c *container.TestContainerConfig) { 45 c.Config.Hostname = hostname 46 c.Config.Domainname = domainname 47 }) 48 inspect, err := apiClient.ContainerInspect(ctx, cID) 49 assert.NilError(t, err) 50 assert.Check(t, is.Equal(hostname, inspect.Config.Hostname)) 51 assert.Check(t, is.Equal(domainname, inspect.Config.Domainname)) 52 53 // Check hostname. 54 res, err := container.Exec(ctx, apiClient, cID, 55 []string{"cat", "/proc/sys/kernel/hostname"}) 56 assert.NilError(t, err) 57 assert.Assert(t, is.Len(res.Stderr(), 0)) 58 assert.Equal(t, 0, res.ExitCode) 59 assert.Check(t, is.Equal(hostname, strings.TrimSpace(res.Stdout()))) 60 61 // Check domainname. 62 res, err = container.Exec(ctx, apiClient, cID, 63 []string{"cat", "/proc/sys/kernel/domainname"}) 64 assert.NilError(t, err) 65 assert.Assert(t, is.Len(res.Stderr(), 0)) 66 assert.Equal(t, 0, res.ExitCode) 67 assert.Check(t, is.Equal(domainname, strings.TrimSpace(res.Stdout()))) 68 } 69 70 func TestHostnameDnsResolution(t *testing.T) { 71 skip.If(t, testEnv.DaemonInfo.OSType != "linux") 72 73 ctx := setupTest(t) 74 apiClient := testEnv.APIClient() 75 76 const ( 77 hostname = "foobar" 78 ) 79 80 // using user defined network as we want to use internal DNS 81 netName := "foobar-net" 82 net.CreateNoError(ctx, t, apiClient, netName, net.WithDriver("bridge")) 83 84 cID := container.Run(ctx, t, apiClient, func(c *container.TestContainerConfig) { 85 c.Config.Hostname = hostname 86 c.HostConfig.NetworkMode = containertypes.NetworkMode(netName) 87 }) 88 inspect, err := apiClient.ContainerInspect(ctx, cID) 89 assert.NilError(t, err) 90 assert.Check(t, is.Equal(hostname, inspect.Config.Hostname)) 91 92 // Clear hosts file so ping will use DNS for hostname resolution 93 res, err := container.Exec(ctx, apiClient, cID, 94 []string{"sh", "-c", "echo 127.0.0.1 localhost | tee /etc/hosts && ping -c 1 foobar"}) 95 assert.NilError(t, err) 96 assert.Check(t, is.Equal("", res.Stderr())) 97 assert.Equal(t, 0, res.ExitCode) 98 } 99 100 func TestUnprivilegedPortsAndPing(t *testing.T) { 101 skip.If(t, testEnv.DaemonInfo.OSType != "linux") 102 skip.If(t, testEnv.IsRootless, "rootless mode doesn't support setting net.ipv4.ping_group_range and net.ipv4.ip_unprivileged_port_start") 103 104 ctx := setupTest(t) 105 apiClient := testEnv.APIClient() 106 107 cID := container.Run(ctx, t, apiClient, func(c *container.TestContainerConfig) { 108 c.Config.User = "1000:1000" 109 }) 110 111 // Check net.ipv4.ping_group_range. 112 res, err := container.Exec(ctx, apiClient, cID, []string{"cat", "/proc/sys/net/ipv4/ping_group_range"}) 113 assert.NilError(t, err) 114 assert.Assert(t, is.Len(res.Stderr(), 0)) 115 assert.Equal(t, 0, res.ExitCode) 116 assert.Equal(t, `0 2147483647`, strings.TrimSpace(res.Stdout())) 117 118 // Check net.ipv4.ip_unprivileged_port_start. 119 res, err = container.Exec(ctx, apiClient, cID, []string{"cat", "/proc/sys/net/ipv4/ip_unprivileged_port_start"}) 120 assert.NilError(t, err) 121 assert.Assert(t, is.Len(res.Stderr(), 0)) 122 assert.Equal(t, 0, res.ExitCode) 123 assert.Equal(t, "0", strings.TrimSpace(res.Stdout())) 124 } 125 126 func TestPrivilegedHostDevices(t *testing.T) { 127 // Host devices are linux only. Also it creates host devices, 128 // so needs to be same host. 129 skip.If(t, testEnv.IsRemoteDaemon) 130 skip.If(t, testEnv.DaemonInfo.OSType != "linux") 131 132 ctx := setupTest(t) 133 apiClient := testEnv.APIClient() 134 135 const ( 136 devTest = "/dev/test" 137 devRootOnlyTest = "/dev/root-only/test" 138 ) 139 140 // Create Null devices. 141 if err := unix.Mknod(devTest, unix.S_IFCHR|0o600, int(unix.Mkdev(1, 3))); err != nil { 142 t.Fatal(err) 143 } 144 defer os.Remove(devTest) 145 if err := os.Mkdir(filepath.Dir(devRootOnlyTest), 0o700); err != nil { 146 t.Fatal(err) 147 } 148 defer os.RemoveAll(filepath.Dir(devRootOnlyTest)) 149 if err := unix.Mknod(devRootOnlyTest, unix.S_IFCHR|0o600, int(unix.Mkdev(1, 3))); err != nil { 150 t.Fatal(err) 151 } 152 defer os.Remove(devRootOnlyTest) 153 154 cID := container.Run(ctx, t, apiClient, container.WithPrivileged(true)) 155 156 // Check test device. 157 res, err := container.Exec(ctx, apiClient, cID, []string{"ls", devTest}) 158 assert.NilError(t, err) 159 assert.Equal(t, 0, res.ExitCode) 160 assert.Check(t, is.Equal(strings.TrimSpace(res.Stdout()), devTest)) 161 162 // Check root-only test device. 163 res, err = container.Exec(ctx, apiClient, cID, []string{"ls", devRootOnlyTest}) 164 assert.NilError(t, err) 165 if testEnv.IsRootless() { 166 assert.Equal(t, 1, res.ExitCode) 167 assert.Check(t, is.Contains(res.Stderr(), "No such file or directory")) 168 } else { 169 assert.Equal(t, 0, res.ExitCode) 170 assert.Check(t, is.Equal(strings.TrimSpace(res.Stdout()), devRootOnlyTest)) 171 } 172 } 173 174 func TestRunConsoleSize(t *testing.T) { 175 skip.If(t, testEnv.DaemonInfo.OSType != "linux") 176 skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.42"), "skip test from new feature") 177 178 ctx := setupTest(t) 179 apiClient := testEnv.APIClient() 180 181 cID := container.Run(ctx, t, apiClient, 182 container.WithTty(true), 183 container.WithImage("busybox"), 184 container.WithCmd("stty", "size"), 185 container.WithConsoleSize(57, 123), 186 ) 187 188 poll.WaitOn(t, container.IsStopped(ctx, apiClient, cID), poll.WithDelay(100*time.Millisecond)) 189 190 out, err := apiClient.ContainerLogs(ctx, cID, containertypes.LogsOptions{ShowStdout: true}) 191 assert.NilError(t, err) 192 defer out.Close() 193 194 var b bytes.Buffer 195 _, err = io.Copy(&b, out) 196 assert.NilError(t, err) 197 198 assert.Equal(t, strings.TrimSpace(b.String()), "123 57") 199 } 200 201 func TestRunWithAlternativeContainerdShim(t *testing.T) { 202 skip.If(t, testEnv.IsRemoteDaemon) 203 skip.If(t, testEnv.DaemonInfo.OSType != "linux") 204 205 ctx := testutil.StartSpan(baseContext, t) 206 207 realShimPath, err := exec.LookPath("containerd-shim-runc-v2") 208 assert.Assert(t, err) 209 realShimPath, err = filepath.Abs(realShimPath) 210 assert.Assert(t, err) 211 212 shimDir := testutil.TempDir(t) 213 assert.Assert(t, err) 214 shimDir, err = filepath.Abs(shimDir) 215 assert.Assert(t, err) 216 assert.Assert(t, os.Symlink(realShimPath, filepath.Join(shimDir, "containerd-shim-realfake-v42"))) 217 218 d := daemon.New(t, 219 daemon.WithEnvVars("PATH="+shimDir+":"+os.Getenv("PATH")), 220 daemon.WithContainerdSocket(""), // A new containerd instance needs to be started which inherits the PATH env var defined above. 221 ) 222 d.StartWithBusybox(ctx, t) 223 defer d.Stop(t) 224 225 apiClient := d.NewClientT(t) 226 227 cID := container.Run(ctx, t, apiClient, 228 container.WithImage("busybox"), 229 container.WithCmd("sh", "-c", `echo 'Hello, world!'`), 230 container.WithRuntime("io.containerd.realfake.v42"), 231 ) 232 233 poll.WaitOn(t, container.IsStopped(ctx, apiClient, cID), poll.WithDelay(100*time.Millisecond)) 234 235 out, err := apiClient.ContainerLogs(ctx, cID, containertypes.LogsOptions{ShowStdout: true}) 236 assert.NilError(t, err) 237 defer out.Close() 238 239 var b bytes.Buffer 240 _, err = stdcopy.StdCopy(&b, io.Discard, out) 241 assert.NilError(t, err) 242 243 assert.Equal(t, strings.TrimSpace(b.String()), "Hello, world!") 244 245 d.Stop(t) 246 d.Start(t, "--default-runtime="+"io.containerd.realfake.v42") 247 248 cID = container.Run(ctx, t, apiClient, 249 container.WithImage("busybox"), 250 container.WithCmd("sh", "-c", `echo 'Hello, world!'`), 251 ) 252 253 poll.WaitOn(t, container.IsStopped(ctx, apiClient, cID), poll.WithDelay(100*time.Millisecond)) 254 255 out, err = apiClient.ContainerLogs(ctx, cID, containertypes.LogsOptions{ShowStdout: true}) 256 assert.NilError(t, err) 257 defer out.Close() 258 259 b.Reset() 260 _, err = stdcopy.StdCopy(&b, io.Discard, out) 261 assert.NilError(t, err) 262 263 assert.Equal(t, strings.TrimSpace(b.String()), "Hello, world!") 264 } 265 266 func TestMacAddressIsAppliedToMainNetworkWithShortID(t *testing.T) { 267 skip.If(t, testEnv.IsRemoteDaemon) 268 skip.If(t, testEnv.DaemonInfo.OSType != "linux") 269 270 ctx := testutil.StartSpan(baseContext, t) 271 272 d := daemon.New(t) 273 d.StartWithBusybox(ctx, t) 274 defer d.Stop(t) 275 276 apiClient, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("1.43")) 277 assert.NilError(t, err) 278 279 n := net.CreateNoError(ctx, t, apiClient, "testnet", net.WithIPAM("192.168.101.0/24", "192.168.101.1")) 280 281 cid := container.Run(ctx, t, apiClient, 282 container.WithImage("busybox:latest"), 283 container.WithCmd("/bin/sleep", "infinity"), 284 container.WithStopSignal("SIGKILL"), 285 container.WithNetworkMode(n[:10]), 286 container.WithContainerWideMacAddress("02:42:08:26:a9:55")) 287 defer container.Remove(ctx, t, apiClient, cid, containertypes.RemoveOptions{Force: true}) 288 289 c := container.Inspect(ctx, t, apiClient, cid) 290 assert.Equal(t, c.NetworkSettings.Networks["testnet"].MacAddress, "02:42:08:26:a9:55") 291 } 292 293 func TestStaticIPOutsideSubpool(t *testing.T) { 294 skip.If(t, testEnv.IsRemoteDaemon) 295 skip.If(t, testEnv.DaemonInfo.OSType != "linux") 296 297 ctx := testutil.StartSpan(baseContext, t) 298 299 d := daemon.New(t) 300 d.StartWithBusybox(ctx, t) 301 defer d.Stop(t) 302 303 apiClient, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("1.43")) 304 assert.NilError(t, err) 305 306 const netname = "subnet-range" 307 n := net.CreateNoError(ctx, t, apiClient, netname, net.WithIPAMRange("10.42.0.0/16", "10.42.128.0/24", "10.42.0.1")) 308 defer net.RemoveNoError(ctx, t, apiClient, n) 309 310 cID := container.Run(ctx, t, apiClient, 311 container.WithImage("busybox:latest"), 312 container.WithCmd("sh", "-c", `ip -4 -oneline addr show eth0`), 313 container.WithNetworkMode(netname), 314 container.WithIPv4(netname, "10.42.1.3"), 315 ) 316 317 poll.WaitOn(t, container.IsStopped(ctx, apiClient, cID), poll.WithDelay(100*time.Millisecond)) 318 319 out, err := apiClient.ContainerLogs(ctx, cID, containertypes.LogsOptions{ShowStdout: true}) 320 assert.NilError(t, err) 321 defer out.Close() 322 323 var b bytes.Buffer 324 _, err = io.Copy(&b, out) 325 assert.NilError(t, err) 326 327 assert.Check(t, is.Contains(b.String(), "inet 10.42.1.3/16")) 328 }