Skip to content

Commit 87fc09a

Browse files
feat(mongodbflex) instance + user: store IDs immediately (#1248)
* fix(rabbitmq): Store IDs immediately after provisioning STACKITTPR-390 * chore(rabbitmq) write tests for saving IDs on create error * fix(lint) ignore write error in mockserver * fix(lint) add explanation to ignore comment * feat(scf) save IDs before calling the waiter in Create also fix rabbitmq tests STACKITTPR-393 * feat(opensearch) instance + credential, save IDs immediately after create STACKITTPR-388 * chore(opensearch) move SavesIDsOnError tests into new file, document - move opensearch, scf and rabbitmq SavesIDsOnError tests into new files - document SavesIDsOnError tests in CONTRIBUTION.md - add annotated example test to github docs * feat(mongodbflex) instance + user: store IDs immediately STACKITTPR-385 * fix(mongodbflex) use us spelling * fix(mongodbflex) move SavesIDsOnError tests into new file
1 parent 18e0fe0 commit 87fc09a

File tree

3 files changed

+194
-18
lines changed

3 files changed

+194
-18
lines changed

stackit/internal/services/mongodbflex/instance/resource.go

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
1515
"github.com/hashicorp/terraform-plugin-framework/attr"
1616
"github.com/hashicorp/terraform-plugin-framework/diag"
17-
"github.com/hashicorp/terraform-plugin-framework/path"
1817
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1918
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
2019
"github.com/hashicorp/terraform-plugin-log/tflog"
@@ -426,14 +425,11 @@ func (r *instanceResource) Create(ctx context.Context, req resource.CreateReques
426425
return
427426
}
428427
instanceId := *createResp.Id
429-
ctx = tflog.SetField(ctx, "instance_id", instanceId)
430-
diags = resp.State.SetAttribute(ctx, path.Root("project_id"), projectId)
431-
resp.Diagnostics.Append(diags...)
432-
if resp.Diagnostics.HasError() {
433-
return
434-
}
435-
diags = resp.State.SetAttribute(ctx, path.Root("instance_id"), instanceId)
436-
resp.Diagnostics.Append(diags...)
428+
ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
429+
"project_id": projectId,
430+
"region": region,
431+
"instance_id": instanceId,
432+
})
437433
if resp.Diagnostics.HasError() {
438434
return
439435
}
@@ -719,9 +715,11 @@ func (r *instanceResource) ImportState(ctx context.Context, req resource.ImportS
719715
return
720716
}
721717

722-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
723-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), idParts[1])...)
724-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[2])...)
718+
ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
719+
"project_id": idParts[0],
720+
"region": idParts[1],
721+
"instance_id": idParts[2],
722+
})
725723
tflog.Info(ctx, "MongoDB Flex instance state imported")
726724
}
727725

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package mongodbflex
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"regexp"
7+
"testing"
8+
9+
"github.com/google/uuid"
10+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
11+
"github.com/stackitcloud/stackit-sdk-go/core/utils"
12+
"github.com/stackitcloud/stackit-sdk-go/services/mongodbflex"
13+
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil"
14+
)
15+
16+
// slow test, delete has a 30s sleep...
17+
func TestMongoDBInstanceSavesIDsOnError(t *testing.T) {
18+
var (
19+
projectId = uuid.NewString()
20+
instanceId = uuid.NewString()
21+
)
22+
const (
23+
name = "instance-test"
24+
region = "eu01"
25+
)
26+
s := testutil.NewMockServer(t)
27+
defer s.Server.Close()
28+
tfConfig := fmt.Sprintf(`
29+
provider "stackit" {
30+
mongodbflex_custom_endpoint = "%s"
31+
service_account_token = "mock-server-needs-no-auth"
32+
}
33+
34+
resource "stackit_mongodbflex_instance" "instance" {
35+
project_id = "%s"
36+
name = "%s"
37+
options = {
38+
type = "Replica"
39+
snapshot_retention_days = 1
40+
daily_snapshot_retention_days = 1
41+
point_in_time_window_hours = 1
42+
}
43+
storage = {
44+
class = "premium-perf2-mongodb"
45+
size = 10
46+
}
47+
replicas = 1
48+
acl = ["192.168.0.0/16"]
49+
flavor = {
50+
cpu =2
51+
ram =4
52+
}
53+
version = "7.0"
54+
backup_schedule = "00 6 * * *"
55+
}
56+
`, s.Server.URL, projectId, name)
57+
58+
resource.UnitTest(t, resource.TestCase{
59+
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
60+
Steps: []resource.TestStep{
61+
{
62+
PreConfig: func() {
63+
s.Reset(
64+
testutil.MockResponse{
65+
Description: "ListFlavors",
66+
ToJsonBody: &mongodbflex.ListFlavorsResponse{Flavors: &[]mongodbflex.InstanceFlavor{
67+
{
68+
Description: utils.Ptr("flava-flav"),
69+
Cpu: utils.Ptr(int64(2)),
70+
Id: utils.Ptr("flavor-id"),
71+
Memory: utils.Ptr(int64(4)),
72+
},
73+
}},
74+
},
75+
testutil.MockResponse{
76+
Description: "create instance",
77+
ToJsonBody: &mongodbflex.CreateInstanceResponse{Id: utils.Ptr(instanceId)},
78+
},
79+
testutil.MockResponse{Description: "create waiter", StatusCode: http.StatusInternalServerError},
80+
)
81+
},
82+
Config: tfConfig,
83+
ExpectError: regexp.MustCompile("Error creating instance.*"),
84+
},
85+
{
86+
PreConfig: func() {
87+
s.Reset(
88+
testutil.MockResponse{
89+
Description: "refresh",
90+
Handler: func(w http.ResponseWriter, req *http.Request) {
91+
expected := fmt.Sprintf("/v2/projects/%s/regions/%s/instances/%s", projectId, region, instanceId)
92+
if req.URL.Path != expected {
93+
t.Errorf("expected request to %s, got %s", expected, req.URL.Path)
94+
}
95+
w.WriteHeader(http.StatusInternalServerError)
96+
},
97+
},
98+
testutil.MockResponse{Description: "delete"},
99+
testutil.MockResponse{Description: "delete waiter", StatusCode: http.StatusNotFound},
100+
)
101+
},
102+
RefreshState: true,
103+
ExpectError: regexp.MustCompile("Error reading instance.*"),
104+
},
105+
},
106+
})
107+
}
108+
109+
func TestMongoDBUserSavesIDsOnError(t *testing.T) {
110+
projectId := uuid.NewString()
111+
instanceId := uuid.NewString()
112+
userId := uuid.NewString()
113+
const region = "eu01"
114+
s := testutil.NewMockServer(t)
115+
defer s.Server.Close()
116+
tfConfig := fmt.Sprintf(`
117+
provider "stackit" {
118+
mongodbflex_custom_endpoint = "%s"
119+
service_account_token = "mock-server-needs-no-auth"
120+
}
121+
122+
resource "stackit_mongodbflex_user" "user" {
123+
project_id = "%s"
124+
instance_id = "%s"
125+
username = "username"
126+
roles = ["read"]
127+
database = "db-name"
128+
}
129+
`, s.Server.URL, projectId, instanceId)
130+
131+
resource.UnitTest(t, resource.TestCase{
132+
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
133+
Steps: []resource.TestStep{
134+
{
135+
PreConfig: func() {
136+
s.Reset(
137+
testutil.MockResponse{
138+
Description: "create user",
139+
ToJsonBody: &mongodbflex.CreateUserResponse{Item: &mongodbflex.User{Id: utils.Ptr(userId)}},
140+
},
141+
testutil.MockResponse{Description: "failing waiter", StatusCode: http.StatusInternalServerError},
142+
)
143+
},
144+
Config: tfConfig,
145+
ExpectError: regexp.MustCompile("Error creating user.*"),
146+
},
147+
{
148+
PreConfig: func() {
149+
s.Reset(
150+
testutil.MockResponse{
151+
Description: "refresh user",
152+
Handler: func(w http.ResponseWriter, req *http.Request) {
153+
expected := fmt.Sprintf("/v2/projects/%s/regions/%s/instances/%s/users/%s", projectId, region, instanceId, userId)
154+
if req.URL.Path != expected {
155+
t.Errorf("expected request to %s, got %s", expected, req.URL.Path)
156+
}
157+
w.WriteHeader(http.StatusInternalServerError)
158+
},
159+
},
160+
testutil.MockResponse{Description: "delete user"},
161+
testutil.MockResponse{Description: "delete user waiter", StatusCode: http.StatusNotFound},
162+
)
163+
},
164+
RefreshState: true,
165+
ExpectError: regexp.MustCompile("Error reading user.*"),
166+
},
167+
},
168+
})
169+
}

stackit/internal/services/mongodbflex/user/resource.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import (
1717
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
1818

1919
"github.com/hashicorp/terraform-plugin-framework/attr"
20-
"github.com/hashicorp/terraform-plugin-framework/path"
2120
"github.com/hashicorp/terraform-plugin-framework/resource"
2221
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
2322
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
@@ -261,7 +260,15 @@ func (r *userResource) Create(ctx context.Context, req resource.CreateRequest, r
261260
return
262261
}
263262
userId := *userResp.Item.Id
264-
ctx = tflog.SetField(ctx, "user_id", userId)
263+
ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
264+
"project_id": projectId,
265+
"region": region,
266+
"instance_id": instanceId,
267+
"user_id": userId,
268+
})
269+
if resp.Diagnostics.HasError() {
270+
return
271+
}
265272

266273
// Map response body to schema
267274
err = mapFieldsCreate(userResp, &model, region)
@@ -448,10 +455,12 @@ func (r *userResource) ImportState(ctx context.Context, req resource.ImportState
448455
return
449456
}
450457

451-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
452-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), idParts[1])...)
453-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[2])...)
454-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("user_id"), idParts[3])...)
458+
ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
459+
"project_id": idParts[0],
460+
"region": idParts[1],
461+
"instance_id": idParts[2],
462+
"user_id": idParts[3],
463+
})
455464
core.LogAndAddWarning(ctx, &resp.Diagnostics,
456465
"MongoDB Flex user imported with empty password and empty uri",
457466
"The user password and uri are not imported as they are only available upon creation of a new user. The password and uri fields will be empty.",

0 commit comments

Comments
 (0)