github.com/gondor/docker@v1.9.0-rc1/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 derr "github.com/docker/docker/errors" 10 "github.com/docker/docker/pkg/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 } 33 34 // NewState creates a default state object with a fresh channel for state changes. 35 func NewState() *State { 36 return &State{ 37 waitChan: make(chan struct{}), 38 } 39 } 40 41 // String returns a human-readable description of the state 42 func (s *State) String() string { 43 if s.Running { 44 if s.Paused { 45 return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt))) 46 } 47 if s.Restarting { 48 return fmt.Sprintf("Restarting (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt))) 49 } 50 51 return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt))) 52 } 53 54 if s.removalInProgress { 55 return "Removal In Progress" 56 } 57 58 if s.Dead { 59 return "Dead" 60 } 61 62 if s.StartedAt.IsZero() { 63 return "Created" 64 } 65 66 if s.FinishedAt.IsZero() { 67 return "" 68 } 69 70 return fmt.Sprintf("Exited (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt))) 71 } 72 73 // StateString returns a single string to describe state 74 func (s *State) StateString() string { 75 if s.Running { 76 if s.Paused { 77 return "paused" 78 } 79 if s.Restarting { 80 return "restarting" 81 } 82 return "running" 83 } 84 85 if s.Dead { 86 return "dead" 87 } 88 89 if s.StartedAt.IsZero() { 90 return "created" 91 } 92 93 return "exited" 94 } 95 96 func isValidStateString(s string) bool { 97 if s != "paused" && 98 s != "restarting" && 99 s != "running" && 100 s != "dead" && 101 s != "created" && 102 s != "exited" { 103 return false 104 } 105 return true 106 } 107 108 func wait(waitChan <-chan struct{}, timeout time.Duration) error { 109 if timeout < 0 { 110 <-waitChan 111 return nil 112 } 113 select { 114 case <-time.After(timeout): 115 return derr.ErrorCodeTimedOut.WithArgs(timeout) 116 case <-waitChan: 117 return nil 118 } 119 } 120 121 // waitRunning waits until state is running. If state is already 122 // running it returns immediately. If you want wait forever you must 123 // supply negative timeout. Returns pid, that was passed to 124 // setRunning. 125 func (s *State) waitRunning(timeout time.Duration) (int, error) { 126 s.Lock() 127 if s.Running { 128 pid := s.Pid 129 s.Unlock() 130 return pid, nil 131 } 132 waitChan := s.waitChan 133 s.Unlock() 134 if err := wait(waitChan, timeout); err != nil { 135 return -1, err 136 } 137 return s.getPID(), nil 138 } 139 140 // WaitStop waits until state is stopped. If state already stopped it returns 141 // immediately. If you want wait forever you must supply negative timeout. 142 // Returns exit code, that was passed to setStoppedLocking 143 func (s *State) WaitStop(timeout time.Duration) (int, error) { 144 s.Lock() 145 if !s.Running { 146 exitCode := s.ExitCode 147 s.Unlock() 148 return exitCode, nil 149 } 150 waitChan := s.waitChan 151 s.Unlock() 152 if err := wait(waitChan, timeout); err != nil { 153 return -1, err 154 } 155 return s.getExitCode(), nil 156 } 157 158 // IsRunning returns whether the running flag is set. Used by Container to check whether a container is running. 159 func (s *State) IsRunning() bool { 160 s.Lock() 161 res := s.Running 162 s.Unlock() 163 return res 164 } 165 166 // GetPID holds the process id of a container. 167 func (s *State) getPID() int { 168 s.Lock() 169 res := s.Pid 170 s.Unlock() 171 return res 172 } 173 174 func (s *State) getExitCode() int { 175 s.Lock() 176 res := s.ExitCode 177 s.Unlock() 178 return res 179 } 180 181 func (s *State) setRunning(pid int) { 182 s.Error = "" 183 s.Running = true 184 s.Paused = false 185 s.Restarting = false 186 s.ExitCode = 0 187 s.Pid = pid 188 s.StartedAt = time.Now().UTC() 189 close(s.waitChan) // fire waiters for start 190 s.waitChan = make(chan struct{}) 191 } 192 193 func (s *State) setStoppedLocking(exitStatus *execdriver.ExitStatus) { 194 s.Lock() 195 s.setStopped(exitStatus) 196 s.Unlock() 197 } 198 199 func (s *State) setStopped(exitStatus *execdriver.ExitStatus) { 200 s.Running = false 201 s.Restarting = false 202 s.Pid = 0 203 s.FinishedAt = time.Now().UTC() 204 s.ExitCode = exitStatus.ExitCode 205 s.OOMKilled = exitStatus.OOMKilled 206 close(s.waitChan) // fire waiters for stop 207 s.waitChan = make(chan struct{}) 208 } 209 210 // setRestarting is when docker handles the auto restart of containers when they are 211 // in the middle of a stop and being restarted again 212 func (s *State) setRestartingLocking(exitStatus *execdriver.ExitStatus) { 213 s.Lock() 214 s.setRestarting(exitStatus) 215 s.Unlock() 216 } 217 218 func (s *State) setRestarting(exitStatus *execdriver.ExitStatus) { 219 // we should consider the container running when it is restarting because of 220 // all the checks in docker around rm/stop/etc 221 s.Running = true 222 s.Restarting = true 223 s.Pid = 0 224 s.FinishedAt = time.Now().UTC() 225 s.ExitCode = exitStatus.ExitCode 226 s.OOMKilled = exitStatus.OOMKilled 227 close(s.waitChan) // fire waiters for stop 228 s.waitChan = make(chan struct{}) 229 } 230 231 // setError sets the container's error state. This is useful when we want to 232 // know the error that occurred when container transits to another state 233 // when inspecting it 234 func (s *State) setError(err error) { 235 s.Error = err.Error() 236 } 237 238 func (s *State) isPaused() bool { 239 s.Lock() 240 res := s.Paused 241 s.Unlock() 242 return res 243 } 244 245 func (s *State) setRemovalInProgress() error { 246 s.Lock() 247 defer s.Unlock() 248 if s.removalInProgress { 249 return derr.ErrorCodeAlreadyRemoving 250 } 251 s.removalInProgress = true 252 return nil 253 } 254 255 func (s *State) resetRemovalInProgress() { 256 s.Lock() 257 s.removalInProgress = false 258 s.Unlock() 259 } 260 261 func (s *State) setDead() { 262 s.Lock() 263 s.Dead = true 264 s.Unlock() 265 }