Accept platform spec on container create
| ... | ... |
@@ -9,6 +9,7 @@ import ( |
| 9 | 9 |
"strconv" |
| 10 | 10 |
"syscall" |
| 11 | 11 |
|
| 12 |
+ "github.com/containerd/containerd/platforms" |
|
| 12 | 13 |
"github.com/docker/docker/api/server/httputils" |
| 13 | 14 |
"github.com/docker/docker/api/types" |
| 14 | 15 |
"github.com/docker/docker/api/types/backend" |
| ... | ... |
@@ -19,6 +20,7 @@ import ( |
| 19 | 19 |
"github.com/docker/docker/errdefs" |
| 20 | 20 |
"github.com/docker/docker/pkg/ioutils" |
| 21 | 21 |
"github.com/docker/docker/pkg/signal" |
| 22 |
+ specs "github.com/opencontainers/image-spec/specs-go/v1" |
|
| 22 | 23 |
"github.com/pkg/errors" |
| 23 | 24 |
"github.com/sirupsen/logrus" |
| 24 | 25 |
"golang.org/x/net/websocket" |
| ... | ... |
@@ -502,6 +504,28 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo |
| 502 | 502 |
} |
| 503 | 503 |
} |
| 504 | 504 |
|
| 505 |
+ var platform *specs.Platform |
|
| 506 |
+ if versions.GreaterThanOrEqualTo(version, "1.41") {
|
|
| 507 |
+ if v := r.Form.Get("platform"); v != "" {
|
|
| 508 |
+ p, err := platforms.Parse(v) |
|
| 509 |
+ if err != nil {
|
|
| 510 |
+ return errdefs.InvalidParameter(err) |
|
| 511 |
+ } |
|
| 512 |
+ platform = &p |
|
| 513 |
+ } |
|
| 514 |
+ defaultPlatform := platforms.DefaultSpec() |
|
| 515 |
+ if platform == nil {
|
|
| 516 |
+ platform = &defaultPlatform |
|
| 517 |
+ } |
|
| 518 |
+ if platform.OS == "" {
|
|
| 519 |
+ platform.OS = defaultPlatform.OS |
|
| 520 |
+ } |
|
| 521 |
+ if platform.Architecture == "" {
|
|
| 522 |
+ platform.Architecture = defaultPlatform.Architecture |
|
| 523 |
+ platform.Variant = defaultPlatform.Variant |
|
| 524 |
+ } |
|
| 525 |
+ } |
|
| 526 |
+ |
|
| 505 | 527 |
if hostConfig != nil && hostConfig.PidsLimit != nil && *hostConfig.PidsLimit <= 0 {
|
| 506 | 528 |
// Don't set a limit if either no limit was specified, or "unlimited" was |
| 507 | 529 |
// explicitly set. |
| ... | ... |
@@ -516,6 +540,7 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo |
| 516 | 516 |
HostConfig: hostConfig, |
| 517 | 517 |
NetworkingConfig: networkingConfig, |
| 518 | 518 |
AdjustCPUShares: adjustCPUShares, |
| 519 |
+ Platform: platform, |
|
| 519 | 520 |
}) |
| 520 | 521 |
if err != nil {
|
| 521 | 522 |
return err |
| ... | ... |
@@ -3,6 +3,7 @@ package types // import "github.com/docker/docker/api/types" |
| 3 | 3 |
import ( |
| 4 | 4 |
"github.com/docker/docker/api/types/container" |
| 5 | 5 |
"github.com/docker/docker/api/types/network" |
| 6 |
+ specs "github.com/opencontainers/image-spec/specs-go/v1" |
|
| 6 | 7 |
) |
| 7 | 8 |
|
| 8 | 9 |
// configs holds structs used for internal communication between the |
| ... | ... |
@@ -15,6 +16,7 @@ type ContainerCreateConfig struct {
|
| 15 | 15 |
Config *container.Config |
| 16 | 16 |
HostConfig *container.HostConfig |
| 17 | 17 |
NetworkingConfig *network.NetworkingConfig |
| 18 |
+ Platform *specs.Platform |
|
| 18 | 19 |
AdjustCPUShares bool |
| 19 | 20 |
} |
| 20 | 21 |
|
| ... | ... |
@@ -5,20 +5,23 @@ import ( |
| 5 | 5 |
"encoding/json" |
| 6 | 6 |
"net/url" |
| 7 | 7 |
|
| 8 |
+ "github.com/containerd/containerd/platforms" |
|
| 8 | 9 |
"github.com/docker/docker/api/types/container" |
| 9 | 10 |
"github.com/docker/docker/api/types/network" |
| 10 | 11 |
"github.com/docker/docker/api/types/versions" |
| 12 |
+ specs "github.com/opencontainers/image-spec/specs-go/v1" |
|
| 11 | 13 |
) |
| 12 | 14 |
|
| 13 | 15 |
type configWrapper struct {
|
| 14 | 16 |
*container.Config |
| 15 | 17 |
HostConfig *container.HostConfig |
| 16 | 18 |
NetworkingConfig *network.NetworkingConfig |
| 19 |
+ Platform *specs.Platform |
|
| 17 | 20 |
} |
| 18 | 21 |
|
| 19 | 22 |
// ContainerCreate creates a new container based in the given configuration. |
| 20 | 23 |
// It can be associated with a name, but it's not mandatory. |
| 21 |
-func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (container.ContainerCreateCreatedBody, error) {
|
|
| 24 |
+func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *specs.Platform, containerName string) (container.ContainerCreateCreatedBody, error) {
|
|
| 22 | 25 |
var response container.ContainerCreateCreatedBody |
| 23 | 26 |
|
| 24 | 27 |
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 |
| 30 | 30 |
hostConfig.AutoRemove = false |
| 31 | 31 |
} |
| 32 | 32 |
|
| 33 |
+ if err := cli.NewVersionError("1.41", "specify container image platform"); platform != nil && err != nil {
|
|
| 34 |
+ return response, err |
|
| 35 |
+ } |
|
| 36 |
+ |
|
| 33 | 37 |
query := url.Values{}
|
| 38 |
+ if platform != nil {
|
|
| 39 |
+ query.Set("platform", platforms.Format(*platform))
|
|
| 40 |
+ } |
|
| 41 |
+ |
|
| 34 | 42 |
if containerName != "" {
|
| 35 | 43 |
query.Set("name", containerName)
|
| 36 | 44 |
} |
| ... | ... |
@@ -18,7 +18,7 @@ func TestContainerCreateError(t *testing.T) {
|
| 18 | 18 |
client := &Client{
|
| 19 | 19 |
client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), |
| 20 | 20 |
} |
| 21 |
- _, err := client.ContainerCreate(context.Background(), nil, nil, nil, "nothing") |
|
| 21 |
+ _, err := client.ContainerCreate(context.Background(), nil, nil, nil, nil, "nothing") |
|
| 22 | 22 |
if !errdefs.IsSystem(err) {
|
| 23 | 23 |
t.Fatalf("expected a Server Error while testing StatusInternalServerError, got %T", err)
|
| 24 | 24 |
} |
| ... | ... |
@@ -27,7 +27,7 @@ func TestContainerCreateError(t *testing.T) {
|
| 27 | 27 |
client = &Client{
|
| 28 | 28 |
client: newMockClient(errorMock(http.StatusNotFound, "Server error")), |
| 29 | 29 |
} |
| 30 |
- _, err = client.ContainerCreate(context.Background(), nil, nil, nil, "nothing") |
|
| 30 |
+ _, err = client.ContainerCreate(context.Background(), nil, nil, nil, nil, "nothing") |
|
| 31 | 31 |
if err == nil || !IsErrNotFound(err) {
|
| 32 | 32 |
t.Fatalf("expected a Server Error while testing StatusNotFound, got %T", err)
|
| 33 | 33 |
} |
| ... | ... |
@@ -37,7 +37,7 @@ func TestContainerCreateImageNotFound(t *testing.T) {
|
| 37 | 37 |
client := &Client{
|
| 38 | 38 |
client: newMockClient(errorMock(http.StatusNotFound, "No such image")), |
| 39 | 39 |
} |
| 40 |
- _, err := client.ContainerCreate(context.Background(), &container.Config{Image: "unknown_image"}, nil, nil, "unknown")
|
|
| 40 |
+ _, err := client.ContainerCreate(context.Background(), &container.Config{Image: "unknown_image"}, nil, nil, nil, "unknown")
|
|
| 41 | 41 |
if err == nil || !IsErrNotFound(err) {
|
| 42 | 42 |
t.Fatalf("expected an imageNotFound error, got %v, %T", err, err)
|
| 43 | 43 |
} |
| ... | ... |
@@ -67,7 +67,7 @@ func TestContainerCreateWithName(t *testing.T) {
|
| 67 | 67 |
}), |
| 68 | 68 |
} |
| 69 | 69 |
|
| 70 |
- r, err := client.ContainerCreate(context.Background(), nil, nil, nil, "container_name") |
|
| 70 |
+ r, err := client.ContainerCreate(context.Background(), nil, nil, nil, nil, "container_name") |
|
| 71 | 71 |
if err != nil {
|
| 72 | 72 |
t.Fatal(err) |
| 73 | 73 |
} |
| ... | ... |
@@ -106,14 +106,14 @@ func TestContainerCreateAutoRemove(t *testing.T) {
|
| 106 | 106 |
client: newMockClient(autoRemoveValidator(false)), |
| 107 | 107 |
version: "1.24", |
| 108 | 108 |
} |
| 109 |
- if _, err := client.ContainerCreate(context.Background(), nil, &container.HostConfig{AutoRemove: true}, nil, ""); err != nil {
|
|
| 109 |
+ if _, err := client.ContainerCreate(context.Background(), nil, &container.HostConfig{AutoRemove: true}, nil, nil, ""); err != nil {
|
|
| 110 | 110 |
t.Fatal(err) |
| 111 | 111 |
} |
| 112 | 112 |
client = &Client{
|
| 113 | 113 |
client: newMockClient(autoRemoveValidator(true)), |
| 114 | 114 |
version: "1.25", |
| 115 | 115 |
} |
| 116 |
- if _, err := client.ContainerCreate(context.Background(), nil, &container.HostConfig{AutoRemove: true}, nil, ""); err != nil {
|
|
| 116 |
+ if _, err := client.ContainerCreate(context.Background(), nil, &container.HostConfig{AutoRemove: true}, nil, nil, ""); err != nil {
|
|
| 117 | 117 |
t.Fatal(err) |
| 118 | 118 |
} |
| 119 | 119 |
} |
| ... | ... |
@@ -16,6 +16,7 @@ import ( |
| 16 | 16 |
"github.com/docker/docker/api/types/registry" |
| 17 | 17 |
"github.com/docker/docker/api/types/swarm" |
| 18 | 18 |
volumetypes "github.com/docker/docker/api/types/volume" |
| 19 |
+ specs "github.com/opencontainers/image-spec/specs-go/v1" |
|
| 19 | 20 |
) |
| 20 | 21 |
|
| 21 | 22 |
// CommonAPIClient is the common methods between stable and experimental versions of APIClient. |
| ... | ... |
@@ -47,7 +48,7 @@ type CommonAPIClient interface {
|
| 47 | 47 |
type ContainerAPIClient interface {
|
| 48 | 48 |
ContainerAttach(ctx context.Context, container string, options types.ContainerAttachOptions) (types.HijackedResponse, error) |
| 49 | 49 |
ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.IDResponse, error) |
| 50 |
- ContainerCreate(ctx context.Context, config *containertypes.Config, hostConfig *containertypes.HostConfig, networkingConfig *networktypes.NetworkingConfig, containerName string) (containertypes.ContainerCreateCreatedBody, error) |
|
| 50 |
+ ContainerCreate(ctx context.Context, config *containertypes.Config, hostConfig *containertypes.HostConfig, networkingConfig *networktypes.NetworkingConfig, platform *specs.Platform, containerName string) (containertypes.ContainerCreateCreatedBody, error) |
|
| 51 | 51 |
ContainerDiff(ctx context.Context, container string) ([]containertypes.ContainerChangeResponseItem, error) |
| 52 | 52 |
ContainerExecAttach(ctx context.Context, execID string, config types.ExecStartCheck) (types.HijackedResponse, error) |
| 53 | 53 |
ContainerExecCreate(ctx context.Context, container string, config types.ExecConfig) (types.IDResponse, error) |
| ... | ... |
@@ -60,7 +60,7 @@ func (daemon *Daemon) containerCreate(opts createOpts) (containertypes.Container |
| 60 | 60 |
|
| 61 | 61 |
os := runtime.GOOS |
| 62 | 62 |
if opts.params.Config.Image != "" {
|
| 63 |
- img, err := daemon.imageService.GetImage(opts.params.Config.Image) |
|
| 63 |
+ img, err := daemon.imageService.GetImage(opts.params.Config.Image, opts.params.Platform) |
|
| 64 | 64 |
if err == nil {
|
| 65 | 65 |
os = img.OS |
| 66 | 66 |
} |
| ... | ... |
@@ -114,7 +114,7 @@ func (daemon *Daemon) create(opts createOpts) (retC *container.Container, retErr |
| 114 | 114 |
|
| 115 | 115 |
os := runtime.GOOS |
| 116 | 116 |
if opts.params.Config.Image != "" {
|
| 117 |
- img, err = daemon.imageService.GetImage(opts.params.Config.Image) |
|
| 117 |
+ img, err = daemon.imageService.GetImage(opts.params.Config.Image, opts.params.Platform) |
|
| 118 | 118 |
if err != nil {
|
| 119 | 119 |
return nil, err |
| 120 | 120 |
} |
| ... | ... |
@@ -15,7 +15,7 @@ func (i *ImageService) MakeImageCache(sourceRefs []string) builder.ImageCache {
|
| 15 | 15 |
cache := cache.New(i.imageStore) |
| 16 | 16 |
|
| 17 | 17 |
for _, ref := range sourceRefs {
|
| 18 |
- img, err := i.GetImage(ref) |
|
| 18 |
+ img, err := i.GetImage(ref, nil) |
|
| 19 | 19 |
if err != nil {
|
| 20 | 20 |
logrus.Warnf("Could not look up %s for cache resolution, skipping: %+v", ref, err)
|
| 21 | 21 |
continue |
| ... | ... |
@@ -3,9 +3,12 @@ package images // import "github.com/docker/docker/daemon/images" |
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
|
| 6 |
+ "github.com/pkg/errors" |
|
| 7 |
+ |
|
| 6 | 8 |
"github.com/docker/distribution/reference" |
| 7 | 9 |
"github.com/docker/docker/errdefs" |
| 8 | 10 |
"github.com/docker/docker/image" |
| 11 |
+ specs "github.com/opencontainers/image-spec/specs-go/v1" |
|
| 9 | 12 |
) |
| 10 | 13 |
|
| 11 | 14 |
// ErrImageDoesNotExist is error returned when no image can be found for a reference. |
| ... | ... |
@@ -25,7 +28,39 @@ func (e ErrImageDoesNotExist) Error() string {
|
| 25 | 25 |
func (e ErrImageDoesNotExist) NotFound() {}
|
| 26 | 26 |
|
| 27 | 27 |
// GetImage returns an image corresponding to the image referred to by refOrID. |
| 28 |
-func (i *ImageService) GetImage(refOrID string) (*image.Image, error) {
|
|
| 28 |
+func (i *ImageService) GetImage(refOrID string, platform *specs.Platform) (retImg *image.Image, retErr error) {
|
|
| 29 |
+ defer func() {
|
|
| 30 |
+ if retErr != nil || retImg == nil || platform == nil {
|
|
| 31 |
+ return |
|
| 32 |
+ } |
|
| 33 |
+ |
|
| 34 |
+ // This allows us to tell clients that we don't have the image they asked for |
|
| 35 |
+ // Where this gets hairy is the image store does not currently support multi-arch images, e.g.: |
|
| 36 |
+ // An image `foo` may have a multi-arch manifest, but the image store only fetches the image for a specific platform |
|
| 37 |
+ // The image store does not store the manifest list and image tags are assigned to architecture specific images. |
|
| 38 |
+ // So we can have a `foo` image that is amd64 but the user requested armv7. If the user looks at the list of images. |
|
| 39 |
+ // This may be confusing. |
|
| 40 |
+ // The alternative to this is to return a errdefs.Conflict error with a helpful message, but clients will not be |
|
| 41 |
+ // able to automatically tell what causes the conflict. |
|
| 42 |
+ if retImg.OS != platform.OS {
|
|
| 43 |
+ 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))
|
|
| 44 |
+ retImg = nil |
|
| 45 |
+ return |
|
| 46 |
+ } |
|
| 47 |
+ if retImg.Architecture != platform.Architecture {
|
|
| 48 |
+ 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))
|
|
| 49 |
+ retImg = nil |
|
| 50 |
+ return |
|
| 51 |
+ } |
|
| 52 |
+ |
|
| 53 |
+ // Only validate variant if retImg has a variant set. |
|
| 54 |
+ // The image variant may not be set since it's a newer field. |
|
| 55 |
+ if platform.Variant != "" && retImg.Variant != "" && retImg.Variant != platform.Variant {
|
|
| 56 |
+ 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))
|
|
| 57 |
+ retImg = nil |
|
| 58 |
+ return |
|
| 59 |
+ } |
|
| 60 |
+ }() |
|
| 29 | 61 |
ref, err := reference.ParseAnyReference(refOrID) |
| 30 | 62 |
if err != nil {
|
| 31 | 63 |
return nil, errdefs.InvalidParameter(err) |
| ... | ... |
@@ -161,7 +161,7 @@ func (i *ImageService) pullForBuilder(ctx context.Context, name string, authConf |
| 161 | 161 |
if err := i.pullImageWithReference(ctx, ref, platform, nil, pullRegistryAuth, output); err != nil {
|
| 162 | 162 |
return nil, err |
| 163 | 163 |
} |
| 164 |
- return i.GetImage(name) |
|
| 164 |
+ return i.GetImage(name, platform) |
|
| 165 | 165 |
} |
| 166 | 166 |
|
| 167 | 167 |
// 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 |
| 184 | 184 |
} |
| 185 | 185 |
|
| 186 | 186 |
if opts.PullOption != backend.PullOptionForcePull {
|
| 187 |
- image, err := i.GetImage(refOrID) |
|
| 187 |
+ image, err := i.GetImage(refOrID, opts.Platform) |
|
| 188 | 188 |
if err != nil && opts.PullOption == backend.PullOptionNoPull {
|
| 189 | 189 |
return nil, nil, err |
| 190 | 190 |
} |
| ... | ... |
@@ -64,7 +64,7 @@ func (i *ImageService) ImageDelete(imageRef string, force, prune bool) ([]types. |
| 64 | 64 |
start := time.Now() |
| 65 | 65 |
records := []types.ImageDeleteResponseItem{}
|
| 66 | 66 |
|
| 67 |
- img, err := i.GetImage(imageRef) |
|
| 67 |
+ img, err := i.GetImage(imageRef, nil) |
|
| 68 | 68 |
if err != nil {
|
| 69 | 69 |
return nil, err |
| 70 | 70 |
} |
| ... | ... |
@@ -11,7 +11,7 @@ func (i *ImageService) LogImageEvent(imageID, refName, action string) {
|
| 11 | 11 |
|
| 12 | 12 |
// LogImageEventWithAttributes generates an event related to an image with specific given attributes. |
| 13 | 13 |
func (i *ImageService) LogImageEventWithAttributes(imageID, refName, action string, attributes map[string]string) {
|
| 14 |
- img, err := i.GetImage(imageID) |
|
| 14 |
+ img, err := i.GetImage(imageID, nil) |
|
| 15 | 15 |
if err == nil && img.Config != nil {
|
| 16 | 16 |
// image has not been removed yet. |
| 17 | 17 |
// it could be missing if the event is `delete`. |
| ... | ... |
@@ -14,7 +14,7 @@ import ( |
| 14 | 14 |
// name by walking the image lineage. |
| 15 | 15 |
func (i *ImageService) ImageHistory(name string) ([]*image.HistoryResponseItem, error) {
|
| 16 | 16 |
start := time.Now() |
| 17 |
- img, err := i.GetImage(name) |
|
| 17 |
+ img, err := i.GetImage(name, nil) |
|
| 18 | 18 |
if err != nil {
|
| 19 | 19 |
return nil, err |
| 20 | 20 |
} |
| ... | ... |
@@ -77,7 +77,7 @@ func (i *ImageService) ImageHistory(name string) ([]*image.HistoryResponseItem, |
| 77 | 77 |
if id == "" {
|
| 78 | 78 |
break |
| 79 | 79 |
} |
| 80 |
- histImg, err = i.GetImage(id.String()) |
|
| 80 |
+ histImg, err = i.GetImage(id.String(), nil) |
|
| 81 | 81 |
if err != nil {
|
| 82 | 82 |
break |
| 83 | 83 |
} |
| ... | ... |
@@ -14,7 +14,7 @@ import ( |
| 14 | 14 |
// LookupImage looks up an image by name and returns it as an ImageInspect |
| 15 | 15 |
// structure. |
| 16 | 16 |
func (i *ImageService) LookupImage(name string) (*types.ImageInspect, error) {
|
| 17 |
- img, err := i.GetImage(name) |
|
| 17 |
+ img, err := i.GetImage(name, nil) |
|
| 18 | 18 |
if err != nil {
|
| 19 | 19 |
return nil, errors.Wrapf(err, "no such image: %s", name) |
| 20 | 20 |
} |
| ... | ... |
@@ -8,7 +8,7 @@ import ( |
| 8 | 8 |
// TagImage creates the tag specified by newTag, pointing to the image named |
| 9 | 9 |
// imageName (alternatively, imageName can also be an image ID). |
| 10 | 10 |
func (i *ImageService) TagImage(imageName, repository, tag string) (string, error) {
|
| 11 |
- img, err := i.GetImage(imageName) |
|
| 11 |
+ img, err := i.GetImage(imageName, nil) |
|
| 12 | 12 |
if err != nil {
|
| 13 | 13 |
return "", err |
| 14 | 14 |
} |
| ... | ... |
@@ -69,7 +69,7 @@ func (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttr |
| 69 | 69 |
|
| 70 | 70 |
var beforeFilter, sinceFilter *image.Image |
| 71 | 71 |
err = imageFilters.WalkValues("before", func(value string) error {
|
| 72 |
- beforeFilter, err = i.GetImage(value) |
|
| 72 |
+ beforeFilter, err = i.GetImage(value, nil) |
|
| 73 | 73 |
return err |
| 74 | 74 |
}) |
| 75 | 75 |
if err != nil {
|
| ... | ... |
@@ -77,7 +77,7 @@ func (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttr |
| 77 | 77 |
} |
| 78 | 78 |
|
| 79 | 79 |
err = imageFilters.WalkValues("since", func(value string) error {
|
| 80 |
- sinceFilter, err = i.GetImage(value) |
|
| 80 |
+ sinceFilter, err = i.GetImage(value, nil) |
|
| 81 | 81 |
return err |
| 82 | 82 |
}) |
| 83 | 83 |
if err != nil {
|
| ... | ... |
@@ -317,7 +317,7 @@ func (daemon *Daemon) foldFilter(view container.View, config *types.ContainerLis |
| 317 | 317 |
if psFilters.Contains("ancestor") {
|
| 318 | 318 |
ancestorFilter = true |
| 319 | 319 |
psFilters.WalkValues("ancestor", func(ancestor string) error {
|
| 320 |
- img, err := daemon.imageService.GetImage(ancestor) |
|
| 320 |
+ img, err := daemon.imageService.GetImage(ancestor, nil) |
|
| 321 | 321 |
if err != nil {
|
| 322 | 322 |
logrus.Warnf("Error while looking up for image %v", ancestor)
|
| 323 | 323 |
return nil |
| ... | ... |
@@ -581,7 +581,7 @@ func (daemon *Daemon) refreshImage(s *container.Snapshot, ctx *listContext) (*ty |
| 581 | 581 |
c := s.Container |
| 582 | 582 |
image := s.Image // keep the original ref if still valid (hasn't changed) |
| 583 | 583 |
if image != s.ImageID {
|
| 584 |
- img, err := daemon.imageService.GetImage(image) |
|
| 584 |
+ img, err := daemon.imageService.GetImage(image, nil) |
|
| 585 | 585 |
if _, isDNE := err.(images.ErrImageDoesNotExist); err != nil && !isDNE {
|
| 586 | 586 |
return nil, err |
| 587 | 587 |
} |
| ... | ... |
@@ -29,7 +29,7 @@ const ( |
| 29 | 29 |
|
| 30 | 30 |
func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
|
| 31 | 31 |
|
| 32 |
- img, err := daemon.imageService.GetImage(string(c.ImageID)) |
|
| 32 |
+ img, err := daemon.imageService.GetImage(string(c.ImageID), nil) |
|
| 33 | 33 |
if err != nil {
|
| 34 | 34 |
return nil, err |
| 35 | 35 |
} |
| ... | ... |
@@ -523,7 +523,7 @@ func (s *DockerSuite) TestContainerAPIBadPort(c *testing.T) {
|
| 523 | 523 |
assert.NilError(c, err) |
| 524 | 524 |
defer cli.Close() |
| 525 | 525 |
|
| 526 |
- _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "")
|
|
| 526 |
+ _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
|
|
| 527 | 527 |
assert.ErrorContains(c, err, `invalid port specification: "aa80"`) |
| 528 | 528 |
} |
| 529 | 529 |
|
| ... | ... |
@@ -537,7 +537,7 @@ func (s *DockerSuite) TestContainerAPICreate(c *testing.T) {
|
| 537 | 537 |
assert.NilError(c, err) |
| 538 | 538 |
defer cli.Close() |
| 539 | 539 |
|
| 540 |
- container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
|
|
| 540 |
+ container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
|
|
| 541 | 541 |
assert.NilError(c, err) |
| 542 | 542 |
|
| 543 | 543 |
out, _ := dockerCmd(c, "start", "-a", container.ID) |
| ... | ... |
@@ -550,7 +550,7 @@ func (s *DockerSuite) TestContainerAPICreateEmptyConfig(c *testing.T) {
|
| 550 | 550 |
assert.NilError(c, err) |
| 551 | 551 |
defer cli.Close() |
| 552 | 552 |
|
| 553 |
- _, err = cli.ContainerCreate(context.Background(), &containertypes.Config{}, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
|
|
| 553 |
+ _, err = cli.ContainerCreate(context.Background(), &containertypes.Config{}, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
|
|
| 554 | 554 |
|
| 555 | 555 |
expected := "No command specified" |
| 556 | 556 |
assert.ErrorContains(c, err, expected) |
| ... | ... |
@@ -574,7 +574,7 @@ func (s *DockerSuite) TestContainerAPICreateMultipleNetworksConfig(c *testing.T) |
| 574 | 574 |
assert.NilError(c, err) |
| 575 | 575 |
defer cli.Close() |
| 576 | 576 |
|
| 577 |
- _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networkingConfig, "")
|
|
| 577 |
+ _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networkingConfig, nil, "")
|
|
| 578 | 578 |
msg := err.Error() |
| 579 | 579 |
// network name order in error message is not deterministic |
| 580 | 580 |
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) |
| 609 | 609 |
assert.NilError(c, err) |
| 610 | 610 |
defer cli.Close() |
| 611 | 611 |
|
| 612 |
- container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "")
|
|
| 612 |
+ container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
|
|
| 613 | 613 |
assert.NilError(c, err) |
| 614 | 614 |
|
| 615 | 615 |
containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) |
| ... | ... |
@@ -636,7 +636,7 @@ func (s *DockerSuite) TestContainerAPICreateWithCpuSharesCpuset(c *testing.T) {
|
| 636 | 636 |
assert.NilError(c, err) |
| 637 | 637 |
defer cli.Close() |
| 638 | 638 |
|
| 639 |
- container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "")
|
|
| 639 |
+ container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
|
|
| 640 | 640 |
assert.NilError(c, err) |
| 641 | 641 |
|
| 642 | 642 |
containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) |
| ... | ... |
@@ -948,7 +948,7 @@ func (s *DockerSuite) TestContainerAPIStart(c *testing.T) {
|
| 948 | 948 |
assert.NilError(c, err) |
| 949 | 949 |
defer cli.Close() |
| 950 | 950 |
|
| 951 |
- _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, name)
|
|
| 951 |
+ _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, name)
|
|
| 952 | 952 |
assert.NilError(c, err) |
| 953 | 953 |
|
| 954 | 954 |
err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{})
|
| ... | ... |
@@ -1272,7 +1272,7 @@ func (s *DockerSuite) TestPostContainerAPICreateWithStringOrSliceEntrypoint(c *t |
| 1272 | 1272 |
assert.NilError(c, err) |
| 1273 | 1273 |
defer cli.Close() |
| 1274 | 1274 |
|
| 1275 |
- _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "echotest")
|
|
| 1275 |
+ _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "echotest")
|
|
| 1276 | 1276 |
assert.NilError(c, err) |
| 1277 | 1277 |
out, _ := dockerCmd(c, "start", "-a", "echotest") |
| 1278 | 1278 |
assert.Equal(c, strings.TrimSpace(out), "hello world") |
| ... | ... |
@@ -1299,7 +1299,7 @@ func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCmd(c *testing.T) |
| 1299 | 1299 |
assert.NilError(c, err) |
| 1300 | 1300 |
defer cli.Close() |
| 1301 | 1301 |
|
| 1302 |
- _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "echotest")
|
|
| 1302 |
+ _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "echotest")
|
|
| 1303 | 1303 |
assert.NilError(c, err) |
| 1304 | 1304 |
out, _ := dockerCmd(c, "start", "-a", "echotest") |
| 1305 | 1305 |
assert.Equal(c, strings.TrimSpace(out), "hello world") |
| ... | ... |
@@ -1342,7 +1342,7 @@ func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCapAddDrop(c *tes |
| 1342 | 1342 |
assert.NilError(c, err) |
| 1343 | 1343 |
defer cli.Close() |
| 1344 | 1344 |
|
| 1345 |
- _, err = cli.ContainerCreate(context.Background(), &config2, &hostConfig, &networktypes.NetworkingConfig{}, "capaddtest1")
|
|
| 1345 |
+ _, err = cli.ContainerCreate(context.Background(), &config2, &hostConfig, &networktypes.NetworkingConfig{}, nil, "capaddtest1")
|
|
| 1346 | 1346 |
assert.NilError(c, err) |
| 1347 | 1347 |
} |
| 1348 | 1348 |
|
| ... | ... |
@@ -1356,7 +1356,7 @@ func (s *DockerSuite) TestContainerAPICreateNoHostConfig118(c *testing.T) {
|
| 1356 | 1356 |
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("v1.18"))
|
| 1357 | 1357 |
assert.NilError(c, err) |
| 1358 | 1358 |
|
| 1359 |
- _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
|
|
| 1359 |
+ _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
|
|
| 1360 | 1360 |
assert.NilError(c, err) |
| 1361 | 1361 |
} |
| 1362 | 1362 |
|
| ... | ... |
@@ -1407,7 +1407,7 @@ func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *testing.T |
| 1407 | 1407 |
} |
| 1408 | 1408 |
name := "wrong-cpuset-cpus" |
| 1409 | 1409 |
|
| 1410 |
- _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig1, &networktypes.NetworkingConfig{}, name)
|
|
| 1410 |
+ _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig1, &networktypes.NetworkingConfig{}, nil, name)
|
|
| 1411 | 1411 |
expected := "Invalid value 1-42,, for cpuset cpus" |
| 1412 | 1412 |
assert.ErrorContains(c, err, expected) |
| 1413 | 1413 |
|
| ... | ... |
@@ -1417,7 +1417,7 @@ func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *testing.T |
| 1417 | 1417 |
}, |
| 1418 | 1418 |
} |
| 1419 | 1419 |
name = "wrong-cpuset-mems" |
| 1420 |
- _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig2, &networktypes.NetworkingConfig{}, name)
|
|
| 1420 |
+ _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig2, &networktypes.NetworkingConfig{}, nil, name)
|
|
| 1421 | 1421 |
expected = "Invalid value 42-3,1-- for cpuset mems" |
| 1422 | 1422 |
assert.ErrorContains(c, err, expected) |
| 1423 | 1423 |
} |
| ... | ... |
@@ -1436,7 +1436,7 @@ func (s *DockerSuite) TestPostContainersCreateShmSizeNegative(c *testing.T) {
|
| 1436 | 1436 |
assert.NilError(c, err) |
| 1437 | 1437 |
defer cli.Close() |
| 1438 | 1438 |
|
| 1439 |
- _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "")
|
|
| 1439 |
+ _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
|
|
| 1440 | 1440 |
assert.ErrorContains(c, err, "SHM size can not be less than 0") |
| 1441 | 1441 |
} |
| 1442 | 1442 |
|
| ... | ... |
@@ -1453,7 +1453,7 @@ func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *testin |
| 1453 | 1453 |
assert.NilError(c, err) |
| 1454 | 1454 |
defer cli.Close() |
| 1455 | 1455 |
|
| 1456 |
- container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
|
|
| 1456 |
+ container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
|
|
| 1457 | 1457 |
assert.NilError(c, err) |
| 1458 | 1458 |
|
| 1459 | 1459 |
containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) |
| ... | ... |
@@ -1480,7 +1480,7 @@ func (s *DockerSuite) TestPostContainersCreateShmSizeOmitted(c *testing.T) {
|
| 1480 | 1480 |
assert.NilError(c, err) |
| 1481 | 1481 |
defer cli.Close() |
| 1482 | 1482 |
|
| 1483 |
- container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
|
|
| 1483 |
+ container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
|
|
| 1484 | 1484 |
assert.NilError(c, err) |
| 1485 | 1485 |
|
| 1486 | 1486 |
containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) |
| ... | ... |
@@ -1511,7 +1511,7 @@ func (s *DockerSuite) TestPostContainersCreateWithShmSize(c *testing.T) {
|
| 1511 | 1511 |
assert.NilError(c, err) |
| 1512 | 1512 |
defer cli.Close() |
| 1513 | 1513 |
|
| 1514 |
- container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "")
|
|
| 1514 |
+ container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
|
|
| 1515 | 1515 |
assert.NilError(c, err) |
| 1516 | 1516 |
|
| 1517 | 1517 |
containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) |
| ... | ... |
@@ -1537,7 +1537,7 @@ func (s *DockerSuite) TestPostContainersCreateMemorySwappinessHostConfigOmitted( |
| 1537 | 1537 |
assert.NilError(c, err) |
| 1538 | 1538 |
defer cli.Close() |
| 1539 | 1539 |
|
| 1540 |
- container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
|
|
| 1540 |
+ container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
|
|
| 1541 | 1541 |
assert.NilError(c, err) |
| 1542 | 1542 |
|
| 1543 | 1543 |
containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) |
| ... | ... |
@@ -1568,7 +1568,7 @@ func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *tes |
| 1568 | 1568 |
defer cli.Close() |
| 1569 | 1569 |
|
| 1570 | 1570 |
name := "oomscoreadj-over" |
| 1571 |
- _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, name)
|
|
| 1571 |
+ _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, name)
|
|
| 1572 | 1572 |
|
| 1573 | 1573 |
expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]" |
| 1574 | 1574 |
assert.ErrorContains(c, err, expected) |
| ... | ... |
@@ -1578,7 +1578,7 @@ func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *tes |
| 1578 | 1578 |
} |
| 1579 | 1579 |
|
| 1580 | 1580 |
name = "oomscoreadj-low" |
| 1581 |
- _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, name)
|
|
| 1581 |
+ _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, name)
|
|
| 1582 | 1582 |
|
| 1583 | 1583 |
expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]" |
| 1584 | 1584 |
assert.ErrorContains(c, err, expected) |
| ... | ... |
@@ -1610,7 +1610,7 @@ func (s *DockerSuite) TestContainerAPIStatsWithNetworkDisabled(c *testing.T) {
|
| 1610 | 1610 |
assert.NilError(c, err) |
| 1611 | 1611 |
defer cli.Close() |
| 1612 | 1612 |
|
| 1613 |
- _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, name)
|
|
| 1613 |
+ _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, name)
|
|
| 1614 | 1614 |
assert.NilError(c, err) |
| 1615 | 1615 |
|
| 1616 | 1616 |
err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{})
|
| ... | ... |
@@ -1926,7 +1926,7 @@ func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *testing.T) {
|
| 1926 | 1926 |
for i, x := range cases {
|
| 1927 | 1927 |
x := x |
| 1928 | 1928 |
c.Run(fmt.Sprintf("case %d", i), func(c *testing.T) {
|
| 1929 |
- _, err = apiClient.ContainerCreate(context.Background(), &x.config, &x.hostConfig, &networktypes.NetworkingConfig{}, "")
|
|
| 1929 |
+ _, err = apiClient.ContainerCreate(context.Background(), &x.config, &x.hostConfig, &networktypes.NetworkingConfig{}, nil, "")
|
|
| 1930 | 1930 |
if len(x.msg) > 0 {
|
| 1931 | 1931 |
assert.ErrorContains(c, err, x.msg, "%v", cases[i].config) |
| 1932 | 1932 |
} else {
|
| ... | ... |
@@ -1959,7 +1959,7 @@ func (s *DockerSuite) TestContainerAPICreateMountsBindRead(c *testing.T) {
|
| 1959 | 1959 |
assert.NilError(c, err) |
| 1960 | 1960 |
defer cli.Close() |
| 1961 | 1961 |
|
| 1962 |
- _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "test")
|
|
| 1962 |
+ _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "test")
|
|
| 1963 | 1963 |
assert.NilError(c, err) |
| 1964 | 1964 |
|
| 1965 | 1965 |
out, _ := dockerCmd(c, "start", "-a", "test") |
| ... | ... |
@@ -2106,6 +2106,7 @@ func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *testing.T) {
|
| 2106 | 2106 |
&containertypes.Config{Image: testImg},
|
| 2107 | 2107 |
&containertypes.HostConfig{Mounts: []mounttypes.Mount{x.spec}},
|
| 2108 | 2108 |
&networktypes.NetworkingConfig{},
|
| 2109 |
+ nil, |
|
| 2109 | 2110 |
"") |
| 2110 | 2111 |
assert.NilError(c, err) |
| 2111 | 2112 |
|
| ... | ... |
@@ -2213,7 +2214,7 @@ func (s *DockerSuite) TestContainersAPICreateMountsTmpfs(c *testing.T) {
|
| 2213 | 2213 |
Mounts: []mounttypes.Mount{x.cfg},
|
| 2214 | 2214 |
} |
| 2215 | 2215 |
|
| 2216 |
- _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, cName)
|
|
| 2216 |
+ _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, cName)
|
|
| 2217 | 2217 |
assert.NilError(c, err) |
| 2218 | 2218 |
out, _ := dockerCmd(c, "start", "-a", cName) |
| 2219 | 2219 |
for _, option := range x.expectedOptions {
|
| ... | ... |
@@ -578,7 +578,7 @@ func (s *DockerSuite) TestDuplicateMountpointsForVolumesFromAndMounts(c *testing |
| 578 | 578 |
}, |
| 579 | 579 |
}, |
| 580 | 580 |
} |
| 581 |
- _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &network.NetworkingConfig{}, "app")
|
|
| 581 |
+ _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &network.NetworkingConfig{}, nil, "app")
|
|
| 582 | 582 |
|
| 583 | 583 |
assert.NilError(c, err) |
| 584 | 584 |
|
| ... | ... |
@@ -10,6 +10,7 @@ import ( |
| 10 | 10 |
|
| 11 | 11 |
"github.com/docker/docker/api/types" |
| 12 | 12 |
"github.com/docker/docker/api/types/container" |
| 13 |
+ containertypes "github.com/docker/docker/api/types/container" |
|
| 13 | 14 |
"github.com/docker/docker/api/types/network" |
| 14 | 15 |
"github.com/docker/docker/api/types/versions" |
| 15 | 16 |
"github.com/docker/docker/client" |
| ... | ... |
@@ -17,6 +18,7 @@ import ( |
| 17 | 17 |
ctr "github.com/docker/docker/integration/internal/container" |
| 18 | 18 |
"github.com/docker/docker/oci" |
| 19 | 19 |
"github.com/docker/docker/testutil/request" |
| 20 |
+ specs "github.com/opencontainers/image-spec/specs-go/v1" |
|
| 20 | 21 |
"gotest.tools/v3/assert" |
| 21 | 22 |
is "gotest.tools/v3/assert/cmp" |
| 22 | 23 |
"gotest.tools/v3/poll" |
| ... | ... |
@@ -57,6 +59,7 @@ func TestCreateFailsWhenIdentifierDoesNotExist(t *testing.T) {
|
| 57 | 57 |
&container.Config{Image: tc.image},
|
| 58 | 58 |
&container.HostConfig{},
|
| 59 | 59 |
&network.NetworkingConfig{},
|
| 60 |
+ nil, |
|
| 60 | 61 |
"", |
| 61 | 62 |
) |
| 62 | 63 |
assert.Check(t, is.ErrorContains(err, tc.expectedError)) |
| ... | ... |
@@ -81,6 +84,7 @@ func TestCreateLinkToNonExistingContainer(t *testing.T) {
|
| 81 | 81 |
Links: []string{"no-such-container"},
|
| 82 | 82 |
}, |
| 83 | 83 |
&network.NetworkingConfig{},
|
| 84 |
+ nil, |
|
| 84 | 85 |
"", |
| 85 | 86 |
) |
| 86 | 87 |
assert.Check(t, is.ErrorContains(err, "could not get container for no-such-container")) |
| ... | ... |
@@ -120,6 +124,7 @@ func TestCreateWithInvalidEnv(t *testing.T) {
|
| 120 | 120 |
}, |
| 121 | 121 |
&container.HostConfig{},
|
| 122 | 122 |
&network.NetworkingConfig{},
|
| 123 |
+ nil, |
|
| 123 | 124 |
"", |
| 124 | 125 |
) |
| 125 | 126 |
assert.Check(t, is.ErrorContains(err, tc.expectedError)) |
| ... | ... |
@@ -166,6 +171,7 @@ func TestCreateTmpfsMountsTarget(t *testing.T) {
|
| 166 | 166 |
Tmpfs: map[string]string{tc.target: ""},
|
| 167 | 167 |
}, |
| 168 | 168 |
&network.NetworkingConfig{},
|
| 169 |
+ nil, |
|
| 169 | 170 |
"", |
| 170 | 171 |
) |
| 171 | 172 |
assert.Check(t, is.ErrorContains(err, tc.expectedError)) |
| ... | ... |
@@ -235,6 +241,7 @@ func TestCreateWithCustomMaskedPaths(t *testing.T) {
|
| 235 | 235 |
&config, |
| 236 | 236 |
&hc, |
| 237 | 237 |
&network.NetworkingConfig{},
|
| 238 |
+ nil, |
|
| 238 | 239 |
name, |
| 239 | 240 |
) |
| 240 | 241 |
assert.NilError(t, err) |
| ... | ... |
@@ -361,6 +368,7 @@ func TestCreateWithCapabilities(t *testing.T) {
|
| 361 | 361 |
&container.Config{Image: "busybox"},
|
| 362 | 362 |
&tc.hostConfig, |
| 363 | 363 |
&network.NetworkingConfig{},
|
| 364 |
+ nil, |
|
| 364 | 365 |
"", |
| 365 | 366 |
) |
| 366 | 367 |
if tc.expectedError == "" {
|
| ... | ... |
@@ -439,6 +447,7 @@ func TestCreateWithCustomReadonlyPaths(t *testing.T) {
|
| 439 | 439 |
&config, |
| 440 | 440 |
&hc, |
| 441 | 441 |
&network.NetworkingConfig{},
|
| 442 |
+ nil, |
|
| 442 | 443 |
name, |
| 443 | 444 |
) |
| 444 | 445 |
assert.NilError(t, err) |
| ... | ... |
@@ -522,7 +531,7 @@ func TestCreateWithInvalidHealthcheckParams(t *testing.T) {
|
| 522 | 522 |
cfg.Healthcheck.StartPeriod = tc.startPeriod |
| 523 | 523 |
} |
| 524 | 524 |
|
| 525 |
- resp, err := client.ContainerCreate(ctx, &cfg, &container.HostConfig{}, nil, "")
|
|
| 525 |
+ resp, err := client.ContainerCreate(ctx, &cfg, &container.HostConfig{}, nil, nil, "")
|
|
| 526 | 526 |
assert.Check(t, is.Equal(len(resp.Warnings), 0)) |
| 527 | 527 |
|
| 528 | 528 |
if versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") {
|
| ... | ... |
@@ -581,3 +590,34 @@ func TestCreateTmpfsOverrideAnonymousVolume(t *testing.T) {
|
| 581 | 581 |
assert.NilError(t, err) |
| 582 | 582 |
} |
| 583 | 583 |
} |
| 584 |
+ |
|
| 585 |
+// Test that if the referenced image platform does not match the requested platform on container create that we get an |
|
| 586 |
+// error. |
|
| 587 |
+func TestCreateDifferentPlatform(t *testing.T) {
|
|
| 588 |
+ defer setupTest(t)() |
|
| 589 |
+ c := testEnv.APIClient() |
|
| 590 |
+ ctx := context.Background() |
|
| 591 |
+ |
|
| 592 |
+ img, _, err := c.ImageInspectWithRaw(ctx, "busybox:latest") |
|
| 593 |
+ assert.NilError(t, err) |
|
| 594 |
+ assert.Assert(t, img.Architecture != "") |
|
| 595 |
+ |
|
| 596 |
+ t.Run("different os", func(t *testing.T) {
|
|
| 597 |
+ p := specs.Platform{
|
|
| 598 |
+ OS: img.Os + "DifferentOS", |
|
| 599 |
+ Architecture: img.Architecture, |
|
| 600 |
+ Variant: img.Variant, |
|
| 601 |
+ } |
|
| 602 |
+ _, err := c.ContainerCreate(ctx, &containertypes.Config{Image: "busybox:latest"}, &containertypes.HostConfig{}, nil, &p, "")
|
|
| 603 |
+ assert.Assert(t, client.IsErrNotFound(err), err) |
|
| 604 |
+ }) |
|
| 605 |
+ t.Run("different cpu arch", func(t *testing.T) {
|
|
| 606 |
+ p := specs.Platform{
|
|
| 607 |
+ OS: img.Os, |
|
| 608 |
+ Architecture: img.Architecture + "DifferentArch", |
|
| 609 |
+ Variant: img.Variant, |
|
| 610 |
+ } |
|
| 611 |
+ _, err := c.ContainerCreate(ctx, &containertypes.Config{Image: "busybox:latest"}, &containertypes.HostConfig{}, nil, &p, "")
|
|
| 612 |
+ assert.Assert(t, client.IsErrNotFound(err), err) |
|
| 613 |
+ }) |
|
| 614 |
+} |
| ... | ... |
@@ -66,7 +66,7 @@ func testIpcNonePrivateShareable(t *testing.T, mode string, mustBeMounted bool, |
| 66 | 66 |
client := testEnv.APIClient() |
| 67 | 67 |
ctx := context.Background() |
| 68 | 68 |
|
| 69 |
- resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "") |
|
| 69 |
+ resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "") |
|
| 70 | 70 |
assert.NilError(t, err) |
| 71 | 71 |
assert.Check(t, is.Equal(len(resp.Warnings), 0)) |
| 72 | 72 |
|
| ... | ... |
@@ -138,7 +138,7 @@ func testIpcContainer(t *testing.T, donorMode string, mustWork bool) {
|
| 138 | 138 |
client := testEnv.APIClient() |
| 139 | 139 |
|
| 140 | 140 |
// create and start the "donor" container |
| 141 |
- resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "") |
|
| 141 |
+ resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "") |
|
| 142 | 142 |
assert.NilError(t, err) |
| 143 | 143 |
assert.Check(t, is.Equal(len(resp.Warnings), 0)) |
| 144 | 144 |
name1 := resp.ID |
| ... | ... |
@@ -148,7 +148,7 @@ func testIpcContainer(t *testing.T, donorMode string, mustWork bool) {
|
| 148 | 148 |
|
| 149 | 149 |
// create and start the second container |
| 150 | 150 |
hostCfg.IpcMode = containertypes.IpcMode("container:" + name1)
|
| 151 |
- resp, err = client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "") |
|
| 151 |
+ resp, err = client.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "") |
|
| 152 | 152 |
assert.NilError(t, err) |
| 153 | 153 |
assert.Check(t, is.Equal(len(resp.Warnings), 0)) |
| 154 | 154 |
name2 := resp.ID |
| ... | ... |
@@ -204,7 +204,7 @@ func TestAPIIpcModeHost(t *testing.T) {
|
| 204 | 204 |
ctx := context.Background() |
| 205 | 205 |
|
| 206 | 206 |
client := testEnv.APIClient() |
| 207 |
- resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "") |
|
| 207 |
+ resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "") |
|
| 208 | 208 |
assert.NilError(t, err) |
| 209 | 209 |
assert.Check(t, is.Equal(len(resp.Warnings), 0)) |
| 210 | 210 |
name := resp.ID |
| ... | ... |
@@ -241,7 +241,7 @@ func testDaemonIpcPrivateShareable(t *testing.T, mustBeShared bool, arg ...strin |
| 241 | 241 |
} |
| 242 | 242 |
ctx := context.Background() |
| 243 | 243 |
|
| 244 |
- resp, err := c.ContainerCreate(ctx, &cfg, &containertypes.HostConfig{}, nil, "")
|
|
| 244 |
+ resp, err := c.ContainerCreate(ctx, &cfg, &containertypes.HostConfig{}, nil, nil, "")
|
|
| 245 | 245 |
assert.NilError(t, err) |
| 246 | 246 |
assert.Check(t, is.Equal(len(resp.Warnings), 0)) |
| 247 | 247 |
|
| ... | ... |
@@ -63,7 +63,7 @@ func TestContainerNetworkMountsNoChown(t *testing.T) {
|
| 63 | 63 |
assert.NilError(t, err) |
| 64 | 64 |
defer cli.Close() |
| 65 | 65 |
|
| 66 |
- ctrCreate, err := cli.ContainerCreate(ctx, &config, &hostConfig, &network.NetworkingConfig{}, "")
|
|
| 66 |
+ ctrCreate, err := cli.ContainerCreate(ctx, &config, &hostConfig, &network.NetworkingConfig{}, nil, "")
|
|
| 67 | 67 |
assert.NilError(t, err) |
| 68 | 68 |
// container will exit immediately because of no tty, but we only need the start sequence to test the condition |
| 69 | 69 |
err = cli.ContainerStart(ctx, ctrCreate.ID, types.ContainerStartOptions{})
|
| ... | ... |
@@ -174,7 +174,7 @@ func TestMountDaemonRoot(t *testing.T) {
|
| 174 | 174 |
c, err := client.ContainerCreate(ctx, &containertypes.Config{
|
| 175 | 175 |
Image: "busybox", |
| 176 | 176 |
Cmd: []string{"true"},
|
| 177 |
- }, hc, nil, "") |
|
| 177 |
+ }, hc, nil, nil, "") |
|
| 178 | 178 |
|
| 179 | 179 |
if err != nil {
|
| 180 | 180 |
if test.expected != "" {
|
| ... | ... |
@@ -77,7 +77,7 @@ func TestDaemonRestartKillContainers(t *testing.T) {
|
| 77 | 77 |
defer d.Stop(t) |
| 78 | 78 |
ctx := context.Background() |
| 79 | 79 |
|
| 80 |
- resp, err := client.ContainerCreate(ctx, c.config, c.hostConfig, nil, "") |
|
| 80 |
+ resp, err := client.ContainerCreate(ctx, c.config, c.hostConfig, nil, nil, "") |
|
| 81 | 81 |
assert.NilError(t, err) |
| 82 | 82 |
defer client.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{Force: true})
|
| 83 | 83 |
|
| ... | ... |
@@ -9,6 +9,7 @@ import ( |
| 9 | 9 |
"github.com/docker/docker/api/types/container" |
| 10 | 10 |
"github.com/docker/docker/api/types/network" |
| 11 | 11 |
"github.com/docker/docker/client" |
| 12 |
+ specs "github.com/opencontainers/image-spec/specs-go/v1" |
|
| 12 | 13 |
"gotest.tools/v3/assert" |
| 13 | 14 |
) |
| 14 | 15 |
|
| ... | ... |
@@ -19,6 +20,7 @@ type TestContainerConfig struct {
|
| 19 | 19 |
Config *container.Config |
| 20 | 20 |
HostConfig *container.HostConfig |
| 21 | 21 |
NetworkingConfig *network.NetworkingConfig |
| 22 |
+ Platform *specs.Platform |
|
| 22 | 23 |
} |
| 23 | 24 |
|
| 24 | 25 |
// 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 |
| 41 | 41 |
op(config) |
| 42 | 42 |
} |
| 43 | 43 |
|
| 44 |
- return client.ContainerCreate(ctx, config.Config, config.HostConfig, config.NetworkingConfig, config.Name) |
|
| 44 |
+ return client.ContainerCreate(ctx, config.Config, config.HostConfig, config.NetworkingConfig, config.Platform, config.Name) |
|
| 45 | 45 |
} |
| 46 | 46 |
|
| 47 | 47 |
// Create creates a container with the specified options, asserting that there was no error |
| ... | ... |
@@ -9,6 +9,7 @@ import ( |
| 9 | 9 |
networktypes "github.com/docker/docker/api/types/network" |
| 10 | 10 |
"github.com/docker/docker/api/types/strslice" |
| 11 | 11 |
"github.com/docker/go-connections/nat" |
| 12 |
+ specs "github.com/opencontainers/image-spec/specs-go/v1" |
|
| 12 | 13 |
) |
| 13 | 14 |
|
| 14 | 15 |
// WithName sets the name of the container |
| ... | ... |
@@ -205,3 +206,10 @@ func WithExtraHost(extraHost string) func(*TestContainerConfig) {
|
| 205 | 205 |
c.HostConfig.ExtraHosts = append(c.HostConfig.ExtraHosts, extraHost) |
| 206 | 206 |
} |
| 207 | 207 |
} |
| 208 |
+ |
|
| 209 |
+// WithPlatform specifies the desired platform the image should have. |
|
| 210 |
+func WithPlatform(p *specs.Platform) func(*TestContainerConfig) {
|
|
| 211 |
+ return func(c *TestContainerConfig) {
|
|
| 212 |
+ c.Platform = p |
|
| 213 |
+ } |
|
| 214 |
+} |
| ... | ... |
@@ -155,7 +155,7 @@ COPY . /static`); err != nil {
|
| 155 | 155 |
// Start the container |
| 156 | 156 |
b, err := c.ContainerCreate(context.Background(), &containertypes.Config{
|
| 157 | 157 |
Image: image, |
| 158 |
- }, &containertypes.HostConfig{}, nil, container)
|
|
| 158 |
+ }, &containertypes.HostConfig{}, nil, nil, container)
|
|
| 159 | 159 |
assert.NilError(t, err) |
| 160 | 160 |
err = c.ContainerStart(context.Background(), b.ID, types.ContainerStartOptions{})
|
| 161 | 161 |
assert.NilError(t, err) |