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) } // 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.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 } } return true, s, nil case strings.ToLower(InstanceStateUnknown), strings.ToLower(InstanceStateFailed): return true, s, 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, }, ) return false, nil, nil default: tflog.Info( ctx, "Wait (create) received unknown status", map[string]interface{}{ "instanceId": instanceId, "status": s.Status, }, ) return false, s, nil } }, ) 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 { return false, nil, err } if s == nil || s.Name == nil || *s.Name != databaseName { 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 }, ) return handler } // DeleteUserWaitHandler will wait for instance deletion func DeleteUserWaitHandler( ctx context.Context, a APIClientUserInterface, projectId, instanceId, region string, userId int64, ) *wait.AsyncActionHandler[struct{}] { handler := wait.New( func() (waitFinished bool, response *struct{}, err error) { err = a.DeleteUserRequestExecute(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 case http.StatusInternalServerError: tflog.Warn(ctx, "Wait handler got error 500") return false, nil, nil default: return false, nil, err } }, ) handler.SetTimeout(15 * time.Minute) handler.SetSleepBeforeWait(15 * time.Second) return handler }