github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/creds/vault/reauther.go (about) 1 package vault 2 3 import ( 4 "code.cloudfoundry.org/lager" 5 "sync" 6 "time" 7 8 "github.com/cenkalti/backoff" 9 ) 10 11 // An Auther is anything which needs to be logged in and then have 12 // that login renewed on a regulary basis. 13 type Auther interface { 14 Login() (time.Duration, error) 15 Renew() (time.Duration, error) 16 } 17 18 // The ReAuther runs the authorization loop (login, renew) and retries 19 // using a bounded exponential backoff strategy. If maxTTL is set, a 20 // new login will be done _regardless_ of the available leaseDuration. 21 type ReAuther struct { 22 auther Auther 23 base time.Duration 24 max time.Duration 25 maxTTL time.Duration 26 27 loggedIn chan struct{} 28 loggedInOnce *sync.Once 29 30 closedCh chan struct{} 31 32 logger lager.Logger 33 } 34 35 // NewReAuther with a retry time and a max retry time. 36 func NewReAuther(logger lager.Logger, auther Auther, maxTTL, retry, max time.Duration) *ReAuther { 37 ra := &ReAuther{ 38 auther: auther, 39 base: retry, 40 max: max, 41 maxTTL: maxTTL, 42 43 loggedIn: make(chan struct{}, 1), 44 loggedInOnce: &sync.Once{}, 45 46 closedCh: make(chan struct{}, 1), 47 48 logger: logger, 49 } 50 51 go ra.authLoop() 52 53 return ra 54 } 55 56 // LoggedIn will receive a signal after every login. Multiple logins 57 // may result in a single signal as this channel is not blocked. 58 func (ra *ReAuther) LoggedIn() <-chan struct{} { 59 return ra.loggedIn 60 } 61 62 func (ra *ReAuther) Close() { 63 ra.logger.Debug("vault-reauther-close") 64 close(ra.closedCh) 65 } 66 67 // we can't renew a secret that has exceeded it's maxTTL or it's lease 68 func (ra *ReAuther) renewable(leaseEnd, tokenEOL time.Time) bool { 69 now := time.Now() 70 71 if ra.maxTTL != 0 && now.After(tokenEOL) { 72 // token has exceeded the configured max TTL 73 return false 74 } 75 76 if now.After(leaseEnd) { 77 // token has exceeded its lease 78 return false 79 } 80 81 return true 82 } 83 84 // sleep until the tokenEOl or half the lease duration 85 func (ra *ReAuther) sleep(leaseEnd, tokenEOL time.Time) { 86 if ra.maxTTL != 0 && leaseEnd.After(tokenEOL) { 87 time.Sleep(time.Until(tokenEOL)) 88 } else { 89 time.Sleep(time.Until(leaseEnd) / 2) 90 } 91 } 92 93 func (ra *ReAuther) closed() bool { 94 select { 95 case <-ra.closedCh: 96 ra.logger.Debug("vault-reauther-closed") 97 return true 98 default: // default clause makes above channel non-blocking. 99 } 100 return false 101 } 102 103 func (ra *ReAuther) authLoop() { 104 var tokenEOL, leaseEnd time.Time 105 106 ra.logger.Debug("vault-reauther-started") 107 defer ra.logger.Debug("vault-reauther-terminated") 108 109 for { 110 exp := backoff.NewExponentialBackOff() 111 exp.InitialInterval = ra.base 112 exp.MaxInterval = ra.max 113 exp.MaxElapsedTime = 0 114 exp.Reset() 115 116 for { 117 if ra.closed() { 118 return 119 } 120 121 lease, err := ra.auther.Login() 122 if err != nil { 123 time.Sleep(exp.NextBackOff()) 124 continue 125 } 126 127 exp.Reset() 128 129 ra.loggedInOnce.Do(func() { 130 close(ra.loggedIn) 131 }) 132 133 now := time.Now() 134 tokenEOL = now.Add(ra.maxTTL) 135 leaseEnd = now.Add(lease) 136 ra.sleep(leaseEnd, tokenEOL) 137 138 break 139 } 140 141 for { 142 if ra.closed() { 143 return 144 } 145 146 if !ra.renewable(leaseEnd, tokenEOL) { 147 break 148 } 149 150 lease, err := ra.auther.Renew() 151 if err != nil { 152 time.Sleep(exp.NextBackOff()) 153 continue 154 } 155 156 exp.Reset() 157 158 leaseEnd = time.Now().Add(lease) 159 ra.sleep(leaseEnd, tokenEOL) 160 } 161 } 162 }