Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azurerm_app_configuration - retry checkNameAvailability to make sure delete is finished, add stateWait to confirm key/feature is created #21750

Merged
merged 12 commits into from
Jun 1, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import (

"github.com/hashicorp/go-azure-helpers/lang/pointer"
"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-helpers/resourcemanager/identity"
"github.com/hashicorp/go-azure-helpers/resourcemanager/location"
"github.com/hashicorp/go-azure-helpers/resourcemanager/tags"
"github.com/hashicorp/go-azure-sdk/resource-manager/appconfiguration/2023-03-01/configurationstores"
"github.com/hashicorp/go-azure-sdk/resource-manager/appconfiguration/2023-03-01/deletedconfigurationstores"
"github.com/hashicorp/go-azure-sdk/resource-manager/appconfiguration/2023-03-01/operations"
"github.com/hashicorp/go-azure-sdk/sdk/client"
"github.com/hashicorp/go-azure-sdk/sdk/client/pollers"
"github.com/hashicorp/terraform-provider-azurerm/helpers/azure"
Expand Down Expand Up @@ -231,6 +233,7 @@ func resourceAppConfiguration() *pluginsdk.Resource {
func resourceAppConfigurationCreate(d *pluginsdk.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).AppConfiguration.ConfigurationStoresClient
deletedConfigurationStoresClient := meta.(*clients.Client).AppConfiguration.DeletedConfigurationStoresClient

subscriptionId := meta.(*clients.Client).Account.SubscriptionId
ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d)
defer cancel()
Expand Down Expand Up @@ -300,7 +303,13 @@ func resourceAppConfigurationCreate(d *pluginsdk.ResourceData, meta interface{})
return fmt.Errorf("expanding `identity`: %+v", err)
}
parameters.Identity = identity
// TODO: retry checkNameAvailability before creation when SDK is ready, see https://github.com/Azure/AppConfiguration/issues/677

// retry checkNameAvailability until the name is released by purged app configuration, see https://github.com/Azure/AppConfiguration/issues/677
operationsClient := meta.(*clients.Client).AppConfiguration.OperationsClient
if err = resourceConfigurationStoreWaitForNameAvailable(ctx, operationsClient, resourceId); err != nil {
return err
}

if err := client.CreateThenPoll(ctx, resourceId, parameters); err != nil {
return fmt.Errorf("creating %s: %+v", resourceId, err)
}
Expand Down Expand Up @@ -571,7 +580,11 @@ func resourceAppConfigurationDelete(d *pluginsdk.ResourceData, meta interface{})
return fmt.Errorf("polling after purging for %s: %+v", *id, err)
}

// TODO: retry checkNameAvailability after deletion when SDK is ready, see https://github.com/Azure/AppConfiguration/issues/677
// retry checkNameAvailability until the name is released by purged app configuration, see https://github.com/Azure/AppConfiguration/issues/677
operationsClient := meta.(*clients.Client).AppConfiguration.OperationsClient
if err = resourceConfigurationStoreWaitForNameAvailable(ctx, operationsClient, *id); err != nil {
return err
}
log.Printf("[DEBUG] Purged AppConfiguration %q.", id.ConfigurationStoreName)
}

Expand Down Expand Up @@ -732,3 +745,57 @@ func parsePublicNetworkAccess(input string) *configurationstores.PublicNetworkAc
out := configurationstores.PublicNetworkAccess(input)
return &out
}

func resourceConfigurationStoreWaitForNameAvailable(ctx context.Context, client *operations.OperationsClient, configurationStoreId configurationstores.ConfigurationStoreId) error {
deadline, ok := ctx.Deadline()
if !ok {
return fmt.Errorf("internal error: context had no deadline")
}
state := &pluginsdk.StateChangeConf{
MinTimeout: 10 * time.Second,
ContinuousTargetOccurence: 2,
Pending: []string{"Unavailable"},
Target: []string{"Available"},
Refresh: resourceConfigurationStoreNameAvailabilityRefreshFunc(ctx, client, configurationStoreId),
Timeout: time.Until(deadline),
}

_, err := state.WaitForStateContext(ctx)
if err != nil {
return fmt.Errorf("waiting for the Configuration Store %s Name Available: %+v", configurationStoreId, err)
}

return nil

}

func resourceConfigurationStoreNameAvailabilityRefreshFunc(ctx context.Context, client *operations.OperationsClient, configurationStoreId configurationstores.ConfigurationStoreId) pluginsdk.StateRefreshFunc {
return func() (interface{}, string, error) {
log.Printf("[DEBUG] Checking to see if Configuration Store %s is name available ..", configurationStoreId)

subscriptionId := commonids.NewSubscriptionID(configurationStoreId.SubscriptionId)

parameters := operations.CheckNameAvailabilityParameters{
Name: configurationStoreId.ConfigurationStoreName,
Type: operations.ConfigurationResourceTypeMicrosoftPointAppConfigurationConfigurationStores,
}

resp, err := client.CheckNameAvailability(ctx, subscriptionId, parameters)
if err != nil {
return resp, "Error", fmt.Errorf("retrieving Deployment: %+v", err)
}

if resp.Model == nil {
return resp, "Error", fmt.Errorf("unexpected null model of %s", configurationStoreId)
}

if resp.Model.NameAvailable == nil {
return resp, "Error", fmt.Errorf("unexpected null NameAvailable property of %s", configurationStoreId)
}

if !*resp.Model.NameAvailable {
return resp, "Unavailable", nil
}
return resp, "Available", nil
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,6 @@ func TestAccAppConfiguration_softDeleteRecoveryDisabled(t *testing.T) {
})
}

// This test may fail due to service API behaviour
// TODO: retry checkNameAvailability to fix this test when SDK is ready, see https://github.com/Azure/AppConfiguration/issues/677
func TestAccAppConfiguration_softDeletePurgeThenRecreate(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_app_configuration", "test")
r := AppConfigurationResource{}
Expand Down
9 changes: 9 additions & 0 deletions internal/services/appconfiguration/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-sdk/resource-manager/appconfiguration/2023-03-01/configurationstores"
"github.com/hashicorp/go-azure-sdk/resource-manager/appconfiguration/2023-03-01/deletedconfigurationstores"
"github.com/hashicorp/go-azure-sdk/resource-manager/appconfiguration/2023-03-01/operations"
authWrapper "github.com/hashicorp/go-azure-sdk/sdk/auth/autorest"
"github.com/hashicorp/go-azure-sdk/sdk/environments"
"github.com/hashicorp/terraform-provider-azurerm/internal/common"
Expand All @@ -18,6 +19,7 @@ import (
type Client struct {
ConfigurationStoresClient *configurationstores.ConfigurationStoresClient
DeletedConfigurationStoresClient *deletedconfigurationstores.DeletedConfigurationStoresClient
OperationsClient *operations.OperationsClient
authorizerFunc common.ApiAuthorizerFunc
configureClientFunc func(c *autorest.Client, authorizer autorest.Authorizer)
}
Expand Down Expand Up @@ -129,9 +131,16 @@ func NewClient(o *common.ClientOptions) (*Client, error) {
}
o.Configure(deletedConfigurationStores.Client, o.Authorizers.ResourceManager)

operationsClient, err := operations.NewOperationsClientWithBaseURI(o.Environment.ResourceManager)
if err != nil {
return nil, fmt.Errorf("building Operations client: %+v", err)
}
o.Configure(operationsClient.Client, o.Authorizers.ResourceManager)

return &Client{
ConfigurationStoresClient: configurationStores,
DeletedConfigurationStoresClient: deletedConfigurationStores,
OperationsClient: operationsClient,
authorizerFunc: o.Authorizers.AuthorizerFunc,
configureClientFunc: o.ConfigureClient,
}, nil
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading