moby/integration/internal/container/exec.go
Ziheng Liu 6233217a31 integration/internal/container: fix a goroutine leak bug by adding 1 buffer
Signed-off-by: Ziheng Liu <lzhfromustc@gmail.com>
2019-10-27 20:22:52 -04:00

86 lines
2 KiB
Go

package container
import (
"bytes"
"context"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/docker/docker/pkg/stdcopy"
)
// 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, 1)
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
}