Skip to content
6 changes: 3 additions & 3 deletions internal/cli/clu2adv/clu2adv.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import (

func Builder() *cobra.Command {
o := &struct {
*cli.BaseOpts
cli.BaseOpts
includeMoved bool
}{
BaseOpts: &cli.BaseOpts{
BaseOpts: cli.BaseOpts{
Fs: afero.NewOsFs(),
},
}
Expand All @@ -28,7 +28,7 @@ func Builder() *cobra.Command {
Aliases: []string{"clu2adv"},
RunE: o.RunE,
}
cli.SetupCommonFlags(cmd, o.BaseOpts)
cli.SetupCommonFlags(cmd, &o.BaseOpts)
cmd.Flags().BoolVarP(&o.includeMoved, flag.IncludeMoved, flag.IncludeMovedShort, false,
"include moved blocks in the output file")
return cmd
Expand Down
93 changes: 92 additions & 1 deletion internal/convert/adv2v2.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package convert

import "github.com/mongodb-labs/atlas-cli-plugin-terraform/internal/hcl"
import (
"slices"

"github.com/hashicorp/hcl/v2/hclwrite"
"github.com/mongodb-labs/atlas-cli-plugin-terraform/internal/hcl"
)

// AdvancedClusterToV2 transforms all mongodbatlas_advanced_cluster resource definitions in a
// Terraform configuration file from SDKv2 schema to TPF (Terraform Plugin Framework) schema.
Expand All @@ -11,5 +16,91 @@ func AdvancedClusterToV2(config []byte) ([]byte, error) {
if err != nil {
return nil, err
}
parserb := parser.Body()
for _, block := range parserb.Blocks() {
updated, err := updateResource(block)
if err != nil {
return nil, err
}
if updated { // If the resource was converted, add a comment at the end so user knows the resource was updated
blockb := block.Body()
blockb.AppendNewline()
hcl.AppendComment(blockb, commentUpdatedBy)
}
}
return parser.Bytes(), nil
}

func updateResource(resource *hclwrite.Block) (bool, error) {
if resource.Type() != resourceType || getResourceName(resource) != advCluster {
return false, nil
}
resourceb := resource.Body()
if hasExpectedBlocksAsAttributes(resourceb) {
return false, nil
}
if err := convertRepSpecs(resourceb); err != nil {
return false, err
}
if err := fillTagsLabelsOpt(resourceb, nTags); err != nil {
return false, err
}
if err := fillTagsLabelsOpt(resourceb, nLabels); err != nil {
return false, err
}
fillBlockOpt(resourceb, nAdvConf)
fillBlockOpt(resourceb, nBiConnector)
fillBlockOpt(resourceb, nPinnedFCV)
fillBlockOpt(resourceb, nTimeouts)
return true, nil
}

func convertRepSpecs(resourceb *hclwrite.Body) error {
block := resourceb.FirstMatchingBlock(nRepSpecs, nil)
if block == nil {
return nil
}
resourceb.RemoveBlock(block)
if err := convertConfig(block.Body()); err != nil {
return err
}
resourceb.SetAttributeRaw(nRepSpecs, hcl.TokensArraySingle(block.Body()))
return nil
}

func convertConfig(repSpecs *hclwrite.Body) error {
block := repSpecs.FirstMatchingBlock(nConfig, nil)
if block == nil {
return nil
}
repSpecs.RemoveBlock(block)
blockb := block.Body()
fillBlockOpt(blockb, nElectableSpecs)
fillBlockOpt(blockb, nReadOnlySpecs)
fillBlockOpt(blockb, nAnalyticsSpecs)
fillBlockOpt(blockb, nAutoScaling)
fillBlockOpt(blockb, nAnalyticsAutoScaling)
repSpecs.SetAttributeRaw(nConfig, hcl.TokensArraySingle(blockb))
return nil
}

// hasExpectedBlocksAsAttributes checks if any of the expected block names
// exist as attributes in the resource body. In that case conversion is not done
// as advanced cluster is not in a valid SDKv2 configuration.
func hasExpectedBlocksAsAttributes(resourceb *hclwrite.Body) bool {
expectedBlocks := []string{
nRepSpecs,
nTags,
nLabels,
nAdvConf,
nBiConnector,
nPinnedFCV,
nTimeouts,
}
for name := range resourceb.Attributes() {
if slices.Contains(expectedBlocks, name) {
return true
}
}
return false
}
25 changes: 1 addition & 24 deletions internal/convert/clu2adv.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,6 @@ import (
"github.com/zclconf/go-cty/cty"
)

const (
resourceType = "resource"
dataSourceType = "data"
cluster = "mongodbatlas_cluster"
advCluster = "mongodbatlas_advanced_cluster"
clusterPlural = "mongodbatlas_clusters"
advClusterPlural = "mongodbatlas_advanced_clusters"
valClusterType = "REPLICASET"
valMaxPriority = 7
valMinPriority = 0
errFreeCluster = "free cluster (because no " + nRepSpecs + ")"
errRepSpecs = "setting " + nRepSpecs
errConfigs = "setting " + nConfig
errPriority = "setting " + nPriority
errNumShards = "setting " + nNumShards

commentGeneratedBy = "Generated by atlas-cli-plugin-terraform."
commentConfirmReferences = "Please review the changes and confirm that references to this resource are updated."
commentMovedBlock = "Moved blocks"
commentRemovedOld = "Note: Remember to remove or comment out the old cluster definitions."
commentPriorityFor = "Regions must be sorted by priority in descending order."
)

var (
dynamicBlockAllowList = []string{nTags, nLabels, nConfigSrc, nRepSpecs}
)
Expand Down Expand Up @@ -188,10 +165,10 @@ func fillCluster(resourceb *hclwrite.Body) error {
if err := fillTagsLabelsOpt(resourceb, nLabels); err != nil {
return err
}
fillBlockOpt(resourceb, nTimeouts)
fillBlockOpt(resourceb, nAdvConf)
fillBlockOpt(resourceb, nBiConnector)
fillBlockOpt(resourceb, nPinnedFCV)
fillBlockOpt(resourceb, nTimeouts)
return nil
}

Expand Down
22 changes: 22 additions & 0 deletions internal/convert/const_names.go → internal/convert/const.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,27 @@
package convert

const (
resourceType = "resource"
dataSourceType = "data"
cluster = "mongodbatlas_cluster"
advCluster = "mongodbatlas_advanced_cluster"
clusterPlural = "mongodbatlas_clusters"
advClusterPlural = "mongodbatlas_advanced_clusters"
valClusterType = "REPLICASET"
valMaxPriority = 7
valMinPriority = 0
errFreeCluster = "free cluster (because no " + nRepSpecs + ")"
errRepSpecs = "setting " + nRepSpecs
errPriority = "setting " + nPriority
errNumShards = "setting " + nNumShards

commentGeneratedBy = "Generated by atlas-cli-plugin-terraform."
commentConfirmReferences = "Please review the changes and confirm that references to this resource are updated."
commentUpdatedBy = "Updated by atlas-cli-plugin-terraform, please review the changes."
commentMovedBlock = "Moved blocks"
commentRemovedOld = "Note: Remember to remove or comment out the old cluster definitions."
commentPriorityFor = "Regions must be sorted by priority in descending order."

nRepSpecs = "replication_specs"
nConfig = "region_configs"
nConfigSrc = "regions_config"
Expand All @@ -12,6 +33,7 @@ const (
nBiConnector = "bi_connector_config"
nElectableSpecs = "electable_specs"
nAutoScaling = "auto_scaling"
nAnalyticsAutoScaling = "analytics_auto_scaling"
nReadOnlySpecs = "read_only_specs"
nAnalyticsSpecs = "analytics_specs"
nRegionNameSrc = "provider_region_name"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
resource "mongodbatlas_advanced_cluster" "this" {
project_id = var.project_id
name = var.cluster_name
cluster_type = "REPLICASET"
replication_specs {
region_configs {
priority = 7
provider_name = "AWS"
region_name = "EU_WEST_1"
electable_specs {
instance_size = "M10"
node_count = 3
}
}
}

advanced_configuration {
# comments in advanced_configuration are kept
javascript_enabled = true
}

bi_connector_config {
# comments in bi_connector_config are kept
enabled = true
read_preference = "secondary"
}

pinned_fcv {
# comments in pinned_fcv are kept
expiration_date = var.fcv_date
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
resource "mongodbatlas_advanced_cluster" "this" {
project_id = var.project_id
name = var.cluster_name
cluster_type = "REPLICASET"



replication_specs = [
{
region_configs = [
{
priority = 7
provider_name = "AWS"
region_name = "EU_WEST_1"
electable_specs = {
instance_size = "M10"
node_count = 3
}
}
]
}
]
advanced_configuration = {
# comments in advanced_configuration are kept
javascript_enabled = true
}
bi_connector_config = {
# comments in bi_connector_config are kept
enabled = true
read_preference = "secondary"
}
pinned_fcv = {
# comments in pinned_fcv are kept
expiration_date = var.fcv_date
}

# Updated by atlas-cli-plugin-terraform, please review the changes.
}
18 changes: 15 additions & 3 deletions internal/convert/testdata/adv2v2/basic.in.tf
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
resource "mongodbatlas_advanced_cluster" "basic" {
name = "basic"
// TODO: missing fields as transformation is not implemented yet
resource "mongodbatlas_advanced_cluster" "clu" {
project_id = var.project_id
name = "clu"
cluster_type = "REPLICASET"
replication_specs {
region_configs {
priority = 7
provider_name = "AWS"
region_name = "EU_WEST_1"
electable_specs {
instance_size = "M10"
node_count = 3
}
}
}
}
24 changes: 21 additions & 3 deletions internal/convert/testdata/adv2v2/basic.out.tf
Original file line number Diff line number Diff line change
@@ -1,4 +1,22 @@
resource "mongodbatlas_advanced_cluster" "basic" {
name = "basic"
// TODO: missing fields as transformation is not implemented yet
resource "mongodbatlas_advanced_cluster" "clu" {
project_id = var.project_id
name = "clu"
cluster_type = "REPLICASET"
replication_specs = [
{
region_configs = [
{
priority = 7
provider_name = "AWS"
region_name = "EU_WEST_1"
electable_specs = {
instance_size = "M10"
node_count = 3
}
}
]
}
]

# Updated by atlas-cli-plugin-terraform, please review the changes.
}
40 changes: 40 additions & 0 deletions internal/convert/testdata/adv2v2/choose_sources.in.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
resource "mongodbatlas_advanced_cluster" "this" {
# updated, doesn't have nested attributes
}

resource "mongodbatlas_advanced_cluster" "this" {
# not updated, replication_specs is not a block, resource already in TPF format
project_id = var.project_id
name = "this"
cluster_type = "REPLICASET"
replication_specs = [
{
region_configs = [
{
priority = 7
provider_name = "AWS"
region_name = "EU_WEST_1"
electable_specs = {
instance_size = "M10"
node_count = 3
}
}
]
}
]
}

resource "mongodbatlas_advanced_cluster" "this" {
# not updated, has an attribute instead of block (timeouts)
timeouts = {
create = "60m"
}
}

datasource "mongodbatlas_advanced_cluster" "this" {
# not updated, data source
}

resource "another_resource" "this" {
# not updated, not advanced cluster
}
Loading