Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion services/iaasalpha/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ module github.com/stackitcloud/stackit-sdk-go/services/iaasalpha

go 1.18

require github.com/stackitcloud/stackit-sdk-go/core v0.13.0
require (
github.com/google/go-cmp v0.6.0
github.com/stackitcloud/stackit-sdk-go/core v0.13.0
)

require (
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
Expand Down
1 change: 1 addition & 0 deletions services/iaasalpha/go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/stackitcloud/stackit-sdk-go/core v0.13.0 h1:BtJT2WXqZdexPPQi/HPUIr8g4JUPOCheh6J9dxiCQ4Q=
Expand Down
69 changes: 69 additions & 0 deletions services/iaasalpha/wait/wait.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package wait

import (
"context"
"fmt"
"net/http"
"time"

"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/core/wait"
"github.com/stackitcloud/stackit-sdk-go/services/iaasalpha"
)

const (
AvailableStatus = "AVAILABLE"
DeleteSuccess = "DELETED"
)

// Interfaces needed for tests
type APIClientInterface interface {
GetVolumeExecute(ctx context.Context, projectId string, volumeId string) (*iaasalpha.Volume, error)
}

// CreateVolumeWaitHandler will wait for volume creation
func CreateVolumeWaitHandler(ctx context.Context, a APIClientInterface, projectId, volumeId string) *wait.AsyncActionHandler[iaasalpha.Volume] {
handler := wait.New(func() (waitFinished bool, response *iaasalpha.Volume, err error) {
volume, err := a.GetVolumeExecute(ctx, projectId, volumeId)
if err != nil {
return false, volume, err
}
if volume.Id == nil || volume.Status == nil {
return false, volume, fmt.Errorf("create failed for volume with id %s, the response is not valid: the id or the status are missing", volumeId)
}
if *volume.Id == volumeId && *volume.Status == AvailableStatus {
return true, volume, nil
}
return false, volume, nil
})
handler.SetTimeout(10 * time.Minute)
return handler
}

// DeleteVolumeWaitHandler will wait for volume deletion
func DeleteVolumeWaitHandler(ctx context.Context, a APIClientInterface, projectId, volumeId string) *wait.AsyncActionHandler[iaasalpha.Volume] {
handler := wait.New(func() (waitFinished bool, response *iaasalpha.Volume, err error) {
volume, err := a.GetVolumeExecute(ctx, projectId, volumeId)
if err == nil {
return false, nil, nil
}
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
if !ok {
return false, volume, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError: %w", err)
}
if oapiErr.StatusCode != http.StatusNotFound {
if volume != nil {
if volume.Id == nil || volume.Status == nil {
return false, nil, fmt.Errorf("delete failed for volume with id %s, the response is not valid: the id or the status are missing", volumeId)
}
if *volume.Id == volumeId && *volume.Status == DeleteSuccess {
return true, nil, nil
}
}
return false, volume, err
}
return true, nil, nil
})
handler.SetTimeout(10 * time.Minute)
return handler
}
157 changes: 157 additions & 0 deletions services/iaasalpha/wait/wait_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package wait

import (
"context"
"testing"
"time"

"github.com/google/go-cmp/cmp"
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/core/utils"
"github.com/stackitcloud/stackit-sdk-go/services/iaasalpha"
)

type apiClientMocked struct {
getVolumeFails bool
isDeleted bool
resourceState string
}

func (a *apiClientMocked) GetVolumeExecute(_ context.Context, _, _ string) (*iaasalpha.Volume, error) {
if a.isDeleted {
return nil, &oapierror.GenericOpenAPIError{
StatusCode: 404,
}
}

if a.getVolumeFails {
return nil, &oapierror.GenericOpenAPIError{
StatusCode: 500,
}
}

return &iaasalpha.Volume{
Id: utils.Ptr("vid"),
Status: &a.resourceState,
}, nil
}

func TestCreateVolumeWaitHandler(t *testing.T) {
tests := []struct {
desc string
getFails bool
resourceState string
wantErr bool
wantResp bool
}{
{
desc: "create_succeeded",
getFails: false,
resourceState: AvailableStatus,
wantErr: false,
wantResp: true,
},
{
desc: "get_fails",
getFails: true,
resourceState: "",
wantErr: true,
wantResp: false,
},
{
desc: "timeout",
getFails: false,
resourceState: "ANOTHER Status",
wantErr: true,
wantResp: true,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
apiClient := &apiClientMocked{
getVolumeFails: tt.getFails,
resourceState: tt.resourceState,
}

var wantRes *iaasalpha.Volume
if tt.wantResp {
wantRes = &iaasalpha.Volume{
Id: utils.Ptr("vid"),
Status: &tt.resourceState,
}
}

handler := CreateVolumeWaitHandler(context.Background(), apiClient, "pid", "vid")

gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background())

if (err != nil) != tt.wantErr {
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
}
if !cmp.Equal(gotRes, wantRes) {
t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes)
}
})
}
}

func TestDeleteVolumeWaitHandler(t *testing.T) {
tests := []struct {
desc string
getFails bool
isDeleted bool
resourceState string
wantErr bool
wantResp bool
}{
{
desc: "delete_succeeded",
getFails: false,
isDeleted: true,
wantErr: false,
wantResp: false,
},
{
desc: "get_fails",
getFails: true,
resourceState: "",
wantErr: true,
wantResp: false,
},
{
desc: "timeout",
getFails: false,
resourceState: "ANOTHER Status",
wantErr: true,
wantResp: false,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
apiClient := &apiClientMocked{
getVolumeFails: tt.getFails,
isDeleted: tt.isDeleted,
resourceState: tt.resourceState,
}

var wantRes *iaasalpha.Volume
if tt.wantResp {
wantRes = &iaasalpha.Volume{
Id: utils.Ptr("vid"),
Status: &tt.resourceState,
}
}

handler := DeleteVolumeWaitHandler(context.Background(), apiClient, "pid", "vid")

gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background())

if (err != nil) != tt.wantErr {
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
}
if !cmp.Equal(gotRes, wantRes) {
t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes)
}
})
}
}