diff --git a/stackit/internal/services/sqlserverflexalpha/instance/functions.go b/stackit/internal/services/sqlserverflexalpha/instance/functions.go index b451eb70..ee7056fc 100644 --- a/stackit/internal/services/sqlserverflexalpha/instance/functions.go +++ b/stackit/internal/services/sqlserverflexalpha/instance/functions.go @@ -175,12 +175,12 @@ func toCreatePayload( storagePayload.Size = conversion.Int64ValueToPointer(storage.Size) } - var encryptionPayload *sqlserverflex.CreateInstanceRequestPayloadGetEncryptionArgType + var encryptionPayload *sqlserverflex.CreateInstanceRequestPayloadGetEncryptionArgType = nil if encryption != nil && - !encryption.KeyId.IsNull() && !encryption.KeyId.IsUnknown() && - !encryption.KeyRingId.IsNull() && !encryption.KeyRingId.IsUnknown() && - !encryption.KeyVersion.IsNull() && !encryption.KeyVersion.IsUnknown() && - !encryption.ServiceAccount.IsNull() && !encryption.ServiceAccount.IsUnknown() { + !encryption.KeyId.IsNull() && !encryption.KeyId.IsUnknown() && encryption.KeyId.ValueString() != "" && + !encryption.KeyRingId.IsNull() && !encryption.KeyRingId.IsUnknown() && encryption.KeyRingId.ValueString() != "" && + !encryption.KeyVersion.IsNull() && !encryption.KeyVersion.IsUnknown() && encryption.KeyVersion.ValueString() != "" && + !encryption.ServiceAccount.IsNull() && !encryption.ServiceAccount.IsUnknown() && encryption.ServiceAccount.ValueString() != "" { encryptionPayload = &sqlserverflex.CreateInstanceRequestPayloadGetEncryptionArgType{ KekKeyId: conversion.StringValueToPointer(encryption.KeyId), KekKeyRingId: conversion.StringValueToPointer(encryption.KeyVersion), diff --git a/stackit/internal/services/sqlserverflexalpha/instance/resource.go b/stackit/internal/services/sqlserverflexalpha/instance/resource.go index 092805f3..8881a59f 100644 --- a/stackit/internal/services/sqlserverflexalpha/instance/resource.go +++ b/stackit/internal/services/sqlserverflexalpha/instance/resource.go @@ -4,35 +4,26 @@ package sqlserverflex import ( "context" + _ "embed" "fmt" "net/http" - "regexp" "strings" "time" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier" + postgresflexUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils" + sqlserverflexalpha2 "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/instance/resources_gen" sqlserverflexUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils" - "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" "github.com/hashicorp/terraform-plugin-log/tflog" + "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha" "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" - "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/validate" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" - "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha" wait "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/wait/sqlserverflexalpha" "github.com/stackitcloud/stackit-sdk-go/core/oapierror" @@ -181,254 +172,272 @@ func (r *instanceResource) ModifyPlan( } } +//go:embed planModifiers.yaml +var modifiersFileByte []byte + // Schema defines the schema for the resource. -func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { - descriptions := map[string]string{ - "main": "SQLServer Flex ALPHA instance resource schema. Must have a `region` specified in the provider configuration.", - "id": "Terraform's internal resource ID. It is structured as \"`project_id`,`region`,`instance_id`\".", - "instance_id": "ID of the SQLServer Flex instance.", - "project_id": "STACKIT project ID to which the instance is associated.", - "name": "Instance name.", - "access_scope": "The access scope of the instance. (SNA | PUBLIC)", - "flavor_id": "The flavor ID of the instance.", - "acl": "The Access Control List (ACL) for the SQLServer Flex instance.", - "backup_schedule": `The backup schedule. Should follow the cron scheduling system format (e.g. "0 0 * * *")`, - "region": "The resource region. If not defined, the provider region is used.", - "encryption": "The encryption block.", - "replicas": "The number of replicas of the SQLServer Flex instance.", - "network": "The network block.", - "keyring_id": "STACKIT KMS - KeyRing ID of the encryption key to use.", - "key_id": "STACKIT KMS - Key ID of the encryption key to use.", - "key_version": "STACKIT KMS - Key version to use in the encryption key.", - "service:account": "STACKIT KMS - service account to use in the encryption key.", - "instance_address": "The returned instance IP address of the SQLServer Flex instance.", - "router_address": "The returned router IP address of the SQLServer Flex instance.", +func (r *instanceResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + //descriptions := map[string]string{ + // "main": "SQLServer Flex ALPHA instance resource schema. Must have a `region` specified in the provider configuration.", + // "id": "Terraform's internal resource ID. It is structured as \"`project_id`,`region`,`instance_id`\".", + // "instance_id": "ID of the SQLServer Flex instance.", + // "project_id": "STACKIT project ID to which the instance is associated.", + // "name": "Instance name.", + // "access_scope": "The access scope of the instance. (SNA | PUBLIC)", + // "flavor_id": "The flavor ID of the instance.", + // "acl": "The Access Control List (ACL) for the SQLServer Flex instance.", + // "backup_schedule": `The backup schedule. Should follow the cron scheduling system format (e.g. "0 0 * * *")`, + // "region": "The resource region. If not defined, the provider region is used.", + // "encryption": "The encryption block.", + // "replicas": "The number of replicas of the SQLServer Flex instance.", + // "network": "The network block.", + // "keyring_id": "STACKIT KMS - KeyRing ID of the encryption key to use.", + // "key_id": "STACKIT KMS - Key ID of the encryption key to use.", + // "key_version": "STACKIT KMS - Key version to use in the encryption key.", + // "service:account": "STACKIT KMS - service account to use in the encryption key.", + // "instance_address": "The returned instance IP address of the SQLServer Flex instance.", + // "router_address": "The returned router IP address of the SQLServer Flex instance.", + //} + + schema := sqlserverflexalpha2.InstanceResourceSchema(ctx) + + fields, err := postgresflexUtils.ReadModifiersConfig(modifiersFileByte) + if err != nil { + resp.Diagnostics.AddError("error during read modifiers config file", err.Error()) + return } - resp.Schema = schema.Schema{ - Description: descriptions["main"], - Attributes: map[string]schema.Attribute{ - "id": schema.StringAttribute{ - Description: descriptions["id"], - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - }, - "instance_id": schema.StringAttribute{ - Description: descriptions["instance_id"], - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{ - validate.UUID(), - validate.NoSeparator(), - }, - }, - "project_id": schema.StringAttribute{ - Description: descriptions["project_id"], - Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, - Validators: []validator.String{ - validate.UUID(), - validate.NoSeparator(), - }, - }, - "name": schema.StringAttribute{ - Description: descriptions["name"], - Required: true, - Validators: []validator.String{ - stringvalidator.LengthAtLeast(1), - stringvalidator.RegexMatches( - regexp.MustCompile("^[a-z]([-a-z0-9]*[a-z0-9])?$"), - "must start with a letter, must have lower case letters, numbers or hyphens, and no hyphen at the end", - ), - }, - }, - "backup_schedule": schema.StringAttribute{ - Description: descriptions["backup_schedule"], - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - }, - "is_deletable": schema.BoolAttribute{ - Description: descriptions["is_deletable"], - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.Bool{ - boolplanmodifier.UseStateForUnknown(), - }, - }, - "flavor_id": schema.StringAttribute{ - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - stringplanmodifier.UseStateForUnknown(), - }, - Required: true, - }, - "replicas": schema.Int64Attribute{ - Computed: true, - PlanModifiers: []planmodifier.Int64{ - int64planmodifier.UseStateForUnknown(), - }, - }, - "storage": schema.SingleNestedAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.Object{ - objectplanmodifier.UseStateForUnknown(), - }, - Attributes: map[string]schema.Attribute{ - "class": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - stringplanmodifier.UseStateForUnknown(), - }, - }, - "size": schema.Int64Attribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.Int64{ - int64planmodifier.UseStateForUnknown(), - }, - }, - }, - }, - "version": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - stringplanmodifier.UseStateForUnknown(), - }, - }, - "edition": schema.StringAttribute{ - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - stringplanmodifier.UseStateForUnknown(), - }, - }, - "retention_days": schema.Int64Attribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.Int64{ - int64planmodifier.UseStateForUnknown(), - }, - }, - "region": schema.StringAttribute{ - Optional: true, - // must be computed to allow for storing the override value from the provider - Computed: true, - Description: descriptions["region"], - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, - }, - "status": schema.StringAttribute{ - Optional: true, - // must be computed to allow for storing the override value from the provider - Computed: true, - Description: descriptions["status"], - }, - "encryption": schema.SingleNestedAttribute{ - Optional: true, - PlanModifiers: []planmodifier.Object{ - objectplanmodifier.RequiresReplace(), - objectplanmodifier.UseStateForUnknown(), - }, - Attributes: map[string]schema.Attribute{ - "key_id": schema.StringAttribute{ - Description: descriptions["key_id"], - Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, - Validators: []validator.String{ - validate.NoSeparator(), - }, - }, - "key_version": schema.StringAttribute{ - Description: descriptions["key_version"], - Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, - Validators: []validator.String{ - validate.NoSeparator(), - }, - }, - "keyring_id": schema.StringAttribute{ - Description: descriptions["keyring_id"], - Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, - Validators: []validator.String{ - validate.NoSeparator(), - }, - }, - "service_account": schema.StringAttribute{ - Description: descriptions["service_account"], - Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, - Validators: []validator.String{ - validate.NoSeparator(), - }, - }, - }, - Description: descriptions["encryption"], - }, - "network": schema.SingleNestedAttribute{ - Required: true, - Attributes: map[string]schema.Attribute{ - "access_scope": schema.StringAttribute{ - Description: descriptions["access_scope"], - Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{ - validate.NoSeparator(), - }, - }, - "acl": schema.ListAttribute{ - Description: descriptions["acl"], - ElementType: types.StringType, - Required: true, - PlanModifiers: []planmodifier.List{ - listplanmodifier.UseStateForUnknown(), - }, - }, - "instance_address": schema.StringAttribute{ - Description: descriptions["instance_address"], - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - }, - "router_address": schema.StringAttribute{ - Description: descriptions["router_address"], - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - }, - }, - Description: descriptions["network"], - }, - }, + err = postgresflexUtils.AddPlanModifiersToResourceSchema(fields, &schema) + if err != nil { + resp.Diagnostics.AddError("error adding plan modifiers", err.Error()) + return } + resp.Schema = schema + + //resp.Schema = schema.Schema{ + // Description: descriptions["main"], + // Attributes: map[string]schema.Attribute{ + // "id": schema.StringAttribute{ + // Description: descriptions["id"], + // Computed: true, + // PlanModifiers: []planmodifier.String{ + // stringplanmodifier.UseStateForUnknown(), + // }, + // }, + // "instance_id": schema.StringAttribute{ + // Description: descriptions["instance_id"], + // Computed: true, + // PlanModifiers: []planmodifier.String{ + // stringplanmodifier.UseStateForUnknown(), + // }, + // Validators: []validator.String{ + // validate.UUID(), + // validate.NoSeparator(), + // }, + // }, + // "project_id": schema.StringAttribute{ + // Description: descriptions["project_id"], + // Required: true, + // PlanModifiers: []planmodifier.String{ + // stringplanmodifier.RequiresReplace(), + // }, + // Validators: []validator.String{ + // validate.UUID(), + // validate.NoSeparator(), + // }, + // }, + // "name": schema.StringAttribute{ + // Description: descriptions["name"], + // Required: true, + // Validators: []validator.String{ + // stringvalidator.LengthAtLeast(1), + // stringvalidator.RegexMatches( + // regexp.MustCompile("^[a-z]([-a-z0-9]*[a-z0-9])?$"), + // "must start with a letter, must have lower case letters, numbers or hyphens, and no hyphen at the end", + // ), + // }, + // }, + // "backup_schedule": schema.StringAttribute{ + // Description: descriptions["backup_schedule"], + // Optional: true, + // Computed: true, + // PlanModifiers: []planmodifier.String{ + // stringplanmodifier.UseStateForUnknown(), + // }, + // }, + // "is_deletable": schema.BoolAttribute{ + // Description: descriptions["is_deletable"], + // Optional: true, + // Computed: true, + // PlanModifiers: []planmodifier.Bool{ + // boolplanmodifier.UseStateForUnknown(), + // }, + // }, + // "flavor_id": schema.StringAttribute{ + // PlanModifiers: []planmodifier.String{ + // stringplanmodifier.RequiresReplace(), + // stringplanmodifier.UseStateForUnknown(), + // }, + // Required: true, + // }, + // "replicas": schema.Int64Attribute{ + // Computed: true, + // PlanModifiers: []planmodifier.Int64{ + // int64planmodifier.UseStateForUnknown(), + // }, + // }, + // "storage": schema.SingleNestedAttribute{ + // Optional: true, + // Computed: true, + // PlanModifiers: []planmodifier.Object{ + // objectplanmodifier.UseStateForUnknown(), + // }, + // Attributes: map[string]schema.Attribute{ + // "class": schema.StringAttribute{ + // Optional: true, + // Computed: true, + // PlanModifiers: []planmodifier.String{ + // stringplanmodifier.RequiresReplace(), + // stringplanmodifier.UseStateForUnknown(), + // }, + // }, + // "size": schema.Int64Attribute{ + // Optional: true, + // Computed: true, + // PlanModifiers: []planmodifier.Int64{ + // int64planmodifier.UseStateForUnknown(), + // }, + // }, + // }, + // }, + // "version": schema.StringAttribute{ + // Optional: true, + // Computed: true, + // PlanModifiers: []planmodifier.String{ + // stringplanmodifier.RequiresReplace(), + // stringplanmodifier.UseStateForUnknown(), + // }, + // }, + // "edition": schema.StringAttribute{ + // Computed: true, + // PlanModifiers: []planmodifier.String{ + // stringplanmodifier.RequiresReplace(), + // stringplanmodifier.UseStateForUnknown(), + // }, + // }, + // "retention_days": schema.Int64Attribute{ + // Optional: true, + // Computed: true, + // PlanModifiers: []planmodifier.Int64{ + // int64planmodifier.UseStateForUnknown(), + // }, + // }, + // "region": schema.StringAttribute{ + // Optional: true, + // // must be computed to allow for storing the override value from the provider + // Computed: true, + // Description: descriptions["region"], + // PlanModifiers: []planmodifier.String{ + // stringplanmodifier.RequiresReplace(), + // }, + // }, + // "status": schema.StringAttribute{ + // Optional: true, + // // must be computed to allow for storing the override value from the provider + // Computed: true, + // Description: descriptions["status"], + // }, + // "encryption": schema.SingleNestedAttribute{ + // Optional: true, + // PlanModifiers: []planmodifier.Object{ + // objectplanmodifier.RequiresReplace(), + // objectplanmodifier.UseStateForUnknown(), + // }, + // Attributes: map[string]schema.Attribute{ + // "key_id": schema.StringAttribute{ + // Description: descriptions["key_id"], + // Required: true, + // PlanModifiers: []planmodifier.String{ + // stringplanmodifier.RequiresReplace(), + // }, + // Validators: []validator.String{ + // validate.NoSeparator(), + // }, + // }, + // "key_version": schema.StringAttribute{ + // Description: descriptions["key_version"], + // Required: true, + // PlanModifiers: []planmodifier.String{ + // stringplanmodifier.RequiresReplace(), + // }, + // Validators: []validator.String{ + // validate.NoSeparator(), + // }, + // }, + // "keyring_id": schema.StringAttribute{ + // Description: descriptions["keyring_id"], + // Required: true, + // PlanModifiers: []planmodifier.String{ + // stringplanmodifier.RequiresReplace(), + // }, + // Validators: []validator.String{ + // validate.NoSeparator(), + // }, + // }, + // "service_account": schema.StringAttribute{ + // Description: descriptions["service_account"], + // Required: true, + // PlanModifiers: []planmodifier.String{ + // stringplanmodifier.RequiresReplace(), + // }, + // Validators: []validator.String{ + // validate.NoSeparator(), + // }, + // }, + // }, + // Description: descriptions["encryption"], + // }, + // "network": schema.SingleNestedAttribute{ + // Required: true, + // Attributes: map[string]schema.Attribute{ + // "access_scope": schema.StringAttribute{ + // Description: descriptions["access_scope"], + // Required: true, + // PlanModifiers: []planmodifier.String{ + // stringplanmodifier.RequiresReplace(), + // stringplanmodifier.UseStateForUnknown(), + // }, + // Validators: []validator.String{ + // validate.NoSeparator(), + // }, + // }, + // "acl": schema.ListAttribute{ + // Description: descriptions["acl"], + // ElementType: types.StringType, + // Required: true, + // PlanModifiers: []planmodifier.List{ + // listplanmodifier.UseStateForUnknown(), + // }, + // }, + // "instance_address": schema.StringAttribute{ + // Description: descriptions["instance_address"], + // Computed: true, + // PlanModifiers: []planmodifier.String{ + // stringplanmodifier.UseStateForUnknown(), + // }, + // }, + // "router_address": schema.StringAttribute{ + // Description: descriptions["router_address"], + // Computed: true, + // PlanModifiers: []planmodifier.String{ + // stringplanmodifier.UseStateForUnknown(), + // }, + // }, + // }, + // Description: descriptions["network"], + // }, + // }, + //} } // Create creates the resource and sets the initial Terraform state. @@ -437,7 +446,7 @@ func (r *instanceResource) Create( req resource.CreateRequest, resp *resource.CreateResponse, ) { // nolint:gocritic // function signature required by Terraform - var model Model + var model sqlserverflexalpha2.InstanceModel diags := req.Plan.Get(ctx, &model) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -560,11 +569,6 @@ func (r *instanceResource) Create( return } - // After the instance creation, database might not be ready to accept connections immediately. - // That is why we add a sleep - // TODO - can get removed? - time.Sleep(120 * time.Second) - tflog.Info(ctx, "SQLServer Flex instance created") }