Skip to content

Commit 7aefa3e

Browse files
authored
feat(block): add utils to v1 (scaleway#2649)
1 parent 8d1f4f5 commit 7aefa3e

File tree

2 files changed

+216
-0
lines changed

2 files changed

+216
-0
lines changed

api/block/v1/snapshot_utils.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package block
2+
3+
import (
4+
"time"
5+
6+
"github.com/scaleway/scaleway-sdk-go/errors"
7+
"github.com/scaleway/scaleway-sdk-go/internal/async"
8+
"github.com/scaleway/scaleway-sdk-go/scw"
9+
)
10+
11+
// WaitForSnapshotRequest is used by WaitForSnapshot method.
12+
type WaitForSnapshotRequest struct {
13+
SnapshotID string
14+
Zone scw.Zone
15+
Timeout *time.Duration
16+
RetryInterval *time.Duration
17+
18+
// If set, will wait until this specific status has been reached or the
19+
// snapshot has an error status. This is useful when we need to wait for
20+
// the snapshot to transition from "in_use" to "available".
21+
TerminalStatus *SnapshotStatus
22+
}
23+
24+
// WaitForSnapshot wait for the snapshot to be in a "terminal state" before returning.
25+
func (s *API) WaitForSnapshot(req *WaitForSnapshotRequest, opts ...scw.RequestOption) (*Snapshot, error) {
26+
timeout := defaultTimeout
27+
if req.Timeout != nil {
28+
timeout = *req.Timeout
29+
}
30+
retryInterval := defaultRetryInterval
31+
if req.RetryInterval != nil {
32+
retryInterval = *req.RetryInterval
33+
}
34+
35+
terminalStatus := map[SnapshotStatus]struct{}{
36+
SnapshotStatusError: {},
37+
SnapshotStatusLocked: {},
38+
SnapshotStatusDeleted: {},
39+
}
40+
41+
if req.TerminalStatus != nil {
42+
terminalStatus[*req.TerminalStatus] = struct{}{}
43+
} else {
44+
terminalStatus[SnapshotStatusAvailable] = struct{}{}
45+
terminalStatus[SnapshotStatusInUse] = struct{}{}
46+
}
47+
48+
snapshot, err := async.WaitSync(&async.WaitSyncConfig{
49+
Get: func() (any, bool, error) {
50+
res, err := s.GetSnapshot(&GetSnapshotRequest{
51+
SnapshotID: req.SnapshotID,
52+
Zone: req.Zone,
53+
}, opts...)
54+
if err != nil {
55+
return nil, false, err
56+
}
57+
_, isTerminal := terminalStatus[res.Status]
58+
59+
return res, isTerminal, err
60+
},
61+
Timeout: timeout,
62+
IntervalStrategy: async.LinearIntervalStrategy(retryInterval),
63+
})
64+
if err != nil {
65+
return nil, errors.Wrap(err, "waiting for snapshot failed")
66+
}
67+
return snapshot.(*Snapshot), nil
68+
}

api/block/v1/volume_utils.go

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package block
2+
3+
import (
4+
"time"
5+
6+
"github.com/scaleway/scaleway-sdk-go/errors"
7+
"github.com/scaleway/scaleway-sdk-go/internal/async"
8+
"github.com/scaleway/scaleway-sdk-go/scw"
9+
)
10+
11+
const (
12+
defaultTimeout = 5 * time.Minute
13+
defaultRetryInterval = 5 * time.Second
14+
)
15+
16+
// WaitForVolumeRequest is used by WaitForVolume method.
17+
type WaitForVolumeRequest struct {
18+
VolumeID string
19+
Zone scw.Zone
20+
Timeout *time.Duration
21+
RetryInterval *time.Duration
22+
23+
// If set, will wait until this specific status has been reached or the
24+
// volume has an error status. This is useful when we need to wait for
25+
// the volume to transition from "in_use" to "available".
26+
TerminalStatus *VolumeStatus
27+
}
28+
29+
// WaitForVolume waits for the volume to be in a "terminal state" before returning.
30+
func (s *API) WaitForVolume(req *WaitForVolumeRequest, opts ...scw.RequestOption) (*Volume, error) {
31+
timeout := defaultTimeout
32+
if req.Timeout != nil {
33+
timeout = *req.Timeout
34+
}
35+
retryInterval := defaultRetryInterval
36+
if req.RetryInterval != nil {
37+
retryInterval = *req.RetryInterval
38+
}
39+
40+
terminalStatus := map[VolumeStatus]struct{}{
41+
VolumeStatusError: {},
42+
VolumeStatusLocked: {},
43+
VolumeStatusDeleted: {},
44+
}
45+
46+
if req.TerminalStatus != nil {
47+
terminalStatus[*req.TerminalStatus] = struct{}{}
48+
} else {
49+
terminalStatus[VolumeStatusAvailable] = struct{}{}
50+
terminalStatus[VolumeStatusInUse] = struct{}{}
51+
}
52+
53+
volume, err := async.WaitSync(&async.WaitSyncConfig{
54+
Get: func() (any, bool, error) {
55+
res, err := s.GetVolume(&GetVolumeRequest{
56+
VolumeID: req.VolumeID,
57+
Zone: req.Zone,
58+
}, opts...)
59+
if err != nil {
60+
return nil, false, err
61+
}
62+
_, isTerminal := terminalStatus[res.Status]
63+
64+
return res, isTerminal, err
65+
},
66+
Timeout: timeout,
67+
IntervalStrategy: async.LinearIntervalStrategy(retryInterval),
68+
})
69+
if err != nil {
70+
return nil, errors.Wrap(err, "waiting for volume failed")
71+
}
72+
return volume.(*Volume), nil
73+
}
74+
75+
// WaitForVolumeAndReferencesRequest is used by WaitForVolumeAndReferences method.
76+
type WaitForVolumeAndReferencesRequest struct {
77+
VolumeID string
78+
Zone scw.Zone
79+
Timeout *time.Duration
80+
RetryInterval *time.Duration
81+
82+
VolumeTerminalStatus *VolumeStatus
83+
ReferenceTerminalStatus *ReferenceStatus
84+
}
85+
86+
// WaitForVolumeAndReferences waits for the volume and its references to be in a "terminal state" before returning.
87+
func (s *API) WaitForVolumeAndReferences(req *WaitForVolumeAndReferencesRequest, opts ...scw.RequestOption) (*Volume, error) {
88+
timeout := defaultTimeout
89+
if req.Timeout != nil {
90+
timeout = *req.Timeout
91+
}
92+
retryInterval := defaultRetryInterval
93+
if req.RetryInterval != nil {
94+
retryInterval = *req.RetryInterval
95+
}
96+
97+
terminalStatus := map[VolumeStatus]struct{}{
98+
VolumeStatusError: {},
99+
VolumeStatusLocked: {},
100+
VolumeStatusDeleted: {},
101+
}
102+
if req.VolumeTerminalStatus != nil {
103+
terminalStatus[*req.VolumeTerminalStatus] = struct{}{}
104+
} else {
105+
terminalStatus[VolumeStatusAvailable] = struct{}{}
106+
terminalStatus[VolumeStatusInUse] = struct{}{}
107+
}
108+
109+
referenceTerminalStatus := map[ReferenceStatus]struct{}{
110+
ReferenceStatusError: {},
111+
}
112+
if req.ReferenceTerminalStatus != nil {
113+
referenceTerminalStatus[*req.ReferenceTerminalStatus] = struct{}{}
114+
} else {
115+
referenceTerminalStatus[ReferenceStatusAttached] = struct{}{}
116+
referenceTerminalStatus[ReferenceStatusDetached] = struct{}{}
117+
}
118+
119+
volume, err := async.WaitSync(&async.WaitSyncConfig{
120+
Get: func() (any, bool, error) {
121+
volume, err := s.GetVolume(&GetVolumeRequest{
122+
VolumeID: req.VolumeID,
123+
Zone: req.Zone,
124+
}, opts...)
125+
if err != nil {
126+
return nil, false, err
127+
}
128+
129+
referencesAreTerminal := true
130+
131+
for _, reference := range volume.References {
132+
_, referenceIsTerminal := referenceTerminalStatus[reference.Status]
133+
referencesAreTerminal = referencesAreTerminal && referenceIsTerminal
134+
}
135+
136+
_, isTerminal := terminalStatus[volume.Status]
137+
138+
return volume, isTerminal && referencesAreTerminal, nil
139+
},
140+
Timeout: timeout,
141+
IntervalStrategy: async.LinearIntervalStrategy(retryInterval),
142+
})
143+
if err != nil {
144+
return nil, errors.Wrap(err, "waiting for Volume failed")
145+
}
146+
147+
return volume.(*Volume), nil
148+
}

0 commit comments

Comments
 (0)