chore: work save
Some checks failed
CI Workflow / Check GoReleaser config (pull_request) Successful in 4s
CI Workflow / Test readiness for publishing provider (pull_request) Failing after 3m57s
CI Workflow / CI run tests (pull_request) Failing after 5m5s
CI Workflow / CI run build and linting (pull_request) Failing after 4m50s
CI Workflow / Code coverage report (pull_request) Has been skipped

This commit is contained in:
Marcel_Henselin 2026-03-05 15:11:15 +01:00
parent 411e99739a
commit d6d3a795bb
118 changed files with 3101 additions and 18065 deletions

View file

@ -4,13 +4,11 @@ import (
"context"
"errors"
"fmt"
"math"
"net/http"
"time"
"github.com/hashicorp/terraform-plugin-log/tflog"
postgresflex "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/postgresflexalpha"
"github.com/stackitcloud/stackit-sdk-go/services/postgresflex/v3alpha1api"
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/core/wait"
@ -30,64 +28,55 @@ const (
// APIClientInstanceInterface Interface needed for tests
type APIClientInstanceInterface interface {
GetInstanceRequestExecute(ctx context.Context, projectId, region, instanceId string) (
*postgresflex.GetInstanceResponse,
error,
)
GetInstanceRequest(ctx context.Context, projectId, region, instanceId string) v3alpha1api.ApiGetInstanceRequestRequest
ListUsersRequestExecute(
ListUsersRequest(
ctx context.Context,
projectId string,
region string,
instanceId string,
) (*postgresflex.ListUserResponse, error)
) v3alpha1api.ApiListUsersRequestRequest
}
// APIClientUserInterface Interface needed for tests
type APIClientUserInterface interface {
GetUserRequestExecute(ctx context.Context, projectId, region, instanceId string, userId int32) (
*postgresflex.GetUserResponse,
error,
)
GetUserRequest(ctx context.Context, projectId, region, instanceId string, userId int32) v3alpha1api.ApiGetUserRequestRequest
}
GetDatabaseRequestExecute(
ctx context.Context,
projectId string,
region string,
instanceId string,
databaseId int32,
) (*postgresflex.GetDatabaseResponse, error)
// APIClientDatabaseInterface Interface needed for tests
type APIClientDatabaseInterface interface {
GetDatabaseRequest(ctx context.Context, projectId string, region string, instanceId string, databaseId int32) v3alpha1api.ApiGetDatabaseRequestRequest
}
// CreateInstanceWaitHandler will wait for instance creation
func CreateInstanceWaitHandler(
ctx context.Context, a APIClientInstanceInterface, projectId, region,
instanceId string,
) *wait.AsyncActionHandler[postgresflex.GetInstanceResponse] {
) *wait.AsyncActionHandler[v3alpha1api.GetInstanceResponse] {
instanceCreated := false
var instanceGetResponse *postgresflex.GetInstanceResponse
var instanceGetResponse *v3alpha1api.GetInstanceResponse
maxWait := time.Minute * 45
startTime := time.Now()
extendedTimeout := 0
handler := wait.New(
func() (waitFinished bool, response *postgresflex.GetInstanceResponse, err error) {
func() (waitFinished bool, response *v3alpha1api.GetInstanceResponse, err error) {
if !instanceCreated {
s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
s, err := a.GetInstanceRequest(ctx, projectId, region, instanceId).Execute()
if err != nil {
return false, nil, err
}
if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil {
if s == nil || s.Id != instanceId {
return false, nil, nil
}
tflog.Debug(
ctx, "waiting for instance ready", map[string]interface{}{
"status": *s.Status,
"status": s.Status,
},
)
switch *s.Status {
switch s.Status {
default:
return true, s, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Status)
return true, s, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, s.Status)
case InstanceStateEmpty:
return false, nil, nil
case InstanceStatePending:
@ -112,7 +101,7 @@ func CreateInstanceWaitHandler(
extendedTimeout++
if *s.Network.AccessScope == "SNA" {
ready := true
if s.Network == nil || s.Network.InstanceAddress == nil {
if s.Network.InstanceAddress == nil {
tflog.Warn(ctx, "Waiting for instance_address")
ready = false
}
@ -124,16 +113,12 @@ func CreateInstanceWaitHandler(
return false, nil, nil
}
}
if s.IsDeletable == nil {
tflog.Warn(ctx, "Waiting for is_deletable")
return false, nil, nil
}
}
instanceCreated = true
instanceGetResponse = s
case InstanceStateSuccess:
if s.Network != nil && s.Network.AccessScope != nil && *s.Network.AccessScope == "SNA" {
if s.Network.AccessScope != nil && *s.Network.AccessScope == "SNA" {
if s.Network.InstanceAddress == nil {
tflog.Warn(ctx, "Waiting for instance_address")
return false, nil, nil
@ -156,7 +141,7 @@ func CreateInstanceWaitHandler(
tflog.Info(ctx, "Waiting for instance (calling list users")
// // 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)
_, err = a.ListUsersRequest(ctx, projectId, region, instanceId).Execute()
if err == nil {
return true, instanceGetResponse, nil
}
@ -175,7 +160,7 @@ func CreateInstanceWaitHandler(
},
)
// Sleep before wait is set because sometimes API returns 404 right after creation request
handler.SetTimeout(90 * time.Minute).SetSleepBeforeWait(30 * time.Second)
// handler.SetTimeout(90 * time.Minute).SetSleepBeforeWait(30 * time.Second)
return handler
}
@ -183,19 +168,19 @@ func CreateInstanceWaitHandler(
func PartialUpdateInstanceWaitHandler(
ctx context.Context, a APIClientInstanceInterface, projectId, region,
instanceId string,
) *wait.AsyncActionHandler[postgresflex.GetInstanceResponse] {
) *wait.AsyncActionHandler[v3alpha1api.GetInstanceResponse] {
handler := wait.New(
func() (waitFinished bool, response *postgresflex.GetInstanceResponse, err error) {
s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
func() (waitFinished bool, response *v3alpha1api.GetInstanceResponse, err error) {
s, err := a.GetInstanceRequest(ctx, projectId, region, instanceId).Execute()
if err != nil {
return false, nil, err
}
if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil {
if s == nil || s.Id != instanceId {
return false, nil, nil
}
switch *s.Status {
switch s.Status {
default:
return true, s, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Status)
return true, s, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, s.Status)
case InstanceStateEmpty:
return false, nil, nil
case InstanceStatePending:
@ -213,7 +198,7 @@ func PartialUpdateInstanceWaitHandler(
}
},
)
handler.SetTimeout(45 * time.Minute).SetSleepBeforeWait(30 * time.Second)
// handler.SetTimeout(45 * time.Minute).SetSleepBeforeWait(30 * time.Second)
return handler
}
@ -222,15 +207,12 @@ func GetUserByIdWaitHandler(
ctx context.Context,
a APIClientUserInterface,
projectId, instanceId, region string,
userId int64,
) *wait.AsyncActionHandler[postgresflex.GetUserResponse] {
userId int32,
) *wait.AsyncActionHandler[v3alpha1api.GetUserResponse] {
handler := wait.New(
func() (waitFinished bool, response *postgresflex.GetUserResponse, err error) {
if userId > math.MaxInt32 {
return false, nil, fmt.Errorf("userId value is too big for int32")
}
userId32 := int32(userId) //nolint:gosec // we need to convert databaseId to int32 because API expects int32
s, err := a.GetUserRequestExecute(ctx, projectId, region, instanceId, userId32)
func() (waitFinished bool, response *v3alpha1api.GetUserResponse, err error) {
userId32 := userId
s, err := a.GetUserRequest(ctx, projectId, region, instanceId, userId32).Execute()
if err != nil {
var oapiErr *oapierror.GenericOpenAPIError
ok := errors.As(err, &oapiErr)
@ -259,14 +241,14 @@ func GetUserByIdWaitHandler(
// GetDatabaseByIdWaitHandler will wait for instance creation
func GetDatabaseByIdWaitHandler(
ctx context.Context,
a APIClientUserInterface,
a APIClientDatabaseInterface,
projectId, instanceId, region string,
databaseId int64,
) *wait.AsyncActionHandler[postgresflex.GetDatabaseResponse] {
databaseId int32,
) *wait.AsyncActionHandler[v3alpha1api.GetDatabaseResponse] {
handler := wait.New(
func() (waitFinished bool, response *postgresflex.GetDatabaseResponse, err error) {
dbId32 := int32(databaseId) //nolint:gosec // we need to convert databaseId to int32 because API expects int32
s, err := a.GetDatabaseRequestExecute(ctx, projectId, region, instanceId, dbId32)
func() (waitFinished bool, response *v3alpha1api.GetDatabaseResponse, err error) {
dbId32 := databaseId
s, err := a.GetDatabaseRequest(ctx, projectId, region, instanceId, dbId32).Execute()
if err != nil {
var oapiErr *oapierror.GenericOpenAPIError
ok := errors.As(err, &oapiErr)
@ -307,8 +289,8 @@ func DeleteInstanceWaitHandler(
timeout, sleepBeforeWait time.Duration,
) error {
handler := wait.New(
func() (waitFinished bool, response *postgresflex.GetInstanceResponse, err error) {
s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
func() (waitFinished bool, response *v3alpha1api.GetInstanceResponse, err error) {
s, err := a.GetInstanceRequest(ctx, projectId, region, instanceId).Execute()
if err != 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 {
@ -319,7 +301,7 @@ func DeleteInstanceWaitHandler(
}
return false, nil, fmt.Errorf("api returned error: %w", err)
}
switch *s.Status {
switch s.Status {
case InstanceStateDeleted:
return true, nil, nil
case InstanceStateEmpty, InstanceStatePending, InstanceStateUnknown, InstanceStateProgressing, InstanceStateSuccess:
@ -327,7 +309,7 @@ func DeleteInstanceWaitHandler(
case InstanceStateFailed:
return true, nil, fmt.Errorf("wait handler got status FAILURE for instance: %s", instanceId)
default:
return true, s, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Status)
return true, s, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, s.Status)
}
},
).SetTimeout(timeout).SetSleepBeforeWait(sleepBeforeWait)

View file

@ -11,7 +11,7 @@ import (
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/core/utils"
postgresflex "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/postgresflexalpha"
postgresflex "github.com/stackitcloud/stackit-sdk-go/services/postgresflex/v3alpha1api"
)
// Used for testing instance operations
@ -24,6 +24,12 @@ type apiClientInstanceMocked struct {
usersGetErrorStatus int
}
func (a *apiClientInstanceMocked) GetInstanceRequest(
_ context.Context,
_, _, _ string,
) *postgresflex.ApiGetInstanceRequestRequest {
}
func (a *apiClientInstanceMocked) GetInstanceRequestExecute(
_ context.Context,
_, _, _ string,
@ -41,9 +47,9 @@ func (a *apiClientInstanceMocked) GetInstanceRequestExecute(
}
return &postgresflex.GetInstanceResponse{
Id: &a.instanceId,
Status: postgresflex.GetInstanceResponseGetStatusAttributeType(&a.instanceState),
Network: postgresflex.GetInstanceResponseGetNetworkAttributeType(&a.instanceNetwork),
Id: a.instanceId,
Status: postgresflex.Status(a.instanceState),
Network: a.instanceNetwork,
}, nil
}
@ -57,12 +63,12 @@ func (a *apiClientInstanceMocked) ListUsersRequestExecute(
}
}
aux := int64(0)
aux := int32(0)
return &postgresflex.ListUserResponse{
Pagination: &postgresflex.Pagination{
TotalRows: &aux,
Pagination: postgresflex.Pagination{
TotalRows: aux,
},
Users: &[]postgresflex.ListUser{},
Users: []postgresflex.ListUser{},
}, nil
}
@ -88,9 +94,9 @@ func TestCreateInstanceWaitHandler(t *testing.T) {
},
wantErr: false,
wantRes: &postgresflex.GetInstanceResponse{
Id: utils.Ptr("foo-bar"),
Status: postgresflex.GetInstanceResponseGetStatusAttributeType(utils.Ptr(InstanceStateSuccess)),
Network: &postgresflex.InstanceNetwork{
Id: "foo-bar",
Status: InstanceStateSuccess,
Network: postgresflex.InstanceNetwork{
AccessScope: nil,
Acl: nil,
InstanceAddress: utils.Ptr("10.0.0.1"),
@ -157,9 +163,9 @@ func TestCreateInstanceWaitHandler(t *testing.T) {
usersGetErrorStatus: 400,
wantErr: true,
wantRes: &postgresflex.GetInstanceResponse{
Id: utils.Ptr("foo-bar"),
Status: postgresflex.GetInstanceResponseGetStatusAttributeType(utils.Ptr(InstanceStateSuccess)),
Network: &postgresflex.InstanceNetwork{
Id: "foo-bar",
Status: InstanceStateSuccess,
Network: postgresflex.InstanceNetwork{
AccessScope: nil,
Acl: nil,
InstanceAddress: utils.Ptr("10.0.0.1"),
@ -172,7 +178,7 @@ func TestCreateInstanceWaitHandler(t *testing.T) {
instanceGetFails: false,
instanceState: InstanceStateSuccess,
instanceNetwork: postgresflex.InstanceNetwork{
AccessScope: postgresflex.InstanceNetworkGetAccessScopeAttributeType(utils.Ptr("SNA")),
AccessScope: (*postgresflex.InstanceNetworkAccessScope)(utils.Ptr("SNA")),
Acl: nil,
InstanceAddress: nil,
RouterAddress: utils.Ptr("10.0.0.1"),
@ -185,7 +191,7 @@ func TestCreateInstanceWaitHandler(t *testing.T) {
instanceGetFails: false,
instanceState: InstanceStateProgressing,
instanceNetwork: postgresflex.InstanceNetwork{
AccessScope: postgresflex.InstanceNetworkGetAccessScopeAttributeType(utils.Ptr("SNA")),
AccessScope: (*postgresflex.InstanceNetworkAccessScope)(utils.Ptr("SNA")),
Acl: nil,
InstanceAddress: utils.Ptr("10.0.0.1"),
RouterAddress: utils.Ptr("10.0.0.1"),
@ -199,6 +205,11 @@ func TestCreateInstanceWaitHandler(t *testing.T) {
tt.desc, func(t *testing.T) {
instanceId := "foo-bar"
apiClientMock := postgresflex.DefaultAPIServiceMock{
CreateInstanceRequestExecuteMock: nil,
GetInstanceRequestExecuteMock: nil,
}
apiClient := &apiClientInstanceMocked{
instanceId: instanceId,
instanceState: tt.instanceState,
@ -207,7 +218,7 @@ func TestCreateInstanceWaitHandler(t *testing.T) {
usersGetErrorStatus: tt.usersGetErrorStatus,
}
handler := CreateInstanceWaitHandler(context.Background(), apiClient, "", "", instanceId)
handler := CreateInstanceWaitHandler(context.Background(), apiClientMock, "", "", instanceId)
gotRes, err := handler.SetTimeout(10 * time.Millisecond).SetSleepBeforeWait(1 * time.Millisecond).WaitWithContext(context.Background())
if (err != nil) != tt.wantErr {