1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-30 19:35:06 +00:00

feat: add tests for different values of mutateExistingOnPolicyUpdate (#10797)

Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com>
This commit is contained in:
Mariam Fahmy 2024-08-07 18:06:01 +03:00 committed by GitHub
parent 53e0ccdc25
commit c7122edfa8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 395 additions and 35 deletions

View file

@ -392,10 +392,6 @@ func (m *Mutation) SetPatchStrategicMerge(in apiextensions.JSON) {
m.RawPatchStrategicMerge = ToJSON(in)
}
func (m *Mutation) IsMutateExistingOnPolicyUpdate() *bool {
return m.MutateExistingOnPolicyUpdate
}
// ForEachMutation applies mutation rules to a list of sub-elements by creating a context for each entry in the list and looping over it to apply the specified logic.
type ForEachMutation struct {
// List specifies a JMESPath expression that results in one or more elements

View file

@ -241,13 +241,13 @@ func (s *Spec) BackgroundProcessingEnabled() bool {
return *s.Background
}
// GetMutateExistingOnPolicyUpdate return MutateExistingOnPolicyUpdate set value
// GetMutateExistingOnPolicyUpdate returns true if any of the rules have MutateExistingOnPolicyUpdate set to true
func (s *Spec) GetMutateExistingOnPolicyUpdate() bool {
for _, rule := range s.Rules {
if rule.HasMutate() {
isMutateExisting := rule.Mutation.IsMutateExistingOnPolicyUpdate()
if isMutateExisting != nil {
return *isMutateExisting
isMutateExisting := rule.Mutation.MutateExistingOnPolicyUpdate
if isMutateExisting != nil && *isMutateExisting {
return true
}
}
}
@ -349,22 +349,17 @@ func (s *Spec) validateDeprecatedFields(path *field.Path) (errs field.ErrorList)
errs = append(errs, field.Forbidden(path.Child("generateExisting"), "remove the deprecated field and use spec.generate[*].generateExisting instead"))
}
}
if rule.HasMutate() && rule.Mutation.IsMutateExistingOnPolicyUpdate() != nil {
if s.MutateExistingOnPolicyUpdate {
errs = append(errs, field.Forbidden(path.Child("mutateExistingOnPolicyUpdate"), "remove the deprecated field and use spec.mutate[*].mutateExistingOnPolicyUpdate instead"))
}
}
}
return errs
}
func (s *Spec) validateMutateTargets(path *field.Path) (errs field.ErrorList) {
if s.GetMutateExistingOnPolicyUpdate() {
for i, rule := range s.Rules {
if !rule.HasMutate() {
continue
}
for i, rule := range s.Rules {
if !rule.HasMutate() {
continue
}
mutateExisting := rule.Mutation.MutateExistingOnPolicyUpdate
if s.MutateExistingOnPolicyUpdate || (mutateExisting != nil && *mutateExisting) {
if len(rule.Mutation.Targets) == 0 {
errs = append(errs, field.Forbidden(path.Child("mutateExistingOnPolicyUpdate"), fmt.Sprintf("rules[%v].mutate.targets has to be specified when mutateExistingOnPolicyUpdate is set", i)))
}

View file

@ -210,13 +210,13 @@ func (s *Spec) BackgroundProcessingEnabled() bool {
return *s.Background
}
// GetMutateExistingOnPolicyUpdate return MutateExistingOnPolicyUpdate set value
// GetMutateExistingOnPolicyUpdate returns true if any of the rules have MutateExistingOnPolicyUpdate set to true
func (s *Spec) GetMutateExistingOnPolicyUpdate() bool {
for _, rule := range s.Rules {
if rule.HasMutate() {
isMutateExisting := rule.Mutation.IsMutateExistingOnPolicyUpdate()
if isMutateExisting != nil {
return *isMutateExisting
isMutateExisting := rule.Mutation.MutateExistingOnPolicyUpdate
if isMutateExisting != nil && *isMutateExisting {
return true
}
}
}
@ -309,10 +309,19 @@ func (s *Spec) ValidateDeprecatedFields(path *field.Path) (errs field.ErrorList)
errs = append(errs, field.Forbidden(path.Child("generateExisting"), "remove the deprecated field and use spec.generate[*].generateExisting instead"))
}
}
}
return errs
}
if rule.HasMutate() && rule.Mutation.IsMutateExistingOnPolicyUpdate() != nil {
if s.MutateExistingOnPolicyUpdate {
errs = append(errs, field.Forbidden(path.Child("mutateExistingOnPolicyUpdate"), "remove the deprecated field and use spec.mutate[*].mutateExistingOnPolicyUpdate instead"))
func (s *Spec) validateMutateTargets(path *field.Path) (errs field.ErrorList) {
for i, rule := range s.Rules {
if !rule.HasMutate() {
continue
}
mutateExisting := rule.Mutation.MutateExistingOnPolicyUpdate
if s.MutateExistingOnPolicyUpdate || (mutateExisting != nil && *mutateExisting) {
if len(rule.Mutation.Targets) == 0 {
errs = append(errs, field.Forbidden(path.Child("mutateExistingOnPolicyUpdate"), fmt.Sprintf("rules[%v].mutate.targets has to be specified when mutateExistingOnPolicyUpdate is set", i)))
}
}
}
@ -324,6 +333,9 @@ func (s *Spec) Validate(path *field.Path, namespaced bool, policyNamespace strin
if err := s.ValidateDeprecatedFields(path); err != nil {
errs = append(errs, err...)
}
if err := s.validateMutateTargets(path); err != nil {
errs = append(errs, err...)
}
if s.WebhookTimeoutSeconds != nil && (*s.WebhookTimeoutSeconds < 1 || *s.WebhookTimeoutSeconds > 30) {
errs = append(errs, field.Invalid(path.Child("webhookTimeoutSeconds"), s.WebhookTimeoutSeconds, "the timeout value must be between 1 and 30 seconds"))
}

View file

@ -15,14 +15,24 @@ func (pc *policyController) handleMutate(policyKey string, policy kyvernov1.Poli
logger.Info("update URs on policy event")
ruleType := kyvernov2.Mutate
spec := policy.GetSpec()
policyNew := policy.CreateDeepCopy()
policyNew.GetSpec().Rules = nil
for _, rule := range policy.GetSpec().Rules {
for _, rule := range spec.Rules {
if !rule.HasMutateExisting() {
continue
}
mutateExisting := rule.Mutation.MutateExistingOnPolicyUpdate
if mutateExisting != nil {
if !*mutateExisting {
continue
}
} else if !spec.MutateExistingOnPolicyUpdate {
continue
}
policyNew.GetSpec().SetRules([]kyvernov1.Rule{rule})
triggers := getTriggers(pc.client, rule, policyNew.IsNamespaced(), policyNew.GetNamespace(), pc.log)
for _, trigger := range triggers {

View file

@ -132,7 +132,6 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
var warnings []string
spec := policy.GetSpec()
background := spec.BackgroundProcessingEnabled()
mutateExistingOnPolicyUpdate := spec.GetMutateExistingOnPolicyUpdate()
if policy.GetSpec().CustomWebhookMatchConditions() &&
!kubeutils.HigherThanKubernetesVersion(client.GetKubeClient().Discovery(), logging.GlobalLogger(), 1, 27, 0) {
return warnings, fmt.Errorf("custom webhook configurations are only supported in kubernetes version 1.27.0 and above")
@ -154,13 +153,6 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
return warnings, err
}
if mutateExistingOnPolicyUpdate {
err := ValidateOnPolicyUpdate(policy, mutateExistingOnPolicyUpdate)
if err != nil {
return warnings, err
}
}
getClusteredResources := func(invalidate bool) (sets.Set[string], error) {
clusterResources := sets.New[string]()
// Get all the cluster type kind supported by cluster
@ -417,6 +409,19 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
}
checkForScaleSubresource(mutationJson, allKinds, &warnings)
checkForStatusSubresource(mutationJson, allKinds, &warnings)
mutateExisting := rule.Mutation.MutateExistingOnPolicyUpdate
if mutateExisting != nil {
if *mutateExisting {
if err := ValidateOnPolicyUpdate(policy, true); err != nil {
return warnings, err
}
}
} else if spec.MutateExistingOnPolicyUpdate {
if err := ValidateOnPolicyUpdate(policy, true); err != nil {
return warnings, err
}
}
}
if rule.HasVerifyImages() {

View file

@ -0,0 +1,22 @@
## Description
This test ensures that a mutate policy of two rules; one of which sets the `mutateExistingOnPolicyUpdate` while the other doesn't, works as expected.
## Expected Behavior
1. Create two Namespaces `staging-2` and `staging-3`.
2. Create two Secrets `test-secret-2` and `test-secret-3` in Namespaces `staging-2` and `staging-3` respectively.
3. Create two ConfigMaps `dictionary-2` and `dictionary-3` in Namespaces `staging-2` and `staging-3` respectively.
4. Create a ClusterPolicy with two mutate rules:
- The first rule matches a ConfigMap named `dictionary-3` in Namespace `staging-3` and doesn't set the `mutateExistingOnPolicyUpdate`. Its target is to mutate a Secret named `test-secret-3` in Namespace `staging-3`. In this case, the rule will take the value from `spec.mutateExistingOnPolicyUpdate` field.
- The second rule matches a ConfigMap named `dictionary-2` in Namespace `staging-2` and sets the value of `mutateExistingOnPolicyUpdate` to `false`. Its target is to mutate a Secret named `test-secret-2` in Namespace `staging-2`.
5. On policy creation, the Secret `test-secret-3` in Namespace `staging-3` should be mutated whereas the Secret `test-secret-2` in Namespace `staging-2` should not be mutated.
## Reference Issue(s)
N/A

View file

@ -0,0 +1,29 @@
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
creationTimestamp: null
name: different-configurations-for-mutate-existing
spec:
steps:
- name: step-01
try:
- apply:
file: namespaces.yaml
- apply:
file: configmaps.yaml
- apply:
file: secrets.yaml
- name: step-02
try:
- apply:
file: policy.yaml
- assert:
file: policy-ready.yaml
- name: step-03
try:
- assert:
file: mutated-secret.yaml
- name: step-04
try:
- error:
file: mutated-secret-error.yaml

View file

@ -0,0 +1,15 @@
apiVersion: v1
data:
foo: bar
kind: ConfigMap
metadata:
name: dictionary-3
namespace: staging-3
---
apiVersion: v1
data:
foo: bar
kind: ConfigMap
metadata:
name: dictionary-2
namespace: staging-2

View file

@ -0,0 +1,7 @@
apiVersion: v1
kind: Secret
metadata:
labels:
foo: dictionary-2
name: test-secret-2
namespace: staging-2

View file

@ -0,0 +1,7 @@
apiVersion: v1
kind: Secret
metadata:
labels:
foo: dictionary-3
name: test-secret-3
namespace: staging-3

View file

@ -0,0 +1,17 @@
apiVersion: v1
kind: Namespace
metadata:
annotations:
cloud.platformzero.com/serviceClass: xl2
labels:
app-type: corp
name: staging-3
---
apiVersion: v1
kind: Namespace
metadata:
annotations:
cloud.platformzero.com/serviceClass: xl2
labels:
app-type: corp
name: staging-2

View file

@ -0,0 +1,9 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: test-post-mutation-create-policy
status:
conditions:
- reason: Succeeded
status: "True"
type: Ready

View file

@ -0,0 +1,48 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: test-post-mutation-create-policy
spec:
mutateExistingOnPolicyUpdate: true
rules:
- match:
any:
- resources:
kinds:
- ConfigMap
names:
- dictionary-3
namespaces:
- staging-3
mutate:
patchStrategicMerge:
metadata:
labels:
foo: '{{ request.object.metadata.name }}'
targets:
- apiVersion: v1
kind: Secret
name: test-secret-3
namespace: '{{ request.object.metadata.namespace }}'
name: mutate-secret-on-policy-create
- match:
any:
- resources:
kinds:
- ConfigMap
names:
- dictionary-2
namespaces:
- staging-2
mutate:
mutateExistingOnPolicyUpdate: false
patchStrategicMerge:
metadata:
labels:
foo: '{{ request.object.metadata.name }}'
targets:
- apiVersion: v1
kind: Secret
name: test-secret-2
namespace: '{{ request.object.metadata.namespace }}'
name: disable-mutate-existing

View file

@ -0,0 +1,17 @@
apiVersion: v1
data:
foo: YmFy
kind: Secret
metadata:
name: test-secret-3
namespace: staging-3
type: Opaque
---
apiVersion: v1
data:
foo: YmFy
kind: Secret
metadata:
name: test-secret-2
namespace: staging-2
type: Opaque

View file

@ -0,0 +1,22 @@
## Description
This test ensures that a mutate policy of two rules with different values of `mutateExistingOnPolicyUpdate` works as expected.
## Expected Behavior
1. Create two Namespaces `staging-2` and `staging-3`.
2. Create two Secrets `test-secret-2` and `test-secret-3` in Namespaces `staging-2` and `staging-3` respectively.
3. Create two ConfigMaps `dictionary-2` and `dictionary-3` in Namespaces `staging-2` and `staging-3` respectively.
4. Create a ClusterPolicy with two mutate rules:
- The first rule matches a ConfigMap named `dictionary-3` in Namespace `staging-3` and sets the value of `mutateExistingOnPolicyUpdate` to `false`. Its target is to mutate a Secret named `test-secret-3` in Namespace `staging-3`.
- The second rule matches a ConfigMap named `dictionary-2` in Namespace `staging-2` and sets the value of `mutateExistingOnPolicyUpdate` to `false`. Its target is to mutate a Secret named `test-secret-2` in Namespace `staging-2`.
5. On policy creation, the Secret `test-secret-3` in Namespace `staging-3` should be mutated whereas the Secret `test-secret-2` in Namespace `staging-2` should not be mutated.
## Reference Issue(s)
N/A

View file

@ -0,0 +1,29 @@
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
creationTimestamp: null
name: different-mutate-existing-values
spec:
steps:
- name: step-01
try:
- apply:
file: namespaces.yaml
- apply:
file: configmaps.yaml
- apply:
file: secrets.yaml
- name: step-02
try:
- apply:
file: policy.yaml
- assert:
file: policy-ready.yaml
- name: step-03
try:
- assert:
file: mutated-secret.yaml
- name: step-04
try:
- error:
file: mutated-secret-error.yaml

View file

@ -0,0 +1,15 @@
apiVersion: v1
data:
foo: bar
kind: ConfigMap
metadata:
name: dictionary-3
namespace: staging-3
---
apiVersion: v1
data:
foo: bar
kind: ConfigMap
metadata:
name: dictionary-2
namespace: staging-2

View file

@ -0,0 +1,7 @@
apiVersion: v1
kind: Secret
metadata:
labels:
foo: dictionary-2
name: test-secret-2
namespace: staging-2

View file

@ -0,0 +1,7 @@
apiVersion: v1
kind: Secret
metadata:
labels:
foo: dictionary-3
name: test-secret-3
namespace: staging-3

View file

@ -0,0 +1,17 @@
apiVersion: v1
kind: Namespace
metadata:
annotations:
cloud.platformzero.com/serviceClass: xl2
labels:
app-type: corp
name: staging-3
---
apiVersion: v1
kind: Namespace
metadata:
annotations:
cloud.platformzero.com/serviceClass: xl2
labels:
app-type: corp
name: staging-2

View file

@ -0,0 +1,9 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: test-post-mutation-create-policy
status:
conditions:
- reason: Succeeded
status: "True"
type: Ready

View file

@ -0,0 +1,48 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: test-post-mutation-create-policy
spec:
rules:
- match:
any:
- resources:
kinds:
- ConfigMap
names:
- dictionary-3
namespaces:
- staging-3
mutate:
mutateExistingOnPolicyUpdate: true
patchStrategicMerge:
metadata:
labels:
foo: '{{ request.object.metadata.name }}'
targets:
- apiVersion: v1
kind: Secret
name: test-secret-3
namespace: '{{ request.object.metadata.namespace }}'
name: mutate-secret-on-policy-create
- match:
any:
- resources:
kinds:
- ConfigMap
names:
- dictionary-2
namespaces:
- staging-2
mutate:
mutateExistingOnPolicyUpdate: false
patchStrategicMerge:
metadata:
labels:
foo: '{{ request.object.metadata.name }}'
targets:
- apiVersion: v1
kind: Secret
name: test-secret-2
namespace: '{{ request.object.metadata.namespace }}'
name: disable-mutate-existing

View file

@ -0,0 +1,17 @@
apiVersion: v1
data:
foo: YmFy
kind: Secret
metadata:
name: test-secret-3
namespace: staging-3
type: Opaque
---
apiVersion: v1
data:
foo: YmFy
kind: Secret
metadata:
name: test-secret-2
namespace: staging-2
type: Opaque