Skip to content

Commit f4db8e5

Browse files
committed
feat: Add wait handlers for VPN gateway creation, update and deletion
1 parent cc6e738 commit f4db8e5

2 files changed

Lines changed: 344 additions & 0 deletions

File tree

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package wait
2+
3+
import (
4+
"context"
5+
"errors"
6+
"net/http"
7+
"time"
8+
9+
"github.com/stackitcloud/stackit-sdk-go/core/wait"
10+
vpn "github.com/stackitcloud/stackit-sdk-go/services/vpn/v1beta1api"
11+
)
12+
13+
func CreateOrUpdateGatewayWaitHandler(ctx context.Context, a vpn.DefaultAPI, projectId string, region vpn.Region, gatewayId string) *wait.AsyncActionHandler[vpn.GatewayResponse] {
14+
waitConfig := wait.WaiterHelper[vpn.GatewayResponse, vpn.GatewayStatus]{
15+
FetchInstance: a.GetVPNGateway(ctx, projectId, region, gatewayId).Execute,
16+
GetState: func(resp *vpn.GatewayResponse) (vpn.GatewayStatus, error) {
17+
if resp == nil {
18+
return "", errors.New("could not get gateway status: response is nil")
19+
}
20+
if resp.State == nil {
21+
return "", errors.New("could not get gateway status: state is nil")
22+
}
23+
return *resp.State, nil
24+
},
25+
ActiveState: []vpn.GatewayStatus{vpn.GATEWAYSTATUS_READY},
26+
ErrorState: []vpn.GatewayStatus{vpn.GATEWAYSTATUS_ERROR, vpn.GATEWAYSTATUS_DELETING},
27+
}
28+
29+
handler := wait.New(waitConfig.Wait())
30+
handler.SetTimeout(45 * time.Minute)
31+
return handler
32+
}
33+
34+
func DeleteGatewayWaitHandler(ctx context.Context, a vpn.DefaultAPI, projectId string, region vpn.Region, gatewayId string) *wait.AsyncActionHandler[vpn.GatewayResponse] {
35+
waitConfig := wait.WaiterHelper[vpn.GatewayResponse, vpn.GatewayStatus]{
36+
FetchInstance: a.GetVPNGateway(ctx, projectId, region, gatewayId).Execute,
37+
GetState: func(resp *vpn.GatewayResponse) (vpn.GatewayStatus, error) {
38+
if resp == nil {
39+
return "", errors.New("could not get gateway status: response is nil")
40+
}
41+
if resp.State == nil {
42+
return "", errors.New("could not get gateway status: state is nil")
43+
}
44+
return *resp.State, nil
45+
},
46+
ErrorState: []vpn.GatewayStatus{vpn.GATEWAYSTATUS_ERROR},
47+
// used default so technically not needed to be set:
48+
DeleteHttpErrorStatusCodes: []int{http.StatusForbidden, http.StatusNotFound, http.StatusGone},
49+
}
50+
51+
handler := wait.New(waitConfig.Wait())
52+
handler.SetTimeout(20 * time.Minute)
53+
return handler
54+
}
Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
package wait
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"testing"
7+
"testing/synctest"
8+
9+
"github.com/google/go-cmp/cmp"
10+
11+
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
12+
"github.com/stackitcloud/stackit-sdk-go/core/utils"
13+
vpn "github.com/stackitcloud/stackit-sdk-go/services/vpn/v1beta1api"
14+
)
15+
16+
type mockSettings struct {
17+
getFails bool
18+
getNotFound bool
19+
getForbidden bool
20+
getGone bool
21+
gatewayState vpn.GatewayStatus
22+
gatewayId string
23+
}
24+
25+
func newAPIMock(settings []mockSettings) vpn.DefaultAPI {
26+
count := 0
27+
return &vpn.DefaultAPIServiceMock{
28+
GetVPNGatewayExecuteMock: utils.Ptr(func(_ vpn.ApiGetVPNGatewayRequest) (*vpn.GatewayResponse, error) {
29+
setting := settings[count%len(settings)]
30+
count++
31+
32+
if setting.getFails {
33+
return nil, &oapierror.GenericOpenAPIError{
34+
StatusCode: http.StatusInternalServerError,
35+
}
36+
}
37+
38+
if setting.getNotFound {
39+
return nil, &oapierror.GenericOpenAPIError{
40+
StatusCode: http.StatusNotFound,
41+
}
42+
}
43+
44+
if setting.getForbidden {
45+
return nil, &oapierror.GenericOpenAPIError{
46+
StatusCode: http.StatusForbidden,
47+
}
48+
}
49+
50+
if setting.getGone {
51+
return nil, &oapierror.GenericOpenAPIError{
52+
StatusCode: http.StatusGone,
53+
}
54+
}
55+
56+
return &vpn.GatewayResponse{
57+
Id: &setting.gatewayId,
58+
State: &setting.gatewayState,
59+
}, nil
60+
}),
61+
}
62+
}
63+
64+
func TestCreateOrUpdateGatewayWaitHandler(t *testing.T) {
65+
tests := []struct {
66+
desc string
67+
mockSettings []mockSettings
68+
wantGatewayState vpn.GatewayStatus
69+
wantErr bool
70+
wantResp bool
71+
}{
72+
{
73+
desc: "create_succeeded",
74+
mockSettings: []mockSettings{
75+
{gatewayState: vpn.GATEWAYSTATUS_READY, gatewayId: "gw-1"},
76+
},
77+
wantGatewayState: vpn.GATEWAYSTATUS_READY,
78+
wantErr: false,
79+
wantResp: true,
80+
},
81+
{
82+
desc: "pending_multiple_times",
83+
mockSettings: []mockSettings{
84+
{
85+
gatewayState: vpn.GATEWAYSTATUS_PENDING,
86+
gatewayId: "gw-1",
87+
},
88+
{
89+
gatewayState: vpn.GATEWAYSTATUS_PENDING,
90+
gatewayId: "gw-1",
91+
},
92+
{
93+
gatewayState: vpn.GATEWAYSTATUS_READY,
94+
gatewayId: "gw-1",
95+
},
96+
},
97+
wantGatewayState: vpn.GATEWAYSTATUS_READY,
98+
wantErr: false,
99+
wantResp: true,
100+
},
101+
{
102+
desc: "error_state",
103+
mockSettings: []mockSettings{
104+
{
105+
gatewayState: vpn.GATEWAYSTATUS_PENDING,
106+
gatewayId: "gw-1",
107+
},
108+
{
109+
gatewayState: vpn.GATEWAYSTATUS_ERROR,
110+
gatewayId: "gw-1",
111+
},
112+
},
113+
wantErr: true,
114+
wantResp: false,
115+
},
116+
{
117+
desc: "deleting_state",
118+
mockSettings: []mockSettings{
119+
{
120+
gatewayState: vpn.GATEWAYSTATUS_PENDING,
121+
gatewayId: "gw-1",
122+
},
123+
{
124+
gatewayState: vpn.GATEWAYSTATUS_DELETING,
125+
gatewayId: "gw-1",
126+
},
127+
},
128+
wantErr: true,
129+
wantResp: false,
130+
},
131+
{
132+
desc: "get_fails",
133+
mockSettings: []mockSettings{
134+
{
135+
gatewayState: vpn.GATEWAYSTATUS_PENDING,
136+
gatewayId: "gw-1",
137+
},
138+
{
139+
gatewayState: vpn.GATEWAYSTATUS_PENDING,
140+
gatewayId: "gw-1",
141+
},
142+
{
143+
getFails: true,
144+
},
145+
},
146+
wantErr: true,
147+
wantResp: false,
148+
},
149+
{
150+
desc: "unknown_state",
151+
mockSettings: []mockSettings{
152+
{
153+
gatewayState: vpn.GatewayStatus("UNKNOWN_STATE"),
154+
gatewayId: "gw-1",
155+
},
156+
},
157+
wantErr: true,
158+
wantResp: false,
159+
},
160+
}
161+
for _, tt := range tests {
162+
t.Run(tt.desc, func(t *testing.T) {
163+
synctest.Test(t, func(t *testing.T) {
164+
apiClient := newAPIMock(tt.mockSettings)
165+
166+
var wantRes *vpn.GatewayResponse
167+
if tt.wantResp {
168+
wantRes = &vpn.GatewayResponse{
169+
Id: utils.Ptr("gw-1"),
170+
State: utils.Ptr(tt.wantGatewayState),
171+
}
172+
}
173+
174+
handler := CreateOrUpdateGatewayWaitHandler(context.Background(), apiClient, "pid", vpn.REGION_EU01, "gw-1")
175+
176+
gotRes, err := handler.WaitWithContext(context.Background())
177+
178+
if (err != nil) != tt.wantErr {
179+
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
180+
}
181+
if !tt.wantErr && !cmp.Equal(gotRes, wantRes) {
182+
t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes)
183+
}
184+
})
185+
})
186+
}
187+
}
188+
189+
func TestDeleteGatewayWaitHandler(t *testing.T) {
190+
tests := []struct {
191+
desc string
192+
mockSettings []mockSettings
193+
wantErr bool
194+
}{
195+
{
196+
desc: "delete_succeeded",
197+
mockSettings: []mockSettings{
198+
{
199+
gatewayState: vpn.GatewayStatus(""),
200+
getFails: false,
201+
getNotFound: true,
202+
},
203+
},
204+
wantErr: false,
205+
},
206+
{
207+
desc: "delete_succeeded_forbidden",
208+
mockSettings: []mockSettings{
209+
{
210+
gatewayState: vpn.GatewayStatus(""),
211+
getForbidden: true,
212+
},
213+
},
214+
wantErr: false,
215+
},
216+
{
217+
desc: "delete_succeeded_gone",
218+
mockSettings: []mockSettings{
219+
{
220+
gatewayState: vpn.GatewayStatus(""),
221+
getGone: true,
222+
},
223+
},
224+
wantErr: false,
225+
},
226+
{
227+
desc: "delete_pending",
228+
mockSettings: []mockSettings{
229+
{
230+
getFails: false,
231+
getNotFound: false,
232+
gatewayState: vpn.GATEWAYSTATUS_DELETING,
233+
gatewayId: "gw-1",
234+
},
235+
{
236+
getFails: false,
237+
getNotFound: false,
238+
gatewayState: vpn.GATEWAYSTATUS_DELETING,
239+
gatewayId: "gw-1",
240+
},
241+
{
242+
getFails: false,
243+
getNotFound: true,
244+
gatewayState: vpn.GatewayStatus(""),
245+
},
246+
},
247+
wantErr: false,
248+
},
249+
{
250+
desc: "error_state",
251+
mockSettings: []mockSettings{
252+
{
253+
gatewayState: vpn.GATEWAYSTATUS_DELETING,
254+
gatewayId: "gw-1",
255+
},
256+
{
257+
gatewayState: vpn.GATEWAYSTATUS_ERROR,
258+
gatewayId: "gw-1",
259+
},
260+
},
261+
wantErr: true,
262+
},
263+
{
264+
desc: "timeout",
265+
mockSettings: []mockSettings{
266+
{
267+
getFails: false,
268+
gatewayState: vpn.GatewayStatus("UNKNOWN_STATE"),
269+
gatewayId: "gw-1",
270+
},
271+
},
272+
wantErr: true,
273+
},
274+
}
275+
for _, tt := range tests {
276+
t.Run(tt.desc, func(t *testing.T) {
277+
synctest.Test(t, func(t *testing.T) {
278+
apiClient := newAPIMock(tt.mockSettings)
279+
280+
handler := DeleteGatewayWaitHandler(context.Background(), apiClient, "pid", vpn.REGION_EU01, "gw-1")
281+
282+
_, err := handler.WaitWithContext(context.Background())
283+
284+
if (err != nil) != tt.wantErr {
285+
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
286+
}
287+
})
288+
})
289+
}
290+
}

0 commit comments

Comments
 (0)