diff --git a/pkg/postgresflexalpha/model_get_instance_response.go b/pkg/postgresflexalpha/model_get_instance_response.go index c07e9d71..8d4a16a4 100644 --- a/pkg/postgresflexalpha/model_get_instance_response.go +++ b/pkg/postgresflexalpha/model_get_instance_response.go @@ -38,6 +38,26 @@ func setGetInstanceResponseGetBackupScheduleAttributeType(arg *GetInstanceRespon type GetInstanceResponseGetBackupScheduleArgType = 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 */ @@ -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. // REQUIRED BackupSchedule GetInstanceResponseGetBackupScheduleAttributeType `json:"backupSchedule" required:"true"` + Encryption GetInstanceResponseGetEncryptionAttributeType `json:"encryption,omitempty"` // The id of the instance flavor. // REQUIRED FlavorId GetInstanceResponseGetFlavorIdAttributeType `json:"flavorId" required:"true"` @@ -323,6 +344,29 @@ func (o *GetInstanceResponse) SetBackupSchedule(v GetInstanceResponseGetBackupSc 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 func (o *GetInstanceResponse) GetFlavorId() (ret GetInstanceResponseGetFlavorIdRetType) { ret, _ = o.GetFlavorIdOk() diff --git a/sample/sqlserver/sqlserver.tf b/sample/sqlserver/sqlserver.tf index 004dab43..923bb423 100644 --- a/sample/sqlserver/sqlserver.tf +++ b/sample/sqlserver/sqlserver.tf @@ -62,6 +62,7 @@ resource "stackitprivatepreview_sqlserverflexalpha_instance" "sqlsrv-nosna" { #keyring_id = stackit_kms_keyring.keyring.keyring_id #key_version = 1 #key_id = var.key_id + # key with scope public key_id = "fe039bcf-8d7b-431a-801d-9e81371a6b7b" keyring_id = var.keyring_id key_version = var.key_version diff --git a/sample/tf.sh b/sample/tf.sh index 2a760db2..8feaf799 100755 --- a/sample/tf.sh +++ b/sample/tf.sh @@ -14,7 +14,9 @@ usage() { CONFIG_FOLDER=$(dirname "$0") BINARY=terraform -while getopts ":b:hdit" arg; do +ADD="" + +while getopts ":b:hdirt" arg; do case $arg in b) # Set binary (default is terraform). BINARY=${OPTARG} @@ -30,6 +32,10 @@ while getopts ":b:hdit" arg; do export TF_LOG shift ;; + r) # Set log level to INFO. + ADD="-refresh-only" + shift + ;; t) # Set log level to TRACE. TF_LOG=TRACE export TF_LOG @@ -44,4 +50,4 @@ done TERRAFORM_CONFIG=${CONFIG_FOLDER}/config.tfrc export TERRAFORM_CONFIG -${BINARY} "$@" +${BINARY} "$@" ${ADD} diff --git a/stackit/internal/services/postgresflexalpha/instance/functions.go b/stackit/internal/services/postgresflexalpha/instance/functions.go index d690d4bb..6fa4ad07 100644 --- a/stackit/internal/services/postgresflexalpha/instance/functions.go +++ b/stackit/internal/services/postgresflexalpha/instance/functions.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-log/tflog" postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha" 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" @@ -14,40 +15,61 @@ import ( ) 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()) - // need to leave out encryption, as the GetInstance endpoint does not provide it - // m.Encryption = postgresflexalpharesource.NewEncryptionValueMust( - // m.Encryption.AttributeTypes(ctx), - // map[string]attr.Value{ - // "kek_key_id": types.StringValue(resp.Encryption.GetKekKeyId()), - // "kek_key_ring_id": types.StringValue(resp.Encryption.GetKekKeyRingId()), - // "kek_key_version": types.StringValue(resp.Encryption.GetKekKeyVersion()), - // "service_account": types.StringValue(resp.Encryption.GetServiceAccount()), - // }, - // ) + if resp.HasEncryption() { + m.Encryption = postgresflexalpharesource.NewEncryptionValueMust( + m.Encryption.AttributeTypes(ctx), + map[string]attr.Value{ + "kek_key_id": types.StringValue(resp.Encryption.GetKekKeyId()), + "kek_key_ring_id": types.StringValue(resp.Encryption.GetKekKeyRingId()), + "kek_key_version": types.StringValue(resp.Encryption.GetKekKeyVersion()), + "service_account": types.StringValue(resp.Encryption.GetServiceAccount()), + }, + ) + } m.FlavorId = types.StringValue(resp.GetFlavorId()) if m.Id.IsNull() || m.Id.IsUnknown() { m.Id = utils.BuildInternalTerraformId(m.ProjectId.ValueString(), m.Region.ValueString(), m.InstanceId.ValueString()) } m.InstanceId = types.StringPointerValue(resp.Id) - m.IsDeletable = types.BoolUnknown() - if isDel, ok := resp.GetIsDeletableOk(); ok { - m.IsDeletable = types.BoolValue(isDel) - } - m.Name = types.StringValue(resp.GetName()) + //m.IsDeletable = types.BoolUnknown() + //if isDel, ok := resp.GetIsDeletableOk(); ok { + // m.IsDeletable = types.BoolValue(isDel) + m.IsDeletable = types.BoolValue(resp.GetIsDeletable()) + //} netAcl, diags := types.ListValueFrom(ctx, types.StringType, resp.Network.GetAcl()) if diags.HasError() { return fmt.Errorf("failed converting network acl from response") } - netInstAdd := types.StringUnknown() + netInstAdd := types.StringValue("") if instAdd, ok := resp.Network.GetInstanceAddressOk(); ok { netInstAdd = types.StringValue(instAdd) } - netRtrAdd := types.StringUnknown() + netRtrAdd := types.StringValue("") if rtrAdd, ok := resp.Network.GetRouterAddressOk(); ok { netRtrAdd = types.StringValue(rtrAdd) } @@ -71,10 +93,11 @@ func mapGetInstanceResponseToModel(ctx context.Context, m *postgresflexalphareso m.Name = types.StringValue(resp.GetName()) - m.Status = types.StringUnknown() - if status, ok := resp.GetStatusOk(); ok { - m.Status = types.StringValue(string(status)) - } + //m.Status = types.StringUnknown() + //if status, ok := resp.GetStatusOk(); ok { + // m.Status = types.StringValue(string(status)) + //} + m.Status = types.StringValue(string(resp.GetStatus())) storage, diags := postgresflexalpharesource.NewStorageValue( postgresflexalpharesource.StorageValue{}.AttributeTypes(ctx), diff --git a/stackit/internal/services/postgresflexalpha/instance/planModifiers.yaml b/stackit/internal/services/postgresflexalpha/instance/planModifiers.yaml index 3149c39f..4843c9ea 100644 --- a/stackit/internal/services/postgresflexalpha/instance/planModifiers.yaml +++ b/stackit/internal/services/postgresflexalpha/instance/planModifiers.yaml @@ -2,14 +2,111 @@ fields: - name: 'backup_schedule' modifiers: - 'UseStateForUnknown' - - 'RequiresReplace' + - name: 'encryption.kek_key_id' + validators: + - validate.NoSeparator modifiers: - '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' modifiers: - '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: - 'UseStateForUnknown' - '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' diff --git a/stackit/internal/services/postgresflexalpha/instance/resource.go b/stackit/internal/services/postgresflexalpha/instance/resource.go index 8acd2ff9..197c2e2d 100644 --- a/stackit/internal/services/postgresflexalpha/instance/resource.go +++ b/stackit/internal/services/postgresflexalpha/instance/resource.go @@ -23,6 +23,8 @@ import ( "github.com/stackitcloud/stackit-sdk-go/core/oapierror" ) +const packageName = "postgresflexalpha" + // Ensure the implementation satisfies the expected interfaces. var ( _ resource.Resource = &instanceResource{} @@ -212,13 +214,6 @@ func (r *instanceResource) Create( 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 identity := InstanceResourceIdentityModel{ ProjectID: types.StringValue(projectId), @@ -301,6 +296,7 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r ctx = core.InitProviderContext(ctx) + // projectId := model.ProjectId.ValueString() // region := r.providerData.GetRegionWithOverride(model.Region) // 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") 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, "instance_id", instanceId) 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 - payload := postgresflex.UpdateInstancePartiallyRequestPayload{ + payload := postgresflex.UpdateInstanceRequestPayload{ BackupSchedule: model.BackupSchedule.ValueStringPointer(), FlavorId: model.FlavorId.ValueStringPointer(), Name: model.Name.ValueStringPointer(), @@ -463,7 +454,7 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques ), Acl: &netAcl, }, - Replicas: postgresflex.UpdateInstancePartiallyRequestPayloadGetReplicasAttributeType(&replInt32), + Replicas: postgresflex.UpdateInstanceRequestPayloadGetReplicasAttributeType(&replInt32), RetentionDays: model.RetentionDays.ValueInt64Pointer(), Storage: &postgresflex.StorageUpdate{ Size: model.Storage.Size.ValueInt64Pointer(), @@ -472,12 +463,12 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques } // Update existing instance - err := r.client.UpdateInstancePartiallyRequest( + err := r.client.UpdateInstanceRequest( ctx, projectId, region, instanceId, - ).UpdateInstancePartiallyRequestPayload(payload).Execute() + ).UpdateInstanceRequestPayload(payload).Execute() if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", err.Error()) 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. // 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) { - tflog.Debug(ctx, "ImportState called with id:", map[string]interface{}{"id": req.ID}) - ctx = core.InitProviderContext(ctx) if req.ID != "" { diff --git a/stackit/internal/wait/sqlserverflexalpha/wait.go b/stackit/internal/wait/sqlserverflexalpha/wait.go index 3bb76020..cc766e52 100644 --- a/stackit/internal/wait/sqlserverflexalpha/wait.go +++ b/stackit/internal/wait/sqlserverflexalpha/wait.go @@ -24,7 +24,7 @@ const ( InstanceStateFailed = "Failed" ) -// Interface needed for tests +// APIClientInstanceInterface Interface needed for tests type APIClientInstanceInterface interface { 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): return true, s, fmt.Errorf("create failed for instance with id %s", instanceId) default: + tflog.Info(ctx, "Wait (create) received unknown status", map[string]interface{}{ + "instanceId": instanceId, + "status": s.Status, + }) return false, s, nil } }) @@ -77,10 +81,14 @@ func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface case strings.ToLower(InstanceStateUnknown), strings.ToLower(InstanceStateFailed): return true, s, fmt.Errorf("update failed for instance with id %s", instanceId) default: + tflog.Info(ctx, "Wait (update) received unknown status", map[string]interface{}{ + "instanceId": instanceId, + "status": s.Status, + }) return false, s, nil } }) - handler.SetSleepBeforeWait(2 * time.Second) + handler.SetSleepBeforeWait(15 * time.Second) handler.SetTimeout(45 * time.Minute) return handler }