From f263b13b29dad989b62bb87080d51bb9d01bfdd8 Mon Sep 17 00:00:00 2001 From: Su Shi <1684739+metacpp@users.noreply.github.com> Date: Mon, 11 Dec 2017 13:06:30 -0800 Subject: [PATCH 1/2] Hotfix: Upgrade go-autorest to v9.5.1 to have more retries after meeting 429 errors. --- .../Azure/go-autorest/autorest/adal/README.md | 34 +++ .../Azure/go-autorest/autorest/adal/config.go | 16 ++ .../Azure/go-autorest/autorest/adal/token.go | 206 +++++++++++++++++- .../go-autorest/autorest/authorization.go | 62 +++++- .../Azure/go-autorest/autorest/azure/rp.go | 2 +- .../Azure/go-autorest/autorest/client.go | 11 +- .../Azure/go-autorest/autorest/preparer.go | 22 ++ .../Azure/go-autorest/autorest/sender.go | 12 +- .../Azure/go-autorest/autorest/utility.go | 47 +++- vendor/vendor.json | 76 +++---- 10 files changed, 433 insertions(+), 55 deletions(-) diff --git a/vendor/github.com/Azure/go-autorest/autorest/adal/README.md b/vendor/github.com/Azure/go-autorest/autorest/adal/README.md index a17cf98c6215..08966c9cf892 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/adal/README.md +++ b/vendor/github.com/Azure/go-autorest/autorest/adal/README.md @@ -218,6 +218,40 @@ if (err == nil) { } ``` +#### Username password authenticate + +```Go +spt, err := adal.NewServicePrincipalTokenFromUsernamePassword( + oauthConfig, + applicationID, + username, + password, + resource, + callbacks...) + +if (err == nil) { + token := spt.Token +} +``` + +#### Authorization code authenticate + +``` Go +spt, err := adal.NewServicePrincipalTokenFromAuthorizationCode( + oauthConfig, + applicationID, + clientSecret, + authorizationCode, + redirectURI, + resource, + callbacks...) + +err = spt.Refresh() +if (err == nil) { + token := spt.Token +} +``` + ### Command Line Tool A command line tool is available in `cmd/adal.go` that can acquire a token for a given resource. It supports all flows mentioned above. diff --git a/vendor/github.com/Azure/go-autorest/autorest/adal/config.go b/vendor/github.com/Azure/go-autorest/autorest/adal/config.go index 49e9214d598a..f570d540a623 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/adal/config.go +++ b/vendor/github.com/Azure/go-autorest/autorest/adal/config.go @@ -32,8 +32,24 @@ type OAuthConfig struct { DeviceCodeEndpoint url.URL } +// IsZero returns true if the OAuthConfig object is zero-initialized. +func (oac OAuthConfig) IsZero() bool { + return oac == OAuthConfig{} +} + +func validateStringParam(param, name string) error { + if len(param) == 0 { + return fmt.Errorf("parameter '" + name + "' cannot be empty") + } + return nil +} + // NewOAuthConfig returns an OAuthConfig with tenant specific urls func NewOAuthConfig(activeDirectoryEndpoint, tenantID string) (*OAuthConfig, error) { + if err := validateStringParam(activeDirectoryEndpoint, "activeDirectoryEndpoint"); err != nil { + return nil, err + } + // it's legal for tenantID to be empty so don't validate it const activeDirectoryEndpointTemplate = "%s/oauth2/%s?api-version=%s" u, err := url.Parse(activeDirectoryEndpoint) if err != nil { diff --git a/vendor/github.com/Azure/go-autorest/autorest/adal/token.go b/vendor/github.com/Azure/go-autorest/autorest/adal/token.go index 67dd97a18c18..52e6ed52e872 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/adal/token.go +++ b/vendor/github.com/Azure/go-autorest/autorest/adal/token.go @@ -42,9 +42,15 @@ const ( // OAuthGrantTypeClientCredentials is the "grant_type" identifier used in credential flows OAuthGrantTypeClientCredentials = "client_credentials" + // OAuthGrantTypeUserPass is the "grant_type" identifier used in username and password auth flows + OAuthGrantTypeUserPass = "password" + // OAuthGrantTypeRefreshToken is the "grant_type" identifier used in refresh token flows OAuthGrantTypeRefreshToken = "refresh_token" + // OAuthGrantTypeAuthorizationCode is the "grant_type" identifier used in authorization code flows + OAuthGrantTypeAuthorizationCode = "authorization_code" + // metadataHeader is the header required by MSI extension metadataHeader = "Metadata" ) @@ -54,6 +60,12 @@ type OAuthTokenProvider interface { OAuthToken() string } +// TokenRefreshError is an interface used by errors returned during token refresh. +type TokenRefreshError interface { + error + Response() *http.Response +} + // Refresher is an interface for token refresh functionality type Refresher interface { Refresh() error @@ -78,6 +90,11 @@ type Token struct { Type string `json:"token_type"` } +// IsZero returns true if the token object is zero-initialized. +func (t Token) IsZero() bool { + return t == Token{} +} + // Expires returns the time.Time when the Token expires. func (t Token) Expires() time.Time { s, err := strconv.Atoi(t.ExpiresOn) @@ -145,6 +162,34 @@ type ServicePrincipalCertificateSecret struct { type ServicePrincipalMSISecret struct { } +// ServicePrincipalUsernamePasswordSecret implements ServicePrincipalSecret for username and password auth. +type ServicePrincipalUsernamePasswordSecret struct { + Username string + Password string +} + +// ServicePrincipalAuthorizationCodeSecret implements ServicePrincipalSecret for authorization code auth. +type ServicePrincipalAuthorizationCodeSecret struct { + ClientSecret string + AuthorizationCode string + RedirectURI string +} + +// SetAuthenticationValues is a method of the interface ServicePrincipalSecret. +func (secret *ServicePrincipalAuthorizationCodeSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error { + v.Set("code", secret.AuthorizationCode) + v.Set("client_secret", secret.ClientSecret) + v.Set("redirect_uri", secret.RedirectURI) + return nil +} + +// SetAuthenticationValues is a method of the interface ServicePrincipalSecret. +func (secret *ServicePrincipalUsernamePasswordSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error { + v.Set("username", secret.Username) + v.Set("password", secret.Password) + return nil +} + // SetAuthenticationValues is a method of the interface ServicePrincipalSecret. func (msiSecret *ServicePrincipalMSISecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error { return nil @@ -210,8 +255,27 @@ type ServicePrincipalToken struct { refreshCallbacks []TokenRefreshCallback } +func validateOAuthConfig(oac OAuthConfig) error { + if oac.IsZero() { + return fmt.Errorf("parameter 'oauthConfig' cannot be zero-initialized") + } + return nil +} + // NewServicePrincipalTokenWithSecret create a ServicePrincipalToken using the supplied ServicePrincipalSecret implementation. func NewServicePrincipalTokenWithSecret(oauthConfig OAuthConfig, id string, resource string, secret ServicePrincipalSecret, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { + if err := validateOAuthConfig(oauthConfig); err != nil { + return nil, err + } + if err := validateStringParam(id, "id"); err != nil { + return nil, err + } + if err := validateStringParam(resource, "resource"); err != nil { + return nil, err + } + if secret == nil { + return nil, fmt.Errorf("parameter 'secret' cannot be nil") + } spt := &ServicePrincipalToken{ oauthConfig: oauthConfig, secret: secret, @@ -227,6 +291,18 @@ func NewServicePrincipalTokenWithSecret(oauthConfig OAuthConfig, id string, reso // NewServicePrincipalTokenFromManualToken creates a ServicePrincipalToken using the supplied token func NewServicePrincipalTokenFromManualToken(oauthConfig OAuthConfig, clientID string, resource string, token Token, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { + if err := validateOAuthConfig(oauthConfig); err != nil { + return nil, err + } + if err := validateStringParam(clientID, "clientID"); err != nil { + return nil, err + } + if err := validateStringParam(resource, "resource"); err != nil { + return nil, err + } + if token.IsZero() { + return nil, fmt.Errorf("parameter 'token' cannot be zero-initialized") + } spt, err := NewServicePrincipalTokenWithSecret( oauthConfig, clientID, @@ -245,6 +321,18 @@ func NewServicePrincipalTokenFromManualToken(oauthConfig OAuthConfig, clientID s // NewServicePrincipalToken creates a ServicePrincipalToken from the supplied Service Principal // credentials scoped to the named resource. func NewServicePrincipalToken(oauthConfig OAuthConfig, clientID string, secret string, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { + if err := validateOAuthConfig(oauthConfig); err != nil { + return nil, err + } + if err := validateStringParam(clientID, "clientID"); err != nil { + return nil, err + } + if err := validateStringParam(secret, "secret"); err != nil { + return nil, err + } + if err := validateStringParam(resource, "resource"); err != nil { + return nil, err + } return NewServicePrincipalTokenWithSecret( oauthConfig, clientID, @@ -256,8 +344,23 @@ func NewServicePrincipalToken(oauthConfig OAuthConfig, clientID string, secret s ) } -// NewServicePrincipalTokenFromCertificate create a ServicePrincipalToken from the supplied pkcs12 bytes. +// NewServicePrincipalTokenFromCertificate creates a ServicePrincipalToken from the supplied pkcs12 bytes. func NewServicePrincipalTokenFromCertificate(oauthConfig OAuthConfig, clientID string, certificate *x509.Certificate, privateKey *rsa.PrivateKey, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { + if err := validateOAuthConfig(oauthConfig); err != nil { + return nil, err + } + if err := validateStringParam(clientID, "clientID"); err != nil { + return nil, err + } + if err := validateStringParam(resource, "resource"); err != nil { + return nil, err + } + if certificate == nil { + return nil, fmt.Errorf("parameter 'certificate' cannot be nil") + } + if privateKey == nil { + return nil, fmt.Errorf("parameter 'privateKey' cannot be nil") + } return NewServicePrincipalTokenWithSecret( oauthConfig, clientID, @@ -270,6 +373,70 @@ func NewServicePrincipalTokenFromCertificate(oauthConfig OAuthConfig, clientID s ) } +// NewServicePrincipalTokenFromUsernamePassword creates a ServicePrincipalToken from the username and password. +func NewServicePrincipalTokenFromUsernamePassword(oauthConfig OAuthConfig, clientID string, username string, password string, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { + if err := validateOAuthConfig(oauthConfig); err != nil { + return nil, err + } + if err := validateStringParam(clientID, "clientID"); err != nil { + return nil, err + } + if err := validateStringParam(username, "username"); err != nil { + return nil, err + } + if err := validateStringParam(password, "password"); err != nil { + return nil, err + } + if err := validateStringParam(resource, "resource"); err != nil { + return nil, err + } + return NewServicePrincipalTokenWithSecret( + oauthConfig, + clientID, + resource, + &ServicePrincipalUsernamePasswordSecret{ + Username: username, + Password: password, + }, + callbacks..., + ) +} + +// NewServicePrincipalTokenFromAuthorizationCode creates a ServicePrincipalToken from the +func NewServicePrincipalTokenFromAuthorizationCode(oauthConfig OAuthConfig, clientID string, clientSecret string, authorizationCode string, redirectURI string, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { + + if err := validateOAuthConfig(oauthConfig); err != nil { + return nil, err + } + if err := validateStringParam(clientID, "clientID"); err != nil { + return nil, err + } + if err := validateStringParam(clientSecret, "clientSecret"); err != nil { + return nil, err + } + if err := validateStringParam(authorizationCode, "authorizationCode"); err != nil { + return nil, err + } + if err := validateStringParam(redirectURI, "redirectURI"); err != nil { + return nil, err + } + if err := validateStringParam(resource, "resource"); err != nil { + return nil, err + } + + return NewServicePrincipalTokenWithSecret( + oauthConfig, + clientID, + resource, + &ServicePrincipalAuthorizationCodeSecret{ + ClientSecret: clientSecret, + AuthorizationCode: authorizationCode, + RedirectURI: redirectURI, + }, + callbacks..., + ) +} + // GetMSIVMEndpoint gets the MSI endpoint on Virtual Machines. func GetMSIVMEndpoint() (string, error) { return getMSIVMEndpoint(msiPath) @@ -318,6 +485,26 @@ func NewServicePrincipalTokenFromMSI(msiEndpoint, resource string, callbacks ... return spt, nil } +// internal type that implements TokenRefreshError +type tokenRefreshError struct { + message string + resp *http.Response +} + +// Error implements the error interface which is part of the TokenRefreshError interface. +func (tre tokenRefreshError) Error() string { + return tre.message +} + +// Response implements the TokenRefreshError interface, it returns the raw HTTP response from the refresh operation. +func (tre tokenRefreshError) Response() *http.Response { + return tre.resp +} + +func newTokenRefreshError(message string, resp *http.Response) TokenRefreshError { + return tokenRefreshError{message: message, resp: resp} +} + // EnsureFresh will refresh the token if it will expire within the refresh window (as set by // RefreshWithin) and autoRefresh flag is on. func (spt *ServicePrincipalToken) EnsureFresh() error { @@ -350,6 +537,17 @@ func (spt *ServicePrincipalToken) RefreshExchange(resource string) error { return spt.refreshInternal(resource) } +func (spt *ServicePrincipalToken) getGrantType() string { + switch spt.secret.(type) { + case *ServicePrincipalUsernamePasswordSecret: + return OAuthGrantTypeUserPass + case *ServicePrincipalAuthorizationCodeSecret: + return OAuthGrantTypeAuthorizationCode + default: + return OAuthGrantTypeClientCredentials + } +} + func (spt *ServicePrincipalToken) refreshInternal(resource string) error { v := url.Values{} v.Set("client_id", spt.clientID) @@ -359,7 +557,7 @@ func (spt *ServicePrincipalToken) refreshInternal(resource string) error { v.Set("grant_type", OAuthGrantTypeRefreshToken) v.Set("refresh_token", spt.RefreshToken) } else { - v.Set("grant_type", OAuthGrantTypeClientCredentials) + v.Set("grant_type", spt.getGrantType()) err := spt.secret.SetAuthenticationValues(spt, &v) if err != nil { return err @@ -388,9 +586,9 @@ func (spt *ServicePrincipalToken) refreshInternal(resource string) error { if resp.StatusCode != http.StatusOK { if err != nil { - return fmt.Errorf("adal: Refresh request failed. Status Code = '%d'. Failed reading response body", resp.StatusCode) + return newTokenRefreshError(fmt.Sprintf("adal: Refresh request failed. Status Code = '%d'. Failed reading response body", resp.StatusCode), resp) } - return fmt.Errorf("adal: Refresh request failed. Status Code = '%d'. Response body: %s", resp.StatusCode, string(rb)) + return newTokenRefreshError(fmt.Sprintf("adal: Refresh request failed. Status Code = '%d'. Response body: %s", resp.StatusCode, string(rb)), resp) } if err != nil { diff --git a/vendor/github.com/Azure/go-autorest/autorest/authorization.go b/vendor/github.com/Azure/go-autorest/autorest/authorization.go index 71e3ced2d6a6..214b0e6f26c7 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/authorization.go +++ b/vendor/github.com/Azure/go-autorest/autorest/authorization.go @@ -24,9 +24,12 @@ import ( ) const ( - bearerChallengeHeader = "Www-Authenticate" - bearer = "Bearer" - tenantID = "tenantID" + bearerChallengeHeader = "Www-Authenticate" + bearer = "Bearer" + tenantID = "tenantID" + apiKeyAuthorizerHeader = "Ocp-Apim-Subscription-Key" + bingAPISdkHeader = "X-BingApis-SDK-Client" + golangBingAPISdkHeaderValue = "Go-SDK" ) // Authorizer is the interface that provides a PrepareDecorator used to supply request @@ -44,6 +47,53 @@ func (na NullAuthorizer) WithAuthorization() PrepareDecorator { return WithNothing() } +// APIKeyAuthorizer implements API Key authorization. +type APIKeyAuthorizer struct { + headers map[string]interface{} + queryParameters map[string]interface{} +} + +// NewAPIKeyAuthorizerWithHeaders creates an ApiKeyAuthorizer with headers. +func NewAPIKeyAuthorizerWithHeaders(headers map[string]interface{}) *APIKeyAuthorizer { + return NewAPIKeyAuthorizer(headers, nil) +} + +// NewAPIKeyAuthorizerWithQueryParameters creates an ApiKeyAuthorizer with query parameters. +func NewAPIKeyAuthorizerWithQueryParameters(queryParameters map[string]interface{}) *APIKeyAuthorizer { + return NewAPIKeyAuthorizer(nil, queryParameters) +} + +// NewAPIKeyAuthorizer creates an ApiKeyAuthorizer with headers. +func NewAPIKeyAuthorizer(headers map[string]interface{}, queryParameters map[string]interface{}) *APIKeyAuthorizer { + return &APIKeyAuthorizer{headers: headers, queryParameters: queryParameters} +} + +// WithAuthorization returns a PrepareDecorator that adds an HTTP headers and Query Paramaters +func (aka *APIKeyAuthorizer) WithAuthorization() PrepareDecorator { + return func(p Preparer) Preparer { + return DecoratePreparer(p, WithHeaders(aka.headers), WithQueryParameters(aka.queryParameters)) + } +} + +// CognitiveServicesAuthorizer implements authorization for Cognitive Services. +type CognitiveServicesAuthorizer struct { + subscriptionKey string +} + +// NewCognitiveServicesAuthorizer is +func NewCognitiveServicesAuthorizer(subscriptionKey string) *CognitiveServicesAuthorizer { + return &CognitiveServicesAuthorizer{subscriptionKey: subscriptionKey} +} + +// WithAuthorization is +func (csa *CognitiveServicesAuthorizer) WithAuthorization() PrepareDecorator { + headers := make(map[string]interface{}) + headers[apiKeyAuthorizerHeader] = csa.subscriptionKey + headers[bingAPISdkHeader] = golangBingAPISdkHeaderValue + + return NewAPIKeyAuthorizerWithHeaders(headers).WithAuthorization() +} + // BearerAuthorizer implements the bearer authorization type BearerAuthorizer struct { tokenProvider adal.OAuthTokenProvider @@ -69,7 +119,11 @@ func (ba *BearerAuthorizer) WithAuthorization() PrepareDecorator { if ok { err := refresher.EnsureFresh() if err != nil { - return r, NewErrorWithError(err, "azure.BearerAuthorizer", "WithAuthorization", nil, + var resp *http.Response + if tokError, ok := err.(adal.TokenRefreshError); ok { + resp = tokError.Response() + } + return r, NewErrorWithError(err, "azure.BearerAuthorizer", "WithAuthorization", resp, "Failed to refresh the Token for request to %s", r.URL) } } diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/rp.go b/vendor/github.com/Azure/go-autorest/autorest/azure/rp.go index 66d1c8c2b3b9..2d9046949129 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/rp.go +++ b/vendor/github.com/Azure/go-autorest/autorest/azure/rp.go @@ -44,7 +44,7 @@ func DoRetryWithRegistration(client autorest.Client) autorest.SendDecorator { return resp, err } - if resp.StatusCode != http.StatusConflict { + if resp.StatusCode != http.StatusConflict || client.SkipResourceProviderRegistration { return resp, err } var re RequestError diff --git a/vendor/github.com/Azure/go-autorest/autorest/client.go b/vendor/github.com/Azure/go-autorest/autorest/client.go index 9eebd83bf84c..d329cb737799 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/client.go +++ b/vendor/github.com/Azure/go-autorest/autorest/client.go @@ -166,6 +166,9 @@ type Client struct { UserAgent string Jar http.CookieJar + + // Set to true to skip attempted registration of resource providers (false by default). + SkipResourceProviderRegistration bool } // NewClientWithUserAgent returns an instance of a Client with the UserAgent set to the passed @@ -204,7 +207,13 @@ func (c Client) Do(r *http.Request) (*http.Response, error) { c.WithInspection(), c.WithAuthorization()) if err != nil { - return nil, NewErrorWithError(err, "autorest/Client", "Do", nil, "Preparing request failed") + var resp *http.Response + if detErr, ok := err.(DetailedError); ok { + // if the authorization failed (e.g. invalid credentials) there will + // be a response associated with the error, be sure to return it. + resp = detErr.Response + } + return resp, NewErrorWithError(err, "autorest/Client", "Do", nil, "Preparing request failed") } resp, err := SendWithSender(c.sender(), r) diff --git a/vendor/github.com/Azure/go-autorest/autorest/preparer.go b/vendor/github.com/Azure/go-autorest/autorest/preparer.go index 2290c4010032..9178cbbe26ee 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/preparer.go +++ b/vendor/github.com/Azure/go-autorest/autorest/preparer.go @@ -112,6 +112,28 @@ func WithHeader(header string, value string) PrepareDecorator { } } +// WithHeaders returns a PrepareDecorator that sets the specified HTTP headers of the http.Request to +// the passed value. It canonicalizes the passed headers name (via http.CanonicalHeaderKey) before +// adding them. +func WithHeaders(headers map[string]interface{}) PrepareDecorator { + h := ensureValueStrings(headers) + return func(p Preparer) Preparer { + return PreparerFunc(func(r *http.Request) (*http.Request, error) { + r, err := p.Prepare(r) + if err == nil { + if r.Header == nil { + r.Header = make(http.Header) + } + + for name, value := range h { + r.Header.Set(http.CanonicalHeaderKey(name), value) + } + } + return r, err + }) + } +} + // WithBearerAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose // value is "Bearer " followed by the supplied token. func WithBearerAuthorization(token string) PrepareDecorator { diff --git a/vendor/github.com/Azure/go-autorest/autorest/sender.go b/vendor/github.com/Azure/go-autorest/autorest/sender.go index e1ec49573ff8..ca6a98576f26 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/sender.go +++ b/vendor/github.com/Azure/go-autorest/autorest/sender.go @@ -215,20 +215,26 @@ func DoRetryForStatusCodes(attempts int, backoff time.Duration, codes ...int) Se rr := NewRetriableRequest(r) // Increment to add the first call (attempts denotes number of retries) attempts++ - for attempt := 0; attempt < attempts; attempt++ { + for attempt := 0; attempt < attempts; { err = rr.Prepare() if err != nil { return resp, err } resp, err = s.Do(rr.Request()) - // we want to retry if err is not nil (e.g. transient network failure) - if err == nil && !ResponseHasStatusCode(resp, codes...) { + // we want to retry if err is not nil (e.g. transient network failure). note that for failed authentication + // resp and err will both have a value, so in this case we don't want to retry as it will never succeed. + if err == nil && !ResponseHasStatusCode(resp, codes...) || IsTokenRefreshError(err) { return resp, err } delayed := DelayWithRetryAfter(resp, r.Cancel) if !delayed { DelayForBackoff(backoff, attempt, r.Cancel) } + // don't count a 429 against the number of attempts + // so that we continue to retry until it succeeds + if resp.StatusCode != http.StatusTooManyRequests { + attempt++ + } } return resp, err }) diff --git a/vendor/github.com/Azure/go-autorest/autorest/utility.go b/vendor/github.com/Azure/go-autorest/autorest/utility.go index 1ef4575fa0bf..031a17f98890 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/utility.go +++ b/vendor/github.com/Azure/go-autorest/autorest/utility.go @@ -25,6 +25,8 @@ import ( "reflect" "sort" "strings" + + "github.com/Azure/go-autorest/autorest/adal" ) // EncodedAs is a series of constants specifying various data encodings @@ -138,13 +140,38 @@ func MapToValues(m map[string]interface{}) url.Values { return v } +// AsStringSlice method converts interface{} to []string. This expects a +//that the parameter passed to be a slice or array of a type that has the underlying +//type a string. +func AsStringSlice(s interface{}) ([]string, error) { + v := reflect.ValueOf(s) + if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { + return nil, NewError("autorest", "AsStringSlice", "the value's type is not an array.") + } + stringSlice := make([]string, 0, v.Len()) + + for i := 0; i < v.Len(); i++ { + stringSlice = append(stringSlice, v.Index(i).String()) + } + return stringSlice, nil +} + // String method converts interface v to string. If interface is a list, it -// joins list elements using separator. +// joins list elements using the seperator. Note that only sep[0] will be used for +// joining if any separator is specified. func String(v interface{}, sep ...string) string { - if len(sep) > 0 { - return ensureValueString(strings.Join(v.([]string), sep[0])) + if len(sep) == 0 { + return ensureValueString(v) } - return ensureValueString(v) + stringSlice, ok := v.([]string) + if ok == false { + var err error + stringSlice, err = AsStringSlice(v) + if err != nil { + panic(fmt.Sprintf("autorest: Couldn't convert value to a string %s.", err)) + } + } + return ensureValueString(strings.Join(stringSlice, sep[0])) } // Encode method encodes url path and query parameters. @@ -202,3 +229,15 @@ func ChangeToGet(req *http.Request) *http.Request { req.Header.Del("Content-Length") return req } + +// IsTokenRefreshError returns true if the specified error implements the TokenRefreshError +// interface. If err is a DetailedError it will walk the chain of Original errors. +func IsTokenRefreshError(err error) bool { + if _, ok := err.(adal.TokenRefreshError); ok { + return true + } + if de, ok := err.(DetailedError); ok { + return IsTokenRefreshError(de.Original) + } + return false +} diff --git a/vendor/vendor.json b/vendor/vendor.json index de47e7dc39ba..e2a49985d6b6 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -259,67 +259,67 @@ "versionExact": "v11.2.2-beta" }, { - "checksumSHA1": "tXSzwlsAPETo3wSlIX6o8GqGZLY=", - "comment": "v9.4.1", + "checksumSHA1": "NmN3ZFnWqKz+2tgHQzWpEWkELJw=", + "comment": "v9.5.1", "path": "github.com/Azure/go-autorest/autorest", - "revision": "c67b24a8e30d876542a85022ebbdecf0e5a935e8", - "revisionTime": "2017-11-17T21:15:24Z", - "version": "v9.4.1", - "versionExact": "v9.4.1" + "revision": "4f2543ee0bc9b28d71bf1be8b9318a1c1904b83a", + "revisionTime": "2017-12-08T22:47:36Z", + "version": "v9.5.1", + "versionExact": "v9.5.1" }, { - "checksumSHA1": "Ktj3H1WpOqxnC9kdAA+F7Ol7/RQ=", - "comment": "v9.4.1", + "checksumSHA1": "GHaYEopkom2TYCTK1yJSbyVf/VE=", + "comment": "v9.5.1", "path": "github.com/Azure/go-autorest/autorest/adal", - "revision": "c67b24a8e30d876542a85022ebbdecf0e5a935e8", - "revisionTime": "2017-11-17T21:15:24Z", - "version": "v9.4.1", - "versionExact": "v9.4.1" + "revision": "4f2543ee0bc9b28d71bf1be8b9318a1c1904b83a", + "revisionTime": "2017-12-08T22:47:36Z", + "version": "v9.5.1", + "versionExact": "v9.5.1" }, { - "checksumSHA1": "zXyLmDVpkYkIsL0yinNLoW82IZc=", - "comment": "v9.4.1", + "checksumSHA1": "fazJNSy32igO+x8x2wXupgNhu+c=", + "comment": "v9.5.1", "path": "github.com/Azure/go-autorest/autorest/azure", - "revision": "c67b24a8e30d876542a85022ebbdecf0e5a935e8", - "revisionTime": "2017-11-17T21:15:24Z", - "version": "v9.4.1", - "versionExact": "v9.4.1" + "revision": "4f2543ee0bc9b28d71bf1be8b9318a1c1904b83a", + "revisionTime": "2017-12-08T22:47:36Z", + "version": "v9.5.1", + "versionExact": "v9.5.1" }, { "checksumSHA1": "+nXRwVB/JVEGe+oLsFhCmSkKPuI=", - "comment": "v9.4.1", + "comment": "v9.5.1", "path": "github.com/Azure/go-autorest/autorest/azure/cli", - "revision": "c67b24a8e30d876542a85022ebbdecf0e5a935e8", - "revisionTime": "2017-11-17T21:15:24Z", - "version": "v9.4.1", - "versionExact": "v9.4.1" + "revision": "4f2543ee0bc9b28d71bf1be8b9318a1c1904b83a", + "revisionTime": "2017-12-08T22:47:36Z", + "version": "v9.5.1", + "versionExact": "v9.5.1" }, { "checksumSHA1": "9nXCi9qQsYjxCeajJKWttxgEt0I=", - "comment": "v9.4.1", + "comment": "v9.5.1", "path": "github.com/Azure/go-autorest/autorest/date", - "revision": "c67b24a8e30d876542a85022ebbdecf0e5a935e8", - "revisionTime": "2017-11-17T21:15:24Z", - "version": "v9.4.1", - "versionExact": "v9.4.1" + "revision": "4f2543ee0bc9b28d71bf1be8b9318a1c1904b83a", + "revisionTime": "2017-12-08T22:47:36Z", + "version": "v9.5.1", + "versionExact": "v9.5.1" }, { "checksumSHA1": "SbBb2GcJNm5GjuPKGL2777QywR4=", - "comment": "v9.4.1", + "comment": "v9.5.1", "path": "github.com/Azure/go-autorest/autorest/to", - "revision": "c67b24a8e30d876542a85022ebbdecf0e5a935e8", - "revisionTime": "2017-11-17T21:15:24Z", - "version": "v9.4.1", - "versionExact": "v9.4.1" + "revision": "4f2543ee0bc9b28d71bf1be8b9318a1c1904b83a", + "revisionTime": "2017-12-08T22:47:36Z", + "version": "v9.5.1", + "versionExact": "v9.5.1" }, { "checksumSHA1": "HfqZyKllcHQDvTwgCaYL1jUPmW0=", - "comment": "v9.4.1", + "comment": "v9.5.1", "path": "github.com/Azure/go-autorest/autorest/validation", - "revision": "c67b24a8e30d876542a85022ebbdecf0e5a935e8", - "revisionTime": "2017-11-17T21:15:24Z", - "version": "v9.4.1", - "versionExact": "v9.4.1" + "revision": "4f2543ee0bc9b28d71bf1be8b9318a1c1904b83a", + "revisionTime": "2017-12-08T22:47:36Z", + "version": "v9.5.1", + "versionExact": "v9.5.1" }, { "checksumSHA1": "FIL83loX9V9APvGQIjJpbxq53F0=", From 76e31086ddc5c765b68ec6c9b10cd65c1518302f Mon Sep 17 00:00:00 2001 From: Su Shi <1684739+metacpp@users.noreply.github.com> Date: Tue, 12 Dec 2017 10:21:06 -0800 Subject: [PATCH 2/2] Upgrade to go-autorest v9.5.2 to contain the fix. --- .../Azure/go-autorest/autorest/sender.go | 2 +- vendor/vendor.json | 72 +++++++++---------- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/vendor/github.com/Azure/go-autorest/autorest/sender.go b/vendor/github.com/Azure/go-autorest/autorest/sender.go index ca6a98576f26..c5efd59a219f 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/sender.go +++ b/vendor/github.com/Azure/go-autorest/autorest/sender.go @@ -232,7 +232,7 @@ func DoRetryForStatusCodes(attempts int, backoff time.Duration, codes ...int) Se } // don't count a 429 against the number of attempts // so that we continue to retry until it succeeds - if resp.StatusCode != http.StatusTooManyRequests { + if resp == nil || resp.StatusCode != http.StatusTooManyRequests { attempt++ } } diff --git a/vendor/vendor.json b/vendor/vendor.json index e2a49985d6b6..d40aa14a65df 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -259,67 +259,67 @@ "versionExact": "v11.2.2-beta" }, { - "checksumSHA1": "NmN3ZFnWqKz+2tgHQzWpEWkELJw=", - "comment": "v9.5.1", + "checksumSHA1": "tmbv8hhVXheuJk0/b5f0az1hzB0=", + "comment": "v9.5.2", "path": "github.com/Azure/go-autorest/autorest", - "revision": "4f2543ee0bc9b28d71bf1be8b9318a1c1904b83a", - "revisionTime": "2017-12-08T22:47:36Z", - "version": "v9.5.1", - "versionExact": "v9.5.1" + "revision": "40e67ab508b948cd6ab4b4108ea42f3793ca19e3", + "revisionTime": "2017-12-12T18:03:12Z", + "version": "v9.5.2", + "versionExact": "v9.5.2" }, { "checksumSHA1": "GHaYEopkom2TYCTK1yJSbyVf/VE=", - "comment": "v9.5.1", + "comment": "v9.5.2", "path": "github.com/Azure/go-autorest/autorest/adal", - "revision": "4f2543ee0bc9b28d71bf1be8b9318a1c1904b83a", - "revisionTime": "2017-12-08T22:47:36Z", - "version": "v9.5.1", - "versionExact": "v9.5.1" + "revision": "40e67ab508b948cd6ab4b4108ea42f3793ca19e3", + "revisionTime": "2017-12-12T18:03:12Z", + "version": "v9.5.2", + "versionExact": "v9.5.2" }, { "checksumSHA1": "fazJNSy32igO+x8x2wXupgNhu+c=", - "comment": "v9.5.1", + "comment": "v9.5.2", "path": "github.com/Azure/go-autorest/autorest/azure", - "revision": "4f2543ee0bc9b28d71bf1be8b9318a1c1904b83a", - "revisionTime": "2017-12-08T22:47:36Z", - "version": "v9.5.1", - "versionExact": "v9.5.1" + "revision": "40e67ab508b948cd6ab4b4108ea42f3793ca19e3", + "revisionTime": "2017-12-12T18:03:12Z", + "version": "v9.5.2", + "versionExact": "v9.5.2" }, { "checksumSHA1": "+nXRwVB/JVEGe+oLsFhCmSkKPuI=", - "comment": "v9.5.1", + "comment": "v9.5.2", "path": "github.com/Azure/go-autorest/autorest/azure/cli", - "revision": "4f2543ee0bc9b28d71bf1be8b9318a1c1904b83a", - "revisionTime": "2017-12-08T22:47:36Z", - "version": "v9.5.1", - "versionExact": "v9.5.1" + "revision": "40e67ab508b948cd6ab4b4108ea42f3793ca19e3", + "revisionTime": "2017-12-12T18:03:12Z", + "version": "v9.5.2", + "versionExact": "v9.5.2" }, { "checksumSHA1": "9nXCi9qQsYjxCeajJKWttxgEt0I=", - "comment": "v9.5.1", + "comment": "v9.5.2", "path": "github.com/Azure/go-autorest/autorest/date", - "revision": "4f2543ee0bc9b28d71bf1be8b9318a1c1904b83a", - "revisionTime": "2017-12-08T22:47:36Z", - "version": "v9.5.1", - "versionExact": "v9.5.1" + "revision": "40e67ab508b948cd6ab4b4108ea42f3793ca19e3", + "revisionTime": "2017-12-12T18:03:12Z", + "version": "v9.5.2", + "versionExact": "v9.5.2" }, { "checksumSHA1": "SbBb2GcJNm5GjuPKGL2777QywR4=", - "comment": "v9.5.1", + "comment": "v9.5.2", "path": "github.com/Azure/go-autorest/autorest/to", - "revision": "4f2543ee0bc9b28d71bf1be8b9318a1c1904b83a", - "revisionTime": "2017-12-08T22:47:36Z", - "version": "v9.5.1", - "versionExact": "v9.5.1" + "revision": "40e67ab508b948cd6ab4b4108ea42f3793ca19e3", + "revisionTime": "2017-12-12T18:03:12Z", + "version": "v9.5.2", + "versionExact": "v9.5.2" }, { "checksumSHA1": "HfqZyKllcHQDvTwgCaYL1jUPmW0=", - "comment": "v9.5.1", + "comment": "v9.5.2", "path": "github.com/Azure/go-autorest/autorest/validation", - "revision": "4f2543ee0bc9b28d71bf1be8b9318a1c1904b83a", - "revisionTime": "2017-12-08T22:47:36Z", - "version": "v9.5.1", - "versionExact": "v9.5.1" + "revision": "40e67ab508b948cd6ab4b4108ea42f3793ca19e3", + "revisionTime": "2017-12-12T18:03:12Z", + "version": "v9.5.2", + "versionExact": "v9.5.2" }, { "checksumSHA1": "FIL83loX9V9APvGQIjJpbxq53F0=",