diff --git a/docs/source/markdown/podman-artifact-add.1.md.in b/docs/source/markdown/podman-artifact-add.1.md.in index 24d4793827..b3da65febd 100644 --- a/docs/source/markdown/podman-artifact-add.1.md.in +++ b/docs/source/markdown/podman-artifact-add.1.md.in @@ -21,7 +21,8 @@ the `--append` option, the original creation timestamp is preserved. @@option annotation.manifest -Note: Set annotations for each file being added. +Note: Set annotations for each file being added. The annotation "org.opencontainers.image.title" is used +to name the layer when mounted into a container, this title must be unigue for each artifact layer. #### **--append**, **-a** @@ -78,6 +79,13 @@ Add files to an existing OCI artifact $ podman artifact add --append quay.io/myimage/myartifact:latest /home/user/config.yaml ``` +Create artifact with the layer title name being replaced, and then mount into a container. + +``` +podman artifact add --annotation org.opencontainers.image.title=smollm2 quay.io/myreg/smollm2:latest blobs/sha256-4d2396b16114669389d7555c15a1592aad584750310f648edad5ca8c4eccda17 +podman run --mount type=artifact,source=quay.io/myreg/smollm2:latest,destination=/mnt fedora ls -l /mnt +smollm2 +``` ## SEE ALSO **[podman(1)](podman.1.md)**, **[podman-artifact(1)](podman-artifact.1.md)** diff --git a/pkg/libartifact/store/store.go b/pkg/libartifact/store/store.go index a9d1a0a651..99e77cc04a 100644 --- a/pkg/libartifact/store/store.go +++ b/pkg/libartifact/store/store.go @@ -221,13 +221,6 @@ func (as ArtifactStore) Add(ctx context.Context, dest string, artifactBlobs []en return nil, errors.New("append option is not compatible with type option") } - // currently we don't allow override of the filename ; if a user requirement emerges, - // we could seemingly accommodate but broadens possibilities of something bad happening - // for things like `artifact extract` - if _, hasTitle := options.Annotations[specV1.AnnotationTitle]; hasTitle { - return nil, fmt.Errorf("cannot override filename with %s annotation", specV1.AnnotationTitle) - } - locked := true as.lock.Lock() defer func() { @@ -320,7 +313,17 @@ func (as ArtifactStore) Add(ctx context.Context, dest string, artifactBlobs []en } annotations := maps.Clone(options.Annotations) - annotations[specV1.AnnotationTitle] = artifactBlob.FileName + if title, ok := annotations[specV1.AnnotationTitle]; ok { + // Verify a duplicate AnnotationTitle is not in use in a different layer. + for _, layer := range artifactManifest.Layers { + if title == layer.Annotations[specV1.AnnotationTitle] { + return nil, fmt.Errorf("duplicate layers %s labels within an artifact not allowed", specV1.AnnotationTitle) + } + } + } else { + // Only override if the user did not specify the Title + annotations[specV1.AnnotationTitle] = artifactBlob.FileName + } newLayer := specV1.Descriptor{ MediaType: options.FileMIMEType, @@ -472,6 +475,11 @@ func (as ArtifactStore) BlobMountPaths(ctx context.Context, nameOrDigest string, mountPaths := make([]libartTypes.BlobMountPath, 0, len(arty.Manifest.Layers)) for _, l := range arty.Manifest.Layers { title := l.Annotations[specV1.AnnotationTitle] + for _, mp := range mountPaths { + if title == mp.Name { + return nil, fmt.Errorf("annotation %q:%q is used in multiple different layers within artifact", specV1.AnnotationTitle, title) + } + } filename, err := generateArtifactBlobName(title, l.Digest) if err != nil { return nil, err diff --git a/pkg/machine/ocipull/ociartifact.go b/pkg/machine/ocipull/ociartifact.go index 360869f47a..21d58307d2 100644 --- a/pkg/machine/ocipull/ociartifact.go +++ b/pkg/machine/ocipull/ociartifact.go @@ -27,7 +27,7 @@ const ( artifactRegistry = "quay.io" artifactRepo = "podman" artifactImageName = "machine-os" - artifactOriginalName = "org.opencontainers.image.title" + artifactOriginalName = specV1.AnnotationTitle machineOS = "linux" ) diff --git a/test/e2e/artifact_test.go b/test/e2e/artifact_test.go index ce5e0f9fcb..1be21999f7 100644 --- a/test/e2e/artifact_test.go +++ b/test/e2e/artifact_test.go @@ -14,6 +14,7 @@ import ( "github.com/containers/podman/v5/utils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + imgspec "github.com/opencontainers/image-spec/specs-go/v1" ) const ( @@ -99,8 +100,12 @@ var _ = Describe("Podman artifact", func() { It("podman artifact add with options", func() { yamlType := "text/yaml" artifact1Name := "localhost/test/artifact1" + artifact2Name := "localhost/test/artifact2" + artifact3Name := "localhost/test/artifact3" artifact1File, err := createArtifactFile(1024) Expect(err).ToNot(HaveOccurred()) + artifact2File, err := createArtifactFile(1024) + Expect(err).ToNot(HaveOccurred()) artifactType := "octet/foobar" annotation1 := "color=blue" @@ -115,9 +120,26 @@ var _ = Describe("Podman artifact", func() { Expect(a.Manifest.Layers[0].Annotations["flavor"]).To(Equal("lemon")) Expect(a.Manifest.Layers[0].MediaType).To(Equal(yamlType)) - failSession := podmanTest.Podman([]string{"artifact", "add", "--annotation", "org.opencontainers.image.title=foobar", "foobar", artifact1File}) + title := RandomString(12) + annotation3 := fmt.Sprintf("%s=%s", imgspec.AnnotationTitle, title) + podmanTest.PodmanExitCleanly("artifact", "add", "--annotation", annotation3, artifact2Name, artifact1File) + a = podmanTest.InspectArtifact(artifact2Name) + Expect(a.Manifest.Layers[0].Annotations[imgspec.AnnotationTitle]).To(Equal(title)) + + failSession := podmanTest.Podman([]string{"artifact", "add", "--append", "--annotation", annotation3, artifact2Name, artifact2File}) failSession.WaitWithDefaultTimeout() - Expect(failSession).Should(ExitWithError(125, "Error: cannot override filename with org.opencontainers.image.title annotation")) + Expect(failSession).Should(ExitWithError(125, "Error: duplicate layers org.opencontainers.image.title labels within an artifact not allowed")) + + title = RandomString(12) + annotation3 = fmt.Sprintf("%s=%s", imgspec.AnnotationTitle, title) + podmanTest.PodmanExitCleanly("artifact", "add", "--append", "--annotation", annotation3, artifact2Name, artifact2File) + a = podmanTest.InspectArtifact(artifact2Name) + Expect(a.Manifest.Layers[1].Annotations[imgspec.AnnotationTitle]).To(Equal(title)) + + failSession = podmanTest.Podman([]string{"artifact", "add", "--annotation", annotation3, artifact3Name, artifact1File, artifact2File}) + failSession.WaitWithDefaultTimeout() + Expect(failSession).Should(ExitWithError(125, "Error: duplicate layers org.opencontainers.image.title labels within an artifact not allowed")) + }) It("podman artifact add multiple", func() { @@ -480,9 +502,9 @@ var _ = Describe("Podman artifact", func() { Expect(a.Manifest.Layers).To(HaveLen(3)) for _, l := range a.Manifest.Layers { - layersNames[l.Annotations["org.opencontainers.image.title"]] += 1 + layersNames[l.Annotations[imgspec.AnnotationTitle]] += 1 - if l.Annotations["org.opencontainers.image.title"] == filepath.Base(artifact3File) { + if l.Annotations[imgspec.AnnotationTitle] == filepath.Base(artifact3File) { Expect(l.Annotations["color"]).To(Equal("blue")) } else { Expect(l.Annotations).To(HaveLen(1))