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 })