exec.go 2.0 KB

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