From 2fab7d169b0714574b6620f454c1408bf8097d4f Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Thu, 23 Jun 2022 15:59:58 +0200 Subject: add podman volume reload to sync volume plugins Libpod requires that all volumes are stored in the libpod db. Because volume plugins can be created outside of podman, it will not show all available plugins. This podman volume reload command allows users to sync the libpod db with their external volume plugins. All new volumes from the plugin are also created in the libpod db and when a volume from the db no longer exists it will be removed if possible. There are some problems: - naming conflicts, in this case we only use the first volume we found. This is not deterministic. - race conditions, we have no control over the volume plugins. It is possible that the volumes changed while we run this command. Fixes #14207 Signed-off-by: Paul Holzinger --- test/e2e/config.go | 2 +- test/e2e/volume_plugin_test.go | 68 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/e2e/config.go b/test/e2e/config.go index 2ca8e2a15..fbcc9dfff 100644 --- a/test/e2e/config.go +++ b/test/e2e/config.go @@ -15,7 +15,7 @@ var ( healthcheck = "quay.io/libpod/alpine_healthcheck:latest" ImageCacheDir = "/tmp/podman/imagecachedir" fedoraToolbox = "registry.fedoraproject.org/fedora-toolbox:36" - volumeTest = "quay.io/libpod/volume-plugin-test-img:latest" + volumeTest = "quay.io/libpod/volume-plugin-test-img:20220623" // This image has seccomp profiles that blocks all syscalls. // The intention behind blocking all syscalls is to prevent diff --git a/test/e2e/volume_plugin_test.go b/test/e2e/volume_plugin_test.go index 4700afdb5..b585f8dd8 100644 --- a/test/e2e/volume_plugin_test.go +++ b/test/e2e/volume_plugin_test.go @@ -6,6 +6,7 @@ import ( "path/filepath" . "github.com/containers/podman/v4/test/utils" + "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" . "github.com/onsi/gomega/gexec" @@ -188,4 +189,71 @@ var _ = Describe("Podman volume plugins", func() { rmAll.WaitWithDefaultTimeout() Expect(rmAll).Should(Exit(0)) }) + + It("podman volume reload", func() { + podmanTest.AddImageToRWStore(volumeTest) + + confFile := filepath.Join(podmanTest.TempDir, "containers.conf") + err := os.WriteFile(confFile, []byte(`[engine] +[engine.volume_plugins] +testvol5 = "/run/docker/plugins/testvol5.sock"`), 0o644) + Expect(err).ToNot(HaveOccurred()) + os.Setenv("CONTAINERS_CONF", confFile) + + pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes") + err = os.Mkdir(pluginStatePath, 0755) + Expect(err).ToNot(HaveOccurred()) + + // Keep this distinct within tests to avoid multiple tests using the same plugin. + pluginName := "testvol5" + ctrName := "pluginCtr" + 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}) + plugin.WaitWithDefaultTimeout() + Expect(plugin).Should(Exit(0)) + + localvol := "local-" + stringid.GenerateNonCryptoID() + // create local volume + session := podmanTest.Podman([]string{"volume", "create", localvol}) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + + vol1 := "vol1-" + stringid.GenerateNonCryptoID() + session = podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, vol1}) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + + // now create volume in plugin without podman + vol2 := "vol2-" + stringid.GenerateNonCryptoID() + plugin = podmanTest.Podman([]string{"exec", ctrName, "/usr/local/bin/testvol", "--sock-name", pluginName, "create", vol2}) + plugin.WaitWithDefaultTimeout() + Expect(plugin).Should(Exit(0)) + + session = podmanTest.Podman([]string{"volume", "ls", "-q"}) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + Expect(session.OutputToStringArray()).To(ContainElements(localvol, vol1)) + Expect(session.ErrorToString()).To(Equal("")) // make sure no errors are shown + + plugin = podmanTest.Podman([]string{"exec", ctrName, "/usr/local/bin/testvol", "--sock-name", pluginName, "remove", vol1}) + plugin.WaitWithDefaultTimeout() + Expect(plugin).Should(Exit(0)) + + // now reload volumes from plugins + session = podmanTest.Podman([]string{"volume", "reload"}) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + Expect(string(session.Out.Contents())).To(Equal(fmt.Sprintf(`Added: +%s +Removed: +%s +`, vol2, vol1))) + Expect(session.ErrorToString()).To(Equal("")) // make sure no errors are shown + + session = podmanTest.Podman([]string{"volume", "ls", "-q"}) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + Expect(session.OutputToStringArray()).To(ContainElements(localvol, vol2)) + Expect(session.ErrorToString()).To(Equal("")) // make no errors are shown + }) }) -- cgit v1.2.3-54-g00ecf