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