Merge pull request #40725 from cpuguy83/check_img_platform

Accept platform spec on container create
This commit is contained in:
Tibor Vass 2020-05-21 11:33:27 -07:00 committed by GitHub
commit 5c10ea6ae8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 188 additions and 62 deletions

View File

@ -9,6 +9,7 @@ import (
"strconv" "strconv"
"syscall" "syscall"
"github.com/containerd/containerd/platforms"
"github.com/docker/docker/api/server/httputils" "github.com/docker/docker/api/server/httputils"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/backend" "github.com/docker/docker/api/types/backend"
@ -19,6 +20,7 @@ import (
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
"github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/signal" "github.com/docker/docker/pkg/signal"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"golang.org/x/net/websocket" "golang.org/x/net/websocket"
@ -502,6 +504,28 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo
} }
} }
var platform *specs.Platform
if versions.GreaterThanOrEqualTo(version, "1.41") {
if v := r.Form.Get("platform"); v != "" {
p, err := platforms.Parse(v)
if err != nil {
return errdefs.InvalidParameter(err)
}
platform = &p
}
defaultPlatform := platforms.DefaultSpec()
if platform == nil {
platform = &defaultPlatform
}
if platform.OS == "" {
platform.OS = defaultPlatform.OS
}
if platform.Architecture == "" {
platform.Architecture = defaultPlatform.Architecture
platform.Variant = defaultPlatform.Variant
}
}
if hostConfig != nil && hostConfig.PidsLimit != nil && *hostConfig.PidsLimit <= 0 { if hostConfig != nil && hostConfig.PidsLimit != nil && *hostConfig.PidsLimit <= 0 {
// Don't set a limit if either no limit was specified, or "unlimited" was // Don't set a limit if either no limit was specified, or "unlimited" was
// explicitly set. // explicitly set.
@ -516,6 +540,7 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo
HostConfig: hostConfig, HostConfig: hostConfig,
NetworkingConfig: networkingConfig, NetworkingConfig: networkingConfig,
AdjustCPUShares: adjustCPUShares, AdjustCPUShares: adjustCPUShares,
Platform: platform,
}) })
if err != nil { if err != nil {
return err return err

View File

@ -3,6 +3,7 @@ package types // import "github.com/docker/docker/api/types"
import ( 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"
specs "github.com/opencontainers/image-spec/specs-go/v1"
) )
// configs holds structs used for internal communication between the // configs holds structs used for internal communication between the
@ -15,6 +16,7 @@ type ContainerCreateConfig struct {
Config *container.Config Config *container.Config
HostConfig *container.HostConfig HostConfig *container.HostConfig
NetworkingConfig *network.NetworkingConfig NetworkingConfig *network.NetworkingConfig
Platform *specs.Platform
AdjustCPUShares bool AdjustCPUShares bool
} }

View File

@ -5,20 +5,23 @@ import (
"encoding/json" "encoding/json"
"net/url" "net/url"
"github.com/containerd/containerd/platforms"
"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/api/types/versions" "github.com/docker/docker/api/types/versions"
specs "github.com/opencontainers/image-spec/specs-go/v1"
) )
type configWrapper struct { type configWrapper struct {
*container.Config *container.Config
HostConfig *container.HostConfig HostConfig *container.HostConfig
NetworkingConfig *network.NetworkingConfig NetworkingConfig *network.NetworkingConfig
Platform *specs.Platform
} }
// ContainerCreate creates a new container based in the given configuration. // ContainerCreate creates a new container based in the given configuration.
// It can be associated with a name, but it's not mandatory. // It can be associated with a name, but it's not mandatory.
func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (container.ContainerCreateCreatedBody, error) { func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *specs.Platform, containerName string) (container.ContainerCreateCreatedBody, error) {
var response container.ContainerCreateCreatedBody var response container.ContainerCreateCreatedBody
if err := cli.NewVersionError("1.25", "stop timeout"); config != nil && config.StopTimeout != nil && err != nil { if err := cli.NewVersionError("1.25", "stop timeout"); config != nil && config.StopTimeout != nil && err != nil {
@ -30,7 +33,15 @@ func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config
hostConfig.AutoRemove = false hostConfig.AutoRemove = false
} }
if err := cli.NewVersionError("1.41", "specify container image platform"); platform != nil && err != nil {
return response, err
}
query := url.Values{} query := url.Values{}
if platform != nil {
query.Set("platform", platforms.Format(*platform))
}
if containerName != "" { if containerName != "" {
query.Set("name", containerName) query.Set("name", containerName)
} }

View File

@ -18,7 +18,7 @@ func TestContainerCreateError(t *testing.T) {
client := &Client{ client := &Client{
client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
} }
_, err := client.ContainerCreate(context.Background(), nil, nil, nil, "nothing") _, err := client.ContainerCreate(context.Background(), nil, nil, nil, nil, "nothing")
if !errdefs.IsSystem(err) { if !errdefs.IsSystem(err) {
t.Fatalf("expected a Server Error while testing StatusInternalServerError, got %T", err) t.Fatalf("expected a Server Error while testing StatusInternalServerError, got %T", err)
} }
@ -27,7 +27,7 @@ func TestContainerCreateError(t *testing.T) {
client = &Client{ client = &Client{
client: newMockClient(errorMock(http.StatusNotFound, "Server error")), client: newMockClient(errorMock(http.StatusNotFound, "Server error")),
} }
_, err = client.ContainerCreate(context.Background(), nil, nil, nil, "nothing") _, err = client.ContainerCreate(context.Background(), nil, nil, nil, nil, "nothing")
if err == nil || !IsErrNotFound(err) { if err == nil || !IsErrNotFound(err) {
t.Fatalf("expected a Server Error while testing StatusNotFound, got %T", err) t.Fatalf("expected a Server Error while testing StatusNotFound, got %T", err)
} }
@ -37,7 +37,7 @@ func TestContainerCreateImageNotFound(t *testing.T) {
client := &Client{ client := &Client{
client: newMockClient(errorMock(http.StatusNotFound, "No such image")), client: newMockClient(errorMock(http.StatusNotFound, "No such image")),
} }
_, err := client.ContainerCreate(context.Background(), &container.Config{Image: "unknown_image"}, nil, nil, "unknown") _, err := client.ContainerCreate(context.Background(), &container.Config{Image: "unknown_image"}, nil, nil, nil, "unknown")
if err == nil || !IsErrNotFound(err) { if err == nil || !IsErrNotFound(err) {
t.Fatalf("expected an imageNotFound error, got %v, %T", err, err) t.Fatalf("expected an imageNotFound error, got %v, %T", err, err)
} }
@ -67,7 +67,7 @@ func TestContainerCreateWithName(t *testing.T) {
}), }),
} }
r, err := client.ContainerCreate(context.Background(), nil, nil, nil, "container_name") r, err := client.ContainerCreate(context.Background(), nil, nil, nil, nil, "container_name")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -106,14 +106,14 @@ func TestContainerCreateAutoRemove(t *testing.T) {
client: newMockClient(autoRemoveValidator(false)), client: newMockClient(autoRemoveValidator(false)),
version: "1.24", version: "1.24",
} }
if _, err := client.ContainerCreate(context.Background(), nil, &container.HostConfig{AutoRemove: true}, nil, ""); err != nil { if _, err := client.ContainerCreate(context.Background(), nil, &container.HostConfig{AutoRemove: true}, nil, nil, ""); err != nil {
t.Fatal(err) t.Fatal(err)
} }
client = &Client{ client = &Client{
client: newMockClient(autoRemoveValidator(true)), client: newMockClient(autoRemoveValidator(true)),
version: "1.25", version: "1.25",
} }
if _, err := client.ContainerCreate(context.Background(), nil, &container.HostConfig{AutoRemove: true}, nil, ""); err != nil { if _, err := client.ContainerCreate(context.Background(), nil, &container.HostConfig{AutoRemove: true}, nil, nil, ""); err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }

View File

@ -16,6 +16,7 @@ import (
"github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/swarm"
volumetypes "github.com/docker/docker/api/types/volume" volumetypes "github.com/docker/docker/api/types/volume"
specs "github.com/opencontainers/image-spec/specs-go/v1"
) )
// CommonAPIClient is the common methods between stable and experimental versions of APIClient. // CommonAPIClient is the common methods between stable and experimental versions of APIClient.
@ -47,7 +48,7 @@ type CommonAPIClient interface {
type ContainerAPIClient interface { type ContainerAPIClient interface {
ContainerAttach(ctx context.Context, container string, options types.ContainerAttachOptions) (types.HijackedResponse, error) ContainerAttach(ctx context.Context, container string, options types.ContainerAttachOptions) (types.HijackedResponse, error)
ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.IDResponse, error) ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.IDResponse, error)
ContainerCreate(ctx context.Context, config *containertypes.Config, hostConfig *containertypes.HostConfig, networkingConfig *networktypes.NetworkingConfig, containerName string) (containertypes.ContainerCreateCreatedBody, error) ContainerCreate(ctx context.Context, config *containertypes.Config, hostConfig *containertypes.HostConfig, networkingConfig *networktypes.NetworkingConfig, platform *specs.Platform, containerName string) (containertypes.ContainerCreateCreatedBody, error)
ContainerDiff(ctx context.Context, container string) ([]containertypes.ContainerChangeResponseItem, error) ContainerDiff(ctx context.Context, container string) ([]containertypes.ContainerChangeResponseItem, error)
ContainerExecAttach(ctx context.Context, execID string, config types.ExecStartCheck) (types.HijackedResponse, error) ContainerExecAttach(ctx context.Context, execID string, config types.ExecStartCheck) (types.HijackedResponse, error)
ContainerExecCreate(ctx context.Context, container string, config types.ExecConfig) (types.IDResponse, error) ContainerExecCreate(ctx context.Context, container string, config types.ExecConfig) (types.IDResponse, error)

View File

@ -60,7 +60,7 @@ func (daemon *Daemon) containerCreate(opts createOpts) (containertypes.Container
os := runtime.GOOS os := runtime.GOOS
if opts.params.Config.Image != "" { if opts.params.Config.Image != "" {
img, err := daemon.imageService.GetImage(opts.params.Config.Image) img, err := daemon.imageService.GetImage(opts.params.Config.Image, opts.params.Platform)
if err == nil { if err == nil {
os = img.OS os = img.OS
} }
@ -114,7 +114,7 @@ func (daemon *Daemon) create(opts createOpts) (retC *container.Container, retErr
os := runtime.GOOS os := runtime.GOOS
if opts.params.Config.Image != "" { if opts.params.Config.Image != "" {
img, err = daemon.imageService.GetImage(opts.params.Config.Image) img, err = daemon.imageService.GetImage(opts.params.Config.Image, opts.params.Platform)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -15,7 +15,7 @@ func (i *ImageService) MakeImageCache(sourceRefs []string) builder.ImageCache {
cache := cache.New(i.imageStore) cache := cache.New(i.imageStore)
for _, ref := range sourceRefs { for _, ref := range sourceRefs {
img, err := i.GetImage(ref) img, err := i.GetImage(ref, nil)
if err != nil { if err != nil {
logrus.Warnf("Could not look up %s for cache resolution, skipping: %+v", ref, err) logrus.Warnf("Could not look up %s for cache resolution, skipping: %+v", ref, err)
continue continue

View File

@ -3,9 +3,12 @@ package images // import "github.com/docker/docker/daemon/images"
import ( import (
"fmt" "fmt"
"github.com/pkg/errors"
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
"github.com/docker/docker/image" "github.com/docker/docker/image"
specs "github.com/opencontainers/image-spec/specs-go/v1"
) )
// ErrImageDoesNotExist is error returned when no image can be found for a reference. // ErrImageDoesNotExist is error returned when no image can be found for a reference.
@ -25,7 +28,39 @@ func (e ErrImageDoesNotExist) Error() string {
func (e ErrImageDoesNotExist) NotFound() {} func (e ErrImageDoesNotExist) NotFound() {}
// GetImage returns an image corresponding to the image referred to by refOrID. // GetImage returns an image corresponding to the image referred to by refOrID.
func (i *ImageService) GetImage(refOrID string) (*image.Image, error) { func (i *ImageService) GetImage(refOrID string, platform *specs.Platform) (retImg *image.Image, retErr error) {
defer func() {
if retErr != nil || retImg == nil || platform == nil {
return
}
// This allows us to tell clients that we don't have the image they asked for
// Where this gets hairy is the image store does not currently support multi-arch images, e.g.:
// An image `foo` may have a multi-arch manifest, but the image store only fetches the image for a specific platform
// The image store does not store the manifest list and image tags are assigned to architecture specific images.
// So we can have a `foo` image that is amd64 but the user requested armv7. If the user looks at the list of images.
// This may be confusing.
// The alternative to this is to return a errdefs.Conflict error with a helpful message, but clients will not be
// able to automatically tell what causes the conflict.
if retImg.OS != platform.OS {
retErr = errdefs.NotFound(errors.Errorf("image with reference %s was found but does not match the specified OS platform: wanted: %s, actual: %s", refOrID, platform.OS, retImg.OS))
retImg = nil
return
}
if retImg.Architecture != platform.Architecture {
retErr = errdefs.NotFound(errors.Errorf("image with reference %s was found but does not match the specified platform cpu architecture: wanted: %s, actual: %s", refOrID, platform.Architecture, retImg.Architecture))
retImg = nil
return
}
// Only validate variant if retImg has a variant set.
// The image variant may not be set since it's a newer field.
if platform.Variant != "" && retImg.Variant != "" && retImg.Variant != platform.Variant {
retErr = errdefs.NotFound(errors.Errorf("image with reference %s was found but does not match the specified platform cpu architecture variant: wanted: %s, actual: %s", refOrID, platform.Variant, retImg.Variant))
retImg = nil
return
}
}()
ref, err := reference.ParseAnyReference(refOrID) ref, err := reference.ParseAnyReference(refOrID)
if err != nil { if err != nil {
return nil, errdefs.InvalidParameter(err) return nil, errdefs.InvalidParameter(err)

View File

@ -161,7 +161,7 @@ func (i *ImageService) pullForBuilder(ctx context.Context, name string, authConf
if err := i.pullImageWithReference(ctx, ref, platform, nil, pullRegistryAuth, output); err != nil { if err := i.pullImageWithReference(ctx, ref, platform, nil, pullRegistryAuth, output); err != nil {
return nil, err return nil, err
} }
return i.GetImage(name) return i.GetImage(name, platform)
} }
// GetImageAndReleasableLayer returns an image and releaseable layer for a reference or ID. // GetImageAndReleasableLayer returns an image and releaseable layer for a reference or ID.
@ -184,7 +184,7 @@ func (i *ImageService) GetImageAndReleasableLayer(ctx context.Context, refOrID s
} }
if opts.PullOption != backend.PullOptionForcePull { if opts.PullOption != backend.PullOptionForcePull {
image, err := i.GetImage(refOrID) image, err := i.GetImage(refOrID, opts.Platform)
if err != nil && opts.PullOption == backend.PullOptionNoPull { if err != nil && opts.PullOption == backend.PullOptionNoPull {
return nil, nil, err return nil, nil, err
} }

View File

@ -64,7 +64,7 @@ func (i *ImageService) ImageDelete(imageRef string, force, prune bool) ([]types.
start := time.Now() start := time.Now()
records := []types.ImageDeleteResponseItem{} records := []types.ImageDeleteResponseItem{}
img, err := i.GetImage(imageRef) img, err := i.GetImage(imageRef, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -11,7 +11,7 @@ func (i *ImageService) LogImageEvent(imageID, refName, action string) {
// LogImageEventWithAttributes generates an event related to an image with specific given attributes. // LogImageEventWithAttributes generates an event related to an image with specific given attributes.
func (i *ImageService) LogImageEventWithAttributes(imageID, refName, action string, attributes map[string]string) { func (i *ImageService) LogImageEventWithAttributes(imageID, refName, action string, attributes map[string]string) {
img, err := i.GetImage(imageID) img, err := i.GetImage(imageID, nil)
if err == nil && img.Config != nil { if err == nil && img.Config != nil {
// image has not been removed yet. // image has not been removed yet.
// it could be missing if the event is `delete`. // it could be missing if the event is `delete`.

View File

@ -14,7 +14,7 @@ import (
// name by walking the image lineage. // name by walking the image lineage.
func (i *ImageService) ImageHistory(name string) ([]*image.HistoryResponseItem, error) { func (i *ImageService) ImageHistory(name string) ([]*image.HistoryResponseItem, error) {
start := time.Now() start := time.Now()
img, err := i.GetImage(name) img, err := i.GetImage(name, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -77,7 +77,7 @@ func (i *ImageService) ImageHistory(name string) ([]*image.HistoryResponseItem,
if id == "" { if id == "" {
break break
} }
histImg, err = i.GetImage(id.String()) histImg, err = i.GetImage(id.String(), nil)
if err != nil { if err != nil {
break break
} }

View File

@ -14,7 +14,7 @@ import (
// LookupImage looks up an image by name and returns it as an ImageInspect // LookupImage looks up an image by name and returns it as an ImageInspect
// structure. // structure.
func (i *ImageService) LookupImage(name string) (*types.ImageInspect, error) { func (i *ImageService) LookupImage(name string) (*types.ImageInspect, error) {
img, err := i.GetImage(name) img, err := i.GetImage(name, nil)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "no such image: %s", name) return nil, errors.Wrapf(err, "no such image: %s", name)
} }

View File

@ -8,7 +8,7 @@ import (
// TagImage creates the tag specified by newTag, pointing to the image named // TagImage creates the tag specified by newTag, pointing to the image named
// imageName (alternatively, imageName can also be an image ID). // imageName (alternatively, imageName can also be an image ID).
func (i *ImageService) TagImage(imageName, repository, tag string) (string, error) { func (i *ImageService) TagImage(imageName, repository, tag string) (string, error) {
img, err := i.GetImage(imageName) img, err := i.GetImage(imageName, nil)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -69,7 +69,7 @@ func (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttr
var beforeFilter, sinceFilter *image.Image var beforeFilter, sinceFilter *image.Image
err = imageFilters.WalkValues("before", func(value string) error { err = imageFilters.WalkValues("before", func(value string) error {
beforeFilter, err = i.GetImage(value) beforeFilter, err = i.GetImage(value, nil)
return err return err
}) })
if err != nil { if err != nil {
@ -77,7 +77,7 @@ func (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttr
} }
err = imageFilters.WalkValues("since", func(value string) error { err = imageFilters.WalkValues("since", func(value string) error {
sinceFilter, err = i.GetImage(value) sinceFilter, err = i.GetImage(value, nil)
return err return err
}) })
if err != nil { if err != nil {

View File

@ -317,7 +317,7 @@ func (daemon *Daemon) foldFilter(view container.View, config *types.ContainerLis
if psFilters.Contains("ancestor") { if psFilters.Contains("ancestor") {
ancestorFilter = true ancestorFilter = true
psFilters.WalkValues("ancestor", func(ancestor string) error { psFilters.WalkValues("ancestor", func(ancestor string) error {
img, err := daemon.imageService.GetImage(ancestor) img, err := daemon.imageService.GetImage(ancestor, nil)
if err != nil { if err != nil {
logrus.Warnf("Error while looking up for image %v", ancestor) logrus.Warnf("Error while looking up for image %v", ancestor)
return nil return nil
@ -581,7 +581,7 @@ func (daemon *Daemon) refreshImage(s *container.Snapshot, ctx *listContext) (*ty
c := s.Container c := s.Container
image := s.Image // keep the original ref if still valid (hasn't changed) image := s.Image // keep the original ref if still valid (hasn't changed)
if image != s.ImageID { if image != s.ImageID {
img, err := daemon.imageService.GetImage(image) img, err := daemon.imageService.GetImage(image, nil)
if _, isDNE := err.(images.ErrImageDoesNotExist); err != nil && !isDNE { if _, isDNE := err.(images.ErrImageDoesNotExist); err != nil && !isDNE {
return nil, err return nil, err
} }

View File

@ -29,7 +29,7 @@ const (
func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
img, err := daemon.imageService.GetImage(string(c.ImageID)) img, err := daemon.imageService.GetImage(string(c.ImageID), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -523,7 +523,7 @@ func (s *DockerSuite) TestContainerAPIBadPort(c *testing.T) {
assert.NilError(c, err) assert.NilError(c, err)
defer cli.Close() defer cli.Close()
_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "") _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
assert.ErrorContains(c, err, `invalid port specification: "aa80"`) assert.ErrorContains(c, err, `invalid port specification: "aa80"`)
} }
@ -537,7 +537,7 @@ func (s *DockerSuite) TestContainerAPICreate(c *testing.T) {
assert.NilError(c, err) assert.NilError(c, err)
defer cli.Close() defer cli.Close()
container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "") container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
assert.NilError(c, err) assert.NilError(c, err)
out, _ := dockerCmd(c, "start", "-a", container.ID) out, _ := dockerCmd(c, "start", "-a", container.ID)
@ -550,7 +550,7 @@ func (s *DockerSuite) TestContainerAPICreateEmptyConfig(c *testing.T) {
assert.NilError(c, err) assert.NilError(c, err)
defer cli.Close() defer cli.Close()
_, err = cli.ContainerCreate(context.Background(), &containertypes.Config{}, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "") _, err = cli.ContainerCreate(context.Background(), &containertypes.Config{}, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
expected := "No command specified" expected := "No command specified"
assert.ErrorContains(c, err, expected) assert.ErrorContains(c, err, expected)
@ -574,7 +574,7 @@ func (s *DockerSuite) TestContainerAPICreateMultipleNetworksConfig(c *testing.T)
assert.NilError(c, err) assert.NilError(c, err)
defer cli.Close() defer cli.Close()
_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networkingConfig, "") _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networkingConfig, nil, "")
msg := err.Error() msg := err.Error()
// network name order in error message is not deterministic // network name order in error message is not deterministic
assert.Assert(c, strings.Contains(msg, "Container cannot be connected to network endpoints")) assert.Assert(c, strings.Contains(msg, "Container cannot be connected to network endpoints"))
@ -609,7 +609,7 @@ func UtilCreateNetworkMode(c *testing.T, networkMode containertypes.NetworkMode)
assert.NilError(c, err) assert.NilError(c, err)
defer cli.Close() defer cli.Close()
container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "") container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
assert.NilError(c, err) assert.NilError(c, err)
containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
@ -636,7 +636,7 @@ func (s *DockerSuite) TestContainerAPICreateWithCpuSharesCpuset(c *testing.T) {
assert.NilError(c, err) assert.NilError(c, err)
defer cli.Close() defer cli.Close()
container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "") container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
assert.NilError(c, err) assert.NilError(c, err)
containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
@ -948,7 +948,7 @@ func (s *DockerSuite) TestContainerAPIStart(c *testing.T) {
assert.NilError(c, err) assert.NilError(c, err)
defer cli.Close() defer cli.Close()
_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, name) _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, name)
assert.NilError(c, err) assert.NilError(c, err)
err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{}) err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{})
@ -1272,7 +1272,7 @@ func (s *DockerSuite) TestPostContainerAPICreateWithStringOrSliceEntrypoint(c *t
assert.NilError(c, err) assert.NilError(c, err)
defer cli.Close() defer cli.Close()
_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "echotest") _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "echotest")
assert.NilError(c, err) assert.NilError(c, err)
out, _ := dockerCmd(c, "start", "-a", "echotest") out, _ := dockerCmd(c, "start", "-a", "echotest")
assert.Equal(c, strings.TrimSpace(out), "hello world") assert.Equal(c, strings.TrimSpace(out), "hello world")
@ -1299,7 +1299,7 @@ func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCmd(c *testing.T)
assert.NilError(c, err) assert.NilError(c, err)
defer cli.Close() defer cli.Close()
_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "echotest") _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "echotest")
assert.NilError(c, err) assert.NilError(c, err)
out, _ := dockerCmd(c, "start", "-a", "echotest") out, _ := dockerCmd(c, "start", "-a", "echotest")
assert.Equal(c, strings.TrimSpace(out), "hello world") assert.Equal(c, strings.TrimSpace(out), "hello world")
@ -1342,7 +1342,7 @@ func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCapAddDrop(c *tes
assert.NilError(c, err) assert.NilError(c, err)
defer cli.Close() defer cli.Close()
_, err = cli.ContainerCreate(context.Background(), &config2, &hostConfig, &networktypes.NetworkingConfig{}, "capaddtest1") _, err = cli.ContainerCreate(context.Background(), &config2, &hostConfig, &networktypes.NetworkingConfig{}, nil, "capaddtest1")
assert.NilError(c, err) assert.NilError(c, err)
} }
@ -1356,7 +1356,7 @@ func (s *DockerSuite) TestContainerAPICreateNoHostConfig118(c *testing.T) {
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("v1.18")) cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("v1.18"))
assert.NilError(c, err) assert.NilError(c, err)
_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "") _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
assert.NilError(c, err) assert.NilError(c, err)
} }
@ -1407,7 +1407,7 @@ func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *testing.T
} }
name := "wrong-cpuset-cpus" name := "wrong-cpuset-cpus"
_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig1, &networktypes.NetworkingConfig{}, name) _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig1, &networktypes.NetworkingConfig{}, nil, name)
expected := "Invalid value 1-42,, for cpuset cpus" expected := "Invalid value 1-42,, for cpuset cpus"
assert.ErrorContains(c, err, expected) assert.ErrorContains(c, err, expected)
@ -1417,7 +1417,7 @@ func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *testing.T
}, },
} }
name = "wrong-cpuset-mems" name = "wrong-cpuset-mems"
_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig2, &networktypes.NetworkingConfig{}, name) _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig2, &networktypes.NetworkingConfig{}, nil, name)
expected = "Invalid value 42-3,1-- for cpuset mems" expected = "Invalid value 42-3,1-- for cpuset mems"
assert.ErrorContains(c, err, expected) assert.ErrorContains(c, err, expected)
} }
@ -1436,7 +1436,7 @@ func (s *DockerSuite) TestPostContainersCreateShmSizeNegative(c *testing.T) {
assert.NilError(c, err) assert.NilError(c, err)
defer cli.Close() defer cli.Close()
_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "") _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
assert.ErrorContains(c, err, "SHM size can not be less than 0") assert.ErrorContains(c, err, "SHM size can not be less than 0")
} }
@ -1453,7 +1453,7 @@ func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *testin
assert.NilError(c, err) assert.NilError(c, err)
defer cli.Close() defer cli.Close()
container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "") container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
assert.NilError(c, err) assert.NilError(c, err)
containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
@ -1480,7 +1480,7 @@ func (s *DockerSuite) TestPostContainersCreateShmSizeOmitted(c *testing.T) {
assert.NilError(c, err) assert.NilError(c, err)
defer cli.Close() defer cli.Close()
container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "") container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
assert.NilError(c, err) assert.NilError(c, err)
containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
@ -1511,7 +1511,7 @@ func (s *DockerSuite) TestPostContainersCreateWithShmSize(c *testing.T) {
assert.NilError(c, err) assert.NilError(c, err)
defer cli.Close() defer cli.Close()
container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "") container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
assert.NilError(c, err) assert.NilError(c, err)
containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
@ -1537,7 +1537,7 @@ func (s *DockerSuite) TestPostContainersCreateMemorySwappinessHostConfigOmitted(
assert.NilError(c, err) assert.NilError(c, err)
defer cli.Close() defer cli.Close()
container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "") container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
assert.NilError(c, err) assert.NilError(c, err)
containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
@ -1568,7 +1568,7 @@ func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *tes
defer cli.Close() defer cli.Close()
name := "oomscoreadj-over" name := "oomscoreadj-over"
_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, name) _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, name)
expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]" expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]"
assert.ErrorContains(c, err, expected) assert.ErrorContains(c, err, expected)
@ -1578,7 +1578,7 @@ func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *tes
} }
name = "oomscoreadj-low" name = "oomscoreadj-low"
_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, name) _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, name)
expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]" expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]"
assert.ErrorContains(c, err, expected) assert.ErrorContains(c, err, expected)
@ -1610,7 +1610,7 @@ func (s *DockerSuite) TestContainerAPIStatsWithNetworkDisabled(c *testing.T) {
assert.NilError(c, err) assert.NilError(c, err)
defer cli.Close() defer cli.Close()
_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, name) _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, name)
assert.NilError(c, err) assert.NilError(c, err)
err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{}) err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{})
@ -1926,7 +1926,7 @@ func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *testing.T) {
for i, x := range cases { for i, x := range cases {
x := x x := x
c.Run(fmt.Sprintf("case %d", i), func(c *testing.T) { c.Run(fmt.Sprintf("case %d", i), func(c *testing.T) {
_, err = apiClient.ContainerCreate(context.Background(), &x.config, &x.hostConfig, &networktypes.NetworkingConfig{}, "") _, err = apiClient.ContainerCreate(context.Background(), &x.config, &x.hostConfig, &networktypes.NetworkingConfig{}, nil, "")
if len(x.msg) > 0 { if len(x.msg) > 0 {
assert.ErrorContains(c, err, x.msg, "%v", cases[i].config) assert.ErrorContains(c, err, x.msg, "%v", cases[i].config)
} else { } else {
@ -1959,7 +1959,7 @@ func (s *DockerSuite) TestContainerAPICreateMountsBindRead(c *testing.T) {
assert.NilError(c, err) assert.NilError(c, err)
defer cli.Close() defer cli.Close()
_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "test") _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "test")
assert.NilError(c, err) assert.NilError(c, err)
out, _ := dockerCmd(c, "start", "-a", "test") out, _ := dockerCmd(c, "start", "-a", "test")
@ -2106,6 +2106,7 @@ func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *testing.T) {
&containertypes.Config{Image: testImg}, &containertypes.Config{Image: testImg},
&containertypes.HostConfig{Mounts: []mounttypes.Mount{x.spec}}, &containertypes.HostConfig{Mounts: []mounttypes.Mount{x.spec}},
&networktypes.NetworkingConfig{}, &networktypes.NetworkingConfig{},
nil,
"") "")
assert.NilError(c, err) assert.NilError(c, err)
@ -2213,7 +2214,7 @@ func (s *DockerSuite) TestContainersAPICreateMountsTmpfs(c *testing.T) {
Mounts: []mounttypes.Mount{x.cfg}, Mounts: []mounttypes.Mount{x.cfg},
} }
_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, cName) _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, cName)
assert.NilError(c, err) assert.NilError(c, err)
out, _ := dockerCmd(c, "start", "-a", cName) out, _ := dockerCmd(c, "start", "-a", cName)
for _, option := range x.expectedOptions { for _, option := range x.expectedOptions {

View File

@ -65,7 +65,7 @@ func (s *DockerSuite) TestContainersAPICreateMountsBindNamedPipe(c *testing.T) {
}, },
}, },
}, },
nil, name) nil, nil, name)
assert.NilError(c, err) assert.NilError(c, err)
err = client.ContainerStart(ctx, name, types.ContainerStartOptions{}) err = client.ContainerStart(ctx, name, types.ContainerStartOptions{})

View File

@ -578,7 +578,7 @@ func (s *DockerSuite) TestDuplicateMountpointsForVolumesFromAndMounts(c *testing
}, },
}, },
} }
_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &network.NetworkingConfig{}, "app") _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &network.NetworkingConfig{}, nil, "app")
assert.NilError(c, err) assert.NilError(c, err)

View File

@ -10,6 +10,7 @@ import (
"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"
containertypes "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/api/types/versions" "github.com/docker/docker/api/types/versions"
"github.com/docker/docker/client" "github.com/docker/docker/client"
@ -17,6 +18,7 @@ import (
ctr "github.com/docker/docker/integration/internal/container" ctr "github.com/docker/docker/integration/internal/container"
"github.com/docker/docker/oci" "github.com/docker/docker/oci"
"github.com/docker/docker/testutil/request" "github.com/docker/docker/testutil/request"
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/poll" "gotest.tools/v3/poll"
@ -57,6 +59,7 @@ func TestCreateFailsWhenIdentifierDoesNotExist(t *testing.T) {
&container.Config{Image: tc.image}, &container.Config{Image: tc.image},
&container.HostConfig{}, &container.HostConfig{},
&network.NetworkingConfig{}, &network.NetworkingConfig{},
nil,
"", "",
) )
assert.Check(t, is.ErrorContains(err, tc.expectedError)) assert.Check(t, is.ErrorContains(err, tc.expectedError))
@ -81,6 +84,7 @@ func TestCreateLinkToNonExistingContainer(t *testing.T) {
Links: []string{"no-such-container"}, Links: []string{"no-such-container"},
}, },
&network.NetworkingConfig{}, &network.NetworkingConfig{},
nil,
"", "",
) )
assert.Check(t, is.ErrorContains(err, "could not get container for no-such-container")) assert.Check(t, is.ErrorContains(err, "could not get container for no-such-container"))
@ -120,6 +124,7 @@ func TestCreateWithInvalidEnv(t *testing.T) {
}, },
&container.HostConfig{}, &container.HostConfig{},
&network.NetworkingConfig{}, &network.NetworkingConfig{},
nil,
"", "",
) )
assert.Check(t, is.ErrorContains(err, tc.expectedError)) assert.Check(t, is.ErrorContains(err, tc.expectedError))
@ -166,6 +171,7 @@ func TestCreateTmpfsMountsTarget(t *testing.T) {
Tmpfs: map[string]string{tc.target: ""}, Tmpfs: map[string]string{tc.target: ""},
}, },
&network.NetworkingConfig{}, &network.NetworkingConfig{},
nil,
"", "",
) )
assert.Check(t, is.ErrorContains(err, tc.expectedError)) assert.Check(t, is.ErrorContains(err, tc.expectedError))
@ -235,6 +241,7 @@ func TestCreateWithCustomMaskedPaths(t *testing.T) {
&config, &config,
&hc, &hc,
&network.NetworkingConfig{}, &network.NetworkingConfig{},
nil,
name, name,
) )
assert.NilError(t, err) assert.NilError(t, err)
@ -361,6 +368,7 @@ func TestCreateWithCapabilities(t *testing.T) {
&container.Config{Image: "busybox"}, &container.Config{Image: "busybox"},
&tc.hostConfig, &tc.hostConfig,
&network.NetworkingConfig{}, &network.NetworkingConfig{},
nil,
"", "",
) )
if tc.expectedError == "" { if tc.expectedError == "" {
@ -439,6 +447,7 @@ func TestCreateWithCustomReadonlyPaths(t *testing.T) {
&config, &config,
&hc, &hc,
&network.NetworkingConfig{}, &network.NetworkingConfig{},
nil,
name, name,
) )
assert.NilError(t, err) assert.NilError(t, err)
@ -522,7 +531,7 @@ func TestCreateWithInvalidHealthcheckParams(t *testing.T) {
cfg.Healthcheck.StartPeriod = tc.startPeriod cfg.Healthcheck.StartPeriod = tc.startPeriod
} }
resp, err := client.ContainerCreate(ctx, &cfg, &container.HostConfig{}, nil, "") resp, err := client.ContainerCreate(ctx, &cfg, &container.HostConfig{}, nil, nil, "")
assert.Check(t, is.Equal(len(resp.Warnings), 0)) assert.Check(t, is.Equal(len(resp.Warnings), 0))
if versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") { if versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") {
@ -581,3 +590,34 @@ func TestCreateTmpfsOverrideAnonymousVolume(t *testing.T) {
assert.NilError(t, err) assert.NilError(t, err)
} }
} }
// Test that if the referenced image platform does not match the requested platform on container create that we get an
// error.
func TestCreateDifferentPlatform(t *testing.T) {
defer setupTest(t)()
c := testEnv.APIClient()
ctx := context.Background()
img, _, err := c.ImageInspectWithRaw(ctx, "busybox:latest")
assert.NilError(t, err)
assert.Assert(t, img.Architecture != "")
t.Run("different os", func(t *testing.T) {
p := specs.Platform{
OS: img.Os + "DifferentOS",
Architecture: img.Architecture,
Variant: img.Variant,
}
_, err := c.ContainerCreate(ctx, &containertypes.Config{Image: "busybox:latest"}, &containertypes.HostConfig{}, nil, &p, "")
assert.Assert(t, client.IsErrNotFound(err), err)
})
t.Run("different cpu arch", func(t *testing.T) {
p := specs.Platform{
OS: img.Os,
Architecture: img.Architecture + "DifferentArch",
Variant: img.Variant,
}
_, err := c.ContainerCreate(ctx, &containertypes.Config{Image: "busybox:latest"}, &containertypes.HostConfig{}, nil, &p, "")
assert.Assert(t, client.IsErrNotFound(err), err)
})
}

View File

@ -66,7 +66,7 @@ func testIpcNonePrivateShareable(t *testing.T, mode string, mustBeMounted bool,
client := testEnv.APIClient() client := testEnv.APIClient()
ctx := context.Background() ctx := context.Background()
resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "") resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "")
assert.NilError(t, err) assert.NilError(t, err)
assert.Check(t, is.Equal(len(resp.Warnings), 0)) assert.Check(t, is.Equal(len(resp.Warnings), 0))
@ -138,7 +138,7 @@ func testIpcContainer(t *testing.T, donorMode string, mustWork bool) {
client := testEnv.APIClient() client := testEnv.APIClient()
// create and start the "donor" container // create and start the "donor" container
resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "") resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "")
assert.NilError(t, err) assert.NilError(t, err)
assert.Check(t, is.Equal(len(resp.Warnings), 0)) assert.Check(t, is.Equal(len(resp.Warnings), 0))
name1 := resp.ID name1 := resp.ID
@ -148,7 +148,7 @@ func testIpcContainer(t *testing.T, donorMode string, mustWork bool) {
// create and start the second container // create and start the second container
hostCfg.IpcMode = containertypes.IpcMode("container:" + name1) hostCfg.IpcMode = containertypes.IpcMode("container:" + name1)
resp, err = client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "") resp, err = client.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "")
assert.NilError(t, err) assert.NilError(t, err)
assert.Check(t, is.Equal(len(resp.Warnings), 0)) assert.Check(t, is.Equal(len(resp.Warnings), 0))
name2 := resp.ID name2 := resp.ID
@ -204,7 +204,7 @@ func TestAPIIpcModeHost(t *testing.T) {
ctx := context.Background() ctx := context.Background()
client := testEnv.APIClient() client := testEnv.APIClient()
resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "") resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "")
assert.NilError(t, err) assert.NilError(t, err)
assert.Check(t, is.Equal(len(resp.Warnings), 0)) assert.Check(t, is.Equal(len(resp.Warnings), 0))
name := resp.ID name := resp.ID
@ -241,7 +241,7 @@ func testDaemonIpcPrivateShareable(t *testing.T, mustBeShared bool, arg ...strin
} }
ctx := context.Background() ctx := context.Background()
resp, err := c.ContainerCreate(ctx, &cfg, &containertypes.HostConfig{}, nil, "") resp, err := c.ContainerCreate(ctx, &cfg, &containertypes.HostConfig{}, nil, nil, "")
assert.NilError(t, err) assert.NilError(t, err)
assert.Check(t, is.Equal(len(resp.Warnings), 0)) assert.Check(t, is.Equal(len(resp.Warnings), 0))

View File

@ -63,7 +63,7 @@ func TestContainerNetworkMountsNoChown(t *testing.T) {
assert.NilError(t, err) assert.NilError(t, err)
defer cli.Close() defer cli.Close()
ctrCreate, err := cli.ContainerCreate(ctx, &config, &hostConfig, &network.NetworkingConfig{}, "") ctrCreate, err := cli.ContainerCreate(ctx, &config, &hostConfig, &network.NetworkingConfig{}, nil, "")
assert.NilError(t, err) assert.NilError(t, err)
// container will exit immediately because of no tty, but we only need the start sequence to test the condition // container will exit immediately because of no tty, but we only need the start sequence to test the condition
err = cli.ContainerStart(ctx, ctrCreate.ID, types.ContainerStartOptions{}) err = cli.ContainerStart(ctx, ctrCreate.ID, types.ContainerStartOptions{})
@ -174,7 +174,7 @@ func TestMountDaemonRoot(t *testing.T) {
c, err := client.ContainerCreate(ctx, &containertypes.Config{ c, err := client.ContainerCreate(ctx, &containertypes.Config{
Image: "busybox", Image: "busybox",
Cmd: []string{"true"}, Cmd: []string{"true"},
}, hc, nil, "") }, hc, nil, nil, "")
if err != nil { if err != nil {
if test.expected != "" { if test.expected != "" {

View File

@ -77,7 +77,7 @@ func TestDaemonRestartKillContainers(t *testing.T) {
defer d.Stop(t) defer d.Stop(t)
ctx := context.Background() ctx := context.Background()
resp, err := client.ContainerCreate(ctx, c.config, c.hostConfig, nil, "") resp, err := client.ContainerCreate(ctx, c.config, c.hostConfig, nil, nil, "")
assert.NilError(t, err) assert.NilError(t, err)
defer client.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{Force: true}) defer client.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{Force: true})

View File

@ -9,6 +9,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"
"gotest.tools/v3/assert" "gotest.tools/v3/assert"
) )
@ -19,6 +20,7 @@ type TestContainerConfig struct {
Config *container.Config Config *container.Config
HostConfig *container.HostConfig HostConfig *container.HostConfig
NetworkingConfig *network.NetworkingConfig NetworkingConfig *network.NetworkingConfig
Platform *specs.Platform
} }
// create creates a container with the specified options // create creates a container with the specified options
@ -41,7 +43,7 @@ func create(ctx context.Context, t *testing.T, client client.APIClient, ops ...f
op(config) op(config)
} }
return client.ContainerCreate(ctx, config.Config, config.HostConfig, config.NetworkingConfig, config.Name) return client.ContainerCreate(ctx, config.Config, config.HostConfig, config.NetworkingConfig, config.Platform, config.Name)
} }
// Create creates a container with the specified options, asserting that there was no error // Create creates a container with the specified options, asserting that there was no error

View File

@ -9,6 +9,7 @@ import (
networktypes "github.com/docker/docker/api/types/network" networktypes "github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/strslice" "github.com/docker/docker/api/types/strslice"
"github.com/docker/go-connections/nat" "github.com/docker/go-connections/nat"
specs "github.com/opencontainers/image-spec/specs-go/v1"
) )
// WithName sets the name of the container // WithName sets the name of the container
@ -205,3 +206,10 @@ func WithExtraHost(extraHost string) func(*TestContainerConfig) {
c.HostConfig.ExtraHosts = append(c.HostConfig.ExtraHosts, extraHost) c.HostConfig.ExtraHosts = append(c.HostConfig.ExtraHosts, extraHost)
} }
} }
// WithPlatform specifies the desired platform the image should have.
func WithPlatform(p *specs.Platform) func(*TestContainerConfig) {
return func(c *TestContainerConfig) {
c.Platform = p
}
}

View File

@ -54,6 +54,7 @@ func TestReadPluginNoRead(t *testing.T) {
cfg, cfg,
&container.HostConfig{LogConfig: container.LogConfig{Type: "test"}}, &container.HostConfig{LogConfig: container.LogConfig{Type: "test"}},
nil, nil,
nil,
"", "",
) )
assert.Assert(t, err) assert.Assert(t, err)

View File

@ -155,7 +155,7 @@ COPY . /static`); err != nil {
// Start the container // Start the container
b, err := c.ContainerCreate(context.Background(), &containertypes.Config{ b, err := c.ContainerCreate(context.Background(), &containertypes.Config{
Image: image, Image: image,
}, &containertypes.HostConfig{}, nil, container) }, &containertypes.HostConfig{}, nil, nil, container)
assert.NilError(t, err) assert.NilError(t, err)
err = c.ContainerStart(context.Background(), b.ID, types.ContainerStartOptions{}) err = c.ContainerStart(context.Background(), b.ID, types.ContainerStartOptions{})
assert.NilError(t, err) assert.NilError(t, err)