From b0a9a94d5156b786a8e54d590c7d6d3d5cb8bec7 Mon Sep 17 00:00:00 2001 From: Heng Lu <79895375+ms-henglu@users.noreply.github.com> Date: Wed, 1 Nov 2023 19:13:13 +0800 Subject: [PATCH] `azurerm_spring_cloud_(customized)_accelerator` - state migration to fix the ID casing (#23749) * `azurerm_spring_cloud_accelerator`, `azurerm_spring_cloud_customized_accelerator` - state migration to fix the ID casing * condense the migration schema --- ...celerator_customized_migration_v0_to_v1.go | 145 +++++++++++++++ .../accelerator_migration_v0_to_v1.go | 43 +++++ .../parse/spring_cloud_accelerator.go | 60 ++++++- .../parse/spring_cloud_accelerator_test.go | 146 ++++++++++++++- .../spring_cloud_customized_accelerator.go | 72 +++++++- ...pring_cloud_customized_accelerator_test.go | 169 +++++++++++++++++- internal/services/springcloud/resourceids.go | 4 +- .../spring_cloud_accelerator_resource.go | 11 ++ ...g_cloud_customized_accelerator_resource.go | 11 ++ .../spring_cloud_accelerator_id_test.go | 8 +- ...ng_cloud_customized_accelerator_id_test.go | 12 +- .../r/spring_cloud_accelerator.html.markdown | 2 +- ...cloud_customized_accelerator.html.markdown | 2 +- 13 files changed, 655 insertions(+), 30 deletions(-) create mode 100644 internal/services/springcloud/migration/accelerator_customized_migration_v0_to_v1.go create mode 100644 internal/services/springcloud/migration/accelerator_migration_v0_to_v1.go diff --git a/internal/services/springcloud/migration/accelerator_customized_migration_v0_to_v1.go b/internal/services/springcloud/migration/accelerator_customized_migration_v0_to_v1.go new file mode 100644 index 000000000000..a2cde5d27b59 --- /dev/null +++ b/internal/services/springcloud/migration/accelerator_customized_migration_v0_to_v1.go @@ -0,0 +1,145 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package migration + +import ( + "context" + "log" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/springcloud/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" +) + +type SpringCloudCustomizedAcceleratorV0ToV1 struct{} + +func (s SpringCloudCustomizedAcceleratorV0ToV1) Schema() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "spring_cloud_accelerator_id": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "git_repository": { + Type: pluginsdk.TypeList, + Required: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "url": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "basic_auth": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "username": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "password": { + Type: pluginsdk.TypeString, + Required: true, + }, + }, + }, + }, + + "ssh_auth": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "private_key": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "host_key": { + Type: pluginsdk.TypeString, + Optional: true, + }, + + "host_key_algorithm": { + Type: pluginsdk.TypeString, + Optional: true, + }, + }, + }, + }, + + "branch": { + Type: pluginsdk.TypeString, + Optional: true, + }, + + "ca_certificate_id": { + Type: pluginsdk.TypeString, + Optional: true, + }, + + "commit": { + Type: pluginsdk.TypeString, + Optional: true, + }, + + "git_tag": { + Type: pluginsdk.TypeString, + Optional: true, + }, + + "interval_in_seconds": { + Type: pluginsdk.TypeInt, + Optional: true, + }, + }, + }, + }, + + "accelerator_tags": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + + "description": { + Type: pluginsdk.TypeString, + Optional: true, + }, + + "display_name": { + Type: pluginsdk.TypeString, + Optional: true, + }, + + "icon_url": { + Type: pluginsdk.TypeString, + Optional: true, + }, + } +} + +func (s SpringCloudCustomizedAcceleratorV0ToV1) UpgradeFunc() pluginsdk.StateUpgraderFunc { + return func(ctx context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) { + oldId := rawState["id"].(string) + newId, err := parse.SpringCloudCustomizedAcceleratorIDInsensitively(oldId) + if err != nil { + return nil, err + } + + log.Printf("[DEBUG] Updating ID from %q to %q", oldId, newId) + + rawState["id"] = newId.ID() + return rawState, nil + } +} diff --git a/internal/services/springcloud/migration/accelerator_migration_v0_to_v1.go b/internal/services/springcloud/migration/accelerator_migration_v0_to_v1.go new file mode 100644 index 000000000000..400b41b67517 --- /dev/null +++ b/internal/services/springcloud/migration/accelerator_migration_v0_to_v1.go @@ -0,0 +1,43 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package migration + +import ( + "context" + "log" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/springcloud/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" +) + +type SpringCloudAcceleratorV0ToV1 struct{} + +func (s SpringCloudAcceleratorV0ToV1) Schema() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "spring_cloud_service_id": { + Type: pluginsdk.TypeString, + Required: true, + }, + } +} + +func (s SpringCloudAcceleratorV0ToV1) UpgradeFunc() pluginsdk.StateUpgraderFunc { + return func(ctx context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) { + oldId := rawState["id"].(string) + newId, err := parse.SpringCloudAcceleratorIDInsensitively(oldId) + if err != nil { + return nil, err + } + + log.Printf("[DEBUG] Updating ID from %q to %q", oldId, newId) + + rawState["id"] = newId.ID() + return rawState, nil + } +} diff --git a/internal/services/springcloud/parse/spring_cloud_accelerator.go b/internal/services/springcloud/parse/spring_cloud_accelerator.go index d735a309f3cb..278276164ef8 100644 --- a/internal/services/springcloud/parse/spring_cloud_accelerator.go +++ b/internal/services/springcloud/parse/spring_cloud_accelerator.go @@ -39,7 +39,7 @@ func (id SpringCloudAcceleratorId) String() string { } func (id SpringCloudAcceleratorId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.AppPlatform/Spring/%s/applicationAccelerators/%s" + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.AppPlatform/spring/%s/applicationAccelerators/%s" return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.SpringName, id.ApplicationAcceleratorName) } @@ -63,7 +63,7 @@ func SpringCloudAcceleratorID(input string) (*SpringCloudAcceleratorId, error) { return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") } - if resourceId.SpringName, err = id.PopSegment("Spring"); err != nil { + if resourceId.SpringName, err = id.PopSegment("spring"); err != nil { return nil, err } if resourceId.ApplicationAcceleratorName, err = id.PopSegment("applicationAccelerators"); err != nil { @@ -76,3 +76,59 @@ func SpringCloudAcceleratorID(input string) (*SpringCloudAcceleratorId, error) { return &resourceId, nil } + +// SpringCloudAcceleratorIDInsensitively parses an SpringCloudAccelerator ID into an SpringCloudAcceleratorId struct, insensitively +// This should only be used to parse an ID for rewriting, the SpringCloudAcceleratorID +// method should be used instead for validation etc. +// +// Whilst this may seem strange, this enables Terraform have consistent casing +// which works around issues in Core, whilst handling broken API responses. +func SpringCloudAcceleratorIDInsensitively(input string) (*SpringCloudAcceleratorId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := SpringCloudAcceleratorId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + // find the correct casing for the 'spring' segment + springKey := "spring" + for key := range id.Path { + if strings.EqualFold(key, springKey) { + springKey = key + break + } + } + if resourceId.SpringName, err = id.PopSegment(springKey); err != nil { + return nil, err + } + + // find the correct casing for the 'applicationAccelerators' segment + applicationAcceleratorsKey := "applicationAccelerators" + for key := range id.Path { + if strings.EqualFold(key, applicationAcceleratorsKey) { + applicationAcceleratorsKey = key + break + } + } + if resourceId.ApplicationAcceleratorName, err = id.PopSegment(applicationAcceleratorsKey); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/springcloud/parse/spring_cloud_accelerator_test.go b/internal/services/springcloud/parse/spring_cloud_accelerator_test.go index 85886504c703..bef5b7249056 100644 --- a/internal/services/springcloud/parse/spring_cloud_accelerator_test.go +++ b/internal/services/springcloud/parse/spring_cloud_accelerator_test.go @@ -15,7 +15,7 @@ var _ resourceids.Id = SpringCloudAcceleratorId{} func TestSpringCloudAcceleratorIDFormatter(t *testing.T) { actual := NewSpringCloudAcceleratorID("12345678-1234-9876-4563-123456789012", "resGroup1", "spring1", "default").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/applicationAccelerators/default" + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/default" if actual != expected { t.Fatalf("Expected %q but got %q", expected, actual) } @@ -66,25 +66,25 @@ func TestSpringCloudAcceleratorID(t *testing.T) { { // missing value for SpringName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/", Error: true, }, { // missing ApplicationAcceleratorName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/", Error: true, }, { // missing value for ApplicationAcceleratorName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/applicationAccelerators/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/", Error: true, }, { // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/applicationAccelerators/default", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/default", Expected: &SpringCloudAcceleratorId{ SubscriptionId: "12345678-1234-9876-4563-123456789012", ResourceGroup: "resGroup1", @@ -129,3 +129,139 @@ func TestSpringCloudAcceleratorID(t *testing.T) { } } } + +func TestSpringCloudAcceleratorIDInsensitively(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *SpringCloudAcceleratorId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing SpringName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/", + Error: true, + }, + + { + // missing value for SpringName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/", + Error: true, + }, + + { + // missing ApplicationAcceleratorName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/", + Error: true, + }, + + { + // missing value for ApplicationAcceleratorName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/default", + Expected: &SpringCloudAcceleratorId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + SpringName: "spring1", + ApplicationAcceleratorName: "default", + }, + }, + + { + // lower-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationaccelerators/default", + Expected: &SpringCloudAcceleratorId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + SpringName: "spring1", + ApplicationAcceleratorName: "default", + }, + }, + + { + // upper-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/SPRING/spring1/APPLICATIONACCELERATORS/default", + Expected: &SpringCloudAcceleratorId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + SpringName: "spring1", + ApplicationAcceleratorName: "default", + }, + }, + + { + // mixed-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/SpRiNg/spring1/ApPlIcAtIoNaCcElErAtOrS/default", + Expected: &SpringCloudAcceleratorId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + SpringName: "spring1", + ApplicationAcceleratorName: "default", + }, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := SpringCloudAcceleratorIDInsensitively(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.SpringName != v.Expected.SpringName { + t.Fatalf("Expected %q but got %q for SpringName", v.Expected.SpringName, actual.SpringName) + } + if actual.ApplicationAcceleratorName != v.Expected.ApplicationAcceleratorName { + t.Fatalf("Expected %q but got %q for ApplicationAcceleratorName", v.Expected.ApplicationAcceleratorName, actual.ApplicationAcceleratorName) + } + } +} diff --git a/internal/services/springcloud/parse/spring_cloud_customized_accelerator.go b/internal/services/springcloud/parse/spring_cloud_customized_accelerator.go index 634c4d4d0492..9fb57d873c46 100644 --- a/internal/services/springcloud/parse/spring_cloud_customized_accelerator.go +++ b/internal/services/springcloud/parse/spring_cloud_customized_accelerator.go @@ -42,7 +42,7 @@ func (id SpringCloudCustomizedAcceleratorId) String() string { } func (id SpringCloudCustomizedAcceleratorId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.AppPlatform/Spring/%s/applicationAccelerators/%s/customizedAccelerators/%s" + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.AppPlatform/spring/%s/applicationAccelerators/%s/customizedAccelerators/%s" return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.SpringName, id.ApplicationAcceleratorName, id.CustomizedAcceleratorName) } @@ -66,7 +66,7 @@ func SpringCloudCustomizedAcceleratorID(input string) (*SpringCloudCustomizedAcc return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") } - if resourceId.SpringName, err = id.PopSegment("Spring"); err != nil { + if resourceId.SpringName, err = id.PopSegment("spring"); err != nil { return nil, err } if resourceId.ApplicationAcceleratorName, err = id.PopSegment("applicationAccelerators"); err != nil { @@ -82,3 +82,71 @@ func SpringCloudCustomizedAcceleratorID(input string) (*SpringCloudCustomizedAcc return &resourceId, nil } + +// SpringCloudCustomizedAcceleratorIDInsensitively parses an SpringCloudCustomizedAccelerator ID into an SpringCloudCustomizedAcceleratorId struct, insensitively +// This should only be used to parse an ID for rewriting, the SpringCloudCustomizedAcceleratorID +// method should be used instead for validation etc. +// +// Whilst this may seem strange, this enables Terraform have consistent casing +// which works around issues in Core, whilst handling broken API responses. +func SpringCloudCustomizedAcceleratorIDInsensitively(input string) (*SpringCloudCustomizedAcceleratorId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := SpringCloudCustomizedAcceleratorId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + // find the correct casing for the 'spring' segment + springKey := "spring" + for key := range id.Path { + if strings.EqualFold(key, springKey) { + springKey = key + break + } + } + if resourceId.SpringName, err = id.PopSegment(springKey); err != nil { + return nil, err + } + + // find the correct casing for the 'applicationAccelerators' segment + applicationAcceleratorsKey := "applicationAccelerators" + for key := range id.Path { + if strings.EqualFold(key, applicationAcceleratorsKey) { + applicationAcceleratorsKey = key + break + } + } + if resourceId.ApplicationAcceleratorName, err = id.PopSegment(applicationAcceleratorsKey); err != nil { + return nil, err + } + + // find the correct casing for the 'customizedAccelerators' segment + customizedAcceleratorsKey := "customizedAccelerators" + for key := range id.Path { + if strings.EqualFold(key, customizedAcceleratorsKey) { + customizedAcceleratorsKey = key + break + } + } + if resourceId.CustomizedAcceleratorName, err = id.PopSegment(customizedAcceleratorsKey); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/springcloud/parse/spring_cloud_customized_accelerator_test.go b/internal/services/springcloud/parse/spring_cloud_customized_accelerator_test.go index 9c3414f6c45a..442b5c49419b 100644 --- a/internal/services/springcloud/parse/spring_cloud_customized_accelerator_test.go +++ b/internal/services/springcloud/parse/spring_cloud_customized_accelerator_test.go @@ -15,7 +15,7 @@ var _ resourceids.Id = SpringCloudCustomizedAcceleratorId{} func TestSpringCloudCustomizedAcceleratorIDFormatter(t *testing.T) { actual := NewSpringCloudCustomizedAcceleratorID("12345678-1234-9876-4563-123456789012", "resGroup1", "spring1", "default", "customizedAccelerator1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/applicationAccelerators/default/customizedAccelerators/customizedAccelerator1" + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/default/customizedAccelerators/customizedAccelerator1" if actual != expected { t.Fatalf("Expected %q but got %q", expected, actual) } @@ -66,37 +66,37 @@ func TestSpringCloudCustomizedAcceleratorID(t *testing.T) { { // missing value for SpringName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/", Error: true, }, { // missing ApplicationAcceleratorName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/", Error: true, }, { // missing value for ApplicationAcceleratorName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/applicationAccelerators/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/", Error: true, }, { // missing CustomizedAcceleratorName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/applicationAccelerators/default/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/default/", Error: true, }, { // missing value for CustomizedAcceleratorName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/applicationAccelerators/default/customizedAccelerators/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/default/customizedAccelerators/", Error: true, }, { // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/applicationAccelerators/default/customizedAccelerators/customizedAccelerator1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/default/customizedAccelerators/customizedAccelerator1", Expected: &SpringCloudCustomizedAcceleratorId{ SubscriptionId: "12345678-1234-9876-4563-123456789012", ResourceGroup: "resGroup1", @@ -145,3 +145,158 @@ func TestSpringCloudCustomizedAcceleratorID(t *testing.T) { } } } + +func TestSpringCloudCustomizedAcceleratorIDInsensitively(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *SpringCloudCustomizedAcceleratorId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing SpringName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/", + Error: true, + }, + + { + // missing value for SpringName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/", + Error: true, + }, + + { + // missing ApplicationAcceleratorName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/", + Error: true, + }, + + { + // missing value for ApplicationAcceleratorName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/", + Error: true, + }, + + { + // missing CustomizedAcceleratorName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/default/", + Error: true, + }, + + { + // missing value for CustomizedAcceleratorName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/default/customizedAccelerators/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/default/customizedAccelerators/customizedAccelerator1", + Expected: &SpringCloudCustomizedAcceleratorId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + SpringName: "spring1", + ApplicationAcceleratorName: "default", + CustomizedAcceleratorName: "customizedAccelerator1", + }, + }, + + { + // lower-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationaccelerators/default/customizedaccelerators/customizedAccelerator1", + Expected: &SpringCloudCustomizedAcceleratorId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + SpringName: "spring1", + ApplicationAcceleratorName: "default", + CustomizedAcceleratorName: "customizedAccelerator1", + }, + }, + + { + // upper-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/SPRING/spring1/APPLICATIONACCELERATORS/default/CUSTOMIZEDACCELERATORS/customizedAccelerator1", + Expected: &SpringCloudCustomizedAcceleratorId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + SpringName: "spring1", + ApplicationAcceleratorName: "default", + CustomizedAcceleratorName: "customizedAccelerator1", + }, + }, + + { + // mixed-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/SpRiNg/spring1/ApPlIcAtIoNaCcElErAtOrS/default/CuStOmIzEdAcCeLeRaToRs/customizedAccelerator1", + Expected: &SpringCloudCustomizedAcceleratorId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + SpringName: "spring1", + ApplicationAcceleratorName: "default", + CustomizedAcceleratorName: "customizedAccelerator1", + }, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := SpringCloudCustomizedAcceleratorIDInsensitively(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.SpringName != v.Expected.SpringName { + t.Fatalf("Expected %q but got %q for SpringName", v.Expected.SpringName, actual.SpringName) + } + if actual.ApplicationAcceleratorName != v.Expected.ApplicationAcceleratorName { + t.Fatalf("Expected %q but got %q for ApplicationAcceleratorName", v.Expected.ApplicationAcceleratorName, actual.ApplicationAcceleratorName) + } + if actual.CustomizedAcceleratorName != v.Expected.CustomizedAcceleratorName { + t.Fatalf("Expected %q but got %q for CustomizedAcceleratorName", v.Expected.CustomizedAcceleratorName, actual.CustomizedAcceleratorName) + } + } +} diff --git a/internal/services/springcloud/resourceids.go b/internal/services/springcloud/resourceids.go index 722edc060d9a..ff25836dcb2d 100644 --- a/internal/services/springcloud/resourceids.go +++ b/internal/services/springcloud/resourceids.go @@ -23,7 +23,7 @@ package springcloud // @tombuildsstuff: the following Resource IDs use the incorrect casing and will need State Migrations to fix the casing prior to moving to `hashicorp/go-azure-sdk` (e.g. `Spring` -> `spring`) // in addition, Resources ending in `/default` shouldn't be exposed as separate resources - and instead should be embedded within the parent resource, as such these Resources will need to be deprecated and instead inlined within the parent Resource -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SpringCloudAccelerator -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/applicationAccelerators/default +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SpringCloudAccelerator -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/default -rewrite=true //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SpringCloudApplicationLiveView -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.AppPlatform/spring/service1/applicationLiveViews/default -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SpringCloudCustomizedAccelerator -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/applicationAccelerators/default/customizedAccelerators/customizedAccelerator1 +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SpringCloudCustomizedAccelerator -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/default/customizedAccelerators/customizedAccelerator1 -rewrite=true //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SpringCloudDevToolPortal -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.AppPlatform/Spring/service1/DevToolPortals/default diff --git a/internal/services/springcloud/spring_cloud_accelerator_resource.go b/internal/services/springcloud/spring_cloud_accelerator_resource.go index c7ade92c2490..3e5fd9bd1798 100644 --- a/internal/services/springcloud/spring_cloud_accelerator_resource.go +++ b/internal/services/springcloud/spring_cloud_accelerator_resource.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/springcloud/migration" "github.com/hashicorp/terraform-provider-azurerm/internal/services/springcloud/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/springcloud/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" @@ -27,6 +28,7 @@ type SpringCloudAcceleratorModel struct { type SpringCloudAcceleratorResource struct{} var _ sdk.Resource = SpringCloudAcceleratorResource{} +var _ sdk.ResourceWithStateMigration = SpringCloudAcceleratorResource{} func (s SpringCloudAcceleratorResource) ResourceType() string { return "azurerm_spring_cloud_accelerator" @@ -40,6 +42,15 @@ func (s SpringCloudAcceleratorResource) IDValidationFunc() pluginsdk.SchemaValid return validate.SpringCloudAcceleratorID } +func (s SpringCloudAcceleratorResource) StateUpgraders() sdk.StateUpgradeData { + return sdk.StateUpgradeData{ + SchemaVersion: 1, + Upgraders: map[int]pluginsdk.StateUpgrade{ + 0: migration.SpringCloudAcceleratorV0ToV1{}, + }, + } +} + func (s SpringCloudAcceleratorResource) Arguments() map[string]*schema.Schema { return map[string]*pluginsdk.Schema{ "name": { diff --git a/internal/services/springcloud/spring_cloud_customized_accelerator_resource.go b/internal/services/springcloud/spring_cloud_customized_accelerator_resource.go index 29054ce1338c..c65a3521999d 100644 --- a/internal/services/springcloud/spring_cloud_customized_accelerator_resource.go +++ b/internal/services/springcloud/spring_cloud_customized_accelerator_resource.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/springcloud/migration" "github.com/hashicorp/terraform-provider-azurerm/internal/services/springcloud/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/springcloud/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" @@ -54,6 +55,7 @@ type SshAuthModel struct { type SpringCloudCustomizedAcceleratorResource struct{} var _ sdk.ResourceWithUpdate = SpringCloudCustomizedAcceleratorResource{} +var _ sdk.ResourceWithStateMigration = SpringCloudCustomizedAcceleratorResource{} func (s SpringCloudCustomizedAcceleratorResource) ResourceType() string { return "azurerm_spring_cloud_customized_accelerator" @@ -67,6 +69,15 @@ func (s SpringCloudCustomizedAcceleratorResource) IDValidationFunc() pluginsdk.S return validate.SpringCloudCustomizedAcceleratorID } +func (s SpringCloudCustomizedAcceleratorResource) StateUpgraders() sdk.StateUpgradeData { + return sdk.StateUpgradeData{ + SchemaVersion: 1, + Upgraders: map[int]pluginsdk.StateUpgrade{ + 0: migration.SpringCloudCustomizedAcceleratorV0ToV1{}, + }, + } +} + func (s SpringCloudCustomizedAcceleratorResource) Arguments() map[string]*schema.Schema { return map[string]*pluginsdk.Schema{ "name": { diff --git a/internal/services/springcloud/validate/spring_cloud_accelerator_id_test.go b/internal/services/springcloud/validate/spring_cloud_accelerator_id_test.go index e107e80e96ea..487ffa262269 100644 --- a/internal/services/springcloud/validate/spring_cloud_accelerator_id_test.go +++ b/internal/services/springcloud/validate/spring_cloud_accelerator_id_test.go @@ -51,25 +51,25 @@ func TestSpringCloudAcceleratorID(t *testing.T) { { // missing value for SpringName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/", Valid: false, }, { // missing ApplicationAcceleratorName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/", Valid: false, }, { // missing value for ApplicationAcceleratorName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/applicationAccelerators/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/", Valid: false, }, { // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/applicationAccelerators/default", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/default", Valid: true, }, diff --git a/internal/services/springcloud/validate/spring_cloud_customized_accelerator_id_test.go b/internal/services/springcloud/validate/spring_cloud_customized_accelerator_id_test.go index 9033ae49af18..28de96b8f1e9 100644 --- a/internal/services/springcloud/validate/spring_cloud_customized_accelerator_id_test.go +++ b/internal/services/springcloud/validate/spring_cloud_customized_accelerator_id_test.go @@ -51,37 +51,37 @@ func TestSpringCloudCustomizedAcceleratorID(t *testing.T) { { // missing value for SpringName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/", Valid: false, }, { // missing ApplicationAcceleratorName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/", Valid: false, }, { // missing value for ApplicationAcceleratorName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/applicationAccelerators/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/", Valid: false, }, { // missing CustomizedAcceleratorName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/applicationAccelerators/default/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/default/", Valid: false, }, { // missing value for CustomizedAcceleratorName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/applicationAccelerators/default/customizedAccelerators/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/default/customizedAccelerators/", Valid: false, }, { // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/applicationAccelerators/default/customizedAccelerators/customizedAccelerator1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/default/customizedAccelerators/customizedAccelerator1", Valid: true, }, diff --git a/website/docs/r/spring_cloud_accelerator.html.markdown b/website/docs/r/spring_cloud_accelerator.html.markdown index ecad63461869..95eb59edabaf 100644 --- a/website/docs/r/spring_cloud_accelerator.html.markdown +++ b/website/docs/r/spring_cloud_accelerator.html.markdown @@ -65,5 +65,5 @@ The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/l Spring Cloud Accelerators can be imported using the `resource id`, e.g. ```shell -terraform import azurerm_spring_cloud_accelerator.example /subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.AppPlatform/Spring/service1/applicationAccelerators/default +terraform import azurerm_spring_cloud_accelerator.example /subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.AppPlatform/spring/service1/applicationAccelerators/default ``` diff --git a/website/docs/r/spring_cloud_customized_accelerator.html.markdown b/website/docs/r/spring_cloud_customized_accelerator.html.markdown index 05172d6c3618..5793d122291f 100644 --- a/website/docs/r/spring_cloud_customized_accelerator.html.markdown +++ b/website/docs/r/spring_cloud_customized_accelerator.html.markdown @@ -129,5 +129,5 @@ The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/l Spring Cloud Customized Accelerators can be imported using the `resource id`, e.g. ```shell -terraform import azurerm_spring_cloud_customized_accelerator.example /subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/applicationAccelerators/default/customizedAccelerators/customizedAccelerator1 +terraform import azurerm_spring_cloud_customized_accelerator.example /subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/spring/spring1/applicationAccelerators/default/customizedAccelerators/customizedAccelerator1 ```