mirror of https://github.com/containers/podman.git
Merge pull request #6063 from QiWang19/manifest-annotate
manifest annotate
This commit is contained in:
commit
7885b5cd52
|
@ -0,0 +1,56 @@
|
|||
package manifest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/containers/libpod/cmd/podman/registry"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
manifestAnnotateOpts = entities.ManifestAnnotateOptions{}
|
||||
annotateCmd = &cobra.Command{
|
||||
Use: "annotate [flags] LIST IMAGE",
|
||||
Short: "Add or update information about an entry in a manifest list or image index",
|
||||
Long: "Adds or updates information about an entry in a manifest list or image index.",
|
||||
RunE: annotate,
|
||||
Example: `podman manifest annotate --annotation left=right mylist:v1.11 image:v1.11-amd64`,
|
||||
Args: cobra.ExactArgs(2),
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||
Command: annotateCmd,
|
||||
Parent: manifestCmd,
|
||||
})
|
||||
flags := annotateCmd.Flags()
|
||||
flags.StringSliceVar(&manifestAnnotateOpts.Annotation, "annotation", nil, "set an `annotation` for the specified image")
|
||||
flags.StringVar(&manifestAnnotateOpts.Arch, "arch", "", "override the `architecture` of the specified image")
|
||||
flags.StringSliceVar(&manifestAnnotateOpts.Features, "features", nil, "override the `features` of the specified image")
|
||||
flags.StringVar(&manifestAnnotateOpts.OS, "os", "", "override the `OS` of the specified image")
|
||||
flags.StringSliceVar(&manifestAnnotateOpts.OSFeatures, "os-features", nil, "override the OS `features` of the specified image")
|
||||
flags.StringVar(&manifestAnnotateOpts.OSVersion, "os-version", "", "override the OS `version` of the specified image")
|
||||
flags.StringVar(&manifestAnnotateOpts.Variant, "variant", "", "override the `variant` of the specified image")
|
||||
}
|
||||
|
||||
func annotate(cmd *cobra.Command, args []string) error {
|
||||
listImageSpec := args[0]
|
||||
instanceSpec := args[1]
|
||||
if listImageSpec == "" {
|
||||
return errors.Errorf(`invalid image name "%s"`, listImageSpec)
|
||||
}
|
||||
if instanceSpec == "" {
|
||||
return errors.Errorf(`invalid image digest "%s"`, instanceSpec)
|
||||
}
|
||||
updatedListID, err := registry.ImageEngine().ManifestAnnotate(context.Background(), args, manifestAnnotateOpts)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error removing from manifest list %s", listImageSpec)
|
||||
}
|
||||
fmt.Printf("%s\n", updatedListID)
|
||||
return nil
|
||||
}
|
|
@ -15,8 +15,10 @@ var (
|
|||
Long: manifestDescription,
|
||||
TraverseChildren: true,
|
||||
RunE: validate.SubCommandExists,
|
||||
Example: `podman manifest create localhost/list
|
||||
podman manifest inspect localhost/list`,
|
||||
Example: `podman manifest add mylist:v1.11 image:v1.11-amd64
|
||||
podman manifest create localhost/list
|
||||
podman manifest inspect localhost/list
|
||||
podman manifest annotate --annotation left=right mylist:v1.11 image:v1.11-amd64`,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -1782,6 +1782,33 @@ _podman_manifest_add() {
|
|||
esac
|
||||
}
|
||||
|
||||
_podman_manifest_annotate() {
|
||||
local options_with_args="
|
||||
--annotation
|
||||
--arch
|
||||
--features
|
||||
--os
|
||||
--os-features
|
||||
--os-version
|
||||
--variant
|
||||
"
|
||||
|
||||
local boolean_options="
|
||||
--help
|
||||
-h
|
||||
"
|
||||
|
||||
_complete_ "$options_with_args" "$boolean_options"
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__podman_complete_images --id
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_podman_manifest_create() {
|
||||
local boolean_options="
|
||||
--all
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
% podman-manifest-annotate(1)
|
||||
|
||||
## NAME
|
||||
podman\-manifest\-annotate - Add or update information about an entry in a manifest list or image index
|
||||
|
||||
## SYNOPSIS
|
||||
**podman manifest annotate** [options...] *listnameorindexname* *imagemanifestdigest*
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
Adds or updates information about an image included in a manifest list or image index.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
**--annotation** *annotation=value*
|
||||
|
||||
Set an annotation on the entry for the specified image.
|
||||
|
||||
**--arch**
|
||||
|
||||
Override the architecture which the list or index records as a requirement for
|
||||
the image. This is usually automatically retrieved from the image's
|
||||
configuration information, so it is rarely necessary to use this option.
|
||||
|
||||
|
||||
**--features**
|
||||
|
||||
Specify the features list which the list or index records as requirements for
|
||||
the image. This option is rarely used.
|
||||
|
||||
**--os**
|
||||
|
||||
Override the OS which the list or index records as a requirement for the image.
|
||||
This is usually automatically retrieved from the image's configuration
|
||||
information, so it is rarely necessary to use this option.
|
||||
|
||||
**--os-features**
|
||||
|
||||
Specify the OS features list which the list or index records as requirements
|
||||
for the image. This option is rarely used.
|
||||
|
||||
**--os-version**
|
||||
|
||||
Specify the OS version which the list or index records as a requirement for the
|
||||
image. This option is rarely used.
|
||||
|
||||
**--variant**
|
||||
|
||||
Specify the variant which the list or index records for the image. This option
|
||||
is typically used to distinguish between multiple entries which share the same
|
||||
architecture value, but which expect different versions of its instruction set.
|
||||
|
||||
## EXAMPLE
|
||||
|
||||
```
|
||||
podman manifest annotate --arch arm64 --variant v8 mylist:v1.11 sha256:59eec8837a4d942cc19a52b8c09ea75121acc38114a2c68b98983ce9356b8610
|
||||
07ec8dc22b5dba3a33c60b68bce28bbd2b905e383fdb32a90708fa5eeac13a07: sha256:59eec8837a4d942cc19a52b8c09ea75121acc38114a2c68b98983ce9356b8610
|
||||
```
|
||||
|
||||
## SEE ALSO
|
||||
podman(1), podman-manifest(1), podman-manifest-add(1), podman-manifest-create(1), podman-manifest-inspect(1), podman-rmi(1)
|
|
@ -13,11 +13,12 @@ The `podman manifest` command provides subcommands which can be used to:
|
|||
|
||||
## SUBCOMMANDS
|
||||
|
||||
| Command | Man Page | Description |
|
||||
| ------- | ---------------------------------------------------------- | --------------------------------------------------------------------------- |
|
||||
| add | [podman-manifest-add(1)](podman-manifest-add.1.md) | Add an image to a manifest list or image index. |
|
||||
| create | [podman-manifest-create(1)](podman-manifest-create.1.md) | Create a manifest list or image index. |
|
||||
| inspect | [podman-manifest-inspect(1)](podman-manifest-inspect.1.md) | Display a manifest list or image index. |
|
||||
| Command | Man Page | Description |
|
||||
| -------- | ------------------------------------------------------------ | --------------------------------------------------------------------------- |
|
||||
| add | [podman-manifest-add(1)](podman-manifest-add.1.md) | Add an image to a manifest list or image index. |
|
||||
| annotate | [podman-manifest-annotate(1)](podman-manifest-annotate.1.md) | Add or update information about an entry in a manifest list or image index. |
|
||||
| create | [podman-manifest-create(1)](podman-manifest-create.1.md) | Create a manifest list or image index. |
|
||||
| inspect | [podman-manifest-inspect(1)](podman-manifest-inspect.1.md) | Display a manifest list or image index. |
|
||||
|
||||
## SEE ALSO
|
||||
podman(1), podman-manifest-add(1), podman-manifest-create(1), podman-manifest-inspect(1)
|
||||
podman(1), podman-manifest-add(1), podman-manifest-annotate(1), podman-manifest-create(1), podman-manifest-inspect(1)
|
||||
|
|
|
@ -24,6 +24,18 @@ type ManifestAddOpts struct {
|
|||
Variant string `json:"variant"`
|
||||
}
|
||||
|
||||
// ManifestAnnotateOptions defines the options for
|
||||
// manifest annotate
|
||||
type ManifestAnnotateOpts struct {
|
||||
Annotation map[string]string `json:"annotation"`
|
||||
Arch string `json:"arch"`
|
||||
Features []string `json:"features"`
|
||||
OS string `json:"os"`
|
||||
OSFeatures []string `json:"os_feature"`
|
||||
OSVersion string `json:"os_version"`
|
||||
Variant string `json:"variant"`
|
||||
}
|
||||
|
||||
// InspectManifest returns a dockerized version of the manifest list
|
||||
func (i *Image) InspectManifest() (*manifest.Schema2List, error) {
|
||||
list, err := i.getManifestList()
|
||||
|
@ -158,3 +170,47 @@ func (i *Image) PushManifest(dest types.ImageReference, opts manifests.PushOptio
|
|||
_, d, err := list.Push(context.Background(), dest, opts)
|
||||
return d, err
|
||||
}
|
||||
|
||||
// AnnotateManifest updates an image configuration of a manifest list.
|
||||
func (i *Image) AnnotateManifest(systemContext types.SystemContext, d digest.Digest, opts ManifestAnnotateOpts) (string, error) {
|
||||
list, err := i.getManifestList()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(opts.OS) > 0 {
|
||||
if err := list.SetOS(d, opts.OS); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
if len(opts.OSVersion) > 0 {
|
||||
if err := list.SetOSVersion(d, opts.OSVersion); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
if len(opts.Features) > 0 {
|
||||
if err := list.SetFeatures(d, opts.Features); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
if len(opts.OSFeatures) > 0 {
|
||||
if err := list.SetOSFeatures(d, opts.OSFeatures); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
if len(opts.Arch) > 0 {
|
||||
if err := list.SetArchitecture(d, opts.Arch); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
if len(opts.Variant) > 0 {
|
||||
if err := list.SetVariant(d, opts.Variant); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
if len(opts.Annotation) > 0 {
|
||||
if err := list.SetAnnotations(&d, opts.Annotation); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return list.SaveToImage(i.imageruntime.store, i.ID(), nil, "")
|
||||
}
|
||||
|
|
|
@ -124,3 +124,24 @@ func Push(ctx context.Context, name string, destination *string, all *bool) (str
|
|||
}
|
||||
return idr.ID, response.Process(&idr)
|
||||
}
|
||||
|
||||
// Annotate updates the image configuration of a given manifest list
|
||||
func Annotate(ctx context.Context, name, digest string, options image.ManifestAnnotateOpts) (string, error) {
|
||||
var idr handlers.IDResponse
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("digest", digest)
|
||||
optionsString, err := jsoniter.MarshalToString(options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
stringReader := strings.NewReader(optionsString)
|
||||
response, err := conn.DoRequest(stringReader, http.MethodPost, "/manifests/%s/annotate", params, name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return idr.ID, response.Process(&idr)
|
||||
}
|
||||
|
|
|
@ -118,6 +118,26 @@ var _ = Describe("Podman containers ", func() {
|
|||
Expect(len(data.Manifests)).To(BeZero())
|
||||
})
|
||||
|
||||
It("annotate manifest", func() {
|
||||
id, err := manifests.Create(bt.conn, []string{"quay.io/libpod/foobar:latest"}, []string{}, nil)
|
||||
Expect(err).To(BeNil())
|
||||
opts := image.ManifestAddOpts{Images: []string{"docker.io/library/alpine:latest"}}
|
||||
|
||||
_, err = manifests.Add(bt.conn, id, opts)
|
||||
Expect(err).To(BeNil())
|
||||
data, err := manifests.Inspect(bt.conn, id)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(len(data.Manifests)).To(BeNumerically("==", 1))
|
||||
digest := data.Manifests[0].Digest.String()
|
||||
annoOpts := image.ManifestAnnotateOpts{OS: "foo"}
|
||||
_, err = manifests.Annotate(bt.conn, id, digest, annoOpts)
|
||||
Expect(err).To(BeNil())
|
||||
list, err := manifests.Inspect(bt.conn, id)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(len(list.Manifests)).To(BeNumerically("==", 1))
|
||||
Expect(list.Manifests[0].Platform.OS).To(Equal("foo"))
|
||||
})
|
||||
|
||||
It("push manifest", func() {
|
||||
Skip("TODO")
|
||||
})
|
||||
|
|
|
@ -29,4 +29,5 @@ type ImageEngine interface {
|
|||
ManifestCreate(ctx context.Context, names, images []string, opts ManifestCreateOptions) (string, error)
|
||||
ManifestInspect(ctx context.Context, name string) ([]byte, error)
|
||||
ManifestAdd(ctx context.Context, opts ManifestAddOptions) (string, error)
|
||||
ManifestAnnotate(ctx context.Context, names []string, opts ManifestAnnotateOptions) (string, error)
|
||||
}
|
||||
|
|
|
@ -14,3 +14,13 @@ type ManifestAddOptions struct {
|
|||
OSVersion string `json:"os_version" schema:"os_version"`
|
||||
Variant string `json:"variant" schema:"variant"`
|
||||
}
|
||||
|
||||
type ManifestAnnotateOptions struct {
|
||||
Annotation []string `json:"annotation"`
|
||||
Arch string `json:"arch" schema:"arch"`
|
||||
Features []string `json:"features" schema:"features"`
|
||||
OS string `json:"os" schema:"os"`
|
||||
OSFeatures []string `json:"os_features" schema:"os_features"`
|
||||
OSVersion string `json:"os_version" schema:"os_version"`
|
||||
Variant string `json:"variant" schema:"variant"`
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
libpodImage "github.com/containers/libpod/libpod/image"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/containers/libpod/pkg/util"
|
||||
"github.com/opencontainers/go-digest"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
@ -71,7 +72,7 @@ func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAd
|
|||
}
|
||||
listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(listImageSpec)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error retriving local image from image name %s", listImageSpec)
|
||||
return "", errors.Wrapf(err, "error retrieving local image from image name %s", listImageSpec)
|
||||
}
|
||||
|
||||
manifestAddOpts := libpodImage.ManifestAddOpts{
|
||||
|
@ -100,3 +101,39 @@ func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAd
|
|||
}
|
||||
return listID, nil
|
||||
}
|
||||
|
||||
// ManifestAnnotate updates an entry of the manifest list
|
||||
func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, names []string, opts entities.ManifestAnnotateOptions) (string, error) {
|
||||
listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(names[0])
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error retreiving local image from image name %s", names[0])
|
||||
}
|
||||
digest, err := digest.Parse(names[1])
|
||||
if err != nil {
|
||||
return "", errors.Errorf(`invalid image digest "%s": %v`, names[1], err)
|
||||
}
|
||||
manifestAnnotateOpts := libpodImage.ManifestAnnotateOpts{
|
||||
Arch: opts.Arch,
|
||||
Features: opts.Features,
|
||||
OS: opts.OS,
|
||||
OSFeatures: opts.OSFeatures,
|
||||
OSVersion: opts.OSVersion,
|
||||
Variant: opts.Variant,
|
||||
}
|
||||
if len(opts.Annotation) > 0 {
|
||||
annotations := make(map[string]string)
|
||||
for _, annotationSpec := range opts.Annotation {
|
||||
spec := strings.SplitN(annotationSpec, "=", 2)
|
||||
if len(spec) != 2 {
|
||||
return "", errors.Errorf("no value given for annotation %q", spec[0])
|
||||
}
|
||||
annotations[spec[0]] = spec[1]
|
||||
}
|
||||
manifestAnnotateOpts.Annotation = annotations
|
||||
}
|
||||
updatedListID, err := listImage.AnnotateManifest(*ir.Libpod.SystemContext(), digest, manifestAnnotateOpts)
|
||||
if err == nil {
|
||||
return fmt.Sprintf("%s: %s", updatedListID, digest.String()), nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package tunnel
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/libpod/libpod/image"
|
||||
|
@ -62,3 +63,31 @@ func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAd
|
|||
}
|
||||
return listID, nil
|
||||
}
|
||||
|
||||
// ManifestAnnotate updates an entry of the manifest list
|
||||
func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, names []string, opts entities.ManifestAnnotateOptions) (string, error) {
|
||||
manifestAnnotateOpts := image.ManifestAnnotateOpts{
|
||||
Arch: opts.Arch,
|
||||
Features: opts.Features,
|
||||
OS: opts.OS,
|
||||
OSFeatures: opts.OSFeatures,
|
||||
OSVersion: opts.OSVersion,
|
||||
Variant: opts.Variant,
|
||||
}
|
||||
if len(opts.Annotation) > 0 {
|
||||
annotations := make(map[string]string)
|
||||
for _, annotationSpec := range opts.Annotation {
|
||||
spec := strings.SplitN(annotationSpec, "=", 2)
|
||||
if len(spec) != 2 {
|
||||
return "", errors.Errorf("no value given for annotation %q", spec[0])
|
||||
}
|
||||
annotations[spec[0]] = spec[1]
|
||||
}
|
||||
manifestAnnotateOpts.Annotation = annotations
|
||||
}
|
||||
updatedListID, err := manifests.Annotate(ctx, names[0], names[1], manifestAnnotateOpts)
|
||||
if err != nil {
|
||||
return updatedListID, errors.Wrapf(err, "error annotating %s of manifest list %s", names[1], names[0])
|
||||
}
|
||||
return fmt.Sprintf("%s :%s", updatedListID, names[1]), nil
|
||||
}
|
||||
|
|
|
@ -98,4 +98,20 @@ var _ = Describe("Podman manifest", func() {
|
|||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(session.OutputToString()).To(ContainSubstring(`"os": "bar"`))
|
||||
})
|
||||
|
||||
It("podman manifest annotate", func() {
|
||||
session := podmanTest.Podman([]string{"manifest", "create", "foo"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
session = podmanTest.Podman([]string{"manifest", "add", "foo", imageListInstance})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
session = podmanTest.Podman([]string{"manifest", "annotate", "--arch", "bar", "foo", imageListARM64InstanceDigest})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
session = podmanTest.Podman([]string{"manifest", "inspect", "foo"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(session.OutputToString()).To(ContainSubstring(`"architecture": "bar"`))
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue