Skip to content

Commit a217638

Browse files
Immutable backups (#14080) (#23087)
[upstream:c3228e5fea156d1729f529cecc08435f0febd2ed] Signed-off-by: Modular Magician <[email protected]>
1 parent fed0d73 commit a217638

File tree

4 files changed

+377
-0
lines changed

4 files changed

+377
-0
lines changed

google/services/netapp/resource_netapp_backup_test.go

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,3 +341,173 @@ resource "google_netapp_backup" "test_backup" {
341341
}
342342
`, context)
343343
}
344+
345+
func TestAccNetappBackup_NetappImmutableBackup(t *testing.T) {
346+
context := map[string]interface{}{
347+
"network_name": acctest.BootstrapSharedServiceNetworkingConnection(t, "gcnv-network-config-3", acctest.ServiceNetworkWithParentService("netapp.servicenetworking.goog")),
348+
"random_suffix": acctest.RandString(t, 10),
349+
}
350+
351+
acctest.VcrTest(t, resource.TestCase{
352+
PreCheck: func() { acctest.AccTestPreCheck(t) },
353+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
354+
CheckDestroy: testAccCheckNetappBackupDestroyProducer(t),
355+
ExternalProviders: map[string]resource.ExternalProvider{
356+
"time": {},
357+
},
358+
Steps: []resource.TestStep{
359+
{
360+
Config: testAccNetappBackup_ImmutableBackup(context),
361+
},
362+
{
363+
ResourceName: "google_netapp_backup.test_backup",
364+
ImportState: true,
365+
ImportStateVerify: true,
366+
ImportStateVerifyIgnore: []string{"labels", "location", "name", "terraform_labels", "vault_name"},
367+
},
368+
{
369+
Config: testAccNetappBackup_ImmutableBackupUpdate(context),
370+
},
371+
{
372+
ResourceName: "google_netapp_backup.test_backup",
373+
ImportState: true,
374+
ImportStateVerify: true,
375+
ImportStateVerifyIgnore: []string{"labels", "location", "name", "terraform_labels", "vault_name"},
376+
},
377+
},
378+
})
379+
}
380+
381+
func testAccNetappBackup_ImmutableBackup(context map[string]interface{}) string {
382+
return acctest.Nprintf(`
383+
data "google_compute_network" "default" {
384+
name = "%{network_name}"
385+
}
386+
resource "google_netapp_storage_pool" "default" {
387+
name = "tf-test-backup-pool%{random_suffix}"
388+
location = "us-central1"
389+
service_level = "FLEX"
390+
capacity_gib = "2048"
391+
network = data.google_compute_network.default.id
392+
zone = "us-central1-a"
393+
replica_zone = "us-central1-b"
394+
}
395+
resource "time_sleep" "wait_3_minutes" {
396+
depends_on = [google_netapp_storage_pool.default]
397+
create_duration = "3m"
398+
}
399+
resource "google_netapp_volume" "default" {
400+
name = "tf-test-backup-volume%{random_suffix}"
401+
location = "us-central1"
402+
capacity_gib = "100"
403+
share_name = "tf-test-backup-volume%{random_suffix}"
404+
storage_pool = google_netapp_storage_pool.default.name
405+
protocols = ["NFSV3"]
406+
deletion_policy = "FORCE"
407+
backup_config {
408+
backup_vault = google_netapp_backup_vault.default.id
409+
}
410+
}
411+
resource "google_netapp_backup_vault" "default" {
412+
name = "tf-test-backup-vault%{random_suffix}"
413+
location = "us-central1"
414+
backup_retention_policy {
415+
backup_minimum_enforced_retention_days = 2
416+
daily_backup_immutable = true
417+
weekly_backup_immutable = false
418+
monthly_backup_immutable = false
419+
manual_backup_immutable = false
420+
}
421+
}
422+
resource "google_netapp_volume_snapshot" "default" {
423+
depends_on = [google_netapp_volume.default]
424+
location = "us-central1"
425+
volume_name = google_netapp_volume.default.name
426+
description = "This is a test description"
427+
name = "testvolumesnap%{random_suffix}"
428+
labels = {
429+
key= "test"
430+
value= "snapshot"
431+
}
432+
}
433+
resource "google_netapp_backup" "test_backup" {
434+
name = "tf-test-test-backup%{random_suffix}"
435+
description = "This is a test immutable backup"
436+
source_volume = google_netapp_volume.default.id
437+
location = "us-central1"
438+
vault_name = google_netapp_backup_vault.default.name
439+
source_snapshot = google_netapp_volume_snapshot.default.id
440+
labels = {
441+
key= "test"
442+
value= "backup"
443+
}
444+
}
445+
`, context)
446+
}
447+
448+
func testAccNetappBackup_ImmutableBackupUpdate(context map[string]interface{}) string {
449+
return acctest.Nprintf(`
450+
data "google_compute_network" "default" {
451+
name = "%{network_name}"
452+
}
453+
resource "google_netapp_storage_pool" "default" {
454+
name = "tf-test-backup-pool%{random_suffix}"
455+
location = "us-central1"
456+
service_level = "FLEX"
457+
capacity_gib = "2048"
458+
network = data.google_compute_network.default.id
459+
zone = "us-central1-a"
460+
replica_zone = "us-central1-b"
461+
}
462+
resource "time_sleep" "wait_3_minutes" {
463+
depends_on = [google_netapp_storage_pool.default]
464+
create_duration = "3m"
465+
}
466+
resource "google_netapp_volume" "default" {
467+
name = "tf-test-backup-volume%{random_suffix}"
468+
location = "us-central1"
469+
capacity_gib = "100"
470+
share_name = "tf-test-backup-volume%{random_suffix}"
471+
storage_pool = google_netapp_storage_pool.default.name
472+
protocols = ["NFSV3"]
473+
deletion_policy = "FORCE"
474+
backup_config {
475+
backup_vault = google_netapp_backup_vault.default.id
476+
}
477+
}
478+
resource "google_netapp_backup_vault" "default" {
479+
name = "tf-test-backup-vault%{random_suffix}"
480+
location = "us-central1"
481+
backup_retention_policy {
482+
backup_minimum_enforced_retention_days = 12
483+
daily_backup_immutable = true
484+
weekly_backup_immutable = true
485+
monthly_backup_immutable = true
486+
manual_backup_immutable = true
487+
}
488+
}
489+
resource "google_netapp_volume_snapshot" "default" {
490+
depends_on = [google_netapp_volume.default]
491+
location = "us-central1"
492+
volume_name = google_netapp_volume.default.name
493+
description = "This is a test description"
494+
name = "testvolumesnap%{random_suffix}"
495+
labels = {
496+
key= "test"
497+
value= "snapshot"
498+
}
499+
}
500+
resource "google_netapp_backup" "test_backup" {
501+
name = "tf-test-test-backup%{random_suffix}"
502+
description = "This is a test immutable backup"
503+
source_volume = google_netapp_volume.default.id
504+
location = "us-central1"
505+
vault_name = google_netapp_backup_vault.default.name
506+
source_snapshot = google_netapp_volume_snapshot.default.id
507+
labels = {
508+
key= "test"
509+
value= "backup"
510+
}
511+
}
512+
`, context)
513+
}

google/services/netapp/resource_netapp_backup_vault.go

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,41 @@ func ResourceNetappBackupVault() *schema.Resource {
7575
Optional: true,
7676
Description: `Region in which backup is stored.`,
7777
},
78+
"backup_retention_policy": {
79+
Type: schema.TypeList,
80+
Optional: true,
81+
Description: `Backup retention policy defining the retention of the backups.`,
82+
MaxItems: 1,
83+
Elem: &schema.Resource{
84+
Schema: map[string]*schema.Schema{
85+
"backup_minimum_enforced_retention_days": {
86+
Type: schema.TypeInt,
87+
Required: true,
88+
Description: `Minimum retention duration in days for backups in the backup vault.`,
89+
},
90+
"daily_backup_immutable": {
91+
Type: schema.TypeBool,
92+
Optional: true,
93+
Description: `Indicates if the daily backups are immutable. At least one of daily_backup_immutable, weekly_backup_immutable, monthly_backup_immutable and manual_backup_immutable must be true.`,
94+
},
95+
"manual_backup_immutable": {
96+
Type: schema.TypeBool,
97+
Optional: true,
98+
Description: `Indicates if the manual backups are immutable. At least one of daily_backup_immutable, weekly_backup_immutable, monthly_backup_immutable and manual_backup_immutable must be true.`,
99+
},
100+
"monthly_backup_immutable": {
101+
Type: schema.TypeBool,
102+
Optional: true,
103+
Description: `Indicates if the monthly backups are immutable. At least one of daily_backup_immutable, weekly_backup_immutable, monthly_backup_immutable and manual_backup_immutable must be true.`,
104+
},
105+
"weekly_backup_immutable": {
106+
Type: schema.TypeBool,
107+
Optional: true,
108+
Description: `Indicates if the weekly backups are immutable. At least one of daily_backup_immutable, weekly_backup_immutable, monthly_backup_immutable and manual_backup_immutable must be true.`,
109+
},
110+
},
111+
},
112+
},
78113
"backup_vault_type": {
79114
Type: schema.TypeString,
80115
Computed: true,
@@ -172,6 +207,12 @@ func resourceNetappBackupVaultCreate(d *schema.ResourceData, meta interface{}) e
172207
} else if v, ok := d.GetOkExists("backup_region"); !tpgresource.IsEmptyValue(reflect.ValueOf(backupRegionProp)) && (ok || !reflect.DeepEqual(v, backupRegionProp)) {
173208
obj["backupRegion"] = backupRegionProp
174209
}
210+
backupRetentionPolicyProp, err := expandNetappBackupVaultBackupRetentionPolicy(d.Get("backup_retention_policy"), d, config)
211+
if err != nil {
212+
return err
213+
} else if v, ok := d.GetOkExists("backup_retention_policy"); !tpgresource.IsEmptyValue(reflect.ValueOf(backupRetentionPolicyProp)) && (ok || !reflect.DeepEqual(v, backupRetentionPolicyProp)) {
214+
obj["backupRetentionPolicy"] = backupRetentionPolicyProp
215+
}
175216
labelsProp, err := expandNetappBackupVaultEffectiveLabels(d.Get("effective_labels"), d, config)
176217
if err != nil {
177218
return err
@@ -304,6 +345,9 @@ func resourceNetappBackupVaultRead(d *schema.ResourceData, meta interface{}) err
304345
if err := d.Set("destination_backup_vault", flattenNetappBackupVaultDestinationBackupVault(res["destinationBackupVault"], d, config)); err != nil {
305346
return fmt.Errorf("Error reading BackupVault: %s", err)
306347
}
348+
if err := d.Set("backup_retention_policy", flattenNetappBackupVaultBackupRetentionPolicy(res["backupRetentionPolicy"], d, config)); err != nil {
349+
return fmt.Errorf("Error reading BackupVault: %s", err)
350+
}
307351
if err := d.Set("terraform_labels", flattenNetappBackupVaultTerraformLabels(res["labels"], d, config)); err != nil {
308352
return fmt.Errorf("Error reading BackupVault: %s", err)
309353
}
@@ -348,6 +392,12 @@ func resourceNetappBackupVaultUpdate(d *schema.ResourceData, meta interface{}) e
348392
} else if v, ok := d.GetOkExists("backup_region"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, backupRegionProp)) {
349393
obj["backupRegion"] = backupRegionProp
350394
}
395+
backupRetentionPolicyProp, err := expandNetappBackupVaultBackupRetentionPolicy(d.Get("backup_retention_policy"), d, config)
396+
if err != nil {
397+
return err
398+
} else if v, ok := d.GetOkExists("backup_retention_policy"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, backupRetentionPolicyProp)) {
399+
obj["backupRetentionPolicy"] = backupRetentionPolicyProp
400+
}
351401
labelsProp, err := expandNetappBackupVaultEffectiveLabels(d.Get("effective_labels"), d, config)
352402
if err != nil {
353403
return err
@@ -376,6 +426,10 @@ func resourceNetappBackupVaultUpdate(d *schema.ResourceData, meta interface{}) e
376426
updateMask = append(updateMask, "backupRegion")
377427
}
378428

429+
if d.HasChange("backup_retention_policy") {
430+
updateMask = append(updateMask, "backupRetentionPolicy")
431+
}
432+
379433
if d.HasChange("effective_labels") {
380434
updateMask = append(updateMask, "labels")
381435
}
@@ -545,6 +599,60 @@ func flattenNetappBackupVaultDestinationBackupVault(v interface{}, d *schema.Res
545599
return v
546600
}
547601

602+
func flattenNetappBackupVaultBackupRetentionPolicy(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
603+
if v == nil {
604+
return nil
605+
}
606+
original := v.(map[string]interface{})
607+
if len(original) == 0 {
608+
return nil
609+
}
610+
transformed := make(map[string]interface{})
611+
transformed["backup_minimum_enforced_retention_days"] =
612+
flattenNetappBackupVaultBackupRetentionPolicyBackupMinimumEnforcedRetentionDays(original["backupMinimumEnforcedRetentionDays"], d, config)
613+
transformed["daily_backup_immutable"] =
614+
flattenNetappBackupVaultBackupRetentionPolicyDailyBackupImmutable(original["dailyBackupImmutable"], d, config)
615+
transformed["weekly_backup_immutable"] =
616+
flattenNetappBackupVaultBackupRetentionPolicyWeeklyBackupImmutable(original["weeklyBackupImmutable"], d, config)
617+
transformed["monthly_backup_immutable"] =
618+
flattenNetappBackupVaultBackupRetentionPolicyMonthlyBackupImmutable(original["monthlyBackupImmutable"], d, config)
619+
transformed["manual_backup_immutable"] =
620+
flattenNetappBackupVaultBackupRetentionPolicyManualBackupImmutable(original["manualBackupImmutable"], d, config)
621+
return []interface{}{transformed}
622+
}
623+
func flattenNetappBackupVaultBackupRetentionPolicyBackupMinimumEnforcedRetentionDays(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
624+
// Handles the string fixed64 format
625+
if strVal, ok := v.(string); ok {
626+
if intVal, err := tpgresource.StringToFixed64(strVal); err == nil {
627+
return intVal
628+
}
629+
}
630+
631+
// number values are represented as float64
632+
if floatVal, ok := v.(float64); ok {
633+
intVal := int(floatVal)
634+
return intVal
635+
}
636+
637+
return v // let terraform core handle it otherwise
638+
}
639+
640+
func flattenNetappBackupVaultBackupRetentionPolicyDailyBackupImmutable(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
641+
return v
642+
}
643+
644+
func flattenNetappBackupVaultBackupRetentionPolicyWeeklyBackupImmutable(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
645+
return v
646+
}
647+
648+
func flattenNetappBackupVaultBackupRetentionPolicyMonthlyBackupImmutable(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
649+
return v
650+
}
651+
652+
func flattenNetappBackupVaultBackupRetentionPolicyManualBackupImmutable(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
653+
return v
654+
}
655+
548656
func flattenNetappBackupVaultTerraformLabels(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
549657
if v == nil {
550658
return v
@@ -576,6 +684,73 @@ func expandNetappBackupVaultBackupRegion(v interface{}, d tpgresource.TerraformR
576684
return v, nil
577685
}
578686

687+
func expandNetappBackupVaultBackupRetentionPolicy(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
688+
l := v.([]interface{})
689+
if len(l) == 0 || l[0] == nil {
690+
return nil, nil
691+
}
692+
raw := l[0]
693+
original := raw.(map[string]interface{})
694+
transformed := make(map[string]interface{})
695+
696+
transformedBackupMinimumEnforcedRetentionDays, err := expandNetappBackupVaultBackupRetentionPolicyBackupMinimumEnforcedRetentionDays(original["backup_minimum_enforced_retention_days"], d, config)
697+
if err != nil {
698+
return nil, err
699+
} else if val := reflect.ValueOf(transformedBackupMinimumEnforcedRetentionDays); val.IsValid() && !tpgresource.IsEmptyValue(val) {
700+
transformed["backupMinimumEnforcedRetentionDays"] = transformedBackupMinimumEnforcedRetentionDays
701+
}
702+
703+
transformedDailyBackupImmutable, err := expandNetappBackupVaultBackupRetentionPolicyDailyBackupImmutable(original["daily_backup_immutable"], d, config)
704+
if err != nil {
705+
return nil, err
706+
} else if val := reflect.ValueOf(transformedDailyBackupImmutable); val.IsValid() && !tpgresource.IsEmptyValue(val) {
707+
transformed["dailyBackupImmutable"] = transformedDailyBackupImmutable
708+
}
709+
710+
transformedWeeklyBackupImmutable, err := expandNetappBackupVaultBackupRetentionPolicyWeeklyBackupImmutable(original["weekly_backup_immutable"], d, config)
711+
if err != nil {
712+
return nil, err
713+
} else if val := reflect.ValueOf(transformedWeeklyBackupImmutable); val.IsValid() && !tpgresource.IsEmptyValue(val) {
714+
transformed["weeklyBackupImmutable"] = transformedWeeklyBackupImmutable
715+
}
716+
717+
transformedMonthlyBackupImmutable, err := expandNetappBackupVaultBackupRetentionPolicyMonthlyBackupImmutable(original["monthly_backup_immutable"], d, config)
718+
if err != nil {
719+
return nil, err
720+
} else if val := reflect.ValueOf(transformedMonthlyBackupImmutable); val.IsValid() && !tpgresource.IsEmptyValue(val) {
721+
transformed["monthlyBackupImmutable"] = transformedMonthlyBackupImmutable
722+
}
723+
724+
transformedManualBackupImmutable, err := expandNetappBackupVaultBackupRetentionPolicyManualBackupImmutable(original["manual_backup_immutable"], d, config)
725+
if err != nil {
726+
return nil, err
727+
} else if val := reflect.ValueOf(transformedManualBackupImmutable); val.IsValid() && !tpgresource.IsEmptyValue(val) {
728+
transformed["manualBackupImmutable"] = transformedManualBackupImmutable
729+
}
730+
731+
return transformed, nil
732+
}
733+
734+
func expandNetappBackupVaultBackupRetentionPolicyBackupMinimumEnforcedRetentionDays(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
735+
return v, nil
736+
}
737+
738+
func expandNetappBackupVaultBackupRetentionPolicyDailyBackupImmutable(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
739+
return v, nil
740+
}
741+
742+
func expandNetappBackupVaultBackupRetentionPolicyWeeklyBackupImmutable(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
743+
return v, nil
744+
}
745+
746+
func expandNetappBackupVaultBackupRetentionPolicyMonthlyBackupImmutable(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
747+
return v, nil
748+
}
749+
750+
func expandNetappBackupVaultBackupRetentionPolicyManualBackupImmutable(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
751+
return v, nil
752+
}
753+
579754
func expandNetappBackupVaultEffectiveLabels(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (map[string]string, error) {
580755
if v == nil {
581756
return map[string]string{}, nil

0 commit comments

Comments
 (0)