Add ability to set layer media type for artifacts

in #25884, it was pointed out that the standard detection used to
determine the artifact's file type can be wrong.  in those cases, it
would be handy for the user to be able to override the media type of the
layer.  as such, added a new option called `--file-type`, which is
optional, and allows users to do just that.

`podman artifact add --file-type text/yaml
quay.io/artifact/config:latest ./config.yaml `

Fixes: #25884

Signed-off-by: Brent Baude <bbaude@redhat.com>
This commit is contained in:
Brent Baude
2025-04-17 09:42:08 -05:00
parent 51c4df1316
commit fdfed9979f
7 changed files with 42 additions and 7 deletions

View File

@ -19,7 +19,9 @@ var (
RunE: add,
Args: cobra.MinimumNArgs(2),
ValidArgsFunction: common.AutocompleteArtifactAdd,
Example: `podman artifact add quay.io/myimage/myartifact:latest /tmp/foobar.txt`,
Example: `podman artifact add quay.io/myimage/myartifact:latest /tmp/foobar.txt
podman artifact add --file-type text/yaml quay.io/myimage/myartifact:latest /tmp/foobar.yaml
podman artifact add --append quay.io/myimage/myartifact:latest /tmp/foobar.tar.gz`,
Annotations: map[string]string{registry.EngineMode: registry.ABIMode},
}
)
@ -28,6 +30,7 @@ type artifactAddOptions struct {
ArtifactType string
Annotations []string
Append bool
FileType string
}
var (
@ -51,6 +54,10 @@ func init() {
appendFlagName := "append"
flags.BoolVarP(&addOpts.Append, appendFlagName, "a", false, "Append files to an existing artifact")
fileTypeFlagName := "file-type"
flags.StringVarP(&addOpts.FileType, fileTypeFlagName, "", "", "Set file type to use for the artifact (layer)")
_ = addCmd.RegisterFlagCompletionFunc(fileTypeFlagName, completion.AutocompleteNone)
}
func add(cmd *cobra.Command, args []string) error {
@ -63,6 +70,7 @@ func add(cmd *cobra.Command, args []string) error {
opts.Annotations = annots
opts.ArtifactType = addOpts.ArtifactType
opts.Append = addOpts.Append
opts.FileType = addOpts.FileType
report, err := registry.ImageEngine().ArtifactAdd(registry.Context(), args[0], args[1:], opts)
if err != nil {

View File

@ -27,6 +27,10 @@ Note: Set annotations for each file being added.
Append files to an existing artifact. This option cannot be used with the **--type** option.
#### **--file-type**
Set the media type of the artifact file instead of allowing detection to determine the type
#### **--help**
Print usage statement.
@ -55,6 +59,16 @@ Set an annotation for an artifact
$ podman artifact add --annotation date=2025-01-30 quay.io/myartifact/myml:latest /tmp/foobar1.ml
```
Append a file to an existing artifact
```
$ podman artifact add --append quay.io/myartifact/tarballs:latest /tmp/foobar.tar.gz
```
Override the media type of the artifact being added
```
$ podman artifact add --file-type text/yaml quay.io/myartifact/descriptors:latest /tmp/info.yaml
```
## SEE ALSO
**[podman(1)](podman.1.md)**, **[podman-artifact(1)](podman-artifact.1.md)**

View File

@ -13,6 +13,7 @@ type ArtifactAddOptions struct {
Annotations map[string]string
ArtifactType string
Append bool
FileType string
}
type ArtifactExtractOptions struct {

View File

@ -192,6 +192,7 @@ func (ir *ImageEngine) ArtifactAdd(ctx context.Context, name string, paths []str
Annotations: opts.Annotations,
ArtifactType: opts.ArtifactType,
Append: opts.Append,
FileType: opts.FileType,
}
artifactDigest, err := artStore.Add(ctx, name, paths, &addOptions)

View File

@ -251,20 +251,26 @@ func (as ArtifactStore) Add(ctx context.Context, dest string, paths []string, op
// ImageDestination, in general, requires the caller to write a full image; here we may write only the added layers.
// This works for the oci/layout transport we hard-code.
for _, path := range paths {
mediaType := options.FileType
// get the new artifact into the local store
newBlobDigest, newBlobSize, err := layout.PutBlobFromLocalFile(ctx, imageDest, path)
if err != nil {
return nil, err
}
detectedType, err := determineManifestType(path)
// If we did not receive an override for the layer's mediatype, use
// detection to determine it.
if len(mediaType) < 1 {
mediaType, err = determineManifestType(path)
if err != nil {
return nil, err
}
}
annotations := maps.Clone(options.Annotations)
annotations[specV1.AnnotationTitle] = filepath.Base(path)
newLayer := specV1.Descriptor{
MediaType: detectedType,
MediaType: mediaType,
Digest: newBlobDigest,
Size: newBlobSize,
Annotations: annotations,

View File

@ -10,6 +10,9 @@ type AddOptions struct {
ArtifactType string `json:",omitempty"`
// append option is not compatible with ArtifactType option
Append bool `json:",omitempty"`
// FileType describes the media type for the layer. It is an override
// for the standard detection
FileType string `json:",omitempty"`
}
// FilterBlobOptions options used to filter for a single blob in an artifact

View File

@ -88,6 +88,7 @@ var _ = Describe("Podman artifact", func() {
})
It("podman artifact add with options", func() {
yamlType := "text/yaml"
artifact1Name := "localhost/test/artifact1"
artifact1File, err := createArtifactFile(1024)
Expect(err).ToNot(HaveOccurred())
@ -96,13 +97,14 @@ var _ = Describe("Podman artifact", func() {
annotation1 := "color=blue"
annotation2 := "flavor=lemon"
podmanTest.PodmanExitCleanly("artifact", "add", "--type", artifactType, "--annotation", annotation1, "--annotation", annotation2, artifact1Name, artifact1File)
podmanTest.PodmanExitCleanly("artifact", "add", "--file-type", yamlType, "--type", artifactType, "--annotation", annotation1, "--annotation", annotation2, artifact1Name, artifact1File)
a := podmanTest.InspectArtifact(artifact1Name)
Expect(a.Name).To(Equal(artifact1Name))
Expect(a.Manifest.ArtifactType).To(Equal(artifactType))
Expect(a.Manifest.Layers[0].Annotations["color"]).To(Equal("blue"))
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})
failSession.WaitWithDefaultTimeout()