state.go 11 KB

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