monitor.go 5.0 KB

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