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 }