Skip to content

Commit c5ee71b

Browse files
remyleoneCodelax
andcommitted
feat(rdb): add support for database backup (scaleway#783)
Co-authored-by: Jules Casteran <[email protected]>
1 parent 97ef8a9 commit c5ee71b

12 files changed

+4454
-6
lines changed
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
layout: "scaleway"
3+
page_title: "Scaleway: scaleway_rdb_database_backup"
4+
description: |-
5+
Gets information about an RDB backup.
6+
---
7+
8+
# scaleway_rdb_database_backup
9+
10+
Gets information about an RDB backup.
11+
12+
## Example Usage
13+
14+
```hcl
15+
data scaleway_rdb_database_backup find_by_name {
16+
name = "mybackup"
17+
}
18+
19+
data scaleway_rdb_database_backup find_by_name_and_instance {
20+
name = "mybackup"
21+
instance_id = "11111111-1111-1111-1111-111111111111"
22+
}
23+
24+
data scaleway_rdb_database_backup find_by_id {
25+
backup_id = "11111111-1111-1111-1111-111111111111"
26+
}
27+
```
28+
29+
## Argument Reference
30+
31+
- `backup_id` - (Optional) The RDB backup ID.
32+
Only one of the `name` and `backup_id` should be specified.
33+
34+
- `instance_id` - (Optional) The RDB instance ID.
35+
36+
- `name` - (Optional) The name of the RDB instance.
37+
Only one of the `name` and `backup_id` should be specified.
38+
39+

docs/resources/rdb_database_backup.md

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
---
2+
page_title: "Scaleway: scaleway_rdb_database_backup"
3+
description: |-
4+
Manages Scaleway RDB Database Backup.
5+
---
6+
7+
# scaleway_rdb_database_backup
8+
9+
Creates and manages Scaleway RDB database backup.
10+
For more information, see [the documentation](https://developers.scaleway.com/en/products/rdb/api).
11+
12+
## Examples
13+
14+
### Basic
15+
16+
```hcl
17+
resource scaleway_rdb_database_backup "main" {
18+
instance_id = data.scaleway_rdb_instance.main.id
19+
database_name = data.scaleway_rdb_database.main.name
20+
}
21+
```
22+
23+
### With expiration
24+
25+
```hcl
26+
resource scaleway_rdb_database_backup "main" {
27+
instance_id = data.scaleway_rdb_instance.main.id
28+
database_name = data.scaleway_rdb_database.main.name
29+
expires_at = "2022-06-16T07:48:44Z"
30+
}
31+
```
32+
33+
## Arguments Reference
34+
35+
The following arguments are supported:
36+
37+
- `instance_id` - (Required) UUID of the instance where the database to backup is.
38+
39+
~> **Important:** Updates to `instance_id` will recreate the Backup.
40+
41+
- `name` - (Required) Name of the database (e.g. `my-database`).
42+
43+
- `expires_at` (Optional) Expiration date (Format ISO 8601).
44+
45+
~> **Important:** `expires_at` cannot be removed after being set.
46+
47+
48+
## Attributes Reference
49+
50+
In addition to all arguments above, the following attributes are exported:
51+
52+
- `size` - Size of the backup (in bytes).
53+
- `instance_name` - Name of the instance of the backup.
54+
- `created_at` - Creation date (Format ISO 8601).
55+
- `updated_at` - Updated date (Format ISO 8601).
56+
57+
58+
## Import
59+
60+
RDB Database can be imported using the `{region}/{id}`, e.g.
61+
62+
```bash
63+
$ terraform import scaleway_rdb_database_backup.mybackup fr-par/11111111-1111-1111-1111-111111111111
64+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package scaleway
2+
3+
import (
4+
"context"
5+
6+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
8+
"github.com/scaleway/scaleway-sdk-go/api/rdb/v1"
9+
)
10+
11+
func dataSourceScalewayRDBDatabaseBackup() *schema.Resource {
12+
// Generate datasource schema from resource
13+
dsSchema := datasourceSchemaFromResourceSchema(resourceScalewayRdbDatabaseBackup().Schema)
14+
15+
addOptionalFieldsToSchema(dsSchema, "name", "region", "instance_id")
16+
17+
dsSchema["instance_id"].RequiredWith = []string{"name"}
18+
dsSchema["backup_id"] = &schema.Schema{
19+
Type: schema.TypeString,
20+
Optional: true,
21+
Description: "The ID of the Backup",
22+
ConflictsWith: []string{"name", "instance_id"},
23+
ValidateFunc: validationUUIDorUUIDWithLocality(),
24+
}
25+
26+
return &schema.Resource{
27+
ReadContext: dataSourceScalewayRDBDatabaseBackupRead,
28+
Schema: dsSchema,
29+
}
30+
}
31+
32+
func dataSourceScalewayRDBDatabaseBackupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
33+
api, region, err := rdbAPIWithRegion(d, meta)
34+
if err != nil {
35+
return diag.FromErr(err)
36+
}
37+
38+
backupID, backupIDExists := d.GetOk("backup_id")
39+
if !backupIDExists {
40+
res, err := api.ListDatabaseBackups(&rdb.ListDatabaseBackupsRequest{
41+
Region: region,
42+
Name: expandStringPtr(d.Get("name")),
43+
InstanceID: expandStringPtr(expandID(d.Get("instance_id"))),
44+
ProjectID: expandStringPtr(d.Get("project_id")),
45+
})
46+
if err != nil {
47+
return diag.FromErr(err)
48+
}
49+
for _, backup := range res.DatabaseBackups {
50+
if backup.Name == d.Get("name").(string) {
51+
if backupID != "" {
52+
return diag.Errorf("more than 1 backup found with the same name %s", d.Get("name"))
53+
}
54+
backupID = backup.ID
55+
}
56+
}
57+
if backupID == "" {
58+
return diag.Errorf("no backup found with the name %s", d.Get("name"))
59+
}
60+
}
61+
62+
regionID := datasourceNewRegionalizedID(backupID, region)
63+
d.SetId(regionID)
64+
err = d.Set("backup_id", regionID)
65+
if err != nil {
66+
return diag.FromErr(err)
67+
}
68+
69+
diags := resourceScalewayRdbDatabaseBackupRead(ctx, d, meta)
70+
if diags != nil {
71+
return append(diags, diag.Errorf("failed to read database backup state")...)
72+
}
73+
74+
if d.Id() == "" {
75+
return diag.Errorf("database backup (%s) not found", regionID)
76+
}
77+
78+
return nil
79+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package scaleway
2+
3+
import (
4+
"testing"
5+
6+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
7+
)
8+
9+
func TestAccScalewayDataSourceRdbDatabaseBackup_Basic(t *testing.T) {
10+
tt := NewTestTools(t)
11+
defer tt.Cleanup()
12+
resource.ParallelTest(t, resource.TestCase{
13+
PreCheck: func() { testAccPreCheck(t) },
14+
ProviderFactories: tt.ProviderFactories,
15+
CheckDestroy: resource.ComposeTestCheckFunc(
16+
testAccCheckScalewayRdbInstanceDestroy(tt),
17+
testAccCheckScalewayRdbDatabaseBackupDestroy(tt),
18+
),
19+
Steps: []resource.TestStep{
20+
{
21+
Config: `
22+
resource "scaleway_rdb_instance" "server" {
23+
name = "test-terraform"
24+
node_type = "db-dev-s"
25+
engine = "PostgreSQL-11"
26+
}
27+
resource "scaleway_rdb_database" "database" {
28+
name = "test-terraform"
29+
instance_id = scaleway_rdb_instance.server.id
30+
}`,
31+
},
32+
{
33+
Config: `
34+
resource "scaleway_rdb_instance" "server" {
35+
name = "test-terraform"
36+
node_type = "db-dev-s"
37+
engine = "PostgreSQL-11"
38+
}
39+
resource "scaleway_rdb_database" "database" {
40+
name = "test-terraform"
41+
instance_id = scaleway_rdb_instance.server.id
42+
}
43+
44+
resource scaleway_rdb_database_backup backup {
45+
instance_id = scaleway_rdb_instance.server.id
46+
database_name = scaleway_rdb_database.database.name
47+
name = "test_backup_datasource"
48+
}
49+
50+
data scaleway_rdb_database_backup find_by_name {
51+
name = scaleway_rdb_database_backup.backup.name
52+
}
53+
54+
data scaleway_rdb_database_backup find_by_name_and_instance {
55+
name = scaleway_rdb_database_backup.backup.name
56+
instance_id = scaleway_rdb_instance.server.id
57+
}
58+
59+
data scaleway_rdb_database_backup find_by_id {
60+
backup_id = scaleway_rdb_database_backup.backup.id
61+
}
62+
`,
63+
Check: resource.ComposeTestCheckFunc(
64+
testAccCheckRdbDatabaseExists(tt, "scaleway_rdb_instance.server", "scaleway_rdb_database.database"),
65+
testAccCheckRdbDatabaseBackupExists(tt, "scaleway_rdb_database_backup.backup"),
66+
67+
resource.TestCheckResourceAttr("data.scaleway_rdb_database_backup.find_by_name", "name", "test_backup_datasource"),
68+
resource.TestCheckResourceAttr("data.scaleway_rdb_database_backup.find_by_name_and_instance", "name", "test_backup_datasource"),
69+
resource.TestCheckResourceAttr("data.scaleway_rdb_database_backup.find_by_id", "name", "test_backup_datasource"),
70+
),
71+
},
72+
},
73+
})
74+
}

scaleway/helpers.go

+23
Original file line numberDiff line numberDiff line change
@@ -637,3 +637,26 @@ func getBool(d *schema.ResourceData, key string) interface{} {
637637
}
638638
return val
639639
}
640+
641+
// validateDate will validate that field is a valid ISO 8601
642+
// It is the same as RFC3339
643+
func validateDate() schema.SchemaValidateDiagFunc {
644+
return func(i interface{}, path cty.Path) diag.Diagnostics {
645+
date, isStr := i.(string)
646+
if !isStr {
647+
return diag.Errorf("%v is not a string", date)
648+
}
649+
_, err := time.Parse(time.RFC3339, date)
650+
if err != nil {
651+
return diag.FromErr(err)
652+
}
653+
return nil
654+
}
655+
}
656+
657+
func flattenSize(size *scw.Size) interface{} {
658+
if size == nil {
659+
return 0
660+
}
661+
return *size
662+
}

scaleway/helpers_rdb.go

+30-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import (
1111
)
1212

1313
const (
14-
defaultRdbInstanceTimeout = 15 * time.Minute
14+
defaultRdbInstanceTimeout = 15 * time.Minute
15+
defaultWaitRDBRetryInterval = 30 * time.Second
1516
)
1617

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

80+
func waitForRDBDatabaseBackup(ctx context.Context, api *rdb.API, region scw.Region, id string, timeout time.Duration) (*rdb.DatabaseBackup, error) {
81+
retryInterval := defaultWaitRDBRetryInterval
82+
if DefaultWaitRetryInterval != nil {
83+
retryInterval = *DefaultWaitRetryInterval
84+
}
85+
86+
return api.WaitForDatabaseBackup(&rdb.WaitForDatabaseBackupRequest{
87+
Region: region,
88+
Timeout: scw.TimeDurationPtr(timeout),
89+
DatabaseBackupID: id,
90+
RetryInterval: &retryInterval,
91+
}, scw.WithContext(ctx))
92+
}
93+
7994
func expandPrivateNetwork(data interface{}, exist bool) ([]*rdb.EndpointSpec, error) {
8095
if data == nil || !exist {
8196
return nil, nil
@@ -212,3 +227,17 @@ func flattenLoadBalancer(endpoints []*rdb.Endpoint) interface{} {
212227

213228
return flat
214229
}
230+
231+
// expandTimePtr returns a time pointer for an RFC3339 time.
232+
// It returns nil if time is not valid, you should use validateDate to validate field.
233+
func expandTimePtr(i interface{}) *time.Time {
234+
rawTime := expandStringPtr(i)
235+
if rawTime == nil {
236+
return nil
237+
}
238+
parsedTime, err := time.Parse(time.RFC3339, *rawTime)
239+
if err != nil {
240+
return nil
241+
}
242+
return &parsedTime
243+
}

scaleway/provider.go

+2
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ func Provider(config *ProviderConfig) plugin.ProviderFunc {
9494
"scaleway_container": resourceScalewayContainer(),
9595
"scaleway_rdb_acl": resourceScalewayRdbACL(),
9696
"scaleway_rdb_database": resourceScalewayRdbDatabase(),
97+
"scaleway_rdb_database_backup": resourceScalewayRdbDatabaseBackup(),
9798
"scaleway_rdb_instance": resourceScalewayRdbInstance(),
9899
"scaleway_rdb_privilege": resourceScalewayRdbPrivilege(),
99100
"scaleway_rdb_user": resourceScalewayRdbUser(),
@@ -140,6 +141,7 @@ func Provider(config *ProviderConfig) plugin.ProviderFunc {
140141
"scaleway_rdb_acl": dataSourceScalewayRDBACL(),
141142
"scaleway_rdb_instance": dataSourceScalewayRDBInstance(),
142143
"scaleway_rdb_database": dataSourceScalewayRDBDatabase(),
144+
"scaleway_rdb_database_backup": dataSourceScalewayRDBDatabaseBackup(),
143145
"scaleway_rdb_privilege": dataSourceScalewayRDBPrivilege(),
144146
"scaleway_redis_cluster": dataSourceScalewayRedisCluster(),
145147
"scaleway_registry_namespace": dataSourceScalewayRegistryNamespace(),

0 commit comments

Comments
 (0)