Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve opa logging #1011

Merged
merged 4 commits into from
Oct 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/_data/examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,9 @@
name: OPA Terraform Example
image: /assets/img/logos/opa-logo.png
files:
- url: /examples/terraform-opa-example/pass/main.tf
- url: /examples/terraform-opa-example/pass/main_pass.tf
id: pass_terraform_main_code
- url: /examples/terraform-opa-example/fail/main.tf
- url: /examples/terraform-opa-example/fail/main_fail.tf
id: fail_terraform_main_code
- url: /examples/terraform-opa-example/policy/enforce_source.rego
id: policy_main_code
Expand Down
34 changes: 30 additions & 4 deletions modules/opa/eval.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package opa

import (
"path/filepath"
"strings"
"sync"

"github.com/gruntwork-io/terratest/modules/logger"
Expand All @@ -26,6 +28,11 @@ type EvalOptions struct {

// When true, keep any temp files and folders that are created for the purpose of running opa eval.
DebugKeepTempFiles bool

// When true, disable the functionality where terratest reruns the opa check on the same file and query all elements
// on error. By default, terratest will rerun the opa eval call with `data` query so you can see all the contents
// evaluated.
DebugDisableQueryDataOnError bool
}

// FailMode signals whether `opa eval` should fail when the query returns an undefined value (FailUndefined), a
Expand Down Expand Up @@ -82,13 +89,23 @@ func asyncEval(
cmd := shell.Command{
Command: "opa",
Args: formatOPAEvalArgs(options, jsonFilePath, resultQuery),
Logger: options.Logger,

// Do not log output from shell package so we can log the full json without breaking it up. This is ok, because
// opa eval is typically very quick.
Logger: logger.Discard,
}
err := shell.RunCommandE(t, cmd)
err := runCommandWithFullLoggingE(t, options.Logger, cmd)
ruleBasePath := filepath.Base(options.RulePath)
if err == nil {
options.Logger.Logf(t, "opa eval passed on file %s", jsonFilePath)
options.Logger.Logf(t, "opa eval passed on file %s (policy %s; query %s)", jsonFilePath, ruleBasePath, resultQuery)
} else {
options.Logger.Logf(t, "Failed opa eval on file %s", jsonFilePath)
options.Logger.Logf(t, "Failed opa eval on file %s (policy %s; query %s)", jsonFilePath, ruleBasePath, resultQuery)
if options.DebugDisableQueryDataOnError == false {
options.Logger.Logf(t, "DEBUG: rerunning opa eval to query for full data.")
cmd.Args = formatOPAEvalArgs(options, jsonFilePath, "data")
// We deliberately ignore the error here as we want to only return the original error.
runCommandWithFullLoggingE(t, options.Logger, cmd)
}
}
errChan <- err
}
Expand All @@ -114,3 +131,12 @@ func formatOPAEvalArgs(options *EvalOptions, jsonFilePath string, resultQuery st
)
return args
}

// runCommandWithFullLogging will log the command output in its entirety with buffering. This avoids breaking up the
// logs when commands are run concurrently. This is a private function used in the context of opa only because opa runs
// very quickly, and the output of opa is hard to parse if it is broken up by interleaved logs.
func runCommandWithFullLoggingE(t testing.TestingT, logger *logger.Logger, cmd shell.Command) error {
output, err := shell.RunCommandAndGetOutputE(t, cmd)
logger.Logf(t, "Output of command `%s %s`:\n%s", cmd.Command, strings.Join(cmd.Args, " "), output)
return err
}
2 changes: 1 addition & 1 deletion test/terraform_opa_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ func TestOPAEvalTerraformModuleFailsCheck(t *testing.T) {
RulePath: policyPath,
}

// website::tag::6:: Here we expect the checks to fail, so we use `OPAEvalE` to check the error.
// website::tag::6:: Here we expect the checks to fail, so we use `OPAEvalE` to check the error. Note that on the files that failed, this function will rerun `opa eval` with the query set to `data`, so you can see the values of all the variables in the policy. This is useful for debugging failures.
require.Error(t, terraform.OPAEvalE(t, tfOpts, opaOpts, "data.enforce_source.allow"))
}