state.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. package container
  2. import (
  3. "fmt"
  4. "sync"
  5. "time"
  6. "golang.org/x/net/context"
  7. "github.com/docker/go-units"
  8. )
  9. // State holds the current container state, and has methods to get and
  10. // set the state. Container has an embed, which allows all of the
  11. // functions defined against State to run against Container.
  12. type State struct {
  13. sync.Mutex
  14. // FIXME: Why do we have both paused and running if a
  15. // container cannot be paused and running at the same time?
  16. Running bool
  17. Paused bool
  18. Restarting bool
  19. OOMKilled bool
  20. RemovalInProgress bool // Not need for this to be persistent on disk.
  21. Dead bool
  22. Pid int
  23. exitCode int
  24. error string // contains last known error when starting the container
  25. StartedAt time.Time
  26. FinishedAt time.Time
  27. waitChan chan struct{}
  28. Health *Health
  29. }
  30. // NewState creates a default state object with a fresh channel for state changes.
  31. func NewState() *State {
  32. return &State{
  33. waitChan: make(chan struct{}),
  34. }
  35. }
  36. // String returns a human-readable description of the state
  37. func (s *State) String() string {
  38. if s.Running {
  39. if s.Paused {
  40. return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
  41. }
  42. if s.Restarting {
  43. return fmt.Sprintf("Restarting (%d) %s ago", s.exitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
  44. }
  45. if h := s.Health; h != nil {
  46. return fmt.Sprintf("Up %s (%s)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)), h.String())
  47. }
  48. return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
  49. }
  50. if s.RemovalInProgress {
  51. return "Removal In Progress"
  52. }
  53. if s.Dead {
  54. return "Dead"
  55. }
  56. if s.StartedAt.IsZero() {
  57. return "Created"
  58. }
  59. if s.FinishedAt.IsZero() {
  60. return ""
  61. }
  62. return fmt.Sprintf("Exited (%d) %s ago", s.exitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
  63. }
  64. // StateString returns a single string to describe state
  65. func (s *State) StateString() string {
  66. if s.Running {
  67. if s.Paused {
  68. return "paused"
  69. }
  70. if s.Restarting {
  71. return "restarting"
  72. }
  73. return "running"
  74. }
  75. if s.Dead {
  76. return "dead"
  77. }
  78. if s.StartedAt.IsZero() {
  79. return "created"
  80. }
  81. return "exited"
  82. }
  83. // IsValidStateString checks if the provided string is a valid container state or not.
  84. func IsValidStateString(s string) bool {
  85. if s != "paused" &&
  86. s != "restarting" &&
  87. s != "running" &&
  88. s != "dead" &&
  89. s != "created" &&
  90. s != "exited" {
  91. return false
  92. }
  93. return true
  94. }
  95. func wait(waitChan <-chan struct{}, timeout time.Duration) error {
  96. if timeout < 0 {
  97. <-waitChan
  98. return nil
  99. }
  100. select {
  101. case <-time.After(timeout):
  102. return fmt.Errorf("Timed out: %v", timeout)
  103. case <-waitChan:
  104. return nil
  105. }
  106. }
  107. // WaitStop waits until state is stopped. If state already stopped it returns
  108. // immediately. If you want wait forever you must supply negative timeout.
  109. // Returns exit code, that was passed to SetStoppedLocking
  110. func (s *State) WaitStop(timeout time.Duration) (int, error) {
  111. s.Lock()
  112. if !s.Running {
  113. exitCode := s.exitCode
  114. s.Unlock()
  115. return exitCode, nil
  116. }
  117. waitChan := s.waitChan
  118. s.Unlock()
  119. if err := wait(waitChan, timeout); err != nil {
  120. return -1, err
  121. }
  122. s.Lock()
  123. defer s.Unlock()
  124. return s.ExitCode(), nil
  125. }
  126. // WaitWithContext waits for the container to stop. Optional context can be
  127. // passed for canceling the request.
  128. func (s *State) WaitWithContext(ctx context.Context) error {
  129. // todo(tonistiigi): make other wait functions use this
  130. s.Lock()
  131. if !s.Running {
  132. state := *s
  133. defer s.Unlock()
  134. if state.exitCode == 0 {
  135. return nil
  136. }
  137. return &state
  138. }
  139. waitChan := s.waitChan
  140. s.Unlock()
  141. select {
  142. case <-waitChan:
  143. s.Lock()
  144. state := *s
  145. s.Unlock()
  146. if state.exitCode == 0 {
  147. return nil
  148. }
  149. return &state
  150. case <-ctx.Done():
  151. return ctx.Err()
  152. }
  153. }
  154. // IsRunning returns whether the running flag is set. Used by Container to check whether a container is running.
  155. func (s *State) IsRunning() bool {
  156. s.Lock()
  157. res := s.Running
  158. s.Unlock()
  159. return res
  160. }
  161. // GetPID holds the process id of a container.
  162. func (s *State) GetPID() int {
  163. s.Lock()
  164. res := s.Pid
  165. s.Unlock()
  166. return res
  167. }
  168. // ExitCode returns current exitcode for the state. Take lock before if state
  169. // may be shared.
  170. func (s *State) ExitCode() int {
  171. res := s.exitCode
  172. return res
  173. }
  174. // SetExitCode sets current exitcode for the state. Take lock before if state
  175. // may be shared.
  176. func (s *State) SetExitCode(ec int) {
  177. s.exitCode = ec
  178. }
  179. // SetRunning sets the state of the container to "running".
  180. func (s *State) SetRunning(pid int, initial bool) {
  181. s.error = ""
  182. s.Running = true
  183. s.Paused = false
  184. s.Restarting = false
  185. s.exitCode = 0
  186. s.Pid = pid
  187. if initial {
  188. s.StartedAt = time.Now().UTC()
  189. }
  190. }
  191. // SetStoppedLocking locks the container state and sets it to "stopped".
  192. func (s *State) SetStoppedLocking(exitStatus *ExitStatus) {
  193. s.Lock()
  194. s.SetStopped(exitStatus)
  195. s.Unlock()
  196. }
  197. // SetStopped sets the container state to "stopped" without locking.
  198. func (s *State) SetStopped(exitStatus *ExitStatus) {
  199. s.Running = false
  200. s.Paused = false
  201. s.Restarting = false
  202. s.Pid = 0
  203. s.FinishedAt = time.Now().UTC()
  204. s.setFromExitStatus(exitStatus)
  205. close(s.waitChan) // fire waiters for stop
  206. s.waitChan = make(chan struct{})
  207. }
  208. // SetRestartingLocking is when docker handles the auto restart of containers when they are
  209. // in the middle of a stop and being restarted again
  210. func (s *State) SetRestartingLocking(exitStatus *ExitStatus) {
  211. s.Lock()
  212. s.SetRestarting(exitStatus)
  213. s.Unlock()
  214. }
  215. // SetRestarting sets the container state to "restarting".
  216. // It also sets the container PID to 0.
  217. func (s *State) SetRestarting(exitStatus *ExitStatus) {
  218. // we should consider the container running when it is restarting because of
  219. // all the checks in docker around rm/stop/etc
  220. s.Running = true
  221. s.Restarting = true
  222. s.Pid = 0
  223. s.FinishedAt = time.Now().UTC()
  224. s.setFromExitStatus(exitStatus)
  225. close(s.waitChan) // fire waiters for stop
  226. s.waitChan = make(chan struct{})
  227. }
  228. // SetError sets the container's error state. This is useful when we want to
  229. // know the error that occurred when container transits to another state
  230. // when inspecting it
  231. func (s *State) SetError(err error) {
  232. s.error = err.Error()
  233. }
  234. // IsPaused returns whether the container is paused or not.
  235. func (s *State) IsPaused() bool {
  236. s.Lock()
  237. res := s.Paused
  238. s.Unlock()
  239. return res
  240. }
  241. // IsRestarting returns whether the container is restarting or not.
  242. func (s *State) IsRestarting() bool {
  243. s.Lock()
  244. res := s.Restarting
  245. s.Unlock()
  246. return res
  247. }
  248. // SetRemovalInProgress sets the container state as being removed.
  249. // It returns true if the container was already in that state.
  250. func (s *State) SetRemovalInProgress() bool {
  251. s.Lock()
  252. defer s.Unlock()
  253. if s.RemovalInProgress {
  254. return true
  255. }
  256. s.RemovalInProgress = true
  257. return false
  258. }
  259. // ResetRemovalInProgress makes the RemovalInProgress state to false.
  260. func (s *State) ResetRemovalInProgress() {
  261. s.Lock()
  262. s.RemovalInProgress = false
  263. s.Unlock()
  264. }
  265. // SetDead sets the container state to "dead"
  266. func (s *State) SetDead() {
  267. s.Lock()
  268. s.Dead = true
  269. s.Unlock()
  270. }
  271. // Error returns current error for the state.
  272. func (s *State) Error() string {
  273. return s.error
  274. }