1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-31 03:45:17 +00:00

refactor: factorise rule handler invocation code (#6694)

* refactor: factorise rule handler invocation code

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* fix

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

---------

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
Charles-Edouard Brétéché 2023-03-27 13:22:54 +02:00 committed by GitHub
parent e3f966b1e5
commit 03220cd8a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 77 additions and 106 deletions

View file

@ -69,7 +69,7 @@ func (e *engine) filterRule(
}
// check if there is a corresponding policy exception
ruleResp := hasPolicyExceptions(logger, ruleType, e.exceptionSelector, policyContext, &rule, e.configuration)
ruleResp := hasPolicyExceptions(logger, ruleType, e.exceptionSelector, policyContext, rule, e.configuration)
if ruleResp != nil {
return ruleResp
}

View file

@ -2,7 +2,10 @@ package engine
import (
"context"
"fmt"
"github.com/go-logr/logr"
gojmespath "github.com/jmespath/go-jmespath"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
"github.com/kyverno/kyverno/pkg/clients/dclient"
@ -13,8 +16,12 @@ import (
"github.com/kyverno/kyverno/pkg/engine/handlers/manifest"
"github.com/kyverno/kyverno/pkg/engine/handlers/mutation"
"github.com/kyverno/kyverno/pkg/engine/internal"
engineutils "github.com/kyverno/kyverno/pkg/engine/utils"
"github.com/kyverno/kyverno/pkg/logging"
"github.com/kyverno/kyverno/pkg/registryclient"
"github.com/kyverno/kyverno/pkg/tracing"
"go.opentelemetry.io/otel/trace"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
type engine struct {
@ -119,3 +126,56 @@ func (e *engine) ContextLoader(
)
}
}
func (e *engine) invokeRuleHandler(
ctx context.Context,
logger logr.Logger,
handler handlers.Handler,
policyContext engineapi.PolicyContext,
resource unstructured.Unstructured,
rule kyvernov1.Rule,
polexFilter func(logr.Logger, engineapi.PolicyContext, kyvernov1.Rule) *engineapi.RuleResponse,
) (unstructured.Unstructured, []engineapi.RuleResponse) {
return tracing.ChildSpan2(
ctx,
"pkg/engine",
fmt.Sprintf("RULE %s", rule.Name),
func(ctx context.Context, span trace.Span) (unstructured.Unstructured, []engineapi.RuleResponse) {
// check if resource and rule match
var excludeResource []string
if len(e.configuration.GetExcludedGroups()) > 0 {
excludeResource = e.configuration.GetExcludedGroups()
}
gvk, subresource := policyContext.ResourceKind()
if err := engineutils.MatchesResourceDescription(
resource,
rule,
policyContext.AdmissionInfo(),
excludeResource,
policyContext.NamespaceLabels(),
policyContext.Policy().GetNamespace(),
gvk,
subresource,
); err != nil {
logger.V(4).Info("rule not matched", "reason", err.Error())
return resource, nil
}
// check if there's an exception
if ruleResp := polexFilter(logger, policyContext, rule); ruleResp != nil {
return resource, handlers.RuleResponses(ruleResp)
}
// load rule context
if err := internal.LoadContext(ctx, e, policyContext, rule); err != nil {
if _, ok := err.(gojmespath.NotFoundError); ok {
logger.V(3).Info("failed to load context", "reason", err.Error())
} else {
logger.Error(err, "failed to load context")
}
// TODO: return error ?
return resource, nil
}
// process handler
return handler.Process(ctx, logger, policyContext, resource, rule)
},
)
}

View file

@ -43,7 +43,7 @@ func findExceptions(
func matchesException(
selector engineapi.PolicyExceptionSelector,
policyContext engineapi.PolicyContext,
rule *kyvernov1.Rule,
rule kyvernov1.Rule,
cfg config.Configuration,
) (*kyvernov2alpha1.PolicyException, error) {
candidates, err := findExceptions(selector, policyContext.Policy(), rule.Name)
@ -76,7 +76,7 @@ func hasPolicyExceptions(
ruleType engineapi.RuleType,
selector engineapi.PolicyExceptionSelector,
ctx engineapi.PolicyContext,
rule *kyvernov1.Rule,
rule kyvernov1.Rule,
cfg config.Configuration,
) *engineapi.RuleResponse {
// if matches, check if there is a corresponding policy exception
@ -87,10 +87,10 @@ func hasPolicyExceptions(
key, err := cache.MetaNamespaceKeyFunc(exception)
if err != nil {
log.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName())
response = internal.RuleError(rule, ruleType, "failed to compute exception key", err)
response = internal.RuleError(&rule, ruleType, "failed to compute exception key", err)
} else {
log.V(3).Info("policy rule skipped due to policy exception", "exception", key)
response = internal.RuleSkip(rule, ruleType, "rule skipped due to policy exception "+key)
response = internal.RuleSkip(&rule, ruleType, "rule skipped due to policy exception "+key)
response.Exception = exception
}
}

View file

@ -16,7 +16,6 @@ type Handler interface {
engineapi.PolicyContext,
unstructured.Unstructured,
kyvernov1.Rule,
func(logr.Logger, engineapi.PolicyContext, kyvernov1.Rule) *engineapi.RuleResponse,
) (unstructured.Unstructured, []engineapi.RuleResponse)
}

View file

@ -50,7 +50,6 @@ func (h handler) Process(
policyContext engineapi.PolicyContext,
resource unstructured.Unstructured,
rule kyvernov1.Rule,
_ func(logr.Logger, engineapi.PolicyContext, kyvernov1.Rule) *engineapi.RuleResponse,
) (unstructured.Unstructured, []engineapi.RuleResponse) {
if engineutils.IsDeleteRequest(policyContext) {
return resource, nil

View file

@ -4,13 +4,11 @@ import (
"context"
"github.com/go-logr/logr"
gojmespath "github.com/jmespath/go-jmespath"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/config"
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/engine/handlers"
"github.com/kyverno/kyverno/pkg/engine/mutate"
engineutils "github.com/kyverno/kyverno/pkg/engine/utils"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
@ -36,56 +34,20 @@ func (h handler) Process(
policyContext engineapi.PolicyContext,
resource unstructured.Unstructured,
rule kyvernov1.Rule,
polexFilter func(logr.Logger, engineapi.PolicyContext, kyvernov1.Rule) *engineapi.RuleResponse,
) (unstructured.Unstructured, []engineapi.RuleResponse) {
policy := policyContext.Policy()
contextLoader := h.contextLoader(policy, rule)
var excludeResource []string
if len(h.configuration.GetExcludedGroups()) > 0 {
excludeResource = h.configuration.GetExcludedGroups()
}
gvk, subresource := policyContext.ResourceKind()
if err := engineutils.MatchesResourceDescription(
resource,
rule,
policyContext.AdmissionInfo(),
excludeResource,
policyContext.NamespaceLabels(),
policyContext.Policy().GetNamespace(),
gvk,
subresource,
); err != nil {
logger.V(4).Info("rule not matched", "reason", err.Error())
return resource, nil
}
// check if there is a corresponding policy exception
if ruleResp := polexFilter(logger, policyContext, rule); ruleResp != nil {
return resource, handlers.RuleResponses(ruleResp)
}
_, subresource := policyContext.ResourceKind()
logger.V(3).Info("processing mutate rule")
if err := contextLoader(ctx, rule.Context, policyContext.JSONContext()); err != nil {
if _, ok := err.(gojmespath.NotFoundError); ok {
logger.V(3).Info("failed to load context", "reason", err.Error())
} else {
logger.Error(err, "failed to load context")
}
// TODO: return error ?
return resource, nil
}
var parentResourceGVR metav1.GroupVersionResource
if subresource != "" {
parentResourceGVR = policyContext.RequestResource()
}
resourceInfo := resourceInfo{
unstructured: resource,
subresource: subresource,
parentResourceGVR: parentResourceGVR,
}
// logger.V(4).Info("apply rule to resource", "resource namespace", patchedResource.unstructured.GetNamespace(), "resource name", patchedResource.unstructured.GetName())
var mutateResp *mutate.Response
if rule.Mutation.ForEachMutation != nil {

View file

@ -4,7 +4,6 @@ import (
"context"
"github.com/go-logr/logr"
gojmespath "github.com/jmespath/go-jmespath"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/clients/dclient"
"github.com/kyverno/kyverno/pkg/config"
@ -12,7 +11,6 @@ import (
"github.com/kyverno/kyverno/pkg/engine/handlers"
"github.com/kyverno/kyverno/pkg/engine/internal"
"github.com/kyverno/kyverno/pkg/engine/mutate"
engineutils "github.com/kyverno/kyverno/pkg/engine/utils"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
@ -40,47 +38,11 @@ func (h handlerExisting) Process(
policyContext engineapi.PolicyContext,
resource unstructured.Unstructured,
rule kyvernov1.Rule,
polexFilter func(logr.Logger, engineapi.PolicyContext, kyvernov1.Rule) *engineapi.RuleResponse,
) (unstructured.Unstructured, []engineapi.RuleResponse) {
policy := policyContext.Policy()
contextLoader := h.contextLoader(policy, rule)
var responses []engineapi.RuleResponse
var excludeResource []string
if len(h.configuration.GetExcludedGroups()) > 0 {
excludeResource = h.configuration.GetExcludedGroups()
}
gvk, subresource := policyContext.ResourceKind()
if err := engineutils.MatchesResourceDescription(
resource,
rule,
policyContext.AdmissionInfo(),
excludeResource,
policyContext.NamespaceLabels(),
policyContext.Policy().GetNamespace(),
gvk,
subresource,
); err != nil {
logger.V(4).Info("rule not matched", "reason", err.Error())
return resource, nil
}
// check if there is a corresponding policy exception
if ruleResp := polexFilter(logger, policyContext, rule); ruleResp != nil {
return resource, handlers.RuleResponses(ruleResp)
}
logger.V(3).Info("processing mutate rule")
if err := contextLoader(ctx, rule.Context, policyContext.JSONContext()); err != nil {
if _, ok := err.(gojmespath.NotFoundError); ok {
logger.V(3).Info("failed to load context", "reason", err.Error())
} else {
logger.Error(err, "failed to load context")
}
// TODO: return error ?
return resource, nil
}
var patchedResources []resourceInfo
targets, err := loadTargets(h.client, rule.Mutation.Targets, policyContext, logger)
if err != nil {

View file

@ -84,7 +84,7 @@ func (e *engine) doVerifyAndPatch(
}
// check if there is a corresponding policy exception
ruleResp := hasPolicyExceptions(logger, engineapi.ImageVerify, e.exceptionSelector, policyContext, rule, e.configuration)
ruleResp := hasPolicyExceptions(logger, engineapi.ImageVerify, e.exceptionSelector, policyContext, *rule, e.configuration)
if ruleResp != nil {
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, *ruleResp)
return

View file

@ -2,7 +2,6 @@ package engine
import (
"context"
"fmt"
"time"
"github.com/go-logr/logr"
@ -10,8 +9,6 @@ import (
"github.com/kyverno/kyverno/pkg/autogen"
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/engine/internal"
"github.com/kyverno/kyverno/pkg/tracing"
"go.opentelemetry.io/otel/trace"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
@ -34,25 +31,18 @@ func (e *engine) mutate(
applyRules := policy.GetSpec().GetApplyRules()
for _, rule := range autogen.ComputeRules(policy) {
logger := internal.LoggerWithRule(logger, rule)
if !rule.HasMutate() {
continue
}
resource, ruleResp := tracing.ChildSpan2(
ctx,
"pkg/engine",
fmt.Sprintf("RULE %s", rule.Name),
func(ctx context.Context, span trace.Span) (unstructured.Unstructured, []engineapi.RuleResponse) {
logger := internal.LoggerWithRule(logger, rule)
polexFilter := func(logger logr.Logger, policyContext engineapi.PolicyContext, rule kyvernov1.Rule) *engineapi.RuleResponse {
return hasPolicyExceptions(logger, engineapi.Validation, e.exceptionSelector, policyContext, &rule, e.configuration)
}
if !policyContext.AdmissionOperation() && rule.IsMutateExisting() {
return e.mutateExistingHandler.Process(ctx, logger, policyContext, matchedResource, rule, polexFilter)
} else {
return e.mutateHandler.Process(ctx, logger, policyContext, matchedResource, rule, polexFilter)
}
},
)
polexFilter := func(logger logr.Logger, policyContext engineapi.PolicyContext, rule kyvernov1.Rule) *engineapi.RuleResponse {
return hasPolicyExceptions(logger, engineapi.Validation, e.exceptionSelector, policyContext, rule, e.configuration)
}
handler := e.mutateHandler
if !policyContext.AdmissionOperation() && rule.IsMutateExisting() {
handler = e.mutateExistingHandler
}
resource, ruleResp := e.invokeRuleHandler(ctx, logger, handler, policyContext, matchedResource, rule, polexFilter)
matchedResource = resource
for _, ruleResp := range ruleResp {
ruleResp := ruleResp

View file

@ -80,7 +80,7 @@ func (e *engine) validateResource(
return nil
}
// check if there is a corresponding policy exception
ruleResp := hasPolicyExceptions(logger, engineapi.Validation, e.exceptionSelector, policyContext, rule, e.configuration)
ruleResp := hasPolicyExceptions(logger, engineapi.Validation, e.exceptionSelector, policyContext, *rule, e.configuration)
if ruleResp != nil {
return handlers.RuleResponses(ruleResp)
}
@ -96,7 +96,6 @@ func (e *engine) validateResource(
policyContext,
policyContext.NewResource(),
*rule,
nil,
)
return rr
}