diff --git a/cmd/podman/images/push.go b/cmd/podman/images/push.go index 1b3419014a..f716ea64f8 100644 --- a/cmd/podman/images/push.go +++ b/cmd/podman/images/push.go @@ -73,8 +73,8 @@ func init() { func pushFlags(cmd *cobra.Command) { flags := cmd.Flags() - // For now default All flag to true, for pushing of manifest lists - pushOptions.All = true + flags.BoolVarP(&pushOptions.All, "all-tags", "a", false, "Push all tagged images in the repository") + authfileFlagName := "authfile" flags.StringVar(&pushOptions.Authfile, authfileFlagName, auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") _ = cmd.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault) diff --git a/docs/source/markdown/podman-push.1.md b/docs/source/markdown/podman-push.1.md index 3cda982aca..1c54c5fe54 100644 --- a/docs/source/markdown/podman-push.1.md +++ b/docs/source/markdown/podman-push.1.md @@ -47,6 +47,10 @@ $ podman push myimage oci-archive:/tmp/myimage ## OPTIONS +#### **--all-tags**, **-a** + +Push all tags for a given image to the repository. When using this flag, do not specify a tag for the DESTINATION. + #### **--authfile**=*path* Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `podman login`. @@ -121,6 +125,10 @@ This example pushes the image specified by the imageID to a container registry n `# podman push imageID docker://registry.example.com/repository:tag` +This example pushes all tags for the image specified by the imageID to a container registry named registry.example.com + +`# podman push --all-tags imageID docker://registry.example.com/repository` + This example pushes the image specified by the imageID to a container registry named registry.example.com and saves the digest in the specified digestfile. `# podman push --digestfile=/tmp/mydigest imageID docker://registry.example.com/repository:tag` diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 38008c7b9c..a72491e187 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -305,6 +305,7 @@ func (ir *ImageEngine) Push(ctx context.Context, source string, destination stri pushOptions.RemoveSignatures = options.RemoveSignatures pushOptions.SignBy = options.SignBy pushOptions.InsecureSkipTLSVerify = options.SkipTLSVerify + pushOptions.AllTags = options.All compressionFormat := options.CompressionFormat if compressionFormat == "" { diff --git a/test/e2e/push_test.go b/test/e2e/push_test.go index 97567e40de..28e240a7cf 100644 --- a/test/e2e/push_test.go +++ b/test/e2e/push_test.go @@ -117,14 +117,18 @@ var _ = Describe("Podman push", func() { push.WaitWithDefaultTimeout() Expect(push).Should(Exit(0)) + pushA := podmanTest.Podman([]string{"push", "-q", "-a", "--tls-verify=false", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"}) + pushA.WaitWithDefaultTimeout() + Expect(pushA).Should(Exit(0)) + SkipIfRemote("Remote does not support --digestfile") // Test --digestfile option - push2 := podmanTest.Podman([]string{"push", "--tls-verify=false", "--digestfile=/tmp/digestfile.txt", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"}) - push2.WaitWithDefaultTimeout() + pushD := podmanTest.Podman([]string{"push", "--tls-verify=false", "--digestfile=/tmp/digestfile.txt", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"}) + pushD.WaitWithDefaultTimeout() fi, err := os.Lstat("/tmp/digestfile.txt") Expect(err).To(BeNil()) Expect(fi.Name()).To(Equal("digestfile.txt")) - Expect(push2).Should(Exit(0)) + Expect(pushD).Should(Exit(0)) }) It("podman push to local registry with authorization", func() { diff --git a/vendor/github.com/containers/common/libimage/push.go b/vendor/github.com/containers/common/libimage/push.go index 7203838aa6..7dcfce3e59 100644 --- a/vendor/github.com/containers/common/libimage/push.go +++ b/vendor/github.com/containers/common/libimage/push.go @@ -2,6 +2,7 @@ package libimage import ( "context" + "fmt" "time" dockerArchiveTransport "github.com/containers/image/v5/docker/archive" @@ -13,6 +14,7 @@ import ( // PushOptions allows for custommizing image pushes. type PushOptions struct { CopyOptions + AllTags bool } // Push pushes the specified source which must refer to an image in the local @@ -36,6 +38,36 @@ func (r *Runtime) Push(ctx context.Context, source, destination string, options return nil, err } + // If specified to push all tags, look them up + if options.AllTags { + namedRepoTags, err := image.NamedTaggedRepoTags() + if err != nil { + return nil, err + } + + logrus.Debugf("Flag --all-tags true, found: %s", namedRepoTags) + + for _, tag := range namedRepoTags { + image, resolvedSource, err := r.LookupImage(tag.Name(), nil) + if err != nil { + return nil, err + } + + fullNamedTag := fmt.Sprintf("%s:%s", destination, tag.Tag()) + _, err = pushImage(ctx, source, fullNamedTag, options, image, resolvedSource, r) + if err != nil { + return nil, err + } + } + } else { + return pushImage(ctx, source, destination, options, image, resolvedSource, r) + } + + return nil, nil +} + +func pushImage(ctx context.Context, source, destination string, options *PushOptions, image *Image, resolvedSource string, r *Runtime) ([]byte, error) { + srcRef, err := image.StorageReference() if err != nil { return nil, err