1515// SPDX-License-Identifier: Apache-2.0
1616
1717// -------------------------------------------------------------------------------
18- // This file is almost to identical to the conftest version of this command.
18+ // This file is " almost" identical to the conftest version of this command.
1919// Use `make conftest-test-cmd-diff` to show a comparison.
2020// Note also that the way that flags are handled here is not consistent with how
2121// it's done elsewhere. This intentional in order to be consistent with Conftest.
2222// -------------------------------------------------------------------------------
2323package test
2424
2525import (
26+ "context"
2627 "encoding/json"
2728 "fmt"
2829 "os"
@@ -31,6 +32,8 @@ import (
3132 "github.com/open-policy-agent/conftest/output"
3233 "github.com/open-policy-agent/conftest/parser"
3334 "github.com/open-policy-agent/conftest/runner"
35+ "github.com/open-policy-agent/opa/v1/ast"
36+ "github.com/open-policy-agent/opa/v1/storage"
3437 "github.com/spf13/cobra"
3538 "github.com/spf13/viper"
3639)
@@ -49,9 +52,7 @@ inputs.
4952Policies are written in the Rego language. For more
5053information on how to write Rego policies, see the documentation:
5154https://www.openpolicyagent.org/docs/latest/policy-language/
52- `
5355
54- const testExample = `
5556The policy location defaults to the policy directory in the local folder.
5657The location can be overridden with the '--policy' flag, e.g.:
5758
@@ -74,14 +75,13 @@ The test command supports the '--output' flag to specify the type, e.g.:
7475 $ EC_EXPERIMENTAL=1 ec test -o table -p examples/kubernetes/policy examples/kubernetes/deployment.yaml
7576
7677Which will return the following output:
77-
78- +---------+----------------------------------+--------------------------------+
79- | RESULT | FILE | MESSAGE |
80- +---------+----------------------------------+--------------------------------+
81- | success | examples/kubernetes/service.yaml | |
82- | warning | examples/kubernetes/service.yaml | Found service hello-kubernetes |
83- | | | but services are not allowed |
84- +---------+----------------------------------+--------------------------------+
78+ +---------+----------------------------------+--------------------------------+
79+ | RESULT | FILE | MESSAGE |
80+ +---------+----------------------------------+--------------------------------+
81+ | success | examples/kubernetes/service.yaml | |
82+ | warning | examples/kubernetes/service.yaml | Found service hello-kubernetes |
83+ | | | but services are not allowed |
84+ +---------+----------------------------------+--------------------------------+
8585
8686By default, it will use the regular stdout output. For a full list of available output types, see the of the '--output' flag.
8787
@@ -93,21 +93,35 @@ It expects one or more urls to fetch the latest policies from, e.g.:
9393See the pull command for more details on supported protocols for fetching policies.
9494
9595When debugging policies it can be useful to use a more verbose policy evaluation output. By using the '--trace' flag
96- the output will include a detailed trace of how the policy was evaluated, e.g.
96+ the output will include a detailed trace of how the policy was evaluated. The trace output will be written to stderr,
97+ while the regular output will be written to stdout. This allows you to use the '--trace' flag together with any output
98+ format, including table, JSON, etc.
9799
100+ # Trace output
98101 $ EC_EXPERIMENTAL=1 ec test --trace <input-file>
102+
103+ # Trace output with any non-standard output format
104+ $ EC_EXPERIMENTAL=1 ec test --trace --output=table <input-file>
105+
106+ # Redirect trace output to a file while viewing formatted output
107+ $ EC_EXPERIMENTAL=1 ec test --trace --output=json <input-file> 2>trace.log
99108`
100109
110+ // TestRun stores the compiler and store for a test run.
111+ type TestRun struct {
112+ Compiler * ast.Compiler
113+ Store storage.Store
114+ }
115+
101116const OutputAppstudio = "appstudio"
102117
103- // newTestCommand creates a new test command.
104- func newTestCommand ( ) * cobra.Command {
118+ // NewTestCommand creates a new test command.
119+ func NewTestCommand ( ctx context. Context ) * cobra.Command {
105120 cmd := cobra.Command {
106- Use : "test <path> [path [...]]" ,
107- Short : "Test your configuration files using Open Policy Agent" ,
108- Long : testDesc ,
109- Example : testExample ,
110- PreRunE : func (cmd * cobra.Command , args []string ) error {
121+ Use : "test <path> [path [...]]" ,
122+ Short : "Test your configuration files using Open Policy Agent" ,
123+ Long : testDesc ,
124+ PreRunE : func (cmd * cobra.Command , _ []string ) error {
111125 flagNames := []string {
112126 "all-namespaces" ,
113127 "combine" ,
@@ -118,16 +132,19 @@ func newTestCommand() *cobra.Command {
118132 "no-color" ,
119133 "no-fail" ,
120134 "suppress-exceptions" ,
121- "file " ,
135+ "output " ,
122136 "parser" ,
123137 "policy" ,
124138 "proto-file-dirs" ,
125139 "capabilities" ,
140+ "rego-version" ,
126141 "trace" ,
127142 "strict" ,
143+ "show-builtin-errors" ,
128144 "update" ,
129145 "junit-hide-message" ,
130146 "quiet" ,
147+ "tls" ,
131148 }
132149 for _ , name := range flagNames {
133150 if err := viper .BindPFlag (name , cmd .Flags ().Lookup (name )); err != nil {
@@ -139,8 +156,6 @@ func newTestCommand() *cobra.Command {
139156 },
140157
141158 RunE : func (cmd * cobra.Command , fileList []string ) error {
142- ctx := cmd .Context ()
143-
144159 if len (fileList ) < 1 {
145160 cmd .Usage () //nolint
146161 return fmt .Errorf ("missing required arguments" )
@@ -151,83 +166,62 @@ func newTestCommand() *cobra.Command {
151166 return fmt .Errorf ("unmarshal parameters: %w" , err )
152167 }
153168
154- outputFormats , err := cmd .Flags ().GetStringSlice ("output" )
155- if err != nil {
156- return fmt .Errorf ("reading flag: %w" , err )
157- }
158- if len (outputFormats ) == 0 {
159- outputFormats = []string {output .OutputStandard }
160- }
161-
162169 results , resultsErr := runner .Run (ctx , fileList )
163- var exitCode int
170+
171+ exitCode := results .ExitCode ()
164172 if runner .FailOnWarn {
165- exitCode = output .ExitCodeFailOnWarn (results )
166- } else {
167- exitCode = output .ExitCode (results )
173+ exitCode = results .ExitCodeFailOnWarn ()
174+ }
175+
176+ // Do this so we can write the appstudio format to a file
177+ // with the `--output appstudio=file.json` style flag
178+ format := runner .Output
179+ parts := strings .SplitN (format , "=" , 2 )
180+ outputFilePath := ""
181+ if len (parts ) > 0 {
182+ format = parts [0 ]
183+ if len (parts ) == 2 {
184+ outputFilePath = parts [1 ]
185+ }
168186 }
169187
170188 if ! runner .Quiet || exitCode != 0 {
171- for _ , outputAndFormat := range outputFormats {
172- parts := strings .SplitN (outputAndFormat , "=" , 2 )
173-
174- format := output .OutputStandard
175- outputFilePath := ""
176- if len (parts ) > 0 {
177- format = parts [0 ]
178- if len (parts ) == 2 {
179- outputFilePath = parts [1 ]
180- }
189+ if format == OutputAppstudio {
190+ // The appstudio format is unknown to Conftest so we handle it ourselves
191+ if resultsErr != nil {
192+ return appstudioErrorHandler (runner .NoFail , "running test" , resultsErr )
181193 }
182194
183- if format == OutputAppstudio {
184- // The appstudio format is unknown to Conftest so we handle it ourselves
185-
186- if resultsErr != nil {
187- return appstudioErrorHandler (runner .NoFail , "running test" , resultsErr )
188- }
195+ report := appstudioReport (results , runner .Namespace )
196+ reportOutput , err := json .Marshal (report )
197+ if err != nil {
198+ return appstudioErrorHandler (runner .NoFail , "output results" , err )
199+ }
189200
190- report := appstudioReport (results , runner .Namespace )
191- reportOutput , err := json .Marshal (report )
201+ if outputFilePath != "" {
202+ // Output to a file
203+ err := os .WriteFile (outputFilePath , reportOutput , 0600 )
192204 if err != nil {
193- return appstudioErrorHandler (runner .NoFail , "output results" , err )
194- }
195-
196- if outputFilePath != "" {
197- err := os .WriteFile (outputFilePath , reportOutput , 0600 )
198- if err != nil {
199- return fmt .Errorf ("creating output file: %w" , err )
200- }
201- } else {
202- fmt .Fprintln (cmd .OutOrStdout (), string (reportOutput ))
205+ return fmt .Errorf ("creating output file: %w" , err )
203206 }
204-
205207 } else {
206- // Conftest handles the output
207-
208- if resultsErr != nil {
209- return fmt .Errorf ("running test: %w" , resultsErr )
210- }
211-
212- var outputFile * os.File
213- if outputFilePath != "" {
214- outputFile , err = os .Create (outputFilePath )
215- if err != nil {
216- return fmt .Errorf ("creating output file %s: %w" , outputFilePath , err )
217- }
218- defer outputFile .Close ()
219- }
208+ // Output to stdout
209+ fmt .Fprintln (cmd .OutOrStdout (), string (reportOutput ))
210+ }
211+ } else {
212+ if resultsErr != nil {
213+ return fmt .Errorf ("running test: %w" , resultsErr )
214+ }
220215
221- outputter := output .Get (format , output.Options {
222- NoColor : runner .NoColor ,
223- SuppressExceptions : runner .SuppressExceptions ,
224- Tracing : runner .Trace ,
225- JUnitHideMessage : viper .GetBool ("junit-hide-message" ),
226- File : outputFile ,
227- })
228- if err := outputter .Output (results ); err != nil {
229- return fmt .Errorf ("output results: %w" , err )
230- }
216+ // Conftest handles the output
217+ outputter := output .Get (runner .Output , output.Options {
218+ NoColor : runner .NoColor ,
219+ SuppressExceptions : runner .SuppressExceptions ,
220+ Tracing : runner .Trace ,
221+ JUnitHideMessage : viper .GetBool ("junit-hide-message" ),
222+ })
223+ if err := outputter .Output (results ); err != nil {
224+ return fmt .Errorf ("output results: %w" , err )
231225 }
232226 }
233227
@@ -252,28 +246,24 @@ func newTestCommand() *cobra.Command {
252246
253247 cmd .Flags ().Bool ("trace" , false , "Enable more verbose trace output for Rego queries" )
254248 cmd .Flags ().Bool ("strict" , false , "Enable strict mode for Rego policies" )
249+ cmd .Flags ().Bool ("show-builtin-errors" , false , "Collect and return all encountered built-in errors" )
255250 cmd .Flags ().Bool ("combine" , false , "Combine all config files to be evaluated together" )
256251
257252 cmd .Flags ().String ("ignore" , "" , "A regex pattern which can be used for ignoring paths" )
258253 cmd .Flags ().String ("parser" , "" , fmt .Sprintf ("Parser to use to parse the configurations. Valid parsers: %s" , parser .Parsers ()))
259254 cmd .Flags ().String ("capabilities" , "" , "Path to JSON file that can restrict opa functionality against a given policy. Default: all operations allowed" )
255+ cmd .Flags ().String ("rego-version" , "v1" , "Which version of Rego syntax to use. Options: v0, v1" )
260256
261- cmd .Flags ().String ( "file " , "" , "File path to write output to" )
257+ cmd .Flags ().StringP ( "output " , "o " , output . OutputStandard , fmt . Sprintf ( "Output format for conftest results - valid options are: %s" , output . Outputs ()) )
262258 cmd .Flags ().Bool ("junit-hide-message" , false , "Do not include the violation message in the JUnit test name" )
263259
264260 cmd .Flags ().StringSliceP ("policy" , "p" , []string {"policy" }, "Path to the Rego policy files directory" )
265261 cmd .Flags ().StringSliceP ("update" , "u" , []string {}, "A list of URLs can be provided to the update flag, which will download before the tests run" )
266262 cmd .Flags ().StringSliceP ("namespace" , "n" , []string {"main" }, "Test policies in a specific namespace" )
267263 cmd .Flags ().StringSliceP ("data" , "d" , []string {}, "A list of paths from which data for the rego policies will be recursively loaded" )
268- cmd .Flags ().StringSliceP ("output" , "o" , []string {}, fmt .Sprintf ("Output format for conftest results - valid options are: %s. You can optionally specify a file for the output, e.g. -o json=out.json" , append (output .Outputs (), OutputAppstudio )))
269264
270265 cmd .Flags ().StringSlice ("proto-file-dirs" , []string {}, "A list of directories containing Protocol Buffer definitions" )
266+ cmd .Flags ().Bool ("tls" , true , "Use TLS to access the registry" )
271267
272268 return & cmd
273269}
274-
275- var TestCmd * cobra.Command
276-
277- func init () {
278- TestCmd = newTestCommand ()
279- }
0 commit comments