mirror of https://github.com/docker/cli.git
LCOW: Add `--platform=` CLI flag to pull/create/run/build
Signed-off-by: John Howard <jhoward@microsoft.com> This is the CLI updates for the document discussed in https://github.com/moby/moby/issues/34617 to support Linux Containers on Windows. It adds --platform= as CLI flags to the four commands listed above. Import still to be completed (needs daemon changes).
This commit is contained in:
parent
92afc3f474
commit
d8b782560e
|
@ -21,7 +21,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type createOptions struct {
|
type createOptions struct {
|
||||||
name string
|
name string
|
||||||
|
platform string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCreateCommand creates a new cobra.Command for `docker create`
|
// NewCreateCommand creates a new cobra.Command for `docker create`
|
||||||
|
@ -51,6 +52,7 @@ func NewCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
// with hostname
|
// with hostname
|
||||||
flags.Bool("help", false, "Print usage")
|
flags.Bool("help", false, "Print usage")
|
||||||
|
|
||||||
|
command.AddPlatformFlag(flags, &opts.platform)
|
||||||
command.AddTrustVerificationFlags(flags)
|
command.AddTrustVerificationFlags(flags)
|
||||||
copts = addFlags(flags)
|
copts = addFlags(flags)
|
||||||
return cmd
|
return cmd
|
||||||
|
@ -62,7 +64,7 @@ func runCreate(dockerCli command.Cli, flags *pflag.FlagSet, opts *createOptions,
|
||||||
reportError(dockerCli.Err(), "create", err.Error(), true)
|
reportError(dockerCli.Err(), "create", err.Error(), true)
|
||||||
return cli.StatusError{StatusCode: 125}
|
return cli.StatusError{StatusCode: 125}
|
||||||
}
|
}
|
||||||
response, err := createContainer(context.Background(), dockerCli, containerConfig, opts.name)
|
response, err := createContainer(context.Background(), dockerCli, containerConfig, opts.name, opts.platform)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -70,7 +72,7 @@ func runCreate(dockerCli command.Cli, flags *pflag.FlagSet, opts *createOptions,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func pullImage(ctx context.Context, dockerCli command.Cli, image string, out io.Writer) error {
|
func pullImage(ctx context.Context, dockerCli command.Cli, image string, platform string, out io.Writer) error {
|
||||||
ref, err := reference.ParseNormalizedNamed(image)
|
ref, err := reference.ParseNormalizedNamed(image)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -90,6 +92,7 @@ func pullImage(ctx context.Context, dockerCli command.Cli, image string, out io.
|
||||||
|
|
||||||
options := types.ImageCreateOptions{
|
options := types.ImageCreateOptions{
|
||||||
RegistryAuth: encodedAuth,
|
RegistryAuth: encodedAuth,
|
||||||
|
Platform: platform,
|
||||||
}
|
}
|
||||||
|
|
||||||
responseBody, err := dockerCli.Client().ImageCreate(ctx, image, options)
|
responseBody, err := dockerCli.Client().ImageCreate(ctx, image, options)
|
||||||
|
@ -155,7 +158,7 @@ func newCIDFile(path string) (*cidFile, error) {
|
||||||
return &cidFile{path: path, file: f}, nil
|
return &cidFile{path: path, file: f}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createContainer(ctx context.Context, dockerCli command.Cli, containerConfig *containerConfig, name string) (*container.ContainerCreateCreatedBody, error) {
|
func createContainer(ctx context.Context, dockerCli command.Cli, containerConfig *containerConfig, name string, platform string) (*container.ContainerCreateCreatedBody, error) {
|
||||||
config := containerConfig.Config
|
config := containerConfig.Config
|
||||||
hostConfig := containerConfig.HostConfig
|
hostConfig := containerConfig.HostConfig
|
||||||
networkingConfig := containerConfig.NetworkingConfig
|
networkingConfig := containerConfig.NetworkingConfig
|
||||||
|
@ -198,7 +201,7 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerConfig
|
||||||
fmt.Fprintf(stderr, "Unable to find image '%s' locally\n", reference.FamiliarString(namedRef))
|
fmt.Fprintf(stderr, "Unable to find image '%s' locally\n", reference.FamiliarString(namedRef))
|
||||||
|
|
||||||
// we don't want to write to stdout anything apart from container.ID
|
// we don't want to write to stdout anything apart from container.ID
|
||||||
if err := pullImage(ctx, dockerCli, config.Image, stderr); err != nil {
|
if err := pullImage(ctx, dockerCli, config.Image, platform, stderr); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if taggedRef, ok := namedRef.(reference.NamedTagged); ok && trustedRef != nil {
|
if taggedRef, ok := namedRef.(reference.NamedTagged); ok && trustedRef != nil {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -106,7 +107,7 @@ func TestCreateContainerPullsImageIfMissing(t *testing.T) {
|
||||||
},
|
},
|
||||||
HostConfig: &container.HostConfig{},
|
HostConfig: &container.HostConfig{},
|
||||||
}
|
}
|
||||||
body, err := createContainer(context.Background(), cli, config, "name")
|
body, err := createContainer(context.Background(), cli, config, "name", runtime.GOOS)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
expected := container.ContainerCreateCreatedBody{ID: containerID}
|
expected := container.ContainerCreateCreatedBody{ID: containerID}
|
||||||
assert.Equal(t, expected, *body)
|
assert.Equal(t, expected, *body)
|
||||||
|
|
|
@ -29,6 +29,7 @@ type runOptions struct {
|
||||||
sigProxy bool
|
sigProxy bool
|
||||||
name string
|
name string
|
||||||
detachKeys string
|
detachKeys string
|
||||||
|
platform string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRunCommand create a new `docker run` command
|
// NewRunCommand create a new `docker run` command
|
||||||
|
@ -62,6 +63,7 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
// with hostname
|
// with hostname
|
||||||
flags.Bool("help", false, "Print usage")
|
flags.Bool("help", false, "Print usage")
|
||||||
|
|
||||||
|
command.AddPlatformFlag(flags, &opts.platform)
|
||||||
command.AddTrustVerificationFlags(flags)
|
command.AddTrustVerificationFlags(flags)
|
||||||
copts = addFlags(flags)
|
copts = addFlags(flags)
|
||||||
return cmd
|
return cmd
|
||||||
|
@ -160,7 +162,7 @@ func runContainer(dockerCli command.Cli, opts *runOptions, copts *containerOptio
|
||||||
|
|
||||||
ctx, cancelFun := context.WithCancel(context.Background())
|
ctx, cancelFun := context.WithCancel(context.Background())
|
||||||
|
|
||||||
createResponse, err := createContainer(ctx, dockerCli, containerConfig, opts.name)
|
createResponse, err := createContainer(ctx, dockerCli, containerConfig, opts.name, opts.platform)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reportError(stderr, cmdPath, err.Error(), true)
|
reportError(stderr, cmdPath, err.Error(), true)
|
||||||
return runStartContainerErr(err)
|
return runStartContainerErr(err)
|
||||||
|
|
|
@ -64,6 +64,7 @@ type buildOptions struct {
|
||||||
target string
|
target string
|
||||||
imageIDFile string
|
imageIDFile string
|
||||||
stream bool
|
stream bool
|
||||||
|
platform string
|
||||||
}
|
}
|
||||||
|
|
||||||
// dockerfileFromStdin returns true when the user specified that the Dockerfile
|
// dockerfileFromStdin returns true when the user specified that the Dockerfile
|
||||||
|
@ -135,6 +136,7 @@ func NewBuildCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
flags.StringVar(&options.imageIDFile, "iidfile", "", "Write the image ID to the file")
|
flags.StringVar(&options.imageIDFile, "iidfile", "", "Write the image ID to the file")
|
||||||
|
|
||||||
command.AddTrustVerificationFlags(flags)
|
command.AddTrustVerificationFlags(flags)
|
||||||
|
command.AddPlatformFlag(flags, &options.platform)
|
||||||
|
|
||||||
flags.BoolVar(&options.squash, "squash", false, "Squash newly built layers into a single new layer")
|
flags.BoolVar(&options.squash, "squash", false, "Squash newly built layers into a single new layer")
|
||||||
flags.SetAnnotation("squash", "experimental", nil)
|
flags.SetAnnotation("squash", "experimental", nil)
|
||||||
|
@ -374,6 +376,7 @@ func runBuild(dockerCli command.Cli, options buildOptions) error {
|
||||||
ExtraHosts: options.extraHosts.GetAll(),
|
ExtraHosts: options.extraHosts.GetAll(),
|
||||||
Target: options.target,
|
Target: options.target,
|
||||||
RemoteContext: remote,
|
RemoteContext: remote,
|
||||||
|
Platform: options.platform,
|
||||||
}
|
}
|
||||||
|
|
||||||
if s != nil {
|
if s != nil {
|
||||||
|
|
|
@ -14,8 +14,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type pullOptions struct {
|
type pullOptions struct {
|
||||||
remote string
|
remote string
|
||||||
all bool
|
all bool
|
||||||
|
platform string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPullCommand creates a new `docker pull` command
|
// NewPullCommand creates a new `docker pull` command
|
||||||
|
@ -35,6 +36,8 @@ func NewPullCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
flags := cmd.Flags()
|
flags := cmd.Flags()
|
||||||
|
|
||||||
flags.BoolVarP(&opts.all, "all-tags", "a", false, "Download all tagged images in the repository")
|
flags.BoolVarP(&opts.all, "all-tags", "a", false, "Download all tagged images in the repository")
|
||||||
|
|
||||||
|
command.AddPlatformFlag(flags, &opts.platform)
|
||||||
command.AddTrustVerificationFlags(flags)
|
command.AddTrustVerificationFlags(flags)
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
|
@ -63,9 +66,9 @@ func runPull(cli command.Cli, opts pullOptions) error {
|
||||||
// Check if reference has a digest
|
// Check if reference has a digest
|
||||||
_, isCanonical := distributionRef.(reference.Canonical)
|
_, isCanonical := distributionRef.(reference.Canonical)
|
||||||
if command.IsTrusted() && !isCanonical {
|
if command.IsTrusted() && !isCanonical {
|
||||||
err = trustedPull(ctx, cli, imgRefAndAuth)
|
err = trustedPull(ctx, cli, imgRefAndAuth, opts.platform)
|
||||||
} else {
|
} else {
|
||||||
err = imagePullPrivileged(ctx, cli, imgRefAndAuth, opts.all)
|
err = imagePullPrivileged(ctx, cli, imgRefAndAuth, opts.all, opts.platform)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "when fetching 'plugin'") {
|
if strings.Contains(err.Error(), "when fetching 'plugin'") {
|
||||||
|
|
|
@ -180,7 +180,7 @@ func imagePushPrivileged(ctx context.Context, cli command.Cli, authConfig types.
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) error {
|
func trustedPull(ctx context.Context, cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth, platform string) error {
|
||||||
refs, err := getTrustedPullTargets(cli, imgRefAndAuth)
|
refs, err := getTrustedPullTargets(cli, imgRefAndAuth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -202,7 +202,7 @@ func trustedPull(ctx context.Context, cli command.Cli, imgRefAndAuth trust.Image
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := imagePullPrivileged(ctx, cli, updatedImgRefAndAuth, false); err != nil {
|
if err := imagePullPrivileged(ctx, cli, updatedImgRefAndAuth, false, platform); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,7 +268,7 @@ func getTrustedPullTargets(cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth)
|
||||||
}
|
}
|
||||||
|
|
||||||
// imagePullPrivileged pulls the image and displays it to the output
|
// imagePullPrivileged pulls the image and displays it to the output
|
||||||
func imagePullPrivileged(ctx context.Context, cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth, all bool) error {
|
func imagePullPrivileged(ctx context.Context, cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth, all bool, platform string) error {
|
||||||
ref := reference.FamiliarString(imgRefAndAuth.Reference())
|
ref := reference.FamiliarString(imgRefAndAuth.Reference())
|
||||||
|
|
||||||
encodedAuth, err := command.EncodeAuthToBase64(*imgRefAndAuth.AuthConfig())
|
encodedAuth, err := command.EncodeAuthToBase64(*imgRefAndAuth.AuthConfig())
|
||||||
|
@ -280,8 +280,8 @@ func imagePullPrivileged(ctx context.Context, cli command.Cli, imgRefAndAuth tru
|
||||||
RegistryAuth: encodedAuth,
|
RegistryAuth: encodedAuth,
|
||||||
PrivilegeFunc: requestPrivilege,
|
PrivilegeFunc: requestPrivilege,
|
||||||
All: all,
|
All: all,
|
||||||
|
Platform: platform,
|
||||||
}
|
}
|
||||||
|
|
||||||
responseBody, err := cli.Client().ImagePull(ctx, ref, options)
|
responseBody, err := cli.Client().ImagePull(ctx, ref, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
"github.com/docker/docker/pkg/system"
|
"github.com/docker/docker/pkg/system"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CopyToFile writes the content of the reader to the specified file
|
// CopyToFile writes the content of the reader to the specified file
|
||||||
|
@ -117,3 +118,10 @@ func PruneFilters(dockerCli Cli, pruneFilters filters.Args) filters.Args {
|
||||||
|
|
||||||
return pruneFilters
|
return pruneFilters
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddPlatformFlag adds `platform` to a set of flags for API version 1.32 and later.
|
||||||
|
func AddPlatformFlag(flags *pflag.FlagSet, target *string) {
|
||||||
|
flags.StringVar(target, "platform", os.Getenv("DOCKER_DEFAULT_PLATFORM"), "Set platform if server is multi-platform capable")
|
||||||
|
flags.SetAnnotation("platform", "version", []string{"1.32"})
|
||||||
|
flags.SetAnnotation("platform", "experimental", nil)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue