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

refactor: introduce context loader interface in engine api (#6164)

* refactor: introduce context loader interface in engine api

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

* factory

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

* mock

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

* fix

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

* fix

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

* fix

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

* fix

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

* test

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

---------

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
Charles-Edouard Brétéché 2023-01-31 15:30:40 +01:00 committed by GitHub
parent fe9ecc8ae6
commit 848596ca8d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 448 additions and 253 deletions

View file

@ -486,7 +486,11 @@ OuterLoop:
WithClient(c.Client).
WithSubresourcesInPolicy(subresources)
mutateResponse := engine.Mutate(context.Background(), registryclient.NewOrDie(), policyContext)
mutateResponse := engine.Mutate(
context.Background(),
engine.LegacyContextLoaderFactory(registryclient.NewOrDie()),
policyContext,
)
if mutateResponse != nil {
engineResponses = append(engineResponses, mutateResponse)
}
@ -510,7 +514,12 @@ OuterLoop:
var info Info
var validateResponse *engineapi.EngineResponse
if policyHasValidate {
validateResponse = engine.Validate(context.Background(), registryclient.NewOrDie(), policyContext, cfg)
validateResponse = engine.Validate(
context.Background(),
engine.LegacyContextLoaderFactory(registryclient.NewOrDie()),
policyContext,
cfg,
)
info = ProcessValidateEngineResponse(c.Policy, validateResponse, resPath, c.Rc, c.PolicyReport, c.AuditWarn)
}
@ -518,7 +527,13 @@ OuterLoop:
engineResponses = append(engineResponses, validateResponse)
}
verifyImageResponse, _ := engine.VerifyAndPatchImages(context.Background(), registryclient.NewOrDie(), policyContext, cfg)
verifyImageResponse, _ := engine.VerifyAndPatchImages(
context.Background(),
engine.LegacyContextLoaderFactory(registryclient.NewOrDie()),
registryclient.NewOrDie(),
policyContext,
cfg,
)
if verifyImageResponse != nil && !verifyImageResponse.IsEmpty() {
engineResponses = append(engineResponses, verifyImageResponse)
info = ProcessValidateEngineResponse(c.Policy, verifyImageResponse, resPath, c.Rc, c.PolicyReport, c.AuditWarn)
@ -532,7 +547,10 @@ OuterLoop:
}
if policyHasGenerate {
generateResponse := engine.ApplyBackgroundChecks(registryclient.NewOrDie(), policyContext)
generateResponse := engine.ApplyBackgroundChecks(
engine.LegacyContextLoaderFactory(registryclient.NewOrDie()),
policyContext,
)
if generateResponse != nil && !generateResponse.IsEmpty() {
newRuleResponse, err := handleGeneratePolicy(generateResponse, *policyContext, c.RuleToCloneSourceResource)
if err != nil {
@ -1065,7 +1083,7 @@ func initializeMockController(objects []runtime.Object) (*generate.GenerateContr
}
client.SetDiscovery(dclient.NewFakeDiscoveryClient(nil))
c := generate.NewGenerateControllerWithOnlyClient(client)
c := generate.NewGenerateControllerWithOnlyClient(client, engine.LegacyContextLoaderFactory(nil))
return c, nil
}

View file

@ -133,7 +133,7 @@ func createNonLeaderControllers(
updateRequestController := background.NewController(
kyvernoClient,
dynamicClient,
rclient,
engine.LegacyContextLoaderFactory(rclient),
kyvernoInformer.Kyverno().V1().ClusterPolicies(),
kyvernoInformer.Kyverno().V1().Policies(),
kyvernoInformer.Kyverno().V1beta1().UpdateRequests(),
@ -177,7 +177,7 @@ func createrLeaderControllers(
policyCtrl, err := policy.NewPolicyController(
kyvernoClient,
dynamicClient,
rclient,
engine.LegacyContextLoaderFactory(rclient),
kyvernoInformer.Kyverno().V1().ClusterPolicies(),
kyvernoInformer.Kyverno().V1().Policies(),
kyvernoInformer.Kyverno().V1beta1().UpdateRequests(),
@ -523,6 +523,7 @@ func main() {
}
}
resourceHandlers := webhooksresource.NewHandlers(
engine.LegacyContextLoaderFactory(rclient),
dClient,
kyvernoClient,
rclient,

View file

@ -24,6 +24,7 @@ import (
backgroundscancontroller "github.com/kyverno/kyverno/pkg/controllers/report/background"
resourcereportcontroller "github.com/kyverno/kyverno/pkg/controllers/report/resource"
"github.com/kyverno/kyverno/pkg/cosign"
"github.com/kyverno/kyverno/pkg/engine"
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/engine/context/resolvers"
"github.com/kyverno/kyverno/pkg/event"
@ -129,6 +130,7 @@ func createReportControllers(
client,
kyvernoClient,
rclient,
engine.LegacyContextLoaderFactory(rclient),
metadataFactory,
kyvernoV1.Policies(),
kyvernoV1.ClusterPolicies(),

View file

@ -21,6 +21,7 @@ import (
"github.com/kyverno/kyverno/pkg/config"
policymetricscontroller "github.com/kyverno/kyverno/pkg/controllers/metrics/policy"
"github.com/kyverno/kyverno/pkg/cosign"
"github.com/kyverno/kyverno/pkg/engine"
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/engine/context/resolvers"
"github.com/kyverno/kyverno/pkg/event"
@ -77,7 +78,7 @@ func createNonLeaderControllers(
updateRequestController := background.NewController(
kyvernoClient,
dynamicClient,
rclient,
engine.LegacyContextLoaderFactory(rclient),
kyvernoInformer.Kyverno().V1().ClusterPolicies(),
kyvernoInformer.Kyverno().V1().Policies(),
kyvernoInformer.Kyverno().V1beta1().UpdateRequests(),
@ -104,7 +105,7 @@ func createrLeaderControllers(
policyCtrl, err := policy.NewPolicyController(
kyvernoClient,
dynamicClient,
rclient,
engine.LegacyContextLoaderFactory(rclient),
kyvernoInformer.Kyverno().V1().ClusterPolicies(),
kyvernoInformer.Kyverno().V1().Policies(),
kyvernoInformer.Kyverno().V1beta1().UpdateRequests(),

View file

@ -25,7 +25,6 @@ import (
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/variables"
"github.com/kyverno/kyverno/pkg/event"
"github.com/kyverno/kyverno/pkg/registryclient"
datautils "github.com/kyverno/kyverno/pkg/utils/data"
engineutils "github.com/kyverno/kyverno/pkg/utils/engine"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
@ -43,7 +42,7 @@ type GenerateController struct {
client dclient.Interface
kyvernoClient versioned.Interface
statusControl common.StatusControlInterface
rclient registryclient.Client
contextLoader engine.ContextLoaderFactory
// listers
urLister kyvernov1beta1listers.UpdateRequestNamespaceLister
@ -63,7 +62,7 @@ func NewGenerateController(
client dclient.Interface,
kyvernoClient versioned.Interface,
statusControl common.StatusControlInterface,
rclient registryclient.Client,
contextLoader engine.ContextLoaderFactory,
policyLister kyvernov1listers.ClusterPolicyLister,
npolicyLister kyvernov1listers.PolicyLister,
urLister kyvernov1beta1listers.UpdateRequestNamespaceLister,
@ -75,9 +74,9 @@ func NewGenerateController(
) *GenerateController {
c := GenerateController{
client: client,
contextLoader: contextLoader,
kyvernoClient: kyvernoClient,
statusControl: statusControl,
rclient: rclient,
policyLister: policyLister,
npolicyLister: npolicyLister,
urLister: urLister,
@ -201,7 +200,7 @@ func (c *GenerateController) applyGenerate(resource unstructured.Unstructured, u
}
// check if the policy still applies to the resource
engineResponse := engine.GenerateResponse(c.rclient, policyContext, ur)
engineResponse := engine.GenerateResponse(c.contextLoader, policyContext, ur)
if len(engineResponse.PolicyResponse.Rules) == 0 {
logger.V(4).Info(doesNotApply)
return nil, false, errors.New(doesNotApply)
@ -347,7 +346,7 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext
}
// add configmap json data to context
if err := engine.LoadContext(context.TODO(), log, c.rclient, rule.Context, policyContext, rule.Name); err != nil {
if err := engine.LoadContext(context.TODO(), c.contextLoader, rule.Context, policyContext, rule.Name); err != nil {
log.Error(err, "cannot add configmaps to context")
return nil, processExisting, err
}
@ -829,9 +828,10 @@ func (c *GenerateController) ApplyResource(resource *unstructured.Unstructured)
}
// NewGenerateControllerWithOnlyClient returns an instance of Controller with only the client.
func NewGenerateControllerWithOnlyClient(client dclient.Interface) *GenerateController {
func NewGenerateControllerWithOnlyClient(client dclient.Interface, contextLoader engine.ContextLoaderFactory) *GenerateController {
c := GenerateController{
client: client,
client: client,
contextLoader: contextLoader,
}
return &c
}

View file

@ -15,7 +15,6 @@ import (
"github.com/kyverno/kyverno/pkg/engine"
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/event"
"github.com/kyverno/kyverno/pkg/registryclient"
"github.com/kyverno/kyverno/pkg/utils"
"go.uber.org/multierr"
yamlv2 "gopkg.in/yaml.v2"
@ -30,7 +29,8 @@ type MutateExistingController struct {
// clients
client dclient.Interface
statusControl common.StatusControlInterface
rclient registryclient.Client
contextLoader engine.ContextLoaderFactory
// listers
policyLister kyvernov1listers.ClusterPolicyLister
npolicyLister kyvernov1listers.PolicyLister
@ -46,7 +46,7 @@ type MutateExistingController struct {
func NewMutateExistingController(
client dclient.Interface,
statusControl common.StatusControlInterface,
rclient registryclient.Client,
contextLoader engine.ContextLoaderFactory,
policyLister kyvernov1listers.ClusterPolicyLister,
npolicyLister kyvernov1listers.PolicyLister,
dynamicConfig config.Configuration,
@ -57,7 +57,7 @@ func NewMutateExistingController(
c := MutateExistingController{
client: client,
statusControl: statusControl,
rclient: rclient,
contextLoader: contextLoader,
policyLister: policyLister,
npolicyLister: npolicyLister,
configuration: dynamicConfig,
@ -97,7 +97,7 @@ func (c *MutateExistingController) ProcessUR(ur *kyvernov1beta1.UpdateRequest) e
continue
}
er := engine.Mutate(context.TODO(), c.rclient, policyContext)
er := engine.Mutate(context.TODO(), c.contextLoader, policyContext)
for _, r := range er.PolicyResponse.Rules {
patched := r.PatchedTarget
patchedTargetSubresourceName := r.PatchedTargetSubresourceName

View file

@ -18,9 +18,9 @@ import (
kyvernov1beta1listers "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1beta1"
"github.com/kyverno/kyverno/pkg/clients/dclient"
"github.com/kyverno/kyverno/pkg/config"
"github.com/kyverno/kyverno/pkg/engine"
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/event"
"github.com/kyverno/kyverno/pkg/registryclient"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
"github.com/pkg/errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
@ -49,7 +49,7 @@ type controller struct {
// clients
client dclient.Interface
kyvernoClient versioned.Interface
rclient registryclient.Client
contextLoader engine.ContextLoaderFactory
// listers
cpolLister kyvernov1listers.ClusterPolicyLister
@ -72,7 +72,7 @@ type controller struct {
func NewController(
kyvernoClient versioned.Interface,
client dclient.Interface,
rclient registryclient.Client,
contextLoader engine.ContextLoaderFactory,
cpolInformer kyvernov1informers.ClusterPolicyInformer,
polInformer kyvernov1informers.PolicyInformer,
urInformer kyvernov1beta1informers.UpdateRequestInformer,
@ -86,7 +86,7 @@ func NewController(
c := controller{
client: client,
kyvernoClient: kyvernoClient,
rclient: rclient,
contextLoader: contextLoader,
cpolLister: cpolInformer.Lister(),
polLister: polInformer.Lister(),
urLister: urLister,
@ -420,10 +420,10 @@ func (c *controller) processUR(ur *kyvernov1beta1.UpdateRequest) error {
statusControl := common.NewStatusControl(c.kyvernoClient, c.urLister)
switch ur.Spec.Type {
case kyvernov1beta1.Mutate:
ctrl := mutate.NewMutateExistingController(c.client, statusControl, c.rclient, c.cpolLister, c.polLister, c.configuration, c.informerCacheResolvers, c.eventGen, logger)
ctrl := mutate.NewMutateExistingController(c.client, statusControl, c.contextLoader, c.cpolLister, c.polLister, c.configuration, c.informerCacheResolvers, c.eventGen, logger)
return ctrl.ProcessUR(ur)
case kyvernov1beta1.Generate:
ctrl := generate.NewGenerateController(c.client, c.kyvernoClient, statusControl, c.rclient, c.cpolLister, c.polLister, c.urLister, c.nsLister, c.configuration, c.informerCacheResolvers, c.eventGen, logger)
ctrl := generate.NewGenerateController(c.client, c.kyvernoClient, statusControl, c.contextLoader, c.cpolLister, c.polLister, c.urLister, c.nsLister, c.configuration, c.informerCacheResolvers, c.eventGen, logger)
return ctrl.ProcessUR(ur)
}
return nil

View file

@ -49,6 +49,7 @@ type controller struct {
client dclient.Interface
kyvernoClient versioned.Interface
rclient registryclient.Client
contextLoader engine.ContextLoaderFactory
// listers
polLister kyvernov1listers.PolicyLister
@ -75,6 +76,7 @@ func NewController(
client dclient.Interface,
kyvernoClient versioned.Interface,
rclient registryclient.Client,
contextLoader engine.ContextLoaderFactory,
metadataFactory metadatainformers.SharedInformerFactory,
polInformer kyvernov1informers.PolicyInformer,
cpolInformer kyvernov1informers.ClusterPolicyInformer,
@ -93,6 +95,7 @@ func NewController(
client: client,
kyvernoClient: kyvernoClient,
rclient: rclient,
contextLoader: contextLoader,
polLister: polInformer.Lister(),
cpolLister: cpolInformer.Lister(),
bgscanrLister: bgscanr.Lister(),
@ -309,7 +312,7 @@ func (c *controller) reconcileReport(
// calculate necessary results
for _, policy := range backgroundPolicies {
if full || actual[reportutils.PolicyLabel(policy)] != policy.GetResourceVersion() {
scanner := utils.NewScanner(logger, c.client, c.rclient, c.informerCacheResolvers, c.polexLister, c.config)
scanner := utils.NewScanner(logger, c.contextLoader, c.client, c.rclient, c.informerCacheResolvers, c.polexLister, c.config)
for _, result := range scanner.ScanResource(ctx, *target, nsLabels, policy) {
if result.Error != nil {
return result.Error

View file

@ -17,6 +17,7 @@ import (
type scanner struct {
logger logr.Logger
contextLoader engine.ContextLoaderFactory
client dclient.Interface
rclient registryclient.Client
informerCacheResolvers engineapi.ConfigmapResolver
@ -36,6 +37,7 @@ type Scanner interface {
func NewScanner(
logger logr.Logger,
contextLoader engine.ContextLoaderFactory,
client dclient.Interface,
rclient registryclient.Client,
informerCacheResolvers engineapi.ConfigmapResolver,
@ -45,6 +47,7 @@ func NewScanner(
) Scanner {
return &scanner{
logger: logger,
contextLoader: contextLoader,
client: client,
rclient: rclient,
informerCacheResolvers: informerCacheResolvers,
@ -103,7 +106,7 @@ func (s *scanner) validateResource(ctx context.Context, resource unstructured.Un
WithExcludeGroupRole(s.excludeGroupRole...).
WithInformerCacheResolver(s.informerCacheResolvers).
WithExceptions(s.polexLister)
return engine.Validate(ctx, s.rclient, policyCtx, s.config), nil
return engine.Validate(ctx, s.contextLoader, policyCtx, s.config), nil
}
func (s *scanner) validateImages(ctx context.Context, resource unstructured.Unstructured, nsLabels map[string]string, policy kyvernov1.PolicyInterface) (*engineapi.EngineResponse, error) {
@ -128,7 +131,7 @@ func (s *scanner) validateImages(ctx context.Context, resource unstructured.Unst
WithExcludeGroupRole(s.excludeGroupRole...).
WithInformerCacheResolver(s.informerCacheResolvers).
WithExceptions(s.polexLister)
response, _ := engine.VerifyAndPatchImages(ctx, s.rclient, policyCtx, s.config)
response, _ := engine.VerifyAndPatchImages(ctx, s.contextLoader, s.rclient, policyCtx, s.config)
if len(response.PolicyResponse.Rules) > 0 {
s.logger.Info("validateImages", "policy", policy, "response", response)
}

View file

@ -0,0 +1,12 @@
package api
import (
"context"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
)
type ContextLoader interface {
Load(ctx context.Context, contextEntries []kyvernov1.ContextEntry, jsonContext enginecontext.Interface) error
}

View file

@ -10,7 +10,6 @@ import (
"github.com/kyverno/kyverno/pkg/engine/utils"
"github.com/kyverno/kyverno/pkg/engine/variables"
"github.com/kyverno/kyverno/pkg/logging"
"github.com/kyverno/kyverno/pkg/registryclient"
)
// ApplyBackgroundChecks checks for validity of generate and mutateExisting rules on the resource
@ -18,12 +17,19 @@ import (
// - the caller has to check the ruleResponse to determine whether the path exist
//
// 2. returns the list of rules that are applicable on this policy and resource, if 1 succeed
func ApplyBackgroundChecks(rclient registryclient.Client, policyContext *PolicyContext) (resp *engineapi.EngineResponse) {
func ApplyBackgroundChecks(
contextLoader ContextLoaderFactory,
policyContext *PolicyContext,
) (resp *engineapi.EngineResponse) {
policyStartTime := time.Now()
return filterRules(rclient, policyContext, policyStartTime)
return filterRules(contextLoader, policyContext, policyStartTime)
}
func filterRules(rclient registryclient.Client, policyContext *PolicyContext, startTime time.Time) *engineapi.EngineResponse {
func filterRules(
contextLoader ContextLoaderFactory,
policyContext *PolicyContext,
startTime time.Time,
) *engineapi.EngineResponse {
kind := policyContext.newResource.GetKind()
name := policyContext.newResource.GetName()
namespace := policyContext.newResource.GetNamespace()
@ -55,7 +61,7 @@ func filterRules(rclient registryclient.Client, policyContext *PolicyContext, st
applyRules := policyContext.policy.GetSpec().GetApplyRules()
for _, rule := range autogen.ComputeRules(policyContext.policy) {
if ruleResp := filterRule(rclient, rule, policyContext); ruleResp != nil {
if ruleResp := filterRule(contextLoader, rule, policyContext); ruleResp != nil {
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, *ruleResp)
if applyRules == kyvernov1.ApplyOne && ruleResp.Status != engineapi.RuleStatusSkip {
break
@ -66,7 +72,11 @@ func filterRules(rclient registryclient.Client, policyContext *PolicyContext, st
return resp
}
func filterRule(rclient registryclient.Client, rule kyvernov1.Rule, policyContext *PolicyContext) *engineapi.RuleResponse {
func filterRule(
contextLoader ContextLoaderFactory,
rule kyvernov1.Rule,
policyContext *PolicyContext,
) *engineapi.RuleResponse {
if !rule.HasGenerate() && !rule.IsMutateExisting() {
return nil
}
@ -122,7 +132,7 @@ func filterRule(rclient registryclient.Client, rule kyvernov1.Rule, policyContex
policyContext.jsonContext.Checkpoint()
defer policyContext.jsonContext.Restore()
if err := LoadContext(context.TODO(), logger, rclient, rule.Context, policyContext, rule.Name); err != nil {
if err := LoadContext(context.TODO(), contextLoader, rule.Context, policyContext, rule.Name); err != nil {
logger.V(4).Info("cannot add external data to the context", "reason", err.Error())
return nil
}

View file

@ -7,17 +7,25 @@ import (
"github.com/kyverno/kyverno/pkg/autogen"
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/logging"
"github.com/kyverno/kyverno/pkg/registryclient"
"k8s.io/client-go/tools/cache"
)
// GenerateResponse checks for validity of generate rule on the resource
func GenerateResponse(rclient registryclient.Client, policyContext *PolicyContext, gr kyvernov1beta1.UpdateRequest) (resp *engineapi.EngineResponse) {
func GenerateResponse(
contextLoader ContextLoaderFactory,
policyContext *PolicyContext,
gr kyvernov1beta1.UpdateRequest,
) (resp *engineapi.EngineResponse) {
policyStartTime := time.Now()
return filterGenerateRules(rclient, policyContext, gr.Spec.Policy, policyStartTime)
return filterGenerateRules(contextLoader, policyContext, gr.Spec.Policy, policyStartTime)
}
func filterGenerateRules(rclient registryclient.Client, policyContext *PolicyContext, policyNameKey string, startTime time.Time) *engineapi.EngineResponse {
func filterGenerateRules(
contextLoader ContextLoaderFactory,
policyContext *PolicyContext,
policyNameKey string,
startTime time.Time,
) *engineapi.EngineResponse {
kind := policyContext.newResource.GetKind()
name := policyContext.newResource.GetName()
namespace := policyContext.newResource.GetNamespace()
@ -53,7 +61,7 @@ func filterGenerateRules(rclient registryclient.Client, policyContext *PolicyCon
}
for _, rule := range autogen.ComputeRules(policyContext.policy) {
if ruleResp := filterRule(rclient, rule, policyContext); ruleResp != nil {
if ruleResp := filterRule(contextLoader, rule, policyContext); ruleResp != nil {
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, *ruleResp)
}
}

View file

@ -68,6 +68,7 @@ func extractMatchingImages(policyContext *PolicyContext, rule *kyvernov1.Rule, c
func VerifyAndPatchImages(
ctx context.Context,
contextLoader ContextLoaderFactory,
rclient registryclient.Client,
policyContext *PolicyContext,
cfg config.Configuration,
@ -138,7 +139,7 @@ func VerifyAndPatchImages(
}
policyContext.jsonContext.Restore()
if err := LoadContext(ctx, logger, rclient, rule.Context, policyContext, rule.Name); err != nil {
if err := LoadContext(ctx, contextLoader, rule.Context, policyContext, rule.Name); err != nil {
appendResponse(resp, rule, fmt.Sprintf("failed to load context: %s", err.Error()), engineapi.RuleStatusError)
return
}

View file

@ -10,13 +10,19 @@ import (
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/config"
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/registryclient"
apiutils "github.com/kyverno/kyverno/pkg/utils/api"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
func processImageValidationRule(ctx context.Context, log logr.Logger, rclient registryclient.Client, enginectx *PolicyContext, rule *kyvernov1.Rule, cfg config.Configuration) *engineapi.RuleResponse {
func processImageValidationRule(
ctx context.Context,
contextLoader ContextLoaderFactory,
log logr.Logger,
enginectx *PolicyContext,
rule *kyvernov1.Rule,
cfg config.Configuration,
) *engineapi.RuleResponse {
if isDeleteRequest(enginectx) {
return nil
}
@ -29,7 +35,7 @@ func processImageValidationRule(ctx context.Context, log logr.Logger, rclient re
if len(matchingImages) == 0 {
return ruleResponse(*rule, engineapi.Validation, "image verified", engineapi.RuleStatusSkip)
}
if err := LoadContext(ctx, log, rclient, rule.Context, enginectx, rule.Name); err != nil {
if err := LoadContext(ctx, contextLoader, rule.Context, enginectx, rule.Name); err != nil {
if _, ok := err.(gojmespath.NotFoundError); ok {
log.V(3).Info("failed to load context", "reason", err.Error())
} else {

View file

@ -160,12 +160,28 @@ var signaturePayloads = [][]byte{
var cfg = config.NewDefaultConfiguration()
func doVerifyAndPatchImages(
ctx context.Context,
rclient registryclient.Client,
pContext *PolicyContext,
cfg config.Configuration,
) (*engineapi.EngineResponse, *ImageVerificationMetadata) {
return VerifyAndPatchImages(
ctx,
LegacyContextLoaderFactory(rclient),
rclient,
pContext,
cfg,
)
}
func Test_CosignMockAttest(t *testing.T) {
policyContext := buildContext(t, testPolicyGood, testResource, "")
err := cosign.SetMock("ghcr.io/jimbugwadia/pause2:latest", attestationPayloads)
assert.NilError(t, err)
er, ivm := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
er, ivm := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass,
fmt.Sprintf("expected: %v, got: %v, failure: %v",
@ -179,7 +195,7 @@ func Test_CosignMockAttest_fail(t *testing.T) {
err := cosign.SetMock("ghcr.io/jimbugwadia/pause2:latest", attestationPayloads)
assert.NilError(t, err)
er, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
er, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail)
}
@ -428,7 +444,7 @@ var (
func Test_ConfigMapMissingSuccess(t *testing.T) {
policyContext := buildContext(t, testConfigMapMissing, testConfigMapMissingResource, "")
cosign.ClearMock()
err, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
err, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, len(err.PolicyResponse.Rules), 1)
assert.Equal(t, err.PolicyResponse.Rules[0].Status, engineapi.RuleStatusSkip, err.PolicyResponse.Rules[0].Message)
}
@ -440,7 +456,7 @@ func Test_ConfigMapMissingFailure(t *testing.T) {
assert.NilError(t, err)
policyContext.informerCacheResolvers = resolver
cosign.ClearMock()
resp, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
resp, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, len(resp.PolicyResponse.Rules), 1)
assert.Equal(t, resp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusError, resp.PolicyResponse.Rules[0].Message)
}
@ -449,7 +465,7 @@ func Test_SignatureGoodSigned(t *testing.T) {
policyContext := buildContext(t, testSampleSingleKeyPolicy, testSampleResource, "")
policyContext.policy.GetSpec().Rules[0].VerifyImages[0].MutateDigest = true
cosign.ClearMock()
engineResp, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
engineResp, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, len(engineResp.PolicyResponse.Rules), 1)
assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass, engineResp.PolicyResponse.Rules[0].Message)
assert.Equal(t, len(engineResp.PolicyResponse.Rules[0].Patches), 1)
@ -461,7 +477,7 @@ func Test_SignatureUnsigned(t *testing.T) {
cosign.ClearMock()
unsigned := strings.Replace(testSampleResource, ":signed", ":unsigned", -1)
policyContext := buildContext(t, testSampleSingleKeyPolicy, unsigned, "")
engineResp, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
engineResp, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, len(engineResp.PolicyResponse.Rules), 1)
assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail, engineResp.PolicyResponse.Rules[0].Message)
}
@ -470,7 +486,7 @@ func Test_SignatureWrongKey(t *testing.T) {
cosign.ClearMock()
otherKey := strings.Replace(testSampleResource, ":signed", ":signed-by-someone-else", -1)
policyContext := buildContext(t, testSampleSingleKeyPolicy, otherKey, "")
engineResp, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
engineResp, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, len(engineResp.PolicyResponse.Rules), 1)
assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail, engineResp.PolicyResponse.Rules[0].Message)
}
@ -481,7 +497,7 @@ func Test_SignaturesMultiKey(t *testing.T) {
policy = strings.Replace(policy, "KEY2", testVerifyImageKey, -1)
policy = strings.Replace(policy, "COUNT", "0", -1)
policyContext := buildContext(t, policy, testSampleResource, "")
engineResp, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
engineResp, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, len(engineResp.PolicyResponse.Rules), 1)
assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass, engineResp.PolicyResponse.Rules[0].Message)
}
@ -491,7 +507,7 @@ func Test_SignaturesMultiKeyFail(t *testing.T) {
policy := strings.Replace(testSampleMultipleKeyPolicy, "KEY1", testVerifyImageKey, -1)
policy = strings.Replace(policy, "COUNT", "0", -1)
policyContext := buildContext(t, policy, testSampleResource, "")
engineResp, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
engineResp, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, len(engineResp.PolicyResponse.Rules), 1)
assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail, engineResp.PolicyResponse.Rules[0].Message)
}
@ -502,7 +518,7 @@ func Test_SignaturesMultiKeyOneGoodKey(t *testing.T) {
policy = strings.Replace(policy, "KEY2", testOtherKey, -1)
policy = strings.Replace(policy, "COUNT", "1", -1)
policyContext := buildContext(t, policy, testSampleResource, "")
engineResp, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
engineResp, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, len(engineResp.PolicyResponse.Rules), 1)
assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass, engineResp.PolicyResponse.Rules[0].Message)
}
@ -513,7 +529,7 @@ func Test_SignaturesMultiKeyZeroGoodKey(t *testing.T) {
policy = strings.Replace(policy, "KEY2", testOtherKey, -1)
policy = strings.Replace(policy, "COUNT", "1", -1)
policyContext := buildContext(t, policy, testSampleResource, "")
resp, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
resp, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, len(resp.PolicyResponse.Rules), 1)
assert.Equal(t, resp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail, resp.PolicyResponse.Rules[0].Message)
}
@ -529,14 +545,14 @@ func Test_RuleSelectorImageVerify(t *testing.T) {
applyAll := kyverno.ApplyAll
spec.ApplyRules = &applyAll
resp, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
resp, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, len(resp.PolicyResponse.Rules), 2)
assert.Equal(t, resp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass, resp.PolicyResponse.Rules[0].Message)
assert.Equal(t, resp.PolicyResponse.Rules[1].Status, engineapi.RuleStatusFail, resp.PolicyResponse.Rules[1].Message)
applyOne := kyverno.ApplyOne
spec.ApplyRules = &applyOne
resp, _ = VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
resp, _ = doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, len(resp.PolicyResponse.Rules), 1)
assert.Equal(t, resp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass, resp.PolicyResponse.Rules[0].Message)
}
@ -640,7 +656,7 @@ func Test_NestedAttestors(t *testing.T) {
policy = strings.Replace(policy, "KEY2", testVerifyImageKey, -1)
policy = strings.Replace(policy, "COUNT", "0", -1)
policyContext := buildContext(t, policy, testSampleResource, "")
err, _ := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
err, _ := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, len(err.PolicyResponse.Rules), 1)
assert.Equal(t, err.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass)
@ -648,7 +664,7 @@ func Test_NestedAttestors(t *testing.T) {
policy = strings.Replace(policy, "KEY2", testOtherKey, -1)
policy = strings.Replace(policy, "COUNT", "0", -1)
policyContext = buildContext(t, policy, testSampleResource, "")
err, _ = VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
err, _ = doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, len(err.PolicyResponse.Rules), 1)
assert.Equal(t, err.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail)
@ -656,7 +672,7 @@ func Test_NestedAttestors(t *testing.T) {
policy = strings.Replace(policy, "KEY2", testOtherKey, -1)
policy = strings.Replace(policy, "COUNT", "1", -1)
policyContext = buildContext(t, policy, testSampleResource, "")
err, _ = VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
err, _ = doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, len(err.PolicyResponse.Rules), 1)
assert.Equal(t, err.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass)
}
@ -749,7 +765,7 @@ func Test_MarkImageVerified(t *testing.T) {
err := cosign.SetMock(image, attestationPayloads)
assert.NilError(t, err)
engineResponse, verifiedImages := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
engineResponse, verifiedImages := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Assert(t, engineResponse != nil)
assert.Equal(t, len(engineResponse.PolicyResponse.Rules), 1)
assert.Equal(t, engineResponse.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass)
@ -842,7 +858,7 @@ func Test_ParsePEMDelimited(t *testing.T) {
err := cosign.SetMock(image, signaturePayloads)
assert.NilError(t, err)
engineResponse, verifiedImages := VerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
engineResponse, verifiedImages := doVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Assert(t, engineResponse != nil)
assert.Equal(t, len(engineResponse.PolicyResponse.Rules), 1)
assert.Equal(t, engineResponse.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass)

View file

@ -8,86 +8,128 @@ import (
"github.com/go-logr/logr"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/store"
"github.com/kyverno/kyverno/pkg/clients/dclient"
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/engine/apicall"
"github.com/kyverno/kyverno/pkg/engine/jmespath"
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
jmespath "github.com/kyverno/kyverno/pkg/engine/jmespath"
"github.com/kyverno/kyverno/pkg/engine/variables"
"github.com/kyverno/kyverno/pkg/logging"
"github.com/kyverno/kyverno/pkg/registryclient"
"github.com/pkg/errors"
)
// LoadContext - Fetches and adds external data to the Context.
func LoadContext(ctx context.Context, logger logr.Logger, rclient registryclient.Client, contextEntries []kyvernov1.ContextEntry, enginectx *PolicyContext, ruleName string) error {
if len(contextEntries) == 0 {
return nil
}
type ContextLoaderFactory = func(pContext *PolicyContext, ruleName string) engineapi.ContextLoader
policyName := enginectx.policy.GetName()
func LegacyContextLoaderFactory(rclient registryclient.Client) ContextLoaderFactory {
if store.IsMock() {
rule := store.GetPolicyRule(policyName, ruleName)
if rule != nil && len(rule.Values) > 0 {
variables := rule.Values
for key, value := range variables {
if err := enginectx.jsonContext.AddVariable(key, value); err != nil {
return err
}
return func(pContext *PolicyContext, ruleName string) engineapi.ContextLoader {
policy := pContext.Policy()
return &mockContextLoader{
logger: logging.WithName("MockContextLoaderFactory"),
policyName: policy.GetName(),
ruleName: ruleName,
client: pContext.Client(),
rclient: rclient,
cmResolver: pContext.informerCacheResolvers,
}
}
hasRegistryAccess := store.GetRegistryAccess()
// Context Variable should be loaded after the values loaded from values file
for _, entry := range contextEntries {
if entry.ImageRegistry != nil && hasRegistryAccess {
rclient := store.GetRegistryClient()
if err := loadImageData(ctx, rclient, logger, entry, enginectx); err != nil {
return err
}
} else if entry.Variable != nil {
if err := loadVariable(logger, entry, enginectx); err != nil {
return err
}
} else if entry.APICall != nil && store.IsApiCallAllowed() {
if err := loadAPIData(ctx, logger, entry, enginectx); err != nil {
return err
}
}
}
return func(pContext *PolicyContext, ruleName string) engineapi.ContextLoader {
return &contextLoader{
logger: logging.WithName("LegacyContextLoaderFactory"),
client: pContext.Client(),
rclient: rclient,
cmResolver: pContext.informerCacheResolvers,
}
}
}
if rule != nil && len(rule.ForEachValues) > 0 {
for key, value := range rule.ForEachValues {
if err := enginectx.jsonContext.AddVariable(key, value[store.GetForeachElement()]); err != nil {
return err
}
type contextLoader struct {
logger logr.Logger
rclient registryclient.Client
client dclient.Interface
cmResolver engineapi.ConfigmapResolver
}
func (l *contextLoader) Load(ctx context.Context, contextEntries []kyvernov1.ContextEntry, enginectx enginecontext.Interface) error {
for _, entry := range contextEntries {
if entry.ConfigMap != nil {
if err := loadConfigMap(ctx, l.logger, entry, enginectx, l.cmResolver); err != nil {
return err
}
}
} else {
for _, entry := range contextEntries {
if entry.ConfigMap != nil {
if err := loadConfigMap(ctx, logger, entry, enginectx); err != nil {
return err
}
} else if entry.APICall != nil {
if err := loadAPIData(ctx, logger, entry, enginectx); err != nil {
return err
}
} else if entry.ImageRegistry != nil {
if err := loadImageData(ctx, rclient, logger, entry, enginectx); err != nil {
return err
}
} else if entry.Variable != nil {
if err := loadVariable(logger, entry, enginectx); err != nil {
return err
}
} else if entry.APICall != nil {
if err := loadAPIData(ctx, l.logger, entry, enginectx, l.client); err != nil {
return err
}
} else if entry.ImageRegistry != nil {
if err := loadImageData(ctx, l.rclient, l.logger, entry, enginectx); err != nil {
return err
}
} else if entry.Variable != nil {
if err := loadVariable(l.logger, entry, enginectx); err != nil {
return err
}
}
}
return nil
}
func loadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyContext) (err error) {
type mockContextLoader struct {
logger logr.Logger
policyName string
ruleName string
rclient registryclient.Client
client dclient.Interface
cmResolver engineapi.ConfigmapResolver
}
func (l *mockContextLoader) Load(ctx context.Context, contextEntries []kyvernov1.ContextEntry, enginectx enginecontext.Interface) error {
rule := store.GetPolicyRule(l.policyName, l.ruleName)
if rule != nil && len(rule.Values) > 0 {
variables := rule.Values
for key, value := range variables {
if err := enginectx.AddVariable(key, value); err != nil {
return err
}
}
}
hasRegistryAccess := store.GetRegistryAccess()
// Context Variable should be loaded after the values loaded from values file
for _, entry := range contextEntries {
if entry.ImageRegistry != nil && hasRegistryAccess {
rclient := store.GetRegistryClient()
if err := loadImageData(ctx, rclient, l.logger, entry, enginectx); err != nil {
return err
}
} else if entry.Variable != nil {
if err := loadVariable(l.logger, entry, enginectx); err != nil {
return err
}
} else if entry.APICall != nil && store.IsApiCallAllowed() {
if err := loadAPIData(ctx, l.logger, entry, enginectx, l.client); err != nil {
return err
}
}
}
if rule != nil && len(rule.ForEachValues) > 0 {
for key, value := range rule.ForEachValues {
if err := enginectx.AddVariable(key, value[store.GetForeachElement()]); err != nil {
return err
}
}
}
return nil
}
func LoadContext(ctx context.Context, factory ContextLoaderFactory, contextEntries []kyvernov1.ContextEntry, pContext *PolicyContext, ruleName string) error {
return factory(pContext, ruleName).Load(ctx, contextEntries, pContext.JSONContext())
}
func loadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx enginecontext.Interface) (err error) {
path := ""
if entry.Variable.JMESPath != "" {
jp, err := variables.SubstituteAll(logger, ctx.jsonContext, entry.Variable.JMESPath)
jp, err := variables.SubstituteAll(logger, ctx, entry.Variable.JMESPath)
if err != nil {
return fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.Variable.JMESPath, err)
}
@ -100,7 +142,7 @@ func loadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyC
if err != nil {
return fmt.Errorf("invalid default for variable %s", entry.Name)
}
defaultValue, err = variables.SubstituteAll(logger, ctx.jsonContext, value)
defaultValue, err = variables.SubstituteAll(logger, ctx, value)
if err != nil {
return fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.Variable.Default, err)
}
@ -109,7 +151,7 @@ func loadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyC
var output interface{} = defaultValue
if entry.Variable.Value != nil {
value, _ := variables.DocumentToUntyped(entry.Variable.Value)
variable, err := variables.SubstituteAll(logger, ctx.jsonContext, value)
variable, err := variables.SubstituteAll(logger, ctx, value)
if err != nil {
return fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.Variable.Value, err)
}
@ -125,7 +167,7 @@ func loadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyC
}
} else {
if path != "" {
if variable, err := ctx.jsonContext.Query(path); err == nil {
if variable, err := ctx.Query(path); err == nil {
output = variable
} else if defaultValue == nil {
return fmt.Errorf("failed to apply jmespath %s to variable %v", path, err)
@ -137,13 +179,13 @@ func loadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyC
return fmt.Errorf("unable to add context entry for variable %s since it evaluated to nil", entry.Name)
}
if outputBytes, err := json.Marshal(output); err == nil {
return ctx.jsonContext.ReplaceContextEntry(entry.Name, outputBytes)
return ctx.ReplaceContextEntry(entry.Name, outputBytes)
} else {
return fmt.Errorf("unable to add context entry for variable %s: %w", entry.Name, err)
}
}
func loadImageData(ctx context.Context, rclient registryclient.Client, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx *PolicyContext) error {
func loadImageData(ctx context.Context, rclient registryclient.Client, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface) error {
imageData, err := fetchImageData(ctx, rclient, logger, entry, enginectx)
if err != nil {
return err
@ -152,14 +194,14 @@ func loadImageData(ctx context.Context, rclient registryclient.Client, logger lo
if err != nil {
return err
}
if err := enginectx.jsonContext.AddContextEntry(entry.Name, jsonBytes); err != nil {
if err := enginectx.AddContextEntry(entry.Name, jsonBytes); err != nil {
return fmt.Errorf("failed to add resource data to context: contextEntry: %v, error: %v", entry, err)
}
return nil
}
func fetchImageData(ctx context.Context, rclient registryclient.Client, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx *PolicyContext) (interface{}, error) {
ref, err := variables.SubstituteAll(logger, enginectx.jsonContext, entry.ImageRegistry.Reference)
func fetchImageData(ctx context.Context, rclient registryclient.Client, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface) (interface{}, error) {
ref, err := variables.SubstituteAll(logger, enginectx, entry.ImageRegistry.Reference)
if err != nil {
return nil, fmt.Errorf("ailed to substitute variables in context entry %s %s: %v", entry.Name, entry.ImageRegistry.Reference, err)
}
@ -167,7 +209,7 @@ func fetchImageData(ctx context.Context, rclient registryclient.Client, logger l
if !ok {
return nil, fmt.Errorf("invalid image reference %s, image reference must be a string", ref)
}
path, err := variables.SubstituteAll(logger, enginectx.jsonContext, entry.ImageRegistry.JMESPath)
path, err := variables.SubstituteAll(logger, enginectx, entry.ImageRegistry.JMESPath)
if err != nil {
return nil, fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.ImageRegistry.JMESPath, err)
}
@ -239,16 +281,14 @@ func fetchImageDataMap(ctx context.Context, rclient registryclient.Client, ref s
return untyped, nil
}
func loadAPIData(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx *PolicyContext) error {
executor, err := apicall.New(ctx, entry, enginectx.JSONContext(), enginectx.Client(), logger)
func loadAPIData(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface, client dclient.Interface) error {
executor, err := apicall.New(ctx, entry, enginectx, client, logger)
if err != nil {
return errors.Wrapf(err, "failed to initialize APICall")
}
if _, err := executor.Execute(); err != nil {
return errors.Wrapf(err, "failed to execute APICall")
}
return nil
}
@ -257,33 +297,30 @@ func applyJMESPath(jmesPath string, data interface{}) (interface{}, error) {
if err != nil {
return nil, fmt.Errorf("failed to compile JMESPath: %s, error: %v", jmesPath, err)
}
return jp.Search(data)
}
func loadConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx *PolicyContext) error {
data, err := fetchConfigMap(ctx, logger, entry, enginectx)
func loadConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface, resolver engineapi.ConfigmapResolver) error {
data, err := fetchConfigMap(ctx, logger, entry, enginectx, resolver)
if err != nil {
return fmt.Errorf("failed to retrieve config map for context entry %s: %v", entry.Name, err)
}
err = enginectx.jsonContext.AddContextEntry(entry.Name, data)
err = enginectx.AddContextEntry(entry.Name, data)
if err != nil {
return fmt.Errorf("failed to add config map for context entry %s: %v", entry.Name, err)
}
return nil
}
func fetchConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx *PolicyContext) ([]byte, error) {
func fetchConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface, resolver engineapi.ConfigmapResolver) ([]byte, error) {
contextData := make(map[string]interface{})
name, err := variables.SubstituteAll(logger, enginectx.jsonContext, entry.ConfigMap.Name)
name, err := variables.SubstituteAll(logger, enginectx, entry.ConfigMap.Name)
if err != nil {
return nil, fmt.Errorf("failed to substitute variables in context %s configMap.name %s: %v", entry.Name, entry.ConfigMap.Name, err)
}
namespace, err := variables.SubstituteAll(logger, enginectx.jsonContext, entry.ConfigMap.Namespace)
namespace, err := variables.SubstituteAll(logger, enginectx, entry.ConfigMap.Namespace)
if err != nil {
return nil, fmt.Errorf("failed to substitute variables in context %s configMap.namespace %s: %v", entry.Name, entry.ConfigMap.Namespace, err)
}
@ -292,7 +329,7 @@ func fetchConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.Con
namespace = "default"
}
obj, err := enginectx.informerCacheResolvers.Get(ctx, namespace.(string), name.(string))
obj, err := resolver.Get(ctx, namespace.(string), name.(string))
if err != nil {
return nil, fmt.Errorf("failed to get configmap %s/%s : %v", namespace, name, err)
}

View file

@ -13,7 +13,6 @@ import (
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/engine/mutate"
"github.com/kyverno/kyverno/pkg/logging"
"github.com/kyverno/kyverno/pkg/registryclient"
"github.com/kyverno/kyverno/pkg/tracing"
"github.com/kyverno/kyverno/pkg/utils/api"
"go.opentelemetry.io/otel/trace"
@ -22,7 +21,11 @@ import (
)
// Mutate performs mutation. Overlay first and then mutation patches
func Mutate(ctx context.Context, rclient registryclient.Client, policyContext *PolicyContext) (resp *engineapi.EngineResponse) {
func Mutate(
ctx context.Context,
contextLoader ContextLoaderFactory,
policyContext *PolicyContext,
) (resp *engineapi.EngineResponse) {
startTime := time.Now()
policy := policyContext.policy
resp = &engineapi.EngineResponse{
@ -89,7 +92,7 @@ func Mutate(ctx context.Context, rclient registryclient.Client, policyContext *P
logger.Error(err, "failed to query resource object")
}
if err := LoadContext(ctx, logger, rclient, rule.Context, policyContext, rule.Name); err != nil {
if err := LoadContext(ctx, contextLoader, rule.Context, policyContext, rule.Name); err != nil {
if _, ok := err.(gojmespath.NotFoundError); ok {
logger.V(3).Info("failed to load context", "reason", err.Error())
} else {
@ -142,7 +145,7 @@ func Mutate(ctx context.Context, rclient registryclient.Client, policyContext *P
policyContext: policyContext,
resource: patchedResource,
log: logger,
rclient: rclient,
contextLoader: contextLoader,
nesting: 0,
}
@ -202,7 +205,7 @@ type forEachMutator struct {
foreach []kyvernov1.ForEachMutation
resource resourceInfo
nesting int
rclient registryclient.Client
contextLoader ContextLoaderFactory
log logr.Logger
}
@ -211,7 +214,7 @@ func (f *forEachMutator) mutateForEach(ctx context.Context) *mutate.Response {
allPatches := make([][]byte, 0)
for _, foreach := range f.foreach {
if err := LoadContext(ctx, f.log, f.rclient, f.rule.Context, f.policyContext, f.rule.Name); err != nil {
if err := LoadContext(ctx, f.contextLoader, f.rule.Context, f.policyContext, f.rule.Name); err != nil {
f.log.Error(err, "failed to load context")
return mutate.NewErrorResponse("failed to load context", err)
}
@ -276,7 +279,7 @@ func (f *forEachMutator) mutateElements(ctx context.Context, foreach kyvernov1.F
return mutate.NewErrorResponse(fmt.Sprintf("failed to add element to mutate.foreach[%d].context", index), err)
}
if err := LoadContext(ctx, f.log, f.rclient, foreach.Context, policyContext, f.rule.Name); err != nil {
if err := LoadContext(ctx, f.contextLoader, foreach.Context, policyContext, f.rule.Name); err != nil {
return mutate.NewErrorResponse(fmt.Sprintf("failed to load to mutate.foreach[%d].context", index), err)
}
@ -304,6 +307,7 @@ func (f *forEachMutator) mutateElements(ctx context.Context, foreach kyvernov1.F
log: f.log,
foreach: nestedForEach,
nesting: f.nesting + 1,
contextLoader: f.contextLoader,
}
mutateResp = m.mutateForEach(ctx)

View file

@ -20,6 +20,18 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
)
func doMutate(
ctx context.Context,
rclient registryclient.Client,
pContext *PolicyContext,
) *engineapi.EngineResponse {
return Mutate(
ctx,
LegacyContextLoaderFactory(rclient),
pContext,
)
}
func Test_VariableSubstitutionPatchStrategicMerge(t *testing.T) {
policyRaw := []byte(`{
"apiVersion": "kyverno.io/v1",
@ -95,7 +107,7 @@ func Test_VariableSubstitutionPatchStrategicMerge(t *testing.T) {
jsonContext: ctx,
newResource: *resourceUnstructured,
}
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext)
t.Log(string(expectedPatch))
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
@ -169,7 +181,7 @@ func Test_variableSubstitutionPathNotExist(t *testing.T) {
jsonContext: ctx,
newResource: *resourceUnstructured,
}
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Assert(t, strings.Contains(er.PolicyResponse.Rules[0].Message, "Unknown key \"name1\" in path"))
}
@ -262,7 +274,7 @@ func Test_variableSubstitutionCLI(t *testing.T) {
newResource: *resourceUnstructured,
}
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1)
t.Log(string(expectedPatch))
@ -371,7 +383,7 @@ func Test_chained_rules(t *testing.T) {
err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx)
assert.NilError(t, err)
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext)
containers, _, err := unstructured.NestedSlice(er.PatchedResource.Object, "spec", "containers")
assert.NilError(t, err)
assert.Equal(t, containers[0].(map[string]interface{})["image"], "otherregistry.corp.com/foo/bash:5.0")
@ -459,7 +471,7 @@ func Test_precondition(t *testing.T) {
newResource: *resourceUnstructured,
}
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext)
t.Log(string(expectedPatch))
t.Log(string(er.PolicyResponse.Rules[0].Patches[0]))
if !reflect.DeepEqual(expectedPatch, er.PolicyResponse.Rules[0].Patches[0]) {
@ -556,7 +568,7 @@ func Test_nonZeroIndexNumberPatchesJson6902(t *testing.T) {
newResource: *resourceUnstructured,
}
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext)
t.Log(string(expectedPatch))
t.Log(string(er.PolicyResponse.Rules[0].Patches[0]))
if !reflect.DeepEqual(expectedPatch, er.PolicyResponse.Rules[0].Patches[0]) {
@ -650,7 +662,7 @@ func Test_foreach(t *testing.T) {
err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx)
assert.NilError(t, err)
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass)
@ -757,7 +769,7 @@ func Test_foreach_element_mutation(t *testing.T) {
err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx)
assert.NilError(t, err)
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass)
@ -883,7 +895,7 @@ func Test_Container_InitContainer_foreach(t *testing.T) {
err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx)
assert.NilError(t, err)
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass)
@ -1033,7 +1045,7 @@ func testApplyPolicyToResource(t *testing.T, policyRaw, resourceRaw []byte) *eng
err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx)
assert.NilError(t, err)
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext)
return er
}
@ -1584,7 +1596,7 @@ func Test_mutate_existing_resources(t *testing.T) {
newResource: *trigger,
}
}
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext)
for _, rr := range er.PolicyResponse.Rules {
for i, p := range rr.Patches {
@ -1692,7 +1704,7 @@ func Test_RuleSelectorMutate(t *testing.T) {
newResource: *resourceUnstructured,
}
er := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
er := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(er.PolicyResponse.Rules), 2)
assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1)
assert.Equal(t, len(er.PolicyResponse.Rules[1].Patches), 1)
@ -1707,7 +1719,7 @@ func Test_RuleSelectorMutate(t *testing.T) {
applyOne := kyverno.ApplyOne
policyContext.policy.GetSpec().ApplyRules = &applyOne
er = Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
er = doMutate(context.TODO(), registryclient.NewOrDie(), policyContext)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1)
@ -2074,7 +2086,7 @@ func Test_SpecialCharacters(t *testing.T) {
}
// Mutate and make sure that we got the expected amount of rules.
patches := Mutate(context.TODO(), registryclient.NewOrDie(), policyContext).GetPatches()
patches := doMutate(context.TODO(), registryclient.NewOrDie(), policyContext).GetPatches()
if !reflect.DeepEqual(patches, tt.want) {
t.Errorf("Mutate() got patches %s, expected %s", patches, tt.want)
}

View file

@ -20,7 +20,6 @@ import (
"github.com/kyverno/kyverno/pkg/engine/variables"
"github.com/kyverno/kyverno/pkg/logging"
"github.com/kyverno/kyverno/pkg/pss"
"github.com/kyverno/kyverno/pkg/registryclient"
"github.com/kyverno/kyverno/pkg/tracing"
"github.com/kyverno/kyverno/pkg/utils/api"
datautils "github.com/kyverno/kyverno/pkg/utils/data"
@ -37,7 +36,12 @@ import (
)
// Validate applies validation rules from policy on the resource
func Validate(ctx context.Context, rclient registryclient.Client, policyContext *PolicyContext, cfg config.Configuration) (resp *engineapi.EngineResponse) {
func Validate(
ctx context.Context,
contextLoader ContextLoaderFactory,
policyContext *PolicyContext,
cfg config.Configuration,
) (resp *engineapi.EngineResponse) {
resp = &engineapi.EngineResponse{}
startTime := time.Now()
@ -48,7 +52,7 @@ func Validate(ctx context.Context, rclient registryclient.Client, policyContext
logger.V(4).Info("finished policy processing", "processingTime", resp.PolicyResponse.ProcessingTime.String(), "validationRulesApplied", resp.PolicyResponse.RulesAppliedCount)
}()
resp = validateResource(ctx, logger, rclient, policyContext, cfg)
resp = validateResource(ctx, contextLoader, logger, policyContext, cfg)
resp.NamespaceLabels = policyContext.namespaceLabels
return
}
@ -97,7 +101,13 @@ func buildResponse(ctx *PolicyContext, resp *engineapi.EngineResponse, startTime
resp.PolicyResponse.Timestamp = startTime.Unix()
}
func validateResource(ctx context.Context, log logr.Logger, rclient registryclient.Client, enginectx *PolicyContext, cfg config.Configuration) *engineapi.EngineResponse {
func validateResource(
ctx context.Context,
contextLoader ContextLoaderFactory,
log logr.Logger,
enginectx *PolicyContext,
cfg config.Configuration,
) *engineapi.EngineResponse {
resp := &engineapi.EngineResponse{}
enginectx.jsonContext.Checkpoint()
@ -148,9 +158,9 @@ func validateResource(ctx context.Context, log logr.Logger, rclient registryclie
log.V(3).Info("processing validation rule", "matchCount", matchCount, "applyRules", applyRules)
enginectx.jsonContext.Reset()
if hasValidate && !hasYAMLSignatureVerify {
return processValidationRule(ctx, log, rclient, enginectx, rule)
return processValidationRule(ctx, contextLoader, log, enginectx, rule)
} else if hasValidateImage {
return processImageValidationRule(ctx, log, rclient, enginectx, rule, cfg)
return processImageValidationRule(ctx, contextLoader, log, enginectx, rule, cfg)
} else if hasYAMLSignatureVerify {
return processYAMLValidationRule(log, enginectx, rule)
}
@ -168,8 +178,14 @@ func validateResource(ctx context.Context, log logr.Logger, rclient registryclie
return resp
}
func processValidationRule(ctx context.Context, log logr.Logger, rclient registryclient.Client, policyContext *PolicyContext, rule *kyvernov1.Rule) *engineapi.RuleResponse {
v := newValidator(log, rclient, policyContext, rule)
func processValidationRule(
ctx context.Context,
contextLoader ContextLoaderFactory,
log logr.Logger,
policyContext *PolicyContext,
rule *kyvernov1.Rule,
) *engineapi.RuleResponse {
v := newValidator(log, contextLoader, policyContext, rule)
return v.validate(ctx)
}
@ -198,17 +214,17 @@ type validator struct {
deny *kyvernov1.Deny
podSecurity *kyvernov1.PodSecurity
forEach []kyvernov1.ForEachValidation
rclient registryclient.Client
contextLoader ContextLoaderFactory
nesting int
}
func newValidator(log logr.Logger, rclient registryclient.Client, ctx *PolicyContext, rule *kyvernov1.Rule) *validator {
func newValidator(log logr.Logger, contextLoader ContextLoaderFactory, ctx *PolicyContext, rule *kyvernov1.Rule) *validator {
ruleCopy := rule.DeepCopy()
return &validator{
log: log,
rule: ruleCopy,
policyContext: ctx,
rclient: rclient,
contextLoader: contextLoader,
contextEntries: ruleCopy.Context,
anyAllConditions: ruleCopy.GetAnyAllConditions(),
pattern: ruleCopy.Validation.GetPattern(),
@ -219,7 +235,14 @@ func newValidator(log logr.Logger, rclient registryclient.Client, ctx *PolicyCon
}
}
func newForEachValidator(foreach kyvernov1.ForEachValidation, rclient registryclient.Client, nesting int, rule *kyvernov1.Rule, ctx *PolicyContext, log logr.Logger) (*validator, error) {
func newForEachValidator(
foreach kyvernov1.ForEachValidation,
contextLoader ContextLoaderFactory,
nesting int,
rule *kyvernov1.Rule,
ctx *PolicyContext,
log logr.Logger,
) (*validator, error) {
ruleCopy := rule.DeepCopy()
anyAllConditions, err := datautils.ToMap(foreach.AnyAllConditions)
if err != nil {
@ -235,7 +258,7 @@ func newForEachValidator(foreach kyvernov1.ForEachValidation, rclient registrycl
log: log,
policyContext: ctx,
rule: ruleCopy,
rclient: rclient,
contextLoader: contextLoader,
contextEntries: foreach.Context,
anyAllConditions: anyAllConditions,
pattern: foreach.GetPattern(),
@ -298,7 +321,7 @@ func (v *validator) validateForEach(ctx context.Context) *engineapi.RuleResponse
continue
}
resp, count := v.validateElements(ctx, v.rclient, foreach, elements, foreach.ElementScope)
resp, count := v.validateElements(ctx, foreach, elements, foreach.ElementScope)
if resp.Status != engineapi.RuleStatusPass {
return resp
}
@ -317,7 +340,7 @@ func (v *validator) validateForEach(ctx context.Context) *engineapi.RuleResponse
return ruleResponse(*v.rule, engineapi.Validation, "rule passed", engineapi.RuleStatusPass)
}
func (v *validator) validateElements(ctx context.Context, rclient registryclient.Client, foreach kyvernov1.ForEachValidation, elements []interface{}, elementScope *bool) (*engineapi.RuleResponse, int) {
func (v *validator) validateElements(ctx context.Context, foreach kyvernov1.ForEachValidation, elements []interface{}, elementScope *bool) (*engineapi.RuleResponse, int) {
v.policyContext.jsonContext.Checkpoint()
defer v.policyContext.jsonContext.Restore()
applyCount := 0
@ -334,7 +357,7 @@ func (v *validator) validateElements(ctx context.Context, rclient registryclient
return ruleError(v.rule, engineapi.Validation, "failed to process foreach", err), applyCount
}
foreachValidator, err := newForEachValidator(foreach, rclient, v.nesting+1, v.rule, policyContext, v.log)
foreachValidator, err := newForEachValidator(foreach, v.contextLoader, v.nesting+1, v.rule, policyContext, v.log)
if err != nil {
v.log.Error(err, "failed to create foreach validator")
return ruleError(v.rule, engineapi.Validation, "failed to create foreach validator", err), applyCount
@ -399,7 +422,7 @@ func addElementToContext(ctx *PolicyContext, element interface{}, index, nesting
}
func (v *validator) loadContext(ctx context.Context) error {
if err := LoadContext(ctx, v.log, v.rclient, v.contextEntries, v.policyContext, v.rule.Name); err != nil {
if err := LoadContext(ctx, v.contextLoader, v.contextEntries, v.policyContext, v.rule.Name); err != nil {
if _, ok := err.(gojmespath.NotFoundError); ok {
v.log.V(3).Info("failed to load context", "reason", err.Error())
} else {

View file

@ -9,6 +9,7 @@ import (
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
urkyverno "github.com/kyverno/kyverno/api/kyverno/v1beta1"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/store"
"github.com/kyverno/kyverno/pkg/config"
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/registryclient"
@ -18,6 +19,15 @@ import (
admissionv1 "k8s.io/api/admission/v1"
)
func doValidate(ctx context.Context, rclient registryclient.Client, pContext *PolicyContext, cfg config.Configuration) *engineapi.EngineResponse {
return Validate(
ctx,
LegacyContextLoaderFactory(rclient),
pContext,
cfg,
)
}
func TestValidate_image_tag_fail(t *testing.T) {
// If image tag is latest then imagepull policy needs to be checked
rawPolicy := []byte(`{
@ -111,7 +121,7 @@ func TestValidate_image_tag_fail(t *testing.T) {
"validation error: imagePullPolicy 'Always' required with tag 'latest'. rule validate-latest failed at path /spec/containers/0/imagePullPolicy/",
}
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
for index, r := range er.PolicyResponse.Rules {
assert.Equal(t, r.Message, msgs[index])
}
@ -211,7 +221,7 @@ func TestValidate_image_tag_pass(t *testing.T) {
"validation rule 'validate-tag' passed.",
"validation rule 'validate-latest' passed.",
}
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
for index, r := range er.PolicyResponse.Rules {
assert.Equal(t, r.Message, msgs[index])
}
@ -285,7 +295,7 @@ func TestValidate_Fail_anyPattern(t *testing.T) {
resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
assert.Assert(t, !er.IsSuccessful())
msgs := []string{"validation error: A namespace is required. rule check-default-namespace[0] failed at path /metadata/namespace/ rule check-default-namespace[1] failed at path /metadata/namespace/"}
@ -368,7 +378,7 @@ func TestValidate_host_network_port(t *testing.T) {
resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
msgs := []string{"validation error: Host network and port are not allowed. rule validate-host-network-port failed at path /spec/containers/0/ports/0/hostPort/"}
for index, r := range er.PolicyResponse.Rules {
@ -458,7 +468,7 @@ func TestValidate_anchor_arraymap_pass(t *testing.T) {
resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
msgs := []string{"validation rule 'validate-host-path' passed."}
for index, r := range er.PolicyResponse.Rules {
@ -546,7 +556,7 @@ func TestValidate_anchor_arraymap_fail(t *testing.T) {
assert.NilError(t, err)
resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
msgs := []string{"validation error: Host path '/var/lib/' is not allowed. rule validate-host-path failed at path /spec/volumes/0/hostPath/path/"}
for index, r := range er.PolicyResponse.Rules {
@ -616,7 +626,7 @@ func TestValidate_anchor_map_notfound(t *testing.T) {
resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
msgs := []string{"validation rule 'pod rule 2' passed."}
for index, r := range er.PolicyResponse.Rules {
@ -689,7 +699,7 @@ func TestValidate_anchor_map_found_valid(t *testing.T) {
resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
msgs := []string{"validation rule 'pod rule 2' passed."}
for index, r := range er.PolicyResponse.Rules {
@ -763,7 +773,7 @@ func TestValidate_inequality_List_Processing(t *testing.T) {
resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
msgs := []string{"validation rule 'pod rule 2' passed."}
for index, r := range er.PolicyResponse.Rules {
@ -843,7 +853,7 @@ func TestValidate_inequality_List_ProcessingBrackets(t *testing.T) {
resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
msgs := []string{"validation rule 'pod rule 2' passed."}
for index, r := range er.PolicyResponse.Rules {
@ -917,7 +927,7 @@ func TestValidate_anchor_map_found_invalid(t *testing.T) {
resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
msgs := []string{"validation error: pod: validate run as non root user. rule pod rule 2 failed at path /spec/securityContext/runAsNonRoot/"}
for index, r := range er.PolicyResponse.Rules {
@ -992,7 +1002,7 @@ func TestValidate_AnchorList_pass(t *testing.T) {
resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
msgs := []string{"validation rule 'pod image rule' passed."}
for index, r := range er.PolicyResponse.Rules {
@ -1067,7 +1077,7 @@ func TestValidate_AnchorList_fail(t *testing.T) {
resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
assert.Assert(t, !er.IsSuccessful())
}
@ -1137,7 +1147,7 @@ func TestValidate_existenceAnchor_fail(t *testing.T) {
resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
assert.Assert(t, !er.IsSuccessful())
}
@ -1207,7 +1217,7 @@ func TestValidate_existenceAnchor_pass(t *testing.T) {
resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
msgs := []string{"validation rule 'pod image rule' passed."}
for index, r := range er.PolicyResponse.Rules {
@ -1295,7 +1305,7 @@ func TestValidate_negationAnchor_deny(t *testing.T) {
resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
msgs := []string{"validation error: Host path is not allowed. rule validate-host-path failed at path /spec/volumes/0/hostPath/"}
for index, r := range er.PolicyResponse.Rules {
@ -1382,7 +1392,7 @@ func TestValidate_negationAnchor_pass(t *testing.T) {
resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
msgs := []string{"validation rule 'validate-host-path' passed."}
for index, r := range er.PolicyResponse.Rules {
@ -1459,7 +1469,7 @@ func Test_VariableSubstitutionPathNotExistInPattern(t *testing.T) {
jsonContext: ctx,
newResource: *resourceUnstructured,
}
er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusError)
@ -1553,7 +1563,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_OnePatternStatisfiesButSu
jsonContext: ctx,
newResource: *resourceUnstructured,
}
er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusError)
@ -1615,7 +1625,7 @@ func Test_VariableSubstitution_NotOperatorWithStringVariable(t *testing.T) {
jsonContext: ctx,
newResource: *resourceUnstructured,
}
er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail)
assert.Equal(t, er.PolicyResponse.Rules[0].Message, "validation error: rule not-operator-with-variable-should-alway-fail-validation failed at path /spec/content/")
}
@ -1707,7 +1717,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathNotPresent(t *test
jsonContext: ctx,
newResource: *resourceUnstructured,
}
er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, len(er.PolicyResponse.Rules), 1)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusError)
@ -1801,7 +1811,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathPresent_NonePatter
jsonContext: ctx,
newResource: *resourceUnstructured,
}
er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail)
assert.Equal(t, er.PolicyResponse.Rules[0].Message,
@ -1907,7 +1917,7 @@ func Test_VariableSubstitutionValidate_VariablesInMessageAreResolved(t *testing.
jsonContext: ctx,
newResource: *resourceUnstructured,
}
er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail)
assert.Equal(t, er.PolicyResponse.Rules[0].Message, "The animal cow is not in the allowed list of animals.")
}
@ -1961,7 +1971,7 @@ func Test_Flux_Kustomization_PathNotPresent(t *testing.T) {
jsonContext: ctx,
newResource: *resourceUnstructured,
}
er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
for i, rule := range er.PolicyResponse.Rules {
assert.Equal(t, er.PolicyResponse.Rules[i].Status, test.expectedResults[i], "\ntest %s failed\nexpected: %s\nactual: %s", test.name, test.expectedResults[i], er.PolicyResponse.Rules[i].Status)
@ -2127,7 +2137,7 @@ func executeTest(t *testing.T, test testCase) {
jsonContext: ctx,
}
resp := Validate(context.TODO(), registryclient.NewOrDie(), pc, cfg)
resp := doValidate(context.TODO(), registryclient.NewOrDie(), pc, cfg)
if resp.IsSuccessful() && test.requestDenied {
t.Errorf("Testcase has failed, policy: %v", policy.Name)
}
@ -2222,7 +2232,7 @@ func TestValidate_context_variable_substitution_CLI(t *testing.T) {
msgs := []string{
"restrict pod counts to be no more than 10 on node minikube",
}
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
for index, r := range er.PolicyResponse.Rules {
assert.Equal(t, r.Message, msgs[index])
}
@ -2311,7 +2321,7 @@ func Test_EmptyStringInDenyCondition(t *testing.T) {
resourceUnstructured, err := kubeutils.BytesToUnstructured(resourceRaw)
assert.NilError(t, err)
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx}, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx}, cfg)
assert.Assert(t, !er.IsSuccessful())
}
@ -2400,7 +2410,7 @@ func Test_StringInDenyCondition(t *testing.T) {
resourceUnstructured, err := kubeutils.BytesToUnstructured(resourceRaw)
assert.NilError(t, err)
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx}, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx}, cfg)
assert.Assert(t, er.IsSuccessful())
}
@ -3071,7 +3081,7 @@ func testForEach(t *testing.T, policyraw []byte, resourceRaw []byte, msg string,
jsonContext: ctx,
newResource: *resourceUnstructured,
}
er := Validate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
assert.Equal(t, er.PolicyResponse.Rules[0].Status, status)
if msg != "" {
@ -3135,7 +3145,7 @@ func Test_delete_ignore_pattern(t *testing.T) {
jsonContext: ctx,
newResource: *resourceUnstructured,
}
engineResponseCreate := Validate(context.TODO(), registryclient.NewOrDie(), policyContextCreate, cfg)
engineResponseCreate := doValidate(context.TODO(), registryclient.NewOrDie(), policyContextCreate, cfg)
assert.Equal(t, len(engineResponseCreate.PolicyResponse.Rules), 1)
assert.Equal(t, engineResponseCreate.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail)
@ -3144,7 +3154,7 @@ func Test_delete_ignore_pattern(t *testing.T) {
jsonContext: ctx,
oldResource: *resourceUnstructured,
}
engineResponseDelete := Validate(context.TODO(), registryclient.NewOrDie(), policyContextDelete, cfg)
engineResponseDelete := doValidate(context.TODO(), registryclient.NewOrDie(), policyContextDelete, cfg)
assert.Equal(t, len(engineResponseDelete.PolicyResponse.Rules), 0)
}
@ -3203,7 +3213,7 @@ func Test_ValidatePattern_anyPattern(t *testing.T) {
resourceUnstructured, err := kubeutils.BytesToUnstructured(tc.rawResource)
assert.NilError(t, err)
er := Validate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
er := doValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg)
if tc.expectedFailed {
assert.Assert(t, er.IsFailed())
} else if tc.expectedSkipped {

View file

@ -25,7 +25,6 @@ import (
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/event"
"github.com/kyverno/kyverno/pkg/metrics"
"github.com/kyverno/kyverno/pkg/registryclient"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
"github.com/pkg/errors"
"go.uber.org/multierr"
@ -58,7 +57,7 @@ const (
type PolicyController struct {
client dclient.Interface
kyvernoClient versioned.Interface
rclient registryclient.Client
contextLoader engine.ContextLoaderFactory
pInformer kyvernov1informers.ClusterPolicyInformer
npInformer kyvernov1informers.PolicyInformer
@ -99,7 +98,7 @@ type PolicyController struct {
func NewPolicyController(
kyvernoClient versioned.Interface,
client dclient.Interface,
rclient registryclient.Client,
contextLoader engine.ContextLoaderFactory,
pInformer kyvernov1informers.ClusterPolicyInformer,
npInformer kyvernov1informers.PolicyInformer,
urInformer kyvernov1beta1informers.UpdateRequestInformer,
@ -120,7 +119,7 @@ func NewPolicyController(
pc := PolicyController{
client: client,
kyvernoClient: kyvernoClient,
rclient: rclient,
contextLoader: contextLoader,
pInformer: pInformer,
npInformer: npInformer,
eventGen: eventGen,
@ -514,7 +513,7 @@ func (pc *PolicyController) handleUpdateRequest(ur *kyvernov1beta1.UpdateRequest
return false, errors.Wrapf(err, "failed to build policy context for rule %s", rule.Name)
}
engineResponse := engine.ApplyBackgroundChecks(pc.rclient, policyContext)
engineResponse := engine.ApplyBackgroundChecks(pc.contextLoader, policyContext)
if len(engineResponse.PolicyResponse.Rules) == 0 {
return true, nil
}

View file

@ -147,7 +147,11 @@ func runTestCase(t *testing.T, tc TestCase) bool {
policyContext := engine.NewPolicyContext().WithPolicy(policy).WithNewResource(*resource)
er := engine.Mutate(context.TODO(), registryclient.NewOrDie(), policyContext)
er := engine.Mutate(
context.TODO(),
engine.LegacyContextLoaderFactory(registryclient.NewOrDie()),
policyContext,
)
t.Log("---Mutation---")
validateResource(t, er.PatchedResource, tc.Expected.Mutation.PatchedResource)
validateResponse(t, er.PolicyResponse, tc.Expected.Mutation.PolicyResponse)
@ -160,7 +164,12 @@ func runTestCase(t *testing.T, tc TestCase) bool {
policyContext = policyContext.WithNewResource(*resource)
cfg := config.NewDefaultConfiguration()
er = engine.Validate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg)
er = engine.Validate(
context.TODO(),
engine.LegacyContextLoaderFactory(registryclient.NewOrDie()),
policyContext,
cfg,
)
t.Log("---Validation---")
validateResponse(t, er.PolicyResponse, tc.Expected.Validation.PolicyResponse)
@ -176,7 +185,10 @@ func runTestCase(t *testing.T, tc TestCase) bool {
} else {
policyContext := policyContext.WithClient(client)
er = engine.ApplyBackgroundChecks(registryclient.NewOrDie(), policyContext)
er = engine.ApplyBackgroundChecks(
engine.LegacyContextLoaderFactory(registryclient.NewOrDie()),
policyContext,
)
t.Log(("---Generation---"))
validateResponse(t, er.PolicyResponse, tc.Expected.Generation.PolicyResponse)
// Expected generate resource will be in same namespaces as resource

View file

@ -7,6 +7,7 @@ import (
kyvernoinformers "github.com/kyverno/kyverno/pkg/client/informers/externalversions"
"github.com/kyverno/kyverno/pkg/clients/dclient"
"github.com/kyverno/kyverno/pkg/config"
"github.com/kyverno/kyverno/pkg/engine"
"github.com/kyverno/kyverno/pkg/engine/context/resolvers"
"github.com/kyverno/kyverno/pkg/event"
"github.com/kyverno/kyverno/pkg/metrics"
@ -38,10 +39,11 @@ func NewFakeHandlers(ctx context.Context, policyCache policycache.Cache) webhook
crbLister := informers.Rbac().V1().ClusterRoleBindings().Lister()
urLister := kyvernoInformers.Kyverno().V1beta1().UpdateRequests().Lister().UpdateRequests(config.KyvernoNamespace())
peLister := kyvernoInformers.Kyverno().V2alpha1().PolicyExceptions().Lister()
rclient := registryclient.NewOrDie()
return &handlers{
client: dclient,
rclient: registryclient.NewOrDie(),
rclient: rclient,
configuration: configuration,
metricsConfig: metricsConfig,
pCache: policyCache,
@ -54,5 +56,6 @@ func NewFakeHandlers(ctx context.Context, policyCache policycache.Cache) webhook
openApiManager: openapi.NewFake(),
pcBuilder: webhookutils.NewPolicyContextBuilder(configuration, dclient, rbLister, crbLister, configMapResolver, peLister),
urUpdater: webhookutils.NewUpdateRequestUpdater(kyvernoclient, urLister),
contextLoader: engine.LegacyContextLoaderFactory(rclient),
}
}

View file

@ -19,7 +19,6 @@ import (
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/event"
"github.com/kyverno/kyverno/pkg/metrics"
"github.com/kyverno/kyverno/pkg/registryclient"
engineutils "github.com/kyverno/kyverno/pkg/utils/engine"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
webhookgenerate "github.com/kyverno/kyverno/pkg/webhooks/updaterequest"
@ -40,7 +39,7 @@ func NewGenerationHandler(
log logr.Logger,
client dclient.Interface,
kyvernoClient versioned.Interface,
rclient registryclient.Client,
contextLoader engine.ContextLoaderFactory,
nsLister corev1listers.NamespaceLister,
urLister kyvernov1beta1listers.UpdateRequestNamespaceLister,
urGenerator webhookgenerate.Generator,
@ -52,7 +51,7 @@ func NewGenerationHandler(
log: log,
client: client,
kyvernoClient: kyvernoClient,
rclient: rclient,
contextLoader: contextLoader,
nsLister: nsLister,
urLister: urLister,
urGenerator: urGenerator,
@ -66,7 +65,7 @@ type generationHandler struct {
log logr.Logger
client dclient.Interface
kyvernoClient versioned.Interface
rclient registryclient.Client
contextLoader engine.ContextLoaderFactory
nsLister corev1listers.NamespaceLister
urLister kyvernov1beta1listers.UpdateRequestNamespaceLister
urGenerator webhookgenerate.Generator
@ -93,7 +92,7 @@ func (h *generationHandler) Handle(
if request.Kind.Kind != "Namespace" && request.Namespace != "" {
policyContext = policyContext.WithNamespaceLabels(engineutils.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, h.log))
}
engineResponse := engine.ApplyBackgroundChecks(h.rclient, policyContext)
engineResponse := engine.ApplyBackgroundChecks(h.contextLoader, policyContext)
for _, rule := range engineResponse.PolicyResponse.Rules {
if rule.Status != engineapi.RuleStatusPass {
h.deleteGR(ctx, engineResponse)

View file

@ -41,6 +41,7 @@ type handlers struct {
client dclient.Interface
kyvernoClient versioned.Interface
rclient registryclient.Client
contextLoader engine.ContextLoaderFactory
// config
configuration config.Configuration
@ -66,6 +67,7 @@ type handlers struct {
}
func NewHandlers(
contextLoader engine.ContextLoaderFactory,
client dclient.Interface,
kyvernoClient versioned.Interface,
rclient registryclient.Client,
@ -84,6 +86,7 @@ func NewHandlers(
admissionReports bool,
) webhooks.ResourceHandlers {
return &handlers{
contextLoader: contextLoader,
client: client,
kyvernoClient: kyvernoClient,
rclient: rclient,
@ -121,7 +124,7 @@ func (h *handlers) Validate(ctx context.Context, logger logr.Logger, request *ad
}
if len(generatePolicies) == 0 && request.Operation == admissionv1.Update {
// handle generate source resource updates
gh := generation.NewGenerationHandler(logger, h.client, h.kyvernoClient, h.rclient, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen, h.metricsConfig)
gh := generation.NewGenerationHandler(logger, h.client, h.kyvernoClient, h.contextLoader, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen, h.metricsConfig)
go gh.HandleUpdatesForGenerateRules(context.TODO(), request, []kyvernov1.PolicyInterface{})
}
@ -137,7 +140,7 @@ func (h *handlers) Validate(ctx context.Context, logger logr.Logger, request *ad
namespaceLabels = engineutils.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, logger)
}
vh := validation.NewValidationHandler(logger, h.kyvernoClient, h.rclient, h.pCache, h.pcBuilder, h.eventGen, h.admissionReports, h.metricsConfig, h.configuration)
vh := validation.NewValidationHandler(logger, h.kyvernoClient, h.contextLoader, h.pCache, h.pcBuilder, h.eventGen, h.admissionReports, h.metricsConfig, h.configuration)
ok, msg, warnings := vh.HandleValidation(ctx, request, policies, policyContext, namespaceLabels, startTime)
if !ok {
@ -171,7 +174,7 @@ func (h *handlers) Mutate(ctx context.Context, logger logr.Logger, request *admi
if err := enginectx.MutateResourceWithImageInfo(request.Object.Raw, policyContext.JSONContext()); err != nil {
logger.Error(err, "failed to patch images info to resource, policies that mutate images may be impacted")
}
mh := mutation.NewMutationHandler(logger, h.rclient, h.eventGen, h.openApiManager, h.nsLister, h.metricsConfig)
mh := mutation.NewMutationHandler(logger, h.contextLoader, h.eventGen, h.openApiManager, h.nsLister, h.metricsConfig)
mutatePatches, mutateWarnings, err := mh.HandleMutation(ctx, request, mutatePolicies, policyContext, startTime)
if err != nil {
logger.Error(err, "mutation failed")
@ -184,7 +187,7 @@ func (h *handlers) Mutate(ctx context.Context, logger logr.Logger, request *admi
logger.Error(err, "failed to build policy context")
return admissionutils.Response(request.UID, err)
}
ivh := imageverification.NewImageVerificationHandler(logger, h.kyvernoClient, h.rclient, h.eventGen, h.admissionReports, h.configuration)
ivh := imageverification.NewImageVerificationHandler(logger, h.kyvernoClient, h.contextLoader, h.rclient, h.eventGen, h.admissionReports, h.configuration)
imagePatches, imageVerifyWarnings, err := ivh.Handle(ctx, newRequest, verifyImagesPolicies, policyContext)
if err != nil {
logger.Error(err, "image verification failed")

View file

@ -32,6 +32,7 @@ type ImageVerificationHandler interface {
type imageVerificationHandler struct {
kyvernoClient versioned.Interface
contextLoader engine.ContextLoaderFactory
rclient registryclient.Client
log logr.Logger
eventGen event.Interface
@ -42,6 +43,7 @@ type imageVerificationHandler struct {
func NewImageVerificationHandler(
log logr.Logger,
kyvernoClient versioned.Interface,
contextLoader engine.ContextLoaderFactory,
rclient registryclient.Client,
eventGen event.Interface,
admissionReports bool,
@ -49,6 +51,7 @@ func NewImageVerificationHandler(
) ImageVerificationHandler {
return &imageVerificationHandler{
kyvernoClient: kyvernoClient,
contextLoader: contextLoader,
rclient: rclient,
log: log,
eventGen: eventGen,
@ -91,7 +94,7 @@ func (h *imageVerificationHandler) handleVerifyImages(
fmt.Sprintf("POLICY %s/%s", policy.GetNamespace(), policy.GetName()),
func(ctx context.Context, span trace.Span) {
policyContext := policyContext.WithPolicy(policy)
resp, ivm := engine.VerifyAndPatchImages(ctx, h.rclient, policyContext, h.cfg)
resp, ivm := engine.VerifyAndPatchImages(ctx, h.contextLoader, h.rclient, policyContext, h.cfg)
engineResponses = append(engineResponses, resp)
patches = append(patches, resp.GetPatches()...)

View file

@ -13,7 +13,6 @@ import (
"github.com/kyverno/kyverno/pkg/event"
"github.com/kyverno/kyverno/pkg/metrics"
"github.com/kyverno/kyverno/pkg/openapi"
"github.com/kyverno/kyverno/pkg/registryclient"
"github.com/kyverno/kyverno/pkg/tracing"
"github.com/kyverno/kyverno/pkg/utils"
engineutils "github.com/kyverno/kyverno/pkg/utils/engine"
@ -36,7 +35,7 @@ type MutationHandler interface {
func NewMutationHandler(
log logr.Logger,
rclient registryclient.Client,
contextLoader engine.ContextLoaderFactory,
eventGen event.Interface,
openApiManager openapi.ValidateInterface,
nsLister corev1listers.NamespaceLister,
@ -44,7 +43,7 @@ func NewMutationHandler(
) MutationHandler {
return &mutationHandler{
log: log,
rclient: rclient,
contextLoader: contextLoader,
eventGen: eventGen,
openApiManager: openApiManager,
nsLister: nsLister,
@ -54,7 +53,7 @@ func NewMutationHandler(
type mutationHandler struct {
log logr.Logger
rclient registryclient.Client
contextLoader engine.ContextLoaderFactory
eventGen event.Interface
openApiManager openapi.ValidateInterface
nsLister corev1listers.NamespaceLister
@ -158,7 +157,7 @@ func (h *mutationHandler) applyMutation(ctx context.Context, request *admissionv
policyContext = policyContext.WithNamespaceLabels(engineutils.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, h.log))
}
engineResponse := engine.Mutate(ctx, h.rclient, policyContext)
engineResponse := engine.Mutate(ctx, h.contextLoader, policyContext)
policyPatches := engineResponse.GetPatches()
if !engineResponse.IsSuccessful() {

View file

@ -18,7 +18,7 @@ import (
// createUpdateRequests applies generate and mutateExisting policies, and creates update requests for background reconcile
func (h *handlers) createUpdateRequests(logger logr.Logger, request *admissionv1.AdmissionRequest, policyContext *engine.PolicyContext, generatePolicies, mutatePolicies []kyvernov1.PolicyInterface, ts time.Time) {
gh := generation.NewGenerationHandler(logger, h.client, h.kyvernoClient, h.rclient, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen, h.metricsConfig)
gh := generation.NewGenerationHandler(logger, h.client, h.kyvernoClient, h.contextLoader, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen, h.metricsConfig)
go h.handleMutateExisting(context.TODO(), logger, request, mutatePolicies, policyContext, ts)
go gh.Handle(context.TODO(), request, generatePolicies, policyContext, ts)
}
@ -43,7 +43,7 @@ func (h *handlers) handleMutateExisting(ctx context.Context, logger logr.Logger,
var rules []engineapi.RuleResponse
policyContext := policyContext.WithPolicy(policy)
engineResponse := engine.ApplyBackgroundChecks(h.rclient, policyContext)
engineResponse := engine.ApplyBackgroundChecks(h.contextLoader, policyContext)
for _, rule := range engineResponse.PolicyResponse.Rules {
if rule.Status == engineapi.RuleStatusPass {

View file

@ -15,7 +15,6 @@ import (
"github.com/kyverno/kyverno/pkg/event"
"github.com/kyverno/kyverno/pkg/metrics"
"github.com/kyverno/kyverno/pkg/policycache"
"github.com/kyverno/kyverno/pkg/registryclient"
"github.com/kyverno/kyverno/pkg/tracing"
admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
controllerutils "github.com/kyverno/kyverno/pkg/utils/controller"
@ -38,7 +37,7 @@ type ValidationHandler interface {
func NewValidationHandler(
log logr.Logger,
kyvernoClient versioned.Interface,
rclient registryclient.Client,
contextLoader engine.ContextLoaderFactory,
pCache policycache.Cache,
pcBuilder webhookutils.PolicyContextBuilder,
eventGen event.Interface,
@ -49,7 +48,7 @@ func NewValidationHandler(
return &validationHandler{
log: log,
kyvernoClient: kyvernoClient,
rclient: rclient,
contextLoader: contextLoader,
pCache: pCache,
pcBuilder: pcBuilder,
eventGen: eventGen,
@ -62,7 +61,7 @@ func NewValidationHandler(
type validationHandler struct {
log logr.Logger
kyvernoClient versioned.Interface
rclient registryclient.Client
contextLoader engine.ContextLoaderFactory
pCache policycache.Cache
pcBuilder webhookutils.PolicyContextBuilder
eventGen event.Interface
@ -114,7 +113,7 @@ func (v *validationHandler) HandleValidation(
failurePolicy = kyvernov1.Fail
}
engineResponse := engine.Validate(ctx, v.rclient, policyContext, v.cfg)
engineResponse := engine.Validate(ctx, v.contextLoader, policyContext, v.cfg)
if engineResponse.IsNil() {
// we get an empty response if old and new resources created the same response
// allow updates if resource update doesnt change the policy evaluation
@ -173,7 +172,7 @@ func (v *validationHandler) buildAuditResponses(
fmt.Sprintf("POLICY %s/%s", policy.GetNamespace(), policy.GetName()),
func(ctx context.Context, span trace.Span) {
policyContext := policyContext.WithPolicy(policy).WithNamespaceLabels(namespaceLabels)
responses = append(responses, engine.Validate(ctx, v.rclient, policyContext, v.cfg))
responses = append(responses, engine.Validate(ctx, v.contextLoader, policyContext, v.cfg))
},
)
}

View file

@ -1058,10 +1058,11 @@ func TestValidate_failure_action_overrides(t *testing.T) {
resourceUnstructured, err := kubeutils.BytesToUnstructured(tc.rawResource)
assert.NilError(t, err)
ctx := engine.NewPolicyContext().WithPolicy(&policy).WithNewResource(*resourceUnstructured).WithNamespaceLabels(tc.rawResourceNamespaceLabels)
er := engine.Validate(
context.TODO(),
registryclient.NewOrDie(),
engine.NewPolicyContext().WithPolicy(&policy).WithNewResource(*resourceUnstructured).WithNamespaceLabels(tc.rawResourceNamespaceLabels),
engine.LegacyContextLoaderFactory(registryclient.NewOrDie()),
ctx,
cfg,
)
if tc.blocked && tc.messages != nil {
@ -1124,7 +1125,12 @@ func Test_RuleSelector(t *testing.T) {
ctx := engine.NewPolicyContext().WithPolicy(&policy).WithNewResource(*resourceUnstructured)
cfg := config.NewDefaultConfiguration()
resp := engine.Validate(context.TODO(), registryclient.NewOrDie(), ctx, cfg)
resp := engine.Validate(
context.TODO(),
engine.LegacyContextLoaderFactory(registryclient.NewOrDie()),
ctx,
cfg,
)
assert.Assert(t, resp.PolicyResponse.RulesAppliedCount == 2)
assert.Assert(t, resp.PolicyResponse.RulesErrorCount == 0)
@ -1135,7 +1141,12 @@ func Test_RuleSelector(t *testing.T) {
applyOne := kyvernov1.ApplyOne
policy.Spec.ApplyRules = &applyOne
resp = engine.Validate(context.TODO(), registryclient.NewOrDie(), ctx, cfg)
resp = engine.Validate(
context.TODO(),
engine.LegacyContextLoaderFactory(registryclient.NewOrDie()),
ctx,
cfg,
)
assert.Assert(t, resp.PolicyResponse.RulesAppliedCount == 1)
assert.Assert(t, resp.PolicyResponse.RulesErrorCount == 0)