1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-05 07:26:55 +00:00

refactor: move generation handler out of webhooks package (#4570)

Signed-off-by: Charles-Edouard Brétéché <charled.breteche@gmail.com>
This commit is contained in:
Charles-Edouard Brétéché 2022-09-09 16:19:38 +02:00 committed by GitHub
parent 10638362dc
commit d558c12470
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 255 additions and 138 deletions

View file

@ -1,4 +1,4 @@
package resource package generation
import ( import (
"context" "context"
@ -11,21 +11,75 @@ import (
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1" kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
"github.com/kyverno/kyverno/pkg/autogen" "github.com/kyverno/kyverno/pkg/autogen"
gen "github.com/kyverno/kyverno/pkg/background/generate" 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/common"
"github.com/kyverno/kyverno/pkg/config" "github.com/kyverno/kyverno/pkg/config"
"github.com/kyverno/kyverno/pkg/engine" "github.com/kyverno/kyverno/pkg/engine"
"github.com/kyverno/kyverno/pkg/engine/response" "github.com/kyverno/kyverno/pkg/engine/response"
enginutils "github.com/kyverno/kyverno/pkg/engine/utils" enginutils "github.com/kyverno/kyverno/pkg/engine/utils"
"github.com/kyverno/kyverno/pkg/event" "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" webhookutils "github.com/kyverno/kyverno/pkg/webhooks/utils"
admissionv1 "k8s.io/api/admission/v1" admissionv1 "k8s.io/api/admission/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
corev1listers "k8s.io/client-go/listers/core/v1"
) )
// handleGenerate handles admission-requests for policies with generate rules type GenerationHandler interface {
func (h *handlers) handleGenerate( // TODO: why do we need to expose that ?
logger logr.Logger, 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, request *admissionv1.AdmissionRequest,
policies []kyvernov1.PolicyInterface, policies []kyvernov1.PolicyInterface,
policyContext *engine.PolicyContext, policyContext *engine.PolicyContext,
@ -34,7 +88,7 @@ func (h *handlers) handleGenerate(
generateEngineResponsesSenderForAdmissionReviewDurationMetric *chan []*response.EngineResponse, generateEngineResponsesSenderForAdmissionReviewDurationMetric *chan []*response.EngineResponse,
generateEngineResponsesSenderForAdmissionRequestsCountMetric *chan []*response.EngineResponse, generateEngineResponsesSenderForAdmissionRequestsCountMetric *chan []*response.EngineResponse,
) { ) {
logger.V(6).Info("update request") h.log.V(6).Info("update request")
var engineResponses []*response.EngineResponse var engineResponses []*response.EngineResponse
if (request.Operation == admissionv1.Create || request.Operation == admissionv1.Update) && len(policies) != 0 { if (request.Operation == admissionv1.Create || request.Operation == admissionv1.Update) && len(policies) != 0 {
@ -42,12 +96,12 @@ func (h *handlers) handleGenerate(
var rules []response.RuleResponse var rules []response.RuleResponse
policyContext.Policy = policy policyContext.Policy = policy
if request.Kind.Kind != "Namespace" && request.Namespace != "" { 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) engineResponse := engine.ApplyBackgroundChecks(policyContext)
for _, rule := range engineResponse.PolicyResponse.Rules { for _, rule := range engineResponse.PolicyResponse.Rules {
if rule.Status != response.RuleStatusPass { if rule.Status != response.RuleStatusPass {
h.deleteGR(logger, engineResponse) h.deleteGR(engineResponse)
continue continue
} }
rules = append(rules, rule) rules = append(rules, rule)
@ -60,9 +114,9 @@ func (h *handlers) handleGenerate(
} }
// registering the kyverno_policy_results_total metric concurrently // 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 // 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 { 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 { 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 // sending the admission request latency to other goroutine (reporting the metrics) over the channel
@ -86,38 +140,38 @@ func (h *handlers) handleGenerate(
*generateEngineResponsesSenderForAdmissionRequestsCountMetric <- engineResponses *generateEngineResponsesSenderForAdmissionRequestsCountMetric <- engineResponses
} }
// handleUpdatesForGenerateRules handles admission-requests for update // HandleUpdatesForGenerateRules handles admission-requests for update
func (h *handlers) handleUpdatesForGenerateRules(logger logr.Logger, request *admissionv1.AdmissionRequest, policies []kyvernov1.PolicyInterface) { func (h *generationHandler) HandleUpdatesForGenerateRules(request *admissionv1.AdmissionRequest, policies []kyvernov1.PolicyInterface) {
if request.Operation != admissionv1.Update { if request.Operation != admissionv1.Update {
return return
} }
resource, err := enginutils.ConvertToUnstructured(request.OldObject.Raw) resource, err := enginutils.ConvertToUnstructured(request.OldObject.Raw)
if err != nil { 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() resLabels := resource.GetLabels()
if resLabels["generate.kyverno.io/clone-policy-name"] != "" { 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 { 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 // 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"], ",") policyNames := strings.Split(resLabels["generate.kyverno.io/clone-policy-name"], ",")
for _, policyName := range policyNames { for _, policyName := range policyNames {
// check if the policy exists // check if the policy exists
_, err := h.kyvernoClient.KyvernoV1().ClusterPolicies().Get(context.TODO(), policyName, metav1.GetOptions{}) _, err := h.kyvernoClient.KyvernoV1().ClusterPolicies().Get(context.TODO(), policyName, metav1.GetOptions{})
if err != nil { if err != nil {
if strings.Contains(err.Error(), "not found") { 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 { } else {
logger.Error(err, "failed to get generate policy", "Name", policyName) h.log.Error(err, "failed to get generate policy", "Name", policyName)
} }
} else { } else {
selector := labels.SelectorFromSet(labels.Set(map[string]string{ 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) urList, err := h.urLister.List(selector)
if err != nil { 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 return
} }
for _, ur := range urList { 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 // 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 enqueueBool := false
newRes, err := enginutils.ConvertToUnstructured(request.Object.Raw) newRes, err := enginutils.ConvertToUnstructured(request.Object.Raw)
if err != nil { 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"] 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{}) policy, err := h.kyvernoClient.KyvernoV1().ClusterPolicies().Get(context.TODO(), policyName, metav1.GetOptions{})
if err != nil { 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 return
} }
for _, rule := range autogen.ComputeRules(policy) { for _, rule := range autogen.ComputeRules(policy) {
if rule.Generation.Kind == targetSourceKind && rule.Generation.Name == targetSourceName { 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 { 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 { } else {
data := updatedRule.Generation.DeepCopy().GetData() data := updatedRule.Generation.DeepCopy().GetData()
if data != nil { 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 enqueueBool = true
break break
} }
@ -173,13 +227,13 @@ func (h *handlers) handleUpdateGenerateTargetResource(request *admissionv1.Admis
if cloneName != "" { if cloneName != "" {
obj, err := h.client.GetResource("", rule.Generation.Kind, rule.Generation.Clone.Namespace, rule.Generation.Clone.Name) obj, err := h.client.GetResource("", rule.Generation.Kind, rule.Generation.Clone.Namespace, rule.Generation.Clone.Name)
if err != nil { 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 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 enqueueBool = true
break break
} }
@ -192,15 +246,15 @@ func (h *handlers) handleUpdateGenerateTargetResource(request *admissionv1.Admis
urName := resLabels["policy.kyverno.io/gr-name"] urName := resLabels["policy.kyverno.io/gr-name"]
ur, err := h.urLister.Get(urName) ur, err := h.urLister.Get(urName)
if err != nil { 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 return
} }
h.urUpdater.UpdateAnnotation(logger, ur.GetName()) h.urUpdater.UpdateAnnotation(h.log, ur.GetName())
} }
} }
func (h *handlers) deleteGR(logger logr.Logger, engineResponse *response.EngineResponse) { func (h *generationHandler) deleteGR(engineResponse *response.EngineResponse) {
logger.V(4).Info("querying all update requests") h.log.V(4).Info("querying all update requests")
selector := labels.SelectorFromSet(labels.Set(map[string]string{ selector := labels.SelectorFromSet(labels.Set(map[string]string{
kyvernov1beta1.URGeneratePolicyLabel: engineResponse.PolicyResponse.Policy.Name, kyvernov1beta1.URGeneratePolicyLabel: engineResponse.PolicyResponse.Policy.Name,
kyvernov1beta1.URGenerateResourceNameLabel: engineResponse.PolicyResponse.Resource.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) urList, err := h.urLister.List(selector)
if err != nil { 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 return
} }
for _, v := range urList { for _, v := range urList {
err := h.kyvernoClient.KyvernoV1beta1().UpdateRequests(config.KyvernoNamespace()).Delete(context.TODO(), v.GetName(), metav1.DeleteOptions{}) err := h.kyvernoClient.KyvernoV1beta1().UpdateRequests(config.KyvernoNamespace()).Delete(context.TODO(), v.GetName(), metav1.DeleteOptions{})
if err != nil { if err != nil {
logger.Error(err, "failed to update ur") h.log.Error(err, "failed to update ur")
} }
} }
} }

View file

@ -1,4 +1,4 @@
package resource package generation
import ( import (
"reflect" "reflect"

View file

@ -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
}

View file

@ -24,6 +24,7 @@ import (
jsonutils "github.com/kyverno/kyverno/pkg/utils/json" jsonutils "github.com/kyverno/kyverno/pkg/utils/json"
"github.com/kyverno/kyverno/pkg/webhooks" "github.com/kyverno/kyverno/pkg/webhooks"
"github.com/kyverno/kyverno/pkg/webhooks/resource/audit" "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/imageverification"
"github.com/kyverno/kyverno/pkg/webhooks/resource/mutation" "github.com/kyverno/kyverno/pkg/webhooks/resource/mutation"
"github.com/kyverno/kyverno/pkg/webhooks/resource/validation" "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 { if len(generatePolicies) == 0 && request.Operation == admissionv1.Update {
// handle generate source resource updates // 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)) logger.V(4).Info("processing policies for validate admission request", "validate", len(policies), "mutate", len(mutatePolicies), "generate", len(generatePolicies))

View file

@ -10,6 +10,7 @@ import (
"github.com/kyverno/kyverno/pkg/engine" "github.com/kyverno/kyverno/pkg/engine"
"github.com/kyverno/kyverno/pkg/engine/response" "github.com/kyverno/kyverno/pkg/engine/response"
"github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/event"
"github.com/kyverno/kyverno/pkg/webhooks/resource/generation"
webhookutils "github.com/kyverno/kyverno/pkg/webhooks/utils" webhookutils "github.com/kyverno/kyverno/pkg/webhooks/utils"
admissionv1 "k8s.io/api/admission/v1" 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) generateEngineResponsesSenderForAdmissionReviewDurationMetric := make(chan []*response.EngineResponse, 1)
generateEngineResponsesSenderForAdmissionRequestsCountMetric := 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.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.RegisterAdmissionReviewDurationMetricGenerate(logger, h.metricsConfig, string(request.Operation), &admissionReviewCompletionLatencyChannel, &generateEngineResponsesSenderForAdmissionReviewDurationMetric)
go webhookutils.RegisterAdmissionRequestsMetricGenerate(logger, h.metricsConfig, string(request.Operation), &generateEngineResponsesSenderForAdmissionRequestsCountMetric) go webhookutils.RegisterAdmissionRequestsMetricGenerate(logger, h.metricsConfig, string(request.Operation), &generateEngineResponsesSenderForAdmissionRequestsCountMetric)

View file

@ -1,21 +1,14 @@
package resource package resource
import ( import (
"encoding/json"
"strings"
"github.com/go-logr/logr" "github.com/go-logr/logr"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1" 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/response"
engineutils "github.com/kyverno/kyverno/pkg/engine/utils" engineutils "github.com/kyverno/kyverno/pkg/engine/utils"
"github.com/kyverno/kyverno/pkg/engine/variables"
admissionutils "github.com/kyverno/kyverno/pkg/utils/admission" admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
"github.com/kyverno/kyverno/pkg/webhooks/updaterequest" "github.com/kyverno/kyverno/pkg/webhooks/updaterequest"
admissionv1 "k8s.io/api/admission/v1" admissionv1 "k8s.io/api/admission/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
) )
type updateRequestResponse struct { type updateRequestResponse struct {
@ -48,99 +41,6 @@ func processResourceWithPatches(patch []byte, resource []byte, log logr.Logger)
return resource 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, func applyUpdateRequest(request *admissionv1.AdmissionRequest, ruleType kyvernov1beta1.RequestType, grGenerator updaterequest.Generator, userRequestInfo kyvernov1beta1.RequestInfo,
action admissionv1.Operation, engineResponses ...*response.EngineResponse, action admissionv1.Operation, engineResponses ...*response.EngineResponse,
) (failedUpdateRequest []updateRequestResponse) { ) (failedUpdateRequest []updateRequestResponse) {