@@ -2,22 +2,38 @@ package hcl
2
2
3
3
import (
4
4
"fmt"
5
+ "strconv"
5
6
6
7
"github.com/hashicorp/hcl/v2"
7
8
"github.com/hashicorp/hcl/v2/hclsyntax"
8
9
"github.com/hashicorp/hcl/v2/hclwrite"
10
+ "github.com/zclconf/go-cty/cty"
9
11
)
10
12
11
13
const (
12
- resourceType = "resource"
13
- cluster = "mongodbatlas_cluster"
14
- advCluster = "mongodbatlas_advanced_cluster"
14
+ resourceType = "resource"
15
+ cluster = "mongodbatlas_cluster"
16
+ advCluster = "mongodbatlas_advanced_cluster"
17
+ nameReplicationSpecs = "replication_specs"
18
+ nameRegionConfigs = "region_configs"
19
+ nameElectableSpecs = "electable_specs"
20
+ nameProviderRegionName = "provider_region_name"
21
+ nameRegionName = "region_name"
22
+ nameProviderName = "provider_name"
23
+ nameBackingProviderName = "backing_provider_name"
24
+ nameProviderInstanceSizeName = "provider_instance_size_name"
25
+ nameInstanceSize = "instance_size"
26
+ nameClusterType = "cluster_type"
27
+ namePriority = "priority"
28
+
29
+ errFreeCluster = "free cluster (because no " + nameReplicationSpecs + ")"
15
30
)
16
31
17
32
// ClusterToAdvancedCluster transforms all mongodbatlas_cluster definitions in a
18
33
// Terraform configuration file into mongodbatlas_advanced_cluster schema v2 definitions.
19
34
// All other resources and data sources are left untouched.
20
- // TODO: at the moment it just changes the resource type.
35
+ // Note: hclwrite.Tokens are used instead of cty.Value so expressions like var.region can be preserved.
36
+ // cty.Value only supports resolved values.
21
37
func ClusterToAdvancedCluster (config []byte ) ([]byte , error ) {
22
38
parser , err := getParser (config )
23
39
if err != nil {
@@ -30,31 +46,105 @@ func ClusterToAdvancedCluster(config []byte) ([]byte, error) {
30
46
continue
31
47
}
32
48
resourceBody := resource .Body ()
33
-
34
- // TODO: Do the full transformation
35
49
labels [0 ] = advCluster
36
50
resource .SetLabels (labels )
51
+
52
+ if isFreeTier (resourceBody ) {
53
+ if err := fillFreeTier (resourceBody ); err != nil {
54
+ return nil , err
55
+ }
56
+ }
57
+
37
58
resourceBody .AppendNewline ()
38
59
appendComment (resourceBody , "Generated by atlas-cli-plugin-terraform." )
39
60
appendComment (resourceBody , "Please confirm that all references to this resource are updated." )
40
61
}
41
62
return parser .Bytes (), nil
42
63
}
43
64
44
- func getParser (config []byte ) (* hclwrite.File , error ) {
45
- parser , diags := hclwrite .ParseConfig (config , "" , hcl.Pos {Line : 1 , Column : 1 })
46
- if diags .HasErrors () {
47
- return nil , fmt .Errorf ("failed to parse Terraform config file: %s" , diags .Error ())
65
+ func isFreeTier (body * hclwrite.Body ) bool {
66
+ return body .FirstMatchingBlock (nameReplicationSpecs , nil ) == nil
67
+ }
68
+
69
+ func fillFreeTier (body * hclwrite.Body ) error {
70
+ const (
71
+ valClusterType = "REPLICASET"
72
+ valPriority = 7
73
+ )
74
+ body .SetAttributeValue (nameClusterType , cty .StringVal (valClusterType ))
75
+ regionConfig := hclwrite .NewEmptyFile ()
76
+ regionConfigBody := regionConfig .Body ()
77
+ setAttrInt (regionConfigBody , "priority" , valPriority )
78
+ if err := moveAttribute (nameProviderRegionName , nameRegionName , body , regionConfigBody , errFreeCluster ); err != nil {
79
+ return err
48
80
}
49
- return parser , nil
81
+ if err := moveAttribute (nameProviderName , nameProviderName , body , regionConfigBody , errFreeCluster ); err != nil {
82
+ return err
83
+ }
84
+ if err := moveAttribute (nameBackingProviderName , nameBackingProviderName , body , regionConfigBody , errFreeCluster ); err != nil {
85
+ return err
86
+ }
87
+ electableSpec := hclwrite .NewEmptyFile ()
88
+ if err := moveAttribute (nameProviderInstanceSizeName , nameInstanceSize , body , electableSpec .Body (), errFreeCluster ); err != nil {
89
+ return err
90
+ }
91
+ regionConfigBody .SetAttributeRaw (nameElectableSpecs , tokensObject (electableSpec ))
92
+
93
+ replicationSpec := hclwrite .NewEmptyFile ()
94
+ replicationSpec .Body ().SetAttributeRaw (nameRegionConfigs , tokensArrayObject (regionConfig ))
95
+ body .SetAttributeRaw (nameReplicationSpecs , tokensArrayObject (replicationSpec ))
96
+ return nil
97
+ }
98
+
99
+ func moveAttribute (fromAttrName , toAttrName string , fromBody , toBody * hclwrite.Body , errPrefix string ) error {
100
+ attr := fromBody .GetAttribute (fromAttrName )
101
+ if attr == nil {
102
+ return fmt .Errorf ("%s: attribute %s not found" , errPrefix , fromAttrName )
103
+ }
104
+ fromBody .RemoveAttribute (fromAttrName )
105
+ toBody .SetAttributeRaw (toAttrName , attr .Expr ().BuildTokens (nil ))
106
+ return nil
107
+ }
108
+
109
+ func setAttrInt (body * hclwrite.Body , attrName string , number int ) {
110
+ tokens := hclwrite.Tokens {
111
+ {Type : hclsyntax .TokenNumberLit , Bytes : []byte (strconv .Itoa (number ))},
112
+ }
113
+ body .SetAttributeRaw (attrName , tokens )
114
+ }
115
+
116
+ func tokensArrayObject (file * hclwrite.File ) hclwrite.Tokens {
117
+ ret := hclwrite.Tokens {
118
+ {Type : hclsyntax .TokenOBrack , Bytes : []byte ("[" )},
119
+ }
120
+ ret = append (ret , tokensObject (file )... )
121
+ ret = append (ret ,
122
+ & hclwrite.Token {Type : hclsyntax .TokenCBrack , Bytes : []byte ("]" )})
123
+ return ret
124
+ }
125
+
126
+ func tokensObject (file * hclwrite.File ) hclwrite.Tokens {
127
+ ret := hclwrite.Tokens {
128
+ {Type : hclsyntax .TokenOBrack , Bytes : []byte ("{" )},
129
+ {Type : hclsyntax .TokenNewline , Bytes : []byte ("\n " )},
130
+ }
131
+ ret = append (ret , file .BuildTokens (nil )... )
132
+ ret = append (ret ,
133
+ & hclwrite.Token {Type : hclsyntax .TokenCBrack , Bytes : []byte ("}" )})
134
+ return ret
50
135
}
51
136
52
137
func appendComment (body * hclwrite.Body , comment string ) {
53
138
tokens := hclwrite.Tokens {
54
- & hclwrite.Token {
55
- Type : hclsyntax .TokenComment ,
56
- Bytes : []byte ("# " + comment + "\n " ),
57
- },
139
+ & hclwrite.Token {Type : hclsyntax .TokenComment , Bytes : []byte ("# " + comment + "\n " )},
58
140
}
59
141
body .AppendUnstructuredTokens (tokens )
60
142
}
143
+
144
+ func getParser (config []byte ) (* hclwrite.File , error ) {
145
+ parser , diags := hclwrite .ParseConfig (config , "" , hcl.Pos {Line : 1 , Column : 1 })
146
+ if diags .HasErrors () {
147
+ return nil , fmt .Errorf ("failed to parse Terraform config file: %s" , diags .Error ())
148
+ }
149
+ return parser , nil
150
+ }
0 commit comments