process.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. /*
  2. Copyright The containerd Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package containerd
  14. import (
  15. "context"
  16. "strings"
  17. "syscall"
  18. "time"
  19. "github.com/containerd/containerd/api/services/tasks/v1"
  20. "github.com/containerd/containerd/cio"
  21. "github.com/containerd/containerd/errdefs"
  22. "github.com/pkg/errors"
  23. )
  24. // Process represents a system process
  25. type Process interface {
  26. // ID of the process
  27. ID() string
  28. // Pid is the system specific process id
  29. Pid() uint32
  30. // Start starts the process executing the user's defined binary
  31. Start(context.Context) error
  32. // Delete removes the process and any resources allocated returning the exit status
  33. Delete(context.Context, ...ProcessDeleteOpts) (*ExitStatus, error)
  34. // Kill sends the provided signal to the process
  35. Kill(context.Context, syscall.Signal, ...KillOpts) error
  36. // Wait asynchronously waits for the process to exit, and sends the exit code to the returned channel
  37. Wait(context.Context) (<-chan ExitStatus, error)
  38. // CloseIO allows various pipes to be closed on the process
  39. CloseIO(context.Context, ...IOCloserOpts) error
  40. // Resize changes the width and heigh of the process's terminal
  41. Resize(ctx context.Context, w, h uint32) error
  42. // IO returns the io set for the process
  43. IO() cio.IO
  44. // Status returns the executing status of the process
  45. Status(context.Context) (Status, error)
  46. }
  47. // NewExitStatus populates an ExitStatus
  48. func NewExitStatus(code uint32, t time.Time, err error) *ExitStatus {
  49. return &ExitStatus{
  50. code: code,
  51. exitedAt: t,
  52. err: err,
  53. }
  54. }
  55. // ExitStatus encapsulates a process' exit status.
  56. // It is used by `Wait()` to return either a process exit code or an error
  57. type ExitStatus struct {
  58. code uint32
  59. exitedAt time.Time
  60. err error
  61. }
  62. // Result returns the exit code and time of the exit status.
  63. // An error may be returned here to which indicates there was an error
  64. // at some point while waiting for the exit status. It does not signify
  65. // an error with the process itself.
  66. // If an error is returned, the process may still be running.
  67. func (s ExitStatus) Result() (uint32, time.Time, error) {
  68. return s.code, s.exitedAt, s.err
  69. }
  70. // ExitCode returns the exit code of the process.
  71. // This is only valid is Error() returns nil
  72. func (s ExitStatus) ExitCode() uint32 {
  73. return s.code
  74. }
  75. // ExitTime returns the exit time of the process
  76. // This is only valid is Error() returns nil
  77. func (s ExitStatus) ExitTime() time.Time {
  78. return s.exitedAt
  79. }
  80. // Error returns the error, if any, that occurred while waiting for the
  81. // process.
  82. func (s ExitStatus) Error() error {
  83. return s.err
  84. }
  85. type process struct {
  86. id string
  87. task *task
  88. pid uint32
  89. io cio.IO
  90. }
  91. func (p *process) ID() string {
  92. return p.id
  93. }
  94. // Pid returns the pid of the process
  95. // The pid is not set until start is called and returns
  96. func (p *process) Pid() uint32 {
  97. return p.pid
  98. }
  99. // Start starts the exec process
  100. func (p *process) Start(ctx context.Context) error {
  101. r, err := p.task.client.TaskService().Start(ctx, &tasks.StartRequest{
  102. ContainerID: p.task.id,
  103. ExecID: p.id,
  104. })
  105. if err != nil {
  106. if p.io != nil {
  107. p.io.Cancel()
  108. p.io.Wait()
  109. p.io.Close()
  110. }
  111. return errdefs.FromGRPC(err)
  112. }
  113. p.pid = r.Pid
  114. return nil
  115. }
  116. func (p *process) Kill(ctx context.Context, s syscall.Signal, opts ...KillOpts) error {
  117. var i KillInfo
  118. for _, o := range opts {
  119. if err := o(ctx, &i); err != nil {
  120. return err
  121. }
  122. }
  123. _, err := p.task.client.TaskService().Kill(ctx, &tasks.KillRequest{
  124. Signal: uint32(s),
  125. ContainerID: p.task.id,
  126. ExecID: p.id,
  127. All: i.All,
  128. })
  129. return errdefs.FromGRPC(err)
  130. }
  131. func (p *process) Wait(ctx context.Context) (<-chan ExitStatus, error) {
  132. c := make(chan ExitStatus, 1)
  133. go func() {
  134. defer close(c)
  135. r, err := p.task.client.TaskService().Wait(ctx, &tasks.WaitRequest{
  136. ContainerID: p.task.id,
  137. ExecID: p.id,
  138. })
  139. if err != nil {
  140. c <- ExitStatus{
  141. code: UnknownExitStatus,
  142. err: err,
  143. }
  144. return
  145. }
  146. c <- ExitStatus{
  147. code: r.ExitStatus,
  148. exitedAt: r.ExitedAt,
  149. }
  150. }()
  151. return c, nil
  152. }
  153. func (p *process) CloseIO(ctx context.Context, opts ...IOCloserOpts) error {
  154. r := &tasks.CloseIORequest{
  155. ContainerID: p.task.id,
  156. ExecID: p.id,
  157. }
  158. var i IOCloseInfo
  159. for _, o := range opts {
  160. o(&i)
  161. }
  162. r.Stdin = i.Stdin
  163. _, err := p.task.client.TaskService().CloseIO(ctx, r)
  164. return errdefs.FromGRPC(err)
  165. }
  166. func (p *process) IO() cio.IO {
  167. return p.io
  168. }
  169. func (p *process) Resize(ctx context.Context, w, h uint32) error {
  170. _, err := p.task.client.TaskService().ResizePty(ctx, &tasks.ResizePtyRequest{
  171. ContainerID: p.task.id,
  172. Width: w,
  173. Height: h,
  174. ExecID: p.id,
  175. })
  176. return errdefs.FromGRPC(err)
  177. }
  178. func (p *process) Delete(ctx context.Context, opts ...ProcessDeleteOpts) (*ExitStatus, error) {
  179. for _, o := range opts {
  180. if err := o(ctx, p); err != nil {
  181. return nil, err
  182. }
  183. }
  184. status, err := p.Status(ctx)
  185. if err != nil {
  186. return nil, err
  187. }
  188. switch status.Status {
  189. case Running, Paused, Pausing:
  190. return nil, errors.Wrapf(errdefs.ErrFailedPrecondition, "process must be stopped before deletion")
  191. }
  192. r, err := p.task.client.TaskService().DeleteProcess(ctx, &tasks.DeleteProcessRequest{
  193. ContainerID: p.task.id,
  194. ExecID: p.id,
  195. })
  196. if err != nil {
  197. return nil, errdefs.FromGRPC(err)
  198. }
  199. if p.io != nil {
  200. p.io.Cancel()
  201. p.io.Wait()
  202. p.io.Close()
  203. }
  204. return &ExitStatus{code: r.ExitStatus, exitedAt: r.ExitedAt}, nil
  205. }
  206. func (p *process) Status(ctx context.Context) (Status, error) {
  207. r, err := p.task.client.TaskService().Get(ctx, &tasks.GetRequest{
  208. ContainerID: p.task.id,
  209. ExecID: p.id,
  210. })
  211. if err != nil {
  212. return Status{}, errdefs.FromGRPC(err)
  213. }
  214. return Status{
  215. Status: ProcessStatus(strings.ToLower(r.Process.Status.String())),
  216. ExitStatus: r.Process.ExitStatus,
  217. }, nil
  218. }