Skip to content

Commit

Permalink
pollers: accounting for dropped connections
Browse files Browse the repository at this point in the history
It's possible that the connection can be dropped whilst polling, if this is the case then
a nil *http.HttpResponse will be returned (since there's no response) - as such we need
to use the `response.WasNotFound` (and similar) methods when checking the HTTP Status Code
which take dropped connections into account
  • Loading branch information
tombuildsstuff committed Feb 27, 2023
1 parent 9d37089 commit f5eb223
Show file tree
Hide file tree
Showing 18 changed files with 124 additions and 68 deletions.
8 changes: 4 additions & 4 deletions internal/services/aadb2c/aadb2c_directory_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package aadb2c
import (
"context"
"fmt"
"net/http"
"time"

"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonids"
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema"
"github.com/hashicorp/go-azure-sdk/resource-manager/aadb2c/2021-04-01-preview/tenants"
Expand Down Expand Up @@ -151,11 +151,11 @@ func (r AadB2cDirectoryResource) Create() sdk.ResourceFunc {

metadata.Logger.Infof("Import check for %s", id)
existing, err := client.Get(ctx, id)
if err != nil && existing.HttpResponse.StatusCode != http.StatusNotFound {
if err != nil && !response.WasNotFound(existing.HttpResponse) {
return fmt.Errorf("checking for presence of existing %s: %+v", id, err)
}

if existing.Model != nil && existing.Model.Id != nil && *existing.Model.Id != "" {
if !response.WasNotFound(existing.HttpResponse) {
return metadata.ResourceRequiresImport(r.ResourceType(), id)
}

Expand Down Expand Up @@ -256,7 +256,7 @@ func (r AadB2cDirectoryResource) Read() sdk.ResourceFunc {
metadata.Logger.Infof("Reading %s", id)
resp, err := client.Get(ctx, *id)
if err != nil {
if resp.HttpResponse.StatusCode == http.StatusNotFound {
if response.WasNotFound(resp.HttpResponse) {
return metadata.MarkAsGone(id)
}
return fmt.Errorf("retrieving %s: %v", id, err)
Expand Down
2 changes: 1 addition & 1 deletion internal/services/containerapps/container_app_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ func (r ContainerAppResource) Update() sdk.ResourceFunc {
// Delta-updates need the secrets back from the list API, or we'll end up removing them or erroring out.
secretsResp, err := client.ListSecrets(ctx, *id)
if err != nil || secretsResp.Model == nil {
if secretsResp.HttpResponse == nil || secretsResp.HttpResponse.StatusCode != http.StatusNoContent {
if !response.WasStatusCode(secretsResp.HttpResponse, http.StatusNoContent) {
return fmt.Errorf("retrieving secrets for update for %s: %+v", *id, err)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ package containers_test
import (
"context"
"fmt"
"net/http"
"regexp"
"strings"
"testing"

"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-sdk/resource-manager/containerservice/2022-09-02-preview/agentpools"
"github.com/hashicorp/go-azure-sdk/resource-manager/containerservice/2022-09-02-preview/managedclusters"
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance"
Expand Down Expand Up @@ -980,7 +980,7 @@ func (KubernetesClusterNodePoolResource) scaleNodePool(nodeCount int) acceptance
return fmt.Errorf("Bad: Get on agentPoolsClient: %+v", err)
}

if nodePool.HttpResponse.StatusCode == http.StatusNotFound {
if response.WasNotFound(nodePool.HttpResponse) {
return fmt.Errorf("Bad: Node Pool %q (Kubernetes Cluster %q / Resource Group: %q) does not exist", nodePoolName, clusterName, resourceGroup)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package containers_test
import (
"context"
"fmt"
"net/http"
"testing"

"github.com/hashicorp/go-azure-helpers/lang/response"

"github.com/hashicorp/go-azure-sdk/resource-manager/containerservice/2022-09-02-preview/agentpools"
"github.com/hashicorp/go-azure-sdk/resource-manager/containerservice/2022-09-02-preview/managedclusters"
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance"
Expand Down Expand Up @@ -222,7 +223,7 @@ func (KubernetesClusterResource) updateDefaultNodePoolAgentCount(nodeCount int)
return fmt.Errorf("Bad: Get on agentPoolsClient: %+v", err)
}

if nodePool.HttpResponse.StatusCode == http.StatusNotFound {
if response.WasNotFound(nodePool.HttpResponse) {
return fmt.Errorf("Bad: Node Pool %q (Kubernetes Cluster %q / Resource Group: %q) does not exist", nodePoolName, clusterName, resourceGroup)
}

Expand Down
9 changes: 2 additions & 7 deletions internal/services/eventhub/eventhub_cluster_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package eventhub
import (
"fmt"
"log"
"net/http"
"regexp"
"strings"
"time"
Expand Down Expand Up @@ -152,19 +153,13 @@ func resourceEventHubClusterDelete(d *pluginsdk.ResourceData, meta interface{})
return pluginsdk.Retry(d.Timeout(pluginsdk.TimeoutDelete), func() *pluginsdk.RetryError {
future, err := client.ClustersDelete(ctx, *id)
if err != nil {
if response.WasNotFound(future.HttpResponse) {
return nil
}
if strings.Contains(err.Error(), "Cluster cannot be deleted until four hours after its creation time") || future.HttpResponse.StatusCode == 429 {
if strings.Contains(err.Error(), "Cluster cannot be deleted until four hours after its creation time") || response.WasStatusCode(future.HttpResponse, http.StatusTooManyRequests) {
return pluginsdk.RetryableError(fmt.Errorf("expected eventhub cluster to be deleted but was in pending creation state, retrying"))
}
return pluginsdk.NonRetryableError(fmt.Errorf("deleting %s: %+v", *id, err))
}

if err := future.Poller.PollUntilDone(); err != nil {
if response.WasNotFound(future.Poller.HttpResponse) {
return nil
}
return pluginsdk.NonRetryableError(fmt.Errorf("deleting %s: %+v", *id, err))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,12 +252,18 @@ func resourceEventHubNamespaceDisasterRecoveryConfigDelete(d *pluginsdk.Resource
resp, err := client.Get(ctx, *id)
if err != nil {
if response.WasNotFound(resp.HttpResponse) {
return resp, strconv.Itoa(resp.HttpResponse.StatusCode), nil
return resp, "404", nil
}
return nil, "nil", fmt.Errorf("polling to check the deletion state for %s: %+v", *id, err)
}

return resp, strconv.Itoa(resp.HttpResponse.StatusCode), nil
// if resp.HttpResponse is nil it's a dropped connection, which is normally checked
// via `response.WasNotFound` however since we want the status code here for the poller
status := "dropped connection"
if resp.HttpResponse != nil {
status = strconv.Itoa(resp.HttpResponse.StatusCode)
}
return resp, status, nil
},
}

Expand All @@ -267,13 +273,17 @@ func resourceEventHubNamespaceDisasterRecoveryConfigDelete(d *pluginsdk.Resource

// it can take some time for the name to become available again
// this is mainly here to enable updating the resource in place
deadline, ok = ctx.Deadline()
if !ok {
return fmt.Errorf("context has no deadline")
}
parentNamespaceId := checknameavailabilitydisasterrecoveryconfigs.NewNamespaceID(id.SubscriptionId, id.ResourceGroupName, id.NamespaceName)
availabilityClient := meta.(*clients.Client).Eventhub.DisasterRecoveryNameAvailabilityClient
nameFreeWait := &pluginsdk.StateChangeConf{
Pending: []string{"NameInUse"},
Target: []string{"None"},
MinTimeout: 30 * time.Second,
Timeout: d.Timeout(pluginsdk.TimeoutDelete),
Timeout: time.Until(deadline),
Refresh: func() (interface{}, string, error) {
input := checknameavailabilitydisasterrecoveryconfigs.CheckNameAvailabilityParameter{
Name: id.DisasterRecoveryConfigName,
Expand All @@ -282,7 +292,9 @@ func resourceEventHubNamespaceDisasterRecoveryConfigDelete(d *pluginsdk.Resource
if err != nil {
return resp, "Error", fmt.Errorf("waiting for the name of %s to become free: %v", *id, err)
}
// TODO: new crash to handle here
if resp.Model == nil || resp.Model.Reason == nil {
return resp, "Error", fmt.Errorf("`model` or `model.Reason` was nil")
}
return resp, string(*resp.Model.Reason), nil
},
}
Expand Down
8 changes: 5 additions & 3 deletions internal/services/eventhub/eventhub_namespace_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -684,18 +684,20 @@ func waitForEventHubNamespaceToBeDeleted(ctx context.Context, client *namespaces
func eventHubNamespaceStateStatusCodeRefreshFunc(ctx context.Context, client *namespaces.NamespacesClient, id namespaces.NamespaceId) pluginsdk.StateRefreshFunc {
return func() (interface{}, string, error) {
res, err := client.Get(ctx, id)
status := "dropped connection"
if res.HttpResponse != nil {
log.Printf("Retrieving %s returned Status %d", id, res.HttpResponse.StatusCode)
status = strconv.Itoa(res.HttpResponse.StatusCode)
}
log.Printf("Retrieving %s returned Status %q", id, status)

if err != nil {
if response.WasNotFound(res.HttpResponse) {
return res, strconv.Itoa(res.HttpResponse.StatusCode), nil
return res, status, nil
}
return nil, "", fmt.Errorf("polling for the status of %s: %+v", id, err)
}

return res, strconv.Itoa(res.HttpResponse.StatusCode), nil
return res, status, nil
}
}

Expand Down
10 changes: 5 additions & 5 deletions internal/services/iothub/iothub_dps_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -412,20 +412,20 @@ func iothubdpsStateStatusCodeRefreshFunc(ctx context.Context, client *iotdpsreso
return func() (interface{}, string, error) {
res, err := client.Get(ctx, id)

statusCode := -1
statusCode := "dropped connection"
if res.HttpResponse != nil {
statusCode = res.HttpResponse.StatusCode
statusCode = strconv.Itoa(res.HttpResponse.StatusCode)
}
log.Printf("Retrieving IoT Device Provisioning Service %q returned Status %d", id, statusCode)
log.Printf("Retrieving IoT Device Provisioning Service %q returned Status %q", id, statusCode)

if err != nil {
if response.WasNotFound(res.HttpResponse) {
return res, strconv.Itoa(statusCode), nil
return res, statusCode, nil
}
return nil, "", fmt.Errorf("polling for the status of the IoT Device Provisioning Service %q: %+v", id, err)
}

return res, strconv.Itoa(statusCode), nil
return res, statusCode, nil
}
}

Expand Down
6 changes: 5 additions & 1 deletion internal/services/netapp/netapp_account_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,10 @@ func netappAccountStateRefreshFunc(ctx context.Context, client *netappaccounts.N
}
}

return res, strconv.Itoa(res.HttpResponse.StatusCode), nil
statusCode := "dropped connection"
if res.HttpResponse != nil {
statusCode = strconv.Itoa(res.HttpResponse.StatusCode)
}
return res, statusCode, nil
}
}
12 changes: 10 additions & 2 deletions internal/services/netapp/netapp_pool_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,11 @@ func netappPoolDeleteStateRefreshFunc(ctx context.Context, client *capacitypools
}
}

return res, strconv.Itoa(res.HttpResponse.StatusCode), nil
statusCode := "dropped connection"
if res.HttpResponse != nil {
statusCode = strconv.Itoa(res.HttpResponse.StatusCode)
}
return res, statusCode, nil
}
}

Expand Down Expand Up @@ -323,6 +327,10 @@ func netappPoolStateRefreshFunc(ctx context.Context, client *capacitypools.Capac
}
}

return res, strconv.Itoa(res.HttpResponse.StatusCode), nil
statusCode := "dropped connection"
if res.HttpResponse != nil {
statusCode = strconv.Itoa(res.HttpResponse.StatusCode)
}
return res, statusCode, nil
}
}
6 changes: 5 additions & 1 deletion internal/services/netapp/netapp_snapshot_policy_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,10 @@ func netappSnapshotPolicyStateRefreshFunc(ctx context.Context, client *snapshotp
}
}

return res, strconv.Itoa(res.HttpResponse.StatusCode), nil
statusCode := "dropped connection"
if res.HttpResponse != nil {
statusCode = strconv.Itoa(res.HttpResponse.StatusCode)
}
return res, statusCode, nil
}
}
6 changes: 5 additions & 1 deletion internal/services/netapp/netapp_snapshot_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ func netappSnapshotDeleteStateRefreshFunc(ctx context.Context, client *snapshots
}
}

return res, strconv.Itoa(res.HttpResponse.StatusCode), nil
statusCode := "dropped connection"
if res.HttpResponse != nil {
statusCode = strconv.Itoa(res.HttpResponse.StatusCode)
}
return res, statusCode, nil
}
}
25 changes: 15 additions & 10 deletions internal/services/netapp/netapp_volume_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -838,7 +838,11 @@ func netappVolumeStateRefreshFunc(ctx context.Context, client *volumes.VolumesCl
}
}

return res, strconv.Itoa(res.HttpResponse.StatusCode), nil
statusCode := "dropped connection"
if res.HttpResponse != nil {
statusCode = strconv.Itoa(res.HttpResponse.StatusCode)
}
return res, statusCode, nil
}
}

Expand Down Expand Up @@ -875,17 +879,18 @@ func netappVolumeReplicationStateRefreshFunc(ctx context.Context, client *volume
return func() (interface{}, string, error) {
res, err := client.VolumesReplicationStatus(ctx, id)
if err != nil {
if httpResponse := res.HttpResponse; httpResponse != nil {
if httpResponse.StatusCode == 400 && (strings.Contains(strings.ToLower(err.Error()), "deleting") || strings.Contains(strings.ToLower(err.Error()), "volume replication missing or deleted")) {
// This error can be ignored until a bug is fixed on RP side that it is returning 400 while the replication is in "Deleting" process
// TODO: remove this workaround when above bug is fixed
} else if !response.WasNotFound(httpResponse) {
return nil, "", fmt.Errorf("retrieving replication status from %s: %s", id, err)
}
if response.WasBadRequest(res.HttpResponse) && (strings.Contains(strings.ToLower(err.Error()), "deleting") || strings.Contains(strings.ToLower(err.Error()), "volume replication missing or deleted")) {
// This error can be ignored until a bug is fixed on RP side that it is returning 400 while the replication is in "Deleting" process
// TODO: remove this workaround when above bug is fixed
} else if !response.WasNotFound(res.HttpResponse) {
return nil, "", fmt.Errorf("retrieving replication status from %s: %s", id, err)
}
}

return res, strconv.Itoa(res.HttpResponse.StatusCode), nil
statusCode := "dropped connection"
if res.HttpResponse != nil {
statusCode = strconv.Itoa(res.HttpResponse.StatusCode)
}
return res, statusCode, nil
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,14 +224,19 @@ func resourceNotificationHubNamespaceDelete(d *pluginsdk.ResourceData, meta inte
func notificationHubNamespaceStateRefreshFunc(ctx context.Context, client *namespaces.NamespacesClient, id namespaces.NamespaceId) pluginsdk.StateRefreshFunc {
return func() (interface{}, string, error) {
resp, err := client.Get(ctx, id)
statusCode := "dropped connection"
if resp.HttpResponse != nil {
statusCode = strconv.Itoa(resp.HttpResponse.StatusCode)
}

if err != nil {
if response.WasNotFound(resp.HttpResponse) {
return nil, "404", nil
return nil, statusCode, nil
}

return nil, "", fmt.Errorf("retrieving %s: %+v", id, err)
}

return resp, strconv.Itoa(resp.HttpResponse.StatusCode), nil
return resp, statusCode, nil
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -209,15 +209,20 @@ func resourceNotificationHubCreateUpdate(d *pluginsdk.ResourceData, meta interfa
func notificationHubStateRefreshFunc(ctx context.Context, client *notificationhubs.NotificationHubsClient, id notificationhubs.NotificationHubId) pluginsdk.StateRefreshFunc {
return func() (interface{}, string, error) {
res, err := client.Get(ctx, id)
statusCode := "dropped connection"
if res.HttpResponse != nil {
statusCode = strconv.Itoa(res.HttpResponse.StatusCode)
}

if err != nil {
if response.WasNotFound(res.HttpResponse) {
return nil, "404", nil
return nil, statusCode, nil
}

return nil, "", fmt.Errorf("retrieving %s: %+v", id, err)
}

return res, strconv.Itoa(res.HttpResponse.StatusCode), nil
return res, statusCode, nil
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,19 @@ func (r LogAnalyticsWorkspaceOnboardResource) Create() sdk.ResourceFunc {
Target: []string{"200"},
Refresh: func() (interface{}, string, error) {
resp, err := client.Get(ctx, id)
statusCode := "dropped connection"
if resp.HttpResponse != nil {
statusCode = strconv.Itoa(resp.HttpResponse.StatusCode)
}

if err != nil {
if response.WasNotFound(resp.HttpResponse) {
return resp, "404", nil
return resp, statusCode, nil
}
return resp, "", err
}
if resp.HttpResponse != nil {
return resp, strconv.Itoa(resp.HttpResponse.StatusCode), nil
}
return resp, "", fmt.Errorf("http response is nil")

return resp, statusCode, nil
},
Timeout: time.Until(deadline),
Delay: 15 * time.Second,
Expand Down
Loading

0 comments on commit f5eb223

Please sign in to comment.