Skip to content

Commit b87553c

Browse files
Changes to modify the google_sql_database_instance resource behavior to prevent Terraform from managing the settings.0.backup_configuration block when an instance is managed by GCBDR. (#15710)
Co-authored-by: Stephen Lewis (Burrows) <[email protected]>
1 parent d1f3420 commit b87553c

File tree

3 files changed

+293
-1
lines changed

3 files changed

+293
-1
lines changed

mmv1/third_party/terraform/services/sql/resource_sql_database_instance.go.tmpl

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ var (
7070
"settings.0.backup_configuration.0.point_in_time_recovery_enabled",
7171
"settings.0.backup_configuration.0.backup_retention_settings",
7272
"settings.0.backup_configuration.0.transaction_log_retention_days",
73+
"settings.0.backup_configuration.0.backup_tier",
7374
}
7475

7576
connectionPoolConfigKeys = []string{
@@ -163,7 +164,7 @@ func nodeCountCustomDiff(ctx context.Context, d *schema.ResourceDiff, meta inter
163164
// Otherwise when autoscaling is enabled, ignore the node count in config.
164165
return d.Clear("node_count")
165166
}
166-
167+
167168
func diskSizeCutomizeDiff(ctx context.Context, d *schema.ResourceDiff, meta interface{}) error {
168169
key := "settings.0.disk_size"
169170

@@ -415,12 +416,14 @@ API (for read pools, effective_availability_type may differ from availability_ty
415416
Type: schema.TypeBool,
416417
Optional: true,
417418
AtLeastOneOf: backupConfigurationKeys,
419+
DiffSuppressFunc: EnhancedBackupManagerDiffSuppressFunc,
418420
Description: `True if binary logging is enabled. If settings.backup_configuration.enabled is false, this must be as well. Can only be used with MySQL.`,
419421
},
420422
"enabled": {
421423
Type: schema.TypeBool,
422424
Optional: true,
423425
AtLeastOneOf: backupConfigurationKeys,
426+
DiffSuppressFunc: EnhancedBackupManagerDiffSuppressFunc,
424427
Description: `True if backup configuration is enabled.`,
425428
},
426429
"start_time": {
@@ -429,6 +432,7 @@ API (for read pools, effective_availability_type may differ from availability_ty
429432
// start_time is randomly assigned if not set
430433
Computed: true,
431434
AtLeastOneOf: backupConfigurationKeys,
435+
DiffSuppressFunc: EnhancedBackupManagerDiffSuppressFunc,
432436
Description: `HH:MM format time indicating when backup configuration starts.`,
433437
},
434438
"location": {
@@ -441,20 +445,23 @@ API (for read pools, effective_availability_type may differ from availability_ty
441445
Type: schema.TypeBool,
442446
Optional: true,
443447
AtLeastOneOf: backupConfigurationKeys,
448+
DiffSuppressFunc: EnhancedBackupManagerDiffSuppressFunc,
444449
Description: `True if Point-in-time recovery is enabled.`,
445450
},
446451
"transaction_log_retention_days": {
447452
Type: schema.TypeInt,
448453
Computed: true,
449454
Optional: true,
450455
AtLeastOneOf: backupConfigurationKeys,
456+
DiffSuppressFunc: EnhancedBackupManagerDiffSuppressFunc,
451457
Description: `The number of days of transaction logs we retain for point in time restore, from 1-7. (For PostgreSQL Enterprise Plus instances, from 1 to 35.)`,
452458
},
453459
"backup_retention_settings": {
454460
Type: schema.TypeList,
455461
Optional: true,
456462
AtLeastOneOf: backupConfigurationKeys,
457463
Computed: true,
464+
DiffSuppressFunc: EnhancedBackupManagerDiffSuppressFunc,
458465
MaxItems: 1,
459466
Elem: &schema.Resource{
460467
Schema: map[string]*schema.Schema{
@@ -472,6 +479,11 @@ API (for read pools, effective_availability_type may differ from availability_ty
472479
},
473480
},
474481
},
482+
"backup_tier": {
483+
Type: schema.TypeString,
484+
Computed: true,
485+
Description: `Backup tier that manages the backups for the instance.`,
486+
},
475487
},
476488
},
477489
},
@@ -1982,6 +1994,7 @@ func expandBackupConfiguration(configured []interface{}) *sqladmin.BackupConfigu
19821994
Location: _backupConfiguration["location"].(string),
19831995
TransactionLogRetentionDays: int64(_backupConfiguration["transaction_log_retention_days"].(int)),
19841996
PointInTimeRecoveryEnabled: _backupConfiguration["point_in_time_recovery_enabled"].(bool),
1997+
BackupTier: _backupConfiguration["backup_tier"].(string),
19851998
ForceSendFields: []string{"BinaryLogEnabled", "Enabled", "PointInTimeRecoveryEnabled"},
19861999
}
19872000
}
@@ -2661,6 +2674,25 @@ func maintenanceVersionDiffSuppress(_, old, new string, _ *schema.ResourceData)
26612674
}
26622675
}
26632676

2677+
// enhancedBackupManagerDiffSuppressFunc suppresses diff changes to settings.backup_configuration
2678+
// when the SQL instance is managed by Google Cloud Backup and Disaster Recovery (DR) Service.
2679+
func EnhancedBackupManagerDiffSuppressFunc(k, old, new string, d *schema.ResourceData) bool {
2680+
// If the API marks this instance as ENHANCED-managed, suppress diffs for backup config fields.
2681+
tier, _ := d.Get("settings.0.backup_configuration.0.backup_tier").(string)
2682+
if tier == "" {
2683+
return false
2684+
}
2685+
if strings.EqualFold(tier, "ENHANCED") {
2686+
log.Printf(
2687+
"[INFO] Instance %s is managed by Google Cloud Backup and Disaster Recovery (BackupDR). "+
2688+
"Terraform will not manage the '%s' field. "+
2689+
"Any changes to this field in your Terraform configuration will be ignored.", d.Get("name"), k,
2690+
)
2691+
return true
2692+
}
2693+
return false
2694+
}
2695+
26642696
func databaseVersionDiffSuppress(_, oldVersion, newVersion string, _ *schema.ResourceData) bool {
26652697
// Suppress diff when newVersion is MYSQL_8_0 and oldVersion is >= MYSQL_8_0_35 for MySQL version auto-upgrade cases.
26662698
if newVersion == "MYSQL_8_0" && strings.HasPrefix(oldVersion, "MYSQL_8_0_") {
@@ -2897,6 +2929,7 @@ func flattenBackupConfiguration(backupConfiguration *sqladmin.BackupConfiguratio
28972929
"point_in_time_recovery_enabled": backupConfiguration.PointInTimeRecoveryEnabled,
28982930
"backup_retention_settings": flattenBackupRetentionSettings(backupConfiguration.BackupRetentionSettings),
28992931
"transaction_log_retention_days": backupConfiguration.TransactionLogRetentionDays,
2932+
"backup_tier": backupConfiguration.BackupTier,
29002933
}
29012934

29022935
return []map[string]interface{}{data}

mmv1/third_party/terraform/services/sql/resource_sql_database_instance_test.go.tmpl

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
{{- end }}
1919
"github.com/hashicorp/terraform-plugin-testing/terraform"
2020
sqladmin "google.golang.org/api/sqladmin/v1beta4"
21+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
2122
)
2223

2324
// Fields that should be ignored in import tests because they aren't returned
@@ -3925,6 +3926,262 @@ func TestAccSqlDatabaseInstance_DiskSizeAutoResizeWithDiskSize(t *testing.T) {
39253926
})
39263927
}
39273928

3929+
func TestEnhancedBackupManagerDiffSuppressFunc(t *testing.T) {
3930+
cases := map[string]struct {
3931+
key string
3932+
old string
3933+
new string
3934+
backupTier string
3935+
expectSuppress bool
3936+
description string
3937+
}{
3938+
"suppress enabled diff when backup tier is enhanced": {
3939+
key: "settings.0.backup_configuration.0.enabled",
3940+
old: "true",
3941+
new: "false",
3942+
backupTier: "ENHANCED",
3943+
expectSuppress: true,
3944+
description: "enabled should be suppressed when ENHANCED",
3945+
},
3946+
"suppress start_time diff when backup tier is enhanced": {
3947+
key: "settings.0.backup_configuration.0.start_time",
3948+
old: "03:00",
3949+
new: "05:00",
3950+
backupTier: "ENHANCED",
3951+
expectSuppress: true,
3952+
description: "start_time should be suppressed when ENHANCED",
3953+
},
3954+
"suppress binary_log_enabled diff when backup tier is enhanced": {
3955+
key: "settings.0.backup_configuration.0.binary_log_enabled",
3956+
old: "true",
3957+
new: "false",
3958+
backupTier: "ENHANCED",
3959+
expectSuppress: true,
3960+
description: "binary_log_enabled should be suppressed when ENHANCED",
3961+
},
3962+
"suppress point_in_time_recovery_enabled diff when backup tier is enhanced": {
3963+
key: "settings.0.backup_configuration.0.point_in_time_recovery_enabled",
3964+
old: "false",
3965+
new: "true",
3966+
backupTier: "ENHANCED",
3967+
expectSuppress: true,
3968+
description: "point_in_time_recovery_enabled should be suppressed when ENHANCED",
3969+
},
3970+
"suppress transaction_log_retention_days diff when backup tier is enhanced": {
3971+
key: "settings.0.backup_configuration.0.transaction_log_retention_days",
3972+
old: "7",
3973+
new: "14",
3974+
backupTier: "ENHANCED",
3975+
expectSuppress: true,
3976+
description: "transaction_log_retention_days should be suppressed when ENHANCED",
3977+
},
3978+
"suppress retained_backups diff when backup tier is enhanced": {
3979+
key: "settings.0.backup_configuration.0.backup_retention_settings.0.retained_backups",
3980+
old: "7",
3981+
new: "14",
3982+
backupTier: "ENHANCED",
3983+
expectSuppress: true,
3984+
description: "retained_backups should be suppressed when ENHANCED",
3985+
},
3986+
"do not suppress diff when backup tier is standard": {
3987+
key: "settings.0.backup_configuration.0.enabled",
3988+
old: "true",
3989+
new: "false",
3990+
backupTier: "STANDARD",
3991+
expectSuppress: false,
3992+
description: "enabled should NOT be suppressed when STANDARD",
3993+
},
3994+
"do not suppress diff when backup tier is empty": {
3995+
key: "settings.0.backup_configuration.0.enabled",
3996+
old: "true",
3997+
new: "false",
3998+
backupTier: "",
3999+
expectSuppress: false,
4000+
description: "enabled should NOT be suppressed when backup_tier is empty",
4001+
},
4002+
"do not suppress when old and new are same": {
4003+
key: "settings.0.backup_configuration.0.enabled",
4004+
old: "true",
4005+
new: "true",
4006+
backupTier: "ENHANCED",
4007+
expectSuppress: true,
4008+
description: "no diff to suppress when old and new are same",
4009+
},
4010+
}
4011+
4012+
for name, tc := range cases {
4013+
t.Run(name, func(t *testing.T) {
4014+
// Build real *schema.ResourceData using the resource schema and TestResourceDataRaw
4015+
// Put backup_tier into the nested settings->backup_configuration block (state)
4016+
rd := schema.TestResourceDataRaw(t, sql.ResourceSqlDatabaseInstance().Schema, map[string]interface{}{
4017+
"name": "test-instance",
4018+
"settings": []interface{}{
4019+
map[string]interface{}{
4020+
"backup_configuration": []interface{}{
4021+
map[string]interface{}{
4022+
"backup_tier": tc.backupTier,
4023+
},
4024+
},
4025+
},
4026+
},
4027+
})
4028+
4029+
suppressed := sql.EnhancedBackupManagerDiffSuppressFunc(tc.key, tc.old, tc.new, rd)
4030+
4031+
if suppressed != tc.expectSuppress {
4032+
t.Errorf("expected suppressed to be %v but got %v. Desc: %s", tc.expectSuppress, suppressed, tc.description)
4033+
}
4034+
})
4035+
}
4036+
}
4037+
4038+
func TestAccSqlDatabaseInstance_updateInstanceTierForEnhancedBackupTierInstance(t *testing.T) {
4039+
t.Parallel()
4040+
4041+
backupVaultID := "bv-test"
4042+
location := "us-central1"
4043+
project := envvar.GetTestProjectFromEnv()
4044+
backupVault := acctest.BootstrapBackupDRVault(t, backupVaultID, location)
4045+
4046+
context := map[string]interface{}{
4047+
"random_suffix": acctest.RandString(t, 10),
4048+
"project": project,
4049+
"backup_vault_id": backupVaultID,
4050+
"backup_vault": backupVault,
4051+
"db_version": "MYSQL_8_0_41",
4052+
}
4053+
4054+
acctest.VcrTest(t, resource.TestCase{
4055+
PreCheck: func() { acctest.AccTestPreCheck(t) },
4056+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
4057+
CheckDestroy: testAccSqlDatabaseInstanceDestroyProducer(t),
4058+
Steps: []resource.TestStep{
4059+
{
4060+
// Create backup plan and associate with instance
4061+
Config: testGoogleSqlDatabaseInstance_attachGCBDR(context),
4062+
Check: resource.ComposeAggregateTestCheckFunc(
4063+
resource.TestCheckResourceAttrSet("google_backup_dr_backup_plan_association.backup_association", "id"),
4064+
),
4065+
},
4066+
{
4067+
// Update instance backup tier to ENHANCED, which should ignore backup_configuration settings
4068+
Config: testGoogleSqlDatabaseInstance_updateTierForGcbdrManagedInstance(context),
4069+
Check: resource.ComposeAggregateTestCheckFunc(
4070+
resource.TestCheckResourceAttr("google_sql_database_instance.instance", "settings.0.tier", "db-g1-small"),
4071+
),
4072+
},
4073+
},
4074+
})
4075+
}
4076+
4077+
func testGoogleSqlDatabaseInstance_attachGCBDR(context map[string]interface{}) string {
4078+
return acctest.Nprintf(`
4079+
data "google_project" "project" {}
4080+
4081+
resource "google_sql_database_instance" "instance" {
4082+
name = "tf-test-instance-%{random_suffix}"
4083+
database_version = "%{db_version}"
4084+
region = "us-central1"
4085+
4086+
settings {
4087+
tier = "db-f1-micro"
4088+
}
4089+
deletion_protection = false
4090+
}
4091+
4092+
resource "google_backup_dr_backup_plan" "plan" {
4093+
location = "us-central1"
4094+
backup_plan_id = "tf-test-bp-test-%{random_suffix}"
4095+
resource_type = "sqladmin.googleapis.com/Instance"
4096+
backup_vault = "%{backup_vault}"
4097+
4098+
backup_rules {
4099+
rule_id = "rule-1"
4100+
backup_retention_days = 7
4101+
4102+
standard_schedule {
4103+
recurrence_type = "DAILY"
4104+
hourly_frequency = 6
4105+
time_zone = "UTC"
4106+
4107+
backup_window {
4108+
start_hour_of_day = 0
4109+
end_hour_of_day = 23
4110+
}
4111+
}
4112+
}
4113+
}
4114+
4115+
resource "google_backup_dr_backup_plan_association" "backup_association" {
4116+
location = "us-central1"
4117+
backup_plan_association_id = "tf-test-bpa-test-%{random_suffix}"
4118+
resource = "projects/${data.google_project.project.project_id}/instances/${google_sql_database_instance.instance.name}"
4119+
resource_type = "sqladmin.googleapis.com/Instance"
4120+
backup_plan = google_backup_dr_backup_plan.plan.name
4121+
}
4122+
`, context)
4123+
}
4124+
4125+
func testGoogleSqlDatabaseInstance_updateTierForGcbdrManagedInstance(context map[string]interface{}) string {
4126+
return acctest.Nprintf(`
4127+
data "google_project" "project" {}
4128+
4129+
resource "google_sql_database_instance" "instance" {
4130+
name = "tf-test-instance-%{random_suffix}"
4131+
database_version = "%{db_version}"
4132+
region = "us-central1"
4133+
4134+
settings {
4135+
tier = "db-g1-small"
4136+
4137+
backup_configuration {
4138+
enabled = false
4139+
binary_log_enabled = false
4140+
start_time = "05:00"
4141+
4142+
backup_retention_settings {
4143+
retained_backups = 8
4144+
retention_unit = "COUNT"
4145+
}
4146+
}
4147+
}
4148+
deletion_protection = false
4149+
}
4150+
4151+
resource "google_backup_dr_backup_plan" "plan" {
4152+
location = "us-central1"
4153+
backup_plan_id = "tf-test-bp-test-%{random_suffix}"
4154+
resource_type = "sqladmin.googleapis.com/Instance"
4155+
backup_vault = "%{backup_vault}"
4156+
4157+
backup_rules {
4158+
rule_id = "rule-1"
4159+
backup_retention_days = 7
4160+
4161+
standard_schedule {
4162+
recurrence_type = "DAILY"
4163+
hourly_frequency = 6
4164+
time_zone = "UTC"
4165+
4166+
backup_window {
4167+
start_hour_of_day = 0
4168+
end_hour_of_day = 23
4169+
}
4170+
}
4171+
}
4172+
}
4173+
4174+
resource "google_backup_dr_backup_plan_association" "backup_association" {
4175+
location = "us-central1"
4176+
backup_plan_association_id = "tf-test-bpa-test-%{random_suffix}"
4177+
resource = "projects/${data.google_project.project.project_id}/instances/${google_sql_database_instance.instance.name}"
4178+
resource_type = "sqladmin.googleapis.com/Instance"
4179+
backup_plan = google_backup_dr_backup_plan.plan.name
4180+
}
4181+
`, context)
4182+
}
4183+
4184+
39284185
func testGoogleSqlDatabaseInstance_setCustomSubjectAlternateName(context map[string]interface{}) string {
39294186
return acctest.Nprintf(`
39304187
data "google_project" "project" {

mmv1/third_party/terraform/website/docs/r/sql_database_instance.html.markdown

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,8 @@ The optional `settings.backup_configuration` subblock supports:
457457

458458
* `enabled` - (Optional) True if backup configuration is enabled.
459459

460+
* `backup_tier` - (Computed) The backup tier that manages the backups for the instance.
461+
460462
* `start_time` - (Optional) `HH:MM` format time indicating when backup
461463
configuration starts.
462464
* `point_in_time_recovery_enabled` - (Optional) True if Point-in-time recovery is enabled. Will restart database if enabled after instance creation. Valid only for PostgreSQL and SQL Server instances. Enabled by default for PostgreSQL Enterprise Plus and SQL Server Enterprise Plus instances.

0 commit comments

Comments
 (0)