exec.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. package container // import "github.com/docker/docker/api/server/router/container"
  2. import (
  3. "context"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "net/http"
  9. "strconv"
  10. "github.com/docker/docker/api/server/httputils"
  11. "github.com/docker/docker/api/types"
  12. "github.com/docker/docker/api/types/versions"
  13. "github.com/docker/docker/errdefs"
  14. "github.com/docker/docker/pkg/stdcopy"
  15. "github.com/sirupsen/logrus"
  16. )
  17. func (s *containerRouter) getExecByID(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  18. eConfig, err := s.backend.ContainerExecInspect(vars["id"])
  19. if err != nil {
  20. return err
  21. }
  22. return httputils.WriteJSON(w, http.StatusOK, eConfig)
  23. }
  24. type execCommandError struct{}
  25. func (execCommandError) Error() string {
  26. return "No exec command specified"
  27. }
  28. func (execCommandError) InvalidParameter() {}
  29. func (s *containerRouter) postContainerExecCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  30. if err := httputils.ParseForm(r); err != nil {
  31. return err
  32. }
  33. if err := httputils.CheckForJSON(r); err != nil {
  34. return err
  35. }
  36. name := vars["name"]
  37. execConfig := &types.ExecConfig{}
  38. if err := json.NewDecoder(r.Body).Decode(execConfig); err != nil {
  39. if err == io.EOF {
  40. return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
  41. }
  42. return errdefs.InvalidParameter(err)
  43. }
  44. if len(execConfig.Cmd) == 0 {
  45. return execCommandError{}
  46. }
  47. // Register an instance of Exec in container.
  48. id, err := s.backend.ContainerExecCreate(name, execConfig)
  49. if err != nil {
  50. logrus.Errorf("Error setting up exec command in container %s: %v", name, err)
  51. return err
  52. }
  53. return httputils.WriteJSON(w, http.StatusCreated, &types.IDResponse{
  54. ID: id,
  55. })
  56. }
  57. // TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start.
  58. func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  59. if err := httputils.ParseForm(r); err != nil {
  60. return err
  61. }
  62. version := httputils.VersionFromContext(ctx)
  63. if versions.GreaterThan(version, "1.21") {
  64. if err := httputils.CheckForJSON(r); err != nil {
  65. return err
  66. }
  67. }
  68. var (
  69. execName = vars["name"]
  70. stdin, inStream io.ReadCloser
  71. stdout, stderr, outStream io.Writer
  72. )
  73. execStartCheck := &types.ExecStartCheck{}
  74. if err := json.NewDecoder(r.Body).Decode(execStartCheck); err != nil {
  75. if err == io.EOF {
  76. return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
  77. }
  78. return errdefs.InvalidParameter(err)
  79. }
  80. if exists, err := s.backend.ExecExists(execName); !exists {
  81. return err
  82. }
  83. if !execStartCheck.Detach {
  84. var err error
  85. // Setting up the streaming http interface.
  86. inStream, outStream, err = httputils.HijackConnection(w)
  87. if err != nil {
  88. return err
  89. }
  90. defer httputils.CloseStreams(inStream, outStream)
  91. if _, ok := r.Header["Upgrade"]; ok {
  92. fmt.Fprint(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n")
  93. } else {
  94. fmt.Fprint(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n")
  95. }
  96. // copy headers that were removed as part of hijack
  97. if err := w.Header().WriteSubset(outStream, nil); err != nil {
  98. return err
  99. }
  100. fmt.Fprint(outStream, "\r\n")
  101. stdin = inStream
  102. stdout = outStream
  103. if !execStartCheck.Tty {
  104. stderr = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
  105. stdout = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
  106. }
  107. }
  108. // Now run the user process in container.
  109. // Maybe we should we pass ctx here if we're not detaching?
  110. if err := s.backend.ContainerExecStart(context.Background(), execName, stdin, stdout, stderr); err != nil {
  111. if execStartCheck.Detach {
  112. return err
  113. }
  114. stdout.Write([]byte(err.Error() + "\r\n"))
  115. logrus.Errorf("Error running exec %s in container: %v", execName, err)
  116. }
  117. return nil
  118. }
  119. func (s *containerRouter) postContainerExecResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  120. if err := httputils.ParseForm(r); err != nil {
  121. return err
  122. }
  123. height, err := strconv.Atoi(r.Form.Get("h"))
  124. if err != nil {
  125. return errdefs.InvalidParameter(err)
  126. }
  127. width, err := strconv.Atoi(r.Form.Get("w"))
  128. if err != nil {
  129. return errdefs.InvalidParameter(err)
  130. }
  131. return s.backend.ContainerExecResize(vars["name"], height, width)
  132. }