Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cli/push: Add platform switch #4984

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
82 changes: 80 additions & 2 deletions cli/command/image/push.go
Expand Up @@ -2,9 +2,12 @@ package image

import (
"context"
"encoding/json"
"fmt"
"io"
"os"

"github.com/containerd/containerd/platforms"
"github.com/distribution/reference"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
Expand All @@ -14,6 +17,7 @@ import (
registrytypes "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/registry"
"github.com/moby/term"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
Expand All @@ -23,6 +27,7 @@ type pushOptions struct {
remote string
untrusted bool
quiet bool
platform string
}

// NewPushCommand creates a new `docker push` command
Expand All @@ -48,12 +53,31 @@ func NewPushCommand(dockerCli command.Cli) *cobra.Command {
flags.BoolVarP(&opts.all, "all-tags", "a", false, "Push all tags of an image to the repository")
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress verbose output")
command.AddTrustSigningFlags(flags, &opts.untrusted, dockerCli.ContentTrustEnabled())
flags.StringVar(&opts.platform, "platform", os.Getenv("DOCKER_DEFAULT_PLATFORM"),
`Push a platform-specific manifest as a single-platform image to the registry.
'os[/arch[/variant]]': Explicit platform (eg. linux/amd64)`)
flags.SetAnnotation("platform", "version", []string{"1.46"})

return cmd
}

// RunPush performs a push against the engine based on the specified options
func RunPush(ctx context.Context, dockerCli command.Cli, opts pushOptions) error {
var platform *platforms.Platform
if opts.platform != "" {
p, err := platforms.Parse(opts.platform)
if err != nil {
_, _ = fmt.Fprintf(dockerCli.Err(), "Invalid platform %s", opts.platform)
return err
}
platform = &p

printNote(dockerCli, `Selecting a single platform will only push one matching image manifest from a multi-platform image index.
This means that any other components attached to the multi-platform image index (like Buildkit attestations) won't be pushed.
If you want to only push a single platform image while preserving the attestations, please use 'docker convert\n'
`)
}

ref, err := reference.ParseNormalizedNamed(opts.remote)
switch {
case err != nil:
Expand Down Expand Up @@ -84,25 +108,79 @@ func RunPush(ctx context.Context, dockerCli command.Cli, opts pushOptions) error
All: opts.all,
RegistryAuth: encodedAuth,
PrivilegeFunc: requestPrivilege,
Platform: platform,
}

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

defer func() {
if strippedNote != "" {
fmt.Fprintln(dockerCli.Err(), "")
printNote(dockerCli, strippedNote)
}
}()

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(io.Discard), nil)
err = jsonmessage.DisplayJSONMessagesToStream(responseBody, streams.NewOut(io.Discard), handleAux(dockerCli))
if err == nil {
fmt.Fprintln(dockerCli.Out(), ref.String())
}
return err
}
return jsonmessage.DisplayJSONMessagesToStream(responseBody, dockerCli.Out(), nil)
return jsonmessage.DisplayJSONMessagesToStream(responseBody, dockerCli.Out(), handleAux(dockerCli))
}

var strippedNote = ""

func handleAux(dockerCli command.Cli) func(jm jsonmessage.JSONMessage) {
return func(jm jsonmessage.JSONMessage) {
b := []byte(*jm.Aux)

aux := map[string]string{}
err := json.Unmarshal(b, &aux)
if err != nil {
return
}

strippedNote = string(b)

_, stripped := aux["Stripped"]
if stripped {
index := aux["Index"]
mfst := aux["Manifest"]

strippedNote = fmt.Sprintf(`Not all multiplatform-content is present and only the available single-platform image was pushed
%s -> %s`, red(index), green(mfst))
}
}
}

func red(text string) string {
return fmt.Sprintf("\033[31m%s\033[0m", text)
}

func green(text string) string {
return fmt.Sprintf("\033[32m%s\033[0m", text)
}

func bold(text string) string {
return fmt.Sprintf("\033[1m%s\033[0m", text)
}

func printNote(dockerCli command.Cli, format string, args ...any) {
if _, isTty := term.GetFdInfo(dockerCli.Err()); isTty {
_, _ = fmt.Fprint(dockerCli.Err(), "\x1b[1;37m\x1b[1;46m[ NOTE ]\x1b[0m\x1b[0m ")
} else {
_, _ = fmt.Fprint(dockerCli.Err(), "[ NOTE ] ")
}
_, _ = fmt.Fprintf(dockerCli.Err(), bold(format)+"\n", args...)
}
11 changes: 6 additions & 5 deletions docs/reference/commandline/image_push.md
Expand Up @@ -9,11 +9,12 @@ Upload an image to a registry

### Options

| Name | Type | Default | Description |
|:---------------------------------------------|:-------|:--------|:--------------------------------------------|
| [`-a`](#all-tags), [`--all-tags`](#all-tags) | | | Push all tags of an image to the repository |
| `--disable-content-trust` | `bool` | `true` | Skip image signing |
| `-q`, `--quiet` | | | Suppress verbose output |
| Name | Type | Default | Description |
|:---------------------------------------------|:---------|:--------|:--------------------------------------------------------------------------------------------------------------------------------------------|
| [`-a`](#all-tags), [`--all-tags`](#all-tags) | | | Push all tags of an image to the repository |
| `--disable-content-trust` | `bool` | `true` | Skip image signing |
| `--platform` | `string` | | Push a platform-specific manifest as a single-platform image to the registry.<br>'os[/arch[/variant]]': Explicit platform (eg. linux/amd64) |
| `-q`, `--quiet` | | | Suppress verbose output |


<!---MARKER_GEN_END-->
Expand Down
11 changes: 6 additions & 5 deletions docs/reference/commandline/push.md
Expand Up @@ -9,11 +9,12 @@ Upload an image to a registry

### Options

| Name | Type | Default | Description |
|:--------------------------|:-------|:--------|:--------------------------------------------|
| `-a`, `--all-tags` | | | Push all tags of an image to the repository |
| `--disable-content-trust` | `bool` | `true` | Skip image signing |
| `-q`, `--quiet` | | | Suppress verbose output |
| Name | Type | Default | Description |
|:--------------------------|:---------|:--------|:--------------------------------------------------------------------------------------------------------------------------------------------|
| `-a`, `--all-tags` | | | Push all tags of an image to the repository |
| `--disable-content-trust` | `bool` | `true` | Skip image signing |
| `--platform` | `string` | | Push a platform-specific manifest as a single-platform image to the registry.<br>'os[/arch[/variant]]': Explicit platform (eg. linux/amd64) |
| `-q`, `--quiet` | | | Suppress verbose output |


<!---MARKER_GEN_END-->
Expand Down
2 changes: 2 additions & 0 deletions vendor.mod
Expand Up @@ -6,6 +6,8 @@ module github.com/docker/cli

go 1.21.0

replace github.com/docker/docker => github.com/vvoland/moby v20.10.3-0.20240520111604-9e5950a4b1db+incompatible

require (
dario.cat/mergo v1.0.0
github.com/containerd/containerd v1.7.15
Expand Down
4 changes: 2 additions & 2 deletions vendor.sum
Expand Up @@ -57,8 +57,6 @@ github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v26.1.1-0.20240516211257-06e3a49d66fa+incompatible h1:Zp6B3afdBCdGNGM6dxdiThsrmUIJSoBFkFLonLhiO1k=
github.com/docker/docker v26.1.1-0.20240516211257-06e3a49d66fa+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.1 h1:j/eKUktUltBtMzKqmfLB0PAgqYyMHOp5vfsD1807oKo=
github.com/docker/docker-credential-helpers v0.8.1/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
Expand Down Expand Up @@ -271,6 +269,8 @@ github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a h1:tlJ
github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a/go.mod h1:Y94A6rPp2OwNfP/7vmf8O2xx2IykP8pPXQ1DLouGnEw=
github.com/tonistiigi/go-rosetta v0.0.0-20200727161949-f79598599c5d h1:wvQZpqy8p0D/FUia6ipKDhXrzPzBVJE4PZyPc5+5Ay0=
github.com/tonistiigi/go-rosetta v0.0.0-20200727161949-f79598599c5d/go.mod h1:xKQhd7snlzKFuUi1taTGWjpRE8iFTA06DeacYi3CVFQ=
github.com/vvoland/moby v20.10.3-0.20240520111604-9e5950a4b1db+incompatible h1:/BD/IWbPXYZNaeUXnPu0t2vJxCuQQLOjPjghTEVu+IY=
github.com/vvoland/moby v20.10.3-0.20240520111604-9e5950a4b1db+incompatible/go.mod h1:nLN96xVmxZq8CPEl0UgxMpO/G2e8MQtjhAdlRDUdBi0=
github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b h1:FsyNrX12e5BkplJq7wKOLk0+C6LZ+KGXvuEcKUYm5ss=
github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
Expand Down
15 changes: 14 additions & 1 deletion vendor/github.com/docker/docker/api/types/image/opts.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions vendor/github.com/docker/docker/client/image_push.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion vendor/modules.txt
Expand Up @@ -56,7 +56,7 @@ github.com/docker/distribution/registry/client/transport
github.com/docker/distribution/registry/storage/cache
github.com/docker/distribution/registry/storage/cache/memory
github.com/docker/distribution/uuid
# github.com/docker/docker v26.1.1-0.20240516211257-06e3a49d66fa+incompatible
# github.com/docker/docker v26.1.1-0.20240516211257-06e3a49d66fa+incompatible => github.com/vvoland/moby v20.10.3-0.20240520111604-9e5950a4b1db+incompatible
## explicit
github.com/docker/docker/api
github.com/docker/docker/api/types
Expand Down Expand Up @@ -539,3 +539,4 @@ gotest.tools/v3/skip
# tags.cncf.io/container-device-interface v0.6.2
## explicit; go 1.19
tags.cncf.io/container-device-interface/pkg/parser
# github.com/docker/docker => github.com/vvoland/moby v20.10.3-0.20240520111604-9e5950a4b1db+incompatible