terraform-provider-stackitp.../stackit/internal/wait/sqlserverflexalpha/wait.go
Marcel S. Henselin 826bb5b36a
Some checks failed
CI Workflow / Check GoReleaser config (pull_request) Successful in 5s
CI Workflow / Test readiness for publishing provider (pull_request) Failing after 3m33s
CI Workflow / CI run tests (pull_request) Failing after 4m51s
CI Workflow / CI run build and linting (pull_request) Failing after 4m37s
CI Workflow / Code coverage report (pull_request) Has been skipped
fix: adjust to new generator and sdk use
2026-03-09 10:26:18 +01:00

387 lines
11 KiB
Go

package sqlserverflexalpha
import (
"context"
"errors"
"fmt"
"net/http"
"strings"
"time"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/core/wait"
"github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex/v3alpha1api"
)
// READY, PENDING, PROGRESSING, FAILURE, UNKNOWN,
const (
InstanceStateEmpty = ""
InstanceStateSuccess = "READY"
InstanceStatePending = "PENDING"
InstanceStateProcessing = "PROGRESSING"
InstanceStateFailed = "FAILURE"
InstanceStateUnknown = "UNKNOWN"
InstanceStateTerminating = "TERMINATING"
)
// APIClientInterface Interface needed for tests
type APIClientInterface interface {
GetInstanceRequest(
ctx context.Context,
projectId, region, instanceId string,
) v3alpha1api.ApiGetInstanceRequestRequest
GetDatabaseRequest(
ctx context.Context,
projectId string,
region string,
instanceId string,
databaseName string,
) v3alpha1api.ApiGetDatabaseRequestRequest
GetUserRequest(
ctx context.Context,
projectId string,
region string,
instanceId string,
userId int64,
) v3alpha1api.ApiGetUserRequestRequest
ListRolesRequest(
ctx context.Context,
projectId string,
region string,
instanceId string,
) v3alpha1api.ApiListRolesRequestRequest
ListUsersRequest(
ctx context.Context,
projectId string,
region string,
instanceId string,
) v3alpha1api.ApiListUsersRequestRequest
}
// APIClientUserInterface Interface needed for tests
type APIClientUserInterface interface {
DeleteUserRequestExecute(
ctx context.Context,
projectId string,
region string,
instanceId string,
userId int64,
) error
}
// CreateInstanceWaitHandler will wait for instance creation
func CreateInstanceWaitHandler(
ctx context.Context,
a APIClientInterface,
projectId, instanceId, region string,
) *wait.AsyncActionHandler[v3alpha1api.GetInstanceResponse] {
handler := wait.New(
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 != instanceId {
return false, nil, nil
}
switch strings.ToLower(string(s.Status)) {
case strings.ToLower(InstanceStateSuccess):
if s.Network.AccessScope != nil && *s.Network.AccessScope == "SNA" {
if s.Network.InstanceAddress == nil {
tflog.Info(ctx, "Waiting for instance_address")
return false, nil, nil
}
if s.Network.RouterAddress == nil {
tflog.Info(ctx, "Waiting for router_address")
return false, nil, nil
}
}
tflog.Info(ctx, "trying to get roles")
time.Sleep(10 * time.Second)
_, rolesErr := a.ListRolesRequest(ctx, projectId, region, instanceId).Execute()
if rolesErr != nil {
var oapiErr *oapierror.GenericOpenAPIError
ok := errors.As(rolesErr, &oapiErr)
if !ok {
return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError")
}
if oapiErr.StatusCode != http.StatusInternalServerError {
tflog.Info(
ctx, "got error from api", map[string]interface{}{
"error": rolesErr.Error(),
},
)
return false, nil, rolesErr
}
tflog.Info(
ctx, "wait for get-roles to work hack", map[string]interface{}{},
)
time.Sleep(10 * time.Second)
return false, nil, nil
}
tflog.Info(ctx, "trying to get users")
time.Sleep(10 * time.Second)
_, usersErr := a.ListUsersRequest(ctx, projectId, region, instanceId).Execute()
if usersErr != nil {
var oapiErr *oapierror.GenericOpenAPIError
ok := errors.As(usersErr, &oapiErr)
if !ok {
return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError")
}
if oapiErr.StatusCode != http.StatusInternalServerError {
tflog.Info(
ctx, "got error from api", map[string]interface{}{
"error": rolesErr.Error(),
},
)
return false, nil, usersErr
}
tflog.Info(
ctx, "wait for get-users to work hack", map[string]interface{}{},
)
time.Sleep(10 * time.Second)
return false, nil, nil
}
return true, s, nil
case strings.ToLower(InstanceStateUnknown), strings.ToLower(InstanceStateFailed):
return true, nil, fmt.Errorf("create failed for instance with id %s", instanceId)
case strings.ToLower(InstanceStatePending), strings.ToLower(InstanceStateProcessing):
tflog.Info(
ctx, "request is being handled", map[string]interface{}{
"status": s.Status,
},
)
time.Sleep(10 * time.Second)
return false, nil, nil
default:
tflog.Info(
ctx, "Wait (create) received unknown status", map[string]interface{}{
"instanceId": instanceId,
"status": s.Status,
},
)
return true, nil, errors.New("unknown status received")
}
},
)
return handler
}
// UpdateInstanceWaitHandler will wait for instance update
func UpdateInstanceWaitHandler(
ctx context.Context,
a APIClientInterface,
projectId, instanceId, region string,
) *wait.AsyncActionHandler[v3alpha1api.GetInstanceResponse] {
handler := wait.New(
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 != instanceId {
return false, nil, nil
}
switch strings.ToLower(string(s.Status)) {
case strings.ToLower(InstanceStateSuccess):
return true, s, nil
case strings.ToLower(InstanceStateUnknown), strings.ToLower(InstanceStateFailed):
return true, s, fmt.Errorf("update failed for instance with id %s", instanceId)
case strings.ToLower(InstanceStatePending), strings.ToLower(InstanceStateProcessing):
tflog.Info(
ctx, "request is being handled", map[string]interface{}{
"status": s.Status,
},
)
return false, s, nil
default:
tflog.Info(
ctx, "Wait (update) received unknown status", map[string]interface{}{
"instanceId": instanceId,
"status": s.Status,
},
)
return false, s, nil
}
},
)
return handler
}
// DeleteInstanceWaitHandler will wait for instance deletion
func DeleteInstanceWaitHandler(
ctx context.Context,
a APIClientInterface,
projectId, instanceId, region string,
) *wait.AsyncActionHandler[v3alpha1api.GetInstanceResponse] {
handler := wait.New(
func() (waitFinished bool, response *v3alpha1api.GetInstanceResponse, err error) {
s, err := a.GetInstanceRequest(ctx, projectId, region, instanceId).Execute()
if err == nil {
return false, s, 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
},
)
handler.SetTimeout(30 * time.Minute)
return handler
}
// CreateDatabaseWaitHandler will wait for instance creation
func CreateDatabaseWaitHandler(
ctx context.Context,
a APIClientInterface,
projectId, instanceId, region, databaseName string,
) *wait.AsyncActionHandler[v3alpha1api.GetDatabaseResponse] {
handler := wait.New(
func() (waitFinished bool, response *v3alpha1api.GetDatabaseResponse, err error) {
s, err := a.GetDatabaseRequest(ctx, projectId, region, instanceId, databaseName).Execute()
if err != nil {
var oapiErr *oapierror.GenericOpenAPIError
ok := errors.As(err, &oapiErr)
if !ok {
return false, nil, fmt.Errorf(
"get database - could not convert error to oapierror.GenericOpenAPIError: %s",
err.Error(),
)
}
if oapiErr.StatusCode != http.StatusNotFound {
return false, nil, err
}
return false, nil, nil
}
if s == nil || s.Name != databaseName {
return false, nil, errors.New("response did return different result")
}
return true, s, nil
},
)
return handler
}
// CreateUserWaitHandler will wait for instance creation
func CreateUserWaitHandler(
ctx context.Context,
a APIClientInterface,
projectId, instanceId, region string,
userId int64,
) *wait.AsyncActionHandler[v3alpha1api.GetUserResponse] {
handler := wait.New(
func() (waitFinished bool, response *v3alpha1api.GetUserResponse, err error) {
s, err := a.GetUserRequest(ctx, projectId, region, instanceId, userId).Execute()
if err != 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 false, nil, nil
}
return true, s, nil
},
)
return handler
}
// WaitForUserWaitHandler will wait for instance creation
func WaitForUserWaitHandler(
ctx context.Context,
a APIClientInterface,
projectId, instanceId, region, userName string,
) *wait.AsyncActionHandler[v3alpha1api.ListUserResponse] {
startTime := time.Now()
timeOut := 2 * time.Minute
handler := wait.New(
func() (waitFinished bool, response *v3alpha1api.ListUserResponse, err error) {
if time.Since(startTime) > timeOut {
return false, nil, errors.New("ran into timeout")
}
s, err := a.ListUsersRequest(ctx, projectId, region, instanceId).Size(100).Execute()
if err != nil {
var oapiErr *oapierror.GenericOpenAPIError
ok := errors.As(err, &oapiErr)
if !ok {
return false, nil, fmt.Errorf(
"wait (list users) could not convert error to oapierror.GenericOpenAPIError: %s",
err.Error(),
)
}
if oapiErr.StatusCode != http.StatusNotFound {
return false, nil, err
}
tflog.Info(
ctx, "Wait (list users) still waiting", map[string]interface{}{},
)
return false, nil, nil
}
users, ok := s.GetUsersOk()
if !ok {
return false, nil, errors.New("no users found")
}
for _, u := range users {
if u.GetUsername() == userName {
return true, s, nil
}
}
tflog.Info(
ctx, "Wait (list users) user still not present", map[string]interface{}{},
)
return false, nil, nil
},
)
return handler
}
// DeleteUserWaitHandler will wait for instance deletion
func DeleteUserWaitHandler(
ctx context.Context,
a APIClientInterface,
projectId, region, instanceId string,
userId int64,
) *wait.AsyncActionHandler[struct{}] {
handler := wait.New(
func() (waitFinished bool, response *struct{}, err error) {
_, err = a.GetUserRequest(ctx, projectId, region, instanceId, userId).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")
}
switch oapiErr.StatusCode {
case http.StatusNotFound:
return true, nil, nil
default:
return false, nil, err
}
},
)
handler.SetTimeout(15 * time.Minute)
handler.SetSleepBeforeWait(15 * time.Second)
return handler
}