-
Notifications
You must be signed in to change notification settings - Fork 1
chore: Convert free clusters with count #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
6667df0
ea632ba
2e9de57
7345e36
ad4b065
9657eb2
bb76598
9a9c906
4c3fc75
8e129d8
5c0c264
203e5f2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,22 +2,38 @@ package hcl | |
|
||
import ( | ||
"fmt" | ||
"strconv" | ||
|
||
"github.com/hashicorp/hcl/v2" | ||
"github.com/hashicorp/hcl/v2/hclsyntax" | ||
"github.com/hashicorp/hcl/v2/hclwrite" | ||
"github.com/zclconf/go-cty/cty" | ||
) | ||
|
||
const ( | ||
resourceType = "resource" | ||
cluster = "mongodbatlas_cluster" | ||
advCluster = "mongodbatlas_advanced_cluster" | ||
resourceType = "resource" | ||
cluster = "mongodbatlas_cluster" | ||
advCluster = "mongodbatlas_advanced_cluster" | ||
strReplicationSpecs = "replication_specs" | ||
strRegionConfigs = "region_configs" | ||
strElectableSpecs = "electable_specs" | ||
strProviderRegionName = "provider_region_name" | ||
strRegionName = "region_name" | ||
strProviderName = "provider_name" | ||
strBackingProviderName = "backing_provider_name" | ||
strProviderInstanceSizeName = "provider_instance_size_name" | ||
strInstanceSize = "instance_size" | ||
strClusterType = "cluster_type" | ||
strPriority = "priority" | ||
|
||
errFreeCluster = "free cluster (because no " + strReplicationSpecs + ")" | ||
EspenAlbert marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
) | ||
|
||
// ClusterToAdvancedCluster transforms all mongodbatlas_cluster definitions in a | ||
// Terraform configuration file into mongodbatlas_advanced_cluster schema v2 definitions. | ||
// All other resources and data sources are left untouched. | ||
// TODO: at the moment it just changes the resource type. | ||
// Note: hclwrite.Tokens are used instead of cty.Value so expressions like var.region can be preserved. | ||
// cty.Value only supports resolved values. | ||
func ClusterToAdvancedCluster(config []byte) ([]byte, error) { | ||
parser, err := getParser(config) | ||
if err != nil { | ||
|
@@ -30,31 +46,105 @@ func ClusterToAdvancedCluster(config []byte) ([]byte, error) { | |
continue | ||
} | ||
resourceBody := resource.Body() | ||
|
||
// TODO: Do the full transformation | ||
labels[0] = advCluster | ||
resource.SetLabels(labels) | ||
|
||
if isFreeTier(resourceBody) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cool approach of starting with one specific case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, I'll go now with one with num_shards |
||
if err := fillFreeTier(resourceBody); err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
resourceBody.AppendNewline() | ||
appendComment(resourceBody, "Generated by atlas-cli-plugin-terraform.") | ||
EspenAlbert marked this conversation as resolved.
Show resolved
Hide resolved
|
||
appendComment(resourceBody, "Please confirm that all references to this resource are updated.") | ||
} | ||
return parser.Bytes(), nil | ||
} | ||
|
||
func getParser(config []byte) (*hclwrite.File, error) { | ||
parser, diags := hclwrite.ParseConfig(config, "", hcl.Pos{Line: 1, Column: 1}) | ||
if diags.HasErrors() { | ||
return nil, fmt.Errorf("failed to parse Terraform config file: %s", diags.Error()) | ||
func isFreeTier(body *hclwrite.Body) bool { | ||
return body.FirstMatchingBlock(strReplicationSpecs, nil) == nil | ||
} | ||
|
||
func fillFreeTier(body *hclwrite.Body) error { | ||
const ( | ||
valClusterType = "REPLICASET" | ||
valPriority = 7 | ||
) | ||
body.SetAttributeValue(strClusterType, cty.StringVal(valClusterType)) | ||
regionConfig := hclwrite.NewEmptyFile() | ||
regionConfigBody := regionConfig.Body() | ||
setAttrInt(regionConfigBody, "priority", valPriority) | ||
if err := moveAttribute(strProviderRegionName, strRegionName, body, regionConfigBody, errFreeCluster); err != nil { | ||
return err | ||
} | ||
return parser, nil | ||
if err := moveAttribute(strProviderName, strProviderName, body, regionConfigBody, errFreeCluster); err != nil { | ||
return err | ||
} | ||
if err := moveAttribute(strBackingProviderName, strBackingProviderName, body, regionConfigBody, errFreeCluster); err != nil { | ||
return err | ||
} | ||
electableSpec := hclwrite.NewEmptyFile() | ||
if err := moveAttribute(strProviderInstanceSizeName, strInstanceSize, body, electableSpec.Body(), errFreeCluster); err != nil { | ||
return err | ||
} | ||
regionConfigBody.SetAttributeRaw(strElectableSpecs, tokensObject(electableSpec)) | ||
|
||
replicationSpec := hclwrite.NewEmptyFile() | ||
replicationSpec.Body().SetAttributeRaw(strRegionConfigs, tokensArrayObject(regionConfig)) | ||
body.SetAttributeRaw(strReplicationSpecs, tokensArrayObject(replicationSpec)) | ||
return nil | ||
} | ||
|
||
func moveAttribute(fromAttrName, toAttrName string, fromBody, toBody *hclwrite.Body, errPrefix string) error { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the abstraction signature and you don't need to worry about the type of the attribute! |
||
attr := fromBody.GetAttribute(fromAttrName) | ||
if attr == nil { | ||
return fmt.Errorf("%s: attribute %s not found", errPrefix, fromAttrName) | ||
} | ||
fromBody.RemoveAttribute(fromAttrName) | ||
toBody.SetAttributeRaw(toAttrName, attr.Expr().BuildTokens(nil)) | ||
return nil | ||
} | ||
|
||
func setAttrInt(body *hclwrite.Body, attrName string, number int) { | ||
tokens := hclwrite.Tokens{ | ||
{Type: hclsyntax.TokenNumberLit, Bytes: []byte(strconv.Itoa(number))}, | ||
} | ||
body.SetAttributeRaw(attrName, tokens) | ||
} | ||
|
||
func tokensArrayObject(file *hclwrite.File) hclwrite.Tokens { | ||
ret := hclwrite.Tokens{ | ||
{Type: hclsyntax.TokenOBrack, Bytes: []byte("[")}, | ||
} | ||
ret = append(ret, tokensObject(file)...) | ||
ret = append(ret, | ||
&hclwrite.Token{Type: hclsyntax.TokenCBrack, Bytes: []byte("]")}) | ||
return ret | ||
} | ||
|
||
func tokensObject(file *hclwrite.File) hclwrite.Tokens { | ||
ret := hclwrite.Tokens{ | ||
{Type: hclsyntax.TokenOBrack, Bytes: []byte("{")}, | ||
{Type: hclsyntax.TokenNewline, Bytes: []byte("\n")}, | ||
} | ||
ret = append(ret, file.BuildTokens(nil)...) | ||
ret = append(ret, | ||
&hclwrite.Token{Type: hclsyntax.TokenCBrack, Bytes: []byte("}")}) | ||
return ret | ||
} | ||
|
||
func appendComment(body *hclwrite.Body, comment string) { | ||
tokens := hclwrite.Tokens{ | ||
&hclwrite.Token{ | ||
Type: hclsyntax.TokenComment, | ||
Bytes: []byte("# " + comment + "\n"), | ||
}, | ||
&hclwrite.Token{Type: hclsyntax.TokenComment, Bytes: []byte("# " + comment + "\n")}, | ||
} | ||
body.AppendUnstructuredTokens(tokens) | ||
} | ||
|
||
func getParser(config []byte) (*hclwrite.File, error) { | ||
parser, diags := hclwrite.ParseConfig(config, "", hcl.Pos{Line: 1, Column: 1}) | ||
if diags.HasErrors() { | ||
return nil, fmt.Errorf("failed to parse Terraform config file: %s", diags.Error()) | ||
} | ||
return parser, nil | ||
} |
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
resource "resource1" "res1" { | ||
name = "name1" | ||
} | ||
|
||
resource "mongodbatlas_cluster" "free_cluster" { # comment in the resource | ||
# comment in own line | ||
count = local.use_free_cluster ? 1 : 0 | ||
project_id = var.project_id # inline comment kept | ||
name = var.cluster_name | ||
provider_name = "TENANT" # inline comment for attribute moved is not kept | ||
backing_provider_name = "AWS" | ||
provider_region_name = var.region | ||
provider_instance_size_name = "M0" | ||
} | ||
|
||
data "mongodbatlas_cluster" "cluster2" { | ||
name = "name4" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
resource "resource1" "res1" { | ||
name = "name1" | ||
} | ||
|
||
resource "mongodbatlas_advanced_cluster" "free_cluster" { # comment in the resource | ||
# comment in own line | ||
count = local.use_free_cluster ? 1 : 0 | ||
project_id = var.project_id # inline comment kept | ||
name = var.cluster_name | ||
cluster_type = "REPLICASET" | ||
replication_specs = [{ | ||
region_configs = [{ | ||
priority = 7 | ||
region_name = var.region | ||
provider_name = "TENANT" | ||
backing_provider_name = "AWS" | ||
electable_specs = { | ||
instance_size = "M0" | ||
} | ||
}] | ||
}] | ||
|
||
# Generated by atlas-cli-plugin-terraform. | ||
# Please confirm that all references to this resource are updated. | ||
} | ||
|
||
data "mongodbatlas_cluster" "cluster2" { | ||
name = "name4" | ||
} |
Uh oh!
There was an error while loading. Please reload this page.