Set platform on container create API.

Previously we only set the platform when performing a pull, which is
only initiated if pull always is set, or if the image reference does not
exist in the daemon.

The daemon now supports specifying which platform you wanted on
container create so it can validate the image reference is the platform
you thought you were getting.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
This commit is contained in:
Brian Goff 2020-05-27 11:32:22 -07:00
parent 8c986d3ff2
commit ccd9d633bb
4 changed files with 30 additions and 4 deletions

View File

@ -8,6 +8,7 @@ import (
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/network"
"github.com/docker/docker/client" "github.com/docker/docker/client"
specs "github.com/opencontainers/image-spec/specs-go/v1"
) )
type fakeClient struct { type fakeClient struct {
@ -18,6 +19,7 @@ type fakeClient struct {
createContainerFunc func(config *container.Config, createContainerFunc func(config *container.Config,
hostConfig *container.HostConfig, hostConfig *container.HostConfig,
networkingConfig *network.NetworkingConfig, networkingConfig *network.NetworkingConfig,
platform *specs.Platform,
containerName string) (container.ContainerCreateCreatedBody, error) containerName string) (container.ContainerCreateCreatedBody, error)
containerStartFunc func(container string, options types.ContainerStartOptions) error containerStartFunc func(container string, options types.ContainerStartOptions) error
imageCreateFunc func(parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) imageCreateFunc func(parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error)
@ -69,10 +71,11 @@ func (f *fakeClient) ContainerCreate(
config *container.Config, config *container.Config,
hostConfig *container.HostConfig, hostConfig *container.HostConfig,
networkingConfig *network.NetworkingConfig, networkingConfig *network.NetworkingConfig,
platform *specs.Platform,
containerName string, containerName string,
) (container.ContainerCreateCreatedBody, error) { ) (container.ContainerCreateCreatedBody, error) {
if f.createContainerFunc != nil { if f.createContainerFunc != nil {
return f.createContainerFunc(config, hostConfig, networkingConfig, containerName) return f.createContainerFunc(config, hostConfig, networkingConfig, platform, containerName)
} }
return container.ContainerCreateCreatedBody{}, nil return container.ContainerCreateCreatedBody{}, nil
} }

View File

@ -7,6 +7,7 @@ import (
"os" "os"
"regexp" "regexp"
"github.com/containerd/containerd/platforms"
"github.com/docker/cli/cli" "github.com/docker/cli/cli"
"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"
@ -14,9 +15,11 @@ import (
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/versions"
apiclient "github.com/docker/docker/client" apiclient "github.com/docker/docker/client"
"github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/registry" "github.com/docker/docker/registry"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
@ -233,13 +236,26 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerConfig
return nil return nil
} }
var platform *specs.Platform
// Engine API version 1.41 first introduced the option to specify platform on
// create. It will produce an error if you try to set a platform on older API
// versions, so check the API version here to maintain backwards
// compatibility for CLI users.
if opts.platform != "" && versions.GreaterThanOrEqualTo(dockerCli.Client().ClientVersion(), "1.41") {
p, err := platforms.Parse(opts.platform)
if err != nil {
return nil, errors.Wrap(err, "error parsing specified platform")
}
platform = &p
}
if opts.pull == PullImageAlways { if opts.pull == PullImageAlways {
if err := pullAndTagImage(); err != nil { if err := pullAndTagImage(); err != nil {
return nil, err return nil, err
} }
} }
response, err := dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, opts.name) response, err := dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, platform, opts.name)
if err != nil { if err != nil {
// Pull image if it does not exist locally and we have the PullImageMissing option. Default behavior. // Pull image if it does not exist locally and we have the PullImageMissing option. Default behavior.
if apiclient.IsErrNotFound(err) && namedRef != nil && opts.pull == PullImageMissing { if apiclient.IsErrNotFound(err) && namedRef != nil && opts.pull == PullImageMissing {
@ -250,7 +266,7 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerConfig
} }
var retryErr error var retryErr error
response, retryErr = dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, opts.name) response, retryErr = dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, platform, opts.name)
if retryErr != nil { if retryErr != nil {
return nil, retryErr return nil, retryErr
} }

View File

@ -18,6 +18,7 @@ import (
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/network"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"gotest.tools/v3/assert" "gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp" is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/fs" "gotest.tools/v3/fs"
@ -116,6 +117,7 @@ func TestCreateContainerImagePullPolicy(t *testing.T) {
config *container.Config, config *container.Config,
hostConfig *container.HostConfig, hostConfig *container.HostConfig,
networkingConfig *network.NetworkingConfig, networkingConfig *network.NetworkingConfig,
platform *specs.Platform,
containerName string, containerName string,
) (container.ContainerCreateCreatedBody, error) { ) (container.ContainerCreateCreatedBody, error) {
defer func() { c.ResponseCounter++ }() defer func() { c.ResponseCounter++ }()
@ -184,6 +186,7 @@ func TestNewCreateCommandWithContentTrustErrors(t *testing.T) {
createContainerFunc: func(config *container.Config, createContainerFunc: func(config *container.Config,
hostConfig *container.HostConfig, hostConfig *container.HostConfig,
networkingConfig *network.NetworkingConfig, networkingConfig *network.NetworkingConfig,
platform *specs.Platform,
containerName string, containerName string,
) (container.ContainerCreateCreatedBody, error) { ) (container.ContainerCreateCreatedBody, error) {
return container.ContainerCreateCreatedBody{}, fmt.Errorf("shouldn't try to pull image") return container.ContainerCreateCreatedBody{}, fmt.Errorf("shouldn't try to pull image")
@ -244,6 +247,7 @@ func TestNewCreateCommandWithWarnings(t *testing.T) {
createContainerFunc: func(config *container.Config, createContainerFunc: func(config *container.Config,
hostConfig *container.HostConfig, hostConfig *container.HostConfig,
networkingConfig *network.NetworkingConfig, networkingConfig *network.NetworkingConfig,
platform *specs.Platform,
containerName string, containerName string,
) (container.ContainerCreateCreatedBody, error) { ) (container.ContainerCreateCreatedBody, error) {
return container.ContainerCreateCreatedBody{}, nil return container.ContainerCreateCreatedBody{}, nil
@ -280,6 +284,7 @@ func TestCreateContainerWithProxyConfig(t *testing.T) {
createContainerFunc: func(config *container.Config, createContainerFunc: func(config *container.Config,
hostConfig *container.HostConfig, hostConfig *container.HostConfig,
networkingConfig *network.NetworkingConfig, networkingConfig *network.NetworkingConfig,
platform *specs.Platform,
containerName string, containerName string,
) (container.ContainerCreateCreatedBody, error) { ) (container.ContainerCreateCreatedBody, error) {
sort.Strings(config.Env) sort.Strings(config.Env)

View File

@ -9,13 +9,14 @@ import (
"github.com/docker/cli/internal/test/notary" "github.com/docker/cli/internal/test/notary"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/network"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"gotest.tools/v3/assert" "gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp" is "gotest.tools/v3/assert/cmp"
) )
func TestRunLabel(t *testing.T) { func TestRunLabel(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{ cli := test.NewFakeCli(&fakeClient{
createContainerFunc: func(_ *container.Config, _ *container.HostConfig, _ *network.NetworkingConfig, _ string) (container.ContainerCreateCreatedBody, error) { createContainerFunc: func(_ *container.Config, _ *container.HostConfig, _ *network.NetworkingConfig, _ *specs.Platform, _ string) (container.ContainerCreateCreatedBody, error) {
return container.ContainerCreateCreatedBody{ return container.ContainerCreateCreatedBody{
ID: "id", ID: "id",
}, nil }, nil
@ -58,6 +59,7 @@ func TestRunCommandWithContentTrustErrors(t *testing.T) {
createContainerFunc: func(config *container.Config, createContainerFunc: func(config *container.Config,
hostConfig *container.HostConfig, hostConfig *container.HostConfig,
networkingConfig *network.NetworkingConfig, networkingConfig *network.NetworkingConfig,
platform *specs.Platform,
containerName string, containerName string,
) (container.ContainerCreateCreatedBody, error) { ) (container.ContainerCreateCreatedBody, error) {
return container.ContainerCreateCreatedBody{}, fmt.Errorf("shouldn't try to pull image") return container.ContainerCreateCreatedBody{}, fmt.Errorf("shouldn't try to pull image")