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

feat(rdb): add support for database backup #783

Merged
merged 18 commits into from
Jun 16, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 39 additions & 0 deletions docs/data-sources/rdb_database_backup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
layout: "scaleway"
page_title: "Scaleway: scaleway_rdb_database_backup"
description: |-
Gets information about an RDB backup.
---

# scaleway_rdb_database_backup

Gets information about an RDB backup.

## Example Usage

```hcl
data scaleway_rdb_database_backup find_by_name {
name = "mybackup"
}

data scaleway_rdb_database_backup find_by_name_and_instance {
name = "mybackup"
instance_id = "11111111-1111-1111-1111-111111111111"
}

data scaleway_rdb_database_backup find_by_id {
backup_id = "11111111-1111-1111-1111-111111111111"
}
```

## Argument Reference

- `backup_id` - (Optional) The RDB backup ID.
Only one of the `name` and `backup_id` should be specified.

- `instance_id` - (Optional) The RDB instance ID.

- `name` - (Optional) The name of the RDB instance.
Only one of the `name` and `backup_id` should be specified.


64 changes: 64 additions & 0 deletions docs/resources/rdb_database_backup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
page_title: "Scaleway: scaleway_rdb_database_backup"
description: |-
Manages Scaleway RDB Database Backup.
---

# scaleway_rdb_database_backup

Creates and manages Scaleway RDB database backup.
For more information, see [the documentation](https://developers.scaleway.com/en/products/rdb/api).

## Examples

### Basic

```hcl
resource scaleway_rdb_database_backup "main" {
instance_id = data.scaleway_rdb_instance.main.id
database_name = data.scaleway_rdb_database.main.name
}
```

### With expiration

```hcl
resource scaleway_rdb_database_backup "main" {
instance_id = data.scaleway_rdb_instance.main.id
database_name = data.scaleway_rdb_database.main.name
expires_at = "2022-06-16T07:48:44Z"
}
```

## Arguments Reference

The following arguments are supported:

- `instance_id` - (Required) UUID of the instance where the database to backup is.

~> **Important:** Updates to `instance_id` will recreate the Backup.

- `name` - (Required) Name of the database (e.g. `my-database`).

- `expires_at` (Optional) Expiration date (Format ISO 8601).

~> **Important:** `expires_at` cannot be removed after being set.


## Attributes Reference

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

- `size` - Size of the backup (in bytes).
- `instance_name` - Name of the instance of the backup.
- `created_at` - Creation date (Format ISO 8601).
- `updated_at` - Updated date (Format ISO 8601).


## Import

RDB Database can be imported using the `{region}/{id}`, e.g.

```bash
$ terraform import scaleway_rdb_database_backup.mybackup fr-par/11111111-1111-1111-1111-111111111111
```
79 changes: 79 additions & 0 deletions scaleway/data_source_rdb_database_backup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package scaleway

import (
"context"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/scaleway/scaleway-sdk-go/api/rdb/v1"
)

func dataSourceScalewayRDBDatabaseBackup() *schema.Resource {
// Generate datasource schema from resource
dsSchema := datasourceSchemaFromResourceSchema(resourceScalewayRdbDatabaseBackup().Schema)

addOptionalFieldsToSchema(dsSchema, "name", "region", "instance_id")

dsSchema["instance_id"].RequiredWith = []string{"name"}
dsSchema["backup_id"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "The ID of the Backup",
ConflictsWith: []string{"name", "instance_id"},
ValidateFunc: validationUUIDorUUIDWithLocality(),
}

return &schema.Resource{
ReadContext: dataSourceScalewayRDBDatabaseBackupRead,
Schema: dsSchema,
}
}

func dataSourceScalewayRDBDatabaseBackupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
api, region, err := rdbAPIWithRegion(d, meta)
if err != nil {
return diag.FromErr(err)
}

backupID, backupIDExists := d.GetOk("backup_id")
if !backupIDExists {
res, err := api.ListDatabaseBackups(&rdb.ListDatabaseBackupsRequest{
Region: region,
Name: expandStringPtr(d.Get("name")),
InstanceID: expandStringPtr(expandID(d.Get("instance_id"))),
ProjectID: expandStringPtr(d.Get("project_id")),
})
if err != nil {
return diag.FromErr(err)
}
for _, backup := range res.DatabaseBackups {
if backup.Name == d.Get("name").(string) {
if backupID != "" {
return diag.Errorf("more than 1 backup found with the same name %s", d.Get("name"))
}
backupID = backup.ID
}
}
if backupID == "" {
return diag.Errorf("no backup found with the name %s", d.Get("name"))
}
}

regionID := datasourceNewRegionalizedID(backupID, region)
d.SetId(regionID)
err = d.Set("backup_id", regionID)
if err != nil {
return diag.FromErr(err)
}

diags := resourceScalewayRdbDatabaseBackupRead(ctx, d, meta)
if diags != nil {
return append(diags, diag.Errorf("failed to read database backup state")...)
}

if d.Id() == "" {
return diag.Errorf("database backup (%s) not found", regionID)
}

return nil
}
74 changes: 74 additions & 0 deletions scaleway/data_source_rdb_database_backup_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package scaleway

import (
"testing"

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

func TestAccScalewayDataSourceRdbDatabaseBackup_Basic(t *testing.T) {
tt := NewTestTools(t)
defer tt.Cleanup()
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: tt.ProviderFactories,
CheckDestroy: resource.ComposeTestCheckFunc(
testAccCheckScalewayRdbInstanceDestroy(tt),
testAccCheckScalewayRdbDatabaseBackupDestroy(tt),
),
Steps: []resource.TestStep{
{
Config: `
resource "scaleway_rdb_instance" "server" {
name = "test-terraform"
node_type = "db-dev-s"
engine = "PostgreSQL-11"
}
resource "scaleway_rdb_database" "database" {
name = "test-terraform"
instance_id = scaleway_rdb_instance.server.id
}`,
},
{
Config: `
resource "scaleway_rdb_instance" "server" {
name = "test-terraform"
node_type = "db-dev-s"
engine = "PostgreSQL-11"
}
resource "scaleway_rdb_database" "database" {
name = "test-terraform"
instance_id = scaleway_rdb_instance.server.id
}

resource scaleway_rdb_database_backup backup {
instance_id = scaleway_rdb_instance.server.id
database_name = scaleway_rdb_database.database.name
name = "test_backup_datasource"
}

data scaleway_rdb_database_backup find_by_name {
name = scaleway_rdb_database_backup.backup.name
}

data scaleway_rdb_database_backup find_by_name_and_instance {
name = scaleway_rdb_database_backup.backup.name
instance_id = scaleway_rdb_instance.server.id
}

data scaleway_rdb_database_backup find_by_id {
backup_id = scaleway_rdb_database_backup.backup.id
}
`,
Check: resource.ComposeTestCheckFunc(
testAccCheckRdbDatabaseExists(tt, "scaleway_rdb_instance.server", "scaleway_rdb_database.database"),
testAccCheckRdbDatabaseBackupExists(tt, "scaleway_rdb_database_backup.backup"),

resource.TestCheckResourceAttr("data.scaleway_rdb_database_backup.find_by_name", "name", "test_backup_datasource"),
resource.TestCheckResourceAttr("data.scaleway_rdb_database_backup.find_by_name_and_instance", "name", "test_backup_datasource"),
resource.TestCheckResourceAttr("data.scaleway_rdb_database_backup.find_by_id", "name", "test_backup_datasource"),
),
},
},
})
}
23 changes: 23 additions & 0 deletions scaleway/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -637,3 +637,26 @@ func getBool(d *schema.ResourceData, key string) interface{} {
}
return val
}

// validateDate will validate that field is a valid ISO 8601
// It is the same as RFC3339
func validateDate() schema.SchemaValidateDiagFunc {
return func(i interface{}, path cty.Path) diag.Diagnostics {
date, isStr := i.(string)
if !isStr {
return diag.Errorf("%v is not a string", date)
}
_, err := time.Parse(time.RFC3339, date)
if err != nil {
return diag.FromErr(err)
}
return nil
}
}

func flattenSize(size *scw.Size) interface{} {
if size == nil {
return 0
}
return *size
}
31 changes: 30 additions & 1 deletion scaleway/helpers_rdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import (
)

const (
defaultRdbInstanceTimeout = 15 * time.Minute
defaultRdbInstanceTimeout = 15 * time.Minute
defaultWaitRDBRetryInterval = 30 * time.Second
)

// newRdbAPI returns a new RDB API
Expand Down Expand Up @@ -76,6 +77,20 @@ func waitForRDBInstance(ctx context.Context, api *rdb.API, region scw.Region, id
}, scw.WithContext(ctx))
}

func waitForRDBDatabaseBackup(ctx context.Context, api *rdb.API, region scw.Region, id string, timeout time.Duration) (*rdb.DatabaseBackup, error) {
retryInterval := defaultWaitRDBRetryInterval
if DefaultWaitRetryInterval != nil {
retryInterval = *DefaultWaitRetryInterval
}

return api.WaitForDatabaseBackup(&rdb.WaitForDatabaseBackupRequest{
Region: region,
Timeout: scw.TimeDurationPtr(timeout),
DatabaseBackupID: id,
RetryInterval: &retryInterval,
}, scw.WithContext(ctx))
}

func expandPrivateNetwork(data interface{}, exist bool) ([]*rdb.EndpointSpec, error) {
if data == nil || !exist {
return nil, nil
Expand Down Expand Up @@ -212,3 +227,17 @@ func flattenLoadBalancer(endpoints []*rdb.Endpoint) interface{} {

return flat
}

// expandTimePtr returns a time pointer for an RFC3339 time.
// It returns nil if time is not valid, you should use validateDate to validate field.
func expandTimePtr(i interface{}) *time.Time {
rawTime := expandStringPtr(i)
if rawTime == nil {
return nil
}
parsedTime, err := time.Parse(time.RFC3339, *rawTime)
if err != nil {
return nil
}
return &parsedTime
}
2 changes: 2 additions & 0 deletions scaleway/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ func Provider(config *ProviderConfig) plugin.ProviderFunc {
"scaleway_container": resourceScalewayContainer(),
"scaleway_rdb_acl": resourceScalewayRdbACL(),
"scaleway_rdb_database": resourceScalewayRdbDatabase(),
"scaleway_rdb_database_backup": resourceScalewayRdbDatabaseBackup(),
"scaleway_rdb_instance": resourceScalewayRdbInstance(),
"scaleway_rdb_privilege": resourceScalewayRdbPrivilege(),
"scaleway_rdb_user": resourceScalewayRdbUser(),
Expand Down Expand Up @@ -137,6 +138,7 @@ func Provider(config *ProviderConfig) plugin.ProviderFunc {
"scaleway_rdb_acl": dataSourceScalewayRDBACL(),
"scaleway_rdb_instance": dataSourceScalewayRDBInstance(),
"scaleway_rdb_database": dataSourceScalewayRDBDatabase(),
"scaleway_rdb_database_backup": dataSourceScalewayRDBDatabaseBackup(),
Codelax marked this conversation as resolved.
Show resolved Hide resolved
"scaleway_rdb_privilege": dataSourceScalewayRDBPrivilege(),
"scaleway_redis_cluster": dataSourceScalewayRedisCluster(),
"scaleway_registry_namespace": dataSourceScalewayRegistryNamespace(),
Expand Down