diff --git a/internal/service/advancedcluster/resource_advanced_cluster_test.go b/internal/service/advancedcluster/resource_advanced_cluster_test.go index fbd52c5fcc..e01bb85483 100644 --- a/internal/service/advancedcluster/resource_advanced_cluster_test.go +++ b/internal/service/advancedcluster/resource_advanced_cluster_test.go @@ -590,15 +590,15 @@ func TestAccClusterAdvancedCluster_withTags(t *testing.T) { Steps: []resource.TestStep{ { Config: configWithKeyValueBlocks(t, true, orgID, projectName, clusterName, "tags"), - Check: checkKeyValueBlocks(true, clusterName, "tags"), + Check: checkKeyValueBlocks(true, true, "tags"), }, { Config: configWithKeyValueBlocks(t, true, orgID, projectName, clusterName, "tags", acc.ClusterTagsMap1, acc.ClusterTagsMap2), - Check: checkKeyValueBlocks(true, clusterName, "tags", acc.ClusterTagsMap1, acc.ClusterTagsMap2), + Check: checkKeyValueBlocks(true, true, "tags", acc.ClusterTagsMap1, acc.ClusterTagsMap2), }, { Config: configWithKeyValueBlocks(t, true, orgID, projectName, clusterName, "tags", acc.ClusterTagsMap3), - Check: checkKeyValueBlocks(true, clusterName, "tags", acc.ClusterTagsMap3), + Check: checkKeyValueBlocks(true, true, "tags", acc.ClusterTagsMap3), }, }, }) @@ -618,15 +618,15 @@ func TestAccClusterAdvancedCluster_withLabels(t *testing.T) { Steps: []resource.TestStep{ { Config: configWithKeyValueBlocks(t, true, orgID, projectName, clusterName, "labels"), - Check: checkKeyValueBlocks(true, clusterName, "labels"), + Check: checkKeyValueBlocks(true, true, "labels"), }, { Config: configWithKeyValueBlocks(t, true, orgID, projectName, clusterName, "labels", acc.ClusterLabelsMap1, acc.ClusterLabelsMap2), - Check: checkKeyValueBlocks(true, clusterName, "labels", acc.ClusterLabelsMap1, acc.ClusterLabelsMap2), + Check: checkKeyValueBlocks(true, true, "labels", acc.ClusterLabelsMap1, acc.ClusterLabelsMap2), }, { Config: configWithKeyValueBlocks(t, true, orgID, projectName, clusterName, "labels", acc.ClusterLabelsMap3), - Check: checkKeyValueBlocks(true, clusterName, "labels", acc.ClusterLabelsMap3), + Check: checkKeyValueBlocks(true, true, "labels", acc.ClusterLabelsMap3), }, }, }) @@ -1134,15 +1134,14 @@ func TestAccMockableAdvancedCluster_replicasetAdvConfigUpdate(t *testing.T) { "replication_specs.0.container_id.AWS:US_EAST_1", } timeoutCheck = resource.TestCheckResourceAttr(resourceName, "timeouts.create", "6000s") // timeouts.create is not set on data sources + tagsLabelsMap = map[string]string{"key": "env", "value": "test"} + tagsCheck = checkKeyValueBlocks(true, false, "tags", tagsLabelsMap) + labelsCheck = checkKeyValueBlocks(true, false, "labels", tagsLabelsMap) checks = checkAggr(true, checksSet, checksMap, timeoutCheck) afterUpdateMap = map[string]string{ "state_name": "IDLE", "backup_enabled": "true", "bi_connector_config.0.enabled": "true", - "labels.0.key": "env", - "labels.0.value": "test", - "tags.0.key": "env", - "tags.0.value": "test", "mongo_db_major_version": "8.0", "pit_enabled": "true", "redact_client_log_data": "true", @@ -1162,7 +1161,7 @@ func TestAccMockableAdvancedCluster_replicasetAdvConfigUpdate(t *testing.T) { "advanced_configuration.0.custom_openssl_cipher_config_tls12.#": "1", "advanced_configuration.0.default_max_time_ms": "65", } - checksUpdate = checkAggr(true, checksSet, afterUpdateMap, timeoutCheck) + checksUpdate = checkAggr(true, checksSet, afterUpdateMap, timeoutCheck, tagsCheck, labelsCheck) fullUpdate = ` backup_enabled = true bi_connector_config { @@ -1479,28 +1478,60 @@ func configWithKeyValueBlocks(t *testing.T, isAcc bool, orgID, projectName, clus `, orgID, projectName, clusterName, extraConfig)) + dataSourcesTFNewSchema } -func checkKeyValueBlocks(isAcc bool, clusterName, blockName string, blocks ...map[string]string) resource.TestCheckFunc { +func checkKeyValueBlocks(isAcc, includeDataSources bool, blockName string, blocks ...map[string]string) resource.TestCheckFunc { + if config.AdvancedClusterV2Schema() { + return checkKeyValueBlocksSchemaV2(isAcc, includeDataSources, blockName, blocks...) + } const pluralPrefix = "results.0." lenStr := strconv.Itoa(len(blocks)) - keyHash := fmt.Sprintf("%s.#", blockName) - keyStar := fmt.Sprintf("%s.*", blockName) + keyHash := blockName + ".#" + keyStar := blockName + ".*" checks := []resource.TestCheckFunc{ acc.TestCheckResourceAttrSchemaV2(isAcc, resourceName, keyHash, lenStr), - acc.TestCheckResourceAttrSchemaV2(isAcc, dataSourceName, keyHash, lenStr), - acc.TestCheckResourceAttrSchemaV2(isAcc, dataSourcePluralName, pluralPrefix+keyHash, lenStr), + } + if includeDataSources { + checks = append(checks, + acc.TestCheckResourceAttrSchemaV2(isAcc, dataSourceName, keyHash, lenStr), + acc.TestCheckResourceAttrSchemaV2(isAcc, dataSourcePluralName, pluralPrefix+keyHash, lenStr)) } for _, block := range blocks { checks = append(checks, acc.TestCheckTypeSetElemNestedAttrsSchemaV2(isAcc, resourceName, keyStar, block), - acc.TestCheckTypeSetElemNestedAttrsSchemaV2(isAcc, dataSourceName, keyStar, block), - acc.TestCheckTypeSetElemNestedAttrsSchemaV2(isAcc, dataSourcePluralName, pluralPrefix+keyStar, block)) + ) + if includeDataSources { + checks = append(checks, + acc.TestCheckTypeSetElemNestedAttrsSchemaV2(isAcc, dataSourceName, keyStar, block), + acc.TestCheckTypeSetElemNestedAttrsSchemaV2(isAcc, dataSourcePluralName, pluralPrefix+keyStar, block)) + } } - return checkAggr(isAcc, - []string{"project_id"}, - map[string]string{ - "name": clusterName, - }, - checks...) + return resource.ComposeAggregateTestCheckFunc(checks...) +} + +func checkKeyValueBlocksSchemaV2(isAcc, includeDataSources bool, blockName string, blocks ...map[string]string) resource.TestCheckFunc { + const pluralPrefix = "results.0." + lenStr := strconv.Itoa(len(blocks)) + keyPct := blockName + ".%" + checks := []resource.TestCheckFunc{ + acc.TestCheckResourceAttrSchemaV2(isAcc, resourceName, keyPct, lenStr), + } + if includeDataSources { + checks = append(checks, + acc.TestCheckResourceAttrSchemaV2(isAcc, dataSourceName, keyPct, lenStr), + acc.TestCheckResourceAttrSchemaV2(isAcc, dataSourcePluralName, pluralPrefix+keyPct, lenStr)) + } + for _, block := range blocks { + key := blockName + "." + block["key"] + value := block["value"] + checks = append(checks, + acc.TestCheckResourceAttrSchemaV2(isAcc, resourceName, key, value), + ) + if includeDataSources { + checks = append(checks, + acc.TestCheckResourceAttrSchemaV2(isAcc, dataSourceName, key, value), + acc.TestCheckResourceAttrSchemaV2(isAcc, dataSourcePluralName, pluralPrefix+key, value)) + } + } + return resource.ComposeAggregateTestCheckFunc(checks...) } func configReplicaSetAWSProvider(t *testing.T, isAcc bool, projectID, name string, diskSizeGB, nodeCountElectable int) string { diff --git a/internal/service/advancedclustertpf/model_ClusterDescription20240805.go b/internal/service/advancedclustertpf/model_ClusterDescription20240805.go index e668ac79fb..9c976c6549 100644 --- a/internal/service/advancedclustertpf/model_ClusterDescription20240805.go +++ b/internal/service/advancedclustertpf/model_ClusterDescription20240805.go @@ -30,9 +30,9 @@ type ExtraAPIInfo struct { func NewTFModel(ctx context.Context, input *admin.ClusterDescription20240805, timeout timeouts.Value, diags *diag.Diagnostics, apiInfo ExtraAPIInfo) *TFModel { biConnector := NewBiConnectorConfigObjType(ctx, input.BiConnector, diags) connectionStrings := NewConnectionStringsObjType(ctx, input.ConnectionStrings, diags) - labels := NewLabelsObjType(ctx, input.Labels, diags) + labels := NewLabelsObjType(ctx, diags, input.Labels) replicationSpecs := NewReplicationSpecsObjType(ctx, input.ReplicationSpecs, diags, &apiInfo) - tags := NewTagsObjType(ctx, input.Tags, diags) + tags := NewTagsObjType(ctx, diags, input.Tags) pinnedFCV := NewPinnedFCVObjType(ctx, input, diags) if diags.HasError() { return nil @@ -100,22 +100,19 @@ func NewConnectionStringsObjType(ctx context.Context, input *admin.ClusterConnec return objType } -func NewLabelsObjType(ctx context.Context, input *[]admin.ComponentLabel, diags *diag.Diagnostics) types.Set { - if input == nil { - return types.SetNull(LabelsObjType) - } - tfModels := make([]TFLabelsModel, 0, len(*input)) - for _, item := range *input { - key := conversion.SafeValue(item.Key) - value := conversion.SafeValue(item.Value) - if key == LegacyIgnoredLabelKey { - continue +func NewLabelsObjType(ctx context.Context, diags *diag.Diagnostics, input *[]admin.ComponentLabel) types.Map { + elms := make(map[string]string) + if input != nil { + for _, item := range *input { + key := item.GetKey() + value := item.GetValue() + if key == LegacyIgnoredLabelKey { + continue + } + elms[key] = value } - tfModels = append(tfModels, TFLabelsModel{Key: types.StringValue(key), Value: types.StringValue(value)}) } - setType, diagsLocal := types.SetValueFrom(ctx, LabelsObjType, tfModels) - diags.Append(diagsLocal...) - return setType + return conversion.ToTFMapOfString(ctx, diags, &elms) } func NewReplicationSpecsObjType(ctx context.Context, input *[]admin.ReplicationSpec20240805, diags *diag.Diagnostics, apiInfo *ExtraAPIInfo) types.List { @@ -235,21 +232,14 @@ func convertReplicationSpecsLegacy(ctx context.Context, input *[]admin.Replicati return &tfModels } -func NewTagsObjType(ctx context.Context, input *[]admin.ResourceTag, diags *diag.Diagnostics) types.Set { - if input == nil { - // API Response not consistent, even when not set in POST/PATCH `[]` is returned instead of null - return types.SetValueMust(TagsObjType, nil) - } - tfModels := make([]TFTagsModel, len(*input)) - for i, item := range *input { - tfModels[i] = TFTagsModel{ - Key: types.StringValue(item.Key), - Value: types.StringValue(item.Value), +func NewTagsObjType(ctx context.Context, diags *diag.Diagnostics, input *[]admin.ResourceTag) types.Map { + elms := make(map[string]string) + if input != nil { + for _, item := range *input { + elms[item.GetKey()] = item.GetValue() } } - setType, diagsLocal := types.SetValueFrom(ctx, TagsObjType, tfModels) - diags.Append(diagsLocal...) - return setType + return conversion.ToTFMapOfString(ctx, diags, &elms) } func NewPrivateEndpointObjType(ctx context.Context, input *[]admin.ClusterDescriptionConnectionStringsPrivateEndpoint, diags *diag.Diagnostics) types.List { diff --git a/internal/service/advancedclustertpf/model_to_ClusterDescription20240805.go b/internal/service/advancedclustertpf/model_to_ClusterDescription20240805.go index eca34e7ccf..4ff18eecb4 100644 --- a/internal/service/advancedclustertpf/model_to_ClusterDescription20240805.go +++ b/internal/service/advancedclustertpf/model_to_ClusterDescription20240805.go @@ -31,7 +31,7 @@ func NewAtlasReq(ctx context.Context, input *TFModel, diags *diag.Diagnostics) * ConfigServerManagementMode: conversion.NilForUnknown(input.ConfigServerManagementMode, input.ConfigServerManagementMode.ValueStringPointer()), EncryptionAtRestProvider: conversion.NilForUnknown(input.EncryptionAtRestProvider, input.EncryptionAtRestProvider.ValueStringPointer()), GlobalClusterSelfManagedSharding: conversion.NilForUnknown(input.GlobalClusterSelfManagedSharding, input.GlobalClusterSelfManagedSharding.ValueBoolPointer()), - Labels: newComponentLabel(ctx, input.Labels, diags), + Labels: newComponentLabel(ctx, diags, input.Labels), MongoDBMajorVersion: majorVersion, Name: input.Name.ValueStringPointer(), Paused: conversion.NilForUnknown(input.Paused, input.Paused.ValueBoolPointer()), @@ -40,7 +40,7 @@ func NewAtlasReq(ctx context.Context, input *TFModel, diags *diag.Diagnostics) * ReplicaSetScalingStrategy: conversion.NilForUnknown(input.ReplicaSetScalingStrategy, input.ReplicaSetScalingStrategy.ValueStringPointer()), ReplicationSpecs: newReplicationSpec20240805(ctx, input.ReplicationSpecs, diags), RootCertType: conversion.NilForUnknown(input.RootCertType, input.RootCertType.ValueStringPointer()), - Tags: newResourceTag(ctx, input.Tags, diags), + Tags: newResourceTag(ctx, diags, input.Tags), TerminationProtectionEnabled: conversion.NilForUnknown(input.TerminationProtectionEnabled, input.TerminationProtectionEnabled.ValueBoolPointer()), VersionReleaseSystem: conversion.NilForUnknown(input.VersionReleaseSystem, input.VersionReleaseSystem.ValueStringPointer()), } @@ -60,29 +60,28 @@ func newBiConnector(ctx context.Context, input types.Object, diags *diag.Diagnos ReadPreference: conversion.NilForUnknown(item.ReadPreference, item.ReadPreference.ValueStringPointer()), } } -func newComponentLabel(ctx context.Context, input types.Set, diags *diag.Diagnostics) *[]admin.ComponentLabel { - if input.IsUnknown() { - return nil - } - elements := make([]TFLabelsModel, len(input.Elements())) - if localDiags := input.ElementsAs(ctx, &elements, false); len(localDiags) > 0 { - diags.Append(localDiags...) + +func newComponentLabel(ctx context.Context, diags *diag.Diagnostics, input types.Map) *[]admin.ComponentLabel { + elms := make(map[string]types.String, len(input.Elements())) + localDiags := input.ElementsAs(ctx, &elms, false) + diags.Append(localDiags...) + if diags.HasError() { return nil } - resp := make([]admin.ComponentLabel, len(input.Elements())) - for i := range elements { - item := &elements[i] - if item.Key.ValueString() == LegacyIgnoredLabelKey { + ret := make([]admin.ComponentLabel, 0, len(input.Elements())) + for key, value := range elms { + if key == LegacyIgnoredLabelKey { diags.AddError(ErrLegacyIgnoreLabel.Error(), ErrLegacyIgnoreLabel.Error()) return nil } - resp[i] = admin.ComponentLabel{ - Key: item.Key.ValueStringPointer(), - Value: item.Value.ValueStringPointer(), - } + ret = append(ret, admin.ComponentLabel{ + Key: &key, + Value: value.ValueStringPointer(), + }) } - return &resp + return &ret } + func newReplicationSpec20240805(ctx context.Context, input types.List, diags *diag.Diagnostics) *[]admin.ReplicationSpec20240805 { if input.IsUnknown() || input.IsNull() { return nil @@ -113,25 +112,23 @@ func resolveZoneNameOrUseDefault(item *TFReplicationSpecsModel) string { return *zoneName } -func newResourceTag(ctx context.Context, input types.Set, diags *diag.Diagnostics) *[]admin.ResourceTag { - if input.IsUnknown() { +func newResourceTag(ctx context.Context, diags *diag.Diagnostics, input types.Map) *[]admin.ResourceTag { + elms := make(map[string]types.String, len(input.Elements())) + localDiags := input.ElementsAs(ctx, &elms, false) + diags.Append(localDiags...) + if diags.HasError() { return nil } - elements := make([]TFTagsModel, len(input.Elements())) - if localDiags := input.ElementsAs(ctx, &elements, false); len(localDiags) > 0 { - diags.Append(localDiags...) - return nil + ret := make([]admin.ResourceTag, 0, len(input.Elements())) + for key, value := range elms { + ret = append(ret, admin.ResourceTag{ + Key: key, + Value: value.ValueString(), + }) } - resp := make([]admin.ResourceTag, len(input.Elements())) - for i := range elements { - item := &elements[i] - resp[i] = admin.ResourceTag{ - Key: item.Key.ValueString(), - Value: item.Value.ValueString(), - } - } - return &resp + return &ret } + func newCloudRegionConfig20240805(ctx context.Context, input types.List, diags *diag.Diagnostics) *[]admin.CloudRegionConfig20240805 { if input.IsUnknown() || input.IsNull() { return nil diff --git a/internal/service/advancedclustertpf/move_upgrade_state.go b/internal/service/advancedclustertpf/move_upgrade_state.go index 2154d7b5e2..1975d19526 100644 --- a/internal/service/advancedclustertpf/move_upgrade_state.go +++ b/internal/service/advancedclustertpf/move_upgrade_state.go @@ -94,6 +94,11 @@ func setStateResponse(ctx context.Context, diags *diag.Diagnostics, stateIn *tfp return } setOptionalModelAttrs(ctx, stateObj, model) + + // Set tags and labels to null instead of empty so there is no plan change if there are no tags or labels when Read is called. + model.Tags = types.MapNull(types.StringType) + model.Labels = types.MapNull(types.StringType) + diags.Append(stateOut.Set(ctx, model)...) } diff --git a/internal/service/advancedclustertpf/resource.go b/internal/service/advancedclustertpf/resource.go index 661e042c34..4a20db9970 100644 --- a/internal/service/advancedclustertpf/resource.go +++ b/internal/service/advancedclustertpf/resource.go @@ -244,8 +244,11 @@ func (r *rs) Update(ctx context.Context, req resource.UpdateRequest, resp *resou if diags.HasError() { return } - modelOut := &state - if clusterResp != nil { + var modelOut *TFModel + if clusterResp == nil { // no Atlas updates needed but override is still needed (e.g. tags going from nil to [] or vice versa) + modelOut = &state + overrideAttributesWithPrevStateValue(&plan, modelOut) + } else { modelOut, _ = getBasicClusterModel(ctx, diags, r.Client, clusterResp, &plan, false) if diags.HasError() { return diff --git a/internal/service/advancedclustertpf/resource_compatiblity.go b/internal/service/advancedclustertpf/resource_compatiblity.go index 0f10517e7b..4fdd47c677 100644 --- a/internal/service/advancedclustertpf/resource_compatiblity.go +++ b/internal/service/advancedclustertpf/resource_compatiblity.go @@ -22,6 +22,18 @@ func overrideAttributesWithPrevStateValue(modelIn, modelOut *TFModel) { if retainBackups != nil && !modelIn.RetainBackupsEnabled.Equal(modelOut.RetainBackupsEnabled) { modelOut.RetainBackupsEnabled = types.BoolPointerValue(retainBackups) } + overrideMapStringWithPrevStateValue(&modelIn.Labels, &modelOut.Labels) + overrideMapStringWithPrevStateValue(&modelIn.Tags, &modelOut.Tags) +} +func overrideMapStringWithPrevStateValue(mapIn, mapOut *types.Map) { + if mapIn == nil || mapOut == nil || len(mapOut.Elements()) > 0 { + return + } + if mapIn.IsNull() { + *mapOut = types.MapNull(types.StringType) + } else { + *mapOut = types.MapValueMust(types.StringType, nil) + } } func findNumShardsUpdates(ctx context.Context, state, plan *TFModel, diags *diag.Diagnostics) map[string]int64 { diff --git a/internal/service/advancedclustertpf/schema.go b/internal/service/advancedclustertpf/schema.go index bf9544c37f..6890768fa1 100644 --- a/internal/service/advancedclustertpf/schema.go +++ b/internal/service/advancedclustertpf/schema.go @@ -308,44 +308,22 @@ func resourceSchema(ctx context.Context) schema.Schema { }, }, }, + "labels": schema.MapAttribute{ + ElementType: types.StringType, + Optional: true, + MarkdownDescription: "Map of key-value pairs between 1 to 255 characters in length that tag and categorize the cluster. The MongoDB Cloud console doesn't display your labels.\n\nCluster labels are deprecated and will be removed in a future release. We strongly recommend that you use [resource tags](https://dochub.mongodb.org/core/add-cluster-tag-atlas) instead.", + }, + "tags": schema.MapAttribute{ + ElementType: types.StringType, + Optional: true, + MarkdownDescription: "Map that contains key-value pairs between 1 to 255 characters in length for tagging and categorizing the cluster.", + }, "timeouts": timeouts.Attributes(ctx, timeouts.Opts{ Create: true, Update: true, Delete: true, }), }, - Blocks: map[string]schema.Block{ - "labels": schema.SetNestedBlock{ - MarkdownDescription: "Collection of key-value pairs between 1 to 255 characters in length that tag and categorize the cluster. The MongoDB Cloud console doesn't display your labels.\n\nCluster labels are deprecated and will be removed in a future release. We strongly recommend that you use [resource tags](https://dochub.mongodb.org/core/add-cluster-tag-atlas) instead.", - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "key": schema.StringAttribute{ - Required: true, - MarkdownDescription: "Key applied to tag and categorize this component.", - }, - "value": schema.StringAttribute{ - Required: true, - MarkdownDescription: "Value set to the Key applied to tag and categorize this component.", - }, - }, - }, - }, - "tags": schema.SetNestedBlock{ - MarkdownDescription: "List that contains key-value pairs between 1 to 255 characters in length for tagging and categorizing the cluster.", - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "key": schema.StringAttribute{ - Required: true, - MarkdownDescription: "Constant that defines the set of the tag. For example, `environment` in the `environment : production` tag.", - }, - "value": schema.StringAttribute{ - Required: true, - MarkdownDescription: "Variable that belongs to the set of the tag. For example, `production` in the `environment : production` tag.", - }, - }, - }, - }, - }, } } @@ -552,9 +530,9 @@ func AdvancedConfigurationSchema(ctx context.Context) schema.SingleNestedAttribu type TFModel struct { DiskSizeGB types.Float64 `tfsdk:"disk_size_gb"` - Labels types.Set `tfsdk:"labels"` + Labels types.Map `tfsdk:"labels"` ReplicationSpecs types.List `tfsdk:"replication_specs"` - Tags types.Set `tfsdk:"tags"` + Tags types.Map `tfsdk:"tags"` StateName types.String `tfsdk:"state_name"` ConnectionStrings types.Object `tfsdk:"connection_strings"` CreateDate types.String `tfsdk:"create_date"` @@ -587,9 +565,9 @@ type TFModel struct { // TFModelDS differs from TFModel: removes timeouts, accept_data_risks_and_force_replica_set_reconfig; adds use_replication_spec_per_shard. type TFModelDS struct { DiskSizeGB types.Float64 `tfsdk:"disk_size_gb"` - Labels types.Set `tfsdk:"labels"` + Labels types.Map `tfsdk:"labels"` ReplicationSpecs types.List `tfsdk:"replication_specs"` - Tags types.Set `tfsdk:"tags"` + Tags types.Map `tfsdk:"tags"` ReplicaSetScalingStrategy types.String `tfsdk:"replica_set_scaling_strategy"` Name types.String `tfsdk:"name"` AdvancedConfiguration types.Object `tfsdk:"advanced_configuration"` @@ -679,16 +657,6 @@ var EndpointsObjType = types.ObjectType{AttrTypes: map[string]attr.Type{ "region": types.StringType, }} -type TFLabelsModel struct { - Key types.String `tfsdk:"key"` - Value types.String `tfsdk:"value"` -} - -var LabelsObjType = types.ObjectType{AttrTypes: map[string]attr.Type{ - "key": types.StringType, - "value": types.StringType, -}} - type TFReplicationSpecsModel struct { RegionConfigs types.List `tfsdk:"region_configs"` ContainerId types.Map `tfsdk:"container_id"` @@ -765,16 +733,6 @@ var SpecsObjType = types.ObjectType{AttrTypes: map[string]attr.Type{ "node_count": types.Int64Type, }} -type TFTagsModel struct { - Key types.String `tfsdk:"key"` - Value types.String `tfsdk:"value"` -} - -var TagsObjType = types.ObjectType{AttrTypes: map[string]attr.Type{ - "key": types.StringType, - "value": types.StringType, -}} - type TFAdvancedConfigurationModel struct { OplogMinRetentionHours types.Float64 `tfsdk:"oplog_min_retention_hours"` CustomOpensslCipherConfigTls12 types.Set `tfsdk:"custom_openssl_cipher_config_tls12"` diff --git a/internal/testutil/acc/advanced_cluster_schema_v2.go b/internal/testutil/acc/advanced_cluster_schema_v2.go index e59001e471..b3d5939d62 100644 --- a/internal/testutil/acc/advanced_cluster_schema_v2.go +++ b/internal/testutil/acc/advanced_cluster_schema_v2.go @@ -109,6 +109,8 @@ func ConvertAdvancedClusterToSchemaV2(t *testing.T, isAcc bool, def string) stri convertAttrs(t, "bi_connector_config", writeBody, false, hcl.GetAttrVal) convertAttrs(t, "pinned_fcv", writeBody, false, hcl.GetAttrVal) convertAttrs(t, "timeouts", writeBody, false, hcl.GetAttrVal) + convertKeyValueAttrs(t, "labels", writeBody) + convertKeyValueAttrs(t, "tags", writeBody) } content := parse.Bytes() return string(content) @@ -145,6 +147,25 @@ func convertAttrs(t *testing.T, name string, writeBody *hclwrite.Body, isList bo } } +func convertKeyValueAttrs(t *testing.T, name string, writeBody *hclwrite.Body) { + t.Helper() + vals := make(map[string]cty.Value) + for { + match := writeBody.FirstMatchingBlock(name, nil) + if match == nil { + break + } + attrs := hcl.GetAttrVal(t, hcl.GetBlockBody(t, match)) + key := attrs.GetAttr("key") + value := attrs.GetAttr("value") + vals[key.AsString()] = value + writeBody.RemoveBlock(match) // TODO: RemoveBlock doesn't remove newline just after the block so an extra line is added + } + if len(vals) > 0 { + writeBody.SetAttributeValue(name, cty.ObjectVal(vals)) + } +} + func getReplicationSpecs(t *testing.T, body *hclsyntax.Body) cty.Value { t.Helper() const name = "region_configs" diff --git a/internal/testutil/acc/advanced_cluster_schema_v2_test.go b/internal/testutil/acc/advanced_cluster_schema_v2_test.go index c6d3172c2c..8a405c1cdc 100644 --- a/internal/testutil/acc/advanced_cluster_schema_v2_test.go +++ b/internal/testutil/acc/advanced_cluster_schema_v2_test.go @@ -126,6 +126,16 @@ func TestConvertAdvancedClusterToSchemaV2(t *testing.T) { value = "Value Label 3" } + labels { + key = "label" + value = "labelvalue" + } + + tags { + key = "tag" + value = "tagvalue" + } + advanced_configuration { fail_index_key_too_long = false javascript_enabled = true @@ -155,31 +165,13 @@ func TestConvertAdvancedClusterToSchemaV2(t *testing.T) { cluster_type = "SHARDED" - - tags { - key = "Key Tag 2" - value = "Value Tag 2" - } - labels { - key = "Key Label 1" - value = "Value Label 1" - } - tags { - key = "Key Tag 1" - value = "Value Tag 1" - } - labels { - key = "Key Label 2" - value = "Value Label 2" - } - labels { - key = "Key Label 3" - value = "Value Label 3" - } + + + replication_specs = [{ @@ -242,6 +234,17 @@ func TestConvertAdvancedClusterToSchemaV2(t *testing.T) { timeouts = { create = "5m" } + labels = { + "Key Label 1" = "Value Label 1" + "Key Label 2" = "Value Label 2" + "Key Label 3" = "Value Label 3" + label = "labelvalue" + } + tags = { + "Key Tag 1" = "Value Tag 1" + "Key Tag 2" = "Value Tag 2" + tag = "tagvalue" + } } ` )