Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,10 @@
- **Feature:** Added `_UNKNOWN_DEFAULT_OPEN_API` fallback value to all enums to handle unknown API values gracefully.
- [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)
- `v1api`:
- **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.12.0
- `v1api`:
- **Improvement**: Use new `WaiterHelper` struct in the SQLServer Flex WaitHandler
- **Breaking change:** Change return type of `wait.DeleteInstanceWaitHandler()` to `*wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse]`

## v1.11.0
- `v3beta1api`: **Feature:** Added `labels` to `CreateInstanceRequestPayload`, `GetInstanceReponse`, `UpdateInstancePartiallyRequestPayload`, `UpdateInstanceRequestPayload`

Expand Down
2 changes: 1 addition & 1 deletion services/sqlserverflex/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v1.11.0
v1.12.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},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to adjust to new structure (enum change)

}

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):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, what about this "toLower" here? Is this based on an API issue?

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