From 7367397178eb2688dd2d3d2f7c717732fbeea687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Charles-Edouard=20Br=C3=A9t=C3=A9ch=C3=A9?= Date: Wed, 8 Feb 2023 14:19:56 +0100 Subject: [PATCH] refactor: reduce store dependency in engine pkg (#6260) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Charles-Edouard Brétéché --- cmd/background-controller/main.go | 2 +- .../kubectl-kyverno/apply/apply_command.go | 4 +- cmd/cli/kubectl-kyverno/test/test_command.go | 4 +- .../kubectl-kyverno/utils/common/common.go | 24 +-- .../utils/store/contextloader.go | 80 +++++++ cmd/cli/kubectl-kyverno/utils/store/store.go | 15 -- cmd/kyverno/main.go | 2 +- cmd/reports-controller/main.go | 2 +- pkg/engine/{jsonContext.go => api/context.go} | 168 +++------------ pkg/engine/api/contextloader.go | 48 ++++- pkg/engine/engine.go | 1 - pkg/engine/imageVerify_test.go | 2 +- pkg/engine/mutation_test.go | 71 ++++--- pkg/engine/test/contextloader.go | 80 +++++++ pkg/engine/utils.go | 20 +- pkg/engine/validation_test.go | 195 ++++++++++-------- pkg/testrunner/scenario.go | 2 +- pkg/webhooks/resource/fake.go | 3 +- pkg/webhooks/resource/validation_test.go | 4 +- 19 files changed, 398 insertions(+), 329 deletions(-) create mode 100644 cmd/cli/kubectl-kyverno/utils/store/contextloader.go rename pkg/engine/{jsonContext.go => api/context.go} (71%) create mode 100644 pkg/engine/test/contextloader.go diff --git a/cmd/background-controller/main.go b/cmd/background-controller/main.go index f7bc6cb41c..69d801765f 100644 --- a/cmd/background-controller/main.go +++ b/cmd/background-controller/main.go @@ -215,7 +215,7 @@ func main() { configuration, dClient, rclient, - engine.LegacyContextLoaderFactory(configMapResolver), + engineapi.DefaultContextLoaderFactory(configMapResolver), // TODO: do we need exceptions here ? nil, ) diff --git a/cmd/cli/kubectl-kyverno/apply/apply_command.go b/cmd/cli/kubectl-kyverno/apply/apply_command.go index 47afb21b36..4defa52658 100644 --- a/cmd/cli/kubectl-kyverno/apply/apply_command.go +++ b/cmd/cli/kubectl-kyverno/apply/apply_command.go @@ -334,14 +334,12 @@ func (c *ApplyCommandConfig) applyCommandHelper() (rc *common.ResultCounts, reso // get the user info as request info from a different file var userInfo v1beta1.RequestInfo - var subjectInfo store.Subject if c.UserInfoPath != "" { - userInfo, subjectInfo, err = common.GetUserInfoFromPath(fs, c.UserInfoPath, false, "") + userInfo, err = common.GetUserInfoFromPath(fs, c.UserInfoPath, false, "") if err != nil { fmt.Printf("Error: failed to load request info\nCause: %s\n", err) osExit(1) } - store.SetSubject(subjectInfo.Subject) } if c.VariablesString != "" { diff --git a/cmd/cli/kubectl-kyverno/test/test_command.go b/cmd/cli/kubectl-kyverno/test/test_command.go index b58cccef56..5a972c0064 100644 --- a/cmd/cli/kubectl-kyverno/test/test_command.go +++ b/cmd/cli/kubectl-kyverno/test/test_command.go @@ -770,15 +770,13 @@ func applyPoliciesFromPath(fs billy.Filesystem, policyBytes []byte, isGit bool, // get the user info as request info from a different file var userInfo v1beta1.RequestInfo - var subjectInfo store.Subject if userInfoFile != "" { - userInfo, subjectInfo, err = common.GetUserInfoFromPath(fs, userInfoFile, isGit, policyResourcePath) + userInfo, err = common.GetUserInfoFromPath(fs, userInfoFile, isGit, policyResourcePath) if err != nil { fmt.Printf("Error: failed to load request info\nCause: %s\n", err) os.Exit(1) } - store.SetSubject(subjectInfo.Subject) } policyFullPath := getFullPath(values.Policies, policyResourcePath, isGit) diff --git a/cmd/cli/kubectl-kyverno/utils/common/common.go b/cmd/cli/kubectl-kyverno/utils/common/common.go index e9634d0aa2..2b522f0eb2 100644 --- a/cmd/cli/kubectl-kyverno/utils/common/common.go +++ b/cmd/cli/kubectl-kyverno/utils/common/common.go @@ -478,7 +478,7 @@ OuterLoop: cfg, c.Client, registryclient.NewOrDie(), - engine.LegacyContextLoaderFactory(nil), + store.ContextLoaderFactory(nil), nil, ) policyContext := engine.NewPolicyContextWithJsonContext(ctx). @@ -1077,7 +1077,7 @@ func initializeMockController(objects []runtime.Object) (*generate.GenerateContr config.NewDefaultConfiguration(), client, nil, - engine.LegacyContextLoaderFactory(nil), + store.ContextLoaderFactory(nil), nil, )) return c, nil @@ -1148,9 +1148,8 @@ func handleGeneratePolicy(generateResponse *engineapi.EngineResponse, policyCont } // GetUserInfoFromPath - get the request info as user info from a given path -func GetUserInfoFromPath(fs billy.Filesystem, path string, isGit bool, policyResourcePath string) (kyvernov1beta1.RequestInfo, store.Subject, error) { +func GetUserInfoFromPath(fs billy.Filesystem, path string, isGit bool, policyResourcePath string) (kyvernov1beta1.RequestInfo, error) { userInfo := &kyvernov1beta1.RequestInfo{} - subjectInfo := &store.Subject{} if isGit { filep, err := fs.Open(filepath.Join(policyResourcePath, path)) if err != nil { @@ -1168,14 +1167,6 @@ func GetUserInfoFromPath(fs billy.Filesystem, path string, isGit bool, policyRes if err := json.Unmarshal(userInfoBytes, userInfo); err != nil { fmt.Printf("failed to decode yaml: %v", err) } - subjectBytes, err := yaml.ToJSON(bytes) - if err != nil { - fmt.Printf("failed to convert to JSON: %v", err) - } - - if err := json.Unmarshal(subjectBytes, subjectInfo); err != nil { - fmt.Printf("failed to decode yaml: %v", err) - } } else { var errors []error pathname := filepath.Clean(filepath.Join(policyResourcePath, path)) @@ -1190,13 +1181,6 @@ func GetUserInfoFromPath(fs billy.Filesystem, path string, isGit bool, policyRes if err := json.Unmarshal(userInfoBytes, userInfo); err != nil { errors = append(errors, sanitizederror.NewWithError("failed to decode yaml", err)) } - if err := json.Unmarshal(userInfoBytes, subjectInfo); err != nil { - errors = append(errors, sanitizederror.NewWithError("failed to decode yaml", err)) - } - if len(errors) > 0 { - return *userInfo, *subjectInfo, sanitizederror.NewWithErrors("failed to read file", errors) - } - if len(errors) > 0 && log.Log.V(1).Enabled() { fmt.Printf("ignoring errors: \n") for _, e := range errors { @@ -1204,7 +1188,7 @@ func GetUserInfoFromPath(fs billy.Filesystem, path string, isGit bool, policyRes } } } - return *userInfo, *subjectInfo, nil + return *userInfo, nil } func IsGitSourcePath(policyPaths []string) bool { diff --git a/cmd/cli/kubectl-kyverno/utils/store/contextloader.go b/cmd/cli/kubectl-kyverno/utils/store/contextloader.go new file mode 100644 index 0000000000..304a7e4901 --- /dev/null +++ b/cmd/cli/kubectl-kyverno/utils/store/contextloader.go @@ -0,0 +1,80 @@ +package store + +import ( + "context" + + "github.com/go-logr/logr" + kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" + "github.com/kyverno/kyverno/pkg/clients/dclient" + engineapi "github.com/kyverno/kyverno/pkg/engine/api" + enginecontext "github.com/kyverno/kyverno/pkg/engine/context" + "github.com/kyverno/kyverno/pkg/logging" + "github.com/kyverno/kyverno/pkg/registryclient" +) + +func ContextLoaderFactory( + cmResolver engineapi.ConfigmapResolver, +) engineapi.ContextLoaderFactory { + return func(policy kyvernov1.PolicyInterface, rule kyvernov1.Rule) engineapi.ContextLoader { + inner := engineapi.DefaultContextLoaderFactory(cmResolver) + if IsMock() { + return &mockContextLoader{ + logger: logging.WithName("MockContextLoaderFactory"), + policyName: policy.GetName(), + ruleName: rule.Name, + } + } else { + return inner(policy, rule) + } + } +} + +type mockContextLoader struct { + logger logr.Logger + policyName string + ruleName string +} + +func (l *mockContextLoader) Load( + ctx context.Context, + client dclient.Interface, + _ registryclient.Client, + contextEntries []kyvernov1.ContextEntry, + jsonContext enginecontext.Interface, +) error { + rule := GetPolicyRule(l.policyName, l.ruleName) + if rule != nil && len(rule.Values) > 0 { + variables := rule.Values + for key, value := range variables { + if err := jsonContext.AddVariable(key, value); err != nil { + return err + } + } + } + hasRegistryAccess := GetRegistryAccess() + // Context Variable should be loaded after the values loaded from values file + for _, entry := range contextEntries { + if entry.ImageRegistry != nil && hasRegistryAccess { + rclient := GetRegistryClient() + if err := engineapi.LoadImageData(ctx, rclient, l.logger, entry, jsonContext); err != nil { + return err + } + } else if entry.Variable != nil { + if err := engineapi.LoadVariable(l.logger, entry, jsonContext); err != nil { + return err + } + } else if entry.APICall != nil && IsApiCallAllowed() { + if err := engineapi.LoadAPIData(ctx, l.logger, entry, jsonContext, client); err != nil { + return err + } + } + } + if rule != nil && len(rule.ForEachValues) > 0 { + for key, value := range rule.ForEachValues { + if err := jsonContext.AddVariable(key, value[GetForeachElement()]); err != nil { + return err + } + } + } + return nil +} diff --git a/cmd/cli/kubectl-kyverno/utils/store/store.go b/cmd/cli/kubectl-kyverno/utils/store/store.go index 06cbbfeffe..5fca547fd0 100644 --- a/cmd/cli/kubectl-kyverno/utils/store/store.go +++ b/cmd/cli/kubectl-kyverno/utils/store/store.go @@ -2,7 +2,6 @@ package store import ( "github.com/kyverno/kyverno/pkg/registryclient" - rbacv1 "k8s.io/api/rbac/v1" ) type Context struct { @@ -20,18 +19,12 @@ type Rule struct { ForEachValues map[string][]interface{} `json:"foreachValues"` } -type Subject struct { - Subject rbacv1.Subject `json:"subject,omitempty" yaml:"subject,omitempty"` -} - var ( mock bool registryClient registryclient.Client allowApiCalls bool policies []Policy - // contextVar Context foreachElement int - subject rbacv1.Subject ) func SetMock(m bool) { @@ -94,14 +87,6 @@ func GetPolicyRule(policyName string, ruleName string) *Rule { return nil } -func SetSubject(s rbacv1.Subject) { - subject = s -} - -func GetSubject() rbacv1.Subject { - return subject -} - func AllowApiCall(allow bool) { allowApiCalls = allow } diff --git a/cmd/kyverno/main.go b/cmd/kyverno/main.go index f4dd44884d..31b8a5d81b 100644 --- a/cmd/kyverno/main.go +++ b/cmd/kyverno/main.go @@ -368,7 +368,7 @@ func main() { configuration, dClient, rclient, - engine.LegacyContextLoaderFactory(configMapResolver), + engineapi.DefaultContextLoaderFactory(configMapResolver), exceptionsLister, ) // create non leader controllers diff --git a/cmd/reports-controller/main.go b/cmd/reports-controller/main.go index a21675adeb..6c43b9ca75 100644 --- a/cmd/reports-controller/main.go +++ b/cmd/reports-controller/main.go @@ -315,7 +315,7 @@ func main() { configuration, dClient, rclient, - engine.LegacyContextLoaderFactory(configMapResolver), + engineapi.DefaultContextLoaderFactory(configMapResolver), exceptionsLister, ) // setup leader election diff --git a/pkg/engine/jsonContext.go b/pkg/engine/api/context.go similarity index 71% rename from pkg/engine/jsonContext.go rename to pkg/engine/api/context.go index cb48e92ae6..1645615ff7 100644 --- a/pkg/engine/jsonContext.go +++ b/pkg/engine/api/context.go @@ -1,4 +1,4 @@ -package engine +package api import ( "context" @@ -7,123 +7,15 @@ 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" enginecontext "github.com/kyverno/kyverno/pkg/engine/context" "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/engine/variables" - "github.com/kyverno/kyverno/pkg/logging" "github.com/kyverno/kyverno/pkg/registryclient" ) -func LegacyContextLoaderFactory( - cmResolver engineapi.ConfigmapResolver, -) engineapi.ContextLoaderFactory { - return func(policy kyvernov1.PolicyInterface, rule kyvernov1.Rule) engineapi.ContextLoader { - if store.IsMock() { - return &mockContextLoader{ - logger: logging.WithName("MockContextLoaderFactory"), - policyName: policy.GetName(), - ruleName: rule.Name, - } - } else { - return &contextLoader{ - logger: logging.WithName("LegacyContextLoaderFactory"), - cmResolver: cmResolver, - } - } - } -} - -type contextLoader struct { - logger logr.Logger - cmResolver engineapi.ConfigmapResolver -} - -func (l *contextLoader) Load( - ctx context.Context, - client dclient.Interface, - rclient registryclient.Client, - exceptionSelector engineapi.PolicyExceptionSelector, - contextEntries []kyvernov1.ContextEntry, - jsonContext enginecontext.Interface, -) error { - for _, entry := range contextEntries { - if entry.ConfigMap != nil { - if err := loadConfigMap(ctx, l.logger, entry, jsonContext, l.cmResolver); err != nil { - return err - } - } else if entry.APICall != nil { - if err := loadAPIData(ctx, l.logger, entry, jsonContext, client); err != nil { - return err - } - } else if entry.ImageRegistry != nil { - if err := loadImageData(ctx, rclient, l.logger, entry, jsonContext); err != nil { - return err - } - } else if entry.Variable != nil { - if err := loadVariable(l.logger, entry, jsonContext); err != nil { - return err - } - } - } - return nil -} - -type mockContextLoader struct { - logger logr.Logger - policyName string - ruleName string -} - -func (l *mockContextLoader) Load( - ctx context.Context, - client dclient.Interface, - _ registryclient.Client, - _ engineapi.PolicyExceptionSelector, - contextEntries []kyvernov1.ContextEntry, - jsonContext enginecontext.Interface, -) error { - rule := store.GetPolicyRule(l.policyName, l.ruleName) - if rule != nil && len(rule.Values) > 0 { - variables := rule.Values - for key, value := range variables { - if err := jsonContext.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, jsonContext); err != nil { - return err - } - } else if entry.Variable != nil { - if err := loadVariable(l.logger, entry, jsonContext); err != nil { - return err - } - } else if entry.APICall != nil && store.IsApiCallAllowed() { - if err := loadAPIData(ctx, l.logger, entry, jsonContext, client); err != nil { - return err - } - } - } - if rule != nil && len(rule.ForEachValues) > 0 { - for key, value := range rule.ForEachValues { - if err := jsonContext.AddVariable(key, value[store.GetForeachElement()]); err != nil { - return err - } - } - } - return nil -} - -func loadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx enginecontext.Interface) (err error) { +func LoadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx enginecontext.Interface) (err error) { path := "" if entry.Variable.JMESPath != "" { jp, err := variables.SubstituteAll(logger, ctx, entry.Variable.JMESPath) @@ -182,7 +74,7 @@ func loadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx engineco } } -func loadImageData(ctx context.Context, rclient registryclient.Client, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface) error { +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 @@ -197,6 +89,29 @@ func loadImageData(ctx context.Context, rclient registryclient.Client, logger lo return nil } +func LoadAPIData(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface, client dclient.Interface) error { + executor, err := apicall.New(ctx, entry, enginectx, client, logger) + if err != nil { + return fmt.Errorf("failed to initialize APICall: %w", err) + } + if _, err := executor.Execute(); err != nil { + return fmt.Errorf("failed to execute APICall: %w", err) + } + return nil +} + +func LoadConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface, resolver 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.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 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 { @@ -278,17 +193,6 @@ 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 enginecontext.Interface, client dclient.Interface) error { - executor, err := apicall.New(ctx, entry, enginectx, client, logger) - if err != nil { - return fmt.Errorf("failed to initialize APICall: %w", err) - } - if _, err := executor.Execute(); err != nil { - return fmt.Errorf("failed to execute APICall: %w", err) - } - return nil -} - func applyJMESPath(jmesPath string, data interface{}) (interface{}, error) { jp, err := jmespath.New(jmesPath) if err != nil { @@ -297,40 +201,23 @@ func applyJMESPath(jmesPath string, data interface{}) (interface{}, error) { return jp.Search(data) } -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.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 enginecontext.Interface, resolver engineapi.ConfigmapResolver) ([]byte, error) { +func fetchConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface, resolver ConfigmapResolver) ([]byte, error) { contextData := make(map[string]interface{}) - 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, 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) } - if namespace == "" { namespace = "default" } - 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) } - // extract configmap data contextData["data"] = obj.Data contextData["metadata"] = obj.ObjectMeta @@ -338,6 +225,5 @@ func fetchConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.Con if err != nil { return nil, fmt.Errorf("failed to unmarshal configmap %s/%s: %v", namespace, name, err) } - return data, nil } diff --git a/pkg/engine/api/contextloader.go b/pkg/engine/api/contextloader.go index 92e6b790bd..454cf8143c 100644 --- a/pkg/engine/api/contextloader.go +++ b/pkg/engine/api/contextloader.go @@ -3,9 +3,11 @@ package api import ( "context" + "github.com/go-logr/logr" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" "github.com/kyverno/kyverno/pkg/clients/dclient" enginecontext "github.com/kyverno/kyverno/pkg/engine/context" + "github.com/kyverno/kyverno/pkg/logging" "github.com/kyverno/kyverno/pkg/registryclient" ) @@ -18,8 +20,52 @@ type ContextLoader interface { ctx context.Context, client dclient.Interface, rclient registryclient.Client, - exceptionSelector PolicyExceptionSelector, contextEntries []kyvernov1.ContextEntry, jsonContext enginecontext.Interface, ) error } + +func DefaultContextLoaderFactory( + cmResolver ConfigmapResolver, +) ContextLoaderFactory { + return func(policy kyvernov1.PolicyInterface, rule kyvernov1.Rule) ContextLoader { + return &contextLoader{ + logger: logging.WithName("DefaultContextLoaderFactory"), + cmResolver: cmResolver, + } + } +} + +type contextLoader struct { + logger logr.Logger + cmResolver ConfigmapResolver +} + +func (l *contextLoader) Load( + ctx context.Context, + client dclient.Interface, + rclient registryclient.Client, + contextEntries []kyvernov1.ContextEntry, + jsonContext enginecontext.Interface, +) error { + for _, entry := range contextEntries { + if entry.ConfigMap != nil { + if err := LoadConfigMap(ctx, l.logger, entry, jsonContext, l.cmResolver); err != nil { + return err + } + } else if entry.APICall != nil { + if err := LoadAPIData(ctx, l.logger, entry, jsonContext, client); err != nil { + return err + } + } else if entry.ImageRegistry != nil { + if err := LoadImageData(ctx, rclient, l.logger, entry, jsonContext); err != nil { + return err + } + } else if entry.Variable != nil { + if err := LoadVariable(l.logger, entry, jsonContext); err != nil { + return err + } + } + } + return nil +} diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 746ec0e12e..34e813f6f9 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -82,7 +82,6 @@ func (e *engine) ContextLoader( ctx, e.client, e.rclient, - e.exceptionSelector, contextEntries, jsonContext, ) diff --git a/pkg/engine/imageVerify_test.go b/pkg/engine/imageVerify_test.go index 98aa7d2bfe..871d4af346 100644 --- a/pkg/engine/imageVerify_test.go +++ b/pkg/engine/imageVerify_test.go @@ -172,7 +172,7 @@ func testVerifyAndPatchImages( cfg, nil, rclient, - LegacyContextLoaderFactory(cmResolver), + engineapi.DefaultContextLoaderFactory(cmResolver), nil, ) return e.VerifyAndPatchImages( diff --git a/pkg/engine/mutation_test.go b/pkg/engine/mutation_test.go index e749d79e7a..87f5959b1f 100644 --- a/pkg/engine/mutation_test.go +++ b/pkg/engine/mutation_test.go @@ -8,11 +8,11 @@ import ( "testing" kyverno "github.com/kyverno/kyverno/api/kyverno/v1" - "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/store" "github.com/kyverno/kyverno/pkg/clients/dclient" client "github.com/kyverno/kyverno/pkg/clients/dclient" engineapi "github.com/kyverno/kyverno/pkg/engine/api" enginecontext "github.com/kyverno/kyverno/pkg/engine/context" + enginetest "github.com/kyverno/kyverno/pkg/engine/test" "github.com/kyverno/kyverno/pkg/registryclient" kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" "gotest.tools/assert" @@ -26,12 +26,16 @@ func testMutate( client dclient.Interface, rclient registryclient.Client, pContext *PolicyContext, + contextLoader engineapi.ContextLoaderFactory, ) *engineapi.EngineResponse { + if contextLoader == nil { + contextLoader = engineapi.DefaultContextLoaderFactory(nil) + } e := NewEngine( cfg, client, rclient, - LegacyContextLoaderFactory(nil), + contextLoader, nil, ) return e.Mutate( @@ -115,7 +119,7 @@ func Test_VariableSubstitutionPatchStrategicMerge(t *testing.T) { jsonContext: ctx, newResource: *resourceUnstructured, } - er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext) + er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext, nil) t.Log(string(expectedPatch)) assert.Equal(t, len(er.PolicyResponse.Rules), 1) @@ -189,7 +193,7 @@ func Test_variableSubstitutionPathNotExist(t *testing.T) { jsonContext: ctx, newResource: *resourceUnstructured, } - er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext) + er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext, nil) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Assert(t, strings.Contains(er.PolicyResponse.Rules[0].Message, "Unknown key \"name1\" in path")) } @@ -252,20 +256,6 @@ func Test_variableSubstitutionCLI(t *testing.T) { expectedPatch := []byte(`{"op":"add","path":"/metadata/labels","value":{"my-environment-name":"dev1"}}`) - store.SetPolicies( - store.Policy{ - Name: "cm-variable-example", - Rules: []store.Rule{ - { - Name: "example-configmap-lookup", - Values: map[string]interface{}{ - "dictionary.data.env": "dev1", - }, - }, - }, - }, - ) - store.SetMock(true) var policy kyverno.ClusterPolicy err := json.Unmarshal(policyRaw, &policy) assert.NilError(t, err) @@ -282,7 +272,26 @@ func Test_variableSubstitutionCLI(t *testing.T) { newResource: *resourceUnstructured, } - er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext) + er := testMutate( + context.TODO(), + nil, + registryclient.NewOrDie(), + policyContext, + enginetest.ContextLoaderFactory( + nil, + map[string]enginetest.Policy{ + "cm-variable-example": { + Rules: map[string]enginetest.Rule{ + "example-configmap-lookup": { + Values: map[string]interface{}{ + "dictionary.data.env": "dev1", + }, + }, + }, + }, + }, + ), + ) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1) t.Log(string(expectedPatch)) @@ -391,7 +400,7 @@ func Test_chained_rules(t *testing.T) { err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx) assert.NilError(t, err) - er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext) + er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext, nil) 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") @@ -462,7 +471,6 @@ func Test_precondition(t *testing.T) { }`) expectedPatch := []byte(`{"op":"add","path":"/metadata/labels/my-added-label","value":"test"}`) - store.SetMock(true) var policy kyverno.ClusterPolicy err := json.Unmarshal(policyRaw, &policy) assert.NilError(t, err) @@ -479,7 +487,7 @@ func Test_precondition(t *testing.T) { newResource: *resourceUnstructured, } - er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext) + er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext, enginetest.ContextLoaderFactory(nil, nil)) t.Log(string(expectedPatch)) t.Log(string(er.PolicyResponse.Rules[0].Patches[0])) if !reflect.DeepEqual(expectedPatch, er.PolicyResponse.Rules[0].Patches[0]) { @@ -559,7 +567,6 @@ func Test_nonZeroIndexNumberPatchesJson6902(t *testing.T) { expectedPatch := []byte(`{"op":"add","path":"/subsets/0/addresses/1","value":{"ip":"192.168.42.172"}}`) - store.SetMock(true) var policy kyverno.ClusterPolicy err := json.Unmarshal(policyraw, &policy) assert.NilError(t, err) @@ -576,7 +583,7 @@ func Test_nonZeroIndexNumberPatchesJson6902(t *testing.T) { newResource: *resourceUnstructured, } - er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext) + er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext, enginetest.ContextLoaderFactory(nil, nil)) t.Log(string(expectedPatch)) t.Log(string(er.PolicyResponse.Rules[0].Patches[0])) if !reflect.DeepEqual(expectedPatch, er.PolicyResponse.Rules[0].Patches[0]) { @@ -670,7 +677,7 @@ func Test_foreach(t *testing.T) { err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx) assert.NilError(t, err) - er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext) + er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext, nil) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass) @@ -777,7 +784,7 @@ func Test_foreach_element_mutation(t *testing.T) { err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx) assert.NilError(t, err) - er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext) + er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext, nil) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass) @@ -903,7 +910,7 @@ func Test_Container_InitContainer_foreach(t *testing.T) { err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx) assert.NilError(t, err) - er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext) + er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext, nil) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass) @@ -1053,7 +1060,7 @@ func testApplyPolicyToResource(t *testing.T, policyRaw, resourceRaw []byte) *eng err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx) assert.NilError(t, err) - er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext) + er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext, nil) return er } @@ -1603,7 +1610,7 @@ func Test_mutate_existing_resources(t *testing.T) { newResource: *trigger, } - er := testMutate(context.TODO(), dclient, registryclient.NewOrDie(), policyContext) + er := testMutate(context.TODO(), dclient, registryclient.NewOrDie(), policyContext, nil) for _, rr := range er.PolicyResponse.Rules { for i, p := range rr.Patches { @@ -1712,7 +1719,7 @@ func Test_RuleSelectorMutate(t *testing.T) { newResource: *resourceUnstructured, } - er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext) + er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext, nil) 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) @@ -1727,7 +1734,7 @@ func Test_RuleSelectorMutate(t *testing.T) { applyOne := kyverno.ApplyOne policyContext.policy.GetSpec().ApplyRules = &applyOne - er = testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext) + er = testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext, nil) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1) @@ -2094,7 +2101,7 @@ func Test_SpecialCharacters(t *testing.T) { } // Mutate and make sure that we got the expected amount of rules. - patches := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext).GetPatches() + patches := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext, nil).GetPatches() if !reflect.DeepEqual(patches, tt.want) { t.Errorf("Mutate() got patches %s, expected %s", patches, tt.want) } diff --git a/pkg/engine/test/contextloader.go b/pkg/engine/test/contextloader.go new file mode 100644 index 0000000000..36392ba408 --- /dev/null +++ b/pkg/engine/test/contextloader.go @@ -0,0 +1,80 @@ +package test + +import ( + "context" + + "github.com/go-logr/logr" + kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" + "github.com/kyverno/kyverno/pkg/clients/dclient" + engineapi "github.com/kyverno/kyverno/pkg/engine/api" + enginecontext "github.com/kyverno/kyverno/pkg/engine/context" + "github.com/kyverno/kyverno/pkg/logging" + "github.com/kyverno/kyverno/pkg/registryclient" +) + +type Policy struct { + Rules map[string]Rule +} + +type Rule struct { + Values map[string]interface{} +} + +func ContextLoaderFactory( + cmResolver engineapi.ConfigmapResolver, + values map[string]Policy, +) engineapi.ContextLoaderFactory { + return func(policy kyvernov1.PolicyInterface, rule kyvernov1.Rule) engineapi.ContextLoader { + return &mockContextLoader{ + logger: logging.WithName("MockContextLoaderFactory"), + policyName: policy.GetName(), + ruleName: rule.Name, + values: values, + } + } +} + +type mockContextLoader struct { + logger logr.Logger + policyName string + ruleName string + values map[string]Policy + allowApiCall bool +} + +func (l *mockContextLoader) Load( + ctx context.Context, + client dclient.Interface, + rclient registryclient.Client, + contextEntries []kyvernov1.ContextEntry, + jsonContext enginecontext.Interface, +) error { + if l.values != nil { + policy := l.values[l.policyName] + if policy.Rules != nil { + rule := policy.Rules[l.ruleName] + for key, value := range rule.Values { + if err := jsonContext.AddVariable(key, value); err != nil { + return err + } + } + } + } + // Context Variable should be loaded after the values loaded from values file + for _, entry := range contextEntries { + if entry.ImageRegistry != nil && rclient != nil { + if err := engineapi.LoadImageData(ctx, rclient, l.logger, entry, jsonContext); err != nil { + return err + } + } else if entry.Variable != nil { + if err := engineapi.LoadVariable(l.logger, entry, jsonContext); err != nil { + return err + } + } else if entry.APICall != nil && l.allowApiCall { + if err := engineapi.LoadAPIData(ctx, l.logger, entry, jsonContext, client); err != nil { + return err + } + } + } + return nil +} diff --git a/pkg/engine/utils.go b/pkg/engine/utils.go index 46467f0a0b..6da59591e4 100644 --- a/pkg/engine/utils.go +++ b/pkg/engine/utils.go @@ -8,7 +8,6 @@ import ( kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1" - "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/store" "github.com/kyverno/kyverno/pkg/engine/context" datautils "github.com/kyverno/kyverno/pkg/utils/data" matchutils "github.com/kyverno/kyverno/pkg/utils/match" @@ -155,24 +154,7 @@ func doesResourceMatchConditionBlock(subresourceGVKToAPIResource map[string]*met // matchSubjects return true if one of ruleSubjects exist in userInfo func matchSubjects(ruleSubjects []rbacv1.Subject, userInfo authenticationv1.UserInfo, dynamicConfig []string) bool { - if store.IsMock() { - mockSubject := store.GetSubject() - for _, subject := range ruleSubjects { - switch subject.Kind { - case "ServiceAccount": - if subject.Name == mockSubject.Name && subject.Namespace == mockSubject.Namespace { - return true - } - case "User", "Group": - if mockSubject.Name == subject.Name { - return true - } - } - } - return false - } else { - return matchutils.CheckSubjects(ruleSubjects, userInfo, dynamicConfig) - } + return matchutils.CheckSubjects(ruleSubjects, userInfo, dynamicConfig) } // MatchesResourceDescription checks if the resource matches resource description of the rule or not diff --git a/pkg/engine/validation_test.go b/pkg/engine/validation_test.go index 4503fa8aab..f54c3ba578 100644 --- a/pkg/engine/validation_test.go +++ b/pkg/engine/validation_test.go @@ -8,10 +8,10 @@ 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" + enginetest "github.com/kyverno/kyverno/pkg/engine/test" "github.com/kyverno/kyverno/pkg/registryclient" admissionutils "github.com/kyverno/kyverno/pkg/utils/admission" kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" @@ -19,12 +19,21 @@ import ( admissionv1 "k8s.io/api/admission/v1" ) -func testValidate(ctx context.Context, rclient registryclient.Client, pContext *PolicyContext, cfg config.Configuration) *engineapi.EngineResponse { +func testValidate( + ctx context.Context, + rclient registryclient.Client, + pContext *PolicyContext, + cfg config.Configuration, + contextLoader engineapi.ContextLoaderFactory, +) *engineapi.EngineResponse { + if contextLoader == nil { + contextLoader = engineapi.DefaultContextLoaderFactory(nil) + } e := NewEngine( cfg, nil, rclient, - LegacyContextLoaderFactory(nil), + contextLoader, nil, ) return e.Validate( @@ -126,7 +135,7 @@ func TestValidate_image_tag_fail(t *testing.T) { "validation error: imagePullPolicy 'Always' required with tag 'latest'. rule validate-latest failed at path /spec/containers/0/imagePullPolicy/", } - er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg, nil) for index, r := range er.PolicyResponse.Rules { assert.Equal(t, r.Message, msgs[index]) } @@ -226,7 +235,7 @@ func TestValidate_image_tag_pass(t *testing.T) { "validation rule 'validate-tag' passed.", "validation rule 'validate-latest' passed.", } - er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg, nil) for index, r := range er.PolicyResponse.Rules { assert.Equal(t, r.Message, msgs[index]) } @@ -300,7 +309,7 @@ func TestValidate_Fail_anyPattern(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg, nil) assert.Assert(t, !er.IsSuccessful()) msgs := []string{"validation error: A namespace is required. rule check-default-namespace[0] failed at path /metadata/namespace/ rule check-default-namespace[1] failed at path /metadata/namespace/"} @@ -383,7 +392,7 @@ func TestValidate_host_network_port(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg, nil) msgs := []string{"validation error: Host network and port are not allowed. rule validate-host-network-port failed at path /spec/containers/0/ports/0/hostPort/"} for index, r := range er.PolicyResponse.Rules { @@ -473,7 +482,7 @@ func TestValidate_anchor_arraymap_pass(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg, nil) msgs := []string{"validation rule 'validate-host-path' passed."} for index, r := range er.PolicyResponse.Rules { @@ -561,7 +570,7 @@ func TestValidate_anchor_arraymap_fail(t *testing.T) { assert.NilError(t, err) resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg, nil) msgs := []string{"validation error: Host path '/var/lib/' is not allowed. rule validate-host-path failed at path /spec/volumes/0/hostPath/path/"} for index, r := range er.PolicyResponse.Rules { @@ -631,7 +640,7 @@ func TestValidate_anchor_map_notfound(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg, nil) msgs := []string{"validation rule 'pod rule 2' passed."} for index, r := range er.PolicyResponse.Rules { @@ -704,7 +713,7 @@ func TestValidate_anchor_map_found_valid(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg, nil) msgs := []string{"validation rule 'pod rule 2' passed."} for index, r := range er.PolicyResponse.Rules { @@ -778,7 +787,7 @@ func TestValidate_inequality_List_Processing(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg, nil) msgs := []string{"validation rule 'pod rule 2' passed."} for index, r := range er.PolicyResponse.Rules { @@ -858,7 +867,7 @@ func TestValidate_inequality_List_ProcessingBrackets(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg, nil) msgs := []string{"validation rule 'pod rule 2' passed."} for index, r := range er.PolicyResponse.Rules { @@ -932,7 +941,7 @@ func TestValidate_anchor_map_found_invalid(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg, nil) msgs := []string{"validation error: pod: validate run as non root user. rule pod rule 2 failed at path /spec/securityContext/runAsNonRoot/"} for index, r := range er.PolicyResponse.Rules { @@ -1007,7 +1016,7 @@ func TestValidate_AnchorList_pass(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg, nil) msgs := []string{"validation rule 'pod image rule' passed."} for index, r := range er.PolicyResponse.Rules { @@ -1082,7 +1091,7 @@ func TestValidate_AnchorList_fail(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg, nil) assert.Assert(t, !er.IsSuccessful()) } @@ -1152,7 +1161,7 @@ func TestValidate_existenceAnchor_fail(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg, nil) assert.Assert(t, !er.IsSuccessful()) } @@ -1222,7 +1231,7 @@ func TestValidate_existenceAnchor_pass(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg, nil) msgs := []string{"validation rule 'pod image rule' passed."} for index, r := range er.PolicyResponse.Rules { @@ -1310,7 +1319,7 @@ func TestValidate_negationAnchor_deny(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg, nil) msgs := []string{"validation error: Host path is not allowed. rule validate-host-path failed at path /spec/volumes/0/hostPath/"} for index, r := range er.PolicyResponse.Rules { @@ -1397,7 +1406,7 @@ func TestValidate_negationAnchor_pass(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg, nil) msgs := []string{"validation rule 'validate-host-path' passed."} for index, r := range er.PolicyResponse.Rules { @@ -1474,7 +1483,7 @@ func Test_VariableSubstitutionPathNotExistInPattern(t *testing.T) { jsonContext: ctx, newResource: *resourceUnstructured, } - er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg, nil) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusError) @@ -1568,7 +1577,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_OnePatternStatisfiesButSu jsonContext: ctx, newResource: *resourceUnstructured, } - er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg, nil) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusError) @@ -1630,7 +1639,7 @@ func Test_VariableSubstitution_NotOperatorWithStringVariable(t *testing.T) { jsonContext: ctx, newResource: *resourceUnstructured, } - er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg, nil) 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/") } @@ -1722,7 +1731,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathNotPresent(t *test jsonContext: ctx, newResource: *resourceUnstructured, } - er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg, nil) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusError) @@ -1816,7 +1825,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathPresent_NonePatter jsonContext: ctx, newResource: *resourceUnstructured, } - er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg, nil) assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail) assert.Equal(t, er.PolicyResponse.Rules[0].Message, @@ -1922,7 +1931,7 @@ func Test_VariableSubstitutionValidate_VariablesInMessageAreResolved(t *testing. jsonContext: ctx, newResource: *resourceUnstructured, } - er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg, nil) 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.") } @@ -1976,7 +1985,7 @@ func Test_Flux_Kustomization_PathNotPresent(t *testing.T) { jsonContext: ctx, newResource: *resourceUnstructured, } - er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg, nil) 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) @@ -2142,7 +2151,7 @@ func executeTest(t *testing.T, test testCase) { jsonContext: ctx, } - resp := testValidate(context.TODO(), registryclient.NewOrDie(), pc, cfg) + resp := testValidate(context.TODO(), registryclient.NewOrDie(), pc, cfg, nil) if resp.IsSuccessful() && test.requestDenied { t.Errorf("Testcase has failed, policy: %v", policy.Name) } @@ -2213,21 +2222,6 @@ func TestValidate_context_variable_substitution_CLI(t *testing.T) { } `) - store.SetPolicies( - store.Policy{ - Name: "restrict-pod-count", - Rules: []store.Rule{ - { - Name: "restrict-pod-count", - Values: map[string]interface{}{ - "podcounts": "12", - }, - }, - }, - }, - ) - store.SetMock(true) - var policy kyverno.ClusterPolicy err := json.Unmarshal(rawPolicy, &policy) assert.NilError(t, err) @@ -2237,7 +2231,26 @@ func TestValidate_context_variable_substitution_CLI(t *testing.T) { msgs := []string{ "restrict pod counts to be no more than 10 on node minikube", } - er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := testValidate( + context.TODO(), + registryclient.NewOrDie(), + &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, + cfg, + enginetest.ContextLoaderFactory( + nil, + map[string]enginetest.Policy{ + "restrict-pod-count": { + Rules: map[string]enginetest.Rule{ + "restrict-pod-count": { + Values: map[string]interface{}{ + "podcounts": "12", + }, + }, + }, + }, + }, + ), + ) for index, r := range er.PolicyResponse.Rules { assert.Equal(t, r.Message, msgs[index]) } @@ -2326,7 +2339,7 @@ func Test_EmptyStringInDenyCondition(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx}, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx}, cfg, nil) assert.Assert(t, !er.IsSuccessful()) } @@ -2415,7 +2428,7 @@ func Test_StringInDenyCondition(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(resourceRaw) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx}, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx}, cfg, nil) assert.Assert(t, er.IsSuccessful()) } @@ -2464,7 +2477,7 @@ func Test_foreach_container_pass(t *testing.T) { } }`) - testForEach(t, policyraw, resourceRaw, "", engineapi.RuleStatusPass) + testForEach(t, policyraw, resourceRaw, "", engineapi.RuleStatusPass, nil) } func Test_foreach_container_fail(t *testing.T) { @@ -2500,7 +2513,7 @@ func Test_foreach_container_fail(t *testing.T) { ] }}]}}`) - testForEach(t, policyraw, resourceRaw, "", engineapi.RuleStatusFail) + testForEach(t, policyraw, resourceRaw, "", engineapi.RuleStatusFail, nil) } func Test_foreach_container_deny_fail(t *testing.T) { @@ -2554,7 +2567,7 @@ func Test_foreach_container_deny_fail(t *testing.T) { } }`) - testForEach(t, policyraw, resourceRaw, "", engineapi.RuleStatusFail) + testForEach(t, policyraw, resourceRaw, "", engineapi.RuleStatusFail, nil) } func Test_foreach_container_deny_success(t *testing.T) { @@ -2596,7 +2609,7 @@ func Test_foreach_container_deny_success(t *testing.T) { ] }}]}}`) - testForEach(t, policyraw, resourceRaw, "", engineapi.RuleStatusFail) + testForEach(t, policyraw, resourceRaw, "", engineapi.RuleStatusFail, nil) } func Test_foreach_container_deny_error(t *testing.T) { @@ -2650,7 +2663,7 @@ func Test_foreach_container_deny_error(t *testing.T) { } }`) - testForEach(t, policyraw, resourceRaw, "", engineapi.RuleStatusError) + testForEach(t, policyraw, resourceRaw, "", engineapi.RuleStatusError, nil) } func Test_foreach_context_preconditions(t *testing.T) { @@ -2723,23 +2736,28 @@ func Test_foreach_context_preconditions(t *testing.T) { } }`) - store.SetPolicies( - store.Policy{ - Name: "test", - Rules: []store.Rule{ - { - Name: "test", - Values: map[string]interface{}{ - "img.data.podvalid": "nginx/nginx:v1", - "img.data.podinvalid": "nginx/nginx:v2", + testForEach( + t, + policyraw, + resourceRaw, + "", + engineapi.RuleStatusPass, + enginetest.ContextLoaderFactory( + nil, + map[string]enginetest.Policy{ + "test": { + Rules: map[string]enginetest.Rule{ + "test": { + Values: map[string]interface{}{ + "img.data.podvalid": "nginx/nginx:v1", + "img.data.podinvalid": "nginx/nginx:v2", + }, + }, }, }, }, - }, + ), ) - store.SetMock(true) - - testForEach(t, policyraw, resourceRaw, "", engineapi.RuleStatusPass) } func Test_foreach_context_preconditions_fail(t *testing.T) { @@ -2813,23 +2831,28 @@ func Test_foreach_context_preconditions_fail(t *testing.T) { } }`) - store.SetPolicies( - store.Policy{ - Name: "test", - Rules: []store.Rule{ - { - Name: "test", - Values: map[string]interface{}{ - "img.data.podvalid": "nginx/nginx:v1", - "img.data.podinvalid": "nginx/nginx:v1", + testForEach( + t, + policyraw, + resourceRaw, + "", + engineapi.RuleStatusFail, + enginetest.ContextLoaderFactory( + nil, + map[string]enginetest.Policy{ + "test": { + Rules: map[string]enginetest.Rule{ + "test": { + Values: map[string]interface{}{ + "img.data.podvalid": "nginx/nginx:v1", + "img.data.podinvalid": "nginx/nginx:v1", + }, + }, }, }, }, - }, + ), ) - store.SetMock(true) - - testForEach(t, policyraw, resourceRaw, "", engineapi.RuleStatusFail) } func Test_foreach_element_validation(t *testing.T) { @@ -2876,7 +2899,7 @@ func Test_foreach_element_validation(t *testing.T) { ] }}]}}`) - testForEach(t, policyraw, resourceRaw, "", engineapi.RuleStatusPass) + testForEach(t, policyraw, resourceRaw, "", engineapi.RuleStatusPass, nil) } func Test_outof_foreach_element_validation(t *testing.T) { @@ -2918,7 +2941,7 @@ func Test_outof_foreach_element_validation(t *testing.T) { } }}]}}`) - testForEach(t, policyraw, resourceRaw, "", engineapi.RuleStatusError) + testForEach(t, policyraw, resourceRaw, "", engineapi.RuleStatusError, nil) } func Test_foreach_skip_initContainer_pass(t *testing.T) { @@ -2972,7 +2995,7 @@ func Test_foreach_skip_initContainer_pass(t *testing.T) { } }`) - testForEach(t, policyraw, resourceRaw, "", engineapi.RuleStatusPass) + testForEach(t, policyraw, resourceRaw, "", engineapi.RuleStatusPass, nil) } func Test_foreach_validate_nested(t *testing.T) { @@ -3068,10 +3091,10 @@ func Test_foreach_validate_nested(t *testing.T) { } }`) - testForEach(t, policyraw, resourceRaw, "", engineapi.RuleStatusPass) + testForEach(t, policyraw, resourceRaw, "", engineapi.RuleStatusPass, nil) } -func testForEach(t *testing.T, policyraw []byte, resourceRaw []byte, msg string, status engineapi.RuleStatus) { +func testForEach(t *testing.T, policyraw []byte, resourceRaw []byte, msg string, status engineapi.RuleStatus, contextLoader engineapi.ContextLoaderFactory) { var policy kyverno.ClusterPolicy assert.NilError(t, json.Unmarshal(policyraw, &policy)) resourceUnstructured, err := kubeutils.BytesToUnstructured(resourceRaw) @@ -3086,7 +3109,7 @@ func testForEach(t *testing.T, policyraw []byte, resourceRaw []byte, msg string, jsonContext: ctx, newResource: *resourceUnstructured, } - er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg, contextLoader) assert.Equal(t, er.PolicyResponse.Rules[0].Status, status) if msg != "" { @@ -3150,7 +3173,7 @@ func Test_delete_ignore_pattern(t *testing.T) { jsonContext: ctx, newResource: *resourceUnstructured, } - engineResponseCreate := testValidate(context.TODO(), registryclient.NewOrDie(), policyContextCreate, cfg) + engineResponseCreate := testValidate(context.TODO(), registryclient.NewOrDie(), policyContextCreate, cfg, nil) assert.Equal(t, len(engineResponseCreate.PolicyResponse.Rules), 1) assert.Equal(t, engineResponseCreate.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail) @@ -3159,7 +3182,7 @@ func Test_delete_ignore_pattern(t *testing.T) { jsonContext: ctx, oldResource: *resourceUnstructured, } - engineResponseDelete := testValidate(context.TODO(), registryclient.NewOrDie(), policyContextDelete, cfg) + engineResponseDelete := testValidate(context.TODO(), registryclient.NewOrDie(), policyContextDelete, cfg, nil) assert.Equal(t, len(engineResponseDelete.PolicyResponse.Rules), 0) } @@ -3218,7 +3241,7 @@ func Test_ValidatePattern_anyPattern(t *testing.T) { resourceUnstructured, err := kubeutils.BytesToUnstructured(tc.rawResource) assert.NilError(t, err) - er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg) + er := testValidate(context.TODO(), registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: enginecontext.NewContext()}, cfg, nil) if tc.expectedFailed { assert.Assert(t, er.IsFailed()) } else if tc.expectedSkipped { diff --git a/pkg/testrunner/scenario.go b/pkg/testrunner/scenario.go index 5c38f41586..423de376b0 100644 --- a/pkg/testrunner/scenario.go +++ b/pkg/testrunner/scenario.go @@ -150,7 +150,7 @@ func runTestCase(t *testing.T, tc TestCase) bool { config.NewDefaultConfiguration(), nil, registryclient.NewOrDie(), - engine.LegacyContextLoaderFactory(nil), + engineapi.DefaultContextLoaderFactory(nil), nil, ) er := eng.Mutate( diff --git a/pkg/webhooks/resource/fake.go b/pkg/webhooks/resource/fake.go index 3996f7aa27..4657c26741 100644 --- a/pkg/webhooks/resource/fake.go +++ b/pkg/webhooks/resource/fake.go @@ -8,6 +8,7 @@ import ( "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/engine/context/resolvers" "github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/metrics" @@ -60,7 +61,7 @@ func NewFakeHandlers(ctx context.Context, policyCache policycache.Cache) webhook configuration, dclient, rclient, - engine.LegacyContextLoaderFactory(configMapResolver), + engineapi.DefaultContextLoaderFactory(configMapResolver), peLister, ), } diff --git a/pkg/webhooks/resource/validation_test.go b/pkg/webhooks/resource/validation_test.go index 970333ff6b..83c09dc823 100644 --- a/pkg/webhooks/resource/validation_test.go +++ b/pkg/webhooks/resource/validation_test.go @@ -1053,7 +1053,7 @@ func TestValidate_failure_action_overrides(t *testing.T) { config.NewDefaultConfiguration(), nil, registryclient.NewOrDie(), - engine.LegacyContextLoaderFactory(nil), + engineapi.DefaultContextLoaderFactory(nil), nil, ) for i, tc := range testcases { @@ -1132,7 +1132,7 @@ func Test_RuleSelector(t *testing.T) { config.NewDefaultConfiguration(), nil, registryclient.NewOrDie(), - engine.LegacyContextLoaderFactory(nil), + engineapi.DefaultContextLoaderFactory(nil), nil, ) resp := eng.Validate(