9 "golang.org/x/net/context"
11 "github.com/docker/docker/api/types"
12 "github.com/docker/go-units"
15 // State holds the current container state, and has methods to get and
16 // set the state. Container has an embed, which allows all of the
17 // functions defined against State to run against Container.
20 // Note that `Running` and `Paused` are not mutually exclusive:
21 // When pausing a container (on Linux), the cgroups freezer is used to suspend
22 // all processes in the container. Freezing the process requires the process to
23 // be running. As a result, paused containers are both `Running` _and_ `Paused`.
28 RemovalInProgress bool // Not need for this to be persistent on disk.
31 ExitCodeValue int `json:"ExitCode"`
32 ErrorMsg string `json:"Error"` // contains last known error when starting the container
37 waitStop chan struct{}
38 waitRemove chan struct{}
41 // StateStatus is used to return container wait results.
42 // Implements exec.ExitCode interface.
43 // This type is needed as State include a sync.Mutex field which make
45 type StateStatus struct {
50 // ExitCode returns current exitcode for the state.
51 func (s StateStatus) ExitCode() int {
55 // Err returns current error for the state. Returns nil if the container had
57 func (s StateStatus) Err() error {
61 // NewState creates a default state object with a fresh channel for state changes.
62 func NewState() *State {
64 waitStop: make(chan struct{}),
65 waitRemove: make(chan struct{}),
69 // String returns a human-readable description of the state
70 func (s *State) String() string {
73 return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
76 return fmt.Sprintf("Restarting (%d) %s ago", s.ExitCodeValue, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
79 if h := s.Health; h != nil {
80 return fmt.Sprintf("Up %s (%s)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)), h.String())
83 return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
86 if s.RemovalInProgress {
87 return "Removal In Progress"
94 if s.StartedAt.IsZero() {
98 if s.FinishedAt.IsZero() {
102 return fmt.Sprintf("Exited (%d) %s ago", s.ExitCodeValue, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
105 // HealthString returns a single string to describe health status.
106 func (s *State) HealthString() string {
108 return types.NoHealthcheck
111 return s.Health.String()
114 // IsValidHealthString checks if the provided string is a valid container health status or not.
115 func IsValidHealthString(s string) bool {
116 return s == types.Starting ||
117 s == types.Healthy ||
118 s == types.Unhealthy ||
119 s == types.NoHealthcheck
122 // StateString returns a single string to describe state
123 func (s *State) StateString() string {
134 if s.RemovalInProgress {
142 if s.StartedAt.IsZero() {
149 // IsValidStateString checks if the provided string is a valid container state or not.
150 func IsValidStateString(s string) bool {
163 // WaitCondition is an enum type for different states to wait for.
164 type WaitCondition int
166 // Possible WaitCondition Values.
168 // WaitConditionNotRunning (default) is used to wait for any of the non-running
169 // states: "created", "exited", "dead", "removing", or "removed".
171 // WaitConditionNextExit is used to wait for the next time the state changes
172 // to a non-running state. If the state is currently "created" or "exited",
173 // this would cause Wait() to block until either the container runs and exits
176 // WaitConditionRemoved is used to wait for the container to be removed.
178 WaitConditionNotRunning WaitCondition = iota
179 WaitConditionNextExit
183 // Wait waits until the container is in a certain state indicated by the given
184 // condition. A context must be used for cancelling the request, controlling
185 // timeouts, and avoiding goroutine leaks. Wait must be called without holding
186 // the state lock. Returns a channel from which the caller will receive the
187 // result. If the container exited on its own, the result's Err() method will
188 // be nil and its ExitCode() method will return the conatiners exit code,
189 // otherwise, the results Err() method will return an error indicating why the
190 // wait operation failed.
191 func (s *State) Wait(ctx context.Context, condition WaitCondition) <-chan StateStatus {
195 if condition == WaitConditionNotRunning && !s.Running {
196 // Buffer so we can put it in the channel now.
197 resultC := make(chan StateStatus, 1)
199 // Send the current status.
200 resultC <- StateStatus{
201 exitCode: s.ExitCode(),
208 // If we are waiting only for removal, the waitStop channel should
209 // remain nil and block forever.
210 var waitStop chan struct{}
211 if condition < WaitConditionRemoved {
212 waitStop = s.waitStop
215 // Always wait for removal, just in case the container gets removed
216 // while it is still in a "created" state, in which case it is never
218 waitRemove := s.waitRemove
220 resultC := make(chan StateStatus)
225 // Context timeout or cancellation.
226 resultC <- StateStatus{
236 result := StateStatus{
237 exitCode: s.ExitCode(),
248 // IsRunning returns whether the running flag is set. Used by Container to check whether a container is running.
249 func (s *State) IsRunning() bool {
256 // GetPID holds the process id of a container.
257 func (s *State) GetPID() int {
264 // ExitCode returns current exitcode for the state. Take lock before if state
266 func (s *State) ExitCode() int {
267 return s.ExitCodeValue
270 // SetExitCode sets current exitcode for the state. Take lock before if state
272 func (s *State) SetExitCode(ec int) {
276 // SetRunning sets the state of the container to "running".
277 func (s *State) SetRunning(pid int, initial bool) {
284 s.StartedAt = time.Now().UTC()
288 // SetStopped sets the container state to "stopped" without locking.
289 func (s *State) SetStopped(exitStatus *ExitStatus) {
294 s.FinishedAt = time.Now().UTC()
295 s.setFromExitStatus(exitStatus)
296 close(s.waitStop) // Fire waiters for stop
297 s.waitStop = make(chan struct{})
300 // SetRestarting sets the container state to "restarting" without locking.
301 // It also sets the container PID to 0.
302 func (s *State) SetRestarting(exitStatus *ExitStatus) {
303 // we should consider the container running when it is restarting because of
304 // all the checks in docker around rm/stop/etc
308 s.FinishedAt = time.Now().UTC()
309 s.setFromExitStatus(exitStatus)
310 close(s.waitStop) // Fire waiters for stop
311 s.waitStop = make(chan struct{})
314 // SetError sets the container's error state. This is useful when we want to
315 // know the error that occurred when container transits to another state
316 // when inspecting it
317 func (s *State) SetError(err error) {
318 s.ErrorMsg = err.Error()
321 // IsPaused returns whether the container is paused or not.
322 func (s *State) IsPaused() bool {
329 // IsRestarting returns whether the container is restarting or not.
330 func (s *State) IsRestarting() bool {
337 // SetRemovalInProgress sets the container state as being removed.
338 // It returns true if the container was already in that state.
339 func (s *State) SetRemovalInProgress() bool {
342 if s.RemovalInProgress {
345 s.RemovalInProgress = true
349 // ResetRemovalInProgress makes the RemovalInProgress state to false.
350 func (s *State) ResetRemovalInProgress() {
352 s.RemovalInProgress = false
356 // SetDead sets the container state to "dead"
357 func (s *State) SetDead() {
363 // SetRemoved assumes this container is already in the "dead" state and
364 // closes the internal waitRemove channel to unblock callers waiting for a
365 // container to be removed.
366 func (s *State) SetRemoved() {
368 close(s.waitRemove) // Unblock those waiting on remove.
372 // Err returns an error if there is one.
373 func (s *State) Err() error {
374 if s.ErrorMsg != "" {
375 return errors.New(s.ErrorMsg)