@@ -181,46 +181,135 @@ func fillReplicationSpecs(resourceb *hclwrite.Body, root attrVals) error {
181181 resourceb .SetAttributeRaw (nRepSpecs , d .tokens )
182182 return nil
183183 }
184- // at least one replication_specs exists here, if not it would be a free tier cluster
185- var specbs []* hclwrite.Body
184+
185+ // Collect all replication_specs blocks first
186+ var repSpecBlocks []* hclwrite.Block
186187 for {
187- var (
188- specSrc = resourceb .FirstMatchingBlock (nRepSpecs , nil )
189- spec = hclwrite .NewEmptyFile ()
190- specb = spec .Body ()
191- )
192- if specSrc == nil {
188+ block := resourceb .FirstMatchingBlock (nRepSpecs , nil )
189+ if block == nil {
193190 break
194191 }
195- specbSrc := specSrc .Body ()
196- d , err := fillWithDynamicRegionConfigs (specbSrc , root , false )
197- if err != nil {
198- return err
199- }
200- if d .IsPresent () {
201- resourceb .RemoveBlock (specSrc )
202- resourceb .SetAttributeRaw (nRepSpecs , d .tokens )
203- return nil
204- }
205- // ok to fail as zone_name is optional
206- _ = hcl .MoveAttr (specbSrc , specb , nZoneName , nZoneName , errRepSpecs )
207- shards := specbSrc .GetAttribute (nNumShards )
208- if shards == nil {
209- return fmt .Errorf ("%s: %s not found" , errRepSpecs , nNumShards )
192+ resourceb .RemoveBlock (block )
193+ repSpecBlocks = append (repSpecBlocks , block )
194+ }
195+
196+ if len (repSpecBlocks ) == 0 {
197+ return fmt .Errorf ("%s: no replication_specs found" , errRepSpecs )
198+ }
199+
200+ // Check if any replication_specs has a variable num_shards
201+ hasVariableNumShards := false
202+ for _ , block := range repSpecBlocks {
203+ shardsAttr := block .Body ().GetAttribute (nNumShards )
204+ if shardsAttr != nil {
205+ _ , err := hcl .GetAttrInt (shardsAttr , errNumShards )
206+ if err != nil {
207+ hasVariableNumShards = true
208+ break
209+ }
210210 }
211- shardsVal , err := hcl .GetAttrInt (shards , errNumShards )
212- if err != nil {
213- return err
211+ }
212+
213+ // If we have any variable num_shards, we need to use concat
214+ if hasVariableNumShards {
215+ var concatParts []hclwrite.Tokens
216+
217+ for _ , block := range repSpecBlocks {
218+ spec := hclwrite .NewEmptyFile ()
219+ specb := spec .Body ()
220+ specbSrc := block .Body ()
221+
222+ // Check for dynamic region configs
223+ d , err := fillWithDynamicRegionConfigs (specbSrc , root , false )
224+ if err != nil {
225+ return err
226+ }
227+ if d .IsPresent () {
228+ concatParts = append (concatParts , d .tokens )
229+ continue
230+ }
231+
232+ // Handle zone_name
233+ _ = hcl .MoveAttr (specbSrc , specb , nZoneName , nZoneName , errRepSpecs )
234+
235+ // Handle num_shards
236+ shardsAttr := specbSrc .GetAttribute (nNumShards )
237+ if shardsAttr == nil {
238+ return fmt .Errorf ("%s: %s not found" , errRepSpecs , nNumShards )
239+ }
240+
241+ shardsVal , err := hcl .GetAttrInt (shardsAttr , errNumShards )
242+
243+ if errConfig := fillRegionConfigs (specb , specbSrc , root ); errConfig != nil {
244+ return errConfig
245+ }
246+
247+ if err != nil {
248+ // num_shards is a variable/expression
249+ shardsExpr := hcl .GetAttrExpr (shardsAttr )
250+ forExpr := fmt .Sprintf ("for i in range(%s) :" , shardsExpr )
251+ tokens := hcl .TokensFromExpr (forExpr )
252+ tokens = append (tokens , hcl .TokensObject (specb )... )
253+ concatParts = append (concatParts , hcl .EncloseBracketsNewLines (tokens ))
254+ } else {
255+ // num_shards is a literal number - create explicit array
256+ var specs []* hclwrite.Body
257+ for range shardsVal {
258+ specs = append (specs , specb )
259+ }
260+ concatParts = append (concatParts , hcl .TokensArray (specs ))
261+ }
214262 }
215- if err := fillRegionConfigs (specb , specbSrc , root ); err != nil {
216- return err
263+
264+ // Use concat to combine all parts
265+ if len (concatParts ) > 1 {
266+ resourceb .SetAttributeRaw (nRepSpecs , hcl .TokensFuncConcat (concatParts ... ))
267+ } else {
268+ resourceb .SetAttributeRaw (nRepSpecs , concatParts [0 ])
217269 }
218- for range shardsVal {
219- specbs = append (specbs , specb )
270+ } else {
271+ // All num_shards are numeric, use simple array
272+ var specbs []* hclwrite.Body
273+ for _ , block := range repSpecBlocks {
274+ spec := hclwrite .NewEmptyFile ()
275+ specb := spec .Body ()
276+ specbSrc := block .Body ()
277+
278+ // Check for dynamic region configs
279+ d , err := fillWithDynamicRegionConfigs (specbSrc , root , false )
280+ if err != nil {
281+ return err
282+ }
283+ if d .IsPresent () {
284+ // For dynamic blocks that have numerical num_shards
285+ // Extract the tokens and add to array
286+ // This is complex, for now just return the dynamic block as is
287+ resourceb .SetAttributeRaw (nRepSpecs , d .tokens )
288+ return nil
289+ }
290+
291+ // Handle zone_name
292+ _ = hcl .MoveAttr (specbSrc , specb , nZoneName , nZoneName , errRepSpecs )
293+
294+ // Handle num_shards
295+ shardsAttr := specbSrc .GetAttribute (nNumShards )
296+ if shardsAttr == nil {
297+ return fmt .Errorf ("%s: %s not found" , errRepSpecs , nNumShards )
298+ }
299+
300+ shardsVal , _ := hcl .GetAttrInt (shardsAttr , errNumShards )
301+
302+ if err := fillRegionConfigs (specb , specbSrc , root ); err != nil {
303+ return err
304+ }
305+
306+ for range shardsVal {
307+ specbs = append (specbs , specb )
308+ }
220309 }
221- resourceb .RemoveBlock ( specSrc )
310+ resourceb .SetAttributeRaw ( nRepSpecs , hcl . TokensArray ( specbs ) )
222311 }
223- resourceb . SetAttributeRaw ( nRepSpecs , hcl . TokensArray ( specbs ))
312+
224313 return nil
225314}
226315
0 commit comments