Skip to content
Draft
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
1 change: 1 addition & 0 deletions cli/command/image/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func NewImageCommand(dockerCli command.Cli) *cobra.Command {
newRemoveCommand(dockerCli),
newInspectCommand(dockerCli),
NewPruneCommand(dockerCli),
NewConvertCommand(dockerCli),
)
return cmd
}
83 changes: 83 additions & 0 deletions cli/command/image/convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package image

import (
"context"
"fmt"

"github.com/containerd/platforms"
"github.com/distribution/reference"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
imagetypes "github.com/docker/docker/api/types/image"
"github.com/spf13/cobra"
)

type convertArgs struct {
Src string
Dst []string
Platforms []string
NoAttestations bool
OnlyAvailablePlatforms bool
}

func NewConvertCommand(dockerCli command.Cli) *cobra.Command {
var args convertArgs

cmd := &cobra.Command{
Use: "convert [OPTIONS]",
Short: "Convert multi-platform images",
Args: cli.ExactArgs(0),
RunE: func(cmd *cobra.Command, _ []string) error {
return runConvert(cmd.Context(), dockerCli, args)
},
Aliases: []string{"convert"},
Annotations: map[string]string{
"aliases": "docker image convert, docker convert",
},
}

flags := cmd.Flags()
flags.StringArrayVar(&args.Platforms, "platforms", nil, "Include only the specified platforms in the destination image")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is named "platform" rather than "platforms" in nerdctl and ctr

flags.BoolVar(&args.NoAttestations, "no-attestations", false, "Do not include image attestations")
flags.BoolVar(&args.OnlyAvailablePlatforms, "available", false, "Only include platforms locally available to the daemon")
flags.StringArrayVar(&args.Dst, "to", nil, "Target image references")
flags.StringVar(&args.Src, "from", "", "Source image reference")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nerdctl already has nerdctl image convert SRC DST with a different purpose (akin to ctr images convert) :
https://github.com/containerd/nerdctl/blob/main/docs/command-reference.md#nerd_face-nerdctl-image-convert

Would it be possible to adopt the same syntax as nerdctl image convert SRC DST, or change the command name to avoid conflict?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in general, the purpose is the same. Unfortunately I don't have any better idea for a name... Do you have any suggestions?

We deliberately chose the src and dst to be flag arguments instead of positional arguments as it gives more clarity, especially when multiple destinations might be specified.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should change our syntax (esp. since we settled on this one as the most user-friendly/better option) just for compatibility's sake.

Unless there's a more explicit/as clear name, I also don't think we should change the name to avoid "colliding" with nerdctls command, although I do understand the possible confusion if we have two commands with the same name/subcommand and different syntax.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We deliberately chose the src and dst to be flag arguments instead of positional arguments as it gives more clarity, especially when multiple destinations might be specified.

What about making it (docker|nerdctl) image convert SRC DST1 DST2 DST3, or (docker|nerdctl) image convert SRC DST1,DST2,DST3?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely a fan of positional for this but I haven't looked at the PR yet, just this thread.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @tianon - I think you had a strong opinion on it being a flag argument 😄

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took me a minute, but I remember now what my thoughts were. 😅

IMO, this command should match docker build -- ala, positional arguments for input, and --tag/-t for output(s).

(I think we could make the same argument for docker push -t foo -t bar -t baz image-to-push)

In the case of convert, we might have multiple inputs as well, right? For example, if we have a way to remove things from an index, wouldn't it make sense to also have a way to add things to one / create one? That could then be multiple positional arguments (now unambiguous if we avoid destination being positional).


return cmd
}

func runConvert(ctx context.Context, dockerCLI command.Cli, args convertArgs) error {
if len(args.Dst) == 0 {
return fmt.Errorf("No destination image specified")
}
if args.Src == "" {
return fmt.Errorf("No source image specified")
}

var dstRefs []reference.NamedTagged
for _, dst := range args.Dst {
dstRef, err := reference.ParseNormalizedNamed(dst)
if err != nil {
return fmt.Errorf("invalid destination image reference: %s: %w", dst, err)
}

dstRef = reference.TagNameOnly(dstRef)
dstRefTagged := dstRef.(reference.NamedTagged)
dstRefs = append(dstRefs, dstRefTagged)
}

opts := imagetypes.ConvertOptions{
NoAttestations: args.NoAttestations,
OnlyAvailablePlatforms: args.OnlyAvailablePlatforms,
}

for _, platform := range args.Platforms {
p, err := platforms.Parse(platform)
if err != nil {
return err
}
opts.Platforms = append(opts.Platforms, p)
}

return dockerCLI.Client().ImageConvert(ctx, args.Src, dstRefs, opts)
}
2 changes: 2 additions & 0 deletions vendor.mod
Original file line number Diff line number Diff line change
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.20240604094150-006d93e1b557+incompatible

require (
dario.cat/mergo v1.0.0
github.com/containerd/platforms v0.2.0
Expand Down
4 changes: 2 additions & 2 deletions vendor.sum
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,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.20240603220541-cd3804655a25+incompatible h1:mzBHld522snTZ7BoMQiw0RXTdraVMr380QGZzGsPRqI=
github.com/docker/docker v26.1.1-0.20240603220541-cd3804655a25+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
Expand Down Expand Up @@ -273,6 +271,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.20240604094150-006d93e1b557+incompatible h1:uHYnyXiVobDAK6nBmqtOv5/uKMekwKkk9YkLh0m+rbs=
github.com/vvoland/moby v20.10.3-0.20240604094150-006d93e1b557+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
92 changes: 92 additions & 0 deletions vendor/github.com/containerd/containerd/errdefs/errors.go

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

147 changes: 147 additions & 0 deletions vendor/github.com/containerd/containerd/errdefs/grpc.go

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

Loading