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  }