gitee.com/bomy/docker.git@v1.13.1/restartmanager/restartmanager.go (about)

     1  package restartmanager
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/docker/docker/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  	restartCount 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, restartCount int) RestartManager {
    40  	return &restartManager{policy: policy, restartCount: restartCount, 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  	// if the container ran for more than 10s, regardless of status and policy reset the
    69  	// the timeout back to the default.
    70  	if executionDuration.Seconds() >= 10 {
    71  		rm.timeout = 0
    72  	}
    73  	if rm.timeout == 0 {
    74  		rm.timeout = defaultTimeout
    75  	} else {
    76  		rm.timeout *= backoffMultiplier
    77  	}
    78  
    79  	var restart bool
    80  	switch {
    81  	case rm.policy.IsAlways():
    82  		restart = true
    83  	case rm.policy.IsUnlessStopped() && !hasBeenManuallyStopped:
    84  		restart = true
    85  	case rm.policy.IsOnFailure():
    86  		// the default value of 0 for MaximumRetryCount means that we will not enforce a maximum count
    87  		if max := rm.policy.MaximumRetryCount; max == 0 || rm.restartCount < max {
    88  			restart = exitCode != 0
    89  		}
    90  	}
    91  
    92  	if !restart {
    93  		rm.active = false
    94  		return false, nil, nil
    95  	}
    96  
    97  	rm.restartCount++
    98  
    99  	unlockOnExit = false
   100  	rm.active = true
   101  	rm.Unlock()
   102  
   103  	ch := make(chan error)
   104  	go func() {
   105  		select {
   106  		case <-rm.cancel:
   107  			ch <- ErrRestartCanceled
   108  			close(ch)
   109  		case <-time.After(rm.timeout):
   110  			rm.Lock()
   111  			close(ch)
   112  			rm.active = false
   113  			rm.Unlock()
   114  		}
   115  	}()
   116  
   117  	return true, ch, nil
   118  }
   119  
   120  func (rm *restartManager) Cancel() error {
   121  	rm.Do(func() {
   122  		rm.Lock()
   123  		rm.canceled = true
   124  		close(rm.cancel)
   125  		rm.Unlock()
   126  	})
   127  	return nil
   128  }