@@ -11,7 +11,6 @@ import (
11
11
// AdvancedClusterToV2 transforms all mongodbatlas_advanced_cluster resource definitions in a
12
12
// Terraform configuration file from SDKv2 schema to TPF (Terraform Plugin Framework) schema.
13
13
// All other resources and data sources are left untouched.
14
- // TODO: Not implemented yet.
15
14
func AdvancedClusterToV2 (config []byte ) ([]byte , error ) {
16
15
parser , err := hcl .GetParser (config )
17
16
if err != nil {
@@ -37,6 +36,9 @@ func updateResource(resource *hclwrite.Block) (bool, error) {
37
36
return false , nil
38
37
}
39
38
resourceb := resource .Body ()
39
+ if errDyn := checkDynamicBlock (resourceb ); errDyn != nil {
40
+ return false , errDyn
41
+ }
40
42
if hasExpectedBlocksAsAttributes (resourceb ) {
41
43
return false , nil
42
44
}
@@ -58,89 +60,163 @@ func updateResource(resource *hclwrite.Block) (bool, error) {
58
60
}
59
61
60
62
func convertRepSpecs (resourceb * hclwrite.Body , diskSizeGB hclwrite.Tokens ) error {
61
- var repSpecs []* hclwrite.Body
62
- for {
63
- block := resourceb .FirstMatchingBlock (nRepSpecs , nil )
64
- if block == nil {
65
- break
66
- }
67
- resourceb .RemoveBlock (block )
63
+ d , err := convertRepSpecsWithDynamicBlock (resourceb , diskSizeGB )
64
+ if err != nil {
65
+ return err
66
+ }
67
+ if d .IsPresent () {
68
+ resourceb .RemoveBlock (d .block )
69
+ resourceb .SetAttributeRaw (nRepSpecs , d .tokens )
70
+ return nil
71
+ }
72
+ repSpecBlocks := collectBlocks (resourceb , nRepSpecs )
73
+ if len (repSpecBlocks ) == 0 {
74
+ return fmt .Errorf ("must have at least one replication_specs" )
75
+ }
76
+ hasVariableShards := hasVariableNumShards (repSpecBlocks )
77
+ var resultTokens []hclwrite.Tokens
78
+ var resultBodies []* hclwrite.Body
79
+ for _ , block := range repSpecBlocks {
68
80
blockb := block .Body ()
69
- numShardsVal := 1 // default to 1 if num_shards not present
70
- if numShardsAttr := blockb .GetAttribute (nNumShards ); numShardsAttr != nil {
71
- var err error
72
- if numShardsVal , err = hcl .GetAttrInt (numShardsAttr , errNumShards ); err != nil {
73
- return err
81
+ shardsAttr := blockb .GetAttribute (nNumShards )
82
+ blockb .RemoveAttribute (nNumShards )
83
+ dConfig , err := getDynamicBlock (blockb , nConfig )
84
+ if err != nil {
85
+ return err
86
+ }
87
+ if dConfig .IsPresent () {
88
+ transformReferences (dConfig .content .Body (), getResourceName (dConfig .block ), nRegion )
89
+ copyAttributesSorted (dConfig .content .Body (), dConfig .content .Body ().Attributes ())
90
+ processAllSpecs (dConfig .content .Body (), diskSizeGB )
91
+ tokens := hcl .TokensFromExpr (buildForExpr (nRegion , hcl .GetAttrExpr (dConfig .forEach ), false ))
92
+ tokens = append (tokens , hcl .TokensObject (dConfig .content .Body ())... )
93
+ blockb .SetAttributeRaw (nConfig , hcl .EncloseBracketsNewLines (tokens ))
94
+ blockb .RemoveBlock (dConfig .block )
95
+ } else {
96
+ var configs []* hclwrite.Body
97
+ for _ , configBlock := range collectBlocks (blockb , nConfig ) {
98
+ configBlockb := configBlock .Body ()
99
+ processAllSpecs (configBlockb , diskSizeGB )
100
+ configs = append (configs , configBlockb )
74
101
}
75
- blockb .RemoveAttribute (nNumShards )
102
+ if len (configs ) == 0 {
103
+ return fmt .Errorf ("replication_specs must have at least one region_configs" )
104
+ }
105
+ blockb .SetAttributeRaw (nConfig , hcl .TokensArray (configs ))
76
106
}
77
- if err := convertConfig (blockb , diskSizeGB ); err != nil {
78
- return err
107
+ if hasVariableShards {
108
+ resultTokens = append (resultTokens , processNumShardsWhenSomeIsVariable (shardsAttr , blockb ))
109
+ continue
110
+ }
111
+ numShardsVal := 1 // Default to 1 if num_shards is not set
112
+ if shardsAttr != nil {
113
+ numShardsVal , _ = hcl .GetAttrInt (shardsAttr , errNumShards )
79
114
}
80
115
for range numShardsVal {
81
- repSpecs = append (repSpecs , blockb )
116
+ resultBodies = append (resultBodies , blockb )
82
117
}
83
118
}
84
- if len (repSpecs ) == 0 {
85
- return fmt .Errorf ("must have at least one replication_specs" )
119
+ if hasVariableShards {
120
+ resourceb .SetAttributeRaw (nRepSpecs , hcl .TokensFuncConcat (resultTokens ... ))
121
+ } else {
122
+ resourceb .SetAttributeRaw (nRepSpecs , hcl .TokensArray (resultBodies ))
86
123
}
87
- resourceb .SetAttributeRaw (nRepSpecs , hcl .TokensArray (repSpecs ))
88
124
return nil
89
125
}
90
126
91
- func convertConfig (repSpecs * hclwrite.Body , diskSizeGB hclwrite.Tokens ) error {
92
- var configs []* hclwrite.Body
93
- for {
94
- block := repSpecs .FirstMatchingBlock (nConfig , nil )
95
- if block == nil {
96
- break
97
- }
98
- repSpecs .RemoveBlock (block )
99
- blockb := block .Body ()
100
- fillSpecOpt (blockb , nElectableSpecs , diskSizeGB )
101
- fillSpecOpt (blockb , nReadOnlySpecs , diskSizeGB )
102
- fillSpecOpt (blockb , nAnalyticsSpecs , diskSizeGB )
103
- fillSpecOpt (blockb , nAutoScaling , nil ) // auto_scaling doesn't need disk_size_gb
104
- fillSpecOpt (blockb , nAnalyticsAutoScaling , nil ) // analytics_auto_scaling doesn't need disk_size_gb
105
- configs = append (configs , blockb )
127
+ func convertRepSpecsWithDynamicBlock (resourceb * hclwrite.Body , diskSizeGB hclwrite.Tokens ) (dynamicBlock , error ) {
128
+ dSpec , err := getDynamicBlock (resourceb , nRepSpecs )
129
+ if err != nil || ! dSpec .IsPresent () {
130
+ return dynamicBlock {}, err
106
131
}
107
- if len (configs ) == 0 {
108
- return fmt .Errorf ("replication_specs must have at least one region_configs" )
132
+ transformReferences (dSpec .content .Body (), nRepSpecs , nSpec )
133
+ dConfig , err := convertConfigsWithDynamicBlock (dSpec .content .Body (), diskSizeGB )
134
+ if err != nil {
135
+ return dynamicBlock {}, err
109
136
}
110
- repSpecs .SetAttributeRaw (nConfig , hcl .TokensArray (configs ))
111
- return nil
137
+ forSpec := hcl .TokensFromExpr (buildForExpr (nSpec , hcl .GetAttrExpr (dSpec .forEach ), true ))
138
+ dSpec .tokens = hcl .TokensFuncFlatten (append (forSpec , dConfig .tokens ... ))
139
+ return dSpec , nil
112
140
}
113
141
114
- func fillSpecOpt ( resourceb * hclwrite.Body , name string , diskSizeGBTokens hclwrite.Tokens ) {
115
- block := resourceb . FirstMatchingBlock ( name , nil )
116
- if block = = nil {
117
- return
142
+ func convertConfigsWithDynamicBlock ( specbSrc * hclwrite.Body , diskSizeGB hclwrite.Tokens ) ( dynamicBlock , error ) {
143
+ d , err := getDynamicBlock ( specbSrc , nConfig )
144
+ if err ! = nil {
145
+ return dynamicBlock {}, err
118
146
}
119
- if diskSizeGBTokens != nil {
120
- blockb := block .Body ()
121
- blockb .RemoveAttribute (nDiskSizeGB )
122
- blockb .SetAttributeRaw (nDiskSizeGB , diskSizeGBTokens )
147
+ configBody := d .content .Body ()
148
+ transformReferences (configBody , getResourceName (d .block ), nRegion )
149
+ regionConfigBody := hclwrite .NewEmptyFile ().Body ()
150
+ copyAttributesSorted (regionConfigBody , configBody .Attributes ())
151
+ for _ , block := range configBody .Blocks () {
152
+ blockType := block .Type ()
153
+ blockBody := hclwrite .NewEmptyFile ().Body ()
154
+ copyAttributesSorted (blockBody , block .Body ().Attributes ())
155
+ if diskSizeGB != nil &&
156
+ (blockType == nElectableSpecs || blockType == nReadOnlySpecs || blockType == nAnalyticsSpecs ) {
157
+ blockBody .SetAttributeRaw (nDiskSizeGB , diskSizeGB )
158
+ }
159
+ regionConfigBody .SetAttributeRaw (blockType , hcl .TokensObject (blockBody ))
123
160
}
124
- fillBlockOpt (resourceb , name )
161
+ repSpecb := hclwrite .NewEmptyFile ().Body ()
162
+ if zoneNameAttr := specbSrc .GetAttribute (nZoneName ); zoneNameAttr != nil {
163
+ repSpecb .SetAttributeRaw (nZoneName , hcl .TokensFromExpr (
164
+ transformReference (hcl .GetAttrExpr (zoneNameAttr ), nRepSpecs , nSpec )))
165
+ }
166
+ regionTokens := hcl .TokensFromExpr (buildForExpr (nRegion , fmt .Sprintf ("%s.%s" , nSpec , nConfig ), false ))
167
+ regionTokens = append (regionTokens , hcl .TokensObject (regionConfigBody )... )
168
+ repSpecb .SetAttributeRaw (nConfig , hcl .EncloseBracketsNewLines (regionTokens ))
169
+ if numShardsAttr := specbSrc .GetAttribute (nNumShards ); numShardsAttr != nil {
170
+ tokens := hcl .TokensFromExpr (buildForExpr ("i" ,
171
+ fmt .Sprintf ("range(%s)" , transformReference (hcl .GetAttrExpr (numShardsAttr ), nRepSpecs , nSpec )), false ))
172
+ tokens = append (tokens , hcl .TokensObject (repSpecb )... )
173
+ return dynamicBlock {tokens : hcl .EncloseBracketsNewLines (tokens )}, nil
174
+ }
175
+ return dynamicBlock {tokens : hcl .TokensArraySingle (repSpecb )}, nil
125
176
}
126
177
127
178
// hasExpectedBlocksAsAttributes checks if any of the expected block names
128
179
// exist as attributes in the resource body. In that case conversion is not done
129
180
// as advanced cluster is not in a valid SDKv2 configuration.
130
181
func hasExpectedBlocksAsAttributes (resourceb * hclwrite.Body ) bool {
131
- expectedBlocks := []string {
132
- nRepSpecs ,
133
- nTags ,
134
- nLabels ,
135
- nAdvConfig ,
136
- nBiConnector ,
137
- nPinnedFCV ,
138
- nTimeouts ,
139
- }
182
+ expectedBlocks := []string {nRepSpecs , nTags , nLabels , nAdvConfig , nBiConnector , nPinnedFCV , nTimeouts }
140
183
for name := range resourceb .Attributes () {
141
184
if slices .Contains (expectedBlocks , name ) {
142
185
return true
143
186
}
144
187
}
145
188
return false
146
189
}
190
+
191
+ func copyAttributesSorted (targetBody * hclwrite.Body , sourceAttrs map [string ]* hclwrite.Attribute ) {
192
+ var names []string
193
+ for name := range sourceAttrs {
194
+ names = append (names , name )
195
+ }
196
+ slices .Sort (names )
197
+ for _ , name := range names {
198
+ expr := hcl .GetAttrExpr (sourceAttrs [name ])
199
+ targetBody .SetAttributeRaw (name , hcl .TokensFromExpr (expr ))
200
+ }
201
+ }
202
+
203
+ func processAllSpecs (body * hclwrite.Body , diskSizeGB hclwrite.Tokens ) {
204
+ fillSpecOpt (body , nElectableSpecs , diskSizeGB )
205
+ fillSpecOpt (body , nReadOnlySpecs , diskSizeGB )
206
+ fillSpecOpt (body , nAnalyticsSpecs , diskSizeGB )
207
+ fillSpecOpt (body , nAutoScaling , nil )
208
+ fillSpecOpt (body , nAnalyticsAutoScaling , nil )
209
+ }
210
+
211
+ func fillSpecOpt (resourceb * hclwrite.Body , name string , diskSizeGBTokens hclwrite.Tokens ) {
212
+ block := resourceb .FirstMatchingBlock (name , nil )
213
+ if block == nil {
214
+ return
215
+ }
216
+ if diskSizeGBTokens != nil {
217
+ blockb := block .Body ()
218
+ blockb .RemoveAttribute (nDiskSizeGB )
219
+ blockb .SetAttributeRaw (nDiskSizeGB , diskSizeGBTokens )
220
+ }
221
+ fillBlockOpt (resourceb , name )
222
+ }
0 commit comments