fix: refactor sqlserver to handle encryption correctly (#31)
## 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: #31
Reviewed-by: Andre_Harms <andre.harms@stackit.cloud>
This commit is contained in:
parent
80d1d12278
commit
4549ba63e5
23 changed files with 4203 additions and 1413 deletions
|
|
@ -4,35 +4,25 @@ 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"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
|
||||
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/types/basetypes"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"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"
|
||||
|
|
@ -44,6 +34,7 @@ var (
|
|||
_ resource.ResourceWithConfigure = &instanceResource{}
|
||||
_ resource.ResourceWithImportState = &instanceResource{}
|
||||
_ resource.ResourceWithModifyPlan = &instanceResource{}
|
||||
_ resource.ResourceWithIdentity = &instanceResource{}
|
||||
)
|
||||
|
||||
//nolint:unused // TODO: remove if not needed later
|
||||
|
|
@ -52,63 +43,10 @@ var validNodeTypes []string = []string{
|
|||
"Replica",
|
||||
}
|
||||
|
||||
type Model struct {
|
||||
Id types.String `tfsdk:"id"` // needed by TF
|
||||
InstanceId types.String `tfsdk:"instance_id"`
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
BackupSchedule types.String `tfsdk:"backup_schedule"`
|
||||
FlavorId types.String `tfsdk:"flavor_id"`
|
||||
Encryption types.Object `tfsdk:"encryption"`
|
||||
IsDeletable types.Bool `tfsdk:"is_deletable"`
|
||||
Storage types.Object `tfsdk:"storage"`
|
||||
Status types.String `tfsdk:"status"`
|
||||
Version types.String `tfsdk:"version"`
|
||||
Replicas types.Int64 `tfsdk:"replicas"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
Network types.Object `tfsdk:"network"`
|
||||
Edition types.String `tfsdk:"edition"`
|
||||
RetentionDays types.Int64 `tfsdk:"retention_days"`
|
||||
}
|
||||
|
||||
type encryptionModel struct {
|
||||
KeyRingId types.String `tfsdk:"keyring_id"`
|
||||
KeyId types.String `tfsdk:"key_id"`
|
||||
KeyVersion types.String `tfsdk:"key_version"`
|
||||
ServiceAccount types.String `tfsdk:"service_account"`
|
||||
}
|
||||
|
||||
var encryptionTypes = map[string]attr.Type{
|
||||
"keyring_id": basetypes.StringType{},
|
||||
"key_id": basetypes.StringType{},
|
||||
"key_version": basetypes.StringType{},
|
||||
"service_account": basetypes.StringType{},
|
||||
}
|
||||
|
||||
type networkModel struct {
|
||||
ACL types.List `tfsdk:"acl"`
|
||||
AccessScope types.String `tfsdk:"access_scope"`
|
||||
InstanceAddress types.String `tfsdk:"instance_address"`
|
||||
RouterAddress types.String `tfsdk:"router_address"`
|
||||
}
|
||||
|
||||
var networkTypes = map[string]attr.Type{
|
||||
"acl": basetypes.ListType{ElemType: types.StringType},
|
||||
"access_scope": basetypes.StringType{},
|
||||
"instance_address": basetypes.StringType{},
|
||||
"router_address": basetypes.StringType{},
|
||||
}
|
||||
|
||||
// Struct corresponding to Model.Storage
|
||||
type storageModel struct {
|
||||
Class types.String `tfsdk:"class"`
|
||||
Size types.Int64 `tfsdk:"size"`
|
||||
}
|
||||
|
||||
// Types corresponding to storageModel
|
||||
var storageTypes = map[string]attr.Type{
|
||||
"class": basetypes.StringType{},
|
||||
"size": basetypes.Int64Type{},
|
||||
type InstanceResourceIdentityModel struct {
|
||||
ProjectID types.String `tfsdk:"project_id"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
InstanceID types.String `tfsdk:"instance_id"`
|
||||
}
|
||||
|
||||
// NewInstanceResource is a helper function to simplify the provider implementation.
|
||||
|
|
@ -154,17 +92,21 @@ func (r *instanceResource) ModifyPlan(
|
|||
req resource.ModifyPlanRequest,
|
||||
resp *resource.ModifyPlanResponse,
|
||||
) { // nolint:gocritic // function signature required by Terraform
|
||||
var configModel Model
|
||||
|
||||
// skip initial empty configuration to avoid follow-up errors
|
||||
if req.Config.Raw.IsNull() {
|
||||
return
|
||||
}
|
||||
var configModel sqlserverflexalpha2.InstanceModel
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var planModel Model
|
||||
if req.Plan.Raw.IsNull() {
|
||||
return
|
||||
}
|
||||
var planModel sqlserverflexalpha2.InstanceModel
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &planModel)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
|
|
@ -175,257 +117,303 @@ func (r *instanceResource) ModifyPlan(
|
|||
return
|
||||
}
|
||||
|
||||
var identityModel InstanceResourceIdentityModel
|
||||
identityModel.ProjectID = planModel.ProjectId
|
||||
identityModel.Region = planModel.Region
|
||||
if !planModel.InstanceId.IsNull() && !planModel.InstanceId.IsUnknown() {
|
||||
identityModel.InstanceID = planModel.InstanceId
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.Identity.Set(ctx, identityModel)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.Plan.Set(ctx, planModel)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
//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(),
|
||||
},
|
||||
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"],
|
||||
// },
|
||||
// },
|
||||
//}
|
||||
}
|
||||
|
||||
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
|
||||
},
|
||||
"instance_id": schema.StringAttribute{
|
||||
Description: descriptions["instance_id"],
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
"region": identityschema.StringAttribute{
|
||||
RequiredForImport: true, // can be defaulted by the provider configuration
|
||||
},
|
||||
"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"],
|
||||
"instance_id": identityschema.StringAttribute{
|
||||
RequiredForImport: true, // can be defaulted by the provider configuration
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -437,49 +425,29 @@ 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() {
|
||||
return
|
||||
}
|
||||
|
||||
// Read identity data
|
||||
var identityData InstanceResourceIdentityModel
|
||||
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx = core.InitProviderContext(ctx)
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
region := model.Region.ValueString()
|
||||
projectId := identityData.ProjectID.ValueString()
|
||||
region := identityData.Region.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
|
||||
var storage = &storageModel{}
|
||||
if !model.Storage.IsNull() && !model.Storage.IsUnknown() {
|
||||
diags = model.Storage.As(ctx, storage, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var encryption = &encryptionModel{}
|
||||
if !model.Encryption.IsNull() && !model.Encryption.IsUnknown() {
|
||||
diags = model.Encryption.As(ctx, encryption, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var network = &networkModel{}
|
||||
if !model.Network.IsNull() && !model.Network.IsUnknown() {
|
||||
diags = model.Network.As(ctx, network, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Generate API request body from model
|
||||
payload, err := toCreatePayload(&model, storage, encryption, network)
|
||||
payload, err := toCreatePayload(ctx, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
|
|
@ -503,6 +471,18 @@ func (r *instanceResource) Create(
|
|||
ctx = core.LogResponse(ctx)
|
||||
|
||||
instanceId := *createResp.Id
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
utils.SetAndLogStateFields(
|
||||
ctx, &resp.Diagnostics, &resp.State, map[string]any{
|
||||
"id": utils.BuildInternalTerraformId(projectId, region, instanceId),
|
||||
|
|
@ -521,7 +501,11 @@ func (r *instanceResource) Create(
|
|||
projectId,
|
||||
instanceId,
|
||||
region,
|
||||
).SetSleepBeforeWait(30 * time.Second).WaitWithContext(ctx)
|
||||
).SetSleepBeforeWait(
|
||||
30 * time.Second,
|
||||
).SetTimeout(
|
||||
90 * time.Minute,
|
||||
).WaitWithContext(ctx)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
|
|
@ -543,7 +527,8 @@ func (r *instanceResource) Create(
|
|||
}
|
||||
|
||||
// Map response body to schema
|
||||
err = mapFields(ctx, waitResp, &model, storage, encryption, network, region)
|
||||
// err = mapFields(ctx, waitResp, &model, storage, encryption, network, region)
|
||||
err = mapResponseToModel(ctx, waitResp, &model, resp.Diagnostics)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
|
|
@ -560,11 +545,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")
|
||||
}
|
||||
|
||||
|
|
@ -574,13 +554,20 @@ func (r *instanceResource) Read(
|
|||
req resource.ReadRequest,
|
||||
resp *resource.ReadResponse,
|
||||
) { // nolint:gocritic // function signature required by Terraform
|
||||
var model Model
|
||||
var model sqlserverflexalpha2.InstanceModel
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Read identity data
|
||||
var identityData InstanceResourceIdentityModel
|
||||
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx = core.InitProviderContext(ctx)
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
|
|
@ -591,33 +578,6 @@ func (r *instanceResource) Read(
|
|||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
|
||||
var storage = &storageModel{}
|
||||
if !model.Storage.IsNull() && !model.Storage.IsUnknown() {
|
||||
diags = model.Storage.As(ctx, storage, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var encryption = &encryptionModel{}
|
||||
if !model.Encryption.IsNull() && !model.Encryption.IsUnknown() {
|
||||
diags = model.Encryption.As(ctx, encryption, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var network = &networkModel{}
|
||||
if !model.Network.IsNull() && !model.Network.IsUnknown() {
|
||||
diags = model.Network.As(ctx, network, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
instanceResp, err := r.client.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
|
||||
|
|
@ -632,7 +592,8 @@ func (r *instanceResource) Read(
|
|||
ctx = core.LogResponse(ctx)
|
||||
|
||||
// Map response body to schema
|
||||
err = mapFields(ctx, instanceResp, &model, storage, encryption, network, region)
|
||||
// err = mapFields(ctx, instanceResp, &model, storage, encryption, network, region)
|
||||
err = mapResponseToModel(ctx, instanceResp, &model, resp.Diagnostics)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
|
|
@ -648,6 +609,17 @@ func (r *instanceResource) Read(
|
|||
if resp.Diagnostics.HasError() {
|
||||
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, "SQLServer Flex instance read")
|
||||
}
|
||||
|
||||
|
|
@ -658,7 +630,7 @@ func (r *instanceResource) Update(
|
|||
resp *resource.UpdateResponse,
|
||||
) { // nolint:gocritic // function signature required by Terraform
|
||||
// Retrieve values from plan
|
||||
var model Model
|
||||
var model sqlserverflexalpha2.InstanceModel
|
||||
diags := req.Plan.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
|
|
@ -675,35 +647,8 @@ func (r *instanceResource) Update(
|
|||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
|
||||
var storage = &storageModel{}
|
||||
if !model.Storage.IsNull() && !model.Storage.IsUnknown() {
|
||||
diags = model.Storage.As(ctx, storage, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var encryption = &encryptionModel{}
|
||||
if !model.Encryption.IsNull() && !model.Encryption.IsUnknown() {
|
||||
diags = model.Encryption.As(ctx, encryption, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var network = &networkModel{}
|
||||
if !model.Network.IsNull() && !model.Network.IsUnknown() {
|
||||
diags = model.Network.As(ctx, network, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Generate API request body from model
|
||||
payload, err := toUpdatePayload(&model, storage, network)
|
||||
payload, err := toUpdatePayload(ctx, &model, resp)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
|
|
@ -739,7 +684,8 @@ func (r *instanceResource) Update(
|
|||
}
|
||||
|
||||
// Map response body to schema
|
||||
err = mapFields(ctx, waitResp, &model, storage, encryption, network, region)
|
||||
err = mapResponseToModel(ctx, waitResp, &model, resp.Diagnostics)
|
||||
// err = mapFields(ctx, waitResp, &model, storage, encryption, network, region)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
|
|
@ -764,7 +710,7 @@ func (r *instanceResource) Delete(
|
|||
resp *resource.DeleteResponse,
|
||||
) { // nolint:gocritic // function signature required by Terraform
|
||||
// Retrieve values from state
|
||||
var model Model
|
||||
var model sqlserverflexalpha2.InstanceModel
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue