github.com/akerouanton/docker@v1.11.0-rc3/container/state.go (about)

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