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

Add restoreBackup support for sql db instance #8138

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
3 changes: 3 additions & 0 deletions .changelog/4336.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
sql: added restore from backup support to `google_sql_database_instance`
```
80 changes: 80 additions & 0 deletions google/resource_sql_database_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,30 @@ settings.backup_configuration.binary_log_enabled are both set to true.`,
Computed: true,
Description: `The URI of the created resource.`,
},
"restore_backup_context": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"backup_run_id": {
Type: schema.TypeInt,
Required: true,
Description: `The ID of the backup run to restore from.`,
},
"instance_id": {
Type: schema.TypeString,
Optional: true,
Description: `The ID of the instance that the backup was taken from.`,
},
"project": {
Type: schema.TypeString,
Optional: true,
Description: `The full project ID of the source instance.`,
},
},
},
},
},
UseJSONNumber: true,
}
Expand Down Expand Up @@ -739,6 +763,14 @@ func resourceSqlDatabaseInstanceCreate(d *schema.ResourceData, meta interface{})
}
}

// Perform a backup restore if the backup context exists
if r, ok := d.GetOk("restore_backup_context"); ok {
err = sqlDatabaseInstanceRestoreFromBackup(d, config, userAgent, project, name, r)
if err != nil {
return err
}
}

return nil
}

Expand Down Expand Up @@ -1027,6 +1059,16 @@ func resourceSqlDatabaseInstanceUpdate(d *schema.ResourceData, meta interface{})
return err
}

// Perform a backup restore if the backup context exists and has changed
if r, ok := d.GetOk("restore_backup_context"); ok {
if d.HasChange("restore_backup_context") {
err = sqlDatabaseInstanceRestoreFromBackup(d, config, userAgent, project, d.Get("name").(string), r)
if err != nil {
return err
}
}
}

return resourceSqlDatabaseInstanceRead(d, meta)
}

Expand Down Expand Up @@ -1319,3 +1361,41 @@ func sqlDatabaseInstanceServiceNetworkPrecheck(d *schema.ResourceData, config *C

return nil
}

func expandRestoreBackupContext(configured []interface{}) *sqladmin.RestoreBackupContext {
if len(configured) == 0 || configured[0] == nil {
return nil
}

_rc := configured[0].(map[string]interface{})
return &sqladmin.RestoreBackupContext{
BackupRunId: int64(_rc["backup_run_id"].(int)),
InstanceId: _rc["instance_id"].(string),
Project: _rc["project"].(string),
}
}

func sqlDatabaseInstanceRestoreFromBackup(d *schema.ResourceData, config *Config, userAgent, project, instanceId string, r interface{}) error {
log.Printf("[DEBUG] Initiating SQL database instance backup restore")
restoreContext := r.([]interface{})

backupRequest := &sqladmin.InstancesRestoreBackupRequest{
RestoreBackupContext: expandRestoreBackupContext(restoreContext),
}

var op *sqladmin.Operation
err := retryTimeDuration(func() (operr error) {
op, operr = config.NewSqlAdminClient(userAgent).Instances.RestoreBackup(project, instanceId, backupRequest).Do()
return operr
}, d.Timeout(schema.TimeoutUpdate), isSqlOperationInProgressError)
if err != nil {
return fmt.Errorf("Error, failed to restore instance from backup %s: %s", instanceId, err)
}

err = sqlAdminOperationWaitTime(config, op, project, "Restore Backup", userAgent, d.Timeout(schema.TimeoutUpdate))
if err != nil {
return err
}

return nil
}
113 changes: 113 additions & 0 deletions google/resource_sql_database_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,71 @@ func TestAccSqlDatabaseInstance_basic_with_user_labels(t *testing.T) {
})
}

func TestAccSqlDatabaseInstance_createFromBackup(t *testing.T) {
// Sqladmin client
skipIfVcr(t)
t.Parallel()

context := map[string]interface{}{
"random_suffix": randString(t, 10),
"original_db_name": BootstrapSharedSQLInstanceBackupRun(t),
}

vcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccSqlDatabaseInstanceDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccSqlDatabaseInstance_restoreFromBackup(context),
},
{
ResourceName: "google_sql_database_instance.instance",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"deletion_protection", "restore_backup_context"},
},
},
})
}

func TestAccSqlDatabaseInstance_backupUpdate(t *testing.T) {
// Sqladmin client
skipIfVcr(t)
t.Parallel()

context := map[string]interface{}{
"random_suffix": randString(t, 10),
"original_db_name": BootstrapSharedSQLInstanceBackupRun(t),
}

vcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccSqlDatabaseInstanceDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccSqlDatabaseInstance_beforeBackup(context),
},
{
ResourceName: "google_sql_database_instance.instance",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"deletion_protection"},
},
{
Config: testAccSqlDatabaseInstance_restoreFromBackup(context),
},
{
ResourceName: "google_sql_database_instance.instance",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"deletion_protection", "restore_backup_context"},
},
},
})
}

func testAccSqlDatabaseInstanceDestroyProducer(t *testing.T) func(s *terraform.State) error {
return func(s *terraform.State) error {
for _, rs := range s.RootModule().Resources {
Expand Down Expand Up @@ -1153,3 +1218,51 @@ resource "google_sql_database_instance" "instance" {
}
`, masterID, pointInTimeRecoveryEnabled)
}

func testAccSqlDatabaseInstance_beforeBackup(context map[string]interface{}) string {
return Nprintf(`
resource "google_sql_database_instance" "instance" {
name = "tf-test-%{random_suffix}"
database_version = "POSTGRES_11"
region = "us-central1"

settings {
tier = "db-f1-micro"
backup_configuration {
enabled = "false"
}
}

deletion_protection = false
}
`, context)
}

func testAccSqlDatabaseInstance_restoreFromBackup(context map[string]interface{}) string {
return Nprintf(`
resource "google_sql_database_instance" "instance" {
name = "tf-test-%{random_suffix}"
database_version = "POSTGRES_11"
region = "us-central1"

settings {
tier = "db-f1-micro"
backup_configuration {
enabled = "false"
}
}

restore_backup_context {
backup_run_id = data.google_sql_backup_run.backup.backup_id
instance_id = data.google_sql_backup_run.backup.instance
}

deletion_protection = false
}

data "google_sql_backup_run" "backup" {
instance = "%{original_db_name}"
most_recent = true
}
`, context)
}
16 changes: 16 additions & 0 deletions website/docs/r/sql_database_instance.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,11 @@ includes an up-to-date reference of supported versions.
* `deletion_protection` - (Optional, Default: `true` ) 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.

* `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.
**NOTE:** Restoring from a backup is an imperative action and not recommended via Terraform. Adding or modifying this
block during resource creation/update will trigger the restore action after the resource is created/updated.

The required `settings` block supports:

* `tier` - (Required) The machine type to use. See [tiers](https://cloud.google.com/sql/docs/admin-api/v1beta4/tiers)
Expand Down Expand Up @@ -373,6 +378,17 @@ to work, cannot be updated, and supports:
* `verify_server_certificate` - (Optional) True if the master's common name
value is checked during the SSL handshake.

The optional `restore_backup_context` block supports:
**NOTE:** Restoring from a backup is an imperative action and not recommended via Terraform. Adding or modifying this
block during resource creation/update will trigger the restore action after the resource is created/updated.

* `backup_run_id` - (Required) The ID of the backup run to restore from.

* `instance_id` - (Optional) The ID of the instance that the backup was taken from. If left empty,
this instance's ID will be used.

* `project` - (Optional) The full project ID of the source instance.`

## Attributes Reference

In addition to the arguments listed above, the following computed attributes are
Expand Down