Skip to content

Commit

Permalink
cmd/exec: Supporting simultaneous input from stdin and files (#6831)
Browse files Browse the repository at this point in the history
Signed-off-by: Johan Fylling <[email protected]>
  • Loading branch information
johanfylling authored Jun 26, 2024
1 parent 5647253 commit e50a306
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 17 deletions.
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]`,

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

0 comments on commit e50a306

Please sign in to comment.