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:
parent
53e0ccdc25
commit
c7122edfa8
23 changed files with 395 additions and 35 deletions
|
@ -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
|
||||
|
|
|
@ -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)))
|
||||
}
|
||||
|
|
|
@ -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"))
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,7 @@
|
|||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
labels:
|
||||
foo: dictionary-2
|
||||
name: test-secret-2
|
||||
namespace: staging-2
|
|
@ -0,0 +1,7 @@
|
|||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
labels:
|
||||
foo: dictionary-3
|
||||
name: test-secret-3
|
||||
namespace: staging-3
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,7 @@
|
|||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
labels:
|
||||
foo: dictionary-2
|
||||
name: test-secret-2
|
||||
namespace: staging-2
|
|
@ -0,0 +1,7 @@
|
|||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
labels:
|
||||
foo: dictionary-3
|
||||
name: test-secret-3
|
||||
namespace: staging-3
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Add table
Reference in a new issue