From d558c12470decab703160bfc801d0aa7c2065cd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Charles-Edouard=20Br=C3=A9t=C3=A9ch=C3=A9?= Date: Fri, 9 Sep 2022 16:19:38 +0200 Subject: [PATCH] refactor: move generation handler out of webhooks package (#4570) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Charles-Edouard Brétéché --- .../resource/{ => generation}/generation.go | 124 ++++++++++---- .../{ => generation}/generation_test.go | 2 +- pkg/webhooks/resource/generation/utils.go | 159 ++++++++++++++++++ pkg/webhooks/resource/handlers.go | 4 +- pkg/webhooks/resource/updaterequest.go | 4 +- pkg/webhooks/resource/utils.go | 100 ----------- 6 files changed, 255 insertions(+), 138 deletions(-) rename pkg/webhooks/resource/{ => generation}/generation.go (59%) rename pkg/webhooks/resource/{ => generation}/generation_test.go (99%) create mode 100644 pkg/webhooks/resource/generation/utils.go diff --git a/pkg/webhooks/resource/generation.go b/pkg/webhooks/resource/generation/generation.go similarity index 59% rename from pkg/webhooks/resource/generation.go rename to pkg/webhooks/resource/generation/generation.go index aca5a6cc0f..f8142f6df3 100644 --- a/pkg/webhooks/resource/generation.go +++ b/pkg/webhooks/resource/generation/generation.go @@ -1,4 +1,4 @@ -package resource +package generation import ( "context" @@ -11,21 +11,75 @@ import ( kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1" "github.com/kyverno/kyverno/pkg/autogen" gen "github.com/kyverno/kyverno/pkg/background/generate" + "github.com/kyverno/kyverno/pkg/client/clientset/versioned" + kyvernov1beta1listers "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1beta1" + "github.com/kyverno/kyverno/pkg/clients/dclient" "github.com/kyverno/kyverno/pkg/common" "github.com/kyverno/kyverno/pkg/config" "github.com/kyverno/kyverno/pkg/engine" "github.com/kyverno/kyverno/pkg/engine/response" enginutils "github.com/kyverno/kyverno/pkg/engine/utils" "github.com/kyverno/kyverno/pkg/event" + "github.com/kyverno/kyverno/pkg/metrics" + webhookgenerate "github.com/kyverno/kyverno/pkg/webhooks/updaterequest" webhookutils "github.com/kyverno/kyverno/pkg/webhooks/utils" admissionv1 "k8s.io/api/admission/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + corev1listers "k8s.io/client-go/listers/core/v1" ) -// handleGenerate handles admission-requests for policies with generate rules -func (h *handlers) handleGenerate( - logger logr.Logger, +type GenerationHandler interface { + // TODO: why do we need to expose that ? + HandleUpdatesForGenerateRules(*admissionv1.AdmissionRequest, []kyvernov1.PolicyInterface) + Handle( + *metrics.MetricsConfig, + *admissionv1.AdmissionRequest, + []kyvernov1.PolicyInterface, + *engine.PolicyContext, + time.Time, + *chan int64, + *chan []*response.EngineResponse, + *chan []*response.EngineResponse, + ) +} + +func NewGenerationHandler( + log logr.Logger, + client dclient.Interface, + kyvernoClient versioned.Interface, + nsLister corev1listers.NamespaceLister, + urLister kyvernov1beta1listers.UpdateRequestNamespaceLister, + urGenerator webhookgenerate.Generator, + urUpdater webhookutils.UpdateRequestUpdater, + eventGen event.Interface, +) GenerationHandler { + return &generationHandler{ + log: log, + client: client, + kyvernoClient: kyvernoClient, + nsLister: nsLister, + urLister: urLister, + urGenerator: urGenerator, + urUpdater: urUpdater, + eventGen: eventGen, + } +} + +type generationHandler struct { + log logr.Logger + client dclient.Interface + kyvernoClient versioned.Interface + nsLister corev1listers.NamespaceLister + urLister kyvernov1beta1listers.UpdateRequestNamespaceLister + urGenerator webhookgenerate.Generator + urUpdater webhookutils.UpdateRequestUpdater + eventGen event.Interface +} + +// Handle handles admission-requests for policies with generate rules +func (h *generationHandler) Handle( + metricsConfig *metrics.MetricsConfig, request *admissionv1.AdmissionRequest, policies []kyvernov1.PolicyInterface, policyContext *engine.PolicyContext, @@ -34,7 +88,7 @@ func (h *handlers) handleGenerate( generateEngineResponsesSenderForAdmissionReviewDurationMetric *chan []*response.EngineResponse, generateEngineResponsesSenderForAdmissionRequestsCountMetric *chan []*response.EngineResponse, ) { - logger.V(6).Info("update request") + h.log.V(6).Info("update request") var engineResponses []*response.EngineResponse if (request.Operation == admissionv1.Create || request.Operation == admissionv1.Update) && len(policies) != 0 { @@ -42,12 +96,12 @@ func (h *handlers) handleGenerate( var rules []response.RuleResponse policyContext.Policy = policy if request.Kind.Kind != "Namespace" && request.Namespace != "" { - policyContext.NamespaceLabels = common.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, logger) + policyContext.NamespaceLabels = common.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, h.log) } engineResponse := engine.ApplyBackgroundChecks(policyContext) for _, rule := range engineResponse.PolicyResponse.Rules { if rule.Status != response.RuleStatusPass { - h.deleteGR(logger, engineResponse) + h.deleteGR(engineResponse) continue } rules = append(rules, rule) @@ -60,9 +114,9 @@ func (h *handlers) handleGenerate( } // registering the kyverno_policy_results_total metric concurrently - go webhookutils.RegisterPolicyResultsMetricGeneration(logger, h.metricsConfig, string(request.Operation), policy, *engineResponse) + go webhookutils.RegisterPolicyResultsMetricGeneration(h.log, metricsConfig, string(request.Operation), policy, *engineResponse) // registering the kyverno_policy_execution_duration_seconds metric concurrently - go webhookutils.RegisterPolicyExecutionDurationMetricGenerate(logger, h.metricsConfig, string(request.Operation), policy, *engineResponse) + go webhookutils.RegisterPolicyExecutionDurationMetricGenerate(h.log, metricsConfig, string(request.Operation), policy, *engineResponse) } if failedResponse := applyUpdateRequest(request, kyvernov1beta1.Generate, h.urGenerator, policyContext.AdmissionInfo, request.Operation, engineResponses...); failedResponse != nil { @@ -76,7 +130,7 @@ func (h *handlers) handleGenerate( } if request.Operation == admissionv1.Update { - h.handleUpdatesForGenerateRules(logger, request, policies) + h.HandleUpdatesForGenerateRules(request, policies) } // sending the admission request latency to other goroutine (reporting the metrics) over the channel @@ -86,38 +140,38 @@ func (h *handlers) handleGenerate( *generateEngineResponsesSenderForAdmissionRequestsCountMetric <- engineResponses } -// handleUpdatesForGenerateRules handles admission-requests for update -func (h *handlers) handleUpdatesForGenerateRules(logger logr.Logger, request *admissionv1.AdmissionRequest, policies []kyvernov1.PolicyInterface) { +// HandleUpdatesForGenerateRules handles admission-requests for update +func (h *generationHandler) HandleUpdatesForGenerateRules(request *admissionv1.AdmissionRequest, policies []kyvernov1.PolicyInterface) { if request.Operation != admissionv1.Update { return } resource, err := enginutils.ConvertToUnstructured(request.OldObject.Raw) if err != nil { - logger.Error(err, "failed to convert object resource to unstructured format") + h.log.Error(err, "failed to convert object resource to unstructured format") } resLabels := resource.GetLabels() if resLabels["generate.kyverno.io/clone-policy-name"] != "" { - h.handleUpdateGenerateSourceResource(resLabels, logger) + h.handleUpdateGenerateSourceResource(resLabels) } if resLabels["app.kubernetes.io/managed-by"] == "kyverno" && resLabels["policy.kyverno.io/synchronize"] == "enable" && request.Operation == admissionv1.Update { - h.handleUpdateGenerateTargetResource(request, policies, resLabels, logger) + h.handleUpdateGenerateTargetResource(request, policies, resLabels) } } // handleUpdateGenerateSourceResource - handles update of clone source for generate policy -func (h *handlers) handleUpdateGenerateSourceResource(resLabels map[string]string, logger logr.Logger) { +func (h *generationHandler) handleUpdateGenerateSourceResource(resLabels map[string]string) { policyNames := strings.Split(resLabels["generate.kyverno.io/clone-policy-name"], ",") for _, policyName := range policyNames { // check if the policy exists _, err := h.kyvernoClient.KyvernoV1().ClusterPolicies().Get(context.TODO(), policyName, metav1.GetOptions{}) if err != nil { if strings.Contains(err.Error(), "not found") { - logger.V(4).Info("skipping update of update request as policy is deleted") + h.log.V(4).Info("skipping update of update request as policy is deleted") } else { - logger.Error(err, "failed to get generate policy", "Name", policyName) + h.log.Error(err, "failed to get generate policy", "Name", policyName) } } else { selector := labels.SelectorFromSet(labels.Set(map[string]string{ @@ -126,23 +180,23 @@ func (h *handlers) handleUpdateGenerateSourceResource(resLabels map[string]strin urList, err := h.urLister.List(selector) if err != nil { - logger.Error(err, "failed to get update request for the resource", "label", kyvernov1beta1.URGeneratePolicyLabel) + h.log.Error(err, "failed to get update request for the resource", "label", kyvernov1beta1.URGeneratePolicyLabel) return } for _, ur := range urList { - h.urUpdater.UpdateAnnotation(logger, ur.GetName()) + h.urUpdater.UpdateAnnotation(h.log, ur.GetName()) } } } } // handleUpdateGenerateTargetResource - handles update of target resource for generate policy -func (h *handlers) handleUpdateGenerateTargetResource(request *admissionv1.AdmissionRequest, policies []kyvernov1.PolicyInterface, resLabels map[string]string, logger logr.Logger) { +func (h *generationHandler) handleUpdateGenerateTargetResource(request *admissionv1.AdmissionRequest, policies []kyvernov1.PolicyInterface, resLabels map[string]string) { enqueueBool := false newRes, err := enginutils.ConvertToUnstructured(request.Object.Raw) if err != nil { - logger.Error(err, "failed to convert object resource to unstructured format") + h.log.Error(err, "failed to convert object resource to unstructured format") } policyName := resLabels["policy.kyverno.io/policy-name"] @@ -151,19 +205,19 @@ func (h *handlers) handleUpdateGenerateTargetResource(request *admissionv1.Admis policy, err := h.kyvernoClient.KyvernoV1().ClusterPolicies().Get(context.TODO(), policyName, metav1.GetOptions{}) if err != nil { - logger.Error(err, "failed to get policy from kyverno client.", "policy name", policyName) + h.log.Error(err, "failed to get policy from kyverno client.", "policy name", policyName) return } for _, rule := range autogen.ComputeRules(policy) { if rule.Generation.Kind == targetSourceKind && rule.Generation.Name == targetSourceName { - updatedRule, err := getGeneratedByResource(newRes, resLabels, h.client, rule, logger) + updatedRule, err := getGeneratedByResource(newRes, resLabels, h.client, rule, h.log) if err != nil { - logger.V(4).Info("skipping generate policy and resource pattern validaton", "error", err) + h.log.V(4).Info("skipping generate policy and resource pattern validaton", "error", err) } else { data := updatedRule.Generation.DeepCopy().GetData() if data != nil { - if _, err := gen.ValidateResourceWithPattern(logger, newRes.Object, data); err != nil { + if _, err := gen.ValidateResourceWithPattern(h.log, newRes.Object, data); err != nil { enqueueBool = true break } @@ -173,13 +227,13 @@ func (h *handlers) handleUpdateGenerateTargetResource(request *admissionv1.Admis if cloneName != "" { obj, err := h.client.GetResource("", rule.Generation.Kind, rule.Generation.Clone.Namespace, rule.Generation.Clone.Name) if err != nil { - logger.Error(err, fmt.Sprintf("source resource %s/%s/%s not found.", rule.Generation.Kind, rule.Generation.Clone.Namespace, rule.Generation.Clone.Name)) + h.log.Error(err, fmt.Sprintf("source resource %s/%s/%s not found.", rule.Generation.Kind, rule.Generation.Clone.Namespace, rule.Generation.Clone.Name)) continue } - sourceObj, newResObj := stripNonPolicyFields(obj.Object, newRes.Object, logger) + sourceObj, newResObj := stripNonPolicyFields(obj.Object, newRes.Object, h.log) - if _, err := gen.ValidateResourceWithPattern(logger, newResObj, sourceObj); err != nil { + if _, err := gen.ValidateResourceWithPattern(h.log, newResObj, sourceObj); err != nil { enqueueBool = true break } @@ -192,15 +246,15 @@ func (h *handlers) handleUpdateGenerateTargetResource(request *admissionv1.Admis urName := resLabels["policy.kyverno.io/gr-name"] ur, err := h.urLister.Get(urName) if err != nil { - logger.Error(err, "failed to get update request", "name", urName) + h.log.Error(err, "failed to get update request", "name", urName) return } - h.urUpdater.UpdateAnnotation(logger, ur.GetName()) + h.urUpdater.UpdateAnnotation(h.log, ur.GetName()) } } -func (h *handlers) deleteGR(logger logr.Logger, engineResponse *response.EngineResponse) { - logger.V(4).Info("querying all update requests") +func (h *generationHandler) deleteGR(engineResponse *response.EngineResponse) { + h.log.V(4).Info("querying all update requests") selector := labels.SelectorFromSet(labels.Set(map[string]string{ kyvernov1beta1.URGeneratePolicyLabel: engineResponse.PolicyResponse.Policy.Name, kyvernov1beta1.URGenerateResourceNameLabel: engineResponse.PolicyResponse.Resource.Name, @@ -210,14 +264,14 @@ func (h *handlers) deleteGR(logger logr.Logger, engineResponse *response.EngineR urList, err := h.urLister.List(selector) if err != nil { - logger.Error(err, "failed to get update request for the resource", "kind", engineResponse.PolicyResponse.Resource.Kind, "name", engineResponse.PolicyResponse.Resource.Name, "namespace", engineResponse.PolicyResponse.Resource.Namespace) + h.log.Error(err, "failed to get update request for the resource", "kind", engineResponse.PolicyResponse.Resource.Kind, "name", engineResponse.PolicyResponse.Resource.Name, "namespace", engineResponse.PolicyResponse.Resource.Namespace) return } for _, v := range urList { err := h.kyvernoClient.KyvernoV1beta1().UpdateRequests(config.KyvernoNamespace()).Delete(context.TODO(), v.GetName(), metav1.DeleteOptions{}) if err != nil { - logger.Error(err, "failed to update ur") + h.log.Error(err, "failed to update ur") } } } diff --git a/pkg/webhooks/resource/generation_test.go b/pkg/webhooks/resource/generation/generation_test.go similarity index 99% rename from pkg/webhooks/resource/generation_test.go rename to pkg/webhooks/resource/generation/generation_test.go index bf23872f74..328ec0b04b 100644 --- a/pkg/webhooks/resource/generation_test.go +++ b/pkg/webhooks/resource/generation/generation_test.go @@ -1,4 +1,4 @@ -package resource +package generation import ( "reflect" diff --git a/pkg/webhooks/resource/generation/utils.go b/pkg/webhooks/resource/generation/utils.go new file mode 100644 index 0000000000..fba6af1683 --- /dev/null +++ b/pkg/webhooks/resource/generation/utils.go @@ -0,0 +1,159 @@ +package generation + +import ( + "encoding/json" + "strings" + + "github.com/go-logr/logr" + kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" + kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1" + "github.com/kyverno/kyverno/pkg/clients/dclient" + enginectx "github.com/kyverno/kyverno/pkg/engine/context" + "github.com/kyverno/kyverno/pkg/engine/response" + "github.com/kyverno/kyverno/pkg/engine/variables" + "github.com/kyverno/kyverno/pkg/webhooks/updaterequest" + admissionv1 "k8s.io/api/admission/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +func getGeneratedByResource(newRes *unstructured.Unstructured, resLabels map[string]string, client dclient.Interface, rule kyvernov1.Rule, logger logr.Logger) (kyvernov1.Rule, error) { + var apiVersion, kind, name, namespace string + sourceRequest := &admissionv1.AdmissionRequest{} + kind = resLabels["kyverno.io/generated-by-kind"] + name = resLabels["kyverno.io/generated-by-name"] + if kind != "Namespace" { + namespace = resLabels["kyverno.io/generated-by-namespace"] + } + obj, err := client.GetResource(apiVersion, kind, namespace, name) + if err != nil { + logger.Error(err, "source resource not found.") + return rule, err + } + rawObj, err := json.Marshal(obj) + if err != nil { + logger.Error(err, "failed to marshal resource") + return rule, err + } + sourceRequest.Object.Raw = rawObj + sourceRequest.Operation = "CREATE" + ctx := enginectx.NewContext() + if err := ctx.AddRequest(sourceRequest); err != nil { + logger.Error(err, "failed to load incoming request in context") + return rule, err + } + if rule, err = variables.SubstituteAllInRule(logger, ctx, rule); err != nil { + logger.Error(err, "variable substitution failed for rule %s", rule.Name) + return rule, err + } + return rule, nil +} + +// stripNonPolicyFields - remove feilds which get updated with each request by kyverno and are non policy fields +func stripNonPolicyFields(obj, newRes map[string]interface{}, logger logr.Logger) (map[string]interface{}, map[string]interface{}) { + if metadata, found := obj["metadata"]; found { + requiredMetadataInObj := make(map[string]interface{}) + if annotations, found := metadata.(map[string]interface{})["annotations"]; found { + delete(annotations.(map[string]interface{}), "kubectl.kubernetes.io/last-applied-configuration") + requiredMetadataInObj["annotations"] = annotations + } + + if labels, found := metadata.(map[string]interface{})["labels"]; found { + delete(labels.(map[string]interface{}), "generate.kyverno.io/clone-policy-name") + requiredMetadataInObj["labels"] = labels + } + obj["metadata"] = requiredMetadataInObj + } + + if metadata, found := newRes["metadata"]; found { + requiredMetadataInNewRes := make(map[string]interface{}) + if annotations, found := metadata.(map[string]interface{})["annotations"]; found { + requiredMetadataInNewRes["annotations"] = annotations + } + + if labels, found := metadata.(map[string]interface{})["labels"]; found { + requiredMetadataInNewRes["labels"] = labels + } + newRes["metadata"] = requiredMetadataInNewRes + } + + delete(obj, "status") + + if _, found := obj["spec"]; found { + delete(obj["spec"].(map[string]interface{}), "tolerations") + } + + if dataMap, found := obj["data"]; found { + keyInData := make([]string, 0) + switch dataMap := dataMap.(type) { + case map[string]interface{}: + for k := range dataMap { + keyInData = append(keyInData, k) + } + } + + if len(keyInData) > 0 { + for _, dataKey := range keyInData { + originalResourceData := dataMap.(map[string]interface{})[dataKey] + replaceData := strings.Replace(originalResourceData.(string), "\n", "", -1) + dataMap.(map[string]interface{})[dataKey] = replaceData + + newResourceData := newRes["data"].(map[string]interface{})[dataKey] + replacenewResourceData := strings.Replace(newResourceData.(string), "\n", "", -1) + newRes["data"].(map[string]interface{})[dataKey] = replacenewResourceData + } + } else { + logger.V(4).Info("data is not of type map[string]interface{}") + } + } + + return obj, newRes +} + +type updateRequestResponse struct { + ur kyvernov1beta1.UpdateRequestSpec + err error +} + +func applyUpdateRequest(request *admissionv1.AdmissionRequest, ruleType kyvernov1beta1.RequestType, grGenerator updaterequest.Generator, userRequestInfo kyvernov1beta1.RequestInfo, + action admissionv1.Operation, engineResponses ...*response.EngineResponse, +) (failedUpdateRequest []updateRequestResponse) { + admissionRequestInfo := kyvernov1beta1.AdmissionRequestInfoObject{ + AdmissionRequest: request, + Operation: action, + } + + for _, er := range engineResponses { + ur := transform(admissionRequestInfo, userRequestInfo, er, ruleType) + if err := grGenerator.Apply(ur, action); err != nil { + failedUpdateRequest = append(failedUpdateRequest, updateRequestResponse{ur: ur, err: err}) + } + } + + return +} + +func transform(admissionRequestInfo kyvernov1beta1.AdmissionRequestInfoObject, userRequestInfo kyvernov1beta1.RequestInfo, er *response.EngineResponse, ruleType kyvernov1beta1.RequestType) kyvernov1beta1.UpdateRequestSpec { + var PolicyNameNamespaceKey string + if er.PolicyResponse.Policy.Namespace != "" { + PolicyNameNamespaceKey = er.PolicyResponse.Policy.Namespace + "/" + er.PolicyResponse.Policy.Name + } else { + PolicyNameNamespaceKey = er.PolicyResponse.Policy.Name + } + + ur := kyvernov1beta1.UpdateRequestSpec{ + Type: ruleType, + Policy: PolicyNameNamespaceKey, + Resource: kyvernov1.ResourceSpec{ + Kind: er.PolicyResponse.Resource.Kind, + Namespace: er.PolicyResponse.Resource.Namespace, + Name: er.PolicyResponse.Resource.Name, + APIVersion: er.PolicyResponse.Resource.APIVersion, + }, + Context: kyvernov1beta1.UpdateRequestSpecContext{ + UserRequestInfo: userRequestInfo, + AdmissionRequestInfo: admissionRequestInfo, + }, + } + + return ur +} diff --git a/pkg/webhooks/resource/handlers.go b/pkg/webhooks/resource/handlers.go index 7c3f250822..8f1e48e432 100644 --- a/pkg/webhooks/resource/handlers.go +++ b/pkg/webhooks/resource/handlers.go @@ -24,6 +24,7 @@ import ( jsonutils "github.com/kyverno/kyverno/pkg/utils/json" "github.com/kyverno/kyverno/pkg/webhooks" "github.com/kyverno/kyverno/pkg/webhooks/resource/audit" + "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" @@ -117,7 +118,8 @@ func (h *handlers) Validate(logger logr.Logger, request *admissionv1.AdmissionRe } if len(generatePolicies) == 0 && request.Operation == admissionv1.Update { // handle generate source resource updates - go h.handleUpdatesForGenerateRules(logger, request, []kyvernov1.PolicyInterface{}) + gh := generation.NewGenerationHandler(logger, h.client, h.kyvernoClient, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen) + go gh.HandleUpdatesForGenerateRules(request, []kyvernov1.PolicyInterface{}) } logger.V(4).Info("processing policies for validate admission request", "validate", len(policies), "mutate", len(mutatePolicies), "generate", len(generatePolicies)) diff --git a/pkg/webhooks/resource/updaterequest.go b/pkg/webhooks/resource/updaterequest.go index b28836f81f..6e060ba167 100644 --- a/pkg/webhooks/resource/updaterequest.go +++ b/pkg/webhooks/resource/updaterequest.go @@ -10,6 +10,7 @@ import ( "github.com/kyverno/kyverno/pkg/engine" "github.com/kyverno/kyverno/pkg/engine/response" "github.com/kyverno/kyverno/pkg/event" + "github.com/kyverno/kyverno/pkg/webhooks/resource/generation" webhookutils "github.com/kyverno/kyverno/pkg/webhooks/utils" admissionv1 "k8s.io/api/admission/v1" ) @@ -20,8 +21,9 @@ func (h *handlers) createUpdateRequests(logger logr.Logger, request *admissionv1 generateEngineResponsesSenderForAdmissionReviewDurationMetric := make(chan []*response.EngineResponse, 1) generateEngineResponsesSenderForAdmissionRequestsCountMetric := make(chan []*response.EngineResponse, 1) + gh := generation.NewGenerationHandler(logger, h.client, h.kyvernoClient, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen) go h.handleMutateExisting(logger, request, mutatePolicies, policyContext, ts) - go h.handleGenerate(logger, request, generatePolicies, policyContext, ts, &admissionReviewCompletionLatencyChannel, &generateEngineResponsesSenderForAdmissionReviewDurationMetric, &generateEngineResponsesSenderForAdmissionRequestsCountMetric) + go gh.Handle(h.metricsConfig, request, generatePolicies, policyContext, ts, &admissionReviewCompletionLatencyChannel, &generateEngineResponsesSenderForAdmissionReviewDurationMetric, &generateEngineResponsesSenderForAdmissionRequestsCountMetric) go webhookutils.RegisterAdmissionReviewDurationMetricGenerate(logger, h.metricsConfig, string(request.Operation), &admissionReviewCompletionLatencyChannel, &generateEngineResponsesSenderForAdmissionReviewDurationMetric) go webhookutils.RegisterAdmissionRequestsMetricGenerate(logger, h.metricsConfig, string(request.Operation), &generateEngineResponsesSenderForAdmissionRequestsCountMetric) diff --git a/pkg/webhooks/resource/utils.go b/pkg/webhooks/resource/utils.go index c4eef8cbc6..10daa5be4b 100644 --- a/pkg/webhooks/resource/utils.go +++ b/pkg/webhooks/resource/utils.go @@ -1,21 +1,14 @@ package resource import ( - "encoding/json" - "strings" - "github.com/go-logr/logr" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1" - "github.com/kyverno/kyverno/pkg/clients/dclient" - enginectx "github.com/kyverno/kyverno/pkg/engine/context" "github.com/kyverno/kyverno/pkg/engine/response" engineutils "github.com/kyverno/kyverno/pkg/engine/utils" - "github.com/kyverno/kyverno/pkg/engine/variables" admissionutils "github.com/kyverno/kyverno/pkg/utils/admission" "github.com/kyverno/kyverno/pkg/webhooks/updaterequest" admissionv1 "k8s.io/api/admission/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) type updateRequestResponse struct { @@ -48,99 +41,6 @@ func processResourceWithPatches(patch []byte, resource []byte, log logr.Logger) return resource } -func getGeneratedByResource(newRes *unstructured.Unstructured, resLabels map[string]string, client dclient.Interface, rule kyvernov1.Rule, logger logr.Logger) (kyvernov1.Rule, error) { - var apiVersion, kind, name, namespace string - sourceRequest := &admissionv1.AdmissionRequest{} - kind = resLabels["kyverno.io/generated-by-kind"] - name = resLabels["kyverno.io/generated-by-name"] - if kind != "Namespace" { - namespace = resLabels["kyverno.io/generated-by-namespace"] - } - obj, err := client.GetResource(apiVersion, kind, namespace, name) - if err != nil { - logger.Error(err, "source resource not found.") - return rule, err - } - rawObj, err := json.Marshal(obj) - if err != nil { - logger.Error(err, "failed to marshal resource") - return rule, err - } - sourceRequest.Object.Raw = rawObj - sourceRequest.Operation = "CREATE" - ctx := enginectx.NewContext() - if err := ctx.AddRequest(sourceRequest); err != nil { - logger.Error(err, "failed to load incoming request in context") - return rule, err - } - if rule, err = variables.SubstituteAllInRule(logger, ctx, rule); err != nil { - logger.Error(err, "variable substitution failed for rule %s", rule.Name) - return rule, err - } - return rule, nil -} - -// stripNonPolicyFields - remove feilds which get updated with each request by kyverno and are non policy fields -func stripNonPolicyFields(obj, newRes map[string]interface{}, logger logr.Logger) (map[string]interface{}, map[string]interface{}) { - if metadata, found := obj["metadata"]; found { - requiredMetadataInObj := make(map[string]interface{}) - if annotations, found := metadata.(map[string]interface{})["annotations"]; found { - delete(annotations.(map[string]interface{}), "kubectl.kubernetes.io/last-applied-configuration") - requiredMetadataInObj["annotations"] = annotations - } - - if labels, found := metadata.(map[string]interface{})["labels"]; found { - delete(labels.(map[string]interface{}), "generate.kyverno.io/clone-policy-name") - requiredMetadataInObj["labels"] = labels - } - obj["metadata"] = requiredMetadataInObj - } - - if metadata, found := newRes["metadata"]; found { - requiredMetadataInNewRes := make(map[string]interface{}) - if annotations, found := metadata.(map[string]interface{})["annotations"]; found { - requiredMetadataInNewRes["annotations"] = annotations - } - - if labels, found := metadata.(map[string]interface{})["labels"]; found { - requiredMetadataInNewRes["labels"] = labels - } - newRes["metadata"] = requiredMetadataInNewRes - } - - delete(obj, "status") - - if _, found := obj["spec"]; found { - delete(obj["spec"].(map[string]interface{}), "tolerations") - } - - if dataMap, found := obj["data"]; found { - keyInData := make([]string, 0) - switch dataMap := dataMap.(type) { - case map[string]interface{}: - for k := range dataMap { - keyInData = append(keyInData, k) - } - } - - if len(keyInData) > 0 { - for _, dataKey := range keyInData { - originalResourceData := dataMap.(map[string]interface{})[dataKey] - replaceData := strings.Replace(originalResourceData.(string), "\n", "", -1) - dataMap.(map[string]interface{})[dataKey] = replaceData - - newResourceData := newRes["data"].(map[string]interface{})[dataKey] - replacenewResourceData := strings.Replace(newResourceData.(string), "\n", "", -1) - newRes["data"].(map[string]interface{})[dataKey] = replacenewResourceData - } - } else { - logger.V(4).Info("data is not of type map[string]interface{}") - } - } - - return obj, newRes -} - func applyUpdateRequest(request *admissionv1.AdmissionRequest, ruleType kyvernov1beta1.RequestType, grGenerator updaterequest.Generator, userRequestInfo kyvernov1beta1.RequestInfo, action admissionv1.Operation, engineResponses ...*response.EngineResponse, ) (failedUpdateRequest []updateRequestResponse) {