package exec import ( "context" "errors" "os" "path" "path/filepath" "strings" "github.com/open-policy-agent/opa/v1/sdk" ) var ( r *jsonReporter parsers = map[string]parser{ ".json": utilParser{}, ".yaml": utilParser{}, ".yml": utilParser{}, } ) const stdInPath = "--stdin-input" // Exec executes OPA against the supplied files and outputs each result. // // NOTE(tsandall): consider expanding functionality: // // - specialized output formats (e.g., pretty/non-JSON outputs) // - exit codes set by convention or policy (e.g,. non-empty set => error) // - support for new input file formats beyond JSON and YAML func Exec(ctx context.Context, opa *sdk.OPA, params *Params) error { if err := params.validateParams(); err != nil { return err } r = &jsonReporter{w: params.Output, buf: make([]result, 0), ctx: &ctx, opa: opa, params: params, decisionFunc: opa.Decision} if params.StdIn { if err := execOnStdIn(); err != nil { return err } } if err := execOnInputFiles(params); err != nil { return err } if err := r.Close(); err != nil { return err } return r.ReportFailure() } func execOnStdIn() error { sr := stdInReader{Reader: os.Stdin} p := utilParser{} raw := sr.ReadInput() input, err := p.Parse(strings.NewReader(raw)) if err != nil { return err } else if input == nil { 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 nil } type fileListItem struct { Path string Error error } func execOnInputFiles(params *Params) error { for item := range listAllPaths(params.Paths) { if item.Error != nil { return item.Error } input, err := parse(item.Path) if err != nil { r.Report(result{Path: item.Path, Error: err}) if params.FailDefined || params.Fail || params.FailNonEmpty { r.errorCount++ } continue } else if input == nil { continue } r.StoreDecision(input, item.Path) } return nil } func listAllPaths(roots []string) chan fileListItem { ch := make(chan fileListItem) go func() { for _, path := range roots { err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } ch <- fileListItem{Path: path} return nil }) if err != nil { ch <- fileListItem{Path: path, Error: err} } } close(ch) }() return ch } func parse(p string) (*any, error) { selectedParser, ok := parsers[path.Ext(p)] if !ok { return nil, nil } f, err := os.Open(p) if err != nil { return nil, err } defer f.Close() val, err := selectedParser.Parse(f) if err != nil { return nil, err } return &val, nil }