1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-13 19:28:55 +00:00

add labels to downstream and source resources (#6322)

Signed-off-by: ShutingZhao <shuting@nirmata.com>
This commit is contained in:
shuting 2023-02-22 18:49:09 +08:00 committed by GitHub
parent 9e4ca53c3c
commit d5684f6794
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 592 additions and 239 deletions

View file

@ -5,8 +5,8 @@ const (
URMutatePolicyLabel = "mutate.updaterequest.kyverno.io/policy-name"
URMutateTriggerNameLabel = "mutate.updaterequest.kyverno.io/trigger-name"
URMutateTriggerNSLabel = "mutate.updaterequest.kyverno.io/trigger-namespace"
URMutatetriggerKindLabel = "mutate.updaterequest.kyverno.io/trigger-kind"
URMutatetriggerAPIVersionLabel = "mutate.updaterequest.kyverno.io/trigger-apiversion"
URMutateTriggerKindLabel = "mutate.updaterequest.kyverno.io/trigger-kind"
URMutateTriggerAPIVersionLabel = "mutate.updaterequest.kyverno.io/trigger-apiversion"
// URGeneratePolicyLabel adds the policy name to URs for generate policies
URGeneratePolicyLabel = "generate.kyverno.io/policy-name"

View file

@ -59,7 +59,7 @@ type UpdateRequest struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec is the information to identify the update request.
// ResourceSpec is the information to identify the trigger resource.
Spec UpdateRequestSpec `json:"spec,omitempty"`
// Status contains statistics related to update request.
@ -86,11 +86,14 @@ type UpdateRequestSpec struct {
// Rule is the associate rule name of the current UR.
Rule string `json:"rule" yaml:"rule"`
// DeleteDownstream represents whether the downstream needs to be deleted.
DeleteDownstream bool `json:"deleteDownstream" yaml:"deleteDownstream"`
// Synchronize represents the sync behavior of the corresponding rule
// Optional. Defaults to "false" if not specified.
Synchronize bool `json:"synchronize,omitempty" yaml:"synchronize,omitempty"`
// ResourceSpec is the information to identify the update request.
// ResourceSpec is the information to identify the trigger resource.
Resource kyvernov1.ResourceSpec `json:"resource" yaml:"resource"`
// Context ...

View file

@ -30315,7 +30315,7 @@ spec:
metadata:
type: object
spec:
description: Spec is the information to identify the update request.
description: ResourceSpec is the information to identify the trigger resource.
properties:
context:
description: Context ...
@ -30570,6 +30570,10 @@ spec:
type: object
type: object
type: object
deleteDownstream:
description: DeleteDownstream represents whether the downstream needs
to be deleted.
type: boolean
policy:
description: Specifies the name of the policy.
type: string
@ -30580,8 +30584,8 @@ spec:
- generate
type: string
resource:
description: ResourceSpec is the information to identify the update
request.
description: ResourceSpec is the information to identify the trigger
resource.
properties:
apiVersion:
description: APIVersion specifies resource apiVersion.
@ -30605,6 +30609,7 @@ spec:
type: boolean
required:
- context
- deleteDownstream
- policy
- resource
- rule

View file

@ -495,6 +495,8 @@ func main() {
kubeInformer.Rbac().V1().RoleBindings().Lister(),
kubeInformer.Rbac().V1().ClusterRoleBindings().Lister(),
kyvernoInformer.Kyverno().V1beta1().UpdateRequests().Lister().UpdateRequests(config.KyvernoNamespace()),
kyvernoInformer.Kyverno().V1().ClusterPolicies(),
kyvernoInformer.Kyverno().V1().Policies(),
urgen,
eventGenerator,
openApiManager,

View file

@ -60,7 +60,7 @@ spec:
metadata:
type: object
spec:
description: Spec is the information to identify the update request.
description: ResourceSpec is the information to identify the trigger resource.
properties:
context:
description: Context ...
@ -315,6 +315,10 @@ spec:
type: object
type: object
type: object
deleteDownstream:
description: DeleteDownstream represents whether the downstream needs
to be deleted.
type: boolean
policy:
description: Specifies the name of the policy.
type: string
@ -325,8 +329,8 @@ spec:
- generate
type: string
resource:
description: ResourceSpec is the information to identify the update
request.
description: ResourceSpec is the information to identify the trigger
resource.
properties:
apiVersion:
description: APIVersion specifies resource apiVersion.
@ -350,6 +354,7 @@ spec:
type: boolean
required:
- context
- deleteDownstream
- policy
- resource
- rule

View file

@ -4379,7 +4379,7 @@ UpdateRequestSpec
</em>
</td>
<td>
<p>Spec is the information to identify the update request.</p>
<p>ResourceSpec is the information to identify the trigger resource.</p>
<br/>
<br/>
<table class="table table-striped">
@ -4420,6 +4420,17 @@ string
</tr>
<tr>
<td>
<code>deleteDownstream</code><br/>
<em>
bool
</em>
</td>
<td>
<p>DeleteDownstream represents whether the downstream needs to be deleted.</p>
</td>
</tr>
<tr>
<td>
<code>synchronize</code><br/>
<em>
bool
@ -4440,7 +4451,7 @@ ResourceSpec
</em>
</td>
<td>
<p>ResourceSpec is the information to identify the update request.</p>
<p>ResourceSpec is the information to identify the trigger resource.</p>
</td>
</tr>
<tr>
@ -4642,6 +4653,17 @@ string
</tr>
<tr>
<td>
<code>deleteDownstream</code><br/>
<em>
bool
</em>
</td>
<td>
<p>DeleteDownstream represents whether the downstream needs to be deleted.</p>
</td>
</tr>
<tr>
<td>
<code>synchronize</code><br/>
<em>
bool
@ -4662,7 +4684,7 @@ ResourceSpec
</em>
</td>
<td>
<p>ResourceSpec is the information to identify the update request.</p>
<p>ResourceSpec is the information to identify the trigger resource.</p>
</td>
</tr>
<tr>

View file

@ -0,0 +1,11 @@
package common
const (
GeneratePolicyLabel = "generate.kyverno.io/policy-name"
GeneratePolicyNamespaceLabel = "generate.kyverno.io/policy-namespace"
GenerateRuleLabel = "generate.kyverno.io/rule-name"
GenerateTriggerNameLabel = "generate.kyverno.io/trigger-name"
GenerateTriggerNSLabel = "generate.kyverno.io/trigger-namespace"
GenerateTriggerKindLabel = "generate.kyverno.io/trigger-kind"
GenerateTriggerAPIVersionLabel = "generate.kyverno.io/trigger-apiversion"
)

View file

@ -54,7 +54,7 @@ func NewBackgroundContext(dclient dclient.Interface, ur *kyvernov1beta1.UpdateRe
err = ctx.AddResource(trigger.Object)
if err != nil {
return nil, false, fmt.Errorf("failed to load resource in contex: %w", err)
return nil, false, fmt.Errorf("failed to load resource in context: %w", err)
}
err = ctx.AddOldResource(old.Object)

View file

@ -26,7 +26,7 @@ type Object interface {
GetAPIVersion() string
}
func ManageLabels(unstr *unstructured.Unstructured, triggerResource unstructured.Unstructured) {
func ManageLabels(unstr *unstructured.Unstructured, triggerResource unstructured.Unstructured, policy kyvernov1.PolicyInterface, ruleName string) {
// add managedBY label if not defined
labels := unstr.GetLabels()
if labels == nil {
@ -38,6 +38,9 @@ func ManageLabels(unstr *unstructured.Unstructured, triggerResource unstructured
// handle generatedBy label
generatedBy(labels, triggerResource)
PolicyInfo(labels, policy, ruleName)
TriggerInfo(labels, &triggerResource)
// update the labels
unstr.SetLabels(labels)
}
@ -52,9 +55,9 @@ func MutateLabelsSet(policyKey string, trigger Object) pkglabels.Set {
if !isNil {
set[kyvernov1beta1.URMutateTriggerNameLabel] = trigger.GetName()
set[kyvernov1beta1.URMutateTriggerNSLabel] = trigger.GetNamespace()
set[kyvernov1beta1.URMutatetriggerKindLabel] = trigger.GetKind()
set[kyvernov1beta1.URMutateTriggerKindLabel] = trigger.GetKind()
if trigger.GetAPIVersion() != "" {
set[kyvernov1beta1.URMutatetriggerAPIVersionLabel] = strings.ReplaceAll(trigger.GetAPIVersion(), "/", "-")
set[kyvernov1beta1.URMutateTriggerAPIVersionLabel] = strings.ReplaceAll(trigger.GetAPIVersion(), "/", "-")
}
}
return set
@ -99,9 +102,7 @@ func generatedBy(labels map[string]string, triggerResource unstructured.Unstruct
}
func checkGeneratedBy(labels map[string]string, key, value string) {
if len(value) > 63 {
value = value[0:63]
}
value = trimByLength(value, 63)
val, ok := labels[key]
if ok {
@ -115,3 +116,23 @@ func checkGeneratedBy(labels map[string]string, key, value string) {
labels[key] = value
}
}
func PolicyInfo(labels map[string]string, policy kyvernov1.PolicyInterface, ruleName string) {
labels[GeneratePolicyLabel] = policy.GetName()
labels[GeneratePolicyNamespaceLabel] = policy.GetNamespace()
labels[GenerateRuleLabel] = ruleName
}
func TriggerInfo(labels map[string]string, obj Object) {
labels[GenerateTriggerAPIVersionLabel] = obj.GetAPIVersion()
labels[GenerateTriggerKindLabel] = obj.GetKind()
labels[GenerateTriggerNSLabel] = obj.GetNamespace()
labels[GenerateTriggerNameLabel] = trimByLength(obj.GetName(), 63)
}
func trimByLength(value string, character int) string {
if len(value) > character {
return value[0:character]
}
return value
}

View file

@ -65,3 +65,10 @@ func UpdateStatus(client versioned.Interface, urLister kyvernov1beta1listers.Upd
}
return ur, err
}
func PolicyKey(namespace, name string) string {
if namespace != "" {
return namespace + "/" + name
}
return name
}

View file

@ -128,7 +128,6 @@ func (c *GenerateController) ProcessUR(ur *kyvernov1beta1.UpdateRequest) error {
// 2 - Apply the generate policy on the resource
namespaceLabels := engineutils.GetNamespaceSelectorsFromNamespaceLister(resource.GetKind(), resource.GetNamespace(), c.nsLister, logger)
genResources, precreatedResource, err = c.applyGenerate(*resource, *ur, namespaceLabels)
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
if strings.Contains(err.Error(), doesNotApply) {
@ -166,7 +165,7 @@ func (c *GenerateController) applyGenerate(resource unstructured.Unstructured, u
return nil, false, err
}
policyContext, precreatedResource, err := common.NewBackgroundContext(c.client, &ur, &policy, &resource, c.configuration, namespaceLabels, logger)
policyContext, precreatedResource, err := common.NewBackgroundContext(c.client, &ur, policy, &resource, c.configuration, namespaceLabels, logger)
if err != nil {
return nil, precreatedResource, err
}
@ -236,31 +235,24 @@ func (c *GenerateController) cleanupClonedResource(targetSpec kyvernov1.Resource
}
// getPolicySpec gets the policy spec from the ClusterPolicy/Policy
func (c *GenerateController) getPolicySpec(ur kyvernov1beta1.UpdateRequest) (kyvernov1.ClusterPolicy, error) {
var policy kyvernov1.ClusterPolicy
func (c *GenerateController) getPolicySpec(ur kyvernov1beta1.UpdateRequest) (kyvernov1.PolicyInterface, error) {
pNamespace, pName, err := cache.SplitMetaNamespaceKey(ur.Spec.Policy)
if err != nil {
return policy, err
return nil, err
}
if pNamespace == "" {
policyObj, err := c.policyLister.Get(pName)
if err != nil {
return policy, err
return nil, err
}
return *policyObj, err
return policyObj, err
}
npolicyObj, err := c.npolicyLister.Policies(pNamespace).Get(pName)
if err != nil {
return policy, err
return nil, err
}
return kyvernov1.ClusterPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: pName,
},
Spec: npolicyObj.Spec,
}, nil
return npolicyObj, nil
}
func updateStatus(statusControl common.StatusControlInterface, ur kyvernov1beta1.UpdateRequest, err error, genResources []kyvernov1.ResourceSpec, precreatedResource bool) error {
@ -328,6 +320,12 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext
return nil, processExisting, err
}
if ur.Spec.DeleteDownstream {
pKey := common.PolicyKey(policy.GetNamespace(), policy.GetName())
err = c.deleteResource(pKey, rule, ur)
return nil, false, err
}
if policy.GetSpec().IsGenerateExistingOnPolicyUpdate() || !processExisting {
genResource, err = applyRule(log, c.client, rule, resource, jsonContext, policy, ur)
if err != nil {
@ -400,7 +398,7 @@ func applyRule(log logr.Logger, client dclient.Interface, rule kyvernov1.Rule, r
logger := log.WithValues("genKind", genKind, "genAPIVersion", genAPIVersion, "genNamespace", genNamespace, "genName", genName)
if rule.Generation.Clone.Name != "" {
cresp, mode, err = manageClone(logger, genAPIVersion, genKind, genNamespace, genName, policy.GetName(), ur, rule.Generation, client)
cresp, mode, err = manageClone(logger, genAPIVersion, genKind, genNamespace, genName, policy, ur, rule, client)
rdatas = append(rdatas, GenerateResponse{
Data: cresp,
Action: mode,
@ -411,7 +409,7 @@ func applyRule(log logr.Logger, client dclient.Interface, rule kyvernov1.Rule, r
Error: err,
})
} else if len(rule.Generation.CloneList.Kinds) != 0 {
rdatas = manageCloneList(logger, genNamespace, policy.GetName(), ur, rule.Generation, client)
rdatas = manageCloneList(logger, genNamespace, ur, policy, rule, client)
} else {
dresp, mode, err = manageData(logger, genAPIVersion, genKind, genNamespace, genName, rule.Generation.RawData, rule.Generation.Synchronize, ur, client)
rdatas = append(rdatas, GenerateResponse{
@ -455,7 +453,7 @@ func applyRule(log logr.Logger, client dclient.Interface, rule kyvernov1.Rule, r
}
newResource.SetAPIVersion(rdata.GenAPIVersion)
common.ManageLabels(newResource, resource)
common.ManageLabels(newResource, resource, policy, rule.Name)
// Add Synchronize label
label := newResource.GetLabels()
@ -590,7 +588,8 @@ func manageData(log logr.Logger, apiVersion, kind, namespace, name string, data
return updateObj.UnstructuredContent(), Update, nil
}
func manageClone(log logr.Logger, apiVersion, kind, namespace, name, policy string, ur kyvernov1beta1.UpdateRequest, clone kyvernov1.Generation, client dclient.Interface) (map[string]interface{}, ResourceMode, error) {
func manageClone(log logr.Logger, apiVersion, kind, namespace, name string, policy kyvernov1.PolicyInterface, ur kyvernov1beta1.UpdateRequest, rule kyvernov1.Rule, client dclient.Interface) (map[string]interface{}, ResourceMode, error) {
clone := rule.Generation
// resource namespace can be nil in case of clusters scope resource
rNamespace := clone.Clone.Namespace
if rNamespace == "" {
@ -613,6 +612,10 @@ func manageClone(log logr.Logger, apiVersion, kind, namespace, name, policy stri
return nil, Skip, fmt.Errorf("source resource %s %s/%s/%s not found. %v", apiVersion, kind, rNamespace, rName, err)
}
if err := updateSourceLabel(client, obj, ur.Spec.Resource, policy, rule); err != nil {
log.Error(err, "failed to add labels to the source", "kind", obj.GetKind(), "namespace", obj.GetNamespace(), "name", obj.GetName())
}
// check if cloned resource exists
cobj, err := client.GetResource(context.TODO(), apiVersion, kind, namespace, name)
if err != nil {
@ -645,9 +648,9 @@ func manageClone(log logr.Logger, apiVersion, kind, namespace, name, policy stri
return obj.UnstructuredContent(), Create, nil
}
func manageCloneList(log logr.Logger, namespace, policy string, ur kyvernov1beta1.UpdateRequest, clone kyvernov1.Generation, client dclient.Interface) []GenerateResponse {
func manageCloneList(log logr.Logger, namespace string, ur kyvernov1beta1.UpdateRequest, policy kyvernov1.PolicyInterface, rule kyvernov1.Rule, client dclient.Interface) []GenerateResponse {
var response []GenerateResponse
clone := rule.Generation
rNamespace := clone.CloneList.Namespace
if rNamespace == "" {
log.V(4).Info("resource namespace %s , optional in case of cluster scope resource", rNamespace)
@ -686,7 +689,7 @@ func manageCloneList(log logr.Logger, namespace, policy string, ur kyvernov1beta
// check if the resource as reference in clone exists?
obj, err := client.GetResource(context.TODO(), apiVersion, kind, rNamespace, rName.GetName())
if err != nil {
log.Error(err, "failed to get resoruce", apiVersion, "apiVersion", kind, "kind", rNamespace, "rNamespace", rName.GetName(), "name")
log.Error(err, "failed to get resource", apiVersion, "apiVersion", kind, "kind", rNamespace, "rNamespace", rName.GetName(), "name")
response = append(response, GenerateResponse{
Data: nil,
Action: Skip,
@ -695,6 +698,10 @@ func manageCloneList(log logr.Logger, namespace, policy string, ur kyvernov1beta
return response
}
if err := updateSourceLabel(client, obj, ur.Spec.Resource, policy, rule); err != nil {
log.Error(err, "failed to add labels to the source", "kind", obj.GetKind(), "namespace", obj.GetNamespace(), "name", obj.GetName())
}
// check if cloned resource exists
cobj, err := client.GetResource(context.TODO(), apiVersion, kind, namespace, rName.GetName())
if apierrors.IsNotFound(err) && len(ur.Status.GeneratedResources) != 0 && !clone.Synchronize {
@ -811,3 +818,15 @@ func (c *GenerateController) GetUnstrResource(genResourceSpec kyvernov1.Resource
}
return resource, nil
}
func (c *GenerateController) deleteResource(policyKey string, rule kyvernov1.Rule, ur kyvernov1beta1.UpdateRequest) error {
if policyKey != ur.Spec.Policy {
return nil
}
if rule.Name == ur.Spec.Rule {
return c.client.DeleteResource(context.TODO(), rule.Generation.GetAPIVersion(), rule.Generation.GetKind(), rule.Generation.GetNamespace(), rule.Generation.GetName(), false)
}
return nil
}

View file

@ -0,0 +1,24 @@
package generate
import (
"context"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/background/common"
"github.com/kyverno/kyverno/pkg/clients/dclient"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
func updateSourceLabel(client dclient.Interface, source *unstructured.Unstructured, trigger kyvernov1.ResourceSpec, policy kyvernov1.PolicyInterface, rule kyvernov1.Rule) error {
labels := source.GetLabels()
if labels == nil {
labels = make(map[string]string)
}
common.PolicyInfo(labels, policy, rule.Name)
common.TriggerInfo(labels, trigger)
source.SetLabels(labels)
_, err := client.UpdateResource(context.TODO(), source.GetAPIVersion(), source.GetKind(), source.GetNamespace(), source, false)
return err
}

View file

@ -4,7 +4,9 @@ import (
"fmt"
"strconv"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
"github.com/kyverno/kyverno/pkg/background/common"
)
func increaseRetryAnnotation(ur *kyvernov1beta1.UpdateRequest) (int, map[string]string, error) {
@ -32,3 +34,12 @@ func increaseRetryAnnotation(ur *kyvernov1beta1.UpdateRequest) (int, map[string]
return retry, urAnnotations, nil
}
func TriggerFromLabels(labels map[string]string) kyvernov1.ResourceSpec {
return kyvernov1.ResourceSpec{
Kind: labels[common.GenerateTriggerKindLabel],
Namespace: labels[common.GenerateTriggerNSLabel],
Name: labels[common.GenerateTriggerNameLabel],
APIVersion: labels[common.GenerateTriggerAPIVersionLabel],
}
}

101
pkg/policy/generate.go Normal file
View file

@ -0,0 +1,101 @@
package policy
import (
"context"
"fmt"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
"github.com/kyverno/kyverno/pkg/background/common"
generateutils "github.com/kyverno/kyverno/pkg/background/generate"
"github.com/kyverno/kyverno/pkg/config"
"go.uber.org/multierr"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
)
func (pc *PolicyController) handleGenerate(policyKey string, policy kyvernov1.PolicyInterface) error {
var errors []error
logger := pc.log.WithName("handleGenerate").WithName(policyKey)
logger.Info("update URs on policy event")
generateURs := pc.listGenerateURs(policyKey, nil)
updateUR(pc.kyvernoClient, pc.urLister.UpdateRequests(config.KyvernoNamespace()), policyKey, generateURs, pc.log.WithName("updateUR"))
for _, rule := range policy.GetSpec().Rules {
if err := pc.createUR(policy, rule); err != nil {
logger.Error(err, "failed to create UR on policy event")
}
var ruleType kyvernov1beta1.RequestType
if policy.GetSpec().IsGenerateExistingOnPolicyUpdate() {
ruleType = kyvernov1beta1.Generate
triggers := generateTriggers(pc.client, rule, pc.log)
for _, trigger := range triggers {
gurs := pc.listGenerateURs(policyKey, trigger)
if gurs != nil {
logger.V(4).Info("UR was created", "rule", rule.Name, "rule type", ruleType, "trigger", trigger.GetNamespace()+"/"+trigger.GetName())
continue
}
ur := newUR(policy, resourceSpecFromUnstructured(trigger), ruleType)
skip, err := pc.handleUpdateRequest(ur, trigger, rule, policy)
if err != nil {
pc.log.Error(err, "failed to create new UR on policy update", "policy", policy.GetName(), "rule", rule.Name, "rule type", ruleType,
"target", fmt.Sprintf("%s/%s/%s/%s", trigger.GetAPIVersion(), trigger.GetKind(), trigger.GetNamespace(), trigger.GetName()))
errors = append(errors, err)
continue
}
if skip {
continue
}
logger.V(4).Info("successfully created UR on policy update", "policy", policy.GetName(), "rule", rule.Name, "rule type", ruleType,
"target", fmt.Sprintf("%s/%s/%s/%s", trigger.GetAPIVersion(), trigger.GetKind(), trigger.GetNamespace(), trigger.GetName()))
}
err := multierr.Combine(errors...)
return err
}
}
return nil
}
func (pc *PolicyController) listGenerateURs(policyKey string, trigger *unstructured.Unstructured) []*kyvernov1beta1.UpdateRequest {
generateURs, err := pc.urLister.List(labels.SelectorFromSet(common.GenerateLabelsSet(policyKey, trigger)))
if err != nil {
pc.log.Error(err, "failed to list update request for generate policy")
}
return generateURs
}
func (pc *PolicyController) createUR(policy kyvernov1.PolicyInterface, rule kyvernov1.Rule) error {
generate := rule.Generation
if !generate.Synchronize {
// no action for non-sync policy/rule
return nil
}
if generate.GetData() != nil {
downstream, err := pc.client.GetResource(context.TODO(), generate.APIVersion, generate.Kind, generate.Namespace, generate.Name)
if err != nil {
return err
}
labels := downstream.GetLabels()
trigger := generateutils.TriggerFromLabels(labels)
ur := newUR(policy, trigger, kyvernov1beta1.Generate)
created, err := pc.kyvernoClient.KyvernoV1beta1().UpdateRequests(config.KyvernoNamespace()).Create(context.TODO(), ur, metav1.CreateOptions{})
if err != nil {
return err
}
updated := created.DeepCopy()
updated.Status.State = kyvernov1beta1.Pending
_, err = pc.kyvernoClient.KyvernoV1beta1().UpdateRequests(config.KyvernoNamespace()).UpdateStatus(context.TODO(), updated, metav1.UpdateOptions{})
if err != nil {
return err
}
}
return nil
}

58
pkg/policy/mutate.go Normal file
View file

@ -0,0 +1,58 @@
package policy
import (
"fmt"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
backgroundcommon "github.com/kyverno/kyverno/pkg/background/common"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
)
func (pc *PolicyController) handleMutate(policyKey string, policy kyvernov1.PolicyInterface) error {
logger := pc.log.WithName("handleMutate").WithName(policyKey)
if !policy.GetSpec().MutateExistingOnPolicyUpdate {
logger.V(4).Info("skip policy application on policy event", "policyKey", policyKey, "mutateExiting", policy.GetSpec().MutateExistingOnPolicyUpdate)
return nil
}
logger.Info("update URs on policy event")
for _, rule := range policy.GetSpec().Rules {
var ruleType kyvernov1beta1.RequestType
if rule.IsMutateExisting() {
ruleType = kyvernov1beta1.Mutate
triggers := generateTriggers(pc.client, rule, pc.log)
for _, trigger := range triggers {
murs := pc.listMutateURs(policyKey, trigger)
if murs != nil {
logger.V(4).Info("UR was created", "rule", rule.Name, "rule type", ruleType, "trigger", trigger.GetNamespace()+trigger.GetName())
continue
}
logger.Info("creating new UR for mutate")
ur := newUR(policy, resourceSpecFromUnstructured(trigger), ruleType)
skip, err := pc.handleUpdateRequest(ur, trigger, rule, policy)
if err != nil {
pc.log.Error(err, "failed to create new UR on policy update", "policy", policy.GetName(), "rule", rule.Name, "rule type", ruleType,
"target", fmt.Sprintf("%s/%s/%s/%s", trigger.GetAPIVersion(), trigger.GetKind(), trigger.GetNamespace(), trigger.GetName()))
continue
}
if skip {
continue
}
pc.log.V(2).Info("successfully created UR on policy update", "policy", policy.GetName(), "rule", rule.Name, "rule type", ruleType,
"target", fmt.Sprintf("%s/%s/%s/%s", trigger.GetAPIVersion(), trigger.GetKind(), trigger.GetNamespace(), trigger.GetName()))
}
}
}
return nil
}
func (pc *PolicyController) listMutateURs(policyKey string, trigger *unstructured.Unstructured) []*kyvernov1beta1.UpdateRequest {
mutateURs, err := pc.urLister.List(labels.SelectorFromSet(backgroundcommon.MutateLabelsSet(policyKey, trigger)))
if err != nil {
pc.log.Error(err, "failed to list update request for mutate policy")
}
return mutateURs
}

View file

@ -26,7 +26,6 @@ import (
"github.com/kyverno/kyverno/pkg/metrics"
engineutils "github.com/kyverno/kyverno/pkg/utils/engine"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
"go.uber.org/multierr"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -160,45 +159,77 @@ func (pc *PolicyController) canBackgroundProcess(p kyvernov1.PolicyInterface) bo
func (pc *PolicyController) addPolicy(obj interface{}) {
logger := pc.log
p := obj.(*kyvernov1.ClusterPolicy)
var p kyvernov1.PolicyInterface
logger.Info("policy created", "uid", p.UID, "kind", "ClusterPolicy", "name", p.Name)
switch obj := obj.(type) {
case *kyvernov1.ClusterPolicy:
p = obj
case *kyvernov1.Policy:
p = obj
default:
return
}
logger.Info("policy created", "uid", p.GetUID(), "kind", p.GetKind(), "namespace", p.GetNamespace(), "name", p.GetName())
if !pc.canBackgroundProcess(p) {
return
}
logger.V(4).Info("queuing policy for background processing", "name", p.Name)
logger.V(4).Info("queuing policy for background processing", "name", p.GetName())
pc.enqueuePolicy(p)
}
func (pc *PolicyController) updatePolicy(old, cur interface{}) {
logger := pc.log
oldP := old.(*kyvernov1.ClusterPolicy)
curP := cur.(*kyvernov1.ClusterPolicy)
var oldP, curP kyvernov1.PolicyInterface
switch obj := old.(type) {
case *kyvernov1.ClusterPolicy:
oldP = obj
case *kyvernov1.Policy:
oldP = obj
default:
return
}
switch obj := cur.(type) {
case *kyvernov1.ClusterPolicy:
curP = obj
case *kyvernov1.Policy:
curP = obj
default:
return
}
if !pc.canBackgroundProcess(curP) {
return
}
if reflect.DeepEqual(oldP.Spec, curP.Spec) {
if reflect.DeepEqual(oldP.GetSpec(), curP.GetSpec()) {
return
}
logger.V(2).Info("updating policy", "name", oldP.Name)
logger.V(2).Info("updating policy", "name", oldP.GetName())
pc.enqueuePolicy(curP)
}
func (pc *PolicyController) deletePolicy(obj interface{}) {
logger := pc.log
p, ok := kubeutils.GetObjectWithTombstone(obj).(*kyvernov1.ClusterPolicy)
if !ok {
var p kyvernov1.PolicyInterface
switch kubeutils.GetObjectWithTombstone(obj).(type) {
case *kyvernov1.ClusterPolicy:
p = kubeutils.GetObjectWithTombstone(obj).(*kyvernov1.ClusterPolicy)
case *kyvernov1.Policy:
p = kubeutils.GetObjectWithTombstone(obj).(*kyvernov1.Policy)
default:
logger.Info("Failed to get deleted object", "obj", obj)
return
}
logger.Info("policy deleted", "uid", p.UID, "kind", "ClusterPolicy", "name", p.Name)
logger.Info("policy deleted", "uid", p.GetUID(), "kind", p.GetKind(), "namespace", p.GetNamespace(), "name", p.GetName())
// do not clean up UR on generate clone (sync=true) policy deletion
rules := autogen.ComputeRules(p)
@ -211,60 +242,6 @@ func (pc *PolicyController) deletePolicy(obj interface{}) {
pc.enqueuePolicy(p)
}
func (pc *PolicyController) addNsPolicy(obj interface{}) {
logger := pc.log
p := obj.(*kyvernov1.Policy)
logger.Info("policy created", "uid", p.UID, "kind", "Policy", "name", p.Name, "namespaces", p.Namespace)
if !pc.canBackgroundProcess(p) {
return
}
logger.V(4).Info("queuing policy for background processing", "namespace", p.GetNamespace(), "name", p.GetName())
pc.enqueuePolicy(p)
}
func (pc *PolicyController) updateNsPolicy(old, cur interface{}) {
logger := pc.log
oldP := old.(*kyvernov1.Policy)
curP := cur.(*kyvernov1.Policy)
if !pc.canBackgroundProcess(curP) {
return
}
if reflect.DeepEqual(oldP.Spec, curP.Spec) {
return
}
logger.V(4).Info("updating namespace policy", "namespace", oldP.Namespace, "name", oldP.Name)
pc.enqueuePolicy(curP)
}
func (pc *PolicyController) deleteNsPolicy(obj interface{}) {
logger := pc.log
p, ok := kubeutils.GetObjectWithTombstone(obj).(*kyvernov1.Policy)
if !ok {
logger.Info("Failed to get deleted object", "obj", obj)
return
}
logger.Info("policy deleted event", "uid", p.UID, "kind", "Policy", "policy_name", p.Name, "namespaces", p.Namespace)
pol := p
// do not clean up UR on generate clone (sync=true) policy deletion
rules := autogen.ComputeRules(pol)
for _, r := range rules {
clone, sync := r.GetCloneSyncForGenerate()
if clone && sync {
return
}
}
pc.enqueuePolicy(pol)
}
func (pc *PolicyController) enqueuePolicy(policy kyvernov1.PolicyInterface) {
logger := pc.log
key, err := cache.MetaNamespaceKeyFunc(policy)
@ -296,9 +273,9 @@ func (pc *PolicyController) Run(ctx context.Context, workers int) {
})
_, _ = pc.npInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: pc.addNsPolicy,
UpdateFunc: pc.updateNsPolicy,
DeleteFunc: pc.deleteNsPolicy,
AddFunc: pc.addPolicy,
UpdateFunc: pc.updatePolicy,
DeleteFunc: pc.deletePolicy,
})
for i := 0; i < workers; i++ {
@ -362,9 +339,14 @@ func (pc *PolicyController) syncPolicy(key string) error {
}
return err
} else {
err = pc.updateUR(key, policy)
err = pc.handleMutate(key, policy)
if err != nil {
logger.Error(err, "failed to updateUR on Policy update")
logger.Error(err, "failed to updateUR on mutate policy update")
}
err = pc.handleGenerate(key, policy)
if err != nil {
logger.Error(err, "failed to updateUR on generate policy update")
}
}
return nil
@ -424,88 +406,6 @@ func (pc *PolicyController) requeuePolicies() {
}
}
func (pc *PolicyController) updateUR(policyKey string, policy kyvernov1.PolicyInterface) error {
logger := pc.log.WithName("updateUR").WithName(policyKey)
if !policy.GetSpec().MutateExistingOnPolicyUpdate && !policy.GetSpec().IsGenerateExistingOnPolicyUpdate() {
logger.V(4).Info("skip policy application on policy event", "policyKey", policyKey, "mutateExiting", policy.GetSpec().MutateExistingOnPolicyUpdate, "generateExisting", policy.GetSpec().IsGenerateExistingOnPolicyUpdate())
return nil
}
logger.Info("update URs on policy event")
var errors []error
mutateURs := pc.listMutateURs(policyKey, nil)
generateURs := pc.listGenerateURs(policyKey, nil)
updateUR(pc.kyvernoClient, pc.urLister.UpdateRequests(config.KyvernoNamespace()), policyKey, append(mutateURs, generateURs...), pc.log.WithName("updateUR"))
for _, rule := range policy.GetSpec().Rules {
var ruleType kyvernov1beta1.RequestType
if rule.IsMutateExisting() {
ruleType = kyvernov1beta1.Mutate
triggers := generateTriggers(pc.client, rule, pc.log)
for _, trigger := range triggers {
murs := pc.listMutateURs(policyKey, trigger)
if murs != nil {
logger.V(4).Info("UR was created", "rule", rule.Name, "rule type", ruleType, "trigger", trigger.GetNamespace()+trigger.GetName())
continue
}
logger.Info("creating new UR for mutate")
ur := newUR(policy, trigger, ruleType)
skip, err := pc.handleUpdateRequest(ur, trigger, rule, policy)
if err != nil {
pc.log.Error(err, "failed to create new UR on policy update", "policy", policy.GetName(), "rule", rule.Name, "rule type", ruleType,
"target", fmt.Sprintf("%s/%s/%s/%s", trigger.GetAPIVersion(), trigger.GetKind(), trigger.GetNamespace(), trigger.GetName()))
continue
}
if skip {
continue
}
pc.log.V(2).Info("successfully created UR on policy update", "policy", policy.GetName(), "rule", rule.Name, "rule type", ruleType,
"target", fmt.Sprintf("%s/%s/%s/%s", trigger.GetAPIVersion(), trigger.GetKind(), trigger.GetNamespace(), trigger.GetName()))
}
}
if policy.GetSpec().IsGenerateExistingOnPolicyUpdate() {
ruleType = kyvernov1beta1.Generate
triggers := generateTriggers(pc.client, rule, pc.log)
for _, trigger := range triggers {
gurs := pc.listGenerateURs(policyKey, trigger)
if gurs != nil {
logger.V(4).Info("UR was created", "rule", rule.Name, "rule type", ruleType, "trigger", trigger.GetNamespace()+"/"+trigger.GetName())
continue
}
ur := newUR(policy, trigger, ruleType)
skip, err := pc.handleUpdateRequest(ur, trigger, rule, policy)
if err != nil {
pc.log.Error(err, "failed to create new UR on policy update", "policy", policy.GetName(), "rule", rule.Name, "rule type", ruleType,
"target", fmt.Sprintf("%s/%s/%s/%s", trigger.GetAPIVersion(), trigger.GetKind(), trigger.GetNamespace(), trigger.GetName()))
errors = append(errors, err)
continue
}
if skip {
continue
}
pc.log.V(4).Info("successfully created UR on policy update", "policy", policy.GetName(), "rule", rule.Name, "rule type", ruleType,
"target", fmt.Sprintf("%s/%s/%s/%s", trigger.GetAPIVersion(), trigger.GetKind(), trigger.GetNamespace(), trigger.GetName()))
}
err := multierr.Combine(errors...)
return err
}
}
return nil
}
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)
policyContext, _, err := backgroundcommon.NewBackgroundContext(pc.client, ur, policy, triggerResource, pc.configHandler, namespaceLabels, pc.log)
@ -533,22 +433,6 @@ func (pc *PolicyController) handleUpdateRequest(ur *kyvernov1beta1.UpdateRequest
return false, err
}
func (pc *PolicyController) listMutateURs(policyKey string, trigger *unstructured.Unstructured) []*kyvernov1beta1.UpdateRequest {
mutateURs, err := pc.urLister.List(labels.SelectorFromSet(backgroundcommon.MutateLabelsSet(policyKey, trigger)))
if err != nil {
pc.log.Error(err, "failed to list update request for mutate policy")
}
return mutateURs
}
func (pc *PolicyController) listGenerateURs(policyKey string, trigger *unstructured.Unstructured) []*kyvernov1beta1.UpdateRequest {
generateURs, err := pc.urLister.List(labels.SelectorFromSet(backgroundcommon.GenerateLabelsSet(policyKey, trigger)))
if err != nil {
pc.log.Error(err, "failed to list update request for generate policy")
}
return generateURs
}
func generateTriggers(client dclient.Interface, rule kyvernov1.Rule, log logr.Logger) []*unstructured.Unstructured {
list := &unstructured.UnstructuredList{}
@ -590,3 +474,12 @@ func updateUR(kyvernoClient versioned.Interface, urLister kyvernov1beta1listers.
}
}
}
func resourceSpecFromUnstructured(obj *unstructured.Unstructured) kyvernov1.ResourceSpec {
return kyvernov1.ResourceSpec{
APIVersion: obj.GetAPIVersion(),
Kind: obj.GetKind(),
Namespace: obj.GetNamespace(),
Name: obj.GetName(),
}
}

View file

@ -6,11 +6,10 @@ import (
common "github.com/kyverno/kyverno/pkg/background/common"
"github.com/kyverno/kyverno/pkg/config"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
)
func newUR(policy kyvernov1.PolicyInterface, trigger *unstructured.Unstructured, ruleType kyvernov1beta1.RequestType) *kyvernov1beta1.UpdateRequest {
func newUR(policy kyvernov1.PolicyInterface, trigger kyvernov1.ResourceSpec, ruleType kyvernov1beta1.RequestType) *kyvernov1beta1.UpdateRequest {
var policyNameNamespaceKey string
if policy.IsNamespaced() {

View file

@ -13,6 +13,7 @@ import (
"github.com/kyverno/kyverno/pkg/background/generate"
gen "github.com/kyverno/kyverno/pkg/background/generate"
"github.com/kyverno/kyverno/pkg/client/clientset/versioned"
kyvernov1listers "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1"
kyvernov1beta1listers "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1beta1"
"github.com/kyverno/kyverno/pkg/clients/dclient"
"github.com/kyverno/kyverno/pkg/config"
@ -32,9 +33,8 @@ import (
)
type GenerationHandler interface {
// TODO: why do we need to expose that ?
HandleUpdatesForGenerateRules(context.Context, *admissionv1.AdmissionRequest, []kyvernov1.PolicyInterface)
Handle(context.Context, *admissionv1.AdmissionRequest, []kyvernov1.PolicyInterface, *engine.PolicyContext, time.Time)
HandleNew(context.Context, *admissionv1.AdmissionRequest, []kyvernov1.PolicyInterface, *engine.PolicyContext)
}
func NewGenerationHandler(
@ -44,6 +44,8 @@ func NewGenerationHandler(
kyvernoClient versioned.Interface,
nsLister corev1listers.NamespaceLister,
urLister kyvernov1beta1listers.UpdateRequestNamespaceLister,
cpolLister kyvernov1listers.ClusterPolicyLister,
polLister kyvernov1listers.PolicyLister,
urGenerator webhookgenerate.Generator,
urUpdater webhookutils.UpdateRequestUpdater,
eventGen event.Interface,
@ -56,6 +58,8 @@ func NewGenerationHandler(
kyvernoClient: kyvernoClient,
nsLister: nsLister,
urLister: urLister,
cpolLister: cpolLister,
polLister: polLister,
urGenerator: urGenerator,
urUpdater: urUpdater,
eventGen: eventGen,
@ -70,6 +74,8 @@ type generationHandler struct {
kyvernoClient versioned.Interface
nsLister corev1listers.NamespaceLister
urLister kyvernov1beta1listers.UpdateRequestNamespaceLister
cpolLister kyvernov1listers.ClusterPolicyLister
polLister kyvernov1listers.PolicyLister
urGenerator webhookgenerate.Generator
urUpdater webhookutils.UpdateRequestUpdater
eventGen event.Interface
@ -127,12 +133,12 @@ func (h *generationHandler) Handle(
}
if request.Operation == admissionv1.Update {
h.HandleUpdatesForGenerateRules(ctx, request, policies)
h.handleUpdatesForGenerateRules(ctx, request, policies)
}
}
// HandleUpdatesForGenerateRules handles admission-requests for update
func (h *generationHandler) HandleUpdatesForGenerateRules(ctx context.Context, request *admissionv1.AdmissionRequest, policies []kyvernov1.PolicyInterface) {
// handleUpdatesForGenerateRules handles admission-requests for update
func (h *generationHandler) handleUpdatesForGenerateRules(ctx context.Context, request *admissionv1.AdmissionRequest, policies []kyvernov1.PolicyInterface) {
if request.Operation != admissionv1.Update {
return
}
@ -200,7 +206,7 @@ func (h *generationHandler) handleUpdateGenerateTargetResource(ctx context.Conte
if rule.Generation.Kind == targetSourceKind && rule.Generation.Name == targetSourceName {
updatedRule, err := getGeneratedByResource(ctx, newRes, resLabels, h.client, rule, h.log)
if err != nil {
h.log.V(4).Info("skipping generate policy and resource pattern validaton", "error", err)
h.log.V(4).Info("skipping generate policy and resource pattern validation", "error", err)
} else {
data := updatedRule.Generation.DeepCopy().GetData()
if data != nil {

View file

@ -0,0 +1,156 @@
package generation
import (
"context"
"fmt"
"reflect"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
"github.com/kyverno/kyverno/pkg/background/common"
generateutils "github.com/kyverno/kyverno/pkg/background/generate"
"github.com/kyverno/kyverno/pkg/engine"
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/event"
engineutils "github.com/kyverno/kyverno/pkg/utils/engine"
webhookutils "github.com/kyverno/kyverno/pkg/webhooks/utils"
admissionv1 "k8s.io/api/admission/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
func (h *generationHandler) HandleNew(
ctx context.Context,
request *admissionv1.AdmissionRequest,
policies []kyvernov1.PolicyInterface,
policyContext *engine.PolicyContext,
) {
h.log.V(6).Info("handle admission request for generate")
if len(policies) != 0 {
h.handleTrigger(ctx, request, policies, policyContext)
}
h.handleNonTrigger(ctx, policyContext, request)
}
func (h *generationHandler) handleTrigger(
ctx context.Context,
request *admissionv1.AdmissionRequest,
policies []kyvernov1.PolicyInterface,
policyContext *engine.PolicyContext,
) {
h.log.V(4).Info("handle trigger resource operation for generate")
var engineResponses []*engineapi.EngineResponse
for _, policy := range policies {
var appliedRules []engineapi.RuleResponse
policyContext := policyContext.WithPolicy(policy)
if request.Kind.Kind != "Namespace" && request.Namespace != "" {
policyContext = policyContext.WithNamespaceLabels(engineutils.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, h.log))
}
engineResponse := h.engine.ApplyBackgroundChecks(ctx, policyContext)
for _, rule := range engineResponse.PolicyResponse.Rules {
if rule.Status == engineapi.RuleStatusPass {
appliedRules = append(appliedRules, rule)
}
}
if len(appliedRules) > 0 {
engineResponse.PolicyResponse.Rules = appliedRules
// some generate rules do apply to the resource
engineResponses = append(engineResponses, engineResponse)
}
// registering the kyverno_policy_results_total metric concurrently
go webhookutils.RegisterPolicyResultsMetricGeneration(ctx, h.log, h.metrics, string(request.Operation), policy, *engineResponse)
// registering the kyverno_policy_execution_duration_seconds metric concurrently
go webhookutils.RegisterPolicyExecutionDurationMetricGenerate(ctx, h.log, h.metrics, string(request.Operation), policy, *engineResponse)
}
if failedResponse := applyUpdateRequest(ctx, request, kyvernov1beta1.Generate, h.urGenerator, policyContext.AdmissionInfo(), request.Operation, engineResponses...); failedResponse != nil {
// report failure event
for _, failedUR := range failedResponse {
err := fmt.Errorf("failed to create Update Request: %v", failedUR.err)
newResource := policyContext.NewResource()
e := event.NewBackgroundFailedEvent(err, failedUR.ur.Policy, "", event.GeneratePolicyController, &newResource)
h.eventGen.Add(e...)
}
}
}
func (h *generationHandler) handleNonTrigger(
ctx context.Context,
policyContext *engine.PolicyContext,
request *admissionv1.AdmissionRequest,
) {
resource := policyContext.OldResource()
labels := resource.GetLabels()
if labels[common.GeneratePolicyLabel] != "" {
h.log.V(4).Info("handle non-trigger resource operation for generate")
if err := h.createUR(ctx, policyContext, request); err != nil {
h.log.Error(err, "failed to create the UR on non-trigger admission request")
}
}
}
func (h *generationHandler) createUR(ctx context.Context, policyContext *engine.PolicyContext, request *admissionv1.AdmissionRequest) (err error) {
var policy kyvernov1.PolicyInterface
new := policyContext.NewResource()
labels := new.GetLabels()
old := policyContext.OldResource()
oldLabels := old.GetLabels()
if !compareLabels(labels, oldLabels) {
return fmt.Errorf("labels have been changed, new: %v, old: %v", labels, oldLabels)
}
deleteDownstream := false
if reflect.DeepEqual(new, unstructured.Unstructured{}) {
deleteDownstream = true
labels = oldLabels
}
pName := labels[common.GeneratePolicyLabel]
pNamespace := labels[common.GeneratePolicyNamespaceLabel]
pRuleName := labels[common.GenerateRuleLabel]
if pNamespace != "" {
policy, err = h.polLister.Policies(pNamespace).Get(pName)
} else {
policy, err = h.cpolLister.Get(pName)
}
if err != nil {
return err
}
pKey := common.PolicyKey(pNamespace, pName)
for _, rule := range policy.GetSpec().Rules {
if rule.Name == pRuleName && rule.Generation.Synchronize {
ur := kyvernov1beta1.UpdateRequestSpec{
Type: kyvernov1beta1.Generate,
Policy: pKey,
Rule: rule.Name,
Resource: generateutils.TriggerFromLabels(labels),
}
ur.DeleteDownstream = deleteDownstream
if err := h.urGenerator.Apply(ctx, ur, admissionv1.Update); err != nil {
e := event.NewBackgroundFailedEvent(err, pKey, pRuleName, event.GeneratePolicyController, &new)
h.eventGen.Add(e...)
return err
}
}
}
return nil
}
func compareLabels(new, old map[string]string) bool {
if new == nil {
return true
}
if new[common.GeneratePolicyLabel] != old[common.GeneratePolicyLabel] ||
new[common.GeneratePolicyNamespaceLabel] != old[common.GeneratePolicyNamespaceLabel] ||
new[common.GenerateRuleLabel] != old[common.GenerateRuleLabel] ||
new[common.GenerateTriggerNameLabel] != old[common.GenerateTriggerNameLabel] ||
new[common.GenerateTriggerNSLabel] != old[common.GenerateTriggerNSLabel] ||
new[common.GenerateTriggerKindLabel] != old[common.GenerateTriggerKindLabel] {
return false
}
return true
}

View file

@ -121,7 +121,7 @@ func applyUpdateRequest(
ctx context.Context,
request *admissionv1.AdmissionRequest,
ruleType kyvernov1beta1.RequestType,
grGenerator updaterequest.Generator,
urGenerator updaterequest.Generator,
userRequestInfo kyvernov1beta1.RequestInfo,
action admissionv1.Operation,
engineResponses ...*engineapi.EngineResponse,
@ -133,7 +133,7 @@ func applyUpdateRequest(
for _, er := range engineResponses {
ur := transform(admissionRequestInfo, userRequestInfo, er, ruleType)
if err := grGenerator.Apply(ctx, ur, action); err != nil {
if err := urGenerator.Apply(ctx, ur, action); err != nil {
failedUpdateRequest = append(failedUpdateRequest, updateRequestResponse{ur: ur, err: err})
}
}

View file

@ -10,6 +10,8 @@ import (
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
"github.com/kyverno/kyverno/pkg/background/generate"
"github.com/kyverno/kyverno/pkg/client/clientset/versioned"
kyvernov1informers "github.com/kyverno/kyverno/pkg/client/informers/externalversions/kyverno/v1"
kyvernov1listers "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1"
kyvernov1beta1listers "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1beta1"
"github.com/kyverno/kyverno/pkg/clients/dclient"
"github.com/kyverno/kyverno/pkg/config"
@ -25,7 +27,6 @@ import (
jsonutils "github.com/kyverno/kyverno/pkg/utils/json"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
"github.com/kyverno/kyverno/pkg/webhooks"
"github.com/kyverno/kyverno/pkg/webhooks/resource/generation"
"github.com/kyverno/kyverno/pkg/webhooks/resource/imageverification"
"github.com/kyverno/kyverno/pkg/webhooks/resource/mutation"
"github.com/kyverno/kyverno/pkg/webhooks/resource/validation"
@ -51,8 +52,10 @@ type handlers struct {
pCache policycache.Cache
// listers
nsLister corev1listers.NamespaceLister
urLister kyvernov1beta1listers.UpdateRequestNamespaceLister
nsLister corev1listers.NamespaceLister
urLister kyvernov1beta1listers.UpdateRequestNamespaceLister
cpolLister kyvernov1listers.ClusterPolicyLister
polLister kyvernov1listers.PolicyLister
urGenerator webhookgenerate.Generator
eventGen event.Interface
@ -75,6 +78,8 @@ func NewHandlers(
rbLister rbacv1listers.RoleBindingLister,
crbLister rbacv1listers.ClusterRoleBindingLister,
urLister kyvernov1beta1listers.UpdateRequestNamespaceLister,
cpolInformer kyvernov1informers.ClusterPolicyInformer,
polInformer kyvernov1informers.PolicyInformer,
urGenerator webhookgenerate.Generator,
eventGen event.Interface,
openApiManager openapi.ValidateInterface,
@ -90,6 +95,8 @@ func NewHandlers(
pCache: pCache,
nsLister: nsLister,
urLister: urLister,
cpolLister: cpolInformer.Lister(),
polLister: polInformer.Lister(),
urGenerator: urGenerator,
eventGen: eventGen,
openApiManager: openApiManager,
@ -114,11 +121,6 @@ func (h *handlers) Validate(ctx context.Context, logger logr.Logger, request *ad
if len(policies) == 0 && len(mutatePolicies) == 0 && len(generatePolicies) == 0 {
logger.V(4).Info("no policies matched admission request")
}
if len(generatePolicies) == 0 && request.Operation == admissionv1.Update {
// handle generate source resource updates
gh := generation.NewGenerationHandler(logger, h.engine, h.client, h.kyvernoClient, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen, h.metricsConfig)
go gh.HandleUpdatesForGenerateRules(context.TODO(), request, []kyvernov1.PolicyInterface{})
}
logger.V(4).Info("processing policies for validate admission request", "validate", len(policies), "mutate", len(mutatePolicies), "generate", len(generatePolicies))
@ -141,7 +143,7 @@ func (h *handlers) Validate(ctx context.Context, logger logr.Logger, request *ad
}
defer h.handleDelete(logger, request)
go h.createUpdateRequests(logger, request, policyContext, generatePolicies, mutatePolicies, startTime)
go h.handleBackgroundApplies(ctx, logger, request, policyContext, generatePolicies, mutatePolicies, startTime)
return admissionutils.ResponseSuccess(request.UID, warnings...)
}

View file

@ -16,11 +16,10 @@ import (
admissionv1 "k8s.io/api/admission/v1"
)
// createUpdateRequests applies generate and mutateExisting policies, and creates update requests for background reconcile
func (h *handlers) createUpdateRequests(logger logr.Logger, request *admissionv1.AdmissionRequest, policyContext *engine.PolicyContext, generatePolicies, mutatePolicies []kyvernov1.PolicyInterface, ts time.Time) {
gh := generation.NewGenerationHandler(logger, h.engine, h.client, h.kyvernoClient, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen, h.metricsConfig)
go h.handleMutateExisting(context.TODO(), logger, request, mutatePolicies, policyContext, ts)
go gh.Handle(context.TODO(), request, generatePolicies, policyContext, ts)
// handleBackgroundApplies applies generate and mutateExisting policies, and creates update requests for background reconcile
func (h *handlers) handleBackgroundApplies(ctx context.Context, logger logr.Logger, request *admissionv1.AdmissionRequest, policyContext *engine.PolicyContext, generatePolicies, mutatePolicies []kyvernov1.PolicyInterface, ts time.Time) {
go h.handleMutateExisting(ctx, logger, request, mutatePolicies, policyContext, ts)
h.handleGenerate(ctx, logger, request, generatePolicies, policyContext, ts)
}
func (h *handlers) handleMutateExisting(ctx context.Context, logger logr.Logger, request *admissionv1.AdmissionRequest, policies []kyvernov1.PolicyInterface, policyContext *engine.PolicyContext, admissionRequestTimestamp time.Time) {
@ -71,3 +70,10 @@ func (h *handlers) handleMutateExisting(ctx context.Context, logger logr.Logger,
}
}
}
func (h *handlers) handleGenerate(ctx context.Context, logger logr.Logger, request *admissionv1.AdmissionRequest, generatePolicies []kyvernov1.PolicyInterface, policyContext *engine.PolicyContext, ts time.Time) {
gh := generation.NewGenerationHandler(logger, h.engine, h.client, h.kyvernoClient, h.nsLister, h.urLister, h.cpolLister, h.polLister, h.urGenerator, h.urUpdater, h.eventGen, h.metricsConfig)
// TODO(shuting): clean up old code
// go gh.Handle(ctx, request, generatePolicies, policyContext, ts)
go gh.HandleNew(ctx, request, generatePolicies, policyContext)
}

View file

@ -40,7 +40,7 @@ func NewGenerator(client versioned.Interface, urInformer kyvernov1beta1informers
// Apply creates update request resource
func (g *generator) Apply(ctx context.Context, ur kyvernov1beta1.UpdateRequestSpec, action admissionv1.Operation) error {
logger.V(4).Info("reconcile Update Request", "request", ur)
logger.V(4).Info("apply Update Request", "request", ur)
if action == admissionv1.Delete && ur.GetRequestType() == kyvernov1beta1.Generate {
return nil
}
@ -72,6 +72,7 @@ func (g *generator) tryApplyResource(ctx context.Context, urSpec kyvernov1beta1.
} else if urSpec.GetRequestType() == kyvernov1beta1.Generate {
queryLabels = common.GenerateLabelsSet(urSpec.Policy, urSpec.GetResource())
}
urList, err := g.urLister.List(labels.SelectorFromSet(queryLabels))
if err != nil {
l.Error(err, "failed to get update request for the resource", "resource", urSpec.GetResource().String())
@ -93,7 +94,8 @@ func (g *generator) tryApplyResource(ctx context.Context, urSpec kyvernov1beta1.
return err
}
}
if len(urList) == 0 {
if len(urList) == 0 || urSpec.DeleteDownstream {
l.V(4).Info("creating new UpdateRequest")
ur := kyvernov1beta1.UpdateRequest{
ObjectMeta: metav1.ObjectMeta{

View file

@ -1,11 +1,11 @@
## Description
This test ensures that deletion of the source (upstream) resource used by a ClusterPolicy `generate` rule with sync enabled using a clone declaration does NOT cause deletion of downstream/cloned resources.
This test ensures that deletion of the source (upstream) resource used by a ClusterPolicy `generate` rule with sync enabled using a clone declaration DOES cause deletion of downstream/cloned resources.
## Expected Behavior
After the source is deleted, the downstream resources should remain. If the downstream resource remains, the test passes. If the downstream resource is deleted, the test fails.
After the source is deleted, the downstream resources should be deleted. If the downstream resource remains, the test fails. If the downstream resource is deleted, the test passes.
## Reference Issue(s)
N/A
https://github.com/kyverno/kyverno/issues/6266