From 17783dda6880c786a6eb3f47b3b6100e43bcdc77 Mon Sep 17 00:00:00 2001 From: Qi Wang Date: Thu, 16 Apr 2020 00:41:09 -0400 Subject: manifest create,add,inspect Implememts manifest subcommands create, add, inspect. Signed-off-by: Qi Wang --- pkg/domain/entities/engine_image.go | 3 ++ pkg/domain/entities/manifest.go | 15 ++++++ pkg/domain/infra/abi/manifest.go | 101 ++++++++++++++++++++++++++++++++++++ pkg/domain/infra/tunnel/manifest.go | 63 ++++++++++++++++++++++ 4 files changed, 182 insertions(+) create mode 100644 pkg/domain/entities/manifest.go create mode 100644 pkg/domain/infra/abi/manifest.go create mode 100644 pkg/domain/infra/tunnel/manifest.go (limited to 'pkg') 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 +} -- cgit v1.2.3-54-g00ecf