github.com/reds/docker@v1.11.2-rc1/restartmanager/restartmanager.go (about)

     1  package restartmanager
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/docker/engine-api/types/container"
    10  )
    11  
    12  const (
    13  	backoffMultiplier = 2
    14  	defaultTimeout    = 100 * time.Millisecond
    15  )
    16  
    17  // ErrRestartCanceled is returned when the restart manager has been
    18  // canceled and will no longer restart the container.
    19  var ErrRestartCanceled = errors.New("restart canceled")
    20  
    21  // RestartManager defines object that controls container restarting rules.
    22  type RestartManager interface {
    23  	Cancel() error
    24  	ShouldRestart(exitCode uint32, hasBeenManuallyStopped bool, executionDuration time.Duration) (bool, chan error, error)
    25  }
    26  
    27  type restartManager struct {
    28  	sync.Mutex
    29  	sync.Once
    30  	policy       container.RestartPolicy
    31  	failureCount int
    32  	timeout      time.Duration
    33  	active       bool
    34  	cancel       chan struct{}
    35  	canceled     bool
    36  }
    37  
    38  // New returns a new restartmanager based on a policy.
    39  func New(policy container.RestartPolicy) RestartManager {
    40  	return &restartManager{policy: policy, cancel: make(chan struct{})}
    41  }
    42  
    43  func (rm *restartManager) SetPolicy(policy container.RestartPolicy) {
    44  	rm.Lock()
    45  	rm.policy = policy
    46  	rm.Unlock()
    47  }
    48  
    49  func (rm *restartManager) ShouldRestart(exitCode uint32, hasBeenManuallyStopped bool, executionDuration time.Duration) (bool, chan error, error) {
    50  	if rm.policy.IsNone() {
    51  		return false, nil, nil
    52  	}
    53  	rm.Lock()
    54  	unlockOnExit := true
    55  	defer func() {
    56  		if unlockOnExit {
    57  			rm.Unlock()
    58  		}
    59  	}()
    60  
    61  	if rm.canceled {
    62  		return false, nil, ErrRestartCanceled
    63  	}
    64  
    65  	if rm.active {
    66  		return false, nil, fmt.Errorf("invalid call on active restartmanager")
    67  	}
    68  
    69  	if exitCode != 0 {
    70  		rm.failureCount++
    71  	} else {
    72  		rm.failureCount = 0
    73  	}
    74  
    75  	// if the container ran for more than 10s, reguardless of status and policy reset the
    76  	// the timeout back to the default.
    77  	if executionDuration.Seconds() >= 10 {
    78  		rm.timeout = 0
    79  	}
    80  	if rm.timeout == 0 {
    81  		rm.timeout = defaultTimeout
    82  	} else {
    83  		rm.timeout *= backoffMultiplier
    84  	}
    85  
    86  	var restart bool
    87  	switch {
    88  	case rm.policy.IsAlways(), rm.policy.IsUnlessStopped():
    89  		restart = true
    90  	case rm.policy.IsOnFailure():
    91  		// the default value of 0 for MaximumRetryCount means that we will not enforce a maximum count
    92  		if max := rm.policy.MaximumRetryCount; max == 0 || rm.failureCount <= max {
    93  			restart = exitCode != 0
    94  		}
    95  	}
    96  
    97  	if !restart {
    98  		rm.active = false
    99  		return false, nil, nil
   100  	}
   101  
   102  	unlockOnExit = false
   103  	rm.active = true
   104  	rm.Unlock()
   105  
   106  	ch := make(chan error)
   107  	go func() {
   108  		select {
   109  		case <-rm.cancel:
   110  			ch <- ErrRestartCanceled
   111  			close(ch)
   112  		case <-time.After(rm.timeout):
   113  			rm.Lock()
   114  			close(ch)
   115  			rm.active = false
   116  			rm.Unlock()
   117  		}
   118  	}()
   119  
   120  	return true, ch, nil
   121  }
   122  
   123  func (rm *restartManager) Cancel() error {
   124  	rm.Do(func() {
   125  		rm.Lock()
   126  		rm.canceled = true
   127  		close(rm.cancel)
   128  		rm.Unlock()
   129  	})
   130  	return nil
   131  }