@@ -46,15 +46,14 @@ func BidirectionalConversion(t *testing.T, ignoredFields []string) {
4646 }
4747
4848 logger := zaptest .NewLogger (t )
49+ // Create a temporary directory for running terraform.
50+ tfDir , err := os .MkdirTemp (tmpDir , "terraform" )
51+ if err != nil {
52+ t .Fatalf ("error creating a temporary directory for running terraform: %v" , err )
53+ }
54+ defer os .RemoveAll (tfDir )
4955
5056 for _ , stepN := range stepNumbers {
51- // Create a temporary directory for running terraform.
52- tfDir , err := os .MkdirTemp (tmpDir , fmt .Sprintf ("terraform%d" , stepN ))
53- if err != nil {
54- t .Fatalf ("error creating a temporary directory for running terraform: %v" , err )
55- }
56- defer os .RemoveAll (tfDir )
57-
5857 subtestName := fmt .Sprintf ("step%d" , stepN )
5958 t .Run (subtestName , func (t * testing.T ) {
6059 retries := 0
@@ -172,6 +171,7 @@ func testSingleResource(t *testing.T, testName string, testData ResourceTestData
172171 }
173172
174173 exportTfFilePath := fmt .Sprintf ("%s/%s_export.tf" , tfDir , testName )
174+ defer os .Remove (exportTfFilePath )
175175 err = os .WriteFile (exportTfFilePath , exportConfigData , 0644 )
176176 if err != nil {
177177 return fmt .Errorf ("error when writing the file %s" , exportTfFilePath )
@@ -218,13 +218,18 @@ func testSingleResource(t *testing.T, testName string, testData ResourceTestData
218218 return fmt .Errorf ("error when converting the round-trip config: %#v" , err )
219219 }
220220
221- roundtripTfFilePath := fmt .Sprintf ("%s_roundtrip.tf" , testName )
221+ rtTfFile := fmt .Sprintf ("%s_roundtrip.tf" , testName )
222+ roundtripTfFilePath := filepath .Join (tfDir , rtTfFile )
223+ defer os .Remove (roundtripTfFilePath )
222224 err = os .WriteFile (roundtripTfFilePath , roundtripConfigData , 0644 )
223225 if err != nil {
224226 return fmt .Errorf ("error when writing the file %s" , roundtripTfFilePath )
225227 }
226- if os .Getenv ("WRITE_FILES" ) == "" {
227- defer os .Remove (roundtripTfFilePath )
228+ if os .Getenv ("WRITE_FILES" ) != "" {
229+ err = os .WriteFile (rtTfFile , roundtripConfigData , 0644 )
230+ if err != nil {
231+ return fmt .Errorf ("error writing file %s" , rtTfFile )
232+ }
228233 }
229234
230235 if diff := cmp .Diff (string (roundtripConfigData ), string (exportConfigData )); diff != "" {
@@ -236,7 +241,7 @@ func testSingleResource(t *testing.T, testName string, testData ResourceTestData
236241 return fmt .Errorf ("error when converting the third round-trip config: %#v" , err )
237242 }
238243
239- if assestsDiff := cmp . Diff (reexportAssets , roundtripAssets ); assestsDiff != "" {
244+ if err = compareCaiAssets (reexportAssets , roundtripAssets , ignoredFieldSet ); err != nil {
240245 log .Printf ("Roundtrip config is different from the export config.\n roundtrip config:\n %s\n export config:\n %s" , string (roundtripConfigData ), string (exportConfigData ))
241246 return fmt .Errorf ("test %s got diff (-want +got): %s" , testName , diff )
242247 }
@@ -245,50 +250,8 @@ func testSingleResource(t *testing.T, testName string, testData ResourceTestData
245250
246251 // Step 3
247252 // Compare most fields between the exported asset and roundtrip asset, except for "data" field for resource
248- assetMap := convertToAssetMap (assets )
249- roundtripAssetMap := convertToAssetMap (roundtripAssets )
250- for assetType , asset := range assetMap {
251- if roundtripAsset , ok := roundtripAssetMap [assetType ]; ! ok {
252- return fmt .Errorf ("roundtrip asset for type %s is missing" , assetType )
253- } else {
254- if _ , ok := ignoredFieldSet ["ASSETNAME" ]; ! ok {
255- if err := compareAssetName (asset .Name , roundtripAsset .Name ); err != nil {
256- return err
257- }
258- }
259- if diff := cmp .Diff (
260- asset .Resource ,
261- roundtripAsset .Resource ,
262- // secretmanager.googleapis.com/SecretVersion has secret as parent, not project
263- cmpopts .IgnoreFields (caiasset.AssetResource {}, "Version" , "Data" , "Location" , "Parent" , "DiscoveryDocumentURI" ),
264- // Consider DiscoveryDocumentURI equal if they have the same number of path segments when split by "/".
265- cmp .FilterPath (func (p cmp.Path ) bool {
266- return p .Last ().String () == ".DiscoveryDocumentURI"
267- }, cmp .Comparer (func (x , y string ) bool {
268- parts1 := strings .Split (x , "/" )
269- parts2 := strings .Split (y , "/" )
270- return len (parts1 ) == len (parts2 )
271- })),
272- cmp .FilterPath (func (p cmp.Path ) bool {
273- return p .Last ().String () == ".DiscoveryName"
274- }, cmp .Comparer (func (x , y string ) bool {
275- xParts := strings .Split (x , "/" )
276- yParts := strings .Split (y , "/" )
277- return xParts [len (xParts )- 1 ] == yParts [len (yParts )- 1 ]
278- })),
279- cmp .FilterPath (func (p cmp.Path ) bool {
280- // Filter if "parent" field in original asset is an empty string
281- // and then ingore comparing
282- if p .Last ().String () == ".Parent" {
283- v1 , _ := p .Index (- 1 ).Values ()
284- return v1 .IsZero ()
285- }
286- return false
287- }, cmp .Ignore ()),
288- ); diff != "" {
289- return fmt .Errorf ("differences found between exported asset and roundtrip asset (-want +got):\n %s" , diff )
290- }
291- }
253+ if err = compareCaiAssets (assets , roundtripAssets , ignoredFieldSet ); err != nil {
254+ return err
292255 }
293256 log .Printf ("Step 3 passes for resource %s. Exported asset and roundtrip asset are identical" , testData .ResourceAddress )
294257
@@ -441,6 +404,7 @@ func tfplan2caiConvert(t *testing.T, tfFileName, jsonFileName string, tfDir stri
441404
442405 planFile := fmt .Sprintf ("%s.tfplan.json" , tfFileName )
443406 planfilePath := filepath .Join (tfDir , planFile )
407+ defer os .Remove (planfilePath )
444408 jsonPlan , err := os .ReadFile (planfilePath )
445409 if err != nil {
446410 return nil , err
@@ -468,80 +432,6 @@ func tfplan2caiConvert(t *testing.T, tfFileName, jsonFileName string, tfDir stri
468432 return assets , nil
469433}
470434
471- // Example:
472- //
473- // data := map[string]interface{}{
474- // "database": map[string]interface{}{
475- // "host": "localhost",
476- // "user": "admin",
477- // },
478- // }
479- //
480- // Path of "host" in "data" is ["database", "host"]
481- type Field struct {
482- Path []string
483- }
484-
485- // Deletes fields from the resource data of CAI assets
486- func deleteFieldsFromAssets (assets []caiasset.Asset , ignoredResourceDataFields []string ) []caiasset.Asset {
487- // The key is the content type, such as "resource"
488- ignoredFieldsMap := make (map [string ][]Field , 0 )
489- for _ , ignoredField := range ignoredResourceDataFields {
490- parts := strings .Split (ignoredField , "." )
491- if len (parts ) <= 1 {
492- continue
493- }
494- if parts [0 ] == "RESOURCE" {
495- if _ , ok := ignoredFieldsMap ["RESOURCE" ]; ! ok {
496- ignoredFieldsMap ["RESOURCE" ] = make ([]Field , 0 )
497- }
498- f := Field {Path : parts [1 :]}
499- ignoredFieldsMap ["RESOURCE" ] = append (ignoredFieldsMap ["RESOURCE" ], f )
500- }
501- }
502-
503- for _ , asset := range assets {
504- if asset .Resource != nil && asset .Resource .Data != nil {
505- data := asset .Resource .Data
506- for _ , ignoredField := range ignoredFieldsMap ["RESOURCE" ] {
507- path := ignoredField .Path
508- deleteMapFieldByPath (data , path )
509- }
510- }
511- }
512- return assets
513- }
514-
515- // Deletes a field from a map by its path.
516- // Example:
517- //
518- // data := map[string]interface{}{
519- // "database": map[string]interface{}{
520- // "host": "localhost",
521- // "user": "admin",
522- // },
523- // }
524- //
525- // path := ["database", "host"]
526- func deleteMapFieldByPath (data map [string ]interface {}, path []string ) {
527- i := 0
528- for i < len (path )- 1 {
529- k := path [i ]
530- if v , ok := data [k ]; ok {
531- if data , ok = v .(map [string ]interface {}); ok && data != nil {
532- i ++
533- } else {
534- break
535- }
536- } else {
537- break
538- }
539- }
540- if i == len (path )- 1 {
541- delete (data , path [i ])
542- }
543- }
544-
545435// Compares the asset name in export asset and roundtrip asset and ignores "null" in the name
546436// Example: //cloudresourcemanager.googleapis.com/projects/123456
547437func compareAssetName (want , got string ) error {
@@ -562,3 +452,52 @@ func compareAssetName(want, got string) error {
562452 }
563453 return nil
564454}
455+
456+ func compareCaiAssets (assets1 , assets2 []caiasset.Asset , ignoredFieldSet map [string ]any ) error {
457+ assetMap := convertToAssetMap (assets1 )
458+ roundtripAssetMap := convertToAssetMap (assets2 )
459+ for assetType , asset := range assetMap {
460+ if roundtripAsset , ok := roundtripAssetMap [assetType ]; ! ok {
461+ return fmt .Errorf ("roundtrip asset for type %s is missing" , assetType )
462+ } else {
463+ if _ , ok := ignoredFieldSet ["ASSETNAME" ]; ! ok {
464+ if err := compareAssetName (asset .Name , roundtripAsset .Name ); err != nil {
465+ return err
466+ }
467+ }
468+ if diff := cmp .Diff (
469+ asset .Resource ,
470+ roundtripAsset .Resource ,
471+ // secretmanager.googleapis.com/SecretVersion has secret as parent, not project
472+ cmpopts .IgnoreFields (caiasset.AssetResource {}, "Version" , "Data" , "Location" , "Parent" , "DiscoveryDocumentURI" ),
473+ // Consider DiscoveryDocumentURI equal if they have the same number of path segments when split by "/".
474+ cmp .FilterPath (func (p cmp.Path ) bool {
475+ return p .Last ().String () == ".DiscoveryDocumentURI"
476+ }, cmp .Comparer (func (x , y string ) bool {
477+ parts1 := strings .Split (x , "/" )
478+ parts2 := strings .Split (y , "/" )
479+ return len (parts1 ) == len (parts2 )
480+ })),
481+ cmp .FilterPath (func (p cmp.Path ) bool {
482+ return p .Last ().String () == ".DiscoveryName"
483+ }, cmp .Comparer (func (x , y string ) bool {
484+ xParts := strings .Split (x , "/" )
485+ yParts := strings .Split (y , "/" )
486+ return xParts [len (xParts )- 1 ] == yParts [len (yParts )- 1 ]
487+ })),
488+ cmp .FilterPath (func (p cmp.Path ) bool {
489+ // Filter if "parent" field in original asset is an empty string
490+ // and then ingore comparing
491+ if p .Last ().String () == ".Parent" {
492+ v1 , _ := p .Index (- 1 ).Values ()
493+ return v1 .IsZero ()
494+ }
495+ return false
496+ }, cmp .Ignore ()),
497+ ); diff != "" {
498+ return fmt .Errorf ("differences found between exported asset and roundtrip asset (-want +got):\n %s" , diff )
499+ }
500+ }
501+ }
502+ return nil
503+ }
0 commit comments