diff --git a/cmd/background-controller/main.go b/cmd/background-controller/main.go index 2dbb67545a..8e8a444ae7 100644 --- a/cmd/background-controller/main.go +++ b/cmd/background-controller/main.go @@ -18,6 +18,7 @@ import ( "github.com/kyverno/kyverno/pkg/config" policymetricscontroller "github.com/kyverno/kyverno/pkg/controllers/metrics/policy" engineapi "github.com/kyverno/kyverno/pkg/engine/api" + "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/leaderelection" "github.com/kyverno/kyverno/pkg/logging" @@ -43,6 +44,7 @@ func createrLeaderControllers( configuration config.Configuration, metricsConfig metrics.MetricsConfigManager, eventGenerator event.Interface, + jp jmespath.Interface, ) ([]internal.Controller, error) { policyCtrl, err := policy.NewPolicyController( kyvernoClient, @@ -57,6 +59,7 @@ func createrLeaderControllers( logging.WithName("PolicyController"), time.Hour, metricsConfig, + jp, ) if err != nil { return nil, err @@ -71,6 +74,7 @@ func createrLeaderControllers( kubeInformer.Core().V1().Namespaces(), eventGenerator, configuration, + jp, ) return []internal.Controller{ internal.NewController("policy-controller", policyCtrl, 2), @@ -137,6 +141,7 @@ func main() { setup.Logger, setup.Configuration, setup.MetricsConfiguration, + setup.Jp, dClient, setup.RegistryClient, setup.KubeClient, @@ -174,6 +179,7 @@ func main() { setup.Configuration, setup.MetricsManager, eventGenerator, + setup.Jp, ) if err != nil { logger.Error(err, "failed to create leader controllers") diff --git a/cmd/cleanup-controller/handlers/cleanup/condition_test.go b/cmd/cleanup-controller/handlers/cleanup/condition_test.go index aab19e7b27..337aa446bf 100644 --- a/cmd/cleanup-controller/handlers/cleanup/condition_test.go +++ b/cmd/cleanup-controller/handlers/cleanup/condition_test.go @@ -5,13 +5,17 @@ import ( "github.com/go-logr/logr" kyvernov2beta1 "github.com/kyverno/kyverno/api/kyverno/v2beta1" + "github.com/kyverno/kyverno/pkg/config" enginecontext "github.com/kyverno/kyverno/pkg/engine/context" + "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/logging" v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" ) +var jp = jmespath.New(config.NewDefaultConfiguration(false)) + func Test_checkCondition(t *testing.T) { - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) ctx.AddResource(map[string]interface{}{ "name": "dummy", }) diff --git a/cmd/cleanup-controller/handlers/cleanup/handlers.go b/cmd/cleanup-controller/handlers/cleanup/handlers.go index cf3d10cc57..aaa1c2c17a 100644 --- a/cmd/cleanup-controller/handlers/cleanup/handlers.go +++ b/cmd/cleanup-controller/handlers/cleanup/handlers.go @@ -11,6 +11,7 @@ import ( "github.com/kyverno/kyverno/pkg/clients/dclient" "github.com/kyverno/kyverno/pkg/config" enginecontext "github.com/kyverno/kyverno/pkg/engine/context" + "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/metrics" controllerutils "github.com/kyverno/kyverno/pkg/utils/controller" @@ -34,6 +35,7 @@ type handlers struct { polLister kyvernov2alpha1listers.CleanupPolicyLister nsLister corev1listers.NamespaceLister recorder record.EventRecorder + jp jmespath.Interface metrics cleanupMetrics } @@ -65,11 +67,12 @@ func newCleanupMetrics(logger logr.Logger) cleanupMetrics { } func New( + logger logr.Logger, client dclient.Interface, cpolLister kyvernov2alpha1listers.ClusterCleanupPolicyLister, polLister kyvernov2alpha1listers.CleanupPolicyLister, nsLister corev1listers.NamespaceLister, - logger logr.Logger, + jp jmespath.Interface, ) *handlers { return &handlers{ client: client, @@ -78,6 +81,7 @@ func New( nsLister: nsLister, recorder: event.NewRecorder(event.CleanupController, client.GetEventsInterface()), metrics: newCleanupMetrics(logger), + jp: jp, } } @@ -181,7 +185,7 @@ func (h *handlers) executePolicy(ctx context.Context, logger logr.Logger, policy } // check conditions if spec.Conditions != nil { - enginectx := enginecontext.NewContext() + enginectx := enginecontext.NewContext(h.jp) if err := enginectx.AddTargetResource(resource.Object); err != nil { debug.Error(err, "failed to add resource in context") errs = append(errs, err) diff --git a/cmd/cleanup-controller/main.go b/cmd/cleanup-controller/main.go index 436855aa4f..498264a32b 100644 --- a/cmd/cleanup-controller/main.go +++ b/cmd/cleanup-controller/main.go @@ -199,7 +199,7 @@ func main() { } // create handlers admissionHandlers := admissionhandlers.New(dClient) - cleanupHandlers := cleanuphandlers.New(dClient, cpolLister, polLister, nsLister, setup.Logger.WithName("cleanup-handler")) + cleanupHandlers := cleanuphandlers.New(setup.Logger.WithName("cleanup-handler"), dClient, cpolLister, polLister, nsLister, setup.Jp) // create server server := NewServer( func() ([]byte, []byte, error) { diff --git a/cmd/cli/kubectl-kyverno/jp/query/query.go b/cmd/cli/kubectl-kyverno/jp/query/query.go index 8086110d77..2fc95ad7e6 100644 --- a/cmd/cli/kubectl-kyverno/jp/query/query.go +++ b/cmd/cli/kubectl-kyverno/jp/query/query.go @@ -10,6 +10,7 @@ import ( "strings" gojmespath "github.com/jmespath/go-jmespath" + "github.com/kyverno/kyverno/pkg/config" "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/spf13/cobra" "sigs.k8s.io/yaml" @@ -162,11 +163,12 @@ func loadInput(file string) (interface{}, error) { } func evaluate(input interface{}, query string) (interface{}, error) { - jp, err := jmespath.New(query) + jp := jmespath.New(config.NewDefaultConfiguration(false)) + q, err := jp.Query(query) if err != nil { return nil, fmt.Errorf("failed to compile JMESPath: %s, error: %v", query, err) } - result, err := jp.Search(input) + result, err := q.Search(input) if err != nil { if syntaxError, ok := err.(gojmespath.SyntaxError); ok { return nil, fmt.Errorf("%s\n%s", syntaxError, syntaxError.HighlightLocation()) diff --git a/cmd/cli/kubectl-kyverno/utils/common/common.go b/cmd/cli/kubectl-kyverno/utils/common/common.go index 644023e06d..762a35477d 100644 --- a/cmd/cli/kubectl-kyverno/utils/common/common.go +++ b/cmd/cli/kubectl-kyverno/utils/common/common.go @@ -23,6 +23,7 @@ import ( "github.com/kyverno/kyverno/pkg/engine" engineapi "github.com/kyverno/kyverno/pkg/engine/api" engineContext "github.com/kyverno/kyverno/pkg/engine/context" + "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/engine/variables/regex" "github.com/kyverno/kyverno/pkg/registryclient" datautils "github.com/kyverno/kyverno/pkg/utils/data" @@ -369,6 +370,8 @@ func GetVariable(variablesString, valuesFile string, fs billy.Filesystem, isGit // ApplyPolicyOnResource - function to apply policy on resource func ApplyPolicyOnResource(c ApplyPolicyConfig) ([]engineapi.EngineResponse, error) { + jp := jmespath.New(config.NewDefaultConfiguration(false)) + var engineResponses []engineapi.EngineResponse namespaceLabels := make(map[string]string) operationIsDelete := false @@ -431,7 +434,7 @@ OuterLoop: if err != nil { log.Log.Error(err, "unable to convert raw resource to unstructured") } - ctx := engineContext.NewContext() + ctx := engineContext.NewContext(jp) if operationIsDelete { err = engineContext.AddOldResource(ctx, resourceRaw) @@ -478,6 +481,7 @@ OuterLoop: eng := engine.NewEngine( cfg, config.NewDefaultMetricsConfiguration(), + jmespath.New(cfg), c.Client, registryclient.NewOrDie(), store.ContextLoaderFactory(nil), @@ -1025,9 +1029,11 @@ func initializeMockController(objects []runtime.Object) (*generate.GenerateContr } client.SetDiscovery(dclient.NewFakeDiscoveryClient(nil)) + cfg := config.NewDefaultConfiguration(false) c := generate.NewGenerateControllerWithOnlyClient(client, engine.NewEngine( - config.NewDefaultConfiguration(false), + cfg, config.NewDefaultMetricsConfiguration(), + jmespath.New(cfg), client, nil, store.ContextLoaderFactory(nil), diff --git a/cmd/cli/kubectl-kyverno/utils/store/contextloader.go b/cmd/cli/kubectl-kyverno/utils/store/contextloader.go index 304a7e4901..abfc04fc48 100644 --- a/cmd/cli/kubectl-kyverno/utils/store/contextloader.go +++ b/cmd/cli/kubectl-kyverno/utils/store/contextloader.go @@ -8,6 +8,7 @@ import ( "github.com/kyverno/kyverno/pkg/clients/dclient" engineapi "github.com/kyverno/kyverno/pkg/engine/api" enginecontext "github.com/kyverno/kyverno/pkg/engine/context" + "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/logging" "github.com/kyverno/kyverno/pkg/registryclient" ) @@ -37,6 +38,7 @@ type mockContextLoader struct { func (l *mockContextLoader) Load( ctx context.Context, + jp jmespath.Interface, client dclient.Interface, _ registryclient.Client, contextEntries []kyvernov1.ContextEntry, @@ -56,15 +58,15 @@ func (l *mockContextLoader) Load( for _, entry := range contextEntries { if entry.ImageRegistry != nil && hasRegistryAccess { rclient := GetRegistryClient() - if err := engineapi.LoadImageData(ctx, rclient, l.logger, entry, jsonContext); err != nil { + if err := engineapi.LoadImageData(ctx, jp, rclient, l.logger, entry, jsonContext); err != nil { return err } } else if entry.Variable != nil { - if err := engineapi.LoadVariable(l.logger, entry, jsonContext); err != nil { + if err := engineapi.LoadVariable(l.logger, jp, entry, jsonContext); err != nil { return err } } else if entry.APICall != nil && IsApiCallAllowed() { - if err := engineapi.LoadAPIData(ctx, l.logger, entry, jsonContext, client); err != nil { + if err := engineapi.LoadAPIData(ctx, jp, l.logger, entry, jsonContext, client); err != nil { return err } } diff --git a/cmd/internal/engine.go b/cmd/internal/engine.go index a295801eaa..78415c56bd 100644 --- a/cmd/internal/engine.go +++ b/cmd/internal/engine.go @@ -13,6 +13,7 @@ import ( "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/engine/jmespath" "github.com/kyverno/kyverno/pkg/registryclient" "k8s.io/client-go/kubernetes" ) @@ -22,6 +23,7 @@ func NewEngine( logger logr.Logger, configuration config.Configuration, metricsConfiguration config.MetricsConfiguration, + jp jmespath.Interface, client dclient.Interface, rclient registryclient.Client, kubeClient kubernetes.Interface, @@ -34,6 +36,7 @@ func NewEngine( return engine.NewEngine( configuration, metricsConfiguration, + jp, client, rclient, engineapi.DefaultContextLoaderFactory(configMapResolver), diff --git a/cmd/internal/setup.go b/cmd/internal/setup.go index 56469dcbfa..6743b7d11f 100644 --- a/cmd/internal/setup.go +++ b/cmd/internal/setup.go @@ -6,6 +6,7 @@ import ( "github.com/go-logr/logr" kubeclient "github.com/kyverno/kyverno/pkg/clients/kube" "github.com/kyverno/kyverno/pkg/config" + "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/metrics" "github.com/kyverno/kyverno/pkg/registryclient" "k8s.io/client-go/kubernetes" @@ -27,6 +28,7 @@ type SetupResult struct { Configuration config.Configuration MetricsConfiguration config.MetricsConfiguration MetricsManager metrics.MetricsConfigManager + Jp jmespath.Interface KubeClient kubernetes.Interface LeaderElectionClient kubernetes.Interface RegistryClient registryclient.Client @@ -59,6 +61,7 @@ func Setup(config Configuration, name string, skipResourceFilters bool) (context Configuration: configuration, MetricsConfiguration: metricsConfiguration, MetricsManager: metricsManager, + Jp: jmespath.New(configuration), KubeClient: client, LeaderElectionClient: leaderElectionClient, RegistryClient: registryClient, diff --git a/cmd/kyverno/main.go b/cmd/kyverno/main.go index 4c6d28b9f3..6cdb1f9c3a 100644 --- a/cmd/kyverno/main.go +++ b/cmd/kyverno/main.go @@ -303,6 +303,7 @@ func main() { setup.Logger, setup.Configuration, setup.MetricsConfiguration, + setup.Jp, dClient, setup.RegistryClient, setup.KubeClient, @@ -426,8 +427,6 @@ func main() { setup.MetricsManager, policyCache, kubeInformer.Core().V1().Namespaces().Lister(), - kubeInformer.Rbac().V1().RoleBindings().Lister(), - kubeInformer.Rbac().V1().ClusterRoleBindings().Lister(), kyvernoInformer.Kyverno().V1beta1().UpdateRequests().Lister().UpdateRequests(config.KyvernoNamespace()), kyvernoInformer.Kyverno().V1().ClusterPolicies(), kyvernoInformer.Kyverno().V1().Policies(), @@ -436,6 +435,7 @@ func main() { openApiManager, admissionReports, backgroundServiceAccountName, + setup.Jp, ) exceptionHandlers := webhooksexception.NewHandlers(exception.ValidationOptions{ Enabled: internal.PolicyExceptionEnabled(), diff --git a/cmd/reports-controller/main.go b/cmd/reports-controller/main.go index 7f3e0c2f9b..77fe2b789b 100644 --- a/cmd/reports-controller/main.go +++ b/cmd/reports-controller/main.go @@ -21,6 +21,7 @@ import ( backgroundscancontroller "github.com/kyverno/kyverno/pkg/controllers/report/background" resourcereportcontroller "github.com/kyverno/kyverno/pkg/controllers/report/resource" engineapi "github.com/kyverno/kyverno/pkg/engine/api" + "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/leaderelection" "github.com/kyverno/kyverno/pkg/logging" @@ -49,6 +50,7 @@ func createReportControllers( kyvernoInformer kyvernoinformer.SharedInformerFactory, backgroundScanInterval time.Duration, configuration config.Configuration, + jp jmespath.Interface, eventGenerator event.Interface, ) ([]internal.Controller, func(context.Context) error) { var ctrls []internal.Controller @@ -105,6 +107,7 @@ func createReportControllers( resourceReportController, backgroundScanInterval, configuration, + jp, eventGenerator, ), backgroundScanWorkers, @@ -134,6 +137,7 @@ func createrLeaderControllers( dynamicClient dclient.Interface, rclient registryclient.Client, configuration config.Configuration, + jp jmespath.Interface, eventGenerator event.Interface, backgroundScanInterval time.Duration, ) ([]internal.Controller, func(context.Context) error, error) { @@ -151,6 +155,7 @@ func createrLeaderControllers( kyvernoInformer, backgroundScanInterval, configuration, + jp, eventGenerator, ) return reportControllers, warmup, nil @@ -223,6 +228,7 @@ func main() { setup.Logger, setup.Configuration, setup.MetricsConfiguration, + setup.Jp, dClient, setup.RegistryClient, setup.KubeClient, @@ -265,6 +271,7 @@ func main() { dClient, setup.RegistryClient, setup.Configuration, + setup.Jp, eventGenerator, backgroundScanInterval, ) diff --git a/pkg/background/common/context.go b/pkg/background/common/context.go index ded48ce225..8495d1c9c0 100644 --- a/pkg/background/common/context.go +++ b/pkg/background/common/context.go @@ -10,18 +10,22 @@ import ( "github.com/kyverno/kyverno/pkg/config" "github.com/kyverno/kyverno/pkg/engine" "github.com/kyverno/kyverno/pkg/engine/context" + "github.com/kyverno/kyverno/pkg/engine/jmespath" admissionutils "github.com/kyverno/kyverno/pkg/utils/admission" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) -func NewBackgroundContext(dclient dclient.Interface, ur *kyvernov1beta1.UpdateRequest, +func NewBackgroundContext( + logger logr.Logger, + dclient dclient.Interface, + ur *kyvernov1beta1.UpdateRequest, policy kyvernov1.PolicyInterface, trigger *unstructured.Unstructured, cfg config.Configuration, + jp jmespath.Interface, namespaceLabels map[string]string, - logger logr.Logger, ) (*engine.PolicyContext, error) { - ctx := context.NewContext() + ctx := context.NewContext(jp) var new, old unstructured.Unstructured var err error diff --git a/pkg/background/generate/generate.go b/pkg/background/generate/generate.go index b2060c1875..338276d141 100644 --- a/pkg/background/generate/generate.go +++ b/pkg/background/generate/generate.go @@ -20,6 +20,7 @@ import ( "github.com/kyverno/kyverno/pkg/engine" engineapi "github.com/kyverno/kyverno/pkg/engine/api" enginecontext "github.com/kyverno/kyverno/pkg/engine/context" + "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/engine/variables" "github.com/kyverno/kyverno/pkg/event" admissionutils "github.com/kyverno/kyverno/pkg/utils/admission" @@ -55,6 +56,7 @@ type GenerateController struct { eventGen event.Interface log logr.Logger + jp jmespath.Interface } // NewGenerateController returns an instance of the Generate-Request Controller @@ -70,6 +72,7 @@ func NewGenerateController( dynamicConfig config.Configuration, eventGen event.Interface, log logr.Logger, + jp jmespath.Interface, ) *GenerateController { c := GenerateController{ client: client, @@ -83,6 +86,7 @@ func NewGenerateController( configuration: dynamicConfig, eventGen: eventGen, log: log, + jp: jp, } return &c } @@ -198,7 +202,7 @@ func (c *GenerateController) applyGenerate(resource unstructured.Unstructured, u return nil, err } - policyContext, err := common.NewBackgroundContext(c.client, &ur, policy, &resource, c.configuration, namespaceLabels, logger) + policyContext, err := common.NewBackgroundContext(logger, c.client, &ur, policy, &resource, c.configuration, c.jp, namespaceLabels) if err != nil { return nil, err } diff --git a/pkg/background/mutate/mutate.go b/pkg/background/mutate/mutate.go index 0280d846f3..a5893f2b5e 100644 --- a/pkg/background/mutate/mutate.go +++ b/pkg/background/mutate/mutate.go @@ -13,6 +13,7 @@ import ( "github.com/kyverno/kyverno/pkg/clients/dclient" "github.com/kyverno/kyverno/pkg/config" engineapi "github.com/kyverno/kyverno/pkg/engine/api" + "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/utils" admissionutils "github.com/kyverno/kyverno/pkg/utils/admission" @@ -28,7 +29,7 @@ import ( var ErrEmptyPatch error = fmt.Errorf("empty resource to patch") -type MutateExistingController struct { +type mutateExistingController struct { // clients client dclient.Interface statusControl common.StatusControlInterface @@ -43,6 +44,7 @@ type MutateExistingController struct { eventGen event.Interface log logr.Logger + jp jmespath.Interface } // NewMutateExistingController returns an instance of the MutateExistingController @@ -56,8 +58,9 @@ func NewMutateExistingController( dynamicConfig config.Configuration, eventGen event.Interface, log logr.Logger, -) *MutateExistingController { - c := MutateExistingController{ + jp jmespath.Interface, +) *mutateExistingController { + c := mutateExistingController{ client: client, statusControl: statusControl, engine: engine, @@ -67,11 +70,12 @@ func NewMutateExistingController( configuration: dynamicConfig, eventGen: eventGen, log: log, + jp: jp, } return &c } -func (c *MutateExistingController) ProcessUR(ur *kyvernov1beta1.UpdateRequest) error { +func (c *mutateExistingController) ProcessUR(ur *kyvernov1beta1.UpdateRequest) error { logger := c.log.WithValues("name", ur.GetName(), "policy", ur.Spec.GetPolicyKey(), "resource", ur.Spec.GetResource().String()) var errs []error @@ -130,7 +134,7 @@ func (c *MutateExistingController) ProcessUR(ur *kyvernov1beta1.UpdateRequest) e } namespaceLabels := engineutils.GetNamespaceSelectorsFromNamespaceLister(trigger.GetKind(), trigger.GetNamespace(), c.nsLister, logger) - policyContext, err := common.NewBackgroundContext(c.client, ur, policy, trigger, c.configuration, namespaceLabels, logger) + policyContext, err := common.NewBackgroundContext(logger, c.client, ur, policy, trigger, c.configuration, c.jp, namespaceLabels) if err != nil { logger.WithName(rule.Name).Error(err, "failed to build policy context") errs = append(errs, err) @@ -210,7 +214,7 @@ func (c *MutateExistingController) ProcessUR(ur *kyvernov1beta1.UpdateRequest) e return updateURStatus(c.statusControl, *ur, err) } -func (c *MutateExistingController) getPolicy(key string) (kyvernov1.PolicyInterface, error) { +func (c *mutateExistingController) getPolicy(key string) (kyvernov1.PolicyInterface, error) { pNamespace, pName, err := cache.SplitMetaNamespaceKey(key) if err != nil { return nil, err @@ -223,7 +227,7 @@ func (c *MutateExistingController) getPolicy(key string) (kyvernov1.PolicyInterf return c.policyLister.Get(pName) } -func (c *MutateExistingController) report(err error, policy, rule string, target *unstructured.Unstructured) { +func (c *mutateExistingController) report(err error, policy, rule string, target *unstructured.Unstructured) { var events []event.Info if target == nil { diff --git a/pkg/background/update_request_controller.go b/pkg/background/update_request_controller.go index 6b0ed8ff10..a27cb2031a 100644 --- a/pkg/background/update_request_controller.go +++ b/pkg/background/update_request_controller.go @@ -18,6 +18,7 @@ import ( "github.com/kyverno/kyverno/pkg/clients/dclient" "github.com/kyverno/kyverno/pkg/config" engineapi "github.com/kyverno/kyverno/pkg/engine/api" + "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/event" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -58,6 +59,7 @@ type controller struct { eventGen event.Interface configuration config.Configuration + jp jmespath.Interface } // NewController returns an instance of the Generate-Request Controller @@ -70,7 +72,8 @@ func NewController( urInformer kyvernov1beta1informers.UpdateRequestInformer, namespaceInformer corev1informers.NamespaceInformer, eventGen event.Interface, - dynamicConfig config.Configuration, + configuration config.Configuration, + jp jmespath.Interface, ) Controller { urLister := urInformer.Lister().UpdateRequests(config.KyvernoNamespace()) c := controller{ @@ -83,7 +86,8 @@ func NewController( nsLister: namespaceInformer.Lister(), queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "background"), eventGen: eventGen, - configuration: dynamicConfig, + configuration: configuration, + jp: jp, } _, _ = urInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: c.addUR, @@ -216,10 +220,10 @@ func (c *controller) processUR(ur *kyvernov1beta1.UpdateRequest) error { statusControl := common.NewStatusControl(c.kyvernoClient, c.urLister) switch ur.Spec.GetRequestType() { case kyvernov1beta1.Mutate: - ctrl := mutate.NewMutateExistingController(c.client, statusControl, c.engine, c.cpolLister, c.polLister, c.nsLister, c.configuration, c.eventGen, logger) + ctrl := mutate.NewMutateExistingController(c.client, statusControl, c.engine, c.cpolLister, c.polLister, c.nsLister, c.configuration, c.eventGen, logger, c.jp) return ctrl.ProcessUR(ur) case kyvernov1beta1.Generate: - ctrl := generate.NewGenerateController(c.client, c.kyvernoClient, statusControl, c.engine, c.cpolLister, c.polLister, c.urLister, c.nsLister, c.configuration, c.eventGen, logger) + ctrl := generate.NewGenerateController(c.client, c.kyvernoClient, statusControl, c.engine, c.cpolLister, c.polLister, c.urLister, c.nsLister, c.configuration, c.eventGen, logger, c.jp) return ctrl.ProcessUR(ur) } return nil diff --git a/pkg/controllers/report/background/controller.go b/pkg/controllers/report/background/controller.go index 5ada59448d..24051e6ccc 100644 --- a/pkg/controllers/report/background/controller.go +++ b/pkg/controllers/report/background/controller.go @@ -17,6 +17,7 @@ import ( "github.com/kyverno/kyverno/pkg/controllers/report/resource" "github.com/kyverno/kyverno/pkg/controllers/report/utils" engineapi "github.com/kyverno/kyverno/pkg/engine/api" + "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/event" controllerutils "github.com/kyverno/kyverno/pkg/utils/controller" datautils "github.com/kyverno/kyverno/pkg/utils/data" @@ -64,6 +65,7 @@ type controller struct { // config config config.Configuration + jp jmespath.Interface eventGen event.Interface } @@ -78,6 +80,7 @@ func NewController( metadataCache resource.MetadataCache, forceDelay time.Duration, config config.Configuration, + jp jmespath.Interface, eventGen event.Interface, ) controllers.Controller { bgscanr := metadataFactory.ForResource(kyvernov1alpha2.SchemeGroupVersion.WithResource("backgroundscanreports")) @@ -96,6 +99,7 @@ func NewController( metadataCache: metadataCache, forceDelay: forceDelay, config: config, + jp: jp, eventGen: eventGen, } controllerutils.AddDefaultEventHandlers(logger, bgscanr.Informer(), queue) @@ -301,7 +305,7 @@ func (c *controller) reconcileReport( // calculate necessary results for _, policy := range backgroundPolicies { if full || actual[reportutils.PolicyLabel(policy)] != policy.GetResourceVersion() { - scanner := utils.NewScanner(logger, c.engine, c.config) + scanner := utils.NewScanner(logger, c.engine, c.config, c.jp) 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 ed37514027..7bcc443694 100644 --- a/pkg/controllers/report/utils/scanner.go +++ b/pkg/controllers/report/utils/scanner.go @@ -9,6 +9,7 @@ import ( "github.com/kyverno/kyverno/pkg/engine" engineapi "github.com/kyverno/kyverno/pkg/engine/api" enginecontext "github.com/kyverno/kyverno/pkg/engine/context" + "github.com/kyverno/kyverno/pkg/engine/jmespath" "go.uber.org/multierr" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) @@ -17,6 +18,7 @@ type scanner struct { logger logr.Logger engine engineapi.Engine config config.Configuration + jp jmespath.Interface } type ScanResult struct { @@ -32,11 +34,13 @@ func NewScanner( logger logr.Logger, engine engineapi.Engine, config config.Configuration, + jp jmespath.Interface, ) Scanner { return &scanner{ logger: logger, engine: engine, config: config, + jp: jp, } } @@ -68,7 +72,7 @@ func (s *scanner) ScanResource(ctx context.Context, resource unstructured.Unstru } func (s *scanner) validateResource(ctx context.Context, resource unstructured.Unstructured, nsLabels map[string]string, policy kyvernov1.PolicyInterface) (*engineapi.EngineResponse, error) { - enginectx := enginecontext.NewContext() + enginectx := enginecontext.NewContext(s.jp) if err := enginectx.AddResource(resource.Object); err != nil { return nil, err } @@ -90,7 +94,7 @@ func (s *scanner) validateResource(ctx context.Context, resource unstructured.Un } func (s *scanner) validateImages(ctx context.Context, resource unstructured.Unstructured, nsLabels map[string]string, policy kyvernov1.PolicyInterface) (*engineapi.EngineResponse, error) { - enginectx := enginecontext.NewContext() + enginectx := enginecontext.NewContext(s.jp) if err := enginectx.AddResource(resource.Object); err != nil { return nil, err } diff --git a/pkg/engine/api/context.go b/pkg/engine/api/context.go index 03c2c32292..d1b80b63d7 100644 --- a/pkg/engine/api/context.go +++ b/pkg/engine/api/context.go @@ -15,7 +15,7 @@ import ( "github.com/kyverno/kyverno/pkg/registryclient" ) -func LoadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx enginecontext.Interface) (err error) { +func LoadVariable(logger logr.Logger, jp jmespath.Interface, entry kyvernov1.ContextEntry, ctx enginecontext.Interface) (err error) { path := "" if entry.Variable.JMESPath != "" { jp, err := variables.SubstituteAll(logger, ctx, entry.Variable.JMESPath) @@ -45,7 +45,7 @@ func LoadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx engineco return fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.Variable.Value, err) } if path != "" { - variable, err := applyJMESPath(path, variable) + variable, err := applyJMESPath(jp, path, variable) if err == nil { output = variable } else if defaultValue == nil { @@ -74,8 +74,8 @@ func LoadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx engineco } } -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) +func LoadImageData(ctx context.Context, jp jmespath.Interface, rclient registryclient.Client, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface) error { + imageData, err := fetchImageData(ctx, jp, rclient, logger, entry, enginectx) if err != nil { return err } @@ -89,8 +89,8 @@ func LoadImageData(ctx context.Context, rclient registryclient.Client, logger lo return nil } -func LoadAPIData(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface, client dclient.Interface) error { - executor, err := apicall.New(logger, entry, enginectx, client) +func LoadAPIData(ctx context.Context, jp jmespath.Interface, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface, client dclient.Interface) error { + executor, err := apicall.New(logger, jp, entry, enginectx, client) if err != nil { return fmt.Errorf("failed to initialize APICall: %w", err) } @@ -112,7 +112,7 @@ func LoadConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.Cont return nil } -func fetchImageData(ctx context.Context, rclient registryclient.Client, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface) (interface{}, error) { +func fetchImageData(ctx context.Context, jp jmespath.Interface, 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) @@ -130,7 +130,7 @@ func fetchImageData(ctx context.Context, rclient registryclient.Client, logger l return nil, err } if path != "" { - imageData, err = applyJMESPath(path.(string), imageData) + imageData, err = applyJMESPath(jp, path.(string), imageData) if err != nil { return nil, fmt.Errorf("failed to apply JMESPath (%s) results to context entry %s, error: %v", entry.ImageRegistry.JMESPath, entry.Name, err) } @@ -193,12 +193,12 @@ func fetchImageDataMap(ctx context.Context, rclient registryclient.Client, ref s return untyped, nil } -func applyJMESPath(jmesPath string, data interface{}) (interface{}, error) { - jp, err := jmespath.New(jmesPath) +func applyJMESPath(jp jmespath.Interface, query string, data interface{}) (interface{}, error) { + q, err := jp.Query(query) if err != nil { - return nil, fmt.Errorf("failed to compile JMESPath: %s, error: %v", jmesPath, err) + return nil, fmt.Errorf("failed to compile JMESPath: %s, error: %v", query, err) } - return jp.Search(data) + return q.Search(data) } func fetchConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface, resolver ConfigmapResolver) ([]byte, error) { diff --git a/pkg/engine/api/contextloader.go b/pkg/engine/api/contextloader.go index 454cf8143c..659b6cba45 100644 --- a/pkg/engine/api/contextloader.go +++ b/pkg/engine/api/contextloader.go @@ -7,6 +7,7 @@ import ( kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" "github.com/kyverno/kyverno/pkg/clients/dclient" enginecontext "github.com/kyverno/kyverno/pkg/engine/context" + "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/logging" "github.com/kyverno/kyverno/pkg/registryclient" ) @@ -18,6 +19,7 @@ type ContextLoaderFactory = func(policy kyvernov1.PolicyInterface, rule kyvernov type ContextLoader interface { Load( ctx context.Context, + jp jmespath.Interface, client dclient.Interface, rclient registryclient.Client, contextEntries []kyvernov1.ContextEntry, @@ -43,6 +45,7 @@ type contextLoader struct { func (l *contextLoader) Load( ctx context.Context, + jp jmespath.Interface, client dclient.Interface, rclient registryclient.Client, contextEntries []kyvernov1.ContextEntry, @@ -54,15 +57,15 @@ func (l *contextLoader) Load( return err } } else if entry.APICall != nil { - if err := LoadAPIData(ctx, l.logger, entry, jsonContext, client); err != nil { + if err := LoadAPIData(ctx, jp, l.logger, entry, jsonContext, client); err != nil { return err } } else if entry.ImageRegistry != nil { - if err := LoadImageData(ctx, rclient, l.logger, entry, jsonContext); err != nil { + if err := LoadImageData(ctx, jp, rclient, l.logger, entry, jsonContext); err != nil { return err } } else if entry.Variable != nil { - if err := LoadVariable(l.logger, entry, jsonContext); err != nil { + if err := LoadVariable(l.logger, jp, entry, jsonContext); err != nil { return err } } diff --git a/pkg/engine/apicall/apiCall.go b/pkg/engine/apicall/apiCall.go index b66dfc2fed..41fca17c1f 100644 --- a/pkg/engine/apicall/apiCall.go +++ b/pkg/engine/apicall/apiCall.go @@ -21,13 +21,15 @@ import ( type apiCall struct { logger logr.Logger + jp jmespath.Interface entry kyvernov1.ContextEntry jsonCtx enginecontext.Interface client dclient.Interface } func New( - log logr.Logger, + logger logr.Logger, + jp jmespath.Interface, entry kyvernov1.ContextEntry, jsonCtx enginecontext.Interface, client dclient.Interface, @@ -36,10 +38,11 @@ func New( return nil, fmt.Errorf("missing APICall in context entry %v", entry) } return &apiCall{ + logger: logger, + jp: jp, entry: entry, jsonCtx: jsonCtx, client: client, - logger: log, }, nil } @@ -205,7 +208,7 @@ func (a *apiCall) transformAndStore(jsonData []byte) ([]byte, error) { return nil, fmt.Errorf("failed to substitute variables in context entry %s JMESPath %s: %w", a.entry.Name, a.entry.APICall.JMESPath, err) } - results, err := applyJMESPathJSON(path.(string), jsonData) + results, err := a.applyJMESPathJSON(path.(string), jsonData) if err != nil { return nil, fmt.Errorf("failed to apply JMESPath %s for context entry %s: %w", path, a.entry.Name, err) } @@ -224,17 +227,11 @@ func (a *apiCall) transformAndStore(jsonData []byte) ([]byte, error) { return contextData, nil } -func applyJMESPathJSON(jmesPath string, jsonData []byte) (interface{}, error) { +func (a *apiCall) applyJMESPathJSON(jmesPath string, jsonData []byte) (interface{}, error) { var data interface{} err := json.Unmarshal(jsonData, &data) if err != nil { return nil, fmt.Errorf("failed to unmarshal JSON: %s, error: %w", string(jsonData), err) } - - jp, err := jmespath.New(jmesPath) - if err != nil { - return nil, fmt.Errorf("failed to compile JMESPath: %s, error: %v", jmesPath, err) - } - - return jp.Search(data) + return a.jp.Search(jmesPath, data) } diff --git a/pkg/engine/apicall/apiCall_test.go b/pkg/engine/apicall/apiCall_test.go index 380e4852c0..217adb9ee4 100644 --- a/pkg/engine/apicall/apiCall_test.go +++ b/pkg/engine/apicall/apiCall_test.go @@ -9,11 +9,15 @@ import ( "github.com/go-logr/logr" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" + "github.com/kyverno/kyverno/pkg/config" enginecontext "github.com/kyverno/kyverno/pkg/engine/context" + "github.com/kyverno/kyverno/pkg/engine/jmespath" "gotest.tools/assert" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" ) +var jp = jmespath.New(config.NewDefaultConfiguration(false)) + func buildTestServer(responseData []byte) *httptest.Server { mux := http.NewServeMux() mux.HandleFunc("/resource", func(w http.ResponseWriter, r *http.Request) { @@ -38,9 +42,9 @@ func Test_serviceGetRequest(t *testing.T) { defer s.Close() entry := kyvernov1.ContextEntry{} - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) - _, err := New(logr.Discard(), entry, ctx, nil) + _, err := New(logr.Discard(), jp, entry, ctx, nil) assert.ErrorContains(t, err, "missing APICall") entry.Name = "test" @@ -50,19 +54,19 @@ func Test_serviceGetRequest(t *testing.T) { }, } - call, err := New(logr.Discard(), entry, ctx, nil) + call, err := New(logr.Discard(), jp, entry, ctx, nil) assert.NilError(t, err) _, err = call.Execute(context.TODO()) assert.ErrorContains(t, err, "invalid request type") entry.APICall.Service.Method = "GET" - call, err = New(logr.Discard(), entry, ctx, nil) + call, err = New(logr.Discard(), jp, entry, ctx, nil) assert.NilError(t, err) _, err = call.Execute(context.TODO()) assert.ErrorContains(t, err, "HTTP 404") entry.APICall.Service.URL = s.URL + "/resource" - call, err = New(logr.Discard(), entry, ctx, nil) + call, err = New(logr.Discard(), jp, entry, ctx, nil) assert.NilError(t, err) data, err := call.Execute(context.TODO()) @@ -86,8 +90,8 @@ func Test_servicePostRequest(t *testing.T) { }, } - ctx := enginecontext.NewContext() - call, err := New(logr.Discard(), entry, ctx, nil) + ctx := enginecontext.NewContext(jp) + call, err := New(logr.Discard(), jp, entry, ctx, nil) assert.NilError(t, err) data, err := call.Execute(context.TODO()) assert.NilError(t, err) @@ -135,7 +139,7 @@ func Test_servicePostRequest(t *testing.T) { }, } - call, err = New(logr.Discard(), entry, ctx, nil) + call, err = New(logr.Discard(), jp, entry, ctx, nil) assert.NilError(t, err) data, err = call.Execute(context.TODO()) assert.NilError(t, err) diff --git a/pkg/engine/attestation_test.go b/pkg/engine/attestation_test.go index 558386f978..5768649ec7 100644 --- a/pkg/engine/attestation_test.go +++ b/pkg/engine/attestation_test.go @@ -6,8 +6,10 @@ import ( "github.com/go-logr/logr" v1 "github.com/kyverno/kyverno/api/kyverno/v1" + "github.com/kyverno/kyverno/pkg/config" "github.com/kyverno/kyverno/pkg/engine/context" "github.com/kyverno/kyverno/pkg/engine/internal" + "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/utils/api" "github.com/kyverno/kyverno/pkg/utils/image" "gotest.tools/assert" @@ -245,7 +247,7 @@ func Test_Conditions(t *testing.T) { }, } - ctx := context.NewContext() + ctx := context.NewContext(jmespath.New(config.NewDefaultConfiguration(false))) img := api.ImageInfo{Pointer: "/spec/containers/0/image"} img.ImageInfo = image.ImageInfo{ Registry: "docker.io", diff --git a/pkg/engine/context/context.go b/pkg/engine/context/context.go index 35ae3850f9..9fa17ed387 100644 --- a/pkg/engine/context/context.go +++ b/pkg/engine/context/context.go @@ -10,6 +10,7 @@ import ( kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1" "github.com/kyverno/kyverno/pkg/config" + "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/logging" apiutils "github.com/kyverno/kyverno/pkg/utils/api" admissionv1 "k8s.io/api/admission/v1" @@ -98,6 +99,7 @@ type Interface interface { // Context stores the data resources as JSON type context struct { + jp jmespath.Interface mutex sync.RWMutex jsonRaw []byte jsonRawCheckpoints [][]byte @@ -105,17 +107,17 @@ type context struct { } // NewContext returns a new context -func NewContext() Interface { - return NewContextFromRaw([]byte(`{}`)) +func NewContext(jp jmespath.Interface) Interface { + return NewContextFromRaw(jp, []byte(`{}`)) } // NewContextFromRaw returns a new context initialized with raw data -func NewContextFromRaw(raw []byte) Interface { - ctx := context{ +func NewContextFromRaw(jp jmespath.Interface, raw []byte) Interface { + return &context{ + jp: jp, jsonRaw: raw, jsonRawCheckpoints: make([][]byte, 0), } - return &ctx } // addJSON merges json data diff --git a/pkg/engine/context/context_test.go b/pkg/engine/context/context_test.go index f098cf5068..d24f498076 100644 --- a/pkg/engine/context/context_test.go +++ b/pkg/engine/context/context_test.go @@ -5,9 +5,13 @@ import ( "testing" urkyverno "github.com/kyverno/kyverno/api/kyverno/v1beta1" + "github.com/kyverno/kyverno/pkg/config" + "github.com/kyverno/kyverno/pkg/engine/jmespath" authenticationv1 "k8s.io/api/authentication/v1" ) +var jp = jmespath.New(config.NewDefaultConfiguration(false)) + func Test_addResourceAndUserContext(t *testing.T) { var err error rawResource := []byte(` @@ -55,7 +59,7 @@ func Test_addResourceAndUserContext(t *testing.T) { } var expectedResult string - ctx := NewContext() + ctx := NewContext(jp) err = AddResource(ctx, rawResource) if err != nil { t.Error(err) diff --git a/pkg/engine/context/evaluate.go b/pkg/engine/context/evaluate.go index e1a45519b8..ae6a71c98e 100644 --- a/pkg/engine/context/evaluate.go +++ b/pkg/engine/context/evaluate.go @@ -5,7 +5,6 @@ import ( "fmt" "strings" - "github.com/kyverno/kyverno/pkg/engine/jmespath" datautils "github.com/kyverno/kyverno/pkg/utils/data" ) @@ -16,7 +15,7 @@ func (ctx *context) Query(query string) (interface{}, error) { return nil, fmt.Errorf("invalid query (nil)") } // compile the query - queryPath, err := jmespath.New(query) + queryPath, err := ctx.jp.Query(query) if err != nil { logger.Error(err, "incorrect query", "query", query) return nil, fmt.Errorf("incorrect query %s: %v", query, err) diff --git a/pkg/engine/context/evaluate_test.go b/pkg/engine/context/evaluate_test.go index 278238e6de..c41c57e90d 100644 --- a/pkg/engine/context/evaluate_test.go +++ b/pkg/engine/context/evaluate_test.go @@ -28,7 +28,7 @@ func TestHasChanged(t *testing.T) { func TestRequestNotInitialize(t *testing.T) { request := admissionv1.AdmissionRequest{} - ctx := NewContext() + ctx := NewContext(jp) ctx.AddRequest(request) _, err := ctx.HasChanged("x.y.z") @@ -37,7 +37,7 @@ func TestRequestNotInitialize(t *testing.T) { func TestMissingOldObject(t *testing.T) { request := admissionv1.AdmissionRequest{} - ctx := NewContext() + ctx := NewContext(jp) ctx.AddRequest(request) request.Object.Raw = []byte(`{"a": {"b": 1, "c": 2}, "d": 3}`) @@ -47,7 +47,7 @@ func TestMissingOldObject(t *testing.T) { func TestMissingObject(t *testing.T) { request := admissionv1.AdmissionRequest{} - ctx := NewContext() + ctx := NewContext(jp) ctx.AddRequest(request) request.OldObject.Raw = []byte(`{"a": {"b": 1, "c": 2}, "d": 3}`) @@ -61,7 +61,7 @@ func createTestContext(obj, oldObj string) Interface { request.Object.Raw = []byte(obj) request.OldObject.Raw = []byte(oldObj) - ctx := NewContext() + ctx := NewContext(jp) ctx.AddRequest(request) return ctx } diff --git a/pkg/engine/context/mock_context.go b/pkg/engine/context/mock_context.go index dfd7f58830..a3f66505fb 100644 --- a/pkg/engine/context/mock_context.go +++ b/pkg/engine/context/mock_context.go @@ -6,6 +6,7 @@ import ( "strings" "sync" + "github.com/kyverno/kyverno/pkg/config" "github.com/kyverno/kyverno/pkg/engine/jmespath" wildcard "github.com/kyverno/kyverno/pkg/utils/wildcard" ) @@ -41,7 +42,8 @@ func (ctx *MockContext) Query(query string) (interface{}, error) { var emptyResult interface{} // compile the query - if _, err := jmespath.New(query); err != nil { + jp := jmespath.New(config.NewDefaultConfiguration(false)) + if _, err := jp.Query(query); err != nil { return emptyResult, fmt.Errorf("invalid JMESPath query %s: %v", query, err) } diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 2e5b5b57a5..94287c188b 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -14,6 +14,7 @@ import ( enginecontext "github.com/kyverno/kyverno/pkg/engine/context" "github.com/kyverno/kyverno/pkg/engine/handlers" "github.com/kyverno/kyverno/pkg/engine/internal" + "github.com/kyverno/kyverno/pkg/engine/jmespath" engineutils "github.com/kyverno/kyverno/pkg/engine/utils" "github.com/kyverno/kyverno/pkg/logging" "github.com/kyverno/kyverno/pkg/metrics" @@ -28,6 +29,7 @@ import ( type engine struct { configuration config.Configuration metricsConfiguration config.MetricsConfiguration + jp jmespath.Interface client dclient.Interface rclient registryclient.Client contextLoader engineapi.ContextLoaderFactory @@ -42,6 +44,7 @@ type handlerFactory = func() (handlers.Handler, error) func NewEngine( configuration config.Configuration, metricsConfiguration config.MetricsConfiguration, + jp jmespath.Interface, client dclient.Interface, rclient registryclient.Client, contextLoader engineapi.ContextLoaderFactory, @@ -50,7 +53,7 @@ func NewEngine( meter := global.MeterProvider().Meter(metrics.MeterName) resultCounter, err := meter.Int64Counter( "kyverno_policy_results", - instrument.WithDescription("can be used to track the results associated with the policies applied in the user’s cluster, at the level from rule to policy to admission requests"), + instrument.WithDescription("can be used to track the results associated with the policies applied in the user's cluster, at the level from rule to policy to admission requests"), ) if err != nil { logging.Error(err, "failed to register metric kyverno_policy_results") @@ -65,6 +68,7 @@ func NewEngine( return &engine{ configuration: configuration, metricsConfiguration: metricsConfiguration, + jp: jp, client: client, rclient: rclient, contextLoader: contextLoader, @@ -165,6 +169,7 @@ func (e *engine) ContextLoader( return func(ctx context.Context, contextEntries []kyvernov1.ContextEntry, jsonContext enginecontext.Interface) error { return loader.Load( ctx, + e.jp, e.client, e.rclient, contextEntries, diff --git a/pkg/engine/forceMutate_test.go b/pkg/engine/forceMutate_test.go index 921351a98b..6508e18a95 100644 --- a/pkg/engine/forceMutate_test.go +++ b/pkg/engine/forceMutate_test.go @@ -6,7 +6,9 @@ import ( "github.com/go-logr/logr" kyverno "github.com/kyverno/kyverno/api/kyverno/v1" + "github.com/kyverno/kyverno/pkg/config" "github.com/kyverno/kyverno/pkg/engine/context" + "github.com/kyverno/kyverno/pkg/engine/jmespath" kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" "gotest.tools/assert" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -100,7 +102,8 @@ func Test_ForceMutateSubstituteVars(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - ctx := context.NewContext() + jp := jmespath.New(config.NewDefaultConfiguration(false)) + ctx := context.NewContext(jp) err = context.AddResource(ctx, rawResource) assert.NilError(t, err) @@ -205,7 +208,8 @@ func Test_ForceMutateSubstituteVarsWithPatchesJson6902(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - ctx := context.NewContext() + jp := jmespath.New(config.NewDefaultConfiguration(false)) + ctx := context.NewContext(jp) err = context.AddResource(ctx, rawResource) assert.NilError(t, err) @@ -291,7 +295,8 @@ func Test_ForceMutateSubstituteVarsWithPatchStrategicMerge(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - ctx := context.NewContext() + jp := jmespath.New(config.NewDefaultConfiguration(false)) + ctx := context.NewContext(jp) err = context.AddResource(ctx, rawResource) assert.NilError(t, err) diff --git a/pkg/engine/handlers/validation/validate_manifest_test.go b/pkg/engine/handlers/validation/validate_manifest_test.go index 2643f46946..58449b308b 100644 --- a/pkg/engine/handlers/validation/validate_manifest_test.go +++ b/pkg/engine/handlers/validation/validate_manifest_test.go @@ -10,6 +10,7 @@ import ( "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/engine/jmespath" "github.com/kyverno/kyverno/pkg/engine/policycontext" kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" "gotest.tools/assert" @@ -17,6 +18,8 @@ import ( apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" ) +var jp = jmespath.New(config.NewDefaultConfiguration(false)) + var test_policy = `{}` var signed_resource = `{ @@ -785,7 +788,7 @@ func buildContext(t *testing.T, policy, resource string, oldResource string) eng resourceUnstructured, err := kubeutils.BytesToUnstructured([]byte(resource)) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = enginecontext.AddResource(ctx, []byte(resource)) assert.NilError(t, err) diff --git a/pkg/engine/image_verify_test.go b/pkg/engine/image_verify_test.go index e69188f1e5..b6207b3275 100644 --- a/pkg/engine/image_verify_test.go +++ b/pkg/engine/image_verify_test.go @@ -15,6 +15,7 @@ import ( enginecontext "github.com/kyverno/kyverno/pkg/engine/context" "github.com/kyverno/kyverno/pkg/engine/context/resolvers" "github.com/kyverno/kyverno/pkg/engine/internal" + "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/engine/policycontext" "github.com/kyverno/kyverno/pkg/engine/utils" engineutils "github.com/kyverno/kyverno/pkg/engine/utils" @@ -164,6 +165,7 @@ var signaturePayloads = [][]byte{ var ( cfg = config.NewDefaultConfiguration(false) metricsCfg = config.NewDefaultMetricsConfiguration() + jp = jmespath.New(cfg) ) func testVerifyAndPatchImages( @@ -176,6 +178,7 @@ func testVerifyAndPatchImages( e := NewEngine( cfg, metricsCfg, + jp, nil, rclient, engineapi.DefaultContextLoaderFactory(cmResolver), @@ -219,7 +222,7 @@ func buildContext(t *testing.T, policy, resource string, oldResource string) *Po resourceUnstructured, err := kubeutils.BytesToUnstructured([]byte(resource)) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = enginecontext.AddResource(ctx, []byte(resource)) assert.NilError(t, err) diff --git a/pkg/engine/jmespath/arithmetic.go b/pkg/engine/jmespath/arithmetic.go index 804834c917..84d813c7af 100644 --- a/pkg/engine/jmespath/arithmetic.go +++ b/pkg/engine/jmespath/arithmetic.go @@ -10,7 +10,7 @@ import ( "k8s.io/apimachinery/pkg/api/resource" ) -type Operand interface { +type operand interface { Add(interface{}, string) (interface{}, error) Subtract(interface{}) (interface{}, error) Multiply(interface{}) (interface{}, error) @@ -18,30 +18,30 @@ type Operand interface { Modulo(interface{}) (interface{}, error) } -type Quantity struct { +type quantity struct { resource.Quantity } -type Duration struct { +type duration struct { time.Duration } -type Scalar struct { +type scalar struct { float64 } -func ParseArithemticOperands(arguments []interface{}, operator string) (Operand, Operand, error) { - op := [2]Operand{nil, nil} +func parseArithemticOperands(arguments []interface{}, operator string) (operand, operand, error) { + op := [2]operand{nil, nil} for i := 0; i < 2; i++ { if tmp, err := validateArg(divide, arguments, i, reflect.Float64); err == nil { - var sc Scalar + var sc scalar sc.float64 = tmp.Float() op[i] = sc } else if tmp, err = validateArg(divide, arguments, i, reflect.String); err == nil { if q, err := resource.ParseQuantity(tmp.String()); err == nil { - op[i] = Quantity{Quantity: q} + op[i] = quantity{Quantity: q} } else if d, err := time.ParseDuration(tmp.String()); err == nil { - op[i] = Duration{Duration: d} + op[i] = duration{Duration: d} } } } @@ -58,9 +58,9 @@ func ParseArithemticOperands(arguments []interface{}, operator string) (Operand, // Scalar +|- Scalar -> Scalar // Scalar +|- Quantity|Duration -> error -func (op1 Quantity) Add(op2 interface{}, operator string) (interface{}, error) { +func (op1 quantity) Add(op2 interface{}, operator string) (interface{}, error) { switch v := op2.(type) { - case Quantity: + case quantity: op1.Quantity.Add(v.Quantity) return op1.String(), nil default: @@ -68,27 +68,27 @@ func (op1 Quantity) Add(op2 interface{}, operator string) (interface{}, error) { } } -func (op1 Duration) Add(op2 interface{}, operator string) (interface{}, error) { +func (op1 duration) Add(op2 interface{}, operator string) (interface{}, error) { switch v := op2.(type) { - case Duration: + case duration: return (op1.Duration + v.Duration).String(), nil default: return nil, formatError(typeMismatchError, operator) } } -func (op1 Scalar) Add(op2 interface{}, operator string) (interface{}, error) { +func (op1 scalar) Add(op2 interface{}, operator string) (interface{}, error) { switch v := op2.(type) { - case Scalar: + case scalar: return op1.float64 + v.float64, nil default: return nil, formatError(typeMismatchError, operator) } } -func (op1 Quantity) Subtract(op2 interface{}) (interface{}, error) { +func (op1 quantity) Subtract(op2 interface{}) (interface{}, error) { switch v := op2.(type) { - case Quantity: + case quantity: op1.Quantity.Sub(v.Quantity) return op1.String(), nil default: @@ -96,18 +96,18 @@ func (op1 Quantity) Subtract(op2 interface{}) (interface{}, error) { } } -func (op1 Duration) Subtract(op2 interface{}) (interface{}, error) { +func (op1 duration) Subtract(op2 interface{}) (interface{}, error) { switch v := op2.(type) { - case Duration: + case duration: return (op1.Duration - v.Duration).String(), nil default: return nil, formatError(typeMismatchError, subtract) } } -func (op1 Scalar) Subtract(op2 interface{}) (interface{}, error) { +func (op1 scalar) Subtract(op2 interface{}) (interface{}, error) { switch v := op2.(type) { - case Scalar: + case scalar: return op1.float64 - v.float64, nil default: return nil, formatError(typeMismatchError, subtract) @@ -124,9 +124,9 @@ func (op1 Scalar) Subtract(op2 interface{}) (interface{}, error) { // Scalar * Quantity -> Quantity // Scalar * Duration -> Duration -func (op1 Quantity) Multiply(op2 interface{}) (interface{}, error) { +func (op1 quantity) Multiply(op2 interface{}) (interface{}, error) { switch v := op2.(type) { - case Scalar: + case scalar: q, err := resource.ParseQuantity(fmt.Sprintf("%v", v.float64)) if err != nil { return nil, err @@ -139,9 +139,9 @@ func (op1 Quantity) Multiply(op2 interface{}) (interface{}, error) { } } -func (op1 Duration) Multiply(op2 interface{}) (interface{}, error) { +func (op1 duration) Multiply(op2 interface{}) (interface{}, error) { switch v := op2.(type) { - case Scalar: + case scalar: seconds := op1.Seconds() * v.float64 return time.Duration(seconds * float64(time.Second)).String(), nil default: @@ -149,13 +149,13 @@ func (op1 Duration) Multiply(op2 interface{}) (interface{}, error) { } } -func (op1 Scalar) Multiply(op2 interface{}) (interface{}, error) { +func (op1 scalar) Multiply(op2 interface{}) (interface{}, error) { switch v := op2.(type) { - case Scalar: + case scalar: return op1.float64 * v.float64, nil - case Quantity: + case quantity: return v.Multiply(op1) - case Duration: + case duration: return v.Multiply(op1) default: return nil, formatError(typeMismatchError, multiply) @@ -174,16 +174,16 @@ func (op1 Scalar) Multiply(op2 interface{}) (interface{}, error) { // Scalar / Quantity -> error // Scalar / Duration -> error -func (op1 Quantity) Divide(op2 interface{}) (interface{}, error) { +func (op1 quantity) Divide(op2 interface{}) (interface{}, error) { switch v := op2.(type) { - case Quantity: + case quantity: divisor := v.AsApproximateFloat64() if divisor == 0 { return nil, formatError(zeroDivisionError, divide) } dividend := op1.AsApproximateFloat64() return dividend / divisor, nil - case Scalar: + case scalar: if v.float64 == 0 { return nil, formatError(zeroDivisionError, divide) } @@ -200,14 +200,14 @@ func (op1 Quantity) Divide(op2 interface{}) (interface{}, error) { } } -func (op1 Duration) Divide(op2 interface{}) (interface{}, error) { +func (op1 duration) Divide(op2 interface{}) (interface{}, error) { switch v := op2.(type) { - case Duration: + case duration: if v.Seconds() == 0 { return nil, formatError(zeroDivisionError, divide) } return op1.Seconds() / v.Seconds(), nil - case Scalar: + case scalar: if v.float64 == 0 { return nil, formatError(zeroDivisionError, divide) } @@ -218,9 +218,9 @@ func (op1 Duration) Divide(op2 interface{}) (interface{}, error) { } } -func (op1 Scalar) Divide(op2 interface{}) (interface{}, error) { +func (op1 scalar) Divide(op2 interface{}) (interface{}, error) { switch v := op2.(type) { - case Scalar: + case scalar: if v.float64 == 0 { return nil, formatError(zeroDivisionError, divide) } @@ -239,9 +239,9 @@ func (op1 Scalar) Divide(op2 interface{}) (interface{}, error) { // Scalar % Quantity|Duration -> error // Scalar % Scalar -> Scalar -func (op1 Quantity) Modulo(op2 interface{}) (interface{}, error) { +func (op1 quantity) Modulo(op2 interface{}) (interface{}, error) { switch v := op2.(type) { - case Quantity: + case quantity: f1 := op1.ToDec().AsApproximateFloat64() f2 := v.ToDec().AsApproximateFloat64() i1 := int64(f1) @@ -261,9 +261,9 @@ func (op1 Quantity) Modulo(op2 interface{}) (interface{}, error) { } } -func (op1 Duration) Modulo(op2 interface{}) (interface{}, error) { +func (op1 duration) Modulo(op2 interface{}) (interface{}, error) { switch v := op2.(type) { - case Duration: + case duration: if v.Duration == 0 { return nil, formatError(zeroDivisionError, modulo) } @@ -273,9 +273,9 @@ func (op1 Duration) Modulo(op2 interface{}) (interface{}, error) { } } -func (op1 Scalar) Modulo(op2 interface{}) (interface{}, error) { +func (op1 scalar) Modulo(op2 interface{}) (interface{}, error) { switch v := op2.(type) { - case Scalar: + case scalar: val1 := int64(op1.float64) val2 := int64(v.float64) if op1.float64 != float64(val1) { diff --git a/pkg/engine/jmespath/arithmetic_test.go b/pkg/engine/jmespath/arithmetic_test.go index 03d76a4c3a..7bdabf9f50 100644 --- a/pkg/engine/jmespath/arithmetic_test.go +++ b/pkg/engine/jmespath/arithmetic_test.go @@ -82,7 +82,7 @@ func Test_Add(t *testing.T) { } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - jp, err := New(tc.test) + jp, err := newJMESPath(tc.test) assert.NilError(t, err) result, err := jp.Search("") @@ -228,7 +228,7 @@ func Test_Sum(t *testing.T) { } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - jp, err := New(tc.test) + jp, err := newJMESPath(tc.test) assert.NilError(t, err) result, err := jp.Search("") @@ -327,7 +327,7 @@ func Test_Subtract(t *testing.T) { } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - jp, err := New(tc.test) + jp, err := newJMESPath(tc.test) assert.NilError(t, err) result, err := jp.Search("") @@ -426,7 +426,7 @@ func Test_Multiply(t *testing.T) { } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - jp, err := New(tc.test) + jp, err := newJMESPath(tc.test) assert.NilError(t, err) result, err := jp.Search("") @@ -588,7 +588,7 @@ func Test_Divide(t *testing.T) { } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - jp, err := New(tc.test) + jp, err := newJMESPath(tc.test) assert.NilError(t, err) result, err := jp.Search("") @@ -744,7 +744,7 @@ func Test_Modulo(t *testing.T) { } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - jp, err := New(tc.test) + jp, err := newJMESPath(tc.test) assert.NilError(t, err) result, err := jp.Search("") @@ -792,7 +792,7 @@ func TestScalar_Multiply(t *testing.T) { }} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - op1 := Scalar{ + op1 := scalar{ float64: tt.fields.float64, } got, err := op1.Multiply(tt.args.op2) @@ -815,8 +815,8 @@ func TestParseArithemticOperands(t *testing.T) { tests := []struct { name string args args - want Operand - want1 Operand + want operand + want1 operand wantErr bool }{{ args: args{ @@ -837,7 +837,7 @@ func TestParseArithemticOperands(t *testing.T) { }} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, got1, err := ParseArithemticOperands(tt.args.arguments, tt.args.operator) + got, got1, err := parseArithemticOperands(tt.args.arguments, tt.args.operator) if (err != nil) != tt.wantErr { t.Errorf("ParseArithemticOperands() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/pkg/engine/jmespath/functions.go b/pkg/engine/jmespath/functions.go index 7762c14ec8..6210e78294 100644 --- a/pkg/engine/jmespath/functions.go +++ b/pkg/engine/jmespath/functions.go @@ -773,7 +773,7 @@ func jpToBoolean(arguments []interface{}) (interface{}, error) { } func _jpAdd(arguments []interface{}, operator string) (interface{}, error) { - op1, op2, err := ParseArithemticOperands(arguments, operator) + op1, op2, err := parseArithemticOperands(arguments, operator) if err != nil { return nil, err } @@ -804,7 +804,7 @@ func jpSum(arguments []interface{}) (interface{}, error) { } func jpSubtract(arguments []interface{}) (interface{}, error) { - op1, op2, err := ParseArithemticOperands(arguments, subtract) + op1, op2, err := parseArithemticOperands(arguments, subtract) if err != nil { return nil, err } @@ -813,7 +813,7 @@ func jpSubtract(arguments []interface{}) (interface{}, error) { } func jpMultiply(arguments []interface{}) (interface{}, error) { - op1, op2, err := ParseArithemticOperands(arguments, multiply) + op1, op2, err := parseArithemticOperands(arguments, multiply) if err != nil { return nil, err } @@ -822,7 +822,7 @@ func jpMultiply(arguments []interface{}) (interface{}, error) { } func jpDivide(arguments []interface{}) (interface{}, error) { - op1, op2, err := ParseArithemticOperands(arguments, divide) + op1, op2, err := parseArithemticOperands(arguments, divide) if err != nil { return nil, err } @@ -831,7 +831,7 @@ func jpDivide(arguments []interface{}) (interface{}, error) { } func jpModulo(arguments []interface{}) (interface{}, error) { - op1, op2, err := ParseArithemticOperands(arguments, modulo) + op1, op2, err := parseArithemticOperands(arguments, modulo) if err != nil { return nil, err } diff --git a/pkg/engine/jmespath/functions_test.go b/pkg/engine/jmespath/functions_test.go index 5c03f966e5..1ef75f2cd2 100644 --- a/pkg/engine/jmespath/functions_test.go +++ b/pkg/engine/jmespath/functions_test.go @@ -30,7 +30,7 @@ func Test_Compare(t *testing.T) { } for _, tc := range testCases { t.Run(tc.jmesPath, func(t *testing.T) { - jp, err := New(tc.jmesPath) + jp, err := newJMESPath(tc.jmesPath) assert.NilError(t, err) result, err := jp.Search("") @@ -57,7 +57,7 @@ func Test_ParseJsonSerde(t *testing.T) { } for _, tc := range testCases { t.Run(tc, func(t *testing.T) { - jp, err := New(fmt.Sprintf(`to_string(parse_json('%s'))`, tc)) + jp, err := newJMESPath(fmt.Sprintf(`to_string(parse_json('%s'))`, tc)) assert.NilError(t, err) result, err := jp.Search("") @@ -88,7 +88,7 @@ func Test_ParseJsonComplex(t *testing.T) { } for _, tc := range testCases { t.Run(tc.input, func(t *testing.T) { - jp, err := New(tc.input) + jp, err := newJMESPath(tc.input) assert.NilError(t, err) result, err := jp.Search("") @@ -165,7 +165,7 @@ bar: null } for _, tc := range testCases { t.Run(tc.input, func(t *testing.T) { - jp, err := New(fmt.Sprintf(`parse_yaml('%s')`, tc.input)) + jp, err := newJMESPath(fmt.Sprintf(`parse_yaml('%s')`, tc.input)) assert.NilError(t, err) result, err := jp.Search("") assert.NilError(t, err) @@ -194,7 +194,7 @@ func Test_EqualFold(t *testing.T) { } for _, tc := range testCases { t.Run(tc.jmesPath, func(t *testing.T) { - jp, err := New(tc.jmesPath) + jp, err := newJMESPath(tc.jmesPath) assert.NilError(t, err) result, err := jp.Search("") @@ -231,7 +231,7 @@ func Test_Replace(t *testing.T) { } for _, tc := range testCases { t.Run(tc.jmesPath, func(t *testing.T) { - jp, err := New(tc.jmesPath) + jp, err := newJMESPath(tc.jmesPath) assert.NilError(t, err) result, err := jp.Search("") @@ -245,7 +245,7 @@ func Test_Replace(t *testing.T) { } func Test_ReplaceAll(t *testing.T) { - jp, err := New("replace_all('Lorem ipsum dolor sit amet', 'ipsum', 'muspi')") + jp, err := newJMESPath("replace_all('Lorem ipsum dolor sit amet', 'ipsum', 'muspi')") assert.NilError(t, err) result, err := jp.Search("") @@ -276,7 +276,7 @@ func Test_ToUpper(t *testing.T) { } for _, tc := range testCases { t.Run(tc.jmesPath, func(t *testing.T) { - jp, err := New(tc.jmesPath) + jp, err := newJMESPath(tc.jmesPath) assert.NilError(t, err) result, err := jp.Search("") @@ -309,7 +309,7 @@ func Test_ToLower(t *testing.T) { } for _, tc := range testCases { t.Run(tc.jmesPath, func(t *testing.T) { - jp, err := New(tc.jmesPath) + jp, err := newJMESPath(tc.jmesPath) assert.NilError(t, err) result, err := jp.Search("") @@ -323,7 +323,7 @@ func Test_ToLower(t *testing.T) { } func Test_Trim(t *testing.T) { - jp, err := New("trim('¡¡¡Hello, Gophers!!!', '!¡')") + jp, err := newJMESPath("trim('¡¡¡Hello, Gophers!!!', '!¡')") assert.NilError(t, err) result, err := jp.Search("") @@ -394,7 +394,7 @@ func Test_TrimPrefix(t *testing.T) { } func Test_Split(t *testing.T) { - jp, err := New("split('Hello, Gophers', ', ')") + jp, err := newJMESPath("split('Hello, Gophers', ', ')") assert.NilError(t, err) result, err := jp.Search("") @@ -407,7 +407,7 @@ func Test_Split(t *testing.T) { } func Test_HasPrefix(t *testing.T) { - jp, err := New("starts_with('Gophers', 'Go')") + jp, err := newJMESPath("starts_with('Gophers', 'Go')") assert.NilError(t, err) result, err := jp.Search("") @@ -419,7 +419,7 @@ func Test_HasPrefix(t *testing.T) { } func Test_HasSuffix(t *testing.T) { - jp, err := New("ends_with('Amigo', 'go')") + jp, err := newJMESPath("ends_with('Amigo', 'go')") assert.NilError(t, err) result, err := jp.Search("") @@ -434,7 +434,7 @@ func Test_RegexMatch(t *testing.T) { data := make(map[string]interface{}) data["foo"] = "hgf'b1a2r'b12g" - query, err := New("regex_match('12.*', foo)") + query, err := newJMESPath("regex_match('12.*', foo)") assert.NilError(t, err) result, err := query.Search(data) @@ -446,7 +446,7 @@ func Test_RegexMatchWithNumber(t *testing.T) { data := make(map[string]interface{}) data["foo"] = -12.0 - query, err := New("regex_match('12.*', abs(foo))") + query, err := newJMESPath("regex_match('12.*', abs(foo))") assert.NilError(t, err) result, err := query.Search(data) @@ -458,7 +458,7 @@ func Test_PatternMatch(t *testing.T) { data := make(map[string]interface{}) data["foo"] = "prefix-foo" - query, err := New("pattern_match('prefix-*', foo)") + query, err := newJMESPath("pattern_match('prefix-*', foo)") assert.NilError(t, err) result, err := query.Search(data) @@ -470,7 +470,7 @@ func Test_PatternMatchWithNumber(t *testing.T) { data := make(map[string]interface{}) data["foo"] = -12.0 - query, err := New("pattern_match('12*', abs(foo))") + query, err := newJMESPath("pattern_match('12*', abs(foo))") assert.NilError(t, err) result, err := query.Search(data) @@ -497,7 +497,7 @@ func Test_RegexReplaceAll(t *testing.T) { var resource interface{} err := json.Unmarshal(resourceRaw, &resource) assert.NilError(t, err) - query, err := New(`regex_replace_all('([Hh]e|G)l', spec.field, '${2}G')`) + query, err := newJMESPath(`regex_replace_all('([Hh]e|G)l', spec.field, '${2}G')`) assert.NilError(t, err) res, err := query.Search(resource) @@ -528,7 +528,7 @@ func Test_RegexReplaceAllLiteral(t *testing.T) { err := json.Unmarshal(resourceRaw, &resource) assert.NilError(t, err) - query, err := New(`regex_replace_all_literal('[Hh]el?', spec.field, 'G')`) + query, err := newJMESPath(`regex_replace_all_literal('[Hh]el?', spec.field, 'G')`) assert.NilError(t, err) res, err := query.Search(resource) @@ -583,7 +583,7 @@ func Test_LabelMatch(t *testing.T) { err := json.Unmarshal(tc.resource, &resource) assert.NilError(t, err) - query, err := New("label_match(`" + tc.test + "`, metadata.labels)") + query, err := newJMESPath("label_match(`" + tc.test + "`, metadata.labels)") assert.NilError(t, err) res, err := query.Search(resource) @@ -627,7 +627,7 @@ func Test_JpToBoolean(t *testing.T) { } func Test_Base64Decode(t *testing.T) { - jp, err := New("base64_decode('SGVsbG8sIHdvcmxkIQ==')") + jp, err := newJMESPath("base64_decode('SGVsbG8sIHdvcmxkIQ==')") assert.NilError(t, err) result, err := jp.Search("") @@ -639,7 +639,7 @@ func Test_Base64Decode(t *testing.T) { } func Test_Base64Encode(t *testing.T) { - jp, err := New("base64_encode('Hello, world!')") + jp, err := newJMESPath("base64_encode('Hello, world!')") assert.NilError(t, err) result, err := jp.Search("") @@ -669,7 +669,7 @@ func Test_Base64Decode_Secret(t *testing.T) { err := json.Unmarshal(resourceRaw, &resource) assert.NilError(t, err) - query, err := New(`base64_decode(data.example1)`) + query, err := newJMESPath(`base64_decode(data.example1)`) assert.NilError(t, err) res, err := query.Search(resource) @@ -744,7 +744,7 @@ func Test_PathCanonicalize(t *testing.T) { } for _, tc := range testCases { t.Run(tc.jmesPath, func(t *testing.T) { - jp, err := New(tc.jmesPath) + jp, err := newJMESPath(tc.jmesPath) assert.NilError(t, err) result, err := jp.Search("") @@ -793,7 +793,7 @@ func Test_Truncate(t *testing.T) { } for _, tc := range testCases { t.Run(tc.jmesPath, func(t *testing.T) { - jp, err := New(tc.jmesPath) + jp, err := newJMESPath(tc.jmesPath) assert.NilError(t, err) result, err := jp.Search("") @@ -830,7 +830,7 @@ func Test_SemverCompare(t *testing.T) { } for _, tc := range testCases { t.Run(tc.jmesPath, func(t *testing.T) { - jp, err := New(tc.jmesPath) + jp, err := newJMESPath(tc.jmesPath) assert.NilError(t, err) result, err := jp.Search("") @@ -877,7 +877,7 @@ func Test_Items(t *testing.T) { } for i, tc := range testCases { t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { - query, err := New("items(`" + tc.object + "`,`" + tc.keyName + "`,`" + tc.valName + "`)") + query, err := newJMESPath("items(`" + tc.object + "`,`" + tc.keyName + "`,`" + tc.valName + "`)") assert.NilError(t, err) res, err := query.Search("") @@ -928,7 +928,7 @@ func Test_ObjectFromLists(t *testing.T) { } for i, tc := range testCases { t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { - query, err := New("object_from_lists(`" + tc.keys + "`,`" + tc.values + "`)") + query, err := newJMESPath("object_from_lists(`" + tc.keys + "`,`" + tc.values + "`)") assert.NilError(t, err) res, err := query.Search("") assert.NilError(t, err) @@ -1022,7 +1022,7 @@ UFOZZVoELaasWS559wy8og39Eq21dDMynb8Bndn/ }} for _, tc := range testCases { t.Run(tc.jmesPath, func(t *testing.T) { - jp, err := New(tc.jmesPath) + jp, err := newJMESPath(tc.jmesPath) assert.NilError(t, err) result, err := jp.Search("") diff --git a/pkg/engine/jmespath/interface.go b/pkg/engine/jmespath/interface.go new file mode 100644 index 0000000000..bcd41f11a9 --- /dev/null +++ b/pkg/engine/jmespath/interface.go @@ -0,0 +1,34 @@ +package jmespath + +import "github.com/kyverno/kyverno/pkg/config" + +type Query interface { + Search(interface{}) (interface{}, error) +} + +type Interface interface { + Query(string) (Query, error) + Search(string, interface{}) (interface{}, error) +} + +type implementation struct { + configuration config.Configuration +} + +func New(configuration config.Configuration) Interface { + return implementation{ + configuration: configuration, + } +} + +func (i implementation) Query(query string) (Query, error) { + return newJMESPath(query) +} + +func (i implementation) Search(query string, data interface{}) (interface{}, error) { + if query, err := i.Query(query); err != nil { + return nil, err + } else { + return query.Search(data) + } +} diff --git a/pkg/engine/jmespath/new.go b/pkg/engine/jmespath/new.go index 7a19a7f41a..edcc07a900 100644 --- a/pkg/engine/jmespath/new.go +++ b/pkg/engine/jmespath/new.go @@ -4,7 +4,7 @@ import ( gojmespath "github.com/jmespath/go-jmespath" ) -func New(query string) (*gojmespath.JMESPath, error) { +func newJMESPath(query string) (*gojmespath.JMESPath, error) { jp, err := gojmespath.Compile(query) if err != nil { return nil, err diff --git a/pkg/engine/jmespath/new_test.go b/pkg/engine/jmespath/new_test.go index 7f66429c4d..76ba21e606 100644 --- a/pkg/engine/jmespath/new_test.go +++ b/pkg/engine/jmespath/new_test.go @@ -25,7 +25,7 @@ func TestNew(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - _, err := New(tt.args.query) + _, err := newJMESPath(tt.args.query) if (err != nil) != tt.wantErr { t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/pkg/engine/jmespath/time_test.go b/pkg/engine/jmespath/time_test.go index 314d7e4276..7debd0ceb4 100644 --- a/pkg/engine/jmespath/time_test.go +++ b/pkg/engine/jmespath/time_test.go @@ -25,7 +25,7 @@ func Test_TimeSince(t *testing.T) { } for i, tc := range testCases { t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { - query, err := New(tc.test) + query, err := newJMESPath(tc.test) assert.NilError(t, err) res, err := query.Search("") @@ -55,7 +55,7 @@ func Test_TimeToCron(t *testing.T) { } for i, tc := range testCases { t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { - query, err := New(tc.test) + query, err := newJMESPath(tc.test) assert.NilError(t, err) res, err := query.Search("") @@ -85,7 +85,7 @@ func Test_TimeAdd(t *testing.T) { } for i, tc := range testCases { t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { - query, err := New(tc.test) + query, err := newJMESPath(tc.test) assert.NilError(t, err) res, err := query.Search("") @@ -115,7 +115,7 @@ func Test_TimeParse(t *testing.T) { } for i, tc := range testCases { t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { - query, err := New(tc.test) + query, err := newJMESPath(tc.test) assert.NilError(t, err) res, err := query.Search("") @@ -145,7 +145,7 @@ func Test_TimeUtc(t *testing.T) { } for i, tc := range testCases { t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { - query, err := New(tc.test) + query, err := newJMESPath(tc.test) assert.NilError(t, err) res, err := query.Search("") @@ -171,7 +171,7 @@ func Test_TimeDiff(t *testing.T) { } for i, tc := range testCases { t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { - query, err := New(tc.test) + query, err := newJMESPath(tc.test) assert.NilError(t, err) res, err := query.Search("") diff --git a/pkg/engine/mutate/mutation_test.go b/pkg/engine/mutate/mutation_test.go index 280e0b28a8..945d4d7a00 100644 --- a/pkg/engine/mutate/mutation_test.go +++ b/pkg/engine/mutate/mutation_test.go @@ -6,8 +6,10 @@ import ( "github.com/go-logr/logr" types "github.com/kyverno/kyverno/api/kyverno/v1" + "github.com/kyverno/kyverno/pkg/config" engineapi "github.com/kyverno/kyverno/pkg/engine/api" "github.com/kyverno/kyverno/pkg/engine/context" + "github.com/kyverno/kyverno/pkg/engine/jmespath" kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" "gotest.tools/assert" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" @@ -47,7 +49,7 @@ const endpointsDocument string = `{ }` func applyPatches(rule *types.Rule, resource unstructured.Unstructured) (*engineapi.RuleResponse, unstructured.Unstructured) { - mutateResp := Mutate(rule, context.NewContext(), resource, logr.Discard()) + mutateResp := Mutate(rule, context.NewContext(jmespath.New(config.NewDefaultConfiguration(false))), resource, logr.Discard()) if mutateResp.Status != engineapi.RuleStatusPass { return engineapi.NewRuleResponse("", engineapi.Mutation, mutateResp.Message, mutateResp.Status), resource } diff --git a/pkg/engine/mutation_test.go b/pkg/engine/mutation_test.go index ec6e2ca6ab..0967b6f3ce 100644 --- a/pkg/engine/mutation_test.go +++ b/pkg/engine/mutation_test.go @@ -34,6 +34,7 @@ func testMutate( e := NewEngine( cfg, config.NewDefaultMetricsConfiguration(), + jp, client, rclient, contextLoader, @@ -104,7 +105,7 @@ func Test_VariableSubstitutionPatchStrategicMerge(t *testing.T) { } resourceUnstructured, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = enginecontext.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) @@ -185,7 +186,7 @@ func Test_variableSubstitutionPathNotExist(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = enginecontext.AddResource(ctx, resourceRaw) assert.NilError(t, err) @@ -262,7 +263,7 @@ func Test_variableSubstitutionCLI(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = enginecontext.AddResource(ctx, resourceRaw) assert.NilError(t, err) @@ -382,7 +383,7 @@ func Test_chained_rules(t *testing.T) { resource, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = ctx.AddResource(resource.Object) assert.NilError(t, err) @@ -470,7 +471,7 @@ func Test_precondition(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = enginecontext.AddResource(ctx, resourceRaw) assert.NilError(t, err) @@ -564,7 +565,7 @@ func Test_nonZeroIndexNumberPatchesJson6902(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = enginecontext.AddResource(ctx, resourceRaw) assert.NilError(t, err) @@ -650,7 +651,7 @@ func Test_foreach(t *testing.T) { resource, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = ctx.AddResource(resource.Object) assert.NilError(t, err) @@ -752,7 +753,7 @@ func Test_foreach_element_mutation(t *testing.T) { resource, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = ctx.AddResource(resource.Object) assert.NilError(t, err) @@ -873,7 +874,7 @@ func Test_Container_InitContainer_foreach(t *testing.T) { resource, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = ctx.AddResource(resource.Object) assert.NilError(t, err) @@ -1018,7 +1019,7 @@ func testApplyPolicyToResource(t *testing.T, policyRaw, resourceRaw []byte) engi resource, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = ctx.AddResource(resource.Object) assert.NilError(t, err) @@ -1556,7 +1557,7 @@ func Test_mutate_existing_resources(t *testing.T) { target, err := kubeutils.BytesToUnstructured(target) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = ctx.AddResource(trigger.Object) assert.NilError(t, err) @@ -1671,7 +1672,7 @@ func Test_RuleSelectorMutate(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = enginecontext.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) @@ -2052,7 +2053,7 @@ func Test_SpecialCharacters(t *testing.T) { } // Create JSON context and add the resource. - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = ctx.AddResource(resource.Object) if err != nil { t.Fatalf("ctx.AddResource() error = %v", err) diff --git a/pkg/engine/policycontext/policy_context.go b/pkg/engine/policycontext/policy_context.go index a3df15c88e..4e6e8a194c 100644 --- a/pkg/engine/policycontext/policy_context.go +++ b/pkg/engine/policycontext/policy_context.go @@ -8,6 +8,7 @@ import ( "github.com/kyverno/kyverno/pkg/config" engineapi "github.com/kyverno/kyverno/pkg/engine/api" enginectx "github.com/kyverno/kyverno/pkg/engine/context" + "github.com/kyverno/kyverno/pkg/engine/jmespath" admissionutils "github.com/kyverno/kyverno/pkg/utils/admission" admissionv1 "k8s.io/api/admission/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -184,17 +185,18 @@ func NewPolicyContextWithJsonContext(operation kyvernov1.AdmissionOperation, jso } } -func NewPolicyContext(operation kyvernov1.AdmissionOperation) *PolicyContext { - return NewPolicyContextWithJsonContext(operation, enginectx.NewContext()) +func NewPolicyContext(jp jmespath.Interface, operation kyvernov1.AdmissionOperation) *PolicyContext { + return NewPolicyContextWithJsonContext(operation, enginectx.NewContext(jp)) } func NewPolicyContextFromAdmissionRequest( + jp jmespath.Interface, request admissionv1.AdmissionRequest, admissionInfo kyvernov1beta1.RequestInfo, gvk schema.GroupVersionKind, configuration config.Configuration, ) (*PolicyContext, error) { - ctx, err := newVariablesContext(request, &admissionInfo) + ctx, err := newVariablesContext(jp, request, &admissionInfo) if err != nil { return nil, fmt.Errorf("failed to create policy rule context: %w", err) } @@ -215,8 +217,12 @@ func NewPolicyContextFromAdmissionRequest( return policyContext, nil } -func newVariablesContext(request admissionv1.AdmissionRequest, userRequestInfo *kyvernov1beta1.RequestInfo) (enginectx.Interface, error) { - ctx := enginectx.NewContext() +func newVariablesContext( + jp jmespath.Interface, + request admissionv1.AdmissionRequest, + userRequestInfo *kyvernov1beta1.RequestInfo, +) (enginectx.Interface, error) { + ctx := enginectx.NewContext(jp) if err := ctx.AddRequest(request); err != nil { return nil, fmt.Errorf("failed to load incoming request in context: %w", err) } diff --git a/pkg/engine/test/contextloader.go b/pkg/engine/test/contextloader.go index 36392ba408..0e4f90a8c0 100644 --- a/pkg/engine/test/contextloader.go +++ b/pkg/engine/test/contextloader.go @@ -8,6 +8,7 @@ import ( "github.com/kyverno/kyverno/pkg/clients/dclient" engineapi "github.com/kyverno/kyverno/pkg/engine/api" enginecontext "github.com/kyverno/kyverno/pkg/engine/context" + "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/logging" "github.com/kyverno/kyverno/pkg/registryclient" ) @@ -44,6 +45,7 @@ type mockContextLoader struct { func (l *mockContextLoader) Load( ctx context.Context, + jp jmespath.Interface, client dclient.Interface, rclient registryclient.Client, contextEntries []kyvernov1.ContextEntry, @@ -63,15 +65,15 @@ func (l *mockContextLoader) Load( // Context Variable should be loaded after the values loaded from values file for _, entry := range contextEntries { if entry.ImageRegistry != nil && rclient != nil { - if err := engineapi.LoadImageData(ctx, rclient, l.logger, entry, jsonContext); err != nil { + if err := engineapi.LoadImageData(ctx, jp, rclient, l.logger, entry, jsonContext); err != nil { return err } } else if entry.Variable != nil { - if err := engineapi.LoadVariable(l.logger, entry, jsonContext); err != nil { + if err := engineapi.LoadVariable(l.logger, jp, entry, jsonContext); err != nil { return err } } else if entry.APICall != nil && l.allowApiCall { - if err := engineapi.LoadAPIData(ctx, l.logger, entry, jsonContext, client); err != nil { + if err := engineapi.LoadAPIData(ctx, jp, l.logger, entry, jsonContext, client); err != nil { return err } } diff --git a/pkg/engine/validation_test.go b/pkg/engine/validation_test.go index 5f3e18bf57..096f0afbbe 100644 --- a/pkg/engine/validation_test.go +++ b/pkg/engine/validation_test.go @@ -32,6 +32,7 @@ func testValidate( e := NewEngine( cfg, config.NewDefaultMetricsConfiguration(), + jp, nil, rclient, contextLoader, @@ -136,7 +137,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 := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext()).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) + er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext(jp)).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) for index, r := range er.PolicyResponse.Rules { assert.Equal(t, r.Message(), msgs[index]) } @@ -236,7 +237,7 @@ func TestValidate_image_tag_pass(t *testing.T) { "validation rule 'validate-tag' passed.", "validation rule 'validate-latest' passed.", } - er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext()).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) + er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext(jp)).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) for index, r := range er.PolicyResponse.Rules { assert.Equal(t, r.Message(), msgs[index]) } @@ -310,7 +311,7 @@ func TestValidate_Fail_anyPattern(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext()).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) + er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext(jp)).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) 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/"} @@ -393,7 +394,7 @@ func TestValidate_host_network_port(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext()).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) + er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext(jp)).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) 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 { @@ -483,7 +484,7 @@ func TestValidate_anchor_arraymap_pass(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext()).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) + er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext(jp)).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) msgs := []string{"validation rule 'validate-host-path' passed."} for index, r := range er.PolicyResponse.Rules { @@ -571,7 +572,7 @@ func TestValidate_anchor_arraymap_fail(t *testing.T) { assert.NilError(t, err) resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext()).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) + er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext(jp)).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) 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 { @@ -641,7 +642,7 @@ func TestValidate_anchor_map_notfound(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext()).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) + er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext(jp)).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) msgs := []string{"validation rule 'pod rule 2' passed."} for index, r := range er.PolicyResponse.Rules { @@ -714,7 +715,7 @@ func TestValidate_anchor_map_found_valid(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext()).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) + er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext(jp)).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) msgs := []string{"validation rule 'pod rule 2' passed."} for index, r := range er.PolicyResponse.Rules { @@ -788,7 +789,7 @@ func TestValidate_inequality_List_Processing(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext()).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) + er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext(jp)).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) msgs := []string{"validation rule 'pod rule 2' passed."} for index, r := range er.PolicyResponse.Rules { @@ -868,7 +869,7 @@ func TestValidate_inequality_List_ProcessingBrackets(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext()).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) + er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext(jp)).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) msgs := []string{"validation rule 'pod rule 2' passed."} for index, r := range er.PolicyResponse.Rules { @@ -942,7 +943,7 @@ func TestValidate_anchor_map_found_invalid(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext()).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) + er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext(jp)).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) 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 { @@ -1017,7 +1018,7 @@ func TestValidate_AnchorList_pass(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext()).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) + er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext(jp)).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) msgs := []string{"validation rule 'pod image rule' passed."} for index, r := range er.PolicyResponse.Rules { @@ -1092,7 +1093,7 @@ func TestValidate_AnchorList_fail(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext()).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) + er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext(jp)).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) assert.Assert(t, !er.IsSuccessful()) } @@ -1162,7 +1163,7 @@ func TestValidate_existenceAnchor_fail(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext()).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) + er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext(jp)).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) assert.Assert(t, !er.IsSuccessful()) } @@ -1232,7 +1233,7 @@ func TestValidate_existenceAnchor_pass(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext()).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) + er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext(jp)).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) msgs := []string{"validation rule 'pod image rule' passed."} for index, r := range er.PolicyResponse.Rules { @@ -1320,7 +1321,7 @@ func TestValidate_negationAnchor_deny(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext()).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) + er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext(jp)).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) 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 { @@ -1407,7 +1408,7 @@ func TestValidate_negationAnchor_pass(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext()).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) + er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext(jp)).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) msgs := []string{"validation rule 'validate-host-path' passed."} for index, r := range er.PolicyResponse.Rules { @@ -1475,7 +1476,7 @@ func Test_VariableSubstitutionPathNotExistInPattern(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = enginecontext.AddResource(ctx, resourceRaw) assert.NilError(t, err) @@ -1565,7 +1566,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_OnePatternStatisfiesButSu resourceUnstructured, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = enginecontext.AddResource(ctx, resourceRaw) assert.NilError(t, err) @@ -1623,7 +1624,7 @@ func Test_VariableSubstitution_NotOperatorWithStringVariable(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = enginecontext.AddResource(ctx, resourceRaw) assert.NilError(t, err) @@ -1711,7 +1712,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathNotPresent(t *test resourceUnstructured, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = enginecontext.AddResource(ctx, resourceRaw) assert.NilError(t, err) @@ -1801,7 +1802,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathPresent_NonePatter resourceUnstructured, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = enginecontext.AddResource(ctx, resourceRaw) assert.NilError(t, err) @@ -1903,7 +1904,7 @@ func Test_VariableSubstitutionValidate_VariablesInMessageAreResolved(t *testing. resourceUnstructured, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = enginecontext.AddResource(ctx, resourceRaw) assert.NilError(t, err) @@ -1953,7 +1954,7 @@ func Test_Flux_Kustomization_PathNotPresent(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(test.resourceRaw) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = enginecontext.AddResource(ctx, test.resourceRaw) assert.NilError(t, err) @@ -2095,7 +2096,7 @@ func executeTest(t *testing.T, test testCase) { t.Fatal(err) } - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = ctx.AddRequest(request) if err != nil { t.Fatal(err) @@ -2205,7 +2206,7 @@ func TestValidate_context_variable_substitution_CLI(t *testing.T) { er := testValidate( context.TODO(), registryclient.NewOrDie(), - NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext()).WithPolicy(&policy).WithNewResource(*resourceUnstructured), + NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext(jp)).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, enginetest.ContextLoaderFactory( nil, @@ -2303,7 +2304,7 @@ func Test_EmptyStringInDenyCondition(t *testing.T) { err := json.Unmarshal(policyRaw, &policy) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = enginecontext.AddResource(ctx, resourceRaw) assert.NilError(t, err) @@ -2396,7 +2397,7 @@ func Test_StringInDenyCondition(t *testing.T) { err := json.Unmarshal(policyRaw, &policy) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = enginecontext.AddResource(ctx, resourceRaw) assert.NilError(t, err) @@ -3079,7 +3080,7 @@ func testForEach(t *testing.T, policyraw []byte, resourceRaw []byte, msg string, resourceUnstructured, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = enginecontext.AddResource(ctx, resourceRaw) assert.NilError(t, err) @@ -3141,7 +3142,7 @@ func Test_delete_ignore_pattern(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - ctx := enginecontext.NewContext() + ctx := enginecontext.NewContext(jp) err = enginecontext.AddResource(ctx, resourceRaw) assert.NilError(t, err) @@ -3216,7 +3217,7 @@ func Test_ValidatePattern_anyPattern(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(tc.rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext()).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) + er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext(jp)).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) if tc.expectedFailed { assert.Assert(t, er.IsFailed()) } else if tc.expectedSkipped { diff --git a/pkg/engine/variables/evaluate_test.go b/pkg/engine/variables/evaluate_test.go index 13b20c0202..de0acaefba 100644 --- a/pkg/engine/variables/evaluate_test.go +++ b/pkg/engine/variables/evaluate_test.go @@ -6,7 +6,9 @@ import ( "github.com/go-logr/logr" kyverno "github.com/kyverno/kyverno/api/kyverno/v1" + "github.com/kyverno/kyverno/pkg/config" "github.com/kyverno/kyverno/pkg/engine/context" + "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/stretchr/testify/assert" ) @@ -376,7 +378,7 @@ func TestEvaluate(t *testing.T) { {kyverno.Condition{RawKey: kyverno.ToJSON([]interface{}{1, 5, 7}), Operator: kyverno.ConditionOperators["AnyNotIn"], RawValue: kyverno.ToJSON("0-10")}, false}, } - ctx := context.NewContext() + ctx := context.NewContext(jmespath.New(config.NewDefaultConfiguration(false))) for _, tc := range testCases { if Evaluate(logr.Discard(), ctx, tc.Condition) != tc.Result { t.Errorf("%v - expected result to be %v", tc.Condition, tc.Result) @@ -401,7 +403,7 @@ func Test_Eval_Equal_Var_Pass(t *testing.T) { `) // context - ctx := context.NewContext() + ctx := context.NewContext(jmespath.New(config.NewDefaultConfiguration(false))) err := context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) @@ -443,7 +445,7 @@ func Test_Eval_Equal_Var_Fail(t *testing.T) { `) // context - ctx := context.NewContext() + ctx := context.NewContext(jmespath.New(config.NewDefaultConfiguration(false))) err := context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) diff --git a/pkg/engine/variables/variables_test.go b/pkg/engine/variables/variables_test.go index 7ca1fdd877..a59295c3d0 100644 --- a/pkg/engine/variables/variables_test.go +++ b/pkg/engine/variables/variables_test.go @@ -7,11 +7,15 @@ import ( "github.com/go-logr/logr" urkyverno "github.com/kyverno/kyverno/api/kyverno/v1beta1" + "github.com/kyverno/kyverno/pkg/config" "github.com/kyverno/kyverno/pkg/engine/context" + "github.com/kyverno/kyverno/pkg/engine/jmespath" "gotest.tools/assert" authenticationv1 "k8s.io/api/authentication/v1" ) +var jp = jmespath.New(config.NewDefaultConfiguration(false)) + func Test_variablesub1(t *testing.T) { patternMap := []byte(` { @@ -74,7 +78,7 @@ func Test_variablesub1(t *testing.T) { t.Error(err) } // context - ctx := context.NewContext() + ctx := context.NewContext(jp) err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) @@ -164,7 +168,7 @@ func Test_variablesub_multiple(t *testing.T) { } // context - ctx := context.NewContext() + ctx := context.NewContext(jp) err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) @@ -252,7 +256,7 @@ func Test_variablesubstitution(t *testing.T) { } // context - ctx := context.NewContext() + ctx := context.NewContext(jp) err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) @@ -317,7 +321,7 @@ func Test_variableSubstitutionValue(t *testing.T) { } // context - ctx := context.NewContext() + ctx := context.NewContext(jp) err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) @@ -374,7 +378,7 @@ func Test_variableSubstitutionValueOperatorNotEqual(t *testing.T) { } // context - ctx := context.NewContext() + ctx := context.NewContext(jp) err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) @@ -432,7 +436,7 @@ func Test_variableSubstitutionValueFail(t *testing.T) { } // context - ctx := context.NewContext() + ctx := context.NewContext(jp) err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) @@ -489,7 +493,7 @@ func Test_variableSubstitutionObject(t *testing.T) { } // context - ctx := context.NewContext() + ctx := context.NewContext(jp) err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) @@ -553,7 +557,7 @@ func Test_variableSubstitutionObjectOperatorNotEqualFail(t *testing.T) { } // context - ctx := context.NewContext() + ctx := context.NewContext(jp) err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) @@ -628,7 +632,7 @@ func Test_variableSubstitutionMultipleObject(t *testing.T) { } // context - ctx := context.NewContext() + ctx := context.NewContext(jp) err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) diff --git a/pkg/engine/variables/vars_test.go b/pkg/engine/variables/vars_test.go index 77ba61ed85..efea0182ce 100644 --- a/pkg/engine/variables/vars_test.go +++ b/pkg/engine/variables/vars_test.go @@ -64,7 +64,7 @@ func Test_subVars_success(t *testing.T) { t.Error(err) } // context - ctx := context.NewContext() + ctx := context.NewContext(jp) err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) @@ -125,7 +125,7 @@ func Test_subVars_failed(t *testing.T) { t.Error(err) } // context - ctx := context.NewContext() + ctx := context.NewContext(jp) err = context.AddResource(ctx, resourceRaw) if err != nil { t.Error(err) @@ -219,7 +219,7 @@ func Test_subVars_with_JMESPath_At(t *testing.T) { err = json.Unmarshal(resourceRaw, &resource) assert.NilError(t, err) // context - ctx := context.NewContext() + ctx := context.NewContext(jp) err = context.AddResource(ctx, resourceRaw) assert.NilError(t, err) @@ -278,7 +278,7 @@ func Test_subVars_withRegexMatch(t *testing.T) { err = json.Unmarshal(resourceRaw, &resource) assert.NilError(t, err) // context - ctx := context.NewContext() + ctx := context.NewContext(jp) err = context.AddResource(ctx, resourceRaw) assert.NilError(t, err) @@ -308,7 +308,7 @@ func Test_subVars_withMerge(t *testing.T) { err = json.Unmarshal(resourceRaw, &resource) assert.NilError(t, err) // context - ctx := context.NewContext() + ctx := context.NewContext(jp) err = context.AddResource(ctx, resourceRaw) assert.NilError(t, err) @@ -351,7 +351,7 @@ func Test_subVars_withRegexReplaceAll(t *testing.T) { err = json.Unmarshal(resourceRaw, &resource) assert.NilError(t, err) // context - ctx := context.NewContext() + ctx := context.NewContext(jp) err = context.AddResource(ctx, resourceRaw) assert.NilError(t, err) @@ -396,7 +396,7 @@ func Test_ReplacingPathWhenDeleting(t *testing.T) { if err != nil { t.Error(err) } - ctx := context.NewContextFromRaw(resourceRaw) + ctx := context.NewContextFromRaw(jp, resourceRaw) assert.NilError(t, err) pattern, err = SubstituteAll(logr.Discard(), ctx, pattern) @@ -431,7 +431,7 @@ func Test_ReplacingNestedVariableWhenDeleting(t *testing.T) { if err != nil { t.Error(err) } - ctx := context.NewContextFromRaw(resourceRaw) + ctx := context.NewContextFromRaw(jp, resourceRaw) assert.NilError(t, err) pattern, err = SubstituteAll(logr.Discard(), ctx, pattern) @@ -457,7 +457,7 @@ var resourceRaw = []byte(` `) func Test_SubstituteSuccess(t *testing.T) { - ctx := context.NewContext() + ctx := context.NewContext(jp) assert.Assert(t, context.AddResource(ctx, resourceRaw)) var pattern interface{} @@ -481,7 +481,7 @@ func Test_SubstituteSuccess(t *testing.T) { } func Test_SubstituteRecursiveErrors(t *testing.T) { - ctx := context.NewContext() + ctx := context.NewContext(jp) assert.Assert(t, context.AddResource(ctx, resourceRaw)) var pattern interface{} @@ -515,7 +515,7 @@ func Test_SubstituteRecursiveErrors(t *testing.T) { } func Test_SubstituteRecursive(t *testing.T) { - ctx := context.NewContext() + ctx := context.NewContext(jp) assert.Assert(t, context.AddResource(ctx, resourceRaw)) var pattern interface{} @@ -633,7 +633,7 @@ func Test_variableSubstitution_array(t *testing.T) { err := json.Unmarshal(ruleRaw, &rule) assert.NilError(t, err) - ctx := context.NewContextFromRaw(configmapRaw) + ctx := context.NewContextFromRaw(jp, configmapRaw) context.AddResource(ctx, resourceRaw) vars, err := SubstituteAllInRule(logr.Discard(), ctx, rule) @@ -679,7 +679,7 @@ func Test_SubstituteNull(t *testing.T) { err = json.Unmarshal(variableObject, &resource) assert.NilError(t, err) - ctx := context.NewContext() + ctx := context.NewContext(jp) context.AddResource(ctx, variableObject) resolved, err := SubstituteAll(logr.Discard(), ctx, pattern) @@ -708,7 +708,7 @@ func Test_SubstituteNullInString(t *testing.T) { err = json.Unmarshal(variableObject, &resource) assert.NilError(t, err) - ctx := context.NewContext() + ctx := context.NewContext(jp) context.AddResource(ctx, variableObject) resolved, err := SubstituteAll(logr.Discard(), ctx, pattern) @@ -737,7 +737,7 @@ func Test_SubstituteArray(t *testing.T) { err = json.Unmarshal(variableObject, &resource) assert.NilError(t, err) - ctx := context.NewContext() + ctx := context.NewContext(jp) context.AddResource(ctx, variableObject) resolved, err := SubstituteAll(logr.Discard(), ctx, pattern) @@ -766,7 +766,7 @@ func Test_SubstituteArrayInString(t *testing.T) { err = json.Unmarshal(variableObject, &resource) assert.NilError(t, err) - ctx := context.NewContext() + ctx := context.NewContext(jp) context.AddResource(ctx, variableObject) resolved, err := SubstituteAll(logr.Discard(), ctx, pattern) @@ -795,7 +795,7 @@ func Test_SubstituteInt(t *testing.T) { err = json.Unmarshal(variableObject, &resource) assert.NilError(t, err) - ctx := context.NewContext() + ctx := context.NewContext(jp) context.AddResource(ctx, variableObject) resolved, err := SubstituteAll(logr.Discard(), ctx, pattern) @@ -824,7 +824,7 @@ func Test_SubstituteIntInString(t *testing.T) { err = json.Unmarshal(variableObject, &resource) assert.NilError(t, err) - ctx := context.NewContext() + ctx := context.NewContext(jp) context.AddResource(ctx, variableObject) resolved, err := SubstituteAll(logr.Discard(), ctx, pattern) @@ -853,7 +853,7 @@ func Test_SubstituteBool(t *testing.T) { err = json.Unmarshal(variableObject, &resource) assert.NilError(t, err) - ctx := context.NewContext() + ctx := context.NewContext(jp) context.AddResource(ctx, variableObject) resolved, err := SubstituteAll(logr.Discard(), ctx, pattern) @@ -882,7 +882,7 @@ func Test_SubstituteBoolInString(t *testing.T) { err = json.Unmarshal(variableObject, &resource) assert.NilError(t, err) - ctx := context.NewContext() + ctx := context.NewContext(jp) context.AddResource(ctx, variableObject) resolved, err := SubstituteAll(logr.Discard(), ctx, pattern) @@ -911,7 +911,7 @@ func Test_SubstituteString(t *testing.T) { err = json.Unmarshal(variableObject, &resource) assert.NilError(t, err) - ctx := context.NewContext() + ctx := context.NewContext(jp) context.AddResource(ctx, variableObject) resolved, err := SubstituteAll(logr.Discard(), ctx, pattern) @@ -940,7 +940,7 @@ func Test_SubstituteStringInString(t *testing.T) { err = json.Unmarshal(variableObject, &resource) assert.NilError(t, err) - ctx := context.NewContext() + ctx := context.NewContext(jp) context.AddResource(ctx, variableObject) resolved, err := SubstituteAll(logr.Discard(), ctx, pattern) @@ -991,7 +991,7 @@ func Test_ReferenceSubstitution(t *testing.T) { err = json.Unmarshal(expectedJSON, &expectedDocument) assert.NilError(t, err) - ctx := context.NewContext() + ctx := context.NewContext(jp) err = context.AddResource(ctx, jsonRaw) assert.NilError(t, err) @@ -1135,7 +1135,7 @@ func Test_EscpReferenceSubstitution(t *testing.T) { err = json.Unmarshal(expectedJSON, &expectedDocument) assert.NilError(t, err) - ctx := context.NewContext() + ctx := context.NewContext(jp) err = context.AddResource(ctx, jsonRaw) assert.NilError(t, err) @@ -1171,7 +1171,7 @@ func Test_ReplacingEscpNestedVariableWhenDeleting(t *testing.T) { if err != nil { t.Error(err) } - ctx := context.NewContextFromRaw(resourceRaw) + ctx := context.NewContextFromRaw(jp, resourceRaw) assert.NilError(t, err) pattern, err = SubstituteAll(logr.Discard(), ctx, pattern) diff --git a/pkg/policy/generate.go b/pkg/policy/generate.go index bf856840e7..604bf70bd0 100644 --- a/pkg/policy/generate.go +++ b/pkg/policy/generate.go @@ -14,7 +14,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func (pc *PolicyController) handleGenerate(policyKey string, policy kyvernov1.PolicyInterface) error { +func (pc *policyController) handleGenerate(policyKey string, policy kyvernov1.PolicyInterface) error { logger := pc.log.WithName("handleGenerate").WithName(policyKey) logger.Info("update URs on policy event") @@ -41,7 +41,7 @@ func (pc *PolicyController) handleGenerate(policyKey string, policy kyvernov1.Po return nil } -func (pc *PolicyController) handleGenerateForExisting(policy kyvernov1.PolicyInterface, rule kyvernov1.Rule) error { +func (pc *policyController) handleGenerateForExisting(policy kyvernov1.PolicyInterface, rule kyvernov1.Rule) error { var errors []error ruleType := kyvernov1beta1.Generate triggers := generateTriggers(pc.client, rule, pc.log) @@ -65,7 +65,7 @@ func (pc *PolicyController) handleGenerateForExisting(policy kyvernov1.PolicyInt return multierr.Combine(errors...) } -func (pc *PolicyController) createURForDownstreamDeletion(policy kyvernov1.PolicyInterface) error { +func (pc *policyController) createURForDownstreamDeletion(policy kyvernov1.PolicyInterface) error { var errs []error rules := autogen.ComputeRules(policy) for _, r := range rules { @@ -79,7 +79,7 @@ func (pc *PolicyController) createURForDownstreamDeletion(policy kyvernov1.Polic return multierr.Combine(errs...) } -func (pc *PolicyController) createURForDataRule(policy kyvernov1.PolicyInterface, rule kyvernov1.Rule, deleteDownstream bool) (bool, error) { +func (pc *policyController) createURForDataRule(policy kyvernov1.PolicyInterface, rule kyvernov1.Rule, deleteDownstream bool) (bool, error) { downstreamExist := false generate := rule.Generation if !generate.Synchronize { diff --git a/pkg/policy/mutate.go b/pkg/policy/mutate.go index 53a6f482c8..cb8ef7308e 100644 --- a/pkg/policy/mutate.go +++ b/pkg/policy/mutate.go @@ -10,7 +10,7 @@ import ( "k8s.io/apimachinery/pkg/labels" ) -func (pc *PolicyController) handleMutate(policyKey string, policy kyvernov1.PolicyInterface) error { +func (pc *policyController) handleMutate(policyKey string, policy kyvernov1.PolicyInterface) error { logger := pc.log.WithName("handleMutate").WithName(policyKey) if !policy.GetSpec().MutateExistingOnPolicyUpdate { logger.V(4).Info("skip policy application on policy event", "policyKey", policyKey, "mutateExiting", policy.GetSpec().MutateExistingOnPolicyUpdate) @@ -49,7 +49,7 @@ func (pc *PolicyController) handleMutate(policyKey string, policy kyvernov1.Poli return nil } -func (pc *PolicyController) listMutateURs(policyKey string, trigger *unstructured.Unstructured) []*kyvernov1beta1.UpdateRequest { +func (pc *policyController) listMutateURs(policyKey string, trigger *unstructured.Unstructured) []*kyvernov1beta1.UpdateRequest { mutateURs, err := pc.urLister.List(labels.SelectorFromSet(backgroundcommon.MutateLabelsSet(policyKey, trigger))) if err != nil { pc.log.Error(err, "failed to list update request for mutate policy") diff --git a/pkg/policy/policy_controller.go b/pkg/policy/policy_controller.go index 3f9f3f95d5..8c0d043d8f 100644 --- a/pkg/policy/policy_controller.go +++ b/pkg/policy/policy_controller.go @@ -18,6 +18,7 @@ import ( "github.com/kyverno/kyverno/pkg/clients/dclient" "github.com/kyverno/kyverno/pkg/config" engineapi "github.com/kyverno/kyverno/pkg/engine/api" + "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/metrics" datautils "github.com/kyverno/kyverno/pkg/utils/data" @@ -47,9 +48,9 @@ const ( maxRetries = 15 ) -// PolicyController is responsible for synchronizing Policy objects stored +// policyController is responsible for synchronizing Policy objects stored // in the system with the corresponding policy violations -type PolicyController struct { +type policyController struct { client dclient.Interface kyvernoClient versioned.Interface engine engineapi.Engine @@ -78,13 +79,15 @@ type PolicyController struct { informersSynced []cache.InformerSynced // helpers to validate against current loaded configuration - configHandler config.Configuration + configuration config.Configuration reconcilePeriod time.Duration log logr.Logger metricsConfig metrics.MetricsConfigManager + + jp jmespath.Interface } // NewPolicyController create a new PolicyController @@ -95,20 +98,21 @@ func NewPolicyController( pInformer kyvernov1informers.ClusterPolicyInformer, npInformer kyvernov1informers.PolicyInformer, urInformer kyvernov1beta1informers.UpdateRequestInformer, - configHandler config.Configuration, + configuration config.Configuration, eventGen event.Interface, namespaces corev1informers.NamespaceInformer, log logr.Logger, reconcilePeriod time.Duration, metricsConfig metrics.MetricsConfigManager, -) (*PolicyController, error) { + jp jmespath.Interface, +) (*policyController, error) { // Event broad caster eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartLogging(log.V(5).Info) eventInterface := client.GetEventsInterface() eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: eventInterface}) - pc := PolicyController{ + pc := policyController{ client: client, kyvernoClient: kyvernoClient, engine: engine, @@ -117,10 +121,11 @@ func NewPolicyController( eventGen: eventGen, eventRecorder: eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: "policy_controller"}), queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "policy"), - configHandler: configHandler, + configuration: configuration, reconcilePeriod: reconcilePeriod, metricsConfig: metricsConfig, log: log, + jp: jp, } pc.pLister = pInformer.Lister() @@ -133,7 +138,7 @@ func NewPolicyController( return &pc, nil } -func (pc *PolicyController) canBackgroundProcess(p kyvernov1.PolicyInterface) bool { +func (pc *policyController) canBackgroundProcess(p kyvernov1.PolicyInterface) bool { logger := pc.log.WithValues("policy", p.GetName()) if !p.BackgroundProcessingEnabled() { if !p.GetSpec().HasGenerate() && !p.GetSpec().IsMutateExisting() { @@ -150,7 +155,7 @@ func (pc *PolicyController) canBackgroundProcess(p kyvernov1.PolicyInterface) bo return true } -func (pc *PolicyController) addPolicy(obj interface{}) { +func (pc *policyController) addPolicy(obj interface{}) { logger := pc.log var p kyvernov1.PolicyInterface @@ -173,7 +178,7 @@ func (pc *PolicyController) addPolicy(obj interface{}) { pc.enqueuePolicy(p) } -func (pc *PolicyController) updatePolicy(old, cur interface{}) { +func (pc *policyController) updatePolicy(old, cur interface{}) { logger := pc.log var oldP, curP kyvernov1.PolicyInterface @@ -214,7 +219,7 @@ func (pc *PolicyController) updatePolicy(old, cur interface{}) { pc.enqueuePolicy(curP) } -func (pc *PolicyController) deletePolicy(obj interface{}) { +func (pc *policyController) deletePolicy(obj interface{}) { logger := pc.log var p kyvernov1.PolicyInterface @@ -235,7 +240,7 @@ func (pc *PolicyController) deletePolicy(obj interface{}) { } } -func (pc *PolicyController) enqueuePolicy(policy kyvernov1.PolicyInterface) { +func (pc *policyController) enqueuePolicy(policy kyvernov1.PolicyInterface) { logger := pc.log key, err := cache.MetaNamespaceKeyFunc(policy) if err != nil { @@ -246,7 +251,7 @@ func (pc *PolicyController) enqueuePolicy(policy kyvernov1.PolicyInterface) { } // Run begins watching and syncing. -func (pc *PolicyController) Run(ctx context.Context, workers int) { +func (pc *policyController) Run(ctx context.Context, workers int) { logger := pc.log defer utilruntime.HandleCrash() @@ -282,12 +287,12 @@ func (pc *PolicyController) Run(ctx context.Context, workers int) { // worker runs a worker thread that just dequeues items, processes them, and marks them done. // It enforces that the syncHandler is never invoked concurrently with the same key. -func (pc *PolicyController) worker(ctx context.Context) { +func (pc *policyController) worker(ctx context.Context) { for pc.processNextWorkItem() { } } -func (pc *PolicyController) processNextWorkItem() bool { +func (pc *policyController) processNextWorkItem() bool { key, quit := pc.queue.Get() if quit { return false @@ -299,7 +304,7 @@ func (pc *PolicyController) processNextWorkItem() bool { return true } -func (pc *PolicyController) handleErr(err error, key interface{}) { +func (pc *policyController) handleErr(err error, key interface{}) { logger := pc.log if err == nil { pc.queue.Forget(key) @@ -317,7 +322,7 @@ func (pc *PolicyController) handleErr(err error, key interface{}) { pc.queue.Forget(key) } -func (pc *PolicyController) syncPolicy(key string) error { +func (pc *policyController) syncPolicy(key string) error { logger := pc.log.WithName("syncPolicy") startTime := time.Now() logger.V(4).Info("started syncing policy", "key", key, "startTime", startTime) @@ -345,7 +350,7 @@ func (pc *PolicyController) syncPolicy(key string) error { return nil } -func (pc *PolicyController) getPolicy(key string) (kyvernov1.PolicyInterface, error) { +func (pc *policyController) getPolicy(key string) (kyvernov1.PolicyInterface, error) { if ns, name, err := cache.SplitMetaNamespaceKey(key); err != nil { pc.log.Error(err, "failed to parse policy name", "policyName", key) return nil, err @@ -359,7 +364,7 @@ func (pc *PolicyController) getPolicy(key string) (kyvernov1.PolicyInterface, er } // forceReconciliation forces a background scan by adding all policies to the workqueue -func (pc *PolicyController) forceReconciliation(ctx context.Context) { +func (pc *policyController) forceReconciliation(ctx context.Context) { logger := pc.log.WithName("forceReconciliation") ticker := time.NewTicker(pc.reconcilePeriod) @@ -375,7 +380,7 @@ func (pc *PolicyController) forceReconciliation(ctx context.Context) { } } -func (pc *PolicyController) requeuePolicies() { +func (pc *policyController) requeuePolicies() { logger := pc.log.WithName("requeuePolicies") if cpols, err := pc.pLister.List(labels.Everything()); err == nil { for _, cpol := range cpols { @@ -399,9 +404,9 @@ func (pc *PolicyController) requeuePolicies() { } } -func (pc *PolicyController) handleUpdateRequest(ur *kyvernov1beta1.UpdateRequest, triggerResource *unstructured.Unstructured, rule kyvernov1.Rule, policy kyvernov1.PolicyInterface) (skip bool, err error) { +func (pc *policyController) handleUpdateRequest(ur *kyvernov1beta1.UpdateRequest, triggerResource *unstructured.Unstructured, rule kyvernov1.Rule, policy kyvernov1.PolicyInterface) (skip bool, err error) { namespaceLabels := engineutils.GetNamespaceSelectorsFromNamespaceLister(triggerResource.GetKind(), triggerResource.GetNamespace(), pc.nsLister, pc.log) - policyContext, err := backgroundcommon.NewBackgroundContext(pc.client, ur, policy, triggerResource, pc.configHandler, namespaceLabels, pc.log) + policyContext, err := backgroundcommon.NewBackgroundContext(pc.log, pc.client, ur, policy, triggerResource, pc.configuration, pc.jp, namespaceLabels) if err != nil { return false, fmt.Errorf("failed to build policy context for rule %s: %w", rule.Name, err) } diff --git a/pkg/utils/api/image.go b/pkg/utils/api/image.go index fb4b25a932..1c53c2b011 100644 --- a/pkg/utils/api/image.go +++ b/pkg/utils/api/image.go @@ -51,7 +51,16 @@ func (i *imageExtractor) ExtractFromResource(resource interface{}, cfg config.Co return imageInfo, nil } -func extract(obj interface{}, path []string, keyPath, valuePath string, fields []string, jmesPath string, imageInfos *map[string]ImageInfo, cfg config.Configuration) error { +func extract( + obj interface{}, + path []string, + keyPath string, + valuePath string, + fields []string, + jmesPath string, + imageInfos *map[string]ImageInfo, + cfg config.Configuration, +) error { if obj == nil { return nil } @@ -94,11 +103,13 @@ func extract(obj interface{}, path []string, keyPath, valuePath string, fields [ return nil } if jmesPath != "" { - jp, err := jmespath.New(jmesPath) + // TODO: should be injected + jp := jmespath.New(cfg) + q, err := jp.Query(jmesPath) if err != nil { return fmt.Errorf("invalid jmespath %s: %v", jmesPath, err) } - result, err := jp.Search(value) + result, err := q.Search(value) if err != nil { return fmt.Errorf("failed to apply jmespath %s: %v", jmesPath, err) } @@ -171,12 +182,10 @@ func lookupImageExtractor(kind string, configs kyvernov1.ImageExtractorConfigs) func ExtractImagesFromResource(resource unstructured.Unstructured, configs kyvernov1.ImageExtractorConfigs, cfg config.Configuration) (map[string]map[string]ImageInfo, error) { infos := map[string]map[string]ImageInfo{} - extractors := lookupImageExtractor(resource.GetKind(), configs) if extractors != nil && len(extractors) == 0 { return nil, fmt.Errorf("no extractors found for %s", resource.GetKind()) } - for _, extractor := range extractors { if infoMap, err := extractor.ExtractFromResource(resource.Object, cfg); err != nil { return nil, err @@ -184,6 +193,5 @@ func ExtractImagesFromResource(resource unstructured.Unstructured, configs kyver infos[extractor.Name] = infoMap } } - return infos, nil } diff --git a/pkg/webhooks/resource/fake.go b/pkg/webhooks/resource/fake.go index 351ee88ebe..73d8a0ebab 100644 --- a/pkg/webhooks/resource/fake.go +++ b/pkg/webhooks/resource/fake.go @@ -10,6 +10,7 @@ import ( "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/engine/jmespath" "github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/metrics" "github.com/kyverno/kyverno/pkg/openapi" @@ -39,6 +40,7 @@ func NewFakeHandlers(ctx context.Context, policyCache policycache.Cache) webhook urLister := kyvernoInformers.Kyverno().V1beta1().UpdateRequests().Lister().UpdateRequests(config.KyvernoNamespace()) peLister := kyvernoInformers.Kyverno().V2alpha1().PolicyExceptions().Lister() rclient := registryclient.NewOrDie() + jp := jmespath.New(configuration) return &resourceHandlers{ client: dclient, @@ -51,10 +53,11 @@ func NewFakeHandlers(ctx context.Context, policyCache policycache.Cache) webhook urGenerator: updaterequest.NewFake(), eventGen: event.NewFake(), openApiManager: openapi.NewFake(), - pcBuilder: webhookutils.NewPolicyContextBuilder(configuration), + pcBuilder: webhookutils.NewPolicyContextBuilder(configuration, jp), engine: engine.NewEngine( configuration, config.NewDefaultMetricsConfiguration(), + jp, dclient, rclient, engineapi.DefaultContextLoaderFactory(configMapResolver), diff --git a/pkg/webhooks/resource/handlers.go b/pkg/webhooks/resource/handlers.go index b5524decb5..21853e34b5 100644 --- a/pkg/webhooks/resource/handlers.go +++ b/pkg/webhooks/resource/handlers.go @@ -14,6 +14,7 @@ import ( "github.com/kyverno/kyverno/pkg/clients/dclient" "github.com/kyverno/kyverno/pkg/config" engineapi "github.com/kyverno/kyverno/pkg/engine/api" + "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/metrics" "github.com/kyverno/kyverno/pkg/openapi" @@ -31,7 +32,6 @@ import ( webhookutils "github.com/kyverno/kyverno/pkg/webhooks/utils" "k8s.io/apimachinery/pkg/runtime/schema" corev1listers "k8s.io/client-go/listers/core/v1" - rbacv1listers "k8s.io/client-go/listers/rbac/v1" ) type resourceHandlers struct { @@ -72,8 +72,6 @@ func NewHandlers( metricsConfig metrics.MetricsConfigManager, pCache policycache.Cache, nsLister corev1listers.NamespaceLister, - rbLister rbacv1listers.RoleBindingLister, - crbLister rbacv1listers.ClusterRoleBindingLister, urLister kyvernov1beta1listers.UpdateRequestNamespaceLister, cpolInformer kyvernov1informers.ClusterPolicyInformer, polInformer kyvernov1informers.PolicyInformer, @@ -82,6 +80,7 @@ func NewHandlers( openApiManager openapi.ValidateInterface, admissionReports bool, backgroungServiceAccountName string, + jp jmespath.Interface, ) webhooks.ResourceHandlers { return &resourceHandlers{ engine: engine, @@ -98,7 +97,7 @@ func NewHandlers( urGenerator: urGenerator, eventGen: eventGen, openApiManager: openApiManager, - pcBuilder: webhookutils.NewPolicyContextBuilder(configuration), + pcBuilder: webhookutils.NewPolicyContextBuilder(configuration, jp), admissionReports: admissionReports, backgroungServiceAccountName: backgroungServiceAccountName, } diff --git a/pkg/webhooks/resource/validation_test.go b/pkg/webhooks/resource/validation_test.go index d5ce71d5ea..e7cb139819 100644 --- a/pkg/webhooks/resource/validation_test.go +++ b/pkg/webhooks/resource/validation_test.go @@ -10,6 +10,7 @@ import ( "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/engine/jmespath" log "github.com/kyverno/kyverno/pkg/logging" "github.com/kyverno/kyverno/pkg/registryclient" kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" @@ -1048,10 +1049,12 @@ func TestValidate_failure_action_overrides(t *testing.T) { }, }, } - + cfg := config.NewDefaultConfiguration(false) + jp := jmespath.New(cfg) eng := engine.NewEngine( - config.NewDefaultConfiguration(false), + cfg, config.NewDefaultMetricsConfiguration(), + jp, nil, registryclient.NewOrDie(), engineapi.DefaultContextLoaderFactory(nil), @@ -1065,7 +1068,7 @@ func TestValidate_failure_action_overrides(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(tc.rawResource) assert.NilError(t, err) - ctx := engine.NewPolicyContext(kyvernov1.Create).WithPolicy(&policy).WithNewResource(*resourceUnstructured).WithNamespaceLabels(tc.rawResourceNamespaceLabels) + ctx := engine.NewPolicyContext(jp, kyvernov1.Create).WithPolicy(&policy).WithNewResource(*resourceUnstructured).WithNamespaceLabels(tc.rawResourceNamespaceLabels) er := eng.Validate( context.TODO(), ctx, @@ -1127,11 +1130,14 @@ func Test_RuleSelector(t *testing.T) { assert.NilError(t, err) assert.Assert(t, resourceUnstructured != nil) - ctx := engine.NewPolicyContext(kyvernov1.Create).WithPolicy(&policy).WithNewResource(*resourceUnstructured) + cfg := config.NewDefaultConfiguration(false) + jp := jmespath.New(cfg) + ctx := engine.NewPolicyContext(jp, kyvernov1.Create).WithPolicy(&policy).WithNewResource(*resourceUnstructured) eng := engine.NewEngine( - config.NewDefaultConfiguration(false), + cfg, config.NewDefaultMetricsConfiguration(), + jp, nil, registryclient.NewOrDie(), engineapi.DefaultContextLoaderFactory(nil), diff --git a/pkg/webhooks/utils/policy_context_builder.go b/pkg/webhooks/utils/policy_context_builder.go index d3ae77b16f..197f335a13 100644 --- a/pkg/webhooks/utils/policy_context_builder.go +++ b/pkg/webhooks/utils/policy_context_builder.go @@ -4,6 +4,7 @@ import ( kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1" "github.com/kyverno/kyverno/pkg/config" "github.com/kyverno/kyverno/pkg/engine" + "github.com/kyverno/kyverno/pkg/engine/jmespath" admissionv1 "k8s.io/api/admission/v1" "k8s.io/apimachinery/pkg/runtime/schema" ) @@ -14,13 +15,16 @@ type PolicyContextBuilder interface { type policyContextBuilder struct { configuration config.Configuration + jp jmespath.Interface } func NewPolicyContextBuilder( configuration config.Configuration, + jp jmespath.Interface, ) PolicyContextBuilder { return &policyContextBuilder{ configuration: configuration, + jp: jp, } } @@ -30,5 +34,5 @@ func (b *policyContextBuilder) Build(request admissionv1.AdmissionRequest, roles Roles: roles, ClusterRoles: clusterRoles, } - return engine.NewPolicyContextFromAdmissionRequest(request, userRequestInfo, gvk, b.configuration) + return engine.NewPolicyContextFromAdmissionRequest(b.jp, request, userRequestInfo, gvk, b.configuration) }