Merge pull request #27238 from rhatdan/annotation

Allow artifact add to override org.opencontainers.image.title annotation
This commit is contained in:
openshift-merge-bot[bot]
2025-10-17 13:04:57 +00:00
committed by GitHub
7 changed files with 56 additions and 18 deletions

View File

@@ -21,7 +21,8 @@ the `--append` option, the original creation timestamp is preserved.
@@option annotation.manifest @@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** #### **--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 $ 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 ## SEE ALSO
**[podman(1)](podman.1.md)**, **[podman-artifact(1)](podman-artifact.1.md)** **[podman(1)](podman.1.md)**, **[podman-artifact(1)](podman-artifact.1.md)**

2
go.mod
View File

@@ -65,7 +65,7 @@ require (
github.com/vbauerster/mpb/v8 v8.10.2 github.com/vbauerster/mpb/v8 v8.10.2
github.com/vishvananda/netlink v1.3.1 github.com/vishvananda/netlink v1.3.1
go.etcd.io/bbolt v1.4.3 go.etcd.io/bbolt v1.4.3
go.podman.io/common v0.65.1-0.20251016133615-aa970d2c7532 go.podman.io/common v0.65.1-0.20251016162239-c4c5e00ad22d
go.podman.io/image/v5 v5.37.1-0.20251016133615-aa970d2c7532 go.podman.io/image/v5 v5.37.1-0.20251016133615-aa970d2c7532
go.podman.io/storage v1.60.1-0.20251016133615-aa970d2c7532 go.podman.io/storage v1.60.1-0.20251016133615-aa970d2c7532
golang.org/x/crypto v0.43.0 golang.org/x/crypto v0.43.0

4
go.sum
View File

@@ -471,8 +471,8 @@ go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKr
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
go.podman.io/common v0.65.1-0.20251016133615-aa970d2c7532 h1:j83jJWTMND24j2fk0G+waJiKF3A/bMHcQxPkkJA9k4Q= go.podman.io/common v0.65.1-0.20251016162239-c4c5e00ad22d h1:xk2iM/F/6UTuPD3+MNOqZvju0xYQ14IpvegxQ1sC464=
go.podman.io/common v0.65.1-0.20251016133615-aa970d2c7532/go.mod h1:kv0yXx/yrT60lUcVb86hezMGz/lXmyx3epvdFlM9du4= go.podman.io/common v0.65.1-0.20251016162239-c4c5e00ad22d/go.mod h1:kv0yXx/yrT60lUcVb86hezMGz/lXmyx3epvdFlM9du4=
go.podman.io/image/v5 v5.37.1-0.20251016133615-aa970d2c7532 h1:J7qB0n2DLY9hLCS9z3eubDuBrl6GOiz1iVMufhTRs3k= go.podman.io/image/v5 v5.37.1-0.20251016133615-aa970d2c7532 h1:J7qB0n2DLY9hLCS9z3eubDuBrl6GOiz1iVMufhTRs3k=
go.podman.io/image/v5 v5.37.1-0.20251016133615-aa970d2c7532/go.mod h1:rzI7vFUroTWKtAAVuYCoT2wy8LbcepZgEuhzzRzfui4= go.podman.io/image/v5 v5.37.1-0.20251016133615-aa970d2c7532/go.mod h1:rzI7vFUroTWKtAAVuYCoT2wy8LbcepZgEuhzzRzfui4=
go.podman.io/storage v1.60.1-0.20251016133615-aa970d2c7532 h1:3+RVXZET/LnyhXdQnpIVvjuNs04Jz3IJtC9T++pwqpI= go.podman.io/storage v1.60.1-0.20251016133615-aa970d2c7532 h1:3+RVXZET/LnyhXdQnpIVvjuNs04Jz3IJtC9T++pwqpI=

View File

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

View File

@@ -14,6 +14,7 @@ import (
"github.com/containers/podman/v5/utils" "github.com/containers/podman/v5/utils"
. "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
imgspec "github.com/opencontainers/image-spec/specs-go/v1"
) )
const ( const (
@@ -99,8 +100,12 @@ var _ = Describe("Podman artifact", func() {
It("podman artifact add with options", func() { It("podman artifact add with options", func() {
yamlType := "text/yaml" yamlType := "text/yaml"
artifact1Name := "localhost/test/artifact1" artifact1Name := "localhost/test/artifact1"
artifact2Name := "localhost/test/artifact2"
artifact3Name := "localhost/test/artifact3"
artifact1File, err := createArtifactFile(1024) artifact1File, err := createArtifactFile(1024)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
artifact2File, err := createArtifactFile(1024)
Expect(err).ToNot(HaveOccurred())
artifactType := "octet/foobar" artifactType := "octet/foobar"
annotation1 := "color=blue" 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].Annotations["flavor"]).To(Equal("lemon"))
Expect(a.Manifest.Layers[0].MediaType).To(Equal(yamlType)) 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() 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() { It("podman artifact add multiple", func() {
@@ -480,9 +502,9 @@ var _ = Describe("Podman artifact", func() {
Expect(a.Manifest.Layers).To(HaveLen(3)) Expect(a.Manifest.Layers).To(HaveLen(3))
for _, l := range a.Manifest.Layers { 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")) Expect(l.Annotations["color"]).To(Equal("blue"))
} else { } else {
Expect(l.Annotations).To(HaveLen(1)) 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") 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 locked := true
as.lock.Lock() as.lock.Lock()
defer func() { defer func() {
@@ -317,7 +310,17 @@ func (as ArtifactStore) Add(ctx context.Context, dest string, artifactBlobs []li
} }
annotations := maps.Clone(options.Annotations) 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{ newLayer := specV1.Descriptor{
MediaType: options.FileMIMEType, 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)) mountPaths := make([]libartTypes.BlobMountPath, 0, len(arty.Manifest.Layers))
for _, l := range arty.Manifest.Layers { for _, l := range arty.Manifest.Layers {
title := l.Annotations[specV1.AnnotationTitle] 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) filename, err := generateArtifactBlobName(title, l.Digest)
if err != nil { if err != nil {
return nil, err return nil, err

2
vendor/modules.txt vendored
View File

@@ -742,7 +742,7 @@ go.opentelemetry.io/otel/trace
go.opentelemetry.io/otel/trace/embedded go.opentelemetry.io/otel/trace/embedded
go.opentelemetry.io/otel/trace/internal/telemetry go.opentelemetry.io/otel/trace/internal/telemetry
go.opentelemetry.io/otel/trace/noop go.opentelemetry.io/otel/trace/noop
# go.podman.io/common v0.65.1-0.20251016133615-aa970d2c7532 # go.podman.io/common v0.65.1-0.20251016162239-c4c5e00ad22d
## explicit; go 1.24.2 ## explicit; go 1.24.2
go.podman.io/common/internal go.podman.io/common/internal
go.podman.io/common/internal/attributedstring go.podman.io/common/internal/attributedstring