From 10af1dbbba39db9275537ad9904616c4172c4f4c Mon Sep 17 00:00:00 2001 From: "Marcel S. Henselin" Date: Fri, 13 Feb 2026 08:15:21 +0000 Subject: [PATCH] fix: postgres_fixes (#54) ## Description relates to #1234 ## Checklist - [ ] Issue was linked above - [ ] Code format was applied: `make fmt` - [ ] Examples were added / adjusted (see `examples/` directory) - [x] Docs are up-to-date: `make generate-docs` (will be checked by CI) - [ ] Unit tests got implemented or updated - [ ] Acceptance tests got implemented or updated (see e.g. [here](https://github.com/stackitcloud/terraform-provider-stackit/blob/f5f99d170996b208672ae684b6da53420e369563/stackit/internal/services/dns/dns_acc_test.go)) - [x] Unit tests are passing: `make test` (will be checked by CI) - [x] No linter issues: `make lint` (will be checked by CI) Reviewed-on: https://tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pulls/54 Co-authored-by: Marcel S. Henselin Co-committed-by: Marcel S. Henselin --- internal/testutils/functions.go | 4 + .../postgresflexalpha/database/mapper.go | 4 +- .../postgresflexalpha/database/resource.go | 39 +-- .../postgresflex_acc_test.go | 26 +- .../services/postgresflexalpha/user/mapper.go | 5 +- .../postgresflexalpha/user/resource.go | 222 ++++++++++-------- .../database/planModifiers.yaml | 3 +- .../sqlserverflex_acc_test.go | 1 + .../sqlserverflexbeta/user/planModifiers.yaml | 6 +- .../internal/wait/postgresflexalpha/wait.go | 72 +++++- 10 files changed, 242 insertions(+), 140 deletions(-) diff --git a/internal/testutils/functions.go b/internal/testutils/functions.go index e672d57c..0006751a 100644 --- a/internal/testutils/functions.go +++ b/internal/testutils/functions.go @@ -123,3 +123,7 @@ func StringFromTemplate(tplFile string, data any) (string, error) { return tplBuf.String(), nil } + +func ResStr(prefix, resource, name string) string { + return fmt.Sprintf("%s_%s.%s", prefix, resource, name) +} diff --git a/stackit/internal/services/postgresflexalpha/database/mapper.go b/stackit/internal/services/postgresflexalpha/database/mapper.go index 5785f4b7..9e38fe3f 100644 --- a/stackit/internal/services/postgresflexalpha/database/mapper.go +++ b/stackit/internal/services/postgresflexalpha/database/mapper.go @@ -51,8 +51,8 @@ func mapFields( return nil } -// mapResourceFields maps fields from a ListDatabase API response to a resourceModel for the resource. -func mapResourceFields(source *postgresflexalpha.ListDatabase, model *resourceModel) error { +// mapResourceFields maps fields from a GetDatabase API response to a resourceModel for the resource. +func mapResourceFields(source *postgresflexalpha.GetDatabaseResponse, model *resourceModel) error { if source == nil { return fmt.Errorf("response is nil") } diff --git a/stackit/internal/services/postgresflexalpha/database/resource.go b/stackit/internal/services/postgresflexalpha/database/resource.go index e2013f29..530bb98c 100644 --- a/stackit/internal/services/postgresflexalpha/database/resource.go +++ b/stackit/internal/services/postgresflexalpha/database/resource.go @@ -6,22 +6,22 @@ import ( "errors" "fmt" "math" - "net/http" "strconv" "strings" + "time" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/stackitcloud/stackit-sdk-go/core/oapierror" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/postgresflexalpha" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core" postgresflexalpha2 "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/database/resources_gen" postgresflexUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils" + postgresflexalpha3 "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/wait/postgresflexalpha" ) var ( @@ -236,7 +236,10 @@ func (r *databaseResource) Create( return } - database, err := getDatabaseById(ctx, r.client, projectId, region, instanceId, databaseId) + database, err := postgresflexalpha3.GetDatabaseByIdWaitHandler(ctx, r.client, projectId, region, instanceId, databaseId). + SetTimeout(15 * time.Minute). + SetSleepBeforeWait(15 * time.Second). + WaitWithContext(ctx) if err != nil { core.LogAndAddError( ctx, @@ -304,14 +307,17 @@ func (r *databaseResource) Read( ctx = tflog.SetField(ctx, "region", region) ctx = tflog.SetField(ctx, "database_id", databaseId) - databaseResp, err := getDatabaseById(ctx, r.client, projectId, region, instanceId, databaseId) + databaseResp, err := postgresflexalpha3.GetDatabaseByIdWaitHandler(ctx, r.client, projectId, region, instanceId, databaseId). + SetTimeout(15 * time.Minute). + SetSleepBeforeWait(15 * time.Second). + WaitWithContext(ctx) 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 && oapiErr.StatusCode == http.StatusNotFound) || errors.Is(err, errDatabaseNotFound) { - resp.State.RemoveResource(ctx) - return - } - core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading database", fmt.Sprintf("Calling API: %v", err)) + core.LogAndAddError( + ctx, + &resp.Diagnostics, + "Error creating database", + fmt.Sprintf("Getting database details after creation: %v", err), + ) return } @@ -433,15 +439,12 @@ func (r *databaseResource) Update( ctx = core.LogResponse(ctx) - // Map response body to schema - databaseResp, err := getDatabaseById(ctx, r.client, projectId, region, instanceId, databaseId64) + databaseResp, err := postgresflexalpha3.GetDatabaseByIdWaitHandler(ctx, r.client, projectId, region, instanceId, databaseId64). + SetTimeout(15 * time.Minute). + SetSleepBeforeWait(15 * time.Second). + WaitWithContext(ctx) 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 && oapiErr.StatusCode == http.StatusNotFound) || errors.Is(err, errDatabaseNotFound) { - resp.State.RemoveResource(ctx) - return - } - core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading database", fmt.Sprintf("Calling API: %v", err)) + core.LogAndAddError(ctx, &resp.Diagnostics, "error updating database", err.Error()) return } diff --git a/stackit/internal/services/postgresflexalpha/postgresflex_acc_test.go b/stackit/internal/services/postgresflexalpha/postgresflex_acc_test.go index 29facfee..7ec8c4f0 100644 --- a/stackit/internal/services/postgresflexalpha/postgresflex_acc_test.go +++ b/stackit/internal/services/postgresflexalpha/postgresflex_acc_test.go @@ -19,6 +19,8 @@ import ( fwresource "github.com/hashicorp/terraform-plugin-framework/resource" ) +const pfx = "stackitprivatepreview_postgresflexalpha" + func TestInstanceResourceSchema(t *testing.T) { t.Parallel() @@ -149,6 +151,8 @@ func getExample() resData { func TestAccInstance(t *testing.T) { exData := getExample() + t.Logf(" ... working on instance %s", exData.TfName) + resName := fmt.Sprintf( "stackitprivatepreview_postgresflexalpha_instance.%s", exData.TfName, @@ -211,6 +215,8 @@ func TestAccInstance(t *testing.T) { func TestAccInstanceWithUsers(t *testing.T) { data := getExample() + t.Logf(" ... working on instance %s", data.TfName) + userName := "testUser" data.Users = []User{ { @@ -220,16 +226,6 @@ func TestAccInstanceWithUsers(t *testing.T) { }, } - resName := fmt.Sprintf( - "stackitprivatepreview_postgresflexalpha_instance.%s", - data.TfName, - ) - - resUserName := fmt.Sprintf( - "stackitprivatepreview_postgresflexalpha_user.%s", - userName, - ) - resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories, @@ -241,10 +237,10 @@ func TestAccInstanceWithUsers(t *testing.T) { data, ), Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(resName, "name", data.Name), - resource.TestCheckResourceAttrSet(resName, "id"), - resource.TestCheckResourceAttr(resUserName, "name", userName), - resource.TestCheckResourceAttrSet(resUserName, "id"), + resource.TestCheckResourceAttr(testutils.ResStr(pfx, "instance", data.TfName), "name", data.Name), + resource.TestCheckResourceAttrSet(testutils.ResStr(pfx, "instance", data.TfName), "id"), + resource.TestCheckResourceAttr(testutils.ResStr(pfx, "user", userName), "name", userName), + resource.TestCheckResourceAttrSet(testutils.ResStr(pfx, "user", userName), "id"), ), }, }, @@ -253,6 +249,8 @@ func TestAccInstanceWithUsers(t *testing.T) { func TestAccInstanceWithDatabases(t *testing.T) { data := getExample() + t.Logf(" ... working on instance %s", data.TfName) + dbName := "testDb" userName := "testUser" data.Users = []User{ diff --git a/stackit/internal/services/postgresflexalpha/user/mapper.go b/stackit/internal/services/postgresflexalpha/user/mapper.go index 37c8fc1f..8fb4278f 100644 --- a/stackit/internal/services/postgresflexalpha/user/mapper.go +++ b/stackit/internal/services/postgresflexalpha/user/mapper.go @@ -7,7 +7,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/types" postgresflex "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/postgresflexalpha" - "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils" ) @@ -80,7 +79,7 @@ func toUpdatePayload(model *resourceModel, roles *[]string) ( } return &postgresflex.UpdateUserRequestPayload{ - Name: conversion.StringValueToPointer(model.Name), + Name: model.Name.ValueStringPointer(), Roles: toPayloadRoles(roles), }, nil } @@ -96,7 +95,7 @@ func toCreatePayload(model *resourceModel, roles *[]string) (*postgresflex.Creat return &postgresflex.CreateUserRequestPayload{ Roles: toPayloadRoles(roles), - Name: conversion.StringValueToPointer(model.Name), + Name: model.Name.ValueStringPointer(), }, nil } diff --git a/stackit/internal/services/postgresflexalpha/user/resource.go b/stackit/internal/services/postgresflexalpha/user/resource.go index 8984a4ea..c6cad445 100644 --- a/stackit/internal/services/postgresflexalpha/user/resource.go +++ b/stackit/internal/services/postgresflexalpha/user/resource.go @@ -3,24 +3,23 @@ package postgresflexalpha import ( "context" _ "embed" - "errors" "fmt" "math" - "net/http" "strconv" "strings" + "time" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" postgresflex "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/postgresflexalpha" postgresflexalpha "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/user/resources_gen" postgresflexUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils" + postgresflexalphaWait "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/wait/postgresflexalpha" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/stackitcloud/stackit-sdk-go/core/oapierror" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils" @@ -52,7 +51,7 @@ type UserResourceIdentityModel struct { ProjectID types.String `tfsdk:"project_id"` Region types.String `tfsdk:"region"` InstanceID types.String `tfsdk:"instance_id"` - UserID types.Int64 `tfsdk:"database_id"` + UserID types.Int64 `tfsdk:"user_id"` } // userResource implements the resource handling for a PostgreSQL Flex user. @@ -150,13 +149,6 @@ func (r *userResource) Create( return } - // Read identity data - var identityData UserResourceIdentityModel - resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...) - if resp.Diagnostics.HasError() { - return - } - ctx = core.InitProviderContext(ctx) arg := &clientArg{ @@ -178,6 +170,7 @@ func (r *userResource) Create( core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating user", fmt.Sprintf("Creating API payload: %v", err)) return } + // Create new user userResp, err := r.client.CreateUserRequest( ctx, @@ -185,13 +178,13 @@ func (r *userResource) Create( arg.region, arg.instanceId, ).CreateUserRequestPayload(*payload).Execute() - if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating user", fmt.Sprintf("Calling API: %v", err)) return } - if userResp.Id == nil || *userResp.Id == 0 { + id, ok := userResp.GetIdOk() + if !ok || id == 0 { core.LogAndAddError( ctx, &resp.Diagnostics, @@ -200,13 +193,9 @@ func (r *userResource) Create( ) return } - model.Id = types.Int64Value(userResp.GetId()) - model.UserId = types.Int64Value(userResp.GetId()) - model.Password = types.StringValue(userResp.GetPassword()) - model.Status = types.StringValue(userResp.GetStatus()) - model.ConnectionString = types.StringValue(userResp.GetConnectionString()) + arg.userId = id - ctx = tflog.SetField(ctx, "user_id", userResp.GetId()) + ctx = tflog.SetField(ctx, "user_id", id) ctx = core.LogResponse(ctx) @@ -215,28 +204,65 @@ func (r *userResource) Create( ProjectID: types.StringValue(arg.projectId), Region: types.StringValue(arg.region), InstanceID: types.StringValue(arg.instanceId), - UserID: types.Int64Value(userResp.GetId()), + UserID: types.Int64Value(id), } resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...) if resp.Diagnostics.HasError() { return } - // Verify creation - exists, err := r.getUserResource(ctx, &model, arg) + model.Id = types.Int64Value(id) + model.UserId = types.Int64Value(id) + model.Password = types.StringValue(userResp.GetPassword()) + model.Status = types.StringValue(userResp.GetStatus()) + model.ConnectionString = types.StringValue(userResp.GetConnectionString()) + + waitResp, err := postgresflexalphaWait.GetUserByIdWaitHandler( + ctx, + r.client, + arg.projectId, + arg.instanceId, + arg.region, + id, + ).SetSleepBeforeWait( + 10 * time.Second, + ).SetTimeout( + 15 * time.Minute, + ).WaitWithContext(ctx) if err != nil { - core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating user", fmt.Sprintf("Calling API: %v", err)) - return - } - - if !exists { core.LogAndAddError( - ctx, &resp.Diagnostics, "Error creating user", - fmt.Sprintf("User ID '%v' resource not found after creation", model.UserId.ValueInt64()), + ctx, + &resp.Diagnostics, + "create user", + fmt.Sprintf("Instance creation waiting: %v", err), ) return } + + if waitResp.Id == nil { + core.LogAndAddError( + ctx, + &resp.Diagnostics, + "create user", + "Instance creation waiting: returned id is nil", + ) + return + } + if waitResp.Id == nil || *waitResp.Id != id { + core.LogAndAddError( + ctx, + &resp.Diagnostics, + "create user", + fmt.Sprintf( + "Instance creation waiting: returned id is wrong: %+v - %+v", + waitResp.Id, + id, + ), + ) + return + } + // Set state to fully populated data diags = resp.State.Set(ctx, model) resp.Diagnostics.Append(diags...) @@ -272,17 +298,39 @@ func (r *userResource) Read( ctx = core.InitProviderContext(ctx) // Read resource state - exists, err := r.getUserResource(ctx, &model, arg) + waitResp, err := postgresflexalphaWait.GetUserByIdWaitHandler( + ctx, + r.client, + arg.projectId, + arg.instanceId, + arg.region, + model.UserId.ValueInt64(), + ).SetSleepBeforeWait( + 10 * time.Second, + ).SetTimeout( + 15 * time.Minute, + ).WaitWithContext(ctx) if err != nil { - core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading user", fmt.Sprintf("Calling API: %v", err)) + core.LogAndAddError( + ctx, + &resp.Diagnostics, + "read user", + fmt.Sprintf("Instance creation waiting: %v", err), + ) return } - if !exists { - resp.State.RemoveResource(ctx) + if waitResp.Id == nil || *waitResp.Id != model.UserId.ValueInt64() { + core.LogAndAddError( + ctx, + &resp.Diagnostics, + "read user", + "Instance creation waiting: returned id is nil or wrong", + ) return } + arg.userId = *waitResp.Id ctx = core.LogResponse(ctx) @@ -320,23 +368,12 @@ func (r *userResource) Update( return } - // Read identity data - var identityData UserResourceIdentityModel - resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...) - if resp.Diagnostics.HasError() { - return - } - ctx = core.InitProviderContext(ctx) - arg, errExt := r.extractIdentityData(model, identityData) - if errExt != nil { - core.LogAndAddError( - ctx, - &resp.Diagnostics, - extractErrorSummary, - fmt.Sprintf(extractErrorMessage, errExt), - ) + arg := &clientArg{ + projectId: model.ProjectId.ValueString(), + instanceId: model.InstanceId.ValueString(), + region: r.providerData.GetRegionWithOverride(model.Region), } ctx = r.setTFLogFields(ctx, arg) @@ -397,21 +434,40 @@ func (r *userResource) Update( } // Verify update - exists, err := r.getUserResource(ctx, &stateModel, arg) + waitResp, err := postgresflexalphaWait.GetUserByIdWaitHandler( + ctx, + r.client, + arg.projectId, + arg.instanceId, + arg.region, + model.UserId.ValueInt64(), + ).SetSleepBeforeWait( + 10 * time.Second, + ).SetTimeout( + 15 * time.Minute, + ).WaitWithContext(ctx) if err != nil { - core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating user", fmt.Sprintf("Calling API: %v", err)) - return - } - - if !exists { core.LogAndAddError( - ctx, &resp.Diagnostics, "Error updating user", - fmt.Sprintf("User ID '%v' resource not found after update", stateModel.UserId.ValueInt64()), + ctx, + &resp.Diagnostics, + "read user", + fmt.Sprintf("Instance creation waiting: %v", err), ) return } + if waitResp.Id == nil || *waitResp.Id != model.UserId.ValueInt64() { + core.LogAndAddError( + ctx, + &resp.Diagnostics, + "read user", + "Instance creation waiting: returned id is nil or wrong", + ) + return + } + arg.userId = *waitResp.Id + // Set state to fully populated data diags = resp.State.Set(ctx, stateModel) resp.Diagnostics.Append(diags...) @@ -470,19 +526,19 @@ func (r *userResource) Delete( ctx = core.LogResponse(ctx) - // Verify deletion - exists, err := r.getUserResource(ctx, &model, arg) - if err != nil { - core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting user", fmt.Sprintf("Calling API: %v", err)) - return - } - if exists { - core.LogAndAddError( - ctx, &resp.Diagnostics, "Error deleting user", - fmt.Sprintf("User ID '%v' resource still exists after deletion", model.UserId.ValueInt64()), - ) - return - } + // TODO: Verify deletion + //exists, err := r.getUserResource(ctx, &model, arg) + //if err != nil { + // core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting user", fmt.Sprintf("Calling API: %v", err)) + // return + //} + //if exists { + // core.LogAndAddError( + // ctx, &resp.Diagnostics, "Error deleting user", + // fmt.Sprintf("User ID '%v' resource still exists after deletion", model.UserId.ValueInt64()), + // ) + // return + //} resp.State.RemoveResource(ctx) @@ -513,34 +569,6 @@ func (r *userResource) IdentitySchema( } } -// getUserResource refreshes the resource state by calling the API and mapping the response to the model. -// Returns true if the resource state was successfully refreshed, false if the resource does not exist. -func (r *userResource) getUserResource(ctx context.Context, model *resourceModel, arg *clientArg) (bool, error) { - - if arg.userId > math.MaxInt32 { - return false, errors.New("error in type conversion: int value too large (userId)") - } - userId := int32(arg.userId) - - // API Call - userResp, err := r.client.GetUserRequest(ctx, arg.projectId, arg.region, arg.instanceId, userId).Execute() - - if err != nil { - var oapiErr *oapierror.GenericOpenAPIError - if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { - return false, nil - } - - return false, fmt.Errorf("error fetching user resource: %w", err) - } - - if err := mapResourceFields(userResp, model, arg.region); err != nil { - return false, fmt.Errorf("error mapping user resource: %w", err) - } - - return true, nil -} - // clientArg holds the arguments for API calls. type clientArg struct { projectId string diff --git a/stackit/internal/services/sqlserverflexbeta/database/planModifiers.yaml b/stackit/internal/services/sqlserverflexbeta/database/planModifiers.yaml index d6209230..1d010ed7 100644 --- a/stackit/internal/services/sqlserverflexbeta/database/planModifiers.yaml +++ b/stackit/internal/services/sqlserverflexbeta/database/planModifiers.yaml @@ -31,7 +31,7 @@ fields: - name: 'owner' modifiers: - - 'RequiresReplace' + - 'UseStateForUnknown' - name: 'database_name' modifiers: @@ -43,6 +43,7 @@ fields: - name: 'compatibility' modifiers: + - 'UseStateForUnknown' - 'RequiresReplace' - name: 'compatibility_level' diff --git a/stackit/internal/services/sqlserverflexbeta/sqlserverflex_acc_test.go b/stackit/internal/services/sqlserverflexbeta/sqlserverflex_acc_test.go index e6c776dc..33f7cd29 100644 --- a/stackit/internal/services/sqlserverflexbeta/sqlserverflex_acc_test.go +++ b/stackit/internal/services/sqlserverflexbeta/sqlserverflex_acc_test.go @@ -120,6 +120,7 @@ func getExample() resData { func TestAccInstance(t *testing.T) { exData := getExample() + t.Logf(" ... working on instance %s", exData.TfName) updNameData := exData updNameData.Name = "name-updated" diff --git a/stackit/internal/services/sqlserverflexbeta/user/planModifiers.yaml b/stackit/internal/services/sqlserverflexbeta/user/planModifiers.yaml index fe4025ee..8ff346ab 100644 --- a/stackit/internal/services/sqlserverflexbeta/user/planModifiers.yaml +++ b/stackit/internal/services/sqlserverflexbeta/user/planModifiers.yaml @@ -3,16 +3,13 @@ fields: modifiers: - 'UseStateForUnknown' - - name: 'user_id' - modifiers: - - 'UseStateForUnknown' - - name: 'instance_id' validators: - validate.NoSeparator - validate.UUID modifiers: - 'UseStateForUnknown' + - 'RequiresReplace' - name: 'project_id' validators: @@ -28,6 +25,7 @@ fields: - name: 'user_id' modifiers: + - 'UseStateForUnknown' - 'RequiresReplace' - name: 'username' diff --git a/stackit/internal/wait/postgresflexalpha/wait.go b/stackit/internal/wait/postgresflexalpha/wait.go index c490b605..8c8b80b3 100644 --- a/stackit/internal/wait/postgresflexalpha/wait.go +++ b/stackit/internal/wait/postgresflexalpha/wait.go @@ -2,7 +2,10 @@ package postgresflexalpha import ( "context" + "errors" "fmt" + "math" + "net/http" "time" "github.com/hashicorp/terraform-plugin-log/tflog" @@ -40,10 +43,18 @@ type APIClientInstanceInterface interface { // APIClientUserInterface Interface needed for tests type APIClientUserInterface interface { - GetUserRequestExecute(ctx context.Context, projectId, region, instanceId string, userId int64) ( + GetUserRequestExecute(ctx context.Context, projectId, region, instanceId string, userId int32) ( *postgresflex.GetUserResponse, error, ) + + GetDatabaseRequestExecute( + ctx context.Context, + projectId string, + region string, + instanceId string, + databaseId int32, + ) (*postgresflex.GetDatabaseResponse, error) } // CreateInstanceWaitHandler will wait for instance creation @@ -202,3 +213,62 @@ func PartialUpdateInstanceWaitHandler( handler.SetTimeout(45 * time.Minute).SetSleepBeforeWait(30 * time.Second) return handler } + +// GetUserByIdWaitHandler will wait for instance creation +func GetUserByIdWaitHandler( + ctx context.Context, + a APIClientUserInterface, + projectId, instanceId, region string, + userId int64, +) *wait.AsyncActionHandler[postgresflex.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) + s, err := a.GetUserRequestExecute(ctx, projectId, region, instanceId, userId32) + 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 +} + +// GetDatabaseByIdWaitHandler will wait for instance creation +func GetDatabaseByIdWaitHandler( + ctx context.Context, + a APIClientUserInterface, + projectId, instanceId, region string, + databaseId int64, +) *wait.AsyncActionHandler[postgresflex.GetDatabaseResponse] { + handler := wait.New( + func() (waitFinished bool, response *postgresflex.GetDatabaseResponse, err error) { + dbId32 := int32(databaseId) + s, err := a.GetDatabaseRequestExecute(ctx, projectId, region, instanceId, dbId32) + 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 +}