mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-15 17:51:20 +00:00
feat: support namespaced parameter resources for CEL expressions in Kyverno policies (#8084)
* feat: support namespaced parameter resources for CEL expressions in Kyverno policies Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com> * fix lint issue Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com> * fix Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com> * fix kuttl test Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com> --------- Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com>
This commit is contained in:
parent
0f9fe30c08
commit
94aa1f18c6
56 changed files with 501 additions and 41 deletions
|
@ -50,6 +50,21 @@ func (a *dclientAdapter) GetNamespace(ctx context.Context, name string, opts met
|
|||
return a.client.GetKubeClient().CoreV1().Namespaces().Get(ctx, name, opts)
|
||||
}
|
||||
|
||||
func (a *dclientAdapter) ListResource(ctx context.Context, apiVersion string, kind string, namespace string, lselector *metav1.LabelSelector) (*unstructured.UnstructuredList, error) {
|
||||
return a.client.ListResource(ctx, apiVersion, kind, namespace, lselector)
|
||||
}
|
||||
|
||||
func (a *dclientAdapter) IsNamespaced(group, version, kind string) (bool, error) {
|
||||
gvrss, err := a.client.Discovery().FindResources(group, version, kind, "")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, apiResource := range gvrss {
|
||||
return apiResource.Namespaced, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (a *dclientAdapter) CanI(ctx context.Context, kind, namespace, verb, subresource, user string) (bool, error) {
|
||||
canI := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, verb, subresource, user)
|
||||
ok, err := canI.RunAccessCheck(ctx)
|
||||
|
|
|
@ -30,8 +30,10 @@ type AuthClient interface {
|
|||
|
||||
type ResourceClient interface {
|
||||
GetResource(ctx context.Context, apiVersion, kind, namespace, name string, subresources ...string) (*unstructured.Unstructured, error)
|
||||
ListResource(ctx context.Context, apiVersion string, kind string, namespace string, lselector *metav1.LabelSelector) (*unstructured.UnstructuredList, error)
|
||||
GetResources(ctx context.Context, group, version, kind, subresource, namespace, name string) ([]Resource, error)
|
||||
GetNamespace(ctx context.Context, name string, opts metav1.GetOptions) (*corev1.Namespace, error)
|
||||
IsNamespaced(group, version, kind string) (bool, error)
|
||||
}
|
||||
|
||||
type Client interface {
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/kyverno/kyverno/pkg/engine/handlers"
|
||||
engineutils "github.com/kyverno/kyverno/pkg/engine/utils"
|
||||
celutils "github.com/kyverno/kyverno/pkg/utils/cel"
|
||||
admissionregistrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
@ -53,7 +54,7 @@ func (h validateCELHandler) Process(
|
|||
|
||||
object := resource.DeepCopyObject()
|
||||
// in case of update request, set the oldObject to the current resource before it gets updated
|
||||
var oldObject, versionedParams runtime.Object
|
||||
var oldObject runtime.Object
|
||||
oldResource := policyContext.OldResource()
|
||||
if oldResource.Object == nil {
|
||||
oldObject = nil
|
||||
|
@ -70,31 +71,7 @@ func (h validateCELHandler) Process(
|
|||
validations := rule.Validation.CEL.Expressions
|
||||
auditAnnotations := rule.Validation.CEL.AuditAnnotations
|
||||
|
||||
// get the parameter resource if exists
|
||||
if hasParam && h.client != nil {
|
||||
paramKind := rule.Validation.CEL.GetParamKind()
|
||||
paramRef := rule.Validation.CEL.GetParamRef()
|
||||
|
||||
apiVersion := paramKind.APIVersion
|
||||
kind := paramKind.Kind
|
||||
|
||||
name := paramRef.Name
|
||||
namespace := paramRef.Namespace
|
||||
|
||||
if namespace == "" {
|
||||
namespace = "default"
|
||||
}
|
||||
|
||||
paramResource, err := h.client.GetResource(ctx, apiVersion, kind, namespace, name, "")
|
||||
if err != nil {
|
||||
return resource, handlers.WithError(rule, engineapi.Validation, "Error while getting the parameterized resource", err)
|
||||
}
|
||||
|
||||
versionedParams = paramResource.DeepCopyObject()
|
||||
}
|
||||
|
||||
optionalVars := cel.OptionalVariableDeclarations{HasParams: hasParam, HasAuthorizer: false}
|
||||
|
||||
// compile CEL expressions
|
||||
compiler, err := celutils.NewCompiler(validations, auditAnnotations, matchConditions, variables)
|
||||
if err != nil {
|
||||
|
@ -129,20 +106,39 @@ func (h validateCELHandler) Process(
|
|||
admissionAttributes := admission.NewAttributesRecord(object, oldObject, gvk, namespaceName, resourceName, gvr, "", admission.Operation(policyContext.Operation()), nil, false, nil)
|
||||
versionedAttr, _ := admission.NewVersionedAttributes(admissionAttributes, admissionAttributes.GetKind(), nil)
|
||||
// validate the incoming object against the rule
|
||||
validateResult := validator.Validate(ctx, gvr, versionedAttr, versionedParams, namespace, celconfig.RuntimeCELCostBudget, nil)
|
||||
var validationResults []validatingadmissionpolicy.ValidateResult
|
||||
if hasParam {
|
||||
paramKind := rule.Validation.CEL.ParamKind
|
||||
paramRef := rule.Validation.CEL.ParamRef
|
||||
|
||||
for _, decision := range validateResult.Decisions {
|
||||
switch decision.Action {
|
||||
case validatingadmissionpolicy.ActionAdmit:
|
||||
if decision.Evaluation == validatingadmissionpolicy.EvalError {
|
||||
params, err := collectParams(ctx, h.client, paramKind, paramRef, namespaceName)
|
||||
if err != nil {
|
||||
return resource, handlers.WithResponses(
|
||||
engineapi.RuleError(rule.Name, engineapi.Validation, "error in parameterized resource", err),
|
||||
)
|
||||
}
|
||||
|
||||
for _, param := range params {
|
||||
validationResults = append(validationResults, validator.Validate(ctx, gvr, versionedAttr, param, namespace, celconfig.RuntimeCELCostBudget, nil))
|
||||
}
|
||||
} else {
|
||||
validationResults = append(validationResults, validator.Validate(ctx, gvr, versionedAttr, nil, namespace, celconfig.RuntimeCELCostBudget, nil))
|
||||
}
|
||||
|
||||
for _, validationResult := range validationResults {
|
||||
for _, decision := range validationResult.Decisions {
|
||||
switch decision.Action {
|
||||
case validatingadmissionpolicy.ActionAdmit:
|
||||
if decision.Evaluation == validatingadmissionpolicy.EvalError {
|
||||
return resource, handlers.WithResponses(
|
||||
engineapi.RuleError(rule.Name, engineapi.Validation, decision.Message, nil),
|
||||
)
|
||||
}
|
||||
case validatingadmissionpolicy.ActionDeny:
|
||||
return resource, handlers.WithResponses(
|
||||
engineapi.RuleError(rule.Name, engineapi.Validation, decision.Message, nil),
|
||||
engineapi.RuleFail(rule.Name, engineapi.Validation, decision.Message),
|
||||
)
|
||||
}
|
||||
case validatingadmissionpolicy.ActionDeny:
|
||||
return resource, handlers.WithResponses(
|
||||
engineapi.RuleFail(rule.Name, engineapi.Validation, decision.Message),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,3 +147,61 @@ func (h validateCELHandler) Process(
|
|||
engineapi.RulePass(rule.Name, engineapi.Validation, msg),
|
||||
)
|
||||
}
|
||||
|
||||
func collectParams(ctx context.Context, client engineapi.Client, paramKind *admissionregistrationv1alpha1.ParamKind, paramRef *admissionregistrationv1alpha1.ParamRef, namespace string) ([]runtime.Object, error) {
|
||||
var params []runtime.Object
|
||||
|
||||
apiVersion := paramKind.APIVersion
|
||||
kind := paramKind.Kind
|
||||
gv, err := schema.ParseGroupVersion(apiVersion)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't parse the parameter resource group version")
|
||||
}
|
||||
|
||||
// If `paramKind` is cluster-scoped, then paramRef.namespace MUST be unset.
|
||||
// If `paramKind` is namespace-scoped, the namespace of the object being evaluated for admission will be used
|
||||
// when paramRef.namespace is left unset.
|
||||
var paramsNamespace string
|
||||
isNamespaced, err := client.IsNamespaced(gv.Group, gv.Version, kind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// check if `paramKind` is namespace-scoped
|
||||
if isNamespaced {
|
||||
// set params namespace to the incoming object's namespace by default.
|
||||
paramsNamespace = namespace
|
||||
if paramRef.Namespace != "" {
|
||||
paramsNamespace = paramRef.Namespace
|
||||
} else if paramsNamespace == "" {
|
||||
return nil, fmt.Errorf("can't use namespaced paramRef to match cluster-scoped resources")
|
||||
}
|
||||
} else {
|
||||
// It isn't allowed to set namespace for cluster-scoped params
|
||||
if paramRef.Namespace != "" {
|
||||
return nil, fmt.Errorf("paramRef.namespace must not be provided for a cluster-scoped `paramKind`")
|
||||
}
|
||||
}
|
||||
|
||||
if paramRef.Name != "" {
|
||||
param, err := client.GetResource(ctx, apiVersion, kind, paramsNamespace, paramRef.Name, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []runtime.Object{param}, nil
|
||||
} else if paramRef.Selector != nil {
|
||||
paramList, err := client.ListResource(ctx, apiVersion, kind, paramsNamespace, paramRef.Selector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range paramList.Items {
|
||||
params = append(params, ¶mList.Items[i])
|
||||
}
|
||||
}
|
||||
|
||||
if len(params) == 0 && paramRef.ParameterNotFoundAction != nil && *paramRef.ParameterNotFoundAction == admissionregistrationv1alpha1.DenyAction {
|
||||
return nil, fmt.Errorf("no params found")
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
|
|
@ -90,8 +90,16 @@ func (v *Validate) Validate(ctx context.Context) (string, error) {
|
|||
}
|
||||
|
||||
if v.rule.CEL.ParamRef != nil {
|
||||
if v.rule.CEL.ParamRef.Name == "" {
|
||||
return "", fmt.Errorf("cel.paramRef.name is required")
|
||||
if v.rule.CEL.ParamRef.Name == "" && v.rule.CEL.ParamRef.Selector == nil {
|
||||
return "", fmt.Errorf("one of cel.paramRef.name or cel.paramRef.selector must be set")
|
||||
}
|
||||
|
||||
if v.rule.CEL.ParamRef.Name != "" && v.rule.CEL.ParamRef.Selector != nil {
|
||||
return "", fmt.Errorf("one of cel.paramRef.name or cel.paramRef.selector must be set")
|
||||
}
|
||||
|
||||
if v.rule.CEL.ParamRef.ParameterNotFoundAction == nil {
|
||||
return "", fmt.Errorf("cel.paramRef.parameterNotFoundAction is required")
|
||||
}
|
||||
|
||||
if v.rule.CEL.ParamKind == nil {
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
apply:
|
||||
- crd.yaml
|
||||
assert:
|
||||
- crd-assert.yaml
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
apply:
|
||||
- namespaceConstraint.yaml
|
||||
assert:
|
||||
- namespaceConstraint.yaml
|
|
@ -0,0 +1,7 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
apply:
|
||||
- file: ns-pass.yaml
|
||||
shouldFail: false
|
||||
- file: ns-fail.yaml
|
||||
shouldFail: true
|
|
@ -0,0 +1,12 @@
|
|||
## Description
|
||||
|
||||
This test validates the use of parameter resources in validate.cel subrule.
|
||||
|
||||
This test creates the following:
|
||||
1. A cluster-scoped custom resource definition `NamespaceConstraint`
|
||||
3. A policy that checks the namespace name using the parameter resource.
|
||||
4. Two namespaces.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
The namespace `testing-ns` is blocked, and the namespace `production-ns` is created.
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: namespaceconstraints.rules.example.com
|
|
@ -0,0 +1,26 @@
|
|||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: namespaceconstraints.rules.example.com
|
||||
spec:
|
||||
group: rules.example.com
|
||||
names:
|
||||
kind: NamespaceConstraint
|
||||
plural: namespaceconstraints
|
||||
scope: Cluster
|
||||
versions:
|
||||
- name: v1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
properties:
|
||||
apiVersion:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
name:
|
||||
type: string
|
|
@ -0,0 +1,5 @@
|
|||
apiVersion: rules.example.com/v1
|
||||
kind: NamespaceConstraint
|
||||
metadata:
|
||||
name: "namespace-constraint-test.example.com"
|
||||
name: "production-ns"
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: testing-ns
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: production-ns
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: check-namespace-name
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
|
@ -0,0 +1,25 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: check-namespace-name
|
||||
spec:
|
||||
validationFailureAction: Enforce
|
||||
background: false
|
||||
rules:
|
||||
- name: namespace-name
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Namespace
|
||||
validate:
|
||||
cel:
|
||||
paramKind:
|
||||
apiVersion: rules.example.com/v1
|
||||
kind: NamespaceConstraint
|
||||
paramRef:
|
||||
name: "namespace-constraint-test.example.com"
|
||||
parameterNotFoundAction: "Deny"
|
||||
expressions:
|
||||
- expression: "object.metadata.name == params.name"
|
||||
messageExpression: "'Namespace name must be ' + params.name"
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
apply:
|
||||
- crd.yaml
|
||||
assert:
|
||||
- crd-assert.yaml
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
apply:
|
||||
- nameConstraint.yaml
|
||||
assert:
|
||||
- nameConstraint.yaml
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
apply:
|
||||
- policy.yaml
|
||||
assert:
|
||||
- policy-assert.yaml
|
|
@ -0,0 +1,5 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
apply:
|
||||
- file: ns.yaml
|
||||
shouldFail: true
|
|
@ -0,0 +1,12 @@
|
|||
## Description
|
||||
|
||||
This test validates the use of parameter resources in validate.cel subrule.
|
||||
|
||||
This test creates the following:
|
||||
1. A namespaced custom resource definition `NameConstraint`
|
||||
3. A policy that checks the namespace name using the parameter resource.
|
||||
4. A namespace `testing`.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Since the parameter resource is namespaced-scope and the policy matches cluster-scoped resource `Namespace`, therefore the creation of a namespace is blocked
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: nameconstraints.rules.example.com
|
|
@ -0,0 +1,26 @@
|
|||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: nameconstraints.rules.example.com
|
||||
spec:
|
||||
group: rules.example.com
|
||||
names:
|
||||
kind: NameConstraint
|
||||
plural: nameconstraints
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
properties:
|
||||
apiVersion:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
name:
|
||||
type: string
|
|
@ -0,0 +1,5 @@
|
|||
apiVersion: rules.example.com/v1
|
||||
kind: NameConstraint
|
||||
metadata:
|
||||
name: "name-constraint-test.example.com"
|
||||
name: "default"
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: testing
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: check-namespace-name
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
|
@ -0,0 +1,25 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: check-namespace-name
|
||||
spec:
|
||||
validationFailureAction: Enforce
|
||||
background: false
|
||||
rules:
|
||||
- name: namespace-name
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Namespace
|
||||
validate:
|
||||
cel:
|
||||
paramKind:
|
||||
apiVersion: rules.example.com/v1
|
||||
kind: NameConstraint
|
||||
paramRef:
|
||||
name: "name-constraint-test.example.com"
|
||||
parameterNotFoundAction: "Deny"
|
||||
expressions:
|
||||
- expression: "object.metadata.name == params.name"
|
||||
messageExpression: "'Namespace name must be ' + params.name"
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
apply:
|
||||
- policy.yaml
|
||||
assert:
|
||||
- policy-assert.yaml
|
|
@ -4,8 +4,8 @@ This test validates the use of parameter resources in validate.cel subrule.
|
|||
|
||||
This test creates the following:
|
||||
1. A namespace `test-params`
|
||||
2. A custom resource definition `ReplicaLimit`
|
||||
3. A policy that checks the deployment replicas using the parameter resource.
|
||||
2. A namespaced custom resource definition `ReplicaLimit`
|
||||
3. A policy that checks the deployment replicas using the parameter resource. The `validate.cel.paramRef.namespace` is set.
|
||||
4. Two deployments.
|
||||
|
||||
## Expected Behavior
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: testing
|
|
@ -18,8 +18,9 @@ spec:
|
|||
apiVersion: rules.example.com/v1
|
||||
kind: ReplicaLimit
|
||||
paramRef:
|
||||
name: "replica-limit-test.example.com"
|
||||
namespace: "test-params"
|
||||
name: "replica-limit"
|
||||
namespace: "testing"
|
||||
parameterNotFoundAction: "Deny"
|
||||
expressions:
|
||||
- expression: "object.spec.replicas <= params.maxReplicas"
|
||||
messageExpression: "'Deployment spec.replicas must be less than ' + string(params.maxReplicas)"
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: rules.example.com/v1
|
||||
kind: ReplicaLimit
|
||||
metadata:
|
||||
name: "replica-limit"
|
||||
namespace: testing
|
||||
maxReplicas: 3
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
apply:
|
||||
- ns.yaml
|
||||
assert:
|
||||
- ns.yaml
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
apply:
|
||||
- crd.yaml
|
||||
assert:
|
||||
- crd-assert.yaml
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
apply:
|
||||
- replicaLimit.yaml
|
||||
assert:
|
||||
- replicaLimit.yaml
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
apply:
|
||||
- policy.yaml
|
||||
assert:
|
||||
- policy-assert.yaml
|
|
@ -0,0 +1,7 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
apply:
|
||||
- file: statefulset-pass.yaml
|
||||
shouldFail: false
|
||||
- file: statefulset-fail.yaml
|
||||
shouldFail: true
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
commands:
|
||||
- command: sleep 3
|
|
@ -0,0 +1,13 @@
|
|||
## Description
|
||||
|
||||
This test validates the use of parameter resources in validate.cel subrule.
|
||||
|
||||
This test creates the following:
|
||||
1. A namespace `test-params`
|
||||
2. A namespaced custom resource definition `ReplicaLimit`
|
||||
3. A policy that checks the statefulset replicas using the parameter resource. The `validate.cel.paramRef.namespace` is unset so it is expected to retrieve the parameter resource from the statefulset's namespace
|
||||
4. Two statefulsets.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
The statefulset `statefulset-fail` is blocked, and the statefulset `statefulset-pass` is created.
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: replicalimits.rules.example.com
|
|
@ -0,0 +1,26 @@
|
|||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: replicalimits.rules.example.com
|
||||
spec:
|
||||
group: rules.example.com
|
||||
names:
|
||||
kind: ReplicaLimit
|
||||
plural: replicalimits
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
properties:
|
||||
apiVersion:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
maxReplicas:
|
||||
type: integer
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: check-statefulset-replicas
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
|
@ -0,0 +1,25 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: check-statefulset-replicas
|
||||
spec:
|
||||
validationFailureAction: Enforce
|
||||
background: false
|
||||
rules:
|
||||
- name: statefulset-replicas
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- StatefulSet
|
||||
validate:
|
||||
cel:
|
||||
paramKind:
|
||||
apiVersion: rules.example.com/v1
|
||||
kind: ReplicaLimit
|
||||
paramRef:
|
||||
name: "replica-limit-test.example.com"
|
||||
parameterNotFoundAction: "Deny"
|
||||
expressions:
|
||||
- expression: "object.spec.replicas <= params.maxReplicas"
|
||||
messageExpression: "'StatefulSet spec.replicas must be less than ' + string(params.maxReplicas)"
|
|
@ -0,0 +1,18 @@
|
|||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: statefulset-fail
|
||||
namespace: test-params
|
||||
spec:
|
||||
replicas: 4
|
||||
selector:
|
||||
matchLabels:
|
||||
app: app
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: app
|
||||
spec:
|
||||
containers:
|
||||
- name: container2
|
||||
image: nginx
|
|
@ -0,0 +1,18 @@
|
|||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: statefulset-pass
|
||||
namespace: test-params
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: app
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: app
|
||||
spec:
|
||||
containers:
|
||||
- name: container2
|
||||
image: nginx
|
Loading…
Reference in a new issue