Skip to content

Commit f77d061

Browse files
Server attachment waiters (#900)
* Request waiter implementation * Extend method description * Fix lint * Fix litn * Add volume attachment waiters * Update services/iaasalpha/wait/wait.go Co-authored-by: João Palet <[email protected]> * Formatting * Update services/iaasalpha/wait/wait.go Co-authored-by: João Palet <[email protected]> --------- Co-authored-by: João Palet <[email protected]>
1 parent 06b55c6 commit f77d061

File tree

2 files changed

+379
-5
lines changed

2 files changed

+379
-5
lines changed

services/iaasalpha/wait/wait.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,24 @@ const (
1717
ErrorStatus = "ERROR"
1818
ServerActiveStatus = "ACTIVE"
1919
ServerResizingStatus = "RESIZING"
20+
21+
RequestCreateAction = "CREATE"
22+
RequestUpdateAction = "UPDATE"
23+
RequestDeleteAction = "DELETE"
24+
RequestCreatedStatus = "CREATED"
25+
RequestUpdatedStatus = "UPDATED"
26+
RequestDeletedStatus = "DELETED"
27+
RequestFailedStatus = "FAILED"
28+
29+
XRequestIDHeader = "X-Request-Id"
2030
)
2131

2232
// Interfaces needed for tests
2333
type APIClientInterface interface {
2434
GetVolumeExecute(ctx context.Context, projectId string, volumeId string) (*iaasalpha.Volume, error)
2535
GetServerExecute(ctx context.Context, projectId string, serverId string) (*iaasalpha.Server, error)
36+
GetProjectRequestExecute(ctx context.Context, projectId string, requestId string) (*iaasalpha.Request, error)
37+
GetAttachedVolumeExecute(ctx context.Context, projectId string, serverId string, volumeId string) (*iaasalpha.VolumeAttachment, error)
2638
}
2739

2840
// CreateVolumeWaitHandler will wait for volume creation
@@ -165,3 +177,117 @@ func DeleteServerWaitHandler(ctx context.Context, a APIClientInterface, projectI
165177
handler.SetTimeout(20 * time.Minute)
166178
return handler
167179
}
180+
181+
// ProjectRequestWaitHandler will wait for a request to succeed.
182+
//
183+
// It receives a request ID that can be obtained from the "X-Request-Id" header in the HTTP response of any operation in the IaaS API.
184+
// To get this response header, use the "runtime.WithCaptureHTTPResponse" method from the "core" packaghe to get the raw HTTP response of an SDK operation.
185+
// Then, the value of the request ID can be obtained by accessing the header key which is defined in the constant "XRequestIDHeader" of this package.
186+
//
187+
// Example usage:
188+
//
189+
// var httpResp *http.Response
190+
// ctxWithHTTPResp := runtime.WithCaptureHTTPResponse(context.Background(), &httpResp)
191+
//
192+
// err = iaasalphaClient.AddPublicIpToServer(ctxWithHTTPResp, projectId, serverId, publicIpId).Execute()
193+
//
194+
// requestId := httpResp.Header[wait.XRequestIDHeader][0]
195+
// _, err = wait.ProjectRequestWaitHandler(context.Background(), iaasalphaClient, projectId, requestId).WaitWithContext(context.Background())
196+
func ProjectRequestWaitHandler(ctx context.Context, a APIClientInterface, projectId, requestId string) *wait.AsyncActionHandler[iaasalpha.Request] {
197+
handler := wait.New(func() (waitFinished bool, response *iaasalpha.Request, err error) {
198+
request, err := a.GetProjectRequestExecute(ctx, projectId, requestId)
199+
if err != nil {
200+
return false, request, err
201+
}
202+
203+
if request == nil {
204+
return false, nil, fmt.Errorf("request failed for request with id %s: nil response from GetProjectRequestExecute", requestId)
205+
}
206+
207+
if request.RequestId == nil || request.RequestAction == nil || request.Status == nil {
208+
return false, request, fmt.Errorf("request failed for request with id %s, the response is not valid: the id, the request action or the status are missing", requestId)
209+
}
210+
211+
if *request.RequestId != requestId {
212+
return false, request, fmt.Errorf("request failed for request with id %s: the response id doesn't match the request id", requestId)
213+
}
214+
215+
switch *request.RequestAction {
216+
case RequestCreateAction:
217+
if *request.Status == RequestCreatedStatus {
218+
return true, request, nil
219+
}
220+
case RequestUpdateAction:
221+
if *request.Status == RequestUpdatedStatus {
222+
return true, request, nil
223+
}
224+
case RequestDeleteAction:
225+
if *request.Status == RequestDeletedStatus {
226+
return true, request, nil
227+
}
228+
default:
229+
return false, request, fmt.Errorf("request failed for request with id %s, the request action %s is not supported", requestId, *request.RequestAction)
230+
}
231+
232+
if *request.Status == RequestFailedStatus {
233+
return true, request, fmt.Errorf("request failed for request with id %s", requestId)
234+
}
235+
236+
return false, request, nil
237+
})
238+
handler.SetTimeout(20 * time.Minute)
239+
return handler
240+
}
241+
242+
// AddVolumeToServerWaitHandler will wait for a volume to be attached to a server
243+
func AddVolumeToServerWaitHandler(ctx context.Context, a APIClientInterface, projectId, serverId, volumeId string) *wait.AsyncActionHandler[iaasalpha.VolumeAttachment] {
244+
handler := wait.New(func() (waitFinished bool, response *iaasalpha.VolumeAttachment, err error) {
245+
volumeAttachment, err := a.GetAttachedVolumeExecute(ctx, projectId, serverId, volumeId)
246+
if err == nil {
247+
if volumeAttachment != nil {
248+
if volumeAttachment.VolumeId == nil {
249+
return false, volumeAttachment, fmt.Errorf("attachment failed for server with id %s and volume with id %s, the response is not valid: the volume id is missing", serverId, volumeId)
250+
}
251+
if *volumeAttachment.VolumeId == volumeId {
252+
return true, volumeAttachment, nil
253+
}
254+
}
255+
return false, nil, nil
256+
}
257+
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
258+
if !ok {
259+
return false, volumeAttachment, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError: %w", err)
260+
}
261+
if oapiErr.StatusCode != http.StatusNotFound {
262+
return false, volumeAttachment, err
263+
}
264+
return false, nil, nil
265+
})
266+
handler.SetTimeout(10 * time.Minute)
267+
return handler
268+
}
269+
270+
// RemoveVolumeFromServerWaitHandler will wait for a volume to be attached to a server
271+
func RemoveVolumeFromServerWaitHandler(ctx context.Context, a APIClientInterface, projectId, serverId, volumeId string) *wait.AsyncActionHandler[iaasalpha.VolumeAttachment] {
272+
handler := wait.New(func() (waitFinished bool, response *iaasalpha.VolumeAttachment, err error) {
273+
volumeAttachment, err := a.GetAttachedVolumeExecute(ctx, projectId, serverId, volumeId)
274+
if err == nil {
275+
if volumeAttachment != nil {
276+
if volumeAttachment.VolumeId == nil {
277+
return false, volumeAttachment, fmt.Errorf("remove volume failed for server with id %s and volume with id %s, the response is not valid: the volume id is missing", serverId, volumeId)
278+
}
279+
}
280+
return false, nil, nil
281+
}
282+
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
283+
if !ok {
284+
return false, volumeAttachment, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError: %w", err)
285+
}
286+
if oapiErr.StatusCode != http.StatusNotFound {
287+
return false, volumeAttachment, err
288+
}
289+
return true, nil, nil
290+
})
291+
handler.SetTimeout(10 * time.Minute)
292+
return handler
293+
}

0 commit comments

Comments
 (0)