-
Notifications
You must be signed in to change notification settings - Fork 297
/
test.go
192 lines (156 loc) · 7.31 KB
/
test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
package commands
import (
"context"
"fmt"
"os"
"github.com/open-policy-agent/conftest/output"
"github.com/open-policy-agent/conftest/parser"
"github.com/open-policy-agent/conftest/runner"
"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/storage"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
const testDesc = `
This command tests your configuration files using the Open Policy Agent.
The test command expects one or more input files that will be evaluated
against Open Policy Agent policies. Directories are also supported as valid
inputs.
Policies are written in the Rego language. For more
information on how to write Rego policies, see the documentation:
https://www.openpolicyagent.org/docs/latest/policy-language/
The policy location defaults to the policy directory in the local folder.
The location can be overridden with the '--policy' flag, e.g.:
$ conftest test --policy <my-directory> <input-file(s)/input-folder>
Some policies are dependant on external data. This data is loaded in separately
from policies. The location of any data directory or file can be specified with
the '--data' flag. If a directory is specified, it will be recursively searched for
any data files. Right now any '.json' or '.yaml' file will be loaded in
and made available in the Rego policies. Data will be made available in Rego based on
the file path where the data was found. For example, if data is stored
under 'policy/exceptions/my_data.yaml', and we execute the following command:
$ conftest test --data policy <input-file>
The data is available under 'import data.exceptions'.
The test command supports the '--output' flag to specify the type, e.g.:
$ conftest test -o table -p examples/kubernetes/policy examples/kubernetes/deployment.yaml
Which will return the following output:
+---------+----------------------------------+--------------------------------+
| RESULT | FILE | MESSAGE |
+---------+----------------------------------+--------------------------------+
| success | examples/kubernetes/service.yaml | |
| warning | examples/kubernetes/service.yaml | Found service hello-kubernetes |
| | | but services are not allowed |
+---------+----------------------------------+--------------------------------+
By default, it will use the regular stdout output. For a full list of available output types, see the of the '--output' flag.
The test command supports the '--update' flag to fetch the latest version of the policy at the given url.
It expects one or more urls to fetch the latest policies from, e.g.:
$ conftest test --update opa.azurecr.io/test
See the pull command for more details on supported protocols for fetching policies.
When debugging policies it can be useful to use a more verbose policy evaluation output. By using the '--trace' flag
the output will include a detailed trace of how the policy was evaluated, e.g.
$ conftest test --trace <input-file>
`
// TestRun stores the compiler and store for a test run.
type TestRun struct {
Compiler *ast.Compiler
Store storage.Store
}
// NewTestCommand creates a new test command.
func NewTestCommand(ctx context.Context) *cobra.Command {
cmd := cobra.Command{
Use: "test <path> [path [...]]",
Short: "Test your configuration files using Open Policy Agent",
Long: testDesc,
PreRunE: func(cmd *cobra.Command, args []string) error {
flagNames := []string{
"all-namespaces",
"combine",
"data",
"fail-on-warn",
"ignore",
"namespace",
"no-color",
"no-fail",
"suppress-exceptions",
"output",
"parser",
"policy",
"proto-file-dirs",
"capabilities",
"trace",
"strict",
"show-builtin-errors",
"update",
"junit-hide-message",
"quiet",
"tls",
}
for _, name := range flagNames {
if err := viper.BindPFlag(name, cmd.Flags().Lookup(name)); err != nil {
return fmt.Errorf("bind flag: %w", err)
}
}
return nil
},
RunE: func(cmd *cobra.Command, fileList []string) error {
if len(fileList) < 1 {
cmd.Usage() //nolint
return fmt.Errorf("missing required arguments")
}
var runner runner.TestRunner
if err := viper.Unmarshal(&runner); err != nil {
return fmt.Errorf("unmarshal parameters: %w", err)
}
results, err := runner.Run(ctx, fileList)
if err != nil {
return fmt.Errorf("running test: %w", err)
}
var exitCode int
if runner.FailOnWarn {
exitCode = output.ExitCodeFailOnWarn(results)
} else {
exitCode = output.ExitCode(results)
}
if !runner.Quiet || exitCode != 0 {
outputter := output.Get(runner.Output, output.Options{
NoColor: runner.NoColor,
SuppressExceptions: runner.SuppressExceptions,
Tracing: runner.Trace,
JUnitHideMessage: viper.GetBool("junit-hide-message"),
})
if err := outputter.Output(results); err != nil {
return fmt.Errorf("output results: %w", err)
}
// When the no-fail parameter is set, there is no need to figure out the error code
// as we always want to return zero.
if runner.NoFail {
return nil
}
}
os.Exit(exitCode)
return nil
},
}
cmd.Flags().Bool("fail-on-warn", false, "Return a non-zero exit code if warnings or errors are found")
cmd.Flags().Bool("no-fail", false, "Return an exit code of zero even if a policy fails")
cmd.Flags().Bool("no-color", false, "Disable color when printing")
cmd.Flags().Bool("suppress-exceptions", false, "Do not include exceptions in output")
cmd.Flags().Bool("all-namespaces", false, "Test policies found in all namespaces")
cmd.Flags().Bool("quiet", false, "Disable successful test output")
cmd.Flags().Bool("trace", false, "Enable more verbose trace output for Rego queries")
cmd.Flags().Bool("strict", false, "Enable strict mode for Rego policies")
cmd.Flags().Bool("show-builtin-errors", false, "Collect and return all encountered built-in errors")
cmd.Flags().Bool("combine", false, "Combine all config files to be evaluated together")
cmd.Flags().String("ignore", "", "A regex pattern which can be used for ignoring paths")
cmd.Flags().String("parser", "", fmt.Sprintf("Parser to use to parse the configurations. Valid parsers: %s", parser.Parsers()))
cmd.Flags().String("capabilities", "", "Path to JSON file that can restrict opa functionality against a given policy. Default: all operations allowed")
cmd.Flags().StringP("output", "o", output.OutputStandard, fmt.Sprintf("Output format for conftest results - valid options are: %s", output.Outputs()))
cmd.Flags().Bool("junit-hide-message", false, "Do not include the violation message in the JUnit test name")
cmd.Flags().StringSliceP("policy", "p", []string{"policy"}, "Path to the Rego policy files directory")
cmd.Flags().StringSliceP("update", "u", []string{}, "A list of URLs can be provided to the update flag, which will download before the tests run")
cmd.Flags().StringSliceP("namespace", "n", []string{"main"}, "Test policies in a specific namespace")
cmd.Flags().StringSliceP("data", "d", []string{}, "A list of paths from which data for the rego policies will be recursively loaded")
cmd.Flags().StringSlice("proto-file-dirs", []string{}, "A list of directories containing Protocol Buffer definitions")
cmd.Flags().Bool("tls", true, "Use TLS to access the registry")
return &cmd
}