Skip to content

Commit 34b4658

Browse files
authored
feat(iaas): add volume backup and volume snapshot wait handler (#2669)
* add volume backup wait handler * add volume snapshot wait handler
1 parent 2bc13c2 commit 34b4658

File tree

4 files changed

+495
-0
lines changed

4 files changed

+495
-0
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
## Release (2025-YY-XX)
2+
- `iaas`: [v0.24.0](services/iaas/CHANGELOG.md#v0240-2025-06-05)
3+
- **Feature:** Add waiters for async operations: `CreateBackupWaitHandler`, `DeleteBackupWaitHandler`, `RestoreBackupWaitHandler`
4+
- **Feature:** Add Waiters for async operations: `CreateSnapshotWaitHandler`, `DeleteSnapshotWaitHandler`
25
- `core`: [v0.17.2](core/CHANGELOG.md#v0172-2025-05-22)
36
- **Bugfix:** Access tokens generated via key flow authentication are refreshed 5 seconds before expiration to prevent timing issues with upstream systems which could lead to unexpected 401 error responses
47
- `alb`: [v0.4.1](services/alb/CHANGELOG.md#v041-2025-06-04)

services/iaas/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## v0.24.0 (2025-06-05)
2+
- **Feature:** Add waiters for async operations: `CreateBackupWaitHandler`, `DeleteBackupWaitHandler`, `RestoreBackupWaitHandler`
3+
- **Feature:** Add Waiters for async operations: `CreateSnapshotWaitHandler`, `DeleteSnapshotWaitHandler`
4+
15
## v0.23.0 (2025-05-15)
26
- **Breaking change:** Introduce interfaces for `APIClient` and the request structs
37

services/iaas/wait/wait.go

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"net/http"
77
"time"
88

9+
"errors"
10+
911
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
1012
"github.com/stackitcloud/stackit-sdk-go/core/wait"
1113
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
@@ -35,6 +37,12 @@ const (
3537
RequestFailedStatus = "FAILED"
3638

3739
XRequestIDHeader = "X-Request-Id"
40+
41+
BackupAvailableStatus = "AVAILABLE"
42+
BackupRestoringStatus = "RESTORING"
43+
BackupDeletingStatus = "DELETING"
44+
45+
SnapshotAvailableStatus = "AVAILABLE"
3846
)
3947

4048
// Interfaces needed for tests
@@ -46,6 +54,8 @@ type APIClientInterface interface {
4654
GetServerExecute(ctx context.Context, projectId string, serverId string) (*iaas.Server, error)
4755
GetAttachedVolumeExecute(ctx context.Context, projectId string, serverId string, volumeId string) (*iaas.VolumeAttachment, error)
4856
GetImageExecute(ctx context.Context, projectId string, imageId string) (*iaas.Image, error)
57+
GetBackupExecute(ctx context.Context, projectId string, backupId string) (*iaas.Backup, error)
58+
GetSnapshotExecute(ctx context.Context, projectId string, snapshotId string) (*iaas.Snapshot, error)
4959
}
5060

5161
// CreateNetworkAreaWaitHandler will wait for network area creation
@@ -599,3 +609,129 @@ func DeleteImageWaitHandler(ctx context.Context, a APIClientInterface, projectId
599609
handler.SetTimeout(15 * time.Minute)
600610
return handler
601611
}
612+
613+
// CreateBackupWaitHandler will wait for backup creation
614+
func CreateBackupWaitHandler(ctx context.Context, a APIClientInterface, projectId, backupId string) *wait.AsyncActionHandler[iaas.Backup] {
615+
handler := wait.New(func() (waitFinished bool, response *iaas.Backup, err error) {
616+
backup, err := a.GetBackupExecute(ctx, projectId, backupId)
617+
if err == nil {
618+
if backup != nil {
619+
if backup.Id == nil || backup.Status == nil {
620+
return false, backup, fmt.Errorf("create failed for backup with id %s, the response is not valid: the id or the status are missing", backupId)
621+
}
622+
if *backup.Id == backupId && *backup.Status == BackupAvailableStatus {
623+
return true, backup, nil
624+
}
625+
if *backup.Id == backupId && *backup.Status == ErrorStatus {
626+
return true, backup, fmt.Errorf("create failed for backup with id %s", backupId)
627+
}
628+
}
629+
return false, nil, nil
630+
}
631+
return false, nil, err
632+
})
633+
handler.SetTimeout(45 * time.Minute)
634+
return handler
635+
}
636+
637+
// DeleteBackupWaitHandler will wait for backup deletion
638+
func DeleteBackupWaitHandler(ctx context.Context, a APIClientInterface, projectId, backupId string) *wait.AsyncActionHandler[iaas.Backup] {
639+
handler := wait.New(func() (waitFinished bool, response *iaas.Backup, err error) {
640+
backup, err := a.GetBackupExecute(ctx, projectId, backupId)
641+
if err == nil {
642+
if backup != nil {
643+
if backup.Id == nil || backup.Status == nil {
644+
return false, backup, fmt.Errorf("delete failed for backup with id %s, the response is not valid: the id or the status are missing", backupId)
645+
}
646+
if *backup.Id == backupId && *backup.Status == DeleteSuccess {
647+
return true, backup, nil
648+
}
649+
}
650+
return false, nil, nil
651+
}
652+
var oapiError *oapierror.GenericOpenAPIError
653+
if errors.As(err, &oapiError) {
654+
if statusCode := oapiError.StatusCode; statusCode == http.StatusNotFound || statusCode == http.StatusGone {
655+
return true, nil, nil
656+
}
657+
}
658+
return false, nil, err
659+
})
660+
handler.SetTimeout(20 * time.Minute)
661+
return handler
662+
}
663+
664+
// RestoreBackupWaitHandler will wait for backup restoration
665+
func RestoreBackupWaitHandler(ctx context.Context, a APIClientInterface, projectId, backupId string) *wait.AsyncActionHandler[iaas.Backup] {
666+
handler := wait.New(func() (waitFinished bool, response *iaas.Backup, err error) {
667+
backup, err := a.GetBackupExecute(ctx, projectId, backupId)
668+
if err == nil {
669+
if backup != nil {
670+
if backup.Id == nil || backup.Status == nil {
671+
return false, backup, fmt.Errorf("restore failed for backup with id %s, the response is not valid: the id or the status are missing", backupId)
672+
}
673+
if *backup.Id == backupId && *backup.Status == BackupAvailableStatus {
674+
return true, backup, nil
675+
}
676+
if *backup.Id == backupId && *backup.Status == ErrorStatus {
677+
return true, backup, fmt.Errorf("restore failed for backup with id %s", backupId)
678+
}
679+
}
680+
return false, nil, nil
681+
}
682+
return false, nil, err
683+
})
684+
handler.SetTimeout(45 * time.Minute)
685+
return handler
686+
}
687+
688+
// CreateSnapshotWaitHandler will wait for snapshot creation
689+
func CreateSnapshotWaitHandler(ctx context.Context, a APIClientInterface, projectId, snapshotId string) *wait.AsyncActionHandler[iaas.Snapshot] {
690+
handler := wait.New(func() (waitFinished bool, response *iaas.Snapshot, err error) {
691+
snapshot, err := a.GetSnapshotExecute(ctx, projectId, snapshotId)
692+
if err == nil {
693+
if snapshot != nil {
694+
if snapshot.Id == nil || snapshot.Status == nil {
695+
return false, snapshot, fmt.Errorf("create failed for snapshot with id %s, the response is not valid: the id or the status are missing", snapshotId)
696+
}
697+
if *snapshot.Id == snapshotId && *snapshot.Status == SnapshotAvailableStatus {
698+
return true, snapshot, nil
699+
}
700+
if *snapshot.Id == snapshotId && *snapshot.Status == ErrorStatus {
701+
return true, snapshot, fmt.Errorf("create failed for snapshot with id %s", snapshotId)
702+
}
703+
}
704+
return false, nil, nil
705+
}
706+
return false, nil, err
707+
})
708+
handler.SetTimeout(45 * time.Minute)
709+
return handler
710+
}
711+
712+
// DeleteSnapshotWaitHandler will wait for snapshot deletion
713+
func DeleteSnapshotWaitHandler(ctx context.Context, a APIClientInterface, projectId, snapshotId string) *wait.AsyncActionHandler[iaas.Snapshot] {
714+
handler := wait.New(func() (waitFinished bool, response *iaas.Snapshot, err error) {
715+
snapshot, err := a.GetSnapshotExecute(ctx, projectId, snapshotId)
716+
if err == nil {
717+
if snapshot != nil {
718+
if snapshot.Id == nil || snapshot.Status == nil {
719+
return false, snapshot, fmt.Errorf("delete failed for snapshot with id %s, the response is not valid: the id or the status are missing", snapshotId)
720+
}
721+
if *snapshot.Id == snapshotId && *snapshot.Status == DeleteSuccess {
722+
return true, snapshot, nil
723+
}
724+
}
725+
return false, nil, nil
726+
}
727+
var oapiError *oapierror.GenericOpenAPIError
728+
if errors.As(err, &oapiError) {
729+
if statusCode := oapiError.StatusCode; statusCode == http.StatusNotFound || statusCode == http.StatusGone {
730+
return true, nil, nil
731+
}
732+
}
733+
return false, nil, err
734+
})
735+
handler.SetTimeout(20 * time.Minute)
736+
return handler
737+
}

0 commit comments

Comments
 (0)