monitor.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. package daemon
  2. import (
  3. "errors"
  4. "fmt"
  5. "io"
  6. "runtime"
  7. "strconv"
  8. "github.com/Sirupsen/logrus"
  9. "github.com/docker/docker/daemon/exec"
  10. "github.com/docker/docker/libcontainerd"
  11. "github.com/docker/docker/runconfig"
  12. )
  13. // StateChanged updates daemon state changes from containerd
  14. func (daemon *Daemon) StateChanged(id string, e libcontainerd.StateInfo) error {
  15. c := daemon.containers.Get(id)
  16. if c == nil {
  17. return fmt.Errorf("no such container: %s", id)
  18. }
  19. switch e.State {
  20. case libcontainerd.StateOOM:
  21. // StateOOM is Linux specific and should never be hit on Windows
  22. if runtime.GOOS == "windows" {
  23. return errors.New("Received StateOOM from libcontainerd on Windows. This should never happen.")
  24. }
  25. daemon.updateHealthMonitor(c)
  26. daemon.LogContainerEvent(c, "oom")
  27. case libcontainerd.StateExit:
  28. c.Lock()
  29. defer c.Unlock()
  30. c.Wait()
  31. c.Reset(false)
  32. c.SetStopped(platformConstructExitStatus(e))
  33. attributes := map[string]string{
  34. "exitCode": strconv.Itoa(int(e.ExitCode)),
  35. }
  36. daemon.updateHealthMonitor(c)
  37. daemon.LogContainerEventWithAttributes(c, "die", attributes)
  38. daemon.Cleanup(c)
  39. // FIXME: here is race condition between two RUN instructions in Dockerfile
  40. // because they share same runconfig and change image. Must be fixed
  41. // in builder/builder.go
  42. if err := c.ToDisk(); err != nil {
  43. return err
  44. }
  45. return daemon.postRunProcessing(c, e)
  46. case libcontainerd.StateRestart:
  47. c.Lock()
  48. defer c.Unlock()
  49. c.Reset(false)
  50. c.RestartCount++
  51. c.SetRestarting(platformConstructExitStatus(e))
  52. attributes := map[string]string{
  53. "exitCode": strconv.Itoa(int(e.ExitCode)),
  54. }
  55. daemon.LogContainerEventWithAttributes(c, "die", attributes)
  56. daemon.updateHealthMonitor(c)
  57. return c.ToDisk()
  58. case libcontainerd.StateExitProcess:
  59. c.Lock()
  60. defer c.Unlock()
  61. if execConfig := c.ExecCommands.Get(e.ProcessID); execConfig != nil {
  62. ec := int(e.ExitCode)
  63. execConfig.ExitCode = &ec
  64. execConfig.Running = false
  65. execConfig.Wait()
  66. if err := execConfig.CloseStreams(); err != nil {
  67. logrus.Errorf("%s: %s", c.ID, err)
  68. }
  69. // remove the exec command from the container's store only and not the
  70. // daemon's store so that the exec command can be inspected.
  71. c.ExecCommands.Delete(execConfig.ID)
  72. } else {
  73. logrus.Warnf("Ignoring StateExitProcess for %v but no exec command found", e)
  74. }
  75. case libcontainerd.StateStart, libcontainerd.StateRestore:
  76. // Container is already locked in this case
  77. c.SetRunning(int(e.Pid), e.State == libcontainerd.StateStart)
  78. c.HasBeenManuallyStopped = false
  79. if err := c.ToDisk(); err != nil {
  80. c.Reset(false)
  81. return err
  82. }
  83. daemon.initHealthMonitor(c)
  84. daemon.LogContainerEvent(c, "start")
  85. case libcontainerd.StatePause:
  86. // Container is already locked in this case
  87. c.Paused = true
  88. daemon.updateHealthMonitor(c)
  89. daemon.LogContainerEvent(c, "pause")
  90. case libcontainerd.StateResume:
  91. // Container is already locked in this case
  92. c.Paused = false
  93. daemon.updateHealthMonitor(c)
  94. daemon.LogContainerEvent(c, "unpause")
  95. }
  96. return nil
  97. }
  98. // AttachStreams is called by libcontainerd to connect the stdio.
  99. func (daemon *Daemon) AttachStreams(id string, iop libcontainerd.IOPipe) error {
  100. var (
  101. s *runconfig.StreamConfig
  102. ec *exec.Config
  103. )
  104. c := daemon.containers.Get(id)
  105. if c == nil {
  106. var err error
  107. ec, err = daemon.getExecConfig(id)
  108. if err != nil {
  109. return fmt.Errorf("no such exec/container: %s", id)
  110. }
  111. s = ec.StreamConfig
  112. } else {
  113. s = c.StreamConfig
  114. if err := daemon.StartLogging(c); err != nil {
  115. c.Reset(false)
  116. return err
  117. }
  118. }
  119. copyFunc := func(w io.Writer, r io.Reader) {
  120. s.Add(1)
  121. go func() {
  122. if _, err := io.Copy(w, r); err != nil {
  123. logrus.Errorf("%v stream copy error: %v", id, err)
  124. }
  125. s.Done()
  126. }()
  127. }
  128. if iop.Stdout != nil {
  129. copyFunc(s.Stdout(), iop.Stdout)
  130. }
  131. if iop.Stderr != nil {
  132. copyFunc(s.Stderr(), iop.Stderr)
  133. }
  134. if stdin := s.Stdin(); stdin != nil {
  135. if iop.Stdin != nil {
  136. go func() {
  137. io.Copy(iop.Stdin, stdin)
  138. iop.Stdin.Close()
  139. }()
  140. }
  141. } else {
  142. //TODO(swernli): On Windows, not closing stdin when no tty is requested by the exec Config
  143. // results in a hang. We should re-evaluate generalizing this fix for all OSes if
  144. // we can determine that is the right thing to do more generally.
  145. if (c != nil && !c.Config.Tty) || (ec != nil && !ec.Tty && runtime.GOOS == "windows") {
  146. // tty is enabled, so dont close containerd's iopipe stdin.
  147. if iop.Stdin != nil {
  148. iop.Stdin.Close()
  149. }
  150. }
  151. }
  152. return nil
  153. }