container_linux.go 6.2 KB

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