github.com/ncdc/docker@v0.10.1-0.20160129113957-6c6729ef5b74/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  }