1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
package gwacl
import (
"encoding/xml"
"errors"
"fmt"
"net/http"
)
// HTTPError is an extended version of the standard "error" interface. It
// adds an HTTP status code.
type HTTPError interface {
error
StatusCode() int
}
// HTTPStatus is an HTTP status code.
type HTTPStatus int
// Status returns the HTTP status code as an int.
func (s HTTPStatus) StatusCode() int {
return int(s)
}
// AzureError is an HTTPError returned by the Azure API. It contains an
// error message, and an Azure-defined error code.
type AzureError struct {
error `xml:"-"`
HTTPStatus `xml:"-"`
Code string `xml:"Code"`
Message string `xml:"Message"`
}
// *AzureError implements HTTPError.
var _ HTTPError = &AzureError{}
func (e *AzureError) Error() string {
description := e.error.Error()
status := e.StatusCode()
name := http.StatusText(status)
return fmt.Sprintf("%s: %s - %s (http code %d: %s)", description, e.Code, e.Message, status, name)
}
// ServerError is a generic HTTPError, without any further helpful information
// from the server that we can count on.
type ServerError struct {
error
HTTPStatus
}
// *ServerError implements HTTPError.
var _ HTTPError = &ServerError{}
func (e *ServerError) Error() string {
description := e.error.Error()
status := e.StatusCode()
name := http.StatusText(status)
return fmt.Sprintf("%s (%d: %s)", description, status, name)
}
// newHTTPError returns the appropriate HTTPError implementation for a given
// HTTP response.
// It takes a status code and response body, rather than just a standard
// http.Response object.
func newHTTPError(status int, body []byte, description string) HTTPError {
httpStatus := HTTPStatus(status)
baseErr := errors.New(description)
azureError := AzureError{error: baseErr, HTTPStatus: httpStatus}
err := xml.Unmarshal(body, &azureError)
if err != nil {
// It's OK if the response body wasn't actually XML... That just means
// it wasn't a proper AzureError. We have another error type for that.
return &ServerError{error: baseErr, HTTPStatus: httpStatus}
}
return &azureError
}
// newAzureErrorFromOperation composes an HTTPError based on an Operation
// struct, i.e. the result of an asynchronous operation.
func newAzureErrorFromOperation(outcome *Operation) *AzureError {
if outcome.Status != FailedOperationStatus {
msg := fmt.Errorf("interpreting Azure %s as an asynchronous failure", outcome.Status)
panic(msg)
}
return &AzureError{
error: errors.New("asynchronous operation failed"),
HTTPStatus: HTTPStatus(outcome.HTTPStatusCode),
Code: outcome.ErrorCode,
Message: outcome.ErrorMessage,
}
}
// extendError returns an error whos description is the concatenation of
// the given message plus the error string from the original error.
// It preserves the value of the error types it knows about (currently only
// ServerError).
//
// The main purpose of this method is to offer a unified way to
// extend the information present in errors while still not losing the
// additioning information present on specific errors gwacl knows out to extend
// in a more meaningful way.
func extendError(err error, message string) error {
switch err := err.(type) {
case *ServerError:
extendedError := *err
extendedError.error = fmt.Errorf(message+"%v", err.error)
return &extendedError
case *AzureError:
extendedError := *err
extendedError.error = fmt.Errorf(message+"%v", err.error)
return &extendedError
default:
return fmt.Errorf(message+"%v", err)
}
// This code cannot be reached but Go insists on having a return or a panic
// statement at the end of this method. Sigh.
panic("invalid extendError state!")
}
// IsNotFoundError returns whether or not the given error is an error (as
// returned by a gwacl method) which corresponds to a 'Not Found' error
// returned by Windows Azure.
func IsNotFoundError(err error) bool {
httpError, ok := err.(HTTPError)
if ok {
return httpError.StatusCode() == http.StatusNotFound
}
return false
}
|