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  })