github.com/mponton/terratest@v0.44.0/modules/terraform/opa_check.go (about) 1 package terraform 2 3 import ( 4 "io/ioutil" 5 "os" 6 "path/filepath" 7 "strings" 8 9 "github.com/hashicorp/go-multierror" 10 "github.com/stretchr/testify/require" 11 "github.com/tmccombs/hcl2json/convert" 12 13 "github.com/mponton/terratest/modules/files" 14 "github.com/mponton/terratest/modules/opa" 15 "github.com/mponton/terratest/modules/testing" 16 ) 17 18 // OPAEval runs `opa eval` with the given option on the terraform files identified in the TerraformDir directory of the 19 // Options struct. Note that since OPA does not natively support parsing HCL code, we first convert all the files to 20 // JSON prior to passing it through OPA. This function fails the test if there is an error. 21 func OPAEval( 22 t testing.TestingT, 23 tfOptions *Options, 24 opaEvalOptions *opa.EvalOptions, 25 resultQuery string, 26 ) { 27 require.NoError(t, OPAEvalE(t, tfOptions, opaEvalOptions, resultQuery)) 28 } 29 30 // OPAEvalE runs `opa eval` with the given option on the terraform files identified in the TerraformDir directory of the 31 // Options struct. Note that since OPA does not natively support parsing HCL code, we first convert all the files to 32 // JSON prior to passing it through OPA. 33 func OPAEvalE( 34 t testing.TestingT, 35 tfOptions *Options, 36 opaEvalOptions *opa.EvalOptions, 37 resultQuery string, 38 ) error { 39 tfOptions.Logger.Logf(t, "Running terraform files in %s through `opa eval` on policy %s", tfOptions.TerraformDir, opaEvalOptions.RulePath) 40 41 // Find all the tf files in the terraform dir to process. 42 tfFiles, err := files.FindTerraformSourceFilesInDir(tfOptions.TerraformDir) 43 if err != nil { 44 return err 45 } 46 47 // Create a temporary dir to store all the json files 48 tmpDir, err := ioutil.TempDir("", "terratest-opa-hcl2json-*") 49 if err != nil { 50 return err 51 } 52 if !opaEvalOptions.DebugKeepTempFiles { 53 defer os.RemoveAll(tmpDir) 54 } 55 tfOptions.Logger.Logf(t, "Using temporary folder %s for json representation of terraform module %s", tmpDir, tfOptions.TerraformDir) 56 57 // Convert all the found tf files to json format so OPA works. 58 jsonFiles := make([]string, len(tfFiles)) 59 errorsOccurred := new(multierror.Error) 60 for i, tfFile := range tfFiles { 61 tfFileBase := filepath.Base(tfFile) 62 tfFileBaseName := strings.TrimSuffix(tfFileBase, filepath.Ext(tfFileBase)) 63 outPath := filepath.Join(tmpDir, tfFileBaseName+".json") 64 tfOptions.Logger.Logf(t, "Converting %s to json %s", tfFile, outPath) 65 if err := HCLFileToJSONFile(tfFile, outPath); err != nil { 66 errorsOccurred = multierror.Append(errorsOccurred, err) 67 } 68 jsonFiles[i] = outPath 69 } 70 if err := errorsOccurred.ErrorOrNil(); err != nil { 71 return err 72 } 73 74 // Run OPA checks on each of the converted json files. 75 return opa.EvalE(t, opaEvalOptions, jsonFiles, resultQuery) 76 } 77 78 // HCLFileToJSONFile is a function that takes a path containing HCL code, and converts it to JSON representation and 79 // writes out the contents to the given path. 80 func HCLFileToJSONFile(hclPath, jsonOutPath string) error { 81 fileBytes, err := ioutil.ReadFile(hclPath) 82 if err != nil { 83 return err 84 } 85 converted, err := convert.Bytes(fileBytes, hclPath, convert.Options{}) 86 if err != nil { 87 return err 88 } 89 return ioutil.WriteFile(jsonOutPath, converted, 0600) 90 }