Skip to content

Commit fbbefb7

Browse files
authored
feat(resourcemanager): refactor wait handler to use helper struct (#6013)
1 parent 020b00d commit fbbefb7

File tree

5 files changed

+151
-90
lines changed

5 files changed

+151
-90
lines changed

CHANGELOG.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,13 @@
129129
- **Dependencies:** Bump STACKIT SDK core module from `v0.22.0` to `v0.23.0`
130130
- `redis`: [v0.27.1](services/redis/CHANGELOG.md#v0271)
131131
- **Dependencies:** Bump STACKIT SDK core module from `v0.22.0` to `v0.23.0`
132-
- `resourcemanager`: [v0.20.1](services/resourcemanager/CHANGELOG.md#v0201)
133-
- **Dependencies:** Bump STACKIT SDK core module from `v0.22.0` to `v0.23.0`
132+
- `resourcemanager`:
133+
- [v0.20.1](services/resourcemanager/CHANGELOG.md#v0201)
134+
- **Dependencies:** Bump STACKIT SDK core module from `v0.22.0` to `v0.23.0`
135+
- [v0.21.0](services/resourcemanager/CHANGELOG.md#v0210)
136+
- `v0api`
137+
- **Improvement**: Use new `WaiterHelper` struct in the DNS WaitHandler
138+
- **Breaking change:** Change return type of `wait.DeleteProjectWaitHandler()` to `*wait.AsyncActionHandler[resourcemanager.GetProjectResponse]`
134139
- `runcommand`: [v1.6.1](services/runcommand/CHANGELOG.md#v161)
135140
- **Dependencies:** Bump STACKIT SDK core module from `v0.22.0` to `v0.23.0`
136141
- `scf`: [v0.6.1](services/scf/CHANGELOG.md#v061)

services/resourcemanager/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## v0.21.0
2+
- `v0api`
3+
- **Improvement**: Use new `WaiterHelper` struct in the DNS WaitHandler
4+
- **Breaking change:** Change return type of `wait.DeleteProjectWaitHandler()` to `*wait.AsyncActionHandler[resourcemanager.GetProjectResponse]`
5+
16
## v0.20.1
27
- **Dependencies:** Bump STACKIT SDK core module from `v0.22.0` to `v0.23.0`
38

services/resourcemanager/VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v0.20.1
1+
v0.21.0

services/resourcemanager/v0api/wait/wait.go

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,51 +2,49 @@ package wait
22

33
import (
44
"context"
5-
"fmt"
5+
"errors"
66
"net/http"
77
"time"
88

9-
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
109
"github.com/stackitcloud/stackit-sdk-go/core/wait"
1110
resourcemanager "github.com/stackitcloud/stackit-sdk-go/services/resourcemanager/v0api"
1211
)
1312

1413
// CreateProjectWaitHandler will wait for project creation
1514
func CreateProjectWaitHandler(ctx context.Context, a resourcemanager.DefaultAPI, containerId string) *wait.AsyncActionHandler[resourcemanager.GetProjectResponse] {
16-
handler := wait.New(func() (waitFinished bool, response *resourcemanager.GetProjectResponse, err error) {
17-
p, err := a.GetProject(ctx, containerId).Execute()
18-
if err != nil {
19-
return false, nil, err
20-
}
21-
if p.ContainerId == containerId && p.LifecycleState == resourcemanager.LIFECYCLESTATE_ACTIVE {
22-
return true, p, nil
23-
}
24-
if p.ContainerId == containerId && p.LifecycleState == resourcemanager.LIFECYCLESTATE_CREATING {
25-
return false, nil, nil
26-
}
27-
return true, p, fmt.Errorf("creation failed: received project state '%s'", p.LifecycleState)
28-
})
15+
waitConfig := wait.WaiterHelper[resourcemanager.GetProjectResponse, resourcemanager.LifecycleState]{
16+
FetchInstance: a.GetProject(ctx, containerId).Execute,
17+
GetState: func(project *resourcemanager.GetProjectResponse) (resourcemanager.LifecycleState, error) {
18+
if project == nil {
19+
return "", errors.New("empty response")
20+
}
21+
return project.LifecycleState, nil
22+
},
23+
ActiveState: []resourcemanager.LifecycleState{resourcemanager.LIFECYCLESTATE_ACTIVE},
24+
ErrorState: []resourcemanager.LifecycleState{resourcemanager.LIFECYCLESTATE_INACTIVE},
25+
}
26+
27+
handler := wait.New(waitConfig.Wait())
2928
handler.SetSleepBeforeWait(1 * time.Minute)
3029
handler.SetTimeout(45 * time.Minute)
3130
return handler
3231
}
3332

3433
// DeleteProjectWaitHandler will wait for project deletion
35-
func DeleteProjectWaitHandler(ctx context.Context, a resourcemanager.DefaultAPI, containerId string) *wait.AsyncActionHandler[struct{}] {
36-
handler := wait.New(func() (waitFinished bool, response *struct{}, err error) {
37-
_, err = a.GetProject(ctx, containerId).Execute()
38-
if err == nil {
39-
return false, nil, nil
40-
}
41-
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
42-
if !ok {
43-
return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError")
44-
}
45-
if oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusForbidden {
46-
return true, nil, nil
47-
}
48-
return false, nil, err
49-
})
34+
func DeleteProjectWaitHandler(ctx context.Context, a resourcemanager.DefaultAPI, containerId string) *wait.AsyncActionHandler[resourcemanager.GetProjectResponse] {
35+
waitConfig := wait.WaiterHelper[resourcemanager.GetProjectResponse, resourcemanager.LifecycleState]{
36+
FetchInstance: a.GetProject(ctx, containerId).Execute,
37+
GetState: func(project *resourcemanager.GetProjectResponse) (resourcemanager.LifecycleState, error) {
38+
if project == nil {
39+
return "", errors.New("empty response")
40+
}
41+
return project.LifecycleState, nil
42+
},
43+
ErrorState: []resourcemanager.LifecycleState{resourcemanager.LIFECYCLESTATE_INACTIVE},
44+
DeleteHttpErrorStatusCodes: []int{http.StatusNotFound, http.StatusForbidden},
45+
}
46+
47+
handler := wait.New(waitConfig.Wait())
5048
handler.SetTimeout(15 * time.Minute)
5149
return handler
5250
}

services/resourcemanager/v0api/wait/wait_test.go

Lines changed: 110 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,27 @@ type mockSettings struct {
1818
projectState resourcemanager.LifecycleState
1919
}
2020

21-
func newAPIMock(settings mockSettings) resourcemanager.DefaultAPI {
21+
func newAPIMock(settings []mockSettings) resourcemanager.DefaultAPI {
22+
count := 0
2223
return &resourcemanager.DefaultAPIServiceMock{
2324
GetProjectExecuteMock: utils.Ptr(func(_ resourcemanager.ApiGetProjectRequest) (*resourcemanager.GetProjectResponse, error) {
24-
if settings.getFails {
25+
setting := settings[count%len(settings)]
26+
count++
27+
28+
if setting.getFails {
2529
return nil, &oapierror.GenericOpenAPIError{
2630
StatusCode: http.StatusInternalServerError,
2731
}
2832
}
2933

30-
if settings.getNotFound {
34+
if setting.getNotFound {
3135
return nil, &oapierror.GenericOpenAPIError{
3236
StatusCode: http.StatusNotFound,
3337
}
3438
}
3539

3640
return &resourcemanager.GetProjectResponse{
37-
LifecycleState: settings.projectState,
41+
LifecycleState: setting.projectState,
3842
ContainerId: "cid",
3943
}, nil
4044
}),
@@ -43,59 +47,81 @@ func newAPIMock(settings mockSettings) resourcemanager.DefaultAPI {
4347

4448
func TestCreateProjectWaitHandler(t *testing.T) {
4549
tests := []struct {
46-
desc string
47-
getFails bool
48-
projectState resourcemanager.LifecycleState
49-
wantErr bool
50-
wantResp bool
50+
desc string
51+
mockSettings []mockSettings
52+
wantProjectState resourcemanager.LifecycleState
53+
wantErr bool
54+
wantResp bool
5155
}{
5256
{
53-
desc: "create_succeeded",
54-
getFails: false,
55-
projectState: resourcemanager.LIFECYCLESTATE_ACTIVE,
56-
wantErr: false,
57-
wantResp: true,
57+
desc: "create_succeeded",
58+
mockSettings: []mockSettings{
59+
{projectState: resourcemanager.LIFECYCLESTATE_ACTIVE},
60+
},
61+
wantProjectState: resourcemanager.LIFECYCLESTATE_ACTIVE,
62+
wantErr: false,
63+
wantResp: true,
5864
},
5965
{
60-
desc: "creating",
61-
getFails: false,
62-
projectState: resourcemanager.LIFECYCLESTATE_CREATING,
63-
wantErr: true,
64-
wantResp: false,
66+
desc: "creating",
67+
mockSettings: []mockSettings{
68+
{
69+
projectState: resourcemanager.LIFECYCLESTATE_CREATING,
70+
},
71+
{
72+
projectState: resourcemanager.LIFECYCLESTATE_CREATING,
73+
},
74+
{
75+
projectState: resourcemanager.LIFECYCLESTATE_ACTIVE,
76+
},
77+
},
78+
wantProjectState: resourcemanager.LIFECYCLESTATE_ACTIVE,
79+
wantErr: false,
80+
wantResp: true,
6581
},
6682
{
67-
desc: "get_fails",
68-
getFails: true,
69-
projectState: resourcemanager.LifecycleState(""),
70-
wantErr: true,
71-
wantResp: false,
83+
desc: "get_fails",
84+
mockSettings: []mockSettings{
85+
{
86+
projectState: resourcemanager.LIFECYCLESTATE_CREATING,
87+
},
88+
{
89+
projectState: resourcemanager.LIFECYCLESTATE_CREATING,
90+
},
91+
{
92+
getFails: true,
93+
},
94+
},
95+
wantErr: true,
96+
wantResp: false,
7297
},
7398
{
74-
desc: "unknown_state",
75-
getFails: false,
76-
projectState: resourcemanager.LifecycleState("ANOTHER STATE"),
77-
wantErr: true,
78-
wantResp: true,
99+
desc: "unknown_state",
100+
mockSettings: []mockSettings{
101+
{
102+
getFails: false,
103+
projectState: resourcemanager.LifecycleState("ANOTHER STATE"),
104+
},
105+
},
106+
wantErr: true,
107+
wantResp: false,
79108
},
80109
}
81110
for _, tt := range tests {
82111
t.Run(tt.desc, func(t *testing.T) {
83-
apiClient := newAPIMock(mockSettings{
84-
getFails: tt.getFails,
85-
projectState: tt.projectState,
86-
})
112+
apiClient := newAPIMock(tt.mockSettings)
87113

88114
var wantRes *resourcemanager.GetProjectResponse
89115
if tt.wantResp {
90116
wantRes = &resourcemanager.GetProjectResponse{
91-
LifecycleState: tt.projectState,
117+
LifecycleState: tt.wantProjectState,
92118
ContainerId: "cid",
93119
}
94120
}
95121

96122
handler := CreateProjectWaitHandler(context.Background(), apiClient, "cid")
97123

98-
gotRes, err := handler.SetTimeout(10 * time.Millisecond).SetSleepBeforeWait(10 * time.Millisecond).WaitWithContext(context.Background())
124+
gotRes, err := handler.SetTimeout(10 * time.Millisecond).SetSleepBeforeWait(0).SetThrottle(1).WaitWithContext(context.Background())
99125

100126
if (err != nil) != tt.wantErr {
101127
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
@@ -110,42 +136,69 @@ func TestCreateProjectWaitHandler(t *testing.T) {
110136
func TestDeleteProjectWaitHandler(t *testing.T) {
111137
tests := []struct {
112138
desc string
113-
getFails bool
114-
getNotFound bool
115-
projectState resourcemanager.LifecycleState
139+
mockSettings []mockSettings
116140
wantErr bool
117141
}{
118142
{
119-
desc: "delete_succeeded",
120-
getFails: false,
121-
getNotFound: true,
122-
projectState: resourcemanager.LifecycleState(""),
123-
wantErr: false,
143+
desc: "delete_succeeded",
144+
mockSettings: []mockSettings{
145+
{
146+
projectState: resourcemanager.LifecycleState(""),
147+
getFails: false,
148+
getNotFound: true,
149+
},
150+
},
151+
wantErr: false,
152+
},
153+
{
154+
desc: "delete_pending",
155+
mockSettings: []mockSettings{
156+
{
157+
getFails: false,
158+
getNotFound: false,
159+
projectState: resourcemanager.LIFECYCLESTATE_DELETING,
160+
},
161+
{
162+
getFails: false,
163+
getNotFound: false,
164+
projectState: resourcemanager.LIFECYCLESTATE_DELETING,
165+
},
166+
{
167+
getFails: false,
168+
getNotFound: true,
169+
projectState: resourcemanager.LifecycleState(""),
170+
},
171+
},
172+
wantErr: false,
124173
},
125174
{
126-
desc: "get_fails",
127-
getFails: true,
128-
projectState: "",
129-
wantErr: true,
175+
desc: "get_fails",
176+
mockSettings: []mockSettings{
177+
{
178+
projectState: resourcemanager.LifecycleState(""),
179+
getFails: true,
180+
},
181+
},
182+
wantErr: true,
130183
},
131184
{
132-
desc: "timeout",
133-
getFails: false,
134-
projectState: "ANOTHER STATE",
135-
wantErr: true,
185+
desc: "timeout",
186+
mockSettings: []mockSettings{
187+
{
188+
getFails: false,
189+
projectState: "ANOTHER STATE",
190+
},
191+
},
192+
wantErr: true,
136193
},
137194
}
138195
for _, tt := range tests {
139196
t.Run(tt.desc, func(t *testing.T) {
140-
apiClient := newAPIMock(mockSettings{
141-
getFails: tt.getFails,
142-
getNotFound: tt.getNotFound,
143-
projectState: tt.projectState,
144-
})
197+
apiClient := newAPIMock(tt.mockSettings)
145198

146199
handler := DeleteProjectWaitHandler(context.Background(), apiClient, "cid")
147200

148-
_, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background())
201+
_, err := handler.SetTimeout(10 * time.Millisecond).SetThrottle(1).WaitWithContext(context.Background())
149202

150203
if (err != nil) != tt.wantErr {
151204
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)

0 commit comments

Comments
 (0)