Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / github.com / Azure / go-autorest / autorest / azure / rp.go
1 // Copyright 2017 Microsoft Corporation
2 //
3 //  Licensed under the Apache License, Version 2.0 (the "License");
4 //  you may not use this file except in compliance with the License.
5 //  You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 //  Unless required by applicable law or agreed to in writing, software
10 //  distributed under the License is distributed on an "AS IS" BASIS,
11 //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 //  See the License for the specific language governing permissions and
13 //  limitations under the License.
14
15 package azure
16
17 import (
18         "errors"
19         "fmt"
20         "net/http"
21         "net/url"
22         "strings"
23         "time"
24
25         "github.com/Azure/go-autorest/autorest"
26 )
27
28 // DoRetryWithRegistration tries to register the resource provider in case it is unregistered.
29 // It also handles request retries
30 func DoRetryWithRegistration(client autorest.Client) autorest.SendDecorator {
31         return func(s autorest.Sender) autorest.Sender {
32                 return autorest.SenderFunc(func(r *http.Request) (resp *http.Response, err error) {
33                         rr := autorest.NewRetriableRequest(r)
34                         for currentAttempt := 0; currentAttempt < client.RetryAttempts; currentAttempt++ {
35                                 err = rr.Prepare()
36                                 if err != nil {
37                                         return resp, err
38                                 }
39
40                                 resp, err = autorest.SendWithSender(s, rr.Request(),
41                                         autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...),
42                                 )
43                                 if err != nil {
44                                         return resp, err
45                                 }
46
47                                 if resp.StatusCode != http.StatusConflict || client.SkipResourceProviderRegistration {
48                                         return resp, err
49                                 }
50                                 var re RequestError
51                                 err = autorest.Respond(
52                                         resp,
53                                         autorest.ByUnmarshallingJSON(&re),
54                                 )
55                                 if err != nil {
56                                         return resp, err
57                                 }
58                                 err = re
59
60                                 if re.ServiceError != nil && re.ServiceError.Code == "MissingSubscriptionRegistration" {
61                                         regErr := register(client, r, re)
62                                         if regErr != nil {
63                                                 return resp, fmt.Errorf("failed auto registering Resource Provider: %s. Original error: %s", regErr, err)
64                                         }
65                                 }
66                         }
67                         return resp, err
68                 })
69         }
70 }
71
72 func getProvider(re RequestError) (string, error) {
73         if re.ServiceError != nil && len(re.ServiceError.Details) > 0 {
74                 return re.ServiceError.Details[0]["target"].(string), nil
75         }
76         return "", errors.New("provider was not found in the response")
77 }
78
79 func register(client autorest.Client, originalReq *http.Request, re RequestError) error {
80         subID := getSubscription(originalReq.URL.Path)
81         if subID == "" {
82                 return errors.New("missing parameter subscriptionID to register resource provider")
83         }
84         providerName, err := getProvider(re)
85         if err != nil {
86                 return fmt.Errorf("missing parameter provider to register resource provider: %s", err)
87         }
88         newURL := url.URL{
89                 Scheme: originalReq.URL.Scheme,
90                 Host:   originalReq.URL.Host,
91         }
92
93         // taken from the resources SDK
94         // with almost identical code, this sections are easier to mantain
95         // It is also not a good idea to import the SDK here
96         // https://github.com/Azure/azure-sdk-for-go/blob/9f366792afa3e0ddaecdc860e793ba9d75e76c27/arm/resources/resources/providers.go#L252
97         pathParameters := map[string]interface{}{
98                 "resourceProviderNamespace": autorest.Encode("path", providerName),
99                 "subscriptionId":            autorest.Encode("path", subID),
100         }
101
102         const APIVersion = "2016-09-01"
103         queryParameters := map[string]interface{}{
104                 "api-version": APIVersion,
105         }
106
107         preparer := autorest.CreatePreparer(
108                 autorest.AsPost(),
109                 autorest.WithBaseURL(newURL.String()),
110                 autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/{resourceProviderNamespace}/register", pathParameters),
111                 autorest.WithQueryParameters(queryParameters),
112         )
113
114         req, err := preparer.Prepare(&http.Request{})
115         if err != nil {
116                 return err
117         }
118         req = req.WithContext(originalReq.Context())
119
120         resp, err := autorest.SendWithSender(client, req,
121                 autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...),
122         )
123         if err != nil {
124                 return err
125         }
126
127         type Provider struct {
128                 RegistrationState *string `json:"registrationState,omitempty"`
129         }
130         var provider Provider
131
132         err = autorest.Respond(
133                 resp,
134                 WithErrorUnlessStatusCode(http.StatusOK),
135                 autorest.ByUnmarshallingJSON(&provider),
136                 autorest.ByClosing(),
137         )
138         if err != nil {
139                 return err
140         }
141
142         // poll for registered provisioning state
143         registrationStartTime := time.Now()
144         for err == nil && (client.PollingDuration == 0 || (client.PollingDuration != 0 && time.Since(registrationStartTime) < client.PollingDuration)) {
145                 // taken from the resources SDK
146                 // https://github.com/Azure/azure-sdk-for-go/blob/9f366792afa3e0ddaecdc860e793ba9d75e76c27/arm/resources/resources/providers.go#L45
147                 preparer := autorest.CreatePreparer(
148                         autorest.AsGet(),
149                         autorest.WithBaseURL(newURL.String()),
150                         autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/{resourceProviderNamespace}", pathParameters),
151                         autorest.WithQueryParameters(queryParameters),
152                 )
153                 req, err = preparer.Prepare(&http.Request{})
154                 if err != nil {
155                         return err
156                 }
157                 req = req.WithContext(originalReq.Context())
158
159                 resp, err := autorest.SendWithSender(client, req,
160                         autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...),
161                 )
162                 if err != nil {
163                         return err
164                 }
165
166                 err = autorest.Respond(
167                         resp,
168                         WithErrorUnlessStatusCode(http.StatusOK),
169                         autorest.ByUnmarshallingJSON(&provider),
170                         autorest.ByClosing(),
171                 )
172                 if err != nil {
173                         return err
174                 }
175
176                 if provider.RegistrationState != nil &&
177                         *provider.RegistrationState == "Registered" {
178                         break
179                 }
180
181                 delayed := autorest.DelayWithRetryAfter(resp, originalReq.Context().Done())
182                 if !delayed && !autorest.DelayForBackoff(client.PollingDelay, 0, originalReq.Context().Done()) {
183                         return originalReq.Context().Err()
184                 }
185         }
186         if client.PollingDuration != 0 && !(time.Since(registrationStartTime) < client.PollingDuration) {
187                 return errors.New("polling for resource provider registration has exceeded the polling duration")
188         }
189         return err
190 }
191
192 func getSubscription(path string) string {
193         parts := strings.Split(path, "/")
194         for i, v := range parts {
195                 if v == "subscriptions" && (i+1) < len(parts) {
196                         return parts[i+1]
197                 }
198         }
199         return ""
200 }