mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-14 11:57:48 +00:00
add applyRules to control whether one or all rules are applied (#4196)
* add ruleSelector Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix selector logic for skipped rules Signed-off-by: Jim Bugwadia <jim@nirmata.com> * change names Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix generated paths Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix linter issues Signed-off-by: Jim Bugwadia <jim@nirmata.com> * add image variable to context when rule processing starts Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix messages Signed-off-by: Jim Bugwadia <jim@nirmata.com> * update generate rules Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix tests Signed-off-by: Jim Bugwadia <jim@nirmata.com> Co-authored-by: shuting <shuting@nirmata.com>
This commit is contained in:
parent
03cec01fb5
commit
4aa0767728
21 changed files with 545 additions and 125 deletions
|
@ -18,6 +18,17 @@ const (
|
|||
Fail FailurePolicyType = "Fail"
|
||||
)
|
||||
|
||||
// ApplyRulesType controls whether processing stops after one rule is applied or all rules are applied.
|
||||
// +kubebuilder:validation:Enum=All;One
|
||||
type ApplyRulesType string
|
||||
|
||||
const (
|
||||
// AllMatchingRules applies all rules in a policy that match.
|
||||
ApplyAll ApplyRulesType = "All"
|
||||
// FirstMatchingRule applies only the first matching rule in the policy.
|
||||
ApplyOne ApplyRulesType = "One"
|
||||
)
|
||||
|
||||
// AnyAllConditions consists of conditions wrapped denoting a logical criteria to be fulfilled.
|
||||
// AnyConditions get fulfilled when at least one of its sub-conditions passes.
|
||||
// AllConditions get fulfilled only when all of its sub-conditions pass.
|
||||
|
|
|
@ -30,6 +30,13 @@ type Spec struct {
|
|||
// each rule can validate, mutate, or generate resources.
|
||||
Rules []Rule `json:"rules,omitempty" yaml:"rules,omitempty"`
|
||||
|
||||
// ApplyRules controls how rules in a policy are applied. Rule are processed in
|
||||
// the order of declaration. When set to `One` processing stops after a rule has
|
||||
// been applied i.e. the rule matches and results in a pass, fail, or error. When
|
||||
// set to `All` all rules in the policy are processed. The default is `All`.
|
||||
// +optional
|
||||
ApplyRules *ApplyRulesType `json:"applyRules,omitempty" yaml:"applyRules,omitempty"`
|
||||
|
||||
// FailurePolicy defines how unrecognized errors from the admission endpoint are handled.
|
||||
// Rules within the same policy share the same failure behavior.
|
||||
// Allowed values are Ignore or Fail. Defaults to Fail.
|
||||
|
@ -69,7 +76,7 @@ type Spec struct {
|
|||
// +optional
|
||||
MutateExistingOnPolicyUpdate bool `json:"mutateExistingOnPolicyUpdate,omitempty" yaml:"mutateExistingOnPolicyUpdate,omitempty"`
|
||||
|
||||
// GenerateExistingOnPolicyUpdate controls wether to trigger generate rule in existing resources
|
||||
// GenerateExistingOnPolicyUpdate controls whether to trigger generate rule in existing resources
|
||||
// If is set to "true" generate rule will be triggered and applied to existing matched resources.
|
||||
// Defaults to "false" if not specified.
|
||||
// +optional
|
||||
|
@ -191,6 +198,14 @@ func (s *Spec) GetValidationFailureAction() ValidationFailureAction {
|
|||
return s.ValidationFailureAction
|
||||
}
|
||||
|
||||
// GetFailurePolicy returns the failure policy to be applied
|
||||
func (s *Spec) GetApplyRules() ApplyRulesType {
|
||||
if s.ApplyRules == nil {
|
||||
return ApplyAll
|
||||
}
|
||||
return *s.ApplyRules
|
||||
}
|
||||
|
||||
// ValidateRuleNames checks if the rule names are unique across a policy
|
||||
func (s *Spec) ValidateRuleNames(path *field.Path) (errs field.ErrorList) {
|
||||
names := sets.NewString()
|
||||
|
|
|
@ -1085,6 +1085,11 @@ func (in *Spec) DeepCopyInto(out *Spec) {
|
|||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.ApplyRules != nil {
|
||||
in, out := &in.ApplyRules, &out.ApplyRules
|
||||
*out = new(ApplyRulesType)
|
||||
**out = **in
|
||||
}
|
||||
if in.FailurePolicy != nil {
|
||||
in, out := &in.FailurePolicy, &out.FailurePolicy
|
||||
*out = new(FailurePolicyType)
|
||||
|
|
|
@ -55,6 +55,12 @@ spec:
|
|||
spec:
|
||||
description: Spec declares policy behaviors.
|
||||
properties:
|
||||
applyRules:
|
||||
description: ApplyRules controls how rules in a policy are applied. Rule are processed in the order of declaration. When set to `One` processing stops after a rule has been applied i.e. the rule matches and results in a pass, fail, or error. When set to `All` all rules in the policy are processed. The default is `All`.
|
||||
enum:
|
||||
- All
|
||||
- One
|
||||
type: string
|
||||
background:
|
||||
description: Background controls if rules are applied to existing resources during a background scan. Optional. Default value is "true". The value must be set to "false" if the policy rule uses variables that are only available in the admission review request (e.g. user name).
|
||||
type: boolean
|
||||
|
@ -65,7 +71,7 @@ spec:
|
|||
- Fail
|
||||
type: string
|
||||
generateExistingOnPolicyUpdate:
|
||||
description: GenerateExistingOnPolicyUpdate controls wether to trigger generate rule in existing resources If is set to "true" generate rule will be triggered and applied to existing matched resources. Defaults to "false" if not specified.
|
||||
description: GenerateExistingOnPolicyUpdate controls whether to trigger generate rule in existing resources If is set to "true" generate rule will be triggered and applied to existing matched resources. Defaults to "false" if not specified.
|
||||
type: boolean
|
||||
mutateExistingOnPolicyUpdate:
|
||||
description: MutateExistingOnPolicyUpdate controls if a mutateExisting policy is applied on policy events. Default value is "false".
|
||||
|
@ -3874,6 +3880,12 @@ spec:
|
|||
spec:
|
||||
description: Spec defines policy behaviors and contains one or more rules.
|
||||
properties:
|
||||
applyRules:
|
||||
description: ApplyRules controls how rules in a policy are applied. Rule are processed in the order of declaration. When set to `One` processing stops after a rule has been applied i.e. the rule matches and results in a pass, fail, or error. When set to `All` all rules in the policy are processed. The default is `All`.
|
||||
enum:
|
||||
- All
|
||||
- One
|
||||
type: string
|
||||
background:
|
||||
description: Background controls if rules are applied to existing resources during a background scan. Optional. Default value is "true". The value must be set to "false" if the policy rule uses variables that are only available in the admission review request (e.g. user name).
|
||||
type: boolean
|
||||
|
@ -3884,7 +3896,7 @@ spec:
|
|||
- Fail
|
||||
type: string
|
||||
generateExistingOnPolicyUpdate:
|
||||
description: GenerateExistingOnPolicyUpdate controls wether to trigger generate rule in existing resources If is set to "true" generate rule will be triggered and applied to existing matched resources. Defaults to "false" if not specified.
|
||||
description: GenerateExistingOnPolicyUpdate controls whether to trigger generate rule in existing resources If is set to "true" generate rule will be triggered and applied to existing matched resources. Defaults to "false" if not specified.
|
||||
type: boolean
|
||||
mutateExistingOnPolicyUpdate:
|
||||
description: MutateExistingOnPolicyUpdate controls if a mutateExisting policy is applied on policy events. Default value is "false".
|
||||
|
|
|
@ -52,6 +52,16 @@ spec:
|
|||
spec:
|
||||
description: Spec declares policy behaviors.
|
||||
properties:
|
||||
applyRules:
|
||||
description: ApplyRules controls how rules in a policy are applied.
|
||||
Rule are processed in the order of declaration. When set to `One`
|
||||
processing stops after a rule has been applied i.e. the rule matches
|
||||
and results in a pass, fail, or error. When set to `All` all rules
|
||||
in the policy are processed. The default is `All`.
|
||||
enum:
|
||||
- All
|
||||
- One
|
||||
type: string
|
||||
background:
|
||||
description: Background controls if rules are applied to existing
|
||||
resources during a background scan. Optional. Default value is "true".
|
||||
|
@ -69,7 +79,7 @@ spec:
|
|||
- Fail
|
||||
type: string
|
||||
generateExistingOnPolicyUpdate:
|
||||
description: GenerateExistingOnPolicyUpdate controls wether to trigger
|
||||
description: GenerateExistingOnPolicyUpdate controls whether to trigger
|
||||
generate rule in existing resources If is set to "true" generate
|
||||
rule will be triggered and applied to existing matched resources.
|
||||
Defaults to "false" if not specified.
|
||||
|
|
|
@ -53,6 +53,16 @@ spec:
|
|||
spec:
|
||||
description: Spec defines policy behaviors and contains one or more rules.
|
||||
properties:
|
||||
applyRules:
|
||||
description: ApplyRules controls how rules in a policy are applied.
|
||||
Rule are processed in the order of declaration. When set to `One`
|
||||
processing stops after a rule has been applied i.e. the rule matches
|
||||
and results in a pass, fail, or error. When set to `All` all rules
|
||||
in the policy are processed. The default is `All`.
|
||||
enum:
|
||||
- All
|
||||
- One
|
||||
type: string
|
||||
background:
|
||||
description: Background controls if rules are applied to existing
|
||||
resources during a background scan. Optional. Default value is "true".
|
||||
|
@ -70,7 +80,7 @@ spec:
|
|||
- Fail
|
||||
type: string
|
||||
generateExistingOnPolicyUpdate:
|
||||
description: GenerateExistingOnPolicyUpdate controls wether to trigger
|
||||
description: GenerateExistingOnPolicyUpdate controls whether to trigger
|
||||
generate rule in existing resources If is set to "true" generate
|
||||
rule will be triggered and applied to existing matched resources.
|
||||
Defaults to "false" if not specified.
|
||||
|
|
|
@ -69,6 +69,16 @@ spec:
|
|||
spec:
|
||||
description: Spec declares policy behaviors.
|
||||
properties:
|
||||
applyRules:
|
||||
description: ApplyRules controls how rules in a policy are applied.
|
||||
Rule are processed in the order of declaration. When set to `One`
|
||||
processing stops after a rule has been applied i.e. the rule matches
|
||||
and results in a pass, fail, or error. When set to `All` all rules
|
||||
in the policy are processed. The default is `All`.
|
||||
enum:
|
||||
- All
|
||||
- One
|
||||
type: string
|
||||
background:
|
||||
description: Background controls if rules are applied to existing
|
||||
resources during a background scan. Optional. Default value is "true".
|
||||
|
@ -86,7 +96,7 @@ spec:
|
|||
- Fail
|
||||
type: string
|
||||
generateExistingOnPolicyUpdate:
|
||||
description: GenerateExistingOnPolicyUpdate controls wether to trigger
|
||||
description: GenerateExistingOnPolicyUpdate controls whether to trigger
|
||||
generate rule in existing resources If is set to "true" generate
|
||||
rule will be triggered and applied to existing matched resources.
|
||||
Defaults to "false" if not specified.
|
||||
|
@ -6050,6 +6060,16 @@ spec:
|
|||
spec:
|
||||
description: Spec defines policy behaviors and contains one or more rules.
|
||||
properties:
|
||||
applyRules:
|
||||
description: ApplyRules controls how rules in a policy are applied.
|
||||
Rule are processed in the order of declaration. When set to `One`
|
||||
processing stops after a rule has been applied i.e. the rule matches
|
||||
and results in a pass, fail, or error. When set to `All` all rules
|
||||
in the policy are processed. The default is `All`.
|
||||
enum:
|
||||
- All
|
||||
- One
|
||||
type: string
|
||||
background:
|
||||
description: Background controls if rules are applied to existing
|
||||
resources during a background scan. Optional. Default value is "true".
|
||||
|
@ -6067,7 +6087,7 @@ spec:
|
|||
- Fail
|
||||
type: string
|
||||
generateExistingOnPolicyUpdate:
|
||||
description: GenerateExistingOnPolicyUpdate controls wether to trigger
|
||||
description: GenerateExistingOnPolicyUpdate controls whether to trigger
|
||||
generate rule in existing resources If is set to "true" generate
|
||||
rule will be triggered and applied to existing matched resources.
|
||||
Defaults to "false" if not specified.
|
||||
|
|
|
@ -67,6 +67,16 @@ spec:
|
|||
spec:
|
||||
description: Spec declares policy behaviors.
|
||||
properties:
|
||||
applyRules:
|
||||
description: ApplyRules controls how rules in a policy are applied.
|
||||
Rule are processed in the order of declaration. When set to `One`
|
||||
processing stops after a rule has been applied i.e. the rule matches
|
||||
and results in a pass, fail, or error. When set to `All` all rules
|
||||
in the policy are processed. The default is `All`.
|
||||
enum:
|
||||
- All
|
||||
- One
|
||||
type: string
|
||||
background:
|
||||
description: Background controls if rules are applied to existing
|
||||
resources during a background scan. Optional. Default value is "true".
|
||||
|
@ -84,7 +94,7 @@ spec:
|
|||
- Fail
|
||||
type: string
|
||||
generateExistingOnPolicyUpdate:
|
||||
description: GenerateExistingOnPolicyUpdate controls wether to trigger
|
||||
description: GenerateExistingOnPolicyUpdate controls whether to trigger
|
||||
generate rule in existing resources If is set to "true" generate
|
||||
rule will be triggered and applied to existing matched resources.
|
||||
Defaults to "false" if not specified.
|
||||
|
@ -6044,6 +6054,16 @@ spec:
|
|||
spec:
|
||||
description: Spec defines policy behaviors and contains one or more rules.
|
||||
properties:
|
||||
applyRules:
|
||||
description: ApplyRules controls how rules in a policy are applied.
|
||||
Rule are processed in the order of declaration. When set to `One`
|
||||
processing stops after a rule has been applied i.e. the rule matches
|
||||
and results in a pass, fail, or error. When set to `All` all rules
|
||||
in the policy are processed. The default is `All`.
|
||||
enum:
|
||||
- All
|
||||
- One
|
||||
type: string
|
||||
background:
|
||||
description: Background controls if rules are applied to existing
|
||||
resources during a background scan. Optional. Default value is "true".
|
||||
|
@ -6061,7 +6081,7 @@ spec:
|
|||
- Fail
|
||||
type: string
|
||||
generateExistingOnPolicyUpdate:
|
||||
description: GenerateExistingOnPolicyUpdate controls wether to trigger
|
||||
description: GenerateExistingOnPolicyUpdate controls whether to trigger
|
||||
generate rule in existing resources If is set to "true" generate
|
||||
rule will be triggered and applied to existing matched resources.
|
||||
Defaults to "false" if not specified.
|
||||
|
|
|
@ -104,6 +104,23 @@ each rule can validate, mutate, or generate resources.</p>
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>applyRules</code></br>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.ApplyRulesType">
|
||||
ApplyRulesType
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>ApplyRules controls how rules in a policy are applied. Rule are processed in
|
||||
the order of declaration. When set to <code>One</code> processing stops after a rule has
|
||||
been applied i.e. the rule matches and results in a pass, fail, or error. When
|
||||
set to <code>All</code> all rules in the policy are processed. The default is <code>All</code>.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>failurePolicy</code></br>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.FailurePolicyType">
|
||||
|
@ -211,7 +228,7 @@ bool
|
|||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>GenerateExistingOnPolicyUpdate controls wether to trigger generate rule in existing resources
|
||||
<p>GenerateExistingOnPolicyUpdate controls whether to trigger generate rule in existing resources
|
||||
If is set to “true” generate rule will be triggered and applied to existing matched resources.
|
||||
Defaults to “false” if not specified.</p>
|
||||
</td>
|
||||
|
@ -311,6 +328,23 @@ each rule can validate, mutate, or generate resources.</p>
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>applyRules</code></br>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.ApplyRulesType">
|
||||
ApplyRulesType
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>ApplyRules controls how rules in a policy are applied. Rule are processed in
|
||||
the order of declaration. When set to <code>One</code> processing stops after a rule has
|
||||
been applied i.e. the rule matches and results in a pass, fail, or error. When
|
||||
set to <code>All</code> all rules in the policy are processed. The default is <code>All</code>.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>failurePolicy</code></br>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.FailurePolicyType">
|
||||
|
@ -418,7 +452,7 @@ bool
|
|||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>GenerateExistingOnPolicyUpdate controls wether to trigger generate rule in existing resources
|
||||
<p>GenerateExistingOnPolicyUpdate controls whether to trigger generate rule in existing resources
|
||||
If is set to “true” generate rule will be triggered and applied to existing matched resources.
|
||||
Defaults to “false” if not specified.</p>
|
||||
</td>
|
||||
|
@ -598,6 +632,15 @@ Here, all of the conditions need to pass</p>
|
|||
</tbody>
|
||||
</table>
|
||||
<hr />
|
||||
<h3 id="kyverno.io/v1.ApplyRulesType">ApplyRulesType
|
||||
(<code>string</code> alias)</p></h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#kyverno.io/v1.Spec">Spec</a>)
|
||||
</p>
|
||||
<p>
|
||||
<p>ApplyRulesType controls whether processing stops after one rule is applied or all rules are applied.</p>
|
||||
</p>
|
||||
<h3 id="kyverno.io/v1.Attestation">Attestation
|
||||
</h3>
|
||||
<p>
|
||||
|
@ -2892,6 +2935,23 @@ each rule can validate, mutate, or generate resources.</p>
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>applyRules</code></br>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.ApplyRulesType">
|
||||
ApplyRulesType
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>ApplyRules controls how rules in a policy are applied. Rule are processed in
|
||||
the order of declaration. When set to <code>One</code> processing stops after a rule has
|
||||
been applied i.e. the rule matches and results in a pass, fail, or error. When
|
||||
set to <code>All</code> all rules in the policy are processed. The default is <code>All</code>.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>failurePolicy</code></br>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.FailurePolicyType">
|
||||
|
@ -2999,7 +3059,7 @@ bool
|
|||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>GenerateExistingOnPolicyUpdate controls wether to trigger generate rule in existing resources
|
||||
<p>GenerateExistingOnPolicyUpdate controls whether to trigger generate rule in existing resources
|
||||
If is set to “true” generate rule will be triggered and applied to existing matched resources.
|
||||
Defaults to “false” if not specified.</p>
|
||||
</td>
|
||||
|
|
|
@ -309,8 +309,10 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext
|
|||
|
||||
jsonContext := policyContext.JSONContext
|
||||
// To manage existing resources, we compare the creation time for the default resource to be generated and policy creation time
|
||||
|
||||
ruleNameToProcessingTime := make(map[string]time.Duration)
|
||||
applyRules := policyContext.Policy.GetSpec().GetApplyRules()
|
||||
applyCount := 0
|
||||
|
||||
for _, rule := range autogen.ComputeRules(policy) {
|
||||
var err error
|
||||
if !rule.HasGenerate() {
|
||||
|
@ -333,6 +335,10 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext
|
|||
}
|
||||
}
|
||||
|
||||
if applyRules == kyvernov1.ApplyOne && applyCount > 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// add configmap json data to context
|
||||
if err := engine.LoadContext(log, rule.Context, policyContext, rule.Name); err != nil {
|
||||
log.Error(err, "cannot add configmaps to context")
|
||||
|
@ -358,6 +364,8 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext
|
|||
if policy.GetSpec().IsGenerateExistingOnPolicyUpdate() {
|
||||
processExisting = false
|
||||
}
|
||||
|
||||
applyCount++
|
||||
}
|
||||
|
||||
return genResources, processExisting, nil
|
||||
|
|
|
@ -258,7 +258,7 @@ func Test_Conditions(t *testing.T) {
|
|||
err := json.Unmarshal([]byte(scanPredicate), &dataMap)
|
||||
assert.NilError(t, err)
|
||||
|
||||
pass, err := evaluateConditions(conditions, ctx, dataMap, img, log.Log)
|
||||
pass, err := evaluateConditions(conditions, ctx, dataMap, log.Log)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, pass, true)
|
||||
}
|
||||
|
|
|
@ -48,9 +48,13 @@ func filterRules(policyContext *PolicyContext, startTime time.Time) *response.En
|
|||
return resp
|
||||
}
|
||||
|
||||
applyRules := policyContext.Policy.GetSpec().GetApplyRules()
|
||||
for _, rule := range autogen.ComputeRules(policyContext.Policy) {
|
||||
if ruleResp := filterRule(rule, policyContext); ruleResp != nil {
|
||||
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, *ruleResp)
|
||||
if applyRules == kyvernov1.ApplyOne && ruleResp.Status != response.RuleStatusSkip {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -241,12 +241,13 @@ func (ctx *context) AddElement(data interface{}, index int) error {
|
|||
|
||||
func (ctx *context) AddImageInfo(info apiutils.ImageInfo) error {
|
||||
data := map[string]interface{}{
|
||||
"image": info.String(),
|
||||
"registry": info.Registry,
|
||||
"path": info.Path,
|
||||
"name": info.Name,
|
||||
"tag": info.Tag,
|
||||
"digest": info.Digest,
|
||||
"reference": info.String(),
|
||||
"referenceWithTag": info.ReferenceWithTag(),
|
||||
"registry": info.Registry,
|
||||
"path": info.Path,
|
||||
"name": info.Name,
|
||||
"tag": info.Tag,
|
||||
"digest": info.Digest,
|
||||
}
|
||||
return addToContext(ctx, data, "image")
|
||||
}
|
||||
|
|
|
@ -50,6 +50,8 @@ func VerifyAndPatchImages(policyContext *PolicyContext) (*response.EngineRespons
|
|||
|
||||
ivm := &ImageVerificationMetadata{}
|
||||
rules := autogen.ComputeRules(policyContext.Policy)
|
||||
applyRules := policy.GetSpec().GetApplyRules()
|
||||
|
||||
for i := range rules {
|
||||
rule := &rules[i]
|
||||
if len(rule.VerifyImages) == 0 {
|
||||
|
@ -60,8 +62,9 @@ func VerifyAndPatchImages(policyContext *PolicyContext) (*response.EngineRespons
|
|||
continue
|
||||
}
|
||||
|
||||
policyContext.JSONContext.Restore()
|
||||
logger.V(3).Info("processing image verification rule", "ruleSelector", applyRules)
|
||||
|
||||
policyContext.JSONContext.Restore()
|
||||
if err := LoadContext(logger, rule.Context, policyContext, rule.Name); err != nil {
|
||||
appendError(resp, rule, fmt.Sprintf("failed to load context: %s", err.Error()), response.RuleStatusError)
|
||||
continue
|
||||
|
@ -97,6 +100,10 @@ func VerifyAndPatchImages(policyContext *PolicyContext) (*response.EngineRespons
|
|||
for _, imageVerify := range ruleCopy.VerifyImages {
|
||||
iv.verify(imageVerify, ruleImages)
|
||||
}
|
||||
|
||||
if applyRules == kyvernov1.ApplyOne && resp.PolicyResponse.RulesAppliedCount > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return resp, ivm
|
||||
|
@ -264,14 +271,20 @@ func (iv *imageVerifier) verifyImage(imageVerify kyvernov1.ImageVerification, im
|
|||
iv.logger.V(2).Info("verifying image signatures", "image", image,
|
||||
"attestors", len(imageVerify.Attestors), "attestations", len(imageVerify.Attestations))
|
||||
|
||||
if err := iv.policyContext.JSONContext.AddImageInfo(imageInfo); err != nil {
|
||||
iv.logger.Error(err, "failed to add image to context")
|
||||
msg := fmt.Sprintf("failed to add image to context %s: %s", image, err.Error())
|
||||
return ruleResponse(*iv.rule, response.ImageVerify, msg, response.RuleStatusError, nil), ""
|
||||
}
|
||||
|
||||
var cosignResponse *cosign.Response
|
||||
for i, attestorSet := range imageVerify.Attestors {
|
||||
var err error
|
||||
path := fmt.Sprintf(".attestors[%d]", i)
|
||||
cosignResponse, err = iv.verifyAttestorSet(attestorSet, imageVerify, imageInfo, path)
|
||||
if err != nil {
|
||||
iv.logger.Error(err, "failed to verify signature")
|
||||
msg := fmt.Sprintf("failed to verify signature for %s: %s", image, err.Error())
|
||||
iv.logger.Error(err, "failed to verify image")
|
||||
msg := fmt.Sprintf("failed to verify image %s: %s", image, err.Error())
|
||||
return ruleResponse(*iv.rule, response.ImageVerify, msg, response.RuleStatusFail, nil), ""
|
||||
}
|
||||
}
|
||||
|
@ -458,10 +471,10 @@ func (iv *imageVerifier) verifyAttestations(statements []map[string]interface{},
|
|||
return fmt.Errorf("predicate type %s not found", ac.PredicateType)
|
||||
}
|
||||
|
||||
iv.logger.Info("checking attestation predicate type %s", "predicates", types, "image", imageInfo.String())
|
||||
iv.logger.Info("checking attestation", "predicates", types, "image", imageInfo.String())
|
||||
|
||||
for _, s := range statements {
|
||||
val, err := iv.checkAttestations(ac, s, imageInfo)
|
||||
val, err := iv.checkAttestations(ac, s)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to check attestations")
|
||||
}
|
||||
|
@ -493,7 +506,7 @@ func buildStatementMap(statements []map[string]interface{}) (map[string][]map[st
|
|||
return results, predicateTypes
|
||||
}
|
||||
|
||||
func (iv *imageVerifier) checkAttestations(a kyvernov1.Attestation, s map[string]interface{}, img apiutils.ImageInfo) (bool, error) {
|
||||
func (iv *imageVerifier) checkAttestations(a kyvernov1.Attestation, s map[string]interface{}) (bool, error) {
|
||||
if len(a.Conditions) == 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
@ -501,14 +514,13 @@ func (iv *imageVerifier) checkAttestations(a kyvernov1.Attestation, s map[string
|
|||
iv.policyContext.JSONContext.Checkpoint()
|
||||
defer iv.policyContext.JSONContext.Restore()
|
||||
|
||||
return evaluateConditions(a.Conditions, iv.policyContext.JSONContext, s, img, iv.logger)
|
||||
return evaluateConditions(a.Conditions, iv.policyContext.JSONContext, s, iv.logger)
|
||||
}
|
||||
|
||||
func evaluateConditions(
|
||||
conditions []kyvernov1.AnyAllConditions,
|
||||
ctx context.Interface,
|
||||
s map[string]interface{},
|
||||
img apiutils.ImageInfo,
|
||||
log logr.Logger) (bool, error) {
|
||||
|
||||
predicate, ok := s["predicate"].(map[string]interface{})
|
||||
|
@ -520,10 +532,6 @@ func evaluateConditions(
|
|||
return false, errors.Wrapf(err, fmt.Sprintf("failed to add Statement to the context %v", s))
|
||||
}
|
||||
|
||||
if err := ctx.AddImageInfo(img); err != nil {
|
||||
return false, errors.Wrapf(err, fmt.Sprintf("failed to add image to the context %v", s))
|
||||
}
|
||||
|
||||
c, err := variables.SubstituteAllInConditions(log, ctx, conditions)
|
||||
if err != nil {
|
||||
return false, errors.Wrapf(err, "failed to substitute variables in attestation conditions")
|
||||
|
|
|
@ -382,9 +382,63 @@ func Test_SignaturesMultiKeyZeroGoodKey(t *testing.T) {
|
|||
policy = strings.Replace(policy, "KEY2", testOtherKey, -1)
|
||||
policy = strings.Replace(policy, "COUNT", "1", -1)
|
||||
policyContext := buildContext(t, policy, testSampleResource, "")
|
||||
err, _ := VerifyAndPatchImages(policyContext)
|
||||
assert.Equal(t, len(err.PolicyResponse.Rules), 1)
|
||||
assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusFail, err.PolicyResponse.Rules[0].Message)
|
||||
resp, _ := VerifyAndPatchImages(policyContext)
|
||||
assert.Equal(t, len(resp.PolicyResponse.Rules), 1)
|
||||
assert.Equal(t, resp.PolicyResponse.Rules[0].Status, response.RuleStatusFail, resp.PolicyResponse.Rules[0].Message)
|
||||
}
|
||||
|
||||
func Test_RuleSelectorImageVerify(t *testing.T) {
|
||||
cosign.ClearMock()
|
||||
|
||||
policyContext := buildContext(t, testSampleSingleKeyPolicy, testSampleResource, "")
|
||||
rule := newStaticKeyRule("match-all", "*", testOtherKey)
|
||||
spec := policyContext.Policy.GetSpec()
|
||||
spec.Rules = append(spec.Rules, *rule)
|
||||
|
||||
applyAll := kyverno.ApplyAll
|
||||
spec.ApplyRules = &applyAll
|
||||
|
||||
resp, _ := VerifyAndPatchImages(policyContext)
|
||||
assert.Equal(t, len(resp.PolicyResponse.Rules), 2)
|
||||
assert.Equal(t, resp.PolicyResponse.Rules[0].Status, response.RuleStatusPass, resp.PolicyResponse.Rules[0].Message)
|
||||
assert.Equal(t, resp.PolicyResponse.Rules[1].Status, response.RuleStatusFail, resp.PolicyResponse.Rules[1].Message)
|
||||
|
||||
applyOne := kyverno.ApplyOne
|
||||
spec.ApplyRules = &applyOne
|
||||
resp, _ = VerifyAndPatchImages(policyContext)
|
||||
assert.Equal(t, len(resp.PolicyResponse.Rules), 1)
|
||||
assert.Equal(t, resp.PolicyResponse.Rules[0].Status, response.RuleStatusPass, resp.PolicyResponse.Rules[0].Message)
|
||||
}
|
||||
|
||||
func newStaticKeyRule(name, imageReference, key string) *kyverno.Rule {
|
||||
return &kyverno.Rule{
|
||||
Name: name,
|
||||
MatchResources: kyverno.MatchResources{
|
||||
All: kyverno.ResourceFilters{
|
||||
{
|
||||
ResourceDescription: kyverno.ResourceDescription{
|
||||
Kinds: []string{"Pod"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
VerifyImages: []kyverno.ImageVerification{
|
||||
{
|
||||
ImageReferences: []string{"*"},
|
||||
Attestors: []kyverno.AttestorSet{
|
||||
{
|
||||
Entries: []kyverno.Attestor{
|
||||
{
|
||||
Keys: &kyverno.StaticKeyAttestor{
|
||||
PublicKeys: key,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var testNestedAttestorPolicy = `
|
||||
|
|
|
@ -39,6 +39,7 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) {
|
|||
defer policyContext.JSONContext.Restore()
|
||||
|
||||
var err error
|
||||
applyRules := policy.GetSpec().GetApplyRules()
|
||||
|
||||
for _, rule := range autogen.ComputeRules(policy) {
|
||||
if !rule.HasMutate() {
|
||||
|
@ -57,8 +58,7 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) {
|
|||
continue
|
||||
}
|
||||
|
||||
logger.V(3).Info("matched mutate rule")
|
||||
|
||||
logger.V(3).Info("processing mutate rule", "applyRules", applyRules)
|
||||
resource, err := policyContext.JSONContext.Query("request.object")
|
||||
policyContext.JSONContext.Reset()
|
||||
if err == nil && resource != nil {
|
||||
|
@ -124,6 +124,10 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if applyRules == kyvernov1.ApplyOne && resp.PolicyResponse.RulesAppliedCount > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for _, r := range resp.PolicyResponse.Rules {
|
||||
|
|
|
@ -20,53 +20,54 @@ import (
|
|||
|
||||
func Test_VariableSubstitutionPatchStrategicMerge(t *testing.T) {
|
||||
policyRaw := []byte(`{
|
||||
"apiVersion": "kyverno.io/v1",
|
||||
"kind": "ClusterPolicy",
|
||||
"metadata": {
|
||||
"name": "add-label"
|
||||
},
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "add-name-label",
|
||||
"match": {
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Pod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"mutate": {
|
||||
"patchStrategicMerge": {
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"appname": "{{request.object.metadata.name}}"
|
||||
"apiVersion": "kyverno.io/v1",
|
||||
"kind": "ClusterPolicy",
|
||||
"metadata": {
|
||||
"name": "add-label"
|
||||
},
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "add-name-label",
|
||||
"match": {
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Pod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"mutate": {
|
||||
"patchStrategicMerge": {
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"appname": "{{request.object.metadata.name}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}`)
|
||||
]
|
||||
}
|
||||
}`)
|
||||
|
||||
resourceRaw := []byte(`{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"metadata": {
|
||||
"name": "check-root-user"
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "check-root-user",
|
||||
"image": "nginxinc/nginx-unprivileged",
|
||||
"securityContext": {
|
||||
"runAsNonRoot": true
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"metadata": {
|
||||
"name": "check-root-user"
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "check-root-user",
|
||||
"image": "nginxinc/nginx-unprivileged",
|
||||
"securityContext": {
|
||||
"runAsNonRoot": true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}`)
|
||||
]
|
||||
}
|
||||
}`)
|
||||
expectedPatch := []byte(`{"op":"add","path":"/metadata/labels","value":{"appname":"check-root-user"}}`)
|
||||
|
||||
var policy kyverno.ClusterPolicy
|
||||
|
@ -1465,3 +1466,124 @@ func Test_mutate_existing_resources(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_RuleSelectorMutate(t *testing.T) {
|
||||
policyRaw := []byte(`{
|
||||
"apiVersion": "kyverno.io/v1",
|
||||
"kind": "ClusterPolicy",
|
||||
"metadata": {
|
||||
"name": "add-label"
|
||||
},
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "add-app-label",
|
||||
"match": {
|
||||
"resources": {
|
||||
"name": "check-root-user",
|
||||
"kinds": [
|
||||
"Pod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"mutate": {
|
||||
"patchStrategicMerge": {
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"app": "root"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "add-appname-label",
|
||||
"match": {
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Pod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"mutate": {
|
||||
"patchStrategicMerge": {
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"appname": "{{request.object.metadata.name}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}`)
|
||||
|
||||
resourceRaw := []byte(`{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"metadata": {
|
||||
"name": "check-root-user"
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "check-root-user",
|
||||
"image": "nginxinc/nginx-unprivileged",
|
||||
"securityContext": {
|
||||
"runAsNonRoot": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}`)
|
||||
|
||||
expectedPatch1 := []byte(`{"op":"add","path":"/metadata/labels","value":{"app":"root"}}`)
|
||||
expectedPatch2 := []byte(`{"op":"add","path":"/metadata/labels/appname","value":"check-root-user"}`)
|
||||
|
||||
var policy kyverno.ClusterPolicy
|
||||
err := json.Unmarshal(policyRaw, &policy)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw)
|
||||
assert.NilError(t, err)
|
||||
ctx := context.NewContext()
|
||||
err = context.AddResource(ctx, resourceRaw)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = ctx.Query("request.object.metadata.name")
|
||||
assert.NilError(t, err)
|
||||
|
||||
policyContext := &PolicyContext{
|
||||
Policy: &policy,
|
||||
JSONContext: ctx,
|
||||
NewResource: *resourceUnstructured,
|
||||
}
|
||||
|
||||
er := Mutate(policyContext)
|
||||
assert.Equal(t, len(er.PolicyResponse.Rules), 2)
|
||||
assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1)
|
||||
assert.Equal(t, len(er.PolicyResponse.Rules[1].Patches), 1)
|
||||
|
||||
if !reflect.DeepEqual(expectedPatch1, er.PolicyResponse.Rules[0].Patches[0]) {
|
||||
t.Error("rule 1 patches dont match")
|
||||
}
|
||||
if !reflect.DeepEqual(expectedPatch2, er.PolicyResponse.Rules[1].Patches[0]) {
|
||||
t.Errorf("rule 2 patches dont match")
|
||||
}
|
||||
|
||||
applyOne := kyverno.ApplyOne
|
||||
policyContext.Policy.GetSpec().ApplyRules = &applyOne
|
||||
|
||||
er = Mutate(policyContext)
|
||||
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
|
||||
assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1)
|
||||
|
||||
if !reflect.DeepEqual(expectedPatch1, er.PolicyResponse.Rules[0].Patches[0]) {
|
||||
t.Error("rule 1 patches dont match")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,6 +90,9 @@ func validateResource(log logr.Logger, ctx *PolicyContext) *response.EngineRespo
|
|||
defer ctx.JSONContext.Restore()
|
||||
|
||||
rules := autogen.ComputeRules(ctx.Policy)
|
||||
matchCount := 0
|
||||
applyRules := ctx.Policy.GetSpec().GetApplyRules()
|
||||
|
||||
for i := range rules {
|
||||
rule := &rules[i]
|
||||
hasValidate := rule.HasValidate()
|
||||
|
@ -103,7 +106,7 @@ func validateResource(log logr.Logger, ctx *PolicyContext) *response.EngineRespo
|
|||
continue
|
||||
}
|
||||
|
||||
log.V(3).Info("matched validate rule")
|
||||
log.V(3).Info("processing validation rule", "matchCount", matchCount, "applyRules", applyRules)
|
||||
ctx.JSONContext.Reset()
|
||||
startTime := time.Now()
|
||||
|
||||
|
@ -116,6 +119,9 @@ func validateResource(log logr.Logger, ctx *PolicyContext) *response.EngineRespo
|
|||
|
||||
if ruleResp != nil {
|
||||
addRuleResponse(log, resp, ruleResp, startTime)
|
||||
if applyRules == kyvernov1.ApplyOne && resp.PolicyResponse.RulesAppliedCount > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,10 @@ func (i *ImageInfo) String() string {
|
|||
}
|
||||
}
|
||||
|
||||
func (i *ImageInfo) ReferenceWithTag() string {
|
||||
return i.Registry + "/" + i.Path + ":" + i.Tag
|
||||
}
|
||||
|
||||
func GetImageInfo(image string) (*ImageInfo, error) {
|
||||
image = addDefaultDomain(image)
|
||||
ref, err := reference.Parse(image)
|
||||
|
|
|
@ -64,9 +64,7 @@ func (v *validationHandler) handleValidation(
|
|||
continue
|
||||
}
|
||||
|
||||
// registering the kyverno_policy_results_total metric concurrently
|
||||
go registerPolicyResultsMetricValidation(logger, metricsConfig, string(request.Operation), policyContext.Policy, *engineResponse)
|
||||
// registering the kyverno_policy_execution_duration_seconds metric concurrently
|
||||
go registerPolicyExecutionDurationMetricValidate(logger, metricsConfig, string(request.Operation), policyContext.Policy, *engineResponse)
|
||||
|
||||
engineResponses = append(engineResponses, engineResponse)
|
||||
|
@ -80,20 +78,7 @@ func (v *validationHandler) handleValidation(
|
|||
}
|
||||
}
|
||||
|
||||
// If Validation fails then reject the request
|
||||
// no violations will be created on "enforce"
|
||||
blocked := toBlockResource(engineResponses, logger)
|
||||
|
||||
// REPORTING EVENTS
|
||||
// Scenario 1:
|
||||
// resource is blocked, as there is a policy in "enforce" mode that failed.
|
||||
// create an event on the policy to inform the resource request was blocked
|
||||
// Scenario 2:
|
||||
// some/all policies failed to apply on the resource. a policy violation is generated.
|
||||
// create an event on the resource and the policy that failed
|
||||
// Scenario 3:
|
||||
// all policies were applied successfully.
|
||||
// create an event on the resource
|
||||
if deletionTimeStamp == nil {
|
||||
events := generateEvents(engineResponses, blocked, logger)
|
||||
v.eventGen.Add(events...)
|
||||
|
@ -101,10 +86,8 @@ func (v *validationHandler) handleValidation(
|
|||
|
||||
if blocked {
|
||||
logger.V(4).Info("resource blocked")
|
||||
// registering the kyverno_admission_review_duration_seconds metric concurrently
|
||||
admissionReviewLatencyDuration := int64(time.Since(time.Unix(admissionRequestTimestamp, 0)))
|
||||
go registerAdmissionReviewDurationMetricValidate(logger, metricsConfig, string(request.Operation), engineResponses, admissionReviewLatencyDuration)
|
||||
//registering the kyverno_admission_requests_total metric concurrently
|
||||
go registerAdmissionRequestsMetricValidate(logger, metricsConfig, string(request.Operation), engineResponses)
|
||||
return false, getEnforceFailureErrorMsg(engineResponses)
|
||||
}
|
||||
|
@ -130,10 +113,8 @@ func (v *validationHandler) handleValidation(
|
|||
prInfos := policyreport.GeneratePRsFromEngineResponse(engineResponses, logger)
|
||||
v.prGenerator.Add(prInfos...)
|
||||
|
||||
// registering the kyverno_admission_review_duration_seconds metric concurrently
|
||||
admissionReviewLatencyDuration := int64(time.Since(time.Unix(admissionRequestTimestamp, 0)))
|
||||
go registerAdmissionReviewDurationMetricValidate(logger, metricsConfig, string(request.Operation), engineResponses, admissionReviewLatencyDuration)
|
||||
//registering the kyverno_admission_requests_total metric concurrently
|
||||
go registerAdmissionRequestsMetricValidate(logger, metricsConfig, string(request.Operation), engineResponses)
|
||||
return true, ""
|
||||
}
|
||||
|
|
|
@ -5,9 +5,9 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
log "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/pkg/engine"
|
||||
"github.com/kyverno/kyverno/pkg/engine/context"
|
||||
"github.com/kyverno/kyverno/pkg/engine/response"
|
||||
|
@ -21,6 +21,7 @@ func TestValidate_failure_action_overrides(t *testing.T) {
|
|||
rawPolicy []byte
|
||||
rawResource []byte
|
||||
blocked bool
|
||||
messages map[string]string
|
||||
}{
|
||||
{
|
||||
rawPolicy: []byte(`
|
||||
|
@ -473,25 +474,25 @@ func TestValidate_failure_action_overrides(t *testing.T) {
|
|||
],
|
||||
"rules": [
|
||||
{
|
||||
"name": "check-label-app",
|
||||
"match": {
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Pod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"validate": {
|
||||
"message": "The label 'app' is required.",
|
||||
"pattern": {
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"app": "?*"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"name": "check-label-app",
|
||||
"match": {
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Pod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"validate": {
|
||||
"message": "The label 'app' is required.",
|
||||
"pattern": {
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"app": "?*"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -515,24 +516,25 @@ func TestValidate_failure_action_overrides(t *testing.T) {
|
|||
}
|
||||
`),
|
||||
blocked: true,
|
||||
messages: map[string]string{
|
||||
"check-label-app": "validation error: The label 'app' is required. rule check-label-app failed at path /metadata/labels/",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testcases {
|
||||
t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
|
||||
var policy kyverno.ClusterPolicy
|
||||
var policy kyvernov1.ClusterPolicy
|
||||
err := json.Unmarshal(tc.rawPolicy, &policy)
|
||||
assert.NilError(t, err)
|
||||
resourceUnstructured, err := utils.ConvertToUnstructured(tc.rawResource)
|
||||
assert.NilError(t, err)
|
||||
msgs := []string{
|
||||
"validation error: The label 'app' is required. rule check-label-app failed at path /metadata/labels/",
|
||||
}
|
||||
|
||||
er := engine.Validate(&engine.PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()})
|
||||
if tc.blocked {
|
||||
for index, r := range er.PolicyResponse.Rules {
|
||||
assert.Equal(t, r.Message, msgs[index])
|
||||
if tc.blocked && tc.messages != nil {
|
||||
for _, r := range er.PolicyResponse.Rules {
|
||||
msg := tc.messages[r.Name]
|
||||
assert.Equal(t, r.Message, msg)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -541,3 +543,66 @@ func TestValidate_failure_action_overrides(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_RuleSelector(t *testing.T) {
|
||||
var rawPolicy = []byte(`{
|
||||
"apiVersion": "kyverno.io/v1",
|
||||
"kind": "ClusterPolicy",
|
||||
"metadata": {"name": "check-label-app"},
|
||||
"spec": {
|
||||
"validationFailureAction": "enforce",
|
||||
"rules": [
|
||||
{
|
||||
"name": "check-label-test",
|
||||
"match": {"name": "test-*", "resources": {"kinds": ["Pod"]}},
|
||||
"validate": {
|
||||
"message": "The label 'app' is required.",
|
||||
"pattern": { "metadata": { "labels": { "app": "?*" } } }
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "check-labels",
|
||||
"match": {"name": "*", "resources": {"kinds": ["Pod"]}},
|
||||
"validate": {
|
||||
"message": "The label 'app' is required.",
|
||||
"pattern": { "metadata": { "labels": { "app": "?*", "test" : "?*" } } }
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}`)
|
||||
|
||||
var rawResource = []byte(`{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"metadata": {"name": "test-pod", "namespace": "", "labels": { "app" : "test-pod" }},
|
||||
"spec": {"containers": [{"name": "nginx", "image": "nginx:latest"}]}
|
||||
}`)
|
||||
|
||||
var policy kyvernov1.ClusterPolicy
|
||||
err := json.Unmarshal(rawPolicy, &policy)
|
||||
assert.NilError(t, err)
|
||||
|
||||
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, resourceUnstructured != nil)
|
||||
|
||||
ctx := &engine.PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()}
|
||||
resp := engine.Validate(ctx)
|
||||
assert.Assert(t, resp.PolicyResponse.RulesAppliedCount == 2)
|
||||
assert.Assert(t, resp.PolicyResponse.RulesErrorCount == 0)
|
||||
|
||||
log := log.Log.WithName("Test_RuleSelector")
|
||||
blocked := toBlockResource([]*response.EngineResponse{resp}, log)
|
||||
assert.Assert(t, blocked == true)
|
||||
|
||||
applyOne := kyvernov1.ApplyOne
|
||||
policy.Spec.ApplyRules = &applyOne
|
||||
|
||||
resp = engine.Validate(ctx)
|
||||
assert.Assert(t, resp.PolicyResponse.RulesAppliedCount == 1)
|
||||
assert.Assert(t, resp.PolicyResponse.RulesErrorCount == 0)
|
||||
|
||||
blocked = toBlockResource([]*response.EngineResponse{resp}, log)
|
||||
assert.Assert(t, blocked == false)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue