mirror of https://github.com/restic/restic.git
181 lines
6.2 KiB
Go
181 lines
6.2 KiB
Go
|
/*
|
||
|
Package azure provides Azure-specific implementations used with AutoRest.
|
||
|
|
||
|
See the included examples for more detail.
|
||
|
*/
|
||
|
package azure
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"io/ioutil"
|
||
|
"net/http"
|
||
|
"strconv"
|
||
|
|
||
|
"github.com/Azure/go-autorest/autorest"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
// HeaderClientID is the Azure extension header to set a user-specified request ID.
|
||
|
HeaderClientID = "x-ms-client-request-id"
|
||
|
|
||
|
// HeaderReturnClientID is the Azure extension header to set if the user-specified request ID
|
||
|
// should be included in the response.
|
||
|
HeaderReturnClientID = "x-ms-return-client-request-id"
|
||
|
|
||
|
// HeaderRequestID is the Azure extension header of the service generated request ID returned
|
||
|
// in the response.
|
||
|
HeaderRequestID = "x-ms-request-id"
|
||
|
)
|
||
|
|
||
|
// ServiceError encapsulates the error response from an Azure service.
|
||
|
type ServiceError struct {
|
||
|
Code string `json:"code"`
|
||
|
Message string `json:"message"`
|
||
|
Details *[]interface{} `json:"details"`
|
||
|
}
|
||
|
|
||
|
func (se ServiceError) Error() string {
|
||
|
if se.Details != nil {
|
||
|
d, err := json.Marshal(*(se.Details))
|
||
|
if err != nil {
|
||
|
return fmt.Sprintf("Code=%q Message=%q Details=%v", se.Code, se.Message, *se.Details)
|
||
|
}
|
||
|
return fmt.Sprintf("Code=%q Message=%q Details=%v", se.Code, se.Message, string(d))
|
||
|
}
|
||
|
return fmt.Sprintf("Code=%q Message=%q", se.Code, se.Message)
|
||
|
}
|
||
|
|
||
|
// RequestError describes an error response returned by Azure service.
|
||
|
type RequestError struct {
|
||
|
autorest.DetailedError
|
||
|
|
||
|
// The error returned by the Azure service.
|
||
|
ServiceError *ServiceError `json:"error"`
|
||
|
|
||
|
// The request id (from the x-ms-request-id-header) of the request.
|
||
|
RequestID string
|
||
|
}
|
||
|
|
||
|
// Error returns a human-friendly error message from service error.
|
||
|
func (e RequestError) Error() string {
|
||
|
return fmt.Sprintf("autorest/azure: Service returned an error. Status=%v %v",
|
||
|
e.StatusCode, e.ServiceError)
|
||
|
}
|
||
|
|
||
|
// IsAzureError returns true if the passed error is an Azure Service error; false otherwise.
|
||
|
func IsAzureError(e error) bool {
|
||
|
_, ok := e.(*RequestError)
|
||
|
return ok
|
||
|
}
|
||
|
|
||
|
// NewErrorWithError creates a new Error conforming object from the
|
||
|
// passed packageType, method, statusCode of the given resp (UndefinedStatusCode
|
||
|
// if resp is nil), message, and original error. message is treated as a format
|
||
|
// string to which the optional args apply.
|
||
|
func NewErrorWithError(original error, packageType string, method string, resp *http.Response, message string, args ...interface{}) RequestError {
|
||
|
if v, ok := original.(*RequestError); ok {
|
||
|
return *v
|
||
|
}
|
||
|
|
||
|
statusCode := autorest.UndefinedStatusCode
|
||
|
if resp != nil {
|
||
|
statusCode = resp.StatusCode
|
||
|
}
|
||
|
return RequestError{
|
||
|
DetailedError: autorest.DetailedError{
|
||
|
Original: original,
|
||
|
PackageType: packageType,
|
||
|
Method: method,
|
||
|
StatusCode: statusCode,
|
||
|
Message: fmt.Sprintf(message, args...),
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// WithReturningClientID returns a PrepareDecorator that adds an HTTP extension header of
|
||
|
// x-ms-client-request-id whose value is the passed, undecorated UUID (e.g.,
|
||
|
// "0F39878C-5F76-4DB8-A25D-61D2C193C3CA"). It also sets the x-ms-return-client-request-id
|
||
|
// header to true such that UUID accompanies the http.Response.
|
||
|
func WithReturningClientID(uuid string) autorest.PrepareDecorator {
|
||
|
preparer := autorest.CreatePreparer(
|
||
|
WithClientID(uuid),
|
||
|
WithReturnClientID(true))
|
||
|
|
||
|
return func(p autorest.Preparer) autorest.Preparer {
|
||
|
return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||
|
r, err := p.Prepare(r)
|
||
|
if err != nil {
|
||
|
return r, err
|
||
|
}
|
||
|
return preparer.Prepare(r)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// WithClientID returns a PrepareDecorator that adds an HTTP extension header of
|
||
|
// x-ms-client-request-id whose value is passed, undecorated UUID (e.g.,
|
||
|
// "0F39878C-5F76-4DB8-A25D-61D2C193C3CA").
|
||
|
func WithClientID(uuid string) autorest.PrepareDecorator {
|
||
|
return autorest.WithHeader(HeaderClientID, uuid)
|
||
|
}
|
||
|
|
||
|
// WithReturnClientID returns a PrepareDecorator that adds an HTTP extension header of
|
||
|
// x-ms-return-client-request-id whose boolean value indicates if the value of the
|
||
|
// x-ms-client-request-id header should be included in the http.Response.
|
||
|
func WithReturnClientID(b bool) autorest.PrepareDecorator {
|
||
|
return autorest.WithHeader(HeaderReturnClientID, strconv.FormatBool(b))
|
||
|
}
|
||
|
|
||
|
// ExtractClientID extracts the client identifier from the x-ms-client-request-id header set on the
|
||
|
// http.Request sent to the service (and returned in the http.Response)
|
||
|
func ExtractClientID(resp *http.Response) string {
|
||
|
return autorest.ExtractHeaderValue(HeaderClientID, resp)
|
||
|
}
|
||
|
|
||
|
// ExtractRequestID extracts the Azure server generated request identifier from the
|
||
|
// x-ms-request-id header.
|
||
|
func ExtractRequestID(resp *http.Response) string {
|
||
|
return autorest.ExtractHeaderValue(HeaderRequestID, resp)
|
||
|
}
|
||
|
|
||
|
// WithErrorUnlessStatusCode returns a RespondDecorator that emits an
|
||
|
// azure.RequestError by reading the response body unless the response HTTP status code
|
||
|
// is among the set passed.
|
||
|
//
|
||
|
// If there is a chance service may return responses other than the Azure error
|
||
|
// format and the response cannot be parsed into an error, a decoding error will
|
||
|
// be returned containing the response body. In any case, the Responder will
|
||
|
// return an error if the status code is not satisfied.
|
||
|
//
|
||
|
// If this Responder returns an error, the response body will be replaced with
|
||
|
// an in-memory reader, which needs no further closing.
|
||
|
func WithErrorUnlessStatusCode(codes ...int) autorest.RespondDecorator {
|
||
|
return func(r autorest.Responder) autorest.Responder {
|
||
|
return autorest.ResponderFunc(func(resp *http.Response) error {
|
||
|
err := r.Respond(resp)
|
||
|
if err == nil && !autorest.ResponseHasStatusCode(resp, codes...) {
|
||
|
var e RequestError
|
||
|
defer resp.Body.Close()
|
||
|
|
||
|
// Copy and replace the Body in case it does not contain an error object.
|
||
|
// This will leave the Body available to the caller.
|
||
|
b, decodeErr := autorest.CopyAndDecode(autorest.EncodedAsJSON, resp.Body, &e)
|
||
|
resp.Body = ioutil.NopCloser(&b)
|
||
|
if decodeErr != nil {
|
||
|
return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b.String(), decodeErr)
|
||
|
} else if e.ServiceError == nil {
|
||
|
e.ServiceError = &ServiceError{Code: "Unknown", Message: "Unknown service error"}
|
||
|
}
|
||
|
|
||
|
e.RequestID = ExtractRequestID(resp)
|
||
|
if e.StatusCode == nil {
|
||
|
e.StatusCode = resp.StatusCode
|
||
|
}
|
||
|
err = &e
|
||
|
}
|
||
|
return err
|
||
|
})
|
||
|
}
|
||
|
}
|