Manifest remove, push
Implements podman manifest remove and podman manifest push. Signed-off-by: Qi Wang <qiwan@redhat.com>
This commit is contained in:
		
							parent
							
								
									0eb905ff2c
								
							
						
					
					
						commit
						5621f5199d
					
				|  | @ -18,7 +18,9 @@ var ( | ||||||
| 		Example: `podman manifest add mylist:v1.11 image:v1.11-amd64 | 		Example: `podman manifest add mylist:v1.11 image:v1.11-amd64 | ||||||
|   podman manifest create localhost/list |   podman manifest create localhost/list | ||||||
|   podman manifest inspect localhost/list |   podman manifest inspect localhost/list | ||||||
|   podman manifest annotate --annotation left=right mylist:v1.11 image:v1.11-amd64`, |   podman manifest annotate --annotation left=right mylist:v1.11 image:v1.11-amd64 | ||||||
|  |   podman manifest push mylist:v1.11 quay.io/myimagelist | ||||||
|  |   podman manifest remove mylist:v1.11 sha256:15352d97781ffdf357bf3459c037be3efac4133dc9070c2dce7eca7c05c3e736`, | ||||||
| 	} | 	} | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,66 @@ | ||||||
|  | package manifest | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 
 | ||||||
|  | 	"github.com/containers/common/pkg/auth" | ||||||
|  | 	"github.com/containers/libpod/cmd/podman/registry" | ||||||
|  | 	"github.com/containers/libpod/pkg/domain/entities" | ||||||
|  | 	"github.com/pkg/errors" | ||||||
|  | 	"github.com/spf13/cobra" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	manifestPushOpts = entities.ManifestPushOptions{} | ||||||
|  | 	pushCmd          = &cobra.Command{ | ||||||
|  | 		Use:     "push [flags] SOURCE DESTINATION", | ||||||
|  | 		Short:   "Push a manifest list or image index to a registry", | ||||||
|  | 		Long:    "Pushes manifest lists and image indexes to registries.", | ||||||
|  | 		RunE:    push, | ||||||
|  | 		Example: `podman manifest push mylist:v1.11 quay.io/myimagelist`, | ||||||
|  | 		Args:    cobra.ExactArgs(2), | ||||||
|  | 	} | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	registry.Commands = append(registry.Commands, registry.CliCommand{ | ||||||
|  | 		Mode:    []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, | ||||||
|  | 		Command: pushCmd, | ||||||
|  | 		Parent:  manifestCmd, | ||||||
|  | 	}) | ||||||
|  | 	flags := pushCmd.Flags() | ||||||
|  | 	flags.BoolVar(&manifestPushOpts.Purge, "purge", false, "remove the manifest list if push succeeds") | ||||||
|  | 	flags.BoolVar(&manifestPushOpts.All, "all", false, "also push the images in the list") | ||||||
|  | 	flags.StringVar(&manifestPushOpts.Authfile, "authfile", auth.GetDefaultAuthFile(), "path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") | ||||||
|  | 	flags.StringVar(&manifestPushOpts.CertDir, "cert-dir", "", "use certificates at the specified path to access the registry") | ||||||
|  | 	flags.StringVar(&manifestPushOpts.Creds, "creds", "", "use `[username[:password]]` for accessing the registry") | ||||||
|  | 	flags.StringVar(&manifestPushOpts.DigestFile, "digestfile", "", "after copying the image, write the digest of the resulting digest to the file") | ||||||
|  | 	flags.StringVarP(&manifestPushOpts.Format, "format", "f", "", "manifest type (oci or v2s2) to attempt to use when pushing the manifest list (default is manifest type of source)") | ||||||
|  | 	flags.BoolVarP(&manifestPushOpts.RemoveSignatures, "remove-signatures", "", false, "don't copy signatures when pushing images") | ||||||
|  | 	flags.StringVar(&manifestPushOpts.SignBy, "sign-by", "", "sign the image using a GPG key with the specified `FINGERPRINT`") | ||||||
|  | 	flags.BoolVar(&manifestPushOpts.TlsVerify, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry") | ||||||
|  | 	flags.BoolVarP(&manifestPushOpts.Quiet, "quiet", "q", false, "don't output progress information when pushing lists") | ||||||
|  | 	if registry.IsRemote() { | ||||||
|  | 		_ = flags.MarkHidden("authfile") | ||||||
|  | 		_ = flags.MarkHidden("cert-dir") | ||||||
|  | 		_ = flags.MarkHidden("tls-verify") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func push(cmd *cobra.Command, args []string) error { | ||||||
|  | 	if err := auth.CheckAuthFile(manifestPushOpts.Authfile); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	listImageSpec := args[0] | ||||||
|  | 	destSpec := args[1] | ||||||
|  | 	if listImageSpec == "" { | ||||||
|  | 		return errors.Errorf(`invalid image name "%s"`, listImageSpec) | ||||||
|  | 	} | ||||||
|  | 	if destSpec == "" { | ||||||
|  | 		return errors.Errorf(`invalid destination "%s"`, destSpec) | ||||||
|  | 	} | ||||||
|  | 	if err := registry.ImageEngine().ManifestPush(context.Background(), args, manifestPushOpts); err != nil { | ||||||
|  | 		return errors.Wrapf(err, "error pushing manifest %s to %s", listImageSpec, destSpec) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | @ -0,0 +1,47 @@ | ||||||
|  | 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 ( | ||||||
|  | 	removeCmd = &cobra.Command{ | ||||||
|  | 		Use:     "remove [flags] LIST IMAGE", | ||||||
|  | 		Short:   "Remove an entry from a manifest list or image index", | ||||||
|  | 		Long:    "Removes an image from a manifest list or image index.", | ||||||
|  | 		RunE:    remove, | ||||||
|  | 		Example: `podman manifest remove mylist:v1.11 sha256:15352d97781ffdf357bf3459c037be3efac4133dc9070c2dce7eca7c05c3e736`, | ||||||
|  | 		Args:    cobra.ExactArgs(2), | ||||||
|  | 	} | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	registry.Commands = append(registry.Commands, registry.CliCommand{ | ||||||
|  | 		Mode:    []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, | ||||||
|  | 		Command: removeCmd, | ||||||
|  | 		Parent:  manifestCmd, | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func remove(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().ManifestRemove(context.Background(), args) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return errors.Wrapf(err, "error removing from manifest list %s", listImageSpec) | ||||||
|  | 	} | ||||||
|  | 	fmt.Printf("%s\n", updatedListID) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | @ -1742,6 +1742,8 @@ _podman_manifest() { | ||||||
|      add |      add | ||||||
|      create |      create | ||||||
|      inspect |      inspect | ||||||
|  |      push | ||||||
|  |      remove | ||||||
|     " |     " | ||||||
|      __podman_subcommands "$subcommands" && return |      __podman_subcommands "$subcommands" && return | ||||||
| 
 | 
 | ||||||
|  | @ -1838,6 +1840,57 @@ _podman_manifest_inspect() { | ||||||
|     esac |     esac | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | _podman_manifest_push() { | ||||||
|  | 	local options_with_args=" | ||||||
|  |       --authfile | ||||||
|  |       --cert-dir | ||||||
|  |       --creds | ||||||
|  |       --digestfile | ||||||
|  |       --format | ||||||
|  |       -f | ||||||
|  |       --sign-by | ||||||
|  |       --signature-policy, | ||||||
|  |     " | ||||||
|  | 
 | ||||||
|  |     local boolean_options=" | ||||||
|  |       --all | ||||||
|  |       --purge | ||||||
|  |       --help | ||||||
|  |       -h | ||||||
|  |       --remove-signatures | ||||||
|  |       --tls-verify | ||||||
|  |       --quiet | ||||||
|  |     " | ||||||
|  | 
 | ||||||
|  |   _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_remove() { | ||||||
|  | 	local options_with_args=" | ||||||
|  |     " | ||||||
|  | 
 | ||||||
|  |     local boolean_options=" | ||||||
|  |     " | ||||||
|  | 
 | ||||||
|  |   _complete_ "$options_with_args" "$boolean_options" | ||||||
|  |     case "$cur" in | ||||||
|  |         -*) | ||||||
|  |             COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur")) | ||||||
|  |             ;; | ||||||
|  |         *) | ||||||
|  |             __podman_complete_images --id | ||||||
|  |             ;; | ||||||
|  |     esac | ||||||
|  | } | ||||||
|  | 
 | ||||||
| _podman_pull() { | _podman_pull() { | ||||||
|     local options_with_args=" |     local options_with_args=" | ||||||
|     --authfile |     --authfile | ||||||
|  |  | ||||||
|  | @ -73,4 +73,4 @@ podman manifest add --arch arm64 --variant v8 mylist:v1.11 docker://71c201d10fff | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ## SEE ALSO | ## SEE ALSO | ||||||
| podman(1), podman-manifest(1), podman-manifest-create(1), podman-manifest-inspect(1), podman-rmi(1) | podman(1), podman-manifest(1), podman-manifest-create(1), podman-manifest-inspect(1), podman-manifest-push(1), podman-manifest-remove(1), podman-rmi(1) | ||||||
|  |  | ||||||
|  | @ -40,4 +40,4 @@ podman manifest create --all mylist:v1.11 docker://fedora | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ## SEE ALSO | ## SEE ALSO | ||||||
| podman(1), podman-manifest(1), podman-manifest-add(1), podman-manifest-inspect(1), podman-rmi(1) | podman(1), podman-manifest(1), podman-manifest-add(1), podman-manifest-inspect(1), podman-manifest-push(1), podman-manifest-remove(1), podman-rmi(1) | ||||||
|  |  | ||||||
|  | @ -21,4 +21,4 @@ podman manifest inspect mylist:v1.11 | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ## SEE ALSO | ## SEE ALSO | ||||||
| podman(1), podman-manifest(1), podman-manifest-create(1), podman-manifest-add(1), podman-rmi(1) | podman(1), podman-manifest(1), podman-manifest-create(1), podman-manifest-add(1), podman-manifest-push(1), podman-manifest-remove(1), podman-rmi(1) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,72 @@ | ||||||
|  | % podman-manifest-push(1) | ||||||
|  | 
 | ||||||
|  | ## NAME | ||||||
|  | podman\-manifest\-push - Push a manifest list or image index to a registry | ||||||
|  | 
 | ||||||
|  | ## SYNOPSIS | ||||||
|  | **podman manifest push** [options...] *listnameorindexname* *transport:details* | ||||||
|  | 
 | ||||||
|  | ## DESCRIPTION | ||||||
|  | Pushes a manifest list or image index to a registry. | ||||||
|  | 
 | ||||||
|  | ## RETURN VALUE | ||||||
|  | The list image's ID and the digest of the image's manifest. | ||||||
|  | 
 | ||||||
|  | ## OPTIONS | ||||||
|  | 
 | ||||||
|  | **--all** | ||||||
|  | 
 | ||||||
|  | Push the images mentioned in the manifest list or image index, in addition to | ||||||
|  | the list or index itself. | ||||||
|  | 
 | ||||||
|  | **--authfile** *path* | ||||||
|  | 
 | ||||||
|  | Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `podman login`. | ||||||
|  | If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. (Not available for remote commands) | ||||||
|  | 
 | ||||||
|  | Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE | ||||||
|  | environment variable. `export REGISTRY_AUTH_FILE=path` | ||||||
|  | 
 | ||||||
|  | **--cert-dir** *path* | ||||||
|  | 
 | ||||||
|  | Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. | ||||||
|  | Default certificates directory is _/etc/containers/certs.d_. (Not available for remote commands) | ||||||
|  | 
 | ||||||
|  | **--creds** *creds* | ||||||
|  | 
 | ||||||
|  | The [username[:password]] to use to authenticate with the registry if required. | ||||||
|  | If one or both values are not supplied, a command line prompt will appear and the | ||||||
|  | value can be entered.  The password is entered without echo. | ||||||
|  | 
 | ||||||
|  | **--digestfile** *Digestfile* | ||||||
|  | 
 | ||||||
|  | After copying the image, write the digest of the resulting image to the file. | ||||||
|  | 
 | ||||||
|  | **--format, -f** | ||||||
|  | 
 | ||||||
|  | Manifest list type (oci or v2s2) to use when pushing the list (default is oci). | ||||||
|  | 
 | ||||||
|  | **--purge** | ||||||
|  | 
 | ||||||
|  | Delete the manifest list or image index from local storage if pushing succeeds. | ||||||
|  | 
 | ||||||
|  | **--remove-signatures** | ||||||
|  | 
 | ||||||
|  | Don't copy signatures when pushing images. | ||||||
|  | 
 | ||||||
|  | **--sign-by** *fingerprint* | ||||||
|  | 
 | ||||||
|  | Sign the pushed images using the GPG key that matches the specified fingerprint. | ||||||
|  | 
 | ||||||
|  | **--tls-verify** *bool-value* | ||||||
|  | 
 | ||||||
|  | Require HTTPS and verify certificates when talking to container registries (defaults to true) (Not available for remote commands) | ||||||
|  | 
 | ||||||
|  | ## EXAMPLE | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | podman manifest push mylist:v1.11 docker://registry.example.org/mylist:v1.11 | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## SEE ALSO | ||||||
|  | podman(1), podman-manifest(1), podman-manifest-add(1), podman-manifest-create(1), podman-manifest-inspect(1), podman-manifest-remove(1), podman-rmi(1) | ||||||
|  | @ -0,0 +1,23 @@ | ||||||
|  | % podman-manifest-remove(1) | ||||||
|  | 
 | ||||||
|  | ## NAME | ||||||
|  | podman\-manifest\-remove - Remove an image from a manifest list or image index | ||||||
|  | 
 | ||||||
|  | ## SYNOPSIS | ||||||
|  | **podman manifest remove** *listnameorindexname* *transport:details* | ||||||
|  | 
 | ||||||
|  | ## DESCRIPTION | ||||||
|  | Removes the image with the specified digest from the specified manifest list or image index. | ||||||
|  | 
 | ||||||
|  | ## RETURN VALUE | ||||||
|  | The list image's ID and the digest of the removed image's manifest. | ||||||
|  | 
 | ||||||
|  | ## EXAMPLE | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | podman manifest remove mylist:v1.11 sha256:cb8a924afdf0229ef7515d9e5b3024e23b3eb03ddbba287f4a19c6ac90b8d221 | ||||||
|  | e604eabaaee4858232761b4fef84e2316ed8f93e15eceafce845966ee3400036 :sha256:cb8a924afdf0229ef7515d9e5b3024e23b3eb03ddbba287f4a19c6ac90b8d221 | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## SEE ALSO | ||||||
|  | podman(1), podman-manifest(1), podman-manifest-add(1), podman-manifest-create(1), podman-manifest-inspect(1), podman-manifest-push(1), podman-rmi(1) | ||||||
|  | @ -19,6 +19,8 @@ The `podman manifest` command provides subcommands which can be used to: | ||||||
| | annotate | [podman-manifest-annotate(1)](podman-manifest-annotate.1.md) | Add or update information about an entry in 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.                                      | | | 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.                                     | | | inspect  | [podman-manifest-inspect(1)](podman-manifest-inspect.1.md)   | Display a manifest list or image index.                                     | | ||||||
|  | | push     | [podman-manifest-push(1)](podman-manifest-push.1.md)         | Push a manifest list or image index to a registry.                          | | ||||||
|  | | remove   | [podman-manifest-remove(1)](podman-manifest-remove.1.md)     | Remove an image from a manifest list or image index.                        | | ||||||
| 
 | 
 | ||||||
| ## SEE ALSO | ## SEE ALSO | ||||||
| podman(1), podman-manifest-add(1), podman-manifest-annotate(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), podman-manifest-push(1), podman-manifest-remove(1) | ||||||
|  |  | ||||||
|  | @ -30,4 +30,6 @@ type ImageEngine interface { | ||||||
| 	ManifestInspect(ctx context.Context, name string) ([]byte, error) | 	ManifestInspect(ctx context.Context, name string) ([]byte, error) | ||||||
| 	ManifestAdd(ctx context.Context, opts ManifestAddOptions) (string, error) | 	ManifestAdd(ctx context.Context, opts ManifestAddOptions) (string, error) | ||||||
| 	ManifestAnnotate(ctx context.Context, names []string, opts ManifestAnnotateOptions) (string, error) | 	ManifestAnnotate(ctx context.Context, names []string, opts ManifestAnnotateOptions) (string, error) | ||||||
|  | 	ManifestRemove(ctx context.Context, names []string) (string, error) | ||||||
|  | 	ManifestPush(ctx context.Context, names []string, manifestPushOpts ManifestPushOptions) error | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -24,3 +24,8 @@ type ManifestAnnotateOptions struct { | ||||||
| 	OSVersion  string   `json:"os_version" schema:"os_version"` | 	OSVersion  string   `json:"os_version" schema:"os_version"` | ||||||
| 	Variant    string   `json:"variant" schema:"variant"` | 	Variant    string   `json:"variant" schema:"variant"` | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | type ManifestPushOptions struct { | ||||||
|  | 	Purge, Quiet, All, TlsVerify, RemoveSignatures       bool | ||||||
|  | 	Authfile, CertDir, Creds, DigestFile, Format, SignBy string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -6,15 +6,21 @@ import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"os" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/containers/buildah/manifests" | ||||||
| 	buildahUtil "github.com/containers/buildah/util" | 	buildahUtil "github.com/containers/buildah/util" | ||||||
|  | 	cp "github.com/containers/image/v5/copy" | ||||||
| 	"github.com/containers/image/v5/docker" | 	"github.com/containers/image/v5/docker" | ||||||
|  | 	"github.com/containers/image/v5/manifest" | ||||||
| 	"github.com/containers/image/v5/transports/alltransports" | 	"github.com/containers/image/v5/transports/alltransports" | ||||||
| 	libpodImage "github.com/containers/libpod/libpod/image" | 	libpodImage "github.com/containers/libpod/libpod/image" | ||||||
| 	"github.com/containers/libpod/pkg/domain/entities" | 	"github.com/containers/libpod/pkg/domain/entities" | ||||||
| 	"github.com/containers/libpod/pkg/util" | 	"github.com/containers/libpod/pkg/util" | ||||||
| 	"github.com/opencontainers/go-digest" | 	"github.com/opencontainers/go-digest" | ||||||
|  | 	imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" | ||||||
| 
 | 
 | ||||||
| 	"github.com/pkg/errors" | 	"github.com/pkg/errors" | ||||||
| ) | ) | ||||||
|  | @ -137,3 +143,68 @@ func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, names []string, opt | ||||||
| 	} | 	} | ||||||
| 	return "", err | 	return "", err | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // ManifestRemove removes specified digest from the specified manifest list
 | ||||||
|  | func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (string, error) { | ||||||
|  | 	instanceDigest, err := digest.Parse(names[1]) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", errors.Errorf(`invalid image digest "%s": %v`, names[1], err) | ||||||
|  | 	} | ||||||
|  | 	listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(names[0]) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", errors.Wrapf(err, "error retriving local image from image name %s", names[0]) | ||||||
|  | 	} | ||||||
|  | 	updatedListID, err := listImage.RemoveManifest(instanceDigest) | ||||||
|  | 	if err == nil { | ||||||
|  | 		return fmt.Sprintf("%s :%s\n", updatedListID, instanceDigest.String()), nil | ||||||
|  | 	} | ||||||
|  | 	return "", err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ManifestPush pushes a manifest list or image index to the destination
 | ||||||
|  | func (ir *ImageEngine) ManifestPush(ctx context.Context, names []string, opts entities.ManifestPushOptions) error { | ||||||
|  | 	listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(names[0]) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return errors.Wrapf(err, "error retriving local image from image name %s", names[0]) | ||||||
|  | 	} | ||||||
|  | 	dest, err := alltransports.ParseImageName(names[1]) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	var manifestType string | ||||||
|  | 	if opts.Format != "" { | ||||||
|  | 		switch opts.Format { | ||||||
|  | 		case "oci": | ||||||
|  | 			manifestType = imgspecv1.MediaTypeImageManifest | ||||||
|  | 		case "v2s2", "docker": | ||||||
|  | 			manifestType = manifest.DockerV2Schema2MediaType | ||||||
|  | 		default: | ||||||
|  | 			return errors.Errorf("unknown format %q. Choose on of the supported formats: 'oci' or 'v2s2'", opts.Format) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	options := manifests.PushOptions{ | ||||||
|  | 		Store:              ir.Libpod.GetStore(), | ||||||
|  | 		SystemContext:      ir.Libpod.SystemContext(), | ||||||
|  | 		ImageListSelection: cp.CopySpecificImages, | ||||||
|  | 		Instances:          nil, | ||||||
|  | 		RemoveSignatures:   opts.RemoveSignatures, | ||||||
|  | 		SignBy:             opts.SignBy, | ||||||
|  | 		ManifestType:       manifestType, | ||||||
|  | 	} | ||||||
|  | 	if opts.All { | ||||||
|  | 		options.ImageListSelection = cp.CopyAllImages | ||||||
|  | 	} | ||||||
|  | 	if !opts.Quiet { | ||||||
|  | 		options.ReportWriter = os.Stderr | ||||||
|  | 	} | ||||||
|  | 	digest, err := listImage.PushManifest(dest, options) | ||||||
|  | 	if err == nil && opts.Purge { | ||||||
|  | 		_, err = ir.Libpod.GetStore().DeleteImage(listImage.ID(), true) | ||||||
|  | 	} | ||||||
|  | 	if opts.DigestFile != "" { | ||||||
|  | 		if err = ioutil.WriteFile(opts.DigestFile, []byte(digest.String()), 0644); err != nil { | ||||||
|  | 			return buildahUtil.GetFailureCause(err, errors.Wrapf(err, "failed to write digest to file %q", opts.DigestFile)) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -91,3 +91,18 @@ func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, names []string, opt | ||||||
| 	} | 	} | ||||||
| 	return fmt.Sprintf("%s :%s", updatedListID, names[1]), nil | 	return fmt.Sprintf("%s :%s", updatedListID, names[1]), nil | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // ManifestRemove removes the digest from manifest list
 | ||||||
|  | func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (string, error) { | ||||||
|  | 	updatedListID, err := manifests.Remove(ctx, names[0], names[1]) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return updatedListID, errors.Wrapf(err, "error removing from manifest %s", names[0]) | ||||||
|  | 	} | ||||||
|  | 	return fmt.Sprintf("%s :%s\n", updatedListID, names[1]), nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ManifestPush pushes a manifest list or image index to the destination
 | ||||||
|  | func (ir *ImageEngine) ManifestPush(ctx context.Context, names []string, opts entities.ManifestPushOptions) error { | ||||||
|  | 	_, err := manifests.Push(ctx, names[0], &names[1], &opts.All) | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -2,6 +2,8 @@ package integration | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	. "github.com/containers/libpod/test/utils" | 	. "github.com/containers/libpod/test/utils" | ||||||
| 	. "github.com/onsi/ginkgo" | 	. "github.com/onsi/ginkgo" | ||||||
|  | @ -114,4 +116,90 @@ var _ = Describe("Podman manifest", func() { | ||||||
| 		Expect(session.ExitCode()).To(Equal(0)) | 		Expect(session.ExitCode()).To(Equal(0)) | ||||||
| 		Expect(session.OutputToString()).To(ContainSubstring(`"architecture": "bar"`)) | 		Expect(session.OutputToString()).To(ContainSubstring(`"architecture": "bar"`)) | ||||||
| 	}) | 	}) | ||||||
|  | 
 | ||||||
|  | 	It("podman manifest remove", func() { | ||||||
|  | 		session := podmanTest.Podman([]string{"manifest", "create", "foo"}) | ||||||
|  | 		session.WaitWithDefaultTimeout() | ||||||
|  | 		Expect(session.ExitCode()).To(Equal(0)) | ||||||
|  | 		session = podmanTest.Podman([]string{"manifest", "add", "--all", "foo", imageList}) | ||||||
|  | 		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(imageListARM64InstanceDigest)) | ||||||
|  | 		session = podmanTest.Podman([]string{"manifest", "remove", "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(imageListAMD64InstanceDigest)) | ||||||
|  | 		Expect(session.OutputToString()).To(ContainSubstring(imageListARMInstanceDigest)) | ||||||
|  | 		Expect(session.OutputToString()).To(ContainSubstring(imageListPPC64LEInstanceDigest)) | ||||||
|  | 		Expect(session.OutputToString()).To(ContainSubstring(imageListS390XInstanceDigest)) | ||||||
|  | 		Expect(session.OutputToString()).To(Not(ContainSubstring(imageListARM64InstanceDigest))) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	It("podman manifest remove not-found", func() { | ||||||
|  | 		session := podmanTest.Podman([]string{"manifest", "create", "foo"}) | ||||||
|  | 		session.WaitWithDefaultTimeout() | ||||||
|  | 		Expect(session.ExitCode()).To(Equal(0)) | ||||||
|  | 		session = podmanTest.Podman([]string{"manifest", "add", "foo", imageList}) | ||||||
|  | 		session.WaitWithDefaultTimeout() | ||||||
|  | 		Expect(session.ExitCode()).To(Equal(0)) | ||||||
|  | 		session = podmanTest.Podman([]string{"manifest", "remove", "foo", "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"}) | ||||||
|  | 		session.WaitWithDefaultTimeout() | ||||||
|  | 		Expect(session.ExitCode()).To(Not(Equal(0))) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	It("podman manifest push", func() { | ||||||
|  | 		session := podmanTest.Podman([]string{"manifest", "create", "foo"}) | ||||||
|  | 		session.WaitWithDefaultTimeout() | ||||||
|  | 		Expect(session.ExitCode()).To(Equal(0)) | ||||||
|  | 		session = podmanTest.Podman([]string{"manifest", "add", "--all", "foo", imageList}) | ||||||
|  | 		session.WaitWithDefaultTimeout() | ||||||
|  | 		Expect(session.ExitCode()).To(Equal(0)) | ||||||
|  | 		dest := filepath.Join(podmanTest.TempDir, "pushed") | ||||||
|  | 		err := os.MkdirAll(dest, os.ModePerm) | ||||||
|  | 		Expect(err).To(BeNil()) | ||||||
|  | 		defer func() { | ||||||
|  | 			os.RemoveAll(dest) | ||||||
|  | 		}() | ||||||
|  | 		session = podmanTest.Podman([]string{"manifest", "push", "--all", "foo", "dir:" + dest}) | ||||||
|  | 		session.WaitWithDefaultTimeout() | ||||||
|  | 		Expect(session.ExitCode()).To(Equal(0)) | ||||||
|  | 		files, err := filepath.Glob(dest + string(os.PathSeparator) + "*") | ||||||
|  | 		Expect(err).To(BeNil()) | ||||||
|  | 		check := SystemExec("sha256sum", files) | ||||||
|  | 		check.WaitWithDefaultTimeout() | ||||||
|  | 		Expect(check.ExitCode()).To(Equal(0)) | ||||||
|  | 		prefix := "sha256:" | ||||||
|  | 		Expect(check.OutputToString()).To(ContainSubstring(strings.TrimPrefix(imageListAMD64InstanceDigest, prefix))) | ||||||
|  | 		Expect(check.OutputToString()).To(ContainSubstring(strings.TrimPrefix(imageListARMInstanceDigest, prefix))) | ||||||
|  | 		Expect(check.OutputToString()).To(ContainSubstring(strings.TrimPrefix(imageListPPC64LEInstanceDigest, prefix))) | ||||||
|  | 		Expect(check.OutputToString()).To(ContainSubstring(strings.TrimPrefix(imageListS390XInstanceDigest, prefix))) | ||||||
|  | 		Expect(check.OutputToString()).To(ContainSubstring(strings.TrimPrefix(imageListARM64InstanceDigest, prefix))) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	It("podman manifest push purge", func() { | ||||||
|  | 		session := podmanTest.Podman([]string{"manifest", "create", "foo"}) | ||||||
|  | 		session.WaitWithDefaultTimeout() | ||||||
|  | 		Expect(session.ExitCode()).To(Equal(0)) | ||||||
|  | 		session = podmanTest.Podman([]string{"manifest", "add", "foo", imageList}) | ||||||
|  | 		session.WaitWithDefaultTimeout() | ||||||
|  | 		Expect(session.ExitCode()).To(Equal(0)) | ||||||
|  | 		dest := filepath.Join(podmanTest.TempDir, "pushed") | ||||||
|  | 		err := os.MkdirAll(dest, os.ModePerm) | ||||||
|  | 		Expect(err).To(BeNil()) | ||||||
|  | 		defer func() { | ||||||
|  | 			os.RemoveAll(dest) | ||||||
|  | 		}() | ||||||
|  | 		session = podmanTest.Podman([]string{"manifest", "push", "--purge", "foo", "dir:" + dest}) | ||||||
|  | 		session.WaitWithDefaultTimeout() | ||||||
|  | 		Expect(session.ExitCode()).To(Equal(0)) | ||||||
|  | 		session = podmanTest.Podman([]string{"manifest", "inspect", "foo"}) | ||||||
|  | 		session.WaitWithDefaultTimeout() | ||||||
|  | 		Expect(session.ExitCode()).To(Not(Equal(0))) | ||||||
|  | 	}) | ||||||
| }) | }) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue