github.com/uriddle/docker@v0.0.0-20210926094723-4072e6aeb013/container/state.go (about) 1 package container 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/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 } 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 // IsValidStateString checks if the provided string is a valid container state or not. 97 func IsValidStateString(s string) bool { 98 if s != "paused" && 99 s != "restarting" && 100 s != "running" && 101 s != "dead" && 102 s != "created" && 103 s != "exited" { 104 return false 105 } 106 return true 107 } 108 109 func wait(waitChan <-chan struct{}, timeout time.Duration) error { 110 if timeout < 0 { 111 <-waitChan 112 return nil 113 } 114 select { 115 case <-time.After(timeout): 116 return derr.ErrorCodeTimedOut.WithArgs(timeout) 117 case <-waitChan: 118 return nil 119 } 120 } 121 122 // waitRunning waits until state is running. If state is already 123 // running it returns immediately. If you want wait forever you must 124 // supply negative timeout. Returns pid, that was passed to 125 // SetRunning. 126 func (s *State) waitRunning(timeout time.Duration) (int, error) { 127 s.Lock() 128 if s.Running { 129 pid := s.Pid 130 s.Unlock() 131 return pid, nil 132 } 133 waitChan := s.waitChan 134 s.Unlock() 135 if err := wait(waitChan, timeout); err != nil { 136 return -1, err 137 } 138 return s.GetPID(), nil 139 } 140 141 // WaitStop waits until state is stopped. If state already stopped it returns 142 // immediately. If you want wait forever you must supply negative timeout. 143 // Returns exit code, that was passed to SetStoppedLocking 144 func (s *State) WaitStop(timeout time.Duration) (int, error) { 145 s.Lock() 146 if !s.Running { 147 exitCode := s.ExitCode 148 s.Unlock() 149 return exitCode, nil 150 } 151 waitChan := s.waitChan 152 s.Unlock() 153 if err := wait(waitChan, timeout); err != nil { 154 return -1, err 155 } 156 return s.getExitCode(), nil 157 } 158 159 // IsRunning returns whether the running flag is set. Used by Container to check whether a container is running. 160 func (s *State) IsRunning() bool { 161 s.Lock() 162 res := s.Running 163 s.Unlock() 164 return res 165 } 166 167 // GetPID holds the process id of a container. 168 func (s *State) GetPID() int { 169 s.Lock() 170 res := s.Pid 171 s.Unlock() 172 return res 173 } 174 175 func (s *State) getExitCode() int { 176 s.Lock() 177 res := s.ExitCode 178 s.Unlock() 179 return res 180 } 181 182 // SetRunning sets the state of the container to "running". 183 func (s *State) SetRunning(pid int) { 184 s.Error = "" 185 s.Running = true 186 s.Paused = false 187 s.Restarting = false 188 s.ExitCode = 0 189 s.Pid = pid 190 s.StartedAt = time.Now().UTC() 191 close(s.waitChan) // fire waiters for start 192 s.waitChan = make(chan struct{}) 193 } 194 195 // SetStoppedLocking locks the container state is sets it to "stopped". 196 func (s *State) SetStoppedLocking(exitStatus *execdriver.ExitStatus) { 197 s.Lock() 198 s.SetStopped(exitStatus) 199 s.Unlock() 200 } 201 202 // SetStopped sets the container state to "stopped" without locking. 203 func (s *State) SetStopped(exitStatus *execdriver.ExitStatus) { 204 s.Running = false 205 s.Restarting = false 206 s.Pid = 0 207 s.FinishedAt = time.Now().UTC() 208 s.setFromExitStatus(exitStatus) 209 close(s.waitChan) // fire waiters for stop 210 s.waitChan = make(chan struct{}) 211 } 212 213 // SetRestartingLocking is when docker handles the auto restart of containers when they are 214 // in the middle of a stop and being restarted again 215 func (s *State) SetRestartingLocking(exitStatus *execdriver.ExitStatus) { 216 s.Lock() 217 s.SetRestarting(exitStatus) 218 s.Unlock() 219 } 220 221 // SetRestarting sets the container state to "restarting". 222 // It also sets the container PID to 0. 223 func (s *State) SetRestarting(exitStatus *execdriver.ExitStatus) { 224 // we should consider the container running when it is restarting because of 225 // all the checks in docker around rm/stop/etc 226 s.Running = true 227 s.Restarting = true 228 s.Pid = 0 229 s.FinishedAt = time.Now().UTC() 230 s.setFromExitStatus(exitStatus) 231 close(s.waitChan) // fire waiters for stop 232 s.waitChan = make(chan struct{}) 233 } 234 235 // SetError sets the container's error state. This is useful when we want to 236 // know the error that occurred when container transits to another state 237 // when inspecting it 238 func (s *State) SetError(err error) { 239 s.Error = err.Error() 240 } 241 242 // IsPaused returns whether the container is paused or not. 243 func (s *State) IsPaused() bool { 244 s.Lock() 245 res := s.Paused 246 s.Unlock() 247 return res 248 } 249 250 // IsRestarting returns whether the container is restarting or not. 251 func (s *State) IsRestarting() bool { 252 s.Lock() 253 res := s.Restarting 254 s.Unlock() 255 return res 256 } 257 258 // SetRemovalInProgress sets the container state as being removed. 259 func (s *State) SetRemovalInProgress() error { 260 s.Lock() 261 defer s.Unlock() 262 if s.RemovalInProgress { 263 return derr.ErrorCodeAlreadyRemoving 264 } 265 s.RemovalInProgress = true 266 return nil 267 } 268 269 // ResetRemovalInProgress make the RemovalInProgress state to false. 270 func (s *State) ResetRemovalInProgress() { 271 s.Lock() 272 s.RemovalInProgress = false 273 s.Unlock() 274 } 275 276 // SetDead sets the container state to "dead" 277 func (s *State) SetDead() { 278 s.Lock() 279 s.Dead = true 280 s.Unlock() 281 }