mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
01143afe54
Some test cases might need an ability to execute a command inside a container (in order to analyse its output and/or exit code). It is a bit complicated operation to do so using engine API. The function provided aims to hide this complexity, making exec almost as simple as 'docker exec'. NOTE that the exec is synchronous, and command's stdin is closed. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
86 lines
2.1 KiB
Go
86 lines
2.1 KiB
Go
package container
|
|
|
|
import (
|
|
"bytes"
|
|
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/client"
|
|
"github.com/docker/docker/pkg/stdcopy"
|
|
"golang.org/x/net/context"
|
|
)
|
|
|
|
// ExecResult represents a result returned from Exec()
|
|
type ExecResult struct {
|
|
ExitCode int
|
|
outBuffer *bytes.Buffer
|
|
errBuffer *bytes.Buffer
|
|
}
|
|
|
|
// Stdout returns stdout output of a command run by Exec()
|
|
func (res *ExecResult) Stdout() string {
|
|
return res.outBuffer.String()
|
|
}
|
|
|
|
// Stderr returns stderr output of a command run by Exec()
|
|
func (res *ExecResult) Stderr() string {
|
|
return res.errBuffer.String()
|
|
}
|
|
|
|
// Combined returns combined stdout and stderr output of a command run by Exec()
|
|
func (res *ExecResult) Combined() string {
|
|
return res.outBuffer.String() + res.errBuffer.String()
|
|
}
|
|
|
|
// Exec executes a command inside a container, returning the result
|
|
// containing stdout, stderr, and exit code. Note:
|
|
// - this is a synchronous operation;
|
|
// - cmd stdin is closed.
|
|
func Exec(ctx context.Context, cli client.APIClient, id string, cmd []string) (ExecResult, error) {
|
|
// prepare exec
|
|
execConfig := types.ExecConfig{
|
|
AttachStdout: true,
|
|
AttachStderr: true,
|
|
Cmd: cmd,
|
|
}
|
|
cresp, err := cli.ContainerExecCreate(ctx, id, execConfig)
|
|
if err != nil {
|
|
return ExecResult{}, err
|
|
}
|
|
execID := cresp.ID
|
|
|
|
// run it, with stdout/stderr attached
|
|
aresp, err := cli.ContainerExecAttach(ctx, execID, types.ExecStartCheck{})
|
|
if err != nil {
|
|
return ExecResult{}, err
|
|
}
|
|
defer aresp.Close()
|
|
|
|
// read the output
|
|
var outBuf, errBuf bytes.Buffer
|
|
outputDone := make(chan error)
|
|
|
|
go func() {
|
|
// StdCopy demultiplexes the stream into two buffers
|
|
_, err = stdcopy.StdCopy(&outBuf, &errBuf, aresp.Reader)
|
|
outputDone <- err
|
|
}()
|
|
|
|
select {
|
|
case err := <-outputDone:
|
|
if err != nil {
|
|
return ExecResult{}, err
|
|
}
|
|
break
|
|
|
|
case <-ctx.Done():
|
|
return ExecResult{}, ctx.Err()
|
|
}
|
|
|
|
// get the exit code
|
|
iresp, err := cli.ContainerExecInspect(ctx, execID)
|
|
if err != nil {
|
|
return ExecResult{}, err
|
|
}
|
|
|
|
return ExecResult{ExitCode: iresp.ExitCode, outBuffer: &outBuf, errBuffer: &errBuf}, nil
|
|
}
|