Skip to content

Commit

Permalink
azurerm_synapse_linked_service adding state migration for id (#19477)
Browse files Browse the repository at this point in the history
  • Loading branch information
mbfrahry authored Nov 29, 2022
1 parent ff15b64 commit ae925cd
Show file tree
Hide file tree
Showing 7 changed files with 311 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package migration

import (
"context"
"log"

"github.com/hashicorp/terraform-provider-azurerm/internal/services/synapse/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
)

type SynapseLinkedServiceV0ToV1 struct{}

func (s SynapseLinkedServiceV0ToV1) Schema() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
"name": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
},

"synapse_workspace_id": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
},

"type": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
},

"type_properties_json": {
Type: pluginsdk.TypeString,
Required: true,
},

"description": {
Type: pluginsdk.TypeString,
Optional: true,
},

"integration_runtime": {
Type: pluginsdk.TypeList,
Optional: true,
MaxItems: 1,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"name": {
Type: pluginsdk.TypeString,
Required: true,
},

"parameters": {
Type: pluginsdk.TypeMap,
Optional: true,
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
},
},
},
},
},

"parameters": {
Type: pluginsdk.TypeMap,
Optional: true,
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
},
},

"annotations": {
Type: pluginsdk.TypeList,
Optional: true,
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
},
},

"additional_properties": {
Type: pluginsdk.TypeMap,
Optional: true,
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
},
},
}
}

func (s SynapseLinkedServiceV0ToV1) 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.LinkedServiceIDInsensitively(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
}
}
60 changes: 58 additions & 2 deletions internal/services/synapse/parse/linked_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (id LinkedServiceId) String() string {
}

func (id LinkedServiceId) ID() string {
fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Synapse/workspaces/%s/linkedservices/%s"
fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Synapse/workspaces/%s/linkedServices/%s"
return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.WorkspaceName, id.Name)
}

Expand All @@ -63,7 +63,63 @@ func LinkedServiceID(input string) (*LinkedServiceId, error) {
if resourceId.WorkspaceName, err = id.PopSegment("workspaces"); err != nil {
return nil, err
}
if resourceId.Name, err = id.PopSegment("linkedservices"); err != nil {
if resourceId.Name, err = id.PopSegment("linkedServices"); err != nil {
return nil, err
}

if err := id.ValidateNoEmptySegments(input); err != nil {
return nil, err
}

return &resourceId, nil
}

// LinkedServiceIDInsensitively parses an LinkedService ID into an LinkedServiceId struct, insensitively
// This should only be used to parse an ID for rewriting, the LinkedServiceID
// 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 LinkedServiceIDInsensitively(input string) (*LinkedServiceId, error) {
id, err := resourceids.ParseAzureResourceID(input)
if err != nil {
return nil, err
}

resourceId := LinkedServiceId{
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 'workspaces' segment
workspacesKey := "workspaces"
for key := range id.Path {
if strings.EqualFold(key, workspacesKey) {
workspacesKey = key
break
}
}
if resourceId.WorkspaceName, err = id.PopSegment(workspacesKey); err != nil {
return nil, err
}

// find the correct casing for the 'linkedServices' segment
linkedServicesKey := "linkedServices"
for key := range id.Path {
if strings.EqualFold(key, linkedServicesKey) {
linkedServicesKey = key
break
}
}
if resourceId.Name, err = id.PopSegment(linkedServicesKey); err != nil {
return nil, err
}

Expand Down
142 changes: 139 additions & 3 deletions internal/services/synapse/parse/linked_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ var _ resourceids.Id = LinkedServiceId{}

func TestLinkedServiceIDFormatter(t *testing.T) {
actual := NewLinkedServiceID("12345678-1234-9876-4563-123456789012", "resGroup1", "workspace1", "linkedservice1").ID()
expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/linkedservices/linkedservice1"
expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/linkedServices/linkedservice1"
if actual != expected {
t.Fatalf("Expected %q but got %q", expected, actual)
}
Expand Down Expand Up @@ -75,13 +75,13 @@ func TestLinkedServiceID(t *testing.T) {

{
// missing value for Name
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/linkedservices/",
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/linkedServices/",
Error: true,
},

{
// valid
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/linkedservices/linkedservice1",
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/linkedServices/linkedservice1",
Expected: &LinkedServiceId{
SubscriptionId: "12345678-1234-9876-4563-123456789012",
ResourceGroup: "resGroup1",
Expand Down Expand Up @@ -126,3 +126,139 @@ func TestLinkedServiceID(t *testing.T) {
}
}
}

func TestLinkedServiceIDInsensitively(t *testing.T) {
testData := []struct {
Input string
Error bool
Expected *LinkedServiceId
}{

{
// 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 WorkspaceName
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/",
Error: true,
},

{
// missing value for WorkspaceName
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/",
Error: true,
},

{
// missing Name
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/",
Error: true,
},

{
// missing value for Name
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/linkedServices/",
Error: true,
},

{
// valid
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/linkedServices/linkedservice1",
Expected: &LinkedServiceId{
SubscriptionId: "12345678-1234-9876-4563-123456789012",
ResourceGroup: "resGroup1",
WorkspaceName: "workspace1",
Name: "linkedservice1",
},
},

{
// lower-cased segment names
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/linkedservices/linkedservice1",
Expected: &LinkedServiceId{
SubscriptionId: "12345678-1234-9876-4563-123456789012",
ResourceGroup: "resGroup1",
WorkspaceName: "workspace1",
Name: "linkedservice1",
},
},

{
// upper-cased segment names
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/WORKSPACES/workspace1/LINKEDSERVICES/linkedservice1",
Expected: &LinkedServiceId{
SubscriptionId: "12345678-1234-9876-4563-123456789012",
ResourceGroup: "resGroup1",
WorkspaceName: "workspace1",
Name: "linkedservice1",
},
},

{
// mixed-cased segment names
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/WoRkSpAcEs/workspace1/LiNkEdSeRvIcEs/linkedservice1",
Expected: &LinkedServiceId{
SubscriptionId: "12345678-1234-9876-4563-123456789012",
ResourceGroup: "resGroup1",
WorkspaceName: "workspace1",
Name: "linkedservice1",
},
},
}

for _, v := range testData {
t.Logf("[DEBUG] Testing %q", v.Input)

actual, err := LinkedServiceIDInsensitively(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.WorkspaceName != v.Expected.WorkspaceName {
t.Fatalf("Expected %q but got %q for WorkspaceName", v.Expected.WorkspaceName, actual.WorkspaceName)
}
if actual.Name != v.Expected.Name {
t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name)
}
}
}
2 changes: 1 addition & 1 deletion internal/services/synapse/resourceids.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package synapse

//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=FirewallRule -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/firewallRules/firewallRule1
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=IntegrationRuntime -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/integrationruntimes/IntegrationRuntime1
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=LinkedService -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/linkedservices/linkedservice1
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=LinkedService -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/linkedServices/linkedservice1 -rewrite=true
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ManagedPrivateEndpoint -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/managedVirtualNetworks/default/managedPrivateEndpoints/endpoint1
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=PrivateLinkHub -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/privateLinkHubs/privateLinkHub1
// RoleAssignment cannot be generated at this time
Expand Down
6 changes: 6 additions & 0 deletions internal/services/synapse/synapse_linked_service_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/hashicorp/terraform-provider-azurerm/helpers/tf"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/synapse/migration"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/synapse/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/synapse/sdk/2021-06-01-preview/artifacts"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/synapse/validate"
Expand All @@ -29,6 +30,11 @@ func resourceSynapseLinkedService() *pluginsdk.Resource {
return err
}),

SchemaVersion: 1,
StateUpgraders: pluginsdk.StateUpgrades(map[int]pluginsdk.StateUpgrade{
0: migration.SynapseLinkedServiceV0ToV1{},
}),

Timeouts: &pluginsdk.ResourceTimeout{
Create: pluginsdk.DefaultTimeout(30 * time.Minute),
Read: pluginsdk.DefaultTimeout(5 * time.Minute),
Expand Down
4 changes: 2 additions & 2 deletions internal/services/synapse/validate/linked_service_id_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ func TestLinkedServiceID(t *testing.T) {

{
// missing value for Name
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/linkedservices/",
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/linkedServices/",
Valid: false,
},

{
// valid
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/linkedservices/linkedservice1",
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/linkedServices/linkedservice1",
Valid: true,
},

Expand Down
2 changes: 1 addition & 1 deletion website/docs/r/synapse_linked_service.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -123,5 +123,5 @@ The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/l
Synapse Linked Services can be imported using the `resource id`, e.g.

```shell
terraform import azurerm_synapse_linked_service.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/linkedservices/linkedservice1
terraform import azurerm_synapse_linked_service.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/linkedServices/linkedservice1
```

0 comments on commit ae925cd

Please sign in to comment.