github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/test/e2e/run_volume_test.go (about) 1 // +build !remoteclient 2 3 package integration 4 5 import ( 6 "fmt" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "strings" 11 12 . "github.com/containers/libpod/test/utils" 13 . "github.com/onsi/ginkgo" 14 . "github.com/onsi/gomega" 15 "github.com/onsi/gomega/gexec" 16 ) 17 18 // in-container mount point: using a path that is definitely not present 19 // on the host system might help to uncover some issues. 20 const dest = "/unique/path" 21 22 var _ = Describe("Podman run with volumes", func() { 23 var ( 24 tempdir string 25 err error 26 podmanTest *PodmanTestIntegration 27 ) 28 29 BeforeEach(func() { 30 tempdir, err = CreateTempDirInTempDir() 31 if err != nil { 32 os.Exit(1) 33 } 34 podmanTest = PodmanTestCreate(tempdir) 35 podmanTest.Setup() 36 podmanTest.SeedImages() 37 }) 38 39 AfterEach(func() { 40 podmanTest.Cleanup() 41 f := CurrentGinkgoTestDescription() 42 processTestResult(f) 43 }) 44 45 It("podman run with volume flag", func() { 46 mountPath := filepath.Join(podmanTest.TempDir, "secrets") 47 os.Mkdir(mountPath, 0755) 48 vol := mountPath + ":" + dest 49 50 session := podmanTest.Podman([]string{"run", "--rm", "-v", vol, ALPINE, "grep", dest, "/proc/self/mountinfo"}) 51 session.WaitWithDefaultTimeout() 52 Expect(session.ExitCode()).To(Equal(0)) 53 found, matches := session.GrepString(dest) 54 Expect(found).Should(BeTrue()) 55 Expect(matches[0]).To(ContainSubstring("rw")) 56 57 session = podmanTest.Podman([]string{"run", "--rm", "-v", vol + ":ro", ALPINE, "grep", dest, "/proc/self/mountinfo"}) 58 session.WaitWithDefaultTimeout() 59 Expect(session.ExitCode()).To(Equal(0)) 60 found, matches = session.GrepString(dest) 61 Expect(found).Should(BeTrue()) 62 Expect(matches[0]).To(ContainSubstring("ro")) 63 64 session = podmanTest.Podman([]string{"run", "--rm", "-v", vol + ":shared", ALPINE, "grep", dest, "/proc/self/mountinfo"}) 65 session.WaitWithDefaultTimeout() 66 Expect(session.ExitCode()).To(Equal(0)) 67 found, matches = session.GrepString(dest) 68 Expect(found).Should(BeTrue()) 69 Expect(matches[0]).To(ContainSubstring("rw")) 70 Expect(matches[0]).To(ContainSubstring("shared")) 71 72 // Cached is ignored 73 session = podmanTest.Podman([]string{"run", "--rm", "-v", vol + ":cached", ALPINE, "grep", dest, "/proc/self/mountinfo"}) 74 session.WaitWithDefaultTimeout() 75 Expect(session.ExitCode()).To(Equal(0)) 76 found, matches = session.GrepString(dest) 77 Expect(found).Should(BeTrue()) 78 Expect(matches[0]).To(ContainSubstring("rw")) 79 Expect(matches[0]).To(Not(ContainSubstring("cached"))) 80 81 // Delegated is ignored 82 session = podmanTest.Podman([]string{"run", "--rm", "-v", vol + ":delegated", ALPINE, "grep", dest, "/proc/self/mountinfo"}) 83 session.WaitWithDefaultTimeout() 84 Expect(session.ExitCode()).To(Equal(0)) 85 found, matches = session.GrepString(dest) 86 Expect(found).Should(BeTrue()) 87 Expect(matches[0]).To(ContainSubstring("rw")) 88 Expect(matches[0]).To(Not(ContainSubstring("delegated"))) 89 }) 90 91 It("podman run with --mount flag", func() { 92 if podmanTest.Host.Arch == "ppc64le" { 93 Skip("skip failing test on ppc64le") 94 } 95 mountPath := filepath.Join(podmanTest.TempDir, "secrets") 96 os.Mkdir(mountPath, 0755) 97 mount := "type=bind,src=" + mountPath + ",target=" + dest 98 99 session := podmanTest.Podman([]string{"run", "--rm", "--mount", mount, ALPINE, "grep", dest, "/proc/self/mountinfo"}) 100 session.WaitWithDefaultTimeout() 101 Expect(session.ExitCode()).To(Equal(0)) 102 Expect(session.OutputToString()).To(ContainSubstring(dest + " rw")) 103 104 session = podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",ro", ALPINE, "grep", dest, "/proc/self/mountinfo"}) 105 session.WaitWithDefaultTimeout() 106 Expect(session.ExitCode()).To(Equal(0)) 107 Expect(session.OutputToString()).To(ContainSubstring(dest + " ro")) 108 109 session = podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",shared", ALPINE, "grep", dest, "/proc/self/mountinfo"}) 110 session.WaitWithDefaultTimeout() 111 Expect(session.ExitCode()).To(Equal(0)) 112 found, matches := session.GrepString(dest) 113 Expect(found).Should(BeTrue()) 114 Expect(matches[0]).To(ContainSubstring("rw")) 115 Expect(matches[0]).To(ContainSubstring("shared")) 116 117 session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=tmpfs,target=" + dest, ALPINE, "grep", dest, "/proc/self/mountinfo"}) 118 session.WaitWithDefaultTimeout() 119 Expect(session.ExitCode()).To(Equal(0)) 120 Expect(session.OutputToString()).To(ContainSubstring(dest + " rw,nosuid,nodev,relatime - tmpfs")) 121 122 session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=tmpfs,target=/etc/ssl,tmpcopyup", ALPINE, "ls", "/etc/ssl"}) 123 session.WaitWithDefaultTimeout() 124 Expect(session.ExitCode()).To(Equal(0)) 125 Expect(session.OutputToString()).To(ContainSubstring("certs")) 126 127 session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=tmpfs,target=/etc/ssl,tmpcopyup,notmpcopyup", ALPINE, "ls", "/etc/ssl"}) 128 session.WaitWithDefaultTimeout() 129 Expect(session.ExitCode()).To(Not(Equal(0))) 130 131 session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=bind,src=/tmp,target=/tmp,tmpcopyup", ALPINE, "true"}) 132 session.WaitWithDefaultTimeout() 133 Expect(session.ExitCode()).To(Not(Equal(0))) 134 135 session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=bind,src=/tmp,target=/tmp,notmpcopyup", ALPINE, "true"}) 136 session.WaitWithDefaultTimeout() 137 Expect(session.ExitCode()).To(Not(Equal(0))) 138 139 session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=tmpfs,target=/etc/ssl,notmpcopyup", ALPINE, "ls", "/etc/ssl"}) 140 session.WaitWithDefaultTimeout() 141 Expect(session.ExitCode()).To(Equal(0)) 142 Expect(session.OutputToString()).To(Not(ContainSubstring("certs"))) 143 }) 144 145 It("podman run with conflicting volumes errors", func() { 146 mountPath := filepath.Join(podmanTest.TmpDir, "secrets") 147 os.Mkdir(mountPath, 0755) 148 session := podmanTest.Podman([]string{"run", "-v", mountPath + ":" + dest, "-v", "/tmp" + ":" + dest, ALPINE, "ls"}) 149 session.WaitWithDefaultTimeout() 150 Expect(session.ExitCode()).To(Equal(125)) 151 }) 152 153 It("podman run with conflict between image volume and user mount succeeds", func() { 154 podmanTest.RestoreArtifact(redis) 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 f.Close() 161 Expect(err).To(BeNil()) 162 session := podmanTest.Podman([]string{"run", "-v", fmt.Sprintf("%s:/data", mountPath), redis, "ls", "/data/test1"}) 163 session.WaitWithDefaultTimeout() 164 Expect(session.ExitCode()).To(Equal(0)) 165 }) 166 167 It("podman run with mount flag and boolean options", func() { 168 mountPath := filepath.Join(podmanTest.TempDir, "secrets") 169 os.Mkdir(mountPath, 0755) 170 mount := "type=bind,src=" + mountPath + ",target=" + dest 171 172 session := podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",ro=false", ALPINE, "grep", dest, "/proc/self/mountinfo"}) 173 session.WaitWithDefaultTimeout() 174 Expect(session.ExitCode()).To(Equal(0)) 175 Expect(session.OutputToString()).To(ContainSubstring(dest + " rw")) 176 177 session = podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",ro=true", ALPINE, "grep", dest, "/proc/self/mountinfo"}) 178 session.WaitWithDefaultTimeout() 179 Expect(session.ExitCode()).To(Equal(0)) 180 Expect(session.OutputToString()).To(ContainSubstring(dest + " ro")) 181 182 session = podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",ro=true,rw=false", ALPINE, "grep", dest, "/proc/self/mountinfo"}) 183 session.WaitWithDefaultTimeout() 184 Expect(session).To(ExitWithError()) 185 }) 186 187 It("podman run with volume flag and multiple named volumes", func() { 188 session := podmanTest.Podman([]string{"run", "--rm", "-v", "testvol1:/testvol1", "-v", "testvol2:/testvol2", ALPINE, "grep", "/testvol", "/proc/self/mountinfo"}) 189 session.WaitWithDefaultTimeout() 190 Expect(session.ExitCode()).To(Equal(0)) 191 Expect(session.OutputToString()).To(ContainSubstring("/testvol1")) 192 Expect(session.OutputToString()).To(ContainSubstring("/testvol2")) 193 }) 194 195 It("podman run with volumes and suid/dev/exec options", func() { 196 mountPath := filepath.Join(podmanTest.TempDir, "secrets") 197 os.Mkdir(mountPath, 0755) 198 199 session := podmanTest.Podman([]string{"run", "--rm", "-v", mountPath + ":" + dest + ":suid,dev,exec", ALPINE, "grep", dest, "/proc/self/mountinfo"}) 200 session.WaitWithDefaultTimeout() 201 Expect(session.ExitCode()).To(Equal(0)) 202 found, matches := session.GrepString(dest) 203 Expect(found).Should(BeTrue()) 204 Expect(matches[0]).To(Not(ContainSubstring("noexec"))) 205 Expect(matches[0]).To(Not(ContainSubstring("nodev"))) 206 Expect(matches[0]).To(Not(ContainSubstring("nosuid"))) 207 208 session = podmanTest.Podman([]string{"run", "--rm", "--tmpfs", dest + ":suid,dev,exec", ALPINE, "grep", dest, "/proc/self/mountinfo"}) 209 session.WaitWithDefaultTimeout() 210 Expect(session.ExitCode()).To(Equal(0)) 211 found, matches = session.GrepString(dest) 212 Expect(found).Should(BeTrue()) 213 Expect(matches[0]).To(Not(ContainSubstring("noexec"))) 214 Expect(matches[0]).To(Not(ContainSubstring("nodev"))) 215 Expect(matches[0]).To(Not(ContainSubstring("nosuid"))) 216 }) 217 218 It("podman run with noexec can't exec", func() { 219 session := podmanTest.Podman([]string{"run", "--rm", "-v", "/bin:/hostbin:noexec", ALPINE, "/hostbin/ls", "/"}) 220 session.WaitWithDefaultTimeout() 221 Expect(session).To(ExitWithError()) 222 }) 223 224 It("podman run with tmpfs named volume mounts and unmounts", func() { 225 SkipIfRootless() 226 volName := "testvol" 227 mkVolume := podmanTest.Podman([]string{"volume", "create", "--opt", "type=tmpfs", "--opt", "device=tmpfs", "--opt", "o=nodev", "testvol"}) 228 mkVolume.WaitWithDefaultTimeout() 229 Expect(mkVolume.ExitCode()).To(Equal(0)) 230 231 // Volume not mounted on create 232 mountCmd1, err := gexec.Start(exec.Command("mount"), GinkgoWriter, GinkgoWriter) 233 Expect(err).To(BeNil()) 234 mountCmd1.Wait(90) 235 Expect(mountCmd1.ExitCode()).To(Equal(0)) 236 os.Stdout.Sync() 237 os.Stderr.Sync() 238 mountOut1 := strings.Join(strings.Fields(fmt.Sprintf("%s", mountCmd1.Out.Contents())), " ") 239 fmt.Printf("Output: %s", mountOut1) 240 Expect(strings.Contains(mountOut1, volName)).To(BeFalse()) 241 242 ctrName := "testctr" 243 podmanSession := podmanTest.Podman([]string{"run", "-d", "--name", ctrName, "-v", fmt.Sprintf("%s:/testvol", volName), ALPINE, "top"}) 244 podmanSession.WaitWithDefaultTimeout() 245 Expect(podmanSession.ExitCode()).To(Equal(0)) 246 247 // Volume now mounted as container is running 248 mountCmd2, err := gexec.Start(exec.Command("mount"), GinkgoWriter, GinkgoWriter) 249 Expect(err).To(BeNil()) 250 mountCmd2.Wait(90) 251 Expect(mountCmd2.ExitCode()).To(Equal(0)) 252 os.Stdout.Sync() 253 os.Stderr.Sync() 254 mountOut2 := strings.Join(strings.Fields(fmt.Sprintf("%s", mountCmd2.Out.Contents())), " ") 255 fmt.Printf("Output: %s", mountOut2) 256 Expect(strings.Contains(mountOut2, volName)).To(BeTrue()) 257 258 // Stop the container to unmount 259 podmanStopSession := podmanTest.Podman([]string{"stop", "--time", "0", ctrName}) 260 podmanStopSession.WaitWithDefaultTimeout() 261 Expect(podmanStopSession.ExitCode()).To(Equal(0)) 262 263 // We have to force cleanup so the unmount happens 264 podmanCleanupSession := podmanTest.Podman([]string{"container", "cleanup", ctrName}) 265 podmanCleanupSession.WaitWithDefaultTimeout() 266 Expect(podmanCleanupSession.ExitCode()).To(Equal(0)) 267 268 // Ensure volume is unmounted 269 mountCmd3, err := gexec.Start(exec.Command("mount"), GinkgoWriter, GinkgoWriter) 270 Expect(err).To(BeNil()) 271 mountCmd3.Wait(90) 272 Expect(mountCmd3.ExitCode()).To(Equal(0)) 273 os.Stdout.Sync() 274 os.Stderr.Sync() 275 mountOut3 := strings.Join(strings.Fields(fmt.Sprintf("%s", mountCmd3.Out.Contents())), " ") 276 fmt.Printf("Output: %s", mountOut3) 277 Expect(strings.Contains(mountOut3, volName)).To(BeFalse()) 278 }) 279 280 It("podman named volume copyup", func() { 281 baselineSession := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", ALPINE, "ls", "/etc/apk/"}) 282 baselineSession.WaitWithDefaultTimeout() 283 Expect(baselineSession.ExitCode()).To(Equal(0)) 284 baselineOutput := baselineSession.OutputToString() 285 286 inlineVolumeSession := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", "-v", "testvol1:/etc/apk", ALPINE, "ls", "/etc/apk/"}) 287 inlineVolumeSession.WaitWithDefaultTimeout() 288 Expect(inlineVolumeSession.ExitCode()).To(Equal(0)) 289 Expect(inlineVolumeSession.OutputToString()).To(Equal(baselineOutput)) 290 291 makeVolumeSession := podmanTest.Podman([]string{"volume", "create", "testvol2"}) 292 makeVolumeSession.WaitWithDefaultTimeout() 293 Expect(makeVolumeSession.ExitCode()).To(Equal(0)) 294 295 separateVolumeSession := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", "-v", "testvol2:/etc/apk", ALPINE, "ls", "/etc/apk/"}) 296 separateVolumeSession.WaitWithDefaultTimeout() 297 Expect(separateVolumeSession.ExitCode()).To(Equal(0)) 298 Expect(separateVolumeSession.OutputToString()).To(Equal(baselineOutput)) 299 }) 300 301 It("podman read-only tmpfs conflict with volume", func() { 302 session := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", "--read-only", "-v", "tmp_volume:" + dest, ALPINE, "touch", dest + "/a"}) 303 session.WaitWithDefaultTimeout() 304 Expect(session.ExitCode()).To(Equal(0)) 305 306 session2 := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", "--read-only", "--tmpfs", dest, ALPINE, "touch", dest + "/a"}) 307 session2.WaitWithDefaultTimeout() 308 Expect(session2.ExitCode()).To(Equal(0)) 309 }) 310 311 It("podman run with anonymous volume", func() { 312 list1 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) 313 list1.WaitWithDefaultTimeout() 314 Expect(list1.ExitCode()).To(Equal(0)) 315 Expect(list1.OutputToString()).To(Equal("")) 316 317 session := podmanTest.Podman([]string{"create", "-v", "/test", ALPINE, "top"}) 318 session.WaitWithDefaultTimeout() 319 Expect(session.ExitCode()).To(Equal(0)) 320 321 list2 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) 322 list2.WaitWithDefaultTimeout() 323 Expect(list2.ExitCode()).To(Equal(0)) 324 arr := list2.OutputToStringArray() 325 Expect(len(arr)).To(Equal(1)) 326 Expect(arr[0]).To(Not(Equal(""))) 327 }) 328 329 It("podman rm -v removes anonymous volume", func() { 330 list1 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) 331 list1.WaitWithDefaultTimeout() 332 Expect(list1.ExitCode()).To(Equal(0)) 333 Expect(list1.OutputToString()).To(Equal("")) 334 335 ctrName := "testctr" 336 session := podmanTest.Podman([]string{"create", "--name", ctrName, "-v", "/test", ALPINE, "top"}) 337 session.WaitWithDefaultTimeout() 338 Expect(session.ExitCode()).To(Equal(0)) 339 340 list2 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) 341 list2.WaitWithDefaultTimeout() 342 Expect(list2.ExitCode()).To(Equal(0)) 343 arr := list2.OutputToStringArray() 344 Expect(len(arr)).To(Equal(1)) 345 Expect(arr[0]).To(Not(Equal(""))) 346 347 remove := podmanTest.Podman([]string{"rm", "-v", ctrName}) 348 remove.WaitWithDefaultTimeout() 349 Expect(remove.ExitCode()).To(Equal(0)) 350 351 list3 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) 352 list3.WaitWithDefaultTimeout() 353 Expect(list3.ExitCode()).To(Equal(0)) 354 Expect(list3.OutputToString()).To(Equal("")) 355 }) 356 357 It("podman rm -v retains named volume", func() { 358 list1 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) 359 list1.WaitWithDefaultTimeout() 360 Expect(list1.ExitCode()).To(Equal(0)) 361 Expect(list1.OutputToString()).To(Equal("")) 362 363 ctrName := "testctr" 364 volName := "testvol" 365 session := podmanTest.Podman([]string{"create", "--name", ctrName, "-v", fmt.Sprintf("%s:/test", volName), ALPINE, "top"}) 366 session.WaitWithDefaultTimeout() 367 Expect(session.ExitCode()).To(Equal(0)) 368 369 list2 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) 370 list2.WaitWithDefaultTimeout() 371 Expect(list2.ExitCode()).To(Equal(0)) 372 arr := list2.OutputToStringArray() 373 Expect(len(arr)).To(Equal(1)) 374 Expect(arr[0]).To(Equal(volName)) 375 376 remove := podmanTest.Podman([]string{"rm", "-v", ctrName}) 377 remove.WaitWithDefaultTimeout() 378 Expect(remove.ExitCode()).To(Equal(0)) 379 380 list3 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) 381 list3.WaitWithDefaultTimeout() 382 Expect(list3.ExitCode()).To(Equal(0)) 383 arr2 := list3.OutputToStringArray() 384 Expect(len(arr2)).To(Equal(1)) 385 Expect(arr2[0]).To(Equal(volName)) 386 }) 387 388 It("podman run image volume is not noexec", func() { 389 session := podmanTest.Podman([]string{"run", "--rm", redis, "grep", "/data", "/proc/self/mountinfo"}) 390 session.WaitWithDefaultTimeout() 391 Expect(session.ExitCode()).To(Equal(0)) 392 Expect(session.OutputToString()).To(Not(ContainSubstring("noexec"))) 393 }) 394 395 It("podman mount with invalid option fails", func() { 396 volName := "testVol" 397 volCreate := podmanTest.Podman([]string{"volume", "create", "--opt", "type=tmpfs", "--opt", "device=tmpfs", "--opt", "o=invalid", volName}) 398 volCreate.WaitWithDefaultTimeout() 399 Expect(volCreate.ExitCode()).To(Equal(0)) 400 401 volMount := podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:/tmp", volName), ALPINE, "ls"}) 402 volMount.WaitWithDefaultTimeout() 403 Expect(volMount.ExitCode()).To(Not(Equal(0))) 404 }) 405 406 It("Podman fix for CVE-2020-1726", func() { 407 volName := "testVol" 408 volCreate := podmanTest.Podman([]string{"volume", "create", volName}) 409 volCreate.WaitWithDefaultTimeout() 410 Expect(volCreate.ExitCode()).To(Equal(0)) 411 412 volPath := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{.Mountpoint}}", volName}) 413 volPath.WaitWithDefaultTimeout() 414 Expect(volPath.ExitCode()).To(Equal(0)) 415 path := volPath.OutputToString() 416 417 fileName := "thisIsATestFile" 418 file, err := os.Create(filepath.Join(path, fileName)) 419 Expect(err).To(BeNil()) 420 defer file.Close() 421 422 runLs := podmanTest.Podman([]string{"run", "-t", "-i", "--rm", "-v", fmt.Sprintf("%v:/etc/ssl", volName), ALPINE, "ls", "-1", "/etc/ssl"}) 423 runLs.WaitWithDefaultTimeout() 424 Expect(runLs.ExitCode()).To(Equal(0)) 425 outputArr := runLs.OutputToStringArray() 426 Expect(len(outputArr)).To(Equal(1)) 427 Expect(strings.Contains(outputArr[0], fileName)).To(BeTrue()) 428 }) 429 430 It("Podman mount over image volume with trailing /", func() { 431 image := "podman-volume-test:trailing" 432 dockerfile := ` 433 FROM alpine:latest 434 VOLUME /test/` 435 podmanTest.BuildImage(dockerfile, image, "false") 436 437 ctrName := "testCtr" 438 create := podmanTest.Podman([]string{"create", "-v", "/tmp:/test", "--name", ctrName, image, "ls"}) 439 create.WaitWithDefaultTimeout() 440 Expect(create.ExitCode()).To(Equal(0)) 441 442 data := podmanTest.InspectContainer(ctrName) 443 Expect(len(data)).To(Equal(1)) 444 Expect(len(data[0].Mounts)).To(Equal(1)) 445 Expect(data[0].Mounts[0].Source).To(Equal("/tmp")) 446 Expect(data[0].Mounts[0].Destination).To(Equal("/test")) 447 }) 448 })