exec.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. // Copyright 2015 go-dockerclient authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package docker
  5. import (
  6. "encoding/json"
  7. "fmt"
  8. "io"
  9. "net/http"
  10. "net/url"
  11. "strconv"
  12. )
  13. // Exec is the type representing a `docker exec` instance and containing the
  14. // instance ID
  15. type Exec struct {
  16. ID string `json:"Id,omitempty" yaml:"Id,omitempty"`
  17. }
  18. // CreateExecOptions specify parameters to the CreateExecContainer function.
  19. //
  20. // See https://goo.gl/1KSIb7 for more details
  21. type CreateExecOptions struct {
  22. AttachStdin bool `json:"AttachStdin,omitempty" yaml:"AttachStdin,omitempty"`
  23. AttachStdout bool `json:"AttachStdout,omitempty" yaml:"AttachStdout,omitempty"`
  24. AttachStderr bool `json:"AttachStderr,omitempty" yaml:"AttachStderr,omitempty"`
  25. Tty bool `json:"Tty,omitempty" yaml:"Tty,omitempty"`
  26. Cmd []string `json:"Cmd,omitempty" yaml:"Cmd,omitempty"`
  27. Container string `json:"Container,omitempty" yaml:"Container,omitempty"`
  28. User string `json:"User,omitempty" yaml:"User,omitempty"`
  29. }
  30. // CreateExec sets up an exec instance in a running container `id`, returning the exec
  31. // instance, or an error in case of failure.
  32. //
  33. // See https://goo.gl/1KSIb7 for more details
  34. func (c *Client) CreateExec(opts CreateExecOptions) (*Exec, error) {
  35. path := fmt.Sprintf("/containers/%s/exec", opts.Container)
  36. resp, err := c.do("POST", path, doOptions{data: opts})
  37. if err != nil {
  38. if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
  39. return nil, &NoSuchContainer{ID: opts.Container}
  40. }
  41. return nil, err
  42. }
  43. defer resp.Body.Close()
  44. var exec Exec
  45. if err := json.NewDecoder(resp.Body).Decode(&exec); err != nil {
  46. return nil, err
  47. }
  48. return &exec, nil
  49. }
  50. // StartExecOptions specify parameters to the StartExecContainer function.
  51. //
  52. // See https://goo.gl/iQCnto for more details
  53. type StartExecOptions struct {
  54. Detach bool `json:"Detach,omitempty" yaml:"Detach,omitempty"`
  55. Tty bool `json:"Tty,omitempty" yaml:"Tty,omitempty"`
  56. InputStream io.Reader `qs:"-"`
  57. OutputStream io.Writer `qs:"-"`
  58. ErrorStream io.Writer `qs:"-"`
  59. // Use raw terminal? Usually true when the container contains a TTY.
  60. RawTerminal bool `qs:"-"`
  61. // If set, after a successful connect, a sentinel will be sent and then the
  62. // client will block on receive before continuing.
  63. //
  64. // It must be an unbuffered channel. Using a buffered channel can lead
  65. // to unexpected behavior.
  66. Success chan struct{} `json:"-"`
  67. }
  68. // StartExec starts a previously set up exec instance id. If opts.Detach is
  69. // true, it returns after starting the exec command. Otherwise, it sets up an
  70. // interactive session with the exec command.
  71. //
  72. // See https://goo.gl/iQCnto for more details
  73. func (c *Client) StartExec(id string, opts StartExecOptions) error {
  74. cw, err := c.StartExecNonBlocking(id, opts)
  75. if err != nil {
  76. return err
  77. }
  78. if cw != nil {
  79. return cw.Wait()
  80. }
  81. return nil
  82. }
  83. // StartExecNonBlocking starts a previously set up exec instance id. If opts.Detach is
  84. // true, it returns after starting the exec command. Otherwise, it sets up an
  85. // interactive session with the exec command.
  86. //
  87. // See https://goo.gl/iQCnto for more details
  88. func (c *Client) StartExecNonBlocking(id string, opts StartExecOptions) (CloseWaiter, error) {
  89. if id == "" {
  90. return nil, &NoSuchExec{ID: id}
  91. }
  92. path := fmt.Sprintf("/exec/%s/start", id)
  93. if opts.Detach {
  94. resp, err := c.do("POST", path, doOptions{data: opts})
  95. if err != nil {
  96. if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
  97. return nil, &NoSuchExec{ID: id}
  98. }
  99. return nil, err
  100. }
  101. defer resp.Body.Close()
  102. return nil, nil
  103. }
  104. return c.hijack("POST", path, hijackOptions{
  105. success: opts.Success,
  106. setRawTerminal: opts.RawTerminal,
  107. in: opts.InputStream,
  108. stdout: opts.OutputStream,
  109. stderr: opts.ErrorStream,
  110. data: opts,
  111. })
  112. }
  113. // ResizeExecTTY resizes the tty session used by the exec command id. This API
  114. // is valid only if Tty was specified as part of creating and starting the exec
  115. // command.
  116. //
  117. // See https://goo.gl/e1JpsA for more details
  118. func (c *Client) ResizeExecTTY(id string, height, width int) error {
  119. params := make(url.Values)
  120. params.Set("h", strconv.Itoa(height))
  121. params.Set("w", strconv.Itoa(width))
  122. path := fmt.Sprintf("/exec/%s/resize?%s", id, params.Encode())
  123. resp, err := c.do("POST", path, doOptions{})
  124. if err != nil {
  125. return err
  126. }
  127. resp.Body.Close()
  128. return nil
  129. }
  130. // ExecProcessConfig is a type describing the command associated to a Exec
  131. // instance. It's used in the ExecInspect type.
  132. type ExecProcessConfig struct {
  133. Privileged bool `json:"privileged,omitempty" yaml:"privileged,omitempty"`
  134. User string `json:"user,omitempty" yaml:"user,omitempty"`
  135. Tty bool `json:"tty,omitempty" yaml:"tty,omitempty"`
  136. EntryPoint string `json:"entrypoint,omitempty" yaml:"entrypoint,omitempty"`
  137. Arguments []string `json:"arguments,omitempty" yaml:"arguments,omitempty"`
  138. }
  139. // ExecInspect is a type with details about a exec instance, including the
  140. // exit code if the command has finished running. It's returned by a api
  141. // call to /exec/(id)/json
  142. //
  143. // See https://goo.gl/gPtX9R for more details
  144. type ExecInspect struct {
  145. ID string `json:"ID,omitempty" yaml:"ID,omitempty"`
  146. Running bool `json:"Running,omitempty" yaml:"Running,omitempty"`
  147. ExitCode int `json:"ExitCode,omitempty" yaml:"ExitCode,omitempty"`
  148. OpenStdin bool `json:"OpenStdin,omitempty" yaml:"OpenStdin,omitempty"`
  149. OpenStderr bool `json:"OpenStderr,omitempty" yaml:"OpenStderr,omitempty"`
  150. OpenStdout bool `json:"OpenStdout,omitempty" yaml:"OpenStdout,omitempty"`
  151. ProcessConfig ExecProcessConfig `json:"ProcessConfig,omitempty" yaml:"ProcessConfig,omitempty"`
  152. Container Container `json:"Container,omitempty" yaml:"Container,omitempty"`
  153. }
  154. // InspectExec returns low-level information about the exec command id.
  155. //
  156. // See https://goo.gl/gPtX9R for more details
  157. func (c *Client) InspectExec(id string) (*ExecInspect, error) {
  158. path := fmt.Sprintf("/exec/%s/json", id)
  159. resp, err := c.do("GET", path, doOptions{})
  160. if err != nil {
  161. if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
  162. return nil, &NoSuchExec{ID: id}
  163. }
  164. return nil, err
  165. }
  166. defer resp.Body.Close()
  167. var exec ExecInspect
  168. if err := json.NewDecoder(resp.Body).Decode(&exec); err != nil {
  169. return nil, err
  170. }
  171. return &exec, nil
  172. }
  173. // NoSuchExec is the error returned when a given exec instance does not exist.
  174. type NoSuchExec struct {
  175. ID string
  176. }
  177. func (err *NoSuchExec) Error() string {
  178. return "No such exec instance: " + err.ID
  179. }