container_linux.go 5.1 KB

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