Skip to content

Commit b8c2245

Browse files
Support extra policy fields
1 parent c0fc740 commit b8c2245

File tree

2 files changed

+89
-33
lines changed

2 files changed

+89
-33
lines changed

cmd/kubeapply/subcmd/validate.go

Lines changed: 77 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,21 @@ import (
1515

1616
var validateCmd = &cobra.Command{
1717
Use: "validate [cluster configs]",
18-
Short: "validate checks the cluster configs using kubeval",
18+
Short: "validate checks the cluster configs using kubeconform and (optionally) opa policies",
1919
Args: cobra.MinimumNArgs(1),
2020
RunE: validateRun,
2121
}
2222

2323
type validateFlags struct {
2424
// Expand before validating.
2525
expand bool
26+
27+
// Number of worker goroutines to use for validation.
28+
numWorkers int
29+
30+
// Paths to OPA policy rego files that will be run against kube resources.
31+
// See https://www.openpolicyagent.org/ for more details.
32+
policies []string
2633
}
2734

2835
var validateFlagValues validateFlags
@@ -34,6 +41,18 @@ func init() {
3441
false,
3542
"Expand before validating",
3643
)
44+
validateCmd.Flags().IntVar(
45+
&validateFlagValues.numWorkers,
46+
"num-workers",
47+
4,
48+
"Number of workers to use for validation",
49+
)
50+
validateCmd.Flags().StringArrayVar(
51+
&validateFlagValues.policies,
52+
"policy",
53+
[]string{},
54+
"Paths to OPA policies",
55+
)
3756

3857
RootCmd.AddCommand(validateCmd)
3958
}
@@ -88,43 +107,75 @@ func validateClusterPath(ctx context.Context, path string) error {
88107
func execValidation(ctx context.Context, clusterConfig *config.ClusterConfig) error {
89108
log.Infof("Validating cluster %s", clusterConfig.DescriptiveName())
90109

91-
kubeValidator, err := validation.NewKubeValidator()
110+
kubeconformChecker, err := validation.NewKubeconformChecker()
92111
if err != nil {
93112
return err
94113
}
95114

115+
policies, err := validation.DefaultPoliciesFromGlobs(ctx, validateFlagValues.policies)
116+
if err != nil {
117+
return err
118+
}
119+
120+
checkers := []validation.Checker{kubeconformChecker}
121+
for _, policy := range policies {
122+
checkers = append(checkers, policy)
123+
}
124+
125+
validator := validation.NewKubeValidator(
126+
validation.KubeValidatorConfig{
127+
NumWorkers: validateFlagValues.numWorkers,
128+
Checkers: checkers,
129+
},
130+
)
131+
96132
log.Infof("Running kubeconform on configs in %+v", clusterConfig.AbsSubpaths())
97-
results, err := kubeValidator.RunValidation(ctx, clusterConfig.AbsSubpaths()[0])
133+
results, err := validator.RunChecks(ctx, clusterConfig.AbsSubpaths()[0])
98134
if err != nil {
99135
return err
100136
}
101137

102138
numInvalidResources := 0
103139

104140
for _, result := range results {
105-
switch result.SchemaStatus {
106-
case validation.StatusValid:
107-
log.Infof("Resource %s in file %s OK", result.PrettyName(), result.Filename)
108-
case validation.StatusSkipped:
109-
log.Debugf("Resource %s in file %s was skipped", result.PrettyName(), result.Filename)
110-
case validation.StatusError:
111-
numInvalidResources++
112-
log.Errorf(
113-
"File %s could not be validated: %+v",
114-
result.Filename,
115-
result.SchemaMessage,
116-
)
117-
case validation.StatusInvalid:
118-
numInvalidResources++
119-
log.Errorf(
120-
"Resource %s in file %s is invalid: %s",
121-
result.PrettyName(),
122-
result.Filename,
123-
result.SchemaMessage,
124-
)
125-
case validation.StatusEmpty:
126-
default:
127-
log.Infof("Unrecognized result type: %+v", result)
141+
for _, checkResult := range result.CheckResults {
142+
switch checkResult.Status {
143+
case validation.StatusValid:
144+
log.Debugf(
145+
"Resource %s in file %s OK according to check %s",
146+
result.Resource.PrettyName(),
147+
result.Resource.Path,
148+
checkResult.CheckName,
149+
)
150+
case validation.StatusSkipped:
151+
log.Debugf(
152+
"Resource %s in file %s was skipped by check %s",
153+
result.Resource.PrettyName(),
154+
result.Resource.Path,
155+
checkResult.CheckName,
156+
)
157+
case validation.StatusError:
158+
numInvalidResources++
159+
log.Errorf(
160+
"Resource %s in file %s could not be processed by check %s: %s",
161+
result.Resource.PrettyName(),
162+
result.Resource.Path,
163+
checkResult.CheckName,
164+
checkResult.Message,
165+
)
166+
case validation.StatusInvalid:
167+
numInvalidResources++
168+
log.Errorf(
169+
"Resource %s in file %s is invalid according to check %s: %s",
170+
result.Resource.PrettyName(),
171+
result.Resource.Path,
172+
checkResult.CheckName,
173+
checkResult.Message,
174+
)
175+
case validation.StatusEmpty:
176+
default:
177+
log.Infof("Unrecognized result type: %+v", result)
178+
}
128179
}
129180
}
130181

pkg/validation/policy.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@ var _ Checker = (*PolicyChecker)(nil)
2626

2727
// PolicyModule contains information about a policy.
2828
type PolicyModule struct {
29-
Name string
30-
Contents string
31-
Package string
32-
Result string
29+
Name string
30+
Contents string
31+
Package string
32+
Result string
33+
ExtraFields map[string]interface{}
3334
}
3435

3536
func NewPolicyChecker(ctx context.Context, module PolicyModule) (*PolicyChecker, error) {
@@ -53,8 +54,8 @@ func NewPolicyChecker(ctx context.Context, module PolicyModule) (*PolicyChecker,
5354
func DefaultPoliciesFromGlobs(
5455
ctx context.Context,
5556
globs []string,
56-
) ([]PolicyChecker, error) {
57-
checkers := []PolicyChecker{}
57+
) ([]*PolicyChecker, error) {
58+
checkers := []*PolicyChecker{}
5859

5960
for _, glob := range globs {
6061
matches, err := filepath.Glob(glob)
@@ -79,7 +80,7 @@ func DefaultPoliciesFromGlobs(
7980
if err != nil {
8081
return nil, err
8182
}
82-
checkers = append(checkers, *checker)
83+
checkers = append(checkers, checker)
8384
}
8485
}
8586

@@ -99,6 +100,10 @@ func (p *PolicyChecker) Check(ctx context.Context, resource Resource) CheckResul
99100
return result
100101
}
101102

103+
for key, value := range p.Module.ExtraFields {
104+
data[key] = value
105+
}
106+
102107
results, err := p.Query.Eval(ctx, rego.EvalInput(data))
103108
if err != nil {
104109
result.Status = StatusError

0 commit comments

Comments
 (0)