Skip to content

Commit 38f45e6

Browse files
authored
Merge pull request #2656 from simonbaird/conftest-upgrade
Upgrade conftest to v0.62.0 (after opa v1.0 upgrade)
2 parents 9663dc7 + 73b94d2 commit 38f45e6

File tree

15 files changed

+1914
-335
lines changed

15 files changed

+1914
-335
lines changed
File renamed without changes.

acceptance/go.mod

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,5 +262,3 @@ require (
262262
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
263263
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
264264
)
265-
266-
replace muzzammil.xyz/jsonc => github.com/muhammadmuzzammil1998/jsonc v1.0.0

cmd/initialize/init_policies.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func initPoliciesCmd() *cobra.Command {
6868
# solution: Easy
6969
# collections:
7070
# - A
71-
deny[result] {
71+
deny contains result if {
7272
false
7373
result := "Never denies"
7474
}

cmd/root.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,6 @@ func AddCommandsTo(cmd *cobra.Command) {
6565
cmd.AddCommand(opa.OPACmd)
6666
cmd.AddCommand(sigstore.SigstoreCmd)
6767
if utils.Experimental() {
68-
cmd.AddCommand(test.TestCmd)
68+
cmd.AddCommand(test.NewTestCommand(context.Background()))
6969
}
7070
}

cmd/test/test.go

Lines changed: 85 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@
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
// -------------------------------------------------------------------------------
2323
package test
2424

2525
import (
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.
4952
Policies are written in the Rego language. For more
5053
information on how to write Rego policies, see the documentation:
5154
https://www.openpolicyagent.org/docs/latest/policy-language/
52-
`
5355
54-
const testExample = `
5556
The policy location defaults to the policy directory in the local folder.
5657
The 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
7677
Which 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
8686
By 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.:
9393
See the pull command for more details on supported protocols for fetching policies.
9494
9595
When 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+
101116
const 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

Comments
 (0)