github.com/brahmaroutu/docker@v1.2.1-0.20160809185609-eb28dde01f16/container/state.go (about)

     1  package container
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"time"
     7  
     8  	"golang.org/x/net/context"
     9  
    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  	Health            *Health
    33  }
    34  
    35  // NewState creates a default state object with a fresh channel for state changes.
    36  func NewState() *State {
    37  	return &State{
    38  		waitChan: make(chan struct{}),
    39  	}
    40  }
    41  
    42  // String returns a human-readable description of the state
    43  func (s *State) String() string {
    44  	if s.Running {
    45  		if s.Paused {
    46  			return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
    47  		}
    48  		if s.Restarting {
    49  			return fmt.Sprintf("Restarting (%d) %s ago", s.exitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
    50  		}
    51  
    52  		if h := s.Health; h != nil {
    53  			return fmt.Sprintf("Up %s (%s)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)), h.String())
    54  		}
    55  		return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
    56  	}
    57  
    58  	if s.RemovalInProgress {
    59  		return "Removal In Progress"
    60  	}
    61  
    62  	if s.Dead {
    63  		return "Dead"
    64  	}
    65  
    66  	if s.StartedAt.IsZero() {
    67  		return "Created"
    68  	}
    69  
    70  	if s.FinishedAt.IsZero() {
    71  		return ""
    72  	}
    73  
    74  	return fmt.Sprintf("Exited (%d) %s ago", s.exitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
    75  }
    76  
    77  // StateString returns a single string to describe state
    78  func (s *State) StateString() string {
    79  	if s.Running {
    80  		if s.Paused {
    81  			return "paused"
    82  		}
    83  		if s.Restarting {
    84  			return "restarting"
    85  		}
    86  		return "running"
    87  	}
    88  
    89  	if s.Dead {
    90  		return "dead"
    91  	}
    92  
    93  	if s.StartedAt.IsZero() {
    94  		return "created"
    95  	}
    96  
    97  	return "exited"
    98  }
    99  
   100  // IsValidStateString checks if the provided string is a valid container state or not.
   101  func IsValidStateString(s string) bool {
   102  	if s != "paused" &&
   103  		s != "restarting" &&
   104  		s != "running" &&
   105  		s != "dead" &&
   106  		s != "created" &&
   107  		s != "exited" {
   108  		return false
   109  	}
   110  	return true
   111  }
   112  
   113  func wait(waitChan <-chan struct{}, timeout time.Duration) error {
   114  	if timeout < 0 {
   115  		<-waitChan
   116  		return nil
   117  	}
   118  	select {
   119  	case <-time.After(timeout):
   120  		return fmt.Errorf("Timed out: %v", timeout)
   121  	case <-waitChan:
   122  		return nil
   123  	}
   124  }
   125  
   126  // WaitStop waits until state is stopped. If state already stopped it returns
   127  // immediately. If you want wait forever you must supply negative timeout.
   128  // Returns exit code, that was passed to SetStoppedLocking
   129  func (s *State) WaitStop(timeout time.Duration) (int, error) {
   130  	s.Lock()
   131  	if !s.Running {
   132  		exitCode := s.exitCode
   133  		s.Unlock()
   134  		return exitCode, nil
   135  	}
   136  	waitChan := s.waitChan
   137  	s.Unlock()
   138  	if err := wait(waitChan, timeout); err != nil {
   139  		return -1, err
   140  	}
   141  	s.Lock()
   142  	defer s.Unlock()
   143  	return s.ExitCode(), nil
   144  }
   145  
   146  // WaitWithContext waits for the container to stop. Optional context can be
   147  // passed for canceling the request.
   148  func (s *State) WaitWithContext(ctx context.Context) error {
   149  	// todo(tonistiigi): make other wait functions use this
   150  	s.Lock()
   151  	if !s.Running {
   152  		state := *s
   153  		defer s.Unlock()
   154  		if state.exitCode == 0 {
   155  			return nil
   156  		}
   157  		return &state
   158  	}
   159  	waitChan := s.waitChan
   160  	s.Unlock()
   161  	select {
   162  	case <-waitChan:
   163  		s.Lock()
   164  		state := *s
   165  		s.Unlock()
   166  		if state.exitCode == 0 {
   167  			return nil
   168  		}
   169  		return &state
   170  	case <-ctx.Done():
   171  		return ctx.Err()
   172  	}
   173  }
   174  
   175  // IsRunning returns whether the running flag is set. Used by Container to check whether a container is running.
   176  func (s *State) IsRunning() bool {
   177  	s.Lock()
   178  	res := s.Running
   179  	s.Unlock()
   180  	return res
   181  }
   182  
   183  // GetPID holds the process id of a container.
   184  func (s *State) GetPID() int {
   185  	s.Lock()
   186  	res := s.Pid
   187  	s.Unlock()
   188  	return res
   189  }
   190  
   191  // ExitCode returns current exitcode for the state. Take lock before if state
   192  // may be shared.
   193  func (s *State) ExitCode() int {
   194  	res := s.exitCode
   195  	return res
   196  }
   197  
   198  // SetExitCode sets current exitcode for the state. Take lock before if state
   199  // may be shared.
   200  func (s *State) SetExitCode(ec int) {
   201  	s.exitCode = ec
   202  }
   203  
   204  // SetRunning sets the state of the container to "running".
   205  func (s *State) SetRunning(pid int, initial bool) {
   206  	s.error = ""
   207  	s.Running = true
   208  	s.Paused = false
   209  	s.Restarting = false
   210  	s.exitCode = 0
   211  	s.Pid = pid
   212  	if initial {
   213  		s.StartedAt = time.Now().UTC()
   214  	}
   215  }
   216  
   217  // SetStoppedLocking locks the container state and sets it to "stopped".
   218  func (s *State) SetStoppedLocking(exitStatus *ExitStatus) {
   219  	s.Lock()
   220  	s.SetStopped(exitStatus)
   221  	s.Unlock()
   222  }
   223  
   224  // SetStopped sets the container state to "stopped" without locking.
   225  func (s *State) SetStopped(exitStatus *ExitStatus) {
   226  	s.Running = false
   227  	s.Paused = false
   228  	s.Restarting = false
   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  // SetRestartingLocking is when docker handles the auto restart of containers when they are
   237  // in the middle of a stop and being restarted again
   238  func (s *State) SetRestartingLocking(exitStatus *ExitStatus) {
   239  	s.Lock()
   240  	s.SetRestarting(exitStatus)
   241  	s.Unlock()
   242  }
   243  
   244  // SetRestarting sets the container state to "restarting".
   245  // It also sets the container PID to 0.
   246  func (s *State) SetRestarting(exitStatus *ExitStatus) {
   247  	// we should consider the container running when it is restarting because of
   248  	// all the checks in docker around rm/stop/etc
   249  	s.Running = true
   250  	s.Restarting = true
   251  	s.Pid = 0
   252  	s.FinishedAt = time.Now().UTC()
   253  	s.setFromExitStatus(exitStatus)
   254  	close(s.waitChan) // fire waiters for stop
   255  	s.waitChan = make(chan struct{})
   256  }
   257  
   258  // SetError sets the container's error state. This is useful when we want to
   259  // know the error that occurred when container transits to another state
   260  // when inspecting it
   261  func (s *State) SetError(err error) {
   262  	s.error = err.Error()
   263  }
   264  
   265  // IsPaused returns whether the container is paused or not.
   266  func (s *State) IsPaused() bool {
   267  	s.Lock()
   268  	res := s.Paused
   269  	s.Unlock()
   270  	return res
   271  }
   272  
   273  // IsRestarting returns whether the container is restarting or not.
   274  func (s *State) IsRestarting() bool {
   275  	s.Lock()
   276  	res := s.Restarting
   277  	s.Unlock()
   278  	return res
   279  }
   280  
   281  // SetRemovalInProgress sets the container state as being removed.
   282  // It returns true if the container was already in that state.
   283  func (s *State) SetRemovalInProgress() bool {
   284  	s.Lock()
   285  	defer s.Unlock()
   286  	if s.RemovalInProgress {
   287  		return true
   288  	}
   289  	s.RemovalInProgress = true
   290  	return false
   291  }
   292  
   293  // ResetRemovalInProgress makes the RemovalInProgress state to false.
   294  func (s *State) ResetRemovalInProgress() {
   295  	s.Lock()
   296  	s.RemovalInProgress = false
   297  	s.Unlock()
   298  }
   299  
   300  // SetDead sets the container state to "dead"
   301  func (s *State) SetDead() {
   302  	s.Lock()
   303  	s.Dead = true
   304  	s.Unlock()
   305  }
   306  
   307  // Error returns current error for the state.
   308  func (s *State) Error() string {
   309  	return s.error
   310  }