From 5195bc5bf298a9d81252c349e4231a7b91cbaec7 Mon Sep 17 00:00:00 2001 From: Pooja Singh Date: Tue, 9 Nov 2021 09:41:29 +0530 Subject: [PATCH] added skip status for generate (#2657) --- api/kyverno/v1/generaterequest_types.go | 3 ++ pkg/generate/generate.go | 39 +++++++++++++------------ pkg/generate/status.go | 18 ++++++++++++ 3 files changed, 42 insertions(+), 18 deletions(-) diff --git a/api/kyverno/v1/generaterequest_types.go b/api/kyverno/v1/generaterequest_types.go index af686d7e82..3a0b0a274d 100644 --- a/api/kyverno/v1/generaterequest_types.go +++ b/api/kyverno/v1/generaterequest_types.go @@ -101,6 +101,9 @@ const ( // Completed - the Generate Request Controller created resources defined in the policy. Completed GenerateRequestState = "Completed" + + // Skip - the Generate Request Controller skips to generate the resource. + Skip GenerateRequestState = "Skip" ) // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/generate/generate.go b/pkg/generate/generate.go index 430678b663..fb770ca5a8 100644 --- a/pkg/generate/generate.go +++ b/pkg/generate/generate.go @@ -34,6 +34,7 @@ func (c *Controller) processGR(gr *kyverno.GenerateRequest) error { var err error var resource *unstructured.Unstructured var genResources []kyverno.ResourceSpec + var precreatedResource bool // 1 - Check if the resource exists resource, err = getResource(c.client, gr.Spec.Resource, c.log) @@ -90,7 +91,7 @@ func (c *Controller) processGR(gr *kyverno.GenerateRequest) error { // 2 - Apply the generate policy on the resource namespaceLabels := pkgcommon.GetNamespaceSelectorsFromGenericInformer(resource.GetKind(), resource.GetNamespace(), c.nsInformer, logger) - genResources, err = c.applyGenerate(*resource, *gr, namespaceLabels) + genResources, precreatedResource, err = c.applyGenerate(*resource, *gr, namespaceLabels) if err != nil { // Need not update the status when policy doesn't apply on resource, because all the generate requests are removed by the cleanup controller @@ -105,12 +106,12 @@ func (c *Controller) processGR(gr *kyverno.GenerateRequest) error { } // 4 - Update Status - return updateStatus(c.statusControl, *gr, err, genResources) + return updateStatus(c.statusControl, *gr, err, genResources, precreatedResource) } const doesNotApply = "policy does not apply to resource" -func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyverno.GenerateRequest, namespaceLabels map[string]string) ([]kyverno.ResourceSpec, error) { +func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyverno.GenerateRequest, namespaceLabels map[string]string) ([]kyverno.ResourceSpec, bool, error) { logger := c.log.WithValues("name", gr.Name, "policy", gr.Spec.Policy, "kind", gr.Spec.Resource.Kind, "apiVersion", gr.Spec.Resource.APIVersion, "namespace", gr.Spec.Resource.Namespace, "name", gr.Spec.Resource.Name) // Get the list of rules to be applied // get policy @@ -136,11 +137,11 @@ func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyvern } } - return nil, nil + return nil, false, nil } logger.Error(err, "error in fetching policy") - return nil, err + return nil, false, err } requestString := gr.Spec.Context.AdmissionRequestInfo.AdmissionRequest @@ -156,31 +157,31 @@ func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyvern if err := ctx.AddRequest(&request); err != nil { logger.Error(err, "failed to load request in context") - return nil, err + return nil, false, err } resourceRaw, err := resource.MarshalJSON() if err != nil { logger.Error(err, "failed to marshal resource") - return nil, err + return nil, false, err } err = ctx.AddResource(resourceRaw) if err != nil { logger.Error(err, "failed to load resource in context") - return nil, err + return nil, false, err } err = ctx.AddUserInfo(gr.Spec.Context.UserRequestInfo) if err != nil { logger.Error(err, "failed to load SA in context") - return nil, err + return nil, false, err } err = ctx.AddServiceAccount(gr.Spec.Context.UserRequestInfo.AdmissionUserInfo.Username) if err != nil { logger.Error(err, "failed to load UserInfo in context") - return nil, err + return nil, false, err } if err := ctx.AddImageInfo(&resource); err != nil { @@ -203,7 +204,7 @@ func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyvern engineResponse := engine.Generate(policyContext) if len(engineResponse.PolicyResponse.Rules) == 0 { logger.V(4).Info(doesNotApply) - return nil, errors.New(doesNotApply) + return nil, false, errors.New(doesNotApply) } var applicableRules []string @@ -238,16 +239,18 @@ func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyvern return c.applyGeneratePolicy(logger, policyContext, gr, applicableRules) } -func updateStatus(statusControl StatusControlInterface, gr kyverno.GenerateRequest, err error, genResources []kyverno.ResourceSpec) error { +func updateStatus(statusControl StatusControlInterface, gr kyverno.GenerateRequest, err error, genResources []kyverno.ResourceSpec, precreatedResource bool) error { if err != nil { return statusControl.Failed(gr, err.Error(), genResources) + } else if precreatedResource { + return statusControl.Skip(gr, genResources) } // Generate request successfully processed return statusControl.Success(gr, genResources) } -func (c *Controller) applyGeneratePolicy(log logr.Logger, policyContext *engine.PolicyContext, gr kyverno.GenerateRequest, applicableRules []string) (genResources []kyverno.ResourceSpec, err error) { +func (c *Controller) applyGeneratePolicy(log logr.Logger, policyContext *engine.PolicyContext, gr kyverno.GenerateRequest, applicableRules []string) (genResources []kyverno.ResourceSpec, processExisting bool, err error) { // Get the response as the actions to be performed on the resource // - - substitute values policy := policyContext.Policy @@ -269,7 +272,7 @@ func (c *Controller) applyGeneratePolicy(log logr.Logger, policyContext *engine. } startTime := time.Now() - processExisting := false + processExisting = false var genResource kyverno.ResourceSpec if len(rule.MatchResources.Kinds) > 0 { @@ -283,12 +286,12 @@ func (c *Controller) applyGeneratePolicy(log logr.Logger, policyContext *engine. // add configmap json data to context if err := engine.LoadContext(log, rule.Context, resCache, policyContext, rule.Name); err != nil { log.Error(err, "cannot add configmaps to context") - return nil, err + return nil, processExisting, err } if rule, err = variables.SubstituteAllInRule(log, policyContext.JSONContext, rule); err != nil { log.Error(err, "variable substitution failed for rule %s", rule.Name) - return nil, err + return nil, processExisting, err } if !processExisting { @@ -296,14 +299,14 @@ func (c *Controller) applyGeneratePolicy(log logr.Logger, policyContext *engine. if err != nil { log.Error(err, "failed to apply generate rule", "policy", policy.Name, "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) } } - return genResources, nil + return genResources, processExisting, nil } func getResourceInfo(object map[string]interface{}) (kind, name, namespace, apiversion string, err error) { diff --git a/pkg/generate/status.go b/pkg/generate/status.go index 36c7bd5fd0..3d61e68d1c 100644 --- a/pkg/generate/status.go +++ b/pkg/generate/status.go @@ -15,6 +15,7 @@ import ( type StatusControlInterface interface { Failed(gr kyverno.GenerateRequest, message string, genResources []kyverno.ResourceSpec) error Success(gr kyverno.GenerateRequest, genResources []kyverno.ResourceSpec) error + Skip(gr kyverno.GenerateRequest, genResources []kyverno.ResourceSpec) error } // StatusControl is default implementaation of GRStatusControlInterface @@ -53,3 +54,20 @@ func (sc StatusControl) Success(gr kyverno.GenerateRequest, genResources []kyver log.Log.V(3).Info("updated generate request status", "name", gr.Name, "status", string(kyverno.Completed)) return nil } + +// Success sets the gr status.state to completed and clears message +func (sc StatusControl) Skip(gr kyverno.GenerateRequest, genResources []kyverno.ResourceSpec) error { + gr.Status.State = kyverno.Skip + gr.Status.Message = "" + // Update Generated Resources + gr.Status.GeneratedResources = genResources + + _, err := sc.client.KyvernoV1().GenerateRequests(config.KyvernoNamespace).UpdateStatus(context.TODO(), &gr, v1.UpdateOptions{}) + if err != nil && !errors.IsNotFound(err) { + log.Log.Error(err, "failed to update generate request status", "name", gr.Name) + return err + } + + log.Log.V(3).Info("updated generate request status", "name", gr.Name, "status", string(kyverno.Skip)) + return nil +}