PageRenderTime 54ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/github.com/Azure/go-autorest/autorest/azure/azure.go

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