github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/test/e2e/run_volume_test.go (about) 1 package integration 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "os/exec" 8 "os/user" 9 "path/filepath" 10 "strings" 11 12 "github.com/hanks177/podman/v4/pkg/rootless" 13 . "github.com/hanks177/podman/v4/test/utils" 14 . "github.com/onsi/ginkgo" 15 . "github.com/onsi/gomega" 16 . "github.com/onsi/gomega/gexec" 17 ) 18 19 // in-container mount point: using a path that is definitely not present 20 // on the host system might help to uncover some issues. 21 const dest = "/unique/path" 22 23 var _ = Describe("Podman run with volumes", func() { 24 var ( 25 tempdir string 26 err error 27 podmanTest *PodmanTestIntegration 28 ) 29 30 BeforeEach(func() { 31 tempdir, err = CreateTempDirInTempDir() 32 if err != nil { 33 os.Exit(1) 34 } 35 podmanTest = PodmanTestCreate(tempdir) 36 podmanTest.Setup() 37 }) 38 39 AfterEach(func() { 40 podmanTest.Cleanup() 41 f := CurrentGinkgoTestDescription() 42 processTestResult(f) 43 }) 44 45 // Returns the /proc/self/mountinfo line for a given mount point 46 getMountInfo := func(volume string) []string { 47 containerDir := strings.SplitN(volume, ":", 3)[1] 48 session := podmanTest.Podman([]string{"run", "--rm", "-v", volume, ALPINE, "awk", fmt.Sprintf(`$5 == "%s" { print }`, containerDir), "/proc/self/mountinfo"}) 49 session.WaitWithDefaultTimeout() 50 Expect(session).Should(Exit(0)) 51 Expect(session.OutputToString()).To(ContainSubstring(containerDir), "mount point not found in /proc/self/mountinfo") 52 return strings.Fields(session.OutputToString()) 53 } 54 55 It("podman run with volume flag", func() { 56 mountPath := filepath.Join(podmanTest.TempDir, "secrets") 57 err = os.Mkdir(mountPath, 0755) 58 Expect(err).ToNot(HaveOccurred()) 59 vol := mountPath + ":" + dest 60 61 // [5] is flags 62 Expect(getMountInfo(vol)[5]).To(ContainSubstring("rw")) 63 Expect(getMountInfo(vol + ":ro")[5]).To(ContainSubstring("ro")) 64 65 mountinfo := getMountInfo(vol + ":shared") 66 Expect(mountinfo[5]).To(ContainSubstring("rw")) 67 Expect(mountinfo[6]).To(ContainSubstring("shared")) 68 69 // Cached is ignored 70 mountinfo = getMountInfo(vol + ":cached") 71 Expect(mountinfo[5]).To(ContainSubstring("rw")) 72 Expect(mountinfo).To(Not(ContainElement(ContainSubstring("cached")))) 73 74 // Delegated is ignored 75 mountinfo = getMountInfo(vol + ":delegated") 76 Expect(mountinfo[5]).To(ContainSubstring("rw")) 77 Expect(mountinfo).To(Not(ContainElement(ContainSubstring("delegated")))) 78 }) 79 80 It("podman run with --mount flag", func() { 81 if podmanTest.Host.Arch == "ppc64le" { 82 Skip("skip failing test on ppc64le") 83 } 84 mountPath := filepath.Join(podmanTest.TempDir, "secrets") 85 err = os.Mkdir(mountPath, 0755) 86 Expect(err).ToNot(HaveOccurred()) 87 mount := "type=bind,src=" + mountPath + ",target=" + dest 88 89 session := podmanTest.Podman([]string{"run", "--rm", "--mount", mount, ALPINE, "grep", dest, "/proc/self/mountinfo"}) 90 session.WaitWithDefaultTimeout() 91 Expect(session).Should(Exit(0)) 92 Expect(session.OutputToString()).To(ContainSubstring(dest + " rw")) 93 94 session = podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",ro", ALPINE, "grep", dest, "/proc/self/mountinfo"}) 95 session.WaitWithDefaultTimeout() 96 Expect(session).Should(Exit(0)) 97 Expect(session.OutputToString()).To(ContainSubstring(dest + " ro")) 98 99 session = podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",readonly", ALPINE, "grep", dest, "/proc/self/mountinfo"}) 100 session.WaitWithDefaultTimeout() 101 Expect(session).Should(Exit(0)) 102 Expect(session.OutputToString()).To(ContainSubstring(dest + " ro")) 103 104 session = podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",consistency=delegated,shared", ALPINE, "grep", dest, "/proc/self/mountinfo"}) 105 session.WaitWithDefaultTimeout() 106 Expect(session).Should(Exit(0)) 107 Expect(session.OutputToString()).To(ContainSubstring("rw")) 108 Expect(session.OutputToString()).To(ContainSubstring("shared")) 109 110 session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=tmpfs,target=" + dest, ALPINE, "grep", dest, "/proc/self/mountinfo"}) 111 session.WaitWithDefaultTimeout() 112 Expect(session).Should(Exit(0)) 113 Expect(session.OutputToString()).To(ContainSubstring(dest + " rw,nosuid,nodev,relatime - tmpfs")) 114 115 session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=tmpfs,target=/etc/ssl,tmpcopyup", ALPINE, "ls", "/etc/ssl"}) 116 session.WaitWithDefaultTimeout() 117 Expect(session).Should(Exit(0)) 118 Expect(session.OutputToString()).To(ContainSubstring("certs")) 119 120 session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=tmpfs,target=/etc/ssl,tmpcopyup,notmpcopyup", ALPINE, "ls", "/etc/ssl"}) 121 session.WaitWithDefaultTimeout() 122 Expect(session).To(ExitWithError()) 123 124 // test csv escaping 125 session = podmanTest.Podman([]string{"run", "--rm", "--mount=type=tmpfs,tmpfs-size=512M,\"destination=/test,\"", ALPINE, "ls", "/test,"}) 126 session.WaitWithDefaultTimeout() 127 Expect(session).Should(Exit(0)) 128 129 session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=bind,src=/tmp,target=/tmp,tmpcopyup", ALPINE, "true"}) 130 session.WaitWithDefaultTimeout() 131 Expect(session).To(ExitWithError()) 132 133 session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=bind,src=/tmp,target=/tmp,notmpcopyup", ALPINE, "true"}) 134 session.WaitWithDefaultTimeout() 135 Expect(session).To(ExitWithError()) 136 137 session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=tmpfs,target=/etc/ssl,notmpcopyup", ALPINE, "ls", "/etc/ssl"}) 138 session.WaitWithDefaultTimeout() 139 Expect(session).Should(Exit(0)) 140 Expect(session.OutputToString()).To(Not(ContainSubstring("certs"))) 141 }) 142 143 It("podman run with conflicting volumes errors", func() { 144 mountPath := filepath.Join(podmanTest.TmpDir, "secrets") 145 err := os.Mkdir(mountPath, 0755) 146 Expect(err).ToNot(HaveOccurred()) 147 session := podmanTest.Podman([]string{"run", "-v", mountPath + ":" + dest, "-v", "/tmp" + ":" + dest, ALPINE, "ls"}) 148 session.WaitWithDefaultTimeout() 149 Expect(session).Should(Exit(125)) 150 }) 151 152 It("podman run with conflict between image volume and user mount succeeds", func() { 153 err = podmanTest.RestoreArtifact(redis) 154 Expect(err).ToNot(HaveOccurred()) 155 mountPath := filepath.Join(podmanTest.TempDir, "secrets") 156 err := os.Mkdir(mountPath, 0755) 157 Expect(err).To(BeNil()) 158 testFile := filepath.Join(mountPath, "test1") 159 f, err := os.Create(testFile) 160 Expect(err).To(BeNil(), "os.Create(testfile)") 161 f.Close() 162 Expect(err).To(BeNil()) 163 session := podmanTest.Podman([]string{"run", "-v", fmt.Sprintf("%s:/data", mountPath), redis, "ls", "/data/test1"}) 164 session.WaitWithDefaultTimeout() 165 Expect(session).Should(Exit(0)) 166 }) 167 168 It("podman run with mount flag and boolean options", func() { 169 mountPath := filepath.Join(podmanTest.TempDir, "secrets") 170 err := os.Mkdir(mountPath, 0755) 171 Expect(err).ToNot(HaveOccurred()) 172 mount := "type=bind,src=" + mountPath + ",target=" + dest 173 174 session := podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",ro=false", ALPINE, "grep", dest, "/proc/self/mountinfo"}) 175 session.WaitWithDefaultTimeout() 176 Expect(session).Should(Exit(0)) 177 Expect(session.OutputToString()).To(ContainSubstring(dest + " rw")) 178 179 session = podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",ro=true", ALPINE, "grep", dest, "/proc/self/mountinfo"}) 180 session.WaitWithDefaultTimeout() 181 Expect(session).Should(Exit(0)) 182 Expect(session.OutputToString()).To(ContainSubstring(dest + " ro")) 183 184 session = podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",ro=true,rw=false", ALPINE, "grep", dest, "/proc/self/mountinfo"}) 185 session.WaitWithDefaultTimeout() 186 Expect(session).To(ExitWithError()) 187 }) 188 189 It("podman run with volume flag and multiple named volumes", func() { 190 session := podmanTest.Podman([]string{"run", "--rm", "-v", "testvol1:/testvol1", "-v", "testvol2:/testvol2", ALPINE, "grep", "/testvol", "/proc/self/mountinfo"}) 191 session.WaitWithDefaultTimeout() 192 Expect(session).Should(Exit(0)) 193 Expect(session.OutputToString()).To(ContainSubstring("/testvol1")) 194 Expect(session.OutputToString()).To(ContainSubstring("/testvol2")) 195 }) 196 197 It("podman run with volumes and suid/dev/exec options", func() { 198 SkipIfRemote("podman-remote does not support --volumes") 199 mountPath := filepath.Join(podmanTest.TempDir, "secrets") 200 err := os.Mkdir(mountPath, 0755) 201 Expect(err).ToNot(HaveOccurred()) 202 203 session := podmanTest.Podman([]string{"run", "--rm", "-v", mountPath + ":" + dest + ":suid,dev,exec", ALPINE, "grep", dest, "/proc/self/mountinfo"}) 204 session.WaitWithDefaultTimeout() 205 Expect(session).Should(Exit(0)) 206 output := session.OutputToString() 207 Expect(output).To(Not(ContainSubstring("noexec"))) 208 Expect(output).To(Not(ContainSubstring("nodev"))) 209 Expect(output).To(Not(ContainSubstring("nosuid"))) 210 211 session = podmanTest.Podman([]string{"run", "--rm", "--tmpfs", dest + ":suid,dev,exec", ALPINE, "grep", dest, "/proc/self/mountinfo"}) 212 session.WaitWithDefaultTimeout() 213 Expect(session).Should(Exit(0)) 214 output = session.OutputToString() 215 Expect(output).To(Not(ContainSubstring("noexec"))) 216 Expect(output).To(Not(ContainSubstring("nodev"))) 217 Expect(output).To(Not(ContainSubstring("nosuid"))) 218 }) 219 220 // Container should start when workdir is overlay volume 221 It("podman run with volume mounted as overlay and used as workdir", func() { 222 SkipIfRemote("Overlay volumes only work locally") 223 if os.Getenv("container") != "" { 224 Skip("Overlay mounts not supported when running in a container") 225 } 226 if rootless.IsRootless() { 227 if _, err := exec.LookPath("fuse-overlayfs"); err != nil { 228 Skip("Fuse-Overlayfs required for rootless overlay mount test") 229 } 230 } 231 mountPath := filepath.Join(podmanTest.TempDir, "secrets") 232 err := os.Mkdir(mountPath, 0755) 233 Expect(err).ToNot(HaveOccurred()) 234 235 // Container should be able to start with custom overlay volume 236 session := podmanTest.Podman([]string{"run", "--rm", "-v", mountPath + ":/data:O", "--workdir=/data", ALPINE, "echo", "hello"}) 237 session.WaitWithDefaultTimeout() 238 Expect(session).Should(Exit(0)) 239 }) 240 241 It("podman support overlay on named volume", func() { 242 SkipIfRemote("Overlay volumes only work locally") 243 if os.Getenv("container") != "" { 244 Skip("Overlay mounts not supported when running in a container") 245 } 246 if rootless.IsRootless() { 247 if _, err := exec.LookPath("fuse-overlayfs"); err != nil { 248 Skip("Fuse-Overlayfs required for rootless overlay mount test") 249 } 250 } 251 session := podmanTest.Podman([]string{"volume", "create", "myvolume"}) 252 session.WaitWithDefaultTimeout() 253 volName := session.OutputToString() 254 Expect(session).Should(Exit(0)) 255 256 // create file on actual volume 257 session = podmanTest.Podman([]string{"run", "--volume", volName + ":/data", ALPINE, "sh", "-c", "echo hello >> " + "/data/test"}) 258 session.WaitWithDefaultTimeout() 259 Expect(session).Should(Exit(0)) 260 261 // create file on overlay volume 262 session = podmanTest.Podman([]string{"run", "--volume", volName + ":/data:O", ALPINE, "sh", "-c", "echo hello >> " + "/data/overlay"}) 263 session.WaitWithDefaultTimeout() 264 Expect(session).Should(Exit(0)) 265 266 // volume should contain only `test` not `overlay` 267 session = podmanTest.Podman([]string{"run", "--volume", volName + ":/data", ALPINE, "sh", "-c", "ls /data"}) 268 session.WaitWithDefaultTimeout() 269 Expect(session.OutputToString()).To(Not(ContainSubstring("overlay"))) 270 Expect(session.OutputToString()).To(ContainSubstring("test")) 271 272 }) 273 274 It("podman support overlay on named volume with custom upperdir and workdir", func() { 275 SkipIfRemote("Overlay volumes only work locally") 276 if os.Getenv("container") != "" { 277 Skip("Overlay mounts not supported when running in a container") 278 } 279 if rootless.IsRootless() { 280 if _, err := exec.LookPath("fuse-overlayfs"); err != nil { 281 Skip("Fuse-Overlayfs required for rootless overlay mount test") 282 } 283 } 284 285 // create persistent upperdir on host 286 upperDir := filepath.Join(tempdir, "upper") 287 err := os.Mkdir(upperDir, 0755) 288 Expect(err).To(BeNil(), "mkdir "+upperDir) 289 290 // create persistent workdir on host 291 workDir := filepath.Join(tempdir, "work") 292 err = os.Mkdir(workDir, 0755) 293 Expect(err).To(BeNil(), "mkdir "+workDir) 294 295 overlayOpts := fmt.Sprintf("upperdir=%s,workdir=%s", upperDir, workDir) 296 297 session := podmanTest.Podman([]string{"volume", "create", "myvolume"}) 298 session.WaitWithDefaultTimeout() 299 volName := session.OutputToString() 300 Expect(session).Should(Exit(0)) 301 302 // create file on actual volume 303 session = podmanTest.Podman([]string{"run", "--volume", volName + ":/data", ALPINE, "sh", "-c", "echo hello >> " + "/data/test"}) 304 session.WaitWithDefaultTimeout() 305 Expect(session).Should(Exit(0)) 306 307 // create file on overlay volume 308 session = podmanTest.Podman([]string{"run", "--volume", volName + ":/data:O," + overlayOpts, ALPINE, "sh", "-c", "echo hello >> " + "/data/overlay"}) 309 session.WaitWithDefaultTimeout() 310 Expect(session).Should(Exit(0)) 311 312 session = podmanTest.Podman([]string{"run", "--volume", volName + ":/data:O," + overlayOpts, ALPINE, "sh", "-c", "ls /data"}) 313 session.WaitWithDefaultTimeout() 314 // must contain `overlay` file since it should be persistent on specified upper and workdir 315 Expect(session.OutputToString()).To(ContainSubstring("overlay")) 316 // this should be there since `test` was written on actual volume not on any overlay 317 Expect(session.OutputToString()).To(ContainSubstring("test")) 318 319 session = podmanTest.Podman([]string{"run", "--volume", volName + ":/data:O", ALPINE, "sh", "-c", "ls /data"}) 320 session.WaitWithDefaultTimeout() 321 // must not contain `overlay` file which was on custom upper and workdir since we have not specified any upper or workdir 322 Expect(session.OutputToString()).To(Not(ContainSubstring("overlay"))) 323 // this should be there since `test` was written on actual volume not on any overlay 324 Expect(session.OutputToString()).To(ContainSubstring("test")) 325 326 }) 327 328 It("podman support overlay volume with custom upperdir and workdir", func() { 329 SkipIfRemote("Overlay volumes only work locally") 330 if os.Getenv("container") != "" { 331 Skip("Overlay mounts not supported when running in a container") 332 } 333 if rootless.IsRootless() { 334 if _, err := exec.LookPath("fuse-overlayfs"); err != nil { 335 Skip("Fuse-Overlayfs required for rootless overlay mount test") 336 } 337 } 338 339 // Use bindsource instead of named volume 340 bindSource := filepath.Join(tempdir, "bindsource") 341 err := os.Mkdir(bindSource, 0755) 342 Expect(err).To(BeNil(), "mkdir "+bindSource) 343 344 // create persistent upperdir on host 345 upperDir := filepath.Join(tempdir, "upper") 346 err = os.Mkdir(upperDir, 0755) 347 Expect(err).To(BeNil(), "mkdir "+upperDir) 348 349 // create persistent workdir on host 350 workDir := filepath.Join(tempdir, "work") 351 err = os.Mkdir(workDir, 0755) 352 Expect(err).To(BeNil(), "mkdir "+workDir) 353 354 overlayOpts := fmt.Sprintf("upperdir=%s,workdir=%s", upperDir, workDir) 355 356 // create file on overlay volume 357 session := podmanTest.Podman([]string{"run", "--volume", bindSource + ":/data:O," + overlayOpts, ALPINE, "sh", "-c", "echo hello >> " + "/data/overlay"}) 358 session.WaitWithDefaultTimeout() 359 Expect(session).Should(Exit(0)) 360 361 session = podmanTest.Podman([]string{"run", "--volume", bindSource + ":/data:O," + overlayOpts, ALPINE, "sh", "-c", "ls /data"}) 362 session.WaitWithDefaultTimeout() 363 // must contain `overlay` file since it should be persistent on specified upper and workdir 364 Expect(session.OutputToString()).To(ContainSubstring("overlay")) 365 366 session = podmanTest.Podman([]string{"run", "--volume", bindSource + ":/data:O", ALPINE, "sh", "-c", "ls /data"}) 367 session.WaitWithDefaultTimeout() 368 // must not contain `overlay` file which was on custom upper and workdir since we have not specified any upper or workdir 369 Expect(session.OutputToString()).To(Not(ContainSubstring("overlay"))) 370 371 }) 372 373 It("podman run with noexec can't exec", func() { 374 session := podmanTest.Podman([]string{"run", "--rm", "-v", "/bin:/hostbin:noexec", ALPINE, "/hostbin/ls", "/"}) 375 session.WaitWithDefaultTimeout() 376 Expect(session).To(ExitWithError()) 377 }) 378 379 It("podman run with tmpfs named volume mounts and unmounts", func() { 380 SkipIfRootless("rootless podman mount requires you to be in a user namespace") 381 SkipIfRemote("podman-remote does not support --volumes. This test could be simplified to be tested on Remote.") 382 volName := "testvol" 383 mkVolume := podmanTest.Podman([]string{"volume", "create", "--opt", "type=tmpfs", "--opt", "device=tmpfs", "--opt", "o=nodev", "testvol"}) 384 mkVolume.WaitWithDefaultTimeout() 385 Expect(mkVolume).Should(Exit(0)) 386 387 // Volume not mounted on create 388 mountCmd1, err := Start(exec.Command("mount"), GinkgoWriter, GinkgoWriter) 389 Expect(err).To(BeNil()) 390 mountCmd1.Wait(90) 391 Expect(mountCmd1).Should(Exit(0)) 392 os.Stdout.Sync() 393 os.Stderr.Sync() 394 mountOut1 := strings.Join(strings.Fields(string(mountCmd1.Out.Contents())), " ") 395 fmt.Printf("Output: %s", mountOut1) 396 Expect(mountOut1).To(Not(ContainSubstring(volName))) 397 398 ctrName := "testctr" 399 podmanSession := podmanTest.Podman([]string{"run", "-d", "--name", ctrName, "-v", fmt.Sprintf("%s:/testvol", volName), ALPINE, "top"}) 400 podmanSession.WaitWithDefaultTimeout() 401 Expect(podmanSession).Should(Exit(0)) 402 403 // Volume now mounted as container is running 404 mountCmd2, err := Start(exec.Command("mount"), GinkgoWriter, GinkgoWriter) 405 Expect(err).To(BeNil()) 406 mountCmd2.Wait(90) 407 Expect(mountCmd2).Should(Exit(0)) 408 os.Stdout.Sync() 409 os.Stderr.Sync() 410 mountOut2 := strings.Join(strings.Fields(string(mountCmd2.Out.Contents())), " ") 411 fmt.Printf("Output: %s", mountOut2) 412 Expect(mountOut2).To(ContainSubstring(volName)) 413 414 // Stop the container to unmount 415 podmanStopSession := podmanTest.Podman([]string{"stop", "--time", "0", ctrName}) 416 podmanStopSession.WaitWithDefaultTimeout() 417 Expect(podmanStopSession).Should(Exit(0)) 418 419 // We have to force cleanup so the unmount happens 420 podmanCleanupSession := podmanTest.Podman([]string{"container", "cleanup", ctrName}) 421 podmanCleanupSession.WaitWithDefaultTimeout() 422 Expect(podmanCleanupSession).Should(Exit(0)) 423 424 // Ensure volume is unmounted 425 mountCmd3, err := Start(exec.Command("mount"), GinkgoWriter, GinkgoWriter) 426 Expect(err).To(BeNil()) 427 mountCmd3.Wait(90) 428 Expect(mountCmd3).Should(Exit(0)) 429 os.Stdout.Sync() 430 os.Stderr.Sync() 431 mountOut3 := strings.Join(strings.Fields(string(mountCmd3.Out.Contents())), " ") 432 fmt.Printf("Output: %s", mountOut3) 433 Expect(mountOut3).To(Not(ContainSubstring(volName))) 434 }) 435 436 It("podman named volume copyup", func() { 437 baselineSession := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", ALPINE, "ls", "/etc/apk/"}) 438 baselineSession.WaitWithDefaultTimeout() 439 Expect(baselineSession).Should(Exit(0)) 440 baselineOutput := baselineSession.OutputToString() 441 442 inlineVolumeSession := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", "-v", "testvol1:/etc/apk", ALPINE, "ls", "/etc/apk/"}) 443 inlineVolumeSession.WaitWithDefaultTimeout() 444 Expect(inlineVolumeSession).Should(Exit(0)) 445 Expect(inlineVolumeSession.OutputToString()).To(Equal(baselineOutput)) 446 447 makeVolumeSession := podmanTest.Podman([]string{"volume", "create", "testvol2"}) 448 makeVolumeSession.WaitWithDefaultTimeout() 449 Expect(makeVolumeSession).Should(Exit(0)) 450 451 separateVolumeSession := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", "-v", "testvol2:/etc/apk", ALPINE, "ls", "/etc/apk/"}) 452 separateVolumeSession.WaitWithDefaultTimeout() 453 Expect(separateVolumeSession).Should(Exit(0)) 454 Expect(separateVolumeSession.OutputToString()).To(Equal(baselineOutput)) 455 }) 456 457 It("podman named volume copyup symlink", func() { 458 imgName := "testimg" 459 dockerfile := fmt.Sprintf(`FROM %s 460 RUN touch /testfile 461 RUN sh -c "cd /etc/apk && ln -s ../../testfile"`, ALPINE) 462 podmanTest.BuildImage(dockerfile, imgName, "false") 463 464 baselineSession := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", imgName, "ls", "/etc/apk/"}) 465 baselineSession.WaitWithDefaultTimeout() 466 Expect(baselineSession).Should(Exit(0)) 467 baselineOutput := baselineSession.OutputToString() 468 469 outputSession := podmanTest.Podman([]string{"run", "-t", "-i", "-v", "/etc/apk/", imgName, "ls", "/etc/apk/"}) 470 outputSession.WaitWithDefaultTimeout() 471 Expect(outputSession).Should(Exit(0)) 472 Expect(outputSession.OutputToString()).To(Equal(baselineOutput)) 473 }) 474 475 It("podman named volume copyup empty directory", func() { 476 baselineSession := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", ALPINE, "ls", "/srv"}) 477 baselineSession.WaitWithDefaultTimeout() 478 Expect(baselineSession).Should(Exit(0)) 479 baselineOutput := baselineSession.OutputToString() 480 481 outputSession := podmanTest.Podman([]string{"run", "-t", "-i", "-v", "/srv", ALPINE, "ls", "/srv"}) 482 outputSession.WaitWithDefaultTimeout() 483 Expect(outputSession).Should(Exit(0)) 484 Expect(outputSession.OutputToString()).To(Equal(baselineOutput)) 485 }) 486 487 It("podman named volume copyup of /var", func() { 488 baselineSession := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", fedoraMinimal, "ls", "/var"}) 489 baselineSession.WaitWithDefaultTimeout() 490 Expect(baselineSession).Should(Exit(0)) 491 baselineOutput := baselineSession.OutputToString() 492 493 outputSession := podmanTest.Podman([]string{"run", "-t", "-i", "-v", "/var", fedoraMinimal, "ls", "/var"}) 494 outputSession.WaitWithDefaultTimeout() 495 Expect(outputSession).Should(Exit(0)) 496 Expect(outputSession.OutputToString()).To(Equal(baselineOutput)) 497 }) 498 499 It("podman read-only tmpfs conflict with volume", func() { 500 session := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", "--read-only", "-v", "tmp_volume:" + dest, ALPINE, "touch", dest + "/a"}) 501 session.WaitWithDefaultTimeout() 502 Expect(session).Should(Exit(0)) 503 504 session2 := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", "--read-only", "--tmpfs", dest, ALPINE, "touch", dest + "/a"}) 505 session2.WaitWithDefaultTimeout() 506 Expect(session2).Should(Exit(0)) 507 }) 508 509 It("podman run with anonymous volume", func() { 510 list1 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) 511 list1.WaitWithDefaultTimeout() 512 Expect(list1).Should(Exit(0)) 513 Expect(list1.OutputToString()).To(Equal("")) 514 515 session := podmanTest.Podman([]string{"create", "-v", "/test", ALPINE, "top"}) 516 session.WaitWithDefaultTimeout() 517 Expect(session).Should(Exit(0)) 518 519 list2 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) 520 list2.WaitWithDefaultTimeout() 521 Expect(list2).Should(Exit(0)) 522 arr := list2.OutputToStringArray() 523 Expect(arr).To(HaveLen(1)) 524 Expect(arr[0]).To(Not(Equal(""))) 525 }) 526 527 It("podman rm -v removes anonymous volume", func() { 528 list1 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) 529 list1.WaitWithDefaultTimeout() 530 Expect(list1).Should(Exit(0)) 531 Expect(list1.OutputToString()).To(Equal("")) 532 533 ctrName := "testctr" 534 session := podmanTest.Podman([]string{"create", "--name", ctrName, "-v", "/test", ALPINE, "top"}) 535 session.WaitWithDefaultTimeout() 536 Expect(session).Should(Exit(0)) 537 538 list2 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) 539 list2.WaitWithDefaultTimeout() 540 Expect(list2).Should(Exit(0)) 541 arr := list2.OutputToStringArray() 542 Expect(arr).To(HaveLen(1)) 543 Expect(arr[0]).To(Not(Equal(""))) 544 545 remove := podmanTest.Podman([]string{"rm", "-v", ctrName}) 546 remove.WaitWithDefaultTimeout() 547 Expect(remove).Should(Exit(0)) 548 549 list3 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) 550 list3.WaitWithDefaultTimeout() 551 Expect(list3).Should(Exit(0)) 552 Expect(list3.OutputToString()).To(Equal("")) 553 }) 554 555 It("podman rm -v retains named volume", func() { 556 list1 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) 557 list1.WaitWithDefaultTimeout() 558 Expect(list1).Should(Exit(0)) 559 Expect(list1.OutputToString()).To(Equal("")) 560 561 ctrName := "testctr" 562 volName := "testvol" 563 session := podmanTest.Podman([]string{"create", "--name", ctrName, "-v", fmt.Sprintf("%s:/test", volName), ALPINE, "top"}) 564 session.WaitWithDefaultTimeout() 565 Expect(session).Should(Exit(0)) 566 567 list2 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) 568 list2.WaitWithDefaultTimeout() 569 Expect(list2).Should(Exit(0)) 570 arr := list2.OutputToStringArray() 571 Expect(arr).To(HaveLen(1)) 572 Expect(arr[0]).To(Equal(volName)) 573 574 remove := podmanTest.Podman([]string{"rm", "-v", ctrName}) 575 remove.WaitWithDefaultTimeout() 576 Expect(remove).Should(Exit(0)) 577 578 list3 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) 579 list3.WaitWithDefaultTimeout() 580 Expect(list3).Should(Exit(0)) 581 arr2 := list3.OutputToStringArray() 582 Expect(arr2).To(HaveLen(1)) 583 Expect(arr2[0]).To(Equal(volName)) 584 }) 585 586 It("podman run image volume is not noexec", func() { 587 session := podmanTest.Podman([]string{"run", "--rm", redis, "grep", "/data", "/proc/self/mountinfo"}) 588 session.WaitWithDefaultTimeout() 589 Expect(session).Should(Exit(0)) 590 Expect(session.OutputToString()).To(Not(ContainSubstring("noexec"))) 591 }) 592 593 It("podman mount with invalid option fails", func() { 594 volName := "testVol" 595 volCreate := podmanTest.Podman([]string{"volume", "create", "--opt", "type=tmpfs", "--opt", "device=tmpfs", "--opt", "o=invalid", volName}) 596 volCreate.WaitWithDefaultTimeout() 597 Expect(volCreate).Should(Exit(0)) 598 599 volMount := podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:/tmp", volName), ALPINE, "ls"}) 600 volMount.WaitWithDefaultTimeout() 601 Expect(volMount).To(ExitWithError()) 602 }) 603 604 It("Podman fix for CVE-2020-1726", func() { 605 volName := "testVol" 606 volCreate := podmanTest.Podman([]string{"volume", "create", volName}) 607 volCreate.WaitWithDefaultTimeout() 608 Expect(volCreate).Should(Exit(0)) 609 610 volPath := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{.Mountpoint}}", volName}) 611 volPath.WaitWithDefaultTimeout() 612 Expect(volPath).Should(Exit(0)) 613 path := volPath.OutputToString() 614 615 fileName := "thisIsATestFile" 616 file, err := os.Create(filepath.Join(path, fileName)) 617 Expect(err).To(BeNil()) 618 defer file.Close() 619 620 runLs := podmanTest.Podman([]string{"run", "-t", "-i", "--rm", "-v", fmt.Sprintf("%v:/etc/ssl", volName), ALPINE, "ls", "-1", "/etc/ssl"}) 621 runLs.WaitWithDefaultTimeout() 622 Expect(runLs).Should(Exit(0)) 623 outputArr := runLs.OutputToStringArray() 624 Expect(outputArr).To(HaveLen(1)) 625 Expect(outputArr[0]).To(ContainSubstring(fileName)) 626 }) 627 628 It("Podman mount over image volume with trailing /", func() { 629 image := "podman-volume-test:trailing" 630 dockerfile := fmt.Sprintf(`FROM %s 631 VOLUME /test/`, ALPINE) 632 podmanTest.BuildImage(dockerfile, image, "false") 633 634 ctrName := "testCtr" 635 create := podmanTest.Podman([]string{"create", "-v", "/tmp:/test", "--name", ctrName, image, "ls"}) 636 create.WaitWithDefaultTimeout() 637 Expect(create).Should(Exit(0)) 638 639 data := podmanTest.InspectContainer(ctrName) 640 Expect(data).To(HaveLen(1)) 641 Expect(data[0].Mounts).To(HaveLen(1)) 642 Expect(data[0].Mounts[0]).To(HaveField("Source", "/tmp")) 643 Expect(data[0].Mounts[0]).To(HaveField("Destination", "/test")) 644 }) 645 646 It("podman run with overlay volume flag", func() { 647 SkipIfRemote("Overlay volumes only work locally") 648 if os.Getenv("container") != "" { 649 Skip("Overlay mounts not supported when running in a container") 650 } 651 if rootless.IsRootless() { 652 if _, err := exec.LookPath("fuse-overlayfs"); err != nil { 653 Skip("Fuse-Overlayfs required for rootless overlay mount test") 654 } 655 } 656 mountPath := filepath.Join(podmanTest.TempDir, "secrets") 657 err := os.Mkdir(mountPath, 0755) 658 Expect(err).ToNot(HaveOccurred()) 659 testFile := filepath.Join(mountPath, "test1") 660 f, err := os.Create(testFile) 661 Expect(err).To(BeNil(), "os.Create "+testFile) 662 f.Close() 663 664 // Make sure host directory gets mounted in to container as overlay 665 volumeFlag := fmt.Sprintf("%s:/run/test:O", mountPath) 666 Expect(getMountInfo(volumeFlag)[7]).To(Equal("overlay")) 667 668 // Make sure host files show up in the container 669 session := podmanTest.Podman([]string{"run", "--rm", "-v", volumeFlag, ALPINE, "ls", "/run/test/test1"}) 670 session.WaitWithDefaultTimeout() 671 Expect(session).Should(Exit(0)) 672 673 // Make sure modifications in container do not show up on host 674 session = podmanTest.Podman([]string{"run", "--rm", "-v", volumeFlag, ALPINE, "touch", "/run/test/container"}) 675 session.WaitWithDefaultTimeout() 676 Expect(session).Should(Exit(0)) 677 _, err = os.Stat(filepath.Join(mountPath, "container")) 678 Expect(err).To(Not(BeNil())) 679 680 // Make sure modifications in container disappear when container is stopped 681 session = podmanTest.Podman([]string{"create", "-v", fmt.Sprintf("%s:/run/test:O", mountPath), ALPINE, "top"}) 682 session.WaitWithDefaultTimeout() 683 Expect(session).Should(Exit(0)) 684 session = podmanTest.Podman([]string{"start", "-l"}) 685 session.WaitWithDefaultTimeout() 686 Expect(session).Should(Exit(0)) 687 session = podmanTest.Podman([]string{"exec", "-l", "touch", "/run/test/container"}) 688 session.WaitWithDefaultTimeout() 689 Expect(session).Should(Exit(0)) 690 session = podmanTest.Podman([]string{"exec", "-l", "ls", "/run/test/container"}) 691 session.WaitWithDefaultTimeout() 692 Expect(session).Should(Exit(0)) 693 session = podmanTest.Podman([]string{"stop", "-l"}) 694 session.WaitWithDefaultTimeout() 695 Expect(session).Should(Exit(0)) 696 session = podmanTest.Podman([]string{"start", "-l"}) 697 session.WaitWithDefaultTimeout() 698 Expect(session).Should(Exit(0)) 699 session = podmanTest.Podman([]string{"exec", "-l", "ls", "/run/test/container"}) 700 session.WaitWithDefaultTimeout() 701 Expect(session).To(ExitWithError()) 702 }) 703 704 It("overlay volume conflicts with named volume and mounts", func() { 705 mountPath := filepath.Join(podmanTest.TempDir, "secrets") 706 err := os.Mkdir(mountPath, 0755) 707 Expect(err).ToNot(HaveOccurred()) 708 testFile := filepath.Join(mountPath, "test1") 709 f, err := os.Create(testFile) 710 Expect(err).To(BeNil()) 711 f.Close() 712 mountSrc := filepath.Join(podmanTest.TempDir, "vol-test1") 713 err = os.MkdirAll(mountSrc, 0755) 714 Expect(err).To(BeNil()) 715 mountDest := "/run/test" 716 volName := "myvol" 717 718 session := podmanTest.Podman([]string{"volume", "create", volName}) 719 session.WaitWithDefaultTimeout() 720 Expect(session).Should(Exit(0)) 721 722 // overlay and named volume destinations conflict 723 session = podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:%s:O", mountPath, mountDest), "-v", fmt.Sprintf("%s:%s", volName, mountDest), ALPINE}) 724 session.WaitWithDefaultTimeout() 725 Expect(session).To(ExitWithError()) 726 // overlay and bind mount destinations conflict 727 session = podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:%s:O", mountPath, mountDest), "--mount", fmt.Sprintf("type=bind,src=%s,target=%s", mountSrc, mountDest), ALPINE}) 728 session.WaitWithDefaultTimeout() 729 Expect(session).To(ExitWithError()) 730 // overlay and tmpfs mount destinations conflict 731 session = podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:%s:O", mountPath, mountDest), "--mount", fmt.Sprintf("type=tmpfs,target=%s", mountDest), ALPINE}) 732 session.WaitWithDefaultTimeout() 733 Expect(session).To(ExitWithError()) 734 }) 735 736 It("same volume in multiple places does not deadlock", func() { 737 volName := "testVol1" 738 session := podmanTest.Podman([]string{"run", "-t", "-i", "-v", fmt.Sprintf("%s:/test1", volName), "-v", fmt.Sprintf("%s:/test2", volName), "--rm", ALPINE, "sh", "-c", "mount | grep /test"}) 739 session.WaitWithDefaultTimeout() 740 Expect(session).Should(Exit(0)) 741 Expect(session.OutputToStringArray()).To(HaveLen(2)) 742 }) 743 744 It("podman run with --volume and U flag", func() { 745 SkipIfRemote("Overlay volumes only work locally") 746 747 u, err := user.Current() 748 Expect(err).To(BeNil()) 749 name := u.Username 750 if name == "root" { 751 name = "containers" 752 } 753 754 content, err := ioutil.ReadFile("/etc/subuid") 755 if err != nil { 756 Skip("cannot read /etc/subuid") 757 } 758 if !strings.Contains(string(content), name) { 759 Skip("cannot find mappings for the current user") 760 } 761 762 if os.Getenv("container") != "" { 763 Skip("Overlay mounts not supported when running in a container") 764 } 765 if rootless.IsRootless() { 766 if _, err := exec.LookPath("fuse_overlay"); err != nil { 767 Skip("Fuse-Overlayfs required for rootless overlay mount test") 768 } 769 } 770 771 mountPath := filepath.Join(podmanTest.TempDir, "secrets") 772 err = os.Mkdir(mountPath, 0755) 773 Expect(err).ToNot(HaveOccurred()) 774 vol := mountPath + ":" + dest + ":U" 775 776 session := podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "-v", vol, ALPINE, "stat", "-c", "%u:%g", dest}) 777 session.WaitWithDefaultTimeout() 778 Expect(session).Should(Exit(0)) 779 Expect(session.OutputToString()).To(ContainSubstring("888:888")) 780 781 session = podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "--userns", "auto", "-v", vol, ALPINE, "stat", "-c", "%u:%g", dest}) 782 session.WaitWithDefaultTimeout() 783 Expect(session).Should(Exit(0)) 784 Expect(session.OutputToString()).To(ContainSubstring("888:888")) 785 786 vol += ",O" 787 session = podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "--userns", "keep-id", "-v", vol, ALPINE, "stat", "-c", "%u:%g", dest}) 788 session.WaitWithDefaultTimeout() 789 Expect(session).Should(Exit(0)) 790 Expect(session.OutputToString()).To(ContainSubstring("888:888")) 791 }) 792 793 It("podman run with --mount and U flag", func() { 794 u, err := user.Current() 795 Expect(err).To(BeNil()) 796 name := u.Username 797 if name == "root" { 798 name = "containers" 799 } 800 801 content, err := ioutil.ReadFile("/etc/subuid") 802 if err != nil { 803 Skip("cannot read /etc/subuid") 804 } 805 806 if !strings.Contains(string(content), name) { 807 Skip("cannot find mappings for the current user") 808 } 809 810 mountPath := filepath.Join(podmanTest.TempDir, "foo") 811 err = os.Mkdir(mountPath, 0755) 812 Expect(err).ToNot(HaveOccurred()) 813 814 // false bind mount 815 vol := "type=bind,src=" + mountPath + ",dst=" + dest + ",U=false" 816 session := podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "--mount", vol, ALPINE, "stat", "-c", "%u:%g", dest}) 817 session.WaitWithDefaultTimeout() 818 Expect(session).Should(Exit(0)) 819 Expect(session.OutputToString()).ShouldNot(Equal("888:888")) 820 821 // invalid bind mount 822 vol = "type=bind,src=" + mountPath + ",dst=" + dest + ",U=invalid" 823 session = podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "--mount", vol, ALPINE, "stat", "-c", "%u:%g", dest}) 824 session.WaitWithDefaultTimeout() 825 Expect(session).To(ExitWithError()) 826 827 // true bind mount 828 vol = "type=bind,src=" + mountPath + ",dst=" + dest + ",U=true" 829 session = podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "--mount", vol, ALPINE, "stat", "-c", "%u:%g", dest}) 830 session.WaitWithDefaultTimeout() 831 Expect(session).Should(Exit(0)) 832 Expect(session.OutputToString()).Should(Equal("888:888")) 833 834 // tmpfs mount 835 vol = "type=tmpfs," + "dst=" + dest + ",chown" 836 session = podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "--mount", vol, ALPINE, "stat", "-c", "%u:%g", dest}) 837 session.WaitWithDefaultTimeout() 838 Expect(session).Should(Exit(0)) 839 Expect(session.OutputToString()).Should(Equal("888:888")) 840 841 // anonymous volume mount 842 vol = "type=volume," + "dst=" + dest 843 session = podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "--mount", vol, ALPINE, "stat", "-c", "%u:%g", dest}) 844 session.WaitWithDefaultTimeout() 845 Expect(session).Should(Exit(0)) 846 Expect(session.OutputToString()).Should(Equal("888:888")) 847 848 // named volume mount 849 namedVolume := podmanTest.Podman([]string{"volume", "create", "foo"}) 850 namedVolume.WaitWithDefaultTimeout() 851 Expect(namedVolume).Should(Exit(0)) 852 853 vol = "type=volume,src=foo,dst=" + dest + ",chown=true" 854 session = podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "--mount", vol, ALPINE, "stat", "-c", "%u:%g", dest}) 855 session.WaitWithDefaultTimeout() 856 Expect(session).Should(Exit(0)) 857 Expect(session.OutputToString()).Should(Equal("888:888")) 858 }) 859 860 It("podman run with --mount and named volume with driver-opts", func() { 861 // anonymous volume mount with driver opts 862 vol := "type=volume,source=test_vol,dst=/test,volume-opt=type=tmpfs,volume-opt=device=tmpfs,volume-opt=o=nodev" 863 session := podmanTest.Podman([]string{"run", "--rm", "--mount", vol, ALPINE, "echo", "hello"}) 864 session.WaitWithDefaultTimeout() 865 Expect(session).Should(Exit(0)) 866 867 inspectVol := podmanTest.Podman([]string{"volume", "inspect", "test_vol"}) 868 inspectVol.WaitWithDefaultTimeout() 869 Expect(inspectVol).Should(Exit(0)) 870 Expect(inspectVol.OutputToString()).To(ContainSubstring("nodev")) 871 }) 872 873 It("volume permissions after run", func() { 874 imgName := "testimg" 875 dockerfile := fmt.Sprintf(`FROM %s 876 RUN useradd -m testuser -u 1005 877 USER testuser`, fedoraMinimal) 878 podmanTest.BuildImage(dockerfile, imgName, "false") 879 880 testString := "testuser testuser" 881 882 test1 := podmanTest.Podman([]string{"run", "-v", "testvol1:/test", imgName, "bash", "-c", "ls -al /test | grep -v root | grep -v total"}) 883 test1.WaitWithDefaultTimeout() 884 Expect(test1).Should(Exit(0)) 885 Expect(test1.OutputToString()).To(ContainSubstring(testString)) 886 887 volName := "testvol2" 888 vol := podmanTest.Podman([]string{"volume", "create", volName}) 889 vol.WaitWithDefaultTimeout() 890 Expect(vol).Should(Exit(0)) 891 892 test2 := podmanTest.Podman([]string{"run", "-v", fmt.Sprintf("%s:/test", volName), imgName, "bash", "-c", "ls -al /test | grep -v root | grep -v total"}) 893 test2.WaitWithDefaultTimeout() 894 Expect(test2).Should(Exit(0)) 895 Expect(test2.OutputToString()).To(ContainSubstring(testString)) 896 897 }) 898 899 It("podman run with named volume check if we honor permission of target dir", func() { 900 session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "stat", "-c", "%a %Y", "/var/tmp"}) 901 session.WaitWithDefaultTimeout() 902 Expect(session).Should(Exit(0)) 903 perms := session.OutputToString() 904 905 session = podmanTest.Podman([]string{"run", "--rm", "-v", "test:/var/tmp", ALPINE, "stat", "-c", "%a %Y", "/var/tmp"}) 906 session.WaitWithDefaultTimeout() 907 Expect(session).Should(Exit(0)) 908 Expect(session.OutputToString()).To(Equal(perms)) 909 }) 910 911 It("podman volume with uid and gid works", func() { 912 volName := "testVol" 913 volCreate := podmanTest.Podman([]string{"volume", "create", "--opt", "o=uid=1000", volName}) 914 volCreate.WaitWithDefaultTimeout() 915 Expect(volCreate).Should(Exit(0)) 916 917 volMount := podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:/test", volName), ALPINE, "stat", "-c", "%u", "/test"}) 918 volMount.WaitWithDefaultTimeout() 919 Expect(volMount).Should(Exit(0)) 920 Expect(volMount.OutputToString()).To(Equal("1000")) 921 922 volName = "testVol2" 923 volCreate = podmanTest.Podman([]string{"volume", "create", "--opt", "o=gid=1000", volName}) 924 volCreate.WaitWithDefaultTimeout() 925 Expect(volCreate).Should(Exit(0)) 926 927 volMount = podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:/test", volName), ALPINE, "stat", "-c", "%g", "/test"}) 928 volMount.WaitWithDefaultTimeout() 929 Expect(volMount).Should(Exit(0)) 930 Expect(volMount.OutputToString()).To(Equal("1000")) 931 932 volName = "testVol3" 933 volCreate = podmanTest.Podman([]string{"volume", "create", "--opt", "o=uid=1000,gid=1000", volName}) 934 volCreate.WaitWithDefaultTimeout() 935 Expect(volCreate).Should(Exit(0)) 936 937 volMount = podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:/test", volName), ALPINE, "stat", "-c", "%u:%g", "/test"}) 938 volMount.WaitWithDefaultTimeout() 939 Expect(volMount).Should(Exit(0)) 940 Expect(volMount.OutputToString()).To(Equal("1000:1000")) 941 }) 942 })