mirror of
https://github.com/moby/moby.git
synced 2026-01-11 18:51:37 +00:00
client/container_exec: Wrap options and result, rename to Exec
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
committed by
Sebastiaan van Stijn
parent
01c36ec0cd
commit
94ab385eb5
@@ -90,11 +90,11 @@ type ContainerAPIClient interface {
|
||||
}
|
||||
|
||||
type ExecAPIClient interface {
|
||||
ContainerExecCreate(ctx context.Context, container string, options ExecCreateOptions) (container.ExecCreateResponse, error)
|
||||
ContainerExecStart(ctx context.Context, execID string, options ExecStartOptions) error
|
||||
ContainerExecAttach(ctx context.Context, execID string, options ExecAttachOptions) (HijackedResponse, error)
|
||||
ContainerExecInspect(ctx context.Context, execID string) (ExecInspect, error)
|
||||
ContainerExecResize(ctx context.Context, execID string, options ContainerResizeOptions) error
|
||||
ExecCreate(ctx context.Context, container string, options ExecCreateOptions) (ExecCreateResult, error)
|
||||
ExecStart(ctx context.Context, execID string, options ExecStartOptions) (ExecStartResult, error)
|
||||
ExecAttach(ctx context.Context, execID string, options ExecAttachOptions) (ExecAttachResult, error)
|
||||
ExecInspect(ctx context.Context, execID string, options ExecInspectOptions) (ExecInspectResult, error)
|
||||
ExecResize(ctx context.Context, execID string, options ExecResizeOptions) (ExecResizeResult, error)
|
||||
}
|
||||
|
||||
// DistributionAPIClient defines API client methods for the registry
|
||||
|
||||
@@ -24,11 +24,16 @@ type ExecCreateOptions struct {
|
||||
Cmd []string // Execution commands and args
|
||||
}
|
||||
|
||||
// ContainerExecCreate creates a new exec configuration to run an exec process.
|
||||
func (cli *Client) ContainerExecCreate(ctx context.Context, containerID string, options ExecCreateOptions) (container.ExecCreateResponse, error) {
|
||||
// ExecCreateResult holds the result of creating a container exec.
|
||||
type ExecCreateResult struct {
|
||||
container.ExecCreateResponse
|
||||
}
|
||||
|
||||
// ExecCreate creates a new exec configuration to run an exec process.
|
||||
func (cli *Client) ExecCreate(ctx context.Context, containerID string, options ExecCreateOptions) (ExecCreateResult, error) {
|
||||
containerID, err := trimID("container", containerID)
|
||||
if err != nil {
|
||||
return container.ExecCreateResponse{}, err
|
||||
return ExecCreateResult{}, err
|
||||
}
|
||||
|
||||
req := container.ExecCreateRequest{
|
||||
@@ -48,17 +53,15 @@ func (cli *Client) ContainerExecCreate(ctx context.Context, containerID string,
|
||||
resp, err := cli.post(ctx, "/containers/"+containerID+"/exec", nil, req, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
if err != nil {
|
||||
return container.ExecCreateResponse{}, err
|
||||
return ExecCreateResult{}, err
|
||||
}
|
||||
|
||||
var response container.ExecCreateResponse
|
||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||
return response, err
|
||||
return ExecCreateResult{ExecCreateResponse: response}, err
|
||||
}
|
||||
|
||||
// ExecStartOptions is a temp struct used by execStart
|
||||
// Config fields is part of ExecConfig in runconfig package
|
||||
type ExecStartOptions struct {
|
||||
type execStartAttachOptions struct {
|
||||
// ExecStart will first check if it's detached
|
||||
Detach bool
|
||||
// Check if there's a tty
|
||||
@@ -67,24 +70,34 @@ type ExecStartOptions struct {
|
||||
ConsoleSize *[2]uint `json:",omitempty"`
|
||||
}
|
||||
|
||||
// ContainerExecStart starts an exec process already created in the docker host.
|
||||
func (cli *Client) ContainerExecStart(ctx context.Context, execID string, config ExecStartOptions) error {
|
||||
// ExecStartOptions holds options for starting a container exec.
|
||||
type ExecStartOptions execStartAttachOptions
|
||||
|
||||
// ExecStartResult holds the result of starting a container exec.
|
||||
type ExecStartResult struct {
|
||||
}
|
||||
|
||||
// ExecStart starts an exec process already created in the docker host.
|
||||
func (cli *Client) ExecStart(ctx context.Context, execID string, options ExecStartOptions) (ExecStartResult, error) {
|
||||
req := container.ExecStartRequest{
|
||||
Detach: config.Detach,
|
||||
Tty: config.Tty,
|
||||
ConsoleSize: config.ConsoleSize,
|
||||
Detach: options.Detach,
|
||||
Tty: options.Tty,
|
||||
ConsoleSize: options.ConsoleSize,
|
||||
}
|
||||
resp, err := cli.post(ctx, "/exec/"+execID+"/start", nil, req, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
return err
|
||||
return ExecStartResult{}, err
|
||||
}
|
||||
|
||||
// ExecAttachOptions is a temp struct used by execAttach.
|
||||
//
|
||||
// TODO(thaJeztah): make this a separate type; ContainerExecAttach does not use the Detach option, and cannot run detached.
|
||||
type ExecAttachOptions = ExecStartOptions
|
||||
// ExecAttachOptions holds options for attaching to a container exec.
|
||||
type ExecAttachOptions execStartAttachOptions
|
||||
|
||||
// ContainerExecAttach attaches a connection to an exec process in the server.
|
||||
// ExecAttachResult holds the result of attaching to a container exec.
|
||||
type ExecAttachResult struct {
|
||||
HijackedResponse
|
||||
}
|
||||
|
||||
// ExecAttach attaches a connection to an exec process 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
|
||||
@@ -102,15 +115,16 @@ type ExecAttachOptions = ExecStartOptions
|
||||
// [Client.ContainerAttach] for details about the multiplexed stream.
|
||||
//
|
||||
// [stdcopy.StdCopy]: https://pkg.go.dev/github.com/moby/moby/api/pkg/stdcopy#StdCopy
|
||||
func (cli *Client) ContainerExecAttach(ctx context.Context, execID string, config ExecAttachOptions) (HijackedResponse, error) {
|
||||
func (cli *Client) ExecAttach(ctx context.Context, execID string, options ExecAttachOptions) (ExecAttachResult, error) {
|
||||
req := container.ExecStartRequest{
|
||||
Detach: config.Detach,
|
||||
Tty: config.Tty,
|
||||
ConsoleSize: config.ConsoleSize,
|
||||
Detach: options.Detach,
|
||||
Tty: options.Tty,
|
||||
ConsoleSize: options.ConsoleSize,
|
||||
}
|
||||
return cli.postHijacked(ctx, "/exec/"+execID+"/start", nil, req, http.Header{
|
||||
response, err := cli.postHijacked(ctx, "/exec/"+execID+"/start", nil, req, http.Header{
|
||||
"Content-Type": {"application/json"},
|
||||
})
|
||||
return ExecAttachResult{HijackedResponse: response}, err
|
||||
}
|
||||
|
||||
// ExecInspect holds information returned by exec inspect.
|
||||
@@ -126,18 +140,27 @@ type ExecInspect struct {
|
||||
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) (ExecInspect, error) {
|
||||
// ExecInspectOptions holds options for inspecting a container exec.
|
||||
type ExecInspectOptions struct {
|
||||
}
|
||||
|
||||
// ExecInspectResult holds the result of inspecting a container exec.
|
||||
type ExecInspectResult struct {
|
||||
ExecInspect
|
||||
}
|
||||
|
||||
// ExecInspect returns information about a specific exec process on the docker host.
|
||||
func (cli *Client) ExecInspect(ctx context.Context, execID string, options ExecInspectOptions) (ExecInspectResult, error) {
|
||||
resp, err := cli.get(ctx, "/exec/"+execID+"/json", nil, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
if err != nil {
|
||||
return ExecInspect{}, err
|
||||
return ExecInspectResult{}, err
|
||||
}
|
||||
|
||||
var response container.ExecInspectResponse
|
||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||
if err != nil {
|
||||
return ExecInspect{}, err
|
||||
return ExecInspectResult{}, err
|
||||
}
|
||||
|
||||
var ec int
|
||||
@@ -145,11 +168,11 @@ func (cli *Client) ContainerExecInspect(ctx context.Context, execID string) (Exe
|
||||
ec = *response.ExitCode
|
||||
}
|
||||
|
||||
return ExecInspect{
|
||||
return ExecInspectResult{ExecInspect: ExecInspect{
|
||||
ExecID: response.ID,
|
||||
ContainerID: response.ContainerID,
|
||||
Running: response.Running,
|
||||
ExitCode: ec,
|
||||
Pid: response.Pid,
|
||||
}, nil
|
||||
}}, nil
|
||||
}
|
||||
|
||||
@@ -15,37 +15,37 @@ import (
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func TestContainerExecCreateError(t *testing.T) {
|
||||
func TestExecCreateError(t *testing.T) {
|
||||
client, err := NewClientWithOpts(
|
||||
WithMockClient(errorMock(http.StatusInternalServerError, "Server error")),
|
||||
)
|
||||
assert.NilError(t, err)
|
||||
|
||||
_, err = client.ContainerExecCreate(context.Background(), "container_id", ExecCreateOptions{})
|
||||
_, err = client.ExecCreate(context.Background(), "container_id", ExecCreateOptions{})
|
||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
|
||||
|
||||
_, err = client.ContainerExecCreate(context.Background(), "", ExecCreateOptions{})
|
||||
_, err = client.ExecCreate(context.Background(), "", ExecCreateOptions{})
|
||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
|
||||
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||
|
||||
_, err = client.ContainerExecCreate(context.Background(), " ", ExecCreateOptions{})
|
||||
_, err = client.ExecCreate(context.Background(), " ", ExecCreateOptions{})
|
||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
|
||||
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||
}
|
||||
|
||||
// TestContainerExecCreateConnectionError verifies that connection errors occurring
|
||||
// TestExecCreateConnectionError verifies that connection errors occurring
|
||||
// during API-version negotiation are not shadowed by API-version errors.
|
||||
//
|
||||
// Regression test for https://github.com/docker/cli/issues/4890
|
||||
func TestContainerExecCreateConnectionError(t *testing.T) {
|
||||
func TestExecCreateConnectionError(t *testing.T) {
|
||||
client, err := NewClientWithOpts(WithAPIVersionNegotiation(), WithHost("tcp://no-such-host.invalid"))
|
||||
assert.NilError(t, err)
|
||||
|
||||
_, err = client.ContainerExecCreate(context.Background(), "container_id", ExecCreateOptions{})
|
||||
_, err = client.ExecCreate(context.Background(), "container_id", ExecCreateOptions{})
|
||||
assert.Check(t, is.ErrorType(err, IsErrConnectionFailed))
|
||||
}
|
||||
|
||||
func TestContainerExecCreate(t *testing.T) {
|
||||
func TestExecCreate(t *testing.T) {
|
||||
const expectedURL = "/containers/container_id/exec"
|
||||
client, err := NewClientWithOpts(
|
||||
WithMockClient(func(req *http.Request) (*http.Response, error) {
|
||||
@@ -77,24 +77,24 @@ func TestContainerExecCreate(t *testing.T) {
|
||||
)
|
||||
assert.NilError(t, err)
|
||||
|
||||
r, err := client.ContainerExecCreate(context.Background(), "container_id", ExecCreateOptions{
|
||||
r, err := client.ExecCreate(context.Background(), "container_id", ExecCreateOptions{
|
||||
User: "user",
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(r.ID, "exec_id"))
|
||||
}
|
||||
|
||||
func TestContainerExecStartError(t *testing.T) {
|
||||
func TestExecStartError(t *testing.T) {
|
||||
client, err := NewClientWithOpts(
|
||||
WithMockClient(errorMock(http.StatusInternalServerError, "Server error")),
|
||||
)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = client.ContainerExecStart(context.Background(), "nothing", ExecStartOptions{})
|
||||
_, err = client.ExecStart(context.Background(), "nothing", ExecStartOptions{})
|
||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
|
||||
}
|
||||
|
||||
func TestContainerExecStart(t *testing.T) {
|
||||
func TestExecStart(t *testing.T) {
|
||||
const expectedURL = "/exec/exec_id/start"
|
||||
client, err := NewClientWithOpts(
|
||||
WithMockClient(func(req *http.Request) (*http.Response, error) {
|
||||
@@ -120,24 +120,24 @@ func TestContainerExecStart(t *testing.T) {
|
||||
)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = client.ContainerExecStart(context.Background(), "exec_id", ExecStartOptions{
|
||||
_, err = client.ExecStart(context.Background(), "exec_id", ExecStartOptions{
|
||||
Detach: true,
|
||||
Tty: false,
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
func TestContainerExecInspectError(t *testing.T) {
|
||||
func TestExecInspectError(t *testing.T) {
|
||||
client, err := NewClientWithOpts(
|
||||
WithMockClient(errorMock(http.StatusInternalServerError, "Server error")),
|
||||
)
|
||||
assert.NilError(t, err)
|
||||
|
||||
_, err = client.ContainerExecInspect(context.Background(), "nothing")
|
||||
_, err = client.ExecInspect(context.Background(), "nothing", ExecInspectOptions{})
|
||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
|
||||
}
|
||||
|
||||
func TestContainerExecInspect(t *testing.T) {
|
||||
func TestExecInspect(t *testing.T) {
|
||||
const expectedURL = "/exec/exec_id/json"
|
||||
client, err := NewClientWithOpts(
|
||||
WithMockClient(func(req *http.Request) (*http.Response, error) {
|
||||
@@ -159,7 +159,7 @@ func TestContainerExecInspect(t *testing.T) {
|
||||
)
|
||||
assert.NilError(t, err)
|
||||
|
||||
inspect, err := client.ContainerExecInspect(context.Background(), "exec_id")
|
||||
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.ContainerID, "container_id"))
|
||||
|
||||
@@ -23,13 +23,21 @@ func (cli *Client) ContainerResize(ctx context.Context, containerID string, opti
|
||||
return cli.resize(ctx, "/containers/"+containerID, options.Height, options.Width)
|
||||
}
|
||||
|
||||
// ContainerExecResize changes the size of the tty for an exec process running inside a container.
|
||||
func (cli *Client) ContainerExecResize(ctx context.Context, execID string, options ContainerResizeOptions) error {
|
||||
// ExecResizeOptions holds options for resizing a container exec TTY.
|
||||
type ExecResizeOptions ContainerResizeOptions
|
||||
|
||||
// ExecResizeResult holds the result of resizing a container exec TTY.
|
||||
type ExecResizeResult struct {
|
||||
}
|
||||
|
||||
// ExecResize changes the size of the tty for an exec process running inside a container.
|
||||
func (cli *Client) ExecResize(ctx context.Context, execID string, options ExecResizeOptions) (ExecResizeResult, error) {
|
||||
execID, err := trimID("exec", execID)
|
||||
if err != nil {
|
||||
return err
|
||||
return ExecResizeResult{}, err
|
||||
}
|
||||
return cli.resize(ctx, "/exec/"+execID, options.Height, options.Width)
|
||||
err = cli.resize(ctx, "/exec/"+execID, options.Height, options.Width)
|
||||
return ExecResizeResult{}, err
|
||||
}
|
||||
|
||||
func (cli *Client) resize(ctx context.Context, basePath string, height, width uint) error {
|
||||
|
||||
@@ -28,10 +28,10 @@ func TestContainerResizeError(t *testing.T) {
|
||||
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||
}
|
||||
|
||||
func TestContainerExecResizeError(t *testing.T) {
|
||||
func TestExecResizeError(t *testing.T) {
|
||||
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
|
||||
assert.NilError(t, err)
|
||||
err = client.ContainerExecResize(context.Background(), "exec_id", ContainerResizeOptions{})
|
||||
_, err = client.ExecResize(context.Background(), "exec_id", ExecResizeOptions{})
|
||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
|
||||
}
|
||||
|
||||
@@ -78,22 +78,22 @@ func TestContainerResize(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainerExecResize(t *testing.T) {
|
||||
func TestExecResize(t *testing.T) {
|
||||
const expectedURL = "/exec/exec_id/resize"
|
||||
tests := []struct {
|
||||
doc string
|
||||
opts ContainerResizeOptions
|
||||
opts ExecResizeOptions
|
||||
expectedHeight, expectedWidth string
|
||||
}{
|
||||
{
|
||||
doc: "zero width height", // valid, but not very useful
|
||||
opts: ContainerResizeOptions{},
|
||||
opts: ExecResizeOptions{},
|
||||
expectedWidth: "0",
|
||||
expectedHeight: "0",
|
||||
},
|
||||
{
|
||||
doc: "valid resize",
|
||||
opts: ContainerResizeOptions{
|
||||
opts: ExecResizeOptions{
|
||||
Height: 500,
|
||||
Width: 600,
|
||||
},
|
||||
@@ -102,7 +102,7 @@ func TestContainerExecResize(t *testing.T) {
|
||||
},
|
||||
{
|
||||
doc: "larger than maxint64",
|
||||
opts: ContainerResizeOptions{
|
||||
opts: ExecResizeOptions{
|
||||
Height: math.MaxInt64 + 1,
|
||||
Width: math.MaxInt64 + 2,
|
||||
},
|
||||
@@ -114,7 +114,7 @@ func TestContainerExecResize(t *testing.T) {
|
||||
t.Run(tc.doc, func(t *testing.T) {
|
||||
client, err := NewClientWithOpts(WithMockClient(resizeTransport(t, expectedURL, tc.expectedHeight, tc.expectedWidth)))
|
||||
assert.NilError(t, err)
|
||||
err = client.ContainerExecResize(context.Background(), "exec_id", tc.opts)
|
||||
_, err = client.ExecResize(context.Background(), "exec_id", tc.opts)
|
||||
assert.NilError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user