github.com/mephux/docker@v1.6.0-rc5/daemon/state.go (about) 1 package daemon 2 3 import ( 4 "fmt" 5 "sync" 6 "time" 7 8 "github.com/docker/docker/daemon/execdriver" 9 "github.com/docker/docker/pkg/units" 10 ) 11 12 type State struct { 13 sync.Mutex 14 Running bool 15 Paused bool 16 Restarting bool 17 OOMKilled bool 18 removalInProgress bool // Not need for this to be persistent on disk. 19 Dead bool 20 Pid int 21 ExitCode int 22 Error string // contains last known error when starting the container 23 StartedAt time.Time 24 FinishedAt time.Time 25 waitChan chan struct{} 26 } 27 28 func NewState() *State { 29 return &State{ 30 waitChan: make(chan struct{}), 31 } 32 } 33 34 // String returns a human-readable description of the state 35 func (s *State) String() string { 36 if s.Running { 37 if s.Paused { 38 return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt))) 39 } 40 if s.Restarting { 41 return fmt.Sprintf("Restarting (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt))) 42 } 43 44 return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt))) 45 } 46 47 if s.removalInProgress { 48 return "Removal In Progress" 49 } 50 51 if s.Dead { 52 return "Dead" 53 } 54 55 if s.FinishedAt.IsZero() { 56 return "" 57 } 58 59 return fmt.Sprintf("Exited (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt))) 60 } 61 62 // StateString returns a single string to describe state 63 func (s *State) StateString() string { 64 if s.Running { 65 if s.Paused { 66 return "paused" 67 } 68 if s.Restarting { 69 return "restarting" 70 } 71 return "running" 72 } 73 74 if s.Dead { 75 return "dead" 76 } 77 78 return "exited" 79 } 80 81 func wait(waitChan <-chan struct{}, timeout time.Duration) error { 82 if timeout < 0 { 83 <-waitChan 84 return nil 85 } 86 select { 87 case <-time.After(timeout): 88 return fmt.Errorf("Timed out: %v", timeout) 89 case <-waitChan: 90 return nil 91 } 92 } 93 94 // WaitRunning waits until state is running. If state already running it returns 95 // immediately. If you want wait forever you must supply negative timeout. 96 // Returns pid, that was passed to SetRunning 97 func (s *State) WaitRunning(timeout time.Duration) (int, error) { 98 s.Lock() 99 if s.Running { 100 pid := s.Pid 101 s.Unlock() 102 return pid, nil 103 } 104 waitChan := s.waitChan 105 s.Unlock() 106 if err := wait(waitChan, timeout); err != nil { 107 return -1, err 108 } 109 return s.GetPid(), nil 110 } 111 112 // WaitStop waits until state is stopped. If state already stopped it returns 113 // immediately. If you want wait forever you must supply negative timeout. 114 // Returns exit code, that was passed to SetStopped 115 func (s *State) WaitStop(timeout time.Duration) (int, error) { 116 s.Lock() 117 if !s.Running { 118 exitCode := s.ExitCode 119 s.Unlock() 120 return exitCode, nil 121 } 122 waitChan := s.waitChan 123 s.Unlock() 124 if err := wait(waitChan, timeout); err != nil { 125 return -1, err 126 } 127 return s.GetExitCode(), nil 128 } 129 130 func (s *State) IsRunning() bool { 131 s.Lock() 132 res := s.Running 133 s.Unlock() 134 return res 135 } 136 137 func (s *State) GetPid() int { 138 s.Lock() 139 res := s.Pid 140 s.Unlock() 141 return res 142 } 143 144 func (s *State) GetExitCode() int { 145 s.Lock() 146 res := s.ExitCode 147 s.Unlock() 148 return res 149 } 150 151 func (s *State) SetRunning(pid int) { 152 s.Lock() 153 s.setRunning(pid) 154 s.Unlock() 155 } 156 157 func (s *State) setRunning(pid int) { 158 s.Error = "" 159 s.Running = true 160 s.Paused = false 161 s.Restarting = false 162 s.ExitCode = 0 163 s.Pid = pid 164 s.StartedAt = time.Now().UTC() 165 close(s.waitChan) // fire waiters for start 166 s.waitChan = make(chan struct{}) 167 } 168 169 func (s *State) SetStopped(exitStatus *execdriver.ExitStatus) { 170 s.Lock() 171 s.setStopped(exitStatus) 172 s.Unlock() 173 } 174 175 func (s *State) setStopped(exitStatus *execdriver.ExitStatus) { 176 s.Running = false 177 s.Restarting = false 178 s.Pid = 0 179 s.FinishedAt = time.Now().UTC() 180 s.ExitCode = exitStatus.ExitCode 181 s.OOMKilled = exitStatus.OOMKilled 182 close(s.waitChan) // fire waiters for stop 183 s.waitChan = make(chan struct{}) 184 } 185 186 // SetRestarting is when docker hanldes the auto restart of containers when they are 187 // in the middle of a stop and being restarted again 188 func (s *State) SetRestarting(exitStatus *execdriver.ExitStatus) { 189 s.Lock() 190 // we should consider the container running when it is restarting because of 191 // all the checks in docker around rm/stop/etc 192 s.Running = true 193 s.Restarting = true 194 s.Pid = 0 195 s.FinishedAt = time.Now().UTC() 196 s.ExitCode = exitStatus.ExitCode 197 s.OOMKilled = exitStatus.OOMKilled 198 close(s.waitChan) // fire waiters for stop 199 s.waitChan = make(chan struct{}) 200 s.Unlock() 201 } 202 203 // setError sets the container's error state. This is useful when we want to 204 // know the error that occurred when container transits to another state 205 // when inspecting it 206 func (s *State) setError(err error) { 207 s.Error = err.Error() 208 } 209 210 func (s *State) IsRestarting() bool { 211 s.Lock() 212 res := s.Restarting 213 s.Unlock() 214 return res 215 } 216 217 func (s *State) SetPaused() { 218 s.Lock() 219 s.Paused = true 220 s.Unlock() 221 } 222 223 func (s *State) SetUnpaused() { 224 s.Lock() 225 s.Paused = false 226 s.Unlock() 227 } 228 229 func (s *State) IsPaused() bool { 230 s.Lock() 231 res := s.Paused 232 s.Unlock() 233 return res 234 } 235 236 func (s *State) SetRemovalInProgress() error { 237 s.Lock() 238 defer s.Unlock() 239 if s.removalInProgress { 240 return fmt.Errorf("Status is already RemovalInProgress") 241 } 242 s.removalInProgress = true 243 return nil 244 } 245 246 func (s *State) ResetRemovalInProgress() { 247 s.Lock() 248 s.removalInProgress = false 249 s.Unlock() 250 } 251 252 func (s *State) SetDead() { 253 s.Lock() 254 s.Dead = true 255 s.Unlock() 256 }