Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
| ... | ... |
@@ -16,8 +16,23 @@ import ( |
| 16 | 16 |
// ContainerCreate creates a new container based on the given configuration. |
| 17 | 17 |
// It can be associated with a name, but it's not mandatory. |
| 18 | 18 |
func (cli *Client) ContainerCreate(ctx context.Context, options ContainerCreateOptions) (ContainerCreateResult, error) {
|
| 19 |
- if options.Config == nil {
|
|
| 20 |
- return ContainerCreateResult{}, cerrdefs.ErrInvalidArgument.WithMessage("config is nil")
|
|
| 19 |
+ cfg := options.Config |
|
| 20 |
+ |
|
| 21 |
+ if cfg == nil {
|
|
| 22 |
+ cfg = &container.Config{}
|
|
| 23 |
+ } |
|
| 24 |
+ |
|
| 25 |
+ if options.Image != "" {
|
|
| 26 |
+ if cfg.Image != "" {
|
|
| 27 |
+ return ContainerCreateResult{}, cerrdefs.ErrInvalidArgument.WithMessage("either Image or config.Image should be set")
|
|
| 28 |
+ } |
|
| 29 |
+ newCfg := *cfg |
|
| 30 |
+ newCfg.Image = options.Image |
|
| 31 |
+ cfg = &newCfg |
|
| 32 |
+ } |
|
| 33 |
+ |
|
| 34 |
+ if cfg.Image == "" {
|
|
| 35 |
+ return ContainerCreateResult{}, cerrdefs.ErrInvalidArgument.WithMessage("config.Image or Image is required")
|
|
| 21 | 36 |
} |
| 22 | 37 |
|
| 23 | 38 |
var response container.CreateResponse |
| ... | ... |
@@ -39,7 +54,7 @@ func (cli *Client) ContainerCreate(ctx context.Context, options ContainerCreateO |
| 39 | 39 |
} |
| 40 | 40 |
|
| 41 | 41 |
body := container.CreateRequest{
|
| 42 |
- Config: options.Config, |
|
| 42 |
+ Config: cfg, |
|
| 43 | 43 |
HostConfig: options.HostConfig, |
| 44 | 44 |
NetworkingConfig: options.NetworkingConfig, |
| 45 | 45 |
} |
| ... | ... |
@@ -13,6 +13,9 @@ type ContainerCreateOptions struct {
|
| 13 | 13 |
NetworkingConfig *network.NetworkingConfig |
| 14 | 14 |
Platform *ocispec.Platform |
| 15 | 15 |
Name string |
| 16 |
+ |
|
| 17 |
+ // Image is a shortcut for Config.Image - only one of Image or Config.Image should be set. |
|
| 18 |
+ Image string |
|
| 16 | 19 |
} |
| 17 | 20 |
|
| 18 | 21 |
// ContainerCreateResult is the result from creating a container. |
| ... | ... |
@@ -23,20 +23,11 @@ func TestContainerCreateError(t *testing.T) {
|
| 23 | 23 |
assert.NilError(t, err) |
| 24 | 24 |
|
| 25 | 25 |
_, err = client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: nil, Name: "nothing"})
|
| 26 |
- assert.Error(t, err, "config is nil") |
|
| 26 |
+ assert.Error(t, err, "config.Image or Image is required") |
|
| 27 | 27 |
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument)) |
| 28 | 28 |
|
| 29 | 29 |
_, err = client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{}, Name: "nothing"})
|
| 30 |
- assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal)) |
|
| 31 |
- |
|
| 32 |
- // 404 doesn't automatically means an unknown image |
|
| 33 |
- client, err = NewClientWithOpts( |
|
| 34 |
- WithMockClient(errorMock(http.StatusNotFound, "Server error")), |
|
| 35 |
- ) |
|
| 36 |
- assert.NilError(t, err) |
|
| 37 |
- |
|
| 38 |
- _, err = client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{}, Name: "nothing"})
|
|
| 39 |
- assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound)) |
|
| 30 |
+ assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument)) |
|
| 40 | 31 |
} |
| 41 | 32 |
|
| 42 | 33 |
func TestContainerCreateImageNotFound(t *testing.T) {
|
| ... | ... |
@@ -74,7 +65,7 @@ func TestContainerCreateWithName(t *testing.T) {
|
| 74 | 74 |
) |
| 75 | 75 |
assert.NilError(t, err) |
| 76 | 76 |
|
| 77 |
- r, err := client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{}, Name: "container_name"})
|
|
| 77 |
+ r, err := client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{Image: "test"}, Name: "container_name"})
|
|
| 78 | 78 |
assert.NilError(t, err) |
| 79 | 79 |
assert.Check(t, is.Equal(r.ID, "container_id")) |
| 80 | 80 |
} |
| ... | ... |
@@ -103,7 +94,7 @@ func TestContainerCreateAutoRemove(t *testing.T) {
|
| 103 | 103 |
) |
| 104 | 104 |
assert.NilError(t, err) |
| 105 | 105 |
|
| 106 |
- resp, err := client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{}, HostConfig: &container.HostConfig{AutoRemove: true}})
|
|
| 106 |
+ resp, err := client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{Image: "test"}, HostConfig: &container.HostConfig{AutoRemove: true}})
|
|
| 107 | 107 |
assert.NilError(t, err) |
| 108 | 108 |
assert.Check(t, is.Equal(resp.ID, "container_id")) |
| 109 | 109 |
} |
| ... | ... |
@@ -116,7 +107,7 @@ func TestContainerCreateConnectionError(t *testing.T) {
|
| 116 | 116 |
client, err := NewClientWithOpts(WithAPIVersionNegotiation(), WithHost("tcp://no-such-host.invalid"))
|
| 117 | 117 |
assert.NilError(t, err) |
| 118 | 118 |
|
| 119 |
- _, err = client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{}})
|
|
| 119 |
+ _, err = client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{Image: "test"}})
|
|
| 120 | 120 |
assert.Check(t, is.ErrorType(err, IsErrConnectionFailed)) |
| 121 | 121 |
} |
| 122 | 122 |
|
| ... | ... |
@@ -165,6 +156,6 @@ func TestContainerCreateCapabilities(t *testing.T) {
|
| 165 | 165 |
) |
| 166 | 166 |
assert.NilError(t, err) |
| 167 | 167 |
|
| 168 |
- _, err = client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{}, HostConfig: &container.HostConfig{CapAdd: inputCaps, CapDrop: inputCaps}})
|
|
| 168 |
+ _, err = client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{Image: "test"}, HostConfig: &container.HostConfig{CapAdd: inputCaps, CapDrop: inputCaps}})
|
|
| 169 | 169 |
assert.NilError(t, err) |
| 170 | 170 |
} |
| ... | ... |
@@ -557,7 +557,7 @@ func (s *DockerAPISuite) TestContainerAPICreateEmptyConfig(c *testing.T) {
|
| 557 | 557 |
NetworkingConfig: &network.NetworkingConfig{},
|
| 558 | 558 |
}) |
| 559 | 559 |
|
| 560 |
- assert.ErrorContains(c, err, "no command specified") |
|
| 560 |
+ assert.ErrorContains(c, err, "config.Image or Image is required") |
|
| 561 | 561 |
} |
| 562 | 562 |
|
| 563 | 563 |
func (s *DockerAPISuite) TestContainerAPICreateBridgeNetworkMode(c *testing.T) {
|
| ... | ... |
@@ -16,8 +16,23 @@ import ( |
| 16 | 16 |
// ContainerCreate creates a new container based on the given configuration. |
| 17 | 17 |
// It can be associated with a name, but it's not mandatory. |
| 18 | 18 |
func (cli *Client) ContainerCreate(ctx context.Context, options ContainerCreateOptions) (ContainerCreateResult, error) {
|
| 19 |
- if options.Config == nil {
|
|
| 20 |
- return ContainerCreateResult{}, cerrdefs.ErrInvalidArgument.WithMessage("config is nil")
|
|
| 19 |
+ cfg := options.Config |
|
| 20 |
+ |
|
| 21 |
+ if cfg == nil {
|
|
| 22 |
+ cfg = &container.Config{}
|
|
| 23 |
+ } |
|
| 24 |
+ |
|
| 25 |
+ if options.Image != "" {
|
|
| 26 |
+ if cfg.Image != "" {
|
|
| 27 |
+ return ContainerCreateResult{}, cerrdefs.ErrInvalidArgument.WithMessage("either Image or config.Image should be set")
|
|
| 28 |
+ } |
|
| 29 |
+ newCfg := *cfg |
|
| 30 |
+ newCfg.Image = options.Image |
|
| 31 |
+ cfg = &newCfg |
|
| 32 |
+ } |
|
| 33 |
+ |
|
| 34 |
+ if cfg.Image == "" {
|
|
| 35 |
+ return ContainerCreateResult{}, cerrdefs.ErrInvalidArgument.WithMessage("config.Image or Image is required")
|
|
| 21 | 36 |
} |
| 22 | 37 |
|
| 23 | 38 |
var response container.CreateResponse |
| ... | ... |
@@ -39,7 +54,7 @@ func (cli *Client) ContainerCreate(ctx context.Context, options ContainerCreateO |
| 39 | 39 |
} |
| 40 | 40 |
|
| 41 | 41 |
body := container.CreateRequest{
|
| 42 |
- Config: options.Config, |
|
| 42 |
+ Config: cfg, |
|
| 43 | 43 |
HostConfig: options.HostConfig, |
| 44 | 44 |
NetworkingConfig: options.NetworkingConfig, |
| 45 | 45 |
} |
| ... | ... |
@@ -13,6 +13,9 @@ type ContainerCreateOptions struct {
|
| 13 | 13 |
NetworkingConfig *network.NetworkingConfig |
| 14 | 14 |
Platform *ocispec.Platform |
| 15 | 15 |
Name string |
| 16 |
+ |
|
| 17 |
+ // Image is a shortcut for Config.Image - only one of Image or Config.Image should be set. |
|
| 18 |
+ Image string |
|
| 16 | 19 |
} |
| 17 | 20 |
|
| 18 | 21 |
// ContainerCreateResult is the result from creating a container. |