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:
John Howard 2017-08-02 17:31:32 -07:00
parent 92afc3f474
commit d8b782560e
7 changed files with 35 additions and 15 deletions

View File

@ -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 {

View File

@ -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)

View File

@ -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)

View File

@ -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 {

View File

@ -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'") {

View File

@ -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

View File

@ -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)
}