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  }