Skip to content

Commit e74fb00

Browse files
committed
handle block on snapshot actions
1 parent d20c1ec commit e74fb00

File tree

3 files changed

+337
-18
lines changed

3 files changed

+337
-18
lines changed

internal/services/instance/action_create_snapshot.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,8 @@ func (c *CreateSnapshot) Invoke(ctx context.Context, req action.InvokeRequest, r
164164
"error creating instance snapshot",
165165
err.Error())
166166

167-
return
168-
}
167+
return
168+
}
169169

170170
if data.Wait.ValueBool() {
171171
_, errWait := c.blockAndInstanceAPI.WaitForSnapshot(&instance.WaitForSnapshotRequest{

internal/services/instance/action_create_snapshot_test.go

Lines changed: 209 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@ import (
88

99
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
1010
"github.com/hashicorp/terraform-plugin-testing/terraform"
11+
blockSDK "github.com/scaleway/scaleway-sdk-go/api/block/v1alpha1"
1112
instanceSDK "github.com/scaleway/scaleway-sdk-go/api/instance/v1"
1213
"github.com/scaleway/scaleway-sdk-go/scw"
1314
"github.com/scaleway/terraform-provider-scaleway/v2/internal/acctest"
15+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/block"
16+
blocktestfuncs "github.com/scaleway/terraform-provider-scaleway/v2/internal/services/block/testfuncs"
1417
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/instance"
1518
instancechecks "github.com/scaleway/terraform-provider-scaleway/v2/internal/services/instance/testfuncs"
1619
)
@@ -36,7 +39,7 @@ func TestAccAction_InstanceCreateSnapshot_Local(t *testing.T) {
3639
ProtoV6ProviderFactories: tt.ProviderFactories,
3740
CheckDestroy: resource.ComposeTestCheckFunc(
3841
instancechecks.IsServerDestroyed(tt),
39-
destroyUntrackedSnapshots(tt, "data.scaleway_instance_volume.main"),
42+
destroyUntrackedInstanceSnapshots(tt, "data.scaleway_instance_volume.main"),
4043
),
4144
Steps: []resource.TestStep{
4245
{
@@ -74,7 +77,7 @@ func TestAccAction_InstanceCreateSnapshot_Local(t *testing.T) {
7477
RefreshState: true,
7578
Check: resource.ComposeTestCheckFunc(
7679
instancechecks.IsVolumePresent(tt, "data.scaleway_instance_volume.main"),
77-
checkSnapshot(tt, "data.scaleway_instance_volume.main", snapshotSpecsCheck{
80+
checkInstanceSnapshot(tt, "data.scaleway_instance_volume.main", snapshotSpecsCheck{
7881
Size: scw.SizePtr(20 * scw.GB),
7982
Type: &localVolumeType,
8083
}),
@@ -118,7 +121,7 @@ func TestAccAction_InstanceCreateSnapshot_Local(t *testing.T) {
118121
RefreshState: true,
119122
Check: resource.ComposeTestCheckFunc(
120123
instancechecks.IsVolumePresent(tt, "data.scaleway_instance_volume.main"),
121-
checkSnapshot(tt, "data.scaleway_instance_volume.main", snapshotSpecsCheck{
124+
checkInstanceSnapshot(tt, "data.scaleway_instance_volume.main", snapshotSpecsCheck{
122125
Name: scw.StringPtr("custom-name-for-snapshot"),
123126
Size: scw.SizePtr(20 * scw.GB),
124127
Tags: []string{"add", "tags", "to", "trigger", "update"},
@@ -130,6 +133,113 @@ func TestAccAction_InstanceCreateSnapshot_Local(t *testing.T) {
130133
})
131134
}
132135

136+
func TestAccAction_InstanceCreateSnapshot_SBS(t *testing.T) {
137+
if acctest.IsRunningOpenTofu() {
138+
t.Skip("Skipping TestAccAction_InstanceCreateSnapshot_SBS because action are not yet supported on OpenTofu")
139+
}
140+
141+
tt := acctest.NewTestTools(t)
142+
defer tt.Cleanup()
143+
144+
sbsVolumeType := instanceSDK.VolumeVolumeTypeSbsVolume
145+
146+
resource.ParallelTest(t, resource.TestCase{
147+
ProtoV6ProviderFactories: tt.ProviderFactories,
148+
CheckDestroy: resource.ComposeTestCheckFunc(
149+
instancechecks.IsServerDestroyed(tt),
150+
destroyUntrackedBlockSnapshots(tt, "data.scaleway_block_volume.main"),
151+
),
152+
Steps: []resource.TestStep{
153+
{
154+
Config: fmt.Sprintf(`
155+
resource "scaleway_instance_server" "main" {
156+
name = "test-tf-action-instance-create-snapshot-sbs"
157+
type = "DEV1-S"
158+
image = "ubuntu_jammy"
159+
160+
root_volume {
161+
volume_type = "%s"
162+
size_in_gb = 20
163+
}
164+
165+
lifecycle {
166+
action_trigger {
167+
events = [after_create]
168+
actions = [action.scaleway_instance_create_snapshot.main]
169+
}
170+
}
171+
}
172+
173+
data "scaleway_block_volume" "main" {
174+
volume_id = scaleway_instance_server.main.root_volume.0.volume_id
175+
}
176+
177+
action "scaleway_instance_create_snapshot" "main" {
178+
config {
179+
volume_id = scaleway_instance_server.main.root_volume.0.volume_id
180+
wait = true
181+
}
182+
}`, sbsVolumeType),
183+
},
184+
{
185+
RefreshState: true,
186+
Check: resource.ComposeTestCheckFunc(
187+
blocktestfuncs.IsVolumePresent(tt, "data.scaleway_block_volume.main"),
188+
checkBlockSnapshot(tt, "data.scaleway_block_volume.main", snapshotSpecsCheck{
189+
Size: scw.SizePtr(20 * scw.GB),
190+
Type: &sbsVolumeType,
191+
}),
192+
),
193+
},
194+
{
195+
Config: fmt.Sprintf(`
196+
resource "scaleway_instance_server" "main" {
197+
name = "test-tf-action-instance-create-snapshot-sbs"
198+
type = "DEV1-S"
199+
image = "ubuntu_jammy"
200+
tags = [ "add", "tags", "to", "trigger", "update" ]
201+
202+
root_volume {
203+
volume_type = "%s"
204+
size_in_gb = 20
205+
}
206+
207+
lifecycle {
208+
action_trigger {
209+
events = [after_update]
210+
actions = [action.scaleway_instance_create_snapshot.main]
211+
}
212+
}
213+
}
214+
215+
data "scaleway_block_volume" "main" {
216+
volume_id = scaleway_instance_server.main.root_volume.0.volume_id
217+
}
218+
219+
action "scaleway_instance_create_snapshot" "main" {
220+
config {
221+
volume_id = scaleway_instance_server.main.root_volume.0.volume_id
222+
tags = scaleway_instance_server.main.tags
223+
name = "custom-name-for-snapshot"
224+
wait = true
225+
}
226+
}`, sbsVolumeType),
227+
},
228+
{
229+
RefreshState: true,
230+
Check: resource.ComposeTestCheckFunc(
231+
blocktestfuncs.IsVolumePresent(tt, "data.scaleway_block_volume.main"),
232+
checkBlockSnapshot(tt, "data.scaleway_block_volume.main", snapshotSpecsCheck{
233+
Name: scw.StringPtr("custom-name-for-snapshot"),
234+
Size: scw.SizePtr(20 * scw.GB),
235+
Tags: []string{"add", "tags", "to", "trigger", "update"},
236+
}),
237+
),
238+
},
239+
},
240+
})
241+
}
242+
133243
func TestAccAction_InstanceCreateSnapshot_Scratch(t *testing.T) {
134244
if acctest.IsRunningOpenTofu() {
135245
t.Skip("Skipping TestAccAction_InstanceCreateSnapshot_Scratch because action are not yet supported on OpenTofu")
@@ -185,7 +295,7 @@ func TestAccAction_InstanceCreateSnapshot_Zone(t *testing.T) {
185295
ProtoV6ProviderFactories: tt.ProviderFactories,
186296
CheckDestroy: resource.ComposeTestCheckFunc(
187297
instancechecks.IsServerDestroyed(tt),
188-
destroyUntrackedSnapshots(tt, "data.scaleway_instance_volume.main"),
298+
destroyUntrackedInstanceSnapshots(tt, "data.scaleway_instance_volume.main"),
189299
),
190300
Steps: []resource.TestStep{
191301
{
@@ -226,7 +336,7 @@ func TestAccAction_InstanceCreateSnapshot_Zone(t *testing.T) {
226336
Check: resource.ComposeTestCheckFunc(
227337
instancechecks.IsVolumePresent(tt, "data.scaleway_instance_volume.main"),
228338
resource.TestCheckResourceAttr("scaleway_instance_server.main", "zone", "fr-par-2"),
229-
checkSnapshot(tt, "data.scaleway_instance_volume.main", snapshotSpecsCheck{
339+
checkInstanceSnapshot(tt, "data.scaleway_instance_volume.main", snapshotSpecsCheck{
230340
Size: scw.SizePtr(20 * scw.GB),
231341
Type: &localVolumeType,
232342
}),
@@ -236,7 +346,7 @@ func TestAccAction_InstanceCreateSnapshot_Zone(t *testing.T) {
236346
})
237347
}
238348

239-
func snapshotMatchesExpectedSpecs(snapshot instanceSDK.Snapshot, expected snapshotSpecsCheck) bool {
349+
func instanceSnapshotMatchesExpectedSpecs(snapshot instanceSDK.Snapshot, expected snapshotSpecsCheck) bool {
240350
if expected.Name != nil && *expected.Name != snapshot.Name {
241351
return false
242352
}
@@ -260,7 +370,7 @@ func snapshotMatchesExpectedSpecs(snapshot instanceSDK.Snapshot, expected snapsh
260370
return true
261371
}
262372

263-
func checkSnapshot(tt *acctest.TestTools, n string, expectedSpecs snapshotSpecsCheck) resource.TestCheckFunc {
373+
func checkInstanceSnapshot(tt *acctest.TestTools, n string, expectedSpecs snapshotSpecsCheck) resource.TestCheckFunc {
264374
return func(state *terraform.State) error {
265375
rs, ok := state.RootModule().Resources[n]
266376
if !ok {
@@ -281,20 +391,20 @@ func checkSnapshot(tt *acctest.TestTools, n string, expectedSpecs snapshotSpecsC
281391
}
282392

283393
if snapshots.TotalCount == 0 {
284-
return fmt.Errorf("could not find any snapshot for volume %s", id)
394+
return fmt.Errorf("could not find any instance snapshot for volume %s", id)
285395
}
286396

287397
for _, snapshot := range snapshots.Snapshots {
288-
if snapshotMatchesExpectedSpecs(*snapshot, expectedSpecs) {
398+
if instanceSnapshotMatchesExpectedSpecs(*snapshot, expectedSpecs) {
289399
return nil
290400
}
291401
}
292402

293-
return fmt.Errorf("could not find any snapshot that matches the specs %+v", expectedSpecs)
403+
return fmt.Errorf("could not find any instance snapshot that matches the specs %+v", expectedSpecs)
294404
}
295405
}
296406

297-
func destroyUntrackedSnapshots(tt *acctest.TestTools, n string) resource.TestCheckFunc {
407+
func destroyUntrackedInstanceSnapshots(tt *acctest.TestTools, n string) resource.TestCheckFunc {
298408
return func(state *terraform.State) error {
299409
rs, ok := state.RootModule().Resources[n]
300410
if !ok {
@@ -327,3 +437,91 @@ func destroyUntrackedSnapshots(tt *acctest.TestTools, n string) resource.TestChe
327437
return nil
328438
}
329439
}
440+
441+
func blockSnapshotMatchesExpectedSpecs(snapshot blockSDK.Snapshot, expected snapshotSpecsCheck) bool {
442+
if expected.Name != nil && *expected.Name != snapshot.Name {
443+
return false
444+
}
445+
446+
if expected.Size != nil && *expected.Size != snapshot.Size {
447+
return false
448+
}
449+
450+
if len(expected.Tags) > 0 && !reflect.DeepEqual(expected.Tags, snapshot.Tags) {
451+
return false
452+
}
453+
454+
if len(snapshot.Tags) > len(expected.Tags) {
455+
return false
456+
}
457+
458+
return true
459+
}
460+
461+
func checkBlockSnapshot(tt *acctest.TestTools, n string, expectedSpecs snapshotSpecsCheck) resource.TestCheckFunc {
462+
return func(state *terraform.State) error {
463+
rs, ok := state.RootModule().Resources[n]
464+
if !ok {
465+
return fmt.Errorf("resource not found: %s", n)
466+
}
467+
468+
api, zone, id, err := block.NewAPIWithZoneAndID(tt.Meta, rs.Primary.ID)
469+
if err != nil {
470+
return err
471+
}
472+
473+
snapshots, err := api.ListSnapshots(&blockSDK.ListSnapshotsRequest{
474+
Zone: zone,
475+
VolumeID: &id,
476+
}, scw.WithAllPages())
477+
if err != nil {
478+
return err
479+
}
480+
481+
if snapshots.TotalCount == 0 {
482+
return fmt.Errorf("could not find any block snapshot for volume %s", id)
483+
}
484+
485+
for _, snapshot := range snapshots.Snapshots {
486+
if blockSnapshotMatchesExpectedSpecs(*snapshot, expectedSpecs) {
487+
return nil
488+
}
489+
}
490+
491+
return fmt.Errorf("could not find any block snapshot that matches the specs %+v", expectedSpecs)
492+
}
493+
}
494+
495+
func destroyUntrackedBlockSnapshots(tt *acctest.TestTools, n string) resource.TestCheckFunc {
496+
return func(state *terraform.State) error {
497+
rs, ok := state.RootModule().Resources[n]
498+
if !ok {
499+
return fmt.Errorf("resource not found: %s", n)
500+
}
501+
502+
api, zone, id, err := block.NewAPIWithZoneAndID(tt.Meta, rs.Primary.ID)
503+
if err != nil {
504+
return err
505+
}
506+
507+
snapshots, err := api.ListSnapshots(&blockSDK.ListSnapshotsRequest{
508+
Zone: zone,
509+
VolumeID: &id,
510+
}, scw.WithAllPages())
511+
if err != nil {
512+
return err
513+
}
514+
515+
for _, snapshot := range snapshots.Snapshots {
516+
err = api.DeleteSnapshot(&blockSDK.DeleteSnapshotRequest{
517+
Zone: zone,
518+
SnapshotID: snapshot.ID,
519+
})
520+
if err != nil {
521+
return err
522+
}
523+
}
524+
525+
return nil
526+
}
527+
}

0 commit comments

Comments
 (0)