@@ -11,7 +11,6 @@ import (
1111// AdvancedClusterToV2 transforms all mongodbatlas_advanced_cluster resource definitions in a 
1212// Terraform configuration file from SDKv2 schema to TPF (Terraform Plugin Framework) schema. 
1313// All other resources and data sources are left untouched. 
14- // TODO: Not implemented yet. 
1514func  AdvancedClusterToV2 (config  []byte ) ([]byte , error ) {
1615	parser , err  :=  hcl .GetParser (config )
1716	if  err  !=  nil  {
@@ -37,6 +36,9 @@ func updateResource(resource *hclwrite.Block) (bool, error) {
3736		return  false , nil 
3837	}
3938	resourceb  :=  resource .Body ()
39+ 	if  errDyn  :=  checkDynamicBlock (resourceb ); errDyn  !=  nil  {
40+ 		return  false , errDyn 
41+ 	}
4042	if  hasExpectedBlocksAsAttributes (resourceb ) {
4143		return  false , nil 
4244	}
@@ -58,89 +60,163 @@ func updateResource(resource *hclwrite.Block) (bool, error) {
5860}
5961
6062func  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  {
6880		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 )
74101			}
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 ))
76106		}
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 )
79114		}
80115		for  range  numShardsVal  {
81- 			repSpecs  =  append (repSpecs , blockb )
116+ 			resultBodies  =  append (resultBodies , blockb )
82117		}
83118	}
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 ))
86123	}
87- 	resourceb .SetAttributeRaw (nRepSpecs , hcl .TokensArray (repSpecs ))
88124	return  nil 
89125}
90126
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 
106131	}
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 
109136	}
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 
112140}
113141
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 
118146	}
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 ))
123160	}
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 
125176}
126177
127178// hasExpectedBlocksAsAttributes checks if any of the expected block names 
128179// exist as attributes in the resource body. In that case conversion is not done 
129180// as advanced cluster is not in a valid SDKv2 configuration. 
130181func  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 }
140183	for  name  :=  range  resourceb .Attributes () {
141184		if  slices .Contains (expectedBlocks , name ) {
142185			return  true 
143186		}
144187	}
145188	return  false 
146189}
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