Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions cli/command/image/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ import (
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/streams"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/registry"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)

type pushOptions struct {
all bool
remote string
untrusted bool
quiet bool
Expand All @@ -35,6 +38,7 @@ func NewPushCommand(dockerCli command.Cli) *cobra.Command {
}

flags := cmd.Flags()
flags.BoolVarP(&opts.all, "all-tags", "a", false, "Push all tagged images in the repository")
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress verbose output")
command.AddTrustSigningFlags(flags, &opts.untrusted, dockerCli.ContentTrustEnabled())

Expand All @@ -44,8 +48,16 @@ func NewPushCommand(dockerCli command.Cli) *cobra.Command {
// RunPush performs a push against the engine based on the specified options
func RunPush(dockerCli command.Cli, opts pushOptions) error {
ref, err := reference.ParseNormalizedNamed(opts.remote)
if err != nil {
switch {
case err != nil:
return err
case opts.all && !reference.IsNameOnly(ref):
return errors.New("tag can't be used with --all-tags/-a")
case !opts.all && reference.IsNameOnly(ref):
ref = reference.TagNameOnly(ref)
if tagged, ok := ref.(reference.Tagged); ok && !opts.quiet {
_, _ = fmt.Fprintf(dockerCli.Out(), "Using default tag: %s\n", tagged.Tag())
}
}

// Resolve the Repository name from fqn to RepositoryInfo
Expand All @@ -58,18 +70,28 @@ func RunPush(dockerCli command.Cli, opts pushOptions) error {

// Resolve the Auth config relevant for this server
authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index)
encodedAuth, err := command.EncodeAuthToBase64(authConfig)
if err != nil {
return err
}
requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(dockerCli, repoInfo.Index, "push")

if !opts.untrusted {
return TrustedPush(ctx, dockerCli, repoInfo, ref, authConfig, requestPrivilege)
options := types.ImagePushOptions{
All: opts.all,
RegistryAuth: encodedAuth,
PrivilegeFunc: requestPrivilege,
}

responseBody, err := imagePushPrivileged(ctx, dockerCli, authConfig, ref, requestPrivilege)
responseBody, err := dockerCli.Client().ImagePush(ctx, reference.FamiliarString(ref), options)
if err != nil {
return err
}

defer responseBody.Close()
if !opts.untrusted {
// TODO PushTrustedReference currently doesn't respect `--quiet`
return PushTrustedReference(dockerCli, repoInfo, ref, authConfig, responseBody)
}

if opts.quiet {
err = jsonmessage.DisplayJSONMessagesToStream(responseBody, streams.NewOut(ioutil.Discard), nil)
if err == nil {
Expand Down
18 changes: 2 additions & 16 deletions cli/command/image/trust.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ type target struct {
}

// TrustedPush handles content trust pushing of an image
func TrustedPush(ctx context.Context, cli command.Cli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error {
responseBody, err := imagePushPrivileged(ctx, cli, authConfig, ref, requestPrivilege)
func TrustedPush(ctx context.Context, cli command.Cli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, options types.ImagePushOptions) error {
responseBody, err := cli.Client().ImagePush(ctx, reference.FamiliarString(ref), options)
if err != nil {
return err
}
Expand Down Expand Up @@ -167,20 +167,6 @@ func AddTargetToAllSignableRoles(repo client.Repository, target *client.Target)
return repo.AddTarget(target, signableRoles...)
}

// imagePushPrivileged push the image
func imagePushPrivileged(ctx context.Context, cli command.Cli, authConfig types.AuthConfig, ref reference.Reference, requestPrivilege types.RequestPrivilegeFunc) (io.ReadCloser, error) {
encodedAuth, err := command.EncodeAuthToBase64(authConfig)
if err != nil {
return nil, err
}
options := types.ImagePushOptions{
RegistryAuth: encodedAuth,
PrivilegeFunc: requestPrivilege,
}

return cli.Client().ImagePush(ctx, reference.FamiliarString(ref), options)
}

// trustedPull handles content trust pulling of an image
func trustedPull(ctx context.Context, cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth, opts PullOptions) error {
refs, err := getTrustedPullTargets(cli, imgRefAndAuth)
Expand Down
13 changes: 12 additions & 1 deletion cli/command/trust/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/image"
"github.com/docker/cli/cli/trust"
"github.com/docker/docker/api/types"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/theupdateframework/notary/client"
Expand Down Expand Up @@ -90,7 +91,17 @@ func runSignImage(cli command.Cli, options signOptions) error {
return err
}
fmt.Fprintf(cli.Err(), "Signing and pushing trust data for local image %s, may overwrite remote trust data\n", imageName)
return image.TrustedPush(ctx, cli, imgRefAndAuth.RepoInfo(), imgRefAndAuth.Reference(), *imgRefAndAuth.AuthConfig(), requestPrivilege)

authConfig := command.ResolveAuthConfig(ctx, cli, imgRefAndAuth.RepoInfo().Index)
encodedAuth, err := command.EncodeAuthToBase64(authConfig)
if err != nil {
return err
}
options := types.ImagePushOptions{
RegistryAuth: encodedAuth,
PrivilegeFunc: requestPrivilege,
}
return image.TrustedPush(ctx, cli, imgRefAndAuth.RepoInfo(), imgRefAndAuth.Reference(), *imgRefAndAuth.AuthConfig(), options)
default:
return err
}
Expand Down
2 changes: 1 addition & 1 deletion contrib/completion/bash/docker
Original file line number Diff line number Diff line change
Expand Up @@ -3067,7 +3067,7 @@ _docker_image_pull() {
_docker_image_push() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--disable-content-trust=false --help --quiet -q" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--all-tags -a --disable-content-trust=false --help --quiet -q" -- "$cur" ) )
;;
*)
local counter=$(__docker_pos_first_nonflag)
Expand Down
1 change: 1 addition & 0 deletions contrib/completion/zsh/_docker
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,7 @@ __docker_image_subcommand() {
(push)
_arguments $(__docker_arguments) \
$opts_help \
"($help -a --all-tags)"{-a,--all-tags}"[Push all tagged images in the repository]" \
"($help)--disable-content-trust[Skip image signing]" \
"($help -): :__docker_complete_images" && ret=0
;;
Expand Down
64 changes: 56 additions & 8 deletions docs/reference/commandline/push.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,21 @@ Usage: docker push [OPTIONS] NAME[:TAG]
Push an image or a repository to a registry

Options:
-a, --all-tags Push all tagged images in the repository
--disable-content-trust Skip image signing (default true)
--help Print usage
-q, --quiet Suppress verbose output
```

## Description

Use `docker push` to share your images to the [Docker Hub](https://hub.docker.com)
Use `docker image push` to share your images to the [Docker Hub](https://hub.docker.com)
registry or to a self-hosted one.

Refer to the [`docker tag`](tag.md) reference for more information about valid
Refer to the [`docker image tag`](tag.md) reference for more information about valid
image and tag names.

Killing the `docker push` process, for example by pressing `CTRL-c` while it is
Killing the `docker image push` process, for example by pressing `CTRL-c` while it is
running in a terminal, terminates the push operation.

Progress bars are shown during docker push, which show the uncompressed size. The
Expand All @@ -54,12 +55,12 @@ this via the `--max-concurrent-uploads` daemon option. See the

### Push a new image to a registry

First save the new image by finding the container ID (using [`docker ps`](ps.md))
First save the new image by finding the container ID (using [`docker container ls`](ps.md))
and then committing it to a new image name. Note that only `a-z0-9-_.` are
allowed when naming images:

```bash
$ docker commit c16378f943fe rhel-httpd
$ docker container commit c16378f943fe rhel-httpd:latest
```

Now, push the image to the registry using the image ID. In this example the
Expand All @@ -68,16 +69,63 @@ this, tag the image with the host name or IP address, and the port of the
registry:

```bash
$ docker tag rhel-httpd registry-host:5000/myadmin/rhel-httpd
$ docker image tag rhel-httpd:latest registry-host:5000/myadmin/rhel-httpd:latest

$ docker push registry-host:5000/myadmin/rhel-httpd
$ docker image push registry-host:5000/myadmin/rhel-httpd:latest
```

Check that this worked by running:

```bash
$ docker images
$ docker image ls
```

You should see both `rhel-httpd` and `registry-host:5000/myadmin/rhel-httpd`
listed.

### Push all tags of an image

Use the `-a` (or `--all-tags`) option to push To push all tags of a local image.

The following example creates multiple tags for an image, and pushes all those
tags to Docker Hub.


```bash
$ docker image tag myimage registry-host:5000/myname/myimage:latest
$ docker image tag myimage registry-host:5000/myname/myimage:v1.0.1
$ docker image tag myimage registry-host:5000/myname/myimage:v1.0
$ docker image tag myimage registry-host:5000/myname/myimage:v1
```

The image is now tagged under multiple names:

```bash
$ docker image ls

REPOSITORY TAG IMAGE ID CREATED SIZE
myimage latest 6d5fcfe5ff17 2 hours ago 1.22MB
registry-host:5000/myname/myimage latest 6d5fcfe5ff17 2 hours ago 1.22MB
registry-host:5000/myname/myimage v1 6d5fcfe5ff17 2 hours ago 1.22MB
registry-host:5000/myname/myimage v1.0 6d5fcfe5ff17 2 hours ago 1.22MB
registry-host:5000/myname/myimage v1.0.1 6d5fcfe5ff17 2 hours ago 1.22MB
```

When pushing with the `--all-tags` option, all tags of the `registry-host:5000/myname/myimage`
image are pushed:


```bash
$ docker image push --all-tags registry-host:5000/myname/myimage

The push refers to repository [registry-host:5000/myname/myimage]
195be5f8be1d: Pushed
latest: digest: sha256:edafc0a0fb057813850d1ba44014914ca02d671ae247107ca70c94db686e7de6 size: 4527
195be5f8be1d: Layer already exists
v1: digest: sha256:edafc0a0fb057813850d1ba44014914ca02d671ae247107ca70c94db686e7de6 size: 4527
195be5f8be1d: Layer already exists
v1.0: digest: sha256:edafc0a0fb057813850d1ba44014914ca02d671ae247107ca70c94db686e7de6 size: 4527
195be5f8be1d: Layer already exists
v1.0.1: digest: sha256:edafc0a0fb057813850d1ba44014914ca02d671ae247107ca70c94db686e7de6 size: 4527
```

35 changes: 28 additions & 7 deletions e2e/image/push_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,30 @@ const (
privkey4 = "./testdata/notary/delgkey4.key"
)

func TestPushAllTags(t *testing.T) {
skip.If(t, environment.RemoteDaemon())

_ = createImage(t, "push-all-tags", "latest", "v1", "v1.0", "v1.0.1")
result := icmd.RunCmd(icmd.Command("docker", "push", "--all-tags", registryPrefix+"/push-all-tags"))

result.Assert(t, icmd.Success)
golden.Assert(t, result.Stderr(), "push-with-content-trust-err.golden")
output.Assert(t, result.Stdout(), map[int]func(string) error{
0: output.Equals("The push refers to repository [registry:5000/push-all-tags]"),
1: output.Equals("5bef08742407: Preparing"),
3: output.Equals("latest: digest: sha256:641b95ddb2ea9dc2af1a0113b6b348ebc20872ba615204fbe12148e98fd6f23d size: 528"),
6: output.Equals("v1: digest: sha256:641b95ddb2ea9dc2af1a0113b6b348ebc20872ba615204fbe12148e98fd6f23d size: 528"),
9: output.Equals("v1.0: digest: sha256:641b95ddb2ea9dc2af1a0113b6b348ebc20872ba615204fbe12148e98fd6f23d size: 528"),
12: output.Equals("v1.0.1: digest: sha256:641b95ddb2ea9dc2af1a0113b6b348ebc20872ba615204fbe12148e98fd6f23d size: 528"),
})
}

func TestPushWithContentTrust(t *testing.T) {
skip.If(t, environment.RemoteDaemon())

dir := fixtures.SetupConfigFile(t)
defer dir.Remove()
image := createImage(t, registryPrefix, "trust-push", "latest")
image := createImage(t, "trust-push", "latest")

result := icmd.RunCmd(icmd.Command("docker", "push", image),
fixtures.WithConfig(dir.Path()),
Expand Down Expand Up @@ -68,7 +86,7 @@ func TestPushWithContentTrustUnreachableServer(t *testing.T) {

dir := fixtures.SetupConfigFile(t)
defer dir.Remove()
image := createImage(t, registryPrefix, "trust-push-unreachable", "latest")
image := createImage(t, "trust-push-unreachable", "latest")

result := icmd.RunCmd(icmd.Command("docker", "push", image),
fixtures.WithConfig(dir.Path()),
Expand All @@ -86,7 +104,7 @@ func TestPushWithContentTrustExistingTag(t *testing.T) {

dir := fixtures.SetupConfigFile(t)
defer dir.Remove()
image := createImage(t, registryPrefix, "trust-push-existing", "latest")
image := createImage(t, "trust-push-existing", "latest")

result := icmd.RunCmd(icmd.Command("docker", "push", image))
result.Assert(t, icmd.Success)
Expand Down Expand Up @@ -297,11 +315,14 @@ func TestPushWithContentTrustSignsForRolesWithKeysAndValidPaths(t *testing.T) {
})
}

func createImage(t *testing.T, registryPrefix, repo, tag string) string {
image := fmt.Sprintf("%s/%s:%s", registryPrefix, repo, tag)
func createImage(t *testing.T, repo string, tags ...string) string {
icmd.RunCommand("docker", "pull", fixtures.AlpineImage).Assert(t, icmd.Success)
icmd.RunCommand("docker", "tag", fixtures.AlpineImage, image).Assert(t, icmd.Success)
return image

for _, tag := range tags {
image := fmt.Sprintf("%s/%s:%s", registryPrefix, repo, tag)
icmd.RunCommand("docker", "tag", fixtures.AlpineImage, image).Assert(t, icmd.Success)
}
return fmt.Sprintf("%s/%s:%s", registryPrefix, repo, tags[0])
}

//nolint: unparam
Expand Down
43 changes: 41 additions & 2 deletions man/src/image/push.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,51 @@ registry is on host named `registry-host` and listening on port `5000`. To do
this, tag the image with the host name or IP address, and the port of the
registry:

# docker image tag rhel-httpd registry-host:5000/myadmin/rhel-httpd
# docker image push registry-host:5000/myadmin/rhel-httpd
# docker image tag rhel-httpd registry-host:5000/myadmin/rhel-httpd:latest
# docker image push registry-host:5000/myadmin/rhel-httpd:latest

Check that this worked by running:

# docker image ls

You should see both `rhel-httpd` and `registry-host:5000/myadmin/rhel-httpd`
listed.

### Push all tags of an image

Use the `-a` (or `--all-tags`) option to push To push all tags of a local image.

The following example creates multiple tags for an image, and pushes all those
tags to Docker Hub.

$ docker image tag myimage registry-host:5000/myname/myimage:latest
$ docker image tag myimage registry-host:5000/myname/myimage:v1.0.1
$ docker image tag myimage registry-host:5000/myname/myimage:v1.0
$ docker image tag myimage registry-host:5000/myname/myimage:v1

The image is now tagged under multiple names:

$ docker image ls

REPOSITORY TAG IMAGE ID CREATED SIZE
myimage latest 6d5fcfe5ff17 2 hours ago 1.22MB
registry-host:5000/myname/myimage latest 6d5fcfe5ff17 2 hours ago 1.22MB
registry-host:5000/myname/myimage v1 6d5fcfe5ff17 2 hours ago 1.22MB
registry-host:5000/myname/myimage v1.0 6d5fcfe5ff17 2 hours ago 1.22MB
registry-host:5000/myname/myimage v1.0.1 6d5fcfe5ff17 2 hours ago 1.22MB

When pushing with the `--all-tags` option, all tags of the `registry-host:5000/myname/myimage`
image are pushed:


$ docker image push --all-tags registry-host:5000/myname/myimage

The push refers to repository [registry-host:5000/myname/myimage]
195be5f8be1d: Pushed
latest: digest: sha256:edafc0a0fb057813850d1ba44014914ca02d671ae247107ca70c94db686e7de6 size: 4527
195be5f8be1d: Layer already exists
v1: digest: sha256:edafc0a0fb057813850d1ba44014914ca02d671ae247107ca70c94db686e7de6 size: 4527
195be5f8be1d: Layer already exists
v1.0: digest: sha256:edafc0a0fb057813850d1ba44014914ca02d671ae247107ca70c94db686e7de6 size: 4527
195be5f8be1d: Layer already exists
v1.0.1: digest: sha256:edafc0a0fb057813850d1ba44014914ca02d671ae247107ca70c94db686e7de6 size: 4527