1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-29 02:45:06 +00:00

Namespace Specific ValidationFailureAction (#2794)

* Implement ValidationFailureActionOverride

Signed-off-by: Kumar Mallikarjuna <kumarmallikarjuna1@gmail.com>

* Update CRDs

Signed-off-by: Kumar Mallikarjuna <kumarmallikarjuna1@gmail.com>

* Update getEnforceFailureErrorMsg()

Signed-off-by: Kumar Mallikarjuna <kumarmallikarjuna1@gmail.com>

* Allow validate policies to be checked

Signed-off-by: Kumar Mallikarjuna <kumarmallikarjuna1@gmail.com>

* Fix linting issues

Signed-off-by: Kumar Mallikarjuna <kumarmallikarjuna1@gmail.com>

* Added tests for ValidationFailureActionOverrides

Signed-off-by: Kumar Mallikarjuna <kumarmallikarjuna1@gmail.com>

* Added schema validation

Signed-off-by: Kumar Mallikarjuna <kumarmallikarjuna1@gmail.com>

* Added description for ValidationFailureActionOverrides

Signed-off-by: Kumar Mallikarjuna <kumar@nirmata.com>

* Policy validation

Signed-off-by: Kumar Mallikarjuna <kumar@nirmata.com>

* Update CRDs

Signed-off-by: Kumar Mallikarjuna <kumar@nirmata.com>

* Replace literals with constants

Signed-off-by: Kumar Mallikarjuna <kumar@nirmata.com>

* Updated Policy Cache

Signed-off-by: Kumar Mallikarjuna <kumar@nirmata.com>

* Refactor

Signed-off-by: Kumar Mallikarjuna <kumar@nirmata.com>

Co-authored-by: shuting <shutting06@gmail.com>
This commit is contained in:
Kumar Mallikarjuna 2022-01-21 18:06:44 +05:30 committed by GitHub
parent 4124e0f682
commit 5ad0d15240
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 904 additions and 3 deletions

View file

@ -55,8 +55,14 @@ type Spec struct {
// the admission review request (enforce), or allow (audit) the admission review request
// and report an error in a policy report. Optional. The default value is "audit".
// +optional
// +kubebuilder:validation:Enum=audit;enforce
ValidationFailureAction string `json:"validationFailureAction,omitempty" yaml:"validationFailureAction,omitempty"`
// ValidationFailureActionOverrides is a Cluter Policy attribute that specifies ValidationFailureAction
// namespace-wise. It overrides ValidationFailureAction for the specified namespaces.
// +optional
ValidationFailureActionOverrides []ValidationFailureActionOverride `json:"validationFailureActionOverrides,omitempty" yaml:"validationFailureActionOverrides,omitempty"`
// 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).
@ -641,3 +647,9 @@ type ResourceSpec struct {
// Name specifies the resource name.
Name string `json:"name,omitempty" yaml:"name,omitempty"`
}
type ValidationFailureActionOverride struct {
// +kubebuilder:validation:Enum=audit;enforce
Action string `json:"action,omitempty" yaml:"action,omitempty"`
Namespaces []string `json:"namespaces,omitempty" yaml:"namespaces,omitempty"`
}

View file

@ -1330,7 +1330,25 @@ spec:
type: boolean
validationFailureAction:
description: ValidationFailureAction controls if a validation policy rule failure should disallow the admission review request (enforce), or allow (audit) the admission review request and report an error in a policy report. Optional. The default value is "audit".
enum:
- audit
- enforce
type: string
validationFailureActionOverrides:
description: ValidationFailureActionOverrides is a Cluter Policy attribute that specifies ValidationFailureAction namespace-wise. It overrides ValidationFailureAction for the specified namespaces.
items:
properties:
action:
enum:
- audit
- enforce
type: string
namespaces:
items:
type: string
type: array
type: object
type: array
webhookTimeoutSeconds:
description: WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. After the configured time expires, the admission request may fail, or may simply ignore the policy results, based on the failure policy. The default timeout is 10s, the value must be between 1 and 30 seconds.
format: int32
@ -3858,7 +3876,25 @@ spec:
type: boolean
validationFailureAction:
description: ValidationFailureAction controls if a validation policy rule failure should disallow the admission review request (enforce), or allow (audit) the admission review request and report an error in a policy report. Optional. The default value is "audit".
enum:
- audit
- enforce
type: string
validationFailureActionOverrides:
description: ValidationFailureActionOverrides is a Cluter Policy attribute that specifies ValidationFailureAction namespace-wise. It overrides ValidationFailureAction for the specified namespaces.
items:
properties:
action:
enum:
- audit
- enforce
type: string
namespaces:
items:
type: string
type: array
type: object
type: array
webhookTimeoutSeconds:
description: WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. After the configured time expires, the admission request may fail, or may simply ignore the policy results, based on the failure policy. The default timeout is 10s, the value must be between 1 and 30 seconds.
format: int32

View file

@ -2121,7 +2121,27 @@ spec:
rule failure should disallow the admission review request (enforce),
or allow (audit) the admission review request and report an error
in a policy report. Optional. The default value is "audit".
enum:
- audit
- enforce
type: string
validationFailureActionOverrides:
description: ValidationFailureActionOverrides is a Cluter Policy attribute
that specifies ValidationFailureAction namespace-wise. It overrides
ValidationFailureAction for the specified namespaces.
items:
properties:
action:
enum:
- audit
- enforce
type: string
namespaces:
items:
type: string
type: array
type: object
type: array
webhookTimeoutSeconds:
description: WebhookTimeoutSeconds specifies the maximum time in seconds
allowed to apply this policy. After the configured time expires,

View file

@ -2122,7 +2122,27 @@ spec:
rule failure should disallow the admission review request (enforce),
or allow (audit) the admission review request and report an error
in a policy report. Optional. The default value is "audit".
enum:
- audit
- enforce
type: string
validationFailureActionOverrides:
description: ValidationFailureActionOverrides is a Cluter Policy attribute
that specifies ValidationFailureAction namespace-wise. It overrides
ValidationFailureAction for the specified namespaces.
items:
properties:
action:
enum:
- audit
- enforce
type: string
namespaces:
items:
type: string
type: array
type: object
type: array
webhookTimeoutSeconds:
description: WebhookTimeoutSeconds specifies the maximum time in seconds
allowed to apply this policy. After the configured time expires,

View file

@ -2137,7 +2137,27 @@ spec:
rule failure should disallow the admission review request (enforce),
or allow (audit) the admission review request and report an error
in a policy report. Optional. The default value is "audit".
enum:
- audit
- enforce
type: string
validationFailureActionOverrides:
description: ValidationFailureActionOverrides is a Cluter Policy attribute
that specifies ValidationFailureAction namespace-wise. It overrides
ValidationFailureAction for the specified namespaces.
items:
properties:
action:
enum:
- audit
- enforce
type: string
namespaces:
items:
type: string
type: array
type: object
type: array
webhookTimeoutSeconds:
description: WebhookTimeoutSeconds specifies the maximum time in seconds
allowed to apply this policy. After the configured time expires,
@ -5856,7 +5876,27 @@ spec:
rule failure should disallow the admission review request (enforce),
or allow (audit) the admission review request and report an error
in a policy report. Optional. The default value is "audit".
enum:
- audit
- enforce
type: string
validationFailureActionOverrides:
description: ValidationFailureActionOverrides is a Cluter Policy attribute
that specifies ValidationFailureAction namespace-wise. It overrides
ValidationFailureAction for the specified namespaces.
items:
properties:
action:
enum:
- audit
- enforce
type: string
namespaces:
items:
type: string
type: array
type: object
type: array
webhookTimeoutSeconds:
description: WebhookTimeoutSeconds specifies the maximum time in seconds
allowed to apply this policy. After the configured time expires,

View file

@ -2126,7 +2126,27 @@ spec:
rule failure should disallow the admission review request (enforce),
or allow (audit) the admission review request and report an error
in a policy report. Optional. The default value is "audit".
enum:
- audit
- enforce
type: string
validationFailureActionOverrides:
description: ValidationFailureActionOverrides is a Cluter Policy attribute
that specifies ValidationFailureAction namespace-wise. It overrides
ValidationFailureAction for the specified namespaces.
items:
properties:
action:
enum:
- audit
- enforce
type: string
namespaces:
items:
type: string
type: array
type: object
type: array
webhookTimeoutSeconds:
description: WebhookTimeoutSeconds specifies the maximum time in seconds
allowed to apply this policy. After the configured time expires,
@ -5821,7 +5841,27 @@ spec:
rule failure should disallow the admission review request (enforce),
or allow (audit) the admission review request and report an error
in a policy report. Optional. The default value is "audit".
enum:
- audit
- enforce
type: string
validationFailureActionOverrides:
description: ValidationFailureActionOverrides is a Cluter Policy attribute
that specifies ValidationFailureAction namespace-wise. It overrides
ValidationFailureAction for the specified namespaces.
items:
properties:
action:
enum:
- audit
- enforce
type: string
namespaces:
items:
type: string
type: array
type: object
type: array
webhookTimeoutSeconds:
description: WebhookTimeoutSeconds specifies the maximum time in seconds
allowed to apply this policy. After the configured time expires,

View file

@ -32,6 +32,8 @@ type PolicyResponse struct {
Rules []RuleResponse `json:"rules"`
// ValidationFailureAction: audit (default) or enforce
ValidationFailureAction string
ValidationFailureActionOverrides []ValidationFailureActionOverride
}
//PolicySpec policy
@ -173,3 +175,8 @@ func (er EngineResponse) getRules(status RuleStatus) []string {
return rules
}
type ValidationFailureActionOverride struct {
Action string `json:"action"`
Namespaces []string `json:"namespaces"`
}

View file

@ -72,6 +72,11 @@ func buildResponse(ctx *PolicyContext, resp *response.EngineResponse, startTime
resp.PolicyResponse.Resource.Kind = resp.PatchedResource.GetKind()
resp.PolicyResponse.Resource.APIVersion = resp.PatchedResource.GetAPIVersion()
resp.PolicyResponse.ValidationFailureAction = ctx.Policy.Spec.ValidationFailureAction
for _, v := range ctx.Policy.Spec.ValidationFailureActionOverrides {
resp.PolicyResponse.ValidationFailureActionOverrides = append(resp.PolicyResponse.ValidationFailureActionOverrides, response.ValidationFailureActionOverride{Action: v.Action, Namespaces: v.Namespaces})
}
resp.PolicyResponse.ProcessingTime = time.Since(startTime)
resp.PolicyResponse.PolicyExecutionTimestamp = startTime.Unix()
}

View file

@ -106,6 +106,10 @@ func Validate(policy *kyverno.ClusterPolicy, client *dclient.Client, mock bool,
clusterResourcesMap := make(map[string]*struct{})
// Get all the cluster type kind supported by cluster
if len(policy.Spec.ValidationFailureActionOverrides) > 0 {
return fmt.Errorf("invalid policy: use of ValidationFailureActionOverrides in a Namespace Policy")
}
res, err := client.DiscoveryClient.DiscoveryCache().ServerPreferredResources()
if err != nil {
return err

View file

@ -104,7 +104,14 @@ func (m *pMap) add(policy *kyverno.ClusterPolicy) {
m.Lock()
defer m.Unlock()
enforcePolicy := policy.Spec.ValidationFailureAction == "enforce"
enforcePolicy := policy.Spec.ValidationFailureAction == common.Enforce
for _, k := range policy.Spec.ValidationFailureActionOverrides {
if k.Action == common.Enforce {
enforcePolicy = true
break
}
}
mutateMap := m.nameCacheMap[Mutate]
validateEnforceMap := m.nameCacheMap[ValidateEnforce]
validateAuditMap := m.nameCacheMap[ValidateAudit]

View file

@ -816,6 +816,114 @@ func newNsMutatePolicy(t *testing.T) *kyverno.ClusterPolicy {
return convertPolicyToClusterPolicy(policy)
}
func newValidateAuditPolicy(t *testing.T) *kyverno.ClusterPolicy {
rawPolicy := []byte(`{
"metadata": {
"name": "check-label-app-audit"
},
"spec": {
"background": false,
"rules": [
{
"match": {
"resources": {
"kinds": [
"Pod"
]
}
},
"name": "check-label-app",
"validate": {
"message": "The label 'app' is required.",
"pattern": {
"metadata": {
"labels": {
"app": "?*"
}
}
}
}
}
],
"validationFailureAction": "audit",
"validationFailureActionOverrides": [
{
"action": "enforce",
"namespaces": [
"default"
]
},
{
"action": "audit",
"namespaces": [
"test"
]
}
]
}
}`)
var policy *kyverno.ClusterPolicy
err := json.Unmarshal(rawPolicy, &policy)
assert.NilError(t, err)
return policy
}
func newValidateEnforcePolicy(t *testing.T) *kyverno.ClusterPolicy {
rawPolicy := []byte(`{
"metadata": {
"name": "check-label-app-enforce"
},
"spec": {
"background": false,
"rules": [
{
"match": {
"resources": {
"kinds": [
"Pod"
]
}
},
"name": "check-label-app",
"validate": {
"message": "The label 'app' is required.",
"pattern": {
"metadata": {
"labels": {
"app": "?*"
}
}
}
}
}
],
"validationFailureAction": "enforce",
"validationFailureActionOverrides": [
{
"action": "enforce",
"namespaces": [
"default"
]
},
{
"action": "audit",
"namespaces": [
"test"
]
}
]
}
}`)
var policy *kyverno.ClusterPolicy
err := json.Unmarshal(rawPolicy, &policy)
assert.NilError(t, err)
return policy
}
func Test_Ns_All(t *testing.T) {
pCache := newPolicyCache(log.Log, dummyLister{}, dummyNsLister{})
policy := newNsPolicy(t)
@ -1045,3 +1153,34 @@ func Test_NsMutate_Policy(t *testing.T) {
}
}
func Test_Validate_Enforce_Policy(t *testing.T) {
pCache := newPolicyCache(log.Log, dummyLister{}, dummyNsLister{})
policy1 := newValidateAuditPolicy(t)
policy2 := newValidateEnforcePolicy(t)
pCache.Add(policy1)
pCache.Add(policy2)
validateEnforce := pCache.get(ValidateEnforce, "Pod", "")
if len(validateEnforce) != 2 {
t.Errorf("adding: expected 2 validate enforce policy, found %v", len(validateEnforce))
}
validateAudit := pCache.get(ValidateAudit, "Pod", "")
if len(validateAudit) != 0 {
t.Errorf("adding: expected 0 validate audit policy, found %v", len(validateAudit))
}
pCache.Remove(policy1)
pCache.Remove(policy2)
validateEnforce = pCache.get(ValidateEnforce, "Pod", "")
if len(validateEnforce) != 0 {
t.Errorf("removing: expected 0 validate enforce policy, found %v", len(validateEnforce))
}
validateAudit = pCache.get(ValidateAudit, "Pod", "")
if len(validateAudit) != 0 {
t.Errorf("removing: expected 0 validate audit policy, found %v", len(validateAudit))
}
}

View file

@ -9,6 +9,7 @@ import (
"github.com/kyverno/kyverno/pkg/common"
"github.com/kyverno/kyverno/pkg/engine/response"
engineutils "github.com/kyverno/kyverno/pkg/engine/utils"
"github.com/minio/pkg/wildcard"
yamlv2 "gopkg.in/yaml.v2"
"k8s.io/api/admission/v1beta1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@ -25,11 +26,37 @@ func isResponseSuccessful(engineReponses []*response.EngineResponse) bool {
return true
}
func checkEngineResponse(er *response.EngineResponse) bool {
nsAction := ""
actionOverride := false
for _, v := range er.PolicyResponse.ValidationFailureActionOverrides {
action := v.Action
if action != common.Enforce && action != common.Audit {
continue
}
for _, ns := range v.Namespaces {
if wildcard.Match(ns, er.PatchedResource.GetNamespace()) {
nsAction = action
actionOverride = true
break
}
}
if actionOverride {
break
}
}
return !er.IsSuccessful() && ((actionOverride && nsAction == common.Enforce) || (!actionOverride && er.PolicyResponse.ValidationFailureAction == common.Enforce))
}
// returns true -> if there is even one policy that blocks resource request
// returns false -> if all the policies are meant to report only, we dont block resource request
func toBlockResource(engineReponses []*response.EngineResponse, log logr.Logger) bool {
for _, er := range engineReponses {
if !er.IsSuccessful() && er.PolicyResponse.ValidationFailureAction == common.Enforce {
if checkEngineResponse(er) {
log.Info("spec.ValidationFailureAction set to enforce blocking resource request", "policy", er.PolicyResponse.Policy.Name)
return true
}
@ -44,7 +71,7 @@ func getEnforceFailureErrorMsg(engineResponses []*response.EngineResponse) strin
policyToRule := make(map[string]interface{})
var resourceName string
for _, er := range engineResponses {
if !er.IsSuccessful() && er.PolicyResponse.ValidationFailureAction == common.Enforce {
if checkEngineResponse(er) {
ruleToReason := make(map[string]string)
for _, rule := range er.PolicyResponse.Rules {
if rule.Status != response.RuleStatusPass {

View file

@ -100,6 +100,7 @@ func (v *validationHandler) handleValidation(
// create an event on the resource
events := generateEvents(engineResponses, blocked, (request.Operation == v1beta1.Update), logger)
v.eventGen.Add(events...)
if blocked {
logger.V(4).Info("resource blocked")
//registering the kyverno_admission_review_duration_seconds metric concurrently

View file

@ -0,0 +1,543 @@
package webhooks
import (
"encoding/json"
"fmt"
"testing"
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
log "sigs.k8s.io/controller-runtime/pkg/log"
"github.com/kyverno/kyverno/pkg/engine"
"github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/response"
"github.com/kyverno/kyverno/pkg/engine/utils"
"gotest.tools/assert"
)
func TestValidate_failure_action_overrides(t *testing.T) {
testcases := []struct {
rawPolicy []byte
rawResource []byte
blocked bool
}{
{
rawPolicy: []byte(`
{
"apiVersion": "kyverno.io/v1",
"kind": "ClusterPolicy",
"metadata": {
"name": "check-label-app"
},
"spec": {
"validationFailureAction": "audit",
"validationFailureActionOverrides":
[
{
"action": "enforce",
"namespaces": [
"default"
]
},
{
"action": "audit",
"namespaces": [
"test"
]
}
],
"rules": [
{
"name": "check-label-app",
"match": {
"resources": {
"kinds": [
"Pod"
]
}
},
"validate": {
"message": "The label 'app' is required.",
"pattern": {
"metadata": {
"labels": {
"app": "?*"
}
}
}
}
}
]
}
}
`),
rawResource: []byte(`
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "test-pod",
"namespace": "default"
},
"spec": {
"containers": [
{
"name": "nginx",
"image": "nginx:latest"
}
]
}
}
`),
blocked: true,
},
{
rawPolicy: []byte(`
{
"apiVersion": "kyverno.io/v1",
"kind": "ClusterPolicy",
"metadata": {
"name": "check-label-app"
},
"spec": {
"validationFailureAction": "audit",
"validationFailureActionOverrides":
[
{
"action": "enforce",
"namespaces": [
"default"
]
},
{
"action": "audit",
"namespaces": [
"test"
]
}
],
"rules": [
{
"name": "check-label-app",
"match": {
"resources": {
"kinds": [
"Pod"
]
}
},
"validate": {
"message": "The label 'app' is required.",
"pattern": {
"metadata": {
"labels": {
"app": "?*"
}
}
}
}
}
]
}
}
`),
rawResource: []byte(`
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "test-pod",
"labels": {
"app": "my-app"
}
},
"spec": {
"containers": [
{
"name": "nginx",
"image": "nginx:latest"
}
]
}
}
`),
blocked: false,
},
{
rawPolicy: []byte(`
{
"apiVersion": "kyverno.io/v1",
"kind": "ClusterPolicy",
"metadata": {
"name": "check-label-app"
},
"spec": {
"validationFailureAction": "audit",
"validationFailureActionOverrides":
[
{
"action": "enforce",
"namespaces": [
"default"
]
},
{
"action": "audit",
"namespaces": [
"test"
]
}
],
"rules": [
{
"name": "check-label-app",
"match": {
"resources": {
"kinds": [
"Pod"
]
}
},
"validate": {
"message": "The label 'app' is required.",
"pattern": {
"metadata": {
"labels": {
"app": "?*"
}
}
}
}
}
]
}
}
`),
rawResource: []byte(`
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "test-pod",
"namespace": "test"
},
"spec": {
"containers": [
{
"name": "nginx",
"image": "nginx:latest"
}
]
}
}
`),
blocked: false,
},
{
rawPolicy: []byte(`
{
"apiVersion": "kyverno.io/v1",
"kind": "ClusterPolicy",
"metadata": {
"name": "check-label-app"
},
"spec": {
"validationFailureAction": "enforce",
"validationFailureActionOverrides":
[
{
"action": "enforce",
"namespaces": [
"default"
]
},
{
"action": "audit",
"namespaces": [
"test"
]
}
],
"rules": [
{
"name": "check-label-app",
"match": {
"resources": {
"kinds": [
"Pod"
]
}
},
"validate": {
"message": "The label 'app' is required.",
"pattern": {
"metadata": {
"labels": {
"app": "?*"
}
}
}
}
}
]
}
}
`),
rawResource: []byte(`
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "test-pod",
"namespace": "default"
},
"spec": {
"containers": [
{
"name": "nginx",
"image": "nginx:latest"
}
]
}
}
`),
blocked: true,
},
{
rawPolicy: []byte(`
{
"apiVersion": "kyverno.io/v1",
"kind": "ClusterPolicy",
"metadata": {
"name": "check-label-app"
},
"spec": {
"validationFailureAction": "enforce",
"validationFailureActionOverrides":
[
{
"action": "enforce",
"namespaces": [
"default"
]
},
{
"action": "audit",
"namespaces": [
"test"
]
}
],
"rules": [
{
"name": "check-label-app",
"match": {
"resources": {
"kinds": [
"Pod"
]
}
},
"validate": {
"message": "The label 'app' is required.",
"pattern": {
"metadata": {
"labels": {
"app": "?*"
}
}
}
}
}
]
}
}
`),
rawResource: []byte(`
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "test-pod",
"labels": {
"app": "my-app"
}
},
"spec": {
"containers": [
{
"name": "nginx",
"image": "nginx:latest"
}
]
}
}
`),
blocked: false,
},
{
rawPolicy: []byte(`
{
"apiVersion": "kyverno.io/v1",
"kind": "ClusterPolicy",
"metadata": {
"name": "check-label-app"
},
"spec": {
"validationFailureAction": "enforce",
"validationFailureActionOverrides":
[
{
"action": "enforce",
"namespaces": [
"default"
]
},
{
"action": "audit",
"namespaces": [
"test"
]
}
],
"rules": [
{
"name": "check-label-app",
"match": {
"resources": {
"kinds": [
"Pod"
]
}
},
"validate": {
"message": "The label 'app' is required.",
"pattern": {
"metadata": {
"labels": {
"app": "?*"
}
}
}
}
}
]
}
}
`),
rawResource: []byte(`
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "test-pod",
"namespace": "test"
},
"spec": {
"containers": [
{
"name": "nginx",
"image": "nginx:latest"
}
]
}
}
`),
blocked: false,
},
{
rawPolicy: []byte(`
{
"apiVersion": "kyverno.io/v1",
"kind": "ClusterPolicy",
"metadata": {
"name": "check-label-app"
},
"spec": {
"validationFailureAction": "enforce",
"validationFailureActionOverrides":
[
{
"action": "enforce",
"namespaces": [
"default"
]
},
{
"action": "audit",
"namespaces": [
"test"
]
}
],
"rules": [
{
"name": "check-label-app",
"match": {
"resources": {
"kinds": [
"Pod"
]
}
},
"validate": {
"message": "The label 'app' is required.",
"pattern": {
"metadata": {
"labels": {
"app": "?*"
}
}
}
}
}
]
}
}
`),
rawResource: []byte(`
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "test-pod",
"namespace": ""
},
"spec": {
"containers": [
{
"name": "nginx",
"image": "nginx:latest"
}
]
}
}
`),
blocked: true,
},
}
for i, tc := range testcases {
t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
var policy kyverno.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])
}
}
blocked := toBlockResource([]*response.EngineResponse{er}, log.Log.WithName("WebhookServer"))
assert.Assert(t, tc.blocked == blocked)
})
}
}