From 2a375fa1b5bb024877604c976d9a5ea3078ef35a Mon Sep 17 00:00:00 2001 From: Max Goncharenko Date: Wed, 8 Sep 2021 22:33:41 +0300 Subject: [PATCH] Remove contains function (#2346) * remove contains function Signed-off-by: Maxim Goncharenko * added test for contains issue case Signed-off-by: Maxim Goncharenko --- pkg/engine/jmespath/functions.go | 25 ------ test/e2e/validate/config.go | 42 +++++++++- test/e2e/validate/resources.go | 70 +++++++++++++++++ test/e2e/validate/validate_test.go | 122 +++++++++++++++++++++++++---- 4 files changed, 218 insertions(+), 41 deletions(-) diff --git a/pkg/engine/jmespath/functions.go b/pkg/engine/jmespath/functions.go index 1614f34df0..1a49c96404 100644 --- a/pkg/engine/jmespath/functions.go +++ b/pkg/engine/jmespath/functions.go @@ -27,7 +27,6 @@ type ( // function names var ( compare = "compare" - contains = "contains" equalFold = "equal_fold" replace = "replace" replaceAll = "replace_all" @@ -35,7 +34,6 @@ var ( toLower = "to_lower" trim = "trim" split = "split" - equals = "equals" regexReplaceAll = "regex_replace_all" regexReplaceAllLiteral = "regex_replace_all_literal" regexMatch = "regex_match" @@ -56,14 +54,6 @@ func getFunctions() []*gojmespath.FunctionEntry { }, Handler: jpfCompare, }, - { - Name: contains, - Arguments: []ArgSpec{ - {Types: []JpType{JpString}}, - {Types: []JpType{JpString}}, - }, - Handler: jpfContains, - }, { Name: equalFold, Arguments: []ArgSpec{ @@ -175,21 +165,6 @@ func jpfCompare(arguments []interface{}) (interface{}, error) { return strings.Compare(a.String(), b.String()), nil } -func jpfContains(arguments []interface{}) (interface{}, error) { - var err error - str, err := validateArg(contains, arguments, 0, reflect.String) - if err != nil { - return nil, err - } - - substr, err := validateArg(contains, arguments, 1, reflect.String) - if err != nil { - return nil, err - } - - return strings.Contains(str.String(), substr.String()), nil -} - func jpfEqualFold(arguments []interface{}) (interface{}, error) { var err error a, err := validateArg(equalFold, arguments, 0, reflect.String) diff --git a/test/e2e/validate/config.go b/test/e2e/validate/config.go index f43d4b6f9a..5210314da6 100644 --- a/test/e2e/validate/config.go +++ b/test/e2e/validate/config.go @@ -1,7 +1,12 @@ package validate -// ValidateTests is E2E Test Config for validation -var ValidateTests = []struct { +import ( + "github.com/kyverno/kyverno/test/e2e" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// FluxValidateTests is E2E Test Config for validation +var FluxValidateTests = []struct { //TestName - Name of the Test TestName string // PolicyRaw - The Yaml file of the ClusterPolicy @@ -28,3 +33,36 @@ var ValidateTests = []struct { MustSucceed: true, }, } + +var podGVR = e2e.GetGVR("", "v1", "pods") + +var ValidateTests = []struct { + //TestDescription - Description of the Test + TestDescription string + // PolicyName - Name of the Policy + PolicyName string + // PolicyRaw - The Yaml file of the ClusterPolicy + PolicyRaw []byte + // ResourceName - Name of the Resource + ResourceName string + // ResourceNamespace - Namespace of the Resource + ResourceNamespace string + // ResourceGVR - GVR of the Resource + ResourceGVR schema.GroupVersionResource + // ResourceRaw - The Yaml file of the ClusterPolicy + ResourceRaw []byte + // MustSucceed - indicates if validation must succeed + MustSucceed bool +}{ + { + // Case for https://github.com/kyverno/kyverno/issues/2345 issue + TestDescription: "checks that contains function works properly with string list", + PolicyName: "drop-cap-net-raw", + PolicyRaw: kyverno_2345_policy, + ResourceName: "test", + ResourceNamespace: "test-validate1", + ResourceGVR: podGVR, + ResourceRaw: kyverno_2345_resource, + MustSucceed: false, + }, +} diff --git a/test/e2e/validate/resources.go b/test/e2e/validate/resources.go index b4056e2cee..861da52d4b 100644 --- a/test/e2e/validate/resources.go +++ b/test/e2e/validate/resources.go @@ -1,5 +1,7 @@ package validate +import "fmt" + // Namespace Description var namespaceYaml = []byte(` apiVersion: v1 @@ -8,6 +10,16 @@ metadata: name: test-validate `) +func newNamespaceYaml(name string) []byte { + ns := fmt.Sprintf(` + apiVersion: v1 + kind: Namespace + metadata: + name: %s + `, name) + return []byte(ns) +} + // Regression: https://github.com/kyverno/kyverno/issues/2043 // Policy: https://github.com/fluxcd/flux2-multi-tenancy/blob/main/infrastructure/kyverno-policies/flux-multi-tenancy.yaml var kyverno_2043_policy = []byte(` @@ -561,3 +573,61 @@ spec: prune: true validation: client `) + +var kyverno_2345_policy = []byte(` +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: drop-cap-net-raw +spec: + validationFailureAction: enforce + background: false + rules: + - name: drop-cap-net-raw + match: + resources: + kinds: + - Pod + validate: + deny: + conditions: + any: + - key: "{{ request.object.spec.containers[].securityContext.capabilities.drop[] | contains(@, 'NET_RAW') }}" + operator: Equals + value: false +`) + +var kyverno_2345_resource = []byte(` +apiVersion: v1 +kind: Pod +metadata: + name: test + namespace: test-validate1 +spec: + initContainers: + - name: jimmy + image: defdasdabian:923 + command: ["/bin/sh", "-c", "sleep infinity"] + securityContext: + capabilities: + drop: + - XXXNET_RAWYYY + - SETUID + containers: + - name: test + image: defdasdabian:923 + command: ["/bin/sh", "-c", "sleep infinity"] + securityContext: + capabilities: + drop: + - XXXNET_RAWYYY + - SETUID + - CAP_FOO_BAR + - name: asdf + image: defdasdabian:923 + command: ["/bin/sh", "-c", "sleep infinity"] + securityContext: + capabilities: + drop: + - CAP_SOMETHING +`) diff --git a/test/e2e/validate/validate_test.go b/test/e2e/validate/validate_test.go index 424d335fa2..52bffe3f30 100644 --- a/test/e2e/validate/validate_test.go +++ b/test/e2e/validate/validate_test.go @@ -8,22 +8,24 @@ import ( "time" "github.com/kyverno/kyverno/test/e2e" + commonE2E "github.com/kyverno/kyverno/test/e2e/common" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + k8sErrors "k8s.io/apimachinery/pkg/api/errors" ) var ( // Cluster Polict GVR - clPolGVR = e2e.GetGVR("kyverno.io", "v1", "clusterpolicies") + policyGVR = e2e.GetGVR("kyverno.io", "v1", "clusterpolicies") // Namespace GVR - nsGVR = e2e.GetGVR("", "v1", "namespaces") + namespaceGVR = e2e.GetGVR("", "v1", "namespaces") // ConfigMap GVR cmGVR = e2e.GetGVR("", "v1", "configmaps") crdGVR = e2e.GetGVR("apiextensions.k8s.io", "v1", "customresourcedefinitions") // ClusterPolicy Namespace - clPolNS = "" + policyNamespace = "" // Namespace Name // Hardcoded in YAML Definition nspace = "test-validate" @@ -31,7 +33,7 @@ var ( crdName = "kustomizations.kustomize.toolkit.fluxcd.io" ) -func Test_Validate_Sets(t *testing.T) { +func Test_Validate_Flux_Sets(t *testing.T) { RegisterTestingT(t) if os.Getenv("E2E") == "" { t.Skip("Skipping E2E Test") @@ -41,21 +43,21 @@ func Test_Validate_Sets(t *testing.T) { e2eClient, err := e2e.NewE2EClient() Expect(err).To(BeNil()) - for _, test := range ValidateTests { + for _, test := range FluxValidateTests { By(fmt.Sprintf("Test to validate objects: \"%s\"", test.TestName)) // Clean up Resources By(fmt.Sprintf("Cleaning Cluster Policies")) - e2eClient.CleanClusterPolicies(clPolGVR) + e2eClient.CleanClusterPolicies(policyGVR) // Clear Namespace By(fmt.Sprintf("Deleting Namespace: \"%s\"", nspace)) - e2eClient.DeleteClusteredResource(nsGVR, nspace) + e2eClient.DeleteClusteredResource(namespaceGVR, nspace) //CleanUp CRDs e2eClient.DeleteClusteredResource(crdGVR, crdName) // Wait Till Deletion of Namespace e2e.GetWithRetry(time.Duration(1*time.Second), 15, func() error { - _, err := e2eClient.GetClusteredResource(nsGVR, nspace) + _, err := e2eClient.GetClusteredResource(namespaceGVR, nspace) if err != nil { return nil } @@ -64,12 +66,12 @@ func Test_Validate_Sets(t *testing.T) { // Create Namespace By(fmt.Sprintf("Creating namespace \"%s\"", nspace)) - _, err = e2eClient.CreateClusteredResourceYaml(nsGVR, namespaceYaml) + _, err = e2eClient.CreateClusteredResourceYaml(namespaceGVR, namespaceYaml) Expect(err).NotTo(HaveOccurred()) // Create policy - By(fmt.Sprintf("Creating policy in \"%s\"", clPolNS)) - _, err = e2eClient.CreateNamespacedResourceYaml(clPolGVR, clPolNS, test.PolicyRaw) + By(fmt.Sprintf("Creating policy in \"%s\"", policyNamespace)) + _, err = e2eClient.CreateNamespacedResourceYaml(policyGVR, policyNamespace, test.PolicyRaw) Expect(err).NotTo(HaveOccurred()) // Create Flux CRD @@ -101,16 +103,16 @@ func Test_Validate_Sets(t *testing.T) { } //CleanUp Resources - e2eClient.CleanClusterPolicies(clPolGVR) + e2eClient.CleanClusterPolicies(policyGVR) //CleanUp CRDs e2eClient.DeleteClusteredResource(crdGVR, crdName) // Clear Namespace - e2eClient.DeleteClusteredResource(nsGVR, nspace) + e2eClient.DeleteClusteredResource(namespaceGVR, nspace) // Wait Till Deletion of Namespace e2e.GetWithRetry(time.Duration(1*time.Second), 15, func() error { - _, err := e2eClient.GetClusteredResource(nsGVR, nspace) + _, err := e2eClient.GetClusteredResource(namespaceGVR, nspace) if err != nil { return nil } @@ -120,3 +122,95 @@ func Test_Validate_Sets(t *testing.T) { By(fmt.Sprintf("Test %s Completed \n\n\n", test.TestName)) } } + +func TestValidate(t *testing.T) { + RegisterTestingT(t) + if os.Getenv("E2E") == "" { + t.Skip("Skipping E2E Test") + } + + e2eClient, err := e2e.NewE2EClient() + Expect(err).To(BeNil()) + + for _, test := range ValidateTests { + By(fmt.Sprintf("Mutation Test: %s", test.TestDescription)) + + By("Deleting Cluster Policies...") + _ = e2eClient.CleanClusterPolicies(policyGVR) + + By("Deleting Resource...") + _ = e2eClient.DeleteNamespacedResource(test.ResourceGVR, test.ResourceNamespace, test.ResourceName) + + By("Deleting Namespace...") + By(fmt.Sprintf("Deleting Namespace: %s...", test.ResourceNamespace)) + _ = e2eClient.DeleteClusteredResource(namespaceGVR, test.ResourceNamespace) + + By("Wait Till Deletion of Namespace...") + err = e2e.GetWithRetry(1*time.Second, 15, func() error { + _, err := e2eClient.GetClusteredResource(namespaceGVR, test.ResourceNamespace) + if err != nil { + return nil + } + return fmt.Errorf("failed to delete namespace: %v", err) + }) + Expect(err).NotTo(HaveOccurred()) + + By(fmt.Sprintf("Creating Namespace: %s...", policyNamespace)) + _, err = e2eClient.CreateClusteredResourceYaml(namespaceGVR, newNamespaceYaml(test.ResourceNamespace)) + Expect(err).NotTo(HaveOccurred()) + + By("Wait Till Creation of Namespace...") + err = e2e.GetWithRetry(1*time.Second, 15, func() error { + _, err := e2eClient.GetClusteredResource(namespaceGVR, test.ResourceNamespace) + if err != nil { + return err + } + + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + By("Creating Policy...") + _, err = e2eClient.CreateNamespacedResourceYaml(policyGVR, policyNamespace, test.PolicyRaw) + Expect(err).NotTo(HaveOccurred()) + + err = commonE2E.PolicyCreated(test.PolicyName) + Expect(err).NotTo(HaveOccurred()) + + By("Creating Resource...") + _, err = e2eClient.CreateNamespacedResourceYaml(test.ResourceGVR, test.ResourceNamespace, test.ResourceRaw) + + statusErr, ok := err.(*k8sErrors.StatusError) + validationError := (ok && statusErr.ErrStatus.Code == 400) // Validation error is always Bad Request + + if test.MustSucceed || !validationError { + Expect(err).NotTo(HaveOccurred()) + } else { + Expect(err).To(HaveOccurred()) + } + + By("Deleting Cluster Policies...") + err = e2eClient.CleanClusterPolicies(policyGVR) + Expect(err).NotTo(HaveOccurred()) + + By("Deleting Resource...") // if it is present, so ignore an error + e2eClient.DeleteNamespacedResource(test.ResourceGVR, test.ResourceNamespace, test.ResourceName) + + By("Deleting Namespace...") + err = e2eClient.DeleteClusteredResource(namespaceGVR, test.ResourceNamespace) + Expect(err).NotTo(HaveOccurred()) + + By("Wait Till Creation of Namespace...") + e2e.GetWithRetry(1*time.Second, 15, func() error { + _, err := e2eClient.GetClusteredResource(namespaceGVR, test.ResourceNamespace) + if err != nil { + return nil + } + return fmt.Errorf("failed to delete namespace: %v", err) + }) + + // Do not fail if waiting fails. Sometimes namespace needs time to be deleted. + + By("Done") + } +}