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