diff options
-rw-r--r-- | Dockerfile.fedora | 20 | ||||
-rw-r--r-- | cmd/podman/cliconfig/config.go | 1 | ||||
-rw-r--r-- | cmd/podman/commands.go | 2 | ||||
-rw-r--r-- | cmd/podman/container.go | 1 | ||||
-rw-r--r-- | cmd/podman/cp.go | 1 | ||||
-rw-r--r-- | cmd/podman/push.go | 3 | ||||
-rw-r--r-- | cmd/podman/shared/intermediate.go | 10 | ||||
-rw-r--r-- | completions/bash/podman | 1 | ||||
-rw-r--r-- | docs/podman-push.1.md | 18 | ||||
-rw-r--r-- | libpod/image/image.go | 23 | ||||
-rw-r--r-- | pkg/adapter/runtime.go | 4 | ||||
-rw-r--r-- | pkg/adapter/runtime_remote.go | 2 | ||||
-rw-r--r-- | pkg/varlinkapi/images.go | 4 | ||||
-rw-r--r-- | test/e2e/push_test.go | 8 |
14 files changed, 59 insertions, 39 deletions
diff --git a/Dockerfile.fedora b/Dockerfile.fedora index 0f82fdc5c..429597f4f 100644 --- a/Dockerfile.fedora +++ b/Dockerfile.fedora @@ -27,36 +27,24 @@ RUN dnf -y install btrfs-progs-devel \ xz \ slirp4netns \ container-selinux \ + containernetworking-plugins \ iptables && dnf clean all -# Install CNI plugins -ENV CNI_COMMIT 485be65581341430f9106a194a98f0f2412245fb -ENV -RUN set -x \ - && export GOPATH="$(mktemp -d)" GOCACHE="$(mktemp -d)" \ - && git clone https://github.com/containernetworking/plugins.git "$GOPATH/src/github.com/containernetworking/plugins" \ - && cd "$GOPATH/src/github.com/containernetworking/plugins" \ - && git checkout -q "$CNI_COMMIT" \ - && ./build_linux.sh \ - && mkdir -p /usr/libexec/cni \ - && cp bin/* /usr/libexec/cni \ - && rm -rf "$GOPATH" - # Install ginkgo RUN set -x \ - && export GOPATH=/go \ + && export GOPATH=/go GOCACHE="$(mktemp -d)" \ && go get -u github.com/onsi/ginkgo/ginkgo \ && install -D -m 755 "$GOPATH"/bin/ginkgo /usr/bin/ # Install gomega RUN set -x \ - && export GOPATH=/go \ + && export GOPATH=/go GOCACHE="$(mktemp -d)" \ && go get github.com/onsi/gomega/... # Install conmon ENV CONMON_COMMIT 6f3572558b97bc60dd8f8c7f0807748e6ce2c440 RUN set -x \ - && export GOPATH="$(mktemp -d)" \ + && export GOPATH="$(mktemp -d)" GOCACHE="$(mktemp -d)" \ && git clone https://github.com/containers/conmon.git "$GOPATH/src/github.com/containers/conmon.git" \ && cd "$GOPATH/src/github.com/containers/conmon.git" \ && git fetch origin --tags \ diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index e7ad921da..8075baf34 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -423,6 +423,7 @@ type PushValues struct { CertDir string Compress bool Creds string + Digestfile string Format string Quiet bool RemoveSignatures bool diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go index e23918a5b..77c76d1b7 100644 --- a/cmd/podman/commands.go +++ b/cmd/podman/commands.go @@ -11,6 +11,7 @@ const remoteclient = false // Commands that the local client implements func getMainCommands() []*cobra.Command { rootCommands := []*cobra.Command{ + _cpCommand, _playCommand, _loginCommand, _logoutCommand, @@ -39,6 +40,7 @@ func getImageSubCommands() []*cobra.Command { func getContainerSubCommands() []*cobra.Command { return []*cobra.Command{ + _cpCommand, _cleanupCommand, _mountCommand, _refreshCommand, diff --git a/cmd/podman/container.go b/cmd/podman/container.go index 557f5fafa..66b58f06e 100644 --- a/cmd/podman/container.go +++ b/cmd/podman/container.go @@ -55,7 +55,6 @@ var ( _commitCommand, _containerExistsCommand, _contInspectSubCommand, - _cpCommand, _diffCommand, _execCommand, _exportCommand, diff --git a/cmd/podman/cp.go b/cmd/podman/cp.go index ad7253ac0..5e1ca8312 100644 --- a/cmd/podman/cp.go +++ b/cmd/podman/cp.go @@ -55,7 +55,6 @@ func init() { flags.BoolVar(&cpCommand.Pause, "pause", false, "Pause the container while copying") cpCommand.SetHelpTemplate(HelpTemplate()) cpCommand.SetUsageTemplate(UsageTemplate()) - rootCmd.AddCommand(cpCommand.Command) } func cpCmd(c *cliconfig.CpValues) error { diff --git a/cmd/podman/push.go b/cmd/podman/push.go index 43df8c2de..13ebe8a1f 100644 --- a/cmd/podman/push.go +++ b/cmd/podman/push.go @@ -51,6 +51,7 @@ func init() { pushCommand.SetUsageTemplate(UsageTemplate()) flags := pushCommand.Flags() flags.StringVar(&pushCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") + flags.StringVar(&pushCommand.Digestfile, "digestfile", "", "After copying the image, write the digest of the resulting image to the file") flags.StringVarP(&pushCommand.Format, "format", "f", "", "Manifest type (oci, v2s1, or v2s2) to use when pushing an image using the 'dir:' transport (default is manifest type of source)") flags.BoolVarP(&pushCommand.Quiet, "quiet", "q", false, "Don't output progress information when pushing images") flags.BoolVar(&pushCommand.RemoveSignatures, "remove-signatures", false, "Discard any pre-existing signatures in the image") @@ -143,5 +144,5 @@ func pushCmd(c *cliconfig.PushValues) error { SignBy: signBy, } - return runtime.Push(getContext(), srcName, destName, manifestType, c.Authfile, c.SignaturePolicy, writer, c.Compress, so, &dockerRegistryOptions, nil) + return runtime.Push(getContext(), srcName, destName, manifestType, c.Authfile, c.String("digestfile"), c.SignaturePolicy, writer, c.Compress, so, &dockerRegistryOptions, nil) } diff --git a/cmd/podman/shared/intermediate.go b/cmd/podman/shared/intermediate.go index c6c32f8a9..5aaac8687 100644 --- a/cmd/podman/shared/intermediate.go +++ b/cmd/podman/shared/intermediate.go @@ -114,7 +114,7 @@ func (f GenericCLIResults) findResult(flag string) GenericCLIResult { if ok { return val } - logrus.Errorf("unable to find flag %s", flag) + logrus.Debugf("unable to find flag %s", flag) return nil } @@ -366,12 +366,10 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes m["add-host"] = newCRStringSlice(c, "add-host") m["annotation"] = newCRStringSlice(c, "annotation") m["attach"] = newCRStringSlice(c, "attach") - m["authfile"] = newCRString(c, "authfile") m["blkio-weight"] = newCRString(c, "blkio-weight") m["blkio-weight-device"] = newCRStringSlice(c, "blkio-weight-device") m["cap-add"] = newCRStringSlice(c, "cap-add") m["cap-drop"] = newCRStringSlice(c, "cap-drop") - m["cgroupns"] = newCRString(c, "cgroupns") m["cgroup-parent"] = newCRString(c, "cgroup-parent") m["cidfile"] = newCRString(c, "cidfile") m["conmon-pidfile"] = newCRString(c, "conmon-pidfile") @@ -395,7 +393,6 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes m["dns-search"] = newCRStringSlice(c, "dns-search") m["entrypoint"] = newCRString(c, "entrypoint") m["env"] = newCRStringArray(c, "env") - m["env-host"] = newCRBool(c, "env-host") m["env-file"] = newCRStringSlice(c, "env-file") m["expose"] = newCRStringSlice(c, "expose") m["gidmap"] = newCRStringSlice(c, "gidmap") @@ -407,7 +404,6 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes m["healthcheck-start-period"] = newCRString(c, "health-start-period") m["healthcheck-timeout"] = newCRString(c, "health-timeout") m["hostname"] = newCRString(c, "hostname") - m["http-proxy"] = newCRBool(c, "http-proxy") m["image-volume"] = newCRString(c, "image-volume") m["init"] = newCRBool(c, "init") m["init-path"] = newCRString(c, "init-path") @@ -465,6 +461,10 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes m["workdir"] = newCRString(c, "workdir") // global flag if !remote { + m["authfile"] = newCRString(c, "authfile") + m["cgroupns"] = newCRString(c, "cgroupns") + m["env-host"] = newCRBool(c, "env-host") + m["http-proxy"] = newCRBool(c, "http-proxy") m["trace"] = newCRBool(c, "trace") m["syslog"] = newCRBool(c, "syslog") } diff --git a/completions/bash/podman b/completions/bash/podman index 962c15a95..7280f4040 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -1758,6 +1758,7 @@ _podman_mount() { _podman_push() { local boolean_options=" --compress + --digestflag --help -h --quiet diff --git a/docs/podman-push.1.md b/docs/podman-push.1.md index 2058a432c..29e4044a3 100644 --- a/docs/podman-push.1.md +++ b/docs/podman-push.1.md @@ -61,13 +61,17 @@ value can be entered. The password is entered without echo. **--cert-dir**=*path* Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. -Default certificates directory is _/etc/containers/certs.d_. (Not available for remote commands) (Not available for remote commands) +Default certificates directory is _/etc/containers/certs.d_. (Not available for remote commands) **--compress** Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type, compressed or uncompressed, as source) Note: This flag can only be set when using the **dir** transport +**--digestfile** *Digestfile* + +After copying the image, write the digest of the resulting image to the file. (Not available for remote commands) + **--format**, **-f**=*format* Manifest Type (oci, v2s1, or v2s2) to use when pushing an image to a directory using the 'dir:' transport (default is manifest type of source) @@ -93,19 +97,23 @@ TLS verification will be used unless the target registry is listed as an insecur ## EXAMPLE -This example extracts the imageID image to a local directory in docker format. +This example pushes the image specified by the imageID to a local directory in docker format. `# podman push imageID dir:/path/to/image` -This example extracts the imageID image to a local directory in oci format. +This example pushes the image specified by the imageID to a local directory in oci format. `# podman push imageID oci-archive:/path/to/layout:image:tag` -This example extracts the imageID image to a container registry named registry.example.com +This example pushes the image specified by the imageID to a container registry named registry.example.com `# podman push imageID docker://registry.example.com/repository:tag` -This example extracts the imageID image and puts into the local docker container store +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` + +This example pushes the image specified by the imageID and puts it into the local docker container store `# podman push imageID docker-daemon:image:tag` diff --git a/libpod/image/image.go b/libpod/image/image.go index cb7c390c6..1ff271a4d 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io" + "io/ioutil" "os" "path/filepath" "strings" @@ -555,7 +556,7 @@ func (i *Image) UntagImage(tag string) error { // PushImageToHeuristicDestination pushes the given image to "destination", which is heuristically parsed. // Use PushImageToReference if the destination is known precisely. -func (i *Image) PushImageToHeuristicDestination(ctx context.Context, destination, manifestMIMEType, authFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { +func (i *Image) PushImageToHeuristicDestination(ctx context.Context, destination, manifestMIMEType, authFile, digestFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { if destination == "" { return errors.Wrapf(syscall.EINVAL, "destination image name must be specified") } @@ -573,11 +574,11 @@ func (i *Image) PushImageToHeuristicDestination(ctx context.Context, destination return err } } - return i.PushImageToReference(ctx, dest, manifestMIMEType, authFile, signaturePolicyPath, writer, forceCompress, signingOptions, dockerRegistryOptions, additionalDockerArchiveTags) + return i.PushImageToReference(ctx, dest, manifestMIMEType, authFile, digestFile, signaturePolicyPath, writer, forceCompress, signingOptions, dockerRegistryOptions, additionalDockerArchiveTags) } // PushImageToReference pushes the given image to a location described by the given path -func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageReference, manifestMIMEType, authFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { +func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageReference, manifestMIMEType, authFile, digestFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { sc := GetSystemContext(signaturePolicyPath, authFile, forceCompress) sc.BlobInfoCacheDir = filepath.Join(i.imageruntime.store.GraphRoot(), "cache") @@ -599,10 +600,22 @@ func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageRefere copyOptions := getCopyOptions(sc, writer, nil, dockerRegistryOptions, signingOptions, manifestMIMEType, additionalDockerArchiveTags) copyOptions.DestinationCtx.SystemRegistriesConfPath = registries.SystemRegistriesConfPath() // FIXME: Set this more globally. Probably no reason not to have it in every types.SystemContext, and to compute the value just once in one place. // Copy the image to the remote destination - _, err = cp.Image(ctx, policyContext, dest, src, copyOptions) + manifestBytes, err := cp.Image(ctx, policyContext, dest, src, copyOptions) if err != nil { return errors.Wrapf(err, "Error copying image to the remote destination") } + digest, err := manifest.Digest(manifestBytes) + if err != nil { + return errors.Wrapf(err, "error computing digest of manifest of new image %q", transports.ImageName(dest)) + } + + logrus.Debugf("Successfully pushed %s with digest %s", transports.ImageName(dest), digest.String()) + + if digestFile != "" { + if err = ioutil.WriteFile(digestFile, []byte(digest.String()), 0644); err != nil { + return errors.Wrapf(err, "failed to write digest to file %q", digestFile) + } + } i.newImageEvent(events.Push) return nil } @@ -1358,7 +1371,7 @@ func (i *Image) Save(ctx context.Context, source, format, output string, moreTag return err } } - if err := i.PushImageToReference(ctx, destRef, manifestType, "", "", writer, compress, SigningOptions{}, &DockerRegistryOptions{}, additionaltags); err != nil { + if err := i.PushImageToReference(ctx, destRef, manifestType, "", "", "", writer, compress, SigningOptions{}, &DockerRegistryOptions{}, additionaltags); err != nil { return errors.Wrapf(err, "unable to save %q", source) } i.newImageEvent(events.Save) diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go index 7d4f97b28..0537308f8 100644 --- a/pkg/adapter/runtime.go +++ b/pkg/adapter/runtime.go @@ -201,12 +201,12 @@ func (r *LocalRuntime) RemoveVolumes(ctx context.Context, c *cliconfig.VolumeRmV } // Push is a wrapper to push an image to a registry -func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestMIMEType, authfile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions image.SigningOptions, dockerRegistryOptions *image.DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { +func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestMIMEType, authfile, digestfile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions image.SigningOptions, dockerRegistryOptions *image.DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { newImage, err := r.ImageRuntime().NewFromLocal(srcName) if err != nil { return err } - return newImage.PushImageToHeuristicDestination(ctx, destination, manifestMIMEType, authfile, signaturePolicyPath, writer, forceCompress, signingOptions, dockerRegistryOptions, nil) + return newImage.PushImageToHeuristicDestination(ctx, destination, manifestMIMEType, authfile, digestfile, signaturePolicyPath, writer, forceCompress, signingOptions, dockerRegistryOptions, nil) } // InspectVolumes returns a slice of volumes based on an arg list or --all diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go index 683bf1d35..8588966b6 100644 --- a/pkg/adapter/runtime_remote.go +++ b/pkg/adapter/runtime_remote.go @@ -619,7 +619,7 @@ func (r *LocalRuntime) RemoveVolumes(ctx context.Context, c *cliconfig.VolumeRmV return iopodman.VolumeRemove().Call(r.Conn, rmOpts) } -func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestMIMEType, authfile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions image.SigningOptions, dockerRegistryOptions *image.DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { +func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestMIMEType, authfile, digestfile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions image.SigningOptions, dockerRegistryOptions *image.DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { reply, err := iopodman.PushImage().Send(r.Conn, varlink.More, srcName, destination, forceCompress, manifestMIMEType, signingOptions.RemoveSignatures, signingOptions.SignBy) if err != nil { diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go index fe7f11b4d..a1fdf5955 100644 --- a/pkg/varlinkapi/images.go +++ b/pkg/varlinkapi/images.go @@ -353,7 +353,7 @@ func (i *LibpodAPI) PushImage(call iopodman.VarlinkCall, name, tag string, compr output := bytes.NewBuffer([]byte{}) c := make(chan error) go func() { - err := newImage.PushImageToHeuristicDestination(getContext(), destname, manifestType, "", "", output, compress, so, &dockerRegistryOptions, nil) + err := newImage.PushImageToHeuristicDestination(getContext(), destname, manifestType, "", "", "", output, compress, so, &dockerRegistryOptions, nil) c <- err close(c) }() @@ -615,7 +615,7 @@ func (i *LibpodAPI) ExportImage(call iopodman.VarlinkCall, name, destination str return err } - if err := newImage.PushImageToHeuristicDestination(getContext(), destination, "", "", "", nil, compress, image.SigningOptions{}, &image.DockerRegistryOptions{}, additionalTags); err != nil { + if err := newImage.PushImageToHeuristicDestination(getContext(), destination, "", "", "", "", nil, compress, image.SigningOptions{}, &image.DockerRegistryOptions{}, additionalTags); err != nil { return call.ReplyErrorOccurred(err.Error()) } return call.ReplyExportImage(newImage.ID()) diff --git a/test/e2e/push_test.go b/test/e2e/push_test.go index cf6279f2f..4360eeece 100644 --- a/test/e2e/push_test.go +++ b/test/e2e/push_test.go @@ -76,6 +76,14 @@ var _ = Describe("Podman push", func() { push := podmanTest.PodmanNoCache([]string{"push", "--tls-verify=false", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"}) push.WaitWithDefaultTimeout() Expect(push.ExitCode()).To(Equal(0)) + + // Test --digestfile option + push2 := podmanTest.PodmanNoCache([]string{"push", "--tls-verify=false", "--digestfile=/tmp/digestfile.txt", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"}) + push2.WaitWithDefaultTimeout() + fi, err := os.Lstat("/tmp/digestfile.txt") + Expect(err).To(BeNil()) + Expect(fi.Name()).To(Equal("digestfile.txt")) + Expect(push2.ExitCode()).To(Equal(0)) }) It("podman push to local registry with authorization", func() { |