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"
14
var _ = Describe("Podman volume plugins", 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())
24
podmanTest.CleanupVolume()
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())
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())
39
It("volume create and remove with running plugin succeeds", func() {
40
podmanTest.AddImageToRWStore(volumeTest)
42
pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes")
43
err := os.Mkdir(pluginStatePath, 0755)
44
Expect(err).ToNot(HaveOccurred())
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.
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())
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())
57
volName := "testVolume1"
58
create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName})
59
create.WaitWithDefaultTimeout()
60
Expect(create).Should(ExitCleanly())
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))
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())
75
remove := podmanTest.Podman([]string{"volume", "rm", volName})
76
remove.WaitWithDefaultTimeout()
77
Expect(remove).Should(ExitCleanly())
79
ls2 := podmanTest.Podman([]string{"volume", "ls", "-q"})
80
ls2.WaitWithDefaultTimeout()
81
Expect(ls2).Should(ExitCleanly())
82
Expect(ls2.OutputToStringArray()).To(BeEmpty())
85
It("volume inspect with running plugin succeeds", func() {
86
podmanTest.AddImageToRWStore(volumeTest)
88
pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes")
89
err := os.Mkdir(pluginStatePath, 0755)
90
Expect(err).ToNot(HaveOccurred())
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())
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())
102
volName := "testVolume1"
103
create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName})
104
create.WaitWithDefaultTimeout()
105
Expect(create).Should(ExitCleanly())
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))
113
It("remove plugin with stopped plugin succeeds", func() {
114
podmanTest.AddImageToRWStore(volumeTest)
116
pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes")
117
err := os.Mkdir(pluginStatePath, 0755)
118
Expect(err).ToNot(HaveOccurred())
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())
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())
131
volName := "testVolume1"
132
create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName})
133
create.WaitWithDefaultTimeout()
134
Expect(create).Should(ExitCleanly())
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))
143
podmanTest.StopContainer(ctrName)
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())
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())
157
It("use plugin in containers", func() {
158
podmanTest.AddImageToRWStore(volumeTest)
160
pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes")
161
err := os.Mkdir(pluginStatePath, 0755)
162
Expect(err).ToNot(HaveOccurred())
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())
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())
174
volName := "testVolume1"
175
create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName})
176
create.WaitWithDefaultTimeout()
177
Expect(create).Should(ExitCleanly())
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())
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"))
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())
198
It("podman volume reload", func() {
199
podmanTest.AddImageToRWStore(volumeTest)
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)
208
pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes")
209
err = os.Mkdir(pluginStatePath, 0755)
210
Expect(err).ToNot(HaveOccurred())
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())
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())
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())
230
vol1 := "vol1-" + stringid.GenerateRandomID()
231
session = podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, vol1})
232
session.WaitWithDefaultTimeout()
233
Expect(session).To(ExitCleanly())
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())
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
247
plugin = podmanTest.Podman([]string{"exec", ctrName, "/usr/local/bin/testvol", "--sock-name", pluginName, "remove", vol1})
248
plugin.WaitWithDefaultTimeout()
249
Expect(plugin).Should(ExitCleanly())
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:
260
Expect(session.ErrorToString()).To(Equal("")) // make sure no errors are shown
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
269
It("volume driver timeouts test", func() {
270
podmanTest.AddImageToRWStore(volumeTest)
272
pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes")
273
err := os.Mkdir(pluginStatePath, 0755)
274
Expect(err).ToNot(HaveOccurred())
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())
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())
286
volName := "testVolume1"
287
create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName})
288
create.WaitWithDefaultTimeout()
289
Expect(create).Should(ExitCleanly())
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"))
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())
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"))