chore: initial push to be able to work together
This commit is contained in:
parent
81e8d48cf6
commit
30070d8470
263 changed files with 45437 additions and 2 deletions
170
pkg/postgresflexalpha/wait/wait.go
Normal file
170
pkg/postgresflexalpha/wait/wait.go
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
package wait
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
postgresflex "github.com/stackitcloud/terraform-provider-stackit/pkg/postgresflexalpha"
|
||||
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/wait"
|
||||
)
|
||||
|
||||
const (
|
||||
InstanceStateEmpty = ""
|
||||
InstanceStateProgressing = "Progressing"
|
||||
InstanceStateSuccess = "Ready"
|
||||
InstanceStateFailed = "Failure"
|
||||
InstanceStateDeleted = "Deleted"
|
||||
)
|
||||
|
||||
// Interface needed for tests
|
||||
type APIClientInstanceInterface interface {
|
||||
GetInstanceRequestExecute(ctx context.Context, projectId, region, instanceId string) (*postgresflex.GetInstanceResponse, error)
|
||||
ListUsersRequestExecute(ctx context.Context, projectId, region, instanceId string) (*postgresflex.ListUserResponse, error)
|
||||
}
|
||||
|
||||
// Interface needed for tests
|
||||
type APIClientUserInterface interface {
|
||||
GetUserExecute(ctx context.Context, projectId, region, instanceId, userId string) (*postgresflex.GetUserResponse, error)
|
||||
}
|
||||
|
||||
// CreateInstanceWaitHandler will wait for instance creation
|
||||
func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, region, instanceId string) *wait.AsyncActionHandler[postgresflex.GetInstanceResponse] {
|
||||
instanceCreated := false
|
||||
var instanceGetResponse *postgresflex.GetInstanceResponse
|
||||
|
||||
handler := wait.New(func() (waitFinished bool, response *postgresflex.GetInstanceResponse, err error) {
|
||||
if !instanceCreated {
|
||||
s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil {
|
||||
return false, nil, nil
|
||||
}
|
||||
switch *s.Status {
|
||||
default:
|
||||
return true, s, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Status)
|
||||
case InstanceStateEmpty:
|
||||
return false, nil, nil
|
||||
case InstanceStateProgressing:
|
||||
return false, nil, nil
|
||||
case InstanceStateSuccess:
|
||||
instanceCreated = true
|
||||
instanceGetResponse = s
|
||||
case InstanceStateFailed:
|
||||
return true, s, fmt.Errorf("create failed for instance with id %s", instanceId)
|
||||
}
|
||||
}
|
||||
|
||||
// User operations aren't available right after an instance is deemed successful
|
||||
// To check if they are, perform a users request
|
||||
_, err = a.ListUsersRequestExecute(ctx, projectId, region, instanceId)
|
||||
if err == nil {
|
||||
return true, instanceGetResponse, 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, nil, err
|
||||
}
|
||||
if oapiErr.StatusCode < 500 {
|
||||
return true, instanceGetResponse, fmt.Errorf("users request after instance creation returned %d status code", oapiErr.StatusCode)
|
||||
}
|
||||
return false, nil, nil
|
||||
})
|
||||
// Sleep before wait is set because sometimes API returns 404 right after creation request
|
||||
handler.SetTimeout(45 * time.Minute).SetSleepBeforeWait(15 * time.Second)
|
||||
return handler
|
||||
}
|
||||
|
||||
// PartialUpdateInstanceWaitHandler will wait for instance update
|
||||
func PartialUpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, region, instanceId string) *wait.AsyncActionHandler[postgresflex.GetInstanceResponse] {
|
||||
handler := wait.New(func() (waitFinished bool, response *postgresflex.GetInstanceResponse, err error) {
|
||||
s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil {
|
||||
return false, nil, nil
|
||||
}
|
||||
switch *s.Status {
|
||||
default:
|
||||
return true, s, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Status)
|
||||
case InstanceStateEmpty:
|
||||
return false, nil, nil
|
||||
case InstanceStateProgressing:
|
||||
return false, nil, nil
|
||||
case InstanceStateSuccess:
|
||||
return true, s, nil
|
||||
case InstanceStateFailed:
|
||||
return true, s, fmt.Errorf("update failed for instance with id %s", instanceId)
|
||||
}
|
||||
})
|
||||
handler.SetTimeout(45 * time.Minute)
|
||||
return handler
|
||||
}
|
||||
|
||||
// DeleteInstanceWaitHandler will wait for instance deletion
|
||||
func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, region, instanceId string) *wait.AsyncActionHandler[struct{}] {
|
||||
handler := wait.New(func() (waitFinished bool, response *struct{}, err error) {
|
||||
s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil {
|
||||
return false, nil, nil
|
||||
}
|
||||
switch *s.Status {
|
||||
default:
|
||||
return true, nil, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Status)
|
||||
case InstanceStateSuccess:
|
||||
return false, nil, nil
|
||||
case InstanceStateDeleted:
|
||||
return true, nil, nil
|
||||
}
|
||||
})
|
||||
handler.SetTimeout(5 * time.Minute)
|
||||
return handler
|
||||
}
|
||||
|
||||
// ForceDeleteInstanceWaitHandler will wait for instance deletion
|
||||
func ForceDeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, region, instanceId string) *wait.AsyncActionHandler[struct{}] {
|
||||
handler := wait.New(func() (waitFinished bool, response *struct{}, err error) {
|
||||
_, err = a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
|
||||
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, nil, err
|
||||
}
|
||||
if oapiErr.StatusCode != 404 {
|
||||
return false, nil, err
|
||||
}
|
||||
return true, nil, nil
|
||||
})
|
||||
handler.SetTimeout(15 * time.Minute)
|
||||
return handler
|
||||
}
|
||||
|
||||
// DeleteUserWaitHandler will wait for delete
|
||||
func DeleteUserWaitHandler(ctx context.Context, a APIClientUserInterface, projectId, region, instanceId, userId string) *wait.AsyncActionHandler[struct{}] {
|
||||
handler := wait.New(func() (waitFinished bool, response *struct{}, err error) {
|
||||
_, err = a.GetUserExecute(ctx, projectId, region, instanceId, userId)
|
||||
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, nil, err
|
||||
}
|
||||
if oapiErr.StatusCode != 404 {
|
||||
return false, nil, err
|
||||
}
|
||||
return true, nil, nil
|
||||
})
|
||||
handler.SetTimeout(1 * time.Minute)
|
||||
return handler
|
||||
}
|
||||
395
pkg/postgresflexalpha/wait/wait_test.go
Normal file
395
pkg/postgresflexalpha/wait/wait_test.go
Normal file
|
|
@ -0,0 +1,395 @@
|
|||
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/postgresflex"
|
||||
)
|
||||
|
||||
// Used for testing instance operations
|
||||
type apiClientInstanceMocked struct {
|
||||
instanceId string
|
||||
instanceState string
|
||||
instanceIsForceDeleted bool
|
||||
instanceGetFails bool
|
||||
usersGetErrorStatus int
|
||||
}
|
||||
|
||||
func (a *apiClientInstanceMocked) GetInstanceExecute(_ context.Context, _, _, _ string) (*postgresflex.InstanceResponse, error) {
|
||||
if a.instanceGetFails {
|
||||
return nil, &oapierror.GenericOpenAPIError{
|
||||
StatusCode: 500,
|
||||
}
|
||||
}
|
||||
|
||||
if a.instanceIsForceDeleted {
|
||||
return nil, &oapierror.GenericOpenAPIError{
|
||||
StatusCode: 404,
|
||||
}
|
||||
}
|
||||
|
||||
return &postgresflex.InstanceResponse{
|
||||
Item: &postgresflex.Instance{
|
||||
Id: &a.instanceId,
|
||||
Status: &a.instanceState,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *apiClientInstanceMocked) ListUsersExecute(_ context.Context, _, _, _ string) (*postgresflex.ListUsersResponse, error) {
|
||||
if a.usersGetErrorStatus != 0 {
|
||||
return nil, &oapierror.GenericOpenAPIError{
|
||||
StatusCode: a.usersGetErrorStatus,
|
||||
}
|
||||
}
|
||||
|
||||
aux := int64(0)
|
||||
return &postgresflex.ListUsersResponse{
|
||||
Count: &aux,
|
||||
Items: &[]postgresflex.ListUsersResponseItem{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Used for testing user operations
|
||||
type apiClientUserMocked struct {
|
||||
getFails bool
|
||||
userId string
|
||||
isUserDeleted bool
|
||||
}
|
||||
|
||||
func (a *apiClientUserMocked) GetUserExecute(_ context.Context, _, _, _, _ string) (*postgresflex.GetUserResponse, error) {
|
||||
if a.getFails {
|
||||
return nil, &oapierror.GenericOpenAPIError{
|
||||
StatusCode: 500,
|
||||
}
|
||||
}
|
||||
|
||||
if a.isUserDeleted {
|
||||
return nil, &oapierror.GenericOpenAPIError{
|
||||
StatusCode: 404,
|
||||
}
|
||||
}
|
||||
|
||||
return &postgresflex.GetUserResponse{
|
||||
Item: &postgresflex.UserResponse{
|
||||
Id: &a.userId,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
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: false,
|
||||
},
|
||||
{
|
||||
desc: "instance_get_fails",
|
||||
instanceGetFails: true,
|
||||
wantErr: true,
|
||||
wantResp: false,
|
||||
},
|
||||
{
|
||||
desc: "users_get_fails",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateSuccess,
|
||||
usersGetErrorStatus: 500,
|
||||
wantErr: true,
|
||||
wantResp: false,
|
||||
},
|
||||
{
|
||||
desc: "users_get_fails_2",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateSuccess,
|
||||
usersGetErrorStatus: 400,
|
||||
wantErr: true,
|
||||
wantResp: true,
|
||||
},
|
||||
{
|
||||
desc: "timeout",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateProgressing,
|
||||
wantErr: true,
|
||||
wantResp: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
instanceId := "foo-bar"
|
||||
|
||||
apiClient := &apiClientInstanceMocked{
|
||||
instanceId: instanceId,
|
||||
instanceState: tt.instanceState,
|
||||
instanceGetFails: tt.instanceGetFails,
|
||||
usersGetErrorStatus: tt.usersGetErrorStatus,
|
||||
}
|
||||
|
||||
var wantRes *postgresflex.InstanceResponse
|
||||
if tt.wantResp {
|
||||
wantRes = &postgresflex.InstanceResponse{
|
||||
Item: &postgresflex.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) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
instanceGetFails bool
|
||||
instanceState string
|
||||
wantErr bool
|
||||
wantResp bool
|
||||
}{
|
||||
{
|
||||
desc: "update_succeeded",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateSuccess,
|
||||
wantErr: false,
|
||||
wantResp: true,
|
||||
},
|
||||
{
|
||||
desc: "update_failed",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateFailed,
|
||||
wantErr: true,
|
||||
wantResp: true,
|
||||
},
|
||||
{
|
||||
desc: "update_failed_2",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateEmpty,
|
||||
wantErr: true,
|
||||
wantResp: false,
|
||||
},
|
||||
{
|
||||
desc: "get_fails",
|
||||
instanceGetFails: true,
|
||||
wantErr: true,
|
||||
wantResp: false,
|
||||
},
|
||||
{
|
||||
desc: "timeout",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateProgressing,
|
||||
wantErr: true,
|
||||
wantResp: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
instanceId := "foo-bar"
|
||||
|
||||
apiClient := &apiClientInstanceMocked{
|
||||
instanceId: instanceId,
|
||||
instanceState: tt.instanceState,
|
||||
instanceGetFails: tt.instanceGetFails,
|
||||
}
|
||||
|
||||
var wantRes *postgresflex.InstanceResponse
|
||||
if tt.wantResp {
|
||||
wantRes = &postgresflex.InstanceResponse{
|
||||
Item: &postgresflex.Instance{
|
||||
Id: &instanceId,
|
||||
Status: utils.Ptr(tt.instanceState),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
handler := PartialUpdateInstanceWaitHandler(context.Background(), apiClient, "", "", instanceId)
|
||||
|
||||
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 TestDeleteInstanceWaitHandler(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
instanceGetFails bool
|
||||
instanceState string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
desc: "delete_succeeded",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateDeleted,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
desc: "delete_failed",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateFailed,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
desc: "get_fails",
|
||||
instanceGetFails: true,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
instanceId := "foo-bar"
|
||||
|
||||
apiClient := &apiClientInstanceMocked{
|
||||
instanceGetFails: tt.instanceGetFails,
|
||||
instanceId: instanceId,
|
||||
instanceState: tt.instanceState,
|
||||
}
|
||||
|
||||
handler := DeleteInstanceWaitHandler(context.Background(), apiClient, "", "", instanceId)
|
||||
|
||||
_, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background())
|
||||
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestForceDeleteInstanceWaitHandler(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
instanceGetFails bool
|
||||
instanceState string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
desc: "delete_succeeded",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateDeleted,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
desc: "delete_failed",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateFailed,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
desc: "get_fails",
|
||||
instanceGetFails: true,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
instanceId := "foo-bar"
|
||||
|
||||
apiClient := &apiClientInstanceMocked{
|
||||
instanceGetFails: tt.instanceGetFails,
|
||||
instanceIsForceDeleted: tt.instanceState == InstanceStateDeleted,
|
||||
instanceId: instanceId,
|
||||
instanceState: tt.instanceState,
|
||||
}
|
||||
|
||||
handler := ForceDeleteInstanceWaitHandler(context.Background(), apiClient, "", "", instanceId)
|
||||
|
||||
_, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background())
|
||||
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteUserWaitHandler(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
deleteFails bool
|
||||
getFails bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
desc: "delete_succeeded",
|
||||
deleteFails: false,
|
||||
getFails: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
desc: "delete_failed",
|
||||
deleteFails: true,
|
||||
getFails: false,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
desc: "get_fails",
|
||||
deleteFails: false,
|
||||
getFails: true,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
userId := "foo-bar"
|
||||
|
||||
apiClient := &apiClientUserMocked{
|
||||
getFails: tt.getFails,
|
||||
userId: userId,
|
||||
isUserDeleted: !tt.deleteFails,
|
||||
}
|
||||
|
||||
handler := DeleteUserWaitHandler(context.Background(), apiClient, "", "", "", userId)
|
||||
|
||||
_, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background())
|
||||
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue