diff --git a/cmd/exec.go b/cmd/exec.go index c1e3de3af6..c59e36421d 100644 --- a/cmd/exec.go +++ b/cmd/exec.go @@ -51,7 +51,10 @@ After: Decision Logs By default, the 'exec' command executes the "default decision" (specified in the OPA configuration) against each input file. This can be overridden by specifying the --decision argument and pointing at a specific policy decision, -e.g., opa exec --decision /foo/bar/baz ...`, +e.g., opa exec --decision /foo/bar/baz ... + +Alternative Usage: + ` + RootCommand.Use + ` exec [ [...]] --stdin-input [flags]`, PreRunE: func(cmd *cobra.Command, _ []string) error { return env.CmdFlags.CheckEnvironmentVariables(cmd) diff --git a/cmd/internal/exec/exec.go b/cmd/internal/exec/exec.go index ed8f940fe1..8748b4dda1 100644 --- a/cmd/internal/exec/exec.go +++ b/cmd/internal/exec/exec.go @@ -36,15 +36,20 @@ func Exec(ctx context.Context, opa *sdk.OPA, params *Params) error { r = &jsonReporter{w: params.Output, buf: make([]result, 0), ctx: &ctx, opa: opa, params: params, decisionFunc: opa.Decision} - var err error if params.StdIn { - err = execOnStdIn() - } else { - err = execOnInputFiles(params) + if err := execOnStdIn(); err != nil { + return err + } } - if err != nil { + + if err := execOnInputFiles(params); err != nil { return err } + + if err := r.Close(); err != nil { + return err + } + return r.ReportFailure() } @@ -59,7 +64,7 @@ func execOnStdIn() error { return errors.New("cannot execute on empty input; please enter valid json or yaml when using the --stdin-input flag") } r.StoreDecision(&input, stdInPath) - return r.Close() + return nil } type fileListItem struct { @@ -87,7 +92,7 @@ func execOnInputFiles(params *Params) error { } r.StoreDecision(input, item.Path) } - return r.Close() + return nil } func listAllPaths(roots []string) chan fileListItem { diff --git a/cmd/internal/exec/exec_test.go b/cmd/internal/exec/exec_test.go index 08f6f85242..2c2476c27f 100644 --- a/cmd/internal/exec/exec_test.go +++ b/cmd/internal/exec/exec_test.go @@ -79,7 +79,7 @@ func TestExec(t *testing.T) { files map[string]string stdIn bool input string - assertion func(err error) + assertion func(t *testing.T, buf string, err error) }{ { description: "should read from valid JSON file and not raise an error", @@ -96,7 +96,7 @@ func TestExec(t *testing.T) { test_fun }`, }, - assertion: func(err error) { + assertion: func(t *testing.T, _ string, err error) { if err != nil { t.Fatalf("unexpected error raised: %q", err.Error()) } @@ -117,7 +117,7 @@ func TestExec(t *testing.T) { test_fun }`, }, - assertion: func(err error) { + assertion: func(t *testing.T, _ string, err error) { if err == nil { t.Fatalf("expected error, found none") } @@ -142,12 +142,58 @@ func TestExec(t *testing.T) { }, stdIn: true, input: `{"foo": 7}`, - assertion: func(err error) { + assertion: func(t *testing.T, _ string, err error) { if err != nil { t.Fatalf("unexpected error raised: %q", err.Error()) } }, }, + { + description: "should read from files and stdin-input if flag is set", + files: map[string]string{ + "files/test.json": `{"foo": 8}`, + "bundle/x.rego": `package system + + test_fun := x { + x = false + x + } + + undefined_test { + test_fun + }`, + }, + stdIn: true, + input: `{"foo": 7}`, + assertion: func(t *testing.T, output string, err error) { + if err != nil { + t.Fatalf("unexpected error raised: %q", err.Error()) + } + + exp := `{ + "result": [ + { + "path": "--stdin-input", + "error": { + "code": "opa_undefined_error", + "message": "/system/main decision was undefined" + } + }, + { + "path": "%ROOT%/files/test.json", + "error": { + "code": "opa_undefined_error", + "message": "/system/main decision was undefined" + } + } + ] +} +` + if output != exp { + t.Fatalf("expected output to be:\n\n%s\n\ngot:\n\n%s", exp, output) + } + }, + }, } for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { @@ -175,11 +221,12 @@ func TestExec(t *testing.T) { os.Remove(tempFile.Name()) }() os.Stdin = tempFile - } else { - if _, ok := tt.files["files/test.json"]; ok { - params.Paths = append(params.Paths, dir+"/files/") - } } + + if _, ok := tt.files["files/test.json"]; ok { + params.Paths = append(params.Paths, dir+"/files/") + } + ctx := context.Background() opa, _ := sdk.New(ctx, sdk.Options{ Config: bytes.NewReader([]byte{}), @@ -190,7 +237,8 @@ func TestExec(t *testing.T) { }) err := Exec(ctx, opa, params) - tt.assertion(err) + output := strings.Replace(buf.String(), dir, "%ROOT%", -1) + tt.assertion(t, output, err) }) }) }