123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- package restartmanager // import "github.com/docker/docker/restartmanager"
- import (
- "errors"
- "fmt"
- "sync"
- "time"
- "github.com/docker/docker/api/types/container"
- )
- const (
- backoffMultiplier = 2
- defaultTimeout = 100 * time.Millisecond
- maxRestartTimeout = 1 * time.Minute
- )
- // ErrRestartCanceled is returned when the restart manager has been
- // canceled and will no longer restart the container.
- var ErrRestartCanceled = errors.New("restart canceled")
- // RestartManager defines object that controls container restarting rules.
- type RestartManager struct {
- sync.Mutex
- sync.Once
- policy container.RestartPolicy
- restartCount int
- timeout time.Duration
- active bool
- cancel chan struct{}
- canceled bool
- }
- // New returns a new RestartManager based on a policy.
- func New(policy container.RestartPolicy, restartCount int) *RestartManager {
- return &RestartManager{policy: policy, restartCount: restartCount, cancel: make(chan struct{})}
- }
- // SetPolicy sets the restart-policy for the RestartManager.
- func (rm *RestartManager) SetPolicy(policy container.RestartPolicy) {
- rm.Lock()
- rm.policy = policy
- rm.Unlock()
- }
- // ShouldRestart returns whether the container should be restarted.
- func (rm *RestartManager) ShouldRestart(exitCode uint32, hasBeenManuallyStopped bool, executionDuration time.Duration) (bool, chan error, error) {
- if rm.policy.IsNone() {
- return false, nil, nil
- }
- rm.Lock()
- unlockOnExit := true
- defer func() {
- if unlockOnExit {
- rm.Unlock()
- }
- }()
- if rm.canceled {
- return false, nil, ErrRestartCanceled
- }
- if rm.active {
- return false, nil, fmt.Errorf("invalid call on an active restart manager")
- }
- // if the container ran for more than 10s, regardless of status and policy reset the
- // the timeout back to the default.
- if executionDuration.Seconds() >= 10 {
- rm.timeout = 0
- }
- switch {
- case rm.timeout == 0:
- rm.timeout = defaultTimeout
- case rm.timeout < maxRestartTimeout:
- rm.timeout *= backoffMultiplier
- }
- if rm.timeout > maxRestartTimeout {
- rm.timeout = maxRestartTimeout
- }
- var restart bool
- switch {
- case rm.policy.IsAlways():
- restart = true
- case rm.policy.IsUnlessStopped() && !hasBeenManuallyStopped:
- restart = true
- case rm.policy.IsOnFailure():
- // the default value of 0 for MaximumRetryCount means that we will not enforce a maximum count
- if maxRetryCount := rm.policy.MaximumRetryCount; maxRetryCount == 0 || rm.restartCount < maxRetryCount {
- restart = exitCode != 0
- }
- }
- if !restart {
- rm.active = false
- return false, nil, nil
- }
- rm.restartCount++
- unlockOnExit = false
- rm.active = true
- rm.Unlock()
- ch := make(chan error)
- go func() {
- timeout := time.NewTimer(rm.timeout)
- defer timeout.Stop()
- select {
- case <-rm.cancel:
- ch <- ErrRestartCanceled
- close(ch)
- case <-timeout.C:
- rm.Lock()
- close(ch)
- rm.active = false
- rm.Unlock()
- }
- }()
- return true, ch, nil
- }
- // Cancel tells the RestartManager to no longer restart the container.
- func (rm *RestartManager) Cancel() {
- rm.Do(func() {
- rm.Lock()
- rm.canceled = true
- close(rm.cancel)
- rm.Unlock()
- })
- }
|