Compare commits

...

2 commits

Author SHA1 Message Date
Marcel S. Henselin
8c98e1d3bc fix: try fix errors
Some checks failed
CI Workflow / Check GoReleaser config (pull_request) Successful in 12s
CI Workflow / CI run build and linting (pull_request) Has been cancelled
CI Workflow / Code coverage report (pull_request) Has been cancelled
CI Workflow / CI run tests (pull_request) Has been cancelled
CI Workflow / Test readiness for publishing provider (pull_request) Has been cancelled
2026-02-16 17:12:10 +01:00
Marcel S. Henselin
7ee82366d7
fix: try fix errors (#71)
All checks were successful
Publish / Check GoReleaser config (push) Successful in 11s
Publish / Publish provider (push) Successful in 35m24s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

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](f5f99d1709/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: #71
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-16 13:40:05 +00:00
18 changed files with 244 additions and 250 deletions

View file

@ -281,24 +281,12 @@ func (r *databaseResource) Read(
return return
} }
// Read identity data
var identityData DatabaseResourceIdentityModel
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
if resp.Diagnostics.HasError() {
return
}
ctx = core.InitProviderContext(ctx) ctx = core.InitProviderContext(ctx)
projectId, region, instanceId, databaseId, errExt := r.extractIdentityData(model, identityData) projectId := model.ProjectId.ValueString()
if errExt != nil { instanceId := model.InstanceId.ValueString()
core.LogAndAddError( region := model.Region.ValueString()
ctx, databaseId := model.DatabaseId.ValueInt64()
&resp.Diagnostics,
extractErrorSummary,
fmt.Sprintf(extractErrorMessage, errExt),
)
}
ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId) ctx = tflog.SetField(ctx, "instance_id", instanceId)

View file

@ -593,7 +593,7 @@ func (r *instanceResource) ImportState(
ctx, &resp.Diagnostics, ctx, &resp.Diagnostics,
"Error importing instance", "Error importing instance",
fmt.Sprintf( fmt.Sprintf(
"Expected import identifier with format: [project_id],[region],[instance_id] Got: %q", "Expected import identifier with format [project_id],[region],[instance_id] Got: %q",
req.ID, req.ID,
), ),
) )

View file

@ -48,7 +48,7 @@ resource "stackitprivatepreview_postgresflexalpha_database" "{{ $db.Name }}" {
project_id = "{{ $db.ProjectId }}" project_id = "{{ $db.ProjectId }}"
instance_id = stackitprivatepreview_postgresflexalpha_instance.{{ $tfName }}.instance_id instance_id = stackitprivatepreview_postgresflexalpha_instance.{{ $tfName }}.instance_id
name = "{{ $db.Name }}" name = "{{ $db.Name }}"
owner = "{{ $db.Owner }}" owner = stackitprivatepreview_postgresflexalpha_user.{{ $db.Owner }}.name
} }
{{ end }} {{ end }}
{{ end }} {{ end }}

View file

@ -164,16 +164,16 @@ func TestMapFieldsCreate(t *testing.T) {
}, },
testRegion, testRegion,
resourceModel{ resourceModel{
Id: types.Int64Value(1), Id: types.Int64Value(1),
UserId: types.Int64Value(1), UserId: types.Int64Value(1),
InstanceId: types.StringValue("iid"), InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"), ProjectId: types.StringValue("pid"),
Name: types.StringNull(), Name: types.StringNull(),
Roles: types.List(types.SetNull(types.StringType)), Roles: types.List(types.SetNull(types.StringType)),
Password: types.StringNull(), Password: types.StringNull(),
Region: types.StringValue(testRegion), Region: types.StringValue(testRegion),
Status: types.StringNull(), Status: types.StringNull(),
ConnectionString: types.StringNull(), //ConnectionString: types.StringNull(),
}, },
true, true,
}, },
@ -186,16 +186,16 @@ func TestMapFieldsCreate(t *testing.T) {
}, },
testRegion, testRegion,
resourceModel{ resourceModel{
Id: types.Int64Value(1), Id: types.Int64Value(1),
UserId: types.Int64Value(1), UserId: types.Int64Value(1),
InstanceId: types.StringValue("iid"), InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"), ProjectId: types.StringValue("pid"),
Name: types.StringValue("username"), Name: types.StringValue("username"),
Roles: types.List(types.SetNull(types.StringType)), Roles: types.List(types.SetNull(types.StringType)),
Password: types.StringNull(), Password: types.StringNull(),
Region: types.StringValue(testRegion), Region: types.StringValue(testRegion),
Status: types.StringValue("status"), Status: types.StringValue("status"),
ConnectionString: types.StringNull(), //ConnectionString: types.StringNull(),
}, },
true, true,
}, },
@ -208,16 +208,16 @@ func TestMapFieldsCreate(t *testing.T) {
}, },
testRegion, testRegion,
resourceModel{ resourceModel{
Id: types.Int64Value(1), Id: types.Int64Value(1),
UserId: types.Int64Value(1), UserId: types.Int64Value(1),
InstanceId: types.StringValue("iid"), InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"), ProjectId: types.StringValue("pid"),
Name: types.StringNull(), Name: types.StringNull(),
Roles: types.List(types.SetNull(types.StringType)), Roles: types.List(types.SetNull(types.StringType)),
Password: types.StringNull(), Password: types.StringNull(),
Region: types.StringValue(testRegion), Region: types.StringValue(testRegion),
Status: types.StringNull(), Status: types.StringNull(),
ConnectionString: types.StringNull(), //ConnectionString: types.StringNull(),
}, },
true, true,
}, },
@ -285,15 +285,15 @@ func TestMapFields(t *testing.T) {
}, },
testRegion, testRegion,
resourceModel{ resourceModel{
Id: types.Int64Value(1), Id: types.Int64Value(1),
UserId: types.Int64Value(int64(1)), UserId: types.Int64Value(int64(1)),
InstanceId: types.StringValue("iid"), InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"), ProjectId: types.StringValue("pid"),
Name: types.StringNull(), Name: types.StringNull(),
Roles: types.List(types.SetNull(types.StringType)), Roles: types.List(types.SetNull(types.StringType)),
Region: types.StringValue(testRegion), Region: types.StringValue(testRegion),
Status: types.StringNull(), Status: types.StringNull(),
ConnectionString: types.StringNull(), //ConnectionString: types.StringNull(),
}, },
true, true,
}, },
@ -324,9 +324,9 @@ func TestMapFields(t *testing.T) {
}, },
), ),
), ),
Region: types.StringValue(testRegion), Region: types.StringValue(testRegion),
Status: types.StringNull(), Status: types.StringNull(),
ConnectionString: types.StringNull(), //ConnectionString: types.StringNull(),
}, },
true, true,
}, },
@ -338,15 +338,15 @@ func TestMapFields(t *testing.T) {
}, },
testRegion, testRegion,
resourceModel{ resourceModel{
Id: types.Int64Value(1), Id: types.Int64Value(1),
UserId: types.Int64Value(1), UserId: types.Int64Value(1),
InstanceId: types.StringValue("iid"), InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"), ProjectId: types.StringValue("pid"),
Name: types.StringNull(), Name: types.StringNull(),
Roles: types.List(types.SetNull(types.StringType)), Roles: types.List(types.SetNull(types.StringType)),
Region: types.StringValue(testRegion), Region: types.StringValue(testRegion),
Status: types.StringNull(), Status: types.StringNull(),
ConnectionString: types.StringNull(), //ConnectionString: types.StringNull(),
}, },
true, true,
}, },

View file

@ -186,25 +186,25 @@ func (r *databaseResource) Create(ctx context.Context, req resource.CreateReques
payLoad.Name = data.Name.ValueStringPointer() payLoad.Name = data.Name.ValueStringPointer()
payLoad.Owner = data.Owner.ValueStringPointer() payLoad.Owner = data.Owner.ValueStringPointer()
_, err := wait.WaitForUserWaitHandler( //_, err := wait.WaitForUserWaitHandler(
ctx, // ctx,
r.client, // r.client,
projectId, // projectId,
instanceId, // instanceId,
region, // region,
data.Owner.ValueString(), // data.Owner.ValueString(),
). //).
SetSleepBeforeWait(10 * time.Second). // SetSleepBeforeWait(10 * time.Second).
WaitWithContext(ctx) // WaitWithContext(ctx)
if err != nil { //if err != nil {
core.LogAndAddError( // core.LogAndAddError(
ctx, // ctx,
&resp.Diagnostics, // &resp.Diagnostics,
createErr, // createErr,
fmt.Sprintf("Calling API: %v", err), // fmt.Sprintf("Calling API: %v", err),
) // )
return // return
} //}
createResp, err := r.client.CreateDatabaseRequest(ctx, projectId, region, instanceId). createResp, err := r.client.CreateDatabaseRequest(ctx, projectId, region, instanceId).
CreateDatabaseRequestPayload(payLoad). CreateDatabaseRequestPayload(payLoad).
@ -361,15 +361,10 @@ func (r *databaseResource) Read(ctx context.Context, req resource.ReadRequest, r
ctx = core.InitProviderContext(ctx) ctx = core.InitProviderContext(ctx)
projectId, region, instanceId, databaseName, errExt := r.extractIdentityData(model, identityData) projectId := model.ProjectId.ValueString()
if errExt != nil { region := model.Region.ValueString()
core.LogAndAddError( instanceId := model.InstanceId.ValueString()
ctx, databaseName := model.DatabaseName.ValueString()
&resp.Diagnostics,
extractErrorSummary,
fmt.Sprintf(extractErrorMessage, errExt),
)
}
ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId) ctx = tflog.SetField(ctx, "instance_id", instanceId)
@ -445,15 +440,10 @@ func (r *databaseResource) Delete(ctx context.Context, req resource.DeleteReques
ctx = core.InitProviderContext(ctx) ctx = core.InitProviderContext(ctx)
projectId, region, instanceId, databaseName, errExt := r.extractIdentityData(model, identityData) projectId := model.ProjectId.ValueString()
if errExt != nil { region := model.Region.ValueString()
core.LogAndAddError( instanceId := model.InstanceId.ValueString()
ctx, databaseName := model.DatabaseName.ValueString()
&resp.Diagnostics,
extractErrorSummary,
fmt.Sprintf(extractErrorMessage, errExt),
)
}
ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId) ctx = tflog.SetField(ctx, "instance_id", instanceId)
@ -508,23 +498,6 @@ func (r *databaseResource) ModifyPlan(
return return
} }
var identityModel DatabaseResourceIdentityModel
identityModel.ProjectID = planModel.ProjectId
identityModel.Region = planModel.Region
if !planModel.InstanceId.IsNull() && !planModel.InstanceId.IsUnknown() {
identityModel.InstanceID = planModel.InstanceId
}
if !planModel.Name.IsNull() && !planModel.Name.IsUnknown() {
identityModel.DatabaseName = planModel.Name
}
resp.Diagnostics.Append(resp.Identity.Set(ctx, identityModel)...)
if resp.Diagnostics.HasError() {
return
}
resp.Diagnostics.Append(resp.Plan.Set(ctx, planModel)...) resp.Diagnostics.Append(resp.Plan.Set(ctx, planModel)...)
if resp.Diagnostics.HasError() { if resp.Diagnostics.HasError() {
return return
@ -548,7 +521,7 @@ func (r *databaseResource) ImportState(
ctx, &resp.Diagnostics, ctx, &resp.Diagnostics,
"Error importing database", "Error importing database",
fmt.Sprintf( fmt.Sprintf(
"Expected import identifier with format: [project_id],[region],[instance_id],[database_name] Got: %q", "Expected import identifier with format [project_id],[region],[instance_id],[database_name] Got: %q",
req.ID, req.ID,
), ),
) )
@ -594,46 +567,3 @@ func (r *databaseResource) ImportState(
tflog.Info(ctx, "sqlserverflexalpha database state imported") tflog.Info(ctx, "sqlserverflexalpha database state imported")
} }
// extractIdentityData extracts essential identifiers from the resource model, falling back to the identity model.
func (r *databaseResource) extractIdentityData(
model resourceModel,
identity DatabaseResourceIdentityModel,
) (projectId, region, instanceId, databaseName string, err error) {
if !model.Name.IsNull() && !model.Name.IsUnknown() {
databaseName = model.Name.ValueString()
} else {
if identity.DatabaseName.IsNull() || identity.DatabaseName.IsUnknown() {
return "", "", "", "", fmt.Errorf("database_name not found in config")
}
databaseName = identity.DatabaseName.ValueString()
}
if !model.ProjectId.IsNull() && !model.ProjectId.IsUnknown() {
projectId = model.ProjectId.ValueString()
} else {
if identity.ProjectID.IsNull() || identity.ProjectID.IsUnknown() {
return "", "", "", "", fmt.Errorf("project_id not found in config")
}
projectId = identity.ProjectID.ValueString()
}
if !model.Region.IsNull() && !model.Region.IsUnknown() {
region = r.providerData.GetRegionWithOverride(model.Region)
} else {
if identity.Region.IsNull() || identity.Region.IsUnknown() {
return "", "", "", "", fmt.Errorf("region not found in config")
}
region = r.providerData.GetRegionWithOverride(identity.Region)
}
if !model.InstanceId.IsNull() && !model.InstanceId.IsUnknown() {
instanceId = model.InstanceId.ValueString()
} else {
if identity.InstanceID.IsNull() || identity.InstanceID.IsUnknown() {
return "", "", "", "", fmt.Errorf("instance_id not found in config")
}
instanceId = identity.InstanceID.ValueString()
}
return projectId, region, instanceId, databaseName, nil
}

View file

@ -523,7 +523,7 @@ func (r *instanceResource) ImportState(
ctx, &resp.Diagnostics, ctx, &resp.Diagnostics,
"Error importing instance", "Error importing instance",
fmt.Sprintf( fmt.Sprintf(
"Expected import identifier with format: [project_id],[region],[instance_id] Got: %q", "Expected import identifier with format [project_id],[region],[instance_id] Got: %q",
req.ID, req.ID,
), ),
) )

View file

@ -43,9 +43,12 @@ func TestMapDataSourceFields(t *testing.T) {
"simple_values", "simple_values",
&sqlserverflexalpha.GetUserResponse{ &sqlserverflexalpha.GetUserResponse{
Roles: &[]string{ Roles: &[]string{
"role_1", "##STACKIT_SQLAgentUser##",
"role_2", "##STACKIT_DatabaseManager##",
"", "##STACKIT_LoginManager##",
"##STACKIT_SQLAgentManager##",
"##STACKIT_ProcessManager##",
"##STACKIT_ServerManager##",
}, },
Username: utils.Ptr("username"), Username: utils.Ptr("username"),
Host: utils.Ptr("host"), Host: utils.Ptr("host"),
@ -63,9 +66,12 @@ func TestMapDataSourceFields(t *testing.T) {
Roles: types.List( Roles: types.List(
types.SetValueMust( types.SetValueMust(
types.StringType, []attr.Value{ types.StringType, []attr.Value{
types.StringValue("role_1"), types.StringValue("##STACKIT_DatabaseManager##"),
types.StringValue("role_2"), types.StringValue("##STACKIT_LoginManager##"),
types.StringValue(""), types.StringValue("##STACKIT_ProcessManager##"),
types.StringValue("##STACKIT_SQLAgentManager##"),
types.StringValue("##STACKIT_SQLAgentUser##"),
types.StringValue("##STACKIT_ServerManager##"),
}, },
), ),
), ),
@ -138,7 +144,7 @@ func TestMapDataSourceFields(t *testing.T) {
t.Fatalf("Should not have failed: %v", err) t.Fatalf("Should not have failed: %v", err)
} }
if tt.isValid { if tt.isValid {
diff := cmp.Diff(state, &tt.expected) diff := cmp.Diff(&tt.expected, state)
if diff != "" { if diff != "" {
t.Fatalf("Data does not match: %s", diff) t.Fatalf("Data does not match: %s", diff)
} }
@ -183,8 +189,8 @@ func TestMapFieldsCreate(t *testing.T) {
&sqlserverflexalpha.CreateUserResponse{ &sqlserverflexalpha.CreateUserResponse{
Id: utils.Ptr(int64(2)), Id: utils.Ptr(int64(2)),
Roles: &[]string{ Roles: &[]string{
"role_1",
"role_2", "role_2",
"role_1",
"", "",
}, },
Username: utils.Ptr("username"), Username: utils.Ptr("username"),
@ -204,9 +210,9 @@ func TestMapFieldsCreate(t *testing.T) {
Roles: types.List( Roles: types.List(
types.SetValueMust( types.SetValueMust(
types.StringType, []attr.Value{ types.StringType, []attr.Value{
types.StringValue(""),
types.StringValue("role_1"), types.StringValue("role_1"),
types.StringValue("role_2"), types.StringValue("role_2"),
types.StringValue(""),
}, },
), ),
), ),
@ -292,7 +298,7 @@ func TestMapFieldsCreate(t *testing.T) {
t.Fatalf("Should not have failed: %v", err) t.Fatalf("Should not have failed: %v", err)
} }
if tt.isValid { if tt.isValid {
diff := cmp.Diff(state, &tt.expected) diff := cmp.Diff(&tt.expected, state)
if diff != "" { if diff != "" {
t.Fatalf("Data does not match: %s", diff) t.Fatalf("Data does not match: %s", diff)
} }
@ -332,8 +338,8 @@ func TestMapFields(t *testing.T) {
"simple_values", "simple_values",
&sqlserverflexalpha.GetUserResponse{ &sqlserverflexalpha.GetUserResponse{
Roles: &[]string{ Roles: &[]string{
"role_1",
"role_2", "role_2",
"role_1",
"", "",
}, },
Username: utils.Ptr("username"), Username: utils.Ptr("username"),
@ -350,9 +356,9 @@ func TestMapFields(t *testing.T) {
Roles: types.List( Roles: types.List(
types.SetValueMust( types.SetValueMust(
types.StringType, []attr.Value{ types.StringType, []attr.Value{
types.StringValue(""),
types.StringValue("role_1"), types.StringValue("role_1"),
types.StringValue("role_2"), types.StringValue("role_2"),
types.StringValue(""),
}, },
), ),
), ),
@ -423,7 +429,7 @@ func TestMapFields(t *testing.T) {
t.Fatalf("Should not have failed: %v", err) t.Fatalf("Should not have failed: %v", err)
} }
if tt.isValid { if tt.isValid {
diff := cmp.Diff(state, &tt.expected) diff := cmp.Diff(&tt.expected, state)
if diff != "" { if diff != "" {
t.Fatalf("Data does not match: %s", diff) t.Fatalf("Data does not match: %s", diff)
} }
@ -516,7 +522,7 @@ func TestToCreatePayload(t *testing.T) {
t.Fatalf("Should not have failed: %v", err) t.Fatalf("Should not have failed: %v", err)
} }
if tt.isValid { if tt.isValid {
diff := cmp.Diff(output, tt.expected) diff := cmp.Diff(tt.expected, output)
if diff != "" { if diff != "" {
t.Fatalf("Data does not match: %s", diff) t.Fatalf("Data does not match: %s", diff)
} }

View file

@ -2,7 +2,6 @@ package sqlserverflexbeta
import ( import (
"fmt" "fmt"
"strings"
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
@ -34,7 +33,7 @@ func mapFields(source *sqlserverflexbeta.GetDatabaseResponse, model *dataSourceM
model.Id = types.Int64Value(databaseId) model.Id = types.Int64Value(databaseId)
model.DatabaseName = types.StringValue(source.GetName()) model.DatabaseName = types.StringValue(source.GetName())
model.Name = types.StringValue(source.GetName()) model.Name = types.StringValue(source.GetName())
model.Owner = types.StringValue(strings.Trim(source.GetOwner(), "\"")) model.Owner = types.StringValue(source.GetOwner())
model.Region = types.StringValue(region) model.Region = types.StringValue(region)
model.ProjectId = types.StringValue(model.ProjectId.ValueString()) model.ProjectId = types.StringValue(model.ProjectId.ValueString())
model.InstanceId = types.StringValue(model.InstanceId.ValueString()) model.InstanceId = types.StringValue(model.InstanceId.ValueString())
@ -75,7 +74,7 @@ func mapResourceFields(source *sqlserverflexbeta.GetDatabaseResponse, model *res
model.Id = types.Int64Value(databaseId) model.Id = types.Int64Value(databaseId)
model.DatabaseName = types.StringValue(source.GetName()) model.DatabaseName = types.StringValue(source.GetName())
model.Name = types.StringValue(source.GetName()) model.Name = types.StringValue(source.GetName())
model.Owner = types.StringValue(strings.Trim(source.GetOwner(), "\"")) model.Owner = types.StringValue(source.GetOwner())
model.Region = types.StringValue(region) model.Region = types.StringValue(region)
model.ProjectId = types.StringValue(model.ProjectId.ValueString()) model.ProjectId = types.StringValue(model.ProjectId.ValueString())
model.InstanceId = types.StringValue(model.InstanceId.ValueString()) model.InstanceId = types.StringValue(model.InstanceId.ValueString())

View file

@ -247,7 +247,6 @@ func (r *databaseResource) Create(ctx context.Context, req resource.CreateReques
return return
} }
// TODO: is this necessary to wait for the database-> API say 200 ?
waitResp, err := wait.CreateDatabaseWaitHandler( waitResp, err := wait.CreateDatabaseWaitHandler(
ctx, ctx,
r.client, r.client,
@ -310,19 +309,8 @@ func (r *databaseResource) Create(ctx context.Context, req resource.CreateReques
return return
} }
database, err := r.client.GetDatabaseRequest(ctx, projectId, region, instanceId, databaseName).Execute()
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error creating database",
fmt.Sprintf("Getting database details after creation: %v", err),
)
return
}
// Map response body to schema // Map response body to schema
err = mapResourceFields(database, &data, region) err = mapResourceFields(waitResp, &data, region)
if err != nil { if err != nil {
core.LogAndAddError( core.LogAndAddError(
ctx, ctx,
@ -361,15 +349,10 @@ func (r *databaseResource) Read(ctx context.Context, req resource.ReadRequest, r
ctx = core.InitProviderContext(ctx) ctx = core.InitProviderContext(ctx)
projectId, region, instanceId, databaseName, errExt := r.extractIdentityData(model, identityData) projectId := model.ProjectId.ValueString()
if errExt != nil { region := model.Region.ValueString()
core.LogAndAddError( instanceId := model.InstanceId.ValueString()
ctx, databaseName := model.DatabaseName.ValueString()
&resp.Diagnostics,
extractErrorSummary,
fmt.Sprintf(extractErrorMessage, errExt),
)
}
ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId) ctx = tflog.SetField(ctx, "instance_id", instanceId)
@ -548,7 +531,7 @@ func (r *databaseResource) ImportState(
ctx, &resp.Diagnostics, ctx, &resp.Diagnostics,
"Error importing database", "Error importing database",
fmt.Sprintf( fmt.Sprintf(
"Expected import identifier with format: [project_id],[region],[instance_id],[database_name] Got: %q", "Expected import identifier with format [project_id],[region],[instance_id],[database_name] Got: %q",
req.ID, req.ID,
), ),
) )

View file

@ -293,13 +293,6 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r
return return
} }
// Read identity data
var identityData InstanceResourceIdentityModel
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
if resp.Diagnostics.HasError() {
return
}
ctx = core.InitProviderContext(ctx) ctx = core.InitProviderContext(ctx)
projectId := data.ProjectId.ValueString() projectId := data.ProjectId.ValueString()
@ -523,7 +516,7 @@ func (r *instanceResource) ImportState(
ctx, &resp.Diagnostics, ctx, &resp.Diagnostics,
"Error importing instance", "Error importing instance",
fmt.Sprintf( fmt.Sprintf(
"Expected import identifier with format: [project_id],[region],[instance_id] Got: %q", "Expected import identifier with format [project_id],[region],[instance_id] Got: %q",
req.ID, req.ID,
), ),
) )

View file

@ -15,7 +15,7 @@ import (
"github.com/stackitcloud/stackit-sdk-go/core/config" "github.com/stackitcloud/stackit-sdk-go/core/config"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/internal/testutils" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/internal/testutils"
sqlserverflexbeta2 "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexbeta" sqlserverflexbetaResGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexbeta"
sqlserverflexbeta "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexbeta/instance" sqlserverflexbeta "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexbeta/instance"
// The fwresource import alias is so there is no collision // The fwresource import alias is so there is no collision
@ -36,7 +36,7 @@ func init() {
F: func(region string) error { F: func(region string) error {
ctx := context.Background() ctx := context.Background()
apiClientConfigOptions := []config.ConfigurationOption{} apiClientConfigOptions := []config.ConfigurationOption{}
apiClient, err := sqlserverflexbeta2.NewAPIClient(apiClientConfigOptions...) apiClient, err := sqlserverflexbetaResGen.NewAPIClient(apiClientConfigOptions...)
if err != nil { if err != nil {
log.Fatalln(err) log.Fatalln(err)
} }
@ -228,6 +228,67 @@ func TestAccInstance(t *testing.T) {
}) })
} }
func TestAccInstanceReApply(t *testing.T) {
exData := getExample()
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
t.Logf(" ... working on instance %s", exData.TfName)
testInstances = append(testInstances, exData.TfName)
},
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Create and verify
{
Config: testutils.StringFromTemplateMust(
"testdata/instance_template.gompl",
exData,
),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resName("instance", exData.TfName), "name", exData.Name),
resource.TestCheckResourceAttrSet(resName("instance", exData.TfName), "id"),
// TODO: check all fields
),
},
// Create and verify
{
Config: testutils.StringFromTemplateMust(
"testdata/instance_template.gompl",
exData,
),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resName("instance", exData.TfName), "name", exData.Name),
resource.TestCheckResourceAttrSet(resName("instance", exData.TfName), "id"),
// TODO: check all fields
),
},
{
RefreshState: true,
},
// Create and verify
{
Config: testutils.StringFromTemplateMust(
"testdata/instance_template.gompl",
exData,
),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resName("instance", exData.TfName), "name", exData.Name),
resource.TestCheckResourceAttrSet(resName("instance", exData.TfName), "id"),
// TODO: check all fields
),
},
// Import test
{
ResourceName: resName("instance", exData.TfName),
ImportStateKind: resource.ImportBlockWithResourceIdentity,
ImportState: true,
// ImportStateVerify is not supported with plannable import blocks
// ImportStateVerify: true,
},
},
})
}
func TestAccInstanceNoEncryption(t *testing.T) { func TestAccInstanceNoEncryption(t *testing.T) {
data := getExample() data := getExample()
@ -241,9 +302,9 @@ func TestAccInstanceNoEncryption(t *testing.T) {
"##STACKIT_DatabaseManager##", "##STACKIT_DatabaseManager##",
"##STACKIT_LoginManager##", "##STACKIT_LoginManager##",
"##STACKIT_ProcessManager##", "##STACKIT_ProcessManager##",
"##STACKIT_ServerManager##",
"##STACKIT_SQLAgentManager##", "##STACKIT_SQLAgentManager##",
"##STACKIT_SQLAgentUser##", "##STACKIT_SQLAgentUser##",
"##STACKIT_ServerManager##",
}, },
}, },
} }
@ -341,7 +402,7 @@ func TestAccInstanceEncryption(t *testing.T) {
{ {
Name: userName, Name: userName,
ProjectId: os.Getenv("TF_ACC_PROJECT_ID"), ProjectId: os.Getenv("TF_ACC_PROJECT_ID"),
Roles: []string{"##STACKIT_LoginManager##", "##STACKIT_DatabaseManager##"}, Roles: []string{"##STACKIT_DatabaseManager##", "##STACKIT_LoginManager##"},
}, },
} }
data.Databases = []Database{ data.Databases = []Database{

View file

@ -46,7 +46,9 @@ func mapDataSourceFields(userResp *sqlserverflexbeta.GetUserResponse, model *dat
model.Roles = types.List(types.SetNull(types.StringType)) model.Roles = types.List(types.SetNull(types.StringType))
} else { } else {
var roles []attr.Value var roles []attr.Value
for _, role := range *user.Roles { resRoles := *user.Roles
slices.Sort(resRoles)
for _, role := range resRoles {
roles = append(roles, types.StringValue(string(role))) roles = append(roles, types.StringValue(string(role)))
} }
rolesSet, diags := types.SetValue(types.StringType, roles) rolesSet, diags := types.SetValue(types.StringType, roles)
@ -92,8 +94,8 @@ func mapFields(userResp *sqlserverflexbeta.GetUserResponse, model *resourceModel
model.Username = types.StringPointerValue(user.Username) model.Username = types.StringPointerValue(user.Username)
// Map roles // Map roles
if user.Roles != nil { if userResp.Roles != nil {
resRoles := *user.Roles resRoles := *userResp.Roles
slices.Sort(resRoles) slices.Sort(resRoles)
var roles []attr.Value var roles []attr.Value
@ -101,11 +103,11 @@ func mapFields(userResp *sqlserverflexbeta.GetUserResponse, model *resourceModel
roles = append(roles, types.StringValue(string(role))) roles = append(roles, types.StringValue(string(role)))
} }
rolesSet, diags := types.SetValue(types.StringType, roles) rolesSet, diags := types.ListValue(types.StringType, roles)
if diags.HasError() { if diags.HasError() {
return fmt.Errorf("failed to map roles: %w", core.DiagsToError(diags)) return fmt.Errorf("failed to map roles: %w", core.DiagsToError(diags))
} }
model.Roles = types.List(rolesSet) model.Roles = rolesSet
} }
// Ensure roles is not null // Ensure roles is not null
@ -151,11 +153,11 @@ func mapFieldsCreate(userResp *sqlserverflexbeta.CreateUserResponse, model *reso
for _, role := range resRoles { for _, role := range resRoles {
roles = append(roles, types.StringValue(string(role))) roles = append(roles, types.StringValue(string(role)))
} }
rolesSet, diags := types.SetValue(types.StringType, roles) rolesList, diags := types.ListValue(types.StringType, roles)
if diags.HasError() { if diags.HasError() {
return fmt.Errorf("failed to map roles: %w", core.DiagsToError(diags)) return fmt.Errorf("failed to map roles: %w", core.DiagsToError(diags))
} }
model.Roles = types.List(rolesSet) model.Roles = rolesList
} }
if model.Roles.IsNull() || model.Roles.IsUnknown() { if model.Roles.IsNull() || model.Roles.IsUnknown() {
@ -183,9 +185,14 @@ func toCreatePayload(
return nil, fmt.Errorf("nil model") return nil, fmt.Errorf("nil model")
} }
return &sqlserverflexbeta.CreateUserRequestPayload{ pl := sqlserverflexbeta.CreateUserRequestPayload{
Username: conversion.StringValueToPointer(model.Username), Username: conversion.StringValueToPointer(model.Username),
DefaultDatabase: conversion.StringValueToPointer(model.DefaultDatabase), Roles: &roles,
Roles: &roles, }
}, nil slices.Sort(roles)
if !model.DefaultDatabase.IsNull() || !model.DefaultDatabase.IsUnknown() {
pl.DefaultDatabase = conversion.StringValueToPointer(model.DefaultDatabase)
}
return &pl, nil
} }

View file

@ -63,9 +63,9 @@ func TestMapDataSourceFields(t *testing.T) {
Roles: types.List( Roles: types.List(
types.SetValueMust( types.SetValueMust(
types.StringType, []attr.Value{ types.StringType, []attr.Value{
types.StringValue(""),
types.StringValue("role_1"), types.StringValue("role_1"),
types.StringValue("role_2"), types.StringValue("role_2"),
types.StringValue(""),
}, },
), ),
), ),
@ -138,7 +138,7 @@ func TestMapDataSourceFields(t *testing.T) {
t.Fatalf("Should not have failed: %v", err) t.Fatalf("Should not have failed: %v", err)
} }
if tt.isValid { if tt.isValid {
diff := cmp.Diff(state, &tt.expected) diff := cmp.Diff(&tt.expected, state)
if diff != "" { if diff != "" {
t.Fatalf("Data does not match: %s", diff) t.Fatalf("Data does not match: %s", diff)
} }
@ -204,9 +204,9 @@ func TestMapFieldsCreate(t *testing.T) {
Roles: types.List( Roles: types.List(
types.SetValueMust( types.SetValueMust(
types.StringType, []attr.Value{ types.StringType, []attr.Value{
types.StringValue(""),
types.StringValue("role_1"), types.StringValue("role_1"),
types.StringValue("role_2"), types.StringValue("role_2"),
types.StringValue(""),
}, },
), ),
), ),
@ -292,7 +292,7 @@ func TestMapFieldsCreate(t *testing.T) {
t.Fatalf("Should not have failed: %v", err) t.Fatalf("Should not have failed: %v", err)
} }
if tt.isValid { if tt.isValid {
diff := cmp.Diff(state, &tt.expected) diff := cmp.Diff(&tt.expected, state)
if diff != "" { if diff != "" {
t.Fatalf("Data does not match: %s", diff) t.Fatalf("Data does not match: %s", diff)
} }
@ -332,8 +332,8 @@ func TestMapFields(t *testing.T) {
"simple_values", "simple_values",
&sqlserverflexbeta.GetUserResponse{ &sqlserverflexbeta.GetUserResponse{
Roles: &[]string{ Roles: &[]string{
"role_1",
"role_2", "role_2",
"role_1",
"", "",
}, },
Username: utils.Ptr("username"), Username: utils.Ptr("username"),
@ -350,9 +350,9 @@ func TestMapFields(t *testing.T) {
Roles: types.List( Roles: types.List(
types.SetValueMust( types.SetValueMust(
types.StringType, []attr.Value{ types.StringType, []attr.Value{
types.StringValue(""),
types.StringValue("role_1"), types.StringValue("role_1"),
types.StringValue("role_2"), types.StringValue("role_2"),
types.StringValue(""),
}, },
), ),
), ),
@ -423,7 +423,7 @@ func TestMapFields(t *testing.T) {
t.Fatalf("Should not have failed: %v", err) t.Fatalf("Should not have failed: %v", err)
} }
if tt.isValid { if tt.isValid {
diff := cmp.Diff(state, &tt.expected) diff := cmp.Diff(&tt.expected, state)
if diff != "" { if diff != "" {
t.Fatalf("Data does not match: %s", diff) t.Fatalf("Data does not match: %s", diff)
} }

View file

@ -2,6 +2,7 @@ fields:
- name: 'id' - name: 'id'
modifiers: modifiers:
- 'UseStateForUnknown' - 'UseStateForUnknown'
- 'RequiresReplace'
- name: 'instance_id' - name: 'instance_id'
validators: validators:
@ -22,6 +23,7 @@ fields:
- name: 'region' - name: 'region'
modifiers: modifiers:
- 'RequiresReplace' - 'RequiresReplace'
- 'RequiresReplace'
- name: 'user_id' - name: 'user_id'
modifiers: modifiers:
@ -31,10 +33,12 @@ fields:
- name: 'username' - name: 'username'
modifiers: modifiers:
- 'UseStateForUnknown' - 'UseStateForUnknown'
- 'RequiresReplace'
- name: 'roles' - name: 'roles'
modifiers: modifiers:
- 'UseStateForUnknown' - 'UseStateForUnknown'
- 'RequiresReplace'
- name: 'password' - name: 'password'
modifiers: modifiers:

View file

@ -107,6 +107,26 @@ func (r *userResource) ModifyPlan(
return return
} }
//// TODO: verify if this is needed - START
//var planRoles []string
//diags := planModel.Roles.ElementsAs(ctx, &planRoles, false)
//resp.Diagnostics.Append(diags...)
//if diags.HasError() {
// return
//}
//slices.Sort(planRoles)
//var roles []attr.Value
//for _, role := range planRoles {
// roles = append(roles, types.StringValue(string(role)))
//}
//rolesSet, diags := types.ListValue(types.StringType, roles)
//resp.Diagnostics.Append(diags...)
//if diags.HasError() {
// return
//}
//planModel.Roles = rolesSet
//// TODO: verify if this is needed - END
resp.Diagnostics.Append(resp.Plan.Set(ctx, planModel)...) resp.Diagnostics.Append(resp.Plan.Set(ctx, planModel)...)
if resp.Diagnostics.HasError() { if resp.Diagnostics.HasError() {
return return

View file

@ -208,7 +208,7 @@ func PartialUpdateInstanceWaitHandler(
case InstanceStateUnknown: case InstanceStateUnknown:
return false, nil, nil return false, nil, nil
case InstanceStateFailed: case InstanceStateFailed:
return true, s, fmt.Errorf("update failed for instance with id %s", instanceId) return true, s, fmt.Errorf("update got status FAILURE for instance with id %s", instanceId)
} }
}, },
) )

View file

@ -161,8 +161,10 @@ func CreateInstanceWaitHandler(
return false, nil, nil return false, nil, nil
} }
return true, s, nil return true, s, nil
case strings.ToLower(InstanceStateUnknown), strings.ToLower(InstanceStateFailed): case strings.ToLower(InstanceStateUnknown):
return true, nil, fmt.Errorf("create failed for instance with id %s", instanceId) return true, nil, fmt.Errorf("create failed for instance %s with status %s", instanceId, InstanceStateUnknown)
case strings.ToLower(InstanceStateFailed):
return true, nil, fmt.Errorf("create failed for instance %s with status %s", instanceId, InstanceStateFailed)
case strings.ToLower(InstanceStatePending), strings.ToLower(InstanceStateProcessing): case strings.ToLower(InstanceStatePending), strings.ToLower(InstanceStateProcessing):
tflog.Info( tflog.Info(
ctx, "request is being handled", map[string]interface{}{ ctx, "request is being handled", map[string]interface{}{

View file

@ -489,17 +489,18 @@ func (p *Provider) Configure(ctx context.Context, req provider.ConfigureRequest,
return return
} }
roundTripper := core.NewRetryRoundTripper( //roundTripper := core.NewRetryRoundTripper(
baseRoundTripper, // baseRoundTripper,
maxRetries, // maxRetries,
initialDelay, // initialDelay,
maxDelay, // maxDelay,
perTryTimeout, // perTryTimeout,
) //)
// Make round tripper and custom endpoints available during DataSource and Resource // Make round tripper and custom endpoints available during DataSource and Resource
// type Configure methods. // type Configure methods.
providerData.RoundTripper = roundTripper // providerData.RoundTripper = roundTripper
providerData.RoundTripper = baseRoundTripper
resp.DataSourceData = providerData resp.DataSourceData = providerData
resp.ResourceData = providerData resp.ResourceData = providerData