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