api/types/container: move ExecInspect type to client

This type was introduced in [moby@3f9f231], at which type no API response
types were defined, and the [`containerRouter.getExecByID`] would return
the daemon's internal [`exec.Config`] type from [`backend.ContainerExecInspect`].

Tracing back history about the discrepancy between the type used by the client
and the actual response type; commit [moby@2a34207] added the missing type in
the API, which was documented as part of the API swagger definition since the
start ([moby@0243936]), and updated in [moby@74cb739], so we can't use the
reduced struct as response type.

[moby@3f9f231]: 3f9f23114f
[moby@2a34207]: 2a342079c6
[`containerRouter.getExecByID`]: 3f9f23114f/api/server/router/container/exec.go (L18-L25)
[`backend.ContainerExecInspect`]: 3f9f23114f/api/server/router/container/backend.go (L18)
[`exec.Config`]: 3f9f23114f/daemon/exec/exec.go (L13-L31)
[moby@0243936]: 0243936d92
[moby@74cb739]: 74cb739766

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2025-09-16 16:46:12 +02:00
parent da5ca1b746
commit ff21989215
7 changed files with 69 additions and 43 deletions

View File

@@ -8,21 +8,6 @@ import "github.com/moby/moby/api/types/common"
// TODO(thaJeztah): make this a distinct type.
type ExecCreateResponse = common.IDResponse
// ExecInspect holds information returned by exec inspect.
//
// It is used by the client to unmarshal a [ExecInspectResponse],
// but currently only provides a subset of the information included
// in that type.
//
// TODO(thaJeztah): merge [ExecInspect] and [ExecInspectResponse],
type ExecInspect struct {
ExecID string `json:"ID"`
ContainerID string `json:"ContainerID"`
Running bool `json:"Running"`
ExitCode int `json:"ExitCode"`
Pid int `json:"Pid"`
}
// ExecInspectResponse is the API response for the "GET /exec/{id}/json"
// endpoint and holds information about and exec.
type ExecInspectResponse struct {

View File

@@ -71,7 +71,7 @@ type ContainerAPIClient interface {
ContainerDiff(ctx context.Context, container string) ([]container.FilesystemChange, error)
ContainerExecAttach(ctx context.Context, execID string, options ExecAttachOptions) (HijackedResponse, error)
ContainerExecCreate(ctx context.Context, container string, options ExecCreateOptions) (container.ExecCreateResponse, error)
ContainerExecInspect(ctx context.Context, execID string) (container.ExecInspect, error)
ContainerExecInspect(ctx context.Context, execID string) (ExecInspect, error)
ContainerExecResize(ctx context.Context, execID string, options ContainerResizeOptions) error
ContainerExecStart(ctx context.Context, execID string, options ExecStartOptions) error
ContainerExport(ctx context.Context, container string) (io.ReadCloser, error)

View File

@@ -146,15 +146,43 @@ func (cli *Client) ContainerExecAttach(ctx context.Context, execID string, confi
})
}
// 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"`
}
// ContainerExecInspect returns information about a specific exec process on the docker host.
func (cli *Client) ContainerExecInspect(ctx context.Context, execID string) (container.ExecInspect, error) {
func (cli *Client) ContainerExecInspect(ctx context.Context, execID string) (ExecInspect, error) {
resp, err := cli.get(ctx, "/exec/"+execID+"/json", nil, nil)
defer ensureReaderClosed(resp)
if err != nil {
return container.ExecInspect{}, err
return ExecInspect{}, err
}
var response container.ExecInspect
var response container.ExecInspectResponse
err = json.NewDecoder(resp.Body).Decode(&response)
return response, err
if err != nil {
return ExecInspect{}, err
}
var ec int
if response.ExitCode != nil {
ec = *response.ExitCode
}
return ExecInspect{
ExecID: response.ID,
ContainerID: response.ContainerID,
Running: response.Running,
ExitCode: ec,
Pid: response.Pid,
}, nil
}

View File

@@ -146,10 +146,10 @@ func TestContainerExecInspect(t *testing.T) {
client, err := NewClientWithOpts(
WithMockClient(func(req *http.Request) (*http.Response, error) {
if !strings.HasPrefix(req.URL.Path, expectedURL) {
return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
return nil, fmt.Errorf("expected URL '%s', got '%s'", expectedURL, req.URL)
}
b, err := json.Marshal(container.ExecInspect{
ExecID: "exec_id",
b, err := json.Marshal(container.ExecInspectResponse{
ID: "exec_id",
ContainerID: "container_id",
})
if err != nil {

View File

@@ -8,21 +8,6 @@ import "github.com/moby/moby/api/types/common"
// TODO(thaJeztah): make this a distinct type.
type ExecCreateResponse = common.IDResponse
// ExecInspect holds information returned by exec inspect.
//
// It is used by the client to unmarshal a [ExecInspectResponse],
// but currently only provides a subset of the information included
// in that type.
//
// TODO(thaJeztah): merge [ExecInspect] and [ExecInspectResponse],
type ExecInspect struct {
ExecID string `json:"ID"`
ContainerID string `json:"ContainerID"`
Running bool `json:"Running"`
ExitCode int `json:"ExitCode"`
Pid int `json:"Pid"`
}
// ExecInspectResponse is the API response for the "GET /exec/{id}/json"
// endpoint and holds information about and exec.
type ExecInspectResponse struct {

View File

@@ -71,7 +71,7 @@ type ContainerAPIClient interface {
ContainerDiff(ctx context.Context, container string) ([]container.FilesystemChange, error)
ContainerExecAttach(ctx context.Context, execID string, options ExecAttachOptions) (HijackedResponse, error)
ContainerExecCreate(ctx context.Context, container string, options ExecCreateOptions) (container.ExecCreateResponse, error)
ContainerExecInspect(ctx context.Context, execID string) (container.ExecInspect, error)
ContainerExecInspect(ctx context.Context, execID string) (ExecInspect, error)
ContainerExecResize(ctx context.Context, execID string, options ContainerResizeOptions) error
ContainerExecStart(ctx context.Context, execID string, options ExecStartOptions) error
ContainerExport(ctx context.Context, container string) (io.ReadCloser, error)

View File

@@ -146,15 +146,43 @@ func (cli *Client) ContainerExecAttach(ctx context.Context, execID string, confi
})
}
// 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"`
}
// ContainerExecInspect returns information about a specific exec process on the docker host.
func (cli *Client) ContainerExecInspect(ctx context.Context, execID string) (container.ExecInspect, error) {
func (cli *Client) ContainerExecInspect(ctx context.Context, execID string) (ExecInspect, error) {
resp, err := cli.get(ctx, "/exec/"+execID+"/json", nil, nil)
defer ensureReaderClosed(resp)
if err != nil {
return container.ExecInspect{}, err
return ExecInspect{}, err
}
var response container.ExecInspect
var response container.ExecInspectResponse
err = json.NewDecoder(resp.Body).Decode(&response)
return response, err
if err != nil {
return ExecInspect{}, err
}
var ec int
if response.ExitCode != nil {
ec = *response.ExitCode
}
return ExecInspect{
ExecID: response.ID,
ContainerID: response.ContainerID,
Running: response.Running,
ExitCode: ec,
Pid: response.Pid,
}, nil
}