init_state.go 12 KB

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