Skip to content

Commit

Permalink
sdk/client/resourcemanager: supporting 202's with a self reference
Browse files Browse the repository at this point in the history
Most of API Management returns a 202 with a self reference containing an `asynctoken`, as such if a self-reference
exists then we can use this URI directly.

Fixes:

```
Error: creating/updating Api (Subscription: "*******"
        Resource Group Name: "acctestRG-230803055317928116"
        Service Name: "acctestAM-230803055317928116"
        Api: "acctestAMA-230803055317928116"): performing CreateOrUpdate: no applicable pollers were found for the response
```

From hashicorp/terraform-provider-azurerm#22783
  • Loading branch information
tombuildsstuff committed Aug 3, 2023
1 parent 0526c2a commit 65d33ef
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 8 deletions.
4 changes: 2 additions & 2 deletions sdk/client/resourcemanager/poller.go
Expand Up @@ -40,13 +40,13 @@ func PollerFromResponse(response *client.Response, client *Client) (poller polle
contentType = response.Request.Header.Get("Content-Type")
}

statusCodesToCheckProvisioningState := response.StatusCode == http.StatusOK || response.StatusCode == http.StatusCreated
statusCodesToCheckProvisioningState := response.StatusCode == http.StatusOK || response.StatusCode == http.StatusCreated || (response.StatusCode == http.StatusAccepted && lroIsSelfReference)
contentTypeMatchesForProvisioningStateCheck := strings.Contains(strings.ToLower(contentType), "application/json")
methodIsApplicable := strings.EqualFold(response.Request.Method, "PATCH") ||
strings.EqualFold(response.Request.Method, "POST") ||
strings.EqualFold(response.Request.Method, "PUT")
if statusCodesToCheckProvisioningState && contentTypeMatchesForProvisioningStateCheck && methodIsApplicable {
provisioningState, provisioningStateErr := provisioningStatePollerFromResponse(response, client, DefaultPollingInterval)
provisioningState, provisioningStateErr := provisioningStatePollerFromResponse(response, lroIsSelfReference, client, DefaultPollingInterval)
if provisioningStateErr != nil {
err = provisioningStateErr
return pollers.Poller{}, fmt.Errorf("building provisioningState poller: %+v", provisioningStateErr)
Expand Down
17 changes: 11 additions & 6 deletions sdk/client/resourcemanager/poller_provisioning_state.go
Expand Up @@ -29,35 +29,40 @@ type provisioningStatePoller struct {
resourcePath string
}

func provisioningStatePollerFromResponse(response *client.Response, client *Client, pollingInterval time.Duration) (*provisioningStatePoller, error) {
func provisioningStatePollerFromResponse(response *client.Response, lroIsSelfReference bool, client *Client, pollingInterval time.Duration) (*provisioningStatePoller, error) {
// if we've gotten to this point then we're polling against a Resource Manager resource/operation of some kind
// we next need to determine if the current URI is a Resource Manager resource, or if we should be polling on the
// resource (e.g. `/my/resource`) rather than an operation on the resource (e.g. `/my/resource/start`)
if response.Request == nil {
return nil, fmt.Errorf("request was nil")
}
originalUri := response.Request.URL.RequestURI()
if response.Request.URL == nil {
return nil, fmt.Errorf("request url was nil")
}
originalUri := response.Request.URL.RequestURI()

// all Resource Manager operations require the `api-version` querystring
apiVersion := response.Request.URL.Query().Get("api-version")
if apiVersion == "" {
return nil, fmt.Errorf("unable to determine `api-version` from %q", originalUri)
}

resourcePath, err := resourceManagerResourcePathFromUri(originalUri)
if err != nil {
return nil, fmt.Errorf("determining Resource Manager Resource Path from %q: %+v", originalUri, err)
resourcePath := originalUri
if !lroIsSelfReference {
// if it's a self-reference (such as API Management's API/API Schema)
path, err := resourceManagerResourcePathFromUri(originalUri)
if err != nil {
return nil, fmt.Errorf("determining Resource Manager Resource Path from %q: %+v", originalUri, err)
}
resourcePath = *path
}

return &provisioningStatePoller{
apiVersion: apiVersion,
client: client,
initialRetryDuration: pollingInterval,
originalUri: originalUri,
resourcePath: *resourcePath,
resourcePath: resourcePath,
}, nil
}

Expand Down
20 changes: 20 additions & 0 deletions sdk/client/resourcemanager/poller_test.go
Expand Up @@ -111,6 +111,26 @@ func TestNewPoller_LongRunningOperationWithSelfReference(t *testing.T) {
},
valid: true,
},
{
// Seen in API Management - API and API Schema
response: &client.Response{
Response: &http.Response{
StatusCode: http.StatusAccepted,
Header: http.Header{
http.CanonicalHeaderKey("Content-Type"): []string{"application/json"},
http.CanonicalHeaderKey("Location"): []string{"https://async-url-test.local/subscriptions/1234/providers/foo/operations/6789?api-version=2020-01-01&asyncId=abc123&asyncCode=201"},
},
Request: &http.Request{
Method: http.MethodPut,
URL: func() *url.URL {
u, _ := url.Parse("https://async-url-test.local/subscriptions/1234/providers/foo/operations/6789?api-version=2020-01-01")
return u
}(),
},
},
},
valid: true,
},
}

for _, v := range testData {
Expand Down

0 comments on commit 65d33ef

Please sign in to comment.