github.com/containers/podman/v4@v4.9.4/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/v4/libpod/define"
    38  	. "github.com/containers/podman/v4/test/utils"
    39  	. "github.com/onsi/ginkgo/v2"
    40  	. "github.com/onsi/gomega"
    41  	. "github.com/onsi/gomega/gexec"
    42  )
    43  
    44  var _ = Describe("Toolbox-specific testing", func() {
    45  
    46  	It("podman run --dns=none - allows self-management of /etc/resolv.conf", func() {
    47  		session := podmanTest.Podman([]string{"run", "--dns", "none", ALPINE, "sh", "-c",
    48  			"rm -f /etc/resolv.conf; touch -d '1970-01-01 00:02:03' /etc/resolv.conf; stat -c %s:%Y /etc/resolv.conf"})
    49  		session.WaitWithDefaultTimeout()
    50  		Expect(session).Should(ExitCleanly())
    51  		Expect(session.OutputToString()).To(ContainSubstring("0:123"))
    52  	})
    53  
    54  	It("podman run --no-hosts - allows self-management of /etc/hosts", func() {
    55  		session := podmanTest.Podman([]string{"run", "--no-hosts", ALPINE, "sh", "-c",
    56  			"rm -f /etc/hosts; touch -d '1970-01-01 00:02:03' /etc/hosts; stat -c %s:%Y /etc/hosts"})
    57  		session.WaitWithDefaultTimeout()
    58  		Expect(session).Should(ExitCleanly())
    59  		Expect(session.OutputToString()).To(ContainSubstring("0:123"))
    60  	})
    61  
    62  	It("podman create --ulimit host + podman exec - correctly mirrors hosts ulimits", func() {
    63  		if podmanTest.RemoteTest {
    64  			Skip("Ulimit check does not work with a remote client")
    65  		}
    66  		var session *PodmanSessionIntegration
    67  		var containerHardLimit int
    68  		var rlimit syscall.Rlimit
    69  		var err error
    70  
    71  		err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlimit)
    72  		Expect(err).ToNot(HaveOccurred())
    73  		GinkgoWriter.Printf("Expected value: %d", rlimit.Max)
    74  
    75  		session = podmanTest.Podman([]string{"create", "--name", "test", "--ulimit", "host", ALPINE,
    76  			"sleep", "1000"})
    77  		session.WaitWithDefaultTimeout()
    78  		Expect(session).Should(ExitCleanly())
    79  
    80  		session = podmanTest.Podman([]string{"start", "test"})
    81  		session.WaitWithDefaultTimeout()
    82  		Expect(session).Should(ExitCleanly())
    83  
    84  		session = podmanTest.Podman([]string{"exec", "test", "sh", "-c",
    85  			"ulimit -H -n"})
    86  		session.WaitWithDefaultTimeout()
    87  		Expect(session).Should(ExitCleanly())
    88  		containerHardLimit, err = strconv.Atoi(strings.Trim(session.OutputToString(), "\n"))
    89  		Expect(err).ToNot(HaveOccurred())
    90  		Expect(containerHardLimit).To(BeNumerically(">=", rlimit.Max))
    91  	})
    92  
    93  	It("podman create --ipc=host --pid=host + podman exec - correct shared memory limit size", func() {
    94  		// Comparison of the size of /dev/shm on the host being equal to the one in
    95  		// a container
    96  		if podmanTest.RemoteTest {
    97  			Skip("Shm size check does not work with a remote client")
    98  		}
    99  		SkipIfRootlessCgroupsV1("Not supported for rootless + CgroupsV1")
   100  		var session *PodmanSessionIntegration
   101  		var cmd *exec.Cmd
   102  		var hostShmSize, containerShmSize int
   103  		var err error
   104  
   105  		// Because Alpine uses busybox, most commands don't offer advanced options
   106  		// like "--output" in df. Therefore the value of the field 'Size' (or
   107  		// ('1K-blocks') needs to be extracted manually.
   108  		cmd = exec.Command("df", "/dev/shm")
   109  		res, err := cmd.Output()
   110  		Expect(err).ToNot(HaveOccurred())
   111  		lines := strings.SplitN(string(res), "\n", 2)
   112  		fields := strings.Fields(lines[len(lines)-1])
   113  		hostShmSize, err = strconv.Atoi(fields[1])
   114  		Expect(err).ToNot(HaveOccurred())
   115  
   116  		session = podmanTest.Podman([]string{"create", "--name", "test", "--ipc=host", "--pid=host", ALPINE,
   117  			"sleep", "1000"})
   118  		session.WaitWithDefaultTimeout()
   119  		Expect(session).Should(ExitCleanly())
   120  
   121  		session = podmanTest.Podman([]string{"start", "test"})
   122  		session.WaitWithDefaultTimeout()
   123  		Expect(session).Should(ExitCleanly())
   124  
   125  		session = podmanTest.Podman([]string{"exec", "test",
   126  			"df", "/dev/shm"})
   127  		session.WaitWithDefaultTimeout()
   128  		Expect(session).Should(ExitCleanly())
   129  		lines = session.OutputToStringArray()
   130  		fields = strings.Fields(lines[len(lines)-1])
   131  		containerShmSize, err = strconv.Atoi(fields[1])
   132  		Expect(err).ToNot(HaveOccurred())
   133  
   134  		// In some cases it may happen that the size of /dev/shm is not exactly
   135  		// equal. Therefore it's fine if there's a slight tolerance between the
   136  		// compared values.
   137  		Expect(hostShmSize).To(BeNumerically("~", containerShmSize, 100))
   138  	})
   139  
   140  	It("podman create --userns=keep-id --user root:root - entrypoint - entrypoint is executed as root", func() {
   141  		SkipIfNotRootless("only meaningful when run rootless")
   142  		session := podmanTest.Podman([]string{"run", "--userns=keep-id", "--user", "root:root", ALPINE,
   143  			"id"})
   144  		session.WaitWithDefaultTimeout()
   145  		Expect(session).Should(ExitCleanly())
   146  		Expect(session.OutputToString()).To(ContainSubstring("uid=0(root) gid=0(root)"))
   147  	})
   148  
   149  	It("podman create --userns=keep-id + podman exec - correct names of user and group", func() {
   150  		SkipIfNotRootless("only meaningful when run rootless")
   151  		var session *PodmanSessionIntegration
   152  		var err error
   153  
   154  		currentUser, err := user.Current()
   155  		Expect(err).ToNot(HaveOccurred())
   156  
   157  		currentGroup, err := user.LookupGroupId(currentUser.Gid)
   158  		Expect(err).ToNot(HaveOccurred())
   159  
   160  		session = podmanTest.Podman([]string{"create", "--name", "test", "--userns=keep-id", ALPINE,
   161  			"sleep", "1000"})
   162  		session.WaitWithDefaultTimeout()
   163  		Expect(session).Should(ExitCleanly())
   164  		Expect(err).ToNot(HaveOccurred())
   165  
   166  		session = podmanTest.Podman([]string{"start", "test"})
   167  		session.WaitWithDefaultTimeout()
   168  		Expect(session).Should(ExitCleanly())
   169  
   170  		expectedOutput := fmt.Sprintf("uid=%s(%s) gid=%s(%s)",
   171  			currentUser.Uid, currentUser.Username,
   172  			currentGroup.Gid, currentGroup.Name)
   173  
   174  		session = podmanTest.Podman([]string{"exec", "test",
   175  			"id"})
   176  		session.WaitWithDefaultTimeout()
   177  		Expect(session).Should(ExitCleanly())
   178  		Expect(session.OutputToString()).To(ContainSubstring(expectedOutput))
   179  	})
   180  
   181  	It("podman create --userns=keep-id - entrypoint - adding user with useradd and then removing their password", func() {
   182  		SkipIfNotRootless("only meaningful when run rootless")
   183  		var session *PodmanSessionIntegration
   184  
   185  		var username = "testuser"
   186  		var homeDir = "/home/testuser"
   187  		var shell = "/bin/sh"
   188  		var uid = "1001"
   189  		var gid = "1001"
   190  
   191  		useradd := fmt.Sprintf("useradd --home-dir %s --shell %s --uid %s %s",
   192  			homeDir, shell, uid, username)
   193  		passwd := fmt.Sprintf("passwd --delete %s", username)
   194  		session = podmanTest.Podman([]string{"create", "--log-driver", "k8s-file", "--name", "test", "--userns=keep-id", "--user", "root:root", fedoraToolbox, "sh", "-c",
   195  			fmt.Sprintf("%s; %s; echo READY; sleep 1000", useradd, passwd)})
   196  		session.WaitWithDefaultTimeout()
   197  		Expect(session).Should(ExitCleanly())
   198  
   199  		session = podmanTest.Podman([]string{"start", "test"})
   200  		session.WaitWithDefaultTimeout()
   201  		Expect(session).Should(ExitCleanly())
   202  
   203  		Expect(WaitContainerReady(podmanTest, "test", "READY", 5, 1)).To(BeTrue())
   204  
   205  		expectedOutput := fmt.Sprintf("%s:x:%s:%s::%s:%s",
   206  			username, uid, gid, homeDir, shell)
   207  
   208  		session = podmanTest.Podman([]string{"exec", "test", "cat", "/etc/passwd"})
   209  		session.WaitWithDefaultTimeout()
   210  		Expect(session).Should(ExitCleanly())
   211  		Expect(session.OutputToString()).To(ContainSubstring(expectedOutput))
   212  
   213  		expectedOutput = "passwd: Note: deleting a password also unlocks the password."
   214  
   215  		session = podmanTest.Podman([]string{"logs", "test"})
   216  		session.WaitWithDefaultTimeout()
   217  		Expect(session).Should(Exit(0))
   218  		Expect(session.ErrorToString()).To(ContainSubstring(expectedOutput))
   219  	})
   220  
   221  	It("podman create --userns=keep-id + podman exec - adding group with groupadd", func() {
   222  		SkipIfNotRootless("only meaningful when run rootless")
   223  		var session *PodmanSessionIntegration
   224  
   225  		var groupName = "testgroup"
   226  		var gid = "1001"
   227  
   228  		groupadd := fmt.Sprintf("groupadd --gid %s %s", gid, groupName)
   229  
   230  		session = podmanTest.Podman([]string{"create", "--log-driver", "k8s-file", "--name", "test", "--userns=keep-id", "--user", "root:root", fedoraToolbox, "sh", "-c",
   231  			fmt.Sprintf("%s; echo READY; sleep 1000", groupadd)})
   232  		session.WaitWithDefaultTimeout()
   233  		Expect(session).Should(ExitCleanly())
   234  
   235  		session = podmanTest.Podman([]string{"start", "test"})
   236  		session.WaitWithDefaultTimeout()
   237  		Expect(session).Should(ExitCleanly())
   238  
   239  		Expect(WaitContainerReady(podmanTest, "test", "READY", 5, 1)).To(BeTrue())
   240  
   241  		session = podmanTest.Podman([]string{"exec", "test", "cat", "/etc/group"})
   242  		session.WaitWithDefaultTimeout()
   243  		Expect(session).Should(ExitCleanly())
   244  		Expect(session.OutputToString()).To(ContainSubstring(groupName))
   245  
   246  		session = podmanTest.Podman([]string{"logs", "test"})
   247  		session.WaitWithDefaultTimeout()
   248  		Expect(session).Should(ExitCleanly())
   249  		Expect(session.OutputToString()).To(ContainSubstring("READY"))
   250  	})
   251  
   252  	It("podman create --userns=keep-id - entrypoint - modifying existing user with usermod - add to new group, change home/shell/uid", func() {
   253  		SkipIfNotRootless("only meaningful when run rootless")
   254  		var session *PodmanSessionIntegration
   255  		var badHomeDir = "/home/badtestuser"
   256  		var badShell = "/bin/sh"
   257  		var badUID = "1001"
   258  		var username = "testuser"
   259  		var homeDir = "/home/testuser"
   260  		var shell = "/bin/bash"
   261  		var uid = "1411"
   262  		var groupName = "testgroup"
   263  		var gid = "1422"
   264  
   265  		// The use of bad* in the name of variables does not imply the invocation
   266  		// of useradd should fail The user is supposed to be created successfully
   267  		// but later his information (uid, home, shell,..) is changed via usermod.
   268  		useradd := fmt.Sprintf("useradd --home-dir %s --shell %s --uid %s %s",
   269  			badHomeDir, badShell, badUID, username)
   270  		groupadd := fmt.Sprintf("groupadd --gid %s %s",
   271  			gid, groupName)
   272  		usermod := fmt.Sprintf("usermod --append --groups wheel --home %s --shell %s --uid %s --gid %s %s",
   273  			homeDir, shell, uid, gid, username)
   274  
   275  		session = podmanTest.Podman([]string{"create", "--log-driver", "k8s-file", "--name", "test", "--userns=keep-id", "--user", "root:root", fedoraToolbox, "sh", "-c",
   276  			fmt.Sprintf("%s; %s; %s; echo READY; sleep 1000", useradd, groupadd, usermod)})
   277  		session.WaitWithDefaultTimeout()
   278  		Expect(session).Should(ExitCleanly())
   279  
   280  		session = podmanTest.Podman([]string{"start", "test"})
   281  		session.WaitWithDefaultTimeout()
   282  		Expect(session).Should(ExitCleanly())
   283  
   284  		Expect(WaitContainerReady(podmanTest, "test", "READY", 5, 1)).To(BeTrue())
   285  
   286  		expectedUser := fmt.Sprintf("%s:x:%s:%s::%s:%s",
   287  			username, uid, gid, homeDir, shell)
   288  
   289  		session = podmanTest.Podman([]string{"exec", "test", "cat", "/etc/passwd"})
   290  		session.WaitWithDefaultTimeout()
   291  		Expect(session).Should(ExitCleanly())
   292  		Expect(session.OutputToString()).To(ContainSubstring(expectedUser))
   293  
   294  		session = podmanTest.Podman([]string{"logs", "test"})
   295  		session.WaitWithDefaultTimeout()
   296  		Expect(session).Should(ExitCleanly())
   297  		Expect(session.OutputToString()).To(ContainSubstring("READY"))
   298  	})
   299  
   300  	It("podman run --privileged --userns=keep-id --user root:root - entrypoint - (bind)mounting", func() {
   301  		SkipIfNotRootless("only meaningful when run rootless")
   302  		var session *PodmanSessionIntegration
   303  
   304  		session = podmanTest.Podman([]string{"run", "--privileged", "--userns=keep-id", "--user", "root:root", ALPINE,
   305  			"mount", "-t", define.TypeTmpfs, define.TypeTmpfs, "/tmp"})
   306  		session.WaitWithDefaultTimeout()
   307  		Expect(session).Should(ExitCleanly())
   308  
   309  		session = podmanTest.Podman([]string{"run", "--privileged", "--userns=keep-id", "--user", "root:root", ALPINE,
   310  			"mount", "--rbind", "/tmp", "/var/tmp"})
   311  		session.WaitWithDefaultTimeout()
   312  		Expect(session).Should(ExitCleanly())
   313  	})
   314  
   315  	It("podman create + start - with all needed switches for create - sleep as entry-point", func() {
   316  		SkipIfNotRootless("only meaningful when run rootless")
   317  		var session *PodmanSessionIntegration
   318  
   319  		// These should be most of the switches that Toolbox uses to create a "toolbox" container
   320  		// https://github.com/containers/toolbox/blob/main/src/cmd/create.go
   321  		session = podmanTest.Podman([]string{"create",
   322  			"--log-driver", "k8s-file",
   323  			"--dns", "none",
   324  			"--hostname", "toolbox",
   325  			"--ipc", "host",
   326  			"--label", "com.github.containers.toolbox=true",
   327  			"--name", "test",
   328  			"--network", "host",
   329  			"--no-hosts",
   330  			"--pid", "host",
   331  			"--privileged",
   332  			"--security-opt", "label=disable",
   333  			"--ulimit", "host",
   334  			"--userns=keep-id",
   335  			"--user", "root:root",
   336  			fedoraToolbox, "sh", "-c", "echo READY; sleep 1000"})
   337  		session.WaitWithDefaultTimeout()
   338  		Expect(session).Should(ExitCleanly())
   339  
   340  		session = podmanTest.Podman([]string{"start", "test"})
   341  		session.WaitWithDefaultTimeout()
   342  		Expect(session).Should(ExitCleanly())
   343  
   344  		Expect(WaitContainerReady(podmanTest, "test", "READY", 5, 1)).To(BeTrue())
   345  
   346  		session = podmanTest.Podman([]string{"logs", "test"})
   347  		session.WaitWithDefaultTimeout()
   348  		Expect(session).Should(ExitCleanly())
   349  		Expect(session.OutputToString()).To(ContainSubstring("READY"))
   350  	})
   351  
   352  	It("podman run --userns=keep-id check $HOME", func() {
   353  		SkipIfNotRootless("only meaningful when run rootless")
   354  		var session *PodmanSessionIntegration
   355  		currentUser, err := user.Current()
   356  		Expect(err).ToNot(HaveOccurred())
   357  
   358  		session = podmanTest.Podman([]string{"run", "-v", fmt.Sprintf("%s:%s", currentUser.HomeDir, currentUser.HomeDir), "--userns=keep-id", fedoraToolbox, "sh", "-c", "echo $HOME"})
   359  		session.WaitWithDefaultTimeout()
   360  		Expect(session).Should(ExitCleanly())
   361  		Expect(session.OutputToString()).To(ContainSubstring(currentUser.HomeDir))
   362  
   363  		if isRootless() {
   364  			location := path.Dir(currentUser.HomeDir)
   365  			volumeArg := fmt.Sprintf("%s:%s", location, location)
   366  			session = podmanTest.Podman([]string{"run",
   367  				"--userns=keep-id",
   368  				"--volume", volumeArg,
   369  				fedoraToolbox, "sh", "-c", "echo $HOME"})
   370  			session.WaitWithDefaultTimeout()
   371  			Expect(session).Should(ExitCleanly())
   372  			Expect(session.OutputToString()).To(ContainSubstring(currentUser.HomeDir))
   373  		}
   374  	})
   375  
   376  })