mirror of https://github.com/docker/cli.git
implement docker push -a/--all-tags
The `docker push` command up until [v0.9.1](https://github.com/moby/moby/blob/v0.9.1/api/client.go#L998) always pushed all tags of a given image, so `docker push foo/bar` would push (e.g.) all of `foo/bar:latest`, `foo:/bar:v1`, `foo/bar:v1.0.0`. Pushing all tags of an image was not desirable in many case, so docker v0.10.0 enhanced `docker push` to optionally specify a tag to push (`docker push foo/bar:v1`) (see https://github.com/moby/moby/issues/3411 and the pull request that implemented this: https://github.com/moby/moby/pull/4948). This behavior exists up until today, and is confusing, because unlike other commands, `docker push` does not default to use the `:latest` tag when omitted, but instead makes it push "all tags of the image" For example, in the following situation; ``` docker images REPOSITORY TAG IMAGE ID CREATED SIZE thajeztah/myimage latest b534869c81f0 41 hours ago 1.22MB ``` Running `docker push thajeztah/myimage` seemingly does the expected behavior (it pushes `thajeztah/myimage:latest` to Docker Hub), however, it does not so for the reason expected (`:latest` being the default tag), but because `:latest` happens to be the only tag present for the `thajeztah/myimage` image. If another tag exists for the image: ``` docker images REPOSITORY TAG IMAGE ID CREATED SIZE thajeztah/myimage latest b534869c81f0 41 hours ago 1.22MB thajeztah/myimage v1.0.0 b534869c81f0 41 hours ago 1.22MB ``` Running the same command (`docker push thajeztah/myimage`) will push _both_ images to Docker Hub. > Note that the behavior described above is currently not (clearly) documented; > the `docker push` reference documentation (https://docs.docker.com/engine/reference/commandline/push/) does not mention that omitting the tag will push all tags This patch changes the default behavior, and if no tag is specified, `:latest` is assumed. To push _all_ tags, a new flag (`-a` / `--all-tags`) is added, similar to the flag that's present on `docker pull`. With this change: - `docker push myname/myimage` will be the equivalent of `docker push myname/myimage:latest` - to push all images, the user needs to set a flag (`--all-tags`), so `docker push --all-tags myname/myimage:latest` Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
3c8c0ff380
commit
9e620e990f
|
@ -9,12 +9,15 @@ import (
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/cli/cli/streams"
|
"github.com/docker/cli/cli/streams"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/pkg/jsonmessage"
|
"github.com/docker/docker/pkg/jsonmessage"
|
||||||
"github.com/docker/docker/registry"
|
"github.com/docker/docker/registry"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
type pushOptions struct {
|
type pushOptions struct {
|
||||||
|
all bool
|
||||||
remote string
|
remote string
|
||||||
untrusted bool
|
untrusted bool
|
||||||
quiet bool
|
quiet bool
|
||||||
|
@ -35,6 +38,7 @@ func NewPushCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
flags := cmd.Flags()
|
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")
|
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress verbose output")
|
||||||
command.AddTrustSigningFlags(flags, &opts.untrusted, dockerCli.ContentTrustEnabled())
|
command.AddTrustSigningFlags(flags, &opts.untrusted, dockerCli.ContentTrustEnabled())
|
||||||
|
|
||||||
|
@ -44,8 +48,16 @@ func NewPushCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
// RunPush performs a push against the engine based on the specified options
|
// RunPush performs a push against the engine based on the specified options
|
||||||
func RunPush(dockerCli command.Cli, opts pushOptions) error {
|
func RunPush(dockerCli command.Cli, opts pushOptions) error {
|
||||||
ref, err := reference.ParseNormalizedNamed(opts.remote)
|
ref, err := reference.ParseNormalizedNamed(opts.remote)
|
||||||
if err != nil {
|
switch {
|
||||||
|
case err != nil:
|
||||||
return err
|
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
|
// Resolve the Repository name from fqn to RepositoryInfo
|
||||||
|
@ -58,18 +70,28 @@ func RunPush(dockerCli command.Cli, opts pushOptions) error {
|
||||||
|
|
||||||
// Resolve the Auth config relevant for this server
|
// Resolve the Auth config relevant for this server
|
||||||
authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index)
|
authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index)
|
||||||
|
encodedAuth, err := command.EncodeAuthToBase64(authConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(dockerCli, repoInfo.Index, "push")
|
requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(dockerCli, repoInfo.Index, "push")
|
||||||
|
options := types.ImagePushOptions{
|
||||||
if !opts.untrusted {
|
All: opts.all,
|
||||||
return TrustedPush(ctx, dockerCli, repoInfo, ref, authConfig, requestPrivilege)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer responseBody.Close()
|
defer responseBody.Close()
|
||||||
|
if !opts.untrusted {
|
||||||
|
// TODO PushTrustedReference currently doesn't respect `--quiet`
|
||||||
|
return PushTrustedReference(dockerCli, repoInfo, ref, authConfig, responseBody)
|
||||||
|
}
|
||||||
|
|
||||||
if opts.quiet {
|
if opts.quiet {
|
||||||
err = jsonmessage.DisplayJSONMessagesToStream(responseBody, streams.NewOut(ioutil.Discard), nil)
|
err = jsonmessage.DisplayJSONMessagesToStream(responseBody, streams.NewOut(ioutil.Discard), nil)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
|
@ -31,8 +31,8 @@ type target struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrustedPush handles content trust pushing of an image
|
// 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 {
|
func TrustedPush(ctx context.Context, cli command.Cli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, options types.ImagePushOptions) error {
|
||||||
responseBody, err := imagePushPrivileged(ctx, cli, authConfig, ref, requestPrivilege)
|
responseBody, err := cli.Client().ImagePush(ctx, reference.FamiliarString(ref), options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -167,20 +167,6 @@ func AddTargetToAllSignableRoles(repo client.Repository, target *client.Target)
|
||||||
return repo.AddTarget(target, signableRoles...)
|
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
|
// trustedPull handles content trust pulling of an image
|
||||||
func trustedPull(ctx context.Context, cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth, opts PullOptions) error {
|
func trustedPull(ctx context.Context, cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth, opts PullOptions) error {
|
||||||
refs, err := getTrustedPullTargets(cli, imgRefAndAuth)
|
refs, err := getTrustedPullTargets(cli, imgRefAndAuth)
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/cli/cli/command/image"
|
"github.com/docker/cli/cli/command/image"
|
||||||
"github.com/docker/cli/cli/trust"
|
"github.com/docker/cli/cli/trust"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/theupdateframework/notary/client"
|
"github.com/theupdateframework/notary/client"
|
||||||
|
@ -90,7 +91,17 @@ func runSignImage(cli command.Cli, options signOptions) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Fprintf(cli.Err(), "Signing and pushing trust data for local image %s, may overwrite remote trust data\n", imageName)
|
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:
|
default:
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -3067,7 +3067,7 @@ _docker_image_pull() {
|
||||||
_docker_image_push() {
|
_docker_image_push() {
|
||||||
case "$cur" in
|
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)
|
local counter=$(__docker_pos_first_nonflag)
|
||||||
|
|
|
@ -1078,6 +1078,7 @@ __docker_image_subcommand() {
|
||||||
(push)
|
(push)
|
||||||
_arguments $(__docker_arguments) \
|
_arguments $(__docker_arguments) \
|
||||||
$opts_help \
|
$opts_help \
|
||||||
|
"($help -a --all-tags)"{-a,--all-tags}"[Push all tagged images in the repository]" \
|
||||||
"($help)--disable-content-trust[Skip image signing]" \
|
"($help)--disable-content-trust[Skip image signing]" \
|
||||||
"($help -): :__docker_complete_images" && ret=0
|
"($help -): :__docker_complete_images" && ret=0
|
||||||
;;
|
;;
|
||||||
|
|
|
@ -21,6 +21,7 @@ Usage: docker push [OPTIONS] NAME[:TAG]
|
||||||
Push an image or a repository to a registry
|
Push an image or a repository to a registry
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
-a, --all-tags Push all tagged images in the repository
|
||||||
--disable-content-trust Skip image signing (default true)
|
--disable-content-trust Skip image signing (default true)
|
||||||
--help Print usage
|
--help Print usage
|
||||||
-q, --quiet Suppress verbose output
|
-q, --quiet Suppress verbose output
|
||||||
|
@ -28,13 +29,13 @@ Options:
|
||||||
|
|
||||||
## Description
|
## 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.
|
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.
|
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.
|
running in a terminal, terminates the push operation.
|
||||||
|
|
||||||
Progress bars are shown during docker push, which show the uncompressed size. The
|
Progress bars are shown during docker push, which show the uncompressed size. The
|
||||||
|
@ -54,12 +55,12 @@ this via the `--max-concurrent-uploads` daemon option. See the
|
||||||
|
|
||||||
### Push a new image to a registry
|
### 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
|
and then committing it to a new image name. Note that only `a-z0-9-_.` are
|
||||||
allowed when naming images:
|
allowed when naming images:
|
||||||
|
|
||||||
```bash
|
```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
|
Now, push the image to the registry using the image ID. In this example the
|
||||||
|
@ -68,16 +69,63 @@ this, tag the image with the host name or IP address, and the port of the
|
||||||
registry:
|
registry:
|
||||||
|
|
||||||
```bash
|
```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:
|
Check that this worked by running:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ docker images
|
$ docker image ls
|
||||||
```
|
```
|
||||||
|
|
||||||
You should see both `rhel-httpd` and `registry-host:5000/myadmin/rhel-httpd`
|
You should see both `rhel-httpd` and `registry-host:5000/myadmin/rhel-httpd`
|
||||||
listed.
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
|
|
@ -30,12 +30,30 @@ const (
|
||||||
privkey4 = "./testdata/notary/delgkey4.key"
|
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) {
|
func TestPushWithContentTrust(t *testing.T) {
|
||||||
skip.If(t, environment.RemoteDaemon())
|
skip.If(t, environment.RemoteDaemon())
|
||||||
|
|
||||||
dir := fixtures.SetupConfigFile(t)
|
dir := fixtures.SetupConfigFile(t)
|
||||||
defer dir.Remove()
|
defer dir.Remove()
|
||||||
image := createImage(t, registryPrefix, "trust-push", "latest")
|
image := createImage(t, "trust-push", "latest")
|
||||||
|
|
||||||
result := icmd.RunCmd(icmd.Command("docker", "push", image),
|
result := icmd.RunCmd(icmd.Command("docker", "push", image),
|
||||||
fixtures.WithConfig(dir.Path()),
|
fixtures.WithConfig(dir.Path()),
|
||||||
|
@ -68,7 +86,7 @@ func TestPushWithContentTrustUnreachableServer(t *testing.T) {
|
||||||
|
|
||||||
dir := fixtures.SetupConfigFile(t)
|
dir := fixtures.SetupConfigFile(t)
|
||||||
defer dir.Remove()
|
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),
|
result := icmd.RunCmd(icmd.Command("docker", "push", image),
|
||||||
fixtures.WithConfig(dir.Path()),
|
fixtures.WithConfig(dir.Path()),
|
||||||
|
@ -86,7 +104,7 @@ func TestPushWithContentTrustExistingTag(t *testing.T) {
|
||||||
|
|
||||||
dir := fixtures.SetupConfigFile(t)
|
dir := fixtures.SetupConfigFile(t)
|
||||||
defer dir.Remove()
|
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 := icmd.RunCmd(icmd.Command("docker", "push", image))
|
||||||
result.Assert(t, icmd.Success)
|
result.Assert(t, icmd.Success)
|
||||||
|
@ -297,11 +315,14 @@ func TestPushWithContentTrustSignsForRolesWithKeysAndValidPaths(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func createImage(t *testing.T, registryPrefix, repo, tag string) string {
|
func createImage(t *testing.T, repo string, tags ...string) string {
|
||||||
image := fmt.Sprintf("%s/%s:%s", registryPrefix, repo, tag)
|
|
||||||
icmd.RunCommand("docker", "pull", fixtures.AlpineImage).Assert(t, icmd.Success)
|
icmd.RunCommand("docker", "pull", fixtures.AlpineImage).Assert(t, icmd.Success)
|
||||||
|
|
||||||
|
for _, tag := range tags {
|
||||||
|
image := fmt.Sprintf("%s/%s:%s", registryPrefix, repo, tag)
|
||||||
icmd.RunCommand("docker", "tag", fixtures.AlpineImage, image).Assert(t, icmd.Success)
|
icmd.RunCommand("docker", "tag", fixtures.AlpineImage, image).Assert(t, icmd.Success)
|
||||||
return image
|
}
|
||||||
|
return fmt.Sprintf("%s/%s:%s", registryPrefix, repo, tags[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint: unparam
|
//nolint: unparam
|
||||||
|
|
|
@ -23,8 +23,8 @@ 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
|
this, tag the image with the host name or IP address, and the port of the
|
||||||
registry:
|
registry:
|
||||||
|
|
||||||
# docker image tag rhel-httpd 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
|
# docker image push registry-host:5000/myadmin/rhel-httpd:latest
|
||||||
|
|
||||||
Check that this worked by running:
|
Check that this worked by running:
|
||||||
|
|
||||||
|
@ -32,3 +32,42 @@ Check that this worked by running:
|
||||||
|
|
||||||
You should see both `rhel-httpd` and `registry-host:5000/myadmin/rhel-httpd`
|
You should see both `rhel-httpd` and `registry-host:5000/myadmin/rhel-httpd`
|
||||||
listed.
|
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
|
||||||
|
|
Loading…
Reference in New Issue