build(deps): bump github.com/gophercloud/gophercloud

Bumps [github.com/gophercloud/gophercloud](https://github.com/gophercloud/gophercloud) from 0.11.0 to 0.20.0.
- [Release notes](https://github.com/gophercloud/gophercloud/releases)
- [Changelog](https://github.com/gophercloud/gophercloud/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gophercloud/gophercloud/compare/v0.11.0...v0.20.0)

---
updated-dependencies:
- dependency-name: github.com/gophercloud/gophercloud
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot] 2021-09-02 13:06:05 +00:00 committed by Ondřej Budai
parent 24727bb2e3
commit 839a708755
20 changed files with 593 additions and 194 deletions

View file

@ -13,7 +13,10 @@ import (
)
// DefaultUserAgent is the default User-Agent string set in the request header.
const DefaultUserAgent = "gophercloud/2.0.0"
const (
DefaultUserAgent = "gophercloud/2.0.0"
DefaultMaxBackoffRetries = 60
)
// UserAgent represents a User-Agent header.
type UserAgent struct {
@ -22,6 +25,14 @@ type UserAgent struct {
prepend []string
}
type RetryBackoffFunc func(context.Context, *ErrUnexpectedResponseCode, error, uint) error
// RetryFunc is a catch-all function for retrying failed API requests.
// If it returns nil, the request will be retried. If it returns an error,
// the request method will exit with that error. failCount is the number of
// times the request has failed (starting at 1).
type RetryFunc func(context context.Context, method, url string, options *RequestOpts, err error, failCount uint) error
// Prepend prepends a user-defined string to the default User-Agent string. Users
// may pass in one or more strings to prepend.
func (ua *UserAgent) Prepend(s ...string) {
@ -80,6 +91,16 @@ type ProviderClient struct {
// Context is the context passed to the HTTP request.
Context context.Context
// Retry backoff func is called when rate limited.
RetryBackoffFunc RetryBackoffFunc
// MaxBackoffRetries set the maximum number of backoffs. When not set, defaults to DefaultMaxBackoffRetries
MaxBackoffRetries uint
// A general failed request handler method - this is always called in the end if a request failed. Leave as nil
// to abort when an error is encountered.
RetryFunc RetryFunc
// mut is a mutex for the client. It protects read and write access to client attributes such as getting
// and setting the TokenID.
mut *sync.RWMutex
@ -94,10 +115,32 @@ type ProviderClient struct {
// reauthlock represents a set of attributes used to help in the reauthentication process.
type reauthlock struct {
sync.RWMutex
// This channel is non-nil during reauthentication. It can be used to ask the
// goroutine doing Reauthenticate() for its result. Look at the implementation
// of Reauthenticate() for details.
ongoing chan<- (chan<- error)
ongoing *reauthFuture
}
// reauthFuture represents future result of the reauthentication process.
// while done channel is not closed, reauthentication is in progress.
// when done channel is closed, err contains the result of reauthentication.
type reauthFuture struct {
done chan struct{}
err error
}
func newReauthFuture() *reauthFuture {
return &reauthFuture{
make(chan struct{}),
nil,
}
}
func (f *reauthFuture) Set(err error) {
f.err = err
close(f.done)
}
func (f *reauthFuture) Get() error {
<-f.done
return f.err
}
// AuthenticatedHeaders returns a map of HTTP headers that are common for all
@ -112,9 +155,7 @@ func (client *ProviderClient) AuthenticatedHeaders() (m map[string]string) {
ongoing := client.reauthmut.ongoing
client.reauthmut.Unlock()
if ongoing != nil {
responseChannel := make(chan error)
ongoing <- responseChannel
_ = <-responseChannel
_ = ongoing.Get()
}
}
t := client.Token()
@ -237,21 +278,19 @@ func (client *ProviderClient) Reauthenticate(previousToken string) error {
return client.ReauthFunc()
}
messages := make(chan (chan<- error))
future := newReauthFuture()
// Check if a Reauthenticate is in progress, or start one if not.
client.reauthmut.Lock()
ongoing := client.reauthmut.ongoing
if ongoing == nil {
client.reauthmut.ongoing = messages
client.reauthmut.ongoing = future
}
client.reauthmut.Unlock()
// If Reauthenticate is running elsewhere, wait for its result.
if ongoing != nil {
responseChannel := make(chan error)
ongoing <- responseChannel
return <-responseChannel
return ongoing.Get()
}
// Perform the actual reauthentication.
@ -264,22 +303,10 @@ func (client *ProviderClient) Reauthenticate(previousToken string) error {
// Mark Reauthenticate as finished.
client.reauthmut.Lock()
client.reauthmut.ongoing.Set(err)
client.reauthmut.ongoing = nil
client.reauthmut.Unlock()
// Report result to all other interested goroutines.
//
// This happens in a separate goroutine because another goroutine might have
// acquired a copy of `client.reauthmut.ongoing` before we cleared it, but not
// have come around to sending its request. By answering in a goroutine, we
// can have that goroutine linger until all responseChannels have been sent.
// When GC has collected all sendings ends of the channel, our receiving end
// will be closed and the goroutine will end.
go func() {
for responseChannel := range messages {
responseChannel <- err
}
}()
return err
}
@ -317,6 +344,8 @@ type requestState struct {
// reauthenticate, but keep getting 401 responses with the fresh token, reauthenticating some more
// will just get us into an infinite loop.
hasReauthenticated bool
// Retry-After backoff counter, increments during each backoff call
retries uint
}
var applicationJSON = "application/json"
@ -397,6 +426,16 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts
// Issue the request.
resp, err := client.HTTPClient.Do(req)
if err != nil {
if client.RetryFunc != nil {
var e error
state.retries = state.retries + 1
e = client.RetryFunc(client.Context, method, url, options, err, state.retries)
if e != nil {
return nil, e
}
return client.doRequest(method, url, options, state)
}
return nil, err
}
@ -421,7 +460,7 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts
respErr := ErrUnexpectedResponseCode{
URL: url,
Method: method,
Expected: options.OkCodes,
Expected: okc,
Actual: resp.StatusCode,
Body: body,
ResponseHeader: resp.Header,
@ -492,11 +531,29 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts
if error409er, ok := errType.(Err409er); ok {
err = error409er.Error409(respErr)
}
case 429:
case http.StatusTooManyRequests, 498:
err = ErrDefault429{respErr}
if error429er, ok := errType.(Err429er); ok {
err = error429er.Error429(respErr)
}
maxTries := client.MaxBackoffRetries
if maxTries == 0 {
maxTries = DefaultMaxBackoffRetries
}
if f := client.RetryBackoffFunc; f != nil && state.retries < maxTries {
var e error
state.retries = state.retries + 1
e = f(client.Context, &respErr, err, state.retries)
if e != nil {
return resp, e
}
return client.doRequest(method, url, options, state)
}
case http.StatusInternalServerError:
err = ErrDefault500{respErr}
if error500er, ok := errType.(Err500er); ok {
@ -513,6 +570,17 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts
err = respErr
}
if err != nil && client.RetryFunc != nil {
var e error
state.retries = state.retries + 1
e = client.RetryFunc(client.Context, method, url, options, err, state.retries)
if e != nil {
return resp, e
}
return client.doRequest(method, url, options, state)
}
return resp, err
}
@ -526,6 +594,16 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts
return resp, err
}
if err := json.NewDecoder(resp.Body).Decode(options.JSONResponse); err != nil {
if client.RetryFunc != nil {
var e error
state.retries = state.retries + 1
e = client.RetryFunc(client.Context, method, url, options, err, state.retries)
if e != nil {
return resp, e
}
return client.doRequest(method, url, options, state)
}
return nil, err
}
}