1
1
package convert
2
2
3
3
import (
4
+ "errors"
4
5
"fmt"
6
+ "sort"
5
7
6
8
"github.com/hashicorp/hcl/v2/hclwrite"
7
9
"github.com/mongodb-labs/atlas-cli-plugin-terraform/internal/hcl"
@@ -13,9 +15,12 @@ const (
13
15
cluster = "mongodbatlas_cluster"
14
16
advCluster = "mongodbatlas_advanced_cluster"
15
17
valClusterType = "REPLICASET"
16
- valPriority = 7
18
+ valMaxPriority = 7
19
+ valMinPriority = 1
17
20
errFreeCluster = "free cluster (because no " + nRepSpecs + ")"
18
21
errRepSpecs = "setting " + nRepSpecs
22
+ errConfigs = "setting " + nConfig
23
+ errPriority = "setting " + nPriority
19
24
)
20
25
21
26
type attrVals struct {
@@ -40,6 +45,9 @@ func ClusterToAdvancedCluster(config []byte) ([]byte, error) {
40
45
continue
41
46
}
42
47
resourceb := resource .Body ()
48
+ if errDyn := checkDynamicBlock (resourceb ); errDyn != nil {
49
+ return nil , errDyn
50
+ }
43
51
labels [0 ] = advCluster
44
52
resource .SetLabels (labels )
45
53
@@ -64,7 +72,7 @@ func fillFreeTier(resourceb *hclwrite.Body) error {
64
72
resourceb .SetAttributeValue (nClusterType , cty .StringVal (valClusterType ))
65
73
config := hclwrite .NewEmptyFile ()
66
74
configb := config .Body ()
67
- hcl .SetAttrInt (configb , "priority" , valPriority )
75
+ hcl .SetAttrInt (configb , nPriority , valMaxPriority )
68
76
if err := hcl .MoveAttr (resourceb , configb , nRegionNameSrc , nRegionName , errFreeCluster ); err != nil {
69
77
return err
70
78
}
@@ -81,71 +89,113 @@ func fillFreeTier(resourceb *hclwrite.Body) error {
81
89
configb .SetAttributeRaw (nElectableSpecs , hcl .TokensObject (electableSpec ))
82
90
83
91
repSpecs := hclwrite .NewEmptyFile ()
84
- repSpecs .Body ().SetAttributeRaw (nConfig , hcl .TokensArrayObject (config ))
85
- resourceb .SetAttributeRaw (nRepSpecs , hcl .TokensArrayObject (repSpecs ))
92
+ repSpecs .Body ().SetAttributeRaw (nConfig , hcl .TokensArraySingle (config ))
93
+ resourceb .SetAttributeRaw (nRepSpecs , hcl .TokensArraySingle (repSpecs ))
86
94
return nil
87
95
}
88
96
89
97
// fillReplicationSpecs is the entry point to convert clusters with replications_specs (all but free tier)
90
98
func fillReplicationSpecs (resourceb * hclwrite.Body ) error {
91
- root , errRoot := popRootAttrs (resourceb , errRepSpecs )
99
+ root , errRoot := popRootAttrs (resourceb )
92
100
if errRoot != nil {
93
101
return errRoot
94
102
}
95
- repSpecsSrc := resourceb .FirstMatchingBlock (nRepSpecs , nil )
96
- configSrc := repSpecsSrc .Body ().FirstMatchingBlock (nConfigSrc , nil )
97
- if configSrc == nil {
98
- return fmt .Errorf ("%s: %s not found" , errRepSpecs , nConfigSrc )
99
- }
100
-
101
103
resourceb .RemoveAttribute (nNumShards ) // num_shards in root is not relevant, only in replication_specs
102
104
// ok to fail as cloud_backup is optional
103
105
_ = hcl .MoveAttr (resourceb , resourceb , nCloudBackup , nBackupEnabled , errRepSpecs )
104
106
105
- config , errConfig := getRegionConfigs (configSrc , root )
106
- if errConfig != nil {
107
- return errConfig
107
+ // at least one replication_specs exists here, if not it would be a free tier cluster
108
+ repSpecsSrc := resourceb .FirstMatchingBlock (nRepSpecs , nil )
109
+ if err := checkDynamicBlock (repSpecsSrc .Body ()); err != nil {
110
+ return err
111
+ }
112
+ configs , errConfigs := getRegionConfigs (repSpecsSrc , root )
113
+ if errConfigs != nil {
114
+ return errConfigs
108
115
}
109
116
repSpecs := hclwrite .NewEmptyFile ()
110
- repSpecs .Body ().SetAttributeRaw (nConfig , config )
111
- resourceb .SetAttributeRaw (nRepSpecs , hcl .TokensArrayObject (repSpecs ))
117
+ repSpecs .Body ().SetAttributeRaw (nConfig , configs )
112
118
119
+ resourceb .SetAttributeRaw (nRepSpecs , hcl .TokensArraySingle (repSpecs ))
113
120
resourceb .RemoveBlock (repSpecsSrc )
114
121
return nil
115
122
}
116
123
117
- func getRegionConfigs (configSrc * hclwrite.Block , root attrVals ) (hclwrite.Tokens , error ) {
124
+ func getRegionConfigs (repSpecsSrc * hclwrite.Block , root attrVals ) (hclwrite.Tokens , error ) {
125
+ var configs []* hclwrite.File
126
+ for {
127
+ configSrc := repSpecsSrc .Body ().FirstMatchingBlock (nConfigSrc , nil )
128
+ if configSrc == nil {
129
+ break
130
+ }
131
+ config , err := getRegionConfig (configSrc , root )
132
+ if err != nil {
133
+ return nil , err
134
+ }
135
+ configs = append (configs , config )
136
+ repSpecsSrc .Body ().RemoveBlock (configSrc )
137
+ }
138
+ if len (configs ) == 0 {
139
+ return nil , fmt .Errorf ("%s: %s not found" , errRepSpecs , nConfigSrc )
140
+ }
141
+ sort .Slice (configs , func (i , j int ) bool {
142
+ pi , _ := hcl .GetAttrInt (configs [i ].Body ().GetAttribute (nPriority ), errPriority )
143
+ pj , _ := hcl .GetAttrInt (configs [j ].Body ().GetAttribute (nPriority ), errPriority )
144
+ return pi > pj
145
+ })
146
+ return hcl .TokensArray (configs ), nil
147
+ }
148
+
149
+ func getRegionConfig (configSrc * hclwrite.Block , root attrVals ) (* hclwrite.File , error ) {
118
150
file := hclwrite .NewEmptyFile ()
119
151
fileb := file .Body ()
120
152
fileb .SetAttributeRaw (nProviderName , root .req [nProviderName ])
121
153
if err := hcl .MoveAttr (configSrc .Body (), fileb , nRegionName , nRegionName , errRepSpecs ); err != nil {
122
154
return nil , err
123
155
}
124
- if err := hcl . MoveAttr ( configSrc .Body (), fileb , nPriority , nPriority , errRepSpecs ); err != nil {
156
+ if err := setPriority ( fileb , configSrc .Body (). GetAttribute ( nPriority ) ); err != nil {
125
157
return nil , err
126
158
}
127
- autoScaling := getAutoScalingOpt (root .opt )
128
- if autoScaling != nil {
129
- fileb .SetAttributeRaw (nAutoScaling , autoScaling )
130
- }
131
- electableSpecs , errElect := getElectableSpecs (configSrc , root )
132
- if errElect != nil {
133
- return nil , errElect
159
+ electableSpecs , errElec := getSpecs (nElectableNodes , configSrc , root )
160
+ if errElec != nil {
161
+ return nil , errElec
134
162
}
135
163
fileb .SetAttributeRaw (nElectableSpecs , electableSpecs )
136
- return hcl .TokensArrayObject (file ), nil
164
+ if readOnly , _ := getSpecs (nReadOnlyNodes , configSrc , root ); readOnly != nil {
165
+ fileb .SetAttributeRaw (nReadOnlySpecs , readOnly )
166
+ }
167
+ if analytics , _ := getSpecs (nAnalyticsNodes , configSrc , root ); analytics != nil {
168
+ fileb .SetAttributeRaw (nAnalyticsSpecs , analytics )
169
+ }
170
+ if autoScaling := getAutoScalingOpt (root .opt ); autoScaling != nil {
171
+ fileb .SetAttributeRaw (nAutoScaling , autoScaling )
172
+ }
173
+ return file , nil
137
174
}
138
175
139
- func getElectableSpecs (configSrc * hclwrite.Block , root attrVals ) (hclwrite.Tokens , error ) {
140
- file := hclwrite .NewEmptyFile ()
141
- fileb := file .Body ()
142
- if err := hcl .MoveAttr (configSrc .Body (), fileb , nElectableNodes , nNodeCount , errRepSpecs ); err != nil {
143
- return nil , err
176
+ func getSpecs (countName string , configSrc * hclwrite.Block , root attrVals ) (hclwrite.Tokens , error ) {
177
+ var (
178
+ file = hclwrite .NewEmptyFile ()
179
+ fileb = file .Body ()
180
+ count = configSrc .Body ().GetAttribute (countName )
181
+ )
182
+ if count == nil {
183
+ return nil , fmt .Errorf ("%s: attribute %s not found" , errRepSpecs , countName )
184
+ }
185
+ if countVal , errVal := hcl .GetAttrInt (count , errRepSpecs ); countVal == 0 && errVal == nil {
186
+ return nil , fmt .Errorf ("%s: attribute %s is 0" , errRepSpecs , countName )
144
187
}
188
+ fileb .SetAttributeRaw (nNodeCount , count .Expr ().BuildTokens (nil ))
145
189
fileb .SetAttributeRaw (nInstanceSize , root .req [nInstanceSizeSrc ])
146
190
if root .opt [nDiskSizeGB ] != nil {
147
191
fileb .SetAttributeRaw (nDiskSizeGB , root .opt [nDiskSizeGB ])
148
192
}
193
+ if root .opt [nEBSVolumeTypeSrc ] != nil {
194
+ fileb .SetAttributeRaw (nEBSVolumeType , root .opt [nEBSVolumeTypeSrc ])
195
+ }
196
+ if root .opt [nDiskIOPSSrc ] != nil {
197
+ fileb .SetAttributeRaw (nDiskIOPS , root .opt [nDiskIOPSSrc ])
198
+ }
149
199
return hcl .TokensObject (file ), nil
150
200
}
151
201
@@ -174,33 +224,62 @@ func getAutoScalingOpt(opt map[string]hclwrite.Tokens) hclwrite.Tokens {
174
224
return hcl .TokensObject (file )
175
225
}
176
226
227
+ func checkDynamicBlock (body * hclwrite.Body ) error {
228
+ for _ , block := range body .Blocks () {
229
+ if block .Type () == "dynamic" {
230
+ return errors .New ("dynamic blocks are not supported" )
231
+ }
232
+ }
233
+ return nil
234
+ }
235
+
236
+ func setPriority (body * hclwrite.Body , priority * hclwrite.Attribute ) error {
237
+ if priority == nil {
238
+ return fmt .Errorf ("%s: %s not found" , errRepSpecs , nPriority )
239
+ }
240
+ valPriority , err := hcl .GetAttrInt (priority , errPriority )
241
+ if err != nil {
242
+ return err
243
+ }
244
+ if valPriority < valMinPriority || valPriority > valMaxPriority {
245
+ return fmt .Errorf ("%s: %s is %d but must be between %d and %d" , errPriority , nPriority , valPriority , valMinPriority , valMaxPriority )
246
+ }
247
+ hcl .SetAttrInt (body , nPriority , valPriority )
248
+ return nil
249
+ }
250
+
177
251
// popRootAttrs deletes the attributes common to all replication_specs/regions_config and returns them.
178
- func popRootAttrs (body * hclwrite.Body , errPrefix string ) (attrVals , error ) {
252
+ func popRootAttrs (body * hclwrite.Body ) (attrVals , error ) {
179
253
var (
180
254
reqNames = []string {
181
255
nProviderName ,
182
256
nInstanceSizeSrc ,
183
257
}
184
258
optNames = []string {
259
+ nElectableNodes ,
260
+ nReadOnlyNodes ,
261
+ nAnalyticsNodes ,
185
262
nDiskSizeGB ,
186
263
nDiskGBEnabledSrc ,
187
264
nComputeEnabledSrc ,
188
265
nComputeMinInstanceSizeSrc ,
189
266
nComputeMaxInstanceSizeSrc ,
190
267
nComputeScaleDownEnabledSrc ,
268
+ nEBSVolumeTypeSrc ,
269
+ nDiskIOPSSrc ,
191
270
}
192
271
req = make (map [string ]hclwrite.Tokens )
193
272
opt = make (map [string ]hclwrite.Tokens )
194
273
)
195
274
for _ , name := range reqNames {
196
- tokens , err := hcl .PopAttr (body , name , errPrefix )
275
+ tokens , err := hcl .PopAttr (body , name , errRepSpecs )
197
276
if err != nil {
198
277
return attrVals {}, err
199
278
}
200
279
req [name ] = tokens
201
280
}
202
281
for _ , name := range optNames {
203
- tokens , _ := hcl .PopAttr (body , name , errPrefix )
282
+ tokens , _ := hcl .PopAttr (body , name , errRepSpecs )
204
283
if tokens != nil {
205
284
opt [name ] = tokens
206
285
}
0 commit comments