container_linux.go 4.0 KB

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