Skip to content

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
  • Loading branch information
teowa committed Sep 15, 2023
1 parent 82a9520 commit 26030d7
Show file tree
Hide file tree
Showing 12 changed files with 86 additions and 52 deletions.
1 change: 1 addition & 0 deletions internal/services/appconfiguration/app_configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ func resourceConfigurationStoreReplicaHash(input interface{}) int {
var buf bytes.Buffer
if rawData, ok := input.(map[string]interface{}); ok {
buf.WriteString(rawData["name"].(string))
buf.WriteString(rawData["location"].(string))
}
return pluginsdk.HashString(buf.String())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,8 @@ func dataSourceAppConfiguration() *pluginsdk.Resource {
},

"replica": {
Type: pluginsdk.TypeSet,
Type: pluginsdk.TypeList,
Computed: true,
Set: resourceConfigurationStoreReplicaHash,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"name": {
Expand Down
65 changes: 31 additions & 34 deletions internal/services/appconfiguration/app_configuration_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"github.com/hashicorp/go-azure-sdk/resource-manager/appconfiguration/2023-03-01/replicas"
"github.com/hashicorp/go-azure-sdk/sdk/client"
"github.com/hashicorp/go-azure-sdk/sdk/client/pollers"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-azurerm/helpers/tf"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/appconfiguration/validate"
Expand Down Expand Up @@ -145,13 +144,7 @@ func resourceAppConfiguration() *pluginsdk.Resource {
Required: true,
ValidateFunc: validate.ConfigurationStoreReplicaName,
},
"location": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: location.EnhancedValidate,
StateFunc: location.StateFunc,
DiffSuppressFunc: location.DiffSuppressFunc,
},
"location": commonschema.LocationWithoutForceNew(),
"endpoint": {
Type: pluginsdk.TypeString,
Computed: true,
Expand Down Expand Up @@ -357,21 +350,14 @@ func resourceAppConfigurationCreate(d *pluginsdk.ResourceData, meta interface{})
}
meta.(*clients.Client).AppConfiguration.AddToCache(resourceId, *resp.Model.Properties.Endpoint)

expandedReplicas, err := expandAppConfigurationReplicas(d.Get("replica").(*schema.Set).List(), name, location)
expandedReplicas, err := expandAppConfigurationReplicas(d.Get("replica").(*pluginsdk.Set).List(), name, location)
if err != nil {
return fmt.Errorf("expanding `replica`: %+v", err)
}

replicaClient := meta.(*clients.Client).AppConfiguration.ReplicasClient
for _, replica := range expandedReplicas {
replicaId := replicas.NewReplicaID(subscriptionId, resourceGroup, name, *replica.Name)

existingReplica, err := replicaClient.Get(ctx, replicaId)
if err != nil {
if !response.WasNotFound(existingReplica.HttpResponse) {
return fmt.Errorf("retrieving %s: %+v", replicaId, err)
}
}
for _, replica := range *expandedReplicas {
replicaId := replicas.NewReplicaID(resourceId.SubscriptionId, resourceId.ResourceGroupName, resourceId.ConfigurationStoreName, *replica.Name)

if err := replicaClient.CreateThenPoll(ctx, replicaId, replica); err != nil {
return fmt.Errorf("creating %s: %+v", replicaId, err)
Expand Down Expand Up @@ -494,6 +480,7 @@ func resourceAppConfigurationUpdate(d *pluginsdk.ResourceData, meta interface{})

// check if a replica has been removed from config and if so, delete it
deleteReplicaIds := make([]replicas.ReplicaId, 0)
unchangedReplicaNames := make(map[string]struct{}, 0)
oldReplicas, newReplicas := d.GetChange("replica")
for _, oldReplica := range oldReplicas.(*pluginsdk.Set).List() {
isRemoved := true
Expand All @@ -502,7 +489,8 @@ func resourceAppConfigurationUpdate(d *pluginsdk.ResourceData, meta interface{})
for _, newReplica := range newReplicas.(*pluginsdk.Set).List() {
newReplicaMap := newReplica.(map[string]interface{})

if strings.EqualFold(oldReplicaMap["name"].(string), newReplicaMap["name"].(string)) && strings.EqualFold(oldReplicaMap["location"].(string), newReplicaMap["location"].(string)) {
if strings.EqualFold(oldReplicaMap["name"].(string), newReplicaMap["name"].(string)) && strings.EqualFold(location.Normalize(oldReplicaMap["location"].(string)), location.Normalize(newReplicaMap["location"].(string))) {
unchangedReplicaNames[oldReplicaMap["name"].(string)] = struct{}{}
isRemoved = false
break
}
Expand All @@ -517,13 +505,17 @@ func resourceAppConfigurationUpdate(d *pluginsdk.ResourceData, meta interface{})
return err
}

expandedReplicas, err := expandAppConfigurationReplicas(d.Get("replica").(*schema.Set).List(), id.ConfigurationStoreName, location.Normalize(existing.Model.Location))
expandedReplicas, err := expandAppConfigurationReplicas(d.Get("replica").(*pluginsdk.Set).List(), id.ConfigurationStoreName, location.Normalize(existing.Model.Location))
if err != nil {
return fmt.Errorf("expanding `replica`: %+v", err)
}

// check if a replica has been added or an existing one changed its location, (re)create it
for _, replica := range expandedReplicas {
for _, replica := range *expandedReplicas {
if _, isUnchanged := unchangedReplicaNames[*replica.Name]; isUnchanged {
continue
}

replicaId := replicas.NewReplicaID(id.SubscriptionId, id.ResourceGroupName, id.ConfigurationStoreName, *replica.Name)

existingReplica, err := replicaClient.Get(ctx, replicaId)
Expand All @@ -534,7 +526,7 @@ func resourceAppConfigurationUpdate(d *pluginsdk.ResourceData, meta interface{})
}

if !response.WasNotFound(existingReplica.HttpResponse) {
continue
return fmt.Errorf("updating %s: replica %s already exists", *id, replicaId)
}

if err = replicaClient.CreateThenPoll(ctx, replicaId, replica); err != nil {
Expand Down Expand Up @@ -786,30 +778,35 @@ func expandAppConfigurationEncryption(input []interface{}) *configurationstores.
return result
}

func expandAppConfigurationReplicas(input []interface{}, configurationStoreName, configurationStoreLocation string) ([]replicas.Replica, error) {
func expandAppConfigurationReplicas(input []interface{}, configurationStoreName, configurationStoreLocation string) (*[]replicas.Replica, error) {
result := make([]replicas.Replica, 0)

locationSet := map[string]interface{}{
configurationStoreLocation: nil,
}
replicaNameSet := map[string]interface{}{}
// check if there are duplicated replica names or locations
// location cannot be same as original configuration store and other replicas
locationSet := make(map[string]string, 0)
replicaNameSet := make(map[string]struct{}, 0)

for _, v := range input {
replica := v.(map[string]interface{})
replicaName := replica["name"].(string)
replicaLocation := location.Normalize(replica["location"].(string))
if strings.EqualFold(replicaLocation, configurationStoreLocation) {
return nil, fmt.Errorf("location (%q) of replica %q is duplicated with original configuration store %q", replicaName, replicaLocation, configurationStoreName)
}

if _, ok := locationSet[replicaLocation]; ok {
return result, fmt.Errorf("replica location %q is duplicated in configuration store %q location", replicaLocation, configurationStoreName)
if name, ok := locationSet[replicaLocation]; ok {
return nil, fmt.Errorf("location (%q) of replica %q is duplicated with replica %q", replicaName, replicaLocation, name)
}
locationSet[replicaLocation] = nil
locationSet[replicaLocation] = replicaName

if _, ok := replicaNameSet[replicaName]; ok {
return result, fmt.Errorf("replica name %q is duplicated", replicaName)
normalizedReplicaName := strings.ToLower(replicaName)
if _, ok := replicaNameSet[normalizedReplicaName]; ok {
return nil, fmt.Errorf("replica name %q is duplicated", replicaName)
}
replicaNameSet[normalizedReplicaName] = struct{}{}

if len(replicaName)+len(configurationStoreName) > 60 {
return result, fmt.Errorf("replica name %q is too long, the total length of replica name and configuration store name should be greater than 60", replicaName)
return nil, fmt.Errorf("replica name %q is too long, the total length of replica name and configuration store name should be greater than 60", replicaName)
}

result = append(result, replicas.Replica{
Expand All @@ -818,7 +815,7 @@ func expandAppConfigurationReplicas(input []interface{}, configurationStoreName,
})
}

return result, nil
return &result, nil
}

func flattenAppConfigurationAccessKeys(values []configurationstores.ApiKey) flattenedAccessKeys {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,13 @@ func TestAccAppConfiguration_replicaUpdate(t *testing.T) {
),
},
data.ImportStep(),
{
Config: r.replicaUpdatedPartial(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
{
Config: r.complete(data),
Check: acceptance.ComposeTestCheckFunc(
Expand Down Expand Up @@ -547,6 +554,36 @@ resource "azurerm_app_configuration" "test" {
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.Locations.Ternary, data.Locations.Secondary)
}

func (AppConfigurationResource) replicaUpdatedPartial(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "test" {
name = "acctestRG-appconfig-%d"
location = "%s"
}
resource "azurerm_app_configuration" "test" {
name = "testaccappconf%d"
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
sku = "standard"
replica {
name = "replica3"
location = "%s"
}
replica {
name = "replica2"
location = "%s"
}
}
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.Locations.Ternary, data.Locations.Secondary)
}

func (r AppConfigurationResource) requiresImport(data acceptance.TestData) string {
template := r.standard(data)
return fmt.Sprintf(`
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.

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.

2 changes: 1 addition & 1 deletion website/docs/d/app_configuration.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ A `replica` block exports the following:

* `location` - The supported Azure location where the replica exists.

* `name` - (Optional) The name of the replica.
* `name` - The name of the replica.


---
Expand Down
8 changes: 4 additions & 4 deletions website/docs/r/app_configuration.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,9 @@ An `identity` block supports the following:

A `replica` block supports the following:

* `location` - (Required) Specifies the supported Azure location where the replica exists.
* `location` - (Required) Specifies the supported Azure location where the replica exists. Changing this forces a new replica to be created.

* `name` - (Optional) Specifies the name of the replica. Changing this forces a new replica to be created.
* `name` - (Required) Specifies the name of the replica. Changing this forces a new replica to be created.

---

Expand Down Expand Up @@ -237,9 +237,9 @@ An `identity` block exports the following:

A `replica` block exports the following:

* `id` - The replica ID.
* `id` - The ID of the App Configuration Replica.

* `endpoint` - The URL of the replica.
* `endpoint` - The URL of the App Configuration Replica.

---

Expand Down

0 comments on commit 26030d7

Please sign in to comment.