Skip to content

Commit

Permalink
support slash in name and label property of `azurerm_app_configur…
Browse files Browse the repository at this point in the history
…ation_feature`
  • Loading branch information
ziyeqf committed Nov 29, 2022
1 parent aca18d4 commit 230dd55
Show file tree
Hide file tree
Showing 6 changed files with 345 additions and 91 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import (
"context"
"encoding/json"
"fmt"
"net/url"
"time"

"github.com/Azure/go-autorest/autorest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-azurerm/helpers/azure"
"github.com/hashicorp/terraform-provider-azurerm/helpers/tf"
"github.com/hashicorp/terraform-provider-azurerm/internal/sdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/appconfiguration/migration"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/appconfiguration/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/appconfiguration/sdk/1.0/appconfiguration"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/appconfiguration/validate"
Expand Down Expand Up @@ -42,6 +44,15 @@ type FeatureResourceModel struct {
TargetingFilters []TargetingFilterAudience `tfschema:"targeting_filter"`
}

func (FeatureResource) StateUpgraders() sdk.StateUpgradeData {
return sdk.StateUpgradeData{
SchemaVersion: 1,
Upgraders: map[int]pluginsdk.StateUpgrade{
0: migration.AppConfigurationFeatureV0ToV1{},
},
}
}

func (k FeatureResource) Arguments() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
"configuration_store_id": {
Expand Down Expand Up @@ -175,8 +186,8 @@ func (k FeatureResource) Create() sdk.ResourceFunc {

appCfgFeatureResourceID := parse.AppConfigurationFeatureId{
ConfigurationStoreId: model.ConfigurationStoreId,
Name: model.Name,
Label: model.Label,
Name: url.QueryEscape(model.Name),
Label: url.QueryEscape(model.Label),
}

featureKey := fmt.Sprintf("%s/%s", FeatureKeyPrefix, model.Name)
Expand Down Expand Up @@ -216,7 +227,18 @@ func (k FeatureResource) Read() sdk.ResourceFunc {
if err != nil {
return fmt.Errorf("while parsing resource ID: %+v", err)
}
featureKey := fmt.Sprintf("%s/%s", FeatureKeyPrefix, resourceID.Name)

decodedName, err := url.QueryUnescape(resourceID.Name)
if err != nil {
return fmt.Errorf("while decoding name of resource ID: %+v", err)
}

decodedLabel, err := url.QueryUnescape(resourceID.Label)
if err != nil {
return fmt.Errorf("while decoding label of resource ID: %+v", err)
}

featureKey := fmt.Sprintf("%s/%s", FeatureKeyPrefix, decodedName)

// We set an empty label as %00 in the ID to make the ID validator happy
// but in reality the label is just an empty string
Expand All @@ -233,7 +255,7 @@ func (k FeatureResource) Read() sdk.ResourceFunc {
return err
}

kv, err := client.GetKeyValue(ctx, featureKey, resourceID.Label, "", "", "", []string{})
kv, err := client.GetKeyValue(ctx, featureKey, decodedLabel, "", "", "", []string{})
if err != nil {
if v, ok := err.(autorest.DetailedError); ok {
if utils.ResponseWasNotFound(autorest.Response{Response: v.Response}) {
Expand Down Expand Up @@ -294,7 +316,18 @@ func (k FeatureResource) Update() sdk.ResourceFunc {
if err != nil {
return fmt.Errorf("while parsing resource ID: %+v", err)
}
featureKey := fmt.Sprintf("%s/%s", FeatureKeyPrefix, resourceID.Name)

decodedName, err := url.QueryUnescape(resourceID.Name)
if err != nil {
return fmt.Errorf("while decoding name of resource ID: %+v", err)
}

decodedLabel, err := url.QueryUnescape(resourceID.Label)
if err != nil {
return fmt.Errorf("while decoding label of resource ID: %+v", err)
}

featureKey := fmt.Sprintf("%s/%s", FeatureKeyPrefix, decodedName)

client, err := metadata.Client.AppConfiguration.DataPlaneClient(ctx, resourceID.ConfigurationStoreId)
if client == nil {
Expand All @@ -311,7 +344,7 @@ func (k FeatureResource) Update() sdk.ResourceFunc {

if metadata.ResourceData.HasChange("tags") || metadata.ResourceData.HasChange("enabled") || metadata.ResourceData.HasChange("locked") || metadata.ResourceData.HasChange("description") {
// Remove the lock, if any. We will put it back again if the model says so.
if _, err = client.DeleteLock(ctx, featureKey, resourceID.Label, "", ""); err != nil {
if _, err = client.DeleteLock(ctx, featureKey, decodedLabel, "", ""); err != nil {
return fmt.Errorf("while unlocking key/label pair %s/%s: %+v", resourceID.Name, resourceID.Label, err)
}
err = createOrUpdateFeature(ctx, client, model)
Expand All @@ -330,7 +363,18 @@ func (k FeatureResource) Delete() sdk.ResourceFunc {
return sdk.ResourceFunc{
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
resourceID, err := parse.FeatureId(metadata.ResourceData.Id())
featureKey := fmt.Sprintf("%s/%s", FeatureKeyPrefix, resourceID.Name)

decodedName, err := url.QueryUnescape(resourceID.Name)
if err != nil {
return fmt.Errorf("while decoding name of resource ID: %+v", err)
}

decodedLabel, err := url.QueryUnescape(resourceID.Label)
if err != nil {
return fmt.Errorf("while decoding label of resource ID: %+v", err)
}

featureKey := fmt.Sprintf("%s/%s", FeatureKeyPrefix, decodedName)

if err != nil {
return fmt.Errorf("while parsing resource ID: %+v", err)
Expand All @@ -344,7 +388,7 @@ func (k FeatureResource) Delete() sdk.ResourceFunc {
return err
}

kv, err := client.GetKeyValues(ctx, featureKey, resourceID.Label, "", "", []string{})
kv, err := client.GetKeyValues(ctx, featureKey, decodedLabel, "", "", []string{})
if err != nil {
return fmt.Errorf("while checking for feature's %q existence: %+v", resourceID.Name, err)
}
Expand All @@ -353,11 +397,11 @@ func (k FeatureResource) Delete() sdk.ResourceFunc {
return nil
}

if _, err = client.DeleteLock(ctx, featureKey, resourceID.Label, "", ""); err != nil {
if _, err = client.DeleteLock(ctx, featureKey, decodedLabel, "", ""); err != nil {
return fmt.Errorf("while unlocking key/label pair %s/%s: %+v", resourceID.Name, resourceID.Label, err)
}

_, err = client.DeleteKeyValue(ctx, featureKey, resourceID.Label, "")
_, err = client.DeleteKeyValue(ctx, featureKey, decodedLabel, "")
if err != nil {
return fmt.Errorf("while removing key %q from App Configuration Store %q: %+v", resourceID.Name, resourceID.ConfigurationStoreId, err)
}
Expand All @@ -373,7 +417,19 @@ func (k FeatureResource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
}

func createOrUpdateFeature(ctx context.Context, client *appconfiguration.BaseClient, model FeatureResourceModel) error {
featureKey := fmt.Sprintf("%s/%s", FeatureKeyPrefix, model.Name)
decodedName, err := url.QueryUnescape(model.Name)
if err != nil {
return fmt.Errorf("while decoding name of resource ID: %+v", err)
}
decodedLabel, err := url.QueryUnescape(model.Label)
if err != nil {
return fmt.Errorf("while decoding label of resource ID: %+v", err)
}

if err != nil {
return fmt.Errorf("")
}
featureKey := fmt.Sprintf("%s/%s", FeatureKeyPrefix, decodedName)
entity := appconfiguration.KeyValue{
Key: utils.String(featureKey),
Label: utils.String(model.Label),
Expand All @@ -383,7 +439,7 @@ func createOrUpdateFeature(ctx context.Context, client *appconfiguration.BaseCli
}

value := FeatureValue{
ID: model.Name,
ID: decodedName,
Description: model.Description,
Enabled: model.Enabled,
}
Expand Down Expand Up @@ -419,16 +475,16 @@ func createOrUpdateFeature(ctx context.Context, client *appconfiguration.BaseCli
return fmt.Errorf("while marshalling FeatureValue struct: %+v", err)
}
entity.Value = utils.String(string(valueBytes))
if _, err = client.PutKeyValue(ctx, featureKey, model.Label, &entity, "", ""); err != nil {
if _, err = client.PutKeyValue(ctx, featureKey, decodedLabel, &entity, "", ""); err != nil {
return err
}

if model.Locked {
if _, err = client.PutLock(ctx, featureKey, model.Label, "", ""); err != nil {
if _, err = client.PutLock(ctx, featureKey, decodedLabel, "", ""); err != nil {
return fmt.Errorf("while locking key/label pair %s/%s: %+v", model.Name, model.Label, err)
}
} else {
if _, err = client.DeleteLock(ctx, featureKey, model.Label, "", ""); err != nil {
if _, err = client.DeleteLock(ctx, featureKey, decodedLabel, "", ""); err != nil {
return fmt.Errorf("while unlocking key/label pair %s/%s: %+v", model.Name, model.Label, err)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,20 @@ func TestAccAppConfigurationFeature_basicNoLabel(t *testing.T) {
})
}

func TestAccAppConfigurationFeature_basicWithSalsh(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_app_configuration_feature", "test")
r := AppConfigurationFeatureResource{}
data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.basicWithSlash(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func TestAccAppConfigurationFeature_basicNoFilters(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_app_configuration_feature", "test")
r := AppConfigurationFeatureResource{}
Expand Down Expand Up @@ -258,6 +272,47 @@ resource "azurerm_app_configuration_feature" "test" {
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger)
}

func (t AppConfigurationFeatureResource) basicWithSlash(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "test" {
name = "acctestRG-appconfig-%d"
location = "%s"
}
data "azurerm_client_config" "test" {
}
resource "azurerm_role_assignment" "test" {
scope = azurerm_resource_group.test.id
role_definition_name = "App Configuration Data Owner"
principal_id = data.azurerm_client_config.test.object_id
}
resource "azurerm_app_configuration" "test" {
name = "testacc-appconf%d"
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
sku = "standard"
depends_on = [
azurerm_role_assignment.test,
]
}
resource "azurerm_app_configuration_feature" "test" {
configuration_store_id = azurerm_app_configuration.test.id
description = "test description"
name = "acctest/ackey/%d"
label = "acctest/label"
enabled = true
}
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger)
}

func (t AppConfigurationFeatureResource) requiresImport(data acceptance.TestData) string {
return fmt.Sprintf(`
%s
Expand Down
Loading

0 comments on commit 230dd55

Please sign in to comment.