container_linux.go 3.9 KB

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