octicon-rss(16/)
You've already forked podman
mirror of
https://github.com/containers/podman.git
synced 2025-11-10 03:46:32 +08:00
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 <pholzing@redhat.com>
260 lines
10 KiB
Go
260 lines
10 KiB
Go
package integration
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"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"
|
|
)
|
|
|
|
var _ = Describe("Podman volume plugins", func() {
|
|
var (
|
|
tempdir string
|
|
err error
|
|
podmanTest *PodmanTestIntegration
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
tempdir, err = CreateTempDirInTempDir()
|
|
if err != nil {
|
|
os.Exit(1)
|
|
}
|
|
podmanTest = PodmanTestCreate(tempdir)
|
|
podmanTest.Setup()
|
|
os.Setenv("CONTAINERS_CONF", "config/containers.conf")
|
|
SkipIfRemote("Volume plugins only supported as local")
|
|
SkipIfRootless("Root is required for volume plugin testing")
|
|
err = os.MkdirAll("/run/docker/plugins", 0755)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
})
|
|
|
|
AfterEach(func() {
|
|
podmanTest.CleanupVolume()
|
|
f := CurrentGinkgoTestDescription()
|
|
processTestResult(f)
|
|
os.Unsetenv("CONTAINERS_CONF")
|
|
})
|
|
|
|
It("volume create with nonexistent plugin errors", func() {
|
|
session := podmanTest.Podman([]string{"volume", "create", "--driver", "notexist", "test_volume_name"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).To(ExitWithError())
|
|
})
|
|
|
|
It("volume create with not-running plugin does not error", func() {
|
|
session := podmanTest.Podman([]string{"volume", "create", "--driver", "testvol0", "test_volume_name"})
|
|
session.WaitWithDefaultTimeout()
|
|
Expect(session).To(ExitWithError())
|
|
})
|
|
|
|
It("volume create and remove with running plugin succeeds", func() {
|
|
podmanTest.AddImageToRWStore(volumeTest)
|
|
|
|
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 := "testvol1"
|
|
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})
|
|
plugin.WaitWithDefaultTimeout()
|
|
Expect(plugin).Should(Exit(0))
|
|
|
|
volName := "testVolume1"
|
|
create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName})
|
|
create.WaitWithDefaultTimeout()
|
|
Expect(create).Should(Exit(0))
|
|
|
|
ls1 := podmanTest.Podman([]string{"volume", "ls", "-q"})
|
|
ls1.WaitWithDefaultTimeout()
|
|
Expect(ls1).Should(Exit(0))
|
|
arrOutput := ls1.OutputToStringArray()
|
|
Expect(arrOutput).To(HaveLen(1))
|
|
Expect(arrOutput[0]).To(ContainSubstring(volName))
|
|
|
|
remove := podmanTest.Podman([]string{"volume", "rm", volName})
|
|
remove.WaitWithDefaultTimeout()
|
|
Expect(remove).Should(Exit(0))
|
|
|
|
ls2 := podmanTest.Podman([]string{"volume", "ls", "-q"})
|
|
ls2.WaitWithDefaultTimeout()
|
|
Expect(ls2).Should(Exit(0))
|
|
Expect(ls2.OutputToStringArray()).To(BeEmpty())
|
|
})
|
|
|
|
It("volume inspect with running plugin succeeds", func() {
|
|
podmanTest.AddImageToRWStore(volumeTest)
|
|
|
|
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 := "testvol2"
|
|
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})
|
|
plugin.WaitWithDefaultTimeout()
|
|
Expect(plugin).Should(Exit(0))
|
|
|
|
volName := "testVolume1"
|
|
create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName})
|
|
create.WaitWithDefaultTimeout()
|
|
Expect(create).Should(Exit(0))
|
|
|
|
volInspect := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .Driver }}", volName})
|
|
volInspect.WaitWithDefaultTimeout()
|
|
Expect(volInspect).Should(Exit(0))
|
|
Expect(volInspect.OutputToString()).To(ContainSubstring(pluginName))
|
|
})
|
|
|
|
It("remove plugin with stopped plugin succeeds", func() {
|
|
podmanTest.AddImageToRWStore(volumeTest)
|
|
|
|
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 := "testvol3"
|
|
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))
|
|
|
|
volName := "testVolume1"
|
|
create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName})
|
|
create.WaitWithDefaultTimeout()
|
|
Expect(create).Should(Exit(0))
|
|
|
|
ls1 := podmanTest.Podman([]string{"volume", "ls", "-q"})
|
|
ls1.WaitWithDefaultTimeout()
|
|
Expect(ls1).Should(Exit(0))
|
|
arrOutput := ls1.OutputToStringArray()
|
|
Expect(arrOutput).To(HaveLen(1))
|
|
Expect(arrOutput[0]).To(ContainSubstring(volName))
|
|
|
|
stop := podmanTest.Podman([]string{"stop", "--timeout", "0", ctrName})
|
|
stop.WaitWithDefaultTimeout()
|
|
Expect(stop).Should(Exit(0))
|
|
|
|
// Remove should exit non-zero because missing plugin
|
|
remove := podmanTest.Podman([]string{"volume", "rm", volName})
|
|
remove.WaitWithDefaultTimeout()
|
|
Expect(remove).To(ExitWithError())
|
|
|
|
// But the volume should still be gone
|
|
ls2 := podmanTest.Podman([]string{"volume", "ls", "-q"})
|
|
ls2.WaitWithDefaultTimeout()
|
|
Expect(ls2).Should(Exit(0))
|
|
Expect(ls2.OutputToStringArray()).To(BeEmpty())
|
|
})
|
|
|
|
It("use plugin in containers", func() {
|
|
podmanTest.AddImageToRWStore(volumeTest)
|
|
|
|
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 := "testvol4"
|
|
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})
|
|
plugin.WaitWithDefaultTimeout()
|
|
Expect(plugin).Should(Exit(0))
|
|
|
|
volName := "testVolume1"
|
|
create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName})
|
|
create.WaitWithDefaultTimeout()
|
|
Expect(create).Should(Exit(0))
|
|
|
|
ctr1Name := "ctr1"
|
|
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"})
|
|
ctr1.WaitWithDefaultTimeout()
|
|
Expect(ctr1).Should(Exit(0))
|
|
|
|
ctr2Name := "ctr2"
|
|
ctr2 := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "--name", ctr2Name, "-v", fmt.Sprintf("%v:/test", volName), ALPINE, "cat", "/test/testfile"})
|
|
ctr2.WaitWithDefaultTimeout()
|
|
Expect(ctr2).Should(Exit(0))
|
|
Expect(ctr2.OutputToString()).To(ContainSubstring("helloworld"))
|
|
|
|
// HACK: `volume rm -f` is timing out trying to remove containers using the volume.
|
|
// Solution: remove them manually...
|
|
// TODO: fix this when I get back
|
|
rmAll := podmanTest.Podman([]string{"rm", "-f", ctr2Name, ctr1Name})
|
|
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
|
|
})
|
|
})
|