diff options
Diffstat (limited to 'pkg/domain/infra/abi/manifest.go')
-rw-r--r-- | pkg/domain/infra/abi/manifest.go | 327 |
1 files changed, 190 insertions, 137 deletions
diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go index 8e3c50fac..f932cf21d 100644 --- a/pkg/domain/infra/abi/manifest.go +++ b/pkg/domain/infra/abi/manifest.go @@ -4,21 +4,17 @@ import ( "bytes" "context" "encoding/json" - "fmt" "os" "strings" - "github.com/containers/buildah/manifests" - buildahManifests "github.com/containers/buildah/pkg/manifests" - buildahUtil "github.com/containers/buildah/util" + "github.com/containers/common/libimage" cp "github.com/containers/image/v5/copy" - "github.com/containers/image/v5/docker" "github.com/containers/image/v5/manifest" + "github.com/containers/image/v5/pkg/shortnames" "github.com/containers/image/v5/transports" "github.com/containers/image/v5/transports/alltransports" - "github.com/containers/image/v5/types" - libpodImage "github.com/containers/podman/v3/libpod/image" "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/storage" "github.com/opencontainers/go-digest" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" @@ -26,51 +22,103 @@ import ( ) // 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) +func (ir *ImageEngine) ManifestCreate(ctx context.Context, names []string, images []string, opts entities.ManifestCreateOptions) (string, error) { + // FIXME: change the interface of manifest create `names []string` -> + // `name string`. + if len(names) == 0 { + return "", errors.New("no name specified for creating a manifest list") } - imageID, err := libpodImage.CreateManifestList(ir.Libpod.ImageRuntime(), *ir.Libpod.SystemContext(), fullNames, images, opts.All) + name := names[0] + + manifestList, err := ir.Libpod.LibimageRuntime().CreateManifestList(name) if err != nil { - return imageID, err + return "", err } - return imageID, err + + addOptions := &libimage.ManifestListAddOptions{All: opts.All} + for _, image := range images { + if _, err := manifestList.Add(ctx, image, addOptions); err != nil { + return "", err + } + } + + return manifestList.ID(), nil } // ManifestExists checks if a manifest list with the given name exists in local storage func (ir *ImageEngine) ManifestExists(ctx context.Context, name string) (*entities.BoolReport, error) { - if image, err := ir.Libpod.ImageRuntime().NewFromLocal(name); err == nil { - exists, err := image.ExistsManifest() - if err != nil && errors.Cause(err) != buildahManifests.ErrManifestTypeNotSupported { - return nil, err + image, _, err := ir.Libpod.LibimageRuntime().LookupImage(name, &libimage.LookupImageOptions{IgnorePlatform: true}) + if err != nil { + if errors.Cause(err) == storage.ErrImageUnknown { + return &entities.BoolReport{Value: false}, nil } - return &entities.BoolReport{Value: exists}, nil + return nil, err + } + + isManifestList, err := image.IsManifestList(ctx) + if err != nil { + return nil, err } - return &entities.BoolReport{Value: false}, nil + return &entities.BoolReport{Value: isManifestList}, nil } // ManifestInspect returns the content of a manifest list or image func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte, error) { - if newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name); err == nil { - // return the manifest in local storage - if list, err := newImage.InspectManifest(); err == nil { - buf, err := json.MarshalIndent(list, "", " ") - if err != nil { - return buf, errors.Wrapf(err, "error rendering manifest %s for display", name) - } - return buf, nil - // no return if local image is not a list of images type - // continue on getting valid manifest through remote service - } else if errors.Cause(err) != buildahManifests.ErrManifestTypeNotSupported { - return nil, errors.Wrapf(err, "loading manifest %q", name) + // NOTE: we have to do a bit of a limbo here as `podman manifest + // inspect foo` wants to do a remote-inspect of foo iff "foo" in the + // containers storage is an ordinary image but not a manifest list. + + lookupOptions := &libimage.LookupImageOptions{IgnorePlatform: true} + image, _, err := ir.Libpod.LibimageRuntime().LookupImage(name, lookupOptions) + if err != nil { + // If the image doesn't exist, do a remote inspect. + if errors.Cause(err) == storage.ErrImageUnknown { + return ir.remoteManifestInspect(ctx, name) } + return nil, err + } + + isManifestList, err := image.IsManifestList(ctx) + if err != nil { + return nil, err + } + + // If the image isn't a manifest list, do a remote inspect. + if !isManifestList { + return ir.remoteManifestInspect(ctx, name) } - sc := ir.Libpod.SystemContext() - refs, err := buildahUtil.ResolveNameToReferences(ir.Libpod.GetStore(), sc, name) + + manifestList, err := image.ToManifestList() if err != nil { return nil, err } + + schema2List, err := manifestList.Inspect() + if err != nil { + return nil, err + } + + rawSchema2List, err := json.Marshal(schema2List) + if err != nil { + return nil, err + } + + var b bytes.Buffer + if err := json.Indent(&b, rawSchema2List, "", " "); err != nil { + return nil, errors.Wrapf(err, "error rendering manifest %s for display", name) + } + return b.Bytes(), nil +} + +// inspect a remote manifest list. +func (ir *ImageEngine) remoteManifestInspect(ctx context.Context, name string) ([]byte, error) { + sys := ir.Libpod.SystemContext() + + resolved, err := shortnames.Resolve(sys, name) + if err != nil { + return nil, err + } + var ( latestErr error result []byte @@ -84,8 +132,13 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte latestErr = errors.Wrapf(latestErr, "tried %v\n", e) } } - for _, ref := range refs { - src, err := ref.NewImageSource(ctx, sc) + + for _, candidate := range resolved.PullCandidates { + ref, err := alltransports.ParseImageName("docker://" + candidate.Value.String()) + if err != nil { + return nil, err + } + src, err := ref.NewImageSource(ctx, sys) if err != nil { appendErr(errors.Wrapf(err, "reading image %q", transports.ImageName(ref))) continue @@ -102,6 +155,7 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte manType = manifestType break } + if len(result) == 0 && latestErr != nil { return nil, latestErr } @@ -138,29 +192,41 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte // 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) + // FIXME: the name options below are *mandatory* arguments and should + // be reflected as such in the signature. + + if len(opts.Images) < 2 { + return "", errors.New("manifest add requires two images") + } + + imageName := opts.Images[0] + listName := opts.Images[1] + + manifestList, err := ir.Libpod.LibimageRuntime().LookupManifestList(listName) if err != nil { - _, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, imageSpec)) - if err != nil { - return "", errors.Errorf("invalid image reference %q", imageSpec) - } + return "", err } - listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(listImageSpec) + + addOptions := &libimage.ManifestListAddOptions{ + All: opts.All, + AuthFilePath: opts.Authfile, + CertDirPath: opts.CertDir, + InsecureSkipTLSVerify: opts.SkipTLSVerify, + Username: opts.Username, + Password: opts.Password, + } + + instanceDigest, err := manifestList.Add(ctx, imageName, addOptions) if err != nil { - return "", errors.Wrapf(err, "error retrieving local image from image name %s", listImageSpec) + return "", err } - manifestAddOpts := libpodImage.ManifestAddOpts{ - All: opts.All, - Arch: opts.Arch, - Features: opts.Features, - Images: opts.Images, - OS: opts.OS, - OSVersion: opts.OSVersion, - Variant: opts.Variant, + annotateOptions := &libimage.ManifestListAnnotateOptions{ + Architecture: opts.Arch, + Features: opts.Features, + OS: opts.OS, + OSVersion: opts.OSVersion, + Variant: opts.Variant, } if len(opts.Annotation) != 0 { annotations := make(map[string]string) @@ -171,51 +237,44 @@ func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAd } annotations[spec[0]] = spec[1] } - manifestAddOpts.Annotation = annotations - } - - // Set the system context. - sys := ir.Libpod.SystemContext() - if sys != nil { - sys = &types.SystemContext{} + annotateOptions.Annotations = annotations } - sys.AuthFilePath = opts.Authfile - sys.DockerInsecureSkipTLSVerify = opts.SkipTLSVerify - sys.DockerCertPath = opts.CertDir - if opts.Username != "" && opts.Password != "" { - sys.DockerAuthConfig = &types.DockerAuthConfig{ - Username: opts.Username, - Password: opts.Password, - } + if err := manifestList.AnnotateInstance(instanceDigest, annotateOptions); err != nil { + return "", err } - listID, err := listImage.AddManifest(*sys, manifestAddOpts) - if err != nil { - return listID, err - } - return listID, nil + return manifestList.ID(), 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 retrieving local image from image name %s", names[0]) + // FIXME: the `names` are *mandatory* arguments and should be + // reflected as such in the signature. + + if len(names) < 2 { + return "", errors.New("manifest annotate requires two names") } - digest, err := digest.Parse(names[1]) + + listName := names[0] + instanceDigest, 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, + + manifestList, err := ir.Libpod.LibimageRuntime().LookupManifestList(listName) + if err != nil { + return "", err + } + + annotateOptions := &libimage.ManifestListAnnotateOptions{ + Architecture: opts.Arch, + Features: opts.Features, + OS: opts.OS, + OSVersion: opts.OSVersion, + Variant: opts.Variant, } - if len(opts.Annotation) > 0 { + if len(opts.Annotation) != 0 { annotations := make(map[string]string) for _, annotationSpec := range opts.Annotation { spec := strings.SplitN(annotationSpec, "=", 2) @@ -224,48 +283,49 @@ func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, names []string, opt } annotations[spec[0]] = spec[1] } - manifestAnnotateOpts.Annotation = annotations + annotateOptions.Annotations = annotations } - updatedListID, err := listImage.AnnotateManifest(*ir.Libpod.SystemContext(), digest, manifestAnnotateOpts) - if err == nil { - return fmt.Sprintf("%s: %s", updatedListID, digest.String()), nil + + if err := manifestList.AnnotateInstance(instanceDigest, annotateOptions); err != nil { + return "", err } - return "", err + + return manifestList.ID(), nil } // ManifestRemove removes specified digest from the specified manifest list func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (string, error) { + // FIXME: the `names` are *mandatory* arguments and should be + // reflected as such in the signature. + + if len(names) < 2 { + return "", errors.New("manifest remove requires two names") + } + + listName := names[0] 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]) + + manifestList, err := ir.Libpod.LibimageRuntime().LookupManifestList(listName) if err != nil { - return "", errors.Wrapf(err, "error retrieving local image from image name %s", names[0]) + return "", err } - updatedListID, err := listImage.RemoveManifest(instanceDigest) - if err == nil { - return fmt.Sprintf("%s :%s\n", updatedListID, instanceDigest.String()), nil + + if err := manifestList.RemoveInstance(instanceDigest); err != nil { + return "", err } - return "", err + + return manifestList.ID(), nil } // ManifestPush pushes a manifest list or image index to the destination func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination string, opts entities.ImagePushOptions) (string, error) { - listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name) + manifestList, err := ir.Libpod.LibimageRuntime().LookupManifestList(name) if err != nil { return "", errors.Wrapf(err, "error retrieving local image from image name %s", name) } - dest, err := alltransports.ParseImageName(destination) - if err != nil { - oldErr := err - // Try adding the images default transport - destination2 := libpodImage.DefaultTransport + destination - dest, err = alltransports.ParseImageName(destination2) - if err != nil { - return "", oldErr - } - } var manifestType string if opts.Format != "" { @@ -279,40 +339,33 @@ func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination strin } } - // Set the system context. - sys := ir.Libpod.SystemContext() - if sys == nil { - sys = new(types.SystemContext) - } - sys.AuthFilePath = opts.Authfile - sys.DockerInsecureSkipTLSVerify = opts.SkipTLSVerify - sys.DockerCertPath = opts.CertDir - - if opts.Username != "" && opts.Password != "" { - sys.DockerAuthConfig = &types.DockerAuthConfig{ - Username: opts.Username, - Password: opts.Password, - } - } + pushOptions := &libimage.ManifestListPushOptions{} + pushOptions.AuthFilePath = opts.Authfile + pushOptions.CertDirPath = opts.CertDir + pushOptions.Username = opts.Username + pushOptions.Password = opts.Password + pushOptions.ImageListSelection = cp.CopySpecificImages + pushOptions.ManifestMIMEType = manifestType + pushOptions.RemoveSignatures = opts.RemoveSignatures + pushOptions.SignBy = opts.SignBy - options := manifests.PushOptions{ - Store: ir.Libpod.GetStore(), - SystemContext: sys, - ImageListSelection: cp.CopySpecificImages, - Instances: nil, - RemoveSignatures: opts.RemoveSignatures, - SignBy: opts.SignBy, - ManifestType: manifestType, - } if opts.All { - options.ImageListSelection = cp.CopyAllImages + pushOptions.ImageListSelection = cp.CopyAllImages } if !opts.Quiet { - options.ReportWriter = os.Stderr + pushOptions.Writer = os.Stderr } - manDigest, err := listImage.PushManifest(dest, options) - if err == nil && opts.Rm { - _, err = ir.Libpod.GetStore().DeleteImage(listImage.ID(), true) + + manDigest, err := manifestList.Push(ctx, destination, pushOptions) + if err != nil { + return "", err + } + + if opts.Rm { + if _, err := ir.Libpod.GetStore().DeleteImage(manifestList.ID(), true); err != nil { + return "", errors.Wrap(err, "error removing manifest after push") + } } + return manDigest.String(), err } |