1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-05 15:37:19 +00:00

fix: remove timestamp checks for the clone rule (#6439)

* remove timestamp checks

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* add kuttl tests

Signed-off-by: ShutingZhao <shuting@nirmata.com>

---------

Signed-off-by: ShutingZhao <shuting@nirmata.com>
This commit is contained in:
shuting 2023-03-01 23:49:05 +08:00 committed by GitHub
parent f4f729623d
commit 264eaec049
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 163 additions and 55 deletions

View file

@ -1124,7 +1124,7 @@ func handleGeneratePolicy(generateResponse *engineapi.EngineResponse, policyCont
var newRuleResponse []engineapi.RuleResponse var newRuleResponse []engineapi.RuleResponse
for _, rule := range generateResponse.PolicyResponse.Rules { for _, rule := range generateResponse.PolicyResponse.Rules {
genResource, _, err := c.ApplyGeneratePolicy(log.Log, &policyContext, gr, []string{rule.Name}) genResource, err := c.ApplyGeneratePolicy(log.Log, &policyContext, gr, []string{rule.Name})
if err != nil { if err != nil {
rule.Status = engineapi.RuleStatusError rule.Status = engineapi.RuleStatusError
return nil, err return nil, err

View file

@ -21,25 +21,25 @@ func NewBackgroundContext(dclient dclient.Interface, ur *kyvernov1beta1.UpdateRe
cfg config.Configuration, cfg config.Configuration,
namespaceLabels map[string]string, namespaceLabels map[string]string,
logger logr.Logger, logger logr.Logger,
) (*engine.PolicyContext, bool, error) { ) (*engine.PolicyContext, error) {
ctx := context.NewContext() ctx := context.NewContext()
var new, old unstructured.Unstructured var new, old unstructured.Unstructured
var err error var err error
if ur.Spec.Context.AdmissionRequestInfo.AdmissionRequest != nil { if ur.Spec.Context.AdmissionRequestInfo.AdmissionRequest != nil {
if err := ctx.AddRequest(ur.Spec.Context.AdmissionRequestInfo.AdmissionRequest); err != nil { if err := ctx.AddRequest(ur.Spec.Context.AdmissionRequestInfo.AdmissionRequest); err != nil {
return nil, false, fmt.Errorf("failed to load request in context: %w", err) return nil, fmt.Errorf("failed to load request in context: %w", err)
} }
new, old, err = admissionutils.ExtractResources(nil, ur.Spec.Context.AdmissionRequestInfo.AdmissionRequest) new, old, err = admissionutils.ExtractResources(nil, ur.Spec.Context.AdmissionRequestInfo.AdmissionRequest)
if err != nil { if err != nil {
return nil, false, fmt.Errorf("failed to load request in context: %w", err) return nil, fmt.Errorf("failed to load request in context: %w", err)
} }
if !reflect.DeepEqual(new, unstructured.Unstructured{}) { if !reflect.DeepEqual(new, unstructured.Unstructured{}) {
if !check(&new, trigger) { if !check(&new, trigger) {
err := fmt.Errorf("resources don't match") err := fmt.Errorf("resources don't match")
return nil, false, fmt.Errorf("resource %v: %w", ur.Spec.GetResource().String(), err) return nil, fmt.Errorf("resource %v: %w", ur.Spec.GetResource().String(), err)
} }
} }
} }
@ -49,27 +49,27 @@ func NewBackgroundContext(dclient dclient.Interface, ur *kyvernov1beta1.UpdateRe
} }
if trigger == nil { if trigger == nil {
return nil, false, fmt.Errorf("trigger resource does not exist") return nil, fmt.Errorf("trigger resource does not exist")
} }
err = ctx.AddResource(trigger.Object) err = ctx.AddResource(trigger.Object)
if err != nil { if err != nil {
return nil, false, fmt.Errorf("failed to load resource in context: %w", err) return nil, fmt.Errorf("failed to load resource in context: %w", err)
} }
err = ctx.AddOldResource(old.Object) err = ctx.AddOldResource(old.Object)
if err != nil { if err != nil {
return nil, false, fmt.Errorf("failed to load resource in context: %w", err) return nil, fmt.Errorf("failed to load resource in context: %w", err)
} }
err = ctx.AddUserInfo(ur.Spec.Context.UserRequestInfo) err = ctx.AddUserInfo(ur.Spec.Context.UserRequestInfo)
if err != nil { if err != nil {
return nil, false, fmt.Errorf("failed to load SA in context: %w", err) return nil, fmt.Errorf("failed to load SA in context: %w", err)
} }
err = ctx.AddServiceAccount(ur.Spec.Context.UserRequestInfo.AdmissionUserInfo.Username) err = ctx.AddServiceAccount(ur.Spec.Context.UserRequestInfo.AdmissionUserInfo.Username)
if err != nil { if err != nil {
return nil, false, fmt.Errorf("failed to load UserInfo in context: %w", err) return nil, fmt.Errorf("failed to load UserInfo in context: %w", err)
} }
if err := ctx.AddImageInfos(trigger, cfg); err != nil { if err := ctx.AddImageInfos(trigger, cfg); err != nil {
@ -83,7 +83,7 @@ func NewBackgroundContext(dclient dclient.Interface, ur *kyvernov1beta1.UpdateRe
WithAdmissionInfo(ur.Spec.Context.UserRequestInfo). WithAdmissionInfo(ur.Spec.Context.UserRequestInfo).
WithNamespaceLabels(namespaceLabels) WithNamespaceLabels(namespaceLabels)
return policyContext, false, nil return policyContext, nil
} }
func check(admissionRsc, existingRsc *unstructured.Unstructured) bool { func check(admissionRsc, existingRsc *unstructured.Unstructured) bool {

View file

@ -90,7 +90,6 @@ func (c *GenerateController) ProcessUR(ur *kyvernov1beta1.UpdateRequest) error {
var err error var err error
var resource *unstructured.Unstructured var resource *unstructured.Unstructured
var genResources []kyvernov1.ResourceSpec var genResources []kyvernov1.ResourceSpec
var precreatedResource bool
logger.Info("start processing UR", "ur", ur.Name, "resourceVersion", ur.GetResourceVersion()) logger.Info("start processing UR", "ur", ur.Name, "resourceVersion", ur.GetResourceVersion())
// 1 - Check if the trigger exists // 1 - Check if the trigger exists
@ -127,7 +126,7 @@ func (c *GenerateController) ProcessUR(ur *kyvernov1beta1.UpdateRequest) error {
// 2 - Apply the generate policy on the resource // 2 - Apply the generate policy on the resource
namespaceLabels := engineutils.GetNamespaceSelectorsFromNamespaceLister(resource.GetKind(), resource.GetNamespace(), c.nsLister, logger) namespaceLabels := engineutils.GetNamespaceSelectorsFromNamespaceLister(resource.GetKind(), resource.GetNamespace(), c.nsLister, logger)
genResources, precreatedResource, err = c.applyGenerate(*resource, *ur, namespaceLabels) genResources, err = c.applyGenerate(*resource, *ur, namespaceLabels)
if err != nil { if err != nil {
// Need not update the status when policy doesn't apply on resource, because all the update requests are removed by the cleanup controller // Need not update the status when policy doesn't apply on resource, because all the update requests are removed by the cleanup controller
if strings.Contains(err.Error(), doesNotApply) { if strings.Contains(err.Error(), doesNotApply) {
@ -141,36 +140,36 @@ func (c *GenerateController) ProcessUR(ur *kyvernov1beta1.UpdateRequest) error {
} }
// 4 - Update Status // 4 - Update Status
return updateStatus(c.statusControl, *ur, err, genResources, precreatedResource) return updateStatus(c.statusControl, *ur, err, genResources)
} }
const doesNotApply = "policy does not apply to resource" const doesNotApply = "policy does not apply to resource"
func (c *GenerateController) applyGenerate(resource unstructured.Unstructured, ur kyvernov1beta1.UpdateRequest, namespaceLabels map[string]string) ([]kyvernov1.ResourceSpec, bool, error) { func (c *GenerateController) applyGenerate(resource unstructured.Unstructured, ur kyvernov1beta1.UpdateRequest, namespaceLabels map[string]string) ([]kyvernov1.ResourceSpec, error) {
logger := c.log.WithValues("name", ur.GetName(), "policy", ur.Spec.GetPolicyKey(), "resource", ur.Spec.GetResource().String()) logger := c.log.WithValues("name", ur.GetName(), "policy", ur.Spec.GetPolicyKey(), "resource", ur.Spec.GetResource().String())
logger.V(3).Info("applying generate policy rule") logger.V(3).Info("applying generate policy rule")
policy, err := c.getPolicySpec(ur) policy, err := c.getPolicySpec(ur)
if err != nil && !apierrors.IsNotFound(err) { if err != nil && !apierrors.IsNotFound(err) {
logger.Error(err, "error in fetching policy") logger.Error(err, "error in fetching policy")
return nil, false, err return nil, err
} }
if ur.Spec.DeleteDownstream || apierrors.IsNotFound(err) { if ur.Spec.DeleteDownstream || apierrors.IsNotFound(err) {
err = c.deleteDownstream(policy, &ur) err = c.deleteDownstream(policy, &ur)
return nil, false, err return nil, err
} }
policyContext, precreatedResource, err := common.NewBackgroundContext(c.client, &ur, policy, &resource, c.configuration, namespaceLabels, logger) policyContext, err := common.NewBackgroundContext(c.client, &ur, policy, &resource, c.configuration, namespaceLabels, logger)
if err != nil { if err != nil {
return nil, precreatedResource, err return nil, err
} }
// check if the policy still applies to the resource // check if the policy still applies to the resource
engineResponse := c.engine.GenerateResponse(context.Background(), policyContext, ur) engineResponse := c.engine.GenerateResponse(context.Background(), policyContext, ur)
if len(engineResponse.PolicyResponse.Rules) == 0 { if len(engineResponse.PolicyResponse.Rules) == 0 {
logger.V(4).Info(doesNotApply) logger.V(4).Info(doesNotApply)
return nil, false, errors.New(doesNotApply) return nil, errors.New(doesNotApply)
} }
var applicableRules []string var applicableRules []string
@ -226,15 +225,11 @@ func (c *GenerateController) getPolicySpec(ur kyvernov1beta1.UpdateRequest) (kyv
return npolicyObj, nil return npolicyObj, nil
} }
func updateStatus(statusControl common.StatusControlInterface, ur kyvernov1beta1.UpdateRequest, err error, genResources []kyvernov1.ResourceSpec, precreatedResource bool) error { func updateStatus(statusControl common.StatusControlInterface, ur kyvernov1beta1.UpdateRequest, err error, genResources []kyvernov1.ResourceSpec) error {
if err != nil { if err != nil {
if _, err := statusControl.Failed(ur.GetName(), err.Error(), genResources); err != nil { if _, err := statusControl.Failed(ur.GetName(), err.Error(), genResources); err != nil {
return err return err
} }
} else if precreatedResource {
if _, err := statusControl.Skip(ur.GetName(), genResources); err != nil {
return err
}
} else { } else {
if _, err := statusControl.Success(ur.GetName(), genResources); err != nil { if _, err := statusControl.Success(ur.GetName(), genResources); err != nil {
return err return err
@ -243,7 +238,7 @@ func updateStatus(statusControl common.StatusControlInterface, ur kyvernov1beta1
return nil return nil
} }
func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext *engine.PolicyContext, ur kyvernov1beta1.UpdateRequest, applicableRules []string) (genResources []kyvernov1.ResourceSpec, processExisting bool, err error) { func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext *engine.PolicyContext, ur kyvernov1beta1.UpdateRequest, applicableRules []string) (genResources []kyvernov1.ResourceSpec, err error) {
// Get the response as the actions to be performed on the resource // Get the response as the actions to be performed on the resource
// - - substitute values // - - substitute values
policy := policyContext.Policy() policy := policyContext.Policy()
@ -265,17 +260,7 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext
} }
startTime := time.Now() startTime := time.Now()
processExisting = false
var genResource []kyvernov1.ResourceSpec var genResource []kyvernov1.ResourceSpec
if len(rule.MatchResources.Kinds) > 0 {
if len(rule.MatchResources.Annotations) == 0 && rule.MatchResources.Selector == nil {
rcreationTime := resource.GetCreationTimestamp()
pcreationTime := policy.GetCreationTimestamp()
processExisting = rcreationTime.Before(&pcreationTime)
}
}
if applyRules == kyvernov1.ApplyOne && applyCount > 0 { if applyRules == kyvernov1.ApplyOne && applyCount > 0 {
break break
} }
@ -283,33 +268,26 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext
// add configmap json data to context // add configmap json data to context
if err := c.engine.ContextLoader(policyContext.Policy(), rule)(context.TODO(), rule.Context, policyContext.JSONContext()); err != nil { if err := c.engine.ContextLoader(policyContext.Policy(), rule)(context.TODO(), rule.Context, policyContext.JSONContext()); err != nil {
log.Error(err, "cannot add configmaps to context") log.Error(err, "cannot add configmaps to context")
return nil, processExisting, err return nil, err
} }
if rule, err = variables.SubstituteAllInRule(log, policyContext.JSONContext(), rule); err != nil { if rule, err = variables.SubstituteAllInRule(log, policyContext.JSONContext(), rule); err != nil {
log.Error(err, "variable substitution failed for rule %s", rule.Name) log.Error(err, "variable substitution failed for rule %s", rule.Name)
return nil, processExisting, err return nil, err
} }
if policy.GetSpec().IsGenerateExistingOnPolicyUpdate() || !processExisting { genResource, err = applyRule(log, c.client, rule, resource, jsonContext, policy, ur)
genResource, err = applyRule(log, c.client, rule, resource, jsonContext, policy, ur) if err != nil {
if err != nil { log.Error(err, "failed to apply generate rule", "policy", policy.GetName(),
log.Error(err, "failed to apply generate rule", "policy", policy.GetName(), "rule", rule.Name, "resource", resource.GetName(), "suggestion", "users need to grant Kyverno's service account additional privileges")
"rule", rule.Name, "resource", resource.GetName(), "suggestion", "users need to grant Kyverno's service account additional privileges") return nil, err
return nil, processExisting, err
}
ruleNameToProcessingTime[rule.Name] = time.Since(startTime)
genResources = append(genResources, genResource...)
} }
ruleNameToProcessingTime[rule.Name] = time.Since(startTime)
if policy.GetSpec().IsGenerateExistingOnPolicyUpdate() { genResources = append(genResources, genResource...)
processExisting = false
}
applyCount++ applyCount++
} }
return genResources, processExisting, nil return genResources, nil
} }
func getResourceInfo(object map[string]interface{}) (kind, name, namespace, apiversion string, err error) { func getResourceInfo(object map[string]interface{}) (kind, name, namespace, apiversion string, err error) {

View file

@ -92,7 +92,7 @@ func (c *MutateExistingController) ProcessUR(ur *kyvernov1beta1.UpdateRequest) e
} }
namespaceLabels := engineutils.GetNamespaceSelectorsFromNamespaceLister(trigger.GetKind(), trigger.GetNamespace(), c.nsLister, logger) namespaceLabels := engineutils.GetNamespaceSelectorsFromNamespaceLister(trigger.GetKind(), trigger.GetNamespace(), c.nsLister, logger)
policyContext, _, err := common.NewBackgroundContext(c.client, ur, policy, trigger, c.configuration, namespaceLabels, logger) policyContext, err := common.NewBackgroundContext(c.client, ur, policy, trigger, c.configuration, namespaceLabels, logger)
if err != nil { if err != nil {
logger.WithName(rule.Name).Error(err, "failed to build policy context") logger.WithName(rule.Name).Error(err, "failed to build policy context")
errs = append(errs, err) errs = append(errs, err)

View file

@ -407,7 +407,7 @@ func (pc *PolicyController) requeuePolicies() {
func (pc *PolicyController) handleUpdateRequest(ur *kyvernov1beta1.UpdateRequest, triggerResource *unstructured.Unstructured, rule kyvernov1.Rule, policy kyvernov1.PolicyInterface) (skip bool, err error) { func (pc *PolicyController) handleUpdateRequest(ur *kyvernov1beta1.UpdateRequest, triggerResource *unstructured.Unstructured, rule kyvernov1.Rule, policy kyvernov1.PolicyInterface) (skip bool, err error) {
namespaceLabels := engineutils.GetNamespaceSelectorsFromNamespaceLister(triggerResource.GetKind(), triggerResource.GetNamespace(), pc.nsLister, pc.log) namespaceLabels := engineutils.GetNamespaceSelectorsFromNamespaceLister(triggerResource.GetKind(), triggerResource.GetNamespace(), pc.nsLister, pc.log)
policyContext, _, err := backgroundcommon.NewBackgroundContext(pc.client, ur, policy, triggerResource, pc.configHandler, namespaceLabels, pc.log) policyContext, err := backgroundcommon.NewBackgroundContext(pc.client, ur, policy, triggerResource, pc.configHandler, namespaceLabels, pc.log)
if err != nil { if err != nil {
return false, fmt.Errorf("failed to build policy context for rule %s: %w", rule.Name, err) return false, fmt.Errorf("failed to build policy context for rule %s: %w", rule.Name, err)
} }

View file

@ -0,0 +1,9 @@
apiVersion: kyverno.io/v2beta1
kind: ClusterPolicy
metadata:
name: cpol-clone-sync-create-source-after-policy
status:
conditions:
- reason: Succeeded
status: "True"
type: Ready

View file

@ -0,0 +1,30 @@
apiVersion: kyverno.io/v2beta1
kind: ClusterPolicy
metadata:
name: cpol-clone-sync-create-source-after-policy
spec:
rules:
- name: clone-secret
match:
any:
- resources:
kinds:
- Namespace
generate:
apiVersion: v1
kind: Secret
name: regcred
namespace: "{{request.object.metadata.name}}"
synchronize: true
clone:
namespace: default
name: regcred
---
apiVersion: v1
data:
foo: YmFy
kind: Secret
metadata:
name: regcred
namespace: default
type: Opaque

View file

@ -0,0 +1,5 @@
apiVersion: v1
kind: Secret
metadata:
name: regcred
namespace: cpol-clone-sync-create-source-after-policy-ns

View file

@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: cpol-clone-sync-create-source-after-policy-ns

View file

@ -0,0 +1,11 @@
## Description
This is a corner case test to ensure a clone rule is applied when the source is created after the ClusterPolicy.
## Expected Behavior
If the downstream resource is created, the test passes. If it is not created, the test fails.
## Reference Issue(s)
https://github.com/kyverno/kyverno/issues/5411

View file

@ -0,0 +1,10 @@
apiVersion: kyverno.io/v2beta1
kind: Policy
metadata:
name: pol-clone-sync-create-source-after-policy
namespace: pol-clone-sync-create-source-after-policy-ns
status:
conditions:
- reason: Succeeded
status: "True"
type: Ready

View file

@ -0,0 +1,36 @@
apiVersion: v1
kind: Namespace
metadata:
name: pol-clone-sync-create-source-after-policy-ns
---
apiVersion: kyverno.io/v2beta1
kind: Policy
metadata:
name: pol-clone-sync-create-source-after-policy
namespace: pol-clone-sync-create-source-after-policy-ns
spec:
rules:
- name: clone-secret
match:
any:
- resources:
kinds:
- ConfigMap
generate:
apiVersion: v1
kind: Secret
name: mynewsecret
namespace: pol-clone-sync-create-source-after-policy-ns
synchronize: true
clone:
namespace: pol-clone-sync-create-source-after-policy-ns
name: regcred
---
apiVersion: v1
data:
foo: YmFy
kind: Secret
metadata:
name: regcred
namespace: pol-clone-sync-create-source-after-policy-ns
type: Opaque

View file

@ -0,0 +1,5 @@
apiVersion: v1
kind: Secret
metadata:
name: mynewsecret
namespace: pol-clone-sync-create-source-after-policy-ns

View file

@ -0,0 +1,9 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: mycm
namespace: pol-clone-sync-create-source-after-policy-ns
data:
food: cheese
day: monday
color: red

View file

@ -0,0 +1,11 @@
## Description
This is a corner case test to ensure a clone rule is applied when the source is created after the ClusterPolicy.
## Expected Behavior
If the downstream resource is created, the test passes. If it is not created, the test fails.
## Reference Issue(s)
https://github.com/kyverno/kyverno/issues/5411