mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
fix deny check and fmt
Signed-off-by: Jim Bugwadia <jim@nirmata.com>
This commit is contained in:
parent
a905a61581
commit
3957a1400e
18 changed files with 128 additions and 112 deletions
|
@ -1,3 +1,4 @@
|
|||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
|
|
|
@ -37,4 +37,4 @@ func ToMap(data interface{}) (map[string]interface{}, error) {
|
|||
}
|
||||
|
||||
return mapData, nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,21 +53,21 @@ type EvalInterface interface {
|
|||
|
||||
//Context stores the data resources as JSON
|
||||
type Context struct {
|
||||
mutex sync.RWMutex
|
||||
jsonRaw []byte
|
||||
mutex sync.RWMutex
|
||||
jsonRaw []byte
|
||||
jsonRawCheckpoints [][]byte
|
||||
builtInVars []string
|
||||
images *Images
|
||||
log logr.Logger
|
||||
builtInVars []string
|
||||
images *Images
|
||||
log logr.Logger
|
||||
}
|
||||
|
||||
//NewContext returns a new context
|
||||
// builtInVars is the list of known variables (e.g. serviceAccountName)
|
||||
func NewContext(builtInVars ...string) *Context {
|
||||
ctx := Context{
|
||||
jsonRaw: []byte(`{}`), // empty json struct
|
||||
builtInVars: builtInVars,
|
||||
log: log.Log.WithName("context"),
|
||||
jsonRaw: []byte(`{}`), // empty json struct
|
||||
builtInVars: builtInVars,
|
||||
log: log.Log.WithName("context"),
|
||||
jsonRawCheckpoints: make([][]byte, 0),
|
||||
}
|
||||
|
||||
|
@ -334,7 +334,7 @@ func (ctx *Context) reset(remove bool) {
|
|||
ctx.mutex.Lock()
|
||||
defer ctx.mutex.Unlock()
|
||||
|
||||
if len(ctx.jsonRawCheckpoints) == 0 {
|
||||
if len(ctx.jsonRawCheckpoints) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -80,8 +80,8 @@ func filterRule(rule kyverno.Rule, policyContext *PolicyContext) *response.RuleR
|
|||
// if the oldResource matched, return "false" to delete GR for it
|
||||
if err = MatchesResourceDescription(oldResource, rule, admissionInfo, excludeGroupRole, namespaceLabels); err == nil {
|
||||
return &response.RuleResponse{
|
||||
Name: rule.Name,
|
||||
Type: "Generation",
|
||||
Name: rule.Name,
|
||||
Type: "Generation",
|
||||
Status: response.RuleStatusFail,
|
||||
RuleStats: response.RuleStats{
|
||||
ProcessingTime: time.Since(startTime),
|
||||
|
@ -123,8 +123,8 @@ func filterRule(rule kyverno.Rule, policyContext *PolicyContext) *response.RuleR
|
|||
|
||||
// build rule Response
|
||||
return &response.RuleResponse{
|
||||
Name: ruleCopy.Name,
|
||||
Type: "Generation",
|
||||
Name: ruleCopy.Name,
|
||||
Type: "Generation",
|
||||
Status: response.RuleStatusPass,
|
||||
RuleStats: response.RuleStats{
|
||||
ProcessingTime: time.Since(startTime),
|
||||
|
|
|
@ -325,7 +325,7 @@ func convertRNodeToInterface(document *yaml.RNode) (interface{}, error) {
|
|||
}
|
||||
|
||||
func checkCondition(logger logr.Logger, pattern *yaml.RNode, resource *yaml.RNode) error {
|
||||
patternInterface, err := convertRNodeToInterface(pattern);
|
||||
patternInterface, err := convertRNodeToInterface(pattern)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -336,7 +336,7 @@ func checkCondition(logger logr.Logger, pattern *yaml.RNode, resource *yaml.RNod
|
|||
}
|
||||
|
||||
err, _ = validate.MatchPattern(logger, resourceInterface, patternInterface)
|
||||
if err != nil{
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -118,7 +118,7 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) {
|
|||
Name: ruleCopy.Name,
|
||||
Type: utils.Validation.String(),
|
||||
Message: fmt.Sprintf("variable substitution failed: %s", err.Error()),
|
||||
Status: response.RuleStatusPass,
|
||||
Status: response.RuleStatusPass,
|
||||
}
|
||||
|
||||
incrementAppliedCount(resp)
|
||||
|
|
|
@ -43,15 +43,15 @@ type PolicyContext struct {
|
|||
|
||||
func (pc *PolicyContext) Copy() *PolicyContext {
|
||||
return &PolicyContext{
|
||||
Policy: pc.Policy,
|
||||
NewResource: pc.NewResource,
|
||||
OldResource: pc.OldResource,
|
||||
AdmissionInfo: pc.AdmissionInfo,
|
||||
Client: pc.Client,
|
||||
ExcludeGroupRole: pc.ExcludeGroupRole,
|
||||
Policy: pc.Policy,
|
||||
NewResource: pc.NewResource,
|
||||
OldResource: pc.OldResource,
|
||||
AdmissionInfo: pc.AdmissionInfo,
|
||||
Client: pc.Client,
|
||||
ExcludeGroupRole: pc.ExcludeGroupRole,
|
||||
ExcludeResourceFunc: pc.ExcludeResourceFunc,
|
||||
ResourceCache: pc.ResourceCache,
|
||||
JSONContext: pc.JSONContext,
|
||||
NamespaceLabels: pc.NamespaceLabels,
|
||||
ResourceCache: pc.ResourceCache,
|
||||
JSONContext: pc.JSONContext,
|
||||
NamespaceLabels: pc.NamespaceLabels,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ type RuleResponse struct {
|
|||
Status RuleStatus `json:"status"`
|
||||
|
||||
// statistics
|
||||
RuleStats `json:",inline"`
|
||||
RuleStats `json:",inline"`
|
||||
}
|
||||
|
||||
//ToString ...
|
||||
|
@ -161,7 +161,7 @@ func (er EngineResponse) GetResourceSpec() ResourceSpec {
|
|||
func (er EngineResponse) getRules(status RuleStatus) []string {
|
||||
var rules []string
|
||||
for _, r := range er.PolicyResponse.Rules {
|
||||
if r.Status == status {
|
||||
if r.Status == status {
|
||||
rules = append(rules, r.Name)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,4 +27,4 @@ func Test_parse_yaml(t *testing.T) {
|
|||
}
|
||||
assert.Equal(t, 1, len(pr.Rules))
|
||||
assert.Equal(t, RuleStatusFail, pr.Rules[0].Status)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,16 +9,21 @@ import (
|
|||
// RuleStatus represents the status of rule execution
|
||||
type RuleStatus int
|
||||
|
||||
// RuleStatusPass is used to report the result of processing a rule.
|
||||
const (
|
||||
// RuleStatusPass indicates that the policy rule requirements are met
|
||||
// RuleStatusPass indicates that the resources meets the policy rule requirements
|
||||
RuleStatusPass RuleStatus = iota
|
||||
// Fail indicates that the policy rule requirements are not met
|
||||
// Fail indicates that the resource does not meet the policy rule requirements
|
||||
RuleStatusFail
|
||||
// Warn indicates that the policy rule requirements are not met, and the policy is not scored
|
||||
// Warn indicates that the the resource does not meet the policy rule requirements, but the policy is not scored
|
||||
RuleStatusWarn
|
||||
// Error indicates that the policy rule could not be evaluated due to a processing error
|
||||
// Error indicates that the policy rule could not be evaluated due to a processing error, for
|
||||
// example when a variable cannot be resolved in the policy rule definition. Note that variables
|
||||
// that cannot be resolved in preconditions are replaced with empty values to allow existence
|
||||
// checks.
|
||||
RuleStatusError
|
||||
// Skip indicates that the policy rule was not selected based on user inputs or applicability
|
||||
// Skip indicates that the policy rule was not selected based on user inputs or applicability, for example
|
||||
// when preconditions are not met, or when conditional or global anchors are not satistied.
|
||||
RuleStatusSkip
|
||||
)
|
||||
|
||||
|
@ -28,18 +33,18 @@ func (s *RuleStatus) String() string {
|
|||
|
||||
var toString = map[RuleStatus]string{
|
||||
RuleStatusPass: "Pass",
|
||||
RuleStatusFail: "Fail",
|
||||
RuleStatusWarn: "Warning",
|
||||
RuleStatusFail: "Fail",
|
||||
RuleStatusWarn: "Warning",
|
||||
RuleStatusError: "Error",
|
||||
RuleStatusSkip: "Skip",
|
||||
RuleStatusSkip: "Skip",
|
||||
}
|
||||
|
||||
var toID = map[string]RuleStatus{
|
||||
"Pass": RuleStatusPass,
|
||||
"Fail": RuleStatusFail,
|
||||
"Pass": RuleStatusPass,
|
||||
"Fail": RuleStatusFail,
|
||||
"Warning": RuleStatusWarn,
|
||||
"Error": RuleStatusError,
|
||||
"Skip": RuleStatusSkip,
|
||||
"Error": RuleStatusError,
|
||||
"Skip": RuleStatusSkip,
|
||||
}
|
||||
|
||||
// MarshalJSON marshals the enum as a quoted json string
|
||||
|
@ -66,7 +71,7 @@ func (s *RuleStatus) UnmarshalJSON(b []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func getRuleStatus(s string) (*RuleStatus, error){
|
||||
func getRuleStatus(s string) (*RuleStatus, error) {
|
||||
for k, v := range toID {
|
||||
if s == k {
|
||||
return &v, nil
|
||||
|
|
|
@ -40,4 +40,3 @@ func getRawKeyIfWrappedWithAttributes(str string) string {
|
|||
return str
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ func validateResourceElement(log logr.Logger, resourceElement, patternElement, o
|
|||
log.V(4).Info("Pattern and resource have different structures.", "path", path, "expected", fmt.Sprintf("%T", patternElement), "current", fmt.Sprintf("%T", resourceElement))
|
||||
return path, fmt.Errorf("Pattern and resource have different structures. Path: %s. Expected %T, found %T", path, patternElement, resourceElement)
|
||||
}
|
||||
// CheckAnchorInResource - check anchor anchor key exists in resource and update the AnchorKey fields.
|
||||
// CheckAnchorInResource - check anchor key exists in resource and update the AnchorKey fields.
|
||||
ac.CheckAnchorInResource(typedPatternElement, typedResourceElement)
|
||||
return validateMap(log, typedResourceElement, typedPatternElement, originPattern, path, ac)
|
||||
// array
|
||||
|
|
|
@ -1516,7 +1516,7 @@ func Test_global_anchor(t *testing.T) {
|
|||
pattern []byte
|
||||
resource []byte
|
||||
nilErr bool
|
||||
} {
|
||||
}{
|
||||
{
|
||||
name: "check global anchor_skip",
|
||||
pattern: []byte(`{"spec": {"containers": [{"name": "*","<(image)": "*:latest","imagePullPolicy": "!Always"}]}}`),
|
||||
|
@ -1535,7 +1535,12 @@ func Test_global_anchor(t *testing.T) {
|
|||
testMatchPattern(t, testCases[1])
|
||||
}
|
||||
|
||||
func testMatchPattern(t *testing.T, testCase struct {name string;pattern []byte;resource []byte;nilErr bool}) {
|
||||
func testMatchPattern(t *testing.T, testCase struct {
|
||||
name string
|
||||
pattern []byte
|
||||
resource []byte
|
||||
nilErr bool
|
||||
}) {
|
||||
var pattern, resource interface{}
|
||||
err := json.Unmarshal(testCase.pattern, &pattern)
|
||||
assert.NilError(t, err)
|
||||
|
@ -1550,4 +1555,4 @@ func testMatchPattern(t *testing.T, testCase struct {name string;pattern []byte;
|
|||
err != nil,
|
||||
fmt.Sprintf("\ntest: %s\npattern: %s\nresource: %s\nmsg: %v", testCase.name, pattern, resource, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,12 +116,11 @@ func validateResource(log logr.Logger, ctx *PolicyContext) *response.EngineRespo
|
|||
}
|
||||
|
||||
func processValidationRule(log logr.Logger, ctx *PolicyContext, rule *kyverno.Rule) *response.RuleResponse {
|
||||
v := newValidator(log, ctx, rule)
|
||||
if rule.Validation.ForEachValidation != nil {
|
||||
v := newValidator(log, ctx, rule)
|
||||
return v.validateForEach()
|
||||
}
|
||||
|
||||
v := newValidator(log, ctx, rule)
|
||||
return v.validate()
|
||||
}
|
||||
|
||||
|
@ -140,41 +139,48 @@ func addRuleResponse(log logr.Logger, resp *response.EngineResponse, ruleResp *r
|
|||
}
|
||||
|
||||
type validator struct {
|
||||
log logr.Logger
|
||||
ctx *PolicyContext
|
||||
rule *kyverno.Rule
|
||||
contextEntries []kyverno.ContextEntry
|
||||
log logr.Logger
|
||||
ctx *PolicyContext
|
||||
rule *kyverno.Rule
|
||||
contextEntries []kyverno.ContextEntry
|
||||
anyAllConditions apiextensions.JSON
|
||||
pattern apiextensions.JSON
|
||||
anyPattern apiextensions.JSON
|
||||
deny *kyverno.Deny
|
||||
pattern apiextensions.JSON
|
||||
anyPattern apiextensions.JSON
|
||||
deny *kyverno.Deny
|
||||
}
|
||||
|
||||
func newValidator(log logr.Logger, ctx *PolicyContext, rule *kyverno.Rule) *validator {
|
||||
ruleCopy := rule.DeepCopy()
|
||||
return &validator{
|
||||
log: log,
|
||||
rule: ruleCopy,
|
||||
ctx: ctx,
|
||||
contextEntries: ruleCopy.Context,
|
||||
log: log,
|
||||
rule: ruleCopy,
|
||||
ctx: ctx,
|
||||
contextEntries: ruleCopy.Context,
|
||||
anyAllConditions: ruleCopy.AnyAllConditions,
|
||||
pattern: ruleCopy.Validation.Pattern,
|
||||
anyPattern: ruleCopy.Validation.AnyPattern,
|
||||
deny: ruleCopy.Validation.Deny,
|
||||
pattern: ruleCopy.Validation.Pattern,
|
||||
anyPattern: ruleCopy.Validation.AnyPattern,
|
||||
deny: ruleCopy.Validation.Deny,
|
||||
}
|
||||
}
|
||||
|
||||
func newForeachValidator(log logr.Logger, ctx *PolicyContext, rule *kyverno.Rule) *validator {
|
||||
ruleCopy := rule.DeepCopy()
|
||||
|
||||
// Variable substitution expects JSON data, so we convert to a map
|
||||
anyAllConditions, err := common.ToMap(ruleCopy.Validation.ForEachValidation.AnyAllConditions)
|
||||
if err != nil {
|
||||
log.Error(err, "failed to convert ruleCopy.Validation.ForEachValidation.AnyAllConditions")
|
||||
}
|
||||
|
||||
return &validator{
|
||||
log: log,
|
||||
ctx: ctx,
|
||||
rule: ruleCopy,
|
||||
contextEntries: ruleCopy.Validation.ForEachValidation.Context,
|
||||
anyAllConditions: ruleCopy.Validation.ForEachValidation.AnyAllConditions,
|
||||
pattern: ruleCopy.Validation.ForEachValidation.Pattern,
|
||||
anyPattern: ruleCopy.Validation.ForEachValidation.AnyPattern,
|
||||
deny: ruleCopy.Validation.ForEachValidation.Deny,
|
||||
log: log,
|
||||
ctx: ctx,
|
||||
rule: ruleCopy,
|
||||
contextEntries: ruleCopy.Validation.ForEachValidation.Context,
|
||||
anyAllConditions: anyAllConditions,
|
||||
pattern: ruleCopy.Validation.ForEachValidation.Pattern,
|
||||
anyPattern: ruleCopy.Validation.ForEachValidation.AnyPattern,
|
||||
deny: ruleCopy.Validation.ForEachValidation.Deny,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -233,6 +239,7 @@ func (v *validator) validateForEach() *response.RuleResponse {
|
|||
v.ctx.JSONContext.Checkpoint()
|
||||
defer v.ctx.JSONContext.Restore()
|
||||
|
||||
applyCount := 0
|
||||
for _, e := range elements {
|
||||
v.ctx.JSONContext.Reset()
|
||||
|
||||
|
@ -246,14 +253,20 @@ func (v *validator) validateForEach() *response.RuleResponse {
|
|||
r := foreachValidator.validate()
|
||||
if r == nil {
|
||||
v.log.Info("skipping rule due to empty result")
|
||||
|
||||
continue
|
||||
} else if r.Status == response.RuleStatusSkip {
|
||||
v.log.Info("skipping rule as preconditions were not met")
|
||||
|
||||
continue
|
||||
} else if r.Status != response.RuleStatusPass {
|
||||
msg := fmt.Sprintf("validation failed in foreach rule for %v", e)
|
||||
msg := fmt.Sprintf("validation failed in foreach rule for %v", r.Message)
|
||||
return ruleResponse(v.rule, msg, r.Status)
|
||||
}
|
||||
|
||||
applyCount++
|
||||
}
|
||||
|
||||
if applyCount == 0 {
|
||||
return ruleResponse(v.rule, "", response.RuleStatusSkip)
|
||||
}
|
||||
|
||||
return ruleResponse(v.rule, "", response.RuleStatusPass)
|
||||
|
@ -290,7 +303,6 @@ func (v *validator) evaluateList(jmesPath string) ([]interface{}, error) {
|
|||
return l, nil
|
||||
}
|
||||
|
||||
|
||||
func (v *validator) loadContext() error {
|
||||
if err := LoadContext(v.log, v.contextEntries, v.ctx.ResourceCache, v.ctx, v.rule.Name); err != nil {
|
||||
if _, ok := err.(gojmespath.NotFoundError); ok {
|
||||
|
@ -481,7 +493,7 @@ func (v *validator) validatePatterns(resource unstructured.Unstructured) *respon
|
|||
return ruleResponse(v.rule, v.rule.Validation.Message, response.RuleStatusPass)
|
||||
}
|
||||
|
||||
func deserializeAnyPattern(anyPattern apiextensions.JSON ) ([]interface{}, error) {
|
||||
func deserializeAnyPattern(anyPattern apiextensions.JSON) ([]interface{}, error) {
|
||||
if anyPattern == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -538,7 +550,7 @@ func buildAnyPatternErrorMessage(rule *kyverno.Rule, errors []string) string {
|
|||
return fmt.Sprintf("validation error: %s. %s", rule.Validation.Message, errStr)
|
||||
}
|
||||
|
||||
func (v *validator) substitutePatterns() error {
|
||||
func (v *validator) substitutePatterns() error {
|
||||
if v.pattern != nil {
|
||||
i, err := variables.SubstituteAll(v.log, v.ctx.JSONContext, v.pattern)
|
||||
if err != nil {
|
||||
|
@ -562,7 +574,7 @@ func (v *validator) substitutePatterns() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (v *validator) substituteDeny() error {
|
||||
func (v *validator) substituteDeny() error {
|
||||
if v.deny == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -586,6 +598,6 @@ func ruleResponse(rule *kyverno.Rule, msg string, status response.RuleStatus) *r
|
|||
Name: rule.Name,
|
||||
Type: utils.Validation.String(),
|
||||
Message: msg,
|
||||
Status: status,
|
||||
Status: status,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1921,9 +1921,9 @@ func Test_VariableSubstitutionValidate_VariablesInMessageAreResolved(t *testing.
|
|||
|
||||
func Test_Flux_Kustomization_PathNotPresent(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
policyRaw []byte
|
||||
resourceRaw []byte
|
||||
name string
|
||||
policyRaw []byte
|
||||
resourceRaw []byte
|
||||
expectedResults []response.RuleStatus
|
||||
expectedMessages []string
|
||||
}{
|
||||
|
@ -1931,7 +1931,7 @@ func Test_Flux_Kustomization_PathNotPresent(t *testing.T) {
|
|||
name: "path-not-present",
|
||||
policyRaw: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"flux-multi-tenancy"},"spec":{"validationFailureAction":"enforce","rules":[{"name":"serviceAccountName","exclude":{"resources":{"namespaces":["flux-system"]}},"match":{"resources":{"kinds":["Kustomization","HelmRelease"]}},"validate":{"message":".spec.serviceAccountName is required","pattern":{"spec":{"serviceAccountName":"?*"}}}},{"name":"sourceRefNamespace","exclude":{"resources":{"namespaces":["flux-system"]}},"match":{"resources":{"kinds":["Kustomization","HelmRelease"]}},"validate":{"message":"spec.sourceRef.namespace must be the same as metadata.namespace","deny":{"conditions":[{"key":"{{request.object.spec.sourceRef.namespace}}","operator":"NotEquals","value":"{{request.object.metadata.namespace}}"}]}}}]}}`),
|
||||
// referred variable path not present
|
||||
resourceRaw: []byte(`{"apiVersion":"kustomize.toolkit.fluxcd.io/v1beta1","kind":"Kustomization","metadata":{"name":"dev-team","namespace":"apps"},"spec":{"serviceAccountName":"dev-team","interval":"5m","sourceRef":{"kind":"GitRepository","name":"dev-team"},"prune":true,"validation":"client"}}`),
|
||||
resourceRaw: []byte(`{"apiVersion":"kustomize.toolkit.fluxcd.io/v1beta1","kind":"Kustomization","metadata":{"name":"dev-team","namespace":"apps"},"spec":{"serviceAccountName":"dev-team","interval":"5m","sourceRef":{"kind":"GitRepository","name":"dev-team"},"prune":true,"validation":"client"}}`),
|
||||
expectedResults: []response.RuleStatus{response.RuleStatusPass, response.RuleStatusError},
|
||||
expectedMessages: []string{"validation rule 'serviceAccountName' passed.", "failed to substitute variables in deny conditions: Unknown key \"namespace\" in path"},
|
||||
},
|
||||
|
@ -1939,7 +1939,7 @@ func Test_Flux_Kustomization_PathNotPresent(t *testing.T) {
|
|||
name: "resource-with-violation",
|
||||
policyRaw: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"flux-multi-tenancy"},"spec":{"validationFailureAction":"enforce","rules":[{"name":"serviceAccountName","exclude":{"resources":{"namespaces":["flux-system"]}},"match":{"resources":{"kinds":["Kustomization","HelmRelease"]}},"validate":{"message":".spec.serviceAccountName is required","pattern":{"spec":{"serviceAccountName":"?*"}}}},{"name":"sourceRefNamespace","exclude":{"resources":{"namespaces":["flux-system"]}},"match":{"resources":{"kinds":["Kustomization","HelmRelease"]}},"validate":{"message":"spec.sourceRef.namespace {{request.object.spec.sourceRef.namespace}} must be the same as metadata.namespace {{request.object.metadata.namespace}}","deny":{"conditions":[{"key":"{{request.object.spec.sourceRef.namespace}}","operator":"NotEquals","value":"{{request.object.metadata.namespace}}"}]}}}]}}`),
|
||||
// referred variable path present with different value
|
||||
resourceRaw: []byte(`{"apiVersion":"kustomize.toolkit.fluxcd.io/v1beta1","kind":"Kustomization","metadata":{"name":"dev-team","namespace":"apps"},"spec":{"serviceAccountName":"dev-team","interval":"5m","sourceRef":{"kind":"GitRepository","name":"dev-team","namespace":"default"},"prune":true,"validation":"client"}}`),
|
||||
resourceRaw: []byte(`{"apiVersion":"kustomize.toolkit.fluxcd.io/v1beta1","kind":"Kustomization","metadata":{"name":"dev-team","namespace":"apps"},"spec":{"serviceAccountName":"dev-team","interval":"5m","sourceRef":{"kind":"GitRepository","name":"dev-team","namespace":"default"},"prune":true,"validation":"client"}}`),
|
||||
expectedResults: []response.RuleStatus{response.RuleStatusPass, response.RuleStatusFail},
|
||||
expectedMessages: []string{"validation rule 'serviceAccountName' passed.", "spec.sourceRef.namespace default must be the same as metadata.namespace apps"},
|
||||
},
|
||||
|
@ -1947,7 +1947,7 @@ func Test_Flux_Kustomization_PathNotPresent(t *testing.T) {
|
|||
name: "resource-comply",
|
||||
policyRaw: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"flux-multi-tenancy"},"spec":{"validationFailureAction":"enforce","rules":[{"name":"serviceAccountName","exclude":{"resources":{"namespaces":["flux-system"]}},"match":{"resources":{"kinds":["Kustomization","HelmRelease"]}},"validate":{"message":".spec.serviceAccountName is required","pattern":{"spec":{"serviceAccountName":"?*"}}}},{"name":"sourceRefNamespace","exclude":{"resources":{"namespaces":["flux-system"]}},"match":{"resources":{"kinds":["Kustomization","HelmRelease"]}},"validate":{"message":"spec.sourceRef.namespace must be the same as metadata.namespace","deny":{"conditions":[{"key":"{{request.object.spec.sourceRef.namespace}}","operator":"NotEquals","value":"{{request.object.metadata.namespace}}"}]}}}]}}`),
|
||||
// referred variable path present with same value - validate passes
|
||||
resourceRaw: []byte(`{"apiVersion":"kustomize.toolkit.fluxcd.io/v1beta1","kind":"Kustomization","metadata":{"name":"dev-team","namespace":"apps"},"spec":{"serviceAccountName":"dev-team","interval":"5m","sourceRef":{"kind":"GitRepository","name":"dev-team","namespace":"apps"},"prune":true,"validation":"client"}}`),
|
||||
resourceRaw: []byte(`{"apiVersion":"kustomize.toolkit.fluxcd.io/v1beta1","kind":"Kustomization","metadata":{"name":"dev-team","namespace":"apps"},"spec":{"serviceAccountName":"dev-team","interval":"5m","sourceRef":{"kind":"GitRepository","name":"dev-team","namespace":"apps"},"prune":true,"validation":"client"}}`),
|
||||
expectedResults: []response.RuleStatus{response.RuleStatusPass, response.RuleStatusPass},
|
||||
expectedMessages: []string{"validation rule 'serviceAccountName' passed.", "validation rule 'sourceRefNamespace' passed."},
|
||||
},
|
||||
|
@ -2603,10 +2603,8 @@ func Test_foreach_context_preconditions(t *testing.T) {
|
|||
"metadata": {"name": "test"},
|
||||
"spec": { "template": { "spec": {
|
||||
"containers": [
|
||||
{"name": "pod1-valid", "image": "nginx/nginx:v1"},
|
||||
{"name": "pod2-valid", "image": "nginx/nginx:v2"},
|
||||
{"name": "pod3-valid", "image": "nginx/nginx:v3"},
|
||||
{"name": "pod4-valid", "image": "nginx/nginx:v4"}
|
||||
{"name": "podvalid", "image": "nginx/nginx:v1"},
|
||||
{"name": "podinvalid", "image": "nginx/nginx:v2"}
|
||||
]
|
||||
}}}}`)
|
||||
|
||||
|
@ -2622,17 +2620,17 @@ func Test_foreach_context_preconditions(t *testing.T) {
|
|||
"validate": {
|
||||
"foreach": {
|
||||
"list": "request.object.spec.template.spec.containers",
|
||||
"context": [{"name": "tags", "configMap": {"name": "mycmap", "namespace": "default"}}],
|
||||
"context": [{"name": "img", "configMap": {"name": "mycmap", "namespace": "default"}}],
|
||||
"preconditions": { "all": [
|
||||
{
|
||||
"key": "{{request.object.name}}",
|
||||
"operator": "Equals",
|
||||
"value": "pod1-valid | pod2-valid | pod3-valid"
|
||||
"operator": "In",
|
||||
"value": ["podvalid"]
|
||||
}
|
||||
]},
|
||||
"deny": {
|
||||
"conditions": [
|
||||
{"key": "images.{{ request.object.name }}.tag", "operator": "NotEquals", "value": "{{ tags.data.{{ request.object.name }} }}"}
|
||||
{"key": "{{ request.object.image }}", "operator": "NotEquals", "value": "{{ img.data.{{ request.object.name }} }}"}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -2646,9 +2644,8 @@ func Test_foreach_context_preconditions(t *testing.T) {
|
|||
{
|
||||
Name: "test",
|
||||
Values: map[string]string{
|
||||
"tags.data.pod1-valid": "v1",
|
||||
"tags.data.pod2-valid": "v2",
|
||||
"tags.data.pod3-valid": "v3",
|
||||
"img.data.podvalid": "nginx/nginx:v1",
|
||||
"img.data.podinvalid": "nginx/nginx:v2",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -2670,10 +2667,8 @@ func Test_foreach_context_preconditions_fail(t *testing.T) {
|
|||
"metadata": {"name": "test"},
|
||||
"spec": { "template": { "spec": {
|
||||
"containers": [
|
||||
{"name": "pod1-valid", "image": "nginx/nginx:v1"},
|
||||
{"name": "pod2-valid", "image": "nginx/nginx:v2"},
|
||||
{"name": "pod3-valid", "image": "nginx/nginx:v3"},
|
||||
{"name": "pod4-valid", "image": "nginx/nginx:v4"}
|
||||
{"name": "podvalid", "image": "nginx/nginx:v1"},
|
||||
{"name": "podinvalid", "image": "nginx/nginx:v2"}
|
||||
]
|
||||
}}}}`)
|
||||
|
||||
|
@ -2689,17 +2684,17 @@ func Test_foreach_context_preconditions_fail(t *testing.T) {
|
|||
"validate": {
|
||||
"foreach": {
|
||||
"list": "request.object.spec.template.spec.containers",
|
||||
"context": [{"name": "tags", "configMap": {"name": "mycmap", "namespace": "default"}}],
|
||||
"context": [{"name": "img", "configMap": {"name": "mycmap", "namespace": "default"}}],
|
||||
"preconditions": { "all": [
|
||||
{
|
||||
"key": "{{request.object.name}}",
|
||||
"operator": "Equals",
|
||||
"value": "pod1-valid | pod2-valid | pod3-valid"
|
||||
"operator": "In",
|
||||
"value": ["podvalid", "podinvalid"]
|
||||
}
|
||||
]},
|
||||
"deny": {
|
||||
"conditions": [
|
||||
{"key": "images.{{ request.object.name }}.tag", "operator": "NotEquals", "value": "{{ tags.data.{{ request.object.name }} }}"}
|
||||
{"key": "{{ request.object.image }}", "operator": "NotEquals", "value": "{{ img.data.{{ request.object.name }} }}"}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -2713,9 +2708,8 @@ func Test_foreach_context_preconditions_fail(t *testing.T) {
|
|||
{
|
||||
Name: "test",
|
||||
Values: map[string]string{
|
||||
"tags.data.pod1-valid": "v1",
|
||||
"tags.data.pod2-valid": "v22",
|
||||
"tags.data.pod3-valid": "v3",
|
||||
"img.data.podvalid": "nginx/nginx:v1",
|
||||
"img.data.podinvalid": "nginx/nginx:v1",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -52,6 +52,8 @@ func newPreconditionsVariableResolver(log logr.Logger) VariableResolver {
|
|||
}
|
||||
}
|
||||
|
||||
// SubstituteAll substitutes variables and references in the document. The document must be JSON data
|
||||
// i.e. string, []interface{}, map[string]interface{}
|
||||
func SubstituteAll(log logr.Logger, ctx context.EvalInterface, document interface{}) (_ interface{}, err error) {
|
||||
return substituteAll(log, ctx, document, DefaultVariableResolver)
|
||||
}
|
||||
|
@ -139,8 +141,6 @@ func SubstituteAllForceMutate(log logr.Logger, ctx context.EvalInterface, typedR
|
|||
return UntypedToRule(rule)
|
||||
}
|
||||
|
||||
//SubstituteVars replaces the variables with the values defined in the context
|
||||
// - if any variable is invalid or has nil value, it is considered as a failed variable substitution
|
||||
func substituteVars(log logr.Logger, ctx context.EvalInterface, rule interface{}, vr VariableResolver) (interface{}, error) {
|
||||
return jsonUtils.NewTraversal(rule, substituteVariablesIfAny(log, ctx, vr)).TraverseJSON()
|
||||
}
|
||||
|
|
|
@ -51,7 +51,6 @@ func Test_parse_file(t *testing.T) {
|
|||
assert.Equal(t, response.RuleStatusFail, s.TestCases[0].Expected.Validation.PolicyResponse.Rules[0].Status, "invalid status")
|
||||
}
|
||||
|
||||
|
||||
func Test_parse_file2(t *testing.T) {
|
||||
path := getRelativePath("test/scenarios/samples/best_practices/disallow_bind_mounts_fail.yaml")
|
||||
data, err := ioutil.ReadFile(path)
|
||||
|
|
Loading…
Add table
Reference in a new issue