1
0
mirror of https://github.com/containers/buildah.git synced 2026-02-05 09:45:38 +01:00

buildah manifest: add artifact-related options

Add functionality for creating artifact manifests and adding them to
image indexes.  `buildah manifest add` gets a `--artifact` option for
telling it to create artifact manifests, and `--artifact-type`,
`--artifact-config`, `--artifact-config-type`, `--artifact-layer-type`,
`--artifact-exclude-titles`, and `--subject` options to fine-tune the
contents of the artifact manifests it creates.

Add a `--index` flag to `buildah manifest annotate` so that it can be
told to set annotations on the index itself instead of on one of the
entries in the image index.

Add a `--subject` flag to `buildah manifest annotate` for setting the
`subject` field of an image index.

Add a `--annotation` flag to `buildah manifest create` to allow for
adding annotations to the new image index.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
This commit is contained in:
Nalin Dahyabhai
2024-01-26 11:45:30 -05:00
parent 07057917dc
commit aca884a89d
7 changed files with 458 additions and 106 deletions

View File

@@ -26,6 +26,7 @@ import (
"github.com/hashicorp/go-multierror"
digest "github.com/opencontainers/go-digest"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -33,12 +34,17 @@ import (
type manifestCreateOpts struct {
os, arch string
all, tlsVerify, insecure, amend bool
annotations []string
}
type manifestAddOpts struct {
authfile, certDir, creds, os, arch, variant, osVersion string
features, osFeatures, annotations []string
tlsVerify, insecure, all bool
artifact, artifactExcludeTitles bool
artifactType, artifactLayerType string
artifactConfigType, artifactConfigFile string
artifactSubject string
}
type manifestRemoveOpts struct{}
@@ -46,6 +52,8 @@ type manifestRemoveOpts struct{}
type manifestAnnotateOpts struct {
os, arch, variant, osVersion string
features, osFeatures, annotations []string
index bool
subject string
}
type manifestInspectOpts struct {
@@ -57,9 +65,9 @@ func init() {
var (
manifestDescription = "\n Creates, modifies, and pushes manifest lists and image indexes."
manifestCreateDescription = "\n Creates manifest lists and image indexes."
manifestAddDescription = "\n Adds an image to a manifest list or image index."
manifestRemoveDescription = "\n Removes an image from a manifest list or image index."
manifestAnnotateDescription = "\n Adds or updates information about an entry in a manifest list or image index."
manifestAddDescription = "\n Adds an image or artifact to a manifest list or image index."
manifestRemoveDescription = "\n Removes an image or artifact from a manifest list or image index."
manifestAnnotateDescription = "\n Adds or updates information about an image index or an entry in a manifest list or image index."
manifestInspectDescription = "\n Display the contents of a manifest list or image index."
manifestPushDescription = "\n Pushes manifest lists and image indexes to registries."
manifestRmDescription = "\n Remove one or more manifest lists from local storage."
@@ -103,6 +111,7 @@ func init() {
flags := manifestCreateCommand.Flags()
flags.BoolVar(&manifestCreateOpts.all, "all", false, "add all of the lists' images if the images to add are lists")
flags.BoolVar(&manifestCreateOpts.amend, "amend", false, "modify an existing list if one with the desired name already exists")
flags.StringSliceVar(&manifestCreateOpts.annotations, "annotation", nil, "set an `annotation` for the image index")
flags.StringVar(&manifestCreateOpts.os, "os", "", "if any of the specified images is a list, choose the one for `os`")
if err := flags.MarkHidden("os"); err != nil {
panic(fmt.Sprintf("error marking --os as hidden: %v", err))
@@ -121,17 +130,25 @@ func init() {
manifestAddCommand := &cobra.Command{
Use: "add",
Short: "Add images to a manifest list or image index",
Short: "Add an image or artifact to a manifest list or image index",
Long: manifestAddDescription,
RunE: func(cmd *cobra.Command, args []string) error {
return manifestAddCmd(cmd, args, manifestAddOpts)
},
Example: `buildah manifest add mylist:v1.11 image:v1.11-amd64
buildah manifest add mylist:v1.11 transport:imageName`,
buildah manifest add mylist:v1.11 transport:imageName
buildah manifest add --artifact --artifact-type text/plain mylist:v1.11 ./somefile.txt ./somefile.png`,
Args: cobra.MinimumNArgs(2),
}
manifestAddCommand.SetUsageTemplate(UsageTemplate())
flags = manifestAddCommand.Flags()
flags.BoolVar(&manifestAddOpts.artifact, "artifact", false, "treat the argument as a filename and add it as an artifact")
flags.StringVar(&manifestAddOpts.artifactType, "artifact-type", "", "artifact manifest media type")
flags.StringVar(&manifestAddOpts.artifactConfigType, "artifact-config-type", imgspecv1.DescriptorEmptyJSON.MediaType, "artifact config media type")
flags.StringVar(&manifestAddOpts.artifactConfigFile, "artifact-config", "", "artifact config file")
flags.StringVar(&manifestAddOpts.artifactLayerType, "artifact-layer-type", "", "artifact layer media type")
flags.BoolVar(&manifestAddOpts.artifactExcludeTitles, "artifact-exclude-titles", false, fmt.Sprintf(`refrain from setting %q annotations on "layers"`, v1.AnnotationTitle))
flags.StringVar(&manifestAddOpts.artifactSubject, "artifact-subject", "", "artifact subject reference")
flags.StringVar(&manifestAddOpts.authfile, "authfile", auth.GetDefaultAuthFile(), "path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
flags.StringVar(&manifestAddOpts.certDir, "cert-dir", "", "use certificates at the specified path to access the registry")
flags.StringVar(&manifestAddOpts.creds, "creds", "", "use `[username[:password]]` for accessing the registry")
@@ -141,7 +158,7 @@ func init() {
flags.StringVar(&manifestAddOpts.osVersion, "os-version", "", "override the OS `version` of the specified image")
flags.StringSliceVar(&manifestAddOpts.features, "features", nil, "override the `features` of the specified image")
flags.StringSliceVar(&manifestAddOpts.osFeatures, "os-features", nil, "override the OS `features` of the specified image")
flags.StringSliceVar(&manifestAddOpts.annotations, "annotation", nil, "set an `annotation` for the specified image")
flags.StringSliceVar(&manifestAddOpts.annotations, "annotation", nil, "set an `annotation` for the specified image or artifact")
flags.BoolVar(&manifestAddOpts.insecure, "insecure", false, "neither require HTTPS nor verify certificates when accessing the registry. TLS verification cannot be used when talking to an insecure registry.")
if err := flags.MarkHidden("insecure"); err != nil {
panic(fmt.Sprintf("error marking insecure as hidden: %v", err))
@@ -185,16 +202,18 @@ func init() {
return manifestAnnotateCmd(cmd, args, manifestAnnotateOpts)
},
Example: `buildah manifest annotate --annotation left=right mylist:v1.11 image:v1.11-amd64`,
Args: cobra.MinimumNArgs(2),
Args: cobra.RangeArgs(1, 2),
}
flags = manifestAnnotateCommand.Flags()
flags.StringVar(&manifestAnnotateOpts.os, "os", "", "override the `OS` of the specified image")
flags.StringVar(&manifestAnnotateOpts.arch, "arch", "", "override the `Architecture` of the specified image")
flags.BoolVar(&manifestAnnotateOpts.index, "index", false, "set annotations or artifact type for the index itself instead of for an entry in the index")
flags.StringVar(&manifestAnnotateOpts.variant, "variant", "", "override the `Variant` of the specified image")
flags.StringVar(&manifestAnnotateOpts.osVersion, "os-version", "", "override the os `version` of the specified image")
flags.StringSliceVar(&manifestAnnotateOpts.features, "features", nil, "override the `features` of the specified image")
flags.StringSliceVar(&manifestAnnotateOpts.osFeatures, "os-features", nil, "override the os `features` of the specified image")
flags.StringSliceVar(&manifestAnnotateOpts.annotations, "annotation", nil, "set an `annotation` for the specified image")
flags.StringVar(&manifestAnnotateOpts.subject, "subject", "", "set a subject for the image index")
manifestAnnotateCommand.SetUsageTemplate(UsageTemplate())
manifestCommand.AddCommand(manifestAnnotateCommand)
@@ -327,7 +346,7 @@ func manifestCreateCmd(c *cobra.Command, args []string, opts manifestCreateOpts)
if err != nil {
return fmt.Errorf("encountered while expanding image name %q: %w", listImageSpec, err)
}
if manifestListID, err = list.SaveToImage(store, "", names, manifest.DockerV2ListMediaType); err != nil {
if manifestListID, err = list.SaveToImage(store, "", names, ""); err != nil {
if errors.Is(err, storage.ErrDuplicateName) && opts.amend {
for _, name := range names {
manifestList, err := runtime.LookupManifestList(listImageSpec)
@@ -361,6 +380,20 @@ func manifestCreateCmd(c *cobra.Command, args []string, opts manifestCreateOpts)
return err
}
if len(opts.annotations) != 0 {
annotations := make(map[string]string)
for _, annotationSpec := range opts.annotations {
k, v, ok := strings.Cut(annotationSpec, "=")
if !ok {
return fmt.Errorf(`no "=" found in annotation %q`, annotationSpec)
}
annotations[k] = v
}
if err := list.SetAnnotations(nil, annotations); err != nil {
return err
}
}
for _, imageSpec := range imageSpecs {
ref, err := alltransports.ParseImageName(imageSpec)
if err != nil {
@@ -376,13 +409,12 @@ func manifestCreateCmd(c *cobra.Command, args []string, opts manifestCreateOpts)
// Found local image so use that.
ref = refLocal
}
_, err = list.Add(getContext(), systemContext, ref, opts.all)
if err != nil {
if _, err = list.Add(getContext(), systemContext, ref, opts.all); err != nil {
return err
}
}
imageID, err := list.SaveToImage(store, manifestListID, names, manifest.DockerV2ListMediaType)
imageID, err := list.SaveToImage(store, manifestListID, names, "")
if err == nil {
fmt.Printf("%s\n", imageID)
}
@@ -396,20 +428,29 @@ func manifestAddCmd(c *cobra.Command, args []string, opts manifestAddOpts) error
listImageSpec := ""
imageSpec := ""
artifactSpec := []string{}
switch len(args) {
case 0, 1:
return errors.New("At least a list image and an image to add must be specified")
return errors.New("At least a list image and an image or artifact to add must be specified")
case 2:
listImageSpec = args[0]
if listImageSpec == "" {
return fmt.Errorf(`Invalid image name "%s"`, args[0])
}
imageSpec = args[1]
if imageSpec == "" {
return fmt.Errorf(`Invalid image name "%s"`, args[1])
if opts.artifact {
artifactSpec = args[1:]
} else {
imageSpec = args[1]
if imageSpec == "" {
return fmt.Errorf(`Invalid image name "%s"`, args[1])
}
}
default:
return errors.New("At least two arguments are necessary: list and image to add to list")
if opts.artifact {
artifactSpec = args[1:]
} else {
return errors.New("Too many arguments: expected list and image add to list")
}
}
store, err := getStore(c)
@@ -442,79 +483,129 @@ func manifestAddCmd(c *cobra.Command, args []string, opts manifestAddOpts) error
if err != nil {
return err
}
ref, err := alltransports.ParseImageName(imageSpec)
if err != nil {
if ref, err = alltransports.ParseImageName(util.DefaultTransport + imageSpec); err != nil {
// check if the local image exists
if ref, _, err = util.FindImage(store, "", systemContext, imageSpec); err != nil {
var instanceDigest digest.Digest
if opts.artifact {
var subjectRef types.ImageReference
if opts.artifactSubject != "" {
if subjectRef, err = alltransports.ParseImageName(opts.artifactSubject); err != nil {
if subjectRef, err = alltransports.ParseImageName(util.DefaultTransport + opts.artifactSubject); err != nil {
if subjectRef, _, err = util.FindImage(store, "", systemContext, opts.artifactSubject); err != nil {
logrus.Errorf("Error while trying to parse artifact subject %q: %v", opts.artifactSubject, err)
return err
}
}
}
}
var artifactType *string
if c.Flags().Changed("artifact-type") {
artifactType = &opts.artifactType
}
var artifactLayerType *string
if c.Flags().Changed("artifact-layer-type") {
artifactLayerType = &opts.artifactLayerType
}
options := manifests.AddArtifactOptions{
ManifestArtifactType: artifactType,
LayerMediaType: artifactLayerType,
SubjectReference: subjectRef,
}
if opts.artifactConfigType != "" {
tmp := imgspecv1.DescriptorEmptyJSON
tmp.MediaType = opts.artifactConfigType
options.ConfigDescriptor = &tmp
}
if opts.artifactConfigFile != "" {
if options.ConfigDescriptor == nil {
tmp := imgspecv1.DescriptorEmptyJSON
if opts.artifactConfigType == "" {
tmp.MediaType = imgspecv1.MediaTypeImageConfig
}
options.ConfigDescriptor = &tmp
}
options.ConfigDescriptor.Size = -1
options.ConfigFile = opts.artifactConfigFile
}
options.ExcludeTitles = opts.artifactExcludeTitles
instanceDigest, err = list.AddArtifact(getContext(), systemContext, options, artifactSpec...)
if err != nil {
logrus.Errorf("Error while trying to add artifact %q to image index: %v", artifactSpec, err)
return err
}
} else {
var ref types.ImageReference
if ref, err = alltransports.ParseImageName(imageSpec); err != nil {
if ref, err = alltransports.ParseImageName(util.DefaultTransport + imageSpec); err != nil {
if ref, _, err = util.FindImage(store, "", systemContext, imageSpec); err != nil {
return err
}
}
}
instanceDigest, err = list.Add(getContext(), systemContext, ref, opts.all)
if err != nil {
var storeErr error
// Retry without a custom system context. A user may want to add
// a custom platform (see #3511).
if ref, _, storeErr = util.FindImage(store, "", nil, imageSpec); storeErr != nil {
logrus.Errorf("Error while trying to find image on local storage: %v", storeErr)
return err
}
instanceDigest, storeErr = list.Add(getContext(), systemContext, ref, opts.all)
if storeErr != nil {
logrus.Errorf("Error while trying to add on manifest list: %v", storeErr)
return err
}
}
}
digest, err := list.Add(getContext(), systemContext, ref, opts.all)
if err != nil {
var storeErr error
// Retry without a custom system context. A user may want to add
// a custom platform (see #3511).
if ref, _, storeErr = util.FindImage(store, "", nil, imageSpec); storeErr != nil {
logrus.Errorf("Error while trying to find image on local storage: %v", storeErr)
return err
}
digest, storeErr = list.Add(getContext(), systemContext, ref, opts.all)
if storeErr != nil {
logrus.Errorf("Error while trying to add on manifest list: %v", storeErr)
return err
}
}
if opts.os != "" {
if err := list.SetOS(digest, opts.os); err != nil {
if err := list.SetOS(instanceDigest, opts.os); err != nil {
return err
}
}
if opts.osVersion != "" {
if err := list.SetOSVersion(digest, opts.osVersion); err != nil {
if err := list.SetOSVersion(instanceDigest, opts.osVersion); err != nil {
return err
}
}
if len(opts.osFeatures) != 0 {
if err := list.SetOSFeatures(digest, opts.osFeatures); err != nil {
if err := list.SetOSFeatures(instanceDigest, opts.osFeatures); err != nil {
return err
}
}
if opts.arch != "" {
if err := list.SetArchitecture(digest, opts.arch); err != nil {
if err := list.SetArchitecture(instanceDigest, opts.arch); err != nil {
return err
}
}
if opts.variant != "" {
if err := list.SetVariant(digest, opts.variant); err != nil {
if err := list.SetVariant(instanceDigest, opts.variant); err != nil {
return err
}
}
if len(opts.features) != 0 {
if err := list.SetFeatures(digest, opts.features); err != nil {
if err := list.SetFeatures(instanceDigest, opts.features); err != nil {
return err
}
}
if len(opts.annotations) != 0 {
annotations := make(map[string]string)
for _, annotationSpec := range opts.annotations {
spec := strings.SplitN(annotationSpec, "=", 2)
if len(spec) != 2 {
return fmt.Errorf("no value given for annotation %q", spec[0])
k, v, ok := strings.Cut(annotationSpec, "=")
if !ok {
return fmt.Errorf(`no "=" found in annotation %q`, annotationSpec)
}
annotations[spec[0]] = spec[1]
annotations[k] = v
}
if err := list.SetAnnotations(&digest, annotations); err != nil {
if err := list.SetAnnotations(&instanceDigest, annotations); err != nil {
return err
}
}
updatedListID, err := list.SaveToImage(store, manifestList.ID(), nil, "")
if err == nil {
fmt.Printf("%s: %s\n", updatedListID, digest.String())
fmt.Printf("%s: %s\n", updatedListID, instanceDigest.String())
}
return err
@@ -523,6 +614,7 @@ func manifestAddCmd(c *cobra.Command, args []string, opts manifestAddOpts) error
func manifestRemoveCmd(c *cobra.Command, args []string, opts manifestRemoveOpts) error {
listImageSpec := ""
var instanceDigest digest.Digest
var instanceSpec string
switch len(args) {
case 0, 1:
return errors.New("At least a list image and one or more instance digests must be specified")
@@ -531,15 +623,10 @@ func manifestRemoveCmd(c *cobra.Command, args []string, opts manifestRemoveOpts)
if listImageSpec == "" {
return fmt.Errorf(`Invalid image name "%s"`, args[0])
}
instanceSpec := args[1]
instanceSpec = args[1]
if instanceSpec == "" {
return fmt.Errorf(`Invalid instance "%s"`, args[1])
}
d, err := digest.Parse(instanceSpec)
if err != nil {
return fmt.Errorf(`Invalid instance "%s": %v`, args[1], err)
}
instanceDigest = d
default:
return errors.New("At least two arguments are necessary: list and digest of instance to remove from list")
}
@@ -562,7 +649,36 @@ func manifestRemoveCmd(c *cobra.Command, args []string, opts manifestRemoveOpts)
if err != nil {
return err
}
_, list, err := manifests.LoadFromImage(store, manifestList.ID())
if err != nil {
return err
}
d, err := list.InstanceByFile(instanceSpec)
if err != nil {
instanceRef, err := alltransports.ParseImageName(instanceSpec)
if err != nil {
if instanceRef, err = alltransports.ParseImageName(util.DefaultTransport + instanceSpec); err != nil {
if instanceRef, _, err = util.FindImage(store, "", systemContext, instanceSpec); err != nil {
return fmt.Errorf(`Invalid instance "%s": %v`, instanceSpec, err)
}
}
}
ctx := getContext()
instanceImg, err := instanceRef.NewImageSource(ctx, systemContext)
if err != nil {
return fmt.Errorf("Reading image instance: %w", err)
}
defer instanceImg.Close()
manifestBytes, _, err := instanceImg.GetManifest(ctx, nil)
if err != nil {
return fmt.Errorf("Reading image instance manifest: %w", err)
}
d, err = manifest.Digest(manifestBytes)
if err != nil {
return fmt.Errorf("Digesting image instance manifest: %w", err)
}
}
instanceDigest = d
if err := manifestList.RemoveInstance(instanceDigest); err != nil {
return err
}
@@ -611,7 +727,11 @@ func manifestRmCmd(c *cobra.Command, args []string) error {
func manifestAnnotateCmd(c *cobra.Command, args []string, opts manifestAnnotateOpts) error {
listImageSpec := ""
imageSpec := ""
instanceSpec := ""
if opts.subject != "" {
// this option is always only working at the index level
opts.index = true
}
switch len(args) {
case 0:
return errors.New("At least a list image must be specified")
@@ -620,17 +740,23 @@ func manifestAnnotateCmd(c *cobra.Command, args []string, opts manifestAnnotateO
if listImageSpec == "" {
return fmt.Errorf(`Invalid image name "%s"`, args[0])
}
if !opts.index {
return errors.New(`Expected an instance digest, image name, or artifact name`)
}
case 2:
listImageSpec = args[0]
if listImageSpec == "" {
return fmt.Errorf(`Invalid image name "%s"`, args[0])
}
imageSpec = args[1]
if imageSpec == "" {
return fmt.Errorf(`Invalid image name "%s"`, args[1])
if opts.index {
return fmt.Errorf(`Did not expect image or artifact name "%s" when modifying the entire index`, args[1])
}
instanceSpec = args[1]
if instanceSpec == "" {
return fmt.Errorf(`Invalid instance digest, image name, or artifact name "%s"`, instanceSpec)
}
default:
return errors.New("At least two arguments are necessary: list and image to add to list")
return errors.New("Expected either a list name and --index or a list name and an image digest or image name or artifact name")
}
store, err := getStore(c)
@@ -664,75 +790,148 @@ func manifestAnnotateCmd(c *cobra.Command, args []string, opts manifestAnnotateO
return err
}
digest, err := digest.Parse(imageSpec)
if err != nil {
ctx := getContext()
ref, _, err := util.FindImage(store, "", systemContext, imageSpec)
var instance digest.Digest
if !opts.index {
d, err := list.InstanceByFile(instanceSpec)
if err != nil {
return err
}
img, err := ref.NewImageSource(ctx, systemContext)
if err != nil {
return err
}
defer img.Close()
manifestBytes, _, err := img.GetManifest(ctx, nil)
if err != nil {
return err
}
digest, err = manifest.Digest(manifestBytes)
if err != nil {
return err
instanceRef, err := alltransports.ParseImageName(instanceSpec)
if err != nil {
if instanceRef, err = alltransports.ParseImageName(util.DefaultTransport + instanceSpec); err != nil {
// check if the local image exists
if instanceRef, _, err = util.FindImage(store, "", systemContext, instanceSpec); err != nil {
return fmt.Errorf(`Invalid instance "%s": %v`, instanceSpec, err)
}
}
}
ctx := getContext()
instanceImg, err := instanceRef.NewImageSource(ctx, systemContext)
if err != nil {
return fmt.Errorf("Reading image instance: %w", err)
}
defer instanceImg.Close()
manifestBytes, _, err := instanceImg.GetManifest(ctx, nil)
if err != nil {
return fmt.Errorf("Reading image instance manifest: %w", err)
}
d, err = manifest.Digest(manifestBytes)
if err != nil {
return fmt.Errorf("Digesting image instance manifest: %w", err)
}
}
instance = d
}
if opts.os != "" {
if err := list.SetOS(digest, opts.os); err != nil {
if opts.index {
return fmt.Errorf("--index is not compatible with --os")
}
if err := list.SetOS(instance, opts.os); err != nil {
return err
}
}
if opts.osVersion != "" {
if err := list.SetOSVersion(digest, opts.osVersion); err != nil {
if opts.index {
return fmt.Errorf("--index is not compatible with --os-version")
}
if err := list.SetOSVersion(instance, opts.osVersion); err != nil {
return err
}
}
if len(opts.osFeatures) != 0 {
if err := list.SetOSFeatures(digest, opts.osFeatures); err != nil {
if opts.index {
return fmt.Errorf("--index is not compatible with --os-features")
}
if err := list.SetOSFeatures(instance, opts.osFeatures); err != nil {
return err
}
}
if opts.arch != "" {
if err := list.SetArchitecture(digest, opts.arch); err != nil {
if opts.index {
return fmt.Errorf("--index is not compatible with --arch")
}
if err := list.SetArchitecture(instance, opts.arch); err != nil {
return err
}
}
if opts.variant != "" {
if err := list.SetVariant(digest, opts.variant); err != nil {
if opts.index {
return fmt.Errorf("--index is not compatible with --variant")
}
if err := list.SetVariant(instance, opts.variant); err != nil {
return err
}
}
if len(opts.features) != 0 {
if err := list.SetFeatures(digest, opts.features); err != nil {
if opts.index {
return fmt.Errorf("--index is not compatible with --features")
}
if err := list.SetFeatures(instance, opts.features); err != nil {
return err
}
}
if len(opts.annotations) != 0 {
annotations := make(map[string]string)
for _, annotationSpec := range opts.annotations {
spec := strings.SplitN(annotationSpec, "=", 2)
if len(spec) != 2 {
return fmt.Errorf("no value given for annotation %q", spec[0])
k, v, ok := strings.Cut(annotationSpec, "=")
if !ok {
return fmt.Errorf(`no "=" found in annotation %q`, annotationSpec)
}
annotations[spec[0]] = spec[1]
annotations[k] = v
}
if err := list.SetAnnotations(&digest, annotations); err != nil {
var instanceDigest *digest.Digest
if !opts.index {
instanceDigest = &instance
}
if err := list.SetAnnotations(instanceDigest, annotations); err != nil {
return err
}
}
if opts.subject != "" {
subjectRef, err := alltransports.ParseImageName(opts.subject)
if err != nil {
if subjectRef, err = alltransports.ParseImageName(util.DefaultTransport + opts.subject); err != nil {
// check if the local image exists
if subjectRef, _, err = util.FindImage(store, "", systemContext, opts.subject); err != nil {
logrus.Errorf("Error while trying to parse artifact subject: %v", err)
return err
}
}
}
ctx := getContext()
src, err := subjectRef.NewImageSource(ctx, systemContext)
if err != nil {
logrus.Errorf("Error while trying to read artifact subject: %v", err)
return err
}
defer src.Close()
manifestBytes, manifestType, err := src.GetManifest(ctx, nil)
if err != nil {
logrus.Errorf("Error while trying to read artifact subject manifest: %v", err)
return err
}
manifestDigest, err := manifest.Digest(manifestBytes)
if err != nil {
logrus.Errorf("Error while trying to digest artifact subject manifest: %v", err)
return err
}
descriptor := imgspecv1.Descriptor{
MediaType: manifestType,
Size: int64(len(manifestBytes)),
Digest: manifestDigest,
}
if err := list.SetSubject(&descriptor); err != nil {
return err
}
}
updatedListID, err := list.SaveToImage(store, manifestList.ID(), nil, "")
if err == nil {
fmt.Printf("%s: %s\n", updatedListID, digest.String())
if instance == "" {
fmt.Printf("%s\n", updatedListID)
} else {
fmt.Printf("%s: %s\n", updatedListID, instance.String())
}
}
return nil

View File

@@ -2,15 +2,16 @@
## NAME
buildah\-manifest\-add - Add an image to a manifest list or image index.
buildah\-manifest\-add - Add an image or artifact to a manifest list or image index.
## SYNOPSIS
**buildah manifest add** *listNameOrIndexName* *imageName*
**buildah manifest add** [options...] *listNameOrIndexName* *imageOrArtifactName* [...]
## DESCRIPTION
Adds the specified image to the specified manifest list or image index.
Adds the specified image to the specified manifest list or image index, or
creates an artifact manifest and adds it to the specified image index.
## RETURN VALUE
@@ -27,7 +28,7 @@ from such a list or index will be added to the list or index. Combining
**--annotation** *annotation=value*
Set an annotation on the entry for the newly-added image.
Set an annotation on the entry for the newly-added image or artifact manifest.
**--arch**
@@ -36,6 +37,55 @@ the image. If *imageName* refers to a manifest list or image index, the
architecture information will be retrieved from it. Otherwise, it will be
retrieved from the image's configuration information.
**--artifact**
Create an artifact manifest and add it to the image index. Arguments after the
index name will be interpreted as file names rather than as image references.
In most scenarios, the **--artifact-type** option should also be specified.
**--artifact-config** *filename*
When creating an artifact manifest and adding it to the image index, use the
specified file's contents as the configuration blob in the artifact manifest.
In most scenarios, leaving the default value, which signifies an empty
configuration, unchanged, is the preferred option.
**--artifact-config-type** *type*
When creating an artifact manifest and adding it to the image index, use the
specified MIME type as the `mediaType` associated with the configuration blob
in the artifact manifest. In most scenarios, leaving the default value, which
signifies either an empty configuration or the standard OCI configuration type,
unchanged, is the preferred option.
**--artifact-exclude-titles**
When creating an artifact manifest and adding it to the image index, do not
set "org.opencontainers.image.title" annotations equal to the file's basename
for each file added to the artifact manifest. Tools which retrieve artifacts
from a registry may use these values to choose names for files when saving
artifacts to disk, so this option is not recommended unless it is required
for interoperability with a particular registry.
**--artifact-layer-type** *type*
When creating an artifact manifest and adding it to the image index, use the
specified MIME type as the `mediaType` associated with the files' contents. If
not specified, guesses based on either the files names or their contents will
be made and used, but the option should be specified if certainty is needed.
**--artifact-subject** *imageName*
When creating an artifact manifest and adding it to the image index, set the
*subject* field in the artifact manifest to mark the artifact manifest as being
associated with the specified image in some way. An artifact manifest can only
be associated with, at most, one subject.
**--artifact-type** *type*
When creating an artifact manifest, use the specified MIME type as the
manifest's `artifactType` value instead of the less informative default value.
**--authfile** *path*
Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. See containers-auth.json(5) for more information. This file is created using `buildah login`.
@@ -105,5 +155,11 @@ buildah manifest add --arch arm64 --variant v8 mylist:v1.11 docker://fedora@sha2
506d8f4bb54931ea03a7e70173a0ed6302e3fb92dfadb3955ba5c17812e95c51: sha256:c829b1810d2dbb456e74a695fd3847530c8319e5a95dca623e9f1b1b89020d8b
```
```
buildah manifest add --artifact --artifact-type application/x-cd-image mylist:v1.11 ./imagefile.iso
506d8f4bb54931ea03a7e70173a0ed6302e3fb92dfadb3955ba5c17812e95c51: sha256:1768fae728f6f8ff3d0f8c7df409d7f4f0ca5c89b070810bd4aa4a2ed2eca8bb
```
## SEE ALSO
buildah(1), buildah-login(1), buildah-manifest(1), buildah-manifest-create(1), buildah-manifest-remove(1), buildah-manifest-annotate(1), buildah-manifest-inspect(1), buildah-manifest-push(1), buildah-rmi(1), docker-login(1), containers-auth.json(5)

View File

@@ -2,15 +2,15 @@
## NAME
buildah\-manifest\-annotate - Add and update information about an image to a manifest list or image index.
buildah\-manifest\-annotate - Add and update information about an image or artifact to a manifest list or image index.
## SYNOPSIS
**buildah manifest annotate** [options...] *listNameOrIndexName* *imageManifestDigest*
**buildah manifest annotate** [options...] *listNameOrIndexName* *imageManifestDigestOrImageOrArtifactName*
## DESCRIPTION
Adds or updates information about an image included in a manifest list or image index.
Adds or updates information about an image or artifact included in a manifest list or image index.
## RETURN VALUE
@@ -20,7 +20,8 @@ The list image's ID and the digest of the image's manifest.
**--annotation** *annotation=value*
Set an annotation on the entry for the specified image.
Set an annotation on the entry for the specified image or artifact. If
**--index** is also specified, sets the annotation on the entire image index.
**--arch**
@@ -33,6 +34,12 @@ configuration information, so it is rarely necessary to use this option.
Specify the features list which the list or index records as requirements for
the image. This option is rarely used.
**--index**
Treats arguments to the **--annotation** option as annotation values to be set
on the image index itself rather than on an entry in the image index. Implied
for **--subject**.
**--os**
Override the OS which the list or index records as a requirement for the image.
@@ -49,6 +56,12 @@ for the image. This option is rarely used.
Specify the OS version which the list or index records as a requirement for the
image. This option is rarely used.
**--subject** *imageName*
Set the *subject* field in the image index to mark the image index as being
associated with the specified image in some way. An image index can only be
associated with, at most, one subject.
**--variant**
Specify the variant which the list or index records for the image. This option
@@ -62,5 +75,10 @@ buildah manifest annotate --arch arm64 --variant v8 mylist:v1.11 sha256:c829b181
506d8f4bb54931ea03a7e70173a0ed6302e3fb92dfadb3955ba5c17812e95c51: sha256:c829b1810d2dbb456e74a695fd3847530c8319e5a95dca623e9f1b1b89020d8b
```
```
buildah manifest annotate --index --annotation food=yummy mylist:v1.11
506d8f4bb54931ea03a7e70173a0ed6302e3fb92dfadb3955ba5c17812e95c51: sha256:c829b1810d2dbb456e74a695fd3847530c8319e5a95dca623e9f1b1b89020d8b
```
## SEE ALSO
buildah(1), buildah-manifest(1), buildah-manifest-create(1), buildah-manifest-add(1), buildah-manifest-remove(1), buildah-manifest-inspect(1), buildah-manifest-push(1), buildah-rmi(1)

View File

@@ -6,7 +6,7 @@ buildah\-manifest\-create - Create a manifest list or image index.
## SYNOPSIS
**buildah manifest create** *listNameOrIndexName* [*imageName* ...]
**buildah manifest create** [options...] *listNameOrIndexName* [*imageName* ...]
## DESCRIPTION
@@ -35,6 +35,10 @@ If a manifest list named *listNameOrIndexName* already exists, modify the
preexisting list instead of exiting with an error. The contents of
*listNameOrIndexName* are not modified if no *imageName*s are given.
**--annotation** *annotation=value*
Set an annotation on the newly-created image index.
**--tls-verify** *bool-value*
Require HTTPS and verification of certificates when talking to container registries (defaults to true). TLS verification cannot be used when talking to an insecure registry.

View File

@@ -6,11 +6,12 @@ buildah\-manifest\-remove - Remove an image from a manifest list or image index.
## SYNOPSIS
**buildah manifest remove** *listNameOrIndexName* *imageManifestDigest*
**buildah manifest remove** *listNameOrIndexName* *imageNameOrManifestDigestOrArtifactName*
## DESCRIPTION
Removes the image with the specified digest from the specified manifest list or image index.
Removes the image with the specified name or digest from the specified manifest
list or image index, or the specified artifact from the specified image index.
## RETURN VALUE

View File

@@ -11,6 +11,7 @@ The `buildah manifest` command provides subcommands which can be used to:
* Create a working Docker manifest list or OCI image index.
* Add an entry to a manifest list or image index for a specified image.
* Add an entry to an image index for an artifact manifest referring to a file.
* Add or update information about an entry in a manifest list or image index.
* Delete a working container or an image.
* Push a manifest list or image index to a registry or other location.
@@ -19,8 +20,8 @@ The `buildah manifest` command provides subcommands which can be used to:
| Command | Man Page | Description |
| ------- | -------------------------------------------------------------- | --------------------------------------------------------------------------- |
| add | [buildah-manifest-add(1)](buildah-manifest-add.1.md) | Add an image to a manifest list or image index. |
| annotate | [buildah-manifest-annotate(1)](buildah-manifest-annotate.1.md) | Add or update information about an image in a manifest list or image index. |
| add | [buildah-manifest-add(1)](buildah-manifest-add.1.md) | Add an image or artifact to a manifest list or image index. |
| annotate | [buildah-manifest-annotate(1)](buildah-manifest-annotate.1.md) | Add or update information about an image or artifact in a manifest list or image index. |
| create | [buildah-manifest-create(1)](buildah-manifest-create.1.md) | Create a manifest list or image index. |
| exists | [buildah-manifest-exists(1)](buildah-manifest-exists.1.md) | Check if a manifest list exists in local storage. |
| inspect | [buildah-manifest-inspect(1)](buildah-manifest-inspect.1.md) | Display the contents of a manifest list or image index. |
@@ -41,8 +42,8 @@ the scope of this example. Building a multi-arch manifest list
$ platarch=linux/amd64,linux/ppc64le,linux/arm64,linux/s390x
$ buildah build --jobs=4 --platform=$platarch --manifest shazam .
**Note:** The `--jobs` argument is optional, and the `-t` or `--tag`
option should *not* be used.
**Note:** The `--jobs` argument is optional, and the `--manifest` option
should be used instead of the`-t` or `--tag` options.
### Assembling a multi-arch manifest from separately built images

View File

@@ -12,12 +12,20 @@ IMAGE_LIST_PPC64LE_INSTANCE_DIGEST=sha256:bcf9771c0b505e68c65440474179592ffdfa98
IMAGE_LIST_S390X_INSTANCE_DIGEST=sha256:882a20ee0df7399a445285361d38b711c299ca093af978217112c73803546d5e
@test "manifest-create" {
_prefetch busybox
run_buildah inspect -f '{{ .FromImageDigest }}' busybox
imagedigest="$output"
run_buildah manifest create foo
listid="$output"
run_buildah 125 manifest create foo
assert "$output" =~ "that name is already in use"
run_buildah manifest create --amend foo
assert "$output" == "$listid"
run_buildah manifest create --amend --annotation red=blue foo busybox
assert "$output" == "$listid"
run_buildah manifest inspect foo
assert "$output" =~ '"red": "blue"'
assert "$output" =~ "${imagedigest}"
# since manifest exists in local storage this should exit with `0`
run_buildah manifest exists foo
# since manifest does not exist in local storage this should exit with `1`
@@ -40,6 +48,33 @@ IMAGE_LIST_S390X_INSTANCE_DIGEST=sha256:882a20ee0df7399a445285361d38b711c299ca09
run_buildah manifest rm foo
}
@test "manifest-add artifact" {
_prefetch busybox
createrandom $TEST_SCRATCH_DIR/randomfile2
createrandom $TEST_SCRATCH_DIR/randomfile
run sha256sum $TEST_SCRATCH_DIR/randomfile
blobencoded="${output%% *}"
run_buildah manifest create foo
run_buildah manifest add --artifact --artifact-type image/jpeg --artifact-layer-type image/not-validated --artifact-config-type text/x-not-really --artifact-subject busybox foo $TEST_SCRATCH_DIR/randomfile2
run_buildah manifest add --artifact --artifact-type image/png --artifact-layer-type image/not-validated --artifact-config-type text/x-not-really --artifact-subject busybox foo $TEST_SCRATCH_DIR/randomfile
digest="${output##* }"
alg="${digest%%:*}"
encoded="${digest##*:}"
run_buildah manifest annotate --annotation red=blue foo $TEST_SCRATCH_DIR/randomfile
run_buildah manifest inspect foo
assert "$output" =~ '"image/png"'
assert "$output" =~ '"red": "blue"'
run_buildah manifest push --all foo oci:$TEST_SCRATCH_DIR/pushed
run cmp $TEST_SCRATCH_DIR/randomfile $TEST_SCRATCH_DIR/pushed/blobs/sha256/$blobencoded
assert "$status" -eq 0 "pushed copy of random file did not match original"
run cat $TEST_SCRATCH_DIR/pushed/blobs/$alg/$encoded
assert "$status" -eq 0 "artifact manifest not found in expected location"
assert "$output" =~ '"artifactType":"image/png"' "cat $TEST_SCRATCH_DIR/pushed/blobs/$alg/$encoded"
assert "$output" =~ '"mediaType":"image/not-validated"' "cat $TEST_SCRATCH_DIR/pushed/blobs/$alg/$encoded"
assert "$output" =~ '"mediaType":"text/x-not-really"' "cat $TEST_SCRATCH_DIR/pushed/blobs/$alg/$encoded"
run_buildah manifest rm foo
}
@test "manifest-add local image" {
target=scratch-image
run_buildah bud $WITH_POLICY_JSON -t ${target} $BUDFILES/from-scratch
@@ -70,6 +105,44 @@ IMAGE_LIST_S390X_INSTANCE_DIGEST=sha256:882a20ee0df7399a445285361d38b711c299ca09
expect_output --substring ${IMAGE_LIST_S390X_INSTANCE_DIGEST}
}
@test "manifest-annotate global annotation" {
_prefetch busybox
run_buildah manifest create foo
run_buildah manifest add foo busybox
run_buildah manifest annotate --index --annotation red=blue foo
run_buildah manifest inspect foo
assert "$output" =~ '"red": "blue"'
}
@test "manifest-annotate instance annotation" {
_prefetch busybox
run_buildah manifest create foo
run_buildah manifest add foo busybox
instance="${output##* }"
run_buildah manifest annotate --annotation red=blue foo "${instance}"
run_buildah manifest annotate --os OperatingSystem foo "${instance}"
run_buildah manifest annotate --arch aRCHITECTURE foo "${instance}"
run_buildah manifest annotate --variant vARIANT foo "${instance}"
run_buildah manifest annotate --features FEATURE1 --features FEATURE2 foo "${instance}"
run_buildah manifest annotate --os-features OSFEATURE1 --os-features OSFEATURE2 foo "${instance}"
run_buildah manifest inspect foo
assert "$output" =~ '"red": "blue"'
assert "$output" =~ '"os": "OperatingSystem"'
assert "$output" =~ '"architecture": "aRCHITECTURE"'
assert "$output" =~ '"variant": "vARIANT"'
}
@test "manifest-annotate subject" {
_prefetch busybox "${IMAGE_LIST_INSTANCE##*://}"
run_buildah manifest create foo
run_buildah manifest add foo busybox
run_buildah manifest annotate --subject "${IMAGE_LIST_INSTANCE##*://}" foo
run_buildah inspect -f '{{ .FromImageDigest }}' "${IMAGE_LIST_INSTANCE##*://}"
imagedigest="$output"
run_buildah manifest inspect foo
assert "$output" =~ "$imagedigest"
}
@test "manifest-remove" {
run_buildah manifest create foo
run_buildah manifest add --all foo ${IMAGE_LIST}