Skip to content

Commit 4d957e8

Browse files
committed
add support for all server actions and snapshot operations
1 parent f826d8c commit 4d957e8

27 files changed

+43274
-1851
lines changed
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
package instance
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+
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
11+
block "github.com/scaleway/scaleway-sdk-go/api/block/v1alpha1"
12+
"github.com/scaleway/scaleway-sdk-go/api/instance/v1"
13+
"github.com/scaleway/scaleway-sdk-go/scw"
14+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality"
15+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/meta"
16+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/instance/instancehelpers"
17+
)
18+
19+
var (
20+
_ action.Action = (*CreateSnapshot)(nil)
21+
_ action.ActionWithConfigure = (*CreateSnapshot)(nil)
22+
)
23+
24+
type CreateSnapshot struct {
25+
blockAndInstanceAPI *instancehelpers.BlockAndInstanceAPI
26+
}
27+
28+
func (c *CreateSnapshot) 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 *scw.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
38+
)
39+
40+
return
41+
}
42+
43+
client := m.ScwClient()
44+
c.blockAndInstanceAPI = instancehelpers.NewBlockAndInstanceAPI(client)
45+
}
46+
47+
func (c *CreateSnapshot) Metadata(_ context.Context, req action.MetadataRequest, resp *action.MetadataResponse) {
48+
resp.TypeName = req.ProviderTypeName + "_instance_create_snapshot"
49+
}
50+
51+
type CreateSnapshotModel struct {
52+
Zone types.String `tfsdk:"zone"`
53+
VolumeID types.String `tfsdk:"volume_id"`
54+
Name types.String `tfsdk:"name"`
55+
Tags types.List `tfsdk:"tags"`
56+
Wait types.Bool `tfsdk:"wait"`
57+
}
58+
59+
func NewCreateSnapshot() action.Action {
60+
return &CreateSnapshot{}
61+
}
62+
63+
func (c *CreateSnapshot) Schema(_ context.Context, _ action.SchemaRequest, resp *action.SchemaResponse) {
64+
resp.Schema = schema.Schema{
65+
Attributes: map[string]schema.Attribute{
66+
"volume_id": schema.StringAttribute{
67+
Required: true,
68+
Description: "ID of the volume to snapshot",
69+
},
70+
"zone": schema.StringAttribute{
71+
Optional: true,
72+
Description: "Zone of the volume to snapshot",
73+
},
74+
"name": schema.StringAttribute{
75+
Optional: true,
76+
Description: "Name of the snapshot",
77+
},
78+
"tags": schema.ListAttribute{
79+
Optional: true,
80+
ElementType: basetypes.StringType{},
81+
Description: "List of tags associated with the snapshot",
82+
},
83+
"wait": schema.BoolAttribute{
84+
Optional: true,
85+
Description: "Wait for snapshotting operation to be completed",
86+
},
87+
},
88+
}
89+
}
90+
91+
func (c *CreateSnapshot) Invoke(ctx context.Context, req action.InvokeRequest, resp *action.InvokeResponse) {
92+
var data CreateSnapshotModel
93+
// Read action config data into the model
94+
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
95+
96+
if resp.Diagnostics.HasError() {
97+
return
98+
}
99+
100+
if c.blockAndInstanceAPI == nil {
101+
resp.Diagnostics.AddError(
102+
"Unconfigured instanceAPI / blockAPI",
103+
"The action was not properly configured. The Scaleway client is missing. "+
104+
"This is usually a bug in the provider. Please report it to the maintainers.",
105+
)
106+
107+
return
108+
}
109+
110+
zone, volumeID, _ := locality.ParseLocalizedID(data.VolumeID.ValueString())
111+
if zone == "" {
112+
if !data.Zone.IsNull() {
113+
zone = data.Zone.ValueString()
114+
} else {
115+
resp.Diagnostics.AddError(
116+
"missing zone in config",
117+
fmt.Sprintf("zone could not be extracted from either the action configuration or the resource ID (%s)",
118+
data.VolumeID.ValueString(),
119+
),
120+
)
121+
122+
return
123+
}
124+
}
125+
126+
volume, err := c.blockAndInstanceAPI.GetUnknownVolume(&instancehelpers.GetUnknownVolumeRequest{
127+
VolumeID: volumeID,
128+
Zone: scw.Zone(zone),
129+
}, scw.WithContext(ctx))
130+
if err != nil {
131+
resp.Diagnostics.AddError(
132+
"could not find volume "+data.VolumeID.ValueString(),
133+
err.Error(),
134+
)
135+
136+
return
137+
}
138+
139+
switch volume.InstanceVolumeType {
140+
case instance.VolumeVolumeTypeLSSD:
141+
actionReq := &instance.CreateSnapshotRequest{
142+
VolumeID: &volumeID,
143+
Zone: scw.Zone(zone),
144+
}
145+
146+
if !data.Name.IsNull() {
147+
actionReq.Name = data.Name.ValueString()
148+
}
149+
150+
if len(data.Tags.Elements()) > 0 {
151+
tags := make([]string, 0, len(data.Tags.Elements()))
152+
153+
diags := data.Tags.ElementsAs(ctx, &tags, false)
154+
if diags.HasError() {
155+
resp.Diagnostics.Append(diags...)
156+
} else {
157+
actionReq.Tags = &tags
158+
}
159+
}
160+
161+
snapshot, err := c.blockAndInstanceAPI.CreateSnapshot(actionReq, scw.WithContext(ctx))
162+
if err != nil {
163+
resp.Diagnostics.AddError(
164+
"error creating instance snapshot",
165+
err.Error())
166+
167+
return
168+
}
169+
170+
if data.Wait.ValueBool() {
171+
_, errWait := c.blockAndInstanceAPI.WaitForSnapshot(&instance.WaitForSnapshotRequest{
172+
SnapshotID: snapshot.Snapshot.ID,
173+
Zone: scw.Zone(zone),
174+
}, scw.WithContext(ctx))
175+
if errWait != nil {
176+
resp.Diagnostics.AddError(
177+
"error waiting for instance snapshot",
178+
err.Error())
179+
}
180+
}
181+
case instance.VolumeVolumeTypeSbsVolume:
182+
api := c.blockAndInstanceAPI.BlockAPI
183+
184+
actionReq := &block.CreateSnapshotRequest{
185+
VolumeID: volumeID,
186+
Zone: scw.Zone(zone),
187+
}
188+
189+
if !data.Name.IsNull() {
190+
actionReq.Name = data.Name.ValueString()
191+
}
192+
193+
if len(data.Tags.Elements()) > 0 {
194+
tags := make([]string, 0, len(data.Tags.Elements()))
195+
196+
diags := data.Tags.ElementsAs(ctx, &tags, false)
197+
if diags.HasError() {
198+
resp.Diagnostics.Append(diags...)
199+
} else {
200+
actionReq.Tags = tags
201+
}
202+
}
203+
204+
snapshot, err := api.CreateSnapshot(actionReq, scw.WithContext(ctx))
205+
if err != nil {
206+
resp.Diagnostics.AddError(
207+
"error creating block snapshot",
208+
err.Error())
209+
210+
return
211+
}
212+
213+
if data.Wait.ValueBool() {
214+
_, errWait := api.WaitForSnapshot(&block.WaitForSnapshotRequest{
215+
SnapshotID: snapshot.ID,
216+
Zone: scw.Zone(zone),
217+
}, scw.WithContext(ctx))
218+
if errWait != nil {
219+
resp.Diagnostics.AddError(
220+
"error waiting for block snapshot",
221+
err.Error())
222+
}
223+
}
224+
case instance.VolumeVolumeTypeScratch:
225+
resp.Diagnostics.AddError(
226+
"invalid volume type",
227+
"cannot create snapshot from a volume of type scratch",
228+
)
229+
default:
230+
resp.Diagnostics.AddError(
231+
"invalid volume type",
232+
fmt.Sprintf("unknown volume type %q", volume.InstanceVolumeType),
233+
)
234+
}
235+
}

0 commit comments

Comments
 (0)