Skip to content

Commit 86c04b6

Browse files
committed
feat(rdb): add 8 new RDB actions with tests and documentation
1 parent c4ee92c commit 86c04b6

File tree

34 files changed

+16273
-0
lines changed

34 files changed

+16273
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package regional
2+
3+
import (
4+
"github.com/hashicorp/terraform-plugin-framework/action/schema"
5+
"github.com/scaleway/scaleway-sdk-go/scw"
6+
)
7+
8+
// AllRegions returns all valid Scaleway regions as strings
9+
func AllRegions() []string {
10+
regions := make([]string, 0, len(scw.AllRegions))
11+
for _, r := range scw.AllRegions {
12+
regions = append(regions, r.String())
13+
}
14+
15+
return regions
16+
}
17+
18+
// SchemaAttribute returns a Plugin Framework schema attribute for a region field
19+
func SchemaAttribute() schema.StringAttribute {
20+
return schema.StringAttribute{
21+
Optional: true,
22+
Description: "The region you want to attach the resource to",
23+
}
24+
}
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
package rdb
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/hashicorp/terraform-plugin-framework/action"
8+
"github.com/hashicorp/terraform-plugin-framework/action/schema"
9+
"github.com/hashicorp/terraform-plugin-framework/types"
10+
rdb "github.com/scaleway/scaleway-sdk-go/api/rdb/v1"
11+
"github.com/scaleway/scaleway-sdk-go/scw"
12+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality"
13+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional"
14+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/meta"
15+
)
16+
17+
var (
18+
_ action.Action = (*DatabaseBackupExportAction)(nil)
19+
_ action.ActionWithConfigure = (*DatabaseBackupExportAction)(nil)
20+
)
21+
22+
// DatabaseBackupExportAction exports a database backup.
23+
type DatabaseBackupExportAction struct {
24+
rdbAPI *rdb.API
25+
meta *meta.Meta
26+
}
27+
28+
func (a *DatabaseBackupExportAction) Configure(_ context.Context, req action.ConfigureRequest, resp *action.ConfigureResponse) {
29+
if req.ProviderData == nil {
30+
return
31+
}
32+
33+
m, ok := req.ProviderData.(*meta.Meta)
34+
if !ok {
35+
resp.Diagnostics.AddError(
36+
"Unexpected Action Configure Type",
37+
fmt.Sprintf("Expected *meta.Meta, got: %T. Please report this issue to the provider developers.", req.ProviderData),
38+
)
39+
40+
return
41+
}
42+
43+
a.meta = m
44+
a.rdbAPI = newAPI(m)
45+
}
46+
47+
func (a *DatabaseBackupExportAction) Metadata(_ context.Context, req action.MetadataRequest, resp *action.MetadataResponse) {
48+
resp.TypeName = req.ProviderTypeName + "_rdb_database_backup_export_action"
49+
}
50+
51+
type DatabaseBackupExportActionModel struct {
52+
BackupID types.String `tfsdk:"backup_id"`
53+
Region types.String `tfsdk:"region"`
54+
Wait types.Bool `tfsdk:"wait"`
55+
}
56+
57+
// NewDatabaseBackupExportAction returns a new RDB database backup export action.
58+
func NewDatabaseBackupExportAction() action.Action {
59+
return &DatabaseBackupExportAction{}
60+
}
61+
62+
func (a *DatabaseBackupExportAction) Schema(_ context.Context, _ action.SchemaRequest, resp *action.SchemaResponse) {
63+
resp.Schema = schema.Schema{
64+
Attributes: map[string]schema.Attribute{
65+
"backup_id": schema.StringAttribute{
66+
Required: true,
67+
Description: "Database backup ID to export. Can be a plain UUID or a regional ID."},
68+
"region": regional.SchemaAttribute(),
69+
"wait": schema.BoolAttribute{
70+
Optional: true,
71+
Description: "Wait for the export operation to complete before returning.",
72+
},
73+
},
74+
}
75+
}
76+
77+
func (a *DatabaseBackupExportAction) Invoke(ctx context.Context, req action.InvokeRequest, resp *action.InvokeResponse) {
78+
var data DatabaseBackupExportActionModel
79+
80+
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
81+
82+
if resp.Diagnostics.HasError() {
83+
return
84+
}
85+
86+
if a.rdbAPI == nil {
87+
resp.Diagnostics.AddError(
88+
"Unconfigured rdbAPI",
89+
"The action was not properly configured. The Scaleway client is missing. "+
90+
"This is usually a bug in the provider. Please report it to the maintainers.",
91+
)
92+
93+
return
94+
}
95+
96+
if data.BackupID.IsNull() || data.BackupID.IsUnknown() || data.BackupID.ValueString() == "" {
97+
resp.Diagnostics.AddError(
98+
"Missing backup_id",
99+
"The backup_id attribute is required to export a database backup.",
100+
)
101+
102+
return
103+
}
104+
105+
backupID := locality.ExpandID(data.BackupID.ValueString())
106+
107+
var region scw.Region
108+
109+
if !data.Region.IsNull() && !data.Region.IsUnknown() && data.Region.ValueString() != "" {
110+
parsedRegion, err := scw.ParseRegion(data.Region.ValueString())
111+
if err != nil {
112+
resp.Diagnostics.AddError(
113+
"Invalid region value",
114+
fmt.Sprintf("The region attribute must be a valid Scaleway region. Got %q: %s", data.Region.ValueString(), err),
115+
)
116+
117+
return
118+
}
119+
120+
region = parsedRegion
121+
} else {
122+
if derivedRegion, id, parseErr := regional.ParseID(data.BackupID.ValueString()); parseErr == nil {
123+
region = derivedRegion
124+
backupID = id
125+
} else if a.meta != nil {
126+
defaultRegion, exists := a.meta.ScwClient().GetDefaultRegion()
127+
if !exists {
128+
resp.Diagnostics.AddError(
129+
"Unable to determine region",
130+
"Failed to get default region from provider configuration. Please set the region attribute, use a regional backup_id, or configure a default region in the provider.",
131+
)
132+
133+
return
134+
}
135+
136+
region = defaultRegion
137+
}
138+
}
139+
140+
if region == "" {
141+
resp.Diagnostics.AddError(
142+
"Missing region",
143+
"Could not determine region for RDB database backup export. Please set the region attribute, use a regional backup_id, or configure a default region in the provider.",
144+
)
145+
146+
return
147+
}
148+
149+
exportReq := &rdb.ExportDatabaseBackupRequest{
150+
Region: region,
151+
DatabaseBackupID: backupID,
152+
}
153+
154+
_, err := a.rdbAPI.ExportDatabaseBackup(exportReq, scw.WithContext(ctx))
155+
if err != nil {
156+
resp.Diagnostics.AddError(
157+
"Error executing RDB ExportDatabaseBackup action",
158+
fmt.Sprintf("Failed to export backup %s: %s", backupID, err),
159+
)
160+
161+
return
162+
}
163+
164+
if data.Wait.ValueBool() {
165+
_, err = waitForRDBDatabaseBackup(ctx, a.rdbAPI, region, backupID, defaultInstanceTimeout)
166+
if err != nil {
167+
resp.Diagnostics.AddError(
168+
"Error waiting for RDB database backup export completion",
169+
fmt.Sprintf("Export operation for backup %s did not complete: %s", backupID, err),
170+
)
171+
172+
return
173+
}
174+
}
175+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package rdb_test
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing"
7+
8+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
9+
"github.com/hashicorp/terraform-plugin-testing/terraform"
10+
rdbSDK "github.com/scaleway/scaleway-sdk-go/api/rdb/v1"
11+
"github.com/scaleway/scaleway-sdk-go/scw"
12+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/acctest"
13+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional"
14+
)
15+
16+
func TestAccActionRDBDatabaseBackupExport_Basic(t *testing.T) {
17+
if acctest.IsRunningOpenTofu() {
18+
t.Skip("Skipping TestAccActionRDBDatabaseBackupExport_Basic because action are not yet supported on OpenTofu")
19+
}
20+
21+
tt := acctest.NewTestTools(t)
22+
defer tt.Cleanup()
23+
24+
resource.ParallelTest(t, resource.TestCase{
25+
ProtoV6ProviderFactories: tt.ProviderFactories,
26+
Steps: []resource.TestStep{
27+
{
28+
Config: `
29+
resource "scaleway_rdb_instance" "main" {
30+
name = "test-rdb-action-backup-export"
31+
node_type = "db-dev-s"
32+
engine = "PostgreSQL-15"
33+
is_ha_cluster = false
34+
disable_backup = true
35+
user_name = "my_initial_user"
36+
password = "thiZ_is_v&ry_s3cret"
37+
volume_type = "sbs_5k"
38+
volume_size_in_gb = 10
39+
}
40+
41+
resource "scaleway_rdb_database" "main" {
42+
instance_id = scaleway_rdb_instance.main.id
43+
name = "test_db"
44+
}
45+
46+
resource "scaleway_rdb_database_backup" "main" {
47+
instance_id = scaleway_rdb_instance.main.id
48+
database_name = scaleway_rdb_database.main.name
49+
name = "test-backup-export"
50+
depends_on = [scaleway_rdb_database.main]
51+
52+
lifecycle {
53+
action_trigger {
54+
events = [after_create]
55+
actions = [action.scaleway_rdb_database_backup_export_action.main]
56+
}
57+
}
58+
}
59+
60+
action "scaleway_rdb_database_backup_export_action" "main" {
61+
config {
62+
backup_id = scaleway_rdb_database_backup.main.id
63+
wait = true
64+
}
65+
}
66+
`,
67+
Check: resource.ComposeTestCheckFunc(
68+
isBackupExported(tt, "scaleway_rdb_database_backup.main"),
69+
),
70+
},
71+
},
72+
})
73+
}
74+
75+
func isBackupExported(tt *acctest.TestTools, resourceName string) resource.TestCheckFunc {
76+
return func(state *terraform.State) error {
77+
rs, ok := state.RootModule().Resources[resourceName]
78+
if !ok {
79+
return fmt.Errorf("not found: %s", resourceName)
80+
}
81+
82+
region, id, err := regional.ParseID(rs.Primary.ID)
83+
if err != nil {
84+
return fmt.Errorf("failed to parse backup ID: %w", err)
85+
}
86+
87+
api := rdbSDK.NewAPI(tt.Meta.ScwClient())
88+
89+
backup, err := api.GetDatabaseBackup(&rdbSDK.GetDatabaseBackupRequest{
90+
Region: region,
91+
DatabaseBackupID: id,
92+
}, scw.WithContext(context.Background()))
93+
if err != nil {
94+
return fmt.Errorf("failed to get backup: %w", err)
95+
}
96+
97+
if backup == nil {
98+
return fmt.Errorf("backup %s not found", id)
99+
}
100+
101+
if backup.Status != rdbSDK.DatabaseBackupStatusReady {
102+
return fmt.Errorf("backup %s is not ready, status: %s", id, backup.Status)
103+
}
104+
105+
return nil
106+
}
107+
}

0 commit comments

Comments
 (0)