gopkg.in/docker/docker.v1@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 }