From 2c3cb7df513a474fc1ced283ebac7b6c23eadb0e Mon Sep 17 00:00:00 2001 From: Leo Antoli <430982+lantoli@users.noreply.github.com> Date: Thu, 20 Feb 2025 09:51:12 +0100 Subject: [PATCH 1/5] datasources --- internal/convert/convert.go | 98 +++++++++++++------ .../testdata/clu2adv/data_sources.in.tf | 11 +++ .../testdata/clu2adv/data_sources.out.tf | 17 ++++ .../clu2adv/free_cluster_with_count.in.tf | 10 +- .../clu2adv/free_cluster_with_count.out.tf | 4 - 5 files changed, 97 insertions(+), 43 deletions(-) create mode 100644 internal/convert/testdata/clu2adv/data_sources.in.tf create mode 100644 internal/convert/testdata/clu2adv/data_sources.out.tf diff --git a/internal/convert/convert.go b/internal/convert/convert.go index 32ef8f9..4961a77 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 { @@ -43,34 +46,65 @@ func ClusterToAdvancedCluster(config []byte) ([]byte, error) { 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) - } + converted, err := convertResource(resource) 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(resource) + if converted { + resourceb := resource.Body() + resourceb.AppendNewline() + hcl.AppendComment(resourceb, "Generated by atlas-cli-plugin-terraform.") + hcl.AppendComment(resourceb, "Please confirm that all references to this resource are updated.") + } } return parser.Bytes(), nil } +func convertResource(resource *hclwrite.Block) (bool, error) { + labels := resource.Labels() + resourceName := labels[0] + if resource.Type() != resourceType || resourceName != cluster { + return false, nil + } + resourceb := resource.Body() + if errDyn := checkDynamicBlock(resourceb); errDyn != nil { + return false, errDyn + } + labels[0] = advCluster + resource.SetLabels(labels) + + var err error + if resourceb.FirstMatchingBlock(nRepSpecs, nil) != nil { + err = fillCluster(resourceb) + } else { + err = fillFreeTierCluster(resourceb) + } + if err != nil { + return false, err + } + return true, nil +} + +func convertDataSource(resource *hclwrite.Block) bool { + if resource.Type() != dataSourceType { + return false + } + labels := resource.Labels() + resourceName := labels[0] + if resourceName == cluster { + labels[0] = advCluster + resource.SetLabels(labels) + return true + } + if resourceName == clusterPlural { + labels[0] = advClusterPlural + resource.SetLabels(labels) + 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)) 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..f0d5fc1 --- /dev/null +++ b/internal/convert/testdata/clu2adv/data_sources.in.tf @@ -0,0 +1,11 @@ +data "mongodbatlas_cluster" "singular" { + # data source content is kep, only data source type is changed - 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 kep, only data source type is changed - plural + project_id = mongodbatlas_advanced_cluster.example.project_id +} 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..a3667cb --- /dev/null +++ b/internal/convert/testdata/clu2adv/data_sources.out.tf @@ -0,0 +1,17 @@ +data "mongodbatlas_advanced_cluster" "singular" { + # data source content is kep, only data source type is changed - singular + project_id = mongodbatlas_advanced_cluster.example.project_id + name = mongodbatlas_advanced_cluster.example.name + depends_on = [mongodbatlas_privatelink_endpoint_service.example_endpoint] + + # 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 kep, only data source type is changed - plural + project_id = mongodbatlas_advanced_cluster.example.project_id + + # Generated by atlas-cli-plugin-terraform. + # Please confirm that all references to this resource are updated. +} 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" -} From 9593cd4126e86a76a2513eeef17077c5957f3d58 Mon Sep 17 00:00:00 2001 From: Leo Antoli <430982+lantoli@users.noreply.github.com> Date: Thu, 20 Feb 2025 09:59:42 +0100 Subject: [PATCH 2/5] refactor --- internal/convert/convert.go | 34 +++++++++++-------- .../testdata/clu2adv/data_sources.in.tf | 11 ++++++ .../testdata/clu2adv/data_sources.out.tf | 11 ++++++ 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/internal/convert/convert.go b/internal/convert/convert.go index 4961a77..475c181 100644 --- a/internal/convert/convert.go +++ b/internal/convert/convert.go @@ -62,17 +62,14 @@ func ClusterToAdvancedCluster(config []byte) ([]byte, error) { } func convertResource(resource *hclwrite.Block) (bool, error) { - labels := resource.Labels() - resourceName := labels[0] - if resource.Type() != resourceType || resourceName != cluster { + if resource.Type() != resourceType || getResourceName(resource) != cluster { return false, nil } + setResourceName(resource, advCluster) resourceb := resource.Body() if errDyn := checkDynamicBlock(resourceb); errDyn != nil { return false, errDyn } - labels[0] = advCluster - resource.SetLabels(labels) var err error if resourceb.FirstMatchingBlock(nRepSpecs, nil) != nil { @@ -90,19 +87,16 @@ func convertDataSource(resource *hclwrite.Block) bool { if resource.Type() != dataSourceType { return false } - labels := resource.Labels() - resourceName := labels[0] - if resourceName == cluster { - labels[0] = advCluster - resource.SetLabels(labels) + switch getResourceName(resource) { + case cluster: + setResourceName(resource, advCluster) return true - } - if resourceName == clusterPlural { - labels[0] = advClusterPlural - resource.SetLabels(labels) + case clusterPlural: + setResourceName(resource, advClusterPlural) return true + default: + return false } - return false } // fillFreeTierCluster is the entry point to convert clusters in free tier @@ -335,6 +329,16 @@ func getAutoScalingOpt(opt map[string]hclwrite.Tokens) hclwrite.Tokens { return hcl.TokensObject(fileb) } +func setResourceName(resource *hclwrite.Block, name string) { + labels := resource.Labels() + labels[0] = name + resource.SetLabels(labels) +} + +func getResourceName(resource *hclwrite.Block) string { + return resource.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 index f0d5fc1..2ea82fb 100644 --- a/internal/convert/testdata/clu2adv/data_sources.in.tf +++ b/internal/convert/testdata/clu2adv/data_sources.in.tf @@ -9,3 +9,14 @@ data "mongodbatlas_clusters" "plural" { # data source content is kep, only data source type is changed - 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 +} diff --git a/internal/convert/testdata/clu2adv/data_sources.out.tf b/internal/convert/testdata/clu2adv/data_sources.out.tf index a3667cb..2db3322 100644 --- a/internal/convert/testdata/clu2adv/data_sources.out.tf +++ b/internal/convert/testdata/clu2adv/data_sources.out.tf @@ -15,3 +15,14 @@ data "mongodbatlas_advanced_clusters" "plural" { # 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 +} From b1d2a2bfdc8382aba6c5065520afccf9a34ff947 Mon Sep 17 00:00:00 2001 From: Leo Antoli <430982+lantoli@users.noreply.github.com> Date: Thu, 20 Feb 2025 12:12:01 +0100 Subject: [PATCH 3/5] rename resource to block --- internal/convert/convert.go | 40 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/internal/convert/convert.go b/internal/convert/convert.go index 475c181..2fcca0d 100644 --- a/internal/convert/convert.go +++ b/internal/convert/convert.go @@ -45,37 +45,37 @@ func ClusterToAdvancedCluster(config []byte) ([]byte, error) { if err != nil { return nil, err } - for _, resource := range parser.Body().Blocks() { - converted, err := convertResource(resource) + for _, block := range parser.Body().Blocks() { + converted, err := convertResource(block) if err != nil { return nil, err } - converted = converted || convertDataSource(resource) + converted = converted || convertDataSource(block) if converted { - resourceb := resource.Body() - resourceb.AppendNewline() - hcl.AppendComment(resourceb, "Generated by atlas-cli-plugin-terraform.") - hcl.AppendComment(resourceb, "Please confirm that all references to this resource are updated.") + 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(resource *hclwrite.Block) (bool, error) { - if resource.Type() != resourceType || getResourceName(resource) != cluster { +func convertResource(block *hclwrite.Block) (bool, error) { + if block.Type() != resourceType || getResourceName(block) != cluster { return false, nil } - setResourceName(resource, advCluster) - resourceb := resource.Body() - if errDyn := checkDynamicBlock(resourceb); errDyn != nil { + setResourceName(block, advCluster) + blockb := block.Body() + if errDyn := checkDynamicBlock(blockb); errDyn != nil { return false, errDyn } var err error - if resourceb.FirstMatchingBlock(nRepSpecs, nil) != nil { - err = fillCluster(resourceb) + if blockb.FirstMatchingBlock(nRepSpecs, nil) != nil { + err = fillCluster(blockb) } else { - err = fillFreeTierCluster(resourceb) + err = fillFreeTierCluster(blockb) } if err != nil { return false, err @@ -83,16 +83,16 @@ func convertResource(resource *hclwrite.Block) (bool, error) { return true, nil } -func convertDataSource(resource *hclwrite.Block) bool { - if resource.Type() != dataSourceType { +func convertDataSource(block *hclwrite.Block) bool { + if block.Type() != dataSourceType { return false } - switch getResourceName(resource) { + switch getResourceName(block) { case cluster: - setResourceName(resource, advCluster) + setResourceName(block, advCluster) return true case clusterPlural: - setResourceName(resource, advClusterPlural) + setResourceName(block, advClusterPlural) return true default: return false From 9297c5fd5a94840c141a55fa00f78b401b6e6992 Mon Sep 17 00:00:00 2001 From: Leo Antoli <430982+lantoli@users.noreply.github.com> Date: Thu, 20 Feb 2025 12:21:54 +0100 Subject: [PATCH 4/5] set use_replication_spec_per_shard in data sources --- internal/convert/const_names.go | 1 + internal/convert/convert.go | 16 ++++++++-------- .../convert/testdata/clu2adv/data_sources.out.tf | 10 ++++++---- 3 files changed, 15 insertions(+), 12 deletions(-) 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 2fcca0d..811fd54 100644 --- a/internal/convert/convert.go +++ b/internal/convert/convert.go @@ -87,16 +87,16 @@ func convertDataSource(block *hclwrite.Block) bool { if block.Type() != dataSourceType { return false } - switch getResourceName(block) { - case cluster: - setResourceName(block, advCluster) - return true - case clusterPlural: - setResourceName(block, advClusterPlural) + 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 - default: - return false } + return false } // fillFreeTierCluster is the entry point to convert clusters in free tier diff --git a/internal/convert/testdata/clu2adv/data_sources.out.tf b/internal/convert/testdata/clu2adv/data_sources.out.tf index 2db3322..c7abdb5 100644 --- a/internal/convert/testdata/clu2adv/data_sources.out.tf +++ b/internal/convert/testdata/clu2adv/data_sources.out.tf @@ -1,8 +1,9 @@ data "mongodbatlas_advanced_cluster" "singular" { # data source content is kep, only data source type is changed - singular - project_id = mongodbatlas_advanced_cluster.example.project_id - name = mongodbatlas_advanced_cluster.example.name - depends_on = [mongodbatlas_privatelink_endpoint_service.example_endpoint] + 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. @@ -10,7 +11,8 @@ data "mongodbatlas_advanced_cluster" "singular" { data "mongodbatlas_advanced_clusters" "plural" { # data source content is kep, only data source type is changed - plural - project_id = mongodbatlas_advanced_cluster.example.project_id + 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. From 84f9adabefa0287be141ad08bc6ecfa9bd9e3cfa Mon Sep 17 00:00:00 2001 From: Leo Antoli <430982+lantoli@users.noreply.github.com> Date: Thu, 20 Feb 2025 12:37:29 +0100 Subject: [PATCH 5/5] more tests --- internal/convert/convert.go | 9 +++++- .../testdata/clu2adv/data_sources.in.tf | 32 +++++++++++++++++-- .../testdata/clu2adv/data_sources.out.tf | 32 +++++++++++++++++-- 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/internal/convert/convert.go b/internal/convert/convert.go index 811fd54..bed092e 100644 --- a/internal/convert/convert.go +++ b/internal/convert/convert.go @@ -331,12 +331,19 @@ func getAutoScalingOpt(opt map[string]hclwrite.Tokens) hclwrite.Tokens { 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 { - return resource.Labels()[0] + labels := resource.Labels() + if len(labels) == 0 { + return "" + } + return labels[0] } func checkDynamicBlock(body *hclwrite.Body) error { diff --git a/internal/convert/testdata/clu2adv/data_sources.in.tf b/internal/convert/testdata/clu2adv/data_sources.in.tf index 2ea82fb..88c4630 100644 --- a/internal/convert/testdata/clu2adv/data_sources.in.tf +++ b/internal/convert/testdata/clu2adv/data_sources.in.tf @@ -1,12 +1,12 @@ data "mongodbatlas_cluster" "singular" { - # data source content is kep, only data source type is changed - 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 kep, only data source type is changed - plural + # data source content is kept - plural project_id = mongodbatlas_advanced_cluster.example.project_id } @@ -20,3 +20,31 @@ 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 index c7abdb5..fb8561d 100644 --- a/internal/convert/testdata/clu2adv/data_sources.out.tf +++ b/internal/convert/testdata/clu2adv/data_sources.out.tf @@ -1,5 +1,5 @@ data "mongodbatlas_advanced_cluster" "singular" { - # data source content is kep, only data source type is changed - 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] @@ -10,7 +10,7 @@ data "mongodbatlas_advanced_cluster" "singular" { } data "mongodbatlas_advanced_clusters" "plural" { - # data source content is kep, only data source type is changed - plural + # data source content is kept - plural project_id = mongodbatlas_advanced_cluster.example.project_id use_replication_spec_per_shard = true @@ -28,3 +28,31 @@ 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 +}