diff options
-rw-r--r-- | cmd/podman/main.go | 1 | ||||
-rw-r--r-- | cmd/podman/manifest/add.go | 49 | ||||
-rw-r--r-- | cmd/podman/manifest/create.go | 44 | ||||
-rw-r--r-- | cmd/podman/manifest/inspect.go | 39 | ||||
-rw-r--r-- | cmd/podman/manifest/manifest.go | 27 | ||||
-rw-r--r-- | completions/bash/podman | 78 | ||||
-rw-r--r-- | docs/source/markdown/podman-manifest-add.1.md | 69 | ||||
-rw-r--r-- | docs/source/markdown/podman-manifest-create.1.md | 43 | ||||
-rw-r--r-- | docs/source/markdown/podman-manifest-inspect.1.md | 24 | ||||
-rw-r--r-- | docs/source/markdown/podman-manifest.1.md | 23 | ||||
-rw-r--r-- | docs/source/markdown/podman.1.md | 1 | ||||
-rw-r--r-- | go.sum | 1 | ||||
-rw-r--r-- | libpod/runtime.go | 5 | ||||
-rw-r--r-- | pkg/domain/entities/engine_image.go | 3 | ||||
-rw-r--r-- | pkg/domain/entities/manifest.go | 15 | ||||
-rw-r--r-- | pkg/domain/infra/abi/manifest.go | 101 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/manifest.go | 63 | ||||
-rw-r--r-- | test/e2e/manifest_test.go | 88 |
18 files changed, 674 insertions, 0 deletions
diff --git a/cmd/podman/main.go b/cmd/podman/main.go index 2d9e45177..8109eca2f 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -6,6 +6,7 @@ import ( _ "github.com/containers/libpod/cmd/podman/containers" _ "github.com/containers/libpod/cmd/podman/healthcheck" _ "github.com/containers/libpod/cmd/podman/images" + _ "github.com/containers/libpod/cmd/podman/manifest" _ "github.com/containers/libpod/cmd/podman/networks" _ "github.com/containers/libpod/cmd/podman/pods" "github.com/containers/libpod/cmd/podman/registry" diff --git a/cmd/podman/manifest/add.go b/cmd/podman/manifest/add.go new file mode 100644 index 000000000..20251ca87 --- /dev/null +++ b/cmd/podman/manifest/add.go @@ -0,0 +1,49 @@ +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 ( + manifestAddOpts = entities.ManifestAddOptions{} + addCmd = &cobra.Command{ + Use: "add", + Short: "Add images to a manifest list or image index", + Long: "Adds an image to a manifest list or image index.", + RunE: add, + Example: `podman manifest add mylist:v1.11 image:v1.11-amd64 + podman manifest add mylist:v1.11 transport:imageName`, + Args: cobra.ExactArgs(2), + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: addCmd, + Parent: manifestCmd, + }) + flags := addCmd.Flags() + flags.BoolVar(&manifestAddOpts.All, "all", false, "add all of the list's images if the image is a list") + flags.StringSliceVar(&manifestAddOpts.Annotation, "annotation", nil, "set an `annotation` for the specified image") + flags.StringVar(&manifestAddOpts.Arch, "arch", "", "override the `architecture` of the specified image") + flags.StringSliceVar(&manifestAddOpts.Features, "features", nil, "override the `features` of the specified image") + flags.StringVar(&manifestAddOpts.OSVersion, "os-version", "", "override the OS `version` of the specified image") + flags.StringVar(&manifestAddOpts.Variant, "variant", "", "override the `Variant` of the specified image") +} + +func add(cmd *cobra.Command, args []string) error { + manifestAddOpts.Images = []string{args[1], args[0]} + listID, err := registry.ImageEngine().ManifestAdd(context.Background(), manifestAddOpts) + if err != nil { + return errors.Wrapf(err, "error adding to manifest list %s", args[0]) + } + fmt.Printf("%s\n", listID) + return nil +} diff --git a/cmd/podman/manifest/create.go b/cmd/podman/manifest/create.go new file mode 100644 index 000000000..4f3e27774 --- /dev/null +++ b/cmd/podman/manifest/create.go @@ -0,0 +1,44 @@ +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 ( + manifestCreateOpts = entities.ManifestCreateOptions{} + createCmd = &cobra.Command{ + Use: "create", + Short: "Create manifest list or image index", + Long: "Creates manifest lists or image indexes.", + RunE: create, + Example: `podman manifest create mylist:v1.11 + podman manifest create mylist:v1.11 arch-specific-image-to-add + podman manifest create --all mylist:v1.11 transport:tagged-image-to-add`, + Args: cobra.MinimumNArgs(1), + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: createCmd, + Parent: manifestCmd, + }) + flags := createCmd.Flags() + flags.BoolVar(&manifestCreateOpts.All, "all", false, "add all of the lists' images if the images to add are lists") +} + +func create(cmd *cobra.Command, args []string) error { + imageID, err := registry.ImageEngine().ManifestCreate(context.Background(), args[:1], args[1:], manifestCreateOpts) + if err != nil { + return errors.Wrapf(err, "error creating manifest %s", args[0]) + } + fmt.Printf("%s\n", imageID) + return nil +} diff --git a/cmd/podman/manifest/inspect.go b/cmd/podman/manifest/inspect.go new file mode 100644 index 000000000..36ecdc87b --- /dev/null +++ b/cmd/podman/manifest/inspect.go @@ -0,0 +1,39 @@ +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 ( + inspectCmd = &cobra.Command{ + Use: "inspect IMAGE", + Short: "Display the contents of a manifest list or image index", + Long: "Display the contents of a manifest list or image index.", + RunE: inspect, + Example: "podman manifest inspect localhost/list", + Args: cobra.ExactArgs(1), + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: inspectCmd, + Parent: manifestCmd, + }) +} + +func inspect(cmd *cobra.Command, args []string) error { + buf, err := registry.ImageEngine().ManifestInspect(context.Background(), args[0]) + if err != nil { + return errors.Wrapf(err, "error inspect manifest %s", args[0]) + } + fmt.Printf("%s\n", buf) + return nil +} diff --git a/cmd/podman/manifest/manifest.go b/cmd/podman/manifest/manifest.go new file mode 100644 index 000000000..b9ac7ea68 --- /dev/null +++ b/cmd/podman/manifest/manifest.go @@ -0,0 +1,27 @@ +package manifest + +import ( + "github.com/containers/libpod/cmd/podman/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + manifestDescription = "Creates, modifies, and pushes manifest lists and image indexes." + manifestCmd = &cobra.Command{ + Use: "manifest", + Short: "Manipulate manifest lists and image indexes", + Long: manifestDescription, + TraverseChildren: true, + RunE: registry.SubCommandExists, + Example: `podman manifest create localhost/list + podman manifest inspect localhost/list`, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: manifestCmd, + }) +} diff --git a/completions/bash/podman b/completions/bash/podman index 6997db3b5..41a76a967 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -1733,6 +1733,83 @@ _podman_logs() { esac } +_podman_manifest() { + local boolean_options=" + --help + -h + " + subcommands=" + add + create + inspect + " + __podman_subcommands "$subcommands" && return + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--help" -- "$cur" ) ) + ;; + *) + COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) ) + ;; + esac +} + +_podman_manifest_add() { + local options_with_args=" + --annotation + --arch + --features + --os-version + --variant + " + + local boolean_options=" + --all + --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 + --help + -h + " + + _complete_ "$boolean_options" +} + +_podman_manifest_inspect() { + 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() { local options_with_args=" --authfile @@ -3356,6 +3433,7 @@ _podman_podman() { login logout logs + manifest mount pause pod diff --git a/docs/source/markdown/podman-manifest-add.1.md b/docs/source/markdown/podman-manifest-add.1.md new file mode 100644 index 000000000..4ecf03900 --- /dev/null +++ b/docs/source/markdown/podman-manifest-add.1.md @@ -0,0 +1,69 @@ +% podman-manifest-add(1) + +## NAME +podman\-manifest\-add - Add an image to a manifest list or image index + +## SYNOPSIS +**podman manifest add** *listnameorindexname* *imagename* + +## DESCRIPTION + +Adds the specified image to the specified manifest list or image index. + +## RETURN VALUE +The list image's ID. + +## OPTIONS + +**--all** + +If the image which should be added to the list or index is itself a list or +index, add all of the contents to the local list. By default, only one image +from such a list or index will be added to the list or index. Combining +*--all* with any of the other options described below is NOT recommended. + +**--annotation** *annotation=value* + +Set an annotation on the entry for the newly-added image. + +**--arch** + +Override the architecture which the list or index records as a requirement for +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. + +**--features** + +Specify the 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 add mylist:v1.11 docker://fedora +71c201d10fffdcac52968a000d85a0a016ca1c7d5473948000d3131c1773d965 +``` + +``` +podman manifest add --all mylist:v1.11 docker://fedora +71c201d10fffdcac52968a000d85a0a016ca1c7d5473948000d3131c1773d965 +``` + +``` +podman manifest add --arch arm64 --variant v8 mylist:v1.11 docker://71c201d10fffdcac52968a000d85a0a016ca1c7d5473948000d3131c1773d965 +``` + +## SEE ALSO +podman(1), podman-manifest(1), podman-manifest-create(1), podman-manifest-inspect(1), podman-rmi(1) diff --git a/docs/source/markdown/podman-manifest-create.1.md b/docs/source/markdown/podman-manifest-create.1.md new file mode 100644 index 000000000..941e70c32 --- /dev/null +++ b/docs/source/markdown/podman-manifest-create.1.md @@ -0,0 +1,43 @@ +% podman-manifest-create(1) + +## NAME +podman\-manifest\-create - Create a manifest list or image index + +## SYNOPSIS +**podman manifest create** [*options*] *listnameorindexname* [*imagename* ...] + +## DESCRIPTION + +Creates a new manifest list and stores it as an image in local storage using +the specified name. + +If additional images are specified, they are added to the newly-created list or +index. + +## OPTIONS + +**--all** + +If any of the images which should be added to the new list or index are +themselves lists or indexes, add all of their contents. By default, only one +image from such a list will be added to the newly-created list or index. + +## EXAMPLES + +``` +podman manifest create mylist:v1.11 +9cfd24048d5fc80903f088f1531a21bff01172abe66effa8941a4c2308dc745f +``` + +``` +podman manifest create mylist:v1.11 docker://fedora +5c2bc76bfb4ba6665a7973f7e1c05ee0536b4580637f27adc9fa5a4b2bc03cf1 +``` + +``` +podman manifest create --all mylist:v1.11 docker://fedora +30330571e79c65288a4fca421d9aed29b0210d57294d9c2056743fdcf6e3967b +``` + +## SEE ALSO +podman(1), podman-manifest(1), podman-manifest-add(1), podman-manifest-inspect(1), podman-rmi(1) diff --git a/docs/source/markdown/podman-manifest-inspect.1.md b/docs/source/markdown/podman-manifest-inspect.1.md new file mode 100644 index 000000000..efde02643 --- /dev/null +++ b/docs/source/markdown/podman-manifest-inspect.1.md @@ -0,0 +1,24 @@ +% podman-manifest-inspect(1) + +## NAME +podman\-manifest\-inspect - Display a manifest list or image index + +## SYNOPSIS +**podman manifest inspect** *listnameorindexname* + +## DESCRIPTION + +Displays the manifest list or image index stored using the specified image name. + +## RETURN VALUE + +A formatted JSON representation of the manifest list or image index. + +## EXAMPLES + +``` +podman manifest inspect mylist:v1.11 +``` + +## SEE ALSO +podman(1), podman-manifest(1), podman-manifest-create(1), podman-manifest-add(1), podman-rmi(1) diff --git a/docs/source/markdown/podman-manifest.1.md b/docs/source/markdown/podman-manifest.1.md new file mode 100644 index 000000000..70d695883 --- /dev/null +++ b/docs/source/markdown/podman-manifest.1.md @@ -0,0 +1,23 @@ +% podman-manifest(1) + +## NAME +podman\-manifest - Create and manipulate manifest lists and image indexes + +## SYNOPSIS +**podman manifest** *subcommand* + +## DESCRIPTION +The `podman manifest` command provides subcommands which can be used to: + + * Create a working Docker manifest list or OCI image index. + +## 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. | + +## SEE ALSO +podman(1), podman-manifest-add(1), podman-manifest-create(1), podman-manifest-inspect(1) diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md index cd4148c95..6bac0cc9d 100644 --- a/docs/source/markdown/podman.1.md +++ b/docs/source/markdown/podman.1.md @@ -169,6 +169,7 @@ the exit codes follow the `chroot` standard, see below: | [podman-login(1)](podman-login.1.md) | Login to a container registry. | | [podman-logout(1)](podman-logout.1.md) | Logout of a container registry. | | [podman-logs(1)](podman-logs.1.md) | Display the logs of one or more containers. | +| [podman-manifest(1)](podman-manifest.1.md) | Create and manipulate manifest lists and image indexes. | | [podman-mount(1)](podman-mount.1.md) | Mount a working container's root filesystem. | | [podman-network(1)](podman-network.1.md) | Manage Podman CNI networks. | | [podman-pause(1)](podman-pause.1.md) | Pause one or more containers. | @@ -131,6 +131,7 @@ github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1 github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= diff --git a/libpod/runtime.go b/libpod/runtime.go index 3b8f9e057..e71483ef9 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -733,6 +733,11 @@ func (r *Runtime) StorageConfig() storage.StoreOptions { return r.storageConfig } +// GetStore returns the runtime stores +func (r *Runtime) GetStore() storage.Store { + return r.store +} + // DBConfig is a set of Libpod runtime configuration settings that are saved in // a State when it is first created, and can subsequently be retrieved. type DBConfig struct { diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go index 84680ab1b..fefcd751d 100644 --- a/pkg/domain/entities/engine_image.go +++ b/pkg/domain/entities/engine_image.go @@ -25,4 +25,7 @@ type ImageEngine interface { Tag(ctx context.Context, nameOrId string, tags []string, options ImageTagOptions) error Tree(ctx context.Context, nameOrId string, options ImageTreeOptions) (*ImageTreeReport, error) Untag(ctx context.Context, nameOrId string, tags []string, options ImageUntagOptions) error + 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) } diff --git a/pkg/domain/entities/manifest.go b/pkg/domain/entities/manifest.go new file mode 100644 index 000000000..a9c961f9d --- /dev/null +++ b/pkg/domain/entities/manifest.go @@ -0,0 +1,15 @@ +package entities + +type ManifestCreateOptions struct { + All bool `schema:"all"` +} + +type ManifestAddOptions struct { + All bool `json:"all" schema:"all"` + Annotation []string `json:"annotation" schema:"annotation"` + Arch string `json:"arch" schema:"arch"` + Features []string `json:"features" schema:"features"` + Images []string `json:"images" schema:"images"` + OSVersion string `json:"os_version" schema:"os_version"` + Variant string `json:"variant" schema:"variant"` +} diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go new file mode 100644 index 000000000..27d4bf9a5 --- /dev/null +++ b/pkg/domain/infra/abi/manifest.go @@ -0,0 +1,101 @@ +// +build ABISupport + +package abi + +import ( + "context" + "encoding/json" + "fmt" + "strings" + + buildahUtil "github.com/containers/buildah/util" + "github.com/containers/image/v5/docker" + "github.com/containers/image/v5/transports/alltransports" + libpodImage "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/util" + + "github.com/pkg/errors" +) + +// ManifestCreate implements logic for creating manifest lists via ImageEngine +func (ir *ImageEngine) ManifestCreate(ctx context.Context, names, images []string, opts entities.ManifestCreateOptions) (string, error) { + fullNames, err := buildahUtil.ExpandNames(names, "", ir.Libpod.SystemContext(), ir.Libpod.GetStore()) + if err != nil { + return "", errors.Wrapf(err, "error encountered while expanding image name %q", names) + } + imageID, err := libpodImage.CreateManifestList(ir.Libpod.ImageRuntime(), *ir.Libpod.SystemContext(), fullNames, images, opts.All) + if err != nil { + return imageID, err + } + return imageID, err +} + +// ManifestInspect returns the content of a manifest list or image +func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte, error) { + dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name()) + _, err := alltransports.ParseImageName(name) + if err != nil { + _, err = alltransports.ParseImageName(dockerPrefix + name) + if err != nil { + return nil, errors.Errorf("invalid image reference %q", name) + } + } + image, err := ir.Libpod.ImageRuntime().New(ctx, name, "", "", nil, nil, libpodImage.SigningOptions{}, nil, util.PullImageMissing) + if err != nil { + return nil, errors.Wrapf(err, "reading image %q", name) + } + + list, err := image.InspectManifest() + if err != nil { + return nil, errors.Wrapf(err, "loading manifest %q", name) + } + buf, err := json.MarshalIndent(list, "", " ") + if err != nil { + return buf, errors.Wrapf(err, "error rendering manifest for display") + } + return buf, nil +} + +// ManifestAdd adds images to the manifest list +func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAddOptions) (string, error) { + imageSpec := opts.Images[0] + listImageSpec := opts.Images[1] + dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name()) + _, err := alltransports.ParseImageName(imageSpec) + if err != nil { + _, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, imageSpec)) + if err != nil { + return "", errors.Errorf("invalid image reference %q", imageSpec) + } + } + listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(listImageSpec) + if err != nil { + return "", errors.Wrapf(err, "error retriving local image from image name %s", listImageSpec) + } + + manifestAddOpts := libpodImage.ManifestAddOpts{ + All: opts.All, + Arch: opts.Arch, + Features: opts.Features, + Images: opts.Images, + 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] + } + manifestAddOpts.Annotation = annotations + } + listID, err := listImage.AddManifest(*ir.Libpod.SystemContext(), manifestAddOpts) + if err != nil { + return listID, err + } + return listID, nil +} diff --git a/pkg/domain/infra/tunnel/manifest.go b/pkg/domain/infra/tunnel/manifest.go new file mode 100644 index 000000000..338256530 --- /dev/null +++ b/pkg/domain/infra/tunnel/manifest.go @@ -0,0 +1,63 @@ +package tunnel + +import ( + "context" + "encoding/json" + "strings" + + "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/bindings/manifests" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" +) + +// ManifestCreate implements manifest create via ImageEngine +func (ir *ImageEngine) ManifestCreate(ctx context.Context, names, images []string, opts entities.ManifestCreateOptions) (string, error) { + imageID, err := manifests.Create(ir.ClientCxt, names, images, &opts.All) + if err != nil { + return imageID, errors.Wrapf(err, "error creating manifest") + } + return imageID, err +} + +// ManifestInspect returns contents of manifest list with given name +func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte, error) { + list, err := manifests.Inspect(ir.ClientCxt, name) + if err != nil { + return nil, errors.Wrapf(err, "error getting content of manifest list or image %s", name) + } + + buf, err := json.MarshalIndent(list, "", " ") + if err != nil { + return buf, errors.Wrapf(err, "error rendering manifest for display") + } + return buf, err +} + +// ManifestAdd adds images to the manifest list +func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAddOptions) (string, error) { + manifestAddOpts := image.ManifestAddOpts{ + All: opts.All, + Arch: opts.Arch, + Features: opts.Features, + Images: opts.Images, + 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] + } + manifestAddOpts.Annotation = annotations + } + listID, err := manifests.Add(ctx, opts.Images[1], manifestAddOpts) + if err != nil { + return listID, errors.Wrapf(err, "error adding to manifest list %s", opts.Images[1]) + } + return listID, nil +} diff --git a/test/e2e/manifest_test.go b/test/e2e/manifest_test.go new file mode 100644 index 000000000..a52916e87 --- /dev/null +++ b/test/e2e/manifest_test.go @@ -0,0 +1,88 @@ +package integration + +import ( + "os" + + . "github.com/containers/libpod/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Podman manifest", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + const ( + imageList = "docker://k8s.gcr.io/pause:3.1" + imageListInstance = "docker://k8s.gcr.io/pause@sha256:f365626a556e58189fc21d099fc64603db0f440bff07f77c740989515c544a39" + imageListARM64InstanceDigest = "sha256:f365626a556e58189fc21d099fc64603db0f440bff07f77c740989515c544a39" + imageListAMD64InstanceDigest = "sha256:59eec8837a4d942cc19a52b8c09ea75121acc38114a2c68b98983ce9356b8610" + imageListARMInstanceDigest = "sha256:c84b0a3a07b628bc4d62e5047d0f8dff80f7c00979e1e28a821a033ecda8fe53" + imageListPPC64LEInstanceDigest = "sha256:bcf9771c0b505e68c65440474179592ffdfa98790eb54ffbf129969c5e429990" + imageListS390XInstanceDigest = "sha256:882a20ee0df7399a445285361d38b711c299ca093af978217112c73803546d5e" + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + podmanTest.SeedImages() + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + processTestResult(f) + + }) + It("podman manifest create", func() { + session := podmanTest.Podman([]string{"manifest", "create", "foo"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + + It("podman manifest add", func() { + session := podmanTest.Podman([]string{"manifest", "create", "foo"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"manifest", "add", "--arch=arm64", "foo", imageListInstance}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + + It("podman manifest add one", func() { + session := podmanTest.Podman([]string{"manifest", "create", "foo"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"manifest", "add", "--arch=arm64", "foo", imageListInstance}) + 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)) + }) + + It("podman manifest add --all", 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(imageListAMD64InstanceDigest)) + Expect(session.OutputToString()).To(ContainSubstring(imageListARMInstanceDigest)) + Expect(session.OutputToString()).To(ContainSubstring(imageListARM64InstanceDigest)) + Expect(session.OutputToString()).To(ContainSubstring(imageListPPC64LEInstanceDigest)) + Expect(session.OutputToString()).To(ContainSubstring(imageListS390XInstanceDigest)) + }) +}) |