state.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. package container // import "github.com/docker/docker/container"
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "sync"
  7. "time"
  8. "github.com/docker/docker/api/types"
  9. units "github.com/docker/go-units"
  10. )
  11. // State holds the current container state, and has methods to get and
  12. // set the state. Container has an embed, which allows all of the
  13. // functions defined against State to run against Container.
  14. type State struct {
  15. sync.Mutex
  16. // Note that `Running` and `Paused` are not mutually exclusive:
  17. // When pausing a container (on Linux), the freezer cgroup is used to suspend
  18. // all processes in the container. Freezing the process requires the process to
  19. // be running. As a result, paused containers are both `Running` _and_ `Paused`.
  20. Running bool
  21. Paused bool
  22. Restarting bool
  23. OOMKilled bool
  24. RemovalInProgress bool // Not need for this to be persistent on disk.
  25. Dead bool
  26. Pid int
  27. ExitCodeValue int `json:"ExitCode"`
  28. ErrorMsg string `json:"Error"` // contains last known error during container start, stop, or remove
  29. StartedAt time.Time
  30. FinishedAt time.Time
  31. Health *Health
  32. waitStop chan struct{}
  33. waitRemove chan struct{}
  34. }
  35. // StateStatus is used to return container wait results.
  36. // Implements exec.ExitCode interface.
  37. // This type is needed as State include a sync.Mutex field which make
  38. // copying it unsafe.
  39. type StateStatus struct {
  40. exitCode int
  41. err error
  42. }
  43. // ExitCode returns current exitcode for the state.
  44. func (s StateStatus) ExitCode() int {
  45. return s.exitCode
  46. }
  47. // Err returns current error for the state. Returns nil if the container had
  48. // exited on its own.
  49. func (s StateStatus) Err() error {
  50. return s.err
  51. }
  52. // NewState creates a default state object with a fresh channel for state changes.
  53. func NewState() *State {
  54. return &State{
  55. waitStop: make(chan struct{}),
  56. waitRemove: make(chan struct{}),
  57. }
  58. }
  59. // String returns a human-readable description of the state
  60. func (s *State) String() string {
  61. if s.Running {
  62. if s.Paused {
  63. return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
  64. }
  65. if s.Restarting {
  66. return fmt.Sprintf("Restarting (%d) %s ago", s.ExitCodeValue, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
  67. }
  68. if h := s.Health; h != nil {
  69. return fmt.Sprintf("Up %s (%s)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)), h.String())
  70. }
  71. return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
  72. }
  73. if s.RemovalInProgress {
  74. return "Removal In Progress"
  75. }
  76. if s.Dead {
  77. return "Dead"
  78. }
  79. if s.StartedAt.IsZero() {
  80. return "Created"
  81. }
  82. if s.FinishedAt.IsZero() {
  83. return ""
  84. }
  85. return fmt.Sprintf("Exited (%d) %s ago", s.ExitCodeValue, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
  86. }
  87. // IsValidHealthString checks if the provided string is a valid container health status or not.
  88. func IsValidHealthString(s string) bool {
  89. return s == types.Starting ||
  90. s == types.Healthy ||
  91. s == types.Unhealthy ||
  92. s == types.NoHealthcheck
  93. }
  94. // StateString returns a single string to describe state
  95. func (s *State) StateString() string {
  96. if s.Running {
  97. if s.Paused {
  98. return "paused"
  99. }
  100. if s.Restarting {
  101. return "restarting"
  102. }
  103. return "running"
  104. }
  105. if s.RemovalInProgress {
  106. return "removing"
  107. }
  108. if s.Dead {
  109. return "dead"
  110. }
  111. if s.StartedAt.IsZero() {
  112. return "created"
  113. }
  114. return "exited"
  115. }
  116. // IsValidStateString checks if the provided string is a valid container state or not.
  117. func IsValidStateString(s string) bool {
  118. if s != "paused" &&
  119. s != "restarting" &&
  120. s != "removing" &&
  121. s != "running" &&
  122. s != "dead" &&
  123. s != "created" &&
  124. s != "exited" {
  125. return false
  126. }
  127. return true
  128. }
  129. // WaitCondition is an enum type for different states to wait for.
  130. type WaitCondition int
  131. // Possible WaitCondition Values.
  132. //
  133. // WaitConditionNotRunning (default) is used to wait for any of the non-running
  134. // states: "created", "exited", "dead", "removing", or "removed".
  135. //
  136. // WaitConditionNextExit is used to wait for the next time the state changes
  137. // to a non-running state. If the state is currently "created" or "exited",
  138. // this would cause Wait() to block until either the container runs and exits
  139. // or is removed.
  140. //
  141. // WaitConditionRemoved is used to wait for the container to be removed.
  142. const (
  143. WaitConditionNotRunning WaitCondition = iota
  144. WaitConditionNextExit
  145. WaitConditionRemoved
  146. )
  147. // Wait waits until the container is in a certain state indicated by the given
  148. // condition. A context must be used for cancelling the request, controlling
  149. // timeouts, and avoiding goroutine leaks. Wait must be called without holding
  150. // the state lock. Returns a channel from which the caller will receive the
  151. // result. If the container exited on its own, the result's Err() method will
  152. // be nil and its ExitCode() method will return the container's exit code,
  153. // otherwise, the results Err() method will return an error indicating why the
  154. // wait operation failed.
  155. func (s *State) Wait(ctx context.Context, condition WaitCondition) <-chan StateStatus {
  156. s.Lock()
  157. defer s.Unlock()
  158. if condition == WaitConditionNotRunning && !s.Running {
  159. // Buffer so we can put it in the channel now.
  160. resultC := make(chan StateStatus, 1)
  161. // Send the current status.
  162. resultC <- StateStatus{
  163. exitCode: s.ExitCode(),
  164. err: s.Err(),
  165. }
  166. return resultC
  167. }
  168. // If we are waiting only for removal, the waitStop channel should
  169. // remain nil and block forever.
  170. var waitStop chan struct{}
  171. if condition < WaitConditionRemoved {
  172. waitStop = s.waitStop
  173. }
  174. // Always wait for removal, just in case the container gets removed
  175. // while it is still in a "created" state, in which case it is never
  176. // actually stopped.
  177. waitRemove := s.waitRemove
  178. resultC := make(chan StateStatus, 1)
  179. go func() {
  180. select {
  181. case <-ctx.Done():
  182. // Context timeout or cancellation.
  183. resultC <- StateStatus{
  184. exitCode: -1,
  185. err: ctx.Err(),
  186. }
  187. return
  188. case <-waitStop:
  189. case <-waitRemove:
  190. }
  191. s.Lock()
  192. result := StateStatus{
  193. exitCode: s.ExitCode(),
  194. err: s.Err(),
  195. }
  196. s.Unlock()
  197. resultC <- result
  198. }()
  199. return resultC
  200. }
  201. // IsRunning returns whether the running flag is set. Used by Container to check whether a container is running.
  202. func (s *State) IsRunning() bool {
  203. s.Lock()
  204. res := s.Running
  205. s.Unlock()
  206. return res
  207. }
  208. // GetPID holds the process id of a container.
  209. func (s *State) GetPID() int {
  210. s.Lock()
  211. res := s.Pid
  212. s.Unlock()
  213. return res
  214. }
  215. // ExitCode returns current exitcode for the state. Take lock before if state
  216. // may be shared.
  217. func (s *State) ExitCode() int {
  218. return s.ExitCodeValue
  219. }
  220. // SetExitCode sets current exitcode for the state. Take lock before if state
  221. // may be shared.
  222. func (s *State) SetExitCode(ec int) {
  223. s.ExitCodeValue = ec
  224. }
  225. // SetRunning sets the state of the container to "running".
  226. func (s *State) SetRunning(pid int, initial bool) {
  227. s.ErrorMsg = ""
  228. s.Paused = false
  229. s.Running = true
  230. s.Restarting = false
  231. if initial {
  232. s.Paused = false
  233. }
  234. s.ExitCodeValue = 0
  235. s.Pid = pid
  236. if initial {
  237. s.StartedAt = time.Now().UTC()
  238. }
  239. }
  240. // SetStopped sets the container state to "stopped" without locking.
  241. func (s *State) SetStopped(exitStatus *ExitStatus) {
  242. s.Running = false
  243. s.Paused = false
  244. s.Restarting = false
  245. s.Pid = 0
  246. if exitStatus.ExitedAt.IsZero() {
  247. s.FinishedAt = time.Now().UTC()
  248. } else {
  249. s.FinishedAt = exitStatus.ExitedAt
  250. }
  251. s.ExitCodeValue = exitStatus.ExitCode
  252. s.OOMKilled = exitStatus.OOMKilled
  253. close(s.waitStop) // fire waiters for stop
  254. s.waitStop = make(chan struct{})
  255. }
  256. // SetRestarting sets the container state to "restarting" without locking.
  257. // It also sets the container PID to 0.
  258. func (s *State) SetRestarting(exitStatus *ExitStatus) {
  259. // we should consider the container running when it is restarting because of
  260. // all the checks in docker around rm/stop/etc
  261. s.Running = true
  262. s.Restarting = true
  263. s.Paused = false
  264. s.Pid = 0
  265. s.FinishedAt = time.Now().UTC()
  266. s.ExitCodeValue = exitStatus.ExitCode
  267. s.OOMKilled = exitStatus.OOMKilled
  268. close(s.waitStop) // fire waiters for stop
  269. s.waitStop = make(chan struct{})
  270. }
  271. // SetError sets the container's error state. This is useful when we want to
  272. // know the error that occurred when container transits to another state
  273. // when inspecting it
  274. func (s *State) SetError(err error) {
  275. s.ErrorMsg = ""
  276. if err != nil {
  277. s.ErrorMsg = err.Error()
  278. }
  279. }
  280. // IsPaused returns whether the container is paused or not.
  281. func (s *State) IsPaused() bool {
  282. s.Lock()
  283. res := s.Paused
  284. s.Unlock()
  285. return res
  286. }
  287. // IsRestarting returns whether the container is restarting or not.
  288. func (s *State) IsRestarting() bool {
  289. s.Lock()
  290. res := s.Restarting
  291. s.Unlock()
  292. return res
  293. }
  294. // SetRemovalInProgress sets the container state as being removed.
  295. // It returns true if the container was already in that state.
  296. func (s *State) SetRemovalInProgress() bool {
  297. s.Lock()
  298. defer s.Unlock()
  299. if s.RemovalInProgress {
  300. return true
  301. }
  302. s.RemovalInProgress = true
  303. return false
  304. }
  305. // ResetRemovalInProgress makes the RemovalInProgress state to false.
  306. func (s *State) ResetRemovalInProgress() {
  307. s.Lock()
  308. s.RemovalInProgress = false
  309. s.Unlock()
  310. }
  311. // IsRemovalInProgress returns whether the RemovalInProgress flag is set.
  312. // Used by Container to check whether a container is being removed.
  313. func (s *State) IsRemovalInProgress() bool {
  314. s.Lock()
  315. res := s.RemovalInProgress
  316. s.Unlock()
  317. return res
  318. }
  319. // IsDead returns whether the Dead flag is set. Used by Container to check whether a container is dead.
  320. func (s *State) IsDead() bool {
  321. s.Lock()
  322. res := s.Dead
  323. s.Unlock()
  324. return res
  325. }
  326. // SetRemoved assumes this container is already in the "dead" state and
  327. // closes the internal waitRemove channel to unblock callers waiting for a
  328. // container to be removed.
  329. func (s *State) SetRemoved() {
  330. s.SetRemovalError(nil)
  331. }
  332. // SetRemovalError is to be called in case a container remove failed.
  333. // It sets an error and closes the internal waitRemove channel to unblock
  334. // callers waiting for the container to be removed.
  335. func (s *State) SetRemovalError(err error) {
  336. s.SetError(err)
  337. s.Lock()
  338. close(s.waitRemove) // Unblock those waiting on remove.
  339. // Recreate the channel so next ContainerWait will work
  340. s.waitRemove = make(chan struct{})
  341. s.Unlock()
  342. }
  343. // Err returns an error if there is one.
  344. func (s *State) Err() error {
  345. if s.ErrorMsg != "" {
  346. return errors.New(s.ErrorMsg)
  347. }
  348. return nil
  349. }