diff --git a/cmd/cli/kubectl-kyverno/utils/common/common.go b/cmd/cli/kubectl-kyverno/utils/common/common.go index 8885182a40..573ed7fe59 100644 --- a/cmd/cli/kubectl-kyverno/utils/common/common.go +++ b/cmd/cli/kubectl-kyverno/utils/common/common.go @@ -486,7 +486,11 @@ OuterLoop: WithClient(c.Client). WithSubresourcesInPolicy(subresources) - mutateResponse := engine.Mutate(context.Background(), registryclient.NewOrDie(), policyContext) + mutateResponse := engine.Mutate( + context.Background(), + engine.LegacyContextLoaderFactory(registryclient.NewOrDie()), + policyContext, + ) if mutateResponse != nil { engineResponses = append(engineResponses, mutateResponse) } @@ -510,7 +514,12 @@ OuterLoop: var info Info var validateResponse *engineapi.EngineResponse if policyHasValidate { - validateResponse = engine.Validate(context.Background(), registryclient.NewOrDie(), policyContext, cfg) + validateResponse = engine.Validate( + context.Background(), + engine.LegacyContextLoaderFactory(registryclient.NewOrDie()), + policyContext, + cfg, + ) info = ProcessValidateEngineResponse(c.Policy, validateResponse, resPath, c.Rc, c.PolicyReport, c.AuditWarn) } @@ -518,7 +527,13 @@ OuterLoop: engineResponses = append(engineResponses, validateResponse) } - verifyImageResponse, _ := engine.VerifyAndPatchImages(context.Background(), registryclient.NewOrDie(), policyContext, cfg) + verifyImageResponse, _ := engine.VerifyAndPatchImages( + context.Background(), + engine.LegacyContextLoaderFactory(registryclient.NewOrDie()), + registryclient.NewOrDie(), + policyContext, + cfg, + ) if verifyImageResponse != nil && !verifyImageResponse.IsEmpty() { engineResponses = append(engineResponses, verifyImageResponse) info = ProcessValidateEngineResponse(c.Policy, verifyImageResponse, resPath, c.Rc, c.PolicyReport, c.AuditWarn) @@ -532,7 +547,10 @@ OuterLoop: } if policyHasGenerate { - generateResponse := engine.ApplyBackgroundChecks(registryclient.NewOrDie(), policyContext) + generateResponse := engine.ApplyBackgroundChecks( + engine.LegacyContextLoaderFactory(registryclient.NewOrDie()), + policyContext, + ) if generateResponse != nil && !generateResponse.IsEmpty() { newRuleResponse, err := handleGeneratePolicy(generateResponse, *policyContext, c.RuleToCloneSourceResource) if err != nil { @@ -1065,7 +1083,7 @@ func initializeMockController(objects []runtime.Object) (*generate.GenerateContr } client.SetDiscovery(dclient.NewFakeDiscoveryClient(nil)) - c := generate.NewGenerateControllerWithOnlyClient(client) + c := generate.NewGenerateControllerWithOnlyClient(client, engine.LegacyContextLoaderFactory(nil)) return c, nil } diff --git a/cmd/kyverno/main.go b/cmd/kyverno/main.go index 1667f312cc..ef5c7d19c2 100644 --- a/cmd/kyverno/main.go +++ b/cmd/kyverno/main.go @@ -133,7 +133,7 @@ func createNonLeaderControllers( updateRequestController := background.NewController( kyvernoClient, dynamicClient, - rclient, + engine.LegacyContextLoaderFactory(rclient), kyvernoInformer.Kyverno().V1().ClusterPolicies(), kyvernoInformer.Kyverno().V1().Policies(), kyvernoInformer.Kyverno().V1beta1().UpdateRequests(), @@ -177,7 +177,7 @@ func createrLeaderControllers( policyCtrl, err := policy.NewPolicyController( kyvernoClient, dynamicClient, - rclient, + engine.LegacyContextLoaderFactory(rclient), kyvernoInformer.Kyverno().V1().ClusterPolicies(), kyvernoInformer.Kyverno().V1().Policies(), kyvernoInformer.Kyverno().V1beta1().UpdateRequests(), @@ -523,6 +523,7 @@ func main() { } } resourceHandlers := webhooksresource.NewHandlers( + engine.LegacyContextLoaderFactory(rclient), dClient, kyvernoClient, rclient, diff --git a/cmd/reports-controller/main.go b/cmd/reports-controller/main.go index 6fd5f2aa09..521069b4e2 100644 --- a/cmd/reports-controller/main.go +++ b/cmd/reports-controller/main.go @@ -24,6 +24,7 @@ import ( backgroundscancontroller "github.com/kyverno/kyverno/pkg/controllers/report/background" resourcereportcontroller "github.com/kyverno/kyverno/pkg/controllers/report/resource" "github.com/kyverno/kyverno/pkg/cosign" + "github.com/kyverno/kyverno/pkg/engine" engineapi "github.com/kyverno/kyverno/pkg/engine/api" "github.com/kyverno/kyverno/pkg/engine/context/resolvers" "github.com/kyverno/kyverno/pkg/event" @@ -129,6 +130,7 @@ func createReportControllers( client, kyvernoClient, rclient, + engine.LegacyContextLoaderFactory(rclient), metadataFactory, kyvernoV1.Policies(), kyvernoV1.ClusterPolicies(), diff --git a/cmd/updaterequest-controller/main.go b/cmd/updaterequest-controller/main.go index 39bf6df7b3..0093ca5f06 100644 --- a/cmd/updaterequest-controller/main.go +++ b/cmd/updaterequest-controller/main.go @@ -21,6 +21,7 @@ import ( "github.com/kyverno/kyverno/pkg/config" policymetricscontroller "github.com/kyverno/kyverno/pkg/controllers/metrics/policy" "github.com/kyverno/kyverno/pkg/cosign" + "github.com/kyverno/kyverno/pkg/engine" engineapi "github.com/kyverno/kyverno/pkg/engine/api" "github.com/kyverno/kyverno/pkg/engine/context/resolvers" "github.com/kyverno/kyverno/pkg/event" @@ -77,7 +78,7 @@ func createNonLeaderControllers( updateRequestController := background.NewController( kyvernoClient, dynamicClient, - rclient, + engine.LegacyContextLoaderFactory(rclient), kyvernoInformer.Kyverno().V1().ClusterPolicies(), kyvernoInformer.Kyverno().V1().Policies(), kyvernoInformer.Kyverno().V1beta1().UpdateRequests(), @@ -104,7 +105,7 @@ func createrLeaderControllers( policyCtrl, err := policy.NewPolicyController( kyvernoClient, dynamicClient, - rclient, + engine.LegacyContextLoaderFactory(rclient), kyvernoInformer.Kyverno().V1().ClusterPolicies(), kyvernoInformer.Kyverno().V1().Policies(), kyvernoInformer.Kyverno().V1beta1().UpdateRequests(), diff --git a/pkg/background/generate/generate.go b/pkg/background/generate/generate.go index ded322a1f1..66e8bbdf26 100644 --- a/pkg/background/generate/generate.go +++ b/pkg/background/generate/generate.go @@ -25,7 +25,6 @@ import ( enginecontext "github.com/kyverno/kyverno/pkg/engine/context" "github.com/kyverno/kyverno/pkg/engine/variables" "github.com/kyverno/kyverno/pkg/event" - "github.com/kyverno/kyverno/pkg/registryclient" datautils "github.com/kyverno/kyverno/pkg/utils/data" engineutils "github.com/kyverno/kyverno/pkg/utils/engine" kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" @@ -43,7 +42,7 @@ type GenerateController struct { client dclient.Interface kyvernoClient versioned.Interface statusControl common.StatusControlInterface - rclient registryclient.Client + contextLoader engine.ContextLoaderFactory // listers urLister kyvernov1beta1listers.UpdateRequestNamespaceLister @@ -63,7 +62,7 @@ func NewGenerateController( client dclient.Interface, kyvernoClient versioned.Interface, statusControl common.StatusControlInterface, - rclient registryclient.Client, + contextLoader engine.ContextLoaderFactory, policyLister kyvernov1listers.ClusterPolicyLister, npolicyLister kyvernov1listers.PolicyLister, urLister kyvernov1beta1listers.UpdateRequestNamespaceLister, @@ -75,9 +74,9 @@ func NewGenerateController( ) *GenerateController { c := GenerateController{ client: client, + contextLoader: contextLoader, kyvernoClient: kyvernoClient, statusControl: statusControl, - rclient: rclient, policyLister: policyLister, npolicyLister: npolicyLister, urLister: urLister, @@ -201,7 +200,7 @@ func (c *GenerateController) applyGenerate(resource unstructured.Unstructured, u } // check if the policy still applies to the resource - engineResponse := engine.GenerateResponse(c.rclient, policyContext, ur) + engineResponse := engine.GenerateResponse(c.contextLoader, policyContext, ur) if len(engineResponse.PolicyResponse.Rules) == 0 { logger.V(4).Info(doesNotApply) return nil, false, errors.New(doesNotApply) @@ -347,7 +346,7 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext } // add configmap json data to context - if err := engine.LoadContext(context.TODO(), log, c.rclient, rule.Context, policyContext, rule.Name); err != nil { + if err := engine.LoadContext(context.TODO(), c.contextLoader, rule.Context, policyContext, rule.Name); err != nil { log.Error(err, "cannot add configmaps to context") return nil, processExisting, err } @@ -829,9 +828,10 @@ func (c *GenerateController) ApplyResource(resource *unstructured.Unstructured) } // NewGenerateControllerWithOnlyClient returns an instance of Controller with only the client. -func NewGenerateControllerWithOnlyClient(client dclient.Interface) *GenerateController { +func NewGenerateControllerWithOnlyClient(client dclient.Interface, contextLoader engine.ContextLoaderFactory) *GenerateController { c := GenerateController{ - client: client, + client: client, + contextLoader: contextLoader, } return &c } diff --git a/pkg/background/mutate/mutate.go b/pkg/background/mutate/mutate.go index 5159893893..27c8643b1c 100644 --- a/pkg/background/mutate/mutate.go +++ b/pkg/background/mutate/mutate.go @@ -15,7 +15,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/utils" "go.uber.org/multierr" yamlv2 "gopkg.in/yaml.v2" @@ -30,7 +29,8 @@ type MutateExistingController struct { // clients client dclient.Interface statusControl common.StatusControlInterface - rclient registryclient.Client + contextLoader engine.ContextLoaderFactory + // listers policyLister kyvernov1listers.ClusterPolicyLister npolicyLister kyvernov1listers.PolicyLister @@ -46,7 +46,7 @@ type MutateExistingController struct { func NewMutateExistingController( client dclient.Interface, statusControl common.StatusControlInterface, - rclient registryclient.Client, + contextLoader engine.ContextLoaderFactory, policyLister kyvernov1listers.ClusterPolicyLister, npolicyLister kyvernov1listers.PolicyLister, dynamicConfig config.Configuration, @@ -57,7 +57,7 @@ func NewMutateExistingController( c := MutateExistingController{ client: client, statusControl: statusControl, - rclient: rclient, + contextLoader: contextLoader, policyLister: policyLister, npolicyLister: npolicyLister, configuration: dynamicConfig, @@ -97,7 +97,7 @@ func (c *MutateExistingController) ProcessUR(ur *kyvernov1beta1.UpdateRequest) e continue } - er := engine.Mutate(context.TODO(), c.rclient, policyContext) + er := engine.Mutate(context.TODO(), c.contextLoader, policyContext) for _, r := range er.PolicyResponse.Rules { patched := r.PatchedTarget patchedTargetSubresourceName := r.PatchedTargetSubresourceName diff --git a/pkg/background/update_request_controller.go b/pkg/background/update_request_controller.go index 698515c52b..245ca8b311 100644 --- a/pkg/background/update_request_controller.go +++ b/pkg/background/update_request_controller.go @@ -18,9 +18,9 @@ import ( kyvernov1beta1listers "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1beta1" "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" "github.com/kyverno/kyverno/pkg/event" - "github.com/kyverno/kyverno/pkg/registryclient" kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" "github.com/pkg/errors" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -49,7 +49,7 @@ type controller struct { // clients client dclient.Interface kyvernoClient versioned.Interface - rclient registryclient.Client + contextLoader engine.ContextLoaderFactory // listers cpolLister kyvernov1listers.ClusterPolicyLister @@ -72,7 +72,7 @@ type controller struct { func NewController( kyvernoClient versioned.Interface, client dclient.Interface, - rclient registryclient.Client, + contextLoader engine.ContextLoaderFactory, cpolInformer kyvernov1informers.ClusterPolicyInformer, polInformer kyvernov1informers.PolicyInformer, urInformer kyvernov1beta1informers.UpdateRequestInformer, @@ -86,7 +86,7 @@ func NewController( c := controller{ client: client, kyvernoClient: kyvernoClient, - rclient: rclient, + contextLoader: contextLoader, cpolLister: cpolInformer.Lister(), polLister: polInformer.Lister(), urLister: urLister, @@ -420,10 +420,10 @@ func (c *controller) processUR(ur *kyvernov1beta1.UpdateRequest) error { statusControl := common.NewStatusControl(c.kyvernoClient, c.urLister) switch ur.Spec.Type { case kyvernov1beta1.Mutate: - ctrl := mutate.NewMutateExistingController(c.client, statusControl, c.rclient, c.cpolLister, c.polLister, c.configuration, c.informerCacheResolvers, c.eventGen, logger) + ctrl := mutate.NewMutateExistingController(c.client, statusControl, c.contextLoader, c.cpolLister, c.polLister, c.configuration, c.informerCacheResolvers, c.eventGen, logger) return ctrl.ProcessUR(ur) case kyvernov1beta1.Generate: - ctrl := generate.NewGenerateController(c.client, c.kyvernoClient, statusControl, c.rclient, c.cpolLister, c.polLister, c.urLister, c.nsLister, c.configuration, c.informerCacheResolvers, c.eventGen, logger) + ctrl := generate.NewGenerateController(c.client, c.kyvernoClient, statusControl, c.contextLoader, c.cpolLister, c.polLister, c.urLister, c.nsLister, c.configuration, c.informerCacheResolvers, c.eventGen, logger) return ctrl.ProcessUR(ur) } return nil diff --git a/pkg/controllers/report/background/controller.go b/pkg/controllers/report/background/controller.go index f6182d18c8..0b733c4682 100644 --- a/pkg/controllers/report/background/controller.go +++ b/pkg/controllers/report/background/controller.go @@ -49,6 +49,7 @@ type controller struct { client dclient.Interface kyvernoClient versioned.Interface rclient registryclient.Client + contextLoader engine.ContextLoaderFactory // listers polLister kyvernov1listers.PolicyLister @@ -75,6 +76,7 @@ func NewController( client dclient.Interface, kyvernoClient versioned.Interface, rclient registryclient.Client, + contextLoader engine.ContextLoaderFactory, metadataFactory metadatainformers.SharedInformerFactory, polInformer kyvernov1informers.PolicyInformer, cpolInformer kyvernov1informers.ClusterPolicyInformer, @@ -93,6 +95,7 @@ func NewController( client: client, kyvernoClient: kyvernoClient, rclient: rclient, + contextLoader: contextLoader, polLister: polInformer.Lister(), cpolLister: cpolInformer.Lister(), bgscanrLister: bgscanr.Lister(), @@ -309,7 +312,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.client, c.rclient, c.informerCacheResolvers, c.polexLister, c.config) + scanner := utils.NewScanner(logger, c.contextLoader, c.client, c.rclient, c.informerCacheResolvers, c.polexLister, 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 b9d7cf8910..97ea37317d 100644 --- a/pkg/controllers/report/utils/scanner.go +++ b/pkg/controllers/report/utils/scanner.go @@ -17,6 +17,7 @@ import ( type scanner struct { logger logr.Logger + contextLoader engine.ContextLoaderFactory client dclient.Interface rclient registryclient.Client informerCacheResolvers engineapi.ConfigmapResolver @@ -36,6 +37,7 @@ type Scanner interface { func NewScanner( logger logr.Logger, + contextLoader engine.ContextLoaderFactory, client dclient.Interface, rclient registryclient.Client, informerCacheResolvers engineapi.ConfigmapResolver, @@ -45,6 +47,7 @@ func NewScanner( ) Scanner { return &scanner{ logger: logger, + contextLoader: contextLoader, client: client, rclient: rclient, informerCacheResolvers: informerCacheResolvers, @@ -103,7 +106,7 @@ func (s *scanner) validateResource(ctx context.Context, resource unstructured.Un WithExcludeGroupRole(s.excludeGroupRole...). WithInformerCacheResolver(s.informerCacheResolvers). WithExceptions(s.polexLister) - return engine.Validate(ctx, s.rclient, policyCtx, s.config), nil + return engine.Validate(ctx, s.contextLoader, policyCtx, s.config), nil } func (s *scanner) validateImages(ctx context.Context, resource unstructured.Unstructured, nsLabels map[string]string, policy kyvernov1.PolicyInterface) (*engineapi.EngineResponse, error) { @@ -128,7 +131,7 @@ func (s *scanner) validateImages(ctx context.Context, resource unstructured.Unst WithExcludeGroupRole(s.excludeGroupRole...). WithInformerCacheResolver(s.informerCacheResolvers). WithExceptions(s.polexLister) - response, _ := engine.VerifyAndPatchImages(ctx, s.rclient, policyCtx, s.config) + response, _ := engine.VerifyAndPatchImages(ctx, s.contextLoader, s.rclient, policyCtx, s.config) 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 new file mode 100644 index 0000000000..7df830171a --- /dev/null +++ b/pkg/engine/api/contextloader.go @@ -0,0 +1,12 @@ +package api + +import ( + "context" + + kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" + enginecontext "github.com/kyverno/kyverno/pkg/engine/context" +) + +type ContextLoader interface { + Load(ctx context.Context, contextEntries []kyvernov1.ContextEntry, jsonContext enginecontext.Interface) error +} diff --git a/pkg/engine/background.go b/pkg/engine/background.go index 05c4f706bc..ad8e5cc12e 100644 --- a/pkg/engine/background.go +++ b/pkg/engine/background.go @@ -10,7 +10,6 @@ import ( "github.com/kyverno/kyverno/pkg/engine/utils" "github.com/kyverno/kyverno/pkg/engine/variables" "github.com/kyverno/kyverno/pkg/logging" - "github.com/kyverno/kyverno/pkg/registryclient" ) // ApplyBackgroundChecks checks for validity of generate and mutateExisting rules on the resource @@ -18,12 +17,19 @@ import ( // - the caller has to check the ruleResponse to determine whether the path exist // // 2. returns the list of rules that are applicable on this policy and resource, if 1 succeed -func ApplyBackgroundChecks(rclient registryclient.Client, policyContext *PolicyContext) (resp *engineapi.EngineResponse) { +func ApplyBackgroundChecks( + contextLoader ContextLoaderFactory, + policyContext *PolicyContext, +) (resp *engineapi.EngineResponse) { policyStartTime := time.Now() - return filterRules(rclient, policyContext, policyStartTime) + return filterRules(contextLoader, policyContext, policyStartTime) } -func filterRules(rclient registryclient.Client, policyContext *PolicyContext, startTime time.Time) *engineapi.EngineResponse { +func filterRules( + contextLoader ContextLoaderFactory, + policyContext *PolicyContext, + startTime time.Time, +) *engineapi.EngineResponse { kind := policyContext.newResource.GetKind() name := policyContext.newResource.GetName() namespace := policyContext.newResource.GetNamespace() @@ -55,7 +61,7 @@ func filterRules(rclient registryclient.Client, policyContext *PolicyContext, st applyRules := policyContext.policy.GetSpec().GetApplyRules() for _, rule := range autogen.ComputeRules(policyContext.policy) { - if ruleResp := filterRule(rclient, rule, policyContext); ruleResp != nil { + if ruleResp := filterRule(contextLoader, rule, policyContext); ruleResp != nil { resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, *ruleResp) if applyRules == kyvernov1.ApplyOne && ruleResp.Status != engineapi.RuleStatusSkip { break @@ -66,7 +72,11 @@ func filterRules(rclient registryclient.Client, policyContext *PolicyContext, st return resp } -func filterRule(rclient registryclient.Client, rule kyvernov1.Rule, policyContext *PolicyContext) *engineapi.RuleResponse { +func filterRule( + contextLoader ContextLoaderFactory, + rule kyvernov1.Rule, + policyContext *PolicyContext, +) *engineapi.RuleResponse { if !rule.HasGenerate() && !rule.IsMutateExisting() { return nil } @@ -122,7 +132,7 @@ func filterRule(rclient registryclient.Client, rule kyvernov1.Rule, policyContex policyContext.jsonContext.Checkpoint() defer policyContext.jsonContext.Restore() - if err := LoadContext(context.TODO(), logger, rclient, rule.Context, policyContext, rule.Name); err != nil { + if err := LoadContext(context.TODO(), contextLoader, rule.Context, policyContext, rule.Name); err != nil { logger.V(4).Info("cannot add external data to the context", "reason", err.Error()) return nil } diff --git a/pkg/engine/generation.go b/pkg/engine/generation.go index 8f121b54f1..e60f7c2794 100644 --- a/pkg/engine/generation.go +++ b/pkg/engine/generation.go @@ -7,17 +7,25 @@ import ( "github.com/kyverno/kyverno/pkg/autogen" engineapi "github.com/kyverno/kyverno/pkg/engine/api" "github.com/kyverno/kyverno/pkg/logging" - "github.com/kyverno/kyverno/pkg/registryclient" "k8s.io/client-go/tools/cache" ) // GenerateResponse checks for validity of generate rule on the resource -func GenerateResponse(rclient registryclient.Client, policyContext *PolicyContext, gr kyvernov1beta1.UpdateRequest) (resp *engineapi.EngineResponse) { +func GenerateResponse( + contextLoader ContextLoaderFactory, + policyContext *PolicyContext, + gr kyvernov1beta1.UpdateRequest, +) (resp *engineapi.EngineResponse) { policyStartTime := time.Now() - return filterGenerateRules(rclient, policyContext, gr.Spec.Policy, policyStartTime) + return filterGenerateRules(contextLoader, policyContext, gr.Spec.Policy, policyStartTime) } -func filterGenerateRules(rclient registryclient.Client, policyContext *PolicyContext, policyNameKey string, startTime time.Time) *engineapi.EngineResponse { +func filterGenerateRules( + contextLoader ContextLoaderFactory, + policyContext *PolicyContext, + policyNameKey string, + startTime time.Time, +) *engineapi.EngineResponse { kind := policyContext.newResource.GetKind() name := policyContext.newResource.GetName() namespace := policyContext.newResource.GetNamespace() @@ -53,7 +61,7 @@ func filterGenerateRules(rclient registryclient.Client, policyContext *PolicyCon } for _, rule := range autogen.ComputeRules(policyContext.policy) { - if ruleResp := filterRule(rclient, rule, policyContext); ruleResp != nil { + if ruleResp := filterRule(contextLoader, rule, policyContext); ruleResp != nil { resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, *ruleResp) } } diff --git a/pkg/engine/imageVerify.go b/pkg/engine/imageVerify.go index e133d55ae1..e612a107b1 100644 --- a/pkg/engine/imageVerify.go +++ b/pkg/engine/imageVerify.go @@ -68,6 +68,7 @@ func extractMatchingImages(policyContext *PolicyContext, rule *kyvernov1.Rule, c func VerifyAndPatchImages( ctx context.Context, + contextLoader ContextLoaderFactory, rclient registryclient.Client, policyContext *PolicyContext, cfg config.Configuration, @@ -138,7 +139,7 @@ func VerifyAndPatchImages( } policyContext.jsonContext.Restore() - if err := LoadContext(ctx, logger, rclient, rule.Context, policyContext, rule.Name); err != nil { + if err := LoadContext(ctx, contextLoader, rule.Context, policyContext, rule.Name); err != nil { appendResponse(resp, rule, fmt.Sprintf("failed to load context: %s", err.Error()), engineapi.RuleStatusError) return } diff --git a/pkg/engine/imageVerifyValidate.go b/pkg/engine/imageVerifyValidate.go index d9fe9998fe..181d8fa1af 100644 --- a/pkg/engine/imageVerifyValidate.go +++ b/pkg/engine/imageVerifyValidate.go @@ -10,13 +10,19 @@ import ( 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/registryclient" apiutils "github.com/kyverno/kyverno/pkg/utils/api" "github.com/pkg/errors" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) -func processImageValidationRule(ctx context.Context, log logr.Logger, rclient registryclient.Client, enginectx *PolicyContext, rule *kyvernov1.Rule, cfg config.Configuration) *engineapi.RuleResponse { +func processImageValidationRule( + ctx context.Context, + contextLoader ContextLoaderFactory, + log logr.Logger, + enginectx *PolicyContext, + rule *kyvernov1.Rule, + cfg config.Configuration, +) *engineapi.RuleResponse { if isDeleteRequest(enginectx) { return nil } @@ -29,7 +35,7 @@ func processImageValidationRule(ctx context.Context, log logr.Logger, rclient re if len(matchingImages) == 0 { return ruleResponse(*rule, engineapi.Validation, "image verified", engineapi.RuleStatusSkip) } - if err := LoadContext(ctx, log, rclient, rule.Context, enginectx, rule.Name); err != nil { + if err := LoadContext(ctx, contextLoader, rule.Context, enginectx, rule.Name); 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 3aa5d6cfb0..9670a27a85 100644 --- a/pkg/engine/imageVerify_test.go +++ b/pkg/engine/imageVerify_test.go @@ -160,12 +160,28 @@ var signaturePayloads = [][]byte{ var cfg = config.NewDefaultConfiguration() +func doVerifyAndPatchImages( + ctx context.Context, + rclient registryclient.Client, + pContext *PolicyContext, + cfg config.Configuration, +) (*engineapi.EngineResponse, *ImageVerificationMetadata) { + return VerifyAndPatchImages( + ctx, + LegacyContextLoaderFactory(rclient), + rclient, + pContext, + cfg, + ) + +} + func Test_CosignMockAttest(t *testing.T) { policyContext := buildContext(t, testPolicyGood, testResource, "") err := cosign.SetMock("ghcr.io/jimbugwadia/pause2:latest", attestationPayloads) assert.NilError(t, err) - er, ivm := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + er, ivm := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass, fmt.Sprintf("expected: %v, got: %v, failure: %v", @@ -179,7 +195,7 @@ func Test_CosignMockAttest_fail(t *testing.T) { err := cosign.SetMock("ghcr.io/jimbugwadia/pause2:latest", attestationPayloads) assert.NilError(t, err) - er, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + er, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail) } @@ -428,7 +444,7 @@ var ( func Test_ConfigMapMissingSuccess(t *testing.T) { policyContext := buildContext(t, testConfigMapMissing, testConfigMapMissingResource, "") cosign.ClearMock() - err, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + err, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, len(err.PolicyResponse.Rules), 1) assert.Equal(t, err.PolicyResponse.Rules[0].Status, engineapi.RuleStatusSkip, err.PolicyResponse.Rules[0].Message) } @@ -440,7 +456,7 @@ func Test_ConfigMapMissingFailure(t *testing.T) { assert.NilError(t, err) policyContext.informerCacheResolvers = resolver cosign.ClearMock() - resp, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + resp, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, len(resp.PolicyResponse.Rules), 1) assert.Equal(t, resp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusError, resp.PolicyResponse.Rules[0].Message) } @@ -449,7 +465,7 @@ func Test_SignatureGoodSigned(t *testing.T) { policyContext := buildContext(t, testSampleSingleKeyPolicy, testSampleResource, "") policyContext.policy.GetSpec().Rules[0].VerifyImages[0].MutateDigest = true cosign.ClearMock() - engineResp, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + engineResp, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, len(engineResp.PolicyResponse.Rules), 1) assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass, engineResp.PolicyResponse.Rules[0].Message) assert.Equal(t, len(engineResp.PolicyResponse.Rules[0].Patches), 1) @@ -461,7 +477,7 @@ func Test_SignatureUnsigned(t *testing.T) { cosign.ClearMock() unsigned := strings.Replace(testSampleResource, ":signed", ":unsigned", -1) policyContext := buildContext(t, testSampleSingleKeyPolicy, unsigned, "") - engineResp, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + engineResp, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, len(engineResp.PolicyResponse.Rules), 1) assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail, engineResp.PolicyResponse.Rules[0].Message) } @@ -470,7 +486,7 @@ func Test_SignatureWrongKey(t *testing.T) { cosign.ClearMock() otherKey := strings.Replace(testSampleResource, ":signed", ":signed-by-someone-else", -1) policyContext := buildContext(t, testSampleSingleKeyPolicy, otherKey, "") - engineResp, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + engineResp, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, len(engineResp.PolicyResponse.Rules), 1) assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail, engineResp.PolicyResponse.Rules[0].Message) } @@ -481,7 +497,7 @@ func Test_SignaturesMultiKey(t *testing.T) { policy = strings.Replace(policy, "KEY2", testVerifyImageKey, -1) policy = strings.Replace(policy, "COUNT", "0", -1) policyContext := buildContext(t, policy, testSampleResource, "") - engineResp, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + engineResp, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, len(engineResp.PolicyResponse.Rules), 1) assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass, engineResp.PolicyResponse.Rules[0].Message) } @@ -491,7 +507,7 @@ func Test_SignaturesMultiKeyFail(t *testing.T) { policy := strings.Replace(testSampleMultipleKeyPolicy, "KEY1", testVerifyImageKey, -1) policy = strings.Replace(policy, "COUNT", "0", -1) policyContext := buildContext(t, policy, testSampleResource, "") - engineResp, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + engineResp, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, len(engineResp.PolicyResponse.Rules), 1) assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail, engineResp.PolicyResponse.Rules[0].Message) } @@ -502,7 +518,7 @@ func Test_SignaturesMultiKeyOneGoodKey(t *testing.T) { policy = strings.Replace(policy, "KEY2", testOtherKey, -1) policy = strings.Replace(policy, "COUNT", "1", -1) policyContext := buildContext(t, policy, testSampleResource, "") - engineResp, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + engineResp, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, len(engineResp.PolicyResponse.Rules), 1) assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass, engineResp.PolicyResponse.Rules[0].Message) } @@ -513,7 +529,7 @@ func Test_SignaturesMultiKeyZeroGoodKey(t *testing.T) { policy = strings.Replace(policy, "KEY2", testOtherKey, -1) policy = strings.Replace(policy, "COUNT", "1", -1) policyContext := buildContext(t, policy, testSampleResource, "") - resp, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + resp, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, len(resp.PolicyResponse.Rules), 1) assert.Equal(t, resp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail, resp.PolicyResponse.Rules[0].Message) } @@ -529,14 +545,14 @@ func Test_RuleSelectorImageVerify(t *testing.T) { applyAll := kyverno.ApplyAll spec.ApplyRules = &applyAll - resp, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + resp, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, len(resp.PolicyResponse.Rules), 2) assert.Equal(t, resp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass, resp.PolicyResponse.Rules[0].Message) assert.Equal(t, resp.PolicyResponse.Rules[1].Status, engineapi.RuleStatusFail, resp.PolicyResponse.Rules[1].Message) applyOne := kyverno.ApplyOne spec.ApplyRules = &applyOne - resp, _ = VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + resp, _ = doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, len(resp.PolicyResponse.Rules), 1) assert.Equal(t, resp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass, resp.PolicyResponse.Rules[0].Message) } @@ -640,7 +656,7 @@ func Test_NestedAttestors(t *testing.T) { policy = strings.Replace(policy, "KEY2", testVerifyImageKey, -1) policy = strings.Replace(policy, "COUNT", "0", -1) policyContext := buildContext(t, policy, testSampleResource, "") - err, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + err, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, len(err.PolicyResponse.Rules), 1) assert.Equal(t, err.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass) @@ -648,7 +664,7 @@ func Test_NestedAttestors(t *testing.T) { policy = strings.Replace(policy, "KEY2", testOtherKey, -1) policy = strings.Replace(policy, "COUNT", "0", -1) policyContext = buildContext(t, policy, testSampleResource, "") - err, _ = VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + err, _ = doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, len(err.PolicyResponse.Rules), 1) assert.Equal(t, err.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail) @@ -656,7 +672,7 @@ func Test_NestedAttestors(t *testing.T) { policy = strings.Replace(policy, "KEY2", testOtherKey, -1) policy = strings.Replace(policy, "COUNT", "1", -1) policyContext = buildContext(t, policy, testSampleResource, "") - err, _ = VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + err, _ = doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, len(err.PolicyResponse.Rules), 1) assert.Equal(t, err.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass) } @@ -749,7 +765,7 @@ func Test_MarkImageVerified(t *testing.T) { err := cosign.SetMock(image, attestationPayloads) assert.NilError(t, err) - engineResponse, verifiedImages := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + engineResponse, verifiedImages := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Assert(t, engineResponse != nil) assert.Equal(t, len(engineResponse.PolicyResponse.Rules), 1) assert.Equal(t, engineResponse.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass) @@ -842,7 +858,7 @@ func Test_ParsePEMDelimited(t *testing.T) { err := cosign.SetMock(image, signaturePayloads) assert.NilError(t, err) - engineResponse, verifiedImages := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + engineResponse, verifiedImages := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Assert(t, engineResponse != nil) assert.Equal(t, len(engineResponse.PolicyResponse.Rules), 1) assert.Equal(t, engineResponse.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass) diff --git a/pkg/engine/jsonContext.go b/pkg/engine/jsonContext.go index 7f8f715235..859d26387b 100644 --- a/pkg/engine/jsonContext.go +++ b/pkg/engine/jsonContext.go @@ -8,86 +8,128 @@ import ( "github.com/go-logr/logr" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/store" + "github.com/kyverno/kyverno/pkg/clients/dclient" + engineapi "github.com/kyverno/kyverno/pkg/engine/api" "github.com/kyverno/kyverno/pkg/engine/apicall" - "github.com/kyverno/kyverno/pkg/engine/jmespath" + enginecontext "github.com/kyverno/kyverno/pkg/engine/context" + jmespath "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/engine/variables" + "github.com/kyverno/kyverno/pkg/logging" "github.com/kyverno/kyverno/pkg/registryclient" "github.com/pkg/errors" ) -// LoadContext - Fetches and adds external data to the Context. -func LoadContext(ctx context.Context, logger logr.Logger, rclient registryclient.Client, contextEntries []kyvernov1.ContextEntry, enginectx *PolicyContext, ruleName string) error { - if len(contextEntries) == 0 { - return nil - } +type ContextLoaderFactory = func(pContext *PolicyContext, ruleName string) engineapi.ContextLoader - policyName := enginectx.policy.GetName() +func LegacyContextLoaderFactory(rclient registryclient.Client) ContextLoaderFactory { if store.IsMock() { - rule := store.GetPolicyRule(policyName, ruleName) - if rule != nil && len(rule.Values) > 0 { - variables := rule.Values - for key, value := range variables { - if err := enginectx.jsonContext.AddVariable(key, value); err != nil { - return err - } + return func(pContext *PolicyContext, ruleName string) engineapi.ContextLoader { + policy := pContext.Policy() + return &mockContextLoader{ + logger: logging.WithName("MockContextLoaderFactory"), + policyName: policy.GetName(), + ruleName: ruleName, + client: pContext.Client(), + rclient: rclient, + cmResolver: pContext.informerCacheResolvers, } } - - hasRegistryAccess := store.GetRegistryAccess() - - // Context Variable should be loaded after the values loaded from values file - for _, entry := range contextEntries { - if entry.ImageRegistry != nil && hasRegistryAccess { - rclient := store.GetRegistryClient() - if err := loadImageData(ctx, rclient, logger, entry, enginectx); err != nil { - return err - } - } else if entry.Variable != nil { - if err := loadVariable(logger, entry, enginectx); err != nil { - return err - } - } else if entry.APICall != nil && store.IsApiCallAllowed() { - if err := loadAPIData(ctx, logger, entry, enginectx); err != nil { - return err - } - } + } + return func(pContext *PolicyContext, ruleName string) engineapi.ContextLoader { + return &contextLoader{ + logger: logging.WithName("LegacyContextLoaderFactory"), + client: pContext.Client(), + rclient: rclient, + cmResolver: pContext.informerCacheResolvers, } + } +} - if rule != nil && len(rule.ForEachValues) > 0 { - for key, value := range rule.ForEachValues { - if err := enginectx.jsonContext.AddVariable(key, value[store.GetForeachElement()]); err != nil { - return err - } +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 { + for _, entry := range contextEntries { + if entry.ConfigMap != nil { + if err := loadConfigMap(ctx, l.logger, entry, enginectx, l.cmResolver); err != nil { + return err } - } - } else { - for _, entry := range contextEntries { - if entry.ConfigMap != nil { - if err := loadConfigMap(ctx, logger, entry, enginectx); err != nil { - return err - } - } else if entry.APICall != nil { - if err := loadAPIData(ctx, logger, entry, enginectx); err != nil { - return err - } - } else if entry.ImageRegistry != nil { - if err := loadImageData(ctx, rclient, logger, entry, enginectx); err != nil { - return err - } - } else if entry.Variable != nil { - if err := loadVariable(logger, entry, enginectx); err != nil { - return err - } + } else if entry.APICall != nil { + if err := loadAPIData(ctx, l.logger, entry, enginectx, l.client); err != nil { + return err + } + } else if entry.ImageRegistry != nil { + if err := loadImageData(ctx, l.rclient, l.logger, entry, enginectx); err != nil { + return err + } + } else if entry.Variable != nil { + if err := loadVariable(l.logger, entry, enginectx); err != nil { + return err } } } return nil } -func loadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyContext) (err error) { +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 { + 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 { + return err + } + } + } + hasRegistryAccess := store.GetRegistryAccess() + // Context Variable should be loaded after the values loaded from values file + for _, entry := range contextEntries { + if entry.ImageRegistry != nil && hasRegistryAccess { + rclient := store.GetRegistryClient() + if err := loadImageData(ctx, rclient, l.logger, entry, enginectx); err != nil { + return err + } + } else if entry.Variable != nil { + if err := loadVariable(l.logger, entry, enginectx); err != nil { + return err + } + } else if entry.APICall != nil && store.IsApiCallAllowed() { + if err := loadAPIData(ctx, l.logger, entry, enginectx, l.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 { + return err + } + } + } + return nil +} + +func LoadContext(ctx context.Context, factory ContextLoaderFactory, contextEntries []kyvernov1.ContextEntry, pContext *PolicyContext, ruleName string) error { + return factory(pContext, ruleName).Load(ctx, contextEntries, pContext.JSONContext()) +} + +func loadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx enginecontext.Interface) (err error) { path := "" if entry.Variable.JMESPath != "" { - jp, err := variables.SubstituteAll(logger, ctx.jsonContext, entry.Variable.JMESPath) + jp, err := variables.SubstituteAll(logger, ctx, entry.Variable.JMESPath) if err != nil { return fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.Variable.JMESPath, err) } @@ -100,7 +142,7 @@ func loadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyC if err != nil { return fmt.Errorf("invalid default for variable %s", entry.Name) } - defaultValue, err = variables.SubstituteAll(logger, ctx.jsonContext, value) + defaultValue, err = variables.SubstituteAll(logger, ctx, value) if err != nil { return fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.Variable.Default, err) } @@ -109,7 +151,7 @@ func loadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyC var output interface{} = defaultValue if entry.Variable.Value != nil { value, _ := variables.DocumentToUntyped(entry.Variable.Value) - variable, err := variables.SubstituteAll(logger, ctx.jsonContext, value) + variable, err := variables.SubstituteAll(logger, ctx, value) if err != nil { return fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.Variable.Value, err) } @@ -125,7 +167,7 @@ func loadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyC } } else { if path != "" { - if variable, err := ctx.jsonContext.Query(path); err == nil { + if variable, err := ctx.Query(path); err == nil { output = variable } else if defaultValue == nil { return fmt.Errorf("failed to apply jmespath %s to variable %v", path, err) @@ -137,13 +179,13 @@ func loadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyC return fmt.Errorf("unable to add context entry for variable %s since it evaluated to nil", entry.Name) } if outputBytes, err := json.Marshal(output); err == nil { - return ctx.jsonContext.ReplaceContextEntry(entry.Name, outputBytes) + return ctx.ReplaceContextEntry(entry.Name, outputBytes) } else { return fmt.Errorf("unable to add context entry for variable %s: %w", entry.Name, err) } } -func loadImageData(ctx context.Context, rclient registryclient.Client, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx *PolicyContext) error { +func loadImageData(ctx context.Context, rclient registryclient.Client, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface) error { imageData, err := fetchImageData(ctx, rclient, logger, entry, enginectx) if err != nil { return err @@ -152,14 +194,14 @@ func loadImageData(ctx context.Context, rclient registryclient.Client, logger lo if err != nil { return err } - if err := enginectx.jsonContext.AddContextEntry(entry.Name, jsonBytes); err != nil { + if err := enginectx.AddContextEntry(entry.Name, jsonBytes); err != nil { return fmt.Errorf("failed to add resource data to context: contextEntry: %v, error: %v", entry, err) } return nil } -func fetchImageData(ctx context.Context, rclient registryclient.Client, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx *PolicyContext) (interface{}, error) { - ref, err := variables.SubstituteAll(logger, enginectx.jsonContext, entry.ImageRegistry.Reference) +func fetchImageData(ctx context.Context, rclient registryclient.Client, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface) (interface{}, error) { + ref, err := variables.SubstituteAll(logger, enginectx, entry.ImageRegistry.Reference) if err != nil { return nil, fmt.Errorf("ailed to substitute variables in context entry %s %s: %v", entry.Name, entry.ImageRegistry.Reference, err) } @@ -167,7 +209,7 @@ func fetchImageData(ctx context.Context, rclient registryclient.Client, logger l if !ok { return nil, fmt.Errorf("invalid image reference %s, image reference must be a string", ref) } - path, err := variables.SubstituteAll(logger, enginectx.jsonContext, entry.ImageRegistry.JMESPath) + path, err := variables.SubstituteAll(logger, enginectx, entry.ImageRegistry.JMESPath) if err != nil { return nil, fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.ImageRegistry.JMESPath, err) } @@ -239,16 +281,14 @@ func fetchImageDataMap(ctx context.Context, rclient registryclient.Client, ref s return untyped, nil } -func loadAPIData(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx *PolicyContext) error { - executor, err := apicall.New(ctx, entry, enginectx.JSONContext(), enginectx.Client(), logger) +func loadAPIData(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface, client dclient.Interface) error { + executor, err := apicall.New(ctx, entry, enginectx, client, logger) if err != nil { return errors.Wrapf(err, "failed to initialize APICall") } - if _, err := executor.Execute(); err != nil { return errors.Wrapf(err, "failed to execute APICall") } - return nil } @@ -257,33 +297,30 @@ func applyJMESPath(jmesPath string, data interface{}) (interface{}, error) { if err != nil { return nil, fmt.Errorf("failed to compile JMESPath: %s, error: %v", jmesPath, err) } - return jp.Search(data) } -func loadConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx *PolicyContext) error { - data, err := fetchConfigMap(ctx, logger, entry, enginectx) +func loadConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface, resolver engineapi.ConfigmapResolver) error { + data, err := fetchConfigMap(ctx, logger, entry, enginectx, resolver) if err != nil { return fmt.Errorf("failed to retrieve config map for context entry %s: %v", entry.Name, err) } - - err = enginectx.jsonContext.AddContextEntry(entry.Name, data) + err = enginectx.AddContextEntry(entry.Name, data) if err != nil { return fmt.Errorf("failed to add config map for context entry %s: %v", entry.Name, err) } - return nil } -func fetchConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx *PolicyContext) ([]byte, error) { +func fetchConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface, resolver engineapi.ConfigmapResolver) ([]byte, error) { contextData := make(map[string]interface{}) - name, err := variables.SubstituteAll(logger, enginectx.jsonContext, entry.ConfigMap.Name) + name, err := variables.SubstituteAll(logger, enginectx, entry.ConfigMap.Name) if err != nil { return nil, fmt.Errorf("failed to substitute variables in context %s configMap.name %s: %v", entry.Name, entry.ConfigMap.Name, err) } - namespace, err := variables.SubstituteAll(logger, enginectx.jsonContext, entry.ConfigMap.Namespace) + namespace, err := variables.SubstituteAll(logger, enginectx, entry.ConfigMap.Namespace) if err != nil { return nil, fmt.Errorf("failed to substitute variables in context %s configMap.namespace %s: %v", entry.Name, entry.ConfigMap.Namespace, err) } @@ -292,7 +329,7 @@ func fetchConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.Con namespace = "default" } - obj, err := enginectx.informerCacheResolvers.Get(ctx, namespace.(string), name.(string)) + obj, err := resolver.Get(ctx, namespace.(string), name.(string)) if err != nil { return nil, fmt.Errorf("failed to get configmap %s/%s : %v", namespace, name, err) } diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go index 626889293b..1a923f5bed 100644 --- a/pkg/engine/mutation.go +++ b/pkg/engine/mutation.go @@ -13,7 +13,6 @@ import ( engineapi "github.com/kyverno/kyverno/pkg/engine/api" "github.com/kyverno/kyverno/pkg/engine/mutate" "github.com/kyverno/kyverno/pkg/logging" - "github.com/kyverno/kyverno/pkg/registryclient" "github.com/kyverno/kyverno/pkg/tracing" "github.com/kyverno/kyverno/pkg/utils/api" "go.opentelemetry.io/otel/trace" @@ -22,7 +21,11 @@ import ( ) // Mutate performs mutation. Overlay first and then mutation patches -func Mutate(ctx context.Context, rclient registryclient.Client, policyContext *PolicyContext) (resp *engineapi.EngineResponse) { +func Mutate( + ctx context.Context, + contextLoader ContextLoaderFactory, + policyContext *PolicyContext, +) (resp *engineapi.EngineResponse) { startTime := time.Now() policy := policyContext.policy resp = &engineapi.EngineResponse{ @@ -89,7 +92,7 @@ func Mutate(ctx context.Context, rclient registryclient.Client, policyContext *P logger.Error(err, "failed to query resource object") } - if err := LoadContext(ctx, logger, rclient, rule.Context, policyContext, rule.Name); err != nil { + if err := LoadContext(ctx, contextLoader, rule.Context, policyContext, rule.Name); err != nil { if _, ok := err.(gojmespath.NotFoundError); ok { logger.V(3).Info("failed to load context", "reason", err.Error()) } else { @@ -142,7 +145,7 @@ func Mutate(ctx context.Context, rclient registryclient.Client, policyContext *P policyContext: policyContext, resource: patchedResource, log: logger, - rclient: rclient, + contextLoader: contextLoader, nesting: 0, } @@ -202,7 +205,7 @@ type forEachMutator struct { foreach []kyvernov1.ForEachMutation resource resourceInfo nesting int - rclient registryclient.Client + contextLoader ContextLoaderFactory log logr.Logger } @@ -211,7 +214,7 @@ func (f *forEachMutator) mutateForEach(ctx context.Context) *mutate.Response { allPatches := make([][]byte, 0) for _, foreach := range f.foreach { - if err := LoadContext(ctx, f.log, f.rclient, f.rule.Context, f.policyContext, f.rule.Name); err != nil { + if err := LoadContext(ctx, f.contextLoader, f.rule.Context, f.policyContext, f.rule.Name); err != nil { f.log.Error(err, "failed to load context") return mutate.NewErrorResponse("failed to load context", err) } @@ -276,7 +279,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 := LoadContext(ctx, f.log, f.rclient, foreach.Context, policyContext, f.rule.Name); err != nil { + if err := LoadContext(ctx, f.contextLoader, foreach.Context, policyContext, f.rule.Name); err != nil { return mutate.NewErrorResponse(fmt.Sprintf("failed to load to mutate.foreach[%d].context", index), err) } @@ -304,6 +307,7 @@ func (f *forEachMutator) mutateElements(ctx context.Context, foreach kyvernov1.F log: f.log, foreach: nestedForEach, nesting: f.nesting + 1, + contextLoader: f.contextLoader, } mutateResp = m.mutateForEach(ctx) diff --git a/pkg/engine/mutation_test.go b/pkg/engine/mutation_test.go index 34c7aafbb0..087de0efa2 100644 --- a/pkg/engine/mutation_test.go +++ b/pkg/engine/mutation_test.go @@ -20,6 +20,18 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" ) +func doMutate( + ctx context.Context, + rclient registryclient.Client, + pContext *PolicyContext, +) *engineapi.EngineResponse { + return Mutate( + ctx, + LegacyContextLoaderFactory(rclient), + pContext, + ) +} + func Test_VariableSubstitutionPatchStrategicMerge(t *testing.T) { policyRaw := []byte(`{ "apiVersion": "kyverno.io/v1", @@ -95,7 +107,7 @@ func Test_VariableSubstitutionPatchStrategicMerge(t *testing.T) { jsonContext: ctx, newResource: *resourceUnstructured, } - er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext) + er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext) t.Log(string(expectedPatch)) assert.Equal(t, len(er.PolicyResponse.Rules), 1) @@ -169,7 +181,7 @@ func Test_variableSubstitutionPathNotExist(t *testing.T) { jsonContext: ctx, newResource: *resourceUnstructured, } - er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext) + er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Assert(t, strings.Contains(er.PolicyResponse.Rules[0].Message, "Unknown key \"name1\" in path")) } @@ -262,7 +274,7 @@ func Test_variableSubstitutionCLI(t *testing.T) { newResource: *resourceUnstructured, } - er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext) + er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1) t.Log(string(expectedPatch)) @@ -371,7 +383,7 @@ func Test_chained_rules(t *testing.T) { err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx) assert.NilError(t, err) - er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext) + er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext) containers, _, err := unstructured.NestedSlice(er.PatchedResource.Object, "spec", "containers") assert.NilError(t, err) assert.Equal(t, containers[0].(map[string]interface{})["image"], "otherregistry.corp.com/foo/bash:5.0") @@ -459,7 +471,7 @@ func Test_precondition(t *testing.T) { newResource: *resourceUnstructured, } - er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext) + er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext) t.Log(string(expectedPatch)) t.Log(string(er.PolicyResponse.Rules[0].Patches[0])) if !reflect.DeepEqual(expectedPatch, er.PolicyResponse.Rules[0].Patches[0]) { @@ -556,7 +568,7 @@ func Test_nonZeroIndexNumberPatchesJson6902(t *testing.T) { newResource: *resourceUnstructured, } - er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext) + er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext) t.Log(string(expectedPatch)) t.Log(string(er.PolicyResponse.Rules[0].Patches[0])) if !reflect.DeepEqual(expectedPatch, er.PolicyResponse.Rules[0].Patches[0]) { @@ -650,7 +662,7 @@ func Test_foreach(t *testing.T) { err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx) assert.NilError(t, err) - er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext) + er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass) @@ -757,7 +769,7 @@ func Test_foreach_element_mutation(t *testing.T) { err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx) assert.NilError(t, err) - er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext) + er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass) @@ -883,7 +895,7 @@ func Test_Container_InitContainer_foreach(t *testing.T) { err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx) assert.NilError(t, err) - er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext) + er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass) @@ -1033,7 +1045,7 @@ func testApplyPolicyToResource(t *testing.T, policyRaw, resourceRaw []byte) *eng err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx) assert.NilError(t, err) - er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext) + er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext) return er } @@ -1584,7 +1596,7 @@ func Test_mutate_existing_resources(t *testing.T) { newResource: *trigger, } } - er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext) + er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext) for _, rr := range er.PolicyResponse.Rules { for i, p := range rr.Patches { @@ -1692,7 +1704,7 @@ func Test_RuleSelectorMutate(t *testing.T) { newResource: *resourceUnstructured, } - er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext) + er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext) assert.Equal(t, len(er.PolicyResponse.Rules), 2) assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1) assert.Equal(t, len(er.PolicyResponse.Rules[1].Patches), 1) @@ -1707,7 +1719,7 @@ func Test_RuleSelectorMutate(t *testing.T) { applyOne := kyverno.ApplyOne policyContext.policy.GetSpec().ApplyRules = &applyOne - er = Mutate(context.TODO(), registryclient.NewOrDie(), policyContext) + er = doMutate(context.TODO(), registryclient.NewOrDie(), policyContext) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1) @@ -2074,7 +2086,7 @@ func Test_SpecialCharacters(t *testing.T) { } // Mutate and make sure that we got the expected amount of rules. - patches := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext).GetPatches() + patches := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext).GetPatches() if !reflect.DeepEqual(patches, tt.want) { t.Errorf("Mutate() got patches %s, expected %s", patches, tt.want) } diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go index 7936955556..260423f638 100644 --- a/pkg/engine/validation.go +++ b/pkg/engine/validation.go @@ -20,7 +20,6 @@ import ( "github.com/kyverno/kyverno/pkg/engine/variables" "github.com/kyverno/kyverno/pkg/logging" "github.com/kyverno/kyverno/pkg/pss" - "github.com/kyverno/kyverno/pkg/registryclient" "github.com/kyverno/kyverno/pkg/tracing" "github.com/kyverno/kyverno/pkg/utils/api" datautils "github.com/kyverno/kyverno/pkg/utils/data" @@ -37,7 +36,12 @@ import ( ) // Validate applies validation rules from policy on the resource -func Validate(ctx context.Context, rclient registryclient.Client, policyContext *PolicyContext, cfg config.Configuration) (resp *engineapi.EngineResponse) { +func Validate( + ctx context.Context, + contextLoader ContextLoaderFactory, + policyContext *PolicyContext, + cfg config.Configuration, +) (resp *engineapi.EngineResponse) { resp = &engineapi.EngineResponse{} startTime := time.Now() @@ -48,7 +52,7 @@ func Validate(ctx context.Context, rclient registryclient.Client, policyContext logger.V(4).Info("finished policy processing", "processingTime", resp.PolicyResponse.ProcessingTime.String(), "validationRulesApplied", resp.PolicyResponse.RulesAppliedCount) }() - resp = validateResource(ctx, logger, rclient, policyContext, cfg) + resp = validateResource(ctx, contextLoader, logger, policyContext, cfg) resp.NamespaceLabels = policyContext.namespaceLabels return } @@ -97,7 +101,13 @@ func buildResponse(ctx *PolicyContext, resp *engineapi.EngineResponse, startTime resp.PolicyResponse.Timestamp = startTime.Unix() } -func validateResource(ctx context.Context, log logr.Logger, rclient registryclient.Client, enginectx *PolicyContext, cfg config.Configuration) *engineapi.EngineResponse { +func validateResource( + ctx context.Context, + contextLoader ContextLoaderFactory, + log logr.Logger, + enginectx *PolicyContext, + cfg config.Configuration, +) *engineapi.EngineResponse { resp := &engineapi.EngineResponse{} enginectx.jsonContext.Checkpoint() @@ -148,9 +158,9 @@ func validateResource(ctx context.Context, log logr.Logger, rclient registryclie log.V(3).Info("processing validation rule", "matchCount", matchCount, "applyRules", applyRules) enginectx.jsonContext.Reset() if hasValidate && !hasYAMLSignatureVerify { - return processValidationRule(ctx, log, rclient, enginectx, rule) + return processValidationRule(ctx, contextLoader, log, enginectx, rule) } else if hasValidateImage { - return processImageValidationRule(ctx, log, rclient, enginectx, rule, cfg) + return processImageValidationRule(ctx, contextLoader, log, enginectx, rule, cfg) } else if hasYAMLSignatureVerify { return processYAMLValidationRule(log, enginectx, rule) } @@ -168,8 +178,14 @@ func validateResource(ctx context.Context, log logr.Logger, rclient registryclie return resp } -func processValidationRule(ctx context.Context, log logr.Logger, rclient registryclient.Client, policyContext *PolicyContext, rule *kyvernov1.Rule) *engineapi.RuleResponse { - v := newValidator(log, rclient, policyContext, rule) +func processValidationRule( + ctx context.Context, + contextLoader ContextLoaderFactory, + log logr.Logger, + policyContext *PolicyContext, + rule *kyvernov1.Rule, +) *engineapi.RuleResponse { + v := newValidator(log, contextLoader, policyContext, rule) return v.validate(ctx) } @@ -198,17 +214,17 @@ type validator struct { deny *kyvernov1.Deny podSecurity *kyvernov1.PodSecurity forEach []kyvernov1.ForEachValidation - rclient registryclient.Client + contextLoader ContextLoaderFactory nesting int } -func newValidator(log logr.Logger, rclient registryclient.Client, ctx *PolicyContext, rule *kyvernov1.Rule) *validator { +func newValidator(log logr.Logger, contextLoader ContextLoaderFactory, ctx *PolicyContext, rule *kyvernov1.Rule) *validator { ruleCopy := rule.DeepCopy() return &validator{ log: log, rule: ruleCopy, policyContext: ctx, - rclient: rclient, + contextLoader: contextLoader, contextEntries: ruleCopy.Context, anyAllConditions: ruleCopy.GetAnyAllConditions(), pattern: ruleCopy.Validation.GetPattern(), @@ -219,7 +235,14 @@ func newValidator(log logr.Logger, rclient registryclient.Client, ctx *PolicyCon } } -func newForEachValidator(foreach kyvernov1.ForEachValidation, rclient registryclient.Client, nesting int, rule *kyvernov1.Rule, ctx *PolicyContext, log logr.Logger) (*validator, error) { +func newForEachValidator( + foreach kyvernov1.ForEachValidation, + contextLoader ContextLoaderFactory, + nesting int, + rule *kyvernov1.Rule, + ctx *PolicyContext, + log logr.Logger, +) (*validator, error) { ruleCopy := rule.DeepCopy() anyAllConditions, err := datautils.ToMap(foreach.AnyAllConditions) if err != nil { @@ -235,7 +258,7 @@ func newForEachValidator(foreach kyvernov1.ForEachValidation, rclient registrycl log: log, policyContext: ctx, rule: ruleCopy, - rclient: rclient, + contextLoader: contextLoader, contextEntries: foreach.Context, anyAllConditions: anyAllConditions, pattern: foreach.GetPattern(), @@ -298,7 +321,7 @@ func (v *validator) validateForEach(ctx context.Context) *engineapi.RuleResponse continue } - resp, count := v.validateElements(ctx, v.rclient, foreach, elements, foreach.ElementScope) + resp, count := v.validateElements(ctx, foreach, elements, foreach.ElementScope) if resp.Status != engineapi.RuleStatusPass { return resp } @@ -317,7 +340,7 @@ func (v *validator) validateForEach(ctx context.Context) *engineapi.RuleResponse return ruleResponse(*v.rule, engineapi.Validation, "rule passed", engineapi.RuleStatusPass) } -func (v *validator) validateElements(ctx context.Context, rclient registryclient.Client, foreach kyvernov1.ForEachValidation, elements []interface{}, elementScope *bool) (*engineapi.RuleResponse, int) { +func (v *validator) validateElements(ctx context.Context, foreach kyvernov1.ForEachValidation, elements []interface{}, elementScope *bool) (*engineapi.RuleResponse, int) { v.policyContext.jsonContext.Checkpoint() defer v.policyContext.jsonContext.Restore() applyCount := 0 @@ -334,7 +357,7 @@ func (v *validator) validateElements(ctx context.Context, rclient registryclient return ruleError(v.rule, engineapi.Validation, "failed to process foreach", err), applyCount } - foreachValidator, err := newForEachValidator(foreach, rclient, v.nesting+1, v.rule, policyContext, v.log) + foreachValidator, err := newForEachValidator(foreach, v.contextLoader, v.nesting+1, v.rule, policyContext, v.log) if err != nil { v.log.Error(err, "failed to create foreach validator") return ruleError(v.rule, engineapi.Validation, "failed to create foreach validator", err), applyCount @@ -399,7 +422,7 @@ func addElementToContext(ctx *PolicyContext, element interface{}, index, nesting } func (v *validator) loadContext(ctx context.Context) error { - if err := LoadContext(ctx, v.log, v.rclient, v.contextEntries, v.policyContext, v.rule.Name); err != nil { + if err := LoadContext(ctx, v.contextLoader, v.contextEntries, v.policyContext, v.rule.Name); 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 b6ee8a5487..da24b0eaa7 100644 --- a/pkg/engine/validation_test.go +++ b/pkg/engine/validation_test.go @@ -9,6 +9,7 @@ import ( kyverno "github.com/kyverno/kyverno/api/kyverno/v1" urkyverno "github.com/kyverno/kyverno/api/kyverno/v1beta1" "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/store" + "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" @@ -18,6 +19,15 @@ import ( admissionv1 "k8s.io/api/admission/v1" ) +func doValidate(ctx context.Context, rclient registryclient.Client, pContext *PolicyContext, cfg config.Configuration) *engineapi.EngineResponse { + return Validate( + ctx, + LegacyContextLoaderFactory(rclient), + pContext, + cfg, + ) +} + func TestValidate_image_tag_fail(t *testing.T) { // If image tag is latest then imagepull policy needs to be checked rawPolicy := []byte(`{ @@ -111,7 +121,7 @@ func TestValidate_image_tag_fail(t *testing.T) { "validation error: imagePullPolicy 'Always' required with tag 'latest'. rule validate-latest failed at path /spec/containers/0/imagePullPolicy/", } - er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) for index, r := range er.PolicyResponse.Rules { assert.Equal(t, r.Message, msgs[index]) } @@ -211,7 +221,7 @@ func TestValidate_image_tag_pass(t *testing.T) { "validation rule 'validate-tag' passed.", "validation rule 'validate-latest' passed.", } - er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) for index, r := range er.PolicyResponse.Rules { assert.Equal(t, r.Message, msgs[index]) } @@ -285,7 +295,7 @@ func TestValidate_Fail_anyPattern(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) assert.Assert(t, !er.IsSuccessful()) msgs := []string{"validation error: A namespace is required. rule check-default-namespace[0] failed at path /metadata/namespace/ rule check-default-namespace[1] failed at path /metadata/namespace/"} @@ -368,7 +378,7 @@ func TestValidate_host_network_port(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) msgs := []string{"validation error: Host network and port are not allowed. rule validate-host-network-port failed at path /spec/containers/0/ports/0/hostPort/"} for index, r := range er.PolicyResponse.Rules { @@ -458,7 +468,7 @@ func TestValidate_anchor_arraymap_pass(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) msgs := []string{"validation rule 'validate-host-path' passed."} for index, r := range er.PolicyResponse.Rules { @@ -546,7 +556,7 @@ func TestValidate_anchor_arraymap_fail(t *testing.T) { assert.NilError(t, err) resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) msgs := []string{"validation error: Host path '/var/lib/' is not allowed. rule validate-host-path failed at path /spec/volumes/0/hostPath/path/"} for index, r := range er.PolicyResponse.Rules { @@ -616,7 +626,7 @@ func TestValidate_anchor_map_notfound(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) msgs := []string{"validation rule 'pod rule 2' passed."} for index, r := range er.PolicyResponse.Rules { @@ -689,7 +699,7 @@ func TestValidate_anchor_map_found_valid(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) msgs := []string{"validation rule 'pod rule 2' passed."} for index, r := range er.PolicyResponse.Rules { @@ -763,7 +773,7 @@ func TestValidate_inequality_List_Processing(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) msgs := []string{"validation rule 'pod rule 2' passed."} for index, r := range er.PolicyResponse.Rules { @@ -843,7 +853,7 @@ func TestValidate_inequality_List_ProcessingBrackets(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) msgs := []string{"validation rule 'pod rule 2' passed."} for index, r := range er.PolicyResponse.Rules { @@ -917,7 +927,7 @@ func TestValidate_anchor_map_found_invalid(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) msgs := []string{"validation error: pod: validate run as non root user. rule pod rule 2 failed at path /spec/securityContext/runAsNonRoot/"} for index, r := range er.PolicyResponse.Rules { @@ -992,7 +1002,7 @@ func TestValidate_AnchorList_pass(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) msgs := []string{"validation rule 'pod image rule' passed."} for index, r := range er.PolicyResponse.Rules { @@ -1067,7 +1077,7 @@ func TestValidate_AnchorList_fail(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) assert.Assert(t, !er.IsSuccessful()) } @@ -1137,7 +1147,7 @@ func TestValidate_existenceAnchor_fail(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) assert.Assert(t, !er.IsSuccessful()) } @@ -1207,7 +1217,7 @@ func TestValidate_existenceAnchor_pass(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) msgs := []string{"validation rule 'pod image rule' passed."} for index, r := range er.PolicyResponse.Rules { @@ -1295,7 +1305,7 @@ func TestValidate_negationAnchor_deny(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) msgs := []string{"validation error: Host path is not allowed. rule validate-host-path failed at path /spec/volumes/0/hostPath/"} for index, r := range er.PolicyResponse.Rules { @@ -1382,7 +1392,7 @@ func TestValidate_negationAnchor_pass(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) msgs := []string{"validation rule 'validate-host-path' passed."} for index, r := range er.PolicyResponse.Rules { @@ -1459,7 +1469,7 @@ func Test_VariableSubstitutionPathNotExistInPattern(t *testing.T) { jsonContext: ctx, newResource: *resourceUnstructured, } - er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusError) @@ -1553,7 +1563,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_OnePatternStatisfiesButSu jsonContext: ctx, newResource: *resourceUnstructured, } - er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusError) @@ -1615,7 +1625,7 @@ func Test_VariableSubstitution_NotOperatorWithStringVariable(t *testing.T) { jsonContext: ctx, newResource: *resourceUnstructured, } - er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail) assert.Equal(t, er.PolicyResponse.Rules[0].Message, "validation error: rule not-operator-with-variable-should-alway-fail-validation failed at path /spec/content/") } @@ -1707,7 +1717,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathNotPresent(t *test jsonContext: ctx, newResource: *resourceUnstructured, } - er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusError) @@ -1801,7 +1811,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathPresent_NonePatter jsonContext: ctx, newResource: *resourceUnstructured, } - er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail) assert.Equal(t, er.PolicyResponse.Rules[0].Message, @@ -1907,7 +1917,7 @@ func Test_VariableSubstitutionValidate_VariablesInMessageAreResolved(t *testing. jsonContext: ctx, newResource: *resourceUnstructured, } - er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail) assert.Equal(t, er.PolicyResponse.Rules[0].Message, "The animal cow is not in the allowed list of animals.") } @@ -1961,7 +1971,7 @@ func Test_Flux_Kustomization_PathNotPresent(t *testing.T) { jsonContext: ctx, newResource: *resourceUnstructured, } - er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) for i, rule := range er.PolicyResponse.Rules { assert.Equal(t, er.PolicyResponse.Rules[i].Status, test.expectedResults[i], "\ntest %s failed\nexpected: %s\nactual: %s", test.name, test.expectedResults[i], er.PolicyResponse.Rules[i].Status) @@ -2127,7 +2137,7 @@ func executeTest(t *testing.T, test testCase) { jsonContext: ctx, } - resp := Validate(context.TODO(), registryclient.NewOrDie(), pc, cfg) + resp := doValidate(context.TODO(), registryclient.NewOrDie(), pc, cfg) if resp.IsSuccessful() && test.requestDenied { t.Errorf("Testcase has failed, policy: %v", policy.Name) } @@ -2222,7 +2232,7 @@ func TestValidate_context_variable_substitution_CLI(t *testing.T) { msgs := []string{ "restrict pod counts to be no more than 10 on node minikube", } - er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) for index, r := range er.PolicyResponse.Rules { assert.Equal(t, r.Message, msgs[index]) } @@ -2311,7 +2321,7 @@ func Test_EmptyStringInDenyCondition(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx}, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx}, cfg) assert.Assert(t, !er.IsSuccessful()) } @@ -2400,7 +2410,7 @@ func Test_StringInDenyCondition(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx}, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx}, cfg) assert.Assert(t, er.IsSuccessful()) } @@ -3071,7 +3081,7 @@ func testForEach(t *testing.T, policyraw []byte, resourceRaw []byte, msg string, jsonContext: ctx, newResource: *resourceUnstructured, } - er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) assert.Equal(t, er.PolicyResponse.Rules[0].Status, status) if msg != "" { @@ -3135,7 +3145,7 @@ func Test_delete_ignore_pattern(t *testing.T) { jsonContext: ctx, newResource: *resourceUnstructured, } - engineResponseCreate := Validate(context.TODO(), registryclient.NewOrDie(), policyContextCreate, cfg) + engineResponseCreate := doValidate(context.TODO(), registryclient.NewOrDie(), policyContextCreate, cfg) assert.Equal(t, len(engineResponseCreate.PolicyResponse.Rules), 1) assert.Equal(t, engineResponseCreate.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail) @@ -3144,7 +3154,7 @@ func Test_delete_ignore_pattern(t *testing.T) { jsonContext: ctx, oldResource: *resourceUnstructured, } - engineResponseDelete := Validate(context.TODO(), registryclient.NewOrDie(), policyContextDelete, cfg) + engineResponseDelete := doValidate(context.TODO(), registryclient.NewOrDie(), policyContextDelete, cfg) assert.Equal(t, len(engineResponseDelete.PolicyResponse.Rules), 0) } @@ -3203,7 +3213,7 @@ func Test_ValidatePattern_anyPattern(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(tc.rawResource) assert.NilError(t, err) - er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) if tc.expectedFailed { assert.Assert(t, er.IsFailed()) } else if tc.expectedSkipped { diff --git a/pkg/policy/policy_controller.go b/pkg/policy/policy_controller.go index 0e86e86a2e..472d310d45 100644 --- a/pkg/policy/policy_controller.go +++ b/pkg/policy/policy_controller.go @@ -25,7 +25,6 @@ import ( engineapi "github.com/kyverno/kyverno/pkg/engine/api" "github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/metrics" - "github.com/kyverno/kyverno/pkg/registryclient" kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" "github.com/pkg/errors" "go.uber.org/multierr" @@ -58,7 +57,7 @@ const ( type PolicyController struct { client dclient.Interface kyvernoClient versioned.Interface - rclient registryclient.Client + contextLoader engine.ContextLoaderFactory pInformer kyvernov1informers.ClusterPolicyInformer npInformer kyvernov1informers.PolicyInformer @@ -99,7 +98,7 @@ type PolicyController struct { func NewPolicyController( kyvernoClient versioned.Interface, client dclient.Interface, - rclient registryclient.Client, + contextLoader engine.ContextLoaderFactory, pInformer kyvernov1informers.ClusterPolicyInformer, npInformer kyvernov1informers.PolicyInformer, urInformer kyvernov1beta1informers.UpdateRequestInformer, @@ -120,7 +119,7 @@ func NewPolicyController( pc := PolicyController{ client: client, kyvernoClient: kyvernoClient, - rclient: rclient, + contextLoader: contextLoader, pInformer: pInformer, npInformer: npInformer, eventGen: eventGen, @@ -514,7 +513,7 @@ func (pc *PolicyController) handleUpdateRequest(ur *kyvernov1beta1.UpdateRequest return false, errors.Wrapf(err, "failed to build policy context for rule %s", rule.Name) } - engineResponse := engine.ApplyBackgroundChecks(pc.rclient, policyContext) + engineResponse := engine.ApplyBackgroundChecks(pc.contextLoader, policyContext) if len(engineResponse.PolicyResponse.Rules) == 0 { return true, nil } diff --git a/pkg/testrunner/scenario.go b/pkg/testrunner/scenario.go index 81a1b67b58..6dc98e107a 100644 --- a/pkg/testrunner/scenario.go +++ b/pkg/testrunner/scenario.go @@ -147,7 +147,11 @@ func runTestCase(t *testing.T, tc TestCase) bool { policyContext := engine.NewPolicyContext().WithPolicy(policy).WithNewResource(*resource) - er := engine.Mutate(context.TODO(), registryclient.NewOrDie(), policyContext) + er := engine.Mutate( + context.TODO(), + engine.LegacyContextLoaderFactory(registryclient.NewOrDie()), + policyContext, + ) t.Log("---Mutation---") validateResource(t, er.PatchedResource, tc.Expected.Mutation.PatchedResource) validateResponse(t, er.PolicyResponse, tc.Expected.Mutation.PolicyResponse) @@ -160,7 +164,12 @@ func runTestCase(t *testing.T, tc TestCase) bool { policyContext = policyContext.WithNewResource(*resource) cfg := config.NewDefaultConfiguration() - er = engine.Validate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + er = engine.Validate( + context.TODO(), + engine.LegacyContextLoaderFactory(registryclient.NewOrDie()), + policyContext, + cfg, + ) t.Log("---Validation---") validateResponse(t, er.PolicyResponse, tc.Expected.Validation.PolicyResponse) @@ -176,7 +185,10 @@ func runTestCase(t *testing.T, tc TestCase) bool { } else { policyContext := policyContext.WithClient(client) - er = engine.ApplyBackgroundChecks(registryclient.NewOrDie(), policyContext) + er = engine.ApplyBackgroundChecks( + engine.LegacyContextLoaderFactory(registryclient.NewOrDie()), + 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 ceda439e9f..e77e6c2ade 100644 --- a/pkg/webhooks/resource/fake.go +++ b/pkg/webhooks/resource/fake.go @@ -7,6 +7,7 @@ import ( kyvernoinformers "github.com/kyverno/kyverno/pkg/client/informers/externalversions" "github.com/kyverno/kyverno/pkg/clients/dclient" "github.com/kyverno/kyverno/pkg/config" + "github.com/kyverno/kyverno/pkg/engine" "github.com/kyverno/kyverno/pkg/engine/context/resolvers" "github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/metrics" @@ -38,10 +39,11 @@ func NewFakeHandlers(ctx context.Context, policyCache policycache.Cache) webhook crbLister := informers.Rbac().V1().ClusterRoleBindings().Lister() urLister := kyvernoInformers.Kyverno().V1beta1().UpdateRequests().Lister().UpdateRequests(config.KyvernoNamespace()) peLister := kyvernoInformers.Kyverno().V2alpha1().PolicyExceptions().Lister() + rclient := registryclient.NewOrDie() return &handlers{ client: dclient, - rclient: registryclient.NewOrDie(), + rclient: rclient, configuration: configuration, metricsConfig: metricsConfig, pCache: policyCache, @@ -54,5 +56,6 @@ func NewFakeHandlers(ctx context.Context, policyCache policycache.Cache) webhook openApiManager: openapi.NewFake(), pcBuilder: webhookutils.NewPolicyContextBuilder(configuration, dclient, rbLister, crbLister, configMapResolver, peLister), urUpdater: webhookutils.NewUpdateRequestUpdater(kyvernoclient, urLister), + contextLoader: engine.LegacyContextLoaderFactory(rclient), } } diff --git a/pkg/webhooks/resource/generation/generation.go b/pkg/webhooks/resource/generation/generation.go index bf5691af21..b0498586c1 100644 --- a/pkg/webhooks/resource/generation/generation.go +++ b/pkg/webhooks/resource/generation/generation.go @@ -19,7 +19,6 @@ import ( engineapi "github.com/kyverno/kyverno/pkg/engine/api" "github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/metrics" - "github.com/kyverno/kyverno/pkg/registryclient" engineutils "github.com/kyverno/kyverno/pkg/utils/engine" kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" webhookgenerate "github.com/kyverno/kyverno/pkg/webhooks/updaterequest" @@ -40,7 +39,7 @@ func NewGenerationHandler( log logr.Logger, client dclient.Interface, kyvernoClient versioned.Interface, - rclient registryclient.Client, + contextLoader engine.ContextLoaderFactory, nsLister corev1listers.NamespaceLister, urLister kyvernov1beta1listers.UpdateRequestNamespaceLister, urGenerator webhookgenerate.Generator, @@ -52,7 +51,7 @@ func NewGenerationHandler( log: log, client: client, kyvernoClient: kyvernoClient, - rclient: rclient, + contextLoader: contextLoader, nsLister: nsLister, urLister: urLister, urGenerator: urGenerator, @@ -66,7 +65,7 @@ type generationHandler struct { log logr.Logger client dclient.Interface kyvernoClient versioned.Interface - rclient registryclient.Client + contextLoader engine.ContextLoaderFactory nsLister corev1listers.NamespaceLister urLister kyvernov1beta1listers.UpdateRequestNamespaceLister urGenerator webhookgenerate.Generator @@ -93,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 := engine.ApplyBackgroundChecks(h.rclient, policyContext) + engineResponse := engine.ApplyBackgroundChecks(h.contextLoader, 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 bf34962d0c..91b47b63ae 100644 --- a/pkg/webhooks/resource/handlers.go +++ b/pkg/webhooks/resource/handlers.go @@ -41,6 +41,7 @@ type handlers struct { client dclient.Interface kyvernoClient versioned.Interface rclient registryclient.Client + contextLoader engine.ContextLoaderFactory // config configuration config.Configuration @@ -66,6 +67,7 @@ type handlers struct { } func NewHandlers( + contextLoader engine.ContextLoaderFactory, client dclient.Interface, kyvernoClient versioned.Interface, rclient registryclient.Client, @@ -84,6 +86,7 @@ func NewHandlers( admissionReports bool, ) webhooks.ResourceHandlers { return &handlers{ + contextLoader: contextLoader, client: client, kyvernoClient: kyvernoClient, rclient: rclient, @@ -121,7 +124,7 @@ func (h *handlers) Validate(ctx context.Context, logger logr.Logger, request *ad } if len(generatePolicies) == 0 && request.Operation == admissionv1.Update { // handle generate source resource updates - gh := generation.NewGenerationHandler(logger, h.client, h.kyvernoClient, h.rclient, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen, h.metricsConfig) + gh := generation.NewGenerationHandler(logger, h.client, h.kyvernoClient, h.contextLoader, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen, h.metricsConfig) go gh.HandleUpdatesForGenerateRules(context.TODO(), request, []kyvernov1.PolicyInterface{}) } @@ -137,7 +140,7 @@ func (h *handlers) Validate(ctx context.Context, logger logr.Logger, request *ad namespaceLabels = engineutils.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, logger) } - vh := validation.NewValidationHandler(logger, h.kyvernoClient, h.rclient, h.pCache, h.pcBuilder, h.eventGen, h.admissionReports, h.metricsConfig, h.configuration) + vh := validation.NewValidationHandler(logger, h.kyvernoClient, h.contextLoader, h.pCache, h.pcBuilder, h.eventGen, h.admissionReports, h.metricsConfig, h.configuration) ok, msg, warnings := vh.HandleValidation(ctx, request, policies, policyContext, namespaceLabels, startTime) if !ok { @@ -171,7 +174,7 @@ func (h *handlers) Mutate(ctx context.Context, logger logr.Logger, request *admi if err := enginectx.MutateResourceWithImageInfo(request.Object.Raw, policyContext.JSONContext()); err != nil { logger.Error(err, "failed to patch images info to resource, policies that mutate images may be impacted") } - mh := mutation.NewMutationHandler(logger, h.rclient, h.eventGen, h.openApiManager, h.nsLister, h.metricsConfig) + mh := mutation.NewMutationHandler(logger, h.contextLoader, h.eventGen, h.openApiManager, h.nsLister, h.metricsConfig) mutatePatches, mutateWarnings, err := mh.HandleMutation(ctx, request, mutatePolicies, policyContext, startTime) if err != nil { logger.Error(err, "mutation failed") @@ -184,7 +187,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.rclient, h.eventGen, h.admissionReports, h.configuration) + ivh := imageverification.NewImageVerificationHandler(logger, h.kyvernoClient, h.contextLoader, h.rclient, 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 0b93aa00c6..781d79d23b 100644 --- a/pkg/webhooks/resource/imageverification/handler.go +++ b/pkg/webhooks/resource/imageverification/handler.go @@ -32,6 +32,7 @@ type ImageVerificationHandler interface { type imageVerificationHandler struct { kyvernoClient versioned.Interface + contextLoader engine.ContextLoaderFactory rclient registryclient.Client log logr.Logger eventGen event.Interface @@ -42,6 +43,7 @@ type imageVerificationHandler struct { func NewImageVerificationHandler( log logr.Logger, kyvernoClient versioned.Interface, + contextLoader engine.ContextLoaderFactory, rclient registryclient.Client, eventGen event.Interface, admissionReports bool, @@ -49,6 +51,7 @@ func NewImageVerificationHandler( ) ImageVerificationHandler { return &imageVerificationHandler{ kyvernoClient: kyvernoClient, + contextLoader: contextLoader, rclient: rclient, log: log, eventGen: eventGen, @@ -91,7 +94,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 := engine.VerifyAndPatchImages(ctx, h.rclient, policyContext, h.cfg) + resp, ivm := engine.VerifyAndPatchImages(ctx, h.contextLoader, h.rclient, policyContext, h.cfg) engineResponses = append(engineResponses, resp) patches = append(patches, resp.GetPatches()...) diff --git a/pkg/webhooks/resource/mutation/mutation.go b/pkg/webhooks/resource/mutation/mutation.go index 8f4492e48b..dfdc7c9c59 100644 --- a/pkg/webhooks/resource/mutation/mutation.go +++ b/pkg/webhooks/resource/mutation/mutation.go @@ -13,7 +13,6 @@ import ( "github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/metrics" "github.com/kyverno/kyverno/pkg/openapi" - "github.com/kyverno/kyverno/pkg/registryclient" "github.com/kyverno/kyverno/pkg/tracing" "github.com/kyverno/kyverno/pkg/utils" engineutils "github.com/kyverno/kyverno/pkg/utils/engine" @@ -36,7 +35,7 @@ type MutationHandler interface { func NewMutationHandler( log logr.Logger, - rclient registryclient.Client, + contextLoader engine.ContextLoaderFactory, eventGen event.Interface, openApiManager openapi.ValidateInterface, nsLister corev1listers.NamespaceLister, @@ -44,7 +43,7 @@ func NewMutationHandler( ) MutationHandler { return &mutationHandler{ log: log, - rclient: rclient, + contextLoader: contextLoader, eventGen: eventGen, openApiManager: openApiManager, nsLister: nsLister, @@ -54,7 +53,7 @@ func NewMutationHandler( type mutationHandler struct { log logr.Logger - rclient registryclient.Client + contextLoader engine.ContextLoaderFactory eventGen event.Interface openApiManager openapi.ValidateInterface nsLister corev1listers.NamespaceLister @@ -158,7 +157,7 @@ func (h *mutationHandler) applyMutation(ctx context.Context, request *admissionv policyContext = policyContext.WithNamespaceLabels(engineutils.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, h.log)) } - engineResponse := engine.Mutate(ctx, h.rclient, policyContext) + engineResponse := engine.Mutate(ctx, h.contextLoader, policyContext) policyPatches := engineResponse.GetPatches() if !engineResponse.IsSuccessful() { diff --git a/pkg/webhooks/resource/updaterequest.go b/pkg/webhooks/resource/updaterequest.go index c678f8067e..64459be380 100644 --- a/pkg/webhooks/resource/updaterequest.go +++ b/pkg/webhooks/resource/updaterequest.go @@ -18,7 +18,7 @@ import ( // createUpdateRequests applies generate and mutateExisting policies, and creates update requests for background reconcile func (h *handlers) createUpdateRequests(logger logr.Logger, request *admissionv1.AdmissionRequest, policyContext *engine.PolicyContext, generatePolicies, mutatePolicies []kyvernov1.PolicyInterface, ts time.Time) { - gh := generation.NewGenerationHandler(logger, h.client, h.kyvernoClient, h.rclient, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen, h.metricsConfig) + gh := generation.NewGenerationHandler(logger, h.client, h.kyvernoClient, h.contextLoader, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen, h.metricsConfig) go h.handleMutateExisting(context.TODO(), logger, request, mutatePolicies, policyContext, ts) go gh.Handle(context.TODO(), request, generatePolicies, policyContext, ts) } @@ -43,7 +43,7 @@ func (h *handlers) handleMutateExisting(ctx context.Context, logger logr.Logger, var rules []engineapi.RuleResponse policyContext := policyContext.WithPolicy(policy) - engineResponse := engine.ApplyBackgroundChecks(h.rclient, policyContext) + engineResponse := engine.ApplyBackgroundChecks(h.contextLoader, policyContext) for _, rule := range engineResponse.PolicyResponse.Rules { if rule.Status == engineapi.RuleStatusPass { diff --git a/pkg/webhooks/resource/validation/validation.go b/pkg/webhooks/resource/validation/validation.go index c378f2a904..73a1f367cd 100644 --- a/pkg/webhooks/resource/validation/validation.go +++ b/pkg/webhooks/resource/validation/validation.go @@ -15,7 +15,6 @@ import ( "github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/metrics" "github.com/kyverno/kyverno/pkg/policycache" - "github.com/kyverno/kyverno/pkg/registryclient" "github.com/kyverno/kyverno/pkg/tracing" admissionutils "github.com/kyverno/kyverno/pkg/utils/admission" controllerutils "github.com/kyverno/kyverno/pkg/utils/controller" @@ -38,7 +37,7 @@ type ValidationHandler interface { func NewValidationHandler( log logr.Logger, kyvernoClient versioned.Interface, - rclient registryclient.Client, + contextLoader engine.ContextLoaderFactory, pCache policycache.Cache, pcBuilder webhookutils.PolicyContextBuilder, eventGen event.Interface, @@ -49,7 +48,7 @@ func NewValidationHandler( return &validationHandler{ log: log, kyvernoClient: kyvernoClient, - rclient: rclient, + contextLoader: contextLoader, pCache: pCache, pcBuilder: pcBuilder, eventGen: eventGen, @@ -62,7 +61,7 @@ func NewValidationHandler( type validationHandler struct { log logr.Logger kyvernoClient versioned.Interface - rclient registryclient.Client + contextLoader engine.ContextLoaderFactory pCache policycache.Cache pcBuilder webhookutils.PolicyContextBuilder eventGen event.Interface @@ -114,7 +113,7 @@ func (v *validationHandler) HandleValidation( failurePolicy = kyvernov1.Fail } - engineResponse := engine.Validate(ctx, v.rclient, policyContext, v.cfg) + engineResponse := engine.Validate(ctx, v.contextLoader, policyContext, v.cfg) if engineResponse.IsNil() { // we get an empty response if old and new resources created the same response // allow updates if resource update doesnt change the policy evaluation @@ -173,7 +172,7 @@ func (v *validationHandler) buildAuditResponses( fmt.Sprintf("POLICY %s/%s", policy.GetNamespace(), policy.GetName()), func(ctx context.Context, span trace.Span) { policyContext := policyContext.WithPolicy(policy).WithNamespaceLabels(namespaceLabels) - responses = append(responses, engine.Validate(ctx, v.rclient, policyContext, v.cfg)) + responses = append(responses, engine.Validate(ctx, v.contextLoader, policyContext, v.cfg)) }, ) } diff --git a/pkg/webhooks/resource/validation_test.go b/pkg/webhooks/resource/validation_test.go index 6f76edfa18..350695360c 100644 --- a/pkg/webhooks/resource/validation_test.go +++ b/pkg/webhooks/resource/validation_test.go @@ -1058,10 +1058,11 @@ func TestValidate_failure_action_overrides(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(tc.rawResource) assert.NilError(t, err) + ctx := engine.NewPolicyContext().WithPolicy(&policy).WithNewResource(*resourceUnstructured).WithNamespaceLabels(tc.rawResourceNamespaceLabels) er := engine.Validate( context.TODO(), - registryclient.NewOrDie(), - engine.NewPolicyContext().WithPolicy(&policy).WithNewResource(*resourceUnstructured).WithNamespaceLabels(tc.rawResourceNamespaceLabels), + engine.LegacyContextLoaderFactory(registryclient.NewOrDie()), + ctx, cfg, ) if tc.blocked && tc.messages != nil { @@ -1124,7 +1125,12 @@ func Test_RuleSelector(t *testing.T) { ctx := engine.NewPolicyContext().WithPolicy(&policy).WithNewResource(*resourceUnstructured) cfg := config.NewDefaultConfiguration() - resp := engine.Validate(context.TODO(), registryclient.NewOrDie(), ctx, cfg) + resp := engine.Validate( + context.TODO(), + engine.LegacyContextLoaderFactory(registryclient.NewOrDie()), + ctx, + cfg, + ) assert.Assert(t, resp.PolicyResponse.RulesAppliedCount == 2) assert.Assert(t, resp.PolicyResponse.RulesErrorCount == 0) @@ -1135,7 +1141,12 @@ func Test_RuleSelector(t *testing.T) { applyOne := kyvernov1.ApplyOne policy.Spec.ApplyRules = &applyOne - resp = engine.Validate(context.TODO(), registryclient.NewOrDie(), ctx, cfg) + resp = engine.Validate( + context.TODO(), + engine.LegacyContextLoaderFactory(registryclient.NewOrDie()), + ctx, + cfg, + ) assert.Assert(t, resp.PolicyResponse.RulesAppliedCount == 1) assert.Assert(t, resp.PolicyResponse.RulesErrorCount == 0)