Skip to content

Commit

Permalink
Add SQL Backup Run Datasource (#4352) (#8100)
Browse files Browse the repository at this point in the history
* Add SQL Backup Run Datasource

* Rename dataSourceSqlDatabaseInstanceBackupRun -> dataSourceSqlBackupRun

* bump bootstrap timeout

* Skip VCR

Signed-off-by: Modular Magician <magic-modules@google.com>
  • Loading branch information
modular-magician authored Dec 30, 2020
1 parent 49662ee commit cc87c28
Show file tree
Hide file tree
Showing 7 changed files with 318 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .changelog/4352.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-datasource
`google_sql_backup_run`
```
75 changes: 75 additions & 0 deletions google/bootstrap_utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"google.golang.org/api/cloudkms/v1"
cloudresourcemanager "google.golang.org/api/cloudresourcemanager/v1"
"google.golang.org/api/iam/v1"
sqladmin "google.golang.org/api/sqladmin/v1beta4"
)

var SharedKeyRing = "tftest-shared-keyring-1"
Expand Down Expand Up @@ -335,3 +336,77 @@ func BootstrapConfig(t *testing.T) *Config {
}
return config
}

// SQL Instance names are not reusable for a week after deletion
const SharedTestSQLInstanceName = "tf-bootstrap-do-not-delete"

// BootstrapSharedSQLInstanceBackupRun will return a shared SQL db instance that
// has a backup created for it.
func BootstrapSharedSQLInstanceBackupRun(t *testing.T) string {
project := getTestProjectFromEnv()

config := BootstrapConfig(t)
if config == nil {
return ""
}

log.Printf("[DEBUG] Getting shared test sql instance %s", SharedTestSQLInstanceName)

instance, err := config.NewSqlAdminClient(config.userAgent).Instances.Get(project, SharedTestSQLInstanceName).Do()
if err != nil {
if isGoogleApiErrorWithCode(err, 404) {
log.Printf("[DEBUG] SQL Instance %q not found, bootstrapping", SharedTestSQLInstanceName)
settings := &sqladmin.Settings{
Tier: "db-f1-micro",
}
instance = &sqladmin.DatabaseInstance{
Name: SharedTestSQLInstanceName,
Region: "us-central1",
Settings: settings,
DatabaseVersion: "POSTGRES_11",
}

var op *sqladmin.Operation
err = retryTimeDuration(func() (operr error) {
op, operr = config.NewSqlAdminClient(config.userAgent).Instances.Insert(project, instance).Do()
return operr
}, time.Duration(20)*time.Minute, isSqlOperationInProgressError)
if err != nil {
t.Fatalf("Error, failed to create instance %s: %s", instance.Name, err)
}
err = sqlAdminOperationWaitTime(config, op, project, "Create Instance", config.userAgent, time.Duration(20)*time.Minute)
if err != nil {
t.Fatalf("Error, failed to create instance %s: %s", instance.Name, err)
}
} else {
t.Fatalf("Unable to bootstrap SQL Instance. Cannot retrieve instance: %s", err)
}
}

res, err := config.NewSqlAdminClient(config.userAgent).BackupRuns.List(project, instance.Name).Do()
if err != nil {
t.Fatalf("Unable to bootstrap SQL Instance. Cannot retrieve backup list: %s", err)
}
backupsList := res.Items
if len(backupsList) == 0 {
log.Printf("[DEBUG] No backups found for %s, creating backup", SharedTestSQLInstanceName)
backupRun := &sqladmin.BackupRun{
Instance: instance.Name,
}

var op *sqladmin.Operation
err = retryTimeDuration(func() (operr error) {
op, operr = config.NewSqlAdminClient(config.userAgent).BackupRuns.Insert(project, instance.Name, backupRun).Do()
return operr
}, time.Duration(20)*time.Minute, isSqlOperationInProgressError)
if err != nil {
t.Fatalf("Error, failed to create instance backup: %s", err)
}
err = sqlAdminOperationWaitTime(config, op, project, "Backup Instance", config.userAgent, time.Duration(20)*time.Minute)
if err != nil {
t.Fatalf("Error, failed to create instance backup: %s", err)
}
}

return instance.Name
}
106 changes: 106 additions & 0 deletions google/data_source_sql_backup_run.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package google

import (
"fmt"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
sqladmin "google.golang.org/api/sqladmin/v1beta4"
)

func dataSourceSqlBackupRun() *schema.Resource {

return &schema.Resource{
Read: dataSourceSqlBackupRunRead,

Schema: map[string]*schema.Schema{
"backup_id": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
Description: `The identifier for this backup run. Unique only for a specific Cloud SQL instance. If left empty and multiple backups exist for the instance, most_recent must be set to true.`,
},
"instance": {
Type: schema.TypeString,
Required: true,
Description: `Name of the database instance.`,
},
"location": {
Type: schema.TypeString,
Computed: true,
Description: `Location of the backups.`,
},
"start_time": {
Type: schema.TypeString,
Computed: true,
Description: `The time the backup operation actually started in UTC timezone in RFC 3339 format, for example 2012-11-15T16:19:00.094Z.`,
},
"status": {
Type: schema.TypeString,
Computed: true,
Description: `The status of this run.`,
},
"most_recent": {
Type: schema.TypeBool,
Optional: true,
Description: `Toggles use of the most recent backup run if multiple backups exist for a Cloud SQL instance.`,
},
},
}
}

func dataSourceSqlBackupRunRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
userAgent, err := generateUserAgentString(d, config.userAgent)
if err != nil {
return err
}
project, err := getProject(d, config)
if err != nil {
return err
}

instance := d.Get("instance").(string)

var backup *sqladmin.BackupRun
if backupId, ok := d.GetOk("backup_id"); ok {
backup, err = config.NewSqlAdminClient(userAgent).BackupRuns.Get(project, instance, int64(backupId.(int))).Do()
if err != nil {
return err
}
} else {
res, err := config.NewSqlAdminClient(userAgent).BackupRuns.List(project, instance).Do()
if err != nil {
return err
}
backupsList := res.Items
if len(backupsList) == 0 {
return fmt.Errorf("No backups found for SQL Database Instance %s", instance)
} else if len(backupsList) > 1 {
mostRecent := d.Get("most_recent").(bool)
if !mostRecent {
return fmt.Errorf("Multiple SQL backup runs listed for Instance %s. Consider setting most_recent or specifying a backup_id", instance)
}
}
backup = backupsList[0]
}

if err := d.Set("backup_id", backup.Id); err != nil {
return fmt.Errorf("Error setting backup_id: %s", err)
}
if err := d.Set("location", backup.Location); err != nil {
return fmt.Errorf("Error setting location: %s", err)
}
if err := d.Set("start_time", backup.StartTime); err != nil {
return fmt.Errorf("Error setting start_time: %s", err)
}
if err := d.Set("status", backup.Status); err != nil {
return fmt.Errorf("Error setting status: %s", err)
}

id, err := replaceVars(d, config, "projects/{{project}}/instances/{{instance}}/backupRuns/{{backup_id}}")
if err != nil {
return fmt.Errorf("Error constructing id: %s", err)
}
d.SetId(id)
return nil
}
85 changes: 85 additions & 0 deletions google/data_source_sql_backup_run_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package google

import (
"fmt"
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

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

instance := BootstrapSharedSQLInstanceBackupRun(t)

vcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccSqlDatabaseInstanceDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccDataSourceSqlBackupRun_basic(instance),
Check: resource.TestMatchResourceAttr("data.google_sql_backup_run.backup", "status", regexp.MustCompile("SUCCESSFUL")),
},
},
})
}

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

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

vcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccSqlDatabaseInstanceDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccDataSourceSqlBackupRun_notFound(context),
ExpectError: regexp.MustCompile("No backups found for SQL Database Instance"),
},
},
})
}

func testAccDataSourceSqlBackupRun_basic(instance string) string {
return fmt.Sprintf(`
data "google_sql_backup_run" "backup" {
instance = "%s"
most_recent = true
}
`, instance)
}

func testAccDataSourceSqlBackupRun_notFound(context map[string]interface{}) string {
return Nprintf(`
resource "google_sql_database_instance" "instance" {
name = "tf-test-instance-%{random_suffix}"
database_version = "POSTGRES_11"
region = "us-central1"
settings {
tier = "db-f1-micro"
backup_configuration {
enabled = "false"
}
}
deletion_protection = false
}
data "google_sql_backup_run" "backup" {
instance = google_sql_database_instance.instance.name
most_recent = true
depends_on = [google_sql_database_instance.instance]
}
`, context)
}
1 change: 1 addition & 0 deletions google/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,7 @@ func Provider() *schema.Provider {
"google_service_account_key": dataSourceGoogleServiceAccountKey(),
"google_spanner_instance": dataSourceSpannerInstance(),
"google_sql_ca_certs": dataSourceGoogleSQLCaCerts(),
"google_sql_backup_run": dataSourceSqlBackupRun(),
"google_sql_database_instance": dataSourceSqlDatabaseInstance(),
"google_storage_bucket_object": dataSourceGoogleStorageBucketObject(),
"google_storage_bucket_object_content": dataSourceGoogleStorageBucketObjectContent(),
Expand Down
44 changes: 44 additions & 0 deletions website/docs/d/sql_backup_run.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
subcategory: "Cloud SQL"
layout: "google"
page_title: "Google: google_sql_backup_run"
sidebar_current: "docs-google-datasource-sql-backup-run"
description: |-
Get a SQL backup run in Google Cloud SQL.
---

# google\_sql\_backup\_run

Use this data source to get information about a Cloud SQL instance backup run.

## Example Usage

```hcl
data "google_sql_backup_run" "backup" {
instance = google_sql_database_instance.master.name
most_recent = true
}
```

## Argument Reference

The following arguments are supported:

* `instance` - (required) The name of the instance the backup is taken from.

* `backup_id` - (optional) The identifier for this backup run. Unique only for a specific Cloud SQL instance.
If left empty and multiple backups exist for the instance, `most_recent` must be set to `true`.

* `most_recent` - (optional) Toggles use of the most recent backup run if multiple backups exist for a
Cloud SQL instance.

## Attributes Reference

In addition to the arguments listed above, the following attributes are exported:

* `location` - Location of the backups.

* `start_time` - The time the backup operation actually started in UTC timezone in RFC 3339 format, for
example 2012-11-15T16:19:00.094Z.

* `status` - The status of this run. Refer to [API reference](https://cloud.google.com/sql/docs/mysql/admin-api/rest/v1beta4/backupRuns#SqlBackupRunStatus) for possible status values.
4 changes: 4 additions & 0 deletions website/google.erb
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,10 @@
<a href="#">Data Sources</a>
<ul class="nav nav-auto-expand">

<li>
<a href="/docs/providers/google/d/sql_backup_run.html">google_sql_backup_run</a>
</li>

<li>
<a href="/docs/providers/google/d/sql_ca_certs.html">google_sql_ca_certs</a>
</li>
Expand Down

0 comments on commit cc87c28

Please sign in to comment.