@@ -4,7 +4,10 @@ import (
44 "errors"
55 "fmt"
66 "sort"
7+ "strconv"
8+ "strings"
79
10+ "github.com/hashicorp/hcl/v2/hclsyntax"
811 "github.com/hashicorp/hcl/v2/hclwrite"
912 "github.com/mongodb-labs/atlas-cli-plugin-terraform/internal/hcl"
1013 "github.com/zclconf/go-cty/cty"
@@ -86,11 +89,11 @@ func fillFreeTier(resourceb *hclwrite.Body) error {
8689 if err := hcl .MoveAttr (resourceb , electableSpec .Body (), nInstanceSizeSrc , nInstanceSize , errFreeCluster ); err != nil {
8790 return err
8891 }
89- configb .SetAttributeRaw (nElectableSpecs , hcl .TokensObject (electableSpec ))
92+ configb .SetAttributeRaw (nElectableSpecs , hcl .TokensObject (electableSpec . Body () ))
9093
9194 repSpecs := hclwrite .NewEmptyFile ()
92- repSpecs .Body ().SetAttributeRaw (nConfig , hcl .TokensArraySingle (config ))
93- resourceb .SetAttributeRaw (nRepSpecs , hcl .TokensArraySingle (repSpecs ))
95+ repSpecs .Body ().SetAttributeRaw (nConfig , hcl .TokensArraySingle (configb ))
96+ resourceb .SetAttributeRaw (nRepSpecs , hcl .TokensArraySingle (repSpecs . Body () ))
9497 return nil
9598}
9699
@@ -115,14 +118,31 @@ func fillReplicationSpecs(resourceb *hclwrite.Body) error {
115118 }
116119 repSpecs := hclwrite .NewEmptyFile ()
117120 repSpecs .Body ().SetAttributeRaw (nConfig , configs )
118-
119- resourceb .SetAttributeRaw (nRepSpecs , hcl .TokensArraySingle (repSpecs ))
121+ resourceb .SetAttributeRaw (nRepSpecs , hcl .TokensArraySingle (repSpecs .Body ()))
122+ tags , errTags := getTagsLabelsOpt (resourceb , nTags )
123+ if errTags != nil {
124+ return errTags
125+ }
126+ if tags != nil {
127+ resourceb .SetAttributeRaw (nTags , tags )
128+ }
129+ labels , errLabels := getTagsLabelsOpt (resourceb , nLabels )
130+ if errLabels != nil {
131+ return errLabels
132+ }
133+ if labels != nil {
134+ resourceb .SetAttributeRaw (nLabels , labels )
135+ }
136+ fillBlockOpt (resourceb , nTimeouts )
137+ fillBlockOpt (resourceb , nAdvConf )
138+ fillBlockOpt (resourceb , nBiConnector )
139+ fillBlockOpt (resourceb , nPinnedFCV )
120140 resourceb .RemoveBlock (repSpecsSrc )
121141 return nil
122142}
123143
124144func getRegionConfigs (repSpecsSrc * hclwrite.Block , root attrVals ) (hclwrite.Tokens , error ) {
125- var configs []* hclwrite.File
145+ var configs []* hclwrite.Body
126146 for {
127147 configSrc := repSpecsSrc .Body ().FirstMatchingBlock (nConfigSrc , nil )
128148 if configSrc == nil {
@@ -132,15 +152,15 @@ func getRegionConfigs(repSpecsSrc *hclwrite.Block, root attrVals) (hclwrite.Toke
132152 if err != nil {
133153 return nil , err
134154 }
135- configs = append (configs , config )
155+ configs = append (configs , config . Body () )
136156 repSpecsSrc .Body ().RemoveBlock (configSrc )
137157 }
138158 if len (configs ) == 0 {
139159 return nil , fmt .Errorf ("%s: %s not found" , errRepSpecs , nConfigSrc )
140160 }
141161 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 )
162+ pi , _ := hcl .GetAttrInt (configs [i ].GetAttribute (nPriority ), errPriority )
163+ pj , _ := hcl .GetAttrInt (configs [j ].GetAttribute (nPriority ), errPriority )
144164 return pi > pj
145165 })
146166 return hcl .TokensArray (configs ), nil
@@ -156,15 +176,15 @@ func getRegionConfig(configSrc *hclwrite.Block, root attrVals) (*hclwrite.File,
156176 if err := setPriority (fileb , configSrc .Body ().GetAttribute (nPriority )); err != nil {
157177 return nil , err
158178 }
159- electableSpecs , errElec := getSpecs (nElectableNodes , configSrc , root )
179+ electableSpecs , errElec := getSpecs (configSrc , nElectableNodes , root )
160180 if errElec != nil {
161181 return nil , errElec
162182 }
163183 fileb .SetAttributeRaw (nElectableSpecs , electableSpecs )
164- if readOnly , _ := getSpecs (nReadOnlyNodes , configSrc , root ); readOnly != nil {
184+ if readOnly , _ := getSpecs (configSrc , nReadOnlyNodes , root ); readOnly != nil {
165185 fileb .SetAttributeRaw (nReadOnlySpecs , readOnly )
166186 }
167- if analytics , _ := getSpecs (nAnalyticsNodes , configSrc , root ); analytics != nil {
187+ if analytics , _ := getSpecs (configSrc , nAnalyticsNodes , root ); analytics != nil {
168188 fileb .SetAttributeRaw (nAnalyticsSpecs , analytics )
169189 }
170190 if autoScaling := getAutoScalingOpt (root .opt ); autoScaling != nil {
@@ -173,7 +193,7 @@ func getRegionConfig(configSrc *hclwrite.Block, root attrVals) (*hclwrite.File,
173193 return file , nil
174194}
175195
176- func getSpecs (countName string , configSrc * hclwrite.Block , root attrVals ) (hclwrite.Tokens , error ) {
196+ func getSpecs (configSrc * hclwrite.Block , countName string , root attrVals ) (hclwrite.Tokens , error ) {
177197 var (
178198 file = hclwrite .NewEmptyFile ()
179199 fileb = file .Body ()
@@ -196,7 +216,7 @@ func getSpecs(countName string, configSrc *hclwrite.Block, root attrVals) (hclwr
196216 if root .opt [nDiskIOPSSrc ] != nil {
197217 fileb .SetAttributeRaw (nDiskIOPS , root .opt [nDiskIOPSSrc ])
198218 }
199- return hcl .TokensObject (file ), nil
219+ return hcl .TokensObject (fileb ), nil
200220}
201221
202222func getAutoScalingOpt (opt map [string ]hclwrite.Tokens ) hclwrite.Tokens {
@@ -209,19 +229,55 @@ func getAutoScalingOpt(opt map[string]hclwrite.Tokens) hclwrite.Tokens {
209229 {nComputeScaleDownEnabledSrc , nComputeScaleDownEnabled },
210230 }
211231 file = hclwrite .NewEmptyFile ()
232+ fileb = file .Body ()
212233 found = false
213234 )
214235 for _ , tuple := range names {
215236 src , dst := tuple [0 ], tuple [1 ]
216237 if tokens := opt [src ]; tokens != nil {
217- file . Body () .SetAttributeRaw (dst , tokens )
238+ fileb .SetAttributeRaw (dst , tokens )
218239 found = true
219240 }
220241 }
221242 if ! found {
222243 return nil
223244 }
224- return hcl .TokensObject (file )
245+ return hcl .TokensObject (fileb )
246+ }
247+
248+ func getTagsLabelsOpt (resourceb * hclwrite.Body , name string ) (hclwrite.Tokens , error ) {
249+ var (
250+ file = hclwrite .NewEmptyFile ()
251+ fileb = file .Body ()
252+ found = false
253+ )
254+ for {
255+ block := resourceb .FirstMatchingBlock (name , nil )
256+ if block == nil {
257+ break
258+ }
259+ key := block .Body ().GetAttribute (nKey )
260+ value := block .Body ().GetAttribute (nValue )
261+ if key == nil || value == nil {
262+ return nil , fmt .Errorf ("%s: %s or %s not found" , name , nKey , nValue )
263+ }
264+ setKeyValue (fileb , key , value )
265+ resourceb .RemoveBlock (block )
266+ found = true
267+ }
268+ if ! found {
269+ return nil , nil
270+ }
271+ return hcl .TokensObject (fileb ), nil
272+ }
273+
274+ func fillBlockOpt (resourceb * hclwrite.Body , name string ) {
275+ block := resourceb .FirstMatchingBlock (name , nil )
276+ if block == nil {
277+ return
278+ }
279+ resourceb .RemoveBlock (block )
280+ resourceb .SetAttributeRaw (name , hcl .TokensObject (block .Body ()))
225281}
226282
227283func checkDynamicBlock (body * hclwrite.Body ) error {
@@ -233,6 +289,19 @@ func checkDynamicBlock(body *hclwrite.Body) error {
233289 return nil
234290}
235291
292+ func setKeyValue (body * hclwrite.Body , key , value * hclwrite.Attribute ) {
293+ keyStr , err := hcl .GetAttrString (key , "" )
294+ if err == nil {
295+ if ! hclsyntax .ValidIdentifier (keyStr ) {
296+ keyStr = strconv .Quote (keyStr ) // wrap in quotes so invalid identifiers (e.g. with blanks) can be used as attribute names
297+ }
298+ } else {
299+ keyStr = strings .TrimSpace (string (key .Expr ().BuildTokens (nil ).Bytes ()))
300+ keyStr = "(" + keyStr + ")" // wrap in parentheses so unresolved expressions can be used as attribute names
301+ }
302+ body .SetAttributeRaw (keyStr , value .Expr ().BuildTokens (nil ))
303+ }
304+
236305func setPriority (body * hclwrite.Body , priority * hclwrite.Attribute ) error {
237306 if priority == nil {
238307 return fmt .Errorf ("%s: %s not found" , errRepSpecs , nPriority )
0 commit comments