container_linux.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. package libcontainerd
  2. import (
  3. "encoding/json"
  4. "io/ioutil"
  5. "os"
  6. "path/filepath"
  7. "syscall"
  8. "github.com/Sirupsen/logrus"
  9. containerd "github.com/docker/containerd/api/grpc/types"
  10. "github.com/docker/docker/restartmanager"
  11. "github.com/opencontainers/specs/specs-go"
  12. "golang.org/x/net/context"
  13. )
  14. type container struct {
  15. containerCommon
  16. // Platform specific fields are below here.
  17. pauseMonitor
  18. oom bool
  19. }
  20. func (ctr *container) clean() error {
  21. if os.Getenv("LIBCONTAINERD_NOCLEAN") == "1" {
  22. return nil
  23. }
  24. if _, err := os.Lstat(ctr.dir); err != nil {
  25. if os.IsNotExist(err) {
  26. return nil
  27. }
  28. return err
  29. }
  30. syscall.Unmount(filepath.Join(ctr.dir, "rootfs"), syscall.MNT_DETACH) // ignore error
  31. if err := os.RemoveAll(ctr.dir); err != nil {
  32. return err
  33. }
  34. return nil
  35. }
  36. // cleanProcess removes the fifos used by an additional process.
  37. // Caller needs to lock container ID before calling this method.
  38. func (ctr *container) cleanProcess(id string) {
  39. if p, ok := ctr.processes[id]; ok {
  40. for _, i := range []int{syscall.Stdin, syscall.Stdout, syscall.Stderr} {
  41. if err := os.Remove(p.fifo(i)); err != nil {
  42. logrus.Warnf("failed to remove %v for process %v: %v", p.fifo(i), id, err)
  43. }
  44. }
  45. }
  46. delete(ctr.processes, id)
  47. }
  48. func (ctr *container) spec() (*specs.Spec, error) {
  49. var spec specs.Spec
  50. dt, err := ioutil.ReadFile(filepath.Join(ctr.dir, configFilename))
  51. if err != nil {
  52. return nil, err
  53. }
  54. if err := json.Unmarshal(dt, &spec); err != nil {
  55. return nil, err
  56. }
  57. return &spec, nil
  58. }
  59. func (ctr *container) start() error {
  60. spec, err := ctr.spec()
  61. if err != nil {
  62. return nil
  63. }
  64. iopipe, err := ctr.openFifos(spec.Process.Terminal)
  65. if err != nil {
  66. return err
  67. }
  68. r := &containerd.CreateContainerRequest{
  69. Id: ctr.containerID,
  70. BundlePath: ctr.dir,
  71. Stdin: ctr.fifo(syscall.Stdin),
  72. Stdout: ctr.fifo(syscall.Stdout),
  73. Stderr: ctr.fifo(syscall.Stderr),
  74. // check to see if we are running in ramdisk to disable pivot root
  75. NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "",
  76. }
  77. ctr.client.appendContainer(ctr)
  78. resp, err := ctr.client.remote.apiClient.CreateContainer(context.Background(), r)
  79. if err != nil {
  80. ctr.closeFifos(iopipe)
  81. return err
  82. }
  83. if err := ctr.client.backend.AttachStreams(ctr.containerID, *iopipe); err != nil {
  84. return err
  85. }
  86. ctr.systemPid = systemPid(resp.Container)
  87. return ctr.client.backend.StateChanged(ctr.containerID, StateInfo{
  88. CommonStateInfo: CommonStateInfo{
  89. State: StateStart,
  90. Pid: ctr.systemPid,
  91. }})
  92. }
  93. func (ctr *container) newProcess(friendlyName string) *process {
  94. return &process{
  95. dir: ctr.dir,
  96. processCommon: processCommon{
  97. containerID: ctr.containerID,
  98. friendlyName: friendlyName,
  99. client: ctr.client,
  100. },
  101. }
  102. }
  103. func (ctr *container) handleEvent(e *containerd.Event) error {
  104. ctr.client.lock(ctr.containerID)
  105. defer ctr.client.unlock(ctr.containerID)
  106. switch e.Type {
  107. case StateExit, StatePause, StateResume, StateOOM:
  108. st := StateInfo{
  109. CommonStateInfo: CommonStateInfo{
  110. State: e.Type,
  111. ExitCode: e.Status,
  112. },
  113. OOMKilled: e.Type == StateExit && ctr.oom,
  114. }
  115. if e.Type == StateOOM {
  116. ctr.oom = true
  117. }
  118. if e.Type == StateExit && e.Pid != InitFriendlyName {
  119. st.ProcessID = e.Pid
  120. st.State = StateExitProcess
  121. }
  122. if st.State == StateExit && ctr.restartManager != nil {
  123. restart, wait, err := ctr.restartManager.ShouldRestart(e.Status, false)
  124. if err != nil {
  125. logrus.Warnf("container %s %v", ctr.containerID, err)
  126. } else if restart {
  127. st.State = StateRestart
  128. ctr.restarting = true
  129. ctr.client.deleteContainer(e.Id)
  130. go func() {
  131. err := <-wait
  132. ctr.client.lock(ctr.containerID)
  133. defer ctr.client.unlock(ctr.containerID)
  134. ctr.restarting = false
  135. if err != nil {
  136. st.State = StateExit
  137. ctr.clean()
  138. ctr.client.q.append(e.Id, func() {
  139. if err := ctr.client.backend.StateChanged(e.Id, st); err != nil {
  140. logrus.Error(err)
  141. }
  142. })
  143. if err != restartmanager.ErrRestartCanceled {
  144. logrus.Error(err)
  145. }
  146. } else {
  147. ctr.start()
  148. }
  149. }()
  150. }
  151. }
  152. // Remove process from list if we have exited
  153. // We need to do so here in case the Message Handler decides to restart it.
  154. switch st.State {
  155. case StateExit:
  156. ctr.clean()
  157. ctr.client.deleteContainer(e.Id)
  158. case StateExitProcess:
  159. ctr.cleanProcess(st.ProcessID)
  160. }
  161. ctr.client.q.append(e.Id, func() {
  162. if err := ctr.client.backend.StateChanged(e.Id, st); err != nil {
  163. logrus.Error(err)
  164. }
  165. if e.Type == StatePause || e.Type == StateResume {
  166. ctr.pauseMonitor.handle(e.Type)
  167. }
  168. if e.Type == StateExit {
  169. if en := ctr.client.getExitNotifier(e.Id); en != nil {
  170. en.close()
  171. }
  172. }
  173. })
  174. default:
  175. logrus.Debugf("event unhandled: %+v", e)
  176. }
  177. return nil
  178. }