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