github.com/rumpl/bof@v23.0.0-rc.2+incompatible/integration/container/run_linux_test.go (about)

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