Prechádzať zdrojové kódy

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>
Kir Kolyshkin 7 rokov pred
rodič
commit
01143afe54
1 zmenil súbory, kde vykonal 86 pridanie a 0 odobranie
  1. 86 0
      integration/internal/container/exec.go

+ 86 - 0
integration/internal/container/exec.go

@@ -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
+}