Skip to content
2 changes: 2 additions & 0 deletions docs/command_adv2v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ dynamic "tags" {
### Dynamic blocks in region_configs

You can use `dynamic` blocks for `region_configs`. The plugin assumes that the value of `for_each` is an expression which evaluates to a `list` of objects.
**Note:** `map` and `set` are not supported.

This is an example of how to use dynamic blocks in `region_configs`:
```hcl
Expand All @@ -81,6 +82,7 @@ replication_specs {
### Dynamic blocks in replication_specs

You can use `dynamic` blocks for `replication_specs`. The plugin assumes that the value of `for_each` is an expression which evaluates to a `list` of objects.
**Note:** `map` and `set` are not supported.

This is an example of how to use dynamic blocks in `replication_specs`:
```hcl
Expand Down
2 changes: 2 additions & 0 deletions docs/command_clu2adv.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ dynamic "tags" {
### Dynamic blocks in regions_config

You can use `dynamic` blocks for `regions_config`. The plugin assumes that the value of `for_each` is an expression which evaluates to a `list` of objects.
**Note:** `map` and `set` are not supported.

This is an example of how to use dynamic blocks in `regions_config`:
```hcl
Expand All @@ -77,6 +78,7 @@ replication_specs {
### Dynamic blocks in replication_specs

You can use `dynamic` blocks for `replication_specs`. The plugin assumes that the value of `for_each` is an expression which evaluates to a `list` of objects.
**Note:** `map` and `set` are not supported.

This is an example of how to use dynamic blocks in `replication_specs`:
```hcl
Expand Down
90 changes: 88 additions & 2 deletions internal/convert/adv2v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,94 @@ func convertRepSpecsWithDynamicBlock(resourceb *hclwrite.Body, diskSizeGB hclwri
if err != nil {
return dynamicBlock{}, err
}
forSpec := hcl.TokensFromExpr(buildForExpr(nSpec, hcl.GetAttrExpr(dSpec.forEach), true))
dSpec.tokens = hcl.TokensFuncFlatten(append(forSpec, dConfig.tokens...))

// Check if we have a dynamic region_configs block that was successfully processed
if dConfig.tokens != nil {
forSpec := hcl.TokensFromExpr(buildForExpr(nSpec, hcl.GetAttrExpr(dSpec.forEach), true))
dSpec.tokens = hcl.TokensFuncFlatten(append(forSpec, dConfig.tokens...))
return dSpec, nil
}

// Handle static region_configs blocks inside dynamic replication_specs
specBody := dSpec.content.Body()

// Collect static region_configs blocks
staticConfigs := collectBlocks(specBody, nConfig)
if len(staticConfigs) == 0 {
// No static blocks found, this might be an error case
// Check if there's also no dynamic block (which would have been handled above)
hasDynamicBlock := false
for _, block := range specBody.Blocks() {
if block.Type() == nDynamic && getResourceName(block) == nConfig {
hasDynamicBlock = true
break
}
}
if !hasDynamicBlock {
return dynamicBlock{}, fmt.Errorf("replication_specs must have at least one region_configs")
}
// There's a dynamic block but convertConfigsWithDynamicBlock returned empty
// This shouldn't happen, but return the error from that function
return dynamicBlock{}, nil
}

repSpecb := hclwrite.NewEmptyFile().Body()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I believe this drops any user comments in the block, is that correct? if yes, that's probably ok for a converter but maybe we should mention in the docs?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maastha this is also happening in other places, good idea about mentioning, WDYT about this: 7584850


// Handle zone_name attribute
if zoneNameAttr := specBody.GetAttribute(nZoneName); zoneNameAttr != nil {
zoneNameExpr := transformReference(hcl.GetAttrExpr(zoneNameAttr), nRepSpecs, nSpec)
repSpecb.SetAttributeRaw(nZoneName, hcl.TokensFromExpr(zoneNameExpr))
}

// Process static region_configs blocks
var configs []*hclwrite.Body
for _, configBlock := range staticConfigs {
configBlockb := configBlock.Body()
// Create a new body with sorted attributes
newConfigBody := hclwrite.NewEmptyFile().Body()

// Copy attributes in the expected order
attrs := configBlockb.Attributes()
// Priority, provider_name, region_name should come first
if priority := attrs["priority"]; priority != nil {
newConfigBody.SetAttributeRaw("priority", priority.Expr().BuildTokens(nil))
}
if provider := attrs["provider_name"]; provider != nil {
newConfigBody.SetAttributeRaw("provider_name", provider.Expr().BuildTokens(nil))
}
if region := attrs["region_name"]; region != nil {
newConfigBody.SetAttributeRaw("region_name", region.Expr().BuildTokens(nil))
}

// Process spec blocks and convert them to attributes
for _, block := range configBlockb.Blocks() {
blockType := block.Type()
blockBody := hclwrite.NewEmptyFile().Body()
copyAttributesSorted(blockBody, block.Body().Attributes())
if diskSizeGB != nil &&
(blockType == nElectableSpecs || blockType == nReadOnlySpecs || blockType == nAnalyticsSpecs) {
blockBody.SetAttributeRaw(nDiskSizeGB, diskSizeGB)
}
newConfigBody.SetAttributeRaw(blockType, hcl.TokensObject(blockBody))
}

configs = append(configs, newConfigBody)
}

repSpecb.SetAttributeRaw(nConfig, hcl.TokensArray(configs))

// Handle num_shards attribute
if numShardsAttr := specBody.GetAttribute(nNumShards); numShardsAttr != nil {
numShardsExpr := transformReference(hcl.GetAttrExpr(numShardsAttr), nRepSpecs, nSpec)
forSpec := hcl.TokensFromExpr(buildForExpr(nSpec, hcl.GetAttrExpr(dSpec.forEach), true))
innerFor := hcl.TokensFromExpr(buildForExpr("i", fmt.Sprintf("range(%s)", numShardsExpr), false))
innerFor = append(innerFor, hcl.TokensObject(repSpecb)...)
dSpec.tokens = hcl.TokensFuncFlatten(append(forSpec, hcl.EncloseBracketsNewLines(innerFor)...))
} else {
forSpec := hcl.TokensFromExpr(buildForExpr(nSpec, hcl.GetAttrExpr(dSpec.forEach), true))
dSpec.tokens = hcl.TokensFuncFlatten(append(forSpec, hcl.TokensArraySingle(repSpecb)...))
}

return dSpec, nil
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
locals {
replication_specs_list = [
{
zone_name = "zone1"
region_name = "US_EAST_1"
},
{
zone_name = "zone2"
region_name = "US_WEST_2"
}
]
}

resource "mongodbatlas_advanced_cluster" "one_config" {
project_id = "123"
name = "cluster"
cluster_type = "SHARDED"

dynamic "replication_specs" {
for_each = local.replication_specs_list
content {
num_shards = 2
zone_name = replication_specs.value.zone_name

region_configs {
provider_name = "AWS"
region_name = replication_specs.value.region_name
priority = 7

electable_specs {
instance_size = "M10"
node_count = 3
}
auto_scaling {
disk_gb_enabled = true
}
}
}
}
}

resource "mongodbatlas_advanced_cluster" "multiple_config" {
project_id = "123"
name = "cluster"
cluster_type = "SHARDED"

dynamic "replication_specs" {
for_each = local.replication_specs_list
content {
num_shards = 2
zone_name = replication_specs.value.zone_name

region_configs {
provider_name = "AWS"
region_name = replication_specs.value.region_name
priority = 7

electable_specs {
instance_size = "M10"
node_count = 2
}
auto_scaling {
disk_gb_enabled = true
}
}

region_configs {
provider_name = "AWS"
region_name = replication_specs.value.region_name
priority = 6

electable_specs {
instance_size = "M10"
node_count = 1
}
auto_scaling {
disk_gb_enabled = true
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
locals {
replication_specs_list = [
{
zone_name = "zone1"
region_name = "US_EAST_1"
},
{
zone_name = "zone2"
region_name = "US_WEST_2"
}
]
}

resource "mongodbatlas_advanced_cluster" "one_config" {
project_id = "123"
name = "cluster"
cluster_type = "SHARDED"

replication_specs = flatten([
for spec in local.replication_specs_list : [
for i in range(2) : {
zone_name = spec.zone_name
region_configs = [
{
priority = 7
provider_name = "AWS"
region_name = spec.region_name
electable_specs = {
instance_size = "M10"
node_count = 3
}
auto_scaling = {
disk_gb_enabled = true
}
}
]
}
]
])

# Updated by atlas-cli-plugin-terraform, please review the changes.
}

resource "mongodbatlas_advanced_cluster" "multiple_config" {
project_id = "123"
name = "cluster"
cluster_type = "SHARDED"

replication_specs = flatten([
for spec in local.replication_specs_list : [
for i in range(2) : {
zone_name = spec.zone_name
region_configs = [
{
priority = 7
provider_name = "AWS"
region_name = spec.region_name
electable_specs = {
instance_size = "M10"
node_count = 2
}
auto_scaling = {
disk_gb_enabled = true
}
},
{
priority = 6
provider_name = "AWS"
region_name = spec.region_name
electable_specs = {
instance_size = "M10"
node_count = 1
}
auto_scaling = {
disk_gb_enabled = true
}
}
]
}
]
])

# Updated by atlas-cli-plugin-terraform, please review the changes.
}