From c246639baa445f27b1dd677801930fc9cd0a11c3 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 24 Oct 2025 11:41:50 +0200 Subject: [PATCH 1/7] client: rename ImageRemoveResult.Deleted to ImageRemoveResult.Items Signed-off-by: Sebastiaan van Stijn --- client/image_remove.go | 2 +- client/image_remove_opts.go | 2 +- client/image_remove_test.go | 4 ++-- integration/build/build_test.go | 2 +- integration/image/history_test.go | 2 +- integration/image/remove_test.go | 12 ++++++------ integration/internal/build/build.go | 4 ++-- integration/volume/mount_test.go | 4 +++- vendor/github.com/moby/moby/client/image_remove.go | 2 +- .../github.com/moby/moby/client/image_remove_opts.go | 2 +- 10 files changed, 19 insertions(+), 17 deletions(-) diff --git a/client/image_remove.go b/client/image_remove.go index 17ed75741c..095b4f04c4 100644 --- a/client/image_remove.go +++ b/client/image_remove.go @@ -35,5 +35,5 @@ func (cli *Client) ImageRemove(ctx context.Context, imageID string, options Imag var dels []image.DeleteResponse err = json.NewDecoder(resp.Body).Decode(&dels) - return ImageRemoveResult{Deleted: dels}, err + return ImageRemoveResult{Items: dels}, err } diff --git a/client/image_remove_opts.go b/client/image_remove_opts.go index d028f46086..3b5d8a77f7 100644 --- a/client/image_remove_opts.go +++ b/client/image_remove_opts.go @@ -14,5 +14,5 @@ type ImageRemoveOptions struct { // ImageRemoveResult holds the delete responses returned by the daemon. type ImageRemoveResult struct { - Deleted []image.DeleteResponse + Items []image.DeleteResponse } diff --git a/client/image_remove_test.go b/client/image_remove_test.go index 0c2934a3a4..cb3b3b677d 100644 --- a/client/image_remove_test.go +++ b/client/image_remove_test.go @@ -91,8 +91,8 @@ func TestImageRemove(t *testing.T) { opts.Platforms = []ocispec.Platform{*removeCase.platform} } - imageDeletes, err := client.ImageRemove(context.Background(), "image_id", opts) + res, err := client.ImageRemove(context.Background(), "image_id", opts) assert.NilError(t, err) - assert.Check(t, is.Len(imageDeletes.Deleted, 2)) + assert.Check(t, is.Len(res.Items, 2)) } } diff --git a/integration/build/build_test.go b/integration/build/build_test.go index e45c560166..e27b7de442 100644 --- a/integration/build/build_test.go +++ b/integration/build/build_test.go @@ -802,7 +802,7 @@ func TestBuildHistoryDoesNotPreventRemoval(t *testing.T) { res, err := apiClient.ImageRemove(ctx, "history-a", client.ImageRemoveOptions{}) assert.NilError(t, err) - assert.Check(t, slices.ContainsFunc(res.Deleted, func(r image.DeleteResponse) bool { + assert.Check(t, slices.ContainsFunc(res.Items, func(r image.DeleteResponse) bool { return r.Deleted != "" })) } diff --git a/integration/image/history_test.go b/integration/image/history_test.go index 5891b97bb1..d65b165a3e 100644 --- a/integration/image/history_test.go +++ b/integration/image/history_test.go @@ -78,7 +78,7 @@ func TestAPIImageHistoryCrossPlatform(t *testing.T) { imgID := build.GetImageIDFromBody(t, resp.Body) t.Cleanup(func() { - apiClient.ImageRemove(ctx, imgID, client.ImageRemoveOptions{Force: true}) + _, _ = apiClient.ImageRemove(ctx, imgID, client.ImageRemoveOptions{Force: true}) }) testCases := []struct { diff --git a/integration/image/remove_test.go b/integration/image/remove_test.go index 099d24db8c..78c436025c 100644 --- a/integration/image/remove_test.go +++ b/integration/image/remove_test.go @@ -147,20 +147,20 @@ func TestRemoveWithPlatform(t *testing.T) { Force: true, }) assert.NilError(t, err) - assert.Check(t, is.Len(res.Deleted, 1)) - for _, r := range res.Deleted { + assert.Check(t, is.Len(res.Items, 1)) + for _, r := range res.Items { assert.Check(t, is.Equal(r.Untagged, ""), "No image should be untagged") } - checkPlatformDeleted(t, imageIdx, res.Deleted, tc.deleted) + checkPlatformDeleted(t, imageIdx, res.Items, tc.deleted) } // Delete the rest resp, err := apiClient.ImageRemove(ctx, imgName, client.ImageRemoveOptions{}) assert.NilError(t, err) - assert.Check(t, is.Len(resp.Deleted, 2)) - assert.Check(t, is.Equal(resp.Deleted[0].Untagged, imgName)) - assert.Check(t, is.Equal(resp.Deleted[1].Deleted, imageIdx.Manifests[0].Digest.String())) + assert.Check(t, is.Len(resp.Items, 2)) + assert.Check(t, is.Equal(resp.Items[0].Untagged, imgName)) + assert.Check(t, is.Equal(resp.Items[1].Deleted, imageIdx.Manifests[0].Digest.String())) // TODO(vvoland): Should it also include platform-specific manifests? https://github.com/moby/moby/pull/49982 } diff --git a/integration/internal/build/build.go b/integration/internal/build/build.go index a435f42076..f21a6c60a7 100644 --- a/integration/internal/build/build.go +++ b/integration/internal/build/build.go @@ -19,13 +19,13 @@ import ( // Do builds an image from the given context and returns the image ID. func Do(ctx context.Context, t *testing.T, apiClient client.APIClient, buildCtx *fakecontext.Fake) string { resp, err := apiClient.ImageBuild(ctx, buildCtx.AsTarReader(t), client.ImageBuildOptions{}) + assert.NilError(t, err) if resp.Body != nil { defer resp.Body.Close() } - assert.NilError(t, err) img := GetImageIDFromBody(t, resp.Body) t.Cleanup(func() { - apiClient.ImageRemove(ctx, img, client.ImageRemoveOptions{Force: true}) + _, _ = apiClient.ImageRemove(ctx, img, client.ImageRemoveOptions{Force: true}) }) return img } diff --git a/integration/volume/mount_test.go b/integration/volume/mount_test.go index a371e8f8ab..d26bb8471a 100644 --- a/integration/volume/mount_test.go +++ b/integration/volume/mount_test.go @@ -151,7 +151,9 @@ func TestRunMountImage(t *testing.T) { t.Run(tc.name, func(t *testing.T) { testImage := setupTestImage(t, ctx, apiClient, tc.name) if testImage != "" { - defer apiClient.ImageRemove(ctx, testImage, client.ImageRemoveOptions{Force: true}) + defer func() { + _, _ = apiClient.ImageRemove(ctx, testImage, client.ImageRemoveOptions{Force: true}) + }() } cfg := containertypes.Config{ diff --git a/vendor/github.com/moby/moby/client/image_remove.go b/vendor/github.com/moby/moby/client/image_remove.go index 17ed75741c..095b4f04c4 100644 --- a/vendor/github.com/moby/moby/client/image_remove.go +++ b/vendor/github.com/moby/moby/client/image_remove.go @@ -35,5 +35,5 @@ func (cli *Client) ImageRemove(ctx context.Context, imageID string, options Imag var dels []image.DeleteResponse err = json.NewDecoder(resp.Body).Decode(&dels) - return ImageRemoveResult{Deleted: dels}, err + return ImageRemoveResult{Items: dels}, err } diff --git a/vendor/github.com/moby/moby/client/image_remove_opts.go b/vendor/github.com/moby/moby/client/image_remove_opts.go index d028f46086..3b5d8a77f7 100644 --- a/vendor/github.com/moby/moby/client/image_remove_opts.go +++ b/vendor/github.com/moby/moby/client/image_remove_opts.go @@ -14,5 +14,5 @@ type ImageRemoveOptions struct { // ImageRemoveResult holds the delete responses returned by the daemon. type ImageRemoveResult struct { - Deleted []image.DeleteResponse + Items []image.DeleteResponse } From c6a45784f9a0d2b2ec31a30e45ffb784f609031b Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 24 Oct 2025 11:55:05 +0200 Subject: [PATCH 2/7] client: VolumeListResult: define local type The `VolumeListResult.Items` field was a `volume.ListResponse`, which in itself also had two slices (for volumes, and warnings). The Volumes field contained a slice of pointers to Volumes. This patch: - Re-defines `ImageRemoveResult` as a distinct type, containing the content of the `volume.ListResponse.Volumes` and `.Warnings`. - The `VolumeListResult` doesn't use a pointer for the volumes to make it slightly easier to deal with (possibly the API type could be changed as well, which could allow us to simplify the client code. Signed-off-by: Sebastiaan van Stijn --- client/volume_list.go | 28 ++++++++++++++++--- client/volume_list_test.go | 2 +- integration/volume/volume_test.go | 9 +++--- internal/testutil/environment/clean.go | 2 +- internal/testutil/environment/protect.go | 2 +- .../moby/moby/client/volume_list.go | 28 ++++++++++++++++--- 6 files changed, 55 insertions(+), 16 deletions(-) diff --git a/client/volume_list.go b/client/volume_list.go index 0c5bd7e61b..802f104eaf 100644 --- a/client/volume_list.go +++ b/client/volume_list.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "net/url" + "slices" "github.com/moby/moby/api/types/volume" ) @@ -15,7 +16,11 @@ type VolumeListOptions struct { // VolumeListResult holds the result from the [Client.VolumeList] method. type VolumeListResult struct { - Items volume.ListResponse + // List of volumes. + Items []volume.Volume + + // Warnings that occurred when fetching the list of volumes. + Warnings []string } // VolumeList returns the volumes configured in the docker host. @@ -29,7 +34,22 @@ func (cli *Client) VolumeList(ctx context.Context, options VolumeListOptions) (V return VolumeListResult{}, err } - var res VolumeListResult - err = json.NewDecoder(resp.Body).Decode(&res.Items) - return res, err + var apiResp volume.ListResponse + err = json.NewDecoder(resp.Body).Decode(&apiResp) + if err != nil { + return VolumeListResult{}, err + } + + res := VolumeListResult{ + Items: make([]volume.Volume, 0, len(apiResp.Volumes)), + Warnings: slices.Clone(apiResp.Warnings), + } + + for _, vol := range apiResp.Volumes { + if vol != nil { + res.Items = append(res.Items, *vol) + } + } + + return res, nil } diff --git a/client/volume_list_test.go b/client/volume_list_test.go index fd3e019b5c..3fa019ce9c 100644 --- a/client/volume_list_test.go +++ b/client/volume_list_test.go @@ -64,6 +64,6 @@ func TestVolumeList(t *testing.T) { result, err := client.VolumeList(context.Background(), VolumeListOptions{Filters: listCase.filters}) assert.NilError(t, err) - assert.Check(t, is.Len(result.Items.Volumes, 1)) + assert.Check(t, is.Len(result.Items, 1)) } } diff --git a/integration/volume/volume_test.go b/integration/volume/volume_test.go index 1987089e41..01ebead3d5 100644 --- a/integration/volume/volume_test.go +++ b/integration/volume/volume_test.go @@ -50,18 +50,17 @@ func TestVolumesCreateAndList(t *testing.T) { res, err := apiClient.VolumeList(ctx, client.VolumeListOptions{}) assert.NilError(t, err) - assert.Assert(t, len(res.Items.Volumes) > 0) + assert.Assert(t, len(res.Items) > 0) - volumes := res.Items.Volumes[:0] - for _, v := range res.Items.Volumes { + volumes := res.Items[:0] + for _, v := range res.Items { if v.Name == namedV.Name { volumes = append(volumes, v) } } assert.Check(t, is.Equal(len(volumes), 1)) - assert.Check(t, volumes[0] != nil) - assert.Check(t, is.DeepEqual(*volumes[0], expected, cmpopts.EquateEmpty())) + assert.Check(t, is.DeepEqual(volumes[0], expected, cmpopts.EquateEmpty())) } func TestVolumesRemove(t *testing.T) { diff --git a/internal/testutil/environment/clean.go b/internal/testutil/environment/clean.go index cef84930e8..7affbaab09 100644 --- a/internal/testutil/environment/clean.go +++ b/internal/testutil/environment/clean.go @@ -130,7 +130,7 @@ func deleteAllVolumes(ctx context.Context, t testing.TB, c client.VolumeAPIClien res, err := c.VolumeList(ctx, client.VolumeListOptions{}) assert.Check(t, err, "failed to list volumes") - for _, v := range res.Items.Volumes { + for _, v := range res.Items { if _, ok := protectedVolumes[v.Name]; ok { continue } diff --git a/internal/testutil/environment/protect.go b/internal/testutil/environment/protect.go index f683ac287a..784727ab9a 100644 --- a/internal/testutil/environment/protect.go +++ b/internal/testutil/environment/protect.go @@ -231,7 +231,7 @@ func getExistingVolumes(ctx context.Context, t testing.TB, testEnv *Execution) [ assert.NilError(t, err, "failed to list volumes") var volumes []string - for _, vol := range res.Items.Volumes { + for _, vol := range res.Items { volumes = append(volumes, vol.Name) } return volumes diff --git a/vendor/github.com/moby/moby/client/volume_list.go b/vendor/github.com/moby/moby/client/volume_list.go index 0c5bd7e61b..802f104eaf 100644 --- a/vendor/github.com/moby/moby/client/volume_list.go +++ b/vendor/github.com/moby/moby/client/volume_list.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "net/url" + "slices" "github.com/moby/moby/api/types/volume" ) @@ -15,7 +16,11 @@ type VolumeListOptions struct { // VolumeListResult holds the result from the [Client.VolumeList] method. type VolumeListResult struct { - Items volume.ListResponse + // List of volumes. + Items []volume.Volume + + // Warnings that occurred when fetching the list of volumes. + Warnings []string } // VolumeList returns the volumes configured in the docker host. @@ -29,7 +34,22 @@ func (cli *Client) VolumeList(ctx context.Context, options VolumeListOptions) (V return VolumeListResult{}, err } - var res VolumeListResult - err = json.NewDecoder(resp.Body).Decode(&res.Items) - return res, err + var apiResp volume.ListResponse + err = json.NewDecoder(resp.Body).Decode(&apiResp) + if err != nil { + return VolumeListResult{}, err + } + + res := VolumeListResult{ + Items: make([]volume.Volume, 0, len(apiResp.Volumes)), + Warnings: slices.Clone(apiResp.Warnings), + } + + for _, vol := range apiResp.Volumes { + if vol != nil { + res.Items = append(res.Items, *vol) + } + } + + return res, nil } From 832590155c2bd6c4bfd42d73644c51c7e5a6be05 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 24 Oct 2025 12:54:41 +0200 Subject: [PATCH 3/7] client: ExecCreateResult: define local type with ID field The `ExecCreateResult` was embedding the `container.ExecCreateRespons`, which in itself was an alias for `common.IDResponse`. This type has a single field (`ID`) currently, but the embedding made it awkward to use, for example, when mocking a `ExecCreateResult` using struct-literals: func execCreateWithID(_ string, _ client.ExecCreateOptions) (client.ExecCreateResult, error) { return client.ExecCreateResult{ExecCreateResponse: container.ExecCreateResponse{ID: "execid"}}, nil } This patch defines it as a local type with the `ID` as field. Signed-off-by: Sebastiaan van Stijn --- client/container_exec.go | 4 ++-- client/container_exec_test.go | 4 ++-- integration-cli/docker_api_exec_test.go | 4 ++-- integration/container/exec_test.go | 16 ++++++++-------- integration/internal/container/exec.go | 4 ++-- integration/internal/swarm/service.go | 4 ++-- integration/system/event_test.go | 6 +++--- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/client/container_exec.go b/client/container_exec.go index 3b23de487d..76e4d09871 100644 --- a/client/container_exec.go +++ b/client/container_exec.go @@ -26,7 +26,7 @@ type ExecCreateOptions struct { // ExecCreateResult holds the result of creating a container exec. type ExecCreateResult struct { - container.ExecCreateResponse + ID string } // ExecCreate creates a new exec configuration to run an exec process. @@ -58,7 +58,7 @@ func (cli *Client) ExecCreate(ctx context.Context, containerID string, options E var response container.ExecCreateResponse err = json.NewDecoder(resp.Body).Decode(&response) - return ExecCreateResult{ExecCreateResponse: response}, err + return ExecCreateResult{ID: response.ID}, err } type execStartAttachOptions struct { diff --git a/client/container_exec_test.go b/client/container_exec_test.go index cf2cc4192f..9c7ae4d424 100644 --- a/client/container_exec_test.go +++ b/client/container_exec_test.go @@ -68,11 +68,11 @@ func TestExecCreate(t *testing.T) { ) assert.NilError(t, err) - r, err := client.ExecCreate(context.Background(), "container_id", ExecCreateOptions{ + res, err := client.ExecCreate(context.Background(), "container_id", ExecCreateOptions{ User: "user", }) assert.NilError(t, err) - assert.Check(t, is.Equal(r.ID, "exec_id")) + assert.Check(t, is.Equal(res.ID, "exec_id")) } func TestExecStartError(t *testing.T) { diff --git a/integration-cli/docker_api_exec_test.go b/integration-cli/docker_api_exec_test.go index 8fe51a278a..86fffc0104 100644 --- a/integration-cli/docker_api_exec_test.go +++ b/integration-cli/docker_api_exec_test.go @@ -128,13 +128,13 @@ func (s *DockerAPISuite) TestExecAPIStartWithDetach(c *testing.T) { assert.NilError(c, err) defer apiClient.Close() - createResp, err := apiClient.ExecCreate(ctx, name, client.ExecCreateOptions{ + res, err := apiClient.ExecCreate(ctx, name, client.ExecCreateOptions{ Cmd: []string{"true"}, AttachStderr: true, }) assert.NilError(c, err) - _, body, err := request.Post(ctx, fmt.Sprintf("/exec/%s/start", createResp.ID), request.RawString(`{"Detach": true}`), request.JSON) + _, body, err := request.Post(ctx, fmt.Sprintf("/exec/%s/start", res.ID), request.RawString(`{"Detach": true}`), request.JSON) assert.NilError(c, err) b, err := request.ReadBody(body) diff --git a/integration/container/exec_test.go b/integration/container/exec_test.go index 0611838bb1..3ec2bc6f54 100644 --- a/integration/container/exec_test.go +++ b/integration/container/exec_test.go @@ -33,14 +33,14 @@ func TestExecWithCloseStdin(t *testing.T) { cID := container.Run(ctx, t, apiClient) const expected = "closeIO" - execResp, err := apiClient.ExecCreate(ctx, cID, client.ExecCreateOptions{ + res, err := apiClient.ExecCreate(ctx, cID, client.ExecCreateOptions{ AttachStdin: true, AttachStdout: true, Cmd: []string{"sh", "-c", "cat && echo " + expected}, }) assert.NilError(t, err) - resp, err := apiClient.ExecAttach(ctx, execResp.ID, client.ExecAttachOptions{}) + resp, err := apiClient.ExecAttach(ctx, res.ID, client.ExecAttachOptions{}) assert.NilError(t, err) defer resp.Close() @@ -88,7 +88,7 @@ func TestExec(t *testing.T) { cID := container.Run(ctx, t, apiClient, container.WithTty(true), container.WithWorkingDir("/root")) - id, err := apiClient.ExecCreate(ctx, cID, client.ExecCreateOptions{ + res, err := apiClient.ExecCreate(ctx, cID, client.ExecCreateOptions{ WorkingDir: "/tmp", Env: []string{"FOO=BAR"}, AttachStdout: true, @@ -96,11 +96,11 @@ func TestExec(t *testing.T) { }) assert.NilError(t, err) - inspect, err := apiClient.ExecInspect(ctx, id.ID, client.ExecInspectOptions{}) + inspect, err := apiClient.ExecInspect(ctx, res.ID, client.ExecInspectOptions{}) assert.NilError(t, err) - assert.Check(t, is.Equal(inspect.ExecID, id.ID)) + assert.Check(t, is.Equal(inspect.ExecID, res.ID)) - resp, err := apiClient.ExecAttach(ctx, id.ID, client.ExecAttachOptions{}) + resp, err := apiClient.ExecAttach(ctx, res.ID, client.ExecAttachOptions{}) assert.NilError(t, err) defer resp.Close() r, err := io.ReadAll(resp.Reader) @@ -126,12 +126,12 @@ func TestExecResize(t *testing.T) { if runtime.GOOS == "windows" { cmd = []string{"sleep", "240"} } - resp, err := apiClient.ExecCreate(ctx, cID, client.ExecCreateOptions{ + res, err := apiClient.ExecCreate(ctx, cID, client.ExecCreateOptions{ Tty: true, // Windows requires a TTY for the resize to work, otherwise fails with "is not a tty: failed precondition", see https://github.com/moby/moby/pull/48665#issuecomment-2412530345 Cmd: cmd, }) assert.NilError(t, err) - execID := resp.ID + execID := res.ID assert.NilError(t, err) _, err = apiClient.ExecStart(ctx, execID, client.ExecStartOptions{ Detach: true, diff --git a/integration/internal/container/exec.go b/integration/internal/container/exec.go index 263ade1990..5748e7915b 100644 --- a/integration/internal/container/exec.go +++ b/integration/internal/container/exec.go @@ -58,11 +58,11 @@ func Exec(ctx context.Context, apiClient client.APIClient, id string, cmd []stri op(&execOptions) } - cresp, err := apiClient.ExecCreate(ctx, id, execOptions) + res, err := apiClient.ExecCreate(ctx, id, execOptions) if err != nil { return ExecResult{}, err } - execID := cresp.ID + execID := res.ID // run it, with stdout/stderr attached aresp, err := apiClient.ExecAttach(ctx, execID, client.ExecAttachOptions{}) diff --git a/integration/internal/swarm/service.go b/integration/internal/swarm/service.go index 3c472d1c83..2254d0ee75 100644 --- a/integration/internal/swarm/service.go +++ b/integration/internal/swarm/service.go @@ -237,10 +237,10 @@ func ExecTask(ctx context.Context, t *testing.T, d *daemon.Daemon, task swarmtyp apiClient := d.NewClientT(t) defer apiClient.Close() - resp, err := apiClient.ExecCreate(ctx, task.Status.ContainerStatus.ContainerID, options) + res, err := apiClient.ExecCreate(ctx, task.Status.ContainerStatus.ContainerID, options) assert.NilError(t, err, "error creating exec") - attach, err := apiClient.ExecAttach(ctx, resp.ID, client.ExecAttachOptions{}) + attach, err := apiClient.ExecAttach(ctx, res.ID, client.ExecAttachOptions{}) assert.NilError(t, err, "error attaching to exec") return attach.HijackedResponse } diff --git a/integration/system/event_test.go b/integration/system/event_test.go index 6ebc53650d..f8d9e80c83 100644 --- a/integration/system/event_test.go +++ b/integration/system/event_test.go @@ -25,7 +25,7 @@ func TestEventsExecDie(t *testing.T) { cID := container.Run(ctx, t, apiClient) - id, err := apiClient.ExecCreate(ctx, cID, client.ExecCreateOptions{ + res, err := apiClient.ExecCreate(ctx, cID, client.ExecCreateOptions{ Cmd: []string{"echo", "hello"}, }) assert.NilError(t, err) @@ -34,7 +34,7 @@ func TestEventsExecDie(t *testing.T) { Filters: make(client.Filters).Add("container", cID).Add("event", string(events.ActionExecDie)), }) - _, err = apiClient.ExecStart(ctx, id.ID, client.ExecStartOptions{ + _, err = apiClient.ExecStart(ctx, res.ID, client.ExecStartOptions{ Detach: true, Tty: false, }) @@ -45,7 +45,7 @@ func TestEventsExecDie(t *testing.T) { assert.Equal(t, m.Type, events.ContainerEventType) assert.Equal(t, m.Actor.ID, cID) assert.Equal(t, m.Action, events.ActionExecDie) - assert.Equal(t, m.Actor.Attributes["execID"], id.ID) + assert.Equal(t, m.Actor.Attributes["execID"], res.ID) assert.Equal(t, m.Actor.Attributes["exitCode"], "0") case err = <-errs: assert.NilError(t, err) From 875c5777113ac291f6d474a0304eb33a535785af Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 24 Oct 2025 13:03:21 +0200 Subject: [PATCH 4/7] client: consolidate node options with their methods Move them together, as we did for most of the other options. Signed-off-by: Sebastiaan van Stijn --- client/node_inspect.go | 3 +++ client/node_list.go | 5 +++++ client/node_remove.go | 4 ++++ client/node_update.go | 8 ++++++++ client/swarm_node_inspect_opts.go | 4 ---- client/swarm_node_list_opts.go | 6 ------ client/swarm_node_remove_opts.go | 6 ------ client/swarm_node_update_opts.go | 9 --------- vendor/github.com/moby/moby/client/container_exec.go | 4 ++-- vendor/github.com/moby/moby/client/node_inspect.go | 3 +++ vendor/github.com/moby/moby/client/node_list.go | 5 +++++ vendor/github.com/moby/moby/client/node_remove.go | 4 ++++ vendor/github.com/moby/moby/client/node_update.go | 8 ++++++++ .../moby/moby/client/swarm_node_inspect_opts.go | 4 ---- .../github.com/moby/moby/client/swarm_node_list_opts.go | 6 ------ .../moby/moby/client/swarm_node_remove_opts.go | 6 ------ .../moby/moby/client/swarm_node_update_opts.go | 9 --------- 17 files changed, 42 insertions(+), 52 deletions(-) delete mode 100644 client/swarm_node_inspect_opts.go delete mode 100644 client/swarm_node_list_opts.go delete mode 100644 client/swarm_node_remove_opts.go delete mode 100644 client/swarm_node_update_opts.go delete mode 100644 vendor/github.com/moby/moby/client/swarm_node_inspect_opts.go delete mode 100644 vendor/github.com/moby/moby/client/swarm_node_list_opts.go delete mode 100644 vendor/github.com/moby/moby/client/swarm_node_remove_opts.go delete mode 100644 vendor/github.com/moby/moby/client/swarm_node_update_opts.go diff --git a/client/node_inspect.go b/client/node_inspect.go index b6ba94fb6e..2fee09fd5e 100644 --- a/client/node_inspect.go +++ b/client/node_inspect.go @@ -9,6 +9,9 @@ import ( "github.com/moby/moby/api/types/swarm" ) +// NodeInspectOptions holds parameters to inspect nodes with. +type NodeInspectOptions struct{} + type NodeInspectResult struct { Node swarm.Node Raw []byte diff --git a/client/node_list.go b/client/node_list.go index 6952d5fe2b..1a1b57922e 100644 --- a/client/node_list.go +++ b/client/node_list.go @@ -8,6 +8,11 @@ import ( "github.com/moby/moby/api/types/swarm" ) +// NodeListOptions holds parameters to list nodes with. +type NodeListOptions struct { + Filters Filters +} + type NodeListResult struct { Items []swarm.Node } diff --git a/client/node_remove.go b/client/node_remove.go index c7f6d7ea8a..56c39d67a6 100644 --- a/client/node_remove.go +++ b/client/node_remove.go @@ -5,6 +5,10 @@ import ( "net/url" ) +// NodeRemoveOptions holds parameters to remove nodes with. +type NodeRemoveOptions struct { + Force bool +} type NodeRemoveResult struct{} // NodeRemove removes a Node. diff --git a/client/node_update.go b/client/node_update.go index 8f9caa4411..dcf5b617ec 100644 --- a/client/node_update.go +++ b/client/node_update.go @@ -3,8 +3,16 @@ package client import ( "context" "net/url" + + "github.com/moby/moby/api/types/swarm" ) +// NodeUpdateOptions holds parameters to update nodes with. +type NodeUpdateOptions struct { + Version swarm.Version + Node swarm.NodeSpec +} + type NodeUpdateResult struct{} // NodeUpdate updates a Node. diff --git a/client/swarm_node_inspect_opts.go b/client/swarm_node_inspect_opts.go deleted file mode 100644 index 5423f5c67c..0000000000 --- a/client/swarm_node_inspect_opts.go +++ /dev/null @@ -1,4 +0,0 @@ -package client - -// NodeInspectOptions holds parameters to inspect nodes with. -type NodeInspectOptions struct{} diff --git a/client/swarm_node_list_opts.go b/client/swarm_node_list_opts.go deleted file mode 100644 index c5293cd174..0000000000 --- a/client/swarm_node_list_opts.go +++ /dev/null @@ -1,6 +0,0 @@ -package client - -// NodeListOptions holds parameters to list nodes with. -type NodeListOptions struct { - Filters Filters -} diff --git a/client/swarm_node_remove_opts.go b/client/swarm_node_remove_opts.go deleted file mode 100644 index 85bc12f812..0000000000 --- a/client/swarm_node_remove_opts.go +++ /dev/null @@ -1,6 +0,0 @@ -package client - -// NodeRemoveOptions holds parameters to remove nodes with. -type NodeRemoveOptions struct { - Force bool -} diff --git a/client/swarm_node_update_opts.go b/client/swarm_node_update_opts.go deleted file mode 100644 index 738b9f871f..0000000000 --- a/client/swarm_node_update_opts.go +++ /dev/null @@ -1,9 +0,0 @@ -package client - -import "github.com/moby/moby/api/types/swarm" - -// NodeUpdateOptions holds parameters to update nodes with. -type NodeUpdateOptions struct { - Version swarm.Version - Node swarm.NodeSpec -} diff --git a/vendor/github.com/moby/moby/client/container_exec.go b/vendor/github.com/moby/moby/client/container_exec.go index 3b23de487d..76e4d09871 100644 --- a/vendor/github.com/moby/moby/client/container_exec.go +++ b/vendor/github.com/moby/moby/client/container_exec.go @@ -26,7 +26,7 @@ type ExecCreateOptions struct { // ExecCreateResult holds the result of creating a container exec. type ExecCreateResult struct { - container.ExecCreateResponse + ID string } // ExecCreate creates a new exec configuration to run an exec process. @@ -58,7 +58,7 @@ func (cli *Client) ExecCreate(ctx context.Context, containerID string, options E var response container.ExecCreateResponse err = json.NewDecoder(resp.Body).Decode(&response) - return ExecCreateResult{ExecCreateResponse: response}, err + return ExecCreateResult{ID: response.ID}, err } type execStartAttachOptions struct { diff --git a/vendor/github.com/moby/moby/client/node_inspect.go b/vendor/github.com/moby/moby/client/node_inspect.go index b6ba94fb6e..2fee09fd5e 100644 --- a/vendor/github.com/moby/moby/client/node_inspect.go +++ b/vendor/github.com/moby/moby/client/node_inspect.go @@ -9,6 +9,9 @@ import ( "github.com/moby/moby/api/types/swarm" ) +// NodeInspectOptions holds parameters to inspect nodes with. +type NodeInspectOptions struct{} + type NodeInspectResult struct { Node swarm.Node Raw []byte diff --git a/vendor/github.com/moby/moby/client/node_list.go b/vendor/github.com/moby/moby/client/node_list.go index 6952d5fe2b..1a1b57922e 100644 --- a/vendor/github.com/moby/moby/client/node_list.go +++ b/vendor/github.com/moby/moby/client/node_list.go @@ -8,6 +8,11 @@ import ( "github.com/moby/moby/api/types/swarm" ) +// NodeListOptions holds parameters to list nodes with. +type NodeListOptions struct { + Filters Filters +} + type NodeListResult struct { Items []swarm.Node } diff --git a/vendor/github.com/moby/moby/client/node_remove.go b/vendor/github.com/moby/moby/client/node_remove.go index c7f6d7ea8a..56c39d67a6 100644 --- a/vendor/github.com/moby/moby/client/node_remove.go +++ b/vendor/github.com/moby/moby/client/node_remove.go @@ -5,6 +5,10 @@ import ( "net/url" ) +// NodeRemoveOptions holds parameters to remove nodes with. +type NodeRemoveOptions struct { + Force bool +} type NodeRemoveResult struct{} // NodeRemove removes a Node. diff --git a/vendor/github.com/moby/moby/client/node_update.go b/vendor/github.com/moby/moby/client/node_update.go index 8f9caa4411..dcf5b617ec 100644 --- a/vendor/github.com/moby/moby/client/node_update.go +++ b/vendor/github.com/moby/moby/client/node_update.go @@ -3,8 +3,16 @@ package client import ( "context" "net/url" + + "github.com/moby/moby/api/types/swarm" ) +// NodeUpdateOptions holds parameters to update nodes with. +type NodeUpdateOptions struct { + Version swarm.Version + Node swarm.NodeSpec +} + type NodeUpdateResult struct{} // NodeUpdate updates a Node. diff --git a/vendor/github.com/moby/moby/client/swarm_node_inspect_opts.go b/vendor/github.com/moby/moby/client/swarm_node_inspect_opts.go deleted file mode 100644 index 5423f5c67c..0000000000 --- a/vendor/github.com/moby/moby/client/swarm_node_inspect_opts.go +++ /dev/null @@ -1,4 +0,0 @@ -package client - -// NodeInspectOptions holds parameters to inspect nodes with. -type NodeInspectOptions struct{} diff --git a/vendor/github.com/moby/moby/client/swarm_node_list_opts.go b/vendor/github.com/moby/moby/client/swarm_node_list_opts.go deleted file mode 100644 index c5293cd174..0000000000 --- a/vendor/github.com/moby/moby/client/swarm_node_list_opts.go +++ /dev/null @@ -1,6 +0,0 @@ -package client - -// NodeListOptions holds parameters to list nodes with. -type NodeListOptions struct { - Filters Filters -} diff --git a/vendor/github.com/moby/moby/client/swarm_node_remove_opts.go b/vendor/github.com/moby/moby/client/swarm_node_remove_opts.go deleted file mode 100644 index 85bc12f812..0000000000 --- a/vendor/github.com/moby/moby/client/swarm_node_remove_opts.go +++ /dev/null @@ -1,6 +0,0 @@ -package client - -// NodeRemoveOptions holds parameters to remove nodes with. -type NodeRemoveOptions struct { - Force bool -} diff --git a/vendor/github.com/moby/moby/client/swarm_node_update_opts.go b/vendor/github.com/moby/moby/client/swarm_node_update_opts.go deleted file mode 100644 index 738b9f871f..0000000000 --- a/vendor/github.com/moby/moby/client/swarm_node_update_opts.go +++ /dev/null @@ -1,9 +0,0 @@ -package client - -import "github.com/moby/moby/api/types/swarm" - -// NodeUpdateOptions holds parameters to update nodes with. -type NodeUpdateOptions struct { - Version swarm.Version - Node swarm.NodeSpec -} From 0b7b7625c67fb52c46f0cb8d7249a69133aa44d2 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 24 Oct 2025 13:06:39 +0200 Subject: [PATCH 5/7] client: rename NodeUpdateOptions.Node to NodeUpdateOptions.Spec Align with the other swarm-types, which use `Spec` for updates. Signed-off-by: Sebastiaan van Stijn --- client/node_update.go | 4 ++-- client/node_update_test.go | 8 ++++---- internal/testutil/daemon/node.go | 2 +- vendor/github.com/moby/moby/client/node_update.go | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/client/node_update.go b/client/node_update.go index dcf5b617ec..4bc7c3b698 100644 --- a/client/node_update.go +++ b/client/node_update.go @@ -10,7 +10,7 @@ import ( // NodeUpdateOptions holds parameters to update nodes with. type NodeUpdateOptions struct { Version swarm.Version - Node swarm.NodeSpec + Spec swarm.NodeSpec } type NodeUpdateResult struct{} @@ -24,7 +24,7 @@ func (cli *Client) NodeUpdate(ctx context.Context, nodeID string, options NodeUp query := url.Values{} query.Set("version", options.Version.String()) - resp, err := cli.post(ctx, "/nodes/"+nodeID+"/update", query, options.Node, nil) + resp, err := cli.post(ctx, "/nodes/"+nodeID+"/update", query, options.Spec, nil) defer ensureReaderClosed(resp) return NodeUpdateResult{}, err } diff --git a/client/node_update_test.go b/client/node_update_test.go index d278699cb8..484e59bfe4 100644 --- a/client/node_update_test.go +++ b/client/node_update_test.go @@ -17,20 +17,20 @@ func TestNodeUpdateError(t *testing.T) { _, err = client.NodeUpdate(context.Background(), "node_id", NodeUpdateOptions{ Version: swarm.Version{}, - Node: swarm.NodeSpec{}, + Spec: swarm.NodeSpec{}, }) assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal)) _, err = client.NodeUpdate(context.Background(), "", NodeUpdateOptions{ Version: swarm.Version{}, - Node: swarm.NodeSpec{}, + Spec: swarm.NodeSpec{}, }) assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument)) assert.Check(t, is.ErrorContains(err, "value is empty")) _, err = client.NodeUpdate(context.Background(), " ", NodeUpdateOptions{ Version: swarm.Version{}, - Node: swarm.NodeSpec{}, + Spec: swarm.NodeSpec{}, }) assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument)) assert.Check(t, is.ErrorContains(err, "value is empty")) @@ -49,7 +49,7 @@ func TestNodeUpdate(t *testing.T) { _, err = client.NodeUpdate(context.Background(), "node_id", NodeUpdateOptions{ Version: swarm.Version{}, - Node: swarm.NodeSpec{}, + Spec: swarm.NodeSpec{}, }) assert.NilError(t, err) } diff --git a/internal/testutil/daemon/node.go b/internal/testutil/daemon/node.go index f631c53710..a541e37084 100644 --- a/internal/testutil/daemon/node.go +++ b/internal/testutil/daemon/node.go @@ -60,7 +60,7 @@ func (d *Daemon) UpdateNode(ctx context.Context, t testing.TB, id string, f ...N _, err := cli.NodeUpdate(ctx, node.ID, client.NodeUpdateOptions{ Version: node.Version, - Node: node.Spec, + Spec: node.Spec, }) if i < 10 && err != nil && strings.Contains(err.Error(), "update out of sequence") { time.Sleep(100 * time.Millisecond) diff --git a/vendor/github.com/moby/moby/client/node_update.go b/vendor/github.com/moby/moby/client/node_update.go index dcf5b617ec..4bc7c3b698 100644 --- a/vendor/github.com/moby/moby/client/node_update.go +++ b/vendor/github.com/moby/moby/client/node_update.go @@ -10,7 +10,7 @@ import ( // NodeUpdateOptions holds parameters to update nodes with. type NodeUpdateOptions struct { Version swarm.Version - Node swarm.NodeSpec + Spec swarm.NodeSpec } type NodeUpdateResult struct{} @@ -24,7 +24,7 @@ func (cli *Client) NodeUpdate(ctx context.Context, nodeID string, options NodeUp query := url.Values{} query.Set("version", options.Version.String()) - resp, err := cli.post(ctx, "/nodes/"+nodeID+"/update", query, options.Node, nil) + resp, err := cli.post(ctx, "/nodes/"+nodeID+"/update", query, options.Spec, nil) defer ensureReaderClosed(resp) return NodeUpdateResult{}, err } From 378116a84f2dbcf054a021ecb67d1528f77ba2eb Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 24 Oct 2025 13:22:49 +0200 Subject: [PATCH 6/7] client: ImageImportResult: prevent panic on nil reader This panicked when creating a stub; we need to look for better ways to allow stubbing these (perhaps we need to expose the rc / body)? Signed-off-by: Sebastiaan van Stijn --- client/image_import.go | 2 +- client/image_import_opts.go | 11 +++++++---- vendor/github.com/moby/moby/client/image_import.go | 2 +- .../github.com/moby/moby/client/image_import_opts.go | 11 +++++++---- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/client/image_import.go b/client/image_import.go index ca0fa1d0f6..b8f1ccb500 100644 --- a/client/image_import.go +++ b/client/image_import.go @@ -42,5 +42,5 @@ func (cli *Client) ImageImport(ctx context.Context, source ImageImportSource, re if err != nil { return ImageImportResult{}, err } - return ImageImportResult{body: resp.Body}, nil + return ImageImportResult{rc: resp.Body}, nil } diff --git a/client/image_import_opts.go b/client/image_import_opts.go index 44ea0e2caa..2ba5b593ec 100644 --- a/client/image_import_opts.go +++ b/client/image_import_opts.go @@ -20,16 +20,19 @@ type ImageImportOptions struct { // ImageImportResult holds the response body returned by the daemon for image import. type ImageImportResult struct { - body io.ReadCloser + rc io.ReadCloser } func (r ImageImportResult) Read(p []byte) (n int, err error) { - return r.body.Read(p) + if r.rc == nil { + return 0, io.EOF + } + return r.rc.Read(p) } func (r ImageImportResult) Close() error { - if r.body == nil { + if r.rc == nil { return nil } - return r.body.Close() + return r.rc.Close() } diff --git a/vendor/github.com/moby/moby/client/image_import.go b/vendor/github.com/moby/moby/client/image_import.go index ca0fa1d0f6..b8f1ccb500 100644 --- a/vendor/github.com/moby/moby/client/image_import.go +++ b/vendor/github.com/moby/moby/client/image_import.go @@ -42,5 +42,5 @@ func (cli *Client) ImageImport(ctx context.Context, source ImageImportSource, re if err != nil { return ImageImportResult{}, err } - return ImageImportResult{body: resp.Body}, nil + return ImageImportResult{rc: resp.Body}, nil } diff --git a/vendor/github.com/moby/moby/client/image_import_opts.go b/vendor/github.com/moby/moby/client/image_import_opts.go index 44ea0e2caa..2ba5b593ec 100644 --- a/vendor/github.com/moby/moby/client/image_import_opts.go +++ b/vendor/github.com/moby/moby/client/image_import_opts.go @@ -20,16 +20,19 @@ type ImageImportOptions struct { // ImageImportResult holds the response body returned by the daemon for image import. type ImageImportResult struct { - body io.ReadCloser + rc io.ReadCloser } func (r ImageImportResult) Read(p []byte) (n int, err error) { - return r.body.Read(p) + if r.rc == nil { + return 0, io.EOF + } + return r.rc.Read(p) } func (r ImageImportResult) Close() error { - if r.body == nil { + if r.rc == nil { return nil } - return r.body.Close() + return r.rc.Close() } From 12123eb5928a0467021e1adc36f2119ee8833de0 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 24 Oct 2025 14:20:46 +0200 Subject: [PATCH 7/7] client: merge ExecInspectResult with ExecInspect The `ExecInspectResult` type was embedding `ExecInspect`, which is also defined by the client, so there's no need to abstract it. While updating, also; - Rename `ExecID` to `ID`, to match the field-name returned by the API. - Rename `Pid` to `PID`, to be in the right casing. - Remove `json` labels, as option-types are not (un)marshaled to JSON. Signed-off-by: Sebastiaan van Stijn --- client/container_exec.go | 31 ++++++++----------- client/container_exec_test.go | 2 +- integration/container/exec_test.go | 2 +- integration/internal/container/exec.go | 4 +-- .../moby/moby/client/container_exec.go | 31 ++++++++----------- 5 files changed, 30 insertions(+), 40 deletions(-) diff --git a/client/container_exec.go b/client/container_exec.go index 76e4d09871..bda89110a3 100644 --- a/client/container_exec.go +++ b/client/container_exec.go @@ -127,26 +127,21 @@ func (cli *Client) ExecAttach(ctx context.Context, execID string, options ExecAt return ExecAttachResult{HijackedResponse: response}, err } -// ExecInspect holds information returned by exec inspect. -// -// It provides a subset of the information included in [container.ExecInspectResponse]. -// -// TODO(thaJeztah): include all fields of [container.ExecInspectResponse] ? -type ExecInspect struct { - ExecID string `json:"ID"` - ContainerID string `json:"ContainerID"` - Running bool `json:"Running"` - ExitCode int `json:"ExitCode"` - Pid int `json:"Pid"` -} - // ExecInspectOptions holds options for inspecting a container exec. type ExecInspectOptions struct { } // ExecInspectResult holds the result of inspecting a container exec. +// +// It provides a subset of the information included in [container.ExecInspectResponse]. +// +// TODO(thaJeztah): include all fields of [container.ExecInspectResponse] ? type ExecInspectResult struct { - ExecInspect + ID string + ContainerID string + Running bool + ExitCode int + PID int } // ExecInspect returns information about a specific exec process on the docker host. @@ -168,11 +163,11 @@ func (cli *Client) ExecInspect(ctx context.Context, execID string, options ExecI ec = *response.ExitCode } - return ExecInspectResult{ExecInspect: ExecInspect{ - ExecID: response.ID, + return ExecInspectResult{ + ID: response.ID, ContainerID: response.ContainerID, Running: response.Running, ExitCode: ec, - Pid: response.Pid, - }}, nil + PID: response.Pid, + }, nil } diff --git a/client/container_exec_test.go b/client/container_exec_test.go index 9c7ae4d424..8248fc544e 100644 --- a/client/container_exec_test.go +++ b/client/container_exec_test.go @@ -141,6 +141,6 @@ func TestExecInspect(t *testing.T) { inspect, err := client.ExecInspect(context.Background(), "exec_id", ExecInspectOptions{}) assert.NilError(t, err) - assert.Check(t, is.Equal(inspect.ExecID, "exec_id")) + assert.Check(t, is.Equal(inspect.ID, "exec_id")) assert.Check(t, is.Equal(inspect.ContainerID, "container_id")) } diff --git a/integration/container/exec_test.go b/integration/container/exec_test.go index 3ec2bc6f54..4bdd9ae081 100644 --- a/integration/container/exec_test.go +++ b/integration/container/exec_test.go @@ -98,7 +98,7 @@ func TestExec(t *testing.T) { inspect, err := apiClient.ExecInspect(ctx, res.ID, client.ExecInspectOptions{}) assert.NilError(t, err) - assert.Check(t, is.Equal(inspect.ExecID, res.ID)) + assert.Check(t, is.Equal(inspect.ID, res.ID)) resp, err := apiClient.ExecAttach(ctx, res.ID, client.ExecAttachOptions{}) assert.NilError(t, err) diff --git a/integration/internal/container/exec.go b/integration/internal/container/exec.go index 5748e7915b..94f7adeb44 100644 --- a/integration/internal/container/exec.go +++ b/integration/internal/container/exec.go @@ -77,12 +77,12 @@ func Exec(ctx context.Context, apiClient client.APIClient, id string, cmd []stri } // get the exit code - iresp, err := apiClient.ExecInspect(ctx, execID, client.ExecInspectOptions{}) + inspect, err := apiClient.ExecInspect(ctx, execID, client.ExecInspectOptions{}) if err != nil { return ExecResult{}, err } - return ExecResult{ExitCode: iresp.ExitCode, outBuffer: &s.stdout, errBuffer: &s.stderr}, nil + return ExecResult{ExitCode: inspect.ExitCode, outBuffer: &s.stdout, errBuffer: &s.stderr}, nil } // ExecT calls Exec() and aborts the test if an error occurs. diff --git a/vendor/github.com/moby/moby/client/container_exec.go b/vendor/github.com/moby/moby/client/container_exec.go index 76e4d09871..bda89110a3 100644 --- a/vendor/github.com/moby/moby/client/container_exec.go +++ b/vendor/github.com/moby/moby/client/container_exec.go @@ -127,26 +127,21 @@ func (cli *Client) ExecAttach(ctx context.Context, execID string, options ExecAt return ExecAttachResult{HijackedResponse: response}, err } -// ExecInspect holds information returned by exec inspect. -// -// It provides a subset of the information included in [container.ExecInspectResponse]. -// -// TODO(thaJeztah): include all fields of [container.ExecInspectResponse] ? -type ExecInspect struct { - ExecID string `json:"ID"` - ContainerID string `json:"ContainerID"` - Running bool `json:"Running"` - ExitCode int `json:"ExitCode"` - Pid int `json:"Pid"` -} - // ExecInspectOptions holds options for inspecting a container exec. type ExecInspectOptions struct { } // ExecInspectResult holds the result of inspecting a container exec. +// +// It provides a subset of the information included in [container.ExecInspectResponse]. +// +// TODO(thaJeztah): include all fields of [container.ExecInspectResponse] ? type ExecInspectResult struct { - ExecInspect + ID string + ContainerID string + Running bool + ExitCode int + PID int } // ExecInspect returns information about a specific exec process on the docker host. @@ -168,11 +163,11 @@ func (cli *Client) ExecInspect(ctx context.Context, execID string, options ExecI ec = *response.ExitCode } - return ExecInspectResult{ExecInspect: ExecInspect{ - ExecID: response.ID, + return ExecInspectResult{ + ID: response.ID, ContainerID: response.ContainerID, Running: response.Running, ExitCode: ec, - Pid: response.Pid, - }}, nil + PID: response.Pid, + }, nil }