Browse code

client_(attach,commit,create,diff): Wrap result and options

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>

Paweł Gronowski authored on 2025/10/23 21:20:05
Showing 32 changed files
... ...
@@ -12,7 +12,6 @@ import (
12 12
 	"github.com/moby/moby/api/types/registry"
13 13
 	"github.com/moby/moby/api/types/swarm"
14 14
 	"github.com/moby/moby/api/types/system"
15
-	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
16 15
 )
17 16
 
18 17
 // APIClient is an interface that clients that talk with a docker server must implement.
... ...
@@ -58,10 +57,10 @@ type HijackDialer interface {
58 58
 
59 59
 // ContainerAPIClient defines API client methods for the containers
60 60
 type ContainerAPIClient interface {
61
-	ContainerAttach(ctx context.Context, container string, options ContainerAttachOptions) (HijackedResponse, error)
62
-	ContainerCommit(ctx context.Context, container string, options ContainerCommitOptions) (container.CommitResponse, error)
63
-	ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error)
64
-	ContainerDiff(ctx context.Context, container string) ([]container.FilesystemChange, error)
61
+	ContainerAttach(ctx context.Context, container string, options ContainerAttachOptions) (ContainerAttachResult, error)
62
+	ContainerCommit(ctx context.Context, container string, options ContainerCommitOptions) (ContainerCommitResult, error)
63
+	ContainerCreate(ctx context.Context, options ContainerCreateOptions) (ContainerCreateResult, error)
64
+	ContainerDiff(ctx context.Context, container string, options ContainerDiffOptions) (ContainerDiffResult, error)
65 65
 	ExecAPIClient
66 66
 	ContainerExport(ctx context.Context, container string) (io.ReadCloser, error)
67 67
 	ContainerInspect(ctx context.Context, container string) (container.InspectResponse, error)
... ...
@@ -16,6 +16,11 @@ type ContainerAttachOptions struct {
16 16
 	Logs       bool
17 17
 }
18 18
 
19
+// ContainerAttachResult is the result from attaching to a container.
20
+type ContainerAttachResult struct {
21
+	HijackedResponse
22
+}
23
+
19 24
 // ContainerAttach attaches a connection to a container in the server.
20 25
 // It returns a [HijackedResponse] with the hijacked connection
21 26
 // and a reader to get output. It's up to the called to close
... ...
@@ -44,10 +49,10 @@ type ContainerAttachOptions struct {
44 44
 // [stdcopy.StdType]: https://pkg.go.dev/github.com/moby/moby/api/pkg/stdcopy#StdType
45 45
 // [Stdout]: https://pkg.go.dev/github.com/moby/moby/api/pkg/stdcopy#Stdout
46 46
 // [Stderr]: https://pkg.go.dev/github.com/moby/moby/api/pkg/stdcopy#Stderr
47
-func (cli *Client) ContainerAttach(ctx context.Context, containerID string, options ContainerAttachOptions) (HijackedResponse, error) {
47
+func (cli *Client) ContainerAttach(ctx context.Context, containerID string, options ContainerAttachOptions) (ContainerAttachResult, error) {
48 48
 	containerID, err := trimID("container", containerID)
49 49
 	if err != nil {
50
-		return HijackedResponse{}, err
50
+		return ContainerAttachResult{}, err
51 51
 	}
52 52
 
53 53
 	query := url.Values{}
... ...
@@ -70,7 +75,12 @@ func (cli *Client) ContainerAttach(ctx context.Context, containerID string, opti
70 70
 		query.Set("logs", "1")
71 71
 	}
72 72
 
73
-	return cli.postHijacked(ctx, "/containers/"+containerID+"/attach", query, nil, http.Header{
73
+	hijacked, err := cli.postHijacked(ctx, "/containers/"+containerID+"/attach", query, nil, http.Header{
74 74
 		"Content-Type": {"text/plain"},
75 75
 	})
76
+	if err != nil {
77
+		return ContainerAttachResult{}, err
78
+	}
79
+
80
+	return ContainerAttachResult{HijackedResponse: hijacked}, nil
76 81
 }
... ...
@@ -20,22 +20,27 @@ type ContainerCommitOptions struct {
20 20
 	Config    *container.Config
21 21
 }
22 22
 
23
+// ContainerCommitResult is the result from committing a container.
24
+type ContainerCommitResult struct {
25
+	ID string
26
+}
27
+
23 28
 // ContainerCommit applies changes to a container and creates a new tagged image.
24
-func (cli *Client) ContainerCommit(ctx context.Context, containerID string, options ContainerCommitOptions) (container.CommitResponse, error) {
29
+func (cli *Client) ContainerCommit(ctx context.Context, containerID string, options ContainerCommitOptions) (ContainerCommitResult, error) {
25 30
 	containerID, err := trimID("container", containerID)
26 31
 	if err != nil {
27
-		return container.CommitResponse{}, err
32
+		return ContainerCommitResult{}, err
28 33
 	}
29 34
 
30 35
 	var repository, tag string
31 36
 	if options.Reference != "" {
32 37
 		ref, err := reference.ParseNormalizedNamed(options.Reference)
33 38
 		if err != nil {
34
-			return container.CommitResponse{}, err
39
+			return ContainerCommitResult{}, err
35 40
 		}
36 41
 
37 42
 		if _, ok := ref.(reference.Digested); ok {
38
-			return container.CommitResponse{}, errors.New("refusing to create a tag with a digest reference")
43
+			return ContainerCommitResult{}, errors.New("refusing to create a tag with a digest reference")
39 44
 		}
40 45
 		ref = reference.TagNameOnly(ref)
41 46
 
... ...
@@ -62,9 +67,9 @@ func (cli *Client) ContainerCommit(ctx context.Context, containerID string, opti
62 62
 	resp, err := cli.post(ctx, "/commit", query, options.Config, nil)
63 63
 	defer ensureReaderClosed(resp)
64 64
 	if err != nil {
65
-		return response, err
65
+		return ContainerCommitResult{}, err
66 66
 	}
67 67
 
68 68
 	err = json.NewDecoder(resp.Body).Decode(&response)
69
-	return response, err
69
+	return ContainerCommitResult{ID: response.ID}, err
70 70
 }
... ...
@@ -10,49 +10,48 @@ import (
10 10
 
11 11
 	cerrdefs "github.com/containerd/errdefs"
12 12
 	"github.com/moby/moby/api/types/container"
13
-	"github.com/moby/moby/api/types/network"
14 13
 	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
15 14
 )
16 15
 
17 16
 // ContainerCreate creates a new container based on the given configuration.
18 17
 // It can be associated with a name, but it's not mandatory.
19
-func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error) {
20
-	if config == nil {
21
-		return container.CreateResponse{}, cerrdefs.ErrInvalidArgument.WithMessage("config is nil")
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")
22 21
 	}
23 22
 
24 23
 	var response container.CreateResponse
25 24
 
26
-	if hostConfig != nil {
27
-		hostConfig.CapAdd = normalizeCapabilities(hostConfig.CapAdd)
28
-		hostConfig.CapDrop = normalizeCapabilities(hostConfig.CapDrop)
25
+	if options.HostConfig != nil {
26
+		options.HostConfig.CapAdd = normalizeCapabilities(options.HostConfig.CapAdd)
27
+		options.HostConfig.CapDrop = normalizeCapabilities(options.HostConfig.CapDrop)
29 28
 	}
30 29
 
31 30
 	query := url.Values{}
32
-	if platform != nil {
33
-		if p := formatPlatform(*platform); p != "unknown" {
31
+	if options.Platform != nil {
32
+		if p := formatPlatform(*options.Platform); p != "unknown" {
34 33
 			query.Set("platform", p)
35 34
 		}
36 35
 	}
37 36
 
38
-	if containerName != "" {
39
-		query.Set("name", containerName)
37
+	if options.ContainerName != "" {
38
+		query.Set("name", options.ContainerName)
40 39
 	}
41 40
 
42 41
 	body := container.CreateRequest{
43
-		Config:           config,
44
-		HostConfig:       hostConfig,
45
-		NetworkingConfig: networkingConfig,
42
+		Config:           options.Config,
43
+		HostConfig:       options.HostConfig,
44
+		NetworkingConfig: options.NetworkingConfig,
46 45
 	}
47 46
 
48 47
 	resp, err := cli.post(ctx, "/containers/create", query, body, nil)
49 48
 	defer ensureReaderClosed(resp)
50 49
 	if err != nil {
51
-		return response, err
50
+		return ContainerCreateResult{}, err
52 51
 	}
53 52
 
54 53
 	err = json.NewDecoder(resp.Body).Decode(&response)
55
-	return response, err
54
+	return ContainerCreateResult{ID: response.ID, Warnings: response.Warnings}, err
56 55
 }
57 56
 
58 57
 // formatPlatform returns a formatted string representing platform (e.g., "linux/arm/v7").
59 58
new file mode 100644
... ...
@@ -0,0 +1,22 @@
0
+package client
1
+
2
+import (
3
+	"github.com/moby/moby/api/types/container"
4
+	"github.com/moby/moby/api/types/network"
5
+	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
6
+)
7
+
8
+// ContainerCreateOptions holds parameters to create a container.
9
+type ContainerCreateOptions struct {
10
+	Config           *container.Config
11
+	HostConfig       *container.HostConfig
12
+	NetworkingConfig *network.NetworkingConfig
13
+	Platform         *ocispec.Platform
14
+	ContainerName    string
15
+}
16
+
17
+// ContainerCreateResult is the result from creating a container.
18
+type ContainerCreateResult struct {
19
+	ID       string
20
+	Warnings []string
21
+}
... ...
@@ -22,11 +22,11 @@ func TestContainerCreateError(t *testing.T) {
22 22
 	)
23 23
 	assert.NilError(t, err)
24 24
 
25
-	_, err = client.ContainerCreate(context.Background(), nil, nil, nil, nil, "nothing")
25
+	_, err = client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: nil, ContainerName: "nothing"})
26 26
 	assert.Error(t, err, "config is nil")
27 27
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
28 28
 
29
-	_, err = client.ContainerCreate(context.Background(), &container.Config{}, nil, nil, nil, "nothing")
29
+	_, err = client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{}, ContainerName: "nothing"})
30 30
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
31 31
 
32 32
 	// 404 doesn't automatically means an unknown image
... ...
@@ -35,7 +35,7 @@ func TestContainerCreateError(t *testing.T) {
35 35
 	)
36 36
 	assert.NilError(t, err)
37 37
 
38
-	_, err = client.ContainerCreate(context.Background(), &container.Config{}, nil, nil, nil, "nothing")
38
+	_, err = client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{}, ContainerName: "nothing"})
39 39
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
40 40
 }
41 41
 
... ...
@@ -45,7 +45,7 @@ func TestContainerCreateImageNotFound(t *testing.T) {
45 45
 	)
46 46
 	assert.NilError(t, err)
47 47
 
48
-	_, err = client.ContainerCreate(context.Background(), &container.Config{Image: "unknown_image"}, nil, nil, nil, "unknown")
48
+	_, err = client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{Image: "unknown_image"}, ContainerName: "unknown"})
49 49
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
50 50
 }
51 51
 
... ...
@@ -74,7 +74,7 @@ func TestContainerCreateWithName(t *testing.T) {
74 74
 	)
75 75
 	assert.NilError(t, err)
76 76
 
77
-	r, err := client.ContainerCreate(context.Background(), &container.Config{}, nil, nil, nil, "container_name")
77
+	r, err := client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{}, ContainerName: "container_name"})
78 78
 	assert.NilError(t, err)
79 79
 	assert.Check(t, is.Equal(r.ID, "container_id"))
80 80
 }
... ...
@@ -103,7 +103,7 @@ func TestContainerCreateAutoRemove(t *testing.T) {
103 103
 	)
104 104
 	assert.NilError(t, err)
105 105
 
106
-	resp, err := client.ContainerCreate(context.Background(), &container.Config{}, &container.HostConfig{AutoRemove: true}, nil, nil, "")
106
+	resp, err := client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{}, 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 +116,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(), &container.Config{}, nil, nil, nil, "")
119
+	_, err = client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{}})
120 120
 	assert.Check(t, is.ErrorType(err, IsErrConnectionFailed))
121 121
 }
122 122
 
... ...
@@ -165,6 +165,6 @@ func TestContainerCreateCapabilities(t *testing.T) {
165 165
 	)
166 166
 	assert.NilError(t, err)
167 167
 
168
-	_, err = client.ContainerCreate(context.Background(), &container.Config{}, &container.HostConfig{CapAdd: inputCaps, CapDrop: inputCaps}, nil, nil, "")
168
+	_, err = client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{}, HostConfig: &container.HostConfig{CapAdd: inputCaps, CapDrop: inputCaps}})
169 169
 	assert.NilError(t, err)
170 170
 }
... ...
@@ -9,22 +9,22 @@ import (
9 9
 )
10 10
 
11 11
 // ContainerDiff shows differences in a container filesystem since it was started.
12
-func (cli *Client) ContainerDiff(ctx context.Context, containerID string) ([]container.FilesystemChange, error) {
12
+func (cli *Client) ContainerDiff(ctx context.Context, containerID string, options ContainerDiffOptions) (ContainerDiffResult, error) {
13 13
 	containerID, err := trimID("container", containerID)
14 14
 	if err != nil {
15
-		return nil, err
15
+		return ContainerDiffResult{}, err
16 16
 	}
17 17
 
18 18
 	resp, err := cli.get(ctx, "/containers/"+containerID+"/changes", url.Values{}, nil)
19 19
 	defer ensureReaderClosed(resp)
20 20
 	if err != nil {
21
-		return nil, err
21
+		return ContainerDiffResult{}, err
22 22
 	}
23 23
 
24 24
 	var changes []container.FilesystemChange
25 25
 	err = json.NewDecoder(resp.Body).Decode(&changes)
26 26
 	if err != nil {
27
-		return nil, err
27
+		return ContainerDiffResult{}, err
28 28
 	}
29
-	return changes, err
29
+	return ContainerDiffResult{Changes: changes}, err
30 30
 }
31 31
new file mode 100644
... ...
@@ -0,0 +1,13 @@
0
+package client
1
+
2
+import "github.com/moby/moby/api/types/container"
3
+
4
+// ContainerDiffOptions holds parameters to show differences in a container filesystem.
5
+type ContainerDiffOptions struct {
6
+	// Currently no options, but this allows for future extensibility
7
+}
8
+
9
+// ContainerDiffResult is the result from showing differences in a container filesystem.
10
+type ContainerDiffResult struct {
11
+	Changes []container.FilesystemChange
12
+}
... ...
@@ -20,14 +20,14 @@ func TestContainerDiffError(t *testing.T) {
20 20
 	)
21 21
 	assert.NilError(t, err)
22 22
 
23
-	_, err = client.ContainerDiff(context.Background(), "nothing")
23
+	_, err = client.ContainerDiff(context.Background(), "nothing", ContainerDiffOptions{})
24 24
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
25 25
 
26
-	_, err = client.ContainerDiff(context.Background(), "")
26
+	_, err = client.ContainerDiff(context.Background(), "", ContainerDiffOptions{})
27 27
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
28 28
 	assert.Check(t, is.ErrorContains(err, "value is empty"))
29 29
 
30
-	_, err = client.ContainerDiff(context.Background(), "    ")
30
+	_, err = client.ContainerDiff(context.Background(), "    ", ContainerDiffOptions{})
31 31
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
32 32
 	assert.Check(t, is.ErrorContains(err, "value is empty"))
33 33
 }
... ...
@@ -67,7 +67,7 @@ func TestContainerDiff(t *testing.T) {
67 67
 	)
68 68
 	assert.NilError(t, err)
69 69
 
70
-	changes, err := client.ContainerDiff(context.Background(), "container_id")
70
+	result, err := client.ContainerDiff(context.Background(), "container_id", ContainerDiffOptions{})
71 71
 	assert.NilError(t, err)
72
-	assert.Check(t, is.DeepEqual(changes, expected))
72
+	assert.Check(t, is.DeepEqual(result.Changes, expected))
73 73
 }
... ...
@@ -130,12 +130,12 @@ func (s *DockerAPISuite) TestContainerAPIGetChanges(c *testing.T) {
130 130
 	assert.NilError(c, err)
131 131
 	defer apiClient.Close()
132 132
 
133
-	changes, err := apiClient.ContainerDiff(testutil.GetContext(c), name)
133
+	result, err := apiClient.ContainerDiff(testutil.GetContext(c), name, client.ContainerDiffOptions{})
134 134
 	assert.NilError(c, err)
135 135
 
136 136
 	// Check the changelog for removal of /etc/passwd
137 137
 	success := false
138
-	for _, elem := range changes {
138
+	for _, elem := range result.Changes {
139 139
 		if elem.Path == "/etc/passwd" && elem.Kind == 2 {
140 140
 			success = true
141 141
 		}
... ...
@@ -517,7 +517,11 @@ func (s *DockerAPISuite) TestContainerAPIBadPort(c *testing.T) {
517 517
 	assert.NilError(c, err)
518 518
 	defer apiClient.Close()
519 519
 
520
-	_, err = apiClient.ContainerCreate(testutil.GetContext(c), &config, &hostConfig, &network.NetworkingConfig{}, nil, "")
520
+	_, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{
521
+		Config:           &config,
522
+		HostConfig:       &hostConfig,
523
+		NetworkingConfig: &network.NetworkingConfig{},
524
+	})
521 525
 	assert.ErrorContains(c, err, `invalid port specification: "aa80"`)
522 526
 }
523 527
 
... ...
@@ -531,7 +535,11 @@ func (s *DockerAPISuite) TestContainerAPICreate(c *testing.T) {
531 531
 	assert.NilError(c, err)
532 532
 	defer apiClient.Close()
533 533
 
534
-	ctr, err := apiClient.ContainerCreate(testutil.GetContext(c), &config, &container.HostConfig{}, &network.NetworkingConfig{}, nil, "")
534
+	ctr, err := apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{
535
+		Config:           &config,
536
+		HostConfig:       &container.HostConfig{},
537
+		NetworkingConfig: &network.NetworkingConfig{},
538
+	})
535 539
 	assert.NilError(c, err)
536 540
 
537 541
 	out := cli.DockerCmd(c, "start", "-a", ctr.ID).Stdout()
... ...
@@ -543,7 +551,11 @@ func (s *DockerAPISuite) TestContainerAPICreateEmptyConfig(c *testing.T) {
543 543
 	assert.NilError(c, err)
544 544
 	defer apiClient.Close()
545 545
 
546
-	_, err = apiClient.ContainerCreate(testutil.GetContext(c), &container.Config{}, &container.HostConfig{}, &network.NetworkingConfig{}, nil, "")
546
+	_, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{
547
+		Config:           &container.Config{},
548
+		HostConfig:       &container.HostConfig{},
549
+		NetworkingConfig: &network.NetworkingConfig{},
550
+	})
547 551
 
548 552
 	assert.ErrorContains(c, err, "no command specified")
549 553
 }
... ...
@@ -574,7 +586,11 @@ func UtilCreateNetworkMode(t *testing.T, networkMode container.NetworkMode) {
574 574
 	assert.NilError(t, err)
575 575
 	defer apiClient.Close()
576 576
 
577
-	ctr, err := apiClient.ContainerCreate(testutil.GetContext(t), &config, &hostConfig, &network.NetworkingConfig{}, nil, "")
577
+	ctr, err := apiClient.ContainerCreate(testutil.GetContext(t), client.ContainerCreateOptions{
578
+		Config:           &config,
579
+		HostConfig:       &hostConfig,
580
+		NetworkingConfig: &network.NetworkingConfig{},
581
+	})
578 582
 	assert.NilError(t, err)
579 583
 
580 584
 	containerJSON, err := apiClient.ContainerInspect(testutil.GetContext(t), ctr.ID)
... ...
@@ -601,7 +617,11 @@ func (s *DockerAPISuite) TestContainerAPICreateWithCpuSharesCpuset(c *testing.T)
601 601
 	assert.NilError(c, err)
602 602
 	defer apiClient.Close()
603 603
 
604
-	ctr, err := apiClient.ContainerCreate(testutil.GetContext(c), &config, &hostConfig, &network.NetworkingConfig{}, nil, "")
604
+	ctr, err := apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{
605
+		Config:           &config,
606
+		HostConfig:       &hostConfig,
607
+		NetworkingConfig: &network.NetworkingConfig{},
608
+	})
605 609
 	assert.NilError(c, err)
606 610
 
607 611
 	containerJSON, err := apiClient.ContainerInspect(testutil.GetContext(c), ctr.ID)
... ...
@@ -746,7 +766,12 @@ func (s *DockerAPISuite) TestContainerAPIStart(c *testing.T) {
746 746
 	assert.NilError(c, err)
747 747
 	defer apiClient.Close()
748 748
 
749
-	_, err = apiClient.ContainerCreate(testutil.GetContext(c), &config, &container.HostConfig{}, &network.NetworkingConfig{}, nil, name)
749
+	_, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{
750
+		Config:           &config,
751
+		HostConfig:       &container.HostConfig{},
752
+		NetworkingConfig: &network.NetworkingConfig{},
753
+		ContainerName:    name,
754
+	})
750 755
 	assert.NilError(c, err)
751 756
 
752 757
 	err = apiClient.ContainerStart(testutil.GetContext(c), name, client.ContainerStartOptions{})
... ...
@@ -989,7 +1014,12 @@ func (s *DockerAPISuite) TestPostContainersCreateWithWrongCpusetValues(c *testin
989 989
 	}
990 990
 	const name = "wrong-cpuset-cpus"
991 991
 
992
-	_, err = apiClient.ContainerCreate(testutil.GetContext(c), &config, &hostConfig1, &network.NetworkingConfig{}, nil, name)
992
+	_, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{
993
+		Config:           &config,
994
+		HostConfig:       &hostConfig1,
995
+		NetworkingConfig: &network.NetworkingConfig{},
996
+		ContainerName:    name,
997
+	})
993 998
 	expected := "Invalid value 1-42,, for cpuset cpus"
994 999
 	assert.ErrorContains(c, err, expected)
995 1000
 
... ...
@@ -999,7 +1029,12 @@ func (s *DockerAPISuite) TestPostContainersCreateWithWrongCpusetValues(c *testin
999 999
 		},
1000 1000
 	}
1001 1001
 	const name2 = "wrong-cpuset-mems"
1002
-	_, err = apiClient.ContainerCreate(testutil.GetContext(c), &config, &hostConfig2, &network.NetworkingConfig{}, nil, name2)
1002
+	_, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{
1003
+		Config:           &config,
1004
+		HostConfig:       &hostConfig2,
1005
+		NetworkingConfig: &network.NetworkingConfig{},
1006
+		ContainerName:    name2,
1007
+	})
1003 1008
 	expected = "Invalid value 42-3,1-- for cpuset mems"
1004 1009
 	assert.ErrorContains(c, err, expected)
1005 1010
 }
... ...
@@ -1015,7 +1050,11 @@ func (s *DockerAPISuite) TestPostContainersCreateMemorySwappinessHostConfigOmitt
1015 1015
 	assert.NilError(c, err)
1016 1016
 	defer apiClient.Close()
1017 1017
 
1018
-	ctr, err := apiClient.ContainerCreate(testutil.GetContext(c), &config, &container.HostConfig{}, &network.NetworkingConfig{}, nil, "")
1018
+	ctr, err := apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{
1019
+		Config:           &config,
1020
+		HostConfig:       &container.HostConfig{},
1021
+		NetworkingConfig: &network.NetworkingConfig{},
1022
+	})
1019 1023
 	assert.NilError(c, err)
1020 1024
 
1021 1025
 	containerJSON, err := apiClient.ContainerInspect(testutil.GetContext(c), ctr.ID)
... ...
@@ -1042,7 +1081,12 @@ func (s *DockerAPISuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *
1042 1042
 	defer apiClient.Close()
1043 1043
 
1044 1044
 	const name = "oomscoreadj-over"
1045
-	_, err = apiClient.ContainerCreate(testutil.GetContext(c), &config, &hostConfig, &network.NetworkingConfig{}, nil, name)
1045
+	_, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{
1046
+		Config:           &config,
1047
+		HostConfig:       &hostConfig,
1048
+		NetworkingConfig: &network.NetworkingConfig{},
1049
+		ContainerName:    name,
1050
+	})
1046 1051
 
1047 1052
 	expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]"
1048 1053
 	assert.ErrorContains(c, err, expected)
... ...
@@ -1052,7 +1096,12 @@ func (s *DockerAPISuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *
1052 1052
 	}
1053 1053
 
1054 1054
 	const name2 = "oomscoreadj-low"
1055
-	_, err = apiClient.ContainerCreate(testutil.GetContext(c), &config, &hostConfig, &network.NetworkingConfig{}, nil, name2)
1055
+	_, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{
1056
+		Config:           &config,
1057
+		HostConfig:       &hostConfig,
1058
+		NetworkingConfig: &network.NetworkingConfig{},
1059
+		ContainerName:    name2,
1060
+	})
1056 1061
 
1057 1062
 	expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]"
1058 1063
 	assert.ErrorContains(c, err, expected)
... ...
@@ -1085,7 +1134,12 @@ func (s *DockerAPISuite) TestContainerAPIStatsWithNetworkDisabled(c *testing.T)
1085 1085
 	assert.NilError(c, err)
1086 1086
 	defer apiClient.Close()
1087 1087
 
1088
-	_, err = apiClient.ContainerCreate(testutil.GetContext(c), &config, &container.HostConfig{}, &network.NetworkingConfig{}, nil, name)
1088
+	_, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{
1089
+		Config:           &config,
1090
+		HostConfig:       &container.HostConfig{},
1091
+		NetworkingConfig: &network.NetworkingConfig{},
1092
+		ContainerName:    name,
1093
+	})
1089 1094
 	assert.NilError(c, err)
1090 1095
 
1091 1096
 	err = apiClient.ContainerStart(testutil.GetContext(c), name, client.ContainerStartOptions{})
... ...
@@ -1421,7 +1475,11 @@ func (s *DockerAPISuite) TestContainersAPICreateMountsValidation(c *testing.T) {
1421 1421
 	// TODO add checks for statuscode returned by API
1422 1422
 	for i, tc := range tests {
1423 1423
 		c.Run(fmt.Sprintf("case %d", i), func(c *testing.T) {
1424
-			_, err = apiClient.ContainerCreate(testutil.GetContext(c), &tc.config, &tc.hostConfig, &network.NetworkingConfig{}, nil, "")
1424
+			_, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{
1425
+				Config:           &tc.config,
1426
+				HostConfig:       &tc.hostConfig,
1427
+				NetworkingConfig: &network.NetworkingConfig{},
1428
+			})
1425 1429
 			if tc.msg != "" {
1426 1430
 				assert.ErrorContains(c, err, tc.msg, "%v", tests[i].config)
1427 1431
 			} else {
... ...
@@ -1454,7 +1512,12 @@ func (s *DockerAPISuite) TestContainerAPICreateMountsBindRead(c *testing.T) {
1454 1454
 	assert.NilError(c, err)
1455 1455
 	defer apiClient.Close()
1456 1456
 
1457
-	_, err = apiClient.ContainerCreate(testutil.GetContext(c), &config, &hostConfig, &network.NetworkingConfig{}, nil, "test")
1457
+	_, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{
1458
+		Config:           &config,
1459
+		HostConfig:       &hostConfig,
1460
+		NetworkingConfig: &network.NetworkingConfig{},
1461
+		ContainerName:    "test",
1462
+	})
1458 1463
 	assert.NilError(c, err)
1459 1464
 
1460 1465
 	out := cli.DockerCmd(c, "start", "-a", "test").Combined()
... ...
@@ -1590,11 +1653,11 @@ func (s *DockerAPISuite) TestContainersAPICreateMountsCreate(c *testing.T) {
1590 1590
 		c.Run(fmt.Sprintf("%d config: %v", i, tc.spec), func(c *testing.T) {
1591 1591
 			ctr, err := apiclient.ContainerCreate(
1592 1592
 				ctx,
1593
-				&container.Config{Image: testImg},
1594
-				&container.HostConfig{Mounts: []mount.Mount{tc.spec}},
1595
-				&network.NetworkingConfig{},
1596
-				nil,
1597
-				"")
1593
+				client.ContainerCreateOptions{
1594
+					Config:           &container.Config{Image: testImg},
1595
+					HostConfig:       &container.HostConfig{Mounts: []mount.Mount{tc.spec}},
1596
+					NetworkingConfig: &network.NetworkingConfig{},
1597
+				})
1598 1598
 			assert.NilError(c, err)
1599 1599
 
1600 1600
 			containerInspect, err := apiclient.ContainerInspect(ctx, ctr.ID)
... ...
@@ -1705,7 +1768,12 @@ func (s *DockerAPISuite) TestContainersAPICreateMountsTmpfs(c *testing.T) {
1705 1705
 			Mounts: []mount.Mount{x.cfg},
1706 1706
 		}
1707 1707
 
1708
-		_, err = apiClient.ContainerCreate(testutil.GetContext(c), &config, &hostConfig, &network.NetworkingConfig{}, nil, cName)
1708
+		_, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{
1709
+			Config:           &config,
1710
+			HostConfig:       &hostConfig,
1711
+			NetworkingConfig: &network.NetworkingConfig{},
1712
+			ContainerName:    cName,
1713
+		})
1709 1714
 		assert.NilError(c, err)
1710 1715
 		out := cli.DockerCmd(c, "start", "-a", cName).Combined()
1711 1716
 		for _, option := range x.expectedOptions {
... ...
@@ -12,6 +12,7 @@ import (
12 12
 	"github.com/Microsoft/go-winio"
13 13
 	"github.com/moby/moby/api/types/container"
14 14
 	"github.com/moby/moby/api/types/mount"
15
+	"github.com/moby/moby/api/types/network"
15 16
 	"github.com/moby/moby/client"
16 17
 	"github.com/moby/moby/v2/internal/testutil"
17 18
 	"github.com/pkg/errors"
... ...
@@ -51,19 +52,23 @@ func (s *DockerAPISuite) TestContainersAPICreateMountsBindNamedPipe(c *testing.T
51 51
 	ctx := testutil.GetContext(c)
52 52
 	apiClient := testEnv.APIClient()
53 53
 	_, err = apiClient.ContainerCreate(ctx,
54
-		&container.Config{
55
-			Image: testEnv.PlatformDefaults.BaseImage,
56
-			Cmd:   []string{"cmd", "/c", cmd},
57
-		}, &container.HostConfig{
58
-			Mounts: []mount.Mount{
59
-				{
60
-					Type:   "npipe",
61
-					Source: hostPipeName,
62
-					Target: containerPipeName,
54
+		client.ContainerCreateOptions{
55
+			Config: &container.Config{
56
+				Image: testEnv.PlatformDefaults.BaseImage,
57
+				Cmd:   []string{"cmd", "/c", cmd},
58
+			},
59
+			HostConfig: &container.HostConfig{
60
+				Mounts: []mount.Mount{
61
+					{
62
+						Type:   "npipe",
63
+						Source: hostPipeName,
64
+						Target: containerPipeName,
65
+					},
63 66
 				},
64 67
 			},
65
-		},
66
-		nil, nil, name)
68
+			NetworkingConfig: &network.NetworkingConfig{},
69
+			ContainerName:    name,
70
+		})
67 71
 	assert.NilError(c, err)
68 72
 
69 73
 	err = apiClient.ContainerStart(ctx, name, client.ContainerStartOptions{})
... ...
@@ -596,7 +596,12 @@ func (s *DockerCLIVolumeSuite) TestDuplicateMountpointsForVolumesFromAndMounts(c
596 596
 			},
597 597
 		},
598 598
 	}
599
-	_, err = apiClient.ContainerCreate(testutil.GetContext(c), &config, &hostConfig, &network.NetworkingConfig{}, nil, "app")
599
+	_, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{
600
+		Config:           &config,
601
+		HostConfig:       &hostConfig,
602
+		NetworkingConfig: &network.NetworkingConfig{},
603
+		ContainerName:    "app",
604
+	})
600 605
 
601 606
 	assert.NilError(c, err)
602 607
 
... ...
@@ -41,17 +41,15 @@ func TestAttach(t *testing.T) {
41 41
 			t.Parallel()
42 42
 
43 43
 			ctx := testutil.StartSpan(ctx, t)
44
-			resp, err := apiClient.ContainerCreate(ctx,
45
-				&container.Config{
44
+			resp, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
45
+				Config: &container.Config{
46 46
 					Image: "busybox",
47 47
 					Cmd:   []string{"echo", "hello"},
48 48
 					Tty:   tc.tty,
49 49
 				},
50
-				&container.HostConfig{},
51
-				&network.NetworkingConfig{},
52
-				nil,
53
-				"",
54
-			)
50
+				HostConfig:       &container.HostConfig{},
51
+				NetworkingConfig: &network.NetworkingConfig{},
52
+			})
55 53
 			assert.NilError(t, err)
56 54
 			attach, err := apiClient.ContainerAttach(ctx, resp.ID, client.ContainerAttachOptions{
57 55
 				Stdout: true,
... ...
@@ -81,16 +79,14 @@ func TestAttachDisconnectLeak(t *testing.T) {
81 81
 
82 82
 	apiClient := d.NewClientT(t)
83 83
 
84
-	resp, err := apiClient.ContainerCreate(ctx,
85
-		&container.Config{
84
+	resp, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
85
+		Config: &container.Config{
86 86
 			Image: "busybox",
87 87
 			Cmd:   []string{"/bin/sh", "-c", "while true; usleep 100000; done"},
88 88
 		},
89
-		&container.HostConfig{},
90
-		&network.NetworkingConfig{},
91
-		nil,
92
-		"",
93
-	)
89
+		HostConfig:       &container.HostConfig{},
90
+		NetworkingConfig: &network.NetworkingConfig{},
91
+	})
94 92
 	assert.NilError(t, err)
95 93
 	cID := resp.ID
96 94
 	defer apiClient.ContainerRemove(ctx, cID, client.ContainerRemoveOptions{
... ...
@@ -59,13 +59,11 @@ func TestCreateFailsWhenIdentifierDoesNotExist(t *testing.T) {
59 59
 		t.Run(tc.doc, func(t *testing.T) {
60 60
 			t.Parallel()
61 61
 			ctx := testutil.StartSpan(ctx, t)
62
-			_, err := apiClient.ContainerCreate(ctx,
63
-				&container.Config{Image: tc.image},
64
-				&container.HostConfig{},
65
-				&network.NetworkingConfig{},
66
-				nil,
67
-				"",
68
-			)
62
+			_, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
63
+				Config:           &container.Config{Image: tc.image},
64
+				HostConfig:       &container.HostConfig{},
65
+				NetworkingConfig: &network.NetworkingConfig{},
66
+			})
69 67
 			assert.Check(t, is.ErrorContains(err, tc.expectedError))
70 68
 			assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
71 69
 		})
... ...
@@ -125,15 +123,11 @@ func TestCreateByImageID(t *testing.T) {
125 125
 		t.Run(tc.doc, func(t *testing.T) {
126 126
 			t.Parallel()
127 127
 			ctx := testutil.StartSpan(ctx, t)
128
-			resp, err := apiClient.ContainerCreate(ctx,
129
-				&container.Config{Image: tc.image},
130
-				&container.HostConfig{},
131
-				&network.NetworkingConfig{},
132
-				nil,
133
-				"",
134
-			)
128
+			resp, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
129
+				Config: &container.Config{Image: tc.image},
130
+			})
135 131
 			if tc.expectedErr != "" {
136
-				assert.Check(t, is.DeepEqual(resp, container.CreateResponse{}))
132
+				assert.Check(t, is.DeepEqual(resp, client.ContainerCreateResult{}))
137 133
 				assert.Check(t, is.Error(err, tc.expectedErr))
138 134
 				assert.Check(t, is.ErrorType(err, tc.expectedErrType))
139 135
 			} else {
... ...
@@ -154,17 +148,14 @@ func TestCreateLinkToNonExistingContainer(t *testing.T) {
154 154
 	ctx := setupTest(t)
155 155
 	apiClient := testEnv.APIClient()
156 156
 
157
-	_, err := apiClient.ContainerCreate(ctx,
158
-		&container.Config{
157
+	_, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
158
+		Config: &container.Config{
159 159
 			Image: "busybox",
160 160
 		},
161
-		&container.HostConfig{
161
+		HostConfig: &container.HostConfig{
162 162
 			Links: []string{"no-such-container"},
163 163
 		},
164
-		&network.NetworkingConfig{},
165
-		nil,
166
-		"",
167
-	)
164
+	})
168 165
 	assert.Check(t, is.ErrorContains(err, "could not get container for no-such-container"))
169 166
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
170 167
 }
... ...
@@ -195,16 +186,12 @@ func TestCreateWithInvalidEnv(t *testing.T) {
195 195
 		t.Run(strconv.Itoa(index), func(t *testing.T) {
196 196
 			t.Parallel()
197 197
 			ctx := testutil.StartSpan(ctx, t)
198
-			_, err := apiClient.ContainerCreate(ctx,
199
-				&container.Config{
198
+			_, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
199
+				Config: &container.Config{
200 200
 					Image: "busybox",
201 201
 					Env:   []string{tc.env},
202 202
 				},
203
-				&container.HostConfig{},
204
-				&network.NetworkingConfig{},
205
-				nil,
206
-				"",
207
-			)
203
+			})
208 204
 			assert.Check(t, is.ErrorContains(err, tc.expectedError))
209 205
 			assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
210 206
 		})
... ...
@@ -241,17 +228,14 @@ func TestCreateTmpfsMountsTarget(t *testing.T) {
241 241
 	}
242 242
 
243 243
 	for _, tc := range testCases {
244
-		_, err := apiClient.ContainerCreate(ctx,
245
-			&container.Config{
244
+		_, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
245
+			Config: &container.Config{
246 246
 				Image: "busybox",
247 247
 			},
248
-			&container.HostConfig{
248
+			HostConfig: &container.HostConfig{
249 249
 				Tmpfs: map[string]string{tc.target: ""},
250 250
 			},
251
-			&network.NetworkingConfig{},
252
-			nil,
253
-			"",
254
-		)
251
+		})
255 252
 		assert.Check(t, is.ErrorContains(err, tc.expectedError))
256 253
 		assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
257 254
 	}
... ...
@@ -298,19 +282,17 @@ func TestCreateWithCustomMaskedPaths(t *testing.T) {
298 298
 			t.Parallel()
299 299
 
300 300
 			// Create the container.
301
-			ctr, err := apiClient.ContainerCreate(ctx,
302
-				&container.Config{
301
+			ctr, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
302
+				Config: &container.Config{
303 303
 					Image: "busybox",
304 304
 					Cmd:   []string{"true"},
305 305
 				},
306
-				&container.HostConfig{
306
+				HostConfig: &container.HostConfig{
307 307
 					Privileged:  tc.privileged,
308 308
 					MaskedPaths: tc.maskedPaths,
309 309
 				},
310
-				nil,
311
-				nil,
312
-				fmt.Sprintf("create-masked-paths-%d", i),
313
-			)
310
+				ContainerName: fmt.Sprintf("create-masked-paths-%d", i),
311
+			})
314 312
 			assert.NilError(t, err)
315 313
 
316 314
 			ctrInspect, err := apiClient.ContainerInspect(ctx, ctr.ID)
... ...
@@ -371,19 +353,17 @@ func TestCreateWithCustomReadonlyPaths(t *testing.T) {
371 371
 	for i, tc := range testCases {
372 372
 		t.Run(tc.doc, func(t *testing.T) {
373 373
 			t.Parallel()
374
-			ctr, err := apiClient.ContainerCreate(ctx,
375
-				&container.Config{
374
+			ctr, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
375
+				Config: &container.Config{
376 376
 					Image: "busybox",
377 377
 					Cmd:   []string{"true"},
378 378
 				},
379
-				&container.HostConfig{
379
+				HostConfig: &container.HostConfig{
380 380
 					Privileged:    tc.privileged,
381 381
 					ReadonlyPaths: tc.readonlyPaths,
382 382
 				},
383
-				nil,
384
-				nil,
385
-				fmt.Sprintf("create-readonly-paths-%d", i),
386
-			)
383
+				ContainerName: fmt.Sprintf("create-readonly-paths-%d", i),
384
+			})
387 385
 			assert.NilError(t, err)
388 386
 
389 387
 			ctrInspect, err := apiClient.ContainerInspect(ctx, ctr.ID)
... ...
@@ -482,7 +462,9 @@ func TestCreateWithInvalidHealthcheckParams(t *testing.T) {
482 482
 				cfg.Healthcheck.StartPeriod = tc.startPeriod
483 483
 			}
484 484
 
485
-			resp, err := apiClient.ContainerCreate(ctx, &cfg, &container.HostConfig{}, nil, nil, "")
485
+			resp, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
486
+				Config: &cfg,
487
+			})
486 488
 			assert.Check(t, is.Equal(len(resp.Warnings), 0))
487 489
 			assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
488 490
 			assert.ErrorContains(t, err, tc.expectedErr)
... ...
@@ -553,7 +535,10 @@ func TestCreateDifferentPlatform(t *testing.T) {
553 553
 			Architecture: img.Architecture,
554 554
 			Variant:      img.Variant,
555 555
 		}
556
-		_, err := apiClient.ContainerCreate(ctx, &container.Config{Image: "busybox:latest"}, &container.HostConfig{}, nil, &p, "")
556
+		_, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
557
+			Config:   &container.Config{Image: "busybox:latest"},
558
+			Platform: &p,
559
+		})
557 560
 		assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
558 561
 	})
559 562
 	t.Run("different cpu arch", func(t *testing.T) {
... ...
@@ -563,7 +548,10 @@ func TestCreateDifferentPlatform(t *testing.T) {
563 563
 			Architecture: img.Architecture + "DifferentArch",
564 564
 			Variant:      img.Variant,
565 565
 		}
566
-		_, err := apiClient.ContainerCreate(ctx, &container.Config{Image: "busybox:latest"}, &container.HostConfig{}, nil, &p, "")
566
+		_, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
567
+			Config:   &container.Config{Image: "busybox:latest"},
568
+			Platform: &p,
569
+		})
567 570
 		assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
568 571
 	})
569 572
 }
... ...
@@ -574,12 +562,10 @@ func TestCreateVolumesFromNonExistingContainer(t *testing.T) {
574 574
 
575 575
 	_, err := apiClient.ContainerCreate(
576 576
 		ctx,
577
-		&container.Config{Image: "busybox"},
578
-		&container.HostConfig{VolumesFrom: []string{"nosuchcontainer"}},
579
-		nil,
580
-		nil,
581
-		"",
582
-	)
577
+		client.ContainerCreateOptions{
578
+			Config:     &container.Config{Image: "busybox"},
579
+			HostConfig: &container.HostConfig{VolumesFrom: []string{"nosuchcontainer"}},
580
+		})
583 581
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
584 582
 }
585 583
 
... ...
@@ -594,12 +580,9 @@ func TestCreatePlatformSpecificImageNoPlatform(t *testing.T) {
594 594
 
595 595
 	_, err := apiClient.ContainerCreate(
596 596
 		ctx,
597
-		&container.Config{Image: "arm32v7/hello-world"},
598
-		&container.HostConfig{},
599
-		nil,
600
-		nil,
601
-		"",
602
-	)
597
+		client.ContainerCreateOptions{
598
+			Config: &container.Config{Image: "arm32v7/hello-world"},
599
+		})
603 600
 	assert.NilError(t, err)
604 601
 }
605 602
 
... ...
@@ -653,7 +636,10 @@ func TestCreateInvalidHostConfig(t *testing.T) {
653 653
 			cfg := container.Config{
654 654
 				Image: "busybox",
655 655
 			}
656
-			resp, err := apiClient.ContainerCreate(ctx, &cfg, &tc.hc, nil, nil, "")
656
+			resp, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
657
+				Config:     &cfg,
658
+				HostConfig: &tc.hc,
659
+			})
657 660
 			assert.Check(t, is.Equal(len(resp.Warnings), 0))
658 661
 			assert.Check(t, cerrdefs.IsInvalidArgument(err), "got: %T", err)
659 662
 			assert.Error(t, err, tc.expectedErr)
... ...
@@ -760,7 +746,10 @@ func TestCreateWithMultipleEndpointSettings(t *testing.T) {
760 760
 					"net3": {},
761 761
 				},
762 762
 			}
763
-			_, err = apiClient.ContainerCreate(ctx, &config, &container.HostConfig{}, &networkingConfig, nil, "")
763
+			_, err = apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
764
+				Config:           &config,
765
+				NetworkingConfig: &networkingConfig,
766
+			})
764 767
 			if tc.expectedErr == "" {
765 768
 				assert.NilError(t, err)
766 769
 			} else {
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"time"
6 6
 
7 7
 	containertypes "github.com/moby/moby/api/types/container"
8
+	"github.com/moby/moby/client"
8 9
 	"github.com/moby/moby/v2/integration/internal/container"
9 10
 	"gotest.tools/v3/assert"
10 11
 	"gotest.tools/v3/poll"
... ...
@@ -24,9 +25,9 @@ func TestDiff(t *testing.T) {
24 24
 	}
25 25
 
26 26
 	poll.WaitOn(t, container.IsStopped(ctx, apiClient, cID))
27
-	items, err := apiClient.ContainerDiff(ctx, cID)
27
+	result, err := apiClient.ContainerDiff(ctx, cID, client.ContainerDiffOptions{})
28 28
 	assert.NilError(t, err)
29
-	assert.DeepEqual(t, expected, items)
29
+	assert.DeepEqual(t, expected, result.Changes)
30 30
 }
31 31
 
32 32
 func TestDiffStoppedContainer(t *testing.T) {
... ...
@@ -51,7 +52,7 @@ func TestDiffStoppedContainer(t *testing.T) {
51 51
 		}
52 52
 	}
53 53
 
54
-	items, err := apiClient.ContainerDiff(ctx, cID)
54
+	result, err := apiClient.ContainerDiff(ctx, cID, client.ContainerDiffOptions{})
55 55
 	assert.NilError(t, err)
56
-	assert.DeepEqual(t, expected, items)
56
+	assert.DeepEqual(t, expected, result.Changes)
57 57
 }
... ...
@@ -64,7 +64,10 @@ func testIpcNonePrivateShareable(t *testing.T, mode string, mustBeMounted bool,
64 64
 	}
65 65
 	apiClient := testEnv.APIClient()
66 66
 
67
-	resp, err := apiClient.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "")
67
+	resp, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
68
+		Config:     &cfg,
69
+		HostConfig: &hostCfg,
70
+	})
68 71
 	assert.NilError(t, err)
69 72
 	assert.Check(t, is.Equal(len(resp.Warnings), 0))
70 73
 
... ...
@@ -135,7 +138,10 @@ func testIpcContainer(t *testing.T, donorMode string, mustWork bool) {
135 135
 	apiClient := testEnv.APIClient()
136 136
 
137 137
 	// create and start the "donor" container
138
-	resp, err := apiClient.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "")
138
+	resp, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
139
+		Config:     &cfg,
140
+		HostConfig: &hostCfg,
141
+	})
139 142
 	assert.NilError(t, err)
140 143
 	assert.Check(t, is.Equal(len(resp.Warnings), 0))
141 144
 	name1 := resp.ID
... ...
@@ -145,7 +151,10 @@ func testIpcContainer(t *testing.T, donorMode string, mustWork bool) {
145 145
 
146 146
 	// create and start the second container
147 147
 	hostCfg.IpcMode = containertypes.IpcMode("container:" + name1)
148
-	resp, err = apiClient.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "")
148
+	resp, err = apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
149
+		Config:     &cfg,
150
+		HostConfig: &hostCfg,
151
+	})
149 152
 	assert.NilError(t, err)
150 153
 	assert.Check(t, is.Equal(len(resp.Warnings), 0))
151 154
 	name2 := resp.ID
... ...
@@ -201,7 +210,10 @@ func TestAPIIpcModeHost(t *testing.T) {
201 201
 	ctx := testutil.StartSpan(baseContext, t)
202 202
 
203 203
 	apiClient := testEnv.APIClient()
204
-	resp, err := apiClient.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "")
204
+	resp, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
205
+		Config:     &cfg,
206
+		HostConfig: &hostCfg,
207
+	})
205 208
 	assert.NilError(t, err)
206 209
 	assert.Check(t, is.Equal(len(resp.Warnings), 0))
207 210
 	name := resp.ID
... ...
@@ -237,7 +249,10 @@ func testDaemonIpcPrivateShareable(t *testing.T, mustBeShared bool, arg ...strin
237 237
 		Cmd:   []string{"top"},
238 238
 	}
239 239
 
240
-	resp, err := c.ContainerCreate(ctx, &cfg, &containertypes.HostConfig{}, nil, nil, "")
240
+	resp, err := c.ContainerCreate(ctx, client.ContainerCreateOptions{
241
+		Config:     &cfg,
242
+		HostConfig: &containertypes.HostConfig{},
243
+	})
241 244
 	assert.NilError(t, err)
242 245
 	assert.Check(t, is.Equal(len(resp.Warnings), 0))
243 246
 
... ...
@@ -67,7 +67,11 @@ func TestContainerNetworkMountsNoChown(t *testing.T) {
67 67
 	assert.NilError(t, err)
68 68
 	defer cli.Close()
69 69
 
70
-	ctrCreate, err := cli.ContainerCreate(ctx, &config, &hostConfig, &network.NetworkingConfig{}, nil, "")
70
+	ctrCreate, err := cli.ContainerCreate(ctx, client.ContainerCreateOptions{
71
+		Config:           &config,
72
+		HostConfig:       &hostConfig,
73
+		NetworkingConfig: &network.NetworkingConfig{},
74
+	})
71 75
 	assert.NilError(t, err)
72 76
 	// container will exit immediately because of no tty, but we only need the start sequence to test the condition
73 77
 	err = cli.ContainerStart(ctx, ctrCreate.ID, client.ContainerStartOptions{})
... ...
@@ -179,10 +183,13 @@ func TestMountDaemonRoot(t *testing.T) {
179 179
 
180 180
 					ctx := testutil.StartSpan(ctx, t)
181 181
 
182
-					c, err := apiClient.ContainerCreate(ctx, &containertypes.Config{
183
-						Image: "busybox",
184
-						Cmd:   []string{"true"},
185
-					}, hc, nil, nil, "")
182
+					c, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
183
+						Config: &containertypes.Config{
184
+							Image: "busybox",
185
+							Cmd:   []string{"true"},
186
+						},
187
+						HostConfig: hc,
188
+					})
186 189
 					if err != nil {
187 190
 						if test.expected != "" {
188 191
 							t.Fatal(err)
... ...
@@ -430,7 +437,13 @@ func TestContainerVolumeAnonymous(t *testing.T) {
430 430
 				},
431 431
 			},
432 432
 		}))
433
-		_, err := apiClient.ContainerCreate(ctx, config.Config, config.HostConfig, config.NetworkingConfig, config.Platform, config.Name)
433
+		_, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
434
+			Config:           config.Config,
435
+			HostConfig:       config.HostConfig,
436
+			NetworkingConfig: config.NetworkingConfig,
437
+			Platform:         config.Platform,
438
+			ContainerName:    config.Name,
439
+		})
434 440
 		// We use [testNonExistingPlugin] for this, which produces an error
435 441
 		// when used, which we use as indicator that the driver was passed
436 442
 		// through. We should have a cleaner way for this, but that would
... ...
@@ -28,7 +28,7 @@ func TestNoOverlayfsWarningsAboutUndefinedBehaviors(t *testing.T) {
28 28
 		operation func(t *testing.T) error
29 29
 	}{
30 30
 		{name: "diff", operation: func(*testing.T) error {
31
-			_, err := apiClient.ContainerDiff(ctx, cID)
31
+			_, err := apiClient.ContainerDiff(ctx, cID, client.ContainerDiffOptions{})
32 32
 			return err
33 33
 		}},
34 34
 		{name: "export", operation: func(*testing.T) error {
... ...
@@ -100,7 +100,10 @@ func TestDaemonRestartKillContainers(t *testing.T) {
100 100
 							Interval:      60 * time.Second,
101 101
 						}
102 102
 					}
103
-					resp, err := apiClient.ContainerCreate(ctx, &config, &hostConfig, nil, nil, "")
103
+					resp, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
104
+						Config:     &config,
105
+						HostConfig: &hostConfig,
106
+					})
104 107
 					assert.NilError(t, err)
105 108
 					defer apiClient.ContainerRemove(ctx, resp.ID, client.ContainerRemoveOptions{Force: true})
106 109
 
... ...
@@ -55,10 +55,13 @@ func TestGraphDriverPersistence(t *testing.T) {
55 55
 	assert.Check(t, info.DriverStatus[0][1] != "io.containerd.snapshotter.v1")
56 56
 	prevDriver := info.Driver
57 57
 
58
-	containerResp, err := c.ContainerCreate(ctx, &containertypes.Config{
59
-		Image: testImage,
60
-		Cmd:   []string{"echo", "test"},
61
-	}, nil, nil, nil, "test-container")
58
+	containerResp, err := c.ContainerCreate(ctx, client.ContainerCreateOptions{
59
+		Config: &containertypes.Config{
60
+			Image: testImage,
61
+			Cmd:   []string{"echo", "test"},
62
+		},
63
+		ContainerName: "test-container",
64
+	})
62 65
 	assert.NilError(t, err, "Failed to create container")
63 66
 
64 67
 	containerID := containerResp.ID
... ...
@@ -141,7 +144,7 @@ func TestInspectGraphDriverAPIBC(t *testing.T) {
141 141
 			}
142 142
 
143 143
 			const testImage = "busybox:latest"
144
-			ctr, err := c.ContainerCreate(ctx, &containertypes.Config{Image: testImage}, nil, nil, nil, "test-container")
144
+			ctr, err := c.ContainerCreate(ctx, client.ContainerCreateOptions{Image: testImage, Name: "test-container"})
145 145
 			assert.NilError(t, err)
146 146
 			defer func() { _ = c.ContainerRemove(ctx, ctr.ID, client.ContainerRemoveOptions{Force: true}) }()
147 147
 
... ...
@@ -55,7 +55,13 @@ func NewTestConfig(ops ...func(*TestContainerConfig)) *TestContainerConfig {
55 55
 func Create(ctx context.Context, t *testing.T, apiClient client.APIClient, ops ...func(*TestContainerConfig)) string {
56 56
 	t.Helper()
57 57
 	config := NewTestConfig(ops...)
58
-	c, err := apiClient.ContainerCreate(ctx, config.Config, config.HostConfig, config.NetworkingConfig, config.Platform, config.Name)
58
+	c, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
59
+		Config:           config.Config,
60
+		HostConfig:       config.HostConfig,
61
+		NetworkingConfig: config.NetworkingConfig,
62
+		Platform:         config.Platform,
63
+		ContainerName:    config.Name,
64
+	})
59 65
 	assert.NilError(t, err)
60 66
 
61 67
 	return c.ID
... ...
@@ -67,8 +73,14 @@ func Create(ctx context.Context, t *testing.T, apiClient client.APIClient, ops .
67 67
 //
68 68
 //	ctr, err := container.CreateFromConfig(ctx, apiClient, container.NewTestConfig(container.WithAutoRemove))
69 69
 //	assert.Check(t, err)
70
-func CreateFromConfig(ctx context.Context, apiClient client.APIClient, config *TestContainerConfig) (container.CreateResponse, error) {
71
-	return apiClient.ContainerCreate(ctx, config.Config, config.HostConfig, config.NetworkingConfig, config.Platform, config.Name)
70
+func CreateFromConfig(ctx context.Context, apiClient client.APIClient, config *TestContainerConfig) (client.ContainerCreateResult, error) {
71
+	return apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
72
+		Config:           config.Config,
73
+		HostConfig:       config.HostConfig,
74
+		NetworkingConfig: config.NetworkingConfig,
75
+		Platform:         config.Platform,
76
+		ContainerName:    config.Name,
77
+	})
72 78
 }
73 79
 
74 80
 // Run creates and start a container with the specified options
... ...
@@ -108,7 +120,7 @@ func RunAttach(ctx context.Context, t *testing.T, apiClient client.APIClient, op
108 108
 	err = apiClient.ContainerStart(ctx, id, client.ContainerStartOptions{})
109 109
 	assert.NilError(t, err)
110 110
 
111
-	s, err := demultiplexStreams(ctx, aresp)
111
+	s, err := demultiplexStreams(ctx, aresp.HijackedResponse)
112 112
 	if !errors.Is(err, context.DeadlineExceeded) && !errors.Is(err, context.Canceled) {
113 113
 		assert.NilError(t, err)
114 114
 	}
... ...
@@ -963,7 +963,13 @@ func TestEmptyPortBindingsBC(t *testing.T) {
963 963
 		config := ctr.NewTestConfig(ctr.WithCmd("top"),
964 964
 			ctr.WithExposedPorts("80/tcp"),
965 965
 			ctr.WithPortMap(networktypes.PortMap{networktypes.MustParsePort("80/tcp"): pbs}))
966
-		c, err := apiClient.ContainerCreate(ctx, config.Config, config.HostConfig, config.NetworkingConfig, config.Platform, config.Name)
966
+		c, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
967
+			Config:           config.Config,
968
+			HostConfig:       config.HostConfig,
969
+			NetworkingConfig: config.NetworkingConfig,
970
+			Platform:         config.Platform,
971
+			ContainerName:    config.Name,
972
+		})
967 973
 		assert.NilError(t, err)
968 974
 		defer apiClient.ContainerRemove(ctx, c.ID, client.ContainerRemoveOptions{Force: true})
969 975
 
... ...
@@ -54,13 +54,12 @@ func TestReadPluginNoRead(t *testing.T) {
54 54
 			ctx := testutil.StartSpan(ctx, t)
55 55
 			d.Start(t, append([]string{"--iptables=false", "--ip6tables=false"}, test.dOpts...)...)
56 56
 			defer d.Stop(t)
57
-			c, err := apiclient.ContainerCreate(ctx,
58
-				cfg,
59
-				&container.HostConfig{LogConfig: container.LogConfig{Type: "test"}},
60
-				nil,
61
-				nil,
62
-				"",
63
-			)
57
+			c, err := apiclient.ContainerCreate(ctx, client.ContainerCreateOptions{
58
+				Config: cfg,
59
+				HostConfig: &container.HostConfig{
60
+					LogConfig: container.LogConfig{Type: "test"},
61
+				},
62
+			})
64 63
 			assert.Assert(t, err)
65 64
 			defer apiclient.ContainerRemove(ctx, c.ID, client.ContainerRemoveOptions{Force: true})
66 65
 
... ...
@@ -80,7 +80,12 @@ func TestRunMountVolumeSubdir(t *testing.T) {
80 80
 			}
81 81
 
82 82
 			ctrName := strings.ReplaceAll(t.Name(), "/", "_")
83
-			create, creatErr := apiClient.ContainerCreate(ctx, &cfg, &hostCfg, &network.NetworkingConfig{}, nil, ctrName)
83
+			create, creatErr := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
84
+				Config:           &cfg,
85
+				HostConfig:       &hostCfg,
86
+				NetworkingConfig: &network.NetworkingConfig{},
87
+				ContainerName:    ctrName,
88
+			})
84 89
 			id := create.ID
85 90
 			if id != "" {
86 91
 				defer apiClient.ContainerRemove(ctx, id, client.ContainerRemoveOptions{Force: true})
... ...
@@ -175,7 +180,12 @@ func TestRunMountImage(t *testing.T) {
175 175
 			}
176 176
 
177 177
 			ctrName := strings.ReplaceAll(t.Name(), "/", "_")
178
-			create, creatErr := apiClient.ContainerCreate(ctx, &cfg, &hostCfg, &network.NetworkingConfig{}, nil, ctrName)
178
+			create, creatErr := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
179
+				Config:           &cfg,
180
+				HostConfig:       &hostCfg,
181
+				NetworkingConfig: &network.NetworkingConfig{},
182
+				ContainerName:    ctrName,
183
+			})
179 184
 			id := create.ID
180 185
 			if id != "" {
181 186
 				defer container.Remove(ctx, t, apiClient, id, client.ContainerRemoveOptions{Force: true})
... ...
@@ -154,10 +154,11 @@ COPY . /static`); err != nil {
154 154
 	assert.NilError(t, err)
155 155
 
156 156
 	// Start the container
157
-	b, err := c.ContainerCreate(context.Background(),
158
-		&containertypes.Config{Image: imgName},
159
-		&containertypes.HostConfig{PublishAllPorts: true},
160
-		nil, nil, ctrName)
157
+	b, err := c.ContainerCreate(context.Background(), client.ContainerCreateOptions{
158
+		Config:        &containertypes.Config{Image: imgName},
159
+		HostConfig:    &containertypes.HostConfig{PublishAllPorts: true},
160
+		ContainerName: ctrName,
161
+	})
161 162
 	assert.NilError(t, err)
162 163
 	err = c.ContainerStart(context.Background(), b.ID, client.ContainerStartOptions{})
163 164
 	assert.NilError(t, err)
... ...
@@ -12,7 +12,6 @@ import (
12 12
 	"github.com/moby/moby/api/types/registry"
13 13
 	"github.com/moby/moby/api/types/swarm"
14 14
 	"github.com/moby/moby/api/types/system"
15
-	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
16 15
 )
17 16
 
18 17
 // APIClient is an interface that clients that talk with a docker server must implement.
... ...
@@ -58,10 +57,10 @@ type HijackDialer interface {
58 58
 
59 59
 // ContainerAPIClient defines API client methods for the containers
60 60
 type ContainerAPIClient interface {
61
-	ContainerAttach(ctx context.Context, container string, options ContainerAttachOptions) (HijackedResponse, error)
62
-	ContainerCommit(ctx context.Context, container string, options ContainerCommitOptions) (container.CommitResponse, error)
63
-	ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error)
64
-	ContainerDiff(ctx context.Context, container string) ([]container.FilesystemChange, error)
61
+	ContainerAttach(ctx context.Context, container string, options ContainerAttachOptions) (ContainerAttachResult, error)
62
+	ContainerCommit(ctx context.Context, container string, options ContainerCommitOptions) (ContainerCommitResult, error)
63
+	ContainerCreate(ctx context.Context, options ContainerCreateOptions) (ContainerCreateResult, error)
64
+	ContainerDiff(ctx context.Context, container string, options ContainerDiffOptions) (ContainerDiffResult, error)
65 65
 	ExecAPIClient
66 66
 	ContainerExport(ctx context.Context, container string) (io.ReadCloser, error)
67 67
 	ContainerInspect(ctx context.Context, container string) (container.InspectResponse, error)
... ...
@@ -16,6 +16,11 @@ type ContainerAttachOptions struct {
16 16
 	Logs       bool
17 17
 }
18 18
 
19
+// ContainerAttachResult is the result from attaching to a container.
20
+type ContainerAttachResult struct {
21
+	HijackedResponse
22
+}
23
+
19 24
 // ContainerAttach attaches a connection to a container in the server.
20 25
 // It returns a [HijackedResponse] with the hijacked connection
21 26
 // and a reader to get output. It's up to the called to close
... ...
@@ -44,10 +49,10 @@ type ContainerAttachOptions struct {
44 44
 // [stdcopy.StdType]: https://pkg.go.dev/github.com/moby/moby/api/pkg/stdcopy#StdType
45 45
 // [Stdout]: https://pkg.go.dev/github.com/moby/moby/api/pkg/stdcopy#Stdout
46 46
 // [Stderr]: https://pkg.go.dev/github.com/moby/moby/api/pkg/stdcopy#Stderr
47
-func (cli *Client) ContainerAttach(ctx context.Context, containerID string, options ContainerAttachOptions) (HijackedResponse, error) {
47
+func (cli *Client) ContainerAttach(ctx context.Context, containerID string, options ContainerAttachOptions) (ContainerAttachResult, error) {
48 48
 	containerID, err := trimID("container", containerID)
49 49
 	if err != nil {
50
-		return HijackedResponse{}, err
50
+		return ContainerAttachResult{}, err
51 51
 	}
52 52
 
53 53
 	query := url.Values{}
... ...
@@ -70,7 +75,12 @@ func (cli *Client) ContainerAttach(ctx context.Context, containerID string, opti
70 70
 		query.Set("logs", "1")
71 71
 	}
72 72
 
73
-	return cli.postHijacked(ctx, "/containers/"+containerID+"/attach", query, nil, http.Header{
73
+	hijacked, err := cli.postHijacked(ctx, "/containers/"+containerID+"/attach", query, nil, http.Header{
74 74
 		"Content-Type": {"text/plain"},
75 75
 	})
76
+	if err != nil {
77
+		return ContainerAttachResult{}, err
78
+	}
79
+
80
+	return ContainerAttachResult{HijackedResponse: hijacked}, nil
76 81
 }
... ...
@@ -20,22 +20,27 @@ type ContainerCommitOptions struct {
20 20
 	Config    *container.Config
21 21
 }
22 22
 
23
+// ContainerCommitResult is the result from committing a container.
24
+type ContainerCommitResult struct {
25
+	ID string
26
+}
27
+
23 28
 // ContainerCommit applies changes to a container and creates a new tagged image.
24
-func (cli *Client) ContainerCommit(ctx context.Context, containerID string, options ContainerCommitOptions) (container.CommitResponse, error) {
29
+func (cli *Client) ContainerCommit(ctx context.Context, containerID string, options ContainerCommitOptions) (ContainerCommitResult, error) {
25 30
 	containerID, err := trimID("container", containerID)
26 31
 	if err != nil {
27
-		return container.CommitResponse{}, err
32
+		return ContainerCommitResult{}, err
28 33
 	}
29 34
 
30 35
 	var repository, tag string
31 36
 	if options.Reference != "" {
32 37
 		ref, err := reference.ParseNormalizedNamed(options.Reference)
33 38
 		if err != nil {
34
-			return container.CommitResponse{}, err
39
+			return ContainerCommitResult{}, err
35 40
 		}
36 41
 
37 42
 		if _, ok := ref.(reference.Digested); ok {
38
-			return container.CommitResponse{}, errors.New("refusing to create a tag with a digest reference")
43
+			return ContainerCommitResult{}, errors.New("refusing to create a tag with a digest reference")
39 44
 		}
40 45
 		ref = reference.TagNameOnly(ref)
41 46
 
... ...
@@ -62,9 +67,9 @@ func (cli *Client) ContainerCommit(ctx context.Context, containerID string, opti
62 62
 	resp, err := cli.post(ctx, "/commit", query, options.Config, nil)
63 63
 	defer ensureReaderClosed(resp)
64 64
 	if err != nil {
65
-		return response, err
65
+		return ContainerCommitResult{}, err
66 66
 	}
67 67
 
68 68
 	err = json.NewDecoder(resp.Body).Decode(&response)
69
-	return response, err
69
+	return ContainerCommitResult{ID: response.ID}, err
70 70
 }
... ...
@@ -10,49 +10,48 @@ import (
10 10
 
11 11
 	cerrdefs "github.com/containerd/errdefs"
12 12
 	"github.com/moby/moby/api/types/container"
13
-	"github.com/moby/moby/api/types/network"
14 13
 	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
15 14
 )
16 15
 
17 16
 // ContainerCreate creates a new container based on the given configuration.
18 17
 // It can be associated with a name, but it's not mandatory.
19
-func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error) {
20
-	if config == nil {
21
-		return container.CreateResponse{}, cerrdefs.ErrInvalidArgument.WithMessage("config is nil")
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")
22 21
 	}
23 22
 
24 23
 	var response container.CreateResponse
25 24
 
26
-	if hostConfig != nil {
27
-		hostConfig.CapAdd = normalizeCapabilities(hostConfig.CapAdd)
28
-		hostConfig.CapDrop = normalizeCapabilities(hostConfig.CapDrop)
25
+	if options.HostConfig != nil {
26
+		options.HostConfig.CapAdd = normalizeCapabilities(options.HostConfig.CapAdd)
27
+		options.HostConfig.CapDrop = normalizeCapabilities(options.HostConfig.CapDrop)
29 28
 	}
30 29
 
31 30
 	query := url.Values{}
32
-	if platform != nil {
33
-		if p := formatPlatform(*platform); p != "unknown" {
31
+	if options.Platform != nil {
32
+		if p := formatPlatform(*options.Platform); p != "unknown" {
34 33
 			query.Set("platform", p)
35 34
 		}
36 35
 	}
37 36
 
38
-	if containerName != "" {
39
-		query.Set("name", containerName)
37
+	if options.ContainerName != "" {
38
+		query.Set("name", options.ContainerName)
40 39
 	}
41 40
 
42 41
 	body := container.CreateRequest{
43
-		Config:           config,
44
-		HostConfig:       hostConfig,
45
-		NetworkingConfig: networkingConfig,
42
+		Config:           options.Config,
43
+		HostConfig:       options.HostConfig,
44
+		NetworkingConfig: options.NetworkingConfig,
46 45
 	}
47 46
 
48 47
 	resp, err := cli.post(ctx, "/containers/create", query, body, nil)
49 48
 	defer ensureReaderClosed(resp)
50 49
 	if err != nil {
51
-		return response, err
50
+		return ContainerCreateResult{}, err
52 51
 	}
53 52
 
54 53
 	err = json.NewDecoder(resp.Body).Decode(&response)
55
-	return response, err
54
+	return ContainerCreateResult{ID: response.ID, Warnings: response.Warnings}, err
56 55
 }
57 56
 
58 57
 // formatPlatform returns a formatted string representing platform (e.g., "linux/arm/v7").
59 58
new file mode 100644
... ...
@@ -0,0 +1,22 @@
0
+package client
1
+
2
+import (
3
+	"github.com/moby/moby/api/types/container"
4
+	"github.com/moby/moby/api/types/network"
5
+	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
6
+)
7
+
8
+// ContainerCreateOptions holds parameters to create a container.
9
+type ContainerCreateOptions struct {
10
+	Config           *container.Config
11
+	HostConfig       *container.HostConfig
12
+	NetworkingConfig *network.NetworkingConfig
13
+	Platform         *ocispec.Platform
14
+	ContainerName    string
15
+}
16
+
17
+// ContainerCreateResult is the result from creating a container.
18
+type ContainerCreateResult struct {
19
+	ID       string
20
+	Warnings []string
21
+}
... ...
@@ -9,22 +9,22 @@ import (
9 9
 )
10 10
 
11 11
 // ContainerDiff shows differences in a container filesystem since it was started.
12
-func (cli *Client) ContainerDiff(ctx context.Context, containerID string) ([]container.FilesystemChange, error) {
12
+func (cli *Client) ContainerDiff(ctx context.Context, containerID string, options ContainerDiffOptions) (ContainerDiffResult, error) {
13 13
 	containerID, err := trimID("container", containerID)
14 14
 	if err != nil {
15
-		return nil, err
15
+		return ContainerDiffResult{}, err
16 16
 	}
17 17
 
18 18
 	resp, err := cli.get(ctx, "/containers/"+containerID+"/changes", url.Values{}, nil)
19 19
 	defer ensureReaderClosed(resp)
20 20
 	if err != nil {
21
-		return nil, err
21
+		return ContainerDiffResult{}, err
22 22
 	}
23 23
 
24 24
 	var changes []container.FilesystemChange
25 25
 	err = json.NewDecoder(resp.Body).Decode(&changes)
26 26
 	if err != nil {
27
-		return nil, err
27
+		return ContainerDiffResult{}, err
28 28
 	}
29
-	return changes, err
29
+	return ContainerDiffResult{Changes: changes}, err
30 30
 }
31 31
new file mode 100644
... ...
@@ -0,0 +1,13 @@
0
+package client
1
+
2
+import "github.com/moby/moby/api/types/container"
3
+
4
+// ContainerDiffOptions holds parameters to show differences in a container filesystem.
5
+type ContainerDiffOptions struct {
6
+	// Currently no options, but this allows for future extensibility
7
+}
8
+
9
+// ContainerDiffResult is the result from showing differences in a container filesystem.
10
+type ContainerDiffResult struct {
11
+	Changes []container.FilesystemChange
12
+}