Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Cloud SQL deletion protection #6441

Merged
merged 3 commits into from
Dec 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions mmv1/products/sql/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,10 @@ objects:
name: 'userLabels'
description: |
User-provided labels, represented as a dictionary where each label is a single key value pair.
- !ruby/object:Api::Type::Boolean
name: 'deletionProtectionEnabled'
description: |
Configuration to protect against accidental instance deletion.
- !ruby/object:Api::Type::String
name: 'gceZone'
output: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,11 @@ is set to true. Defaults to ZONAL.`,
ValidateFunc: validation.StringInSlice([]string{"NOT_REQUIRED", "REQUIRED"}, false),
Description: `Specifies if connections must use Cloud SQL connectors.`,
},
"deletion_protection_enabled": {
Type: schema.TypeBool,
Optional: true,
Description: `Configuration to protect against accidental instance deletion.`,
},
},
},
Description: `The settings to use for the database. The configuration is detailed below.`,
Expand Down Expand Up @@ -1088,28 +1093,29 @@ func expandSqlDatabaseInstanceSettings(configured []interface{}) *sqladmin.Setti
_settings := configured[0].(map[string]interface{})
settings := &sqladmin.Settings{
// Version is unset in Create but is set during update
SettingsVersion: int64(_settings["version"].(int)),
Tier: _settings["tier"].(string),
ForceSendFields: []string{"StorageAutoResize"},
ActivationPolicy: _settings["activation_policy"].(string),
ActiveDirectoryConfig: expandActiveDirectoryConfig(_settings["active_directory_config"].([]interface{})),
DenyMaintenancePeriods: expandDenyMaintenancePeriod(_settings["deny_maintenance_period"].([]interface{})),
SqlServerAuditConfig: expandSqlServerAuditConfig(_settings["sql_server_audit_config"].([]interface{})),
TimeZone: _settings["time_zone"].(string),
AvailabilityType: _settings["availability_type"].(string),
ConnectorEnforcement: _settings["connector_enforcement"].(string),
Collation: _settings["collation"].(string),
DataDiskSizeGb: int64(_settings["disk_size"].(int)),
DataDiskType: _settings["disk_type"].(string),
PricingPlan: _settings["pricing_plan"].(string),
UserLabels: convertStringMap(_settings["user_labels"].(map[string]interface{})),
BackupConfiguration: expandBackupConfiguration(_settings["backup_configuration"].([]interface{})),
DatabaseFlags: expandDatabaseFlags(_settings["database_flags"].([]interface{})),
IpConfiguration: expandIpConfiguration(_settings["ip_configuration"].([]interface{})),
LocationPreference: expandLocationPreference(_settings["location_preference"].([]interface{})),
MaintenanceWindow: expandMaintenanceWindow(_settings["maintenance_window"].([]interface{})),
InsightsConfig: expandInsightsConfig(_settings["insights_config"].([]interface{})),
PasswordValidationPolicy: expandPasswordValidationPolicy(_settings["password_validation_policy"].([]interface{})),
SettingsVersion: int64(_settings["version"].(int)),
Tier: _settings["tier"].(string),
ForceSendFields: []string{"StorageAutoResize"},
ActivationPolicy: _settings["activation_policy"].(string),
ActiveDirectoryConfig: expandActiveDirectoryConfig(_settings["active_directory_config"].([]interface{})),
DenyMaintenancePeriods: expandDenyMaintenancePeriod(_settings["deny_maintenance_period"].([]interface{})),
SqlServerAuditConfig: expandSqlServerAuditConfig(_settings["sql_server_audit_config"].([]interface{})),
TimeZone: _settings["time_zone"].(string),
AvailabilityType: _settings["availability_type"].(string),
ConnectorEnforcement: _settings["connector_enforcement"].(string),
Collation: _settings["collation"].(string),
DataDiskSizeGb: int64(_settings["disk_size"].(int)),
DataDiskType: _settings["disk_type"].(string),
PricingPlan: _settings["pricing_plan"].(string),
DeletionProtectionEnabled: _settings["deletion_protection_enabled"].(bool),
UserLabels: convertStringMap(_settings["user_labels"].(map[string]interface{})),
BackupConfiguration: expandBackupConfiguration(_settings["backup_configuration"].([]interface{})),
DatabaseFlags: expandDatabaseFlags(_settings["database_flags"].([]interface{})),
IpConfiguration: expandIpConfiguration(_settings["ip_configuration"].([]interface{})),
LocationPreference: expandLocationPreference(_settings["location_preference"].([]interface{})),
MaintenanceWindow: expandMaintenanceWindow(_settings["maintenance_window"].([]interface{})),
InsightsConfig: expandInsightsConfig(_settings["insights_config"].([]interface{})),
PasswordValidationPolicy: expandPasswordValidationPolicy(_settings["password_validation_policy"].([]interface{})),
}

resize := _settings["disk_autoresize"].(bool)
Expand Down Expand Up @@ -1626,18 +1632,19 @@ func resourceSqlDatabaseInstanceImport(d *schema.ResourceData, meta interface{})

func flattenSettings(settings *sqladmin.Settings) []map[string]interface{} {
data := map[string]interface{}{
"version": settings.SettingsVersion,
"tier": settings.Tier,
"activation_policy": settings.ActivationPolicy,
"availability_type": settings.AvailabilityType,
"collation": settings.Collation,
"connector_enforcement": settings.ConnectorEnforcement,
"disk_type": settings.DataDiskType,
"disk_size": settings.DataDiskSizeGb,
"pricing_plan": settings.PricingPlan,
"user_labels": settings.UserLabels,
"password_validation_policy": settings.PasswordValidationPolicy,
"time_zone": settings.TimeZone,
"version": settings.SettingsVersion,
"tier": settings.Tier,
"activation_policy": settings.ActivationPolicy,
"availability_type": settings.AvailabilityType,
"collation": settings.Collation,
"connector_enforcement": settings.ConnectorEnforcement,
"disk_type": settings.DataDiskType,
"disk_size": settings.DataDiskSizeGb,
"pricing_plan": settings.PricingPlan,
"user_labels": settings.UserLabels,
"password_validation_policy": settings.PasswordValidationPolicy,
"time_zone": settings.TimeZone,
"deletion_protection_enabled": settings.DeletionProtectionEnabled,
}

if settings.ActiveDirectoryConfig != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,46 @@ func TestAccSqlDatabaseInstance_maintenanceVersion(t *testing.T) {
})
}

func TestAccSqlDatabaseInstance_settings_deletionProtectionEnabled(t *testing.T) {
t.Parallel()

databaseName := "tf-test-" + randString(t, 10)

vcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccSqlDatabaseInstanceDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(
testGoogleSqlDatabaseInstance_settings_deletionProtectionEnabled, databaseName, "true"),
},
{
ResourceName: "google_sql_database_instance.instance",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"deletion_protection"},
},
{
Config: fmt.Sprintf(
testGoogleSqlDatabaseInstance_settings_deletionProtectionEnabled, databaseName, "true"),
Destroy: true,
ExpectError: regexp.MustCompile(fmt.Sprintf("Error, failed to delete instance %s: googleapi: Error 400: The instance is protected. Please disable the deletion protection and try again. To disable deletion protection, update the instance settings with deletionProtectionEnabled set to false.", databaseName)),
},
{
Config: fmt.Sprintf(
testGoogleSqlDatabaseInstance_settings_deletionProtectionEnabled, databaseName, "false"),
},
{
ResourceName: "google_sql_database_instance.instance",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"deletion_protection"},
},
},
})
}

func TestAccSqlDatabaseInstance_settings_checkServiceNetworking(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -1853,6 +1893,7 @@ resource "google_sql_database_instance" "instance" {
}
}
`

var testGoogleSqlDatabaseInstance_maintenanceVersionWithOldVersion = `
resource "google_sql_database_instance" "instance" {
name = "%s"
Expand All @@ -1866,6 +1907,19 @@ resource "google_sql_database_instance" "instance" {
}
`

var testGoogleSqlDatabaseInstance_settings_deletionProtectionEnabled = `
resource "google_sql_database_instance" "instance" {
name = "%s"
region = "us-central1"
database_version = "MYSQL_5_7"
deletion_protection = false
settings {
deletion_protection_enabled = %s
tier = "db-f1-micro"
}
}
`

var testGoogleSqlDatabaseInstance_settings_checkServiceNetworking = `
resource "google_compute_network" "servicenet" {
name = "%s"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ includes an up-to-date reference of supported versions.

* `deletion_protection` - (Optional) Whether or not to allow Terraform to destroy the instance. Unless this field is set to false
in Terraform state, a `terraform destroy` or `terraform apply` command that deletes the instance will fail. Defaults to `true`.

~> **NOTE:** This flag only protects instances from deletion within Terraform. To protect your instances from accidental deletion across all surfaces (API, gcloud, Cloud Console and Terraform), use the API flag `settings.deletion_protection_enabled`.

* `restore_backup_context` - (optional) The context needed to restore the database to a backup run. This field will
cause Terraform to trigger the database to restore from the backup run indicated. The configuration is detailed below.
Expand Down Expand Up @@ -240,6 +242,8 @@ The `settings` block supports:

* `connector_enforcement` - (Optional) Specifies if connections must use Cloud SQL connectors.

* `deletion_protection_enabled` - (Optional) Enables protection of an instance from accidental deletion protection across all surfaces (API, gcloud, Cloud Console and Terraform). Defaults to `false`.

* `disk_autoresize` - (Optional) Enables auto-resizing of the storage size. Defaults to `true`.

* `disk_autoresize_limit` - (Optional) The maximum size to which storage capacity can be automatically increased. The default value is 0, which specifies that there is no limit.
Expand Down