init_state.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. // +build !windows
  2. /*
  3. Copyright The containerd Authors.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. */
  14. package proc
  15. import (
  16. "context"
  17. "sync"
  18. "syscall"
  19. "github.com/containerd/console"
  20. "github.com/containerd/containerd/errdefs"
  21. "github.com/containerd/containerd/runtime/proc"
  22. "github.com/containerd/fifo"
  23. runc "github.com/containerd/go-runc"
  24. google_protobuf "github.com/gogo/protobuf/types"
  25. "github.com/pkg/errors"
  26. )
  27. type initState interface {
  28. proc.State
  29. Pause(context.Context) error
  30. Resume(context.Context) error
  31. Update(context.Context, *google_protobuf.Any) error
  32. Checkpoint(context.Context, *CheckpointConfig) error
  33. Exec(context.Context, string, *ExecConfig) (proc.Process, error)
  34. }
  35. type createdState struct {
  36. p *Init
  37. }
  38. func (s *createdState) transition(name string) error {
  39. switch name {
  40. case "running":
  41. s.p.initState = &runningState{p: s.p}
  42. case "stopped":
  43. s.p.initState = &stoppedState{p: s.p}
  44. case "deleted":
  45. s.p.initState = &deletedState{}
  46. default:
  47. return errors.Errorf("invalid state transition %q to %q", stateName(s), name)
  48. }
  49. return nil
  50. }
  51. func (s *createdState) Pause(ctx context.Context) error {
  52. s.p.mu.Lock()
  53. defer s.p.mu.Unlock()
  54. return errors.Errorf("cannot pause task in created state")
  55. }
  56. func (s *createdState) Resume(ctx context.Context) error {
  57. s.p.mu.Lock()
  58. defer s.p.mu.Unlock()
  59. return errors.Errorf("cannot resume task in created state")
  60. }
  61. func (s *createdState) Update(context context.Context, r *google_protobuf.Any) error {
  62. s.p.mu.Lock()
  63. defer s.p.mu.Unlock()
  64. return s.p.update(context, r)
  65. }
  66. func (s *createdState) Checkpoint(context context.Context, r *CheckpointConfig) error {
  67. s.p.mu.Lock()
  68. defer s.p.mu.Unlock()
  69. return errors.Errorf("cannot checkpoint a task in created state")
  70. }
  71. func (s *createdState) Resize(ws console.WinSize) error {
  72. s.p.mu.Lock()
  73. defer s.p.mu.Unlock()
  74. return s.p.resize(ws)
  75. }
  76. func (s *createdState) Start(ctx context.Context) error {
  77. s.p.mu.Lock()
  78. defer s.p.mu.Unlock()
  79. if err := s.p.start(ctx); err != nil {
  80. return err
  81. }
  82. return s.transition("running")
  83. }
  84. func (s *createdState) Delete(ctx context.Context) error {
  85. s.p.mu.Lock()
  86. defer s.p.mu.Unlock()
  87. if err := s.p.delete(ctx); err != nil {
  88. return err
  89. }
  90. return s.transition("deleted")
  91. }
  92. func (s *createdState) Kill(ctx context.Context, sig uint32, all bool) error {
  93. s.p.mu.Lock()
  94. defer s.p.mu.Unlock()
  95. return s.p.kill(ctx, sig, all)
  96. }
  97. func (s *createdState) SetExited(status int) {
  98. s.p.mu.Lock()
  99. defer s.p.mu.Unlock()
  100. s.p.setExited(status)
  101. if err := s.transition("stopped"); err != nil {
  102. panic(err)
  103. }
  104. }
  105. func (s *createdState) Exec(ctx context.Context, path string, r *ExecConfig) (proc.Process, error) {
  106. s.p.mu.Lock()
  107. defer s.p.mu.Unlock()
  108. return s.p.exec(ctx, path, r)
  109. }
  110. type createdCheckpointState struct {
  111. p *Init
  112. opts *runc.RestoreOpts
  113. }
  114. func (s *createdCheckpointState) transition(name string) error {
  115. switch name {
  116. case "running":
  117. s.p.initState = &runningState{p: s.p}
  118. case "stopped":
  119. s.p.initState = &stoppedState{p: s.p}
  120. case "deleted":
  121. s.p.initState = &deletedState{}
  122. default:
  123. return errors.Errorf("invalid state transition %q to %q", stateName(s), name)
  124. }
  125. return nil
  126. }
  127. func (s *createdCheckpointState) Pause(ctx context.Context) error {
  128. s.p.mu.Lock()
  129. defer s.p.mu.Unlock()
  130. return errors.Errorf("cannot pause task in created state")
  131. }
  132. func (s *createdCheckpointState) Resume(ctx context.Context) error {
  133. s.p.mu.Lock()
  134. defer s.p.mu.Unlock()
  135. return errors.Errorf("cannot resume task in created state")
  136. }
  137. func (s *createdCheckpointState) Update(context context.Context, r *google_protobuf.Any) error {
  138. s.p.mu.Lock()
  139. defer s.p.mu.Unlock()
  140. return s.p.update(context, r)
  141. }
  142. func (s *createdCheckpointState) Checkpoint(context context.Context, r *CheckpointConfig) error {
  143. s.p.mu.Lock()
  144. defer s.p.mu.Unlock()
  145. return errors.Errorf("cannot checkpoint a task in created state")
  146. }
  147. func (s *createdCheckpointState) Resize(ws console.WinSize) error {
  148. s.p.mu.Lock()
  149. defer s.p.mu.Unlock()
  150. return s.p.resize(ws)
  151. }
  152. func (s *createdCheckpointState) Start(ctx context.Context) error {
  153. s.p.mu.Lock()
  154. defer s.p.mu.Unlock()
  155. p := s.p
  156. sio := p.stdio
  157. var (
  158. err error
  159. socket *runc.Socket
  160. )
  161. if sio.Terminal {
  162. if socket, err = runc.NewTempConsoleSocket(); err != nil {
  163. return errors.Wrap(err, "failed to create OCI runtime console socket")
  164. }
  165. defer socket.Close()
  166. s.opts.ConsoleSocket = socket
  167. }
  168. if _, err := s.p.runtime.Restore(ctx, p.id, p.bundle, s.opts); err != nil {
  169. return p.runtimeError(err, "OCI runtime restore failed")
  170. }
  171. if sio.Stdin != "" {
  172. sc, err := fifo.OpenFifo(ctx, sio.Stdin, syscall.O_WRONLY|syscall.O_NONBLOCK, 0)
  173. if err != nil {
  174. return errors.Wrapf(err, "failed to open stdin fifo %s", sio.Stdin)
  175. }
  176. p.stdin = sc
  177. p.closers = append(p.closers, sc)
  178. }
  179. var copyWaitGroup sync.WaitGroup
  180. if socket != nil {
  181. console, err := socket.ReceiveMaster()
  182. if err != nil {
  183. return errors.Wrap(err, "failed to retrieve console master")
  184. }
  185. console, err = p.platform.CopyConsole(ctx, console, sio.Stdin, sio.Stdout, sio.Stderr, &p.wg, &copyWaitGroup)
  186. if err != nil {
  187. return errors.Wrap(err, "failed to start console copy")
  188. }
  189. p.console = console
  190. } else if !sio.IsNull() {
  191. if err := copyPipes(ctx, p.io, sio.Stdin, sio.Stdout, sio.Stderr, &p.wg, &copyWaitGroup); err != nil {
  192. return errors.Wrap(err, "failed to start io pipe copy")
  193. }
  194. }
  195. copyWaitGroup.Wait()
  196. pid, err := runc.ReadPidFile(s.opts.PidFile)
  197. if err != nil {
  198. return errors.Wrap(err, "failed to retrieve OCI runtime container pid")
  199. }
  200. p.pid = pid
  201. return s.transition("running")
  202. }
  203. func (s *createdCheckpointState) Delete(ctx context.Context) error {
  204. s.p.mu.Lock()
  205. defer s.p.mu.Unlock()
  206. if err := s.p.delete(ctx); err != nil {
  207. return err
  208. }
  209. return s.transition("deleted")
  210. }
  211. func (s *createdCheckpointState) Kill(ctx context.Context, sig uint32, all bool) error {
  212. s.p.mu.Lock()
  213. defer s.p.mu.Unlock()
  214. return s.p.kill(ctx, sig, all)
  215. }
  216. func (s *createdCheckpointState) SetExited(status int) {
  217. s.p.mu.Lock()
  218. defer s.p.mu.Unlock()
  219. s.p.setExited(status)
  220. if err := s.transition("stopped"); err != nil {
  221. panic(err)
  222. }
  223. }
  224. func (s *createdCheckpointState) Exec(ctx context.Context, path string, r *ExecConfig) (proc.Process, error) {
  225. s.p.mu.Lock()
  226. defer s.p.mu.Unlock()
  227. return nil, errors.Errorf("cannot exec in a created state")
  228. }
  229. type runningState struct {
  230. p *Init
  231. }
  232. func (s *runningState) transition(name string) error {
  233. switch name {
  234. case "stopped":
  235. s.p.initState = &stoppedState{p: s.p}
  236. case "paused":
  237. s.p.initState = &pausedState{p: s.p}
  238. default:
  239. return errors.Errorf("invalid state transition %q to %q", stateName(s), name)
  240. }
  241. return nil
  242. }
  243. func (s *runningState) Pause(ctx context.Context) error {
  244. s.p.mu.Lock()
  245. defer s.p.mu.Unlock()
  246. if err := s.p.pause(ctx); err != nil {
  247. return err
  248. }
  249. return s.transition("paused")
  250. }
  251. func (s *runningState) Resume(ctx context.Context) error {
  252. s.p.mu.Lock()
  253. defer s.p.mu.Unlock()
  254. return errors.Errorf("cannot resume a running process")
  255. }
  256. func (s *runningState) Update(context context.Context, r *google_protobuf.Any) error {
  257. s.p.mu.Lock()
  258. defer s.p.mu.Unlock()
  259. return s.p.update(context, r)
  260. }
  261. func (s *runningState) Checkpoint(ctx context.Context, r *CheckpointConfig) error {
  262. s.p.mu.Lock()
  263. defer s.p.mu.Unlock()
  264. return s.p.checkpoint(ctx, r)
  265. }
  266. func (s *runningState) Resize(ws console.WinSize) error {
  267. s.p.mu.Lock()
  268. defer s.p.mu.Unlock()
  269. return s.p.resize(ws)
  270. }
  271. func (s *runningState) Start(ctx context.Context) error {
  272. s.p.mu.Lock()
  273. defer s.p.mu.Unlock()
  274. return errors.Errorf("cannot start a running process")
  275. }
  276. func (s *runningState) Delete(ctx context.Context) error {
  277. s.p.mu.Lock()
  278. defer s.p.mu.Unlock()
  279. return errors.Errorf("cannot delete a running process")
  280. }
  281. func (s *runningState) Kill(ctx context.Context, sig uint32, all bool) error {
  282. s.p.mu.Lock()
  283. defer s.p.mu.Unlock()
  284. return s.p.kill(ctx, sig, all)
  285. }
  286. func (s *runningState) SetExited(status int) {
  287. s.p.mu.Lock()
  288. defer s.p.mu.Unlock()
  289. s.p.setExited(status)
  290. if err := s.transition("stopped"); err != nil {
  291. panic(err)
  292. }
  293. }
  294. func (s *runningState) Exec(ctx context.Context, path string, r *ExecConfig) (proc.Process, error) {
  295. s.p.mu.Lock()
  296. defer s.p.mu.Unlock()
  297. return s.p.exec(ctx, path, r)
  298. }
  299. type pausedState struct {
  300. p *Init
  301. }
  302. func (s *pausedState) transition(name string) error {
  303. switch name {
  304. case "running":
  305. s.p.initState = &runningState{p: s.p}
  306. case "stopped":
  307. s.p.initState = &stoppedState{p: s.p}
  308. default:
  309. return errors.Errorf("invalid state transition %q to %q", stateName(s), name)
  310. }
  311. return nil
  312. }
  313. func (s *pausedState) Pause(ctx context.Context) error {
  314. s.p.mu.Lock()
  315. defer s.p.mu.Unlock()
  316. return errors.Errorf("cannot pause a paused container")
  317. }
  318. func (s *pausedState) Resume(ctx context.Context) error {
  319. s.p.mu.Lock()
  320. defer s.p.mu.Unlock()
  321. if err := s.p.resume(ctx); err != nil {
  322. return err
  323. }
  324. return s.transition("running")
  325. }
  326. func (s *pausedState) Update(context context.Context, r *google_protobuf.Any) error {
  327. s.p.mu.Lock()
  328. defer s.p.mu.Unlock()
  329. return s.p.update(context, r)
  330. }
  331. func (s *pausedState) Checkpoint(ctx context.Context, r *CheckpointConfig) error {
  332. s.p.mu.Lock()
  333. defer s.p.mu.Unlock()
  334. return s.p.checkpoint(ctx, r)
  335. }
  336. func (s *pausedState) Resize(ws console.WinSize) error {
  337. s.p.mu.Lock()
  338. defer s.p.mu.Unlock()
  339. return s.p.resize(ws)
  340. }
  341. func (s *pausedState) Start(ctx context.Context) error {
  342. s.p.mu.Lock()
  343. defer s.p.mu.Unlock()
  344. return errors.Errorf("cannot start a paused process")
  345. }
  346. func (s *pausedState) Delete(ctx context.Context) error {
  347. s.p.mu.Lock()
  348. defer s.p.mu.Unlock()
  349. return errors.Errorf("cannot delete a paused process")
  350. }
  351. func (s *pausedState) Kill(ctx context.Context, sig uint32, all bool) error {
  352. s.p.mu.Lock()
  353. defer s.p.mu.Unlock()
  354. return s.p.kill(ctx, sig, all)
  355. }
  356. func (s *pausedState) SetExited(status int) {
  357. s.p.mu.Lock()
  358. defer s.p.mu.Unlock()
  359. s.p.setExited(status)
  360. if err := s.transition("stopped"); err != nil {
  361. panic(err)
  362. }
  363. }
  364. func (s *pausedState) Exec(ctx context.Context, path string, r *ExecConfig) (proc.Process, error) {
  365. s.p.mu.Lock()
  366. defer s.p.mu.Unlock()
  367. return nil, errors.Errorf("cannot exec in a paused state")
  368. }
  369. type stoppedState struct {
  370. p *Init
  371. }
  372. func (s *stoppedState) transition(name string) error {
  373. switch name {
  374. case "deleted":
  375. s.p.initState = &deletedState{}
  376. default:
  377. return errors.Errorf("invalid state transition %q to %q", stateName(s), name)
  378. }
  379. return nil
  380. }
  381. func (s *stoppedState) Pause(ctx context.Context) error {
  382. s.p.mu.Lock()
  383. defer s.p.mu.Unlock()
  384. return errors.Errorf("cannot pause a stopped container")
  385. }
  386. func (s *stoppedState) Resume(ctx context.Context) error {
  387. s.p.mu.Lock()
  388. defer s.p.mu.Unlock()
  389. return errors.Errorf("cannot resume a stopped container")
  390. }
  391. func (s *stoppedState) Update(context context.Context, r *google_protobuf.Any) error {
  392. s.p.mu.Lock()
  393. defer s.p.mu.Unlock()
  394. return errors.Errorf("cannot update a stopped container")
  395. }
  396. func (s *stoppedState) Checkpoint(ctx context.Context, r *CheckpointConfig) error {
  397. s.p.mu.Lock()
  398. defer s.p.mu.Unlock()
  399. return errors.Errorf("cannot checkpoint a stopped container")
  400. }
  401. func (s *stoppedState) Resize(ws console.WinSize) error {
  402. s.p.mu.Lock()
  403. defer s.p.mu.Unlock()
  404. return errors.Errorf("cannot resize a stopped container")
  405. }
  406. func (s *stoppedState) Start(ctx context.Context) error {
  407. s.p.mu.Lock()
  408. defer s.p.mu.Unlock()
  409. return errors.Errorf("cannot start a stopped process")
  410. }
  411. func (s *stoppedState) Delete(ctx context.Context) error {
  412. s.p.mu.Lock()
  413. defer s.p.mu.Unlock()
  414. if err := s.p.delete(ctx); err != nil {
  415. return err
  416. }
  417. return s.transition("deleted")
  418. }
  419. func (s *stoppedState) Kill(ctx context.Context, sig uint32, all bool) error {
  420. return errdefs.ToGRPCf(errdefs.ErrNotFound, "process %s not found", s.p.id)
  421. }
  422. func (s *stoppedState) SetExited(status int) {
  423. // no op
  424. }
  425. func (s *stoppedState) Exec(ctx context.Context, path string, r *ExecConfig) (proc.Process, error) {
  426. s.p.mu.Lock()
  427. defer s.p.mu.Unlock()
  428. return nil, errors.Errorf("cannot exec in a stopped state")
  429. }