fix: remove identity from postgres-flex

[skip ci]
This commit is contained in:
Marcel S. Henselin 2026-03-16 15:45:53 +01:00
parent c1f463935b
commit ba579760d5
20 changed files with 570 additions and 581 deletions

View file

@ -5,6 +5,7 @@ import (
"fmt"
"net/http"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/stackit-sdk-go/services/postgresflex/v3alpha1api"
@ -72,7 +73,13 @@ func (r *instanceDataSource) Configure(
// Schema defines the schema for the data source.
func (r *instanceDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = postgresflexalpha2.InstanceDataSourceSchema(ctx)
sch := postgresflexalpha2.InstanceDataSourceSchema(ctx)
sch.Attributes["id"] = schema.StringAttribute{
Computed: true,
Description: "internal ID",
MarkdownDescription: "internal ID",
}
resp.Schema = sch
}
// Read refreshes the Terraform state with the latest data.
@ -90,22 +97,22 @@ func (r *instanceDataSource) Read(
ctx = core.InitProviderContext(ctx)
projectId := model.ProjectId.ValueString()
instanceId := model.InstanceId.ValueString()
projectID := model.ProjectId.ValueString()
instanceID := model.InstanceId.ValueString()
region := r.providerData.GetRegionWithOverride(model.Region)
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId)
ctx = tflog.SetField(ctx, "project_id", projectID)
ctx = tflog.SetField(ctx, "instance_id", instanceID)
ctx = tflog.SetField(ctx, "region", region)
instanceResp, err := r.client.DefaultAPI.GetInstanceRequest(ctx, projectId, region, instanceId).Execute()
instanceResp, err := r.client.DefaultAPI.GetInstanceRequest(ctx, projectID, region, instanceID).Execute()
if err != nil {
utils.LogError(
ctx,
&resp.Diagnostics,
err,
"Reading instance",
fmt.Sprintf("Instance with ID %q does not exist in project %q.", instanceId, projectId),
fmt.Sprintf("Instance with ID %q does not exist in project %q.", instanceID, projectID),
map[int]string{
http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectID),
},
)
resp.State.RemoveResource(ctx)

View file

@ -155,7 +155,7 @@ func InstanceDataSourceSchema(ctx context.Context) schema.Schema {
MarkdownDescription: "The STACKIT project ID.",
},
"region": schema.StringAttribute{
Required: true,
Optional: true,
Description: "The region which should be addressed",
MarkdownDescription: "The region which should be addressed",
Validators: []validator.String{

View file

@ -66,12 +66,12 @@ func mapGetInstanceResponseToModel(
m.IsDeletable = types.BoolValue(resp.GetIsDeletable())
netAcl, diags := types.ListValueFrom(ctx, types.StringType, resp.Network.GetAcl())
netACL, diags := types.ListValueFrom(ctx, types.StringType, resp.Network.GetAcl())
if diags.HasError() {
return fmt.Errorf("failed converting network acl from response")
}
m.Acl = netAcl
m.Acl = netACL
netInstAdd := types.StringValue("")
if instAdd, ok := resp.Network.GetInstanceAddressOk(); ok {
@ -87,7 +87,7 @@ func mapGetInstanceResponseToModel(
postgresflexalpharesource.NetworkValue{}.AttributeTypes(ctx),
map[string]attr.Value{
"access_scope": basetypes.NewStringValue(string(resp.Network.GetAccessScope())),
"acl": netAcl,
"acl": netACL,
"instance_address": netInstAdd,
"router_address": netRtrAdd,
},
@ -130,7 +130,8 @@ func mapGetDataInstanceResponseToModel(
handleConnectionInfo(ctx, m, resp)
m.FlavorId = types.StringValue(resp.GetFlavorId())
m.Id = utils.BuildInternalTerraformId(m.ProjectId.ValueString(), m.Region.ValueString(), m.InstanceId.ValueString())
m.Id = types.StringValue(resp.Id)
m.TerraformID = utils.BuildInternalTerraformId(m.ProjectId.ValueString(), m.Region.ValueString(), m.InstanceId.ValueString())
m.InstanceId = types.StringValue(resp.Id)
m.IsDeletable = types.BoolValue(resp.GetIsDeletable())
m.Name = types.StringValue(resp.GetName())
@ -212,14 +213,14 @@ func handleNetwork(ctx context.Context, m *dataSourceModel, resp *postgresflex.G
}
func handleEncryption(m *dataSourceModel, resp *postgresflex.GetInstanceResponse) {
keyId := ""
if keyIdVal, ok := resp.Encryption.GetKekKeyIdOk(); ok {
keyId = *keyIdVal
keyID := ""
if keyIDVal, ok := resp.Encryption.GetKekKeyIdOk(); ok {
keyID = *keyIDVal
}
keyRingId := ""
if keyRingIdVal, ok := resp.Encryption.GetKekKeyRingIdOk(); ok {
keyRingId = *keyRingIdVal
keyRingID := ""
if keyRingIDVal, ok := resp.Encryption.GetKekKeyRingIdOk(); ok {
keyRingID = *keyRingIDVal
}
keyVersion := ""
@ -233,8 +234,8 @@ func handleEncryption(m *dataSourceModel, resp *postgresflex.GetInstanceResponse
}
m.Encryption = postgresflexalphadatasource.EncryptionValue{
KekKeyId: types.StringValue(keyId),
KekKeyRingId: types.StringValue(keyRingId),
KekKeyId: types.StringValue(keyID),
KekKeyRingId: types.StringValue(keyRingID),
KekKeyVersion: types.StringValue(keyVersion),
ServiceAccount: types.StringValue(svcAcc),
}

View file

@ -11,8 +11,6 @@ import (
"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"
coreUtils "github.com/stackitcloud/stackit-sdk-go/core/utils"
@ -33,7 +31,6 @@ var (
_ resource.ResourceWithImportState = &instanceResource{}
_ resource.ResourceWithModifyPlan = &instanceResource{}
_ resource.ResourceWithValidateConfig = &instanceResource{}
_ resource.ResourceWithIdentity = &instanceResource{}
)
// NewInstanceResource is a helper function to simplify the provider implementation.
@ -41,15 +38,6 @@ func NewInstanceResource() resource.Resource {
return &instanceResource{}
}
// resourceModel describes the resource data model.
type resourceModel = postgresflexalpha.InstanceModel
type InstanceResourceIdentityModel struct {
ProjectID types.String `tfsdk:"project_id"`
Region types.String `tfsdk:"region"`
InstanceID types.String `tfsdk:"instance_id"`
}
// instanceResource is the resource implementation.
type instanceResource struct {
client *v3alpha1api.APIClient
@ -61,7 +49,7 @@ func (r *instanceResource) ValidateConfig(
req resource.ValidateConfigRequest,
resp *resource.ValidateConfigResponse,
) {
var data resourceModel
var data postgresflexalpha.InstanceModel
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
@ -85,7 +73,7 @@ func (r *instanceResource) ModifyPlan(
req resource.ModifyPlanRequest,
resp *resource.ModifyPlanResponse,
) { // nolint:gocritic // function signature required by Terraform
var configModel resourceModel
var configModel postgresflexalpha.InstanceModel
// skip initial empty configuration to avoid follow-up errors
if req.Config.Raw.IsNull() {
return
@ -95,7 +83,7 @@ func (r *instanceResource) ModifyPlan(
return
}
var planModel resourceModel
var planModel postgresflexalpha.InstanceModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &planModel)...)
if resp.Diagnostics.HasError() {
return
@ -161,33 +149,13 @@ func (r *instanceResource) Schema(ctx context.Context, _ resource.SchemaRequest,
resp.Schema = schema
}
func (r *instanceResource) IdentitySchema(
_ context.Context,
_ resource.IdentitySchemaRequest,
resp *resource.IdentitySchemaResponse,
) {
resp.IdentitySchema = identityschema.Schema{
Attributes: map[string]identityschema.Attribute{
"project_id": identityschema.StringAttribute{
RequiredForImport: true, // must be set during import by the practitioner
},
"region": identityschema.StringAttribute{
RequiredForImport: true, // can be defaulted by the provider configuration
},
"instance_id": identityschema.StringAttribute{
RequiredForImport: true, // can be defaulted by the provider configuration
},
},
}
}
// Create creates the resource and sets the initial Terraform state.
func (r *instanceResource) Create(
ctx context.Context,
req resource.CreateRequest,
resp *resource.CreateResponse,
) { // nolint:gocritic // function signature required by Terraform
var model resourceModel
var model postgresflexalpha.InstanceModel
diags := req.Plan.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
@ -202,15 +170,15 @@ func (r *instanceResource) Create(
ctx = tflog.SetField(ctx, "project_id", projectID)
ctx = tflog.SetField(ctx, "region", region)
var netAcl []string
diag := model.Network.Acl.ElementsAs(ctx, &netAcl, false)
var netACL []string
diag := model.Network.Acl.ElementsAs(ctx, &netACL, false)
resp.Diagnostics.Append(diags...)
if diag.HasError() {
return
}
replVal := model.Replicas.ValueInt64() // nolint:gosec // check is performed above
payload := modelToCreateInstancePayload(netAcl, model, replVal)
payload := modelToCreateInstancePayload(netACL, model, replVal)
// Create new instance
createResp, err := r.client.DefaultAPI.CreateInstanceRequest(
@ -230,16 +198,14 @@ func (r *instanceResource) Create(
return
}
// Set data returned by API in identity
identity := InstanceResourceIdentityModel{
ProjectID: types.StringValue(projectID),
Region: types.StringValue(region),
InstanceID: types.StringPointerValue(instanceID),
}
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
if resp.Diagnostics.HasError() {
return
}
// Set data returned by API in id
resp.Diagnostics.Append(
resp.State.SetAttribute(
ctx,
path.Root("id"),
utils.BuildInternalTerraformId(projectID, region, *instanceID),
)...,
)
waitResp, err := wait.CreateInstanceWaitHandler(ctx, r.client.DefaultAPI, projectID, region, *instanceID).
SetTimeout(30 * time.Minute).
@ -317,7 +283,7 @@ func (r *instanceResource) Read(
) { // nolint:gocritic // function signature required by Terraform
functionErrorSummary := "read instance failed"
var model resourceModel
var model postgresflexalpha.InstanceModel
diags := req.State.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
@ -326,9 +292,9 @@ func (r *instanceResource) Read(
ctx = core.InitProviderContext(ctx)
var projectId string
var projectID string
if !model.ProjectId.IsNull() && !model.ProjectId.IsUnknown() {
projectId = model.ProjectId.ValueString()
projectID = model.ProjectId.ValueString()
}
var region string
@ -336,16 +302,16 @@ func (r *instanceResource) Read(
region = r.providerData.GetRegionWithOverride(model.Region)
}
var instanceId string
var instanceID string
if !model.InstanceId.IsNull() && !model.InstanceId.IsUnknown() {
instanceId = model.InstanceId.ValueString()
instanceID = model.InstanceId.ValueString()
}
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId)
ctx = tflog.SetField(ctx, "project_id", projectID)
ctx = tflog.SetField(ctx, "instance_id", instanceID)
ctx = tflog.SetField(ctx, "region", region)
instanceResp, err := r.client.DefaultAPI.GetInstanceRequest(ctx, projectId, region, instanceId).Execute()
instanceResp, err := r.client.DefaultAPI.GetInstanceRequest(ctx, projectID, region, instanceID).Execute()
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 {
@ -364,7 +330,7 @@ func (r *instanceResource) Read(
return
}
if !model.InstanceId.IsUnknown() && !model.InstanceId.IsNull() {
if *respInstanceID != instanceId {
if *respInstanceID != instanceID {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
@ -375,6 +341,10 @@ func (r *instanceResource) Read(
}
}
if !model.Id.IsUnknown() && !model.Id.IsNull() {
model.Id = utils.BuildInternalTerraformId(projectID, region, instanceID)
}
err = mapGetInstanceResponseToModel(ctx, &model, instanceResp)
if err != nil {
core.LogAndAddError(
@ -392,17 +362,6 @@ func (r *instanceResource) Read(
return
}
// Set data returned by API in identity
identity := InstanceResourceIdentityModel{
ProjectID: types.StringValue(projectId),
Region: types.StringValue(region),
InstanceID: types.StringValue(instanceId),
}
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
if resp.Diagnostics.HasError() {
return
}
tflog.Info(ctx, "Postgres Flex instance read")
}
@ -412,7 +371,7 @@ func (r *instanceResource) Update(
req resource.UpdateRequest,
resp *resource.UpdateResponse,
) { // nolint:gocritic // function signature required by Terraform
var model resourceModel
var model postgresflexalpha.InstanceModel
diags := req.Plan.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
@ -422,15 +381,8 @@ func (r *instanceResource) Update(
ctx = core.InitProviderContext(ctx)
// Read identity data
var identityData InstanceResourceIdentityModel
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
if resp.Diagnostics.HasError() {
return
}
projectID := identityData.ProjectID.ValueString()
instanceID := identityData.InstanceID.ValueString()
projectID := model.ProjectId.ValueString()
instanceID := model.InstanceId.ValueString()
region := model.Region.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectID)
ctx = tflog.SetField(ctx, "instance_id", instanceID)
@ -532,7 +484,7 @@ func (r *instanceResource) Delete(
req resource.DeleteRequest,
resp *resource.DeleteResponse,
) { // nolint:gocritic // function signature required by Terraform
var model resourceModel
var model postgresflexalpha.InstanceModel
diags := req.State.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
@ -542,15 +494,15 @@ func (r *instanceResource) Delete(
ctx = core.InitProviderContext(ctx)
projectId := model.ProjectId.ValueString()
instanceId := model.InstanceId.ValueString()
projectID := model.ProjectId.ValueString()
instanceID := model.InstanceId.ValueString()
region := model.Region.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId)
ctx = tflog.SetField(ctx, "project_id", projectID)
ctx = tflog.SetField(ctx, "instance_id", instanceID)
ctx = tflog.SetField(ctx, "region", region)
// Delete existing instance
err := r.client.DefaultAPI.DeleteInstanceRequest(ctx, projectId, region, instanceId).Execute()
err := r.client.DefaultAPI.DeleteInstanceRequest(ctx, projectID, region, instanceID).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", fmt.Sprintf("Calling API: %v", err))
return
@ -558,7 +510,7 @@ func (r *instanceResource) Delete(
ctx = core.LogResponse(ctx)
_, err = r.client.DefaultAPI.GetInstanceRequest(ctx, projectId, region, instanceId).Execute()
_, err = r.client.DefaultAPI.GetInstanceRequest(ctx, projectID, region, instanceID).Execute()
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 {
@ -580,41 +532,30 @@ func (r *instanceResource) ImportState(
) {
ctx = core.InitProviderContext(ctx)
if req.ID != "" {
idParts := strings.Split(req.ID, core.Separator)
idParts := strings.Split(req.ID, core.Separator)
if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" {
core.LogAndAddError(
ctx, &resp.Diagnostics,
"Error importing instance",
fmt.Sprintf(
"Expected import identifier with format [project_id],[region],[instance_id] Got: %q",
req.ID,
),
)
return
}
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), idParts[1])...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[2])...)
if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" {
core.LogAndAddError(
ctx, &resp.Diagnostics,
"Error importing instance",
fmt.Sprintf(
"Expected import identifier with format [project_id],[region],[instance_id] Got: %q",
req.ID,
),
)
return
}
// If no ID is provided, attempt to read identity attributes from the import configuration
var identityData InstanceResourceIdentityModel
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
if resp.Diagnostics.HasError() {
return
}
projectId := identityData.ProjectID.ValueString()
region := identityData.Region.ValueString()
instanceId := identityData.InstanceID.ValueString()
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), projectId)...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), region)...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), instanceId)...)
resp.Diagnostics.Append(
resp.State.SetAttribute(
ctx,
path.Root("id"),
utils.BuildInternalTerraformId(idParts...),
)...,
)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), idParts[1])...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[2])...)
tflog.Info(ctx, "Postgres Flex instance state imported")
}