From 3ff83c7bdd1d955fedbc948e5b181577a228c5a2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Charles-Edouard=20Br=C3=A9t=C3=A9ch=C3=A9?=
 <charled.breteche@gmail.com>
Date: Wed, 8 Feb 2023 06:55:03 +0100
Subject: [PATCH] refactor: context loading and engine methods (#6253)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* refactor: context loading and engine methods

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>
---
 cmd/background-controller/main.go             |  3 +-
 .../kubectl-kyverno/utils/common/common.go    | 16 ++---
 cmd/kyverno/main.go                           |  3 +-
 cmd/reports-controller/main.go                |  4 +-
 pkg/background/generate/generate.go           |  4 +-
 .../report/background/controller.go           |  6 +-
 pkg/controllers/report/utils/scanner.go       | 22 ++-----
 pkg/engine/api/contextloader.go               | 13 +++-
 pkg/engine/api/engine.go                      | 14 ++--
 pkg/engine/background.go                      |  3 +-
 pkg/engine/engine.go                          | 32 +++++++---
 pkg/engine/generation.go                      |  2 +
 pkg/engine/imageVerify.go                     |  6 +-
 pkg/engine/imageVerifyValidate.go             |  2 +-
 pkg/engine/imageVerify_test.go                |  4 +-
 pkg/engine/internal/context.go                | 10 ++-
 pkg/engine/jsonContext.go                     | 64 +++++++++----------
 pkg/engine/mutation.go                        | 10 +--
 pkg/engine/mutation_test.go                   |  3 +-
 pkg/engine/validation.go                      | 10 +--
 pkg/engine/validation_test.go                 |  3 +-
 pkg/policy/policy_controller.go               |  2 +-
 pkg/testrunner/scenario.go                    |  9 +--
 pkg/webhooks/resource/fake.go                 |  3 +-
 .../resource/generation/generation.go         |  2 +-
 pkg/webhooks/resource/handlers.go             |  2 +-
 .../resource/imageverification/handler.go     |  6 +-
 pkg/webhooks/resource/updaterequest.go        |  2 +-
 pkg/webhooks/resource/validation_test.go      |  6 +-
 29 files changed, 143 insertions(+), 123 deletions(-)

diff --git a/cmd/background-controller/main.go b/cmd/background-controller/main.go
index 69759dfa99..f7bc6cb41c 100644
--- a/cmd/background-controller/main.go
+++ b/cmd/background-controller/main.go
@@ -214,7 +214,8 @@ func main() {
 	engine := engine.NewEngine(
 		configuration,
 		dClient,
-		engine.LegacyContextLoaderFactory(dClient, rclient, configMapResolver),
+		rclient,
+		engine.LegacyContextLoaderFactory(configMapResolver),
 		// TODO: do we need exceptions here ?
 		nil,
 	)
diff --git a/cmd/cli/kubectl-kyverno/utils/common/common.go b/cmd/cli/kubectl-kyverno/utils/common/common.go
index 9480f01d59..e9634d0aa2 100644
--- a/cmd/cli/kubectl-kyverno/utils/common/common.go
+++ b/cmd/cli/kubectl-kyverno/utils/common/common.go
@@ -477,7 +477,8 @@ OuterLoop:
 	eng := engine.NewEngine(
 		cfg,
 		c.Client,
-		engine.LegacyContextLoaderFactory(c.Client, registryclient.NewOrDie(), nil),
+		registryclient.NewOrDie(),
+		engine.LegacyContextLoaderFactory(nil),
 		nil,
 	)
 	policyContext := engine.NewPolicyContextWithJsonContext(ctx).
@@ -525,11 +526,7 @@ OuterLoop:
 		engineResponses = append(engineResponses, validateResponse)
 	}
 
-	verifyImageResponse, _ := eng.VerifyAndPatchImages(
-		context.Background(),
-		registryclient.NewOrDie(),
-		policyContext,
-	)
+	verifyImageResponse, _ := eng.VerifyAndPatchImages(context.TODO(), policyContext)
 	if verifyImageResponse != nil && !verifyImageResponse.IsEmpty() {
 		engineResponses = append(engineResponses, verifyImageResponse)
 		info = ProcessValidateEngineResponse(c.Policy, verifyImageResponse, resPath, c.Rc, c.PolicyReport, c.AuditWarn)
@@ -543,9 +540,7 @@ OuterLoop:
 	}
 
 	if policyHasGenerate {
-		generateResponse := eng.ApplyBackgroundChecks(
-			policyContext,
-		)
+		generateResponse := eng.ApplyBackgroundChecks(context.TODO(), policyContext)
 		if generateResponse != nil && !generateResponse.IsEmpty() {
 			newRuleResponse, err := handleGeneratePolicy(generateResponse, *policyContext, c.RuleToCloneSourceResource)
 			if err != nil {
@@ -1081,7 +1076,8 @@ func initializeMockController(objects []runtime.Object) (*generate.GenerateContr
 	c := generate.NewGenerateControllerWithOnlyClient(client, engine.NewEngine(
 		config.NewDefaultConfiguration(),
 		client,
-		engine.LegacyContextLoaderFactory(client, nil, nil),
+		nil,
+		engine.LegacyContextLoaderFactory(nil),
 		nil,
 	))
 	return c, nil
diff --git a/cmd/kyverno/main.go b/cmd/kyverno/main.go
index f5d7c77f25..f4dd44884d 100644
--- a/cmd/kyverno/main.go
+++ b/cmd/kyverno/main.go
@@ -367,7 +367,8 @@ func main() {
 	eng := engine.NewEngine(
 		configuration,
 		dClient,
-		engine.LegacyContextLoaderFactory(dClient, rclient, configMapResolver),
+		rclient,
+		engine.LegacyContextLoaderFactory(configMapResolver),
 		exceptionsLister,
 	)
 	// create non leader controllers
diff --git a/cmd/reports-controller/main.go b/cmd/reports-controller/main.go
index 75493be2ea..a21675adeb 100644
--- a/cmd/reports-controller/main.go
+++ b/cmd/reports-controller/main.go
@@ -129,7 +129,6 @@ func createReportControllers(
 				backgroundscancontroller.NewController(
 					client,
 					kyvernoClient,
-					rclient,
 					eng,
 					metadataFactory,
 					kyvernoV1.Policies(),
@@ -315,7 +314,8 @@ func main() {
 	eng := engine.NewEngine(
 		configuration,
 		dClient,
-		engine.LegacyContextLoaderFactory(dClient, rclient, configMapResolver),
+		rclient,
+		engine.LegacyContextLoaderFactory(configMapResolver),
 		exceptionsLister,
 	)
 	// setup leader election
diff --git a/pkg/background/generate/generate.go b/pkg/background/generate/generate.go
index 4507c56580..eb249f1e2a 100644
--- a/pkg/background/generate/generate.go
+++ b/pkg/background/generate/generate.go
@@ -197,7 +197,7 @@ func (c *GenerateController) applyGenerate(resource unstructured.Unstructured, u
 	}
 
 	// check if the policy still applies to the resource
-	engineResponse := c.engine.GenerateResponse(policyContext, ur)
+	engineResponse := c.engine.GenerateResponse(context.Background(), policyContext, ur)
 	if len(engineResponse.PolicyResponse.Rules) == 0 {
 		logger.V(4).Info(doesNotApply)
 		return nil, false, errors.New(doesNotApply)
@@ -343,7 +343,7 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext
 		}
 
 		// add configmap json data to context
-		if err := c.engine.ContextLoader(policyContext, rule.Name).Load(context.TODO(), rule.Context, policyContext.JSONContext()); err != nil {
+		if err := c.engine.ContextLoader(policyContext.Policy(), rule)(context.TODO(), rule.Context, policyContext.JSONContext()); err != nil {
 			log.Error(err, "cannot add configmaps to context")
 			return nil, processExisting, err
 		}
diff --git a/pkg/controllers/report/background/controller.go b/pkg/controllers/report/background/controller.go
index 382131946f..a3e859a94f 100644
--- a/pkg/controllers/report/background/controller.go
+++ b/pkg/controllers/report/background/controller.go
@@ -19,7 +19,6 @@ import (
 	"github.com/kyverno/kyverno/pkg/controllers/report/utils"
 	engineapi "github.com/kyverno/kyverno/pkg/engine/api"
 	"github.com/kyverno/kyverno/pkg/event"
-	"github.com/kyverno/kyverno/pkg/registryclient"
 	controllerutils "github.com/kyverno/kyverno/pkg/utils/controller"
 	reportutils "github.com/kyverno/kyverno/pkg/utils/report"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -47,7 +46,6 @@ type controller struct {
 	// clients
 	client        dclient.Interface
 	kyvernoClient versioned.Interface
-	rclient       registryclient.Client
 	engine        engineapi.Engine
 
 	// listers
@@ -73,7 +71,6 @@ type controller struct {
 func NewController(
 	client dclient.Interface,
 	kyvernoClient versioned.Interface,
-	rclient registryclient.Client,
 	engine engineapi.Engine,
 	metadataFactory metadatainformers.SharedInformerFactory,
 	polInformer kyvernov1informers.PolicyInformer,
@@ -91,7 +88,6 @@ func NewController(
 	c := controller{
 		client:                 client,
 		kyvernoClient:          kyvernoClient,
-		rclient:                rclient,
 		engine:                 engine,
 		polLister:              polInformer.Lister(),
 		cpolLister:             cpolInformer.Lister(),
@@ -308,7 +304,7 @@ func (c *controller) reconcileReport(
 	// calculate necessary results
 	for _, policy := range backgroundPolicies {
 		if full || actual[reportutils.PolicyLabel(policy)] != policy.GetResourceVersion() {
-			scanner := utils.NewScanner(logger, c.engine, c.client, c.rclient, c.config)
+			scanner := utils.NewScanner(logger, c.engine, c.config)
 			for _, result := range scanner.ScanResource(ctx, *target, nsLabels, policy) {
 				if result.Error != nil {
 					return result.Error
diff --git a/pkg/controllers/report/utils/scanner.go b/pkg/controllers/report/utils/scanner.go
index 238acb7807..4fb212618c 100644
--- a/pkg/controllers/report/utils/scanner.go
+++ b/pkg/controllers/report/utils/scanner.go
@@ -5,22 +5,18 @@ import (
 
 	"github.com/go-logr/logr"
 	kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
-	"github.com/kyverno/kyverno/pkg/clients/dclient"
 	"github.com/kyverno/kyverno/pkg/config"
 	"github.com/kyverno/kyverno/pkg/engine"
 	engineapi "github.com/kyverno/kyverno/pkg/engine/api"
 	enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
-	"github.com/kyverno/kyverno/pkg/registryclient"
 	"go.uber.org/multierr"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 )
 
 type scanner struct {
-	logger  logr.Logger
-	engine  engineapi.Engine
-	client  dclient.Interface
-	rclient registryclient.Client
-	config  config.Configuration
+	logger logr.Logger
+	engine engineapi.Engine
+	config config.Configuration
 }
 
 type ScanResult struct {
@@ -35,16 +31,12 @@ type Scanner interface {
 func NewScanner(
 	logger logr.Logger,
 	engine engineapi.Engine,
-	client dclient.Interface,
-	rclient registryclient.Client,
 	config config.Configuration,
 ) Scanner {
 	return &scanner{
-		logger:  logger,
-		engine:  engine,
-		client:  client,
-		rclient: rclient,
-		config:  config,
+		logger: logger,
+		engine: engine,
+		config: config,
 	}
 }
 
@@ -114,7 +106,7 @@ func (s *scanner) validateImages(ctx context.Context, resource unstructured.Unst
 		WithNewResource(resource).
 		WithPolicy(policy).
 		WithNamespaceLabels(nsLabels)
-	response, _ := s.engine.VerifyAndPatchImages(ctx, s.rclient, policyCtx)
+	response, _ := s.engine.VerifyAndPatchImages(ctx, policyCtx)
 	if len(response.PolicyResponse.Rules) > 0 {
 		s.logger.Info("validateImages", "policy", policy, "response", response)
 	}
diff --git a/pkg/engine/api/contextloader.go b/pkg/engine/api/contextloader.go
index ed99fbe452..92e6b790bd 100644
--- a/pkg/engine/api/contextloader.go
+++ b/pkg/engine/api/contextloader.go
@@ -4,13 +4,22 @@ import (
 	"context"
 
 	kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
+	"github.com/kyverno/kyverno/pkg/clients/dclient"
 	enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 )
 
 // ContextLoaderFactory provides a ContextLoader given a policy context and rule name
-type ContextLoaderFactory = func(pContext PolicyContext, ruleName string) ContextLoader
+type ContextLoaderFactory = func(policy kyvernov1.PolicyInterface, rule kyvernov1.Rule) ContextLoader
 
 // ContextLoader abstracts the mechanics to load context entries in the underlying json context
 type ContextLoader interface {
-	Load(ctx context.Context, contextEntries []kyvernov1.ContextEntry, jsonContext enginecontext.Interface) error
+	Load(
+		ctx context.Context,
+		client dclient.Interface,
+		rclient registryclient.Client,
+		exceptionSelector PolicyExceptionSelector,
+		contextEntries []kyvernov1.ContextEntry,
+		jsonContext enginecontext.Interface,
+	) error
 }
diff --git a/pkg/engine/api/engine.go b/pkg/engine/api/engine.go
index e969f5b1de..e7f85bd9eb 100644
--- a/pkg/engine/api/engine.go
+++ b/pkg/engine/api/engine.go
@@ -3,10 +3,13 @@ package api
 import (
 	"context"
 
+	kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
 	kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
-	"github.com/kyverno/kyverno/pkg/registryclient"
+	enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
 )
 
+type EngineContextLoader = func(ctx context.Context, contextEntries []kyvernov1.ContextEntry, jsonContext enginecontext.Interface) error
+
 // Engine is the main interface to run policies against resources
 type Engine interface {
 	// Validate applies validation rules from policy on the resource
@@ -24,7 +27,6 @@ type Engine interface {
 	// VerifyAndPatchImages ...
 	VerifyAndPatchImages(
 		ctx context.Context,
-		rclient registryclient.Client,
 		policyContext PolicyContext,
 	) (*EngineResponse, *ImageVerificationMetadata)
 
@@ -34,17 +36,19 @@ type Engine interface {
 	//
 	// 2. returns the list of rules that are applicable on this policy and resource, if 1 succeed
 	ApplyBackgroundChecks(
+		ctx context.Context,
 		policyContext PolicyContext,
 	) *EngineResponse
 
 	// GenerateResponse checks for validity of generate rule on the resource
 	GenerateResponse(
+		ctx context.Context,
 		policyContext PolicyContext,
 		gr kyvernov1beta1.UpdateRequest,
 	) *EngineResponse
 
 	ContextLoader(
-		policyContext PolicyContext,
-		ruleName string,
-	) ContextLoader
+		policy kyvernov1.PolicyInterface,
+		rule kyvernov1.Rule,
+	) EngineContextLoader
 }
diff --git a/pkg/engine/background.go b/pkg/engine/background.go
index 7b40ba879a..1e5679663c 100644
--- a/pkg/engine/background.go
+++ b/pkg/engine/background.go
@@ -19,6 +19,7 @@ import (
 //
 // 2. returns the list of rules that are applicable on this policy and resource, if 1 succeed
 func (e *engine) applyBackgroundChecks(
+	ctx context.Context,
 	policyContext engineapi.PolicyContext,
 ) (resp *engineapi.EngineResponse) {
 	policyStartTime := time.Now()
@@ -132,7 +133,7 @@ func (e *engine) filterRule(
 	policyContext.JSONContext().Checkpoint()
 	defer policyContext.JSONContext().Restore()
 
-	if err := internal.LoadContext(context.TODO(), e.contextLoader, rule.Context, policyContext, rule.Name); err != nil {
+	if err := internal.LoadContext(context.TODO(), e, policyContext, rule); err != nil {
 		logger.V(4).Info("cannot add external data to the context", "reason", err.Error())
 		return nil
 	}
diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go
index 6562c5c478..746ec0e12e 100644
--- a/pkg/engine/engine.go
+++ b/pkg/engine/engine.go
@@ -3,16 +3,19 @@ package engine
 import (
 	"context"
 
+	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/config"
 	engineapi "github.com/kyverno/kyverno/pkg/engine/api"
+	enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
 	"github.com/kyverno/kyverno/pkg/registryclient"
 )
 
 type engine struct {
 	configuration     config.Configuration
 	client            dclient.Interface
+	rclient           registryclient.Client
 	contextLoader     engineapi.ContextLoaderFactory
 	exceptionSelector engineapi.PolicyExceptionSelector
 }
@@ -20,12 +23,14 @@ type engine struct {
 func NewEngine(
 	configuration config.Configuration,
 	client dclient.Interface,
+	rclient registryclient.Client,
 	contextLoader engineapi.ContextLoaderFactory,
 	exceptionSelector engineapi.PolicyExceptionSelector,
 ) engineapi.Engine {
 	return &engine{
 		configuration:     configuration,
 		client:            client,
+		rclient:           rclient,
 		contextLoader:     contextLoader,
 		exceptionSelector: exceptionSelector,
 	}
@@ -47,28 +52,39 @@ func (e *engine) Mutate(
 
 func (e *engine) VerifyAndPatchImages(
 	ctx context.Context,
-	rclient registryclient.Client,
 	policyContext engineapi.PolicyContext,
 ) (*engineapi.EngineResponse, *engineapi.ImageVerificationMetadata) {
-	return e.verifyAndPatchImages(ctx, rclient, policyContext)
+	return e.verifyAndPatchImages(ctx, policyContext)
 }
 
 func (e *engine) ApplyBackgroundChecks(
+	ctx context.Context,
 	policyContext engineapi.PolicyContext,
 ) *engineapi.EngineResponse {
-	return e.applyBackgroundChecks(policyContext)
+	return e.applyBackgroundChecks(ctx, policyContext)
 }
 
 func (e *engine) GenerateResponse(
+	ctx context.Context,
 	policyContext engineapi.PolicyContext,
 	gr kyvernov1beta1.UpdateRequest,
 ) *engineapi.EngineResponse {
-	return e.generateResponse(policyContext, gr)
+	return e.generateResponse(ctx, policyContext, gr)
 }
 
 func (e *engine) ContextLoader(
-	policyContext engineapi.PolicyContext,
-	ruleName string,
-) engineapi.ContextLoader {
-	return e.contextLoader(policyContext, ruleName)
+	policy kyvernov1.PolicyInterface,
+	rule kyvernov1.Rule,
+) engineapi.EngineContextLoader {
+	loader := e.contextLoader(policy, rule)
+	return func(ctx context.Context, contextEntries []kyvernov1.ContextEntry, jsonContext enginecontext.Interface) error {
+		return loader.Load(
+			ctx,
+			e.client,
+			e.rclient,
+			e.exceptionSelector,
+			contextEntries,
+			jsonContext,
+		)
+	}
 }
diff --git a/pkg/engine/generation.go b/pkg/engine/generation.go
index 5ef86ad24f..1a8372485e 100644
--- a/pkg/engine/generation.go
+++ b/pkg/engine/generation.go
@@ -1,6 +1,7 @@
 package engine
 
 import (
+	"context"
 	"time"
 
 	kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
@@ -12,6 +13,7 @@ import (
 
 // GenerateResponse checks for validity of generate rule on the resource
 func (e *engine) generateResponse(
+	ctx context.Context,
 	policyContext engineapi.PolicyContext,
 	gr kyvernov1beta1.UpdateRequest,
 ) (resp *engineapi.EngineResponse) {
diff --git a/pkg/engine/imageVerify.go b/pkg/engine/imageVerify.go
index 4f776e4819..2ffbb0df7f 100644
--- a/pkg/engine/imageVerify.go
+++ b/pkg/engine/imageVerify.go
@@ -14,7 +14,6 @@ import (
 	"github.com/kyverno/kyverno/pkg/engine/internal"
 	"github.com/kyverno/kyverno/pkg/engine/variables"
 	"github.com/kyverno/kyverno/pkg/logging"
-	"github.com/kyverno/kyverno/pkg/registryclient"
 	"github.com/kyverno/kyverno/pkg/tracing"
 	apiutils "github.com/kyverno/kyverno/pkg/utils/api"
 	"github.com/kyverno/kyverno/pkg/utils/wildcard"
@@ -23,7 +22,6 @@ import (
 
 func (e *engine) verifyAndPatchImages(
 	ctx context.Context,
-	rclient registryclient.Client,
 	policyContext engineapi.PolicyContext,
 ) (*engineapi.EngineResponse, *engineapi.ImageVerificationMetadata) {
 	resp := &engineapi.EngineResponse{}
@@ -98,7 +96,7 @@ func (e *engine) verifyAndPatchImages(
 					return
 				}
 				policyContext.JSONContext().Restore()
-				if err := internal.LoadContext(ctx, e.contextLoader, rule.Context, policyContext, rule.Name); err != nil {
+				if err := internal.LoadContext(ctx, e, policyContext, *rule); err != nil {
 					internal.AddRuleResponse(
 						&resp.PolicyResponse,
 						internal.RuleError(rule, engineapi.ImageVerify, "failed to load context", err),
@@ -117,7 +115,7 @@ func (e *engine) verifyAndPatchImages(
 				}
 				iv := internal.NewImageVerifier(
 					logger,
-					rclient,
+					e.rclient,
 					policyContext,
 					ruleCopy,
 					ivm,
diff --git a/pkg/engine/imageVerifyValidate.go b/pkg/engine/imageVerifyValidate.go
index f2037d7a7e..0d85c72878 100644
--- a/pkg/engine/imageVerifyValidate.go
+++ b/pkg/engine/imageVerifyValidate.go
@@ -32,7 +32,7 @@ func (e *engine) processImageValidationRule(
 	if len(matchingImages) == 0 {
 		return internal.RuleSkip(rule, engineapi.Validation, "image verified")
 	}
-	if err := internal.LoadContext(ctx, e.contextLoader, rule.Context, enginectx, rule.Name); err != nil {
+	if err := internal.LoadContext(ctx, e, enginectx, *rule); err != nil {
 		if _, ok := err.(gojmespath.NotFoundError); ok {
 			log.V(3).Info("failed to load context", "reason", err.Error())
 		} else {
diff --git a/pkg/engine/imageVerify_test.go b/pkg/engine/imageVerify_test.go
index 3fbf56af05..98aa7d2bfe 100644
--- a/pkg/engine/imageVerify_test.go
+++ b/pkg/engine/imageVerify_test.go
@@ -171,12 +171,12 @@ func testVerifyAndPatchImages(
 	e := NewEngine(
 		cfg,
 		nil,
-		LegacyContextLoaderFactory(nil, rclient, cmResolver),
+		rclient,
+		LegacyContextLoaderFactory(cmResolver),
 		nil,
 	)
 	return e.VerifyAndPatchImages(
 		ctx,
-		rclient,
 		pContext,
 	)
 }
diff --git a/pkg/engine/internal/context.go b/pkg/engine/internal/context.go
index 43b04cb42a..6d9d1f09f6 100644
--- a/pkg/engine/internal/context.go
+++ b/pkg/engine/internal/context.go
@@ -7,6 +7,12 @@ import (
 	engineapi "github.com/kyverno/kyverno/pkg/engine/api"
 )
 
-func LoadContext(ctx context.Context, factory engineapi.ContextLoaderFactory, contextEntries []kyvernov1.ContextEntry, pContext engineapi.PolicyContext, ruleName string) error {
-	return factory(pContext, ruleName).Load(ctx, contextEntries, pContext.JSONContext())
+func LoadContext(
+	ctx context.Context,
+	engine engineapi.Engine,
+	pContext engineapi.PolicyContext,
+	rule kyvernov1.Rule,
+) error {
+	loader := engine.ContextLoader(pContext.Policy(), rule)
+	return loader(ctx, rule.Context, pContext.JSONContext())
 }
diff --git a/pkg/engine/jsonContext.go b/pkg/engine/jsonContext.go
index 076341243c..cb48e92ae6 100644
--- a/pkg/engine/jsonContext.go
+++ b/pkg/engine/jsonContext.go
@@ -19,56 +19,52 @@ import (
 )
 
 func LegacyContextLoaderFactory(
-	client dclient.Interface,
-	rclient registryclient.Client,
 	cmResolver engineapi.ConfigmapResolver,
 ) engineapi.ContextLoaderFactory {
-	if store.IsMock() {
-		return func(pContext engineapi.PolicyContext, ruleName string) engineapi.ContextLoader {
-			policy := pContext.Policy()
+	return func(policy kyvernov1.PolicyInterface, rule kyvernov1.Rule) engineapi.ContextLoader {
+		if store.IsMock() {
 			return &mockContextLoader{
 				logger:     logging.WithName("MockContextLoaderFactory"),
 				policyName: policy.GetName(),
-				ruleName:   ruleName,
-				client:     client,
-				rclient:    rclient,
+				ruleName:   rule.Name,
+			}
+		} else {
+			return &contextLoader{
+				logger:     logging.WithName("LegacyContextLoaderFactory"),
 				cmResolver: cmResolver,
 			}
 		}
 	}
-	return func(pContext engineapi.PolicyContext, ruleName string) engineapi.ContextLoader {
-		return &contextLoader{
-			logger:     logging.WithName("LegacyContextLoaderFactory"),
-			client:     client,
-			rclient:    rclient,
-			cmResolver: cmResolver,
-		}
-	}
 }
 
 type contextLoader struct {
 	logger     logr.Logger
-	rclient    registryclient.Client
-	client     dclient.Interface
 	cmResolver engineapi.ConfigmapResolver
 }
 
-func (l *contextLoader) Load(ctx context.Context, contextEntries []kyvernov1.ContextEntry, enginectx enginecontext.Interface) error {
+func (l *contextLoader) Load(
+	ctx context.Context,
+	client dclient.Interface,
+	rclient registryclient.Client,
+	exceptionSelector engineapi.PolicyExceptionSelector,
+	contextEntries []kyvernov1.ContextEntry,
+	jsonContext enginecontext.Interface,
+) error {
 	for _, entry := range contextEntries {
 		if entry.ConfigMap != nil {
-			if err := loadConfigMap(ctx, l.logger, entry, enginectx, l.cmResolver); err != nil {
+			if err := loadConfigMap(ctx, l.logger, entry, jsonContext, l.cmResolver); err != nil {
 				return err
 			}
 		} else if entry.APICall != nil {
-			if err := loadAPIData(ctx, l.logger, entry, enginectx, l.client); err != nil {
+			if err := loadAPIData(ctx, l.logger, entry, jsonContext, client); err != nil {
 				return err
 			}
 		} else if entry.ImageRegistry != nil {
-			if err := loadImageData(ctx, l.rclient, l.logger, entry, enginectx); err != nil {
+			if err := loadImageData(ctx, rclient, l.logger, entry, jsonContext); err != nil {
 				return err
 			}
 		} else if entry.Variable != nil {
-			if err := loadVariable(l.logger, entry, enginectx); err != nil {
+			if err := loadVariable(l.logger, entry, jsonContext); err != nil {
 				return err
 			}
 		}
@@ -80,17 +76,21 @@ type mockContextLoader struct {
 	logger     logr.Logger
 	policyName string
 	ruleName   string
-	rclient    registryclient.Client
-	client     dclient.Interface
-	cmResolver engineapi.ConfigmapResolver
 }
 
-func (l *mockContextLoader) Load(ctx context.Context, contextEntries []kyvernov1.ContextEntry, enginectx enginecontext.Interface) error {
+func (l *mockContextLoader) Load(
+	ctx context.Context,
+	client dclient.Interface,
+	_ registryclient.Client,
+	_ engineapi.PolicyExceptionSelector,
+	contextEntries []kyvernov1.ContextEntry,
+	jsonContext enginecontext.Interface,
+) error {
 	rule := store.GetPolicyRule(l.policyName, l.ruleName)
 	if rule != nil && len(rule.Values) > 0 {
 		variables := rule.Values
 		for key, value := range variables {
-			if err := enginectx.AddVariable(key, value); err != nil {
+			if err := jsonContext.AddVariable(key, value); err != nil {
 				return err
 			}
 		}
@@ -100,22 +100,22 @@ func (l *mockContextLoader) Load(ctx context.Context, contextEntries []kyvernov1
 	for _, entry := range contextEntries {
 		if entry.ImageRegistry != nil && hasRegistryAccess {
 			rclient := store.GetRegistryClient()
-			if err := loadImageData(ctx, rclient, l.logger, entry, enginectx); err != nil {
+			if err := loadImageData(ctx, rclient, l.logger, entry, jsonContext); err != nil {
 				return err
 			}
 		} else if entry.Variable != nil {
-			if err := loadVariable(l.logger, entry, enginectx); err != nil {
+			if err := loadVariable(l.logger, entry, jsonContext); err != nil {
 				return err
 			}
 		} else if entry.APICall != nil && store.IsApiCallAllowed() {
-			if err := loadAPIData(ctx, l.logger, entry, enginectx, l.client); err != nil {
+			if err := loadAPIData(ctx, l.logger, entry, jsonContext, client); err != nil {
 				return err
 			}
 		}
 	}
 	if rule != nil && len(rule.ForEachValues) > 0 {
 		for key, value := range rule.ForEachValues {
-			if err := enginectx.AddVariable(key, value[store.GetForeachElement()]); err != nil {
+			if err := jsonContext.AddVariable(key, value[store.GetForeachElement()]); err != nil {
 				return err
 			}
 		}
diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go
index 8ebb3c21f9..b4ff0a5f30 100644
--- a/pkg/engine/mutation.go
+++ b/pkg/engine/mutation.go
@@ -91,7 +91,7 @@ func (e *engine) mutate(
 					logger.Error(err, "failed to query resource object")
 				}
 
-				if err := internal.LoadContext(ctx, e.contextLoader, rule.Context, policyContext, rule.Name); err != nil {
+				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 {
@@ -144,7 +144,7 @@ func (e *engine) mutate(
 							policyContext: policyContext,
 							resource:      patchedResource,
 							log:           logger,
-							contextLoader: e.contextLoader,
+							contextLoader: e.ContextLoader(policyContext.Policy(), *ruleCopy),
 							nesting:       0,
 						}
 
@@ -198,7 +198,7 @@ type forEachMutator struct {
 	foreach       []kyvernov1.ForEachMutation
 	resource      resourceInfo
 	nesting       int
-	contextLoader engineapi.ContextLoaderFactory
+	contextLoader engineapi.EngineContextLoader
 	log           logr.Logger
 }
 
@@ -207,7 +207,7 @@ func (f *forEachMutator) mutateForEach(ctx context.Context) *mutate.Response {
 	allPatches := make([][]byte, 0)
 
 	for _, foreach := range f.foreach {
-		if err := internal.LoadContext(ctx, f.contextLoader, f.rule.Context, f.policyContext, f.rule.Name); err != nil {
+		if err := f.contextLoader(ctx, f.rule.Context, f.policyContext.JSONContext()); err != nil {
 			f.log.Error(err, "failed to load context")
 			return mutate.NewErrorResponse("failed to load context", err)
 		}
@@ -272,7 +272,7 @@ func (f *forEachMutator) mutateElements(ctx context.Context, foreach kyvernov1.F
 			return mutate.NewErrorResponse(fmt.Sprintf("failed to add element to mutate.foreach[%d].context", index), err)
 		}
 
-		if err := internal.LoadContext(ctx, f.contextLoader, foreach.Context, policyContext, f.rule.Name); err != nil {
+		if err := f.contextLoader(ctx, foreach.Context, policyContext.JSONContext()); err != nil {
 			return mutate.NewErrorResponse(fmt.Sprintf("failed to load to mutate.foreach[%d].context", index), err)
 		}
 
diff --git a/pkg/engine/mutation_test.go b/pkg/engine/mutation_test.go
index bc95120338..e749d79e7a 100644
--- a/pkg/engine/mutation_test.go
+++ b/pkg/engine/mutation_test.go
@@ -30,7 +30,8 @@ func testMutate(
 	e := NewEngine(
 		cfg,
 		client,
-		LegacyContextLoaderFactory(client, rclient, nil),
+		rclient,
+		LegacyContextLoaderFactory(nil),
 		nil,
 	)
 	return e.Mutate(
diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go
index 2451376ba6..52f22a07e4 100644
--- a/pkg/engine/validation.go
+++ b/pkg/engine/validation.go
@@ -127,7 +127,7 @@ func (e *engine) processValidationRule(
 	policyContext engineapi.PolicyContext,
 	rule *kyvernov1.Rule,
 ) *engineapi.RuleResponse {
-	v := newValidator(log, e.contextLoader, policyContext, rule)
+	v := newValidator(log, e.ContextLoader(policyContext.Policy(), *rule), policyContext, rule)
 	return v.validate(ctx)
 }
 
@@ -142,11 +142,11 @@ type validator struct {
 	deny             *kyvernov1.Deny
 	podSecurity      *kyvernov1.PodSecurity
 	forEach          []kyvernov1.ForEachValidation
-	contextLoader    engineapi.ContextLoaderFactory
+	contextLoader    engineapi.EngineContextLoader
 	nesting          int
 }
 
-func newValidator(log logr.Logger, contextLoader engineapi.ContextLoaderFactory, ctx engineapi.PolicyContext, rule *kyvernov1.Rule) *validator {
+func newValidator(log logr.Logger, contextLoader engineapi.EngineContextLoader, ctx engineapi.PolicyContext, rule *kyvernov1.Rule) *validator {
 	ruleCopy := rule.DeepCopy()
 	return &validator{
 		log:              log,
@@ -165,7 +165,7 @@ func newValidator(log logr.Logger, contextLoader engineapi.ContextLoaderFactory,
 
 func newForEachValidator(
 	foreach kyvernov1.ForEachValidation,
-	contextLoader engineapi.ContextLoaderFactory,
+	contextLoader engineapi.EngineContextLoader,
 	nesting int,
 	rule *kyvernov1.Rule,
 	ctx engineapi.PolicyContext,
@@ -344,7 +344,7 @@ func addElementToContext(ctx engineapi.PolicyContext, element interface{}, index
 }
 
 func (v *validator) loadContext(ctx context.Context) error {
-	if err := internal.LoadContext(ctx, v.contextLoader, v.contextEntries, v.policyContext, v.rule.Name); err != nil {
+	if err := v.contextLoader(ctx, v.contextEntries, v.policyContext.JSONContext()); err != nil {
 		if _, ok := err.(gojmespath.NotFoundError); ok {
 			v.log.V(3).Info("failed to load context", "reason", err.Error())
 		} else {
diff --git a/pkg/engine/validation_test.go b/pkg/engine/validation_test.go
index 76b4f8ed5f..4503fa8aab 100644
--- a/pkg/engine/validation_test.go
+++ b/pkg/engine/validation_test.go
@@ -23,7 +23,8 @@ func testValidate(ctx context.Context, rclient registryclient.Client, pContext *
 	e := NewEngine(
 		cfg,
 		nil,
-		LegacyContextLoaderFactory(nil, rclient, nil),
+		rclient,
+		LegacyContextLoaderFactory(nil),
 		nil,
 	)
 	return e.Validate(
diff --git a/pkg/policy/policy_controller.go b/pkg/policy/policy_controller.go
index b0b976bcb6..527399664a 100644
--- a/pkg/policy/policy_controller.go
+++ b/pkg/policy/policy_controller.go
@@ -513,7 +513,7 @@ func (pc *PolicyController) handleUpdateRequest(ur *kyvernov1beta1.UpdateRequest
 		return false, fmt.Errorf("failed to build policy context for rule %s: %w", rule.Name, err)
 	}
 
-	engineResponse := pc.engine.ApplyBackgroundChecks(policyContext)
+	engineResponse := pc.engine.ApplyBackgroundChecks(context.TODO(), policyContext)
 	if len(engineResponse.PolicyResponse.Rules) == 0 {
 		return true, nil
 	}
diff --git a/pkg/testrunner/scenario.go b/pkg/testrunner/scenario.go
index 57eba27421..5c38f41586 100644
--- a/pkg/testrunner/scenario.go
+++ b/pkg/testrunner/scenario.go
@@ -149,7 +149,8 @@ func runTestCase(t *testing.T, tc TestCase) bool {
 	eng := engine.NewEngine(
 		config.NewDefaultConfiguration(),
 		nil,
-		engine.LegacyContextLoaderFactory(nil, registryclient.NewOrDie(), nil),
+		registryclient.NewOrDie(),
+		engine.LegacyContextLoaderFactory(nil),
 		nil,
 	)
 	er := eng.Mutate(
@@ -184,11 +185,7 @@ func runTestCase(t *testing.T, tc TestCase) bool {
 		if err := createNamespace(client, resource); err != nil {
 			t.Error(err)
 		} else {
-			// policyContext := policyContext.WithClient(client)
-
-			er = eng.ApplyBackgroundChecks(
-				policyContext,
-			)
+			er = eng.ApplyBackgroundChecks(context.TODO(), policyContext)
 			t.Log(("---Generation---"))
 			validateResponse(t, er.PolicyResponse, tc.Expected.Generation.PolicyResponse)
 			// Expected generate resource will be in same namespaces as resource
diff --git a/pkg/webhooks/resource/fake.go b/pkg/webhooks/resource/fake.go
index f5335b678c..3996f7aa27 100644
--- a/pkg/webhooks/resource/fake.go
+++ b/pkg/webhooks/resource/fake.go
@@ -59,7 +59,8 @@ func NewFakeHandlers(ctx context.Context, policyCache policycache.Cache) webhook
 		engine: engine.NewEngine(
 			configuration,
 			dclient,
-			engine.LegacyContextLoaderFactory(dclient, rclient, configMapResolver),
+			rclient,
+			engine.LegacyContextLoaderFactory(configMapResolver),
 			peLister,
 		),
 	}
diff --git a/pkg/webhooks/resource/generation/generation.go b/pkg/webhooks/resource/generation/generation.go
index 8e38733a82..9c86398b1f 100644
--- a/pkg/webhooks/resource/generation/generation.go
+++ b/pkg/webhooks/resource/generation/generation.go
@@ -92,7 +92,7 @@ func (h *generationHandler) Handle(
 			if request.Kind.Kind != "Namespace" && request.Namespace != "" {
 				policyContext = policyContext.WithNamespaceLabels(engineutils.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, h.log))
 			}
-			engineResponse := h.engine.ApplyBackgroundChecks(policyContext)
+			engineResponse := h.engine.ApplyBackgroundChecks(ctx, policyContext)
 			for _, rule := range engineResponse.PolicyResponse.Rules {
 				if rule.Status != engineapi.RuleStatusPass {
 					h.deleteGR(ctx, engineResponse)
diff --git a/pkg/webhooks/resource/handlers.go b/pkg/webhooks/resource/handlers.go
index f9a991eb20..f381777126 100644
--- a/pkg/webhooks/resource/handlers.go
+++ b/pkg/webhooks/resource/handlers.go
@@ -182,7 +182,7 @@ func (h *handlers) Mutate(ctx context.Context, logger logr.Logger, request *admi
 		logger.Error(err, "failed to build policy context")
 		return admissionutils.Response(request.UID, err)
 	}
-	ivh := imageverification.NewImageVerificationHandler(logger, h.kyvernoClient, h.engine, h.rclient, h.eventGen, h.admissionReports, h.configuration)
+	ivh := imageverification.NewImageVerificationHandler(logger, h.kyvernoClient, h.engine, h.eventGen, h.admissionReports, h.configuration)
 	imagePatches, imageVerifyWarnings, err := ivh.Handle(ctx, newRequest, verifyImagesPolicies, policyContext)
 	if err != nil {
 		logger.Error(err, "image verification failed")
diff --git a/pkg/webhooks/resource/imageverification/handler.go b/pkg/webhooks/resource/imageverification/handler.go
index 3469280fe1..915e3460af 100644
--- a/pkg/webhooks/resource/imageverification/handler.go
+++ b/pkg/webhooks/resource/imageverification/handler.go
@@ -13,7 +13,6 @@ import (
 	"github.com/kyverno/kyverno/pkg/engine"
 	engineapi "github.com/kyverno/kyverno/pkg/engine/api"
 	"github.com/kyverno/kyverno/pkg/event"
-	"github.com/kyverno/kyverno/pkg/registryclient"
 	"github.com/kyverno/kyverno/pkg/tracing"
 	controllerutils "github.com/kyverno/kyverno/pkg/utils/controller"
 	jsonutils "github.com/kyverno/kyverno/pkg/utils/json"
@@ -33,7 +32,6 @@ type ImageVerificationHandler interface {
 type imageVerificationHandler struct {
 	kyvernoClient    versioned.Interface
 	engine           engineapi.Engine
-	rclient          registryclient.Client
 	log              logr.Logger
 	eventGen         event.Interface
 	admissionReports bool
@@ -44,7 +42,6 @@ func NewImageVerificationHandler(
 	log logr.Logger,
 	kyvernoClient versioned.Interface,
 	engine engineapi.Engine,
-	rclient registryclient.Client,
 	eventGen event.Interface,
 	admissionReports bool,
 	cfg config.Configuration,
@@ -52,7 +49,6 @@ func NewImageVerificationHandler(
 	return &imageVerificationHandler{
 		kyvernoClient:    kyvernoClient,
 		engine:           engine,
-		rclient:          rclient,
 		log:              log,
 		eventGen:         eventGen,
 		admissionReports: admissionReports,
@@ -94,7 +90,7 @@ func (h *imageVerificationHandler) handleVerifyImages(
 			fmt.Sprintf("POLICY %s/%s", policy.GetNamespace(), policy.GetName()),
 			func(ctx context.Context, span trace.Span) {
 				policyContext := policyContext.WithPolicy(policy)
-				resp, ivm := h.engine.VerifyAndPatchImages(ctx, h.rclient, policyContext)
+				resp, ivm := h.engine.VerifyAndPatchImages(ctx, policyContext)
 
 				engineResponses = append(engineResponses, resp)
 				patches = append(patches, resp.GetPatches()...)
diff --git a/pkg/webhooks/resource/updaterequest.go b/pkg/webhooks/resource/updaterequest.go
index 1b6b2a5808..a20e3aa2b8 100644
--- a/pkg/webhooks/resource/updaterequest.go
+++ b/pkg/webhooks/resource/updaterequest.go
@@ -43,7 +43,7 @@ func (h *handlers) handleMutateExisting(ctx context.Context, logger logr.Logger,
 
 		var rules []engineapi.RuleResponse
 		policyContext := policyContext.WithPolicy(policy)
-		engineResponse := h.engine.ApplyBackgroundChecks(policyContext)
+		engineResponse := h.engine.ApplyBackgroundChecks(ctx, policyContext)
 
 		for _, rule := range engineResponse.PolicyResponse.Rules {
 			if rule.Status == engineapi.RuleStatusPass {
diff --git a/pkg/webhooks/resource/validation_test.go b/pkg/webhooks/resource/validation_test.go
index 91e2084f9b..970333ff6b 100644
--- a/pkg/webhooks/resource/validation_test.go
+++ b/pkg/webhooks/resource/validation_test.go
@@ -1052,7 +1052,8 @@ func TestValidate_failure_action_overrides(t *testing.T) {
 	eng := engine.NewEngine(
 		config.NewDefaultConfiguration(),
 		nil,
-		engine.LegacyContextLoaderFactory(nil, registryclient.NewOrDie(), nil),
+		registryclient.NewOrDie(),
+		engine.LegacyContextLoaderFactory(nil),
 		nil,
 	)
 	for i, tc := range testcases {
@@ -1130,7 +1131,8 @@ func Test_RuleSelector(t *testing.T) {
 	eng := engine.NewEngine(
 		config.NewDefaultConfiguration(),
 		nil,
-		engine.LegacyContextLoaderFactory(nil, registryclient.NewOrDie(), nil),
+		registryclient.NewOrDie(),
+		engine.LegacyContextLoaderFactory(nil),
 		nil,
 	)
 	resp := eng.Validate(