stop.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. package daemon // import "github.com/docker/docker/daemon"
  2. import (
  3. "context"
  4. "time"
  5. containerpkg "github.com/docker/docker/container"
  6. "github.com/docker/docker/errdefs"
  7. "github.com/pkg/errors"
  8. "github.com/sirupsen/logrus"
  9. )
  10. // ContainerStop looks for the given container and stops it.
  11. // In case the container fails to stop gracefully within a time duration
  12. // specified by the timeout argument, in seconds, it is forcefully
  13. // terminated (killed).
  14. //
  15. // If the timeout is nil, the container's StopTimeout value is used, if set,
  16. // otherwise the engine default. A negative timeout value can be specified,
  17. // meaning no timeout, i.e. no forceful termination is performed.
  18. func (daemon *Daemon) ContainerStop(name string, timeout *int) error {
  19. container, err := daemon.GetContainer(name)
  20. if err != nil {
  21. return err
  22. }
  23. if !container.IsRunning() {
  24. return containerNotModifiedError{running: false}
  25. }
  26. if timeout == nil {
  27. stopTimeout := container.StopTimeout()
  28. timeout = &stopTimeout
  29. }
  30. if err := daemon.containerStop(container, *timeout); err != nil {
  31. return errdefs.System(errors.Wrapf(err, "cannot stop container: %s", name))
  32. }
  33. return nil
  34. }
  35. // containerStop sends a stop signal, waits, sends a kill signal.
  36. func (daemon *Daemon) containerStop(container *containerpkg.Container, seconds int) error {
  37. // TODO propagate a context down to this function
  38. ctx := context.TODO()
  39. if !container.IsRunning() {
  40. return nil
  41. }
  42. var wait time.Duration
  43. if seconds >= 0 {
  44. wait = time.Duration(seconds) * time.Second
  45. }
  46. success := func() error {
  47. daemon.LogContainerEvent(container, "stop")
  48. return nil
  49. }
  50. stopSignal := container.StopSignal()
  51. // 1. Send a stop signal
  52. err := daemon.killPossiblyDeadProcess(container, stopSignal)
  53. if err != nil {
  54. wait = 2 * time.Second
  55. }
  56. var subCtx context.Context
  57. var cancel context.CancelFunc
  58. if seconds >= 0 {
  59. subCtx, cancel = context.WithTimeout(ctx, wait)
  60. } else {
  61. subCtx, cancel = context.WithCancel(ctx)
  62. }
  63. defer cancel()
  64. if status := <-container.Wait(subCtx, containerpkg.WaitConditionNotRunning); status.Err() == nil {
  65. // container did exit, so ignore any previous errors and return
  66. return success()
  67. }
  68. if err != nil {
  69. // the container has still not exited, and the kill function errored, so log the error here:
  70. logrus.WithError(err).WithField("container", container.ID).Errorf("Error sending stop (signal %d) to container", stopSignal)
  71. }
  72. if seconds < 0 {
  73. // if the client requested that we never kill / wait forever, but container.Wait was still
  74. // interrupted (parent context cancelled, for example), we should propagate the signal failure
  75. return err
  76. }
  77. logrus.WithField("container", container.ID).Infof("Container failed to exit within %s of signal %d - using the force", wait, stopSignal)
  78. // Stop either failed or container didnt exit, so fallback to kill.
  79. if err := daemon.Kill(container); err != nil {
  80. // got a kill error, but give container 2 more seconds to exit just in case
  81. subCtx, cancel := context.WithTimeout(ctx, 2*time.Second)
  82. defer cancel()
  83. if status := <-container.Wait(subCtx, containerpkg.WaitConditionNotRunning); status.Err() == nil {
  84. // container did exit, so ignore error and return
  85. return success()
  86. }
  87. logrus.WithError(err).WithField("container", container.ID).Error("Error killing the container")
  88. return err
  89. }
  90. return success()
  91. }