1
2
3
4
5
6
7
8
9
10
11
12
13
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
29
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
51 var re RequestError
52 if strings.Contains(r.Header.Get("Content-Type"), "xml") {
53
54 err = autorest.Respond(resp, autorest.ByUnmarshallingXML(&re.ServiceError))
55 } else {
56 err = autorest.Respond(resp, autorest.ByUnmarshallingJSON(&re))
57 }
58
59 if err != nil {
60 return resp, err
61 }
62 err = re
63
64 if re.ServiceError != nil && re.ServiceError.Code == "MissingSubscriptionRegistration" {
65 regErr := register(client, r, re)
66 if regErr != nil {
67 return resp, fmt.Errorf("failed auto registering Resource Provider: %s. Original error: %w", regErr, err)
68 }
69 }
70 }
71 return resp, err
72 })
73 }
74 }
75
76 func getProvider(re RequestError) (string, error) {
77 if re.ServiceError != nil && len(re.ServiceError.Details) > 0 {
78 return re.ServiceError.Details[0]["target"].(string), nil
79 }
80 return "", errors.New("provider was not found in the response")
81 }
82
83 func register(client autorest.Client, originalReq *http.Request, re RequestError) error {
84 subID := getSubscription(originalReq.URL.Path)
85 if subID == "" {
86 return errors.New("missing parameter subscriptionID to register resource provider")
87 }
88 providerName, err := getProvider(re)
89 if err != nil {
90 return fmt.Errorf("missing parameter provider to register resource provider: %s", err)
91 }
92 newURL := url.URL{
93 Scheme: originalReq.URL.Scheme,
94 Host: originalReq.URL.Host,
95 }
96
97
98
99
100
101 pathParameters := map[string]interface{}{
102 "resourceProviderNamespace": autorest.Encode("path", providerName),
103 "subscriptionId": autorest.Encode("path", subID),
104 }
105
106 const APIVersion = "2016-09-01"
107 queryParameters := map[string]interface{}{
108 "api-version": APIVersion,
109 }
110
111 preparer := autorest.CreatePreparer(
112 autorest.AsPost(),
113 autorest.WithBaseURL(newURL.String()),
114 autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/{resourceProviderNamespace}/register", pathParameters),
115 autorest.WithQueryParameters(queryParameters),
116 )
117
118 req, err := preparer.Prepare(&http.Request{})
119 if err != nil {
120 return err
121 }
122 req = req.WithContext(originalReq.Context())
123
124 resp, err := autorest.SendWithSender(client, req,
125 autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...),
126 )
127 if err != nil {
128 return err
129 }
130
131 type Provider struct {
132 RegistrationState *string `json:"registrationState,omitempty"`
133 }
134 var provider Provider
135
136 err = autorest.Respond(
137 resp,
138 WithErrorUnlessStatusCode(http.StatusOK),
139 autorest.ByUnmarshallingJSON(&provider),
140 autorest.ByClosing(),
141 )
142 if err != nil {
143 return err
144 }
145
146
147 registrationStartTime := time.Now()
148 for err == nil && (client.PollingDuration == 0 || (client.PollingDuration != 0 && time.Since(registrationStartTime) < client.PollingDuration)) {
149
150
151 preparer := autorest.CreatePreparer(
152 autorest.AsGet(),
153 autorest.WithBaseURL(newURL.String()),
154 autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/{resourceProviderNamespace}", pathParameters),
155 autorest.WithQueryParameters(queryParameters),
156 )
157 req, err = preparer.Prepare(&http.Request{})
158 if err != nil {
159 return err
160 }
161 req = req.WithContext(originalReq.Context())
162
163 resp, err := autorest.SendWithSender(client, req,
164 autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...),
165 )
166 if err != nil {
167 return err
168 }
169
170 err = autorest.Respond(
171 resp,
172 WithErrorUnlessStatusCode(http.StatusOK),
173 autorest.ByUnmarshallingJSON(&provider),
174 autorest.ByClosing(),
175 )
176 if err != nil {
177 return err
178 }
179
180 if provider.RegistrationState != nil &&
181 *provider.RegistrationState == "Registered" {
182 break
183 }
184
185 delayed := autorest.DelayWithRetryAfter(resp, originalReq.Context().Done())
186 if !delayed && !autorest.DelayForBackoff(client.PollingDelay, 0, originalReq.Context().Done()) {
187 return originalReq.Context().Err()
188 }
189 }
190 if client.PollingDuration != 0 && !(time.Since(registrationStartTime) < client.PollingDuration) {
191 return errors.New("polling for resource provider registration has exceeded the polling duration")
192 }
193 return err
194 }
195
196 func getSubscription(path string) string {
197 parts := strings.Split(path, "/")
198 for i, v := range parts {
199 if v == "subscriptions" && (i+1) < len(parts) {
200 return parts[i+1]
201 }
202 }
203 return ""
204 }
205
View as plain text