container_linux.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. package libcontainerd
  2. import (
  3. "encoding/json"
  4. "io"
  5. "io/ioutil"
  6. "os"
  7. "path/filepath"
  8. "syscall"
  9. "time"
  10. "github.com/Sirupsen/logrus"
  11. containerd "github.com/docker/containerd/api/grpc/types"
  12. "github.com/docker/docker/pkg/ioutils"
  13. "github.com/docker/docker/restartmanager"
  14. "github.com/opencontainers/runtime-spec/specs-go"
  15. "golang.org/x/net/context"
  16. )
  17. type container struct {
  18. containerCommon
  19. // Platform specific fields are below here.
  20. pauseMonitor
  21. oom bool
  22. runtime string
  23. runtimeArgs []string
  24. }
  25. type runtime struct {
  26. path string
  27. args []string
  28. }
  29. // WithRuntime sets the runtime to be used for the created container
  30. func WithRuntime(path string, args []string) CreateOption {
  31. return runtime{path, args}
  32. }
  33. func (rt runtime) Apply(p interface{}) error {
  34. if pr, ok := p.(*container); ok {
  35. pr.runtime = rt.path
  36. pr.runtimeArgs = rt.args
  37. }
  38. return nil
  39. }
  40. func (ctr *container) clean() error {
  41. if os.Getenv("LIBCONTAINERD_NOCLEAN") == "1" {
  42. return nil
  43. }
  44. if _, err := os.Lstat(ctr.dir); err != nil {
  45. if os.IsNotExist(err) {
  46. return nil
  47. }
  48. return err
  49. }
  50. if err := os.RemoveAll(ctr.dir); err != nil {
  51. return err
  52. }
  53. return nil
  54. }
  55. // cleanProcess removes the fifos used by an additional process.
  56. // Caller needs to lock container ID before calling this method.
  57. func (ctr *container) cleanProcess(id string) {
  58. if p, ok := ctr.processes[id]; ok {
  59. for _, i := range []int{syscall.Stdin, syscall.Stdout, syscall.Stderr} {
  60. if err := os.Remove(p.fifo(i)); err != nil {
  61. logrus.Warnf("libcontainerd: failed to remove %v for process %v: %v", p.fifo(i), id, err)
  62. }
  63. }
  64. }
  65. delete(ctr.processes, id)
  66. }
  67. func (ctr *container) spec() (*specs.Spec, error) {
  68. var spec specs.Spec
  69. dt, err := ioutil.ReadFile(filepath.Join(ctr.dir, configFilename))
  70. if err != nil {
  71. return nil, err
  72. }
  73. if err := json.Unmarshal(dt, &spec); err != nil {
  74. return nil, err
  75. }
  76. return &spec, nil
  77. }
  78. func (ctr *container) start(checkpoint string, checkpointDir string) error {
  79. spec, err := ctr.spec()
  80. if err != nil {
  81. return nil
  82. }
  83. createChan := make(chan struct{})
  84. iopipe, err := ctr.openFifos(spec.Process.Terminal)
  85. if err != nil {
  86. return err
  87. }
  88. // we need to delay stdin closure after container start or else "stdin close"
  89. // event will be rejected by containerd.
  90. // stdin closure happens in AttachStreams
  91. stdin := iopipe.Stdin
  92. iopipe.Stdin = ioutils.NewWriteCloserWrapper(stdin, func() error {
  93. go func() {
  94. <-createChan
  95. stdin.Close()
  96. }()
  97. return nil
  98. })
  99. r := &containerd.CreateContainerRequest{
  100. Id: ctr.containerID,
  101. BundlePath: ctr.dir,
  102. Stdin: ctr.fifo(syscall.Stdin),
  103. Stdout: ctr.fifo(syscall.Stdout),
  104. Stderr: ctr.fifo(syscall.Stderr),
  105. Checkpoint: checkpoint,
  106. CheckpointDir: checkpointDir,
  107. // check to see if we are running in ramdisk to disable pivot root
  108. NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "",
  109. Runtime: ctr.runtime,
  110. RuntimeArgs: ctr.runtimeArgs,
  111. }
  112. ctr.client.appendContainer(ctr)
  113. if err := ctr.client.backend.AttachStreams(ctr.containerID, *iopipe); err != nil {
  114. close(createChan)
  115. ctr.closeFifos(iopipe)
  116. return err
  117. }
  118. resp, err := ctr.client.remote.apiClient.CreateContainer(context.Background(), r)
  119. if err != nil {
  120. close(createChan)
  121. ctr.closeFifos(iopipe)
  122. return err
  123. }
  124. ctr.startedAt = time.Now()
  125. ctr.systemPid = systemPid(resp.Container)
  126. close(createChan)
  127. return ctr.client.backend.StateChanged(ctr.containerID, StateInfo{
  128. CommonStateInfo: CommonStateInfo{
  129. State: StateStart,
  130. Pid: ctr.systemPid,
  131. }})
  132. }
  133. func (ctr *container) newProcess(friendlyName string) *process {
  134. return &process{
  135. dir: ctr.dir,
  136. processCommon: processCommon{
  137. containerID: ctr.containerID,
  138. friendlyName: friendlyName,
  139. client: ctr.client,
  140. },
  141. }
  142. }
  143. func (ctr *container) handleEvent(e *containerd.Event) error {
  144. ctr.client.lock(ctr.containerID)
  145. defer ctr.client.unlock(ctr.containerID)
  146. switch e.Type {
  147. case StateExit, StatePause, StateResume, StateOOM:
  148. var waitRestart chan error
  149. st := StateInfo{
  150. CommonStateInfo: CommonStateInfo{
  151. State: e.Type,
  152. ExitCode: e.Status,
  153. },
  154. OOMKilled: e.Type == StateExit && ctr.oom,
  155. }
  156. if e.Type == StateOOM {
  157. ctr.oom = true
  158. }
  159. if e.Type == StateExit && e.Pid != InitFriendlyName {
  160. st.ProcessID = e.Pid
  161. st.State = StateExitProcess
  162. }
  163. if st.State == StateExit && ctr.restartManager != nil {
  164. restart, wait, err := ctr.restartManager.ShouldRestart(e.Status, false, time.Since(ctr.startedAt))
  165. if err != nil {
  166. logrus.Warnf("libcontainerd: container %s %v", ctr.containerID, err)
  167. } else if restart {
  168. st.State = StateRestart
  169. ctr.restarting = true
  170. ctr.client.deleteContainer(e.Id)
  171. waitRestart = wait
  172. }
  173. }
  174. // Remove process from list if we have exited
  175. // We need to do so here in case the Message Handler decides to restart it.
  176. switch st.State {
  177. case StateExit:
  178. ctr.clean()
  179. ctr.client.deleteContainer(e.Id)
  180. case StateExitProcess:
  181. ctr.cleanProcess(st.ProcessID)
  182. }
  183. ctr.client.q.append(e.Id, func() {
  184. if err := ctr.client.backend.StateChanged(e.Id, st); err != nil {
  185. logrus.Errorf("libcontainerd: backend.StateChanged(): %v", err)
  186. }
  187. if st.State == StateRestart {
  188. go func() {
  189. err := <-waitRestart
  190. ctr.client.lock(ctr.containerID)
  191. defer ctr.client.unlock(ctr.containerID)
  192. ctr.restarting = false
  193. if err == nil {
  194. if err = ctr.start("", ""); err != nil {
  195. logrus.Errorf("libcontainerd: error restarting %v", err)
  196. }
  197. }
  198. if err != nil {
  199. st.State = StateExit
  200. ctr.clean()
  201. ctr.client.q.append(e.Id, func() {
  202. if err := ctr.client.backend.StateChanged(e.Id, st); err != nil {
  203. logrus.Errorf("libcontainerd: %v", err)
  204. }
  205. })
  206. if err != restartmanager.ErrRestartCanceled {
  207. logrus.Errorf("libcontainerd: %v", err)
  208. }
  209. }
  210. }()
  211. }
  212. if e.Type == StatePause || e.Type == StateResume {
  213. ctr.pauseMonitor.handle(e.Type)
  214. }
  215. if e.Type == StateExit {
  216. if en := ctr.client.getExitNotifier(e.Id); en != nil {
  217. en.close()
  218. }
  219. }
  220. })
  221. default:
  222. logrus.Debugf("libcontainerd: event unhandled: %+v", e)
  223. }
  224. return nil
  225. }
  226. // discardFifos attempts to fully read the container fifos to unblock processes
  227. // that may be blocked on the writer side.
  228. func (ctr *container) discardFifos() {
  229. for _, i := range []int{syscall.Stdout, syscall.Stderr} {
  230. f := ctr.fifo(i)
  231. c := make(chan struct{})
  232. go func() {
  233. r := openReaderFromFifo(f)
  234. close(c) // this channel is used to not close the writer too early, before readonly open has been called.
  235. io.Copy(ioutil.Discard, r)
  236. }()
  237. <-c
  238. closeReaderFifo(f) // avoid blocking permanently on open if there is no writer side
  239. }
  240. }