chore: work save

This commit is contained in:
Marcel S. Henselin 2026-01-27 16:43:53 +01:00
parent a60b1db1f4
commit 3e3f13d36d
7 changed files with 214 additions and 46 deletions

View file

@ -38,6 +38,26 @@ func setGetInstanceResponseGetBackupScheduleAttributeType(arg *GetInstanceRespon
type GetInstanceResponseGetBackupScheduleArgType = string type GetInstanceResponseGetBackupScheduleArgType = string
type GetInstanceResponseGetBackupScheduleRetType = string type GetInstanceResponseGetBackupScheduleRetType = string
/*
types and functions for encryption
*/
// isModel
type GetInstanceResponseGetEncryptionAttributeType = *InstanceEncryption
type GetInstanceResponseGetEncryptionArgType = InstanceEncryption
type GetInstanceResponseGetEncryptionRetType = InstanceEncryption
func getGetInstanceResponseGetEncryptionAttributeTypeOk(arg GetInstanceResponseGetEncryptionAttributeType) (ret GetInstanceResponseGetEncryptionRetType, ok bool) {
if arg == nil {
return ret, false
}
return *arg, true
}
func setGetInstanceResponseGetEncryptionAttributeType(arg *GetInstanceResponseGetEncryptionAttributeType, val GetInstanceResponseGetEncryptionRetType) {
*arg = &val
}
/* /*
types and functions for flavorId types and functions for flavorId
*/ */
@ -247,6 +267,7 @@ type GetInstanceResponse struct {
// The schedule for on what time and how often the database backup will be created. The schedule is written as a cron schedule. // The schedule for on what time and how often the database backup will be created. The schedule is written as a cron schedule.
// REQUIRED // REQUIRED
BackupSchedule GetInstanceResponseGetBackupScheduleAttributeType `json:"backupSchedule" required:"true"` BackupSchedule GetInstanceResponseGetBackupScheduleAttributeType `json:"backupSchedule" required:"true"`
Encryption GetInstanceResponseGetEncryptionAttributeType `json:"encryption,omitempty"`
// The id of the instance flavor. // The id of the instance flavor.
// REQUIRED // REQUIRED
FlavorId GetInstanceResponseGetFlavorIdAttributeType `json:"flavorId" required:"true"` FlavorId GetInstanceResponseGetFlavorIdAttributeType `json:"flavorId" required:"true"`
@ -323,6 +344,29 @@ func (o *GetInstanceResponse) SetBackupSchedule(v GetInstanceResponseGetBackupSc
setGetInstanceResponseGetBackupScheduleAttributeType(&o.BackupSchedule, v) setGetInstanceResponseGetBackupScheduleAttributeType(&o.BackupSchedule, v)
} }
// GetEncryption returns the Encryption field value if set, zero value otherwise.
func (o *GetInstanceResponse) GetEncryption() (res GetInstanceResponseGetEncryptionRetType) {
res, _ = o.GetEncryptionOk()
return
}
// GetEncryptionOk returns a tuple with the Encryption field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *GetInstanceResponse) GetEncryptionOk() (ret GetInstanceResponseGetEncryptionRetType, ok bool) {
return getGetInstanceResponseGetEncryptionAttributeTypeOk(o.Encryption)
}
// HasEncryption returns a boolean if a field has been set.
func (o *GetInstanceResponse) HasEncryption() bool {
_, ok := o.GetEncryptionOk()
return ok
}
// SetEncryption gets a reference to the given InstanceEncryption and assigns it to the Encryption field.
func (o *GetInstanceResponse) SetEncryption(v GetInstanceResponseGetEncryptionRetType) {
setGetInstanceResponseGetEncryptionAttributeType(&o.Encryption, v)
}
// GetFlavorId returns the FlavorId field value // GetFlavorId returns the FlavorId field value
func (o *GetInstanceResponse) GetFlavorId() (ret GetInstanceResponseGetFlavorIdRetType) { func (o *GetInstanceResponse) GetFlavorId() (ret GetInstanceResponseGetFlavorIdRetType) {
ret, _ = o.GetFlavorIdOk() ret, _ = o.GetFlavorIdOk()

View file

@ -62,6 +62,7 @@ resource "stackitprivatepreview_sqlserverflexalpha_instance" "sqlsrv-nosna" {
#keyring_id = stackit_kms_keyring.keyring.keyring_id #keyring_id = stackit_kms_keyring.keyring.keyring_id
#key_version = 1 #key_version = 1
#key_id = var.key_id #key_id = var.key_id
# key with scope public
key_id = "fe039bcf-8d7b-431a-801d-9e81371a6b7b" key_id = "fe039bcf-8d7b-431a-801d-9e81371a6b7b"
keyring_id = var.keyring_id keyring_id = var.keyring_id
key_version = var.key_version key_version = var.key_version

View file

@ -14,7 +14,9 @@ usage() {
CONFIG_FOLDER=$(dirname "$0") CONFIG_FOLDER=$(dirname "$0")
BINARY=terraform BINARY=terraform
while getopts ":b:hdit" arg; do ADD=""
while getopts ":b:hdirt" arg; do
case $arg in case $arg in
b) # Set binary (default is terraform). b) # Set binary (default is terraform).
BINARY=${OPTARG} BINARY=${OPTARG}
@ -30,6 +32,10 @@ while getopts ":b:hdit" arg; do
export TF_LOG export TF_LOG
shift shift
;; ;;
r) # Set log level to INFO.
ADD="-refresh-only"
shift
;;
t) # Set log level to TRACE. t) # Set log level to TRACE.
TF_LOG=TRACE TF_LOG=TRACE
export TF_LOG export TF_LOG
@ -44,4 +50,4 @@ done
TERRAFORM_CONFIG=${CONFIG_FOLDER}/config.tfrc TERRAFORM_CONFIG=${CONFIG_FOLDER}/config.tfrc
export TERRAFORM_CONFIG export TERRAFORM_CONFIG
${BINARY} "$@" ${BINARY} "$@" ${ADD}

View file

@ -7,6 +7,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes" "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-log/tflog"
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha" postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
postgresflexalphadatasource "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/instance/datasources_gen" postgresflexalphadatasource "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/instance/datasources_gen"
postgresflexalpharesource "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/instance/resources_gen" postgresflexalpharesource "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/instance/resources_gen"
@ -14,40 +15,61 @@ import (
) )
func mapGetInstanceResponseToModel(ctx context.Context, m *postgresflexalpharesource.InstanceModel, resp *postgresflex.GetInstanceResponse) error { func mapGetInstanceResponseToModel(ctx context.Context, m *postgresflexalpharesource.InstanceModel, resp *postgresflex.GetInstanceResponse) error {
tflog.Info(ctx, ">>>> MSH DEBUG <<<<", map[string]interface{}{
"id": m.Id.ValueString(),
"instance_id": m.InstanceId.ValueString(),
"backup_schedule": m.BackupSchedule.ValueString(),
"flavor_id": m.FlavorId.ValueString(),
"encryption.kek_key_id": m.Encryption.KekKeyId.ValueString(),
"encryption.kek_key_ring_id": m.Encryption.KekKeyRingId.ValueString(),
"encryption.kek_key_version": m.Encryption.KekKeyVersion.ValueString(),
"encryption.service_account": m.Encryption.ServiceAccount.ValueString(),
"is_deletable": m.IsDeletable.ValueBool(),
"name": m.Name.ValueString(),
"status": m.Status.ValueString(),
"retention_days": m.RetentionDays.ValueInt64(),
"replicas": m.Replicas.ValueInt64(),
"network.instance_address": m.Network.InstanceAddress.ValueString(),
"network.router_address": m.Network.RouterAddress.ValueString(),
"version": m.Version.ValueString(),
"network.acl": m.Network.Acl.String(),
})
m.BackupSchedule = types.StringValue(resp.GetBackupSchedule()) m.BackupSchedule = types.StringValue(resp.GetBackupSchedule())
// need to leave out encryption, as the GetInstance endpoint does not provide it if resp.HasEncryption() {
// m.Encryption = postgresflexalpharesource.NewEncryptionValueMust( m.Encryption = postgresflexalpharesource.NewEncryptionValueMust(
// m.Encryption.AttributeTypes(ctx), m.Encryption.AttributeTypes(ctx),
// map[string]attr.Value{ map[string]attr.Value{
// "kek_key_id": types.StringValue(resp.Encryption.GetKekKeyId()), "kek_key_id": types.StringValue(resp.Encryption.GetKekKeyId()),
// "kek_key_ring_id": types.StringValue(resp.Encryption.GetKekKeyRingId()), "kek_key_ring_id": types.StringValue(resp.Encryption.GetKekKeyRingId()),
// "kek_key_version": types.StringValue(resp.Encryption.GetKekKeyVersion()), "kek_key_version": types.StringValue(resp.Encryption.GetKekKeyVersion()),
// "service_account": types.StringValue(resp.Encryption.GetServiceAccount()), "service_account": types.StringValue(resp.Encryption.GetServiceAccount()),
// }, },
// ) )
}
m.FlavorId = types.StringValue(resp.GetFlavorId()) m.FlavorId = types.StringValue(resp.GetFlavorId())
if m.Id.IsNull() || m.Id.IsUnknown() { if m.Id.IsNull() || m.Id.IsUnknown() {
m.Id = utils.BuildInternalTerraformId(m.ProjectId.ValueString(), m.Region.ValueString(), m.InstanceId.ValueString()) m.Id = utils.BuildInternalTerraformId(m.ProjectId.ValueString(), m.Region.ValueString(), m.InstanceId.ValueString())
} }
m.InstanceId = types.StringPointerValue(resp.Id) m.InstanceId = types.StringPointerValue(resp.Id)
m.IsDeletable = types.BoolUnknown() //m.IsDeletable = types.BoolUnknown()
if isDel, ok := resp.GetIsDeletableOk(); ok { //if isDel, ok := resp.GetIsDeletableOk(); ok {
m.IsDeletable = types.BoolValue(isDel) // m.IsDeletable = types.BoolValue(isDel)
} m.IsDeletable = types.BoolValue(resp.GetIsDeletable())
m.Name = types.StringValue(resp.GetName()) //}
netAcl, diags := types.ListValueFrom(ctx, types.StringType, resp.Network.GetAcl()) netAcl, diags := types.ListValueFrom(ctx, types.StringType, resp.Network.GetAcl())
if diags.HasError() { if diags.HasError() {
return fmt.Errorf("failed converting network acl from response") return fmt.Errorf("failed converting network acl from response")
} }
netInstAdd := types.StringUnknown() netInstAdd := types.StringValue("")
if instAdd, ok := resp.Network.GetInstanceAddressOk(); ok { if instAdd, ok := resp.Network.GetInstanceAddressOk(); ok {
netInstAdd = types.StringValue(instAdd) netInstAdd = types.StringValue(instAdd)
} }
netRtrAdd := types.StringUnknown() netRtrAdd := types.StringValue("")
if rtrAdd, ok := resp.Network.GetRouterAddressOk(); ok { if rtrAdd, ok := resp.Network.GetRouterAddressOk(); ok {
netRtrAdd = types.StringValue(rtrAdd) netRtrAdd = types.StringValue(rtrAdd)
} }
@ -71,10 +93,11 @@ func mapGetInstanceResponseToModel(ctx context.Context, m *postgresflexalphareso
m.Name = types.StringValue(resp.GetName()) m.Name = types.StringValue(resp.GetName())
m.Status = types.StringUnknown() //m.Status = types.StringUnknown()
if status, ok := resp.GetStatusOk(); ok { //if status, ok := resp.GetStatusOk(); ok {
m.Status = types.StringValue(string(status)) // m.Status = types.StringValue(string(status))
} //}
m.Status = types.StringValue(string(resp.GetStatus()))
storage, diags := postgresflexalpharesource.NewStorageValue( storage, diags := postgresflexalpharesource.NewStorageValue(
postgresflexalpharesource.StorageValue{}.AttributeTypes(ctx), postgresflexalpharesource.StorageValue{}.AttributeTypes(ctx),

View file

@ -2,14 +2,111 @@ fields:
- name: 'backup_schedule' - name: 'backup_schedule'
modifiers: modifiers:
- 'UseStateForUnknown' - 'UseStateForUnknown'
- 'RequiresReplace'
- name: 'encryption.kek_key_id' - name: 'encryption.kek_key_id'
validators:
- validate.NoSeparator
modifiers: modifiers:
- 'RequiresReplace' - 'RequiresReplace'
- name: 'encryption.kek_key_ring_id'
validators:
- validate.NoSeparator
modifiers:
- 'RequiresReplace'
- name: 'encryption.kek_key_version'
validators:
- validate.NoSeparator
modifiers:
- 'RequiresReplace'
- name: 'encryption.service_account'
validators:
- validate.NoSeparator
modifiers:
- 'RequiresReplace'
- name: 'flavor_id'
modifiers:
- 'UseStateForUnknown'
- 'RequiresReplace'
- name: 'id'
modifiers:
- 'UseStateForUnknown'
- name: 'instance_id'
validators:
- validate.NoSeparator
- validate.UUID
modifiers:
- 'UseStateForUnknown'
- name: 'is_deletable'
modifiers:
- 'UseStateForUnknown'
- name: 'name'
modifiers:
- 'UseStateForUnknown'
- name: 'network.access_scope'
validators:
- validate.NoSeparator
modifiers:
- 'UseStateForUnknown'
- 'RequiresReplace'
- name: 'network.acl' - name: 'network.acl'
modifiers: modifiers:
- 'UseStateForUnknown' - 'UseStateForUnknown'
- name: 'network.access_scope'
- name: 'network.instance_address'
modifiers:
- 'UseStateForUnknown'
- name: 'network.router_address'
modifiers:
- 'UseStateForUnknown'
- name: 'project_id'
validators:
- validate.NoSeparator
- validate.UUID
modifiers: modifiers:
- 'UseStateForUnknown' - 'UseStateForUnknown'
- 'RequiresReplace' - 'RequiresReplace'
- name: 'region'
modifiers:
- 'RequiresReplace'
- name: 'replicas'
modifiers:
- 'UseStateForUnknown'
- name: 'retention_days'
modifiers:
- 'UseStateForUnknown'
- name: 'status'
modifiers:
- 'UseStateForUnknown'
- name: 'storage'
modifiers:
- 'UseStateForUnknown'
- name: 'storage.performance_class'
modifiers:
- 'UseStateForUnknown'
- 'RequiresReplace'
- name: 'storage.size'
modifiers:
- 'UseStateForUnknown'
- name: 'version'
modifiers:
- 'UseStateForUnknown'

View file

@ -23,6 +23,8 @@ import (
"github.com/stackitcloud/stackit-sdk-go/core/oapierror" "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
) )
const packageName = "postgresflexalpha"
// Ensure the implementation satisfies the expected interfaces. // Ensure the implementation satisfies the expected interfaces.
var ( var (
_ resource.Resource = &instanceResource{} _ resource.Resource = &instanceResource{}
@ -212,13 +214,6 @@ func (r *instanceResource) Create(
return return
} }
model.InstanceId = types.StringValue(instanceId)
model.Id = utils.BuildInternalTerraformId(projectId, region, instanceId)
resp.Diagnostics.Append(resp.State.Set(ctx, &model)...)
if resp.Diagnostics.HasError() {
return
}
// Set data returned by API in identity // Set data returned by API in identity
identity := InstanceResourceIdentityModel{ identity := InstanceResourceIdentityModel{
ProjectID: types.StringValue(projectId), ProjectID: types.StringValue(projectId),
@ -301,6 +296,7 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r
ctx = core.InitProviderContext(ctx) ctx = core.InitProviderContext(ctx)
// projectId := model.ProjectId.ValueString()
// region := r.providerData.GetRegionWithOverride(model.Region) // region := r.providerData.GetRegionWithOverride(model.Region)
// instanceId := model.InstanceId.ValueString() // instanceId := model.InstanceId.ValueString()
@ -334,14 +330,9 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r
core.LogAndAddError(ctx, &resp.Diagnostics, functionErrorSummary, "instance_id not found in config") core.LogAndAddError(ctx, &resp.Diagnostics, functionErrorSummary, "instance_id not found in config")
return return
} }
region = identityData.Region.ValueString() instanceId = identityData.InstanceID.ValueString()
} }
tflog.Info(ctx, "Reading instance model", map[string]interface{}{
"projectId": projectId,
"region": region,
"instanceId": instanceId,
})
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)
ctx = tflog.SetField(ctx, "region", region) ctx = tflog.SetField(ctx, "region", region)
@ -453,7 +444,7 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques
} }
replInt32 := int32(model.Replicas.ValueInt64()) // nolint:gosec // check is performed above replInt32 := int32(model.Replicas.ValueInt64()) // nolint:gosec // check is performed above
payload := postgresflex.UpdateInstancePartiallyRequestPayload{ payload := postgresflex.UpdateInstanceRequestPayload{
BackupSchedule: model.BackupSchedule.ValueStringPointer(), BackupSchedule: model.BackupSchedule.ValueStringPointer(),
FlavorId: model.FlavorId.ValueStringPointer(), FlavorId: model.FlavorId.ValueStringPointer(),
Name: model.Name.ValueStringPointer(), Name: model.Name.ValueStringPointer(),
@ -463,7 +454,7 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques
), ),
Acl: &netAcl, Acl: &netAcl,
}, },
Replicas: postgresflex.UpdateInstancePartiallyRequestPayloadGetReplicasAttributeType(&replInt32), Replicas: postgresflex.UpdateInstanceRequestPayloadGetReplicasAttributeType(&replInt32),
RetentionDays: model.RetentionDays.ValueInt64Pointer(), RetentionDays: model.RetentionDays.ValueInt64Pointer(),
Storage: &postgresflex.StorageUpdate{ Storage: &postgresflex.StorageUpdate{
Size: model.Storage.Size.ValueInt64Pointer(), Size: model.Storage.Size.ValueInt64Pointer(),
@ -472,12 +463,12 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques
} }
// Update existing instance // Update existing instance
err := r.client.UpdateInstancePartiallyRequest( err := r.client.UpdateInstanceRequest(
ctx, ctx,
projectId, projectId,
region, region,
instanceId, instanceId,
).UpdateInstancePartiallyRequestPayload(payload).Execute() ).UpdateInstanceRequestPayload(payload).Execute()
if err != nil { if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", err.Error()) core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", err.Error())
return return
@ -549,8 +540,6 @@ func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteReques
// ImportState imports a resource into the Terraform state on success. // ImportState imports a resource into the Terraform state on success.
// The expected format of the resource import identifier is: project_id,region,instance_id // The expected format of the resource import identifier is: project_id,region,instance_id
func (r *instanceResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { func (r *instanceResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
tflog.Debug(ctx, "ImportState called with id:", map[string]interface{}{"id": req.ID})
ctx = core.InitProviderContext(ctx) ctx = core.InitProviderContext(ctx)
if req.ID != "" { if req.ID != "" {

View file

@ -24,7 +24,7 @@ const (
InstanceStateFailed = "Failed" InstanceStateFailed = "Failed"
) )
// Interface needed for tests // APIClientInstanceInterface Interface needed for tests
type APIClientInstanceInterface interface { type APIClientInstanceInterface interface {
GetInstanceRequestExecute(ctx context.Context, projectId, region, instanceId string) (*sqlserverflex.GetInstanceResponse, error) GetInstanceRequestExecute(ctx context.Context, projectId, region, instanceId string) (*sqlserverflex.GetInstanceResponse, error)
} }
@ -53,6 +53,10 @@ func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface
case strings.ToLower(InstanceStateUnknown), strings.ToLower(InstanceStateFailed): case strings.ToLower(InstanceStateUnknown), strings.ToLower(InstanceStateFailed):
return true, s, fmt.Errorf("create failed for instance with id %s", instanceId) return true, s, fmt.Errorf("create failed for instance with id %s", instanceId)
default: default:
tflog.Info(ctx, "Wait (create) received unknown status", map[string]interface{}{
"instanceId": instanceId,
"status": s.Status,
})
return false, s, nil return false, s, nil
} }
}) })
@ -77,10 +81,14 @@ func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface
case strings.ToLower(InstanceStateUnknown), strings.ToLower(InstanceStateFailed): case strings.ToLower(InstanceStateUnknown), strings.ToLower(InstanceStateFailed):
return true, s, fmt.Errorf("update failed for instance with id %s", instanceId) return true, s, fmt.Errorf("update failed for instance with id %s", instanceId)
default: default:
tflog.Info(ctx, "Wait (update) received unknown status", map[string]interface{}{
"instanceId": instanceId,
"status": s.Status,
})
return false, s, nil return false, s, nil
} }
}) })
handler.SetSleepBeforeWait(2 * time.Second) handler.SetSleepBeforeWait(15 * time.Second)
handler.SetTimeout(45 * time.Minute) handler.SetTimeout(45 * time.Minute)
return handler return handler
} }