From 5203809b737b3c271d3c0b3610cff2615de3a63a Mon Sep 17 00:00:00 2001 From: shuting Date: Wed, 14 Aug 2024 22:32:49 +0800 Subject: [PATCH] chore: refactor background controller (#10850) * chore: refactor Signed-off-by: ShutingZhao * feat: add foreach for generate.daya to api Signed-off-by: ShutingZhao * chore: refactor Signed-off-by: ShutingZhao --------- Signed-off-by: ShutingZhao --- pkg/background/generate/clone.go | 17 +- .../generate/{generate.go => controller.go} | 205 ++++-------------- pkg/background/generate/generator.go | 140 ++++++++++++ pkg/background/generate/utils.go | 17 ++ 4 files changed, 208 insertions(+), 171 deletions(-) rename pkg/background/generate/{generate.go => controller.go} (67%) create mode 100644 pkg/background/generate/generator.go diff --git a/pkg/background/generate/clone.go b/pkg/background/generate/clone.go index bd85a800063a..fbfe04f54ff6 100644 --- a/pkg/background/generate/clone.go +++ b/pkg/background/generate/clone.go @@ -13,7 +13,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func manageClone(log logr.Logger, target, sourceSpec kyvernov1.ResourceSpec, policy kyvernov1.PolicyInterface, rule kyvernov1.Rule, client dclient.Interface) generateResponse { +func manageClone(log logr.Logger, target, sourceSpec kyvernov1.ResourceSpec, severSideApply bool, rule kyvernov1.Rule, client dclient.Interface) generateResponse { source := sourceSpec clone := rule.Generation if clone.Clone.Name != "" { @@ -29,8 +29,7 @@ func manageClone(log logr.Logger, target, sourceSpec kyvernov1.ResourceSpec, pol log.V(4).Info("namespace is optional in case of cluster scope resource", "source namespace", source.GetNamespace()) } - if source.GetNamespace() == target.GetNamespace() && source.GetName() == target.GetName() || - (rule.Generation.CloneList.Kinds != nil) && (source.GetNamespace() == target.GetNamespace()) { + if source.GetNamespace() == target.GetNamespace() && source.GetName() == target.GetName() { log.V(4).Info("skip resource self-clone") return newSkipGenerateResponse(nil, target, nil) } @@ -65,7 +64,7 @@ func manageClone(log logr.Logger, target, sourceSpec kyvernov1.ResourceSpec, pol } if targetObj != nil { - if !policy.GetSpec().UseServerSideApply { + if !severSideApply { sourceObjCopy.SetUID(targetObj.GetUID()) sourceObjCopy.SetSelfLink(targetObj.GetSelfLink()) sourceObjCopy.SetCreationTimestamp(targetObj.GetCreationTimestamp()) @@ -81,7 +80,7 @@ func manageClone(log logr.Logger, target, sourceSpec kyvernov1.ResourceSpec, pol return newCreateGenerateResponse(sourceObjCopy.UnstructuredContent(), target, nil) } -func manageCloneList(log logr.Logger, targetNamespace string, policy kyvernov1.PolicyInterface, rule kyvernov1.Rule, client dclient.Interface) []generateResponse { +func manageCloneList(log logr.Logger, targetNamespace string, severSideApply bool, rule kyvernov1.Rule, client dclient.Interface) []generateResponse { var responses []generateResponse cloneList := rule.Generation.CloneList sourceNamespace := cloneList.Namespace @@ -101,8 +100,14 @@ func manageCloneList(log logr.Logger, targetNamespace string, policy kyvernov1.P for _, source := range sources.Items { target := newResourceSpec(source.GetAPIVersion(), source.GetKind(), targetNamespace, source.GetName()) + + if (cloneList.Kinds != nil) && (source.GetNamespace() == target.GetNamespace()) { + log.V(4).Info("skip resource self-clone") + responses = append(responses, newSkipGenerateResponse(nil, target, nil)) + continue + } responses = append(responses, - manageClone(log, target, newResourceSpec(source.GetAPIVersion(), source.GetKind(), source.GetNamespace(), source.GetName()), policy, rule, client)) + manageClone(log, target, newResourceSpec(source.GetAPIVersion(), source.GetKind(), source.GetNamespace(), source.GetName()), severSideApply, rule, client)) } } return responses diff --git a/pkg/background/generate/generate.go b/pkg/background/generate/controller.go similarity index 67% rename from pkg/background/generate/generate.go rename to pkg/background/generate/controller.go index cff081e90b79..24fcdff799d4 100644 --- a/pkg/background/generate/generate.go +++ b/pkg/background/generate/controller.go @@ -3,6 +3,7 @@ package generate import ( "context" "encoding/json" + "errors" "fmt" "slices" "strings" @@ -11,7 +12,6 @@ import ( "github.com/go-logr/logr" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" kyvernov2 "github.com/kyverno/kyverno/api/kyverno/v2" - "github.com/kyverno/kyverno/pkg/autogen" "github.com/kyverno/kyverno/pkg/background/common" "github.com/kyverno/kyverno/pkg/client/clientset/versioned" kyvernov1listers "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1" @@ -21,7 +21,6 @@ import ( "github.com/kyverno/kyverno/pkg/engine" engineapi "github.com/kyverno/kyverno/pkg/engine/api" "github.com/kyverno/kyverno/pkg/engine/jmespath" - "github.com/kyverno/kyverno/pkg/engine/validate" "github.com/kyverno/kyverno/pkg/engine/variables" regex "github.com/kyverno/kyverno/pkg/engine/variables/regex" "github.com/kyverno/kyverno/pkg/event" @@ -29,7 +28,6 @@ import ( engineutils "github.com/kyverno/kyverno/pkg/utils/engine" kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" validationpolicy "github.com/kyverno/kyverno/pkg/validation/policy" - "github.com/pkg/errors" "go.uber.org/multierr" admissionv1 "k8s.io/api/admission/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -97,6 +95,11 @@ func (c *GenerateController) ProcessUR(ur *kyvernov2.UpdateRequest) error { logger.Info("start processing UR", "ur", ur.Name, "resourceVersion", ur.GetResourceVersion()) var failures []error + policy, err := c.getPolicyObject(*ur) + if err != nil && !apierrors.IsNotFound(err) { + return fmt.Errorf("error in fetching policy: %v", err) + } + for i := 0; i < len(ur.Spec.RuleContext); i++ { rule := ur.Spec.RuleContext[i] trigger, err := c.getTrigger(ur.Spec, i) @@ -106,19 +109,12 @@ func (c *GenerateController) ProcessUR(ur *kyvernov2.UpdateRequest) error { continue } - namespaceLabels := engineutils.GetNamespaceSelectorsFromNamespaceLister(trigger.GetKind(), trigger.GetNamespace(), c.nsLister, logger) - genResources, err = c.applyGenerate(*trigger, *ur, i, namespaceLabels) + genResources, err = c.applyGenerate(*trigger, *ur, policy, i) if err != nil { if strings.Contains(err.Error(), doesNotApply) { logger.V(4).Info(fmt.Sprintf("skipping rule %s: %v", rule.Rule, err.Error())) } - policy, err := c.getPolicyObject(*ur) - if err != nil { - failures = append(failures, fmt.Errorf("rule %v failed: failed to get policy object: %s", rule.Rule, err)) - continue - } - events := event.NewBackgroundFailedEvent(err, policy, ur.Spec.RuleContext[i].Rule, event.GeneratePolicyController, kyvernov1.ResourceSpec{Kind: trigger.GetKind(), Namespace: trigger.GetNamespace(), Name: trigger.GetName()}) c.eventGen.Add(events...) @@ -194,35 +190,22 @@ func (c *GenerateController) getTriggerForCreateOperation(spec kyvernov2.UpdateR return trigger, err } -func (c *GenerateController) applyGenerate(trigger unstructured.Unstructured, ur kyvernov2.UpdateRequest, i int, namespaceLabels map[string]string) ([]kyvernov1.ResourceSpec, error) { +func (c *GenerateController) applyGenerate(trigger unstructured.Unstructured, ur kyvernov2.UpdateRequest, policy kyvernov1.PolicyInterface, i int) ([]kyvernov1.ResourceSpec, error) { logger := c.log.WithValues("name", ur.GetName(), "policy", ur.Spec.GetPolicyKey()) logger.V(3).Info("applying generate policy") - policy, err := c.getPolicyObject(ur) - if err != nil && !apierrors.IsNotFound(err) { - logger.Error(err, "error in fetching policy") - return nil, err - } - ruleContext := ur.Spec.RuleContext[i] - if ruleContext.DeleteDownstream || apierrors.IsNotFound(err) { - err = c.deleteDownstream(policy, ruleContext, &ur) - return nil, err + if ruleContext.DeleteDownstream || policy == nil { + return nil, c.deleteDownstream(policy, ruleContext, &ur) } - var rule *kyvernov1.Rule - p := policy.CreateDeepCopy() - for j := range p.GetSpec().Rules { - if p.GetSpec().Rules[j].Name == ruleContext.Rule { - rule = &p.GetSpec().Rules[j] - break - } - } - if rule == nil { - logger.Info("skip rule application as the rule does not exist in the updaterequest", "rule", ruleContext.Rule) + p, ok := buildPolicyWithAppliedRules(policy, ruleContext.Rule) + if !ok { + logger.V(4).Info("skip rule application as the rule does not exist in the updaterequest", "rule", ruleContext.Rule) return nil, nil } - p.GetSpec().SetRules([]kyvernov1.Rule{*rule}) + + namespaceLabels := engineutils.GetNamespaceSelectorsFromNamespaceLister(trigger.GetKind(), trigger.GetNamespace(), c.nsLister, logger) policyContext, err := common.NewBackgroundContext(logger, c.client, ur.Spec.Context, p, &trigger, c.configuration, c.jp, namespaceLabels) if err != nil { return nil, err @@ -288,19 +271,6 @@ func (c *GenerateController) getPolicyObject(ur kyvernov2.UpdateRequest) (kyvern return npolicyObj, nil } -func updateStatus(statusControl common.StatusControlInterface, ur kyvernov2.UpdateRequest, err error, genResources []kyvernov1.ResourceSpec) error { - if err != nil { - if _, err := statusControl.Failed(ur.GetName(), err.Error(), genResources); err != nil { - return err - } - } else { - if _, err := statusControl.Success(ur.GetName(), genResources); err != nil { - return err - } - } - return nil -} - func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext *engine.PolicyContext, applicableRules []string) (genResources []kyvernov1.ResourceSpec, err error) { policy := policyContext.Policy() resource := policyContext.NewResource() @@ -309,7 +279,7 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext applyRules := policy.GetSpec().GetApplyRules() applyCount := 0 - for _, rule := range autogen.ComputeRules(policy, "") { + for _, rule := range policy.GetSpec().Rules { var err error if !rule.HasGenerate() { continue @@ -341,7 +311,7 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext } logger := log.WithValues("rule", rule.Name) // 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(policy, rule)(context.TODO(), rule.Context, policyContext.JSONContext()); err != nil { log.Error(err, "cannot add configmaps to context") return nil, err } @@ -351,7 +321,8 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext return nil, err } - genResource, err = applyRule(logger, c.client, rule, resource, policy) + g := newGenerator(c.client, logger, policy, rule, resource) + genResource, err = g.generate() if err != nil { log.Error(err, "failed to apply generate rule", "policy", policy.GetName(), "rule", rule.Name, "resource", resource.GetName()) return nil, err @@ -364,123 +335,6 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext return genResources, nil } -func applyRule(log logr.Logger, client dclient.Interface, rule kyvernov1.Rule, trigger unstructured.Unstructured, policy kyvernov1.PolicyInterface) ([]kyvernov1.ResourceSpec, error) { - responses := []generateResponse{} - var err error - var newGenResources []kyvernov1.ResourceSpec - - target := rule.Generation.ResourceSpec - logger := log.WithValues("target", target.String()) - - if rule.Generation.Clone.Name != "" { - resp := manageClone(logger.WithValues("type", "clone"), target, kyvernov1.ResourceSpec{}, policy, rule, client) - responses = append(responses, resp) - } else if len(rule.Generation.CloneList.Kinds) != 0 { - responses = manageCloneList(logger.WithValues("type", "cloneList"), target.GetNamespace(), policy, rule, client) - } else { - resp := manageData(logger.WithValues("type", "data"), target, rule.Generation.RawData, rule.Generation.Synchronize, client) - responses = append(responses, resp) - } - - for _, response := range responses { - targetMeta := response.GetTarget() - if response.GetError() != nil { - logger.Error(response.GetError(), "failed to generate resource", "mode", response.GetAction()) - return newGenResources, err - } - - if response.GetAction() == Skip { - continue - } - - logger.V(3).Info("applying generate rule", "mode", response.GetAction()) - if response.GetData() == nil && response.GetAction() == Update { - logger.V(4).Info("no changes required for generate target resource") - return newGenResources, nil - } - - newResource := &unstructured.Unstructured{} - newResource.SetUnstructuredContent(response.GetData()) - newResource.SetName(targetMeta.GetName()) - newResource.SetNamespace(targetMeta.GetNamespace()) - if newResource.GetKind() == "" { - newResource.SetKind(targetMeta.GetKind()) - } - - newResource.SetAPIVersion(targetMeta.GetAPIVersion()) - common.ManageLabels(newResource, trigger, policy, rule.Name) - if response.GetAction() == Create { - newResource.SetResourceVersion("") - if policy.GetSpec().UseServerSideApply { - _, err = client.ApplyResource(context.TODO(), targetMeta.GetAPIVersion(), targetMeta.GetKind(), targetMeta.GetNamespace(), targetMeta.GetName(), newResource, false, "generate") - } else { - _, err = client.CreateResource(context.TODO(), targetMeta.GetAPIVersion(), targetMeta.GetKind(), targetMeta.GetNamespace(), newResource, false) - } - if err != nil { - if !apierrors.IsAlreadyExists(err) { - return newGenResources, err - } - } - logger.V(2).Info("created generate target resource") - newGenResources = append(newGenResources, targetMeta) - } else if response.GetAction() == Update { - generatedObj, err := client.GetResource(context.TODO(), targetMeta.GetAPIVersion(), targetMeta.GetKind(), targetMeta.GetNamespace(), targetMeta.GetName()) - if err != nil { - logger.V(2).Info("creating new target due to the failure when fetching", "err", err.Error()) - if policy.GetSpec().UseServerSideApply { - _, err = client.ApplyResource(context.TODO(), targetMeta.GetAPIVersion(), targetMeta.GetKind(), targetMeta.GetNamespace(), targetMeta.GetName(), newResource, false, "generate") - } else { - _, err = client.CreateResource(context.TODO(), targetMeta.GetAPIVersion(), targetMeta.GetKind(), targetMeta.GetNamespace(), newResource, false) - } - if err != nil { - return newGenResources, err - } - newGenResources = append(newGenResources, targetMeta) - } else { - if !rule.Generation.Synchronize { - logger.V(4).Info("synchronize disabled, skip syncing changes") - continue - } - if err := validate.MatchPattern(logger, newResource.Object, generatedObj.Object); err == nil { - if err := validate.MatchPattern(logger, generatedObj.Object, newResource.Object); err == nil { - logger.V(4).Info("patterns match, skipping updates") - continue - } - } - - logger.V(4).Info("updating existing resource") - if targetMeta.GetAPIVersion() == "" { - generatedResourceAPIVersion := generatedObj.GetAPIVersion() - newResource.SetAPIVersion(generatedResourceAPIVersion) - } - if targetMeta.GetNamespace() == "" { - newResource.SetNamespace("default") - } - - if policy.GetSpec().UseServerSideApply { - _, err = client.ApplyResource(context.TODO(), targetMeta.GetAPIVersion(), targetMeta.GetKind(), targetMeta.GetNamespace(), targetMeta.GetName(), newResource, false, "generate") - } else { - _, err = client.UpdateResource(context.TODO(), targetMeta.GetAPIVersion(), targetMeta.GetKind(), targetMeta.GetNamespace(), newResource, false) - } - if err != nil { - logger.Error(err, "failed to update resource") - return newGenResources, err - } - } - logger.V(3).Info("updated generate target resource") - } - } - return newGenResources, nil -} - -func GetUnstrRule(rule *kyvernov1.Generation) (*unstructured.Unstructured, error) { - ruleData, err := json.Marshal(rule) - if err != nil { - return nil, err - } - return kubeutils.BytesToUnstructured(ruleData) -} - // NewGenerateControllerWithOnlyClient returns an instance of Controller with only the client. func NewGenerateControllerWithOnlyClient(client dclient.Interface, engine engineapi.Engine) *GenerateController { c := GenerateController{ @@ -498,3 +352,24 @@ func (c *GenerateController) GetUnstrResource(genResourceSpec kyvernov1.Resource } return resource, nil } + +func updateStatus(statusControl common.StatusControlInterface, ur kyvernov2.UpdateRequest, err error, genResources []kyvernov1.ResourceSpec) error { + if err != nil { + if _, err := statusControl.Failed(ur.GetName(), err.Error(), genResources); err != nil { + return err + } + } else { + if _, err := statusControl.Success(ur.GetName(), genResources); err != nil { + return err + } + } + return nil +} + +func GetUnstrRule(rule *kyvernov1.Generation) (*unstructured.Unstructured, error) { + ruleData, err := json.Marshal(rule) + if err != nil { + return nil, err + } + return kubeutils.BytesToUnstructured(ruleData) +} diff --git a/pkg/background/generate/generator.go b/pkg/background/generate/generator.go new file mode 100644 index 000000000000..53dbcc231df1 --- /dev/null +++ b/pkg/background/generate/generator.go @@ -0,0 +1,140 @@ +package generate + +import ( + "context" + + "github.com/go-logr/logr" + kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" + "github.com/kyverno/kyverno/pkg/background/common" + "github.com/kyverno/kyverno/pkg/clients/dclient" + "github.com/kyverno/kyverno/pkg/engine/validate" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +type generator struct { + client dclient.Interface + logger logr.Logger + policy kyvernov1.PolicyInterface + rule kyvernov1.Rule + trigger unstructured.Unstructured +} + +func newGenerator(client dclient.Interface, logger logr.Logger, policy kyvernov1.PolicyInterface, rule kyvernov1.Rule, trigger unstructured.Unstructured) *generator { + return &generator{ + client: client, + logger: logger, + policy: policy, + rule: rule, + trigger: trigger, + } +} + +func (g *generator) generate() ([]kyvernov1.ResourceSpec, error) { + responses := []generateResponse{} + var err error + var newGenResources []kyvernov1.ResourceSpec + + target := g.rule.Generation.ResourceSpec + logger := g.logger.WithValues("target", target.String()) + + if g.rule.Generation.Clone.Name != "" { + resp := manageClone(logger.WithValues("type", "clone"), target, kyvernov1.ResourceSpec{}, g.policy.GetSpec().UseServerSideApply, g.rule, g.client) + responses = append(responses, resp) + } else if len(g.rule.Generation.CloneList.Kinds) != 0 { + responses = manageCloneList(logger.WithValues("type", "cloneList"), target.GetNamespace(), g.policy.GetSpec().UseServerSideApply, g.rule, g.client) + } else { + resp := manageData(logger.WithValues("type", "data"), target, g.rule.Generation.RawData, g.rule.Generation.Synchronize, g.client) + responses = append(responses, resp) + } + + for _, response := range responses { + targetMeta := response.GetTarget() + if response.GetError() != nil { + logger.Error(response.GetError(), "failed to generate resource", "mode", response.GetAction()) + return newGenResources, err + } + + if response.GetAction() == Skip { + continue + } + + logger.V(3).Info("applying generate rule", "mode", response.GetAction()) + if response.GetData() == nil && response.GetAction() == Update { + logger.V(4).Info("no changes required for generate target resource") + return newGenResources, nil + } + + newResource := &unstructured.Unstructured{} + newResource.SetUnstructuredContent(response.GetData()) + newResource.SetName(targetMeta.GetName()) + newResource.SetNamespace(targetMeta.GetNamespace()) + if newResource.GetKind() == "" { + newResource.SetKind(targetMeta.GetKind()) + } + + newResource.SetAPIVersion(targetMeta.GetAPIVersion()) + common.ManageLabels(newResource, g.trigger, g.policy, g.rule.Name) + if response.GetAction() == Create { + newResource.SetResourceVersion("") + if g.policy.GetSpec().UseServerSideApply { + _, err = g.client.ApplyResource(context.TODO(), targetMeta.GetAPIVersion(), targetMeta.GetKind(), targetMeta.GetNamespace(), targetMeta.GetName(), newResource, false, "generate") + } else { + _, err = g.client.CreateResource(context.TODO(), targetMeta.GetAPIVersion(), targetMeta.GetKind(), targetMeta.GetNamespace(), newResource, false) + } + if err != nil { + if !apierrors.IsAlreadyExists(err) { + return newGenResources, err + } + } + logger.V(2).Info("created generate target resource") + newGenResources = append(newGenResources, targetMeta) + } else if response.GetAction() == Update { + generatedObj, err := g.client.GetResource(context.TODO(), targetMeta.GetAPIVersion(), targetMeta.GetKind(), targetMeta.GetNamespace(), targetMeta.GetName()) + if err != nil { + logger.V(2).Info("creating new target due to the failure when fetching", "err", err.Error()) + if g.policy.GetSpec().UseServerSideApply { + _, err = g.client.ApplyResource(context.TODO(), targetMeta.GetAPIVersion(), targetMeta.GetKind(), targetMeta.GetNamespace(), targetMeta.GetName(), newResource, false, "generate") + } else { + _, err = g.client.CreateResource(context.TODO(), targetMeta.GetAPIVersion(), targetMeta.GetKind(), targetMeta.GetNamespace(), newResource, false) + } + if err != nil { + return newGenResources, err + } + newGenResources = append(newGenResources, targetMeta) + } else { + if !g.rule.Generation.Synchronize { + logger.V(4).Info("synchronize disabled, skip syncing changes") + continue + } + if err := validate.MatchPattern(logger, newResource.Object, generatedObj.Object); err == nil { + if err := validate.MatchPattern(logger, generatedObj.Object, newResource.Object); err == nil { + logger.V(4).Info("patterns match, skipping updates") + continue + } + } + + logger.V(4).Info("updating existing resource") + if targetMeta.GetAPIVersion() == "" { + generatedResourceAPIVersion := generatedObj.GetAPIVersion() + newResource.SetAPIVersion(generatedResourceAPIVersion) + } + if targetMeta.GetNamespace() == "" { + newResource.SetNamespace("default") + } + + if g.policy.GetSpec().UseServerSideApply { + _, err = g.client.ApplyResource(context.TODO(), targetMeta.GetAPIVersion(), targetMeta.GetKind(), targetMeta.GetNamespace(), targetMeta.GetName(), newResource, false, "generate") + } else { + _, err = g.client.UpdateResource(context.TODO(), targetMeta.GetAPIVersion(), targetMeta.GetKind(), targetMeta.GetNamespace(), newResource, false) + } + if err != nil { + logger.Error(err, "failed to update resource") + return newGenResources, err + } + } + logger.V(3).Info("updated generate target resource") + } + } + return newGenResources, nil +} diff --git a/pkg/background/generate/utils.go b/pkg/background/generate/utils.go index 78cf7a8379a7..fd89bb4afbc8 100644 --- a/pkg/background/generate/utils.go +++ b/pkg/background/generate/utils.go @@ -29,3 +29,20 @@ func TriggerFromLabels(labels map[string]string) kyvernov1.ResourceSpec { APIVersion: apiVersion.String(), } } + +func buildPolicyWithAppliedRules(policy kyvernov1.PolicyInterface, expect string) (kyvernov1.PolicyInterface, bool) { + var rule *kyvernov1.Rule + p := policy.CreateDeepCopy() + for j := range p.GetSpec().Rules { + if p.GetSpec().Rules[j].Name == expect { + rule = &p.GetSpec().Rules[j] + break + } + } + if rule == nil { + return nil, false + } + + p.GetSpec().SetRules([]kyvernov1.Rule{*rule}) + return p, true +}