7
7
"strconv"
8
8
"strings"
9
9
"sync/atomic"
10
+ "time"
10
11
11
12
resty "github.com/go-resty/resty/v2"
12
13
"github.com/prometheus/client_golang/prometheus"
@@ -32,6 +33,8 @@ type AzureDevopsClient struct {
32
33
semaphore chan bool
33
34
concurrency int64
34
35
36
+ delayUntil * time.Time
37
+
35
38
LimitProject int64
36
39
LimitBuildsPerProject int64
37
40
LimitBuildsPerDefinition int64
@@ -123,7 +126,12 @@ func (c *AzureDevopsClient) rest() *resty.Client {
123
126
c .restClient .SetHeader ("Accept" , "application/json" )
124
127
c .restClient .SetBasicAuth ("" , * c .accessToken )
125
128
c .restClient .SetRetryCount (c .RequestRetries )
126
- c .restClient .OnBeforeRequest (c .restOnBeforeRequest )
129
+ if c .delayUntil != nil {
130
+ c .restClient .OnBeforeRequest (c .restOnBeforeRequestDelay )
131
+ } else {
132
+ c .restClient .OnBeforeRequest (c .restOnBeforeRequest )
133
+ }
134
+
127
135
c .restClient .OnAfterResponse (c .restOnAfterResponse )
128
136
129
137
}
@@ -157,6 +165,19 @@ func (c *AzureDevopsClient) concurrencyUnlock() {
157
165
<- c .semaphore
158
166
}
159
167
168
+ // PreRequestHook is a resty hook that is called before every request
169
+ // It checks that the delay is ok before requesting
170
+ func (c * AzureDevopsClient ) restOnBeforeRequestDelay (client * resty.Client , request * resty.Request ) (err error ) {
171
+ atomic .AddUint64 (& c .RequestCount , 1 )
172
+ if c .delayUntil != nil {
173
+ if time .Now ().Before (* c .delayUntil ) {
174
+ time .Sleep (time .Until (* c .delayUntil ))
175
+ }
176
+ c .delayUntil = nil
177
+ }
178
+ return
179
+ }
180
+
160
181
func (c * AzureDevopsClient ) restOnBeforeRequest (client * resty.Client , request * resty.Request ) (err error ) {
161
182
atomic .AddUint64 (& c .RequestCount , 1 )
162
183
return
@@ -187,6 +208,14 @@ func (c *AzureDevopsClient) checkResponse(response *resty.Response, err error) e
187
208
return err
188
209
}
189
210
if response != nil {
211
+ // check delay from usage quota
212
+ if d := response .Header ().Get ("Retry-After" ); d != "" {
213
+ // convert string to int to time.Duration
214
+ if dInt , err := strconv .Atoi (d ); err != nil {
215
+ dD := time .Now ().Add (time .Duration (dInt ) * time .Second )
216
+ c .delayUntil = & dD
217
+ }
218
+ }
190
219
// check status code
191
220
statusCode := response .StatusCode ()
192
221
if statusCode != 200 {
0 commit comments