github.com/lacework-dev/go-moby@v20.10.12+incompatible/integration/internal/container/exec.go (about)

     1  package container
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  
     7  	"github.com/docker/docker/api/types"
     8  	"github.com/docker/docker/client"
     9  	"github.com/docker/docker/pkg/stdcopy"
    10  )
    11  
    12  // ExecResult represents a result returned from Exec()
    13  type ExecResult struct {
    14  	ExitCode  int
    15  	outBuffer *bytes.Buffer
    16  	errBuffer *bytes.Buffer
    17  }
    18  
    19  // Stdout returns stdout output of a command run by Exec()
    20  func (res *ExecResult) Stdout() string {
    21  	return res.outBuffer.String()
    22  }
    23  
    24  // Stderr returns stderr output of a command run by Exec()
    25  func (res *ExecResult) Stderr() string {
    26  	return res.errBuffer.String()
    27  }
    28  
    29  // Combined returns combined stdout and stderr output of a command run by Exec()
    30  func (res *ExecResult) Combined() string {
    31  	return res.outBuffer.String() + res.errBuffer.String()
    32  }
    33  
    34  // Exec executes a command inside a container, returning the result
    35  // containing stdout, stderr, and exit code. Note:
    36  //  - this is a synchronous operation;
    37  //  - cmd stdin is closed.
    38  func Exec(ctx context.Context, cli client.APIClient, id string, cmd []string) (ExecResult, error) {
    39  	// prepare exec
    40  	execConfig := types.ExecConfig{
    41  		AttachStdout: true,
    42  		AttachStderr: true,
    43  		Cmd:          cmd,
    44  	}
    45  	cresp, err := cli.ContainerExecCreate(ctx, id, execConfig)
    46  	if err != nil {
    47  		return ExecResult{}, err
    48  	}
    49  	execID := cresp.ID
    50  
    51  	// run it, with stdout/stderr attached
    52  	aresp, err := cli.ContainerExecAttach(ctx, execID, types.ExecStartCheck{})
    53  	if err != nil {
    54  		return ExecResult{}, err
    55  	}
    56  	defer aresp.Close()
    57  
    58  	// read the output
    59  	var outBuf, errBuf bytes.Buffer
    60  	outputDone := make(chan error, 1)
    61  
    62  	go func() {
    63  		// StdCopy demultiplexes the stream into two buffers
    64  		_, err = stdcopy.StdCopy(&outBuf, &errBuf, aresp.Reader)
    65  		outputDone <- err
    66  	}()
    67  
    68  	select {
    69  	case err := <-outputDone:
    70  		if err != nil {
    71  			return ExecResult{}, err
    72  		}
    73  		break
    74  
    75  	case <-ctx.Done():
    76  		return ExecResult{}, ctx.Err()
    77  	}
    78  
    79  	// get the exit code
    80  	iresp, err := cli.ContainerExecInspect(ctx, execID)
    81  	if err != nil {
    82  		return ExecResult{}, err
    83  	}
    84  
    85  	return ExecResult{ExitCode: iresp.ExitCode, outBuffer: &outBuf, errBuffer: &errBuf}, nil
    86  }