github.com/containers/podman/v5@v5.1.0-rc1/test/e2e/toolbox_test.go (about)

     1  package integration
     2  
     3  /*
     4  	toolbox_test.go is under the care of the Toolbox Team.
     5  
     6  	The tests are trying to stress parts of Podman that Toolbox[0] needs for
     7  	its functionality.
     8  
     9  	[0] https://github.com/containers/toolbox
    10  
    11  	Info about test cases:
    12  	- some tests rely on a certain configuration of a container that is done by
    13  		executing several commands in the entry-point of a container. To make
    14  		sure the initialization had enough time to be executed,
    15  		WaitContainerReady() after the container is started.
    16  
    17  	- in several places there's an invocation of 'podman logs' It is there mainly
    18  		to ease debugging when a test goes wrong (during the initialization of a
    19  		container) but sometimes it is also used in the test case itself.
    20  
    21  	Maintainers (Toolbox Team):
    22  	- Ondřej Míchal <harrymichal@fedoraproject.org>
    23  	- Debarshi Ray <rishi@fedoraproject.org>
    24  
    25  	Also available on Freenode IRC on #silverblue or #podman
    26  */
    27  
    28  import (
    29  	"fmt"
    30  	"os/exec"
    31  	"os/user"
    32  	"path"
    33  	"strconv"
    34  	"strings"
    35  	"syscall"
    36  
    37  	"github.com/containers/podman/v5/libpod/define"
    38  	. "github.com/containers/podman/v5/test/utils"
    39  	. "github.com/onsi/ginkgo/v2"
    40  	. "github.com/onsi/gomega"
    41  )
    42  
    43  var _ = Describe("Toolbox-specific testing", func() {
    44  
    45  	It("podman run --dns=none - allows self-management of /etc/resolv.conf", func() {
    46  		session := podmanTest.Podman([]string{"run", "--dns", "none", ALPINE, "sh", "-c",
    47  			"rm -f /etc/resolv.conf; touch -d '1970-01-01 00:02:03' /etc/resolv.conf; stat -c %s:%Y /etc/resolv.conf"})
    48  		session.WaitWithDefaultTimeout()
    49  		Expect(session).Should(ExitCleanly())
    50  		Expect(session.OutputToString()).To(ContainSubstring("0:123"))
    51  	})
    52  
    53  	It("podman run --no-hosts - allows self-management of /etc/hosts", func() {
    54  		session := podmanTest.Podman([]string{"run", "--no-hosts", ALPINE, "sh", "-c",
    55  			"rm -f /etc/hosts; touch -d '1970-01-01 00:02:03' /etc/hosts; stat -c %s:%Y /etc/hosts"})
    56  		session.WaitWithDefaultTimeout()
    57  		Expect(session).Should(ExitCleanly())
    58  		Expect(session.OutputToString()).To(ContainSubstring("0:123"))
    59  	})
    60  
    61  	It("podman create --ulimit host + podman exec - correctly mirrors hosts ulimits", func() {
    62  		if podmanTest.RemoteTest {
    63  			Skip("Ulimit check does not work with a remote client")
    64  		}
    65  		var session *PodmanSessionIntegration
    66  		var containerHardLimit int
    67  		var rlimit syscall.Rlimit
    68  		var err error
    69  
    70  		err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlimit)
    71  		Expect(err).ToNot(HaveOccurred())
    72  		GinkgoWriter.Printf("Expected value: %d", rlimit.Max)
    73  
    74  		session = podmanTest.Podman([]string{"create", "--name", "test", "--ulimit", "host", ALPINE,
    75  			"sleep", "1000"})
    76  		session.WaitWithDefaultTimeout()
    77  		Expect(session).Should(ExitCleanly())
    78  
    79  		session = podmanTest.Podman([]string{"start", "test"})
    80  		session.WaitWithDefaultTimeout()
    81  		Expect(session).Should(ExitCleanly())
    82  
    83  		session = podmanTest.Podman([]string{"exec", "test", "sh", "-c",
    84  			"ulimit -H -n"})
    85  		session.WaitWithDefaultTimeout()
    86  		Expect(session).Should(ExitCleanly())
    87  		containerHardLimit, err = strconv.Atoi(strings.Trim(session.OutputToString(), "\n"))
    88  		Expect(err).ToNot(HaveOccurred())
    89  		Expect(containerHardLimit).To(BeNumerically(">=", rlimit.Max))
    90  	})
    91  
    92  	It("podman create --ipc=host --pid=host + podman exec - correct shared memory limit size", func() {
    93  		// Comparison of the size of /dev/shm on the host being equal to the one in
    94  		// a container
    95  		if podmanTest.RemoteTest {
    96  			Skip("Shm size check does not work with a remote client")
    97  		}
    98  		SkipIfRootlessCgroupsV1("Not supported for rootless + CgroupsV1")
    99  		var session *PodmanSessionIntegration
   100  		var cmd *exec.Cmd
   101  		var hostShmSize, containerShmSize int
   102  		var err error
   103  
   104  		// Because Alpine uses busybox, most commands don't offer advanced options
   105  		// like "--output" in df. Therefore the value of the field 'Size' (or
   106  		// ('1K-blocks') needs to be extracted manually.
   107  		cmd = exec.Command("df", "/dev/shm")
   108  		res, err := cmd.Output()
   109  		Expect(err).ToNot(HaveOccurred())
   110  		lines := strings.SplitN(string(res), "\n", 2)
   111  		fields := strings.Fields(lines[len(lines)-1])
   112  		hostShmSize, err = strconv.Atoi(fields[1])
   113  		Expect(err).ToNot(HaveOccurred())
   114  
   115  		session = podmanTest.Podman([]string{"create", "--name", "test", "--ipc=host", "--pid=host", ALPINE,
   116  			"sleep", "1000"})
   117  		session.WaitWithDefaultTimeout()
   118  		Expect(session).Should(ExitCleanly())
   119  
   120  		session = podmanTest.Podman([]string{"start", "test"})
   121  		session.WaitWithDefaultTimeout()
   122  		Expect(session).Should(ExitCleanly())
   123  
   124  		session = podmanTest.Podman([]string{"exec", "test",
   125  			"df", "/dev/shm"})
   126  		session.WaitWithDefaultTimeout()
   127  		Expect(session).Should(ExitCleanly())
   128  		lines = session.OutputToStringArray()
   129  		fields = strings.Fields(lines[len(lines)-1])
   130  		containerShmSize, err = strconv.Atoi(fields[1])
   131  		Expect(err).ToNot(HaveOccurred())
   132  
   133  		// In some cases it may happen that the size of /dev/shm is not exactly
   134  		// equal. Therefore it's fine if there's a slight tolerance between the
   135  		// compared values.
   136  		Expect(hostShmSize).To(BeNumerically("~", containerShmSize, 100))
   137  	})
   138  
   139  	It("podman create --userns=keep-id --user root:root - entrypoint - entrypoint is executed as root", func() {
   140  		SkipIfNotRootless("only meaningful when run rootless")
   141  		session := podmanTest.Podman([]string{"run", "--userns=keep-id", "--user", "root:root", ALPINE,
   142  			"id"})
   143  		session.WaitWithDefaultTimeout()
   144  		Expect(session).Should(ExitCleanly())
   145  		Expect(session.OutputToString()).To(ContainSubstring("uid=0(root) gid=0(root)"))
   146  	})
   147  
   148  	It("podman create --userns=keep-id + podman exec - correct names of user and group", func() {
   149  		SkipIfNotRootless("only meaningful when run rootless")
   150  		var session *PodmanSessionIntegration
   151  		var err error
   152  
   153  		currentUser, err := user.Current()
   154  		Expect(err).ToNot(HaveOccurred())
   155  
   156  		currentGroup, err := user.LookupGroupId(currentUser.Gid)
   157  		Expect(err).ToNot(HaveOccurred())
   158  
   159  		session = podmanTest.Podman([]string{"create", "--name", "test", "--userns=keep-id", ALPINE,
   160  			"sleep", "1000"})
   161  		session.WaitWithDefaultTimeout()
   162  		Expect(session).Should(ExitCleanly())
   163  		Expect(err).ToNot(HaveOccurred())
   164  
   165  		session = podmanTest.Podman([]string{"start", "test"})
   166  		session.WaitWithDefaultTimeout()
   167  		Expect(session).Should(ExitCleanly())
   168  
   169  		expectedOutput := fmt.Sprintf("uid=%s(%s) gid=%s(%s)",
   170  			currentUser.Uid, currentUser.Username,
   171  			currentGroup.Gid, currentGroup.Name)
   172  
   173  		session = podmanTest.Podman([]string{"exec", "test",
   174  			"id"})
   175  		session.WaitWithDefaultTimeout()
   176  		Expect(session).Should(ExitCleanly())
   177  		Expect(session.OutputToString()).To(ContainSubstring(expectedOutput))
   178  	})
   179  
   180  	It("podman run --userns=keep-id - modify /etc/passwd and /etc/group", func() {
   181  		passwdLine := "testuser:x:1001:1001::/home/testuser:/bin/sh"
   182  		groupLine := "testuser:x:1001:"
   183  
   184  		// ensure that the container can edit passwd and group files
   185  		session := podmanTest.Podman([]string{"run", "--log-driver", "k8s-file", "--name", "test", "--userns=keep-id",
   186  			"--user", "root:root", ALPINE, "sh", "-c",
   187  			fmt.Sprintf("echo %s > /etc/passwd && echo %s > /etc/group && cat /etc/passwd && cat /etc/group", passwdLine, groupLine)})
   188  		session.WaitWithDefaultTimeout()
   189  		Expect(session).Should(ExitCleanly())
   190  		Expect(session.OutputToString()).Should(ContainSubstring(passwdLine))
   191  		Expect(session.OutputToString()).Should(ContainSubstring(groupLine))
   192  	})
   193  
   194  	It("podman run --privileged --userns=keep-id --user root:root - entrypoint - (bind)mounting", func() {
   195  		SkipIfNotRootless("only meaningful when run rootless")
   196  		var session *PodmanSessionIntegration
   197  
   198  		session = podmanTest.Podman([]string{"run", "--privileged", "--userns=keep-id", "--user", "root:root", ALPINE,
   199  			"mount", "-t", define.TypeTmpfs, define.TypeTmpfs, "/tmp"})
   200  		session.WaitWithDefaultTimeout()
   201  		Expect(session).Should(ExitCleanly())
   202  
   203  		session = podmanTest.Podman([]string{"run", "--privileged", "--userns=keep-id", "--user", "root:root", ALPINE,
   204  			"mount", "--rbind", "/tmp", "/var/tmp"})
   205  		session.WaitWithDefaultTimeout()
   206  		Expect(session).Should(ExitCleanly())
   207  	})
   208  
   209  	It("podman create + start - with all needed switches for create", func() {
   210  		SkipIfNotRootless("only meaningful when run rootless")
   211  
   212  		// These should be most of the switches that Toolbox uses to create a "toolbox" container
   213  		// https://github.com/containers/toolbox/blob/main/src/cmd/create.go
   214  		session := podmanTest.Podman([]string{"create",
   215  			"--log-driver", "k8s-file",
   216  			"--dns", "none",
   217  			"--hostname", "toolbox",
   218  			"--ipc", "host",
   219  			"--label", "com.github.containers.toolbox=true",
   220  			"--name", "test",
   221  			"--network", "host",
   222  			"--no-hosts",
   223  			"--pid", "host",
   224  			"--privileged",
   225  			"--security-opt", "label=disable",
   226  			"--ulimit", "host",
   227  			"--userns=keep-id",
   228  			"--user", "root:root",
   229  			ALPINE, "sh", "-c", "echo READY"})
   230  		session.WaitWithDefaultTimeout()
   231  		Expect(session).Should(ExitCleanly())
   232  
   233  		session = podmanTest.Podman([]string{"start", "-a", "test"})
   234  		session.WaitWithDefaultTimeout()
   235  		Expect(session).Should(ExitCleanly())
   236  		Expect(session.OutputToString()).Should(ContainSubstring("READY"))
   237  	})
   238  
   239  	It("podman run --userns=keep-id check $HOME", func() {
   240  		SkipIfNotRootless("only meaningful when run rootless")
   241  		var session *PodmanSessionIntegration
   242  		currentUser, err := user.Current()
   243  		Expect(err).ToNot(HaveOccurred())
   244  
   245  		session = podmanTest.Podman([]string{"run", "-v", fmt.Sprintf("%s:%s", currentUser.HomeDir, currentUser.HomeDir), "--userns=keep-id", ALPINE, "sh", "-c", "echo $HOME"})
   246  		session.WaitWithDefaultTimeout()
   247  		Expect(session).Should(ExitCleanly())
   248  		Expect(session.OutputToString()).To(ContainSubstring(currentUser.HomeDir))
   249  
   250  		location := path.Dir(currentUser.HomeDir)
   251  		volumeArg := fmt.Sprintf("%s:%s", location, location)
   252  		session = podmanTest.Podman([]string{"run",
   253  			"--userns=keep-id",
   254  			"--volume", volumeArg,
   255  			ALPINE, "sh", "-c", "echo $HOME"})
   256  		session.WaitWithDefaultTimeout()
   257  		Expect(session).Should(ExitCleanly())
   258  		Expect(session.OutputToString()).To(ContainSubstring(currentUser.HomeDir))
   259  	})
   260  
   261  })