github.com/someshkoli/terratest@v0.41.1/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/gruntwork-io/terratest/modules/files"
    14  	"github.com/gruntwork-io/terratest/modules/opa"
    15  	"github.com/gruntwork-io/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  }