From cf51c7ed9f955390a0e417f208046e0b8fbadb26 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Fri, 15 Jan 2021 03:49:42 -0500 Subject: Allow podman push to push manifest lists When doing a podman images, manifests lists look just like images, so it is logical that users would assume that they can just podman push them to a registry. The problem is we throw out weird errors when this happens and users need to somehow figure out this is a manifest list rather then an image, and frankly the user will not understand the difference. This PR will make podman push just do the right thing, by failing over and attempting to push the manifest if it fails to push the image. Fix up handling of manifest push Protocol should bring back a digest string, which can either be printed or stored in a file. We should not reimplement the manifest push setup code in the tunnel code but take advantage of the api path, to make sure remote and local work the same way. Signed-off-by: Daniel J Walsh --- pkg/domain/infra/abi/images.go | 40 ++++++++----------------------------- pkg/domain/infra/abi/manifest.go | 27 ++++++++++++------------- pkg/domain/infra/tunnel/manifest.go | 22 ++++++++++++++++---- 3 files changed, 39 insertions(+), 50 deletions(-) (limited to 'pkg/domain/infra') diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 1c233d9d5..1288ab09b 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -367,7 +367,7 @@ func (ir *ImageEngine) Push(ctx context.Context, source string, destination stri return err } - return newImage.PushImageToHeuristicDestination( + err = newImage.PushImageToHeuristicDestination( ctx, destination, manifestType, @@ -379,39 +379,15 @@ func (ir *ImageEngine) Push(ctx context.Context, source string, destination stri signOptions, &dockerRegistryOptions, nil) + if err != nil && errors.Cause(err) != storage.ErrImageUnknown { + // Image might be a manifest list so attempt a manifest push + if _, manifestErr := ir.ManifestPush(ctx, source, destination, options); manifestErr == nil { + return nil + } + } + return err } -// func (r *imageRuntime) Delete(ctx context.Context, nameOrID string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) { -// image, err := r.libpod.ImageEngine().NewFromLocal(nameOrID) -// if err != nil { -// return nil, err -// } -// -// results, err := r.libpod.RemoveImage(ctx, image, opts.Force) -// if err != nil { -// return nil, err -// } -// -// report := entities.ImageDeleteReport{} -// if err := domainUtils.DeepCopy(&report, results); err != nil { -// return nil, err -// } -// return &report, nil -// } -// -// func (r *imageRuntime) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) { -// // TODO: map FilterOptions -// id, err := r.libpod.ImageEngine().PruneImages(ctx, opts.All, []string{}) -// if err != nil { -// return nil, err -// } -// -// // TODO: Determine Size -// report := entities.ImagePruneReport{} -// copy(report.Report.ID, id) -// return &report, nil -// } - func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, options entities.ImageTagOptions) error { newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID) if err != nil { diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go index a68ed8788..139032ad6 100644 --- a/pkg/domain/infra/abi/manifest.go +++ b/pkg/domain/infra/abi/manifest.go @@ -7,7 +7,6 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" "os" "strings" @@ -24,9 +23,8 @@ import ( "github.com/containers/podman/v2/pkg/domain/entities" "github.com/opencontainers/go-digest" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/sirupsen/logrus" - "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) // ManifestCreate implements logic for creating manifest lists via ImageEngine @@ -243,14 +241,20 @@ func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (stri } // ManifestPush pushes a manifest list or image index to the destination -func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination string, opts entities.ManifestPushOptions) error { +func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination string, opts entities.ImagePushOptions) (string, error) { listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name) if err != nil { - return errors.Wrapf(err, "error retrieving local image from image name %s", name) + return "", errors.Wrapf(err, "error retrieving local image from image name %s", name) } dest, err := alltransports.ParseImageName(destination) if err != nil { - return err + 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 @@ -261,7 +265,7 @@ func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination strin case "v2s2", "docker": manifestType = manifest.DockerV2Schema2MediaType default: - return errors.Errorf("unknown format %q. Choose one of the supported formats: 'oci' or 'v2s2'", opts.Format) + return "", errors.Errorf("unknown format %q. Choose one of the supported formats: 'oci' or 'v2s2'", opts.Format) } } @@ -297,13 +301,8 @@ func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination strin options.ReportWriter = os.Stderr } manDigest, err := listImage.PushManifest(dest, options) - if err == nil && opts.Purge { + if err == nil && opts.Rm { _, err = ir.Libpod.GetStore().DeleteImage(listImage.ID(), true) } - if opts.DigestFile != "" { - if err = ioutil.WriteFile(opts.DigestFile, []byte(manDigest.String()), 0644); err != nil { - return buildahUtil.GetFailureCause(err, errors.Wrapf(err, "failed to write digest to file %q", opts.DigestFile)) - } - } - return err + return manDigest.String(), err } diff --git a/pkg/domain/infra/tunnel/manifest.go b/pkg/domain/infra/tunnel/manifest.go index c71349fe0..22ca44165 100644 --- a/pkg/domain/infra/tunnel/manifest.go +++ b/pkg/domain/infra/tunnel/manifest.go @@ -6,6 +6,8 @@ import ( "fmt" "strings" + "github.com/containers/image/v5/types" + images "github.com/containers/podman/v2/pkg/bindings/images" "github.com/containers/podman/v2/pkg/bindings/manifests" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -73,8 +75,20 @@ func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (stri } // ManifestPush pushes a manifest list or image index to the destination -func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination string, opts entities.ManifestPushOptions) error { - options := new(manifests.PushOptions).WithAll(opts.All) - _, err := manifests.Push(ir.ClientCtx, name, destination, options) - return err +func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination string, opts entities.ImagePushOptions) (string, error) { + options := new(images.PushOptions) + options.WithUsername(opts.Username).WithSignaturePolicy(opts.SignaturePolicy).WithQuiet(opts.Quiet) + options.WithPassword(opts.Password).WithCertDir(opts.CertDir).WithAuthfile(opts.Authfile) + options.WithCompress(opts.Compress).WithDigestFile(opts.DigestFile).WithFormat(opts.Format) + options.WithRemoveSignatures(opts.RemoveSignatures).WithSignBy(opts.SignBy) + + if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { + if s == types.OptionalBoolTrue { + options.WithSkipTLSVerify(true) + } else { + options.WithSkipTLSVerify(false) + } + } + digest, err := manifests.Push(ir.ClientCtx, name, destination, options) + return digest, err } -- cgit v1.2.3-54-g00ecf