Merge pull request #13767 from hashicorp/f-bump-autorest-dep

core: Bump AutoRest Dep
This commit is contained in:
Jake Champlin 2017-04-19 13:26:07 -04:00 committed by GitHub
commit 53ff003826
11 changed files with 218 additions and 118 deletions

View File

@ -16,6 +16,7 @@ and Responding. A typical pattern is:
DoRetryForAttempts(5, time.Second)) DoRetryForAttempts(5, time.Second))
err = Respond(resp, err = Respond(resp,
ByDiscardingBody(),
ByClosing()) ByClosing())
Each phase relies on decorators to modify and / or manage processing. Decorators may first modify Each phase relies on decorators to modify and / or manage processing. Decorators may first modify

View File

@ -3,12 +3,13 @@ package azure
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/date"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"strings" "strings"
"time" "time"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/date"
) )
const ( const (

View File

@ -19,93 +19,108 @@ var environments = map[string]Environment{
// Environment represents a set of endpoints for each of Azure's Clouds. // Environment represents a set of endpoints for each of Azure's Clouds.
type Environment struct { type Environment struct {
Name string `json:"name"` Name string `json:"name"`
ManagementPortalURL string `json:"managementPortalURL"` ManagementPortalURL string `json:"managementPortalURL"`
PublishSettingsURL string `json:"publishSettingsURL"` PublishSettingsURL string `json:"publishSettingsURL"`
ServiceManagementEndpoint string `json:"serviceManagementEndpoint"` ServiceManagementEndpoint string `json:"serviceManagementEndpoint"`
ResourceManagerEndpoint string `json:"resourceManagerEndpoint"` ResourceManagerEndpoint string `json:"resourceManagerEndpoint"`
ActiveDirectoryEndpoint string `json:"activeDirectoryEndpoint"` ActiveDirectoryEndpoint string `json:"activeDirectoryEndpoint"`
GalleryEndpoint string `json:"galleryEndpoint"` GalleryEndpoint string `json:"galleryEndpoint"`
KeyVaultEndpoint string `json:"keyVaultEndpoint"` KeyVaultEndpoint string `json:"keyVaultEndpoint"`
GraphEndpoint string `json:"graphEndpoint"` GraphEndpoint string `json:"graphEndpoint"`
StorageEndpointSuffix string `json:"storageEndpointSuffix"` StorageEndpointSuffix string `json:"storageEndpointSuffix"`
SQLDatabaseDNSSuffix string `json:"sqlDatabaseDNSSuffix"` SQLDatabaseDNSSuffix string `json:"sqlDatabaseDNSSuffix"`
TrafficManagerDNSSuffix string `json:"trafficManagerDNSSuffix"` TrafficManagerDNSSuffix string `json:"trafficManagerDNSSuffix"`
KeyVaultDNSSuffix string `json:"keyVaultDNSSuffix"` KeyVaultDNSSuffix string `json:"keyVaultDNSSuffix"`
ServiceBusEndpointSuffix string `json:"serviceBusEndpointSuffix"` ServiceBusEndpointSuffix string `json:"serviceBusEndpointSuffix"`
ServiceManagementVMDNSSuffix string `json:"serviceManagementVMDNSSuffix"`
ResourceManagerVMDNSSuffix string `json:"resourceManagerVMDNSSuffix"`
ContainerRegistryDNSSuffix string `json:"containerRegistryDNSSuffix"`
} }
var ( var (
// PublicCloud is the default public Azure cloud environment // PublicCloud is the default public Azure cloud environment
PublicCloud = Environment{ PublicCloud = Environment{
Name: "AzurePublicCloud", Name: "AzurePublicCloud",
ManagementPortalURL: "https://manage.windowsazure.com/", ManagementPortalURL: "https://manage.windowsazure.com/",
PublishSettingsURL: "https://manage.windowsazure.com/publishsettings/index", PublishSettingsURL: "https://manage.windowsazure.com/publishsettings/index",
ServiceManagementEndpoint: "https://management.core.windows.net/", ServiceManagementEndpoint: "https://management.core.windows.net/",
ResourceManagerEndpoint: "https://management.azure.com/", ResourceManagerEndpoint: "https://management.azure.com/",
ActiveDirectoryEndpoint: "https://login.microsoftonline.com/", ActiveDirectoryEndpoint: "https://login.microsoftonline.com/",
GalleryEndpoint: "https://gallery.azure.com/", GalleryEndpoint: "https://gallery.azure.com/",
KeyVaultEndpoint: "https://vault.azure.net/", KeyVaultEndpoint: "https://vault.azure.net/",
GraphEndpoint: "https://graph.windows.net/", GraphEndpoint: "https://graph.windows.net/",
StorageEndpointSuffix: "core.windows.net", StorageEndpointSuffix: "core.windows.net",
SQLDatabaseDNSSuffix: "database.windows.net", SQLDatabaseDNSSuffix: "database.windows.net",
TrafficManagerDNSSuffix: "trafficmanager.net", TrafficManagerDNSSuffix: "trafficmanager.net",
KeyVaultDNSSuffix: "vault.azure.net", KeyVaultDNSSuffix: "vault.azure.net",
ServiceBusEndpointSuffix: "servicebus.azure.com", ServiceBusEndpointSuffix: "servicebus.azure.com",
ServiceManagementVMDNSSuffix: "cloudapp.net",
ResourceManagerVMDNSSuffix: "cloudapp.azure.com",
ContainerRegistryDNSSuffix: "azurecr.io",
} }
// USGovernmentCloud is the cloud environment for the US Government // USGovernmentCloud is the cloud environment for the US Government
USGovernmentCloud = Environment{ USGovernmentCloud = Environment{
Name: "AzureUSGovernmentCloud", Name: "AzureUSGovernmentCloud",
ManagementPortalURL: "https://manage.windowsazure.us/", ManagementPortalURL: "https://manage.windowsazure.us/",
PublishSettingsURL: "https://manage.windowsazure.us/publishsettings/index", PublishSettingsURL: "https://manage.windowsazure.us/publishsettings/index",
ServiceManagementEndpoint: "https://management.core.usgovcloudapi.net/", ServiceManagementEndpoint: "https://management.core.usgovcloudapi.net/",
ResourceManagerEndpoint: "https://management.usgovcloudapi.net/", ResourceManagerEndpoint: "https://management.usgovcloudapi.net/",
ActiveDirectoryEndpoint: "https://login.microsoftonline.com/", ActiveDirectoryEndpoint: "https://login.microsoftonline.com/",
GalleryEndpoint: "https://gallery.usgovcloudapi.net/", GalleryEndpoint: "https://gallery.usgovcloudapi.net/",
KeyVaultEndpoint: "https://vault.usgovcloudapi.net/", KeyVaultEndpoint: "https://vault.usgovcloudapi.net/",
GraphEndpoint: "https://graph.usgovcloudapi.net/", GraphEndpoint: "https://graph.usgovcloudapi.net/",
StorageEndpointSuffix: "core.usgovcloudapi.net", StorageEndpointSuffix: "core.usgovcloudapi.net",
SQLDatabaseDNSSuffix: "database.usgovcloudapi.net", SQLDatabaseDNSSuffix: "database.usgovcloudapi.net",
TrafficManagerDNSSuffix: "usgovtrafficmanager.net", TrafficManagerDNSSuffix: "usgovtrafficmanager.net",
KeyVaultDNSSuffix: "vault.usgovcloudapi.net", KeyVaultDNSSuffix: "vault.usgovcloudapi.net",
ServiceBusEndpointSuffix: "servicebus.usgovcloudapi.net", ServiceBusEndpointSuffix: "servicebus.usgovcloudapi.net",
ServiceManagementVMDNSSuffix: "usgovcloudapp.net",
ResourceManagerVMDNSSuffix: "cloudapp.windowsazure.us",
ContainerRegistryDNSSuffix: "azurecr.io",
} }
// ChinaCloud is the cloud environment operated in China // ChinaCloud is the cloud environment operated in China
ChinaCloud = Environment{ ChinaCloud = Environment{
Name: "AzureChinaCloud", Name: "AzureChinaCloud",
ManagementPortalURL: "https://manage.chinacloudapi.com/", ManagementPortalURL: "https://manage.chinacloudapi.com/",
PublishSettingsURL: "https://manage.chinacloudapi.com/publishsettings/index", PublishSettingsURL: "https://manage.chinacloudapi.com/publishsettings/index",
ServiceManagementEndpoint: "https://management.core.chinacloudapi.cn/", ServiceManagementEndpoint: "https://management.core.chinacloudapi.cn/",
ResourceManagerEndpoint: "https://management.chinacloudapi.cn/", ResourceManagerEndpoint: "https://management.chinacloudapi.cn/",
ActiveDirectoryEndpoint: "https://login.chinacloudapi.cn/?api-version=1.0", ActiveDirectoryEndpoint: "https://login.chinacloudapi.cn/",
GalleryEndpoint: "https://gallery.chinacloudapi.cn/", GalleryEndpoint: "https://gallery.chinacloudapi.cn/",
KeyVaultEndpoint: "https://vault.azure.cn/", KeyVaultEndpoint: "https://vault.azure.cn/",
GraphEndpoint: "https://graph.chinacloudapi.cn/", GraphEndpoint: "https://graph.chinacloudapi.cn/",
StorageEndpointSuffix: "core.chinacloudapi.cn", StorageEndpointSuffix: "core.chinacloudapi.cn",
SQLDatabaseDNSSuffix: "database.chinacloudapi.cn", SQLDatabaseDNSSuffix: "database.chinacloudapi.cn",
TrafficManagerDNSSuffix: "trafficmanager.cn", TrafficManagerDNSSuffix: "trafficmanager.cn",
KeyVaultDNSSuffix: "vault.azure.cn", KeyVaultDNSSuffix: "vault.azure.cn",
ServiceBusEndpointSuffix: "servicebus.chinacloudapi.net", ServiceBusEndpointSuffix: "servicebus.chinacloudapi.net",
ServiceManagementVMDNSSuffix: "chinacloudapp.cn",
ResourceManagerVMDNSSuffix: "cloudapp.azure.cn",
ContainerRegistryDNSSuffix: "azurecr.io",
} }
// GermanCloud is the cloud environment operated in Germany // GermanCloud is the cloud environment operated in Germany
GermanCloud = Environment{ GermanCloud = Environment{
Name: "AzureGermanCloud", Name: "AzureGermanCloud",
ManagementPortalURL: "http://portal.microsoftazure.de/", ManagementPortalURL: "http://portal.microsoftazure.de/",
PublishSettingsURL: "https://manage.microsoftazure.de/publishsettings/index", PublishSettingsURL: "https://manage.microsoftazure.de/publishsettings/index",
ServiceManagementEndpoint: "https://management.core.cloudapi.de/", ServiceManagementEndpoint: "https://management.core.cloudapi.de/",
ResourceManagerEndpoint: "https://management.microsoftazure.de/", ResourceManagerEndpoint: "https://management.microsoftazure.de/",
ActiveDirectoryEndpoint: "https://login.microsoftonline.de/", ActiveDirectoryEndpoint: "https://login.microsoftonline.de/",
GalleryEndpoint: "https://gallery.cloudapi.de/", GalleryEndpoint: "https://gallery.cloudapi.de/",
KeyVaultEndpoint: "https://vault.microsoftazure.de/", KeyVaultEndpoint: "https://vault.microsoftazure.de/",
GraphEndpoint: "https://graph.cloudapi.de/", GraphEndpoint: "https://graph.cloudapi.de/",
StorageEndpointSuffix: "core.cloudapi.de", StorageEndpointSuffix: "core.cloudapi.de",
SQLDatabaseDNSSuffix: "database.cloudapi.de", SQLDatabaseDNSSuffix: "database.cloudapi.de",
TrafficManagerDNSSuffix: "azuretrafficmanager.de", TrafficManagerDNSSuffix: "azuretrafficmanager.de",
KeyVaultDNSSuffix: "vault.microsoftazure.de", KeyVaultDNSSuffix: "vault.microsoftazure.de",
ServiceBusEndpointSuffix: "servicebus.cloudapi.de", ServiceBusEndpointSuffix: "servicebus.cloudapi.de",
ServiceManagementVMDNSSuffix: "azurecloudapp.de",
ResourceManagerVMDNSSuffix: "cloudapp.microsoftazure.de",
ContainerRegistryDNSSuffix: "azurecr.io",
} }
) )
@ -121,8 +136,13 @@ func EnvironmentFromName(name string) (Environment, error) {
// OAuthConfigForTenant returns an OAuthConfig with tenant specific urls // OAuthConfigForTenant returns an OAuthConfig with tenant specific urls
func (env Environment) OAuthConfigForTenant(tenantID string) (*OAuthConfig, error) { func (env Environment) OAuthConfigForTenant(tenantID string) (*OAuthConfig, error) {
return OAuthConfigForTenant(env.ActiveDirectoryEndpoint, tenantID)
}
// OAuthConfigForTenant returns an OAuthConfig with tenant specific urls for target cloud auth endpoint
func OAuthConfigForTenant(activeDirectoryEndpoint, tenantID string) (*OAuthConfig, error) {
template := "%s/oauth2/%s?api-version=%s" template := "%s/oauth2/%s?api-version=%s"
u, err := url.Parse(env.ActiveDirectoryEndpoint) u, err := url.Parse(activeDirectoryEndpoint)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -91,7 +91,7 @@ type ServicePrincipalNoSecret struct {
// SetAuthenticationValues is a method of the interface ServicePrincipalSecret // SetAuthenticationValues is a method of the interface ServicePrincipalSecret
// It only returns an error for the ServicePrincipalNoSecret type // It only returns an error for the ServicePrincipalNoSecret type
func (noSecret *ServicePrincipalNoSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error { func (noSecret *ServicePrincipalNoSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error {
return fmt.Errorf("Manually created ServicePrincipalToken does not contain secret material to retrieve a new access token.") return fmt.Errorf("Manually created ServicePrincipalToken does not contain secret material to retrieve a new access token")
} }
// ServicePrincipalSecret is an interface that allows various secret mechanism to fill the form // ServicePrincipalSecret is an interface that allows various secret mechanism to fill the form
@ -138,7 +138,7 @@ func (secret *ServicePrincipalCertificateSecret) SignJwt(spt *ServicePrincipalTo
token := jwt.New(jwt.SigningMethodRS256) token := jwt.New(jwt.SigningMethodRS256)
token.Header["x5t"] = thumbprint token.Header["x5t"] = thumbprint
token.Claims = jwt.MapClaims{ token.Claims = jwt.MapClaims{
"aud": spt.oauthConfig.TokenEndpoint, "aud": spt.oauthConfig.TokenEndpoint.String(),
"iss": spt.clientID, "iss": spt.clientID,
"sub": spt.clientID, "sub": spt.clientID,
"jti": base64.URLEncoding.EncodeToString(jti), "jti": base64.URLEncoding.EncodeToString(jti),
@ -302,7 +302,7 @@ func (spt *ServicePrincipalToken) refreshInternal(resource string) error {
var newToken Token var newToken Token
err = autorest.Respond(resp, err = autorest.Respond(resp,
autorest.WithErrorUnlessOK(), autorest.WithErrorUnlessStatusCode(http.StatusOK),
autorest.ByUnmarshallingJSON(&newToken), autorest.ByUnmarshallingJSON(&newToken),
autorest.ByClosing()) autorest.ByClosing())
if err != nil { if err != nil {

View File

@ -8,6 +8,7 @@ import (
"log" "log"
"net/http" "net/http"
"net/http/cookiejar" "net/http/cookiejar"
"runtime"
"time" "time"
) )
@ -22,13 +23,24 @@ const (
DefaultRetryAttempts = 3 DefaultRetryAttempts = 3
) )
var statusCodesForRetry = []int{ var (
http.StatusRequestTimeout, // 408 // defaultUserAgent builds a string containing the Go version, system archityecture and OS,
http.StatusInternalServerError, // 500 // and the go-autorest version.
http.StatusBadGateway, // 502 defaultUserAgent = fmt.Sprintf("Go/%s (%s-%s) go-autorest/%s",
http.StatusServiceUnavailable, // 503 runtime.Version(),
http.StatusGatewayTimeout, // 504 runtime.GOARCH,
} runtime.GOOS,
Version(),
)
statusCodesForRetry = []int{
http.StatusRequestTimeout, // 408
http.StatusInternalServerError, // 500
http.StatusBadGateway, // 502
http.StatusServiceUnavailable, // 503
http.StatusGatewayTimeout, // 504
}
)
const ( const (
requestFormat = `HTTP Request Begin =================================================== requestFormat = `HTTP Request Begin ===================================================
@ -140,13 +152,24 @@ type Client struct {
// NewClientWithUserAgent returns an instance of a Client with the UserAgent set to the passed // NewClientWithUserAgent returns an instance of a Client with the UserAgent set to the passed
// string. // string.
func NewClientWithUserAgent(ua string) Client { func NewClientWithUserAgent(ua string) Client {
return Client{ c := Client{
PollingDelay: DefaultPollingDelay, PollingDelay: DefaultPollingDelay,
PollingDuration: DefaultPollingDuration, PollingDuration: DefaultPollingDuration,
RetryAttempts: DefaultRetryAttempts, RetryAttempts: DefaultRetryAttempts,
RetryDuration: 30 * time.Second, RetryDuration: 30 * time.Second,
UserAgent: ua, UserAgent: defaultUserAgent,
} }
c.AddToUserAgent(ua)
return c
}
// AddToUserAgent adds an extension to the current user agent
func (c *Client) AddToUserAgent(extension string) error {
if extension != "" {
c.UserAgent = fmt.Sprintf("%s %s", c.UserAgent, extension)
return nil
}
return fmt.Errorf("Extension was empty, User Agent stayed as %s", c.UserAgent)
} }
// Do implements the Sender interface by invoking the active Sender after applying authorization. // Do implements the Sender interface by invoking the active Sender after applying authorization.

View File

@ -28,6 +28,9 @@ type DetailedError struct {
// Message is the error message. // Message is the error message.
Message string Message string
// Service Error is the response body of failed API in bytes
ServiceError []byte
} }
// NewError creates a new Error conforming object from the passed packageType, method, and // NewError creates a new Error conforming object from the passed packageType, method, and

View File

@ -183,6 +183,16 @@ func WithBaseURL(baseURL string) PrepareDecorator {
} }
} }
// WithCustomBaseURL returns a PrepareDecorator that replaces brace-enclosed keys within the
// request base URL (i.e., http.Request.URL) with the corresponding values from the passed map.
func WithCustomBaseURL(baseURL string, urlParameters map[string]interface{}) PrepareDecorator {
parameters := ensureValueStrings(urlParameters)
for key, value := range parameters {
baseURL = strings.Replace(baseURL, "{"+key+"}", value, -1)
}
return WithBaseURL(baseURL)
}
// WithFormData returns a PrepareDecoratore that "URL encodes" (e.g., bar=baz&foo=quux) into the // WithFormData returns a PrepareDecoratore that "URL encodes" (e.g., bar=baz&foo=quux) into the
// http.Request body. // http.Request body.
func WithFormData(v url.Values) PrepareDecorator { func WithFormData(v url.Values) PrepareDecorator {

View File

@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"encoding/xml" "encoding/xml"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"strings" "strings"
@ -87,6 +88,24 @@ func ByCopying(b *bytes.Buffer) RespondDecorator {
} }
} }
// ByDiscardingBody returns a RespondDecorator that first invokes the passed Responder after which
// it copies the remaining bytes (if any) in the response body to ioutil.Discard. Since the passed
// Responder is invoked prior to discarding the response body, the decorator may occur anywhere
// within the set.
func ByDiscardingBody() RespondDecorator {
return func(r Responder) Responder {
return ResponderFunc(func(resp *http.Response) error {
err := r.Respond(resp)
if err == nil && resp != nil && resp.Body != nil {
if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil {
return fmt.Errorf("Error discarding the response body: %v", err)
}
}
return err
})
}
}
// ByClosing returns a RespondDecorator that first invokes the passed Responder after which it // ByClosing returns a RespondDecorator that first invokes the passed Responder after which it
// closes the response body. Since the passed Responder is invoked prior to closing the response // closes the response body. Since the passed Responder is invoked prior to closing the response
// body, the decorator may occur anywhere within the set. // body, the decorator may occur anywhere within the set.
@ -128,6 +147,8 @@ func ByUnmarshallingJSON(v interface{}) RespondDecorator {
err := r.Respond(resp) err := r.Respond(resp)
if err == nil { if err == nil {
b, errInner := ioutil.ReadAll(resp.Body) b, errInner := ioutil.ReadAll(resp.Body)
// Some responses might include a BOM, remove for successful unmarshalling
b = bytes.TrimPrefix(b, []byte("\xef\xbb\xbf"))
if errInner != nil { if errInner != nil {
err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner) err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner)
} else if len(strings.Trim(string(b), " ")) > 0 { } else if len(strings.Trim(string(b), " ")) > 0 {
@ -165,17 +186,24 @@ func ByUnmarshallingXML(v interface{}) RespondDecorator {
} }
// WithErrorUnlessStatusCode returns a RespondDecorator that emits an error unless the response // WithErrorUnlessStatusCode returns a RespondDecorator that emits an error unless the response
// StatusCode is among the set passed. Since these are artificial errors, the response body // StatusCode is among the set passed. On error, response body is fully read into a buffer and
// may still require closing. // presented in the returned error, as well as in the response body.
func WithErrorUnlessStatusCode(codes ...int) RespondDecorator { func WithErrorUnlessStatusCode(codes ...int) RespondDecorator {
return func(r Responder) Responder { return func(r Responder) Responder {
return ResponderFunc(func(resp *http.Response) error { return ResponderFunc(func(resp *http.Response) error {
err := r.Respond(resp) err := r.Respond(resp)
if err == nil && !ResponseHasStatusCode(resp, codes...) { if err == nil && !ResponseHasStatusCode(resp, codes...) {
err = NewErrorWithResponse("autorest", "WithErrorUnlessStatusCode", resp, "%v %v failed with %s", derr := NewErrorWithResponse("autorest", "WithErrorUnlessStatusCode", resp, "%v %v failed with %s",
resp.Request.Method, resp.Request.Method,
resp.Request.URL, resp.Request.URL,
resp.Status) resp.Status)
if resp.Body != nil {
defer resp.Body.Close()
b, _ := ioutil.ReadAll(resp.Body)
derr.ServiceError = b
resp.Body = ioutil.NopCloser(bytes.NewReader(b))
}
err = derr
} }
return err return err
}) })

View File

@ -73,7 +73,7 @@ func SendWithSender(s Sender, r *http.Request, decorators ...SendDecorator) (*ht
func AfterDelay(d time.Duration) SendDecorator { func AfterDelay(d time.Duration) SendDecorator {
return func(s Sender) Sender { return func(s Sender) Sender {
return SenderFunc(func(r *http.Request) (*http.Response, error) { return SenderFunc(func(r *http.Request) (*http.Response, error) {
if !DelayForBackoff(d, 1, r.Cancel) { if !DelayForBackoff(d, 0, r.Cancel) {
return nil, fmt.Errorf("autorest: AfterDelay canceled before full delay") return nil, fmt.Errorf("autorest: AfterDelay canceled before full delay")
} }
return s.Do(r) return s.Do(r)
@ -97,7 +97,7 @@ func DoCloseIfError() SendDecorator {
return SenderFunc(func(r *http.Request) (*http.Response, error) { return SenderFunc(func(r *http.Request) (*http.Response, error) {
resp, err := s.Do(r) resp, err := s.Do(r)
if err != nil { if err != nil {
Respond(resp, ByClosing()) Respond(resp, ByDiscardingBody(), ByClosing())
} }
return resp, err return resp, err
}) })
@ -156,6 +156,7 @@ func DoPollForStatusCodes(duration time.Duration, delay time.Duration, codes ...
for err == nil && ResponseHasStatusCode(resp, codes...) { for err == nil && ResponseHasStatusCode(resp, codes...) {
Respond(resp, Respond(resp,
ByDiscardingBody(),
ByClosing()) ByClosing())
resp, err = SendWithSender(s, r, resp, err = SendWithSender(s, r,
AfterDelay(GetRetryAfter(resp, delay))) AfterDelay(GetRetryAfter(resp, delay)))
@ -257,6 +258,8 @@ func WithLogging(logger *log.Logger) SendDecorator {
// passed attempt (i.e., an exponential backoff delay). Backoff duration is in seconds and can set // passed attempt (i.e., an exponential backoff delay). Backoff duration is in seconds and can set
// to zero for no delay. The delay may be canceled by closing the passed channel. If terminated early, // to zero for no delay. The delay may be canceled by closing the passed channel. If terminated early,
// returns false. // returns false.
// Note: Passing attempt 1 will result in doubling "backoff" duration. Treat this as a zero-based attempt
// count.
func DelayForBackoff(backoff time.Duration, attempt int, cancel <-chan struct{}) bool { func DelayForBackoff(backoff time.Duration, attempt int, cancel <-chan struct{}) bool {
select { select {
case <-time.After(time.Duration(backoff.Seconds()*math.Pow(2, float64(attempt))) * time.Second): case <-time.After(time.Duration(backoff.Seconds()*math.Pow(2, float64(attempt))) * time.Second):

View File

@ -2,17 +2,28 @@ package autorest
import ( import (
"fmt" "fmt"
"strings"
"sync"
) )
const ( const (
major = "7" major = 7
minor = "0" minor = 3
patch = "0" patch = 1
tag = "" tag = ""
semVerFormat = "%s.%s.%s%s"
) )
var versionLock sync.Once
var version string
// Version returns the semantic version (see http://semver.org). // Version returns the semantic version (see http://semver.org).
func Version() string { func Version() string {
return fmt.Sprintf(semVerFormat, major, minor, patch, tag) versionLock.Do(func() {
version = fmt.Sprintf("v%d.%d.%d", major, minor, patch)
if trimmed := strings.TrimPrefix(tag, "-"); trimmed != "" {
version = fmt.Sprintf("%s-%s", version, trimmed)
}
})
return version
} }

44
vendor/vendor.json vendored
View File

@ -276,48 +276,48 @@
"revisionTime": "2016-06-22T17:32:16Z" "revisionTime": "2016-06-22T17:32:16Z"
}, },
{ {
"checksumSHA1": "eVSHe6GIHj9/ziFrQLZ1SC7Nn6k=", "checksumSHA1": "U2+FgaMOPEFg/yHLD5RbiXI1cq4=",
"comment": "v7.0.5", "comment": "v7.0.5",
"path": "github.com/Azure/go-autorest/autorest", "path": "github.com/Azure/go-autorest/autorest",
"revision": "0781901f19f1e7db3034d97ec57af753db0bf808", "revision": "a2fdd780c9a50455cecd249b00bdc3eb73a78e31",
"revisionTime": "2016-10-03T18:39:13Z", "revisionTime": "2017-04-06T20:28:05Z",
"version": "v7.2.1", "version": "v7.3.1",
"versionExact": "v7.2.1" "versionExact": "v7.3.1"
}, },
{ {
"checksumSHA1": "z8FwqeLK0Pluo7FYC5k2MVBoils=", "checksumSHA1": "ghrnc4vZv6q8zzeakZnrS8CGFhE=",
"comment": "v7.0.5", "comment": "v7.0.5",
"path": "github.com/Azure/go-autorest/autorest/azure", "path": "github.com/Azure/go-autorest/autorest/azure",
"revision": "0781901f19f1e7db3034d97ec57af753db0bf808", "revision": "a2fdd780c9a50455cecd249b00bdc3eb73a78e31",
"revisionTime": "2016-10-03T18:39:13Z", "revisionTime": "2017-04-06T20:28:05Z",
"version": "v7.2.1", "version": "v7.3.1",
"versionExact": "v7.2.1" "versionExact": "v7.3.1"
}, },
{ {
"checksumSHA1": "q9Qz8PAxK5FTOZwgYKe5Lj38u4c=", "checksumSHA1": "q9Qz8PAxK5FTOZwgYKe5Lj38u4c=",
"comment": "v7.0.5", "comment": "v7.0.5",
"path": "github.com/Azure/go-autorest/autorest/date", "path": "github.com/Azure/go-autorest/autorest/date",
"revision": "0781901f19f1e7db3034d97ec57af753db0bf808", "revision": "a2fdd780c9a50455cecd249b00bdc3eb73a78e31",
"revisionTime": "2016-10-03T18:39:13Z", "revisionTime": "2017-04-06T20:28:05Z",
"version": "v7.2.1", "version": "v7.3.1",
"versionExact": "v7.2.1" "versionExact": "v7.3.1"
}, },
{ {
"checksumSHA1": "Ev8qCsbFjDlMlX0N2tYAhYQFpUc=", "checksumSHA1": "Ev8qCsbFjDlMlX0N2tYAhYQFpUc=",
"comment": "v7.0.5", "comment": "v7.0.5",
"path": "github.com/Azure/go-autorest/autorest/to", "path": "github.com/Azure/go-autorest/autorest/to",
"revision": "0781901f19f1e7db3034d97ec57af753db0bf808", "revision": "a2fdd780c9a50455cecd249b00bdc3eb73a78e31",
"revisionTime": "2016-10-03T18:39:13Z", "revisionTime": "2017-04-06T20:28:05Z",
"version": "v7.2.1", "version": "v7.3.1",
"versionExact": "v7.2.1" "versionExact": "v7.3.1"
}, },
{ {
"checksumSHA1": "oBixceM+55gdk47iff8DSEIh3po=", "checksumSHA1": "oBixceM+55gdk47iff8DSEIh3po=",
"path": "github.com/Azure/go-autorest/autorest/validation", "path": "github.com/Azure/go-autorest/autorest/validation",
"revision": "0781901f19f1e7db3034d97ec57af753db0bf808", "revision": "a2fdd780c9a50455cecd249b00bdc3eb73a78e31",
"revisionTime": "2016-10-03T18:39:13Z", "revisionTime": "2017-04-06T20:28:05Z",
"version": "v7.2.1", "version": "v7.3.1",
"versionExact": "v7.2.1" "versionExact": "v7.3.1"
}, },
{ {
"checksumSHA1": "ICScouhAqYHoJEpJlJMYg7EzgyY=", "checksumSHA1": "ICScouhAqYHoJEpJlJMYg7EzgyY=",