@@ -10,25 +10,6 @@ import (
10
10
"github.com/zclconf/go-cty/cty"
11
11
)
12
12
13
- const (
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 + ")"
30
- )
31
-
32
13
// ClusterToAdvancedCluster transforms all mongodbatlas_cluster definitions in a
33
14
// Terraform configuration file into mongodbatlas_advanced_cluster schema v2 definitions.
34
15
// All other resources and data sources are left untouched.
@@ -45,67 +26,195 @@ func ClusterToAdvancedCluster(config []byte) ([]byte, error) {
45
26
if resource .Type () != resourceType || resourceName != cluster {
46
27
continue
47
28
}
48
- resourceBody := resource .Body ()
29
+ resourceb := resource .Body ()
49
30
labels [0 ] = advCluster
50
31
resource .SetLabels (labels )
51
32
52
- if isFreeTier (resourceBody ) {
53
- if err := fillFreeTier (resourceBody ); err != nil {
54
- return nil , err
55
- }
33
+ if resourceb .FirstMatchingBlock (nRepSpecs , nil ) != nil {
34
+ err = fillReplicationSpecs (resourceb )
35
+ } else {
36
+ err = fillFreeTier (resourceb )
37
+ }
38
+ if err != nil {
39
+ return nil , err
56
40
}
57
41
58
- resourceBody .AppendNewline ()
59
- appendComment (resourceBody , "Generated by atlas-cli-plugin-terraform." )
60
- appendComment (resourceBody , "Please confirm that all references to this resource are updated." )
42
+ resourceb .AppendNewline ()
43
+ appendComment (resourceb , "Generated by atlas-cli-plugin-terraform." )
44
+ appendComment (resourceb , "Please confirm that all references to this resource are updated." )
61
45
}
62
46
return parser .Bytes (), nil
63
47
}
64
48
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 {
49
+ // fillFreeTier is the entry point to convert clusters in free tier
50
+ func fillFreeTier (resourceb * hclwrite.Body ) error {
51
+ resourceb .SetAttributeValue (nClusterType , cty .StringVal (valClusterType ))
52
+ config := hclwrite .NewEmptyFile ()
53
+ configb := config .Body ()
54
+ setAttrInt (configb , "priority" , valPriority )
55
+ if err := moveAttr (resourceb , configb , nRegionNameSrc , nRegionName , errFreeCluster ); err != nil {
79
56
return err
80
57
}
81
- if err := moveAttribute ( nameProviderName , nameProviderName , body , regionConfigBody , errFreeCluster ); err != nil {
58
+ if err := moveAttr ( resourceb , configb , nProviderName , nProviderName , errFreeCluster ); err != nil {
82
59
return err
83
60
}
84
- if err := moveAttribute ( nameBackingProviderName , nameBackingProviderName , body , regionConfigBody , errFreeCluster ); err != nil {
61
+ if err := moveAttr ( resourceb , configb , nBackingProviderName , nBackingProviderName , errFreeCluster ); err != nil {
85
62
return err
86
63
}
87
64
electableSpec := hclwrite .NewEmptyFile ()
88
- if err := moveAttribute ( nameProviderInstanceSizeName , nameInstanceSize , body , electableSpec .Body (), errFreeCluster ); err != nil {
65
+ if err := moveAttr ( resourceb , electableSpec .Body (), nInstanceSizeSrc , nInstanceSize , errFreeCluster ); err != nil {
89
66
return err
90
67
}
91
- regionConfigBody .SetAttributeRaw (nameElectableSpecs , tokensObject (electableSpec ))
68
+ configb .SetAttributeRaw (nElectableSpecs , tokensObject (electableSpec ))
92
69
93
- replicationSpec := hclwrite .NewEmptyFile ()
94
- replicationSpec .Body ().SetAttributeRaw (nameRegionConfigs , tokensArrayObject (regionConfig ))
95
- body .SetAttributeRaw (nameReplicationSpecs , tokensArrayObject (replicationSpec ))
70
+ repSpecs := hclwrite .NewEmptyFile ()
71
+ repSpecs .Body ().SetAttributeRaw (nConfig , tokensArrayObject (config ))
72
+ resourceb .SetAttributeRaw (nRepSpecs , tokensArrayObject (repSpecs ))
96
73
return nil
97
74
}
98
75
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 )
76
+ // fillReplicationSpecs is the entry point to convert clusters with replications_specs (all but free tier)
77
+ func fillReplicationSpecs (resourceb * hclwrite.Body ) error {
78
+ root , errRoot := popRootAttrs (resourceb , errRepSpecs )
79
+ if errRoot != nil {
80
+ return errRoot
103
81
}
104
- fromBody .RemoveAttribute (fromAttrName )
105
- toBody .SetAttributeRaw (toAttrName , attr .Expr ().BuildTokens (nil ))
82
+ repSpecsSrc := resourceb .FirstMatchingBlock (nRepSpecs , nil )
83
+ configSrc := repSpecsSrc .Body ().FirstMatchingBlock (nConfigSrc , nil )
84
+ if configSrc == nil {
85
+ return fmt .Errorf ("%s: %s not found" , errRepSpecs , nConfigSrc )
86
+ }
87
+
88
+ resourceb .RemoveAttribute (nNumShards ) // num_shards in root is not relevant, only in replication_specs
89
+ // ok to fail as cloud_backup is optional
90
+ _ = moveAttr (resourceb , resourceb , nCloudBackup , nBackupEnabled , errRepSpecs )
91
+
92
+ config , errConfig := getRegionConfigs (configSrc , root )
93
+ if errConfig != nil {
94
+ return errConfig
95
+ }
96
+ repSpecs := hclwrite .NewEmptyFile ()
97
+ repSpecs .Body ().SetAttributeRaw (nConfig , config )
98
+ resourceb .SetAttributeRaw (nRepSpecs , tokensArrayObject (repSpecs ))
99
+
100
+ resourceb .RemoveBlock (repSpecsSrc )
106
101
return nil
107
102
}
108
103
104
+ // popRootAttrs deletes the attributes common to all replication_specs/regions_config and returns them.
105
+ func popRootAttrs (body * hclwrite.Body , errPrefix string ) (attrVals , error ) {
106
+ var (
107
+ reqNames = []string {
108
+ nProviderName ,
109
+ nInstanceSizeSrc ,
110
+ }
111
+ optNames = []string {
112
+ nDiskSizeGB ,
113
+ nDiskGBEnabledSrc ,
114
+ nComputeEnabledSrc ,
115
+ nComputeMinInstanceSizeSrc ,
116
+ nComputeMaxInstanceSizeSrc ,
117
+ nComputeScaleDownEnabledSrc ,
118
+ }
119
+ req = make (map [string ]hclwrite.Tokens )
120
+ opt = make (map [string ]hclwrite.Tokens )
121
+ )
122
+ for _ , name := range reqNames {
123
+ tokens , err := popAttr (body , name , errPrefix )
124
+ if err != nil {
125
+ return attrVals {}, err
126
+ }
127
+ req [name ] = tokens
128
+ }
129
+ for _ , name := range optNames {
130
+ tokens , _ := popAttr (body , name , errPrefix )
131
+ if tokens != nil {
132
+ opt [name ] = tokens
133
+ }
134
+ }
135
+ return attrVals {req : req , opt : opt }, nil
136
+ }
137
+
138
+ func getRegionConfigs (configSrc * hclwrite.Block , root attrVals ) (hclwrite.Tokens , error ) {
139
+ file := hclwrite .NewEmptyFile ()
140
+ fileb := file .Body ()
141
+ fileb .SetAttributeRaw (nProviderName , root .req [nProviderName ])
142
+ if err := moveAttr (configSrc .Body (), fileb , nRegionName , nRegionName , errRepSpecs ); err != nil {
143
+ return nil , err
144
+ }
145
+ if err := moveAttr (configSrc .Body (), fileb , nPriority , nPriority , errRepSpecs ); err != nil {
146
+ return nil , err
147
+ }
148
+ autoScaling := getAutoScalingOpt (root .opt )
149
+ if autoScaling != nil {
150
+ fileb .SetAttributeRaw (nAutoScaling , autoScaling )
151
+ }
152
+ electableSpecs , errElect := getElectableSpecs (configSrc , root )
153
+ if errElect != nil {
154
+ return nil , errElect
155
+ }
156
+ fileb .SetAttributeRaw (nElectableSpecs , electableSpecs )
157
+ return tokensArrayObject (file ), nil
158
+ }
159
+
160
+ func getElectableSpecs (configSrc * hclwrite.Block , root attrVals ) (hclwrite.Tokens , error ) {
161
+ file := hclwrite .NewEmptyFile ()
162
+ fileb := file .Body ()
163
+ if err := moveAttr (configSrc .Body (), fileb , nElectableNodes , nNodeCount , errRepSpecs ); err != nil {
164
+ return nil , err
165
+ }
166
+ fileb .SetAttributeRaw (nInstanceSize , root .req [nInstanceSizeSrc ])
167
+ if root .opt [nDiskSizeGB ] != nil {
168
+ fileb .SetAttributeRaw (nDiskSizeGB , root .opt [nDiskSizeGB ])
169
+ }
170
+ return tokensObject (file ), nil
171
+ }
172
+
173
+ func getAutoScalingOpt (opt map [string ]hclwrite.Tokens ) hclwrite.Tokens {
174
+ var (
175
+ names = [][2 ]string { // use slice instead of map to preserve order
176
+ {nDiskGBEnabledSrc , nDiskGBEnabled },
177
+ {nComputeEnabledSrc , nComputeEnabled },
178
+ {nComputeMinInstanceSizeSrc , nComputeMinInstanceSize },
179
+ {nComputeMaxInstanceSizeSrc , nComputeMaxInstanceSize },
180
+ {nComputeScaleDownEnabledSrc , nComputeScaleDownEnabled },
181
+ }
182
+ file = hclwrite .NewEmptyFile ()
183
+ found = false
184
+ )
185
+ for _ , tuple := range names {
186
+ src , dst := tuple [0 ], tuple [1 ]
187
+ if tokens := opt [src ]; tokens != nil {
188
+ file .Body ().SetAttributeRaw (dst , tokens )
189
+ found = true
190
+ }
191
+ }
192
+ if ! found {
193
+ return nil
194
+ }
195
+ return tokensObject (file )
196
+ }
197
+
198
+ // popAttr deletes an attribute from fromBody and adds it to toBody.
199
+ func moveAttr (fromBody , toBody * hclwrite.Body , fromAttrName , toAttrName , errPrefix string ) error {
200
+ tokens , err := popAttr (fromBody , fromAttrName , errPrefix )
201
+ if err == nil {
202
+ toBody .SetAttributeRaw (toAttrName , tokens )
203
+ }
204
+ return err
205
+ }
206
+
207
+ // popAttr deletes an attribute and returns it value.
208
+ func popAttr (body * hclwrite.Body , attrName , errPrefix string ) (hclwrite.Tokens , error ) {
209
+ attr := body .GetAttribute (attrName )
210
+ if attr == nil {
211
+ return nil , fmt .Errorf ("%s: attribute %s not found" , errPrefix , attrName )
212
+ }
213
+ tokens := attr .Expr ().BuildTokens (nil )
214
+ body .RemoveAttribute (attrName )
215
+ return tokens , nil
216
+ }
217
+
109
218
func setAttrInt (body * hclwrite.Body , attrName string , number int ) {
110
219
tokens := hclwrite.Tokens {
111
220
{Type : hclsyntax .TokenNumberLit , Bytes : []byte (strconv .Itoa (number ))},
@@ -148,3 +257,50 @@ func getParser(config []byte) (*hclwrite.File, error) {
148
257
}
149
258
return parser , nil
150
259
}
260
+
261
+ type attrVals struct {
262
+ req map [string ]hclwrite.Tokens
263
+ opt map [string ]hclwrite.Tokens
264
+ }
265
+
266
+ const (
267
+ resourceType = "resource"
268
+ cluster = "mongodbatlas_cluster"
269
+ advCluster = "mongodbatlas_advanced_cluster"
270
+
271
+ nRepSpecs = "replication_specs"
272
+ nConfig = "region_configs"
273
+ nConfigSrc = "regions_config"
274
+ nElectableSpecs = "electable_specs"
275
+ nAutoScaling = "auto_scaling"
276
+ nRegionNameSrc = "provider_region_name"
277
+ nRegionName = "region_name"
278
+ nProviderName = "provider_name"
279
+ nBackingProviderName = "backing_provider_name"
280
+ nInstanceSizeSrc = "provider_instance_size_name"
281
+ nInstanceSize = "instance_size"
282
+ nClusterType = "cluster_type"
283
+ nPriority = "priority"
284
+ nNumShards = "num_shards"
285
+ nBackupEnabled = "backup_enabled"
286
+ nCloudBackup = "cloud_backup"
287
+ nDiskSizeGB = "disk_size_gb"
288
+ nDiskGBEnabledSrc = "auto_scaling_disk_gb_enabled"
289
+ nComputeEnabledSrc = "auto_scaling_compute_enabled"
290
+ nComputeScaleDownEnabledSrc = "auto_scaling_compute_scale_down_enabled"
291
+ nComputeMinInstanceSizeSrc = "provider_auto_scaling_compute_min_instance_size"
292
+ nComputeMaxInstanceSizeSrc = "provider_auto_scaling_compute_max_instance_size"
293
+ nDiskGBEnabled = "disk_gb_enabled"
294
+ nComputeEnabled = "compute_enabled"
295
+ nComputeScaleDownEnabled = "compute_scale_down_enabled"
296
+ nComputeMinInstanceSize = "compute_min_instance_size"
297
+ nComputeMaxInstanceSize = "compute_max_instance_size"
298
+ nNodeCount = "node_count"
299
+ nElectableNodes = "electable_nodes"
300
+
301
+ valClusterType = "REPLICASET"
302
+ valPriority = 7
303
+
304
+ errFreeCluster = "free cluster (because no " + nRepSpecs + ")"
305
+ errRepSpecs = "setting " + nRepSpecs
306
+ )
0 commit comments