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

cmd/exec: Supporting simultaneous input from stdin and files #6831

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 4 additions & 1 deletion cmd/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 [<path> [...]] --stdin-input [flags]`,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$ opa exec -h
Execute against input files.

The 'exec' command executes OPA against one or more input files. If the paths
refer to directories, OPA will execute against files contained inside those
directories, recursively.

The 'exec' command accepts a --config-file/-c or series of --set options as
arguments. These options behave the same as way as 'opa run'. Since the 'exec'
command is intended to execute OPA in one-shot, the 'exec' command will
manually trigger plugins before and after policy execution:

Before: Discovery -> Bundle -> Status
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 ...

Alternative Usage:
  opa_darwin_arm64 exec [<path> [...]] --stdin-input [flags]

Usage:
  opa_darwin_arm64 exec <path> [<path> [...]] [flags]


PreRunE: func(cmd *cobra.Command, _ []string) error {
return env.CmdFlags.CheckEnvironmentVariables(cmd)
Expand Down
19 changes: 12 additions & 7 deletions cmd/internal/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}

Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
66 changes: 57 additions & 9 deletions cmd/internal/exec/exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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())
}
Expand All @@ -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")
}
Expand All @@ -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) {
Expand Down Expand Up @@ -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{}),
Expand All @@ -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)
})
})
}
Expand Down
Loading