Some checks failed
CI Workflow / Check GoReleaser config (pull_request) Successful in 5s
CI Workflow / CI (pull_request) Failing after 13m44s
CI Workflow / Code coverage report (pull_request) Has been skipped
CI Workflow / Test readiness for publishing provider (pull_request) Has been cancelled
381 lines
11 KiB
Go
381 lines
11 KiB
Go
package sqlserverflexbeta
|
|
|
|
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"
|
|
|
|
sqlserverflex "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexbeta"
|
|
)
|
|
|
|
// 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 {
|
|
GetInstanceRequestExecute(
|
|
ctx context.Context,
|
|
projectId, region, instanceId string,
|
|
) (*sqlserverflex.GetInstanceResponse, error)
|
|
GetDatabaseRequestExecute(
|
|
ctx context.Context,
|
|
projectId string,
|
|
region string,
|
|
instanceId string,
|
|
databaseName string,
|
|
) (*sqlserverflex.GetDatabaseResponse, error)
|
|
GetUserRequestExecute(
|
|
ctx context.Context,
|
|
projectId string,
|
|
region string,
|
|
instanceId string,
|
|
userId int64,
|
|
) (*sqlserverflex.GetUserResponse, error)
|
|
|
|
ListRolesRequestExecute(
|
|
ctx context.Context,
|
|
projectId string,
|
|
region string,
|
|
instanceId string,
|
|
) (*sqlserverflex.ListRolesResponse, error)
|
|
|
|
ListUsersRequest(ctx context.Context, projectId string, region string, instanceId string) sqlserverflex.ApiListUsersRequestRequest
|
|
|
|
ListUsersRequestExecute(
|
|
ctx context.Context,
|
|
projectId string,
|
|
region string,
|
|
instanceId string,
|
|
) (*sqlserverflex.ListUserResponse, error)
|
|
}
|
|
|
|
// 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[sqlserverflex.GetInstanceResponse] {
|
|
handler := wait.New(
|
|
func() (waitFinished bool, response *sqlserverflex.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 strings.ToLower(string(*s.Status)) {
|
|
case strings.ToLower(InstanceStateSuccess):
|
|
if s.Network != nil && 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.ListRolesRequestExecute(ctx, projectId, region, instanceId)
|
|
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.ListUsersRequestExecute(ctx, projectId, region, instanceId)
|
|
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[sqlserverflex.GetInstanceResponse] {
|
|
handler := wait.New(
|
|
func() (waitFinished bool, response *sqlserverflex.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 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[sqlserverflex.GetInstanceResponse] {
|
|
handler := wait.New(
|
|
func() (waitFinished bool, response *sqlserverflex.GetInstanceResponse, err error) {
|
|
s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
|
|
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[sqlserverflex.GetDatabaseResponse] {
|
|
handler := wait.New(
|
|
func() (waitFinished bool, response *sqlserverflex.GetDatabaseResponse, err error) {
|
|
s, err := a.GetDatabaseRequestExecute(ctx, projectId, region, instanceId, databaseName)
|
|
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 == 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[sqlserverflex.GetUserResponse] {
|
|
handler := wait.New(
|
|
func() (waitFinished bool, response *sqlserverflex.GetUserResponse, err error) {
|
|
s, err := a.GetUserRequestExecute(ctx, projectId, region, instanceId, userId)
|
|
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[sqlserverflex.ListUserResponse] {
|
|
startTime := time.Now()
|
|
timeOut := 2 * time.Minute
|
|
|
|
handler := wait.New(
|
|
func() (waitFinished bool, response *sqlserverflex.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.GetUserRequestExecute(ctx, projectId, region, instanceId, userId)
|
|
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
|
|
}
|