diff --git a/docs/data-sources/sqlserverflex_instance.md b/docs/data-sources/sqlserverflex_instance.md index 4fa559558..6547050c3 100644 --- a/docs/data-sources/sqlserverflex_instance.md +++ b/docs/data-sources/sqlserverflex_instance.md @@ -37,6 +37,7 @@ data "stackit_sqlserverflex_instance" "example" { - `acl` (List of String) The Access Control List (ACL) for the SQLServer Flex instance. - `backup_schedule` (String) The backup schedule. Should follow the cron scheduling system format (e.g. "0 0 * * *"). - `edition` (String) Edition of the MSSQL server instance. +- `encryption` (Attributes) Parameter to define which key to use for storage encryption. (see [below for nested schema](#nestedatt--encryption)) - `flavor` (Attributes) (see [below for nested schema](#nestedatt--flavor)) - `flavor_id` (String) The flavor ID of the SQLServer Flex instance. - `id` (String) Terraform's internal data source. ID. It is structured as "`project_id`,`region`,`instance_id`". @@ -57,6 +58,19 @@ Optional: Read-Only: - `acl` (List of String) List of IPV4 cidr. +- `instance_address` (String) Address of this instance. +- `router_address` (String) Address of the router. + + + +### Nested Schema for `encryption` + +Read-Only: + +- `kek_key_id` (String) UUID of the key within the STACKIT-KMS to use for the encryption. +- `kek_key_version` (String) Version of the key within the STACKIT-KMS to use for the encryption. +- `kek_keyring_id` (String) UUID of the keyring where the key is located within the STACKTI-KMS. +- `service_account` (String) Service-Account linked to the Key within the STACKIT-KMS. diff --git a/docs/resources/sqlserverflex_instance.md b/docs/resources/sqlserverflex_instance.md index b520e4a3b..06bfdac1b 100644 --- a/docs/resources/sqlserverflex_instance.md +++ b/docs/resources/sqlserverflex_instance.md @@ -16,14 +16,17 @@ SQLServer Flex instance resource schema. Must have a `region` specified in the p resource "stackit_sqlserverflex_instance" "example" { project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" name = "example-instance" - acl = ["XXX.XXX.XXX.X/XX", "XX.XXX.XX.X/XX"] - backup_schedule = "00 00 * * *" flavor_id = "4.16-Single" + backup_schedule = "0 0 * * *" + network = { + acl = ["XXX.XXX.XXX.X/XX", "XX.XXX.XX.X/XX"] + } storage = { class = "premium-perf2-stackit" size = 5 } - version = 2022 + retention_days = 32 + version = "2022" } ``` @@ -39,6 +42,7 @@ resource "stackit_sqlserverflex_instance" "example" { - `acl` (List of String, Deprecated) The Access Control List (ACL) for the SQLServer Flex instance. - `backup_schedule` (String) The backup schedule. Should follow the cron scheduling system format (e.g. "0 0 * * *") Will be required in the future. Set a value to prevent breaking changes. +- `encryption` (Attributes) Parameter to define which key to use for storage encryption. (see [below for nested schema](#nestedatt--encryption)) - `flavor` (Attributes) (see [below for nested schema](#nestedatt--flavor)) - `flavor_id` (String) The flavor ID of the SQLServer Flex instance. - `network` (Attributes) The network configuration of the instance. Will be required in the future. Set a value to prevent breaking changes. (see [below for nested schema](#nestedatt--network)) @@ -55,6 +59,17 @@ resource "stackit_sqlserverflex_instance" "example" { - `instance_id` (String) ID of the SQLServer Flex instance. - `replicas` (Number) + +### Nested Schema for `encryption` + +Required: + +- `kek_key_id` (String) UUID of the key within the STACKIT-KMS to use for the encryption. +- `kek_key_version` (String) Version of the key within the STACKIT-KMS to use for the encryption. +- `kek_keyring_id` (String) UUID of the keyring where the key is located within the STACKTI-KMS. +- `service_account` (String) Service-Account linked to the Key within the STACKIT-KMS. + + ### Nested Schema for `flavor` @@ -77,6 +92,11 @@ Optional: - `access_scope` (String) The network access scope of the instance. This feature is in private preview. Supplying this object is only permitted for enabled accounts. If your account does not have access, the request will be rejected. Possible values are: `PUBLIC`, `SNA`. - `acl` (List of String) List of IPV4 cidr. +Read-Only: + +- `instance_address` (String) Address of this instance. +- `router_address` (String) Address of the router. + ### Nested Schema for `options` diff --git a/examples/resources/stackit_sqlserverflex_instance/resource.tf b/examples/resources/stackit_sqlserverflex_instance/resource.tf index 45bb93b54..6cdbf71b5 100644 --- a/examples/resources/stackit_sqlserverflex_instance/resource.tf +++ b/examples/resources/stackit_sqlserverflex_instance/resource.tf @@ -1,12 +1,15 @@ resource "stackit_sqlserverflex_instance" "example" { project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" name = "example-instance" - acl = ["XXX.XXX.XXX.X/XX", "XX.XXX.XX.X/XX"] - backup_schedule = "00 00 * * *" flavor_id = "4.16-Single" + backup_schedule = "0 0 * * *" + network = { + acl = ["XXX.XXX.XXX.X/XX", "XX.XXX.XX.X/XX"] + } storage = { class = "premium-perf2-stackit" size = 5 } - version = 2022 + retention_days = 32 + version = "2022" } diff --git a/stackit/internal/services/sqlserverflex/instance/datasource.go b/stackit/internal/services/sqlserverflex/instance/datasource.go index e3807d012..45dd5bb6c 100644 --- a/stackit/internal/services/sqlserverflex/instance/datasource.go +++ b/stackit/internal/services/sqlserverflex/instance/datasource.go @@ -69,11 +69,18 @@ func (r *instanceDataSource) Schema(_ context.Context, _ datasource.SchemaReques "name": "Instance name.", "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 * * *").`, + "encryption": "Parameter to define which key to use for storage encryption.", + "kek_key_id": "UUID of the key within the STACKIT-KMS to use for the encryption.", + "kek_keyring_id": "UUID of the keyring where the key is located within the STACKTI-KMS.", + "kek_key_version": "Version of the key within the STACKIT-KMS to use for the encryption.", + "service_account": "Service-Account linked to the Key within the STACKIT-KMS.", "options": "Custom parameters for the SQLServer Flex instance.", "flavor_id": "The flavor ID of the SQLServer Flex instance.", "network": "The network configuration of the instance.", "network.access_scope": "The network access scope of the instance. This feature is in private preview. Supplying this object is only permitted for enabled accounts. If your account does not have access, the request will be rejected.", "network.acl": "List of IPV4 cidr.", + "instance_address": "Address of this instance.", + "router_address": "Address of the router.", "retention_days": "The days (30 to 90) for how long the backup files should be stored before cleaned up.", "edition": "Edition of the MSSQL server instance.", "region": "The resource region. If not defined, the provider region is used.", @@ -115,6 +122,28 @@ func (r *instanceDataSource) Schema(_ context.Context, _ datasource.SchemaReques Description: descriptions["backup_schedule"], Computed: true, }, + "encryption": schema.SingleNestedAttribute{ + Description: descriptions["encryption"], + Computed: true, + Attributes: map[string]schema.Attribute{ + "kek_key_id": schema.StringAttribute{ + Description: descriptions["kek_key_id"], + Computed: true, + }, + "kek_keyring_id": schema.StringAttribute{ + Description: descriptions["kek_keyring_id"], + Computed: true, + }, + "kek_key_version": schema.StringAttribute{ + Description: descriptions["kek_key_version"], + Computed: true, + }, + "service_account": schema.StringAttribute{ + Description: descriptions["service_account"], + Computed: true, + }, + }, + }, "flavor": schema.SingleNestedAttribute{ Computed: true, Attributes: map[string]schema.Attribute{ @@ -149,6 +178,14 @@ func (r *instanceDataSource) Schema(_ context.Context, _ datasource.SchemaReques ElementType: types.StringType, Computed: true, }, + "instance_address": schema.StringAttribute{ + Description: descriptions["instance_address"], + Computed: true, + }, + "router_address": schema.StringAttribute{ + Description: descriptions["router_address"], + Computed: true, + }, }, }, "replicas": schema.Int32Attribute{ diff --git a/stackit/internal/services/sqlserverflex/instance/resource.go b/stackit/internal/services/sqlserverflex/instance/resource.go index 71a9a5ce0..6f82c44eb 100644 --- a/stackit/internal/services/sqlserverflex/instance/resource.go +++ b/stackit/internal/services/sqlserverflex/instance/resource.go @@ -62,6 +62,7 @@ type Model struct { // Deprecated: ACL is deprecated and will be removed after January 2027 ACL types.List `tfsdk:"acl"` BackupSchedule types.String `tfsdk:"backup_schedule"` + Encryption types.Object `tfsdk:"encryption"` Flavor types.Object `tfsdk:"flavor"` FlavorId types.String `tfsdk:"flavor_id"` Storage types.Object `tfsdk:"storage"` @@ -75,16 +76,36 @@ type Model struct { Region types.String `tfsdk:"region"` } +// Struct corresponding to Model.Encryption +type encryptionModel struct { + KekKeyId types.String `tfsdk:"kek_key_id"` + KekKeyRingId types.String `tfsdk:"kek_keyring_id"` + KekKeyVersion types.String `tfsdk:"kek_key_version"` + ServiceAccount types.String `tfsdk:"service_account"` +} + +// types corresponding to encryptionModel +var encryptionTypes = map[string]attr.Type{ + "kek_key_id": basetypes.StringType{}, + "kek_keyring_id": basetypes.StringType{}, + "kek_key_version": basetypes.StringType{}, + "service_account": basetypes.StringType{}, +} + // Struct corresponding to Model.Network type networkModel struct { - AccessScope types.String `tfsdk:"access_scope"` - Acl types.List `tfsdk:"acl"` + AccessScope types.String `tfsdk:"access_scope"` + Acl types.List `tfsdk:"acl"` + InstanceAddress types.String `tfsdk:"instance_address"` + RouterAddress types.String `tfsdk:"router_address"` } // types corresponding to Network var networkTypes = map[string]attr.Type{ - "access_scope": basetypes.StringType{}, - "acl": basetypes.ListType{ElemType: types.StringType}, + "access_scope": basetypes.StringType{}, + "acl": basetypes.ListType{ElemType: types.StringType}, + "instance_address": basetypes.StringType{}, + "router_address": basetypes.StringType{}, } // Struct corresponding to Model.Flavor @@ -300,11 +321,18 @@ func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, r "name": "Instance name.", "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 * * *")` + willBeRequired, + "encryption": "Parameter to define which key to use for storage encryption.", + "kek_key_id": "UUID of the key within the STACKIT-KMS to use for the encryption.", + "kek_keyring_id": "UUID of the keyring where the key is located within the STACKTI-KMS.", + "kek_key_version": "Version of the key within the STACKIT-KMS to use for the encryption.", + "service_account": "Service-Account linked to the Key within the STACKIT-KMS.", "options": "Custom parameters for the SQLServer Flex instance.", "flavor_id": "The flavor ID of the SQLServer Flex instance.", "network": "The network configuration of the instance." + willBeRequired, "network.access_scope": "The network access scope of the instance. This feature is in private preview. Supplying this object is only permitted for enabled accounts. If your account does not have access, the request will be rejected.", "network.acl": "List of IPV4 cidr." + willBeRequired, + "instance_address": "Address of this instance.", + "router_address": "Address of the router.", "retention_days": "The days (30 to 90) for how long the backup files should be stored before cleaned up." + willBeRequired, "edition": "Edition of the MSSQL server instance.", "region": "The resource region. If not defined, the provider region is used.", @@ -381,6 +409,51 @@ func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, r stringplanmodifier.UseStateForUnknown(), }, }, + "encryption": schema.SingleNestedAttribute{ + Description: descriptions["encryption"], + Optional: true, + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.RequiresReplace(), + }, + Attributes: map[string]schema.Attribute{ + "kek_key_id": schema.StringAttribute{ + Description: descriptions["kek_key_id"], + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + validate.UUID(), + validate.NoSeparator(), + }, + }, + "kek_keyring_id": schema.StringAttribute{ + Description: descriptions["kek_keyring_id"], + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + validate.UUID(), + validate.NoSeparator(), + }, + }, + "kek_key_version": schema.StringAttribute{ + Description: descriptions["kek_key_version"], + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "service_account": schema.StringAttribute{ + Description: descriptions["service_account"], + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + }, + }, "flavor": schema.SingleNestedAttribute{ Computed: true, Optional: true, @@ -454,6 +527,14 @@ func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, r listvalidator.SizeAtLeast(1), }, }, + "instance_address": schema.StringAttribute{ + Description: descriptions["instance_address"], + Computed: true, + }, + "router_address": schema.StringAttribute{ + Description: descriptions["router_address"], + Computed: true, + }, }, }, "replicas": schema.Int32Attribute{ @@ -587,6 +668,17 @@ func (r *instanceResource) Create(ctx context.Context, req resource.CreateReques return } } + + var encryption *encryptionModel + if !(model.Encryption.IsNull() || model.Encryption.IsUnknown()) { + encryption = &encryptionModel{} + diags = model.Encryption.As(ctx, encryption, basetypes.ObjectAsOptions{}) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + } + var flavor = &flavorModel{} if !(model.Flavor.IsNull() || model.Flavor.IsUnknown()) { diags = model.Flavor.As(ctx, flavor, basetypes.ObjectAsOptions{}) @@ -628,7 +720,7 @@ func (r *instanceResource) Create(ctx context.Context, req resource.CreateReques } // Generate API request body from model - payload, err := toCreatePayload(&model, acl, flavor, storage, options, network) + payload, err := toCreatePayload(&model, acl, encryption, flavor, storage, options, network) if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Creating API payload: %v", err)) return @@ -964,8 +1056,10 @@ func mapFields(ctx context.Context, resp *sqlserverflex.GetInstanceResponse, mod } networkValues := map[string]attr.Value{ - "acl": aclList, - "access_scope": types.StringPointerValue((*string)(resp.Network.AccessScope)), + "acl": aclList, + "access_scope": types.StringPointerValue((*string)(resp.Network.AccessScope)), + "instance_address": types.StringPointerValue(resp.Network.InstanceAddress), + "router_address": types.StringPointerValue(resp.Network.RouterAddress), } networkObject, diags := types.ObjectValue(networkTypes, networkValues) if diags.HasError() { @@ -1001,6 +1095,21 @@ func mapFields(ctx context.Context, resp *sqlserverflex.GetInstanceResponse, mod return fmt.Errorf("creating options: %w", core.DiagsToError(diags)) } + var encryptionValues map[string]attr.Value + encryptionObject := types.ObjectNull(encryptionTypes) + if resp.Encryption != nil { + encryptionValues = map[string]attr.Value{ + "kek_key_id": types.StringValue(resp.Encryption.KekKeyId), + "kek_keyring_id": types.StringValue(resp.Encryption.KekKeyRingId), + "kek_key_version": types.StringValue(resp.Encryption.KekKeyVersion), + "service_account": types.StringValue(resp.Encryption.ServiceAccount), + } + encryptionObject, diags = types.ObjectValue(encryptionTypes, encryptionValues) + if diags.HasError() { + return fmt.Errorf("creating encryption: %w", core.DiagsToError(diags)) + } + } + // If the API returned "0 0 * * *" but user defined "00 00 * * *" in its config, // we keep the user's "00 00 * * *" in the state to satisfy Terraform. backupScheduleApiResp := types.StringValue(resp.BackupSchedule) @@ -1023,14 +1132,26 @@ func mapFields(ctx context.Context, resp *sqlserverflex.GetInstanceResponse, mod model.RetentionDays = types.Int32Value(resp.RetentionDays) model.Edition = types.StringValue(string(resp.Edition)) model.Network = networkObject + model.Encryption = encryptionObject return nil } -func toCreatePayload(model *Model, acl []string, flavor *flavorModel, storage *storageModel, options *optionsModel, network *networkModel) (*sqlserverflex.CreateInstancePayload, error) { +func toCreatePayload(model *Model, acl []string, encryption *encryptionModel, flavor *flavorModel, storage *storageModel, options *optionsModel, network *networkModel) (*sqlserverflex.CreateInstancePayload, error) { if model == nil { return nil, fmt.Errorf("nil model") } + // Encryption + var encryptionPayload *sqlserverflex.InstanceEncryption + if encryption != nil { + encryptionPayload = &sqlserverflex.InstanceEncryption{ + KekKeyId: encryption.KekKeyId.ValueString(), + KekKeyRingId: encryption.KekKeyRingId.ValueString(), + KekKeyVersion: encryption.KekKeyVersion.ValueString(), + ServiceAccount: encryption.ServiceAccount.ValueString(), + } + } + // Network networkPayload := sqlserverflex.CreateInstancePayloadNetwork{} if acl != nil { @@ -1077,7 +1198,7 @@ func toCreatePayload(model *Model, acl []string, flavor *flavorModel, storage *s return &sqlserverflex.CreateInstancePayload{ BackupSchedule: model.BackupSchedule.ValueString(), - Encryption: nil, + Encryption: encryptionPayload, FlavorId: flavorId, Labels: nil, Name: model.Name.ValueString(), diff --git a/stackit/internal/services/sqlserverflex/instance/resource_test.go b/stackit/internal/services/sqlserverflex/instance/resource_test.go index d8d5051b0..7478f9d95 100644 --- a/stackit/internal/services/sqlserverflex/instance/resource_test.go +++ b/stackit/internal/services/sqlserverflex/instance/resource_test.go @@ -74,8 +74,10 @@ func TestMapFields(t *testing.T) { "retention_days": types.Int32Value(0), }), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ - "acl": types.ListNull(types.StringType), - "access_scope": types.StringNull(), + "acl": types.ListNull(types.StringType), + "access_scope": types.StringNull(), + "instance_address": types.StringNull(), + "router_address": types.StringNull(), }), RetentionDays: types.Int32Value(0), Edition: types.StringValue(""), @@ -135,8 +137,10 @@ func TestMapFields(t *testing.T) { Edition: types.StringValue("edition"), RetentionDays: types.Int32Value(1), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ - "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("ip1"), types.StringValue("ip2"), types.StringValue("")}), - "access_scope": types.StringNull(), + "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("ip1"), types.StringValue("ip2"), types.StringValue("")}), + "access_scope": types.StringNull(), + "instance_address": types.StringNull(), + "router_address": types.StringNull(), }), Replicas: types.Int32Value(56), Storage: types.ObjectValueMust(storageTypes, map[string]attr.Value{ @@ -207,8 +211,10 @@ func TestMapFields(t *testing.T) { RetentionDays: types.Int32Value(1), Replicas: types.Int32Value(56), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ - "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("ip1"), types.StringValue("ip2"), types.StringValue("")}), - "access_scope": types.StringNull(), + "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("ip1"), types.StringValue("ip2"), types.StringValue("")}), + "access_scope": types.StringNull(), + "instance_address": types.StringNull(), + "router_address": types.StringNull(), }), Storage: types.ObjectValueMust(storageTypes, map[string]attr.Value{ "class": types.StringValue("class"), @@ -283,8 +289,10 @@ func TestMapFields(t *testing.T) { RetentionDays: types.Int32Value(1), Replicas: types.Int32Value(56), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ - "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("ip2"), types.StringValue(""), types.StringValue("ip1")}), - "access_scope": types.StringNull(), + "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("ip2"), types.StringValue(""), types.StringValue("ip1")}), + "access_scope": types.StringNull(), + "instance_address": types.StringNull(), + "router_address": types.StringNull(), }), Storage: types.ObjectValueMust(storageTypes, map[string]attr.Value{ "class": types.StringValue("class"), @@ -344,15 +352,16 @@ func TestMapFields(t *testing.T) { func TestToCreatePayload(t *testing.T) { tests := []struct { - description string - input *Model - inputAcl []string - inputFlavor *flavorModel - inputStorage *storageModel - inputOptions *optionsModel - inputNetwork *networkModel - expected *sqlserverflex.CreateInstancePayload - isValid bool + description string + input *Model + inputAcl []string + inputEncryption *encryptionModel + inputFlavor *flavorModel + inputStorage *storageModel + inputOptions *optionsModel + inputNetwork *networkModel + expected *sqlserverflex.CreateInstancePayload + isValid bool }{ { description: "default_values", @@ -360,11 +369,12 @@ func TestToCreatePayload(t *testing.T) { FlavorId: types.StringValue("fid"), RetentionDays: types.Int32Value(1), }, - inputAcl: []string{}, - inputFlavor: &flavorModel{}, - inputStorage: &storageModel{}, - inputOptions: &optionsModel{}, - inputNetwork: &networkModel{}, + inputAcl: []string{}, + inputEncryption: &encryptionModel{}, + inputFlavor: &flavorModel{}, + inputStorage: &storageModel{}, + inputOptions: &optionsModel{}, + inputNetwork: &networkModel{}, expected: &sqlserverflex.CreateInstancePayload{ FlavorId: "fid", RetentionDays: 1, @@ -375,6 +385,7 @@ func TestToCreatePayload(t *testing.T) { Class: "", Size: 0, }, + Encryption: &sqlserverflex.InstanceEncryption{}, }, isValid: true, }, @@ -401,6 +412,12 @@ func TestToCreatePayload(t *testing.T) { RetentionDays: types.Int32Value(1), }, inputNetwork: &networkModel{}, + inputEncryption: &encryptionModel{ + KekKeyId: types.StringValue("id"), + KekKeyRingId: types.StringValue("keyRingId"), + KekKeyVersion: types.StringValue("keyVersion"), + ServiceAccount: types.StringValue("some_service_account"), + }, expected: &sqlserverflex.CreateInstancePayload{ Network: sqlserverflex.CreateInstancePayloadNetwork{ Acl: []string{"ip_1", "ip_2"}, @@ -414,6 +431,12 @@ func TestToCreatePayload(t *testing.T) { }, RetentionDays: 1, Version: "version", + Encryption: &sqlserverflex.InstanceEncryption{ + KekKeyId: "id", + KekKeyRingId: "keyRingId", + KekKeyVersion: "keyVersion", + ServiceAccount: "some_service_account", + }, }, isValid: true, }, @@ -522,7 +545,7 @@ func TestToCreatePayload(t *testing.T) { } for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { - output, err := toCreatePayload(tt.input, tt.inputAcl, tt.inputFlavor, tt.inputStorage, tt.inputOptions, tt.inputNetwork) + output, err := toCreatePayload(tt.input, tt.inputAcl, tt.inputEncryption, tt.inputFlavor, tt.inputStorage, tt.inputOptions, tt.inputNetwork) if !tt.isValid && err == nil { t.Fatalf("Should have failed") } @@ -888,8 +911,10 @@ func TestHandleV3Migration(t *testing.T) { }), Version: types.StringValue(string(sqlserverflex.INSTANCEVERSIONOPT__2022)), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ - "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), - "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), + "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "instance_address": types.StringValue("instance_address"), + "router_address": types.StringValue("router_address"), }), RetentionDays: types.Int32Value(45), }, @@ -902,8 +927,10 @@ func TestHandleV3Migration(t *testing.T) { }), Version: types.StringValue(string(sqlserverflex.INSTANCEVERSIONOPT__2022)), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ - "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), - "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), + "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "instance_address": types.StringValue("instance_address"), + "router_address": types.StringValue("router_address"), }), RetentionDays: types.Int32Value(45), }, @@ -916,8 +943,10 @@ func TestHandleV3Migration(t *testing.T) { }), Version: types.StringValue(string(sqlserverflex.INSTANCEVERSIONOPT__2022)), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ - "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), - "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), + "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "instance_address": types.StringValue("instance_address"), + "router_address": types.StringValue("router_address"), }), RetentionDays: types.Int32Value(45), }, @@ -968,8 +997,10 @@ func TestHandleV3Migration(t *testing.T) { Version: types.StringValue(string(sqlserverflex.INSTANCEVERSIONOPT__2022)), ACL: types.ListNull(types.StringType), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ - "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), - "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), + "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "instance_address": types.StringValue("instance_address"), + "router_address": types.StringValue("router_address"), }), RetentionDays: types.Int32Value(1), Options: types.ObjectNull(optionsTypes), @@ -983,8 +1014,10 @@ func TestHandleV3Migration(t *testing.T) { Version: types.StringValue(string(sqlserverflex.INSTANCEVERSIONOPT__2022)), ACL: types.ListNull(types.StringType), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ - "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), - "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), + "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "instance_address": types.StringValue("instance_address"), + "router_address": types.StringValue("router_address"), }), RetentionDays: types.Int32Value(1), Options: types.ObjectNull(optionsTypes), @@ -998,8 +1031,10 @@ func TestHandleV3Migration(t *testing.T) { Version: types.StringValue(string(sqlserverflex.INSTANCEVERSION__2022)), ACL: types.ListNull(types.StringType), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ - "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), - "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), + "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "instance_address": types.StringValue("instance_address"), + "router_address": types.StringValue("router_address"), }), RetentionDays: types.Int32Value(1), Options: types.ObjectNull(optionsTypes), @@ -1017,8 +1052,10 @@ func TestHandleV3Migration(t *testing.T) { Version: types.StringValue(string(sqlserverflex.INSTANCEVERSIONOPT__2022)), ACL: types.ListNull(types.StringType), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ - "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), - "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), + "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "instance_address": types.StringValue("instance_address"), + "router_address": types.StringValue("router_address"), }), RetentionDays: types.Int32Value(1), Options: types.ObjectNull(optionsTypes), @@ -1032,8 +1069,10 @@ func TestHandleV3Migration(t *testing.T) { Version: types.StringValue(string(sqlserverflex.INSTANCEVERSIONOPT__2022)), ACL: types.ListNull(types.StringType), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ - "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), - "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), + "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "instance_address": types.StringValue("instance_address"), + "router_address": types.StringValue("router_address"), }), RetentionDays: types.Int32Value(1), Options: types.ObjectNull(optionsTypes), @@ -1047,8 +1086,10 @@ func TestHandleV3Migration(t *testing.T) { Version: types.StringValue(string(sqlserverflex.INSTANCEVERSION__2022)), ACL: types.ListNull(types.StringType), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ - "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), - "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), + "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "instance_address": types.StringValue("instance_address"), + "router_address": types.StringValue("router_address"), }), RetentionDays: types.Int32Value(1), Options: types.ObjectNull(optionsTypes), @@ -1066,8 +1107,10 @@ func TestHandleV3Migration(t *testing.T) { Version: types.StringNull(), ACL: types.ListNull(types.StringType), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ - "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), - "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), + "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "instance_address": types.StringValue("instance_address"), + "router_address": types.StringValue("router_address"), }), RetentionDays: types.Int32Value(1), Options: types.ObjectNull(optionsTypes), @@ -1081,8 +1124,10 @@ func TestHandleV3Migration(t *testing.T) { Version: types.StringNull(), ACL: types.ListNull(types.StringType), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ - "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), - "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), + "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "instance_address": types.StringValue("instance_address"), + "router_address": types.StringValue("router_address"), }), RetentionDays: types.Int32Value(1), Options: types.ObjectNull(optionsTypes), @@ -1096,8 +1141,10 @@ func TestHandleV3Migration(t *testing.T) { Version: types.StringValue(string(sqlserverflex.INSTANCEVERSION__2022)), ACL: types.ListNull(types.StringType), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ - "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), - "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("10.0.0.0/24")}), + "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "instance_address": types.StringValue("instance_address"), + "router_address": types.StringValue("router_address"), }), RetentionDays: types.Int32Value(1), Options: types.ObjectNull(optionsTypes), @@ -1115,8 +1162,10 @@ func TestHandleV3Migration(t *testing.T) { }), Version: types.StringValue(string(sqlserverflex.INSTANCEVERSION__2022)), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ - "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("193.148.160.0/19")}), - "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("193.148.160.0/19")}), + "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "instance_address": types.StringValue("instance_address"), + "router_address": types.StringValue("router_address"), }), RetentionDays: types.Int32Null(), Options: types.ObjectValueMust(optionsTypes, map[string]attr.Value{ @@ -1133,8 +1182,10 @@ func TestHandleV3Migration(t *testing.T) { }), Version: types.StringValue(string(sqlserverflex.INSTANCEVERSION__2022)), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ - "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("193.148.160.0/19")}), - "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("193.148.160.0/19")}), + "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "instance_address": types.StringValue("instance_address"), + "router_address": types.StringValue("router_address"), }), RetentionDays: types.Int32Null(), Options: types.ObjectValueMust(optionsTypes, map[string]attr.Value{ @@ -1151,8 +1202,10 @@ func TestHandleV3Migration(t *testing.T) { }), Version: types.StringValue(string(sqlserverflex.INSTANCEVERSION__2022)), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ - "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("193.148.160.0/19")}), - "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "acl": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("193.148.160.0/19")}), + "access_scope": types.StringValue(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "instance_address": types.StringValue("instance_address"), + "router_address": types.StringValue("router_address"), }), RetentionDays: types.Int32Null(), Options: types.ObjectValueMust(optionsTypes, map[string]attr.Value{ diff --git a/stackit/internal/services/sqlserverflex/sqlserverflex_acc_test.go b/stackit/internal/services/sqlserverflex/sqlserverflex_acc_test.go index bda442e1a..ad7f44ccd 100644 --- a/stackit/internal/services/sqlserverflex/sqlserverflex_acc_test.go +++ b/stackit/internal/services/sqlserverflex/sqlserverflex_acc_test.go @@ -147,7 +147,7 @@ func TestAccSQLServerFlexMinResource(t *testing.T) { resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_user.user", "user_id"), resource.TestCheckResourceAttr("data.stackit_sqlserverflex_user.user", "username", testutil.ConvertConfigVariable(testConfigVarsMin["username"])), resource.TestCheckResourceAttr("data.stackit_sqlserverflex_user.user", "roles.#", "1"), - resource.TestCheckResourceAttr("data.stackit_sqlserverflex_user.user", "roles.0", testutil.ConvertConfigVariable(testConfigVarsMax["role"])), + resource.TestCheckResourceAttr("data.stackit_sqlserverflex_user.user", "roles.0", testutil.ConvertConfigVariable(testConfigVarsMin["role"])), ), }, // Import diff --git a/stackit/internal/services/sqlserverflex/sqlserverflex_private_preview_acc_test.go b/stackit/internal/services/sqlserverflex/sqlserverflex_private_preview_acc_test.go new file mode 100644 index 000000000..e076bd699 --- /dev/null +++ b/stackit/internal/services/sqlserverflex/sqlserverflex_private_preview_acc_test.go @@ -0,0 +1,265 @@ +package sqlserverflex_test + +import ( + _ "embed" + "fmt" + "maps" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/terraform" + + kms "github.com/stackitcloud/stackit-sdk-go/services/kms/v1api" + sqlserverflex "github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex/v3api" + + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil" +) + +var ( + //go:embed testdata/resource-max-private-preview.tf + resourcePrivatePreviewConfig string +) + +var testConfigVarsMaxPrivatePreview = config.Variables{ + "project_id": config.StringVariable(testutil.ProjectId), + "name": config.StringVariable(fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum))), + "acl1": config.StringVariable("192.168.0.0/16"), + "storage_class": config.StringVariable("premium-perf2-stackit"), + "storage_size": config.IntegerVariable(40), + "server_version": config.StringVariable("2022"), + "replicas": config.IntegerVariable(1), + "access_scope": config.StringVariable(string(sqlserverflex.INSTANCENETWORKACCESSSCOPE_PUBLIC)), + "retention_days": config.IntegerVariable(32), + "flavor_id": config.StringVariable("4.16-Single"), + "backup_schedule": config.StringVariable("0 6 * * *"), + "username": config.StringVariable(fmt.Sprintf("tf-acc-user-%s", acctest.RandStringFromCharSet(7, acctest.CharSetAlpha))), + "role": config.StringVariable("##STACKIT_LoginManager##"), + "region": config.StringVariable(testutil.Region), + "kek_key_version": config.StringVariable("1"), + "service_account_email": config.StringVariable(testutil.TestProjectServiceAccountEmail), + + "keyring_display_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)), + "display_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)), + "algorithm": config.StringVariable(string(kms.ALGORITHM_AES_256_GCM)), + "protection": config.StringVariable("software"), + "purpose": config.StringVariable(string(kms.PURPOSE_SYMMETRIC_ENCRYPT_DECRYPT)), +} + +func configVarsMaxPrivatePreviewUpdated() config.Variables { + temp := maps.Clone(testConfigVarsMaxPrivatePreview) + temp["backup_schedule"] = config.StringVariable("0 12 * * *") + temp["acl1"] = config.StringVariable("192.168.2.0/16") + temp["retention_days"] = config.IntegerVariable(40) + return temp +} + +func TestAccSQLServerFlexMaxPrivatePreviewResource(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories, + CheckDestroy: testAccChecksqlserverflexDestroy, + Steps: []resource.TestStep{ + // Creation + { + Config: testutil.NewConfigBuilder().BuildProviderConfig() + "\n" + resourcePrivatePreviewConfig, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("stackit_sqlserverflex_instance.instance", plancheck.ResourceActionCreate), + plancheck.ExpectResourceAction("stackit_sqlserverflex_user.user", plancheck.ResourceActionCreate), + }, + }, + ConfigVariables: testConfigVarsMaxPrivatePreview, + Check: resource.ComposeAggregateTestCheckFunc( + // Key and Keyring + resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("stackit_kms_keyring.keyring", "project_id", testutil.ProjectId), + resource.TestCheckResourceAttr("stackit_kms_keyring.keyring", "region", testutil.Region), + resource.TestCheckResourceAttr("stackit_kms_keyring.keyring", "display_name", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["display_name"])), + resource.TestCheckResourceAttrSet("stackit_kms_keyring.keyring", "keyring_id"), + resource.TestCheckNoResourceAttr("stackit_kms_keyring.keyring", "description"), + ), + + // Instance + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "project_id", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["project_id"])), + resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "instance_id"), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "name", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["name"])), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "network.acl.#", "1"), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "network.acl.0", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["acl1"])), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "network.access_scope", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["access_scope"])), + resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "flavor_id"), + resource.TestCheckNoResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.id"), + resource.TestCheckNoResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.description"), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "replicas", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["replicas"])), + resource.TestCheckNoResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.cpu"), + resource.TestCheckNoResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.ram"), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "storage.class", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["storage_class"])), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "storage.size", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["storage_size"])), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "version", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["server_version"])), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "retention_days", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["retention_days"])), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "backup_schedule", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["backup_schedule"])), + + resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "encryption.kek_key_id"), + resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "encryption.kek_keyring_id"), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "encryption.kek_key_version", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["kek_key_version"])), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "encryption.service_account", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["service_account_email"])), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "region", testutil.Region), + // User + resource.TestCheckResourceAttrPair( + "stackit_sqlserverflex_user.user", "project_id", + "stackit_sqlserverflex_instance.instance", "project_id", + ), + resource.TestCheckResourceAttrPair( + "stackit_sqlserverflex_user.user", "instance_id", + "stackit_sqlserverflex_instance.instance", "instance_id", + ), + resource.TestCheckResourceAttrSet("stackit_sqlserverflex_user.user", "user_id"), + resource.TestCheckResourceAttrSet("stackit_sqlserverflex_user.user", "password"), + ), + }, + // data source + { + Config: testutil.NewConfigBuilder().BuildProviderConfig() + "\n" + resourcePrivatePreviewConfig, + ConfigVariables: testConfigVarsMaxPrivatePreview, + Check: resource.ComposeAggregateTestCheckFunc( + // Instance data + resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "project_id", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["project_id"])), + resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "name", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["name"])), + resource.TestCheckResourceAttrPair( + "data.stackit_sqlserverflex_instance.instance", "project_id", + "stackit_sqlserverflex_instance.instance", "project_id", + ), + resource.TestCheckResourceAttrPair( + "data.stackit_sqlserverflex_instance.instance", "instance_id", + "stackit_sqlserverflex_instance.instance", "instance_id", + ), + resource.TestCheckResourceAttrPair( + "data.stackit_sqlserverflex_user.user", "instance_id", + "stackit_sqlserverflex_user.user", "instance_id", + ), + + resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "acl.#", "1"), + resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "acl.0", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["acl1"])), + resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_instance.instance", "flavor_id"), + resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_instance.instance", "flavor.id"), + resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_instance.instance", "flavor.description"), + resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_instance.instance", "flavor.cpu"), + resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_instance.instance", "flavor.ram"), + resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "replicas", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["replicas"])), + resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "retention_days", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["retention_days"])), + resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "backup_schedule", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["backup_schedule"])), + + resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_instance.instance", "encryption.kek_key_id"), + resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_instance.instance", "encryption.kek_keyring_id"), + resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "encryption.kek_key_version", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["kek_key_version"])), + resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "encryption.service_account", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["service_account_email"])), + + // User data + resource.TestCheckResourceAttr("data.stackit_sqlserverflex_user.user", "project_id", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["project_id"])), + resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_user.user", "user_id"), + resource.TestCheckResourceAttr("data.stackit_sqlserverflex_user.user", "username", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["username"])), + resource.TestCheckResourceAttr("data.stackit_sqlserverflex_user.user", "roles.#", "1"), + resource.TestCheckResourceAttr("data.stackit_sqlserverflex_user.user", "roles.0", testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["role"])), + resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_user.user", "host"), + resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_user.user", "port"), + ), + }, + // Import + { + ConfigVariables: testConfigVarsMaxPrivatePreview, + ResourceName: "stackit_sqlserverflex_instance.instance", + ImportStateIdFunc: func(s *terraform.State) (string, error) { + r, ok := s.RootModule().Resources["stackit_sqlserverflex_instance.instance"] + if !ok { + return "", fmt.Errorf("couldn't find resource stackit_sqlserverflex_instance.instance") + } + instanceId, ok := r.Primary.Attributes["instance_id"] + if !ok { + return "", fmt.Errorf("couldn't find attribute instance_id") + } + + return fmt.Sprintf("%s,%s,%s", testutil.ProjectId, testutil.Region, instanceId), nil + }, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "backup_schedule", + // Will be added during the import, because it's unknown which attribute defined the flavor + "flavor.cpu", + "flavor.description", + "flavor.id", + "flavor.ram", + }, + ImportStateCheck: func(s []*terraform.InstanceState) error { + if len(s) != 1 { + return fmt.Errorf("expected 1 state, got %d", len(s)) + } + if s[0].Attributes["backup_schedule"] != testutil.ConvertConfigVariable(testConfigVarsMaxPrivatePreview["backup_schedule"]) { + return fmt.Errorf("expected backup_schedule %s, got %s", testConfigVarsMaxPrivatePreview["backup_schedule"], s[0].Attributes["backup_schedule"]) + } + return nil + }, + }, + { + ResourceName: "stackit_sqlserverflex_user.user", + ConfigVariables: testConfigVarsMaxPrivatePreview, + ImportStateIdFunc: func(s *terraform.State) (string, error) { + r, ok := s.RootModule().Resources["stackit_sqlserverflex_user.user"] + if !ok { + return "", fmt.Errorf("couldn't find resource stackit_sqlserverflex_user.user") + } + instanceId, ok := r.Primary.Attributes["instance_id"] + if !ok { + return "", fmt.Errorf("couldn't find attribute instance_id") + } + userId, ok := r.Primary.Attributes["user_id"] + if !ok { + return "", fmt.Errorf("couldn't find attribute user_id") + } + + return fmt.Sprintf("%s,%s,%s,%s", testutil.ProjectId, testutil.Region, instanceId, userId), nil + }, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"password"}, + }, + // Update + { + Config: testutil.NewConfigBuilder().BuildProviderConfig() + "\n" + resourcePrivatePreviewConfig, + ConfigVariables: configVarsMaxPrivatePreviewUpdated(), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("stackit_sqlserverflex_instance.instance", plancheck.ResourceActionUpdate), + plancheck.ExpectResourceAction("stackit_sqlserverflex_user.user", plancheck.ResourceActionNoop), + }, + }, + Check: resource.ComposeAggregateTestCheckFunc( + // Instance data + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "project_id", testutil.ConvertConfigVariable(configVarsMaxPrivatePreviewUpdated()["project_id"])), + resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "instance_id"), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "name", testutil.ConvertConfigVariable(configVarsMaxPrivatePreviewUpdated()["name"])), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "acl.#", "1"), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "acl.0", testutil.ConvertConfigVariable(configVarsMaxPrivatePreviewUpdated()["acl1"])), + resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "flavor_id"), + resource.TestCheckNoResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.id"), + resource.TestCheckNoResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.description"), + resource.TestCheckNoResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.cpu"), + resource.TestCheckNoResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.ram"), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "replicas", testutil.ConvertConfigVariable(configVarsMaxPrivatePreviewUpdated()["replicas"])), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "storage.class", testutil.ConvertConfigVariable(configVarsMaxPrivatePreviewUpdated()["storage_class"])), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "storage.size", testutil.ConvertConfigVariable(configVarsMaxPrivatePreviewUpdated()["storage_size"])), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "version", testutil.ConvertConfigVariable(configVarsMaxPrivatePreviewUpdated()["server_version"])), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "retention_days", testutil.ConvertConfigVariable(configVarsMaxPrivatePreviewUpdated()["retention_days"])), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "backup_schedule", testutil.ConvertConfigVariable(configVarsMaxPrivatePreviewUpdated()["backup_schedule"])), + + resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "encryption.kek_key_id"), + resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "encryption.kek_keyring_id"), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "encryption.kek_key_version", testutil.ConvertConfigVariable(configVarsMaxPrivatePreviewUpdated()["kek_key_version"])), + resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "encryption.service_account", testutil.ConvertConfigVariable(configVarsMaxPrivatePreviewUpdated()["service_account_email"])), + ), + }, + // Deletion is done by the framework implicitly + }, + }) +} diff --git a/stackit/internal/services/sqlserverflex/testdata/resource-max-private-preview.tf b/stackit/internal/services/sqlserverflex/testdata/resource-max-private-preview.tf new file mode 100644 index 000000000..2873f5972 --- /dev/null +++ b/stackit/internal/services/sqlserverflex/testdata/resource-max-private-preview.tf @@ -0,0 +1,79 @@ +variable "project_id" {} +variable "name" {} +variable "acl1" {} +variable "flavor_id" {} +variable "storage_class" {} +variable "storage_size" {} +variable "access_scope" {} +variable "retention_days" {} +variable "backup_schedule" {} +variable "username" {} +variable "role" {} +variable "server_version" {} +variable "region" {} + +variable "kek_key_version" {} +variable "service_account_email" {} + +variable "keyring_display_name" {} +variable "display_name" {} +variable "protection" {} +variable "algorithm" {} +variable "purpose" {} + + +resource "stackit_kms_keyring" "keyring" { + project_id = var.project_id + display_name = var.keyring_display_name +} + +resource "stackit_kms_key" "key" { + project_id = var.project_id + keyring_id = stackit_kms_keyring.keyring.keyring_id + protection = var.protection + algorithm = var.algorithm + display_name = var.display_name + purpose = var.purpose +} + +resource "stackit_sqlserverflex_instance" "instance" { + project_id = var.project_id + name = var.name + flavor_id = var.flavor_id + storage = { + class = var.storage_class + size = var.storage_size + } + network = { + acl = [var.acl1] + access_scope = var.access_scope + } + retention_days = var.retention_days + version = var.server_version + encryption = { + kek_key_id = stackit_kms_key.key.key_id + kek_keyring_id = stackit_kms_keyring.keyring.keyring_id + kek_key_version = var.kek_key_version + service_account = var.service_account_email + } + backup_schedule = var.backup_schedule + region = var.region +} + +resource "stackit_sqlserverflex_user" "user" { + project_id = stackit_sqlserverflex_instance.instance.project_id + instance_id = stackit_sqlserverflex_instance.instance.instance_id + username = var.username + roles = [var.role] +} + +data "stackit_sqlserverflex_instance" "instance" { + project_id = var.project_id + instance_id = stackit_sqlserverflex_instance.instance.instance_id +} + +data "stackit_sqlserverflex_user" "user" { + project_id = var.project_id + instance_id = stackit_sqlserverflex_instance.instance.instance_id + user_id = stackit_sqlserverflex_user.user.user_id +}