mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
integration: add container.Exec()
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>
This commit is contained in:
parent
1474ec1ecf
commit
01143afe54
1 changed files with 86 additions and 0 deletions
86
integration/internal/container/exec.go
Normal file
86
integration/internal/container/exec.go
Normal file
|
@ -0,0 +1,86 @@
|
|||
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
|
||||
}
|
Loading…
Reference in a new issue