1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-15 20:20:22 +00:00

refactor: don't process context/preconditions in invokeHandler (#6751)

* refactor: engine handlers

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>

* refactor: don't process context/preconditions in invokeHandler

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>

* fix

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>

* fix

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-04-03 06:57:48 +02:00 committed by GitHub
parent e75c766acd
commit b4a4e3a4f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 217 additions and 219 deletions

View file

@ -162,7 +162,7 @@ func (c *GenerateController) applyGenerate(resource unstructured.Unstructured, u
} }
// check if the policy still applies to the resource // check if the policy still applies to the resource
engineResponse := c.engine.GenerateResponse(context.Background(), policyContext, ur) engineResponse := c.engine.Generate(context.Background(), policyContext)
if len(engineResponse.PolicyResponse.Rules) == 0 { if len(engineResponse.PolicyResponse.Rules) == 0 {
logger.V(4).Info(doesNotApply) logger.V(4).Info(doesNotApply)
return nil, errors.New(doesNotApply) return nil, errors.New(doesNotApply)

View file

@ -4,7 +4,6 @@ import (
"context" "context"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
enginecontext "github.com/kyverno/kyverno/pkg/engine/context" enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
) )
@ -28,6 +27,12 @@ type Engine interface {
policyContext PolicyContext, policyContext PolicyContext,
) EngineResponse ) EngineResponse
// Generate checks for validity of generate rule on the resource
Generate(
ctx context.Context,
policyContext PolicyContext,
) EngineResponse
// VerifyAndPatchImages ... // VerifyAndPatchImages ...
VerifyAndPatchImages( VerifyAndPatchImages(
ctx context.Context, ctx context.Context,
@ -44,13 +49,6 @@ type Engine interface {
policyContext PolicyContext, policyContext PolicyContext,
) EngineResponse ) EngineResponse
// GenerateResponse checks for validity of generate rule on the resource
GenerateResponse(
ctx context.Context,
policyContext PolicyContext,
gr kyvernov1beta1.UpdateRequest,
) EngineResponse
ContextLoader( ContextLoader(
policy kyvernov1.PolicyInterface, policy kyvernov1.PolicyInterface,
rule kyvernov1.Rule, rule kyvernov1.Rule,

View file

@ -6,9 +6,7 @@ import (
"time" "time"
"github.com/go-logr/logr" "github.com/go-logr/logr"
gojmespath "github.com/jmespath/go-jmespath"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
"github.com/kyverno/kyverno/pkg/clients/dclient" "github.com/kyverno/kyverno/pkg/clients/dclient"
"github.com/kyverno/kyverno/pkg/config" "github.com/kyverno/kyverno/pkg/config"
engineapi "github.com/kyverno/kyverno/pkg/engine/api" engineapi "github.com/kyverno/kyverno/pkg/engine/api"
@ -34,6 +32,7 @@ type engine struct {
validateResourceHandler handlers.Handler validateResourceHandler handlers.Handler
validateManifestHandler handlers.Handler validateManifestHandler handlers.Handler
validatePssHandler handlers.Handler validatePssHandler handlers.Handler
validateImageHandler handlers.Handler
mutateResourceHandler handlers.Handler mutateResourceHandler handlers.Handler
mutateExistingHandler handlers.Handler mutateExistingHandler handlers.Handler
} }
@ -63,11 +62,12 @@ func NewEngine(
rclient: rclient, rclient: rclient,
engineContextLoaderFactory: engineContextLoaderFactory, engineContextLoaderFactory: engineContextLoaderFactory,
exceptionSelector: exceptionSelector, exceptionSelector: exceptionSelector,
validateResourceHandler: validation.NewValidateResourceHandler(engineContextLoaderFactory), validateResourceHandler: validation.NewValidateResourceHandler(),
validateManifestHandler: validation.NewValidateManifestHandler(client), validateManifestHandler: validation.NewValidateManifestHandler(client),
validatePssHandler: validation.NewValidatePssHandler(), validatePssHandler: validation.NewValidatePssHandler(),
mutateResourceHandler: mutation.NewMutateResourceHandler(engineContextLoaderFactory), validateImageHandler: validation.NewValidateImageHandler(configuration),
mutateExistingHandler: mutation.NewMutateExistingHandler(client, engineContextLoaderFactory), mutateResourceHandler: mutation.NewMutateResourceHandler(),
mutateExistingHandler: mutation.NewMutateExistingHandler(client),
} }
} }
@ -99,6 +99,19 @@ func (e *engine) Mutate(
return response.Done(time.Now()) return response.Done(time.Now())
} }
func (e *engine) Generate(
ctx context.Context,
policyContext engineapi.PolicyContext,
) engineapi.EngineResponse {
response := engineapi.NewEngineResponseFromPolicyContext(policyContext, time.Now())
logger := internal.LoggerWithPolicyContext(logging.WithName("engine.generate"), policyContext)
if internal.MatchPolicyContext(logger, policyContext, e.configuration) {
policyResponse := e.generateResponse(ctx, logger, policyContext)
response = response.WithPolicyResponse(policyResponse)
}
return response.Done(time.Now())
}
func (e *engine) VerifyAndPatchImages( func (e *engine) VerifyAndPatchImages(
ctx context.Context, ctx context.Context,
policyContext engineapi.PolicyContext, policyContext engineapi.PolicyContext,
@ -126,20 +139,6 @@ func (e *engine) ApplyBackgroundChecks(
return response.Done(time.Now()) return response.Done(time.Now())
} }
func (e *engine) GenerateResponse(
ctx context.Context,
policyContext engineapi.PolicyContext,
gr kyvernov1beta1.UpdateRequest,
) engineapi.EngineResponse {
response := engineapi.NewEngineResponseFromPolicyContext(policyContext, time.Now())
logger := internal.LoggerWithPolicyContext(logging.WithName("engine.generate"), policyContext)
if internal.MatchPolicyContext(logger, policyContext, e.configuration) {
policyResponse := e.generateResponse(ctx, logger, policyContext, gr)
response = response.WithPolicyResponse(policyResponse)
}
return response.Done(time.Now())
}
func (e *engine) ContextLoader( func (e *engine) ContextLoader(
policy kyvernov1.PolicyInterface, policy kyvernov1.PolicyInterface,
rule kyvernov1.Rule, rule kyvernov1.Rule,
@ -189,7 +188,7 @@ func matches(
func (e *engine) invokeRuleHandler( func (e *engine) invokeRuleHandler(
ctx context.Context, ctx context.Context,
logger logr.Logger, logger logr.Logger,
handlerFactory handlers.HandlerFactory, handler handlers.Handler,
policyContext engineapi.PolicyContext, policyContext engineapi.PolicyContext,
resource unstructured.Unstructured, resource unstructured.Unstructured,
rule kyvernov1.Rule, rule kyvernov1.Rule,
@ -209,32 +208,8 @@ func (e *engine) invokeRuleHandler(
if ruleResp := e.hasPolicyExceptions(logger, ruleType, policyContext, rule); ruleResp != nil { if ruleResp := e.hasPolicyExceptions(logger, ruleType, policyContext, rule); ruleResp != nil {
return resource, handlers.RuleResponses(ruleResp) return resource, handlers.RuleResponses(ruleResp)
} }
if handlerFactory == nil { // process handler
return resource, handlers.RuleResponses(internal.RuleError(rule, ruleType, "failed to instantiate handler", nil)) return handler.Process(ctx, logger, policyContext, resource, rule, e.ContextLoader(policyContext.Policy(), rule))
} else if handler, err := handlerFactory(); err != nil {
return resource, handlers.RuleResponses(internal.RuleError(rule, ruleType, "failed to instantiate handler", err))
} else if handler != nil {
// 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")
}
return resource, handlers.RuleResponses(internal.RuleError(rule, ruleType, "failed to load context", err))
}
// check preconditions
preconditionsPassed, err := internal.CheckPreconditions(logger, policyContext.JSONContext(), rule.GetAnyAllConditions())
if err != nil {
return resource, handlers.RuleResponses(internal.RuleError(rule, ruleType, "failed to evaluate preconditions", err))
}
if !preconditionsPassed {
return resource, handlers.RuleResponses(internal.RuleSkip(rule, ruleType, "preconditions not met"))
}
// process handler
return handler.Process(ctx, logger, policyContext, resource, rule)
}
return resource, nil
}, },
) )
} }

View file

@ -2,10 +2,8 @@ package engine
import ( import (
"context" "context"
"time"
"github.com/go-logr/logr" "github.com/go-logr/logr"
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
"github.com/kyverno/kyverno/pkg/autogen" "github.com/kyverno/kyverno/pkg/autogen"
engineapi "github.com/kyverno/kyverno/pkg/engine/api" engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/engine/internal" "github.com/kyverno/kyverno/pkg/engine/internal"
@ -16,16 +14,6 @@ func (e *engine) generateResponse(
ctx context.Context, ctx context.Context,
logger logr.Logger, logger logr.Logger,
policyContext engineapi.PolicyContext, policyContext engineapi.PolicyContext,
gr kyvernov1beta1.UpdateRequest,
) engineapi.PolicyResponse {
return e.filterGenerateRules(policyContext, logger, gr.Spec.Policy, time.Now())
}
func (e *engine) filterGenerateRules(
policyContext engineapi.PolicyContext,
logger logr.Logger,
policyNameKey string,
startTime time.Time,
) engineapi.PolicyResponse { ) engineapi.PolicyResponse {
resp := engineapi.NewPolicyResponse() resp := engineapi.NewPolicyResponse()
for _, rule := range autogen.ComputeRules(policyContext.Policy()) { for _, rule := range autogen.ComputeRules(policyContext.Policy()) {

View file

@ -16,17 +16,10 @@ type Handler interface {
engineapi.PolicyContext, engineapi.PolicyContext,
unstructured.Unstructured, unstructured.Unstructured,
kyvernov1.Rule, kyvernov1.Rule,
engineapi.EngineContextLoader,
) (unstructured.Unstructured, []engineapi.RuleResponse) ) (unstructured.Unstructured, []engineapi.RuleResponse)
} }
type HandlerFactory = func() (Handler, error)
func WithHandler(handler Handler) HandlerFactory {
return func() (Handler, error) {
return handler, nil
}
}
func RuleResponses(rrs ...*engineapi.RuleResponse) []engineapi.RuleResponse { func RuleResponses(rrs ...*engineapi.RuleResponse) []engineapi.RuleResponse {
var out []engineapi.RuleResponse var out []engineapi.RuleResponse
for _, rr := range rrs { for _, rr := range rrs {

View file

@ -14,17 +14,26 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
) )
func mutateResource(rule *kyvernov1.Rule, policyContext engineapi.PolicyContext, resource unstructured.Unstructured, logger logr.Logger) *mutate.Response { func mutateResource(
ctx context.Context,
contextLoader engineapi.EngineContextLoader,
rule kyvernov1.Rule,
policyContext engineapi.PolicyContext,
resource unstructured.Unstructured,
logger logr.Logger,
) *mutate.Response {
if err := contextLoader(ctx, rule.Context, policyContext.JSONContext()); err != nil {
logger.Error(err, "failed to load context")
return mutate.NewErrorResponse("failed to load context", err)
}
preconditionsPassed, err := internal.CheckPreconditions(logger, policyContext.JSONContext(), rule.GetAnyAllConditions()) preconditionsPassed, err := internal.CheckPreconditions(logger, policyContext.JSONContext(), rule.GetAnyAllConditions())
if err != nil { if err != nil {
return mutate.NewErrorResponse("failed to evaluate preconditions", err) return mutate.NewErrorResponse("failed to evaluate preconditions", err)
} }
if !preconditionsPassed { if !preconditionsPassed {
return mutate.NewResponse(engineapi.RuleStatusSkip, resource, nil, "preconditions not met") return mutate.NewResponse(engineapi.RuleStatusSkip, resource, nil, "preconditions not met")
} }
return mutate.Mutate(&rule, policyContext.JSONContext(), resource, logger)
return mutate.Mutate(rule, policyContext.JSONContext(), resource, logger)
} }
type forEachMutator struct { type forEachMutator struct {

View file

@ -14,17 +14,14 @@ import (
) )
type mutateExistingHandler struct { type mutateExistingHandler struct {
client dclient.Interface client dclient.Interface
contextLoader engineapi.EngineContextLoaderFactory
} }
func NewMutateExistingHandler( func NewMutateExistingHandler(
client dclient.Interface, client dclient.Interface,
contextLoader engineapi.EngineContextLoaderFactory,
) handlers.Handler { ) handlers.Handler {
return mutateExistingHandler{ return mutateExistingHandler{
client: client, client: client,
contextLoader: contextLoader,
} }
} }
@ -34,9 +31,8 @@ func (h mutateExistingHandler) Process(
policyContext engineapi.PolicyContext, policyContext engineapi.PolicyContext,
resource unstructured.Unstructured, resource unstructured.Unstructured,
rule kyvernov1.Rule, rule kyvernov1.Rule,
contextLoader engineapi.EngineContextLoader,
) (unstructured.Unstructured, []engineapi.RuleResponse) { ) (unstructured.Unstructured, []engineapi.RuleResponse) {
policy := policyContext.Policy()
contextLoader := h.contextLoader(policy, rule)
var responses []engineapi.RuleResponse var responses []engineapi.RuleResponse
logger.V(3).Info("processing mutate rule") logger.V(3).Info("processing mutate rule")
var patchedResources []resourceInfo var patchedResources []resourceInfo
@ -72,7 +68,7 @@ func (h mutateExistingHandler) Process(
} }
mutateResp = m.mutateForEach(ctx) mutateResp = m.mutateForEach(ctx)
} else { } else {
mutateResp = mutateResource(&rule, policyContext, patchedResource.unstructured, logger) mutateResp = mutateResource(ctx, contextLoader, rule, policyContext, patchedResource.unstructured, logger)
} }
if ruleResponse := buildRuleResponse(&rule, mutateResp, patchedResource); ruleResponse != nil { if ruleResponse := buildRuleResponse(&rule, mutateResp, patchedResource); ruleResponse != nil {
responses = append(responses, *ruleResponse) responses = append(responses, *ruleResponse)

View file

@ -4,6 +4,7 @@ import (
"context" "context"
"github.com/go-logr/logr" "github.com/go-logr/logr"
gojmespath "github.com/jmespath/go-jmespath"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/config" "github.com/kyverno/kyverno/pkg/config"
engineapi "github.com/kyverno/kyverno/pkg/engine/api" engineapi "github.com/kyverno/kyverno/pkg/engine/api"
@ -13,7 +14,6 @@ import (
engineutils "github.com/kyverno/kyverno/pkg/engine/utils" engineutils "github.com/kyverno/kyverno/pkg/engine/utils"
"github.com/kyverno/kyverno/pkg/engine/variables" "github.com/kyverno/kyverno/pkg/engine/variables"
"github.com/kyverno/kyverno/pkg/registryclient" "github.com/kyverno/kyverno/pkg/registryclient"
apiutils "github.com/kyverno/kyverno/pkg/utils/api"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
) )
@ -21,30 +21,18 @@ type mutateImageHandler struct {
configuration config.Configuration configuration config.Configuration
rclient registryclient.Client rclient registryclient.Client
ivm *engineapi.ImageVerificationMetadata ivm *engineapi.ImageVerificationMetadata
images []apiutils.ImageInfo
} }
func NewMutateImageHandler( func NewMutateImageHandler(
policyContext engineapi.PolicyContext,
resource unstructured.Unstructured,
rule kyvernov1.Rule,
configuration config.Configuration, configuration config.Configuration,
rclient registryclient.Client, rclient registryclient.Client,
ivm *engineapi.ImageVerificationMetadata, ivm *engineapi.ImageVerificationMetadata,
) (handlers.Handler, error) { ) handlers.Handler {
ruleImages, _, err := engineutils.ExtractMatchingImages(resource, policyContext.JSONContext(), rule, configuration)
if err != nil {
return nil, err
}
if len(ruleImages) == 0 {
return nil, nil
}
return mutateImageHandler{ return mutateImageHandler{
configuration: configuration, configuration: configuration,
rclient: rclient, rclient: rclient,
ivm: ivm, ivm: ivm,
images: ruleImages, }
}, nil
} }
func (h mutateImageHandler) Process( func (h mutateImageHandler) Process(
@ -53,11 +41,39 @@ func (h mutateImageHandler) Process(
policyContext engineapi.PolicyContext, policyContext engineapi.PolicyContext,
resource unstructured.Unstructured, resource unstructured.Unstructured,
rule kyvernov1.Rule, rule kyvernov1.Rule,
contextLoader engineapi.EngineContextLoader,
) (unstructured.Unstructured, []engineapi.RuleResponse) { ) (unstructured.Unstructured, []engineapi.RuleResponse) {
if engineutils.IsDeleteRequest(policyContext) {
return resource, nil
}
if len(rule.VerifyImages) == 0 { if len(rule.VerifyImages) == 0 {
return resource, nil return resource, nil
} }
ruleImages, _, err := engineutils.ExtractMatchingImages(resource, policyContext.JSONContext(), rule, h.configuration)
if err != nil {
return resource, handlers.RuleResponses(internal.RuleError(rule, engineapi.ImageVerify, "failed to extract images", err))
}
if len(ruleImages) == 0 {
return resource, nil
}
jsonContext := policyContext.JSONContext() jsonContext := policyContext.JSONContext()
// load context
if err := contextLoader(ctx, rule.Context, 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")
}
return resource, handlers.RuleResponses(internal.RuleError(rule, engineapi.ImageVerify, "failed to load context", err))
}
// check preconditions
preconditionsPassed, err := internal.CheckPreconditions(logger, jsonContext, rule.GetAnyAllConditions())
if err != nil {
return resource, handlers.RuleResponses(internal.RuleError(rule, engineapi.ImageVerify, "failed to evaluate preconditions", err))
}
if !preconditionsPassed {
return resource, handlers.RuleResponses(internal.RuleSkip(rule, engineapi.ImageVerify, "preconditions not met"))
}
ruleCopy, err := substituteVariables(rule, jsonContext, logger) ruleCopy, err := substituteVariables(rule, jsonContext, logger)
if err != nil { if err != nil {
return resource, handlers.RuleResponses( return resource, handlers.RuleResponses(
@ -67,7 +83,7 @@ func (h mutateImageHandler) Process(
iv := internal.NewImageVerifier(logger, h.rclient, policyContext, *ruleCopy, h.ivm) iv := internal.NewImageVerifier(logger, h.rclient, policyContext, *ruleCopy, h.ivm)
var engineResponses []*engineapi.RuleResponse var engineResponses []*engineapi.RuleResponse
for _, imageVerify := range ruleCopy.VerifyImages { for _, imageVerify := range ruleCopy.VerifyImages {
engineResponses = append(engineResponses, iv.Verify(ctx, imageVerify, h.images, h.configuration)...) engineResponses = append(engineResponses, iv.Verify(ctx, imageVerify, ruleImages, h.configuration)...)
} }
return resource, handlers.RuleResponses(engineResponses...) return resource, handlers.RuleResponses(engineResponses...)
} }

View file

@ -12,16 +12,10 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
) )
type mutateResourceHandler struct { type mutateResourceHandler struct{}
contextLoader engineapi.EngineContextLoaderFactory
}
func NewMutateResourceHandler( func NewMutateResourceHandler() handlers.Handler {
contextLoader engineapi.EngineContextLoaderFactory, return mutateResourceHandler{}
) handlers.Handler {
return mutateResourceHandler{
contextLoader: contextLoader,
}
} }
func (h mutateResourceHandler) Process( func (h mutateResourceHandler) Process(
@ -30,9 +24,8 @@ func (h mutateResourceHandler) Process(
policyContext engineapi.PolicyContext, policyContext engineapi.PolicyContext,
resource unstructured.Unstructured, resource unstructured.Unstructured,
rule kyvernov1.Rule, rule kyvernov1.Rule,
contextLoader engineapi.EngineContextLoader,
) (unstructured.Unstructured, []engineapi.RuleResponse) { ) (unstructured.Unstructured, []engineapi.RuleResponse) {
policy := policyContext.Policy()
contextLoader := h.contextLoader(policy, rule)
_, subresource := policyContext.ResourceKind() _, subresource := policyContext.ResourceKind()
logger.V(3).Info("processing mutate rule") logger.V(3).Info("processing mutate rule")
var parentResourceGVR metav1.GroupVersionResource var parentResourceGVR metav1.GroupVersionResource
@ -58,7 +51,7 @@ func (h mutateResourceHandler) Process(
} }
mutateResp = m.mutateForEach(ctx) mutateResp = m.mutateForEach(ctx)
} else { } else {
mutateResp = mutateResource(&rule, policyContext, resourceInfo.unstructured, logger) mutateResp = mutateResource(ctx, contextLoader, rule, policyContext, resourceInfo.unstructured, logger)
} }
if mutateResp == nil { if mutateResp == nil {
return resource, nil return resource, nil

View file

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"github.com/go-logr/logr" "github.com/go-logr/logr"
gojmespath "github.com/jmespath/go-jmespath"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/config" "github.com/kyverno/kyverno/pkg/config"
engineapi "github.com/kyverno/kyverno/pkg/engine/api" engineapi "github.com/kyverno/kyverno/pkg/engine/api"
@ -15,22 +16,16 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
) )
type validateImageHandler struct{} type validateImageHandler struct {
configuration config.Configuration
}
func NewValidateImageHandler( func NewValidateImageHandler(
policyContext engineapi.PolicyContext,
resource unstructured.Unstructured,
rule kyvernov1.Rule,
configuration config.Configuration, configuration config.Configuration,
) (handlers.Handler, error) { ) handlers.Handler {
ruleImages, _, err := engineutils.ExtractMatchingImages(resource, policyContext.JSONContext(), rule, configuration) return validateImageHandler{
if err != nil { configuration: configuration,
return nil, err
} }
if len(ruleImages) == 0 {
return nil, nil
}
return validateImageHandler{}, nil
} }
func (h validateImageHandler) Process( func (h validateImageHandler) Process(
@ -39,11 +34,32 @@ func (h validateImageHandler) Process(
policyContext engineapi.PolicyContext, policyContext engineapi.PolicyContext,
resource unstructured.Unstructured, resource unstructured.Unstructured,
rule kyvernov1.Rule, rule kyvernov1.Rule,
contextLoader engineapi.EngineContextLoader,
) (unstructured.Unstructured, []engineapi.RuleResponse) { ) (unstructured.Unstructured, []engineapi.RuleResponse) {
if engineutils.IsDeleteRequest(policyContext) { if engineutils.IsDeleteRequest(policyContext) {
return resource, nil return resource, nil
} }
preconditionsPassed, err := internal.CheckPreconditions(logger, policyContext.JSONContext(), rule.RawAnyAllConditions) if len(rule.VerifyImages) == 0 {
return resource, nil
}
ruleImages, _, err := engineutils.ExtractMatchingImages(resource, policyContext.JSONContext(), rule, h.configuration)
if err != nil {
return resource, handlers.RuleResponses(internal.RuleError(rule, engineapi.Validation, "failed to extract images", err))
}
if len(ruleImages) == 0 {
return resource, nil
}
// load context
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")
}
return resource, handlers.RuleResponses(internal.RuleError(rule, engineapi.Validation, "failed to load context", err))
}
// check preconditions
preconditionsPassed, err := internal.CheckPreconditions(logger, policyContext.JSONContext(), rule.GetAnyAllConditions())
if err != nil { if err != nil {
return resource, handlers.RuleResponses(internal.RuleError(rule, engineapi.Validation, "failed to evaluate preconditions", err)) return resource, handlers.RuleResponses(internal.RuleError(rule, engineapi.Validation, "failed to evaluate preconditions", err))
} }

View file

@ -14,6 +14,7 @@ import (
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
"github.com/go-logr/logr" "github.com/go-logr/logr"
gojmespath "github.com/jmespath/go-jmespath"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/auth" "github.com/kyverno/kyverno/pkg/auth"
"github.com/kyverno/kyverno/pkg/clients/dclient" "github.com/kyverno/kyverno/pkg/clients/dclient"
@ -50,10 +51,29 @@ func (h validateManifestHandler) Process(
policyContext engineapi.PolicyContext, policyContext engineapi.PolicyContext,
resource unstructured.Unstructured, resource unstructured.Unstructured,
rule kyvernov1.Rule, rule kyvernov1.Rule,
contextLoader engineapi.EngineContextLoader,
) (unstructured.Unstructured, []engineapi.RuleResponse) { ) (unstructured.Unstructured, []engineapi.RuleResponse) {
if engineutils.IsDeleteRequest(policyContext) { if engineutils.IsDeleteRequest(policyContext) {
return resource, nil return resource, nil
} }
// load context
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")
}
return resource, handlers.RuleResponses(internal.RuleError(rule, engineapi.Validation, "failed to load context", err))
}
// check preconditions
preconditionsPassed, err := internal.CheckPreconditions(logger, policyContext.JSONContext(), rule.GetAnyAllConditions())
if err != nil {
return resource, handlers.RuleResponses(internal.RuleError(rule, engineapi.Validation, "failed to evaluate preconditions", err))
}
if !preconditionsPassed {
return resource, handlers.RuleResponses(internal.RuleSkip(rule, engineapi.Validation, "preconditions not met"))
}
// verify manifest
verified, reason, err := h.verifyManifest(ctx, logger, policyContext, *rule.Validation.Manifests) verified, reason, err := h.verifyManifest(ctx, logger, policyContext, *rule.Validation.Manifests)
if err != nil { if err != nil {
logger.V(3).Info("verifyManifest return err", "error", err.Error()) logger.V(3).Info("verifyManifest return err", "error", err.Error())

View file

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"github.com/go-logr/logr" "github.com/go-logr/logr"
gojmespath "github.com/jmespath/go-jmespath"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
engineapi "github.com/kyverno/kyverno/pkg/engine/api" engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/engine/handlers" "github.com/kyverno/kyverno/pkg/engine/handlers"
@ -30,9 +31,27 @@ func (h validatePssHandler) Process(
policyContext engineapi.PolicyContext, policyContext engineapi.PolicyContext,
resource unstructured.Unstructured, resource unstructured.Unstructured,
rule kyvernov1.Rule, rule kyvernov1.Rule,
contextLoader engineapi.EngineContextLoader,
) (unstructured.Unstructured, []engineapi.RuleResponse) { ) (unstructured.Unstructured, []engineapi.RuleResponse) {
podSecurity := rule.Validation.PodSecurity // load context
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")
}
return resource, handlers.RuleResponses(internal.RuleError(rule, engineapi.Validation, "failed to load context", err))
}
// check preconditions
preconditionsPassed, err := internal.CheckPreconditions(logger, policyContext.JSONContext(), rule.GetAnyAllConditions())
if err != nil {
return resource, handlers.RuleResponses(internal.RuleError(rule, engineapi.Validation, "failed to evaluate preconditions", err))
}
if !preconditionsPassed {
return resource, handlers.RuleResponses(internal.RuleSkip(rule, engineapi.Validation, "preconditions not met"))
}
// Marshal pod metadata and spec // Marshal pod metadata and spec
podSecurity := rule.Validation.PodSecurity
podSpec, metadata, err := getSpec(resource) podSpec, metadata, err := getSpec(resource)
if err != nil { if err != nil {
return resource, handlers.RuleResponses(internal.RuleError(rule, engineapi.Validation, "Error while getting new resource", err)) return resource, handlers.RuleResponses(internal.RuleError(rule, engineapi.Validation, "Error while getting new resource", err))

View file

@ -21,16 +21,10 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
) )
type validateResourceHandler struct { type validateResourceHandler struct{}
contextLoader engineapi.EngineContextLoaderFactory
}
func NewValidateResourceHandler( func NewValidateResourceHandler() handlers.Handler {
contextLoader engineapi.EngineContextLoaderFactory, return validateResourceHandler{}
) handlers.Handler {
return validateResourceHandler{
contextLoader: contextLoader,
}
} }
func (h validateResourceHandler) Process( func (h validateResourceHandler) Process(
@ -39,9 +33,8 @@ func (h validateResourceHandler) Process(
policyContext engineapi.PolicyContext, policyContext engineapi.PolicyContext,
resource unstructured.Unstructured, resource unstructured.Unstructured,
rule kyvernov1.Rule, rule kyvernov1.Rule,
contextLoader engineapi.EngineContextLoader,
) (unstructured.Unstructured, []engineapi.RuleResponse) { ) (unstructured.Unstructured, []engineapi.RuleResponse) {
policy := policyContext.Policy()
contextLoader := h.contextLoader(policy, rule)
v := newValidator(logger, contextLoader, policyContext, rule) v := newValidator(logger, contextLoader, policyContext, rule)
return resource, handlers.RuleResponses(v.validate(ctx)) return resource, handlers.RuleResponses(v.validate(ctx))
} }
@ -87,12 +80,10 @@ func newForEachValidator(
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to convert ruleCopy.Validation.ForEachValidation.AnyAllConditions: %w", err) return nil, fmt.Errorf("failed to convert ruleCopy.Validation.ForEachValidation.AnyAllConditions: %w", err)
} }
nestedForEach, err := api.DeserializeJSONArray[kyvernov1.ForEachValidation](foreach.ForEachValidation) nestedForEach, err := api.DeserializeJSONArray[kyvernov1.ForEachValidation](foreach.ForEachValidation)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to convert ruleCopy.Validation.ForEachValidation.AnyAllConditions: %w", err) return nil, fmt.Errorf("failed to convert ruleCopy.Validation.ForEachValidation.AnyAllConditions: %w", err)
} }
return &validator{ return &validator{
log: log, log: log,
policyContext: ctx, policyContext: ctx,
@ -112,12 +103,10 @@ func (v *validator) validate(ctx context.Context) *engineapi.RuleResponse {
if err := v.loadContext(ctx); err != nil { if err := v.loadContext(ctx); err != nil {
return internal.RuleError(v.rule, engineapi.Validation, "failed to load context", err) return internal.RuleError(v.rule, engineapi.Validation, "failed to load context", err)
} }
preconditionsPassed, err := internal.CheckPreconditions(v.log, v.policyContext.JSONContext(), v.anyAllConditions) preconditionsPassed, err := internal.CheckPreconditions(v.log, v.policyContext.JSONContext(), v.anyAllConditions)
if err != nil { if err != nil {
return internal.RuleError(v.rule, engineapi.Validation, "failed to evaluate preconditions", err) return internal.RuleError(v.rule, engineapi.Validation, "failed to evaluate preconditions", err)
} }
if !preconditionsPassed { if !preconditionsPassed {
return internal.RuleSkip(v.rule, engineapi.Validation, "preconditions not met") return internal.RuleSkip(v.rule, engineapi.Validation, "preconditions not met")
} }
@ -222,10 +211,8 @@ func (v *validator) loadContext(ctx context.Context) error {
} else { } else {
v.log.Error(err, "failed to load context") v.log.Error(err, "failed to load context")
} }
return err return err
} }
return nil return nil
} }

View file

@ -8,7 +8,6 @@ import (
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/autogen" "github.com/kyverno/kyverno/pkg/autogen"
engineapi "github.com/kyverno/kyverno/pkg/engine/api" engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/engine/handlers"
"github.com/kyverno/kyverno/pkg/engine/handlers/mutation" "github.com/kyverno/kyverno/pkg/engine/handlers/mutation"
"github.com/kyverno/kyverno/pkg/engine/internal" "github.com/kyverno/kyverno/pkg/engine/internal"
) )
@ -19,45 +18,42 @@ func (e *engine) verifyAndPatchImages(
policyContext engineapi.PolicyContext, policyContext engineapi.PolicyContext,
) (engineapi.PolicyResponse, engineapi.ImageVerificationMetadata) { ) (engineapi.PolicyResponse, engineapi.ImageVerificationMetadata) {
resp := engineapi.NewPolicyResponse() resp := engineapi.NewPolicyResponse()
policy := policyContext.Policy()
matchedResource := policyContext.NewResource()
applyRules := policy.GetSpec().GetApplyRules()
ivm := engineapi.ImageVerificationMetadata{}
policyContext.JSONContext().Checkpoint() policyContext.JSONContext().Checkpoint()
defer policyContext.JSONContext().Restore() defer policyContext.JSONContext().Restore()
ivm := engineapi.ImageVerificationMetadata{} for _, rule := range autogen.ComputeRules(policy) {
policy := policyContext.Policy() startTime := time.Now()
matchedResource := policyContext.NewResource() logger := internal.LoggerWithRule(logger, rule)
applyRules := policy.GetSpec().GetApplyRules() if !rule.HasVerifyImages() {
for _, rule := range autogen.ComputeRules(policyContext.Policy()) { continue
if rule.HasVerifyImages() { }
startTime := time.Now() handler := mutation.NewMutateImageHandler(
handlerFactory := func() (handlers.Handler, error) { e.configuration,
return mutation.NewMutateImageHandler( e.rclient,
policyContext, &ivm,
matchedResource, )
rule, resource, ruleResp := e.invokeRuleHandler(
e.configuration, ctx,
e.rclient, logger,
&ivm, handler,
) policyContext,
} matchedResource,
resource, ruleResp := e.invokeRuleHandler( rule,
ctx, engineapi.ImageVerify,
logger, )
handlerFactory, matchedResource = resource
policyContext, for _, ruleResp := range ruleResp {
matchedResource, ruleResp := ruleResp
rule, internal.AddRuleResponse(&resp, &ruleResp, startTime)
engineapi.ImageVerify, logger.V(4).Info("finished processing rule", "processingTime", ruleResp.Stats.ProcessingTime.String())
) }
matchedResource = resource if applyRules == kyvernov1.ApplyOne && resp.Stats.RulesAppliedCount > 0 {
for _, ruleResp := range ruleResp { break
ruleResp := ruleResp
internal.AddRuleResponse(&resp, &ruleResp, startTime)
logger.V(4).Info("finished processing rule", "processingTime", ruleResp.Stats.ProcessingTime.String())
}
if applyRules == kyvernov1.ApplyOne && resp.Stats.RulesAppliedCount > 0 {
break
}
} }
} }
// TODO: i doesn't make sense to not return the patched resource here // TODO: i doesn't make sense to not return the patched resource here

View file

@ -22,26 +22,26 @@ func (e *engine) mutate(
resp := engineapi.NewPolicyResponse() resp := engineapi.NewPolicyResponse()
policy := policyContext.Policy() policy := policyContext.Policy()
matchedResource := policyContext.NewResource() matchedResource := policyContext.NewResource()
applyRules := policy.GetSpec().GetApplyRules()
policyContext.JSONContext().Checkpoint() policyContext.JSONContext().Checkpoint()
defer policyContext.JSONContext().Restore() defer policyContext.JSONContext().Restore()
applyRules := policy.GetSpec().GetApplyRules()
for _, rule := range autogen.ComputeRules(policy) { for _, rule := range autogen.ComputeRules(policy) {
startTime := time.Now() startTime := time.Now()
logger := internal.LoggerWithRule(logger, rule) logger := internal.LoggerWithRule(logger, rule)
if !rule.HasMutate() { if !rule.HasMutate() {
continue continue
} }
handlerFactory := handlers.WithHandler(e.mutateResourceHandler) var handler handlers.Handler
handler = e.mutateResourceHandler
if !policyContext.AdmissionOperation() && rule.IsMutateExisting() { if !policyContext.AdmissionOperation() && rule.IsMutateExisting() {
handlerFactory = handlers.WithHandler(e.mutateExistingHandler) handler = e.mutateExistingHandler
} }
resource, ruleResp := e.invokeRuleHandler( resource, ruleResp := e.invokeRuleHandler(
ctx, ctx,
logger, logger,
handlerFactory, handler,
policyContext, policyContext,
matchedResource, matchedResource,
rule, rule,

View file

@ -9,7 +9,6 @@ import (
"github.com/kyverno/kyverno/pkg/autogen" "github.com/kyverno/kyverno/pkg/autogen"
engineapi "github.com/kyverno/kyverno/pkg/engine/api" engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/engine/handlers" "github.com/kyverno/kyverno/pkg/engine/handlers"
"github.com/kyverno/kyverno/pkg/engine/handlers/validation"
"github.com/kyverno/kyverno/pkg/engine/internal" "github.com/kyverno/kyverno/pkg/engine/internal"
) )
@ -19,56 +18,49 @@ func (e *engine) validate(
policyContext engineapi.PolicyContext, policyContext engineapi.PolicyContext,
) engineapi.PolicyResponse { ) engineapi.PolicyResponse {
resp := engineapi.NewPolicyResponse() resp := engineapi.NewPolicyResponse()
policy := policyContext.Policy()
matchedResource := policyContext.NewResource()
applyRules := policy.GetSpec().GetApplyRules()
policyContext.JSONContext().Checkpoint() policyContext.JSONContext().Checkpoint()
defer policyContext.JSONContext().Restore() defer policyContext.JSONContext().Restore()
applyRules := policyContext.Policy().GetSpec().GetApplyRules() for _, rule := range autogen.ComputeRules(policy) {
for _, rule := range autogen.ComputeRules(policyContext.Policy()) {
logger := internal.LoggerWithRule(logger, rule)
startTime := time.Now() startTime := time.Now()
logger := internal.LoggerWithRule(logger, rule)
hasValidate := rule.HasValidate() hasValidate := rule.HasValidate()
hasVerifyImageChecks := rule.HasVerifyImageChecks() hasVerifyImageChecks := rule.HasVerifyImageChecks()
if !hasValidate && !hasVerifyImageChecks { if !hasValidate && !hasVerifyImageChecks {
continue continue
} }
var handlerFactory handlers.HandlerFactory var handler handlers.Handler
if hasValidate { if hasValidate {
hasVerifyManifest := rule.HasVerifyManifests() hasVerifyManifest := rule.HasVerifyManifests()
hasValidatePss := rule.HasValidatePodSecurity() hasValidatePss := rule.HasValidatePodSecurity()
if hasVerifyManifest { if hasVerifyManifest {
handlerFactory = handlers.WithHandler(e.validateManifestHandler) handler = e.validateManifestHandler
} else if hasValidatePss { } else if hasValidatePss {
handlerFactory = handlers.WithHandler(e.validatePssHandler) handler = e.validatePssHandler
} else { } else {
handlerFactory = handlers.WithHandler(e.validateResourceHandler) handler = e.validateResourceHandler
} }
} else if hasVerifyImageChecks { } else if hasVerifyImageChecks {
handlerFactory = func() (handlers.Handler, error) { handler = e.validateImageHandler
return validation.NewValidateImageHandler(
policyContext,
policyContext.NewResource(),
rule,
e.configuration,
)
}
} }
if handlerFactory != nil { resource, ruleResp := e.invokeRuleHandler(
_, ruleResp := e.invokeRuleHandler( ctx,
ctx, logger,
logger, handler,
handlerFactory, policyContext,
policyContext, matchedResource,
policyContext.NewResource(), rule,
rule, engineapi.Validation,
engineapi.Validation, )
) matchedResource = resource
for _, ruleResp := range ruleResp { for _, ruleResp := range ruleResp {
ruleResp := ruleResp ruleResp := ruleResp
internal.AddRuleResponse(&resp, &ruleResp, startTime) internal.AddRuleResponse(&resp, &ruleResp, startTime)
logger.V(4).Info("finished processing rule", "processingTime", ruleResp.Stats.ProcessingTime.String()) logger.V(4).Info("finished processing rule", "processingTime", ruleResp.Stats.ProcessingTime.String())
}
} }
if applyRules == kyvernov1.ApplyOne && resp.Stats.RulesAppliedCount > 0 { if applyRules == kyvernov1.ApplyOne && resp.Stats.RulesAppliedCount > 0 {
break break