Skip to content

Commit 717ba68

Browse files
authored
fix: Uses overwriteBackupPolicies in mongodbatlas_backup_compliance_policy to avoid overwriting non complying backup policies in updates (#2054)
1 parent 3c68921 commit 717ba68

File tree

2 files changed

+182
-13
lines changed

2 files changed

+182
-13
lines changed

internal/service/backupcompliancepolicy/resource_backup_compliance_policy.go

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ import (
88
"net/http"
99
"strings"
1010

11+
"go.mongodb.org/atlas-sdk/v20231115008/admin"
12+
1113
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
1214
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
15+
"github.com/spf13/cast"
16+
1317
"github.com/mongodb/terraform-provider-mongodbatlas/internal/common/conversion"
1418
"github.com/mongodb/terraform-provider-mongodbatlas/internal/config"
1519
"github.com/mongodb/terraform-provider-mongodbatlas/internal/service/cloudbackupschedule"
16-
"github.com/spf13/cast"
17-
"go.mongodb.org/atlas-sdk/v20231115008/admin"
1820
)
1921

2022
const (
@@ -233,7 +235,7 @@ func resourceCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.
233235
connV2 := meta.(*config.MongoDBClient).AtlasV2
234236
projectID := d.Get("project_id").(string)
235237

236-
params := &admin.DataProtectionSettings20231001{
238+
dataProtectionSettings := &admin.DataProtectionSettings20231001{
237239
ProjectId: conversion.StringPtr(projectID),
238240
AuthorizedEmail: d.Get("authorized_email").(string),
239241
AuthorizedUserFirstName: d.Get("authorized_user_first_name").(string),
@@ -291,10 +293,15 @@ func resourceCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.
291293
}
292294
}
293295
if len(backupPoliciesItem) > 0 {
294-
params.ScheduledPolicyItems = &backupPoliciesItem
296+
dataProtectionSettings.ScheduledPolicyItems = &backupPoliciesItem
295297
}
296298

297-
_, _, err := connV2.CloudBackupsApi.UpdateDataProtectionSettings(ctx, projectID, params).Execute()
299+
params := admin.UpdateDataProtectionSettingsApiParams{
300+
GroupId: projectID,
301+
DataProtectionSettings20231001: dataProtectionSettings,
302+
OverwriteBackupPolicies: conversion.Pointer(false),
303+
}
304+
_, _, err := connV2.CloudBackupsApi.UpdateDataProtectionSettingsWithParams(ctx, &params).Execute()
298305
if err != nil {
299306
return diag.FromErr(fmt.Errorf(errorBackupPolicyUpdate, projectID, err))
300307
}
@@ -393,7 +400,7 @@ func resourceUpdate(ctx context.Context, d *schema.ResourceData, meta any) diag.
393400
ids := conversion.DecodeStateID(d.Id())
394401
projectID := ids["project_id"]
395402

396-
params := &admin.DataProtectionSettings20231001{
403+
dataProtectionSettings := &admin.DataProtectionSettings20231001{
397404
ProjectId: conversion.StringPtr(projectID),
398405
AuthorizedEmail: d.Get("authorized_email").(string),
399406
AuthorizedUserFirstName: d.Get("authorized_user_first_name").(string),
@@ -402,19 +409,19 @@ func resourceUpdate(ctx context.Context, d *schema.ResourceData, meta any) diag.
402409
}
403410

404411
if d.HasChange("copy_protection_enabled") {
405-
params.CopyProtectionEnabled = conversion.Pointer(d.Get("copy_protection_enabled").(bool))
412+
dataProtectionSettings.CopyProtectionEnabled = conversion.Pointer(d.Get("copy_protection_enabled").(bool))
406413
}
407414

408415
if d.HasChange("encryption_at_rest_enabled") {
409-
params.EncryptionAtRestEnabled = conversion.Pointer(d.Get("encryption_at_rest_enabled").(bool))
416+
dataProtectionSettings.EncryptionAtRestEnabled = conversion.Pointer(d.Get("encryption_at_rest_enabled").(bool))
410417
}
411418

412419
if d.HasChange("pit_enabled") {
413-
params.PitEnabled = conversion.Pointer(d.Get("pit_enabled").(bool))
420+
dataProtectionSettings.PitEnabled = conversion.Pointer(d.Get("pit_enabled").(bool))
414421
}
415422

416423
if d.HasChange("restore_window_days") {
417-
params.RestoreWindowDays = conversion.Pointer(cast.ToInt(d.Get("restore_window_days")))
424+
dataProtectionSettings.RestoreWindowDays = conversion.Pointer(cast.ToInt(d.Get("restore_window_days")))
418425
}
419426

420427
var backupPoliciesItem []admin.BackupComplianceScheduledPolicyItem
@@ -463,10 +470,15 @@ func resourceUpdate(ctx context.Context, d *schema.ResourceData, meta any) diag.
463470
}
464471
}
465472
if len(backupPoliciesItem) > 0 {
466-
params.ScheduledPolicyItems = &backupPoliciesItem
473+
dataProtectionSettings.ScheduledPolicyItems = &backupPoliciesItem
467474
}
468475

469-
_, _, err := connV2.CloudBackupsApi.UpdateDataProtectionSettings(ctx, projectID, params).Execute()
476+
params := admin.UpdateDataProtectionSettingsApiParams{
477+
GroupId: projectID,
478+
DataProtectionSettings20231001: dataProtectionSettings,
479+
OverwriteBackupPolicies: conversion.Pointer(false),
480+
}
481+
_, _, err := connV2.CloudBackupsApi.UpdateDataProtectionSettingsWithParams(ctx, &params).Execute()
470482
if err != nil {
471483
return diag.FromErr(fmt.Errorf(errorBackupPolicyUpdate, projectID, err))
472484
}

internal/service/backupcompliancepolicy/resource_backup_compliance_policy_test.go

Lines changed: 158 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import (
44
"context"
55
"fmt"
66
"os"
7+
"regexp"
78
"testing"
89

910
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
1011
"github.com/hashicorp/terraform-plugin-testing/terraform"
12+
1113
"github.com/mongodb/terraform-provider-mongodbatlas/internal/common/conversion"
1214
"github.com/mongodb/terraform-provider-mongodbatlas/internal/testutil/acc"
1315
)
@@ -41,7 +43,7 @@ func TestAccGenericBackupRSBackupCompliancePolicy_basic(t *testing.T) {
4143
})
4244
}
4345

44-
func TestAccGenericBackupRSBackupCompliancePolicy_withFirstLastName(t *testing.T) {
46+
func TestAccGenericBackupRSBackupCompliancePolicy_update(t *testing.T) {
4547
var (
4648
orgID = os.Getenv("MONGODB_ATLAS_ORG_ID")
4749
projectOwnerID = os.Getenv("MONGODB_ATLAS_PROJECT_OWNER_ID")
@@ -53,6 +55,17 @@ func TestAccGenericBackupRSBackupCompliancePolicy_withFirstLastName(t *testing.T
5355
ProtoV6ProviderFactories: acc.TestAccProviderV6Factories,
5456
CheckDestroy: checkDestroy,
5557
Steps: []resource.TestStep{
58+
{
59+
Config: configWithoutOptionals(projectName, orgID, projectOwnerID),
60+
Check: resource.ComposeTestCheckFunc(
61+
checkExists(resourceName),
62+
resource.TestCheckResourceAttr(resourceName, "authorized_user_first_name", "First"),
63+
resource.TestCheckResourceAttr(resourceName, "authorized_user_last_name", "Last"),
64+
resource.TestCheckResourceAttr(resourceName, "pit_enabled", "false"),
65+
resource.TestCheckResourceAttr(resourceName, "encryption_at_rest_enabled", "false"),
66+
resource.TestCheckResourceAttr(resourceName, "copy_protection_enabled", "false"),
67+
),
68+
},
5669
{
5770
Config: configBasic(projectName, orgID, projectOwnerID),
5871
Check: resource.ComposeTestCheckFunc(
@@ -65,6 +78,150 @@ func TestAccGenericBackupRSBackupCompliancePolicy_withFirstLastName(t *testing.T
6578
})
6679
}
6780

81+
func TestAccGenericBackupRSBackupCompliancePolicy_overwriteBackupPolicies(t *testing.T) {
82+
var (
83+
orgID = os.Getenv("MONGODB_ATLAS_ORG_ID")
84+
projectOwnerID = os.Getenv("MONGODB_ATLAS_PROJECT_OWNER_ID")
85+
projectName = acc.RandomProjectName()
86+
)
87+
88+
resource.ParallelTest(t, resource.TestCase{
89+
PreCheck: func() { acc.PreCheckBasic(t) },
90+
ProtoV6ProviderFactories: acc.TestAccProviderV6Factories,
91+
CheckDestroy: checkDestroy,
92+
Steps: []resource.TestStep{
93+
{
94+
Config: configClusterWithBackupSchedule(projectName, orgID, projectOwnerID),
95+
},
96+
{
97+
Config: configOverwriteIncompatibleBackupPoliciesError(projectName, orgID, projectOwnerID),
98+
ExpectError: regexp.MustCompile(`BACKUP_POLICIES_NOT_MEETING_BACKUP_COMPLIANCE_POLICY_REQUIREMENTS`),
99+
},
100+
},
101+
})
102+
}
103+
104+
func configOverwriteIncompatibleBackupPoliciesError(projectName, orgID, projectOwnerID string) string {
105+
return acc.ConfigProjectWithSettings(projectName, orgID, projectOwnerID, false) + `
106+
resource "mongodbatlas_cluster" "test" {
107+
project_id = mongodbatlas_project.test.id
108+
name = "test1"
109+
provider_name = "AWS"
110+
cluster_type = "REPLICASET"
111+
mongo_db_major_version = "6.0"
112+
provider_instance_size_name = "M10"
113+
auto_scaling_compute_enabled = false
114+
cloud_backup = true
115+
auto_scaling_disk_gb_enabled = true
116+
disk_size_gb = 12
117+
provider_volume_type = "STANDARD"
118+
retain_backups_enabled = true
119+
120+
advanced_configuration {
121+
oplog_min_retention_hours = 8
122+
}
123+
124+
replication_specs {
125+
num_shards = 1
126+
regions_config {
127+
region_name = "US_EAST_1"
128+
electable_nodes = 3
129+
priority = 7
130+
read_only_nodes = 0
131+
}
132+
}
133+
}
134+
135+
resource "mongodbatlas_cloud_backup_schedule" "test" {
136+
cluster_name = mongodbatlas_cluster.test.name
137+
project_id = mongodbatlas_project.test.id
138+
139+
reference_hour_of_day = 3
140+
reference_minute_of_hour = 45
141+
restore_window_days = 2
142+
143+
copy_settings {
144+
cloud_provider = "AWS"
145+
frequencies = ["DAILY"]
146+
region_name = "US_WEST_1"
147+
replication_spec_id = one(mongodbatlas_cluster.test.replication_specs).id
148+
should_copy_oplogs = false
149+
}
150+
}
151+
152+
resource "mongodbatlas_backup_compliance_policy" "test" {
153+
project_id = mongodbatlas_project.test.id
154+
authorized_email = "[email protected]"
155+
authorized_user_first_name = "First"
156+
authorized_user_last_name = "Last"
157+
copy_protection_enabled = true
158+
pit_enabled = false
159+
encryption_at_rest_enabled = false
160+
161+
on_demand_policy_item {
162+
frequency_interval = 1
163+
retention_unit = "days"
164+
retention_value = 1
165+
}
166+
167+
policy_item_daily {
168+
frequency_interval = 1
169+
retention_unit = "days"
170+
retention_value = 1
171+
}
172+
}
173+
`
174+
}
175+
176+
func configClusterWithBackupSchedule(projectName, orgID, projectOwnerID string) string {
177+
return acc.ConfigProjectWithSettings(projectName, orgID, projectOwnerID, false) + `
178+
resource "mongodbatlas_cluster" "test" {
179+
project_id = mongodbatlas_project.test.id
180+
name = "test1"
181+
provider_name = "AWS"
182+
cluster_type = "REPLICASET"
183+
mongo_db_major_version = "6.0"
184+
provider_instance_size_name = "M10"
185+
auto_scaling_compute_enabled = false
186+
cloud_backup = true
187+
auto_scaling_disk_gb_enabled = true
188+
disk_size_gb = 12
189+
provider_volume_type = "STANDARD"
190+
retain_backups_enabled = true
191+
192+
advanced_configuration {
193+
oplog_min_retention_hours = 8
194+
}
195+
196+
replication_specs {
197+
num_shards = 1
198+
regions_config {
199+
region_name = "US_EAST_1"
200+
electable_nodes = 3
201+
priority = 7
202+
read_only_nodes = 0
203+
}
204+
}
205+
}
206+
207+
resource "mongodbatlas_cloud_backup_schedule" "test" {
208+
cluster_name = mongodbatlas_cluster.test.name
209+
project_id = mongodbatlas_project.test.id
210+
211+
reference_hour_of_day = 3
212+
reference_minute_of_hour = 45
213+
restore_window_days = 2
214+
215+
copy_settings {
216+
cloud_provider = "AWS"
217+
frequencies = ["DAILY"]
218+
region_name = "US_WEST_1"
219+
replication_spec_id = one(mongodbatlas_cluster.test.replication_specs).id
220+
should_copy_oplogs = false
221+
}
222+
}
223+
`
224+
}
68225
func TestAccGenericBackupRSBackupCompliancePolicy_withoutOptionals(t *testing.T) {
69226
var (
70227
orgID = os.Getenv("MONGODB_ATLAS_ORG_ID")

0 commit comments

Comments
 (0)