Skip to content

Commit f19b84f

Browse files
PLAT-7481 Support Terraform Provider region_id to provider and name m… (#63)
* PLAT-7481 Support Terraform Provider region_id to provider and name migration * Fail on migration to deprecated region_id * Fix state check
1 parent 557c822 commit f19b84f

File tree

6 files changed

+86
-30
lines changed

6 files changed

+86
-30
lines changed

docs/resources/workspace_group.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@ provider "singlestoredb" {
1919
// You can generate this key from the SingleStore Portal at https://portal.singlestore.com/organizations/org-id/api-keys.
2020
}
2121
22-
data "singlestoredb_regions" "all" {}
22+
data "singlestoredb_regions_v2" "all" {}
2323
2424
resource "singlestoredb_workspace_group" "this" {
2525
name = "group"
2626
firewall_ranges = ["0.0.0.0/0"] // Ensure restrictive ranges for production environments.
2727
expires_at = "2222-01-01T00:00:00Z"
28-
region_id = data.singlestoredb_regions.all.regions.0.id // Prefer specifying the explicit region ID in production environments as the list of regions may vary.
29-
admin_password = "initialSfkjDIJ423d44w1sfooBar1$"
28+
cloud_provider = data.singlestoredb_regions_v2.all.regions.0.provider
29+
region_name = data.singlestoredb_regions_v2.all.regions.0.region_name
30+
admin_password = "mockPassword193!"
3031
}
3132
```
3233

examples/resources/singlestoredb_workspace_group/resource.tf

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ provider "singlestoredb" {
44
// You can generate this key from the SingleStore Portal at https://portal.singlestore.com/organizations/org-id/api-keys.
55
}
66

7-
data "singlestoredb_regions" "all" {}
7+
data "singlestoredb_regions_v2" "all" {}
88

99
resource "singlestoredb_workspace_group" "this" {
1010
name = "group"
1111
firewall_ranges = ["0.0.0.0/0"] // Ensure restrictive ranges for production environments.
1212
expires_at = "2222-01-01T00:00:00Z"
13-
region_id = data.singlestoredb_regions.all.regions.0.id // Prefer specifying the explicit region ID in production environments as the list of regions may vary.
14-
admin_password = "initialSfkjDIJ423d44w1sfooBar1$"
13+
cloud_provider = data.singlestoredb_regions_v2.all.regions.0.provider
14+
region_name = data.singlestoredb_regions_v2.all.regions.0.region_name
15+
admin_password = "mockPassword193!"
1516
}

examples/resources/singlestoredb_workspace_group_advanced/resource.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ resource "singlestoredb_workspace_group" "this" {
1010
expires_at = "2222-01-01T00:00:00Z"
1111
region_name = "us-west-2"
1212
cloud_provider = "AWS"
13-
admin_password = "initialSfkjDIJ423d44w1sfooBar1$"
13+
admin_password = "mockPassword193!"
1414
deployment_type = "NON-PRODUCTION"
1515
opt_in_preview_feature = true
1616
high_availability_two_zones = true

internal/provider/config/const.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ const (
5757
// TestInitialWorkspaceGroupExpiresAt is the initial workspace group expiration in examples.
5858
TestInitialWorkspaceGroupExpiresAt = "2222-01-01T00:00:00Z"
5959
// TestInitialAdminPassword is the initial workspace admin password in examples.
60-
TestInitialAdminPassword = "initialSfkjDIJ423d44w1sfooBar1$" //nolint:gosec
60+
TestInitialAdminPassword = "mockPassword193!"
6161
// TestInitialFirewallRange is the firewall range in the example.
6262
TestInitialFirewallRange = "0.0.0.0/0"
6363
// TestFirewallRangeAllTraffic is the firewall range in the example for allowing all traffic.

internal/provider/workspacegroups/resource.go

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ func (r *workspaceGroupResource) Create(ctx context.Context, req resource.Create
158158
return
159159
}
160160

161-
if err := validateCreateRegionParameters(plan); err != nil {
161+
if err := validateRequiredRegionParameters(&plan); err != nil {
162162
resp.Diagnostics.AddError(err.Summary, err.Detail)
163163

164164
return
@@ -217,7 +217,7 @@ func (r *workspaceGroupResource) Create(ctx context.Context, req resource.Create
217217
resp.Diagnostics.Append(diags...)
218218
}
219219

220-
func validateCreateRegionParameters(plan workspaceGroupResourceModel) *util.SummaryWithDetailError {
220+
func validateRequiredRegionParameters(plan *workspaceGroupResourceModel) *util.SummaryWithDetailError {
221221
providerAndRegionNameAreSet := !plan.CloudProvider.IsNull() && !plan.CloudProvider.IsUnknown() && !plan.RegionName.IsNull() && !plan.RegionName.IsUnknown()
222222
regionIDIsSet := !plan.RegionID.IsNull() && !plan.RegionID.IsUnknown()
223223

@@ -388,7 +388,7 @@ func (r *workspaceGroupResource) ModifyPlan(ctx context.Context, req resource.Mo
388388
return
389389
}
390390

391-
if err := validateModifyPlanRegionParameters(plan, state); err != nil {
391+
if err := validateModifyPlanRegionParameters(ctx, r, plan, state); err != nil {
392392
resp.Diagnostics.AddError(err.Summary, err.Detail)
393393

394394
return
@@ -418,25 +418,74 @@ func (r *workspaceGroupResource) ModifyPlan(ctx context.Context, req resource.Mo
418418
}
419419
}
420420

421-
func validateModifyPlanRegionParameters(plan, state *workspaceGroupResourceModel) *util.SummaryWithDetailError {
422-
if !plan.RegionID.Equal(state.RegionID) {
421+
func validateModifyPlanRegionParameters(ctx context.Context, r *workspaceGroupResource, plan, state *workspaceGroupResourceModel) *util.SummaryWithDetailError {
422+
if err := validateRequiredRegionParameters(plan); err != nil {
423+
return err
424+
}
425+
426+
if err := handleRegionMigrationState(ctx, r, plan, state); err != nil {
427+
return err
428+
}
429+
430+
if err := validateModifyRegionID(plan, state); err != nil {
431+
return err
432+
}
433+
434+
if err := validateModifyRegionNameAndProvider(plan, state); err != nil {
435+
return err
436+
}
437+
438+
return nil
439+
}
440+
441+
func handleRegionMigrationState(ctx context.Context, r *workspaceGroupResource, plan, state *workspaceGroupResourceModel) *util.SummaryWithDetailError {
442+
shouldMigrateToRegionNameAndProvider := !state.RegionID.IsNull() && plan.RegionID.IsNull()
443+
shouldMigrateToDepricatedRegionID := !state.RegionName.IsNull() && plan.RegionName.IsNull() && !state.CloudProvider.IsNull() && plan.CloudProvider.IsNull()
444+
445+
if shouldMigrateToDepricatedRegionID {
446+
return &util.SummaryWithDetailError{
447+
Summary: "Cannot change cloud_provider and region_name to the deprecated region_id.",
448+
Detail: "Changing cloud_provider and region_name to the deprecated region_id is not permitted. Use the cloud_provider and region_name parameters instead.",
449+
}
450+
}
451+
452+
if shouldMigrateToRegionNameAndProvider {
453+
workspaceGroup, err := r.GetV1WorkspaceGroupsWorkspaceGroupIDWithResponse(ctx,
454+
uuid.MustParse(state.ID.ValueString()),
455+
&management.GetV1WorkspaceGroupsWorkspaceGroupIDParams{},
456+
)
457+
if serr := util.StatusOK(workspaceGroup, err); serr != nil {
458+
return serr
459+
}
460+
state.CloudProvider = types.StringValue(string(workspaceGroup.JSON200.Provider))
461+
state.RegionName = types.StringValue(workspaceGroup.JSON200.RegionName)
462+
}
463+
464+
return nil
465+
}
466+
467+
func validateModifyRegionID(plan, state *workspaceGroupResourceModel) *util.SummaryWithDetailError {
468+
if !plan.RegionID.IsNull() && !state.RegionID.IsNull() && !plan.RegionID.Equal(state.RegionID) {
423469
return &util.SummaryWithDetailError{
424470
Summary: "Cannot update workspace group region_id",
425-
Detail: "To prevent accidental deletion of the workspace group and loss of data, updating the region_id is not permitted. Please explicitly delete the workspace group before changing its region_id.",
471+
Detail: "Updating the region_id is not permitted. Warning: this field is deprecated. Use cloud_provider and region_name instead.",
426472
}
427473
}
428474

475+
return nil
476+
}
477+
478+
func validateModifyRegionNameAndProvider(plan, state *workspaceGroupResourceModel) *util.SummaryWithDetailError {
429479
if !plan.RegionName.Equal(state.RegionName) {
430480
return &util.SummaryWithDetailError{
431481
Summary: "Cannot update workspace group region_name",
432-
Detail: "To prevent accidental deletion of the workspace group and loss of data, updating the region_name is not permitted. Please explicitly delete the workspace group before changing its region_name.",
482+
Detail: fmt.Sprintf("Updating the region_name is not permitted. Expected value is '%s', but now is '%s'.", state.RegionName.ValueString(), plan.RegionName.ValueString()),
433483
}
434484
}
435-
436485
if !plan.CloudProvider.Equal(state.CloudProvider) {
437486
return &util.SummaryWithDetailError{
438487
Summary: "Cannot update workspace group cloud_provider",
439-
Detail: "To prevent accidental deletion of the workspace group and loss of data, updating the cloud_provider is not permitted. Please explicitly delete the workspace group before changing its cloud_provider.",
488+
Detail: fmt.Sprintf("Updating the cloud_provider is not permitted. Expected value is '%s'.", state.CloudProvider.ValueString()),
440489
}
441490
}
442491

internal/provider/workspacegroups/resource_test.go

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,17 @@ import (
2323

2424
var (
2525
updatedWorkspaceGroupName = strings.Join([]string{"updated", config.TestInitialWorkspaceGroupName}, "-")
26-
updatedAdminPassword = "buzDzBd4qAR12c9$" //nolint:gosec
26+
updatedAdminPassword = "mockPasswordUpdated193!"
2727
defaultDeploymentType = management.WorkspaceGroupDeploymentTypePRODUCTION
2828
updatedDeploymentType = management.WorkspaceGroupDeploymentTypeNONPRODUCTION
2929
)
3030

3131
func TestCRUDWorkspaceGroup(t *testing.T) {
32-
regions := []management.Region{
32+
regionsv2 := []management.RegionV2{
3333
{
34-
RegionID: uuid.MustParse("2ca3d358-021d-45ed-86cb-38b8d14ac507"),
35-
Region: "GS - US West 2 (Oregon) - aws-oregon-gs1",
36-
Provider: management.RegionProviderAWS,
34+
Region: "GS - US West 2 (Oregon) - aws-oregon-gs1",
35+
Provider: management.RegionV2ProviderAWS,
36+
RegionName: "aws-oregon-gs1",
3737
},
3838
}
3939

@@ -44,7 +44,8 @@ func TestCRUDWorkspaceGroup(t *testing.T) {
4444
ExpiresAt: util.Ptr(config.TestInitialWorkspaceGroupExpiresAt),
4545
FirewallRanges: util.Ptr([]string{config.TestInitialFirewallRange}),
4646
Name: config.TestInitialWorkspaceGroupName,
47-
RegionID: regions[0].RegionID,
47+
RegionName: regionsv2[0].RegionName,
48+
Provider: management.WorkspaceGroupProviderAWS,
4849
State: management.WorkspaceGroupStatePENDING, // During the first poll, the status will still be PENDING.
4950
TerminatedAt: nil,
5051
UpdateWindow: nil,
@@ -55,13 +56,13 @@ func TestCRUDWorkspaceGroup(t *testing.T) {
5556

5657
updatedExpiresAt := time.Now().UTC().Add(time.Hour * 2).Format(time.RFC3339)
5758

58-
regionsHandler := func(w http.ResponseWriter, r *http.Request) bool {
59-
if r.URL.Path != "/v1/regions" || r.Method != http.MethodGet {
59+
regionsv2Handler := func(w http.ResponseWriter, r *http.Request) bool {
60+
if r.URL.Path != "/v2/regions" || r.Method != http.MethodGet {
6061
return false
6162
}
6263

6364
w.Header().Add("Content-Type", "json")
64-
_, err := w.Write(testutil.MustJSON(regions))
65+
_, err := w.Write(testutil.MustJSON(regionsv2))
6566
require.NoError(t, err)
6667

6768
return true
@@ -103,7 +104,7 @@ func TestCRUDWorkspaceGroup(t *testing.T) {
103104
require.Equal(t, config.TestInitialWorkspaceGroupExpiresAt, util.Deref(input.ExpiresAt))
104105
require.Equal(t, []string{config.TestInitialFirewallRange}, input.FirewallRanges)
105106
require.Equal(t, config.TestInitialWorkspaceGroupName, input.Name)
106-
require.Equal(t, regions[0].RegionID, *input.RegionID)
107+
require.Equal(t, regionsv2[0].RegionName, *input.RegionName)
107108

108109
w.Header().Add("Content-Type", "json")
109110
_, err = w.Write(testutil.MustJSON(
@@ -173,7 +174,7 @@ func TestCRUDWorkspaceGroup(t *testing.T) {
173174
}
174175

175176
readOnlyHandlers := []func(w http.ResponseWriter, r *http.Request) bool{
176-
regionsHandler,
177+
regionsv2Handler,
177178
workspaceGroupsGetHandler,
178179
}
179180

@@ -213,7 +214,8 @@ func TestCRUDWorkspaceGroup(t *testing.T) {
213214
resource.TestCheckResourceAttr("singlestoredb_workspace_group.this", "name", config.TestInitialWorkspaceGroupName),
214215
resource.TestCheckResourceAttr("singlestoredb_workspace_group.this", "created_at", workspaceGroup.CreatedAt),
215216
resource.TestCheckResourceAttr("singlestoredb_workspace_group.this", "expires_at", *workspaceGroup.ExpiresAt),
216-
resource.TestCheckResourceAttr("singlestoredb_workspace_group.this", "region_id", regions[0].RegionID.String()),
217+
resource.TestCheckResourceAttr("singlestoredb_workspace_group.this", "cloud_provider", string(management.RegionProviderAWS)),
218+
resource.TestCheckResourceAttr("singlestoredb_workspace_group.this", "region_name", workspaceGroup.RegionName),
217219
resource.TestCheckResourceAttr("singlestoredb_workspace_group.this", "admin_password", config.TestInitialAdminPassword),
218220
resource.TestCheckResourceAttr("singlestoredb_workspace_group.this", "firewall_ranges.#", "1"),
219221
resource.TestCheckResourceAttr("singlestoredb_workspace_group.this", "firewall_ranges.0", config.TestInitialFirewallRange),
@@ -228,13 +230,16 @@ func TestCRUDWorkspaceGroup(t *testing.T) {
228230
WithWorkspaceGroupResource("this")("expires_at", cty.StringVal(updatedExpiresAt)).
229231
WithWorkspaceGroupResource("this")("firewall_ranges", cty.ListValEmpty(cty.String)).
230232
WithWorkspaceGroupResource("this")("deployment_type", cty.StringVal(string(updatedDeploymentType))).
233+
WithWorkspaceGroupResource("this")("cloud_provider", cty.StringVal(string(management.RegionProviderAWS))).
234+
WithWorkspaceGroupResource("this")("region_name", cty.StringVal(workspaceGroup.RegionName)).
231235
String(),
232236
Check: resource.ComposeAggregateTestCheckFunc(
233237
resource.TestCheckResourceAttr("singlestoredb_workspace_group.this", config.IDAttribute, workspaceGroupID.String()),
234238
resource.TestCheckResourceAttr("singlestoredb_workspace_group.this", "name", updatedWorkspaceGroupName),
235239
resource.TestCheckResourceAttr("singlestoredb_workspace_group.this", "created_at", workspaceGroup.CreatedAt),
236240
resource.TestCheckResourceAttr("singlestoredb_workspace_group.this", "expires_at", updatedExpiresAt),
237-
resource.TestCheckResourceAttr("singlestoredb_workspace_group.this", "region_id", regions[0].RegionID.String()),
241+
resource.TestCheckResourceAttr("singlestoredb_workspace_group.this", "cloud_provider", string(management.RegionProviderAWS)),
242+
resource.TestCheckResourceAttr("singlestoredb_workspace_group.this", "region_name", workspaceGroup.RegionName),
238243
resource.TestCheckResourceAttr("singlestoredb_workspace_group.this", "admin_password", updatedAdminPassword),
239244
resource.TestCheckResourceAttr("singlestoredb_workspace_group.this", "firewall_ranges.#", "0"),
240245
resource.TestCheckResourceAttr("singlestoredb_workspace_group.this", "deployment_type", string(updatedDeploymentType)),

0 commit comments

Comments
 (0)