Skip to content

Commit

Permalink
Add support for non-zero exit code (open-policy-agent#1006)
Browse files Browse the repository at this point in the history
* Add support for non-zero exit code

Adds support for non-zero exit code when providing the --fail flag to
the eval command. 0 means no error, 1 means undefined result and 2 means
an error.

Fixes open-policy-agent#981

Signed-off-by: Kim Christensen <[email protected]>
  • Loading branch information
kichristensen authored and tsandall committed Oct 18, 2018
1 parent b952276 commit 58598d7
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 12 deletions.
39 changes: 27 additions & 12 deletions cmd/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package cmd
import (
"context"
"fmt"
"io"
"io/ioutil"
"os"
"strconv"
Expand Down Expand Up @@ -44,6 +45,7 @@ type evalCommandParams struct {
profileCriteria repeatedStringFlag
profileLimit intFlag
prettyLimit intFlag
fail bool
}

const (
Expand Down Expand Up @@ -155,9 +157,13 @@ Set the output format with the --format flag.
return nil
},
Run: func(cmd *cobra.Command, args []string) {
if err := eval(args, params); err != nil {
code, err := eval(args, params, os.Stdout)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
os.Exit(2)
}
if params.fail {
os.Exit(code)
}
},
}
Expand All @@ -177,19 +183,20 @@ Set the output format with the --format flag.
evalCommand.Flags().VarP(&params.profileCriteria, "profile-sort", "", "set sort order of expression profiler results")
evalCommand.Flags().VarP(&params.profileLimit, "profile-limit", "", "set number of profiling results to show")
evalCommand.Flags().VarP(&params.prettyLimit, "pretty-limit", "", "set limit after which pretty output gets truncated")
evalCommand.Flags().BoolVarP(&params.fail, "fail", "", false, "exits with non-zero exit code on undefined result and errors")
setIgnore(evalCommand.Flags(), &params.ignore)

RootCommand.AddCommand(evalCommand)
}

func eval(args []string, params evalCommandParams) (err error) {
func eval(args []string, params evalCommandParams, w io.Writer) (int, error) {

var query string

if params.stdin {
bs, err := ioutil.ReadAll(os.Stdin)
if err != nil {
return err
return 0, err
}
query = string(bs)
} else {
Expand All @@ -198,7 +205,7 @@ func eval(args []string, params evalCommandParams) (err error) {

info, err := runtime.Term(runtime.Params{})
if err != nil {
return err
return 2, err
}

regoArgs := []func(*rego.Rego){rego.Query(query), rego.Runtime(info)}
Expand All @@ -219,7 +226,7 @@ func eval(args []string, params evalCommandParams) (err error) {

loadResult, err := loader.Filtered(params.dataPaths.v, f.Apply)
if err != nil {
return err
return 0, err
}
regoArgs = append(regoArgs, rego.Store(inmem.NewFromObject(loadResult.Documents)))
for _, file := range loadResult.Modules {
Expand All @@ -229,11 +236,11 @@ func eval(args []string, params evalCommandParams) (err error) {

bs, err := readInputBytes(params)
if err != nil {
return err
return 2, err
} else if bs != nil {
term, err := ast.ParseTerm(string(bs))
if err != nil {
return err
return 2, err
}
regoArgs = append(regoArgs, rego.ParsedInput(term.Value))
}
Expand Down Expand Up @@ -292,15 +299,23 @@ func eval(args []string, params evalCommandParams) (err error) {
result.Profile = p.ReportTopNResults(params.profileLimit.v, sortOrder)
}

err = nil
switch params.outputFormat.String() {
case evalBindingsOutput:
return pr.Bindings(os.Stdout, result)
err = pr.Bindings(w, result)
case evalValuesOutput:
return pr.Values(os.Stdout, result)
err = pr.Values(w, result)
case evalPrettyOutput:
return pr.Pretty(os.Stdout, result)
err = pr.Pretty(w, result)
default:
return pr.JSON(os.Stdout, result)
err = pr.JSON(w, result)
}
if err != nil || result.Error != nil {
return 2, err
} else if len(result.Result) == 0 {
return 1, nil
} else {
return 0, nil
}
}

Expand Down
42 changes: 42 additions & 0 deletions cmd/eval_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2018 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

package cmd

import (
"bufio"
"bytes"
"testing"

"github.com/open-policy-agent/opa/util"
)

func TestEvalExitCode(t *testing.T) {
params := evalCommandParams{
fail: true,
explain: util.NewEnumFlag(explainModeOff, []string{explainModeFull}),
outputFormat: util.NewEnumFlag(evalJSONOutput, []string{evalJSONOutput}),
}
tests := []struct {
note string
query string
expectedCode int
}{
{"defined result", "true=true", 0},
{"undefined result", "true = false", 1},
{"on error", "x = 1/0", 2},
}

var b bytes.Buffer
writer := bufio.NewWriter(&b)
for _, tc := range tests {
code, err := eval([]string{tc.query}, params, writer)
if err != nil {
t.Fatalf("%v: Unexpected error %v", tc.note, err)
}
if code != tc.expectedCode {
t.Fatalf("%v: Expected code %v, got %v", tc.note, tc.expectedCode, code)
}
}
}

0 comments on commit 58598d7

Please sign in to comment.