Remove BPA from Makefile
[icn.git] / cmd / bpa-operator / vendor / github.com / Azure / go-autorest / autorest / azure / azure.go
1 // Package azure provides Azure-specific implementations used with AutoRest.
2 // See the included examples for more detail.
3 package azure
4
5 // Copyright 2017 Microsoft Corporation
6 //
7 //  Licensed under the Apache License, Version 2.0 (the "License");
8 //  you may not use this file except in compliance with the License.
9 //  You may obtain a copy of the License at
10 //
11 //      http://www.apache.org/licenses/LICENSE-2.0
12 //
13 //  Unless required by applicable law or agreed to in writing, software
14 //  distributed under the License is distributed on an "AS IS" BASIS,
15 //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 //  See the License for the specific language governing permissions and
17 //  limitations under the License.
18
19 import (
20         "encoding/json"
21         "fmt"
22         "io/ioutil"
23         "net/http"
24         "regexp"
25         "strconv"
26         "strings"
27
28         "github.com/Azure/go-autorest/autorest"
29 )
30
31 const (
32         // HeaderClientID is the Azure extension header to set a user-specified request ID.
33         HeaderClientID = "x-ms-client-request-id"
34
35         // HeaderReturnClientID is the Azure extension header to set if the user-specified request ID
36         // should be included in the response.
37         HeaderReturnClientID = "x-ms-return-client-request-id"
38
39         // HeaderRequestID is the Azure extension header of the service generated request ID returned
40         // in the response.
41         HeaderRequestID = "x-ms-request-id"
42 )
43
44 // ServiceError encapsulates the error response from an Azure service.
45 // It adhears to the OData v4 specification for error responses.
46 type ServiceError struct {
47         Code           string                   `json:"code"`
48         Message        string                   `json:"message"`
49         Target         *string                  `json:"target"`
50         Details        []map[string]interface{} `json:"details"`
51         InnerError     map[string]interface{}   `json:"innererror"`
52         AdditionalInfo []map[string]interface{} `json:"additionalInfo"`
53 }
54
55 func (se ServiceError) Error() string {
56         result := fmt.Sprintf("Code=%q Message=%q", se.Code, se.Message)
57
58         if se.Target != nil {
59                 result += fmt.Sprintf(" Target=%q", *se.Target)
60         }
61
62         if se.Details != nil {
63                 d, err := json.Marshal(se.Details)
64                 if err != nil {
65                         result += fmt.Sprintf(" Details=%v", se.Details)
66                 }
67                 result += fmt.Sprintf(" Details=%v", string(d))
68         }
69
70         if se.InnerError != nil {
71                 d, err := json.Marshal(se.InnerError)
72                 if err != nil {
73                         result += fmt.Sprintf(" InnerError=%v", se.InnerError)
74                 }
75                 result += fmt.Sprintf(" InnerError=%v", string(d))
76         }
77
78         if se.AdditionalInfo != nil {
79                 d, err := json.Marshal(se.AdditionalInfo)
80                 if err != nil {
81                         result += fmt.Sprintf(" AdditionalInfo=%v", se.AdditionalInfo)
82                 }
83                 result += fmt.Sprintf(" AdditionalInfo=%v", string(d))
84         }
85
86         return result
87 }
88
89 // UnmarshalJSON implements the json.Unmarshaler interface for the ServiceError type.
90 func (se *ServiceError) UnmarshalJSON(b []byte) error {
91         // per the OData v4 spec the details field must be an array of JSON objects.
92         // unfortunately not all services adhear to the spec and just return a single
93         // object instead of an array with one object.  so we have to perform some
94         // shenanigans to accommodate both cases.
95         // http://docs.oasis-open.org/odata/odata-json-format/v4.0/os/odata-json-format-v4.0-os.html#_Toc372793091
96
97         type serviceError1 struct {
98                 Code           string                   `json:"code"`
99                 Message        string                   `json:"message"`
100                 Target         *string                  `json:"target"`
101                 Details        []map[string]interface{} `json:"details"`
102                 InnerError     map[string]interface{}   `json:"innererror"`
103                 AdditionalInfo []map[string]interface{} `json:"additionalInfo"`
104         }
105
106         type serviceError2 struct {
107                 Code           string                   `json:"code"`
108                 Message        string                   `json:"message"`
109                 Target         *string                  `json:"target"`
110                 Details        map[string]interface{}   `json:"details"`
111                 InnerError     map[string]interface{}   `json:"innererror"`
112                 AdditionalInfo []map[string]interface{} `json:"additionalInfo"`
113         }
114
115         se1 := serviceError1{}
116         err := json.Unmarshal(b, &se1)
117         if err == nil {
118                 se.populate(se1.Code, se1.Message, se1.Target, se1.Details, se1.InnerError, se1.AdditionalInfo)
119                 return nil
120         }
121
122         se2 := serviceError2{}
123         err = json.Unmarshal(b, &se2)
124         if err == nil {
125                 se.populate(se2.Code, se2.Message, se2.Target, nil, se2.InnerError, se2.AdditionalInfo)
126                 se.Details = append(se.Details, se2.Details)
127                 return nil
128         }
129         return err
130 }
131
132 func (se *ServiceError) populate(code, message string, target *string, details []map[string]interface{}, inner map[string]interface{}, additional []map[string]interface{}) {
133         se.Code = code
134         se.Message = message
135         se.Target = target
136         se.Details = details
137         se.InnerError = inner
138         se.AdditionalInfo = additional
139 }
140
141 // RequestError describes an error response returned by Azure service.
142 type RequestError struct {
143         autorest.DetailedError
144
145         // The error returned by the Azure service.
146         ServiceError *ServiceError `json:"error"`
147
148         // The request id (from the x-ms-request-id-header) of the request.
149         RequestID string
150 }
151
152 // Error returns a human-friendly error message from service error.
153 func (e RequestError) Error() string {
154         return fmt.Sprintf("autorest/azure: Service returned an error. Status=%v %v",
155                 e.StatusCode, e.ServiceError)
156 }
157
158 // IsAzureError returns true if the passed error is an Azure Service error; false otherwise.
159 func IsAzureError(e error) bool {
160         _, ok := e.(*RequestError)
161         return ok
162 }
163
164 // Resource contains details about an Azure resource.
165 type Resource struct {
166         SubscriptionID string
167         ResourceGroup  string
168         Provider       string
169         ResourceType   string
170         ResourceName   string
171 }
172
173 // ParseResourceID parses a resource ID into a ResourceDetails struct.
174 // See https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-template-functions-resource#return-value-4.
175 func ParseResourceID(resourceID string) (Resource, error) {
176
177         const resourceIDPatternText = `(?i)subscriptions/(.+)/resourceGroups/(.+)/providers/(.+?)/(.+?)/(.+)`
178         resourceIDPattern := regexp.MustCompile(resourceIDPatternText)
179         match := resourceIDPattern.FindStringSubmatch(resourceID)
180
181         if len(match) == 0 {
182                 return Resource{}, fmt.Errorf("parsing failed for %s. Invalid resource Id format", resourceID)
183         }
184
185         v := strings.Split(match[5], "/")
186         resourceName := v[len(v)-1]
187
188         result := Resource{
189                 SubscriptionID: match[1],
190                 ResourceGroup:  match[2],
191                 Provider:       match[3],
192                 ResourceType:   match[4],
193                 ResourceName:   resourceName,
194         }
195
196         return result, nil
197 }
198
199 // NewErrorWithError creates a new Error conforming object from the
200 // passed packageType, method, statusCode of the given resp (UndefinedStatusCode
201 // if resp is nil), message, and original error. message is treated as a format
202 // string to which the optional args apply.
203 func NewErrorWithError(original error, packageType string, method string, resp *http.Response, message string, args ...interface{}) RequestError {
204         if v, ok := original.(*RequestError); ok {
205                 return *v
206         }
207
208         statusCode := autorest.UndefinedStatusCode
209         if resp != nil {
210                 statusCode = resp.StatusCode
211         }
212         return RequestError{
213                 DetailedError: autorest.DetailedError{
214                         Original:    original,
215                         PackageType: packageType,
216                         Method:      method,
217                         StatusCode:  statusCode,
218                         Message:     fmt.Sprintf(message, args...),
219                 },
220         }
221 }
222
223 // WithReturningClientID returns a PrepareDecorator that adds an HTTP extension header of
224 // x-ms-client-request-id whose value is the passed, undecorated UUID (e.g.,
225 // "0F39878C-5F76-4DB8-A25D-61D2C193C3CA"). It also sets the x-ms-return-client-request-id
226 // header to true such that UUID accompanies the http.Response.
227 func WithReturningClientID(uuid string) autorest.PrepareDecorator {
228         preparer := autorest.CreatePreparer(
229                 WithClientID(uuid),
230                 WithReturnClientID(true))
231
232         return func(p autorest.Preparer) autorest.Preparer {
233                 return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) {
234                         r, err := p.Prepare(r)
235                         if err != nil {
236                                 return r, err
237                         }
238                         return preparer.Prepare(r)
239                 })
240         }
241 }
242
243 // WithClientID returns a PrepareDecorator that adds an HTTP extension header of
244 // x-ms-client-request-id whose value is passed, undecorated UUID (e.g.,
245 // "0F39878C-5F76-4DB8-A25D-61D2C193C3CA").
246 func WithClientID(uuid string) autorest.PrepareDecorator {
247         return autorest.WithHeader(HeaderClientID, uuid)
248 }
249
250 // WithReturnClientID returns a PrepareDecorator that adds an HTTP extension header of
251 // x-ms-return-client-request-id whose boolean value indicates if the value of the
252 // x-ms-client-request-id header should be included in the http.Response.
253 func WithReturnClientID(b bool) autorest.PrepareDecorator {
254         return autorest.WithHeader(HeaderReturnClientID, strconv.FormatBool(b))
255 }
256
257 // ExtractClientID extracts the client identifier from the x-ms-client-request-id header set on the
258 // http.Request sent to the service (and returned in the http.Response)
259 func ExtractClientID(resp *http.Response) string {
260         return autorest.ExtractHeaderValue(HeaderClientID, resp)
261 }
262
263 // ExtractRequestID extracts the Azure server generated request identifier from the
264 // x-ms-request-id header.
265 func ExtractRequestID(resp *http.Response) string {
266         return autorest.ExtractHeaderValue(HeaderRequestID, resp)
267 }
268
269 // WithErrorUnlessStatusCode returns a RespondDecorator that emits an
270 // azure.RequestError by reading the response body unless the response HTTP status code
271 // is among the set passed.
272 //
273 // If there is a chance service may return responses other than the Azure error
274 // format and the response cannot be parsed into an error, a decoding error will
275 // be returned containing the response body. In any case, the Responder will
276 // return an error if the status code is not satisfied.
277 //
278 // If this Responder returns an error, the response body will be replaced with
279 // an in-memory reader, which needs no further closing.
280 func WithErrorUnlessStatusCode(codes ...int) autorest.RespondDecorator {
281         return func(r autorest.Responder) autorest.Responder {
282                 return autorest.ResponderFunc(func(resp *http.Response) error {
283                         err := r.Respond(resp)
284                         if err == nil && !autorest.ResponseHasStatusCode(resp, codes...) {
285                                 var e RequestError
286                                 defer resp.Body.Close()
287
288                                 // Copy and replace the Body in case it does not contain an error object.
289                                 // This will leave the Body available to the caller.
290                                 b, decodeErr := autorest.CopyAndDecode(autorest.EncodedAsJSON, resp.Body, &e)
291                                 resp.Body = ioutil.NopCloser(&b)
292                                 if decodeErr != nil {
293                                         return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b.String(), decodeErr)
294                                 }
295                                 if e.ServiceError == nil {
296                                         // Check if error is unwrapped ServiceError
297                                         if err := json.Unmarshal(b.Bytes(), &e.ServiceError); err != nil {
298                                                 return err
299                                         }
300                                 }
301                                 if e.ServiceError.Message == "" {
302                                         // if we're here it means the returned error wasn't OData v4 compliant.
303                                         // try to unmarshal the body as raw JSON in hopes of getting something.
304                                         rawBody := map[string]interface{}{}
305                                         if err := json.Unmarshal(b.Bytes(), &rawBody); err != nil {
306                                                 return err
307                                         }
308                                         e.ServiceError = &ServiceError{
309                                                 Code:    "Unknown",
310                                                 Message: "Unknown service error",
311                                         }
312                                         if len(rawBody) > 0 {
313                                                 e.ServiceError.Details = []map[string]interface{}{rawBody}
314                                         }
315                                 }
316                                 e.Response = resp
317                                 e.RequestID = ExtractRequestID(resp)
318                                 if e.StatusCode == nil {
319                                         e.StatusCode = resp.StatusCode
320                                 }
321                                 err = &e
322                         }
323                         return err
324                 })
325         }
326 }