github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/integration/internal/container/exec.go (about) 1 package container 2 3 import ( 4 "bytes" 5 "context" 6 7 "github.com/demonoid81/moby/api/types" 8 "github.com/demonoid81/moby/client" 9 "github.com/demonoid81/moby/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 }