github.com/containers/podman/v5@v5.1.0-rc1/test/e2e/volume_plugin_test.go (about) 1 package integration 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 8 . "github.com/containers/podman/v5/test/utils" 9 "github.com/containers/storage/pkg/stringid" 10 . "github.com/onsi/ginkgo/v2" 11 . "github.com/onsi/gomega" 12 ) 13 14 var _ = Describe("Podman volume plugins", func() { 15 BeforeEach(func() { 16 os.Setenv("CONTAINERS_CONF", "config/containers.conf") 17 SkipIfRemote("Volume plugins only supported as local") 18 SkipIfRootless("Root is required for volume plugin testing") 19 err = os.MkdirAll("/run/docker/plugins", 0755) 20 Expect(err).ToNot(HaveOccurred()) 21 }) 22 23 AfterEach(func() { 24 podmanTest.CleanupVolume() 25 }) 26 27 It("volume create with nonexistent plugin errors", func() { 28 session := podmanTest.Podman([]string{"volume", "create", "--driver", "notexist", "test_volume_name"}) 29 session.WaitWithDefaultTimeout() 30 Expect(session).To(ExitWithError(125, "volume test_volume_name uses volume plugin notexist but it could not be retrieved: no volume plugin with name notexist available: required plugin missing")) 31 }) 32 33 It("volume create with not-running plugin does not error", func() { 34 session := podmanTest.Podman([]string{"volume", "create", "--driver", "testvol0", "test_volume_name"}) 35 session.WaitWithDefaultTimeout() 36 Expect(session).To(ExitWithError(125, `Error: volume test_volume_name uses volume plugin testvol0 but it could not be retrieved: cannot access plugin testvol0 socket "/run/docker/plugins/testvol0.sock": stat /run/docker/plugins/testvol0.sock: no such file or directory`)) 37 }) 38 39 It("volume create and remove with running plugin succeeds", func() { 40 podmanTest.AddImageToRWStore(volumeTest) 41 42 pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes") 43 err := os.Mkdir(pluginStatePath, 0755) 44 Expect(err).ToNot(HaveOccurred()) 45 46 // Keep this distinct within tests to avoid multiple tests using the same plugin. 47 // This one verifies that the "image" plugin uses a volume plugin, not the "image" driver. 48 pluginName := "image" 49 plugin := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "-v", "/run/docker/plugins:/run/docker/plugins", "-v", fmt.Sprintf("%v:%v", pluginStatePath, pluginStatePath), "-d", volumeTest, "--sock-name", pluginName, "--path", pluginStatePath}) 50 plugin.WaitWithDefaultTimeout() 51 Expect(plugin).Should(ExitCleanly()) 52 53 // Make sure the socket is available (see #17956) 54 err = WaitForFile(fmt.Sprintf("/run/docker/plugins/%s.sock", pluginName)) 55 Expect(err).ToNot(HaveOccurred()) 56 57 volName := "testVolume1" 58 create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName}) 59 create.WaitWithDefaultTimeout() 60 Expect(create).Should(ExitCleanly()) 61 62 ls1 := podmanTest.Podman([]string{"volume", "ls", "-q"}) 63 ls1.WaitWithDefaultTimeout() 64 Expect(ls1).Should(ExitCleanly()) 65 arrOutput := ls1.OutputToStringArray() 66 Expect(arrOutput).To(HaveLen(1)) 67 Expect(arrOutput[0]).To(ContainSubstring(volName)) 68 69 // Verify this is not an image volume. 70 inspect := podmanTest.Podman([]string{"volume", "inspect", volName, "--format", "{{.StorageID}}"}) 71 inspect.WaitWithDefaultTimeout() 72 Expect(inspect).Should(ExitCleanly()) 73 Expect(inspect.OutputToString()).To(BeEmpty()) 74 75 remove := podmanTest.Podman([]string{"volume", "rm", volName}) 76 remove.WaitWithDefaultTimeout() 77 Expect(remove).Should(ExitCleanly()) 78 79 ls2 := podmanTest.Podman([]string{"volume", "ls", "-q"}) 80 ls2.WaitWithDefaultTimeout() 81 Expect(ls2).Should(ExitCleanly()) 82 Expect(ls2.OutputToStringArray()).To(BeEmpty()) 83 }) 84 85 It("volume inspect with running plugin succeeds", func() { 86 podmanTest.AddImageToRWStore(volumeTest) 87 88 pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes") 89 err := os.Mkdir(pluginStatePath, 0755) 90 Expect(err).ToNot(HaveOccurred()) 91 92 // Keep this distinct within tests to avoid multiple tests using the same plugin. 93 pluginName := "testvol2" 94 plugin := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "-v", "/run/docker/plugins:/run/docker/plugins", "-v", fmt.Sprintf("%v:%v", pluginStatePath, pluginStatePath), "-d", volumeTest, "--sock-name", pluginName, "--path", pluginStatePath}) 95 plugin.WaitWithDefaultTimeout() 96 Expect(plugin).Should(ExitCleanly()) 97 98 // Make sure the socket is available (see #17956) 99 err = WaitForFile(fmt.Sprintf("/run/docker/plugins/%s.sock", pluginName)) 100 Expect(err).ToNot(HaveOccurred()) 101 102 volName := "testVolume1" 103 create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName}) 104 create.WaitWithDefaultTimeout() 105 Expect(create).Should(ExitCleanly()) 106 107 volInspect := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .Driver }}", volName}) 108 volInspect.WaitWithDefaultTimeout() 109 Expect(volInspect).Should(ExitCleanly()) 110 Expect(volInspect.OutputToString()).To(ContainSubstring(pluginName)) 111 }) 112 113 It("remove plugin with stopped plugin succeeds", func() { 114 podmanTest.AddImageToRWStore(volumeTest) 115 116 pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes") 117 err := os.Mkdir(pluginStatePath, 0755) 118 Expect(err).ToNot(HaveOccurred()) 119 120 // Keep this distinct within tests to avoid multiple tests using the same plugin. 121 pluginName := "testvol3" 122 ctrName := "pluginCtr" 123 plugin := podmanTest.Podman([]string{"run", "--name", ctrName, "--security-opt", "label=disable", "-v", "/run/docker/plugins:/run/docker/plugins", "-v", fmt.Sprintf("%v:%v", pluginStatePath, pluginStatePath), "-d", volumeTest, "--sock-name", pluginName, "--path", pluginStatePath}) 124 plugin.WaitWithDefaultTimeout() 125 Expect(plugin).Should(ExitCleanly()) 126 127 // Make sure the socket is available (see #17956) 128 err = WaitForFile(fmt.Sprintf("/run/docker/plugins/%s.sock", pluginName)) 129 Expect(err).ToNot(HaveOccurred()) 130 131 volName := "testVolume1" 132 create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName}) 133 create.WaitWithDefaultTimeout() 134 Expect(create).Should(ExitCleanly()) 135 136 ls1 := podmanTest.Podman([]string{"volume", "ls", "-q"}) 137 ls1.WaitWithDefaultTimeout() 138 Expect(ls1).Should(ExitCleanly()) 139 arrOutput := ls1.OutputToStringArray() 140 Expect(arrOutput).To(HaveLen(1)) 141 Expect(arrOutput[0]).To(ContainSubstring(volName)) 142 143 podmanTest.StopContainer(ctrName) 144 145 // Remove should exit non-zero because missing plugin 146 remove := podmanTest.Podman([]string{"volume", "rm", volName}) 147 remove.WaitWithDefaultTimeout() 148 Expect(remove).To(ExitWithError(125, "cannot remove volume testVolume1 from plugin testvol3, but it has been removed from Podman: required plugin missing")) 149 150 // But the volume should still be gone 151 ls2 := podmanTest.Podman([]string{"volume", "ls", "-q"}) 152 ls2.WaitWithDefaultTimeout() 153 Expect(ls2).Should(ExitCleanly()) 154 Expect(ls2.OutputToStringArray()).To(BeEmpty()) 155 }) 156 157 It("use plugin in containers", func() { 158 podmanTest.AddImageToRWStore(volumeTest) 159 160 pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes") 161 err := os.Mkdir(pluginStatePath, 0755) 162 Expect(err).ToNot(HaveOccurred()) 163 164 // Keep this distinct within tests to avoid multiple tests using the same plugin. 165 pluginName := "testvol4" 166 plugin := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "-v", "/run/docker/plugins:/run/docker/plugins", "-v", fmt.Sprintf("%v:%v", pluginStatePath, pluginStatePath), "-d", volumeTest, "--sock-name", pluginName, "--path", pluginStatePath}) 167 plugin.WaitWithDefaultTimeout() 168 Expect(plugin).Should(ExitCleanly()) 169 170 // Make sure the socket is available (see #17956) 171 err = WaitForFile(fmt.Sprintf("/run/docker/plugins/%s.sock", pluginName)) 172 Expect(err).ToNot(HaveOccurred()) 173 174 volName := "testVolume1" 175 create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName}) 176 create.WaitWithDefaultTimeout() 177 Expect(create).Should(ExitCleanly()) 178 179 ctr1Name := "ctr1" 180 ctr1 := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "--name", ctr1Name, "-v", fmt.Sprintf("%v:/test", volName), ALPINE, "sh", "-c", "touch /test/testfile && echo helloworld > /test/testfile"}) 181 ctr1.WaitWithDefaultTimeout() 182 Expect(ctr1).Should(ExitCleanly()) 183 184 ctr2Name := "ctr2" 185 ctr2 := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "--name", ctr2Name, "-v", fmt.Sprintf("%v:/test", volName), ALPINE, "cat", "/test/testfile"}) 186 ctr2.WaitWithDefaultTimeout() 187 Expect(ctr2).Should(ExitCleanly()) 188 Expect(ctr2.OutputToString()).To(ContainSubstring("helloworld")) 189 190 // HACK: `volume rm -f` is timing out trying to remove containers using the volume. 191 // Solution: remove them manually... 192 // TODO: fix this when I get back 193 rmAll := podmanTest.Podman([]string{"rm", "-f", ctr2Name, ctr1Name}) 194 rmAll.WaitWithDefaultTimeout() 195 Expect(rmAll).Should(ExitCleanly()) 196 }) 197 198 It("podman volume reload", func() { 199 podmanTest.AddImageToRWStore(volumeTest) 200 201 confFile := filepath.Join(podmanTest.TempDir, "containers.conf") 202 err := os.WriteFile(confFile, []byte(`[engine] 203 [engine.volume_plugins] 204 testvol5 = "/run/docker/plugins/testvol5.sock"`), 0o644) 205 Expect(err).ToNot(HaveOccurred()) 206 os.Setenv("CONTAINERS_CONF", confFile) 207 208 pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes") 209 err = os.Mkdir(pluginStatePath, 0755) 210 Expect(err).ToNot(HaveOccurred()) 211 212 // Keep this distinct within tests to avoid multiple tests using the same plugin. 213 pluginName := "testvol5" 214 ctrName := "pluginCtr" 215 plugin := podmanTest.Podman([]string{"run", "--name", ctrName, "--security-opt", "label=disable", "-v", "/run/docker/plugins:/run/docker/plugins", 216 "-v", fmt.Sprintf("%v:%v", pluginStatePath, pluginStatePath), "-d", volumeTest, "--sock-name", pluginName, "--path", pluginStatePath}) 217 plugin.WaitWithDefaultTimeout() 218 Expect(plugin).Should(ExitCleanly()) 219 220 // Make sure the socket is available (see #17956) 221 err = WaitForFile(fmt.Sprintf("/run/docker/plugins/%s.sock", pluginName)) 222 Expect(err).ToNot(HaveOccurred()) 223 224 localvol := "local-" + stringid.GenerateRandomID() 225 // create local volume 226 session := podmanTest.Podman([]string{"volume", "create", localvol}) 227 session.WaitWithDefaultTimeout() 228 Expect(session).To(ExitCleanly()) 229 230 vol1 := "vol1-" + stringid.GenerateRandomID() 231 session = podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, vol1}) 232 session.WaitWithDefaultTimeout() 233 Expect(session).To(ExitCleanly()) 234 235 // now create volume in plugin without podman 236 vol2 := "vol2-" + stringid.GenerateRandomID() 237 plugin = podmanTest.Podman([]string{"exec", ctrName, "/usr/local/bin/testvol", "--sock-name", pluginName, "create", vol2}) 238 plugin.WaitWithDefaultTimeout() 239 Expect(plugin).Should(ExitCleanly()) 240 241 session = podmanTest.Podman([]string{"volume", "ls", "-q"}) 242 session.WaitWithDefaultTimeout() 243 Expect(session).To(ExitCleanly()) 244 Expect(session.OutputToStringArray()).To(ContainElements(localvol, vol1)) 245 Expect(session.ErrorToString()).To(Equal("")) // make sure no errors are shown 246 247 plugin = podmanTest.Podman([]string{"exec", ctrName, "/usr/local/bin/testvol", "--sock-name", pluginName, "remove", vol1}) 248 plugin.WaitWithDefaultTimeout() 249 Expect(plugin).Should(ExitCleanly()) 250 251 // now reload volumes from plugins 252 session = podmanTest.Podman([]string{"volume", "reload"}) 253 session.WaitWithDefaultTimeout() 254 Expect(session).To(ExitCleanly()) 255 Expect(string(session.Out.Contents())).To(Equal(fmt.Sprintf(`Added: 256 %s 257 Removed: 258 %s 259 `, vol2, vol1))) 260 Expect(session.ErrorToString()).To(Equal("")) // make sure no errors are shown 261 262 session = podmanTest.Podman([]string{"volume", "ls", "-q"}) 263 session.WaitWithDefaultTimeout() 264 Expect(session).To(ExitCleanly()) 265 Expect(session.OutputToStringArray()).To(ContainElements(localvol, vol2)) 266 Expect(session.ErrorToString()).To(Equal("")) // make no errors are shown 267 }) 268 269 It("volume driver timeouts test", func() { 270 podmanTest.AddImageToRWStore(volumeTest) 271 272 pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes") 273 err := os.Mkdir(pluginStatePath, 0755) 274 Expect(err).ToNot(HaveOccurred()) 275 276 // Keep this distinct within tests to avoid multiple tests using the same plugin. 277 pluginName := "testvol6" 278 plugin := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "-v", "/run/docker/plugins:/run/docker/plugins", "-v", fmt.Sprintf("%v:%v", pluginStatePath, pluginStatePath), "-d", volumeTest, "--sock-name", pluginName, "--path", pluginStatePath}) 279 plugin.WaitWithDefaultTimeout() 280 Expect(plugin).Should(ExitCleanly()) 281 282 // Make sure the socket is available (see #17956) 283 err = WaitForFile(fmt.Sprintf("/run/docker/plugins/%s.sock", pluginName)) 284 Expect(err).ToNot(HaveOccurred()) 285 286 volName := "testVolume1" 287 create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName}) 288 create.WaitWithDefaultTimeout() 289 Expect(create).Should(ExitCleanly()) 290 291 volInspect := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .Timeout }}", volName}) 292 volInspect.WaitWithDefaultTimeout() 293 Expect(volInspect).Should(ExitCleanly()) 294 Expect(volInspect.OutputToString()).To(ContainSubstring("15")) 295 296 volName2 := "testVolume2" 297 create2 := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, "--opt", "o=timeout=3", volName2}) 298 create2.WaitWithDefaultTimeout() 299 Expect(create2).Should(ExitCleanly()) 300 301 volInspect2 := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .Timeout }}", volName2}) 302 volInspect2.WaitWithDefaultTimeout() 303 Expect(volInspect2).Should(ExitCleanly()) 304 Expect(volInspect2.OutputToString()).To(ContainSubstring("3")) 305 }) 306 })