Skip to content
Merged
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,11 @@
- [v1.11.0](services/sqlserverflex/CHANGELOG.md#v1110)
- `v3beta1api`: **Feature:** Added `labels` to `CreateInstanceRequestPayload`, `GetInstanceReponse`, `UpdateInstancePartiallyRequestPayload`, `UpdateInstanceRequestPayload`
- [v1.12.0](services/sqlserverflex/CHANGELOG.md#v1120)
- **Feature:** Introduce enums for various attributes
- **Feature:** Introduce enums for various attributes
- [v1.13.0](services/sqlserverflex/CHANGELOG.md#v1130)
- `v2api`:
- **Improvement**: Use new `WaiterHelper` struct in the SQLServer Flex WaitHandler
- **Breaking change:** Change return type of `wait.DeleteInstanceWaitHandler()` to `*wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse]`
- `stackitmarketplace`:
- [v1.17.5](services/stackitmarketplace/CHANGELOG.md#v1175)
- **Dependencies:** Bump STACKIT SDK core module from `v0.24.0` to `v0.24.1`
Expand Down
5 changes: 5 additions & 0 deletions services/sqlserverflex/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## v1.13.0
- `v2api`:
- **Improvement**: Use new `WaiterHelper` struct in the SQLServer Flex WaitHandler
- **Breaking change:** Change return type of `wait.DeleteInstanceWaitHandler()` to `*wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse]`

## v1.12.0
- **Feature:** Introduce enums for various attributes

Expand Down
2 changes: 1 addition & 1 deletion services/sqlserverflex/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v1.12.0
v1.13.0
107 changes: 43 additions & 64 deletions services/sqlserverflex/v2api/wait/wait.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@ package wait
import (
"context"
"errors"
"fmt"
"net/http"
"strings"
"time"

"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/core/wait"
sqlserverflex "github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex/v2api"
)
Expand All @@ -21,76 +18,58 @@ const (
InstanceStateFailed = "Failed"
)

// CreateInstanceWaitHandler will wait for instance creation
func CreateInstanceWaitHandler(ctx context.Context, a sqlserverflex.DefaultAPI, projectId, instanceId, region string) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] {
handler := wait.New(func() (waitFinished bool, response *sqlserverflex.GetInstanceResponse, err error) {
s, err := a.GetInstance(ctx, projectId, instanceId, region).Execute()
if err != nil {
return false, nil, err
}
if s == nil || s.Item == nil || s.Item.Id == nil || *s.Item.Id != instanceId || s.Item.Status == nil {
return false, nil, nil
}
switch strings.ToLower(*s.Item.Status) {
case strings.ToLower(InstanceStateSuccess):
return true, s, nil
case strings.ToLower(InstanceStateUnknown), strings.ToLower(InstanceStateFailed):
return true, s, fmt.Errorf("create failed for instance with id %s", instanceId)
default:
return false, s, nil
}
})
handler.SetTimeout(45 * time.Minute)
func createOrUpdateInstanceWaitHandler(ctx context.Context, client sqlserverflex.DefaultAPI, projectId, instanceId, region string) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] {
waitConfig := wait.WaiterHelper[sqlserverflex.GetInstanceResponse, string]{
FetchInstance: client.GetInstance(ctx, projectId, instanceId, region).Execute,
GetState: func(response *sqlserverflex.GetInstanceResponse) (string, error) {
if response == nil {
return "", errors.New("empty response")
}
if response.Item == nil {
return "", errors.New("empty instance")
}
if response.Item.Status == nil {
return "", errors.New("status is missing")
}
return *response.Item.Status, nil
},
ActiveState: []string{InstanceStateSuccess},
ErrorState: []string{InstanceStateUnknown, InstanceStateFailed, InstanceStateEmpty},
Comment thread
Fyusel marked this conversation as resolved.
}

handler := wait.New(waitConfig.Wait())
handler.SetSleepBeforeWait(5 * time.Second)
handler.SetTimeout(45 * time.Minute)
return handler
}

// UpdateInstanceWaitHandler will wait for instance update
func UpdateInstanceWaitHandler(ctx context.Context, a sqlserverflex.DefaultAPI, projectId, instanceId, region string) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] {
handler := wait.New(func() (waitFinished bool, response *sqlserverflex.GetInstanceResponse, err error) {
s, err := a.GetInstance(ctx, projectId, instanceId, region).Execute()
if err != nil {
return false, nil, err
}
if s == nil || s.Item == nil || s.Item.Id == nil || *s.Item.Id != instanceId || s.Item.Status == nil {
return false, nil, nil
}
switch strings.ToLower(*s.Item.Status) {
case strings.ToLower(InstanceStateSuccess):
return true, s, nil
case strings.ToLower(InstanceStateUnknown), strings.ToLower(InstanceStateFailed):
Comment thread
Fyusel marked this conversation as resolved.
return true, s, fmt.Errorf("update failed for instance with id %s", instanceId)
default:
return false, s, nil
}
})
handler.SetSleepBeforeWait(2 * time.Second)
handler.SetTimeout(45 * time.Minute)
return handler
// CreateInstanceWaitHandler will wait for instance creation
func CreateInstanceWaitHandler(ctx context.Context, client sqlserverflex.DefaultAPI, projectId, instanceId, region string) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] {
return createOrUpdateInstanceWaitHandler(ctx, client, projectId, instanceId, region)
}

// PartialUpdateInstanceWaitHandler will wait for instance update
func PartialUpdateInstanceWaitHandler(ctx context.Context, a sqlserverflex.DefaultAPI, projectId, instanceId, region string) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] {
return UpdateInstanceWaitHandler(ctx, a, projectId, instanceId, region)
// UpdateInstanceWaitHandler will wait for instance update
func UpdateInstanceWaitHandler(ctx context.Context, client sqlserverflex.DefaultAPI, projectId, instanceId, region string) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] {
return createOrUpdateInstanceWaitHandler(ctx, client, projectId, instanceId, region)
}

// DeleteInstanceWaitHandler will wait for instance deletion
func DeleteInstanceWaitHandler(ctx context.Context, a sqlserverflex.DefaultAPI, projectId, instanceId, region string) *wait.AsyncActionHandler[struct{}] {
handler := wait.New(func() (waitFinished bool, response *struct{}, err error) {
_, err = a.GetInstance(ctx, projectId, instanceId, region).Execute()
if err == nil {
return false, nil, nil
}
var oapiErr *oapierror.GenericOpenAPIError
ok := errors.As(err, &oapiErr)
if !ok {
return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError")
}
if oapiErr.StatusCode != http.StatusNotFound {
return false, nil, err
}
return true, nil, nil
})
func DeleteInstanceWaitHandler(ctx context.Context, client sqlserverflex.DefaultAPI, projectId, instanceId, region string) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] {
waitConfig := wait.WaiterHelper[sqlserverflex.GetInstanceResponse, string]{
FetchInstance: client.GetInstance(ctx, projectId, instanceId, region).Execute,
GetState: func(response *sqlserverflex.GetInstanceResponse) (string, error) {
if response == nil {
return "", errors.New("empty response")
}
if response.Item.Status == nil {
return "", errors.New("status is missing in response")
}
return *response.Item.Status, nil
},
ErrorState: []string{InstanceStateFailed},
DeleteHttpErrorStatusCodes: []int{http.StatusNotFound},
}
handler := wait.New(waitConfig.Wait())
handler.SetTimeout(15 * time.Minute)
return handler
}
159 changes: 44 additions & 115 deletions services/sqlserverflex/v2api/wait/wait_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package wait

import (
"context"
"fmt"
"testing"
"testing/synctest"
"time"
Expand All @@ -10,6 +11,7 @@ import (

"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/core/utils"
"github.com/stackitcloud/stackit-sdk-go/core/wait"
sqlserverflex "github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex/v2api"
)

Expand Down Expand Up @@ -46,88 +48,7 @@ func newAPIMock(settings mockSettings) sqlserverflex.DefaultAPI {
}
}

func TestCreateInstanceWaitHandler(t *testing.T) {
tests := []struct {
desc string
instanceGetFails bool
instanceState string
usersGetErrorStatus int
wantErr bool
wantResp bool
}{
{
desc: "create_succeeded",
instanceGetFails: false,
instanceState: InstanceStateSuccess,
wantErr: false,
wantResp: true,
},
{
desc: "create_failed",
instanceGetFails: false,
instanceState: InstanceStateFailed,
wantErr: true,
wantResp: true,
},
{
desc: "create_failed_2",
instanceGetFails: false,
instanceState: InstanceStateEmpty,
wantErr: true,
wantResp: true,
},
{
desc: "instance_get_fails",
instanceGetFails: true,
wantErr: true,
wantResp: false,
},
{
desc: "timeout",
instanceGetFails: false,
instanceState: InstanceStateProcessing,
wantErr: true,
wantResp: true,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
instanceId := "foo-bar"
instanceState := tt.instanceState

apiClient := newAPIMock(mockSettings{
instanceId: &instanceId,
instanceState: &instanceState,
instanceGetFails: tt.instanceGetFails,
})

var wantRes *sqlserverflex.GetInstanceResponse
if tt.wantResp {
wantRes = &sqlserverflex.GetInstanceResponse{
Item: &sqlserverflex.Instance{
Id: &instanceId,
Status: utils.Ptr(tt.instanceState),
},
}
}

handler := CreateInstanceWaitHandler(context.Background(), apiClient, "", instanceId, "")

gotRes, err := handler.SetTimeout(10 * time.Millisecond).SetSleepBeforeWait(1 * 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 TestUpdateInstanceWaitHandler(t *testing.T) {
func TestCreateOrUpdateInstanceWaitHandler(t *testing.T) {
tests := []struct {
desc string
instanceGetFails bool
Expand All @@ -136,28 +57,28 @@ func TestUpdateInstanceWaitHandler(t *testing.T) {
wantResp bool
}{
{
desc: "update_succeeded",
desc: "create_or_update_succeeded",
instanceGetFails: false,
instanceState: InstanceStateSuccess,
wantErr: false,
wantResp: true,
},
{
desc: "update_failed",
desc: "create_or_update_failed",
instanceGetFails: false,
instanceState: InstanceStateFailed,
wantErr: true,
wantResp: true,
},
{
desc: "update_failed_2",
desc: "create_or_update_failed_2",
instanceGetFails: false,
instanceState: InstanceStateEmpty,
wantErr: true,
wantResp: true,
},
{
desc: "get_fails",
desc: "instance_get_fails",
instanceGetFails: true,
wantErr: true,
wantResp: false,
Expand All @@ -167,43 +88,51 @@ func TestUpdateInstanceWaitHandler(t *testing.T) {
instanceGetFails: false,
instanceState: InstanceStateProcessing,
wantErr: true,
wantResp: true,
wantResp: false,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
instanceId := "foo-bar"
instanceState := tt.instanceState

apiClient := newAPIMock(mockSettings{
instanceId: &instanceId,
instanceState: &instanceState,
instanceGetFails: tt.instanceGetFails,
})
handlers := map[string]func(context.Context, sqlserverflex.DefaultAPI, string, string, string) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse]{
"common logic": createOrUpdateInstanceWaitHandler,
"create": CreateInstanceWaitHandler,
"update": UpdateInstanceWaitHandler,
}

var wantRes *sqlserverflex.GetInstanceResponse
if tt.wantResp {
wantRes = &sqlserverflex.GetInstanceResponse{
Item: &sqlserverflex.Instance{
Id: &instanceId,
Status: utils.Ptr(tt.instanceState),
},
for handlerDesc, handlerFn := range handlers {
for _, tt := range tests {
t.Run(fmt.Sprintf("%s - %s", handlerDesc, tt.desc), func(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
instanceId := "foo-bar"

apiClient := newAPIMock(mockSettings{
instanceGetFails: tt.instanceGetFails,
instanceId: &instanceId,
instanceState: &tt.instanceState,
})

var wantRes *sqlserverflex.GetInstanceResponse
if tt.wantResp {
wantRes = &sqlserverflex.GetInstanceResponse{
Item: &sqlserverflex.Instance{
Id: &instanceId,
Status: utils.Ptr(tt.instanceState),
},
}
}
}

handler := UpdateInstanceWaitHandler(context.Background(), apiClient, "", instanceId, "")
handler := handlerFn(context.Background(), apiClient, "", instanceId, "")
gotRes, err := handler.SetTimeout(10 * time.Millisecond).SetSleepBeforeWait(1 * time.Millisecond).WaitWithContext(context.Background())

gotRes, err := handler.SetTimeout(10 * time.Millisecond).SetSleepBeforeWait(1 * 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)
}
if (err != nil) != tt.wantErr {
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
}
diff := cmp.Diff(gotRes, wantRes)
if diff != "" {
t.Fatalf("handler gotRes = %+v\n want %+v\n diff = %s", gotRes, wantRes, diff)
}
})
})
})
}
}
}

Expand Down
Loading