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