Skip to content

Commit e61bfe0

Browse files
authored
Set backupSchedule as required in MongoDB Flex instance (#96)
* Set backupSchedule as required in MongoDB Flex instance * Update example and generate docs * Add regex explanation comment * Fix acceptance test * Fix map fields
1 parent 59ee1b5 commit e61bfe0

File tree

5 files changed

+134
-27
lines changed

5 files changed

+134
-27
lines changed

docs/resources/mongodbflex_instance.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ resource "stackit_mongodbflex_instance" "example" {
3030
options = {
3131
type = "Single"
3232
}
33+
backup_schedule = "0 0 * * *"
3334
}
3435
```
3536

@@ -39,6 +40,7 @@ resource "stackit_mongodbflex_instance" "example" {
3940
### Required
4041

4142
- `acl` (List of String) The Access Control List (ACL) for the MongoDB Flex instance.
43+
- `backup_schedule` (String)
4244
- `flavor` (Attributes) (see [below for nested schema](#nestedatt--flavor))
4345
- `name` (String) Instance name.
4446
- `options` (Attributes) (see [below for nested schema](#nestedatt--options))
@@ -49,7 +51,6 @@ resource "stackit_mongodbflex_instance" "example" {
4951

5052
### Read-Only
5153

52-
- `backup_schedule` (String)
5354
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`instance_id`".
5455
- `instance_id` (String) ID of the MongoDB Flex instance.
5556

examples/resources/stackit_mongodbflex_instance/resource.tf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ resource "stackit_mongodbflex_instance" "example" {
1515
options = {
1616
type = "Single"
1717
}
18+
backup_schedule = "0 0 * * *"
1819
}

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

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"github.com/hashicorp/terraform-plugin-framework/resource"
2222
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
2323
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
24-
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
2524
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
2625
"github.com/hashicorp/terraform-plugin-framework/types"
2726
"github.com/stackitcloud/stackit-sdk-go/core/config"
@@ -206,11 +205,7 @@ func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, r
206205
Required: true,
207206
},
208207
"backup_schedule": schema.StringAttribute{
209-
Computed: true, // Update functionality for this field is currently not working properly on the API side
210-
PlanModifiers: []planmodifier.String{
211-
stringplanmodifier.UseStateForUnknown(),
212-
},
213-
Default: stringdefault.StaticString(DefaultBackupSchedule), // Using the same default value as the Portal, as the field is required
208+
Required: true,
214209
},
215210
"flavor": schema.SingleNestedAttribute{
216211
Required: true,
@@ -637,6 +632,13 @@ func mapFields(resp *mongodbflex.GetInstanceResponse, model *Model, flavor *flav
637632
return fmt.Errorf("creating options: %w", core.DiagsToError(diags))
638633
}
639634

635+
simplifiedModelBackupSchedule := simplifyBackupSchedule(model.BackupSchedule.ValueString())
636+
// If the value returned by the API is different from the one in the model after simplification,
637+
// we update the model so that it causes an error in Terraform
638+
if simplifiedModelBackupSchedule != types.StringPointerValue(instance.BackupSchedule).ValueString() {
639+
model.BackupSchedule = types.StringPointerValue(instance.BackupSchedule)
640+
}
641+
640642
idParts := []string{
641643
model.ProjectId.ValueString(),
642644
instanceId,
@@ -647,7 +649,6 @@ func mapFields(resp *mongodbflex.GetInstanceResponse, model *Model, flavor *flav
647649
model.InstanceId = types.StringValue(instanceId)
648650
model.Name = types.StringPointerValue(instance.Name)
649651
model.ACL = aclList
650-
model.BackupSchedule = types.StringPointerValue(instance.BackupSchedule)
651652
model.Flavor = flavorObject
652653
model.Replicas = conversion.ToTypeInt64(instance.Replicas)
653654
model.Storage = storageObject
@@ -781,3 +782,17 @@ func loadFlavorId(ctx context.Context, client mongoDBFlexClient, model *Model, f
781782

782783
return nil
783784
}
785+
786+
// Remove leading 0s from backup schedule numbers (e.g. "00 00 * * *" becomes "0 0 * * *")
787+
// Needed as the API does it internally and would otherwise cause inconsistent result in Terraform
788+
func simplifyBackupSchedule(schedule string) string {
789+
regex := regexp.MustCompile(`0+\d+`) // Matches series of one or more zeros followed by a series of one or more digits
790+
simplifiedSchedule := regex.ReplaceAllStringFunc(schedule, func(match string) string {
791+
simplified := strings.TrimLeft(match, "0")
792+
if simplified == "" {
793+
simplified = "0"
794+
}
795+
return simplified
796+
})
797+
return simplifiedSchedule
798+
}

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

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,3 +748,75 @@ func TestLoadFlavorId(t *testing.T) {
748748
})
749749
}
750750
}
751+
752+
func TestSimplifyBackupSchedule(t *testing.T) {
753+
tests := []struct {
754+
description string
755+
input string
756+
expected string
757+
}{
758+
{
759+
"simple schedule",
760+
"0 0 * * *",
761+
"0 0 * * *",
762+
},
763+
{
764+
"schedule with leading zeros",
765+
"00 00 * * *",
766+
"0 0 * * *",
767+
},
768+
{
769+
"schedule with leading zeros 2",
770+
"00 001 * * *",
771+
"0 1 * * *",
772+
},
773+
{
774+
"schedule with leading zeros 3",
775+
"00 0010 * * *",
776+
"0 10 * * *",
777+
},
778+
{
779+
"simple schedule with slash",
780+
"0 0/6 * * *",
781+
"0 0/6 * * *",
782+
},
783+
{
784+
"schedule with leading zeros and slash",
785+
"00 00/6 * * *",
786+
"0 0/6 * * *",
787+
},
788+
{
789+
"schedule with leading zeros and slash 2",
790+
"00 001/06 * * *",
791+
"0 1/6 * * *",
792+
},
793+
{
794+
"simple schedule with comma",
795+
"0 10,15 * * *",
796+
"0 10,15 * * *",
797+
},
798+
{
799+
"schedule with leading zeros and comma",
800+
"0 010,0015 * * *",
801+
"0 10,15 * * *",
802+
},
803+
{
804+
"simple schedule with comma and slash",
805+
"0 0-11/10 * * *",
806+
"0 0-11/10 * * *",
807+
},
808+
{
809+
"schedule with leading zeros, comma, and slash",
810+
"00 000-011/010 * * *",
811+
"0 0-11/10 * * *",
812+
},
813+
}
814+
for _, tt := range tests {
815+
t.Run(tt.description, func(t *testing.T) {
816+
output := simplifyBackupSchedule(tt.input)
817+
if output != tt.expected {
818+
t.Fatalf("Data does not match: %s", output)
819+
}
820+
})
821+
}
822+
}

stackit/internal/services/mongodbflex/mongodbflex_acc_test.go

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,22 @@ import (
2020

2121
// Instance resource data
2222
var instanceResource = map[string]string{
23-
"project_id": testutil.ProjectId,
24-
"name": fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum)),
25-
"acl": "192.168.0.0/16",
26-
"flavor_cpu": "2",
27-
"flavor_ram": "4",
28-
"flavor_description": "Small, Compute optimized",
29-
"replicas": "1",
30-
"storage_class": "premium-perf2-mongodb",
31-
"storage_size": "10",
32-
"version": "5.0",
33-
"version_updated": "6.0",
34-
"options_type": "Single",
35-
"flavor_id": "2.4",
23+
"project_id": testutil.ProjectId,
24+
"name": fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum)),
25+
"acl": "192.168.0.0/16",
26+
"flavor_cpu": "2",
27+
"flavor_ram": "4",
28+
"flavor_description": "Small, Compute optimized",
29+
"replicas": "1",
30+
"storage_class": "premium-perf2-mongodb",
31+
"storage_size": "10",
32+
"version": "5.0",
33+
"version_updated": "6.0",
34+
"options_type": "Single",
35+
"flavor_id": "2.4",
36+
"backup_schedule": "00 6 * * *",
37+
"backup_schedule_updated": "00 12 * * *",
38+
"backup_schedule_read": "0 6 * * *",
3639
}
3740

3841
// User resource data
@@ -43,7 +46,7 @@ var userResource = map[string]string{
4346
"project_id": instanceResource["project_id"],
4447
}
4548

46-
func configResources(version string) string {
49+
func configResources(version, backupSchedule string) string {
4750
return fmt.Sprintf(`
4851
%s
4952
@@ -64,6 +67,7 @@ func configResources(version string) string {
6467
options = {
6568
type = "%s"
6669
}
70+
backup_schedule = "%s"
6771
}
6872
6973
resource "stackit_mongodbflex_user" "user" {
@@ -85,6 +89,7 @@ func configResources(version string) string {
8589
instanceResource["storage_size"],
8690
version,
8791
instanceResource["options_type"],
92+
backupSchedule,
8893
userResource["username"],
8994
userResource["role"],
9095
userResource["database"],
@@ -98,7 +103,7 @@ func TestAccMongoDBFlexFlexResource(t *testing.T) {
98103
Steps: []resource.TestStep{
99104
// Creation
100105
{
101-
Config: configResources(instanceResource["version"]),
106+
Config: configResources(instanceResource["version"], instanceResource["backup_schedule"]),
102107
Check: resource.ComposeAggregateTestCheckFunc(
103108
// Instance
104109
resource.TestCheckResourceAttr("stackit_mongodbflex_instance.instance", "project_id", instanceResource["project_id"]),
@@ -115,6 +120,7 @@ func TestAccMongoDBFlexFlexResource(t *testing.T) {
115120
resource.TestCheckResourceAttr("stackit_mongodbflex_instance.instance", "storage.size", instanceResource["storage_size"]),
116121
resource.TestCheckResourceAttr("stackit_mongodbflex_instance.instance", "version", instanceResource["version"]),
117122
resource.TestCheckResourceAttr("stackit_mongodbflex_instance.instance", "options.type", instanceResource["options_type"]),
123+
resource.TestCheckResourceAttr("stackit_mongodbflex_instance.instance", "backup_schedule", instanceResource["backup_schedule"]),
118124

119125
// User
120126
resource.TestCheckResourceAttrPair(
@@ -147,7 +153,7 @@ func TestAccMongoDBFlexFlexResource(t *testing.T) {
147153
user_id = stackit_mongodbflex_user.user.user_id
148154
}
149155
`,
150-
configResources(instanceResource["version"]),
156+
configResources(instanceResource["version"], instanceResource["backup_schedule"]),
151157
),
152158
Check: resource.ComposeAggregateTestCheckFunc(
153159
// Instance data
@@ -174,6 +180,7 @@ func TestAccMongoDBFlexFlexResource(t *testing.T) {
174180
resource.TestCheckResourceAttr("data.stackit_mongodbflex_instance.instance", "flavor.ram", instanceResource["flavor_ram"]),
175181
resource.TestCheckResourceAttr("data.stackit_mongodbflex_instance.instance", "replicas", instanceResource["replicas"]),
176182
resource.TestCheckResourceAttr("data.stackit_mongodbflex_instance.instance", "options.type", instanceResource["options_type"]),
183+
resource.TestCheckResourceAttr("data.stackit_mongodbflex_instance.instance", "backup_schedule", instanceResource["backup_schedule_read"]),
177184

178185
// User data
179186
resource.TestCheckResourceAttr("data.stackit_mongodbflex_user.user", "project_id", userResource["project_id"]),
@@ -201,8 +208,18 @@ func TestAccMongoDBFlexFlexResource(t *testing.T) {
201208

202209
return fmt.Sprintf("%s,%s", testutil.ProjectId, instanceId), nil
203210
},
204-
ImportState: true,
205-
ImportStateVerify: true,
211+
ImportState: true,
212+
ImportStateVerify: true,
213+
ImportStateVerifyIgnore: []string{"backup_schedule"},
214+
ImportStateCheck: func(s []*terraform.InstanceState) error {
215+
if len(s) != 1 {
216+
return fmt.Errorf("expected 1 state, got %d", len(s))
217+
}
218+
if s[0].Attributes["backup_schedule"] != instanceResource["backup_schedule_read"] {
219+
return fmt.Errorf("expected backup_schedule %s, got %s", instanceResource["backup_schedule_read"], s[0].Attributes["backup_schedule"])
220+
}
221+
return nil
222+
},
206223
},
207224
{
208225
ResourceName: "stackit_mongodbflex_user.user",
@@ -228,7 +245,7 @@ func TestAccMongoDBFlexFlexResource(t *testing.T) {
228245
},
229246
// Update
230247
{
231-
Config: configResources(instanceResource["version_updated"]),
248+
Config: configResources(instanceResource["version_updated"], instanceResource["backup_schedule_updated"]),
232249
Check: resource.ComposeAggregateTestCheckFunc(
233250
// Instance data
234251
resource.TestCheckResourceAttr("stackit_mongodbflex_instance.instance", "project_id", instanceResource["project_id"]),
@@ -245,6 +262,7 @@ func TestAccMongoDBFlexFlexResource(t *testing.T) {
245262
resource.TestCheckResourceAttr("stackit_mongodbflex_instance.instance", "storage.size", instanceResource["storage_size"]),
246263
resource.TestCheckResourceAttr("stackit_mongodbflex_instance.instance", "version", instanceResource["version_updated"]),
247264
resource.TestCheckResourceAttr("stackit_mongodbflex_instance.instance", "options.type", instanceResource["options_type"]),
265+
resource.TestCheckResourceAttr("stackit_mongodbflex_instance.instance", "backup_schedule", instanceResource["backup_schedule_updated"]),
248266
),
249267
},
250268
// Deletion is done by the framework implicitly

0 commit comments

Comments
 (0)