diff --git a/internal/convert/const_names.go b/internal/convert/const_names.go index 87a8994..a31564e 100644 --- a/internal/convert/const_names.go +++ b/internal/convert/const_names.go @@ -47,4 +47,5 @@ const ( nZoneName = "zone_name" nKey = "key" nValue = "value" + nUseRepSpecsPerShard = "use_replication_spec_per_shard" ) diff --git a/internal/convert/convert.go b/internal/convert/convert.go index 32ef8f9..bed092e 100644 --- a/internal/convert/convert.go +++ b/internal/convert/convert.go @@ -14,17 +14,20 @@ import ( ) const ( - resourceType = "resource" - cluster = "mongodbatlas_cluster" - advCluster = "mongodbatlas_advanced_cluster" - valClusterType = "REPLICASET" - valMaxPriority = 7 - valMinPriority = 1 - errFreeCluster = "free cluster (because no " + nRepSpecs + ")" - errRepSpecs = "setting " + nRepSpecs - errConfigs = "setting " + nConfig - errPriority = "setting " + nPriority - errNumShards = "setting " + nNumShards + resourceType = "resource" + dataSourceType = "data" + cluster = "mongodbatlas_cluster" + advCluster = "mongodbatlas_advanced_cluster" + clusterPlural = "mongodbatlas_clusters" + advClusterPlural = "mongodbatlas_advanced_clusters" + valClusterType = "REPLICASET" + valMaxPriority = 7 + valMinPriority = 1 + errFreeCluster = "free cluster (because no " + nRepSpecs + ")" + errRepSpecs = "setting " + nRepSpecs + errConfigs = "setting " + nConfig + errPriority = "setting " + nPriority + errNumShards = "setting " + nNumShards ) type attrVals struct { @@ -42,35 +45,60 @@ func ClusterToAdvancedCluster(config []byte) ([]byte, error) { if err != nil { return nil, err } - for _, resource := range parser.Body().Blocks() { - labels := resource.Labels() - resourceName := labels[0] - if resource.Type() != resourceType || resourceName != cluster { - continue - } - resourceb := resource.Body() - if errDyn := checkDynamicBlock(resourceb); errDyn != nil { - return nil, errDyn - } - labels[0] = advCluster - resource.SetLabels(labels) - - if resourceb.FirstMatchingBlock(nRepSpecs, nil) != nil { - err = fillCluster(resourceb) - } else { - err = fillFreeTierCluster(resourceb) - } + for _, block := range parser.Body().Blocks() { + converted, err := convertResource(block) if err != nil { return nil, err } - - resourceb.AppendNewline() - hcl.AppendComment(resourceb, "Generated by atlas-cli-plugin-terraform.") - hcl.AppendComment(resourceb, "Please confirm that all references to this resource are updated.") + converted = converted || convertDataSource(block) + if converted { + blockb := block.Body() + blockb.AppendNewline() + hcl.AppendComment(blockb, "Generated by atlas-cli-plugin-terraform.") + hcl.AppendComment(blockb, "Please confirm that all references to this resource are updated.") + } } return parser.Bytes(), nil } +func convertResource(block *hclwrite.Block) (bool, error) { + if block.Type() != resourceType || getResourceName(block) != cluster { + return false, nil + } + setResourceName(block, advCluster) + blockb := block.Body() + if errDyn := checkDynamicBlock(blockb); errDyn != nil { + return false, errDyn + } + + var err error + if blockb.FirstMatchingBlock(nRepSpecs, nil) != nil { + err = fillCluster(blockb) + } else { + err = fillFreeTierCluster(blockb) + } + if err != nil { + return false, err + } + return true, nil +} + +func convertDataSource(block *hclwrite.Block) bool { + if block.Type() != dataSourceType { + return false + } + convertMap := map[string]string{ + cluster: advCluster, + clusterPlural: advClusterPlural, + } + if newName, found := convertMap[getResourceName(block)]; found { + setResourceName(block, newName) + block.Body().SetAttributeValue(nUseRepSpecsPerShard, cty.True) + return true + } + return false +} + // fillFreeTierCluster is the entry point to convert clusters in free tier func fillFreeTierCluster(resourceb *hclwrite.Body) error { resourceb.SetAttributeValue(nClusterType, cty.StringVal(valClusterType)) @@ -301,6 +329,23 @@ func getAutoScalingOpt(opt map[string]hclwrite.Tokens) hclwrite.Tokens { return hcl.TokensObject(fileb) } +func setResourceName(resource *hclwrite.Block, name string) { + labels := resource.Labels() + if len(labels) == 0 { + return + } + labels[0] = name + resource.SetLabels(labels) +} + +func getResourceName(resource *hclwrite.Block) string { + labels := resource.Labels() + if len(labels) == 0 { + return "" + } + return labels[0] +} + func checkDynamicBlock(body *hclwrite.Body) error { for _, block := range body.Blocks() { if block.Type() == "dynamic" { diff --git a/internal/convert/testdata/clu2adv/data_sources.in.tf b/internal/convert/testdata/clu2adv/data_sources.in.tf new file mode 100644 index 0000000..88c4630 --- /dev/null +++ b/internal/convert/testdata/clu2adv/data_sources.in.tf @@ -0,0 +1,50 @@ +data "mongodbatlas_cluster" "singular" { + # data source content is kept - singular + project_id = mongodbatlas_advanced_cluster.example.project_id + name = mongodbatlas_advanced_cluster.example.name + depends_on = [mongodbatlas_privatelink_endpoint_service.example_endpoint] +} + +data "mongodbatlas_clusters" "plural" { + # data source content is kept - plural + project_id = mongodbatlas_advanced_cluster.example.project_id +} + +data "mongodbatlas_advanced_cluster" "adv_singular" { + # adv_cluster is not changed - adv_singular + project_id = mongodbatlas_advanced_cluster.example.project_id + name = mongodbatlas_advanced_cluster.example.name +} + +data "mongodbatlas_advanced_cluster" "adv_plural" { + # adv_cluster is not changed - adv_plural + project_id = mongodbatlas_advanced_cluster.example.project_id +} + +resource "random_resource" "random1" { + # other resources are left unchanged - random1 + hi1 = "there1" +} + +data "random_datasource" "random2" { + # other resources are left unchanged - random2 + hi2 = "there2" +} + +# comments out of resources are kept + +unknown_block "hello" { + # unkown block types are kept +} + +unknown_block { + # plugin doesn't panic with unlabeled blocks - unknown_block +} + +resource { + # plugin doesn't panic with unlabeled blocks - resource +} + +data { + # plugin doesn't panic with unlabeled blocks - data +} diff --git a/internal/convert/testdata/clu2adv/data_sources.out.tf b/internal/convert/testdata/clu2adv/data_sources.out.tf new file mode 100644 index 0000000..fb8561d --- /dev/null +++ b/internal/convert/testdata/clu2adv/data_sources.out.tf @@ -0,0 +1,58 @@ +data "mongodbatlas_advanced_cluster" "singular" { + # data source content is kept - singular + project_id = mongodbatlas_advanced_cluster.example.project_id + name = mongodbatlas_advanced_cluster.example.name + depends_on = [mongodbatlas_privatelink_endpoint_service.example_endpoint] + use_replication_spec_per_shard = true + + # Generated by atlas-cli-plugin-terraform. + # Please confirm that all references to this resource are updated. +} + +data "mongodbatlas_advanced_clusters" "plural" { + # data source content is kept - plural + project_id = mongodbatlas_advanced_cluster.example.project_id + use_replication_spec_per_shard = true + + # Generated by atlas-cli-plugin-terraform. + # Please confirm that all references to this resource are updated. +} + +data "mongodbatlas_advanced_cluster" "adv_singular" { + # adv_cluster is not changed - adv_singular + project_id = mongodbatlas_advanced_cluster.example.project_id + name = mongodbatlas_advanced_cluster.example.name +} + +data "mongodbatlas_advanced_cluster" "adv_plural" { + # adv_cluster is not changed - adv_plural + project_id = mongodbatlas_advanced_cluster.example.project_id +} + +resource "random_resource" "random1" { + # other resources are left unchanged - random1 + hi1 = "there1" +} + +data "random_datasource" "random2" { + # other resources are left unchanged - random2 + hi2 = "there2" +} + +# comments out of resources are kept + +unknown_block "hello" { + # unkown block types are kept +} + +unknown_block { + # plugin doesn't panic with unlabeled blocks - unknown_block +} + +resource { + # plugin doesn't panic with unlabeled blocks - resource +} + +data { + # plugin doesn't panic with unlabeled blocks - data +} diff --git a/internal/convert/testdata/clu2adv/free_cluster_with_count.in.tf b/internal/convert/testdata/clu2adv/free_cluster_with_count.in.tf index fef02c0..4702169 100644 --- a/internal/convert/testdata/clu2adv/free_cluster_with_count.in.tf +++ b/internal/convert/testdata/clu2adv/free_cluster_with_count.in.tf @@ -4,9 +4,9 @@ resource "resource1" "res1" { resource "mongodbatlas_cluster" "free_cluster" { # comment in the resource # comment in own line in the beginning - count = local.use_free_cluster ? 1 : 0 - project_id = var.project_id # inline comment kept - name = var.cluster_name + count = local.use_free_cluster ? 1 : 0 + project_id = var.project_id # inline comment kept + name = var.cluster_name # comment in own line in the middle is deleted provider_name = "TENANT" # inline comment for attribute moved is not kept backing_provider_name = "AWS" @@ -14,7 +14,3 @@ resource "mongodbatlas_cluster" "free_cluster" { # comment in the resource provider_instance_size_name = "M0" # comment in own line at the end happens before replication_specs } - -data "mongodbatlas_cluster" "cluster2" { - name = "name4" -} diff --git a/internal/convert/testdata/clu2adv/free_cluster_with_count.out.tf b/internal/convert/testdata/clu2adv/free_cluster_with_count.out.tf index eb596a6..c24b56f 100644 --- a/internal/convert/testdata/clu2adv/free_cluster_with_count.out.tf +++ b/internal/convert/testdata/clu2adv/free_cluster_with_count.out.tf @@ -28,7 +28,3 @@ resource "mongodbatlas_advanced_cluster" "free_cluster" { # comment in the resou # Generated by atlas-cli-plugin-terraform. # Please confirm that all references to this resource are updated. } - -data "mongodbatlas_cluster" "cluster2" { - name = "name4" -}