diff options
-rw-r--r-- | test/e2e/config.go | 1 | ||||
-rw-r--r-- | test/e2e/toolbox_test.go | 368 |
2 files changed, 369 insertions, 0 deletions
diff --git a/test/e2e/config.go b/test/e2e/config.go index 49a47c7da..54e39f9d2 100644 --- a/test/e2e/config.go +++ b/test/e2e/config.go @@ -14,6 +14,7 @@ var ( BB = "docker.io/library/busybox:latest" healthcheck = "docker.io/libpod/alpine_healthcheck:latest" ImageCacheDir = "/tmp/podman/imagecachedir" + fedoraToolbox = "registry.fedoraproject.org/f32/fedora-toolbox:latest" // This image has seccomp profiles that blocks all syscalls. // The intention behind blocking all syscalls is to prevent diff --git a/test/e2e/toolbox_test.go b/test/e2e/toolbox_test.go new file mode 100644 index 000000000..6122cee19 --- /dev/null +++ b/test/e2e/toolbox_test.go @@ -0,0 +1,368 @@ +package integration + +/* + toolbox_test.go is under the care of the Toolbox Team. + + The tests are trying to stress parts of Podman that Toolbox[0] needs for + its functionality. + + [0] https://github.com/containers/toolbox + + Info about test cases: + - some tests rely on a certain configuration of a container that is done by + executing several commands in the entry-point of a container. To make + sure the initialization had enough time to be executed, + WaitContainerReady() after the container is started. + + - in several places there's an invocation of 'podman logs' It is there mainly + to ease debugging when a test goes wrong (during the initialization of a + container) but sometimes it is also used in the test case itself. + + Maintainers (Toolbox Team): + - Ondřej Míchal <harrymichal@fedoraproject.org> + - Debarshi Ray <rishi@fedoraproject.org> + + Also available on Freenode IRC on #silverblue or #podman +*/ + +import ( + "fmt" + "os" + "os/exec" + "os/user" + "strconv" + "strings" + "syscall" + + . "github.com/containers/podman/v2/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Toolbox-specific testing", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + podmanTest.SeedImages() + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + processTestResult(f) + }) + + It("podman run --dns=none - allows self-management of /etc/resolv.conf", func() { + var session *PodmanSessionIntegration + + session = podmanTest.Podman([]string{"run", "--dns", "none", ALPINE, "sh", "-c", + "rm -f /etc/resolv.conf; touch -d '1970-01-01 00:02:03' /etc/resolv.conf; stat -c %s:%Y /etc/resolv.conf"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("0:123")) + }) + + It("podman run --no-hosts - allows self-management of /etc/hosts", func() { + var session *PodmanSessionIntegration + + session = podmanTest.Podman([]string{"run", "--no-hosts", ALPINE, "sh", "-c", + "rm -f /etc/hosts; touch -d '1970-01-01 00:02:03' /etc/hosts; stat -c %s:%Y /etc/hosts"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("0:123")) + }) + + It("podman create --ulimit host + podman exec - correctly mirrors hosts ulimits", func() { + if podmanTest.RemoteTest { + Skip("Ulimit check does not work with a remote client") + } + var session *PodmanSessionIntegration + var containerHardLimit int + var rlimit syscall.Rlimit + var err error + + err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlimit) + Expect(err).To(BeNil()) + fmt.Printf("Expected value: %d", rlimit.Max) + + session = podmanTest.Podman([]string{"create", "--name", "test", "--ulimit", "host", ALPINE, + "sleep", "1000"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"start", "test"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"exec", "test", "sh", "-c", + "ulimit -H -n"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + containerHardLimit, err = strconv.Atoi(strings.Trim(session.OutputToString(), "\n")) + Expect(err).To(BeNil()) + Expect(containerHardLimit).To(BeNumerically(">=", rlimit.Max)) + }) + + It("podman create --ipc=host --pid=host + podman exec - correct shared memory limit size", func() { + // Comparison of the size of /dev/shm on the host being equal to the one in + // a container + if podmanTest.RemoteTest { + Skip("Shm size check does not work with a remote client") + } + var session *PodmanSessionIntegration + var cmd *exec.Cmd + var hostShmSize, containerShmSize int + var err error + + // Because Alpine uses busybox, most commands don't offer advanced options + // like "--output" in df. Therefore the value of the field 'Size' (or + // ('1K-blocks') needs to be extracted manually. + cmd = exec.Command("df", "/dev/shm") + res, err := cmd.Output() + Expect(err).To(BeNil()) + lines := strings.SplitN(string(res), "\n", 2) + fields := strings.Fields(lines[len(lines)-1]) + hostShmSize, err = strconv.Atoi(fields[1]) + Expect(err).To(BeNil()) + + session = podmanTest.Podman([]string{"create", "--name", "test", "--ipc=host", "--pid=host", ALPINE, + "sleep", "1000"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"start", "test"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"exec", "test", + "df", "/dev/shm"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + lines = session.OutputToStringArray() + fields = strings.Fields(lines[len(lines)-1]) + containerShmSize, err = strconv.Atoi(fields[1]) + Expect(err).To(BeNil()) + + // In some cases it may happen that the size of /dev/shm is not exactly + // equal. Therefore it's fine if there's a slight tolerance between the + // compared values. + Expect(hostShmSize).To(BeNumerically("~", containerShmSize, 100)) + }) + + It("podman create --userns=keep-id --user root:root - entrypoint - entrypoint is executed as root", func() { + var session *PodmanSessionIntegration + + session = podmanTest.Podman([]string{"run", "--userns=keep-id", "--user", "root:root", ALPINE, + "id"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("uid=0(root) gid=0(root)")) + }) + + It("podman create --userns=keep-id + podman exec - correct names of user and group", func() { + var session *PodmanSessionIntegration + var err error + + currentUser, err := user.Current() + Expect(err).To(BeNil()) + + currentGroup, err := user.LookupGroupId(currentUser.Gid) + Expect(err).To(BeNil()) + + session = podmanTest.Podman([]string{"create", "--name", "test", "--userns=keep-id", ALPINE, + "sleep", "1000"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(err).To(BeNil()) + + session = podmanTest.Podman([]string{"start", "test"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + expectedOutput := fmt.Sprintf("uid=%s(%s) gid=%s(%s)", + currentUser.Uid, currentUser.Username, + currentGroup.Gid, currentGroup.Name) + + session = podmanTest.Podman([]string{"exec", "test", + "id"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(expectedOutput)) + }) + + It("podman create --userns=keep-id - entrypoint - adding user with useradd and then removing their password", func() { + var session *PodmanSessionIntegration + + var username string = "testuser" + var homeDir string = "/home/testuser" + var shell string = "/bin/sh" + var uid string = "1001" + var gid string = "1001" + + useradd := fmt.Sprintf("useradd --home-dir %s --shell %s --uid %s %s", + homeDir, shell, uid, username) + passwd := fmt.Sprintf("passwd --delete %s", username) + + session = podmanTest.Podman([]string{"create", "--name", "test", "--userns=keep-id", "--user", "root:root", fedoraToolbox, "sh", "-c", + fmt.Sprintf("%s; %s; echo READY; sleep 1000", useradd, passwd)}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"start", "test"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + Expect(WaitContainerReady(podmanTest, "test", "READY", 2, 1)).To(BeTrue()) + + expectedOutput := fmt.Sprintf("%s:x:%s:%s::%s:%s", + username, uid, gid, homeDir, shell) + + session = podmanTest.Podman([]string{"exec", "test", "cat", "/etc/passwd"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(expectedOutput)) + + expectedOutput = "passwd: Note: deleting a password also unlocks the password." + + session = podmanTest.Podman([]string{"logs", "test"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(expectedOutput)) + }) + + It("podman create --userns=keep-id + podman exec - adding group with groupadd", func() { + var session *PodmanSessionIntegration + + var groupName string = "testgroup" + var gid string = "1001" + + groupadd := fmt.Sprintf("groupadd --gid %s %s", gid, groupName) + + session = podmanTest.Podman([]string{"create", "--name", "test", "--userns=keep-id", "--user", "root:root", fedoraToolbox, "sh", "-c", + fmt.Sprintf("%s; echo READY; sleep 1000", groupadd)}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"start", "test"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + Expect(WaitContainerReady(podmanTest, "test", "READY", 2, 1)).To(BeTrue()) + + session = podmanTest.Podman([]string{"exec", "test", "cat", "/etc/group"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(groupName)) + + session = podmanTest.Podman([]string{"logs", "test"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("READY")) + }) + + It("podman create --userns=keep-id - entrypoint - modifying existing user with usermod - add to new group, change home/shell/uid", func() { + var session *PodmanSessionIntegration + var badHomeDir string = "/home/badtestuser" + var badShell string = "/bin/sh" + var badUID string = "1001" + var username string = "testuser" + var homeDir string = "/home/testuser" + var shell string = "/bin/bash" + var uid string = "2000" + var groupName string = "testgroup" + var gid string = "2000" + + // The use of bad* in the name of variables does not imply the invocation + // of useradd should fail The user is supposed to be created successfuly + // but later his information (uid, home, shell,..) is changed via usermod. + useradd := fmt.Sprintf("useradd --home-dir %s --shell %s --uid %s %s", + badHomeDir, badShell, badUID, username) + groupadd := fmt.Sprintf("groupadd --gid %s %s", + gid, groupName) + usermod := fmt.Sprintf("usermod --append --groups wheel --home %s --shell %s --uid %s --gid %s %s", + homeDir, shell, uid, gid, username) + + session = podmanTest.Podman([]string{"create", "--name", "test", "--userns=keep-id", "--user", "root:root", fedoraToolbox, "sh", "-c", + fmt.Sprintf("%s; %s; %s; echo READY; sleep 1000", useradd, groupadd, usermod)}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"start", "test"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + Expect(WaitContainerReady(podmanTest, "test", "READY", 2, 1)).To(BeTrue()) + + expectedUser := fmt.Sprintf("%s:x:%s:%s::%s:%s", + username, uid, gid, homeDir, shell) + + session = podmanTest.Podman([]string{"exec", "test", "cat", "/etc/passwd"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(expectedUser)) + + session = podmanTest.Podman([]string{"logs", "test"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("READY")) + }) + + It("podman run --privileged --userns=keep-id --user root:root - entrypoint - (bind)mounting", func() { + var session *PodmanSessionIntegration + + session = podmanTest.Podman([]string{"run", "--privileged", "--userns=keep-id", "--user", "root:root", ALPINE, + "mount", "-t", "tmpfs", "tmpfs", "/tmp"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"run", "--privileged", "--userns=keep-id", "--user", "root:root", ALPINE, + "mount", "--rbind", "/tmp", "/var/tmp"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + + It("podman create + start - with all needed switches for create - sleep as entry-point", func() { + var session *PodmanSessionIntegration + + // These should be most of the switches that Toolbox uses to create a "toolbox" container + // https://github.com/containers/toolbox/blob/master/src/cmd/create.go + session = podmanTest.Podman([]string{"create", + "--dns", "none", + "--hostname", "toolbox", + "--ipc", "host", + "--label", "com.github.containers.toolbox=true", + "--name", "test", + "--network", "host", + "--no-hosts", + "--pid", "host", + "--privileged", + "--security-opt", "label=disable", + "--ulimit", "host", + "--userns=keep-id", + "--user", "root:root", + fedoraToolbox, "sh", "-c", "echo READY; sleep 1000"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"start", "test"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + Expect(WaitContainerReady(podmanTest, "test", "READY", 2, 1)).To(BeTrue()) + + session = podmanTest.Podman([]string{"logs", "test"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("READY")) + }) +}) |