Files
podman/test/e2e/artifact_mount_test.go
Lewis Roy 23ebb7d94c feat: add Podman artifact support to Go bindings and remote clients
Add the Go bindings implementation necessary to support Artifacts.
Implement the tunnel interface that consumes the Artifacts Go bindings.

With this patch, users of the Podman remote clients will now be able to
manage OCI artifacts via the Podman CLI and Podman machine.

Jira: https://issues.redhat.com/browse/RUN-2714#

Signed-off-by: Lewis Roy <lewis@redhat.com>
2025-08-01 00:10:50 +10:00

249 lines
7.5 KiB
Go

//go:build linux || freebsd
package integration
import (
"os"
"path/filepath"
. "github.com/containers/podman/v5/test/utils"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Podman artifact mount", func() {
It("podman artifact mount single blob", func() {
podmanTest.PodmanExitCleanly("artifact", "pull", ARTIFACT_SINGLE)
const artifactContent = "mRuO9ykak1Q2j"
tests := []struct {
name string
mountOpts string
containerFile string
}{
{
name: "single artifact mount",
mountOpts: "dst=/test",
containerFile: "/test",
},
{
name: "single artifact mount on existing file",
mountOpts: "dst=/etc/os-release",
containerFile: "/etc/os-release",
},
{
name: "single artifact mount with title",
mountOpts: "dst=/tmp,title=testfile",
containerFile: "/tmp/testfile",
},
{
name: "single artifact mount with digest",
mountOpts: "dst=/data,digest=sha256:e9510923578af3632946ecf5ae479c1b5f08b47464e707b5cbab9819272a9752",
containerFile: "/data",
},
{
name: "single artifact mount with name",
mountOpts: "dst=/tmp,name=abcd",
containerFile: "/tmp/abcd",
},
}
for _, tt := range tests {
By(tt.name)
// FIXME: we need https://github.com/containers/container-selinux/pull/360 to fix the selinux access problem, until then disable it.
session := podmanTest.PodmanExitCleanly("run", "--security-opt=label=disable", "--rm", "--mount", "type=artifact,src="+ARTIFACT_SINGLE+","+tt.mountOpts, ALPINE, "cat", tt.containerFile)
Expect(session.OutputToString()).To(Equal(artifactContent))
}
})
It("podman artifact mount multi blob", func() {
podmanTest.PodmanExitCleanly("artifact", "pull", ARTIFACT_MULTI)
podmanTest.PodmanExitCleanly("artifact", "pull", ARTIFACT_MULTI_NO_TITLE)
const (
artifactContent1 = "xuHWedtC0ADST"
artifactContent2 = "tAyZczFlgFsi4"
)
type expectedFiles struct {
file string
content string
}
tests := []struct {
name string
mountOpts string
containerFiles []expectedFiles
}{
{
name: "multi blob with title",
mountOpts: "src=" + ARTIFACT_MULTI + ",dst=/test",
containerFiles: []expectedFiles{
{
file: "/test/test1",
content: artifactContent1,
},
{
file: "/test/test2",
content: artifactContent2,
},
},
},
{
name: "multi blob without title",
mountOpts: "src=" + ARTIFACT_MULTI_NO_TITLE + ",dst=/test",
containerFiles: []expectedFiles{
{
file: "/test/sha256-8257bba28b9d19ac353c4b713b470860278857767935ef7e139afd596cb1bb2d",
content: artifactContent1,
},
{
file: "/test/sha256-63700c54129c6daaafe3a20850079f82d6d658d69de73d6158d81f920c6fbdd7",
content: artifactContent2,
},
},
},
{
name: "multi blob filter by title on non existing file",
mountOpts: "src=" + ARTIFACT_MULTI + ",dst=/test,title=test2",
containerFiles: []expectedFiles{
{
file: "/test",
content: artifactContent2,
},
},
},
{
name: "multi blob filter by title on existing file",
mountOpts: "src=" + ARTIFACT_MULTI + ",dst=/tmp,title=test2",
containerFiles: []expectedFiles{
{
file: "/tmp/test2",
content: artifactContent2,
},
},
},
{
name: "multi blob filter by digest",
mountOpts: "src=" + ARTIFACT_MULTI + ",dst=/tmp,digest=sha256:8257bba28b9d19ac353c4b713b470860278857767935ef7e139afd596cb1bb2d",
containerFiles: []expectedFiles{
{
file: "/tmp/sha256-8257bba28b9d19ac353c4b713b470860278857767935ef7e139afd596cb1bb2d",
content: artifactContent1,
},
},
},
{
name: "multi blob with name",
mountOpts: "src=" + ARTIFACT_MULTI + ",dst=/test,name=myname",
containerFiles: []expectedFiles{
{
file: "/test/myname-0",
content: artifactContent1,
},
{
file: "/test/myname-1",
content: artifactContent2,
},
},
},
}
for _, tt := range tests {
By(tt.name)
// FIXME: we need https://github.com/containers/container-selinux/pull/360 to fix the selinux access problem, until then disable it.
args := []string{"run", "--security-opt=label=disable", "--rm", "--mount", "type=artifact," + tt.mountOpts, ALPINE, "cat"}
for _, f := range tt.containerFiles {
args = append(args, f.file)
}
session := podmanTest.PodmanExitCleanly(args...)
outs := session.OutputToStringArray()
Expect(outs).To(HaveLen(len(tt.containerFiles)))
for i, f := range tt.containerFiles {
Expect(outs[i]).To(Equal(f.content))
}
}
})
It("podman artifact mount remove while in use", func() {
ctrName := "ctr1"
artifactName := "localhost/test"
artifactFileName := "somefile"
artifactFile := filepath.Join(podmanTest.TempDir, artifactFileName)
err := os.WriteFile(artifactFile, []byte("hello world\n"), 0o644)
Expect(err).ToNot(HaveOccurred())
podmanTest.PodmanExitCleanly("artifact", "add", artifactName, artifactFile)
// FIXME: we need https://github.com/containers/container-selinux/pull/360 to fix the selinux access problem, until then disable it.
podmanTest.PodmanExitCleanly("run", "--security-opt=label=disable", "--name", ctrName, "-d", "--mount", "type=artifact,src="+artifactName+",dst=/tmp", ALPINE, "sleep", "100")
podmanTest.PodmanExitCleanly("artifact", "rm", artifactName)
// file must sill be readable after artifact removal
session := podmanTest.PodmanExitCleanly("exec", ctrName, "cat", "/tmp/"+artifactFileName)
Expect(session.OutputToString()).To(Equal("hello world"))
// restart will fail if artifact does not exist
session = podmanTest.Podman([]string{"restart", "-t0", ctrName})
session.WaitWithDefaultTimeout()
Expect(session).To(ExitWithError(125, artifactName+": artifact does not exist"))
// create a artifact with the same name again and add another file to ensure it picks up the changes
artifactFile2Name := "otherfile"
artifactFile2 := filepath.Join(podmanTest.TempDir, artifactFile2Name)
err = os.WriteFile(artifactFile2, []byte("second file"), 0o644)
Expect(err).ToNot(HaveOccurred())
podmanTest.PodmanExitCleanly("artifact", "add", artifactName, artifactFile, artifactFile2)
podmanTest.PodmanExitCleanly("start", ctrName)
session = podmanTest.PodmanExitCleanly("exec", ctrName, "cat", "/tmp/"+artifactFileName, "/tmp/"+artifactFile2Name)
Expect(session.OutputToString()).To(Equal("hello world second file"))
})
It("podman artifact mount dest conflict", func() {
tests := []struct {
name string
mount string
}{
{
name: "bind mount --volume",
mount: "--volume=/tmp:/test",
},
{
name: "overlay mount",
mount: "--volume=/tmp:/test:O",
},
{
name: "named volume",
mount: "--volume=abc:/test:O",
},
{
name: "bind mount --mount type=bind",
mount: "--mount=type=bind,src=/tmp,dst=/test",
},
{
name: "image mount",
mount: "--mount=type=bind,src=someimage,dst=/test",
},
{
name: "tmpfs mount",
mount: "--tmpfs=/test",
},
{
name: "artifact mount",
mount: "--mount=type=artifact,src=abc,dst=/test",
},
}
for _, tt := range tests {
By(tt.name)
session := podmanTest.Podman([]string{"run", "--rm", "--mount", "type=artifact,src=someartifact,dst=/test", tt.mount, ALPINE})
session.WaitWithDefaultTimeout()
Expect(session).To(ExitWithError(125, "/test: duplicate mount destination"))
}
})
})