github.com/kinvolk/docker@v1.13.1/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/docker/api/types"
    11  	"github.com/docker/go-units"
    12  )
    13  
    14  // State holds the current container state, and has methods to get and
    15  // set the state. Container has an embed, which allows all of the
    16  // functions defined against State to run against Container.
    17  type State struct {
    18  	sync.Mutex
    19  	// FIXME: Why do we have both paused and running if a
    20  	// container cannot be paused and running at the same time?
    21  	Running           bool
    22  	Paused            bool
    23  	Restarting        bool
    24  	OOMKilled         bool
    25  	RemovalInProgress bool // Not need for this to be persistent on disk.
    26  	Dead              bool
    27  	Pid               int
    28  	ExitCodeValue     int    `json:"ExitCode"`
    29  	ErrorMsg          string `json:"Error"` // contains last known error when starting the container
    30  	StartedAt         time.Time
    31  	FinishedAt        time.Time
    32  	waitChan          chan struct{}
    33  	Health            *Health
    34  }
    35  
    36  // StateStatus is used to return an error type implementing both
    37  // exec.ExitCode and error.
    38  // This type is needed as State include a sync.Mutex field which make
    39  // copying it unsafe.
    40  type StateStatus struct {
    41  	exitCode int
    42  	error    string
    43  }
    44  
    45  func newStateStatus(ec int, err string) *StateStatus {
    46  	return &StateStatus{
    47  		exitCode: ec,
    48  		error:    err,
    49  	}
    50  }
    51  
    52  // ExitCode returns current exitcode for the state.
    53  func (ss *StateStatus) ExitCode() int {
    54  	return ss.exitCode
    55  }
    56  
    57  // Error returns current error for the state.
    58  func (ss *StateStatus) Error() string {
    59  	return ss.error
    60  }
    61  
    62  // NewState creates a default state object with a fresh channel for state changes.
    63  func NewState() *State {
    64  	return &State{
    65  		waitChan: make(chan struct{}),
    66  	}
    67  }
    68  
    69  // String returns a human-readable description of the state
    70  func (s *State) String() string {
    71  	if s.Running {
    72  		if s.Paused {
    73  			return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
    74  		}
    75  		if s.Restarting {
    76  			return fmt.Sprintf("Restarting (%d) %s ago", s.ExitCodeValue, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
    77  		}
    78  
    79  		if h := s.Health; h != nil {
    80  			return fmt.Sprintf("Up %s (%s)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)), h.String())
    81  		}
    82  
    83  		return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
    84  	}
    85  
    86  	if s.RemovalInProgress {
    87  		return "Removal In Progress"
    88  	}
    89  
    90  	if s.Dead {
    91  		return "Dead"
    92  	}
    93  
    94  	if s.StartedAt.IsZero() {
    95  		return "Created"
    96  	}
    97  
    98  	if s.FinishedAt.IsZero() {
    99  		return ""
   100  	}
   101  
   102  	return fmt.Sprintf("Exited (%d) %s ago", s.ExitCodeValue, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
   103  }
   104  
   105  // HealthString returns a single string to describe health status.
   106  func (s *State) HealthString() string {
   107  	if s.Health == nil {
   108  		return types.NoHealthcheck
   109  	}
   110  
   111  	return s.Health.String()
   112  }
   113  
   114  // IsValidHealthString checks if the provided string is a valid container health status or not.
   115  func IsValidHealthString(s string) bool {
   116  	return s == types.Starting ||
   117  		s == types.Healthy ||
   118  		s == types.Unhealthy ||
   119  		s == types.NoHealthcheck
   120  }
   121  
   122  // StateString returns a single string to describe state
   123  func (s *State) StateString() string {
   124  	if s.Running {
   125  		if s.Paused {
   126  			return "paused"
   127  		}
   128  		if s.Restarting {
   129  			return "restarting"
   130  		}
   131  		return "running"
   132  	}
   133  
   134  	if s.RemovalInProgress {
   135  		return "removing"
   136  	}
   137  
   138  	if s.Dead {
   139  		return "dead"
   140  	}
   141  
   142  	if s.StartedAt.IsZero() {
   143  		return "created"
   144  	}
   145  
   146  	return "exited"
   147  }
   148  
   149  // IsValidStateString checks if the provided string is a valid container state or not.
   150  func IsValidStateString(s string) bool {
   151  	if s != "paused" &&
   152  		s != "restarting" &&
   153  		s != "removing" &&
   154  		s != "running" &&
   155  		s != "dead" &&
   156  		s != "created" &&
   157  		s != "exited" {
   158  		return false
   159  	}
   160  	return true
   161  }
   162  
   163  func wait(waitChan <-chan struct{}, timeout time.Duration) error {
   164  	if timeout < 0 {
   165  		<-waitChan
   166  		return nil
   167  	}
   168  	select {
   169  	case <-time.After(timeout):
   170  		return fmt.Errorf("Timed out: %v", timeout)
   171  	case <-waitChan:
   172  		return nil
   173  	}
   174  }
   175  
   176  // WaitStop waits until state is stopped. If state already stopped it returns
   177  // immediately. If you want wait forever you must supply negative timeout.
   178  // Returns exit code, that was passed to SetStopped
   179  func (s *State) WaitStop(timeout time.Duration) (int, error) {
   180  	s.Lock()
   181  	if !s.Running {
   182  		exitCode := s.ExitCodeValue
   183  		s.Unlock()
   184  		return exitCode, nil
   185  	}
   186  	waitChan := s.waitChan
   187  	s.Unlock()
   188  	if err := wait(waitChan, timeout); err != nil {
   189  		return -1, err
   190  	}
   191  	s.Lock()
   192  	defer s.Unlock()
   193  	return s.ExitCode(), nil
   194  }
   195  
   196  // WaitWithContext waits for the container to stop. Optional context can be
   197  // passed for canceling the request.
   198  func (s *State) WaitWithContext(ctx context.Context) error {
   199  	// todo(tonistiigi): make other wait functions use this
   200  	s.Lock()
   201  	if !s.Running {
   202  		state := newStateStatus(s.ExitCode(), s.Error())
   203  		defer s.Unlock()
   204  		if state.ExitCode() == 0 {
   205  			return nil
   206  		}
   207  		return state
   208  	}
   209  	waitChan := s.waitChan
   210  	s.Unlock()
   211  	select {
   212  	case <-waitChan:
   213  		s.Lock()
   214  		state := newStateStatus(s.ExitCode(), s.Error())
   215  		s.Unlock()
   216  		if state.ExitCode() == 0 {
   217  			return nil
   218  		}
   219  		return state
   220  	case <-ctx.Done():
   221  		return ctx.Err()
   222  	}
   223  }
   224  
   225  // IsRunning returns whether the running flag is set. Used by Container to check whether a container is running.
   226  func (s *State) IsRunning() bool {
   227  	s.Lock()
   228  	res := s.Running
   229  	s.Unlock()
   230  	return res
   231  }
   232  
   233  // GetPID holds the process id of a container.
   234  func (s *State) GetPID() int {
   235  	s.Lock()
   236  	res := s.Pid
   237  	s.Unlock()
   238  	return res
   239  }
   240  
   241  // ExitCode returns current exitcode for the state. Take lock before if state
   242  // may be shared.
   243  func (s *State) ExitCode() int {
   244  	return s.ExitCodeValue
   245  }
   246  
   247  // SetExitCode sets current exitcode for the state. Take lock before if state
   248  // may be shared.
   249  func (s *State) SetExitCode(ec int) {
   250  	s.ExitCodeValue = ec
   251  }
   252  
   253  // SetRunning sets the state of the container to "running".
   254  func (s *State) SetRunning(pid int, initial bool) {
   255  	s.ErrorMsg = ""
   256  	s.Running = true
   257  	s.Restarting = false
   258  	s.ExitCodeValue = 0
   259  	s.Pid = pid
   260  	if initial {
   261  		s.StartedAt = time.Now().UTC()
   262  	}
   263  }
   264  
   265  // SetStopped sets the container state to "stopped" without locking.
   266  func (s *State) SetStopped(exitStatus *ExitStatus) {
   267  	s.Running = false
   268  	s.Paused = false
   269  	s.Restarting = false
   270  	s.Pid = 0
   271  	s.FinishedAt = time.Now().UTC()
   272  	s.setFromExitStatus(exitStatus)
   273  	close(s.waitChan) // fire waiters for stop
   274  	s.waitChan = make(chan struct{})
   275  }
   276  
   277  // SetRestarting sets the container state to "restarting" without locking.
   278  // It also sets the container PID to 0.
   279  func (s *State) SetRestarting(exitStatus *ExitStatus) {
   280  	// we should consider the container running when it is restarting because of
   281  	// all the checks in docker around rm/stop/etc
   282  	s.Running = true
   283  	s.Restarting = true
   284  	s.Pid = 0
   285  	s.FinishedAt = time.Now().UTC()
   286  	s.setFromExitStatus(exitStatus)
   287  	close(s.waitChan) // fire waiters for stop
   288  	s.waitChan = make(chan struct{})
   289  }
   290  
   291  // SetError sets the container's error state. This is useful when we want to
   292  // know the error that occurred when container transits to another state
   293  // when inspecting it
   294  func (s *State) SetError(err error) {
   295  	s.ErrorMsg = err.Error()
   296  }
   297  
   298  // IsPaused returns whether the container is paused or not.
   299  func (s *State) IsPaused() bool {
   300  	s.Lock()
   301  	res := s.Paused
   302  	s.Unlock()
   303  	return res
   304  }
   305  
   306  // IsRestarting returns whether the container is restarting or not.
   307  func (s *State) IsRestarting() bool {
   308  	s.Lock()
   309  	res := s.Restarting
   310  	s.Unlock()
   311  	return res
   312  }
   313  
   314  // SetRemovalInProgress sets the container state as being removed.
   315  // It returns true if the container was already in that state.
   316  func (s *State) SetRemovalInProgress() bool {
   317  	s.Lock()
   318  	defer s.Unlock()
   319  	if s.RemovalInProgress {
   320  		return true
   321  	}
   322  	s.RemovalInProgress = true
   323  	return false
   324  }
   325  
   326  // ResetRemovalInProgress makes the RemovalInProgress state to false.
   327  func (s *State) ResetRemovalInProgress() {
   328  	s.Lock()
   329  	s.RemovalInProgress = false
   330  	s.Unlock()
   331  }
   332  
   333  // SetDead sets the container state to "dead"
   334  func (s *State) SetDead() {
   335  	s.Lock()
   336  	s.Dead = true
   337  	s.Unlock()
   338  }
   339  
   340  // Error returns current error for the state.
   341  func (s *State) Error() string {
   342  	return s.ErrorMsg
   343  }