From bd31b8b1c7247cb7922fa7c880d3b72fdbf25219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Thu, 23 Oct 2025 14:20:05 +0200 Subject: [PATCH] client_(attach,commit,create,diff): Wrap result and options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Paweł Gronowski --- client/client_interfaces.go | 9 +- client/container_attach.go | 16 ++- client/container_commit.go | 17 ++- client/container_create.go | 31 +++-- client/container_create_opts.go | 22 ++++ client/container_create_test.go | 16 +-- client/container_diff.go | 10 +- client/container_diff_opts.go | 13 ++ client/container_diff_test.go | 10 +- integration-cli/docker_api_containers_test.go | 112 ++++++++++++---- .../docker_api_containers_windows_test.go | 27 ++-- integration-cli/docker_cli_volume_test.go | 7 +- integration/container/attach_test.go | 24 ++-- integration/container/create_test.go | 123 ++++++++---------- integration/container/diff_test.go | 9 +- integration/container/ipcmode_linux_test.go | 25 +++- integration/container/mounts_linux_test.go | 25 +++- integration/container/overlayfs_linux_test.go | 2 +- integration/container/restart_test.go | 5 +- integration/daemon/default_storage_test.go | 13 +- integration/internal/container/container.go | 20 ++- .../network/bridge/bridge_linux_test.go | 8 +- integration/plugin/logging/read_test.go | 13 +- integration/volume/mount_test.go | 14 +- internal/testutil/fakestorage/storage.go | 9 +- .../moby/moby/client/client_interfaces.go | 9 +- .../moby/moby/client/container_attach.go | 16 ++- .../moby/moby/client/container_commit.go | 17 ++- .../moby/moby/client/container_create.go | 31 +++-- .../moby/moby/client/container_create_opts.go | 22 ++++ .../moby/moby/client/container_diff.go | 10 +- .../moby/moby/client/container_diff_opts.go | 13 ++ 32 files changed, 460 insertions(+), 238 deletions(-) create mode 100644 client/container_create_opts.go create mode 100644 client/container_diff_opts.go create mode 100644 vendor/github.com/moby/moby/client/container_create_opts.go create mode 100644 vendor/github.com/moby/moby/client/container_diff_opts.go diff --git a/client/client_interfaces.go b/client/client_interfaces.go index 14d59b642e..a038c33c6e 100644 --- a/client/client_interfaces.go +++ b/client/client_interfaces.go @@ -12,7 +12,6 @@ import ( "github.com/moby/moby/api/types/registry" "github.com/moby/moby/api/types/swarm" "github.com/moby/moby/api/types/system" - ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) // APIClient is an interface that clients that talk with a docker server must implement. @@ -58,10 +57,10 @@ type HijackDialer interface { // ContainerAPIClient defines API client methods for the containers type ContainerAPIClient interface { - ContainerAttach(ctx context.Context, container string, options ContainerAttachOptions) (HijackedResponse, error) - ContainerCommit(ctx context.Context, container string, options ContainerCommitOptions) (container.CommitResponse, error) - ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error) - ContainerDiff(ctx context.Context, container string) ([]container.FilesystemChange, error) + ContainerAttach(ctx context.Context, container string, options ContainerAttachOptions) (ContainerAttachResult, error) + ContainerCommit(ctx context.Context, container string, options ContainerCommitOptions) (ContainerCommitResult, error) + ContainerCreate(ctx context.Context, options ContainerCreateOptions) (ContainerCreateResult, error) + ContainerDiff(ctx context.Context, container string, options ContainerDiffOptions) (ContainerDiffResult, error) ExecAPIClient ContainerExport(ctx context.Context, container string) (io.ReadCloser, error) ContainerInspect(ctx context.Context, container string) (container.InspectResponse, error) diff --git a/client/container_attach.go b/client/container_attach.go index a4ffb3e64d..fa3a2efcc9 100644 --- a/client/container_attach.go +++ b/client/container_attach.go @@ -16,6 +16,11 @@ type ContainerAttachOptions struct { Logs bool } +// ContainerAttachResult is the result from attaching to a container. +type ContainerAttachResult struct { + HijackedResponse +} + // ContainerAttach attaches a connection to a container in the server. // It returns a [HijackedResponse] with the hijacked connection // and a reader to get output. It's up to the called to close @@ -44,10 +49,10 @@ type ContainerAttachOptions struct { // [stdcopy.StdType]: https://pkg.go.dev/github.com/moby/moby/api/pkg/stdcopy#StdType // [Stdout]: https://pkg.go.dev/github.com/moby/moby/api/pkg/stdcopy#Stdout // [Stderr]: https://pkg.go.dev/github.com/moby/moby/api/pkg/stdcopy#Stderr -func (cli *Client) ContainerAttach(ctx context.Context, containerID string, options ContainerAttachOptions) (HijackedResponse, error) { +func (cli *Client) ContainerAttach(ctx context.Context, containerID string, options ContainerAttachOptions) (ContainerAttachResult, error) { containerID, err := trimID("container", containerID) if err != nil { - return HijackedResponse{}, err + return ContainerAttachResult{}, err } query := url.Values{} @@ -70,7 +75,12 @@ func (cli *Client) ContainerAttach(ctx context.Context, containerID string, opti query.Set("logs", "1") } - return cli.postHijacked(ctx, "/containers/"+containerID+"/attach", query, nil, http.Header{ + hijacked, err := cli.postHijacked(ctx, "/containers/"+containerID+"/attach", query, nil, http.Header{ "Content-Type": {"text/plain"}, }) + if err != nil { + return ContainerAttachResult{}, err + } + + return ContainerAttachResult{HijackedResponse: hijacked}, nil } diff --git a/client/container_commit.go b/client/container_commit.go index d948fbd41d..79da44a54f 100644 --- a/client/container_commit.go +++ b/client/container_commit.go @@ -20,22 +20,27 @@ type ContainerCommitOptions struct { Config *container.Config } +// ContainerCommitResult is the result from committing a container. +type ContainerCommitResult struct { + ID string +} + // ContainerCommit applies changes to a container and creates a new tagged image. -func (cli *Client) ContainerCommit(ctx context.Context, containerID string, options ContainerCommitOptions) (container.CommitResponse, error) { +func (cli *Client) ContainerCommit(ctx context.Context, containerID string, options ContainerCommitOptions) (ContainerCommitResult, error) { containerID, err := trimID("container", containerID) if err != nil { - return container.CommitResponse{}, err + return ContainerCommitResult{}, err } var repository, tag string if options.Reference != "" { ref, err := reference.ParseNormalizedNamed(options.Reference) if err != nil { - return container.CommitResponse{}, err + return ContainerCommitResult{}, err } if _, ok := ref.(reference.Digested); ok { - return container.CommitResponse{}, errors.New("refusing to create a tag with a digest reference") + return ContainerCommitResult{}, errors.New("refusing to create a tag with a digest reference") } ref = reference.TagNameOnly(ref) @@ -62,9 +67,9 @@ func (cli *Client) ContainerCommit(ctx context.Context, containerID string, opti resp, err := cli.post(ctx, "/commit", query, options.Config, nil) defer ensureReaderClosed(resp) if err != nil { - return response, err + return ContainerCommitResult{}, err } err = json.NewDecoder(resp.Body).Decode(&response) - return response, err + return ContainerCommitResult{ID: response.ID}, err } diff --git a/client/container_create.go b/client/container_create.go index 9d72c636ee..3181685ce5 100644 --- a/client/container_create.go +++ b/client/container_create.go @@ -10,49 +10,48 @@ import ( cerrdefs "github.com/containerd/errdefs" "github.com/moby/moby/api/types/container" - "github.com/moby/moby/api/types/network" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) // ContainerCreate creates a new container based on the given configuration. // It can be associated with a name, but it's not mandatory. -func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error) { - if config == nil { - return container.CreateResponse{}, cerrdefs.ErrInvalidArgument.WithMessage("config is nil") +func (cli *Client) ContainerCreate(ctx context.Context, options ContainerCreateOptions) (ContainerCreateResult, error) { + if options.Config == nil { + return ContainerCreateResult{}, cerrdefs.ErrInvalidArgument.WithMessage("config is nil") } var response container.CreateResponse - if hostConfig != nil { - hostConfig.CapAdd = normalizeCapabilities(hostConfig.CapAdd) - hostConfig.CapDrop = normalizeCapabilities(hostConfig.CapDrop) + if options.HostConfig != nil { + options.HostConfig.CapAdd = normalizeCapabilities(options.HostConfig.CapAdd) + options.HostConfig.CapDrop = normalizeCapabilities(options.HostConfig.CapDrop) } query := url.Values{} - if platform != nil { - if p := formatPlatform(*platform); p != "unknown" { + if options.Platform != nil { + if p := formatPlatform(*options.Platform); p != "unknown" { query.Set("platform", p) } } - if containerName != "" { - query.Set("name", containerName) + if options.ContainerName != "" { + query.Set("name", options.ContainerName) } body := container.CreateRequest{ - Config: config, - HostConfig: hostConfig, - NetworkingConfig: networkingConfig, + Config: options.Config, + HostConfig: options.HostConfig, + NetworkingConfig: options.NetworkingConfig, } resp, err := cli.post(ctx, "/containers/create", query, body, nil) defer ensureReaderClosed(resp) if err != nil { - return response, err + return ContainerCreateResult{}, err } err = json.NewDecoder(resp.Body).Decode(&response) - return response, err + return ContainerCreateResult{ID: response.ID, Warnings: response.Warnings}, err } // formatPlatform returns a formatted string representing platform (e.g., "linux/arm/v7"). diff --git a/client/container_create_opts.go b/client/container_create_opts.go new file mode 100644 index 0000000000..249860e845 --- /dev/null +++ b/client/container_create_opts.go @@ -0,0 +1,22 @@ +package client + +import ( + "github.com/moby/moby/api/types/container" + "github.com/moby/moby/api/types/network" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +// ContainerCreateOptions holds parameters to create a container. +type ContainerCreateOptions struct { + Config *container.Config + HostConfig *container.HostConfig + NetworkingConfig *network.NetworkingConfig + Platform *ocispec.Platform + ContainerName string +} + +// ContainerCreateResult is the result from creating a container. +type ContainerCreateResult struct { + ID string + Warnings []string +} diff --git a/client/container_create_test.go b/client/container_create_test.go index a9b63b0529..79a768a02f 100644 --- a/client/container_create_test.go +++ b/client/container_create_test.go @@ -22,11 +22,11 @@ func TestContainerCreateError(t *testing.T) { ) assert.NilError(t, err) - _, err = client.ContainerCreate(context.Background(), nil, nil, nil, nil, "nothing") + _, err = client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: nil, ContainerName: "nothing"}) assert.Error(t, err, "config is nil") assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument)) - _, err = client.ContainerCreate(context.Background(), &container.Config{}, nil, nil, nil, "nothing") + _, err = client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{}, ContainerName: "nothing"}) assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal)) // 404 doesn't automatically means an unknown image @@ -35,7 +35,7 @@ func TestContainerCreateError(t *testing.T) { ) assert.NilError(t, err) - _, err = client.ContainerCreate(context.Background(), &container.Config{}, nil, nil, nil, "nothing") + _, err = client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{}, ContainerName: "nothing"}) assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound)) } @@ -45,7 +45,7 @@ func TestContainerCreateImageNotFound(t *testing.T) { ) assert.NilError(t, err) - _, err = client.ContainerCreate(context.Background(), &container.Config{Image: "unknown_image"}, nil, nil, nil, "unknown") + _, err = client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{Image: "unknown_image"}, ContainerName: "unknown"}) assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound)) } @@ -74,7 +74,7 @@ func TestContainerCreateWithName(t *testing.T) { ) assert.NilError(t, err) - r, err := client.ContainerCreate(context.Background(), &container.Config{}, nil, nil, nil, "container_name") + r, err := client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{}, ContainerName: "container_name"}) assert.NilError(t, err) assert.Check(t, is.Equal(r.ID, "container_id")) } @@ -103,7 +103,7 @@ func TestContainerCreateAutoRemove(t *testing.T) { ) assert.NilError(t, err) - resp, err := client.ContainerCreate(context.Background(), &container.Config{}, &container.HostConfig{AutoRemove: true}, nil, nil, "") + resp, err := client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{}, HostConfig: &container.HostConfig{AutoRemove: true}}) assert.NilError(t, err) assert.Check(t, is.Equal(resp.ID, "container_id")) } @@ -116,7 +116,7 @@ func TestContainerCreateConnectionError(t *testing.T) { client, err := NewClientWithOpts(WithAPIVersionNegotiation(), WithHost("tcp://no-such-host.invalid")) assert.NilError(t, err) - _, err = client.ContainerCreate(context.Background(), &container.Config{}, nil, nil, nil, "") + _, err = client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{}}) assert.Check(t, is.ErrorType(err, IsErrConnectionFailed)) } @@ -165,6 +165,6 @@ func TestContainerCreateCapabilities(t *testing.T) { ) assert.NilError(t, err) - _, err = client.ContainerCreate(context.Background(), &container.Config{}, &container.HostConfig{CapAdd: inputCaps, CapDrop: inputCaps}, nil, nil, "") + _, err = client.ContainerCreate(context.Background(), ContainerCreateOptions{Config: &container.Config{}, HostConfig: &container.HostConfig{CapAdd: inputCaps, CapDrop: inputCaps}}) assert.NilError(t, err) } diff --git a/client/container_diff.go b/client/container_diff.go index c87c6e4be9..ec904337e0 100644 --- a/client/container_diff.go +++ b/client/container_diff.go @@ -9,22 +9,22 @@ import ( ) // ContainerDiff shows differences in a container filesystem since it was started. -func (cli *Client) ContainerDiff(ctx context.Context, containerID string) ([]container.FilesystemChange, error) { +func (cli *Client) ContainerDiff(ctx context.Context, containerID string, options ContainerDiffOptions) (ContainerDiffResult, error) { containerID, err := trimID("container", containerID) if err != nil { - return nil, err + return ContainerDiffResult{}, err } resp, err := cli.get(ctx, "/containers/"+containerID+"/changes", url.Values{}, nil) defer ensureReaderClosed(resp) if err != nil { - return nil, err + return ContainerDiffResult{}, err } var changes []container.FilesystemChange err = json.NewDecoder(resp.Body).Decode(&changes) if err != nil { - return nil, err + return ContainerDiffResult{}, err } - return changes, err + return ContainerDiffResult{Changes: changes}, err } diff --git a/client/container_diff_opts.go b/client/container_diff_opts.go new file mode 100644 index 0000000000..5e3c37ab4e --- /dev/null +++ b/client/container_diff_opts.go @@ -0,0 +1,13 @@ +package client + +import "github.com/moby/moby/api/types/container" + +// ContainerDiffOptions holds parameters to show differences in a container filesystem. +type ContainerDiffOptions struct { + // Currently no options, but this allows for future extensibility +} + +// ContainerDiffResult is the result from showing differences in a container filesystem. +type ContainerDiffResult struct { + Changes []container.FilesystemChange +} diff --git a/client/container_diff_test.go b/client/container_diff_test.go index e96df1f75e..7aa153e6cd 100644 --- a/client/container_diff_test.go +++ b/client/container_diff_test.go @@ -20,14 +20,14 @@ func TestContainerDiffError(t *testing.T) { ) assert.NilError(t, err) - _, err = client.ContainerDiff(context.Background(), "nothing") + _, err = client.ContainerDiff(context.Background(), "nothing", ContainerDiffOptions{}) assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal)) - _, err = client.ContainerDiff(context.Background(), "") + _, err = client.ContainerDiff(context.Background(), "", ContainerDiffOptions{}) assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument)) assert.Check(t, is.ErrorContains(err, "value is empty")) - _, err = client.ContainerDiff(context.Background(), " ") + _, err = client.ContainerDiff(context.Background(), " ", ContainerDiffOptions{}) assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument)) assert.Check(t, is.ErrorContains(err, "value is empty")) } @@ -67,7 +67,7 @@ func TestContainerDiff(t *testing.T) { ) assert.NilError(t, err) - changes, err := client.ContainerDiff(context.Background(), "container_id") + result, err := client.ContainerDiff(context.Background(), "container_id", ContainerDiffOptions{}) assert.NilError(t, err) - assert.Check(t, is.DeepEqual(changes, expected)) + assert.Check(t, is.DeepEqual(result.Changes, expected)) } diff --git a/integration-cli/docker_api_containers_test.go b/integration-cli/docker_api_containers_test.go index 54fc412fee..e6ca3eedb7 100644 --- a/integration-cli/docker_api_containers_test.go +++ b/integration-cli/docker_api_containers_test.go @@ -130,12 +130,12 @@ func (s *DockerAPISuite) TestContainerAPIGetChanges(c *testing.T) { assert.NilError(c, err) defer apiClient.Close() - changes, err := apiClient.ContainerDiff(testutil.GetContext(c), name) + result, err := apiClient.ContainerDiff(testutil.GetContext(c), name, client.ContainerDiffOptions{}) assert.NilError(c, err) // Check the changelog for removal of /etc/passwd success := false - for _, elem := range changes { + for _, elem := range result.Changes { if elem.Path == "/etc/passwd" && elem.Kind == 2 { success = true } @@ -517,7 +517,11 @@ func (s *DockerAPISuite) TestContainerAPIBadPort(c *testing.T) { assert.NilError(c, err) defer apiClient.Close() - _, err = apiClient.ContainerCreate(testutil.GetContext(c), &config, &hostConfig, &network.NetworkingConfig{}, nil, "") + _, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{ + Config: &config, + HostConfig: &hostConfig, + NetworkingConfig: &network.NetworkingConfig{}, + }) assert.ErrorContains(c, err, `invalid port specification: "aa80"`) } @@ -531,7 +535,11 @@ func (s *DockerAPISuite) TestContainerAPICreate(c *testing.T) { assert.NilError(c, err) defer apiClient.Close() - ctr, err := apiClient.ContainerCreate(testutil.GetContext(c), &config, &container.HostConfig{}, &network.NetworkingConfig{}, nil, "") + ctr, err := apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{ + Config: &config, + HostConfig: &container.HostConfig{}, + NetworkingConfig: &network.NetworkingConfig{}, + }) assert.NilError(c, err) out := cli.DockerCmd(c, "start", "-a", ctr.ID).Stdout() @@ -543,7 +551,11 @@ func (s *DockerAPISuite) TestContainerAPICreateEmptyConfig(c *testing.T) { assert.NilError(c, err) defer apiClient.Close() - _, err = apiClient.ContainerCreate(testutil.GetContext(c), &container.Config{}, &container.HostConfig{}, &network.NetworkingConfig{}, nil, "") + _, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{ + Config: &container.Config{}, + HostConfig: &container.HostConfig{}, + NetworkingConfig: &network.NetworkingConfig{}, + }) assert.ErrorContains(c, err, "no command specified") } @@ -574,7 +586,11 @@ func UtilCreateNetworkMode(t *testing.T, networkMode container.NetworkMode) { assert.NilError(t, err) defer apiClient.Close() - ctr, err := apiClient.ContainerCreate(testutil.GetContext(t), &config, &hostConfig, &network.NetworkingConfig{}, nil, "") + ctr, err := apiClient.ContainerCreate(testutil.GetContext(t), client.ContainerCreateOptions{ + Config: &config, + HostConfig: &hostConfig, + NetworkingConfig: &network.NetworkingConfig{}, + }) assert.NilError(t, err) containerJSON, err := apiClient.ContainerInspect(testutil.GetContext(t), ctr.ID) @@ -601,7 +617,11 @@ func (s *DockerAPISuite) TestContainerAPICreateWithCpuSharesCpuset(c *testing.T) assert.NilError(c, err) defer apiClient.Close() - ctr, err := apiClient.ContainerCreate(testutil.GetContext(c), &config, &hostConfig, &network.NetworkingConfig{}, nil, "") + ctr, err := apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{ + Config: &config, + HostConfig: &hostConfig, + NetworkingConfig: &network.NetworkingConfig{}, + }) assert.NilError(c, err) containerJSON, err := apiClient.ContainerInspect(testutil.GetContext(c), ctr.ID) @@ -746,7 +766,12 @@ func (s *DockerAPISuite) TestContainerAPIStart(c *testing.T) { assert.NilError(c, err) defer apiClient.Close() - _, err = apiClient.ContainerCreate(testutil.GetContext(c), &config, &container.HostConfig{}, &network.NetworkingConfig{}, nil, name) + _, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{ + Config: &config, + HostConfig: &container.HostConfig{}, + NetworkingConfig: &network.NetworkingConfig{}, + ContainerName: name, + }) assert.NilError(c, err) err = apiClient.ContainerStart(testutil.GetContext(c), name, client.ContainerStartOptions{}) @@ -989,7 +1014,12 @@ func (s *DockerAPISuite) TestPostContainersCreateWithWrongCpusetValues(c *testin } const name = "wrong-cpuset-cpus" - _, err = apiClient.ContainerCreate(testutil.GetContext(c), &config, &hostConfig1, &network.NetworkingConfig{}, nil, name) + _, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{ + Config: &config, + HostConfig: &hostConfig1, + NetworkingConfig: &network.NetworkingConfig{}, + ContainerName: name, + }) expected := "Invalid value 1-42,, for cpuset cpus" assert.ErrorContains(c, err, expected) @@ -999,7 +1029,12 @@ func (s *DockerAPISuite) TestPostContainersCreateWithWrongCpusetValues(c *testin }, } const name2 = "wrong-cpuset-mems" - _, err = apiClient.ContainerCreate(testutil.GetContext(c), &config, &hostConfig2, &network.NetworkingConfig{}, nil, name2) + _, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{ + Config: &config, + HostConfig: &hostConfig2, + NetworkingConfig: &network.NetworkingConfig{}, + ContainerName: name2, + }) expected = "Invalid value 42-3,1-- for cpuset mems" assert.ErrorContains(c, err, expected) } @@ -1015,7 +1050,11 @@ func (s *DockerAPISuite) TestPostContainersCreateMemorySwappinessHostConfigOmitt assert.NilError(c, err) defer apiClient.Close() - ctr, err := apiClient.ContainerCreate(testutil.GetContext(c), &config, &container.HostConfig{}, &network.NetworkingConfig{}, nil, "") + ctr, err := apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{ + Config: &config, + HostConfig: &container.HostConfig{}, + NetworkingConfig: &network.NetworkingConfig{}, + }) assert.NilError(c, err) containerJSON, err := apiClient.ContainerInspect(testutil.GetContext(c), ctr.ID) @@ -1042,7 +1081,12 @@ func (s *DockerAPISuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c * defer apiClient.Close() const name = "oomscoreadj-over" - _, err = apiClient.ContainerCreate(testutil.GetContext(c), &config, &hostConfig, &network.NetworkingConfig{}, nil, name) + _, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{ + Config: &config, + HostConfig: &hostConfig, + NetworkingConfig: &network.NetworkingConfig{}, + ContainerName: name, + }) expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]" assert.ErrorContains(c, err, expected) @@ -1052,7 +1096,12 @@ func (s *DockerAPISuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c * } const name2 = "oomscoreadj-low" - _, err = apiClient.ContainerCreate(testutil.GetContext(c), &config, &hostConfig, &network.NetworkingConfig{}, nil, name2) + _, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{ + Config: &config, + HostConfig: &hostConfig, + NetworkingConfig: &network.NetworkingConfig{}, + ContainerName: name2, + }) expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]" assert.ErrorContains(c, err, expected) @@ -1085,7 +1134,12 @@ func (s *DockerAPISuite) TestContainerAPIStatsWithNetworkDisabled(c *testing.T) assert.NilError(c, err) defer apiClient.Close() - _, err = apiClient.ContainerCreate(testutil.GetContext(c), &config, &container.HostConfig{}, &network.NetworkingConfig{}, nil, name) + _, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{ + Config: &config, + HostConfig: &container.HostConfig{}, + NetworkingConfig: &network.NetworkingConfig{}, + ContainerName: name, + }) assert.NilError(c, err) err = apiClient.ContainerStart(testutil.GetContext(c), name, client.ContainerStartOptions{}) @@ -1421,7 +1475,11 @@ func (s *DockerAPISuite) TestContainersAPICreateMountsValidation(c *testing.T) { // TODO add checks for statuscode returned by API for i, tc := range tests { c.Run(fmt.Sprintf("case %d", i), func(c *testing.T) { - _, err = apiClient.ContainerCreate(testutil.GetContext(c), &tc.config, &tc.hostConfig, &network.NetworkingConfig{}, nil, "") + _, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{ + Config: &tc.config, + HostConfig: &tc.hostConfig, + NetworkingConfig: &network.NetworkingConfig{}, + }) if tc.msg != "" { assert.ErrorContains(c, err, tc.msg, "%v", tests[i].config) } else { @@ -1454,7 +1512,12 @@ func (s *DockerAPISuite) TestContainerAPICreateMountsBindRead(c *testing.T) { assert.NilError(c, err) defer apiClient.Close() - _, err = apiClient.ContainerCreate(testutil.GetContext(c), &config, &hostConfig, &network.NetworkingConfig{}, nil, "test") + _, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{ + Config: &config, + HostConfig: &hostConfig, + NetworkingConfig: &network.NetworkingConfig{}, + ContainerName: "test", + }) assert.NilError(c, err) out := cli.DockerCmd(c, "start", "-a", "test").Combined() @@ -1590,11 +1653,11 @@ func (s *DockerAPISuite) TestContainersAPICreateMountsCreate(c *testing.T) { c.Run(fmt.Sprintf("%d config: %v", i, tc.spec), func(c *testing.T) { ctr, err := apiclient.ContainerCreate( ctx, - &container.Config{Image: testImg}, - &container.HostConfig{Mounts: []mount.Mount{tc.spec}}, - &network.NetworkingConfig{}, - nil, - "") + client.ContainerCreateOptions{ + Config: &container.Config{Image: testImg}, + HostConfig: &container.HostConfig{Mounts: []mount.Mount{tc.spec}}, + NetworkingConfig: &network.NetworkingConfig{}, + }) assert.NilError(c, err) containerInspect, err := apiclient.ContainerInspect(ctx, ctr.ID) @@ -1705,7 +1768,12 @@ func (s *DockerAPISuite) TestContainersAPICreateMountsTmpfs(c *testing.T) { Mounts: []mount.Mount{x.cfg}, } - _, err = apiClient.ContainerCreate(testutil.GetContext(c), &config, &hostConfig, &network.NetworkingConfig{}, nil, cName) + _, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{ + Config: &config, + HostConfig: &hostConfig, + NetworkingConfig: &network.NetworkingConfig{}, + ContainerName: cName, + }) assert.NilError(c, err) out := cli.DockerCmd(c, "start", "-a", cName).Combined() for _, option := range x.expectedOptions { diff --git a/integration-cli/docker_api_containers_windows_test.go b/integration-cli/docker_api_containers_windows_test.go index b7419e05e4..e3ad509414 100644 --- a/integration-cli/docker_api_containers_windows_test.go +++ b/integration-cli/docker_api_containers_windows_test.go @@ -12,6 +12,7 @@ import ( "github.com/Microsoft/go-winio" "github.com/moby/moby/api/types/container" "github.com/moby/moby/api/types/mount" + "github.com/moby/moby/api/types/network" "github.com/moby/moby/client" "github.com/moby/moby/v2/internal/testutil" "github.com/pkg/errors" @@ -51,19 +52,23 @@ func (s *DockerAPISuite) TestContainersAPICreateMountsBindNamedPipe(c *testing.T ctx := testutil.GetContext(c) apiClient := testEnv.APIClient() _, err = apiClient.ContainerCreate(ctx, - &container.Config{ - Image: testEnv.PlatformDefaults.BaseImage, - Cmd: []string{"cmd", "/c", cmd}, - }, &container.HostConfig{ - Mounts: []mount.Mount{ - { - Type: "npipe", - Source: hostPipeName, - Target: containerPipeName, + client.ContainerCreateOptions{ + Config: &container.Config{ + Image: testEnv.PlatformDefaults.BaseImage, + Cmd: []string{"cmd", "/c", cmd}, + }, + HostConfig: &container.HostConfig{ + Mounts: []mount.Mount{ + { + Type: "npipe", + Source: hostPipeName, + Target: containerPipeName, + }, }, }, - }, - nil, nil, name) + NetworkingConfig: &network.NetworkingConfig{}, + ContainerName: name, + }) assert.NilError(c, err) err = apiClient.ContainerStart(ctx, name, client.ContainerStartOptions{}) diff --git a/integration-cli/docker_cli_volume_test.go b/integration-cli/docker_cli_volume_test.go index dcd7640011..b7e2217b7d 100644 --- a/integration-cli/docker_cli_volume_test.go +++ b/integration-cli/docker_cli_volume_test.go @@ -596,7 +596,12 @@ func (s *DockerCLIVolumeSuite) TestDuplicateMountpointsForVolumesFromAndMounts(c }, }, } - _, err = apiClient.ContainerCreate(testutil.GetContext(c), &config, &hostConfig, &network.NetworkingConfig{}, nil, "app") + _, err = apiClient.ContainerCreate(testutil.GetContext(c), client.ContainerCreateOptions{ + Config: &config, + HostConfig: &hostConfig, + NetworkingConfig: &network.NetworkingConfig{}, + ContainerName: "app", + }) assert.NilError(c, err) diff --git a/integration/container/attach_test.go b/integration/container/attach_test.go index c26ae78d70..3ea917e97c 100644 --- a/integration/container/attach_test.go +++ b/integration/container/attach_test.go @@ -41,17 +41,15 @@ func TestAttach(t *testing.T) { t.Parallel() ctx := testutil.StartSpan(ctx, t) - resp, err := apiClient.ContainerCreate(ctx, - &container.Config{ + resp, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &container.Config{ Image: "busybox", Cmd: []string{"echo", "hello"}, Tty: tc.tty, }, - &container.HostConfig{}, - &network.NetworkingConfig{}, - nil, - "", - ) + HostConfig: &container.HostConfig{}, + NetworkingConfig: &network.NetworkingConfig{}, + }) assert.NilError(t, err) attach, err := apiClient.ContainerAttach(ctx, resp.ID, client.ContainerAttachOptions{ Stdout: true, @@ -81,16 +79,14 @@ func TestAttachDisconnectLeak(t *testing.T) { apiClient := d.NewClientT(t) - resp, err := apiClient.ContainerCreate(ctx, - &container.Config{ + resp, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &container.Config{ Image: "busybox", Cmd: []string{"/bin/sh", "-c", "while true; usleep 100000; done"}, }, - &container.HostConfig{}, - &network.NetworkingConfig{}, - nil, - "", - ) + HostConfig: &container.HostConfig{}, + NetworkingConfig: &network.NetworkingConfig{}, + }) assert.NilError(t, err) cID := resp.ID defer apiClient.ContainerRemove(ctx, cID, client.ContainerRemoveOptions{ diff --git a/integration/container/create_test.go b/integration/container/create_test.go index c0dc2b37a1..b7363e8613 100644 --- a/integration/container/create_test.go +++ b/integration/container/create_test.go @@ -59,13 +59,11 @@ func TestCreateFailsWhenIdentifierDoesNotExist(t *testing.T) { t.Run(tc.doc, func(t *testing.T) { t.Parallel() ctx := testutil.StartSpan(ctx, t) - _, err := apiClient.ContainerCreate(ctx, - &container.Config{Image: tc.image}, - &container.HostConfig{}, - &network.NetworkingConfig{}, - nil, - "", - ) + _, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &container.Config{Image: tc.image}, + HostConfig: &container.HostConfig{}, + NetworkingConfig: &network.NetworkingConfig{}, + }) assert.Check(t, is.ErrorContains(err, tc.expectedError)) assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound)) }) @@ -125,15 +123,11 @@ func TestCreateByImageID(t *testing.T) { t.Run(tc.doc, func(t *testing.T) { t.Parallel() ctx := testutil.StartSpan(ctx, t) - resp, err := apiClient.ContainerCreate(ctx, - &container.Config{Image: tc.image}, - &container.HostConfig{}, - &network.NetworkingConfig{}, - nil, - "", - ) + resp, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &container.Config{Image: tc.image}, + }) if tc.expectedErr != "" { - assert.Check(t, is.DeepEqual(resp, container.CreateResponse{})) + assert.Check(t, is.DeepEqual(resp, client.ContainerCreateResult{})) assert.Check(t, is.Error(err, tc.expectedErr)) assert.Check(t, is.ErrorType(err, tc.expectedErrType)) } else { @@ -154,17 +148,14 @@ func TestCreateLinkToNonExistingContainer(t *testing.T) { ctx := setupTest(t) apiClient := testEnv.APIClient() - _, err := apiClient.ContainerCreate(ctx, - &container.Config{ + _, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &container.Config{ Image: "busybox", }, - &container.HostConfig{ + HostConfig: &container.HostConfig{ Links: []string{"no-such-container"}, }, - &network.NetworkingConfig{}, - nil, - "", - ) + }) assert.Check(t, is.ErrorContains(err, "could not get container for no-such-container")) assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument)) } @@ -195,16 +186,12 @@ func TestCreateWithInvalidEnv(t *testing.T) { t.Run(strconv.Itoa(index), func(t *testing.T) { t.Parallel() ctx := testutil.StartSpan(ctx, t) - _, err := apiClient.ContainerCreate(ctx, - &container.Config{ + _, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &container.Config{ Image: "busybox", Env: []string{tc.env}, }, - &container.HostConfig{}, - &network.NetworkingConfig{}, - nil, - "", - ) + }) assert.Check(t, is.ErrorContains(err, tc.expectedError)) assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument)) }) @@ -241,17 +228,14 @@ func TestCreateTmpfsMountsTarget(t *testing.T) { } for _, tc := range testCases { - _, err := apiClient.ContainerCreate(ctx, - &container.Config{ + _, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &container.Config{ Image: "busybox", }, - &container.HostConfig{ + HostConfig: &container.HostConfig{ Tmpfs: map[string]string{tc.target: ""}, }, - &network.NetworkingConfig{}, - nil, - "", - ) + }) assert.Check(t, is.ErrorContains(err, tc.expectedError)) assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument)) } @@ -298,19 +282,17 @@ func TestCreateWithCustomMaskedPaths(t *testing.T) { t.Parallel() // Create the container. - ctr, err := apiClient.ContainerCreate(ctx, - &container.Config{ + ctr, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &container.Config{ Image: "busybox", Cmd: []string{"true"}, }, - &container.HostConfig{ + HostConfig: &container.HostConfig{ Privileged: tc.privileged, MaskedPaths: tc.maskedPaths, }, - nil, - nil, - fmt.Sprintf("create-masked-paths-%d", i), - ) + ContainerName: fmt.Sprintf("create-masked-paths-%d", i), + }) assert.NilError(t, err) ctrInspect, err := apiClient.ContainerInspect(ctx, ctr.ID) @@ -371,19 +353,17 @@ func TestCreateWithCustomReadonlyPaths(t *testing.T) { for i, tc := range testCases { t.Run(tc.doc, func(t *testing.T) { t.Parallel() - ctr, err := apiClient.ContainerCreate(ctx, - &container.Config{ + ctr, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &container.Config{ Image: "busybox", Cmd: []string{"true"}, }, - &container.HostConfig{ + HostConfig: &container.HostConfig{ Privileged: tc.privileged, ReadonlyPaths: tc.readonlyPaths, }, - nil, - nil, - fmt.Sprintf("create-readonly-paths-%d", i), - ) + ContainerName: fmt.Sprintf("create-readonly-paths-%d", i), + }) assert.NilError(t, err) ctrInspect, err := apiClient.ContainerInspect(ctx, ctr.ID) @@ -482,7 +462,9 @@ func TestCreateWithInvalidHealthcheckParams(t *testing.T) { cfg.Healthcheck.StartPeriod = tc.startPeriod } - resp, err := apiClient.ContainerCreate(ctx, &cfg, &container.HostConfig{}, nil, nil, "") + resp, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &cfg, + }) assert.Check(t, is.Equal(len(resp.Warnings), 0)) assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument)) assert.ErrorContains(t, err, tc.expectedErr) @@ -553,7 +535,10 @@ func TestCreateDifferentPlatform(t *testing.T) { Architecture: img.Architecture, Variant: img.Variant, } - _, err := apiClient.ContainerCreate(ctx, &container.Config{Image: "busybox:latest"}, &container.HostConfig{}, nil, &p, "") + _, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &container.Config{Image: "busybox:latest"}, + Platform: &p, + }) assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound)) }) t.Run("different cpu arch", func(t *testing.T) { @@ -563,7 +548,10 @@ func TestCreateDifferentPlatform(t *testing.T) { Architecture: img.Architecture + "DifferentArch", Variant: img.Variant, } - _, err := apiClient.ContainerCreate(ctx, &container.Config{Image: "busybox:latest"}, &container.HostConfig{}, nil, &p, "") + _, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &container.Config{Image: "busybox:latest"}, + Platform: &p, + }) assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound)) }) } @@ -574,12 +562,10 @@ func TestCreateVolumesFromNonExistingContainer(t *testing.T) { _, err := apiClient.ContainerCreate( ctx, - &container.Config{Image: "busybox"}, - &container.HostConfig{VolumesFrom: []string{"nosuchcontainer"}}, - nil, - nil, - "", - ) + client.ContainerCreateOptions{ + Config: &container.Config{Image: "busybox"}, + HostConfig: &container.HostConfig{VolumesFrom: []string{"nosuchcontainer"}}, + }) assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument)) } @@ -594,12 +580,9 @@ func TestCreatePlatformSpecificImageNoPlatform(t *testing.T) { _, err := apiClient.ContainerCreate( ctx, - &container.Config{Image: "arm32v7/hello-world"}, - &container.HostConfig{}, - nil, - nil, - "", - ) + client.ContainerCreateOptions{ + Config: &container.Config{Image: "arm32v7/hello-world"}, + }) assert.NilError(t, err) } @@ -653,7 +636,10 @@ func TestCreateInvalidHostConfig(t *testing.T) { cfg := container.Config{ Image: "busybox", } - resp, err := apiClient.ContainerCreate(ctx, &cfg, &tc.hc, nil, nil, "") + resp, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &cfg, + HostConfig: &tc.hc, + }) assert.Check(t, is.Equal(len(resp.Warnings), 0)) assert.Check(t, cerrdefs.IsInvalidArgument(err), "got: %T", err) assert.Error(t, err, tc.expectedErr) @@ -760,7 +746,10 @@ func TestCreateWithMultipleEndpointSettings(t *testing.T) { "net3": {}, }, } - _, err = apiClient.ContainerCreate(ctx, &config, &container.HostConfig{}, &networkingConfig, nil, "") + _, err = apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &config, + NetworkingConfig: &networkingConfig, + }) if tc.expectedErr == "" { assert.NilError(t, err) } else { diff --git a/integration/container/diff_test.go b/integration/container/diff_test.go index b15ed475ce..a46e068a20 100644 --- a/integration/container/diff_test.go +++ b/integration/container/diff_test.go @@ -5,6 +5,7 @@ import ( "time" containertypes "github.com/moby/moby/api/types/container" + "github.com/moby/moby/client" "github.com/moby/moby/v2/integration/internal/container" "gotest.tools/v3/assert" "gotest.tools/v3/poll" @@ -24,9 +25,9 @@ func TestDiff(t *testing.T) { } poll.WaitOn(t, container.IsStopped(ctx, apiClient, cID)) - items, err := apiClient.ContainerDiff(ctx, cID) + result, err := apiClient.ContainerDiff(ctx, cID, client.ContainerDiffOptions{}) assert.NilError(t, err) - assert.DeepEqual(t, expected, items) + assert.DeepEqual(t, expected, result.Changes) } func TestDiffStoppedContainer(t *testing.T) { @@ -51,7 +52,7 @@ func TestDiffStoppedContainer(t *testing.T) { } } - items, err := apiClient.ContainerDiff(ctx, cID) + result, err := apiClient.ContainerDiff(ctx, cID, client.ContainerDiffOptions{}) assert.NilError(t, err) - assert.DeepEqual(t, expected, items) + assert.DeepEqual(t, expected, result.Changes) } diff --git a/integration/container/ipcmode_linux_test.go b/integration/container/ipcmode_linux_test.go index 690d390817..8be264bdf1 100644 --- a/integration/container/ipcmode_linux_test.go +++ b/integration/container/ipcmode_linux_test.go @@ -64,7 +64,10 @@ func testIpcNonePrivateShareable(t *testing.T, mode string, mustBeMounted bool, } apiClient := testEnv.APIClient() - resp, err := apiClient.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "") + resp, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &cfg, + HostConfig: &hostCfg, + }) assert.NilError(t, err) assert.Check(t, is.Equal(len(resp.Warnings), 0)) @@ -135,7 +138,10 @@ func testIpcContainer(t *testing.T, donorMode string, mustWork bool) { apiClient := testEnv.APIClient() // create and start the "donor" container - resp, err := apiClient.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "") + resp, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &cfg, + HostConfig: &hostCfg, + }) assert.NilError(t, err) assert.Check(t, is.Equal(len(resp.Warnings), 0)) name1 := resp.ID @@ -145,7 +151,10 @@ func testIpcContainer(t *testing.T, donorMode string, mustWork bool) { // create and start the second container hostCfg.IpcMode = containertypes.IpcMode("container:" + name1) - resp, err = apiClient.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "") + resp, err = apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &cfg, + HostConfig: &hostCfg, + }) assert.NilError(t, err) assert.Check(t, is.Equal(len(resp.Warnings), 0)) name2 := resp.ID @@ -201,7 +210,10 @@ func TestAPIIpcModeHost(t *testing.T) { ctx := testutil.StartSpan(baseContext, t) apiClient := testEnv.APIClient() - resp, err := apiClient.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "") + resp, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &cfg, + HostConfig: &hostCfg, + }) assert.NilError(t, err) assert.Check(t, is.Equal(len(resp.Warnings), 0)) name := resp.ID @@ -237,7 +249,10 @@ func testDaemonIpcPrivateShareable(t *testing.T, mustBeShared bool, arg ...strin Cmd: []string{"top"}, } - resp, err := c.ContainerCreate(ctx, &cfg, &containertypes.HostConfig{}, nil, nil, "") + resp, err := c.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &cfg, + HostConfig: &containertypes.HostConfig{}, + }) assert.NilError(t, err) assert.Check(t, is.Equal(len(resp.Warnings), 0)) diff --git a/integration/container/mounts_linux_test.go b/integration/container/mounts_linux_test.go index 3d5c157d8a..5b68011b10 100644 --- a/integration/container/mounts_linux_test.go +++ b/integration/container/mounts_linux_test.go @@ -67,7 +67,11 @@ func TestContainerNetworkMountsNoChown(t *testing.T) { assert.NilError(t, err) defer cli.Close() - ctrCreate, err := cli.ContainerCreate(ctx, &config, &hostConfig, &network.NetworkingConfig{}, nil, "") + ctrCreate, err := cli.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &config, + HostConfig: &hostConfig, + NetworkingConfig: &network.NetworkingConfig{}, + }) assert.NilError(t, err) // container will exit immediately because of no tty, but we only need the start sequence to test the condition err = cli.ContainerStart(ctx, ctrCreate.ID, client.ContainerStartOptions{}) @@ -179,10 +183,13 @@ func TestMountDaemonRoot(t *testing.T) { ctx := testutil.StartSpan(ctx, t) - c, err := apiClient.ContainerCreate(ctx, &containertypes.Config{ - Image: "busybox", - Cmd: []string{"true"}, - }, hc, nil, nil, "") + c, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &containertypes.Config{ + Image: "busybox", + Cmd: []string{"true"}, + }, + HostConfig: hc, + }) if err != nil { if test.expected != "" { t.Fatal(err) @@ -430,7 +437,13 @@ func TestContainerVolumeAnonymous(t *testing.T) { }, }, })) - _, err := apiClient.ContainerCreate(ctx, config.Config, config.HostConfig, config.NetworkingConfig, config.Platform, config.Name) + _, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: config.Config, + HostConfig: config.HostConfig, + NetworkingConfig: config.NetworkingConfig, + Platform: config.Platform, + ContainerName: config.Name, + }) // We use [testNonExistingPlugin] for this, which produces an error // when used, which we use as indicator that the driver was passed // through. We should have a cleaner way for this, but that would diff --git a/integration/container/overlayfs_linux_test.go b/integration/container/overlayfs_linux_test.go index 88cf51086b..b7769e103c 100644 --- a/integration/container/overlayfs_linux_test.go +++ b/integration/container/overlayfs_linux_test.go @@ -28,7 +28,7 @@ func TestNoOverlayfsWarningsAboutUndefinedBehaviors(t *testing.T) { operation func(t *testing.T) error }{ {name: "diff", operation: func(*testing.T) error { - _, err := apiClient.ContainerDiff(ctx, cID) + _, err := apiClient.ContainerDiff(ctx, cID, client.ContainerDiffOptions{}) return err }}, {name: "export", operation: func(*testing.T) error { diff --git a/integration/container/restart_test.go b/integration/container/restart_test.go index 631802015e..9cd89d7362 100644 --- a/integration/container/restart_test.go +++ b/integration/container/restart_test.go @@ -100,7 +100,10 @@ func TestDaemonRestartKillContainers(t *testing.T) { Interval: 60 * time.Second, } } - resp, err := apiClient.ContainerCreate(ctx, &config, &hostConfig, nil, nil, "") + resp, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &config, + HostConfig: &hostConfig, + }) assert.NilError(t, err) defer apiClient.ContainerRemove(ctx, resp.ID, client.ContainerRemoveOptions{Force: true}) diff --git a/integration/daemon/default_storage_test.go b/integration/daemon/default_storage_test.go index e9dadfbcb7..beed5886d3 100644 --- a/integration/daemon/default_storage_test.go +++ b/integration/daemon/default_storage_test.go @@ -55,10 +55,13 @@ func TestGraphDriverPersistence(t *testing.T) { assert.Check(t, info.DriverStatus[0][1] != "io.containerd.snapshotter.v1") prevDriver := info.Driver - containerResp, err := c.ContainerCreate(ctx, &containertypes.Config{ - Image: testImage, - Cmd: []string{"echo", "test"}, - }, nil, nil, nil, "test-container") + containerResp, err := c.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &containertypes.Config{ + Image: testImage, + Cmd: []string{"echo", "test"}, + }, + ContainerName: "test-container", + }) assert.NilError(t, err, "Failed to create container") containerID := containerResp.ID @@ -141,7 +144,7 @@ func TestInspectGraphDriverAPIBC(t *testing.T) { } const testImage = "busybox:latest" - ctr, err := c.ContainerCreate(ctx, &containertypes.Config{Image: testImage}, nil, nil, nil, "test-container") + ctr, err := c.ContainerCreate(ctx, client.ContainerCreateOptions{Image: testImage, Name: "test-container"}) assert.NilError(t, err) defer func() { _ = c.ContainerRemove(ctx, ctr.ID, client.ContainerRemoveOptions{Force: true}) }() diff --git a/integration/internal/container/container.go b/integration/internal/container/container.go index df38a5f1ea..32fab876e0 100644 --- a/integration/internal/container/container.go +++ b/integration/internal/container/container.go @@ -55,7 +55,13 @@ func NewTestConfig(ops ...func(*TestContainerConfig)) *TestContainerConfig { func Create(ctx context.Context, t *testing.T, apiClient client.APIClient, ops ...func(*TestContainerConfig)) string { t.Helper() config := NewTestConfig(ops...) - c, err := apiClient.ContainerCreate(ctx, config.Config, config.HostConfig, config.NetworkingConfig, config.Platform, config.Name) + c, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: config.Config, + HostConfig: config.HostConfig, + NetworkingConfig: config.NetworkingConfig, + Platform: config.Platform, + ContainerName: config.Name, + }) assert.NilError(t, err) return c.ID @@ -67,8 +73,14 @@ func Create(ctx context.Context, t *testing.T, apiClient client.APIClient, ops . // // ctr, err := container.CreateFromConfig(ctx, apiClient, container.NewTestConfig(container.WithAutoRemove)) // assert.Check(t, err) -func CreateFromConfig(ctx context.Context, apiClient client.APIClient, config *TestContainerConfig) (container.CreateResponse, error) { - return apiClient.ContainerCreate(ctx, config.Config, config.HostConfig, config.NetworkingConfig, config.Platform, config.Name) +func CreateFromConfig(ctx context.Context, apiClient client.APIClient, config *TestContainerConfig) (client.ContainerCreateResult, error) { + return apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: config.Config, + HostConfig: config.HostConfig, + NetworkingConfig: config.NetworkingConfig, + Platform: config.Platform, + ContainerName: config.Name, + }) } // 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 err = apiClient.ContainerStart(ctx, id, client.ContainerStartOptions{}) assert.NilError(t, err) - s, err := demultiplexStreams(ctx, aresp) + s, err := demultiplexStreams(ctx, aresp.HijackedResponse) if !errors.Is(err, context.DeadlineExceeded) && !errors.Is(err, context.Canceled) { assert.NilError(t, err) } diff --git a/integration/network/bridge/bridge_linux_test.go b/integration/network/bridge/bridge_linux_test.go index 9e9d3bc598..f17eed2c3b 100644 --- a/integration/network/bridge/bridge_linux_test.go +++ b/integration/network/bridge/bridge_linux_test.go @@ -963,7 +963,13 @@ func TestEmptyPortBindingsBC(t *testing.T) { config := ctr.NewTestConfig(ctr.WithCmd("top"), ctr.WithExposedPorts("80/tcp"), ctr.WithPortMap(networktypes.PortMap{networktypes.MustParsePort("80/tcp"): pbs})) - c, err := apiClient.ContainerCreate(ctx, config.Config, config.HostConfig, config.NetworkingConfig, config.Platform, config.Name) + c, err := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: config.Config, + HostConfig: config.HostConfig, + NetworkingConfig: config.NetworkingConfig, + Platform: config.Platform, + ContainerName: config.Name, + }) assert.NilError(t, err) defer apiClient.ContainerRemove(ctx, c.ID, client.ContainerRemoveOptions{Force: true}) diff --git a/integration/plugin/logging/read_test.go b/integration/plugin/logging/read_test.go index ef800789a1..02836a50aa 100644 --- a/integration/plugin/logging/read_test.go +++ b/integration/plugin/logging/read_test.go @@ -54,13 +54,12 @@ func TestReadPluginNoRead(t *testing.T) { ctx := testutil.StartSpan(ctx, t) d.Start(t, append([]string{"--iptables=false", "--ip6tables=false"}, test.dOpts...)...) defer d.Stop(t) - c, err := apiclient.ContainerCreate(ctx, - cfg, - &container.HostConfig{LogConfig: container.LogConfig{Type: "test"}}, - nil, - nil, - "", - ) + c, err := apiclient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: cfg, + HostConfig: &container.HostConfig{ + LogConfig: container.LogConfig{Type: "test"}, + }, + }) assert.Assert(t, err) defer apiclient.ContainerRemove(ctx, c.ID, client.ContainerRemoveOptions{Force: true}) diff --git a/integration/volume/mount_test.go b/integration/volume/mount_test.go index 699c3550f5..433fe5d4ec 100644 --- a/integration/volume/mount_test.go +++ b/integration/volume/mount_test.go @@ -80,7 +80,12 @@ func TestRunMountVolumeSubdir(t *testing.T) { } ctrName := strings.ReplaceAll(t.Name(), "/", "_") - create, creatErr := apiClient.ContainerCreate(ctx, &cfg, &hostCfg, &network.NetworkingConfig{}, nil, ctrName) + create, creatErr := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &cfg, + HostConfig: &hostCfg, + NetworkingConfig: &network.NetworkingConfig{}, + ContainerName: ctrName, + }) id := create.ID if id != "" { defer apiClient.ContainerRemove(ctx, id, client.ContainerRemoveOptions{Force: true}) @@ -175,7 +180,12 @@ func TestRunMountImage(t *testing.T) { } ctrName := strings.ReplaceAll(t.Name(), "/", "_") - create, creatErr := apiClient.ContainerCreate(ctx, &cfg, &hostCfg, &network.NetworkingConfig{}, nil, ctrName) + create, creatErr := apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{ + Config: &cfg, + HostConfig: &hostCfg, + NetworkingConfig: &network.NetworkingConfig{}, + ContainerName: ctrName, + }) id := create.ID if id != "" { defer container.Remove(ctx, t, apiClient, id, client.ContainerRemoveOptions{Force: true}) diff --git a/internal/testutil/fakestorage/storage.go b/internal/testutil/fakestorage/storage.go index 434b9f497e..2cf90f6ad4 100644 --- a/internal/testutil/fakestorage/storage.go +++ b/internal/testutil/fakestorage/storage.go @@ -154,10 +154,11 @@ COPY . /static`); err != nil { assert.NilError(t, err) // Start the container - b, err := c.ContainerCreate(context.Background(), - &containertypes.Config{Image: imgName}, - &containertypes.HostConfig{PublishAllPorts: true}, - nil, nil, ctrName) + b, err := c.ContainerCreate(context.Background(), client.ContainerCreateOptions{ + Config: &containertypes.Config{Image: imgName}, + HostConfig: &containertypes.HostConfig{PublishAllPorts: true}, + ContainerName: ctrName, + }) assert.NilError(t, err) err = c.ContainerStart(context.Background(), b.ID, client.ContainerStartOptions{}) assert.NilError(t, err) diff --git a/vendor/github.com/moby/moby/client/client_interfaces.go b/vendor/github.com/moby/moby/client/client_interfaces.go index 14d59b642e..a038c33c6e 100644 --- a/vendor/github.com/moby/moby/client/client_interfaces.go +++ b/vendor/github.com/moby/moby/client/client_interfaces.go @@ -12,7 +12,6 @@ import ( "github.com/moby/moby/api/types/registry" "github.com/moby/moby/api/types/swarm" "github.com/moby/moby/api/types/system" - ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) // APIClient is an interface that clients that talk with a docker server must implement. @@ -58,10 +57,10 @@ type HijackDialer interface { // ContainerAPIClient defines API client methods for the containers type ContainerAPIClient interface { - ContainerAttach(ctx context.Context, container string, options ContainerAttachOptions) (HijackedResponse, error) - ContainerCommit(ctx context.Context, container string, options ContainerCommitOptions) (container.CommitResponse, error) - ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error) - ContainerDiff(ctx context.Context, container string) ([]container.FilesystemChange, error) + ContainerAttach(ctx context.Context, container string, options ContainerAttachOptions) (ContainerAttachResult, error) + ContainerCommit(ctx context.Context, container string, options ContainerCommitOptions) (ContainerCommitResult, error) + ContainerCreate(ctx context.Context, options ContainerCreateOptions) (ContainerCreateResult, error) + ContainerDiff(ctx context.Context, container string, options ContainerDiffOptions) (ContainerDiffResult, error) ExecAPIClient ContainerExport(ctx context.Context, container string) (io.ReadCloser, error) ContainerInspect(ctx context.Context, container string) (container.InspectResponse, error) diff --git a/vendor/github.com/moby/moby/client/container_attach.go b/vendor/github.com/moby/moby/client/container_attach.go index a4ffb3e64d..fa3a2efcc9 100644 --- a/vendor/github.com/moby/moby/client/container_attach.go +++ b/vendor/github.com/moby/moby/client/container_attach.go @@ -16,6 +16,11 @@ type ContainerAttachOptions struct { Logs bool } +// ContainerAttachResult is the result from attaching to a container. +type ContainerAttachResult struct { + HijackedResponse +} + // ContainerAttach attaches a connection to a container in the server. // It returns a [HijackedResponse] with the hijacked connection // and a reader to get output. It's up to the called to close @@ -44,10 +49,10 @@ type ContainerAttachOptions struct { // [stdcopy.StdType]: https://pkg.go.dev/github.com/moby/moby/api/pkg/stdcopy#StdType // [Stdout]: https://pkg.go.dev/github.com/moby/moby/api/pkg/stdcopy#Stdout // [Stderr]: https://pkg.go.dev/github.com/moby/moby/api/pkg/stdcopy#Stderr -func (cli *Client) ContainerAttach(ctx context.Context, containerID string, options ContainerAttachOptions) (HijackedResponse, error) { +func (cli *Client) ContainerAttach(ctx context.Context, containerID string, options ContainerAttachOptions) (ContainerAttachResult, error) { containerID, err := trimID("container", containerID) if err != nil { - return HijackedResponse{}, err + return ContainerAttachResult{}, err } query := url.Values{} @@ -70,7 +75,12 @@ func (cli *Client) ContainerAttach(ctx context.Context, containerID string, opti query.Set("logs", "1") } - return cli.postHijacked(ctx, "/containers/"+containerID+"/attach", query, nil, http.Header{ + hijacked, err := cli.postHijacked(ctx, "/containers/"+containerID+"/attach", query, nil, http.Header{ "Content-Type": {"text/plain"}, }) + if err != nil { + return ContainerAttachResult{}, err + } + + return ContainerAttachResult{HijackedResponse: hijacked}, nil } diff --git a/vendor/github.com/moby/moby/client/container_commit.go b/vendor/github.com/moby/moby/client/container_commit.go index d948fbd41d..79da44a54f 100644 --- a/vendor/github.com/moby/moby/client/container_commit.go +++ b/vendor/github.com/moby/moby/client/container_commit.go @@ -20,22 +20,27 @@ type ContainerCommitOptions struct { Config *container.Config } +// ContainerCommitResult is the result from committing a container. +type ContainerCommitResult struct { + ID string +} + // ContainerCommit applies changes to a container and creates a new tagged image. -func (cli *Client) ContainerCommit(ctx context.Context, containerID string, options ContainerCommitOptions) (container.CommitResponse, error) { +func (cli *Client) ContainerCommit(ctx context.Context, containerID string, options ContainerCommitOptions) (ContainerCommitResult, error) { containerID, err := trimID("container", containerID) if err != nil { - return container.CommitResponse{}, err + return ContainerCommitResult{}, err } var repository, tag string if options.Reference != "" { ref, err := reference.ParseNormalizedNamed(options.Reference) if err != nil { - return container.CommitResponse{}, err + return ContainerCommitResult{}, err } if _, ok := ref.(reference.Digested); ok { - return container.CommitResponse{}, errors.New("refusing to create a tag with a digest reference") + return ContainerCommitResult{}, errors.New("refusing to create a tag with a digest reference") } ref = reference.TagNameOnly(ref) @@ -62,9 +67,9 @@ func (cli *Client) ContainerCommit(ctx context.Context, containerID string, opti resp, err := cli.post(ctx, "/commit", query, options.Config, nil) defer ensureReaderClosed(resp) if err != nil { - return response, err + return ContainerCommitResult{}, err } err = json.NewDecoder(resp.Body).Decode(&response) - return response, err + return ContainerCommitResult{ID: response.ID}, err } diff --git a/vendor/github.com/moby/moby/client/container_create.go b/vendor/github.com/moby/moby/client/container_create.go index 9d72c636ee..3181685ce5 100644 --- a/vendor/github.com/moby/moby/client/container_create.go +++ b/vendor/github.com/moby/moby/client/container_create.go @@ -10,49 +10,48 @@ import ( cerrdefs "github.com/containerd/errdefs" "github.com/moby/moby/api/types/container" - "github.com/moby/moby/api/types/network" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) // ContainerCreate creates a new container based on the given configuration. // It can be associated with a name, but it's not mandatory. -func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error) { - if config == nil { - return container.CreateResponse{}, cerrdefs.ErrInvalidArgument.WithMessage("config is nil") +func (cli *Client) ContainerCreate(ctx context.Context, options ContainerCreateOptions) (ContainerCreateResult, error) { + if options.Config == nil { + return ContainerCreateResult{}, cerrdefs.ErrInvalidArgument.WithMessage("config is nil") } var response container.CreateResponse - if hostConfig != nil { - hostConfig.CapAdd = normalizeCapabilities(hostConfig.CapAdd) - hostConfig.CapDrop = normalizeCapabilities(hostConfig.CapDrop) + if options.HostConfig != nil { + options.HostConfig.CapAdd = normalizeCapabilities(options.HostConfig.CapAdd) + options.HostConfig.CapDrop = normalizeCapabilities(options.HostConfig.CapDrop) } query := url.Values{} - if platform != nil { - if p := formatPlatform(*platform); p != "unknown" { + if options.Platform != nil { + if p := formatPlatform(*options.Platform); p != "unknown" { query.Set("platform", p) } } - if containerName != "" { - query.Set("name", containerName) + if options.ContainerName != "" { + query.Set("name", options.ContainerName) } body := container.CreateRequest{ - Config: config, - HostConfig: hostConfig, - NetworkingConfig: networkingConfig, + Config: options.Config, + HostConfig: options.HostConfig, + NetworkingConfig: options.NetworkingConfig, } resp, err := cli.post(ctx, "/containers/create", query, body, nil) defer ensureReaderClosed(resp) if err != nil { - return response, err + return ContainerCreateResult{}, err } err = json.NewDecoder(resp.Body).Decode(&response) - return response, err + return ContainerCreateResult{ID: response.ID, Warnings: response.Warnings}, err } // formatPlatform returns a formatted string representing platform (e.g., "linux/arm/v7"). diff --git a/vendor/github.com/moby/moby/client/container_create_opts.go b/vendor/github.com/moby/moby/client/container_create_opts.go new file mode 100644 index 0000000000..249860e845 --- /dev/null +++ b/vendor/github.com/moby/moby/client/container_create_opts.go @@ -0,0 +1,22 @@ +package client + +import ( + "github.com/moby/moby/api/types/container" + "github.com/moby/moby/api/types/network" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +// ContainerCreateOptions holds parameters to create a container. +type ContainerCreateOptions struct { + Config *container.Config + HostConfig *container.HostConfig + NetworkingConfig *network.NetworkingConfig + Platform *ocispec.Platform + ContainerName string +} + +// ContainerCreateResult is the result from creating a container. +type ContainerCreateResult struct { + ID string + Warnings []string +} diff --git a/vendor/github.com/moby/moby/client/container_diff.go b/vendor/github.com/moby/moby/client/container_diff.go index c87c6e4be9..ec904337e0 100644 --- a/vendor/github.com/moby/moby/client/container_diff.go +++ b/vendor/github.com/moby/moby/client/container_diff.go @@ -9,22 +9,22 @@ import ( ) // ContainerDiff shows differences in a container filesystem since it was started. -func (cli *Client) ContainerDiff(ctx context.Context, containerID string) ([]container.FilesystemChange, error) { +func (cli *Client) ContainerDiff(ctx context.Context, containerID string, options ContainerDiffOptions) (ContainerDiffResult, error) { containerID, err := trimID("container", containerID) if err != nil { - return nil, err + return ContainerDiffResult{}, err } resp, err := cli.get(ctx, "/containers/"+containerID+"/changes", url.Values{}, nil) defer ensureReaderClosed(resp) if err != nil { - return nil, err + return ContainerDiffResult{}, err } var changes []container.FilesystemChange err = json.NewDecoder(resp.Body).Decode(&changes) if err != nil { - return nil, err + return ContainerDiffResult{}, err } - return changes, err + return ContainerDiffResult{Changes: changes}, err } diff --git a/vendor/github.com/moby/moby/client/container_diff_opts.go b/vendor/github.com/moby/moby/client/container_diff_opts.go new file mode 100644 index 0000000000..5e3c37ab4e --- /dev/null +++ b/vendor/github.com/moby/moby/client/container_diff_opts.go @@ -0,0 +1,13 @@ +package client + +import "github.com/moby/moby/api/types/container" + +// ContainerDiffOptions holds parameters to show differences in a container filesystem. +type ContainerDiffOptions struct { + // Currently no options, but this allows for future extensibility +} + +// ContainerDiffResult is the result from showing differences in a container filesystem. +type ContainerDiffResult struct { + Changes []container.FilesystemChange +}