Allow artifact add to override org.opencontainers.image.title annotation

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
Daniel J Walsh
2025-10-06 13:12:21 -04:00
parent bc571ae542
commit a27fd9bd89
4 changed files with 52 additions and 14 deletions

View File

@ -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)**

View File

@ -27,7 +27,7 @@ const (
artifactRegistry = "quay.io"
artifactRepo = "podman"
artifactImageName = "machine-os"
artifactOriginalName = "org.opencontainers.image.title"
artifactOriginalName = specV1.AnnotationTitle
machineOS = "linux"
)

View File

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

View File

@ -218,13 +218,6 @@ func (as ArtifactStore) Add(ctx context.Context, dest string, artifactBlobs []li
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() {
@ -317,7 +310,17 @@ func (as ArtifactStore) Add(ctx context.Context, dest string, artifactBlobs []li
}
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,
@ -469,6 +472,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