Skip to content

Commit b851648

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

File tree

36 files changed

+16428
-0
lines changed

36 files changed

+16428
-0
lines changed

go.sum

Lines changed: 60 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
}
25+
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
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/schema/validator"
10+
"github.com/hashicorp/terraform-plugin-framework/types"
11+
rdb "github.com/scaleway/scaleway-sdk-go/api/rdb/v1"
12+
"github.com/scaleway/scaleway-sdk-go/scw"
13+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality"
14+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional"
15+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/meta"
16+
)
17+
18+
var (
19+
_ action.Action = (*DatabaseBackupExportAction)(nil)
20+
_ action.ActionWithConfigure = (*DatabaseBackupExportAction)(nil)
21+
)
22+
23+
// DatabaseBackupExportAction exports a database backup.
24+
type DatabaseBackupExportAction struct {
25+
rdbAPI *rdb.API
26+
meta *meta.Meta
27+
}
28+
29+
func (a *DatabaseBackupExportAction) Configure(_ context.Context, req action.ConfigureRequest, resp *action.ConfigureResponse) {
30+
if req.ProviderData == nil {
31+
return
32+
}
33+
34+
m, ok := req.ProviderData.(*meta.Meta)
35+
if !ok {
36+
resp.Diagnostics.AddError(
37+
"Unexpected Action Configure Type",
38+
fmt.Sprintf("Expected *meta.Meta, got: %T. Please report this issue to the provider developers.", req.ProviderData),
39+
)
40+
41+
return
42+
}
43+
44+
a.meta = m
45+
a.rdbAPI = newAPI(m)
46+
}
47+
48+
func (a *DatabaseBackupExportAction) Metadata(_ context.Context, req action.MetadataRequest, resp *action.MetadataResponse) {
49+
resp.TypeName = req.ProviderTypeName + "_rdb_database_backup_export_action"
50+
}
51+
52+
type DatabaseBackupExportActionModel struct {
53+
BackupID types.String `tfsdk:"backup_id"`
54+
Region types.String `tfsdk:"region"`
55+
Wait types.Bool `tfsdk:"wait"`
56+
}
57+
58+
// NewDatabaseBackupExportAction returns a new RDB database backup export action.
59+
func NewDatabaseBackupExportAction() action.Action {
60+
return &DatabaseBackupExportAction{}
61+
}
62+
63+
func (a *DatabaseBackupExportAction) Schema(_ context.Context, _ action.SchemaRequest, resp *action.SchemaResponse) {
64+
resp.Schema = schema.Schema{
65+
Attributes: map[string]schema.Attribute{
66+
"backup_id": schema.StringAttribute{
67+
Required: true,
68+
Description: "Database backup ID to export. Can be a plain UUID or a regional ID.",
69+
Validators: []validator.String{
70+
},
71+
},
72+
"region": regional.SchemaAttribute(),
73+
"wait": schema.BoolAttribute{
74+
Optional: true,
75+
Description: "Wait for the export operation to complete before returning.",
76+
},
77+
},
78+
}
79+
}
80+
81+
func (a *DatabaseBackupExportAction) Invoke(ctx context.Context, req action.InvokeRequest, resp *action.InvokeResponse) {
82+
var data DatabaseBackupExportActionModel
83+
84+
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
85+
86+
if resp.Diagnostics.HasError() {
87+
return
88+
}
89+
90+
if a.rdbAPI == nil {
91+
resp.Diagnostics.AddError(
92+
"Unconfigured rdbAPI",
93+
"The action was not properly configured. The Scaleway client is missing. "+
94+
"This is usually a bug in the provider. Please report it to the maintainers.",
95+
)
96+
97+
return
98+
}
99+
100+
if data.BackupID.IsNull() || data.BackupID.IsUnknown() || data.BackupID.ValueString() == "" {
101+
resp.Diagnostics.AddError(
102+
"Missing backup_id",
103+
"The backup_id attribute is required to export a database backup.",
104+
)
105+
106+
return
107+
}
108+
109+
backupID := locality.ExpandID(data.BackupID.ValueString())
110+
111+
var region scw.Region
112+
113+
if !data.Region.IsNull() && !data.Region.IsUnknown() && data.Region.ValueString() != "" {
114+
parsedRegion, err := scw.ParseRegion(data.Region.ValueString())
115+
if err != nil {
116+
resp.Diagnostics.AddError(
117+
"Invalid region value",
118+
fmt.Sprintf("The region attribute must be a valid Scaleway region. Got %q: %s", data.Region.ValueString(), err),
119+
)
120+
121+
return
122+
}
123+
124+
region = parsedRegion
125+
} else {
126+
if derivedRegion, id, parseErr := regional.ParseID(data.BackupID.ValueString()); parseErr == nil {
127+
region = derivedRegion
128+
backupID = id
129+
} else if a.meta != nil {
130+
defaultRegion, exists := a.meta.ScwClient().GetDefaultRegion()
131+
if !exists {
132+
resp.Diagnostics.AddError(
133+
"Unable to determine region",
134+
"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.",
135+
)
136+
137+
return
138+
}
139+
140+
region = defaultRegion
141+
}
142+
}
143+
144+
if region == "" {
145+
resp.Diagnostics.AddError(
146+
"Missing region",
147+
"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.",
148+
)
149+
150+
return
151+
}
152+
153+
exportReq := &rdb.ExportDatabaseBackupRequest{
154+
Region: region,
155+
DatabaseBackupID: backupID,
156+
}
157+
158+
_, err := a.rdbAPI.ExportDatabaseBackup(exportReq, scw.WithContext(ctx))
159+
if err != nil {
160+
resp.Diagnostics.AddError(
161+
"Error executing RDB ExportDatabaseBackup action",
162+
fmt.Sprintf("Failed to export backup %s: %s", backupID, err),
163+
)
164+
165+
return
166+
}
167+
168+
if data.Wait.ValueBool() {
169+
_, err = waitForRDBDatabaseBackup(ctx, a.rdbAPI, region, backupID, defaultInstanceTimeout)
170+
if err != nil {
171+
resp.Diagnostics.AddError(
172+
"Error waiting for RDB database backup export completion",
173+
fmt.Sprintf("Export operation for backup %s did not complete: %s", backupID, err),
174+
)
175+
176+
return
177+
}
178+
}
179+
}
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)