1
2
3 package azure
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import (
20 "bytes"
21 "encoding/json"
22 "fmt"
23 "io/ioutil"
24 "net/http"
25 "regexp"
26 "strconv"
27 "strings"
28
29 "github.com/Azure/go-autorest/autorest"
30 )
31
32 const (
33
34 HeaderClientID = "x-ms-client-request-id"
35
36
37
38 HeaderReturnClientID = "x-ms-return-client-request-id"
39
40
41 HeaderContentType = "Content-Type"
42
43
44
45 HeaderRequestID = "x-ms-request-id"
46 )
47
48
49
50 type ServiceError struct {
51 Code string `json:"code"`
52 Message string `json:"message"`
53 Target *string `json:"target"`
54 Details []map[string]interface{} `json:"details"`
55 InnerError map[string]interface{} `json:"innererror"`
56 AdditionalInfo []map[string]interface{} `json:"additionalInfo"`
57 }
58
59 func (se ServiceError) Error() string {
60 result := fmt.Sprintf("Code=%q Message=%q", se.Code, se.Message)
61
62 if se.Target != nil {
63 result += fmt.Sprintf(" Target=%q", *se.Target)
64 }
65
66 if se.Details != nil {
67 d, err := json.Marshal(se.Details)
68 if err != nil {
69 result += fmt.Sprintf(" Details=%v", se.Details)
70 }
71 result += fmt.Sprintf(" Details=%s", d)
72 }
73
74 if se.InnerError != nil {
75 d, err := json.Marshal(se.InnerError)
76 if err != nil {
77 result += fmt.Sprintf(" InnerError=%v", se.InnerError)
78 }
79 result += fmt.Sprintf(" InnerError=%s", d)
80 }
81
82 if se.AdditionalInfo != nil {
83 d, err := json.Marshal(se.AdditionalInfo)
84 if err != nil {
85 result += fmt.Sprintf(" AdditionalInfo=%v", se.AdditionalInfo)
86 }
87 result += fmt.Sprintf(" AdditionalInfo=%s", d)
88 }
89
90 return result
91 }
92
93
94 func (se *ServiceError) UnmarshalJSON(b []byte) error {
95
96
97 type serviceErrorInternal struct {
98 Code string `json:"code"`
99 Message string `json:"message"`
100 Target *string `json:"target,omitempty"`
101 AdditionalInfo []map[string]interface{} `json:"additionalInfo,omitempty"`
102
103
104
105
106 Details interface{} `json:"details,omitempty"`
107
108
109 InnerError interface{} `json:"innererror,omitempty"`
110 }
111
112 sei := serviceErrorInternal{}
113 if err := json.Unmarshal(b, &sei); err != nil {
114 return err
115 }
116
117
118 se.AdditionalInfo = sei.AdditionalInfo
119 se.Code = sei.Code
120 se.Message = sei.Message
121 se.Target = sei.Target
122
123
124 arrayOfObjs := func(v interface{}) ([]map[string]interface{}, bool) {
125 arrayOf, ok := v.([]interface{})
126 if !ok {
127 return nil, false
128 }
129 final := []map[string]interface{}{}
130 for _, item := range arrayOf {
131 as, ok := item.(map[string]interface{})
132 if !ok {
133 return nil, false
134 }
135 final = append(final, as)
136 }
137 return final, true
138 }
139
140
141
142 if c, ok := arrayOfObjs(sei.Details); ok {
143 se.Details = c
144 } else if c, ok := sei.Details.(map[string]interface{}); ok {
145 se.Details = []map[string]interface{}{c}
146 } else if sei.Details != nil {
147
148 se.Details = []map[string]interface{}{
149 {"raw": sei.Details},
150 }
151 }
152
153 if c, ok := sei.InnerError.(map[string]interface{}); ok {
154 se.InnerError = c
155 } else if c, ok := arrayOfObjs(sei.InnerError); ok {
156
157 if len(c) == 1 {
158 se.InnerError = c[0]
159 } else {
160
161 se.InnerError = map[string]interface{}{
162 "multi": c,
163 }
164 }
165 } else if c, ok := sei.InnerError.(string); ok {
166 se.InnerError = map[string]interface{}{"error": c}
167 } else if sei.InnerError != nil {
168
169 se.InnerError = map[string]interface{}{
170 "raw": sei.InnerError,
171 }
172 }
173 return nil
174 }
175
176
177 type RequestError struct {
178 autorest.DetailedError
179
180
181 ServiceError *ServiceError `json:"error" xml:"Error"`
182
183
184 RequestID string
185 }
186
187
188 func (e RequestError) Error() string {
189 return fmt.Sprintf("autorest/azure: Service returned an error. Status=%v %v",
190 e.StatusCode, e.ServiceError)
191 }
192
193
194 func IsAzureError(e error) bool {
195 _, ok := e.(*RequestError)
196 return ok
197 }
198
199
200 type Resource struct {
201 SubscriptionID string
202 ResourceGroup string
203 Provider string
204 ResourceType string
205 ResourceName string
206 }
207
208
209 func (r Resource) String() string {
210 return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/%s/%s/%s", r.SubscriptionID, r.ResourceGroup, r.Provider, r.ResourceType, r.ResourceName)
211 }
212
213
214
215 func ParseResourceID(resourceID string) (Resource, error) {
216
217 const resourceIDPatternText = `(?i)^/subscriptions/(.+)/resourceGroups/(.+)/providers/(.+?)/(.+?)/(.+)$`
218 resourceIDPattern := regexp.MustCompile(resourceIDPatternText)
219 match := resourceIDPattern.FindStringSubmatch(resourceID)
220
221 if len(match) == 0 {
222 return Resource{}, fmt.Errorf("parsing failed for %s. Invalid resource Id format", resourceID)
223 }
224
225 v := strings.Split(match[5], "/")
226 resourceName := v[len(v)-1]
227
228 result := Resource{
229 SubscriptionID: match[1],
230 ResourceGroup: match[2],
231 Provider: match[3],
232 ResourceType: match[4],
233 ResourceName: resourceName,
234 }
235
236 return result, nil
237 }
238
239
240
241
242
243 func NewErrorWithError(original error, packageType string, method string, resp *http.Response, message string, args ...interface{}) RequestError {
244 if v, ok := original.(*RequestError); ok {
245 return *v
246 }
247
248 statusCode := autorest.UndefinedStatusCode
249 if resp != nil {
250 statusCode = resp.StatusCode
251 }
252 return RequestError{
253 DetailedError: autorest.DetailedError{
254 Original: original,
255 PackageType: packageType,
256 Method: method,
257 StatusCode: statusCode,
258 Message: fmt.Sprintf(message, args...),
259 },
260 }
261 }
262
263
264
265
266
267 func WithReturningClientID(uuid string) autorest.PrepareDecorator {
268 preparer := autorest.CreatePreparer(
269 WithClientID(uuid),
270 WithReturnClientID(true))
271
272 return func(p autorest.Preparer) autorest.Preparer {
273 return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) {
274 r, err := p.Prepare(r)
275 if err != nil {
276 return r, err
277 }
278 return preparer.Prepare(r)
279 })
280 }
281 }
282
283
284
285
286 func WithClientID(uuid string) autorest.PrepareDecorator {
287 return autorest.WithHeader(HeaderClientID, uuid)
288 }
289
290
291
292
293 func WithReturnClientID(b bool) autorest.PrepareDecorator {
294 return autorest.WithHeader(HeaderReturnClientID, strconv.FormatBool(b))
295 }
296
297
298
299 func ExtractClientID(resp *http.Response) string {
300 return autorest.ExtractHeaderValue(HeaderClientID, resp)
301 }
302
303
304
305 func ExtractRequestID(resp *http.Response) string {
306 return autorest.ExtractHeaderValue(HeaderRequestID, resp)
307 }
308
309
310
311
312
313
314
315
316
317
318
319
320 func WithErrorUnlessStatusCode(codes ...int) autorest.RespondDecorator {
321 return func(r autorest.Responder) autorest.Responder {
322 return autorest.ResponderFunc(func(resp *http.Response) error {
323 err := r.Respond(resp)
324 if err == nil && !autorest.ResponseHasStatusCode(resp, codes...) {
325 var e RequestError
326 defer resp.Body.Close()
327
328 encodedAs := autorest.EncodedAsJSON
329 if strings.Contains(resp.Header.Get("Content-Type"), "xml") {
330 encodedAs = autorest.EncodedAsXML
331 }
332
333
334
335 b, decodeErr := autorest.CopyAndDecode(encodedAs, resp.Body, &e)
336 resp.Body = ioutil.NopCloser(&b)
337 if decodeErr != nil {
338 return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b, decodeErr)
339 }
340 if e.ServiceError == nil {
341
342 decoder := autorest.NewDecoder(encodedAs, bytes.NewReader(b.Bytes()))
343 if err := decoder.Decode(&e.ServiceError); err != nil {
344 return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b, err)
345 }
346
347
348 if e.ServiceError == nil {
349 e.ServiceError = &ServiceError{
350 Code: "Unknown",
351 Message: "Unknown service error",
352 Details: []map[string]interface{}{
353 {
354 "HttpResponse.Body": b.String(),
355 },
356 },
357 }
358 }
359 }
360
361 if e.ServiceError != nil && e.ServiceError.Message == "" {
362
363
364 rawBody := map[string]interface{}{}
365 decoder := autorest.NewDecoder(encodedAs, bytes.NewReader(b.Bytes()))
366 if err := decoder.Decode(&rawBody); err != nil {
367 return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b, err)
368 }
369
370 e.ServiceError = &ServiceError{
371 Code: "Unknown",
372 Message: "Unknown service error",
373 }
374 if len(rawBody) > 0 {
375 e.ServiceError.Details = []map[string]interface{}{rawBody}
376 }
377 }
378 e.Response = resp
379 e.RequestID = ExtractRequestID(resp)
380 if e.StatusCode == nil {
381 e.StatusCode = resp.StatusCode
382 }
383 err = &e
384 }
385 return err
386 })
387 }
388 }
389
View as plain text