From 8a62aaa6eb15e654f036aca343032f762dbbe075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Charles-Edouard=20Br=C3=A9t=C3=A9ch=C3=A9?= Date: Mon, 19 Jun 2023 08:06:30 +0200 Subject: [PATCH] fix: autogen not working correctly with cronjob conditions (#7571) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Charles-Edouard Brétéché --- pkg/autogen/autogen.go | 52 --- pkg/autogen/autogen_test.go | 314 ------------------ pkg/autogen/rule.go | 5 +- .../kuttl/autogen/conditions/01-policy.yaml | 6 + .../kuttl/autogen/conditions/README.md | 11 + .../autogen/conditions/policy-assert.yaml | 49 +++ .../kuttl/autogen/conditions/policy.yaml | 24 ++ 7 files changed, 94 insertions(+), 367 deletions(-) create mode 100644 test/conformance/kuttl/autogen/conditions/01-policy.yaml create mode 100644 test/conformance/kuttl/autogen/conditions/README.md create mode 100644 test/conformance/kuttl/autogen/conditions/policy-assert.yaml create mode 100644 test/conformance/kuttl/autogen/conditions/policy.yaml diff --git a/pkg/autogen/autogen.go b/pkg/autogen/autogen.go index 47db6a8319..fde853f495 100644 --- a/pkg/autogen/autogen.go +++ b/pkg/autogen/autogen.go @@ -2,12 +2,9 @@ package autogen import ( "encoding/json" - "fmt" - "strconv" "strings" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" - jsonutils "github.com/kyverno/kyverno/pkg/utils/json" kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" "golang.org/x/exp/slices" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -167,55 +164,6 @@ func GetControllers(meta *metav1.ObjectMeta, spec *kyvernov1.Spec) ([]string, [] // copy entire match / exclude block, it's users' responsibility to // make sure all fields are applicable to pod controllers -// GenerateRulePatches generates rule for podControllers based on scenario A and C -func GenerateRulePatches(spec *kyvernov1.Spec, controllers string) (rulePatches [][]byte, errs []error) { - ruleIndex := make(map[string]int) - for index, rule := range spec.Rules { - ruleIndex[rule.Name] = index - } - insertIdx := len(spec.Rules) - genRules := generateRules(spec, controllers) - for i := range genRules { - patchPostion := insertIdx - convertToPatches := func(genRule kyvernoRule, patchPostion int) []byte { - operation := "add" - if existingIndex, alreadyExists := ruleIndex[genRule.Name]; alreadyExists { - operation = "replace" - patchPostion = existingIndex - } - patch := jsonutils.NewPatchOperation(fmt.Sprintf("/spec/rules/%s", strconv.Itoa(patchPostion)), operation, genRule) - pbytes, err := patch.Marshal() - if err != nil { - errs = append(errs, err) - return nil - } - if err := jsonutils.CheckPatch(pbytes); err != nil { - errs = append(errs, err) - return nil - } - return pbytes - } - genRule := createRule(&genRules[i]) - if genRule != nil { - pbytes := convertToPatches(*genRule, patchPostion) - if pbytes != nil { - rulePatches = append(rulePatches, pbytes) - } - insertIdx++ - } - } - return -} - -// podControllersKey annotation could be: -// scenario A: not exist, set default to "all", which generates on all pod controllers -// - if name / selector exist in resource description -> skip -// as these fields may not be applicable to pod controllers -// scenario B: "none", user explicitly disable this feature -> skip -// scenario C: some certain controllers that user set -> generate on defined controllers -// copy entire match / exclude block, it's users' responsibility to -// make sure all fields are applicable to pod controllers - // generateRules generates rule for podControllers based on scenario A and C func generateRules(spec *kyvernov1.Spec, controllers string) []kyvernov1.Rule { var rules []kyvernov1.Rule diff --git a/pkg/autogen/autogen_test.go b/pkg/autogen/autogen_test.go index cc1809fee0..567560082d 100644 --- a/pkg/autogen/autogen_test.go +++ b/pkg/autogen/autogen_test.go @@ -3,8 +3,6 @@ package autogen import ( "encoding/json" "fmt" - "os" - "path/filepath" "reflect" "strings" "testing" @@ -310,250 +308,6 @@ func Test_GetRequestedControllers(t *testing.T) { } } -func Test_Any(t *testing.T) { - dir, err := os.Getwd() - baseDir := filepath.Dir(filepath.Dir(dir)) - assert.NilError(t, err) - file, err := os.ReadFile(baseDir + "/test/best_practices/disallow_bind_mounts.yaml") - if err != nil { - t.Log(err) - } - policies, _, err := yamlutils.GetPolicy(file) - if err != nil { - t.Log(err) - } - - policy := policies[0] - spec := policy.GetSpec() - spec.Rules[0].MatchResources.Any = kyverno.ResourceFilters{ - { - ResourceDescription: kyverno.ResourceDescription{ - Kinds: []string{"Pod"}, - }, - }, - } - - rulePatches, errs := GenerateRulePatches(spec, PodControllers) - if len(errs) != 0 { - t.Log(errs) - } - expectedPatches := [][]byte{ - []byte(`{"path":"/spec/rules/1","op":"add","value":{"name":"autogen-validate-hostPath","match":{"any":[{"resources":{"kinds":["DaemonSet","Deployment","Job","StatefulSet","ReplicaSet","ReplicationController"]}}],"resources":{"kinds":["Pod"]}},"validate":{"message":"Host path volumes are not allowed","pattern":{"spec":{"template":{"spec":{"=(volumes)":[{"X(hostPath)":"null"}]}}}}}}}`), - []byte(`{"path":"/spec/rules/2","op":"add","value":{"name":"autogen-cronjob-validate-hostPath","match":{"any":[{"resources":{"kinds":["CronJob"]}}],"resources":{"kinds":["Pod"]}},"validate":{"message":"Host path volumes are not allowed","pattern":{"spec":{"jobTemplate":{"spec":{"template":{"spec":{"=(volumes)":[{"X(hostPath)":"null"}]}}}}}}}}}`), - } - - for i, ep := range expectedPatches { - assert.Equal(t, string(rulePatches[i]), string(ep), - fmt.Sprintf("unexpected patch: %s\nexpected: %s", rulePatches[i], ep)) - } -} - -func Test_All(t *testing.T) { - dir, err := os.Getwd() - baseDir := filepath.Dir(filepath.Dir(dir)) - assert.NilError(t, err) - file, err := os.ReadFile(baseDir + "/test/best_practices/disallow_bind_mounts.yaml") - if err != nil { - t.Log(err) - } - policies, _, err := yamlutils.GetPolicy(file) - if err != nil { - t.Log(err) - } - - policy := policies[0] - spec := policy.GetSpec() - spec.Rules[0].MatchResources.All = kyverno.ResourceFilters{ - { - ResourceDescription: kyverno.ResourceDescription{ - Kinds: []string{"Pod"}, - }, - }, - } - - rulePatches, errs := GenerateRulePatches(spec, PodControllers) - if len(errs) != 0 { - t.Log(errs) - } - - expectedPatches := [][]byte{ - []byte(`{"path":"/spec/rules/1","op":"add","value":{"name":"autogen-validate-hostPath","match":{"all":[{"resources":{"kinds":["DaemonSet","Deployment","Job","StatefulSet","ReplicaSet","ReplicationController"]}}],"resources":{"kinds":["Pod"]}},"validate":{"message":"Host path volumes are not allowed","pattern":{"spec":{"template":{"spec":{"=(volumes)":[{"X(hostPath)":"null"}]}}}}}}}`), - []byte(`{"path":"/spec/rules/2","op":"add","value":{"name":"autogen-cronjob-validate-hostPath","match":{"all":[{"resources":{"kinds":["CronJob"]}}],"resources":{"kinds":["Pod"]}},"validate":{"message":"Host path volumes are not allowed","pattern":{"spec":{"jobTemplate":{"spec":{"template":{"spec":{"=(volumes)":[{"X(hostPath)":"null"}]}}}}}}}}}`), - } - - for i, ep := range expectedPatches { - assert.Equal(t, string(rulePatches[i]), string(ep), - fmt.Sprintf("unexpected patch: %s\nexpected: %s", rulePatches[i], ep)) - } -} - -func Test_Exclude(t *testing.T) { - dir, err := os.Getwd() - baseDir := filepath.Dir(filepath.Dir(dir)) - assert.NilError(t, err) - file, err := os.ReadFile(baseDir + "/test/best_practices/disallow_bind_mounts.yaml") - if err != nil { - t.Log(err) - } - policies, _, err := yamlutils.GetPolicy(file) - if err != nil { - t.Log(err) - } - - policy := policies[0] - spec := policy.GetSpec() - spec.Rules[0].ExcludeResources.Namespaces = []string{"fake-namespce"} - - rulePatches, errs := GenerateRulePatches(spec, PodControllers) - if len(errs) != 0 { - t.Log(errs) - } - - expectedPatches := [][]byte{ - []byte(`{"path":"/spec/rules/1","op":"add","value":{"name":"autogen-validate-hostPath","match":{"resources":{"kinds":["DaemonSet","Deployment","Job","StatefulSet","ReplicaSet","ReplicationController"]}},"exclude":{"resources":{"namespaces":["fake-namespce"]}},"validate":{"message":"Host path volumes are not allowed","pattern":{"spec":{"template":{"spec":{"=(volumes)":[{"X(hostPath)":"null"}]}}}}}}}`), - []byte(`{"path":"/spec/rules/2","op":"add","value":{"name":"autogen-cronjob-validate-hostPath","match":{"resources":{"kinds":["CronJob"]}},"exclude":{"resources":{"namespaces":["fake-namespce"]}},"validate":{"message":"Host path volumes are not allowed","pattern":{"spec":{"jobTemplate":{"spec":{"template":{"spec":{"=(volumes)":[{"X(hostPath)":"null"}]}}}}}}}}}`), - } - - for i, ep := range expectedPatches { - assert.Equal(t, string(rulePatches[i]), string(ep), - fmt.Sprintf("unexpected patch: %s\nexpected: %s", rulePatches[i], ep)) - } -} - -func Test_CronJobOnly(t *testing.T) { - controllers := PodControllerCronJob - dir, err := os.Getwd() - baseDir := filepath.Dir(filepath.Dir(dir)) - assert.NilError(t, err) - file, err := os.ReadFile(baseDir + "/test/best_practices/disallow_bind_mounts.yaml") - if err != nil { - t.Log(err) - } - policies, _, err := yamlutils.GetPolicy(file) - if err != nil { - t.Log(err) - } - - policy := policies[0] - policy.SetAnnotations(map[string]string{ - kyverno.PodControllersAnnotation: controllers, - }) - - rulePatches, errs := GenerateRulePatches(policy.GetSpec(), controllers) - if len(errs) != 0 { - t.Log(errs) - } - - expectedPatches := [][]byte{ - []byte(`{"path":"/spec/rules/1","op":"add","value":{"name":"autogen-cronjob-validate-hostPath","match":{"resources":{"kinds":["CronJob"]}},"validate":{"message":"Host path volumes are not allowed","pattern":{"spec":{"jobTemplate":{"spec":{"template":{"spec":{"=(volumes)":[{"X(hostPath)":"null"}]}}}}}}}}}`), - } - - assert.DeepEqual(t, rulePatches, expectedPatches) -} - -func Test_ForEachPod(t *testing.T) { - dir, err := os.Getwd() - baseDir := filepath.Dir(filepath.Dir(dir)) - assert.NilError(t, err) - file, err := os.ReadFile(baseDir + "/test/policy/mutate/policy_mutate_pod_foreach_with_context.yaml") - if err != nil { - t.Log(err) - } - policies, _, err := yamlutils.GetPolicy(file) - if err != nil { - t.Log(err) - } - - policy := policies[0] - spec := policy.GetSpec() - spec.Rules[0].ExcludeResources.Namespaces = []string{"fake-namespce"} - - rulePatches, errs := GenerateRulePatches(spec, PodControllers) - if len(errs) != 0 { - t.Log(errs) - } - - expectedPatches := [][]byte{ - []byte(`{"path":"/spec/rules/1","op":"add","value":{"name":"autogen-resolve-image-containers","match":{"resources":{"kinds":["DaemonSet","Deployment","Job","StatefulSet","ReplicaSet","ReplicationController"]}},"exclude":{"resources":{"namespaces":["fake-namespce"]}},"preconditions":{"all":[{"key":"{{request.operation}}","operator":"In","value":["CREATE","UPDATE"]}]},"mutate":{"foreach":[{"list":"request.object.spec.template.spec.containers","context":[{"name":"dictionary","configMap":{"name":"some-config-map","namespace":"some-namespace"}}],"patchStrategicMerge":{"spec":{"template":{"spec":{"containers":[{"image":"{{ dictionary.data.image }}","name":"{{ element.name }}"}]}}}}}]}}}`), - []byte(`{"path":"/spec/rules/2","op":"add","value":{"name":"autogen-cronjob-resolve-image-containers","match":{"resources":{"kinds":["CronJob"]}},"exclude":{"resources":{"namespaces":["fake-namespce"]}},"preconditions":{"all":[{"key":"{{request.operation}}","operator":"In","value":["CREATE","UPDATE"]}]},"mutate":{"foreach":[{"list":"request.object.spec.jobTemplate.spec.template.spec.containers","context":[{"name":"dictionary","configMap":{"name":"some-config-map","namespace":"some-namespace"}}],"patchStrategicMerge":{"spec":{"jobTemplate":{"spec":{"template":{"spec":{"containers":[{"image":"{{ dictionary.data.image }}","name":"{{ element.name }}"}]}}}}}}}]}}}`), - } - - for i, ep := range expectedPatches { - assert.Equal(t, string(rulePatches[i]), string(ep), - fmt.Sprintf("unexpected patch: %s\nexpected: %s", rulePatches[i], ep)) - } -} - -func Test_CronJob_hasExclude(t *testing.T) { - controllers := PodControllerCronJob - dir, err := os.Getwd() - baseDir := filepath.Dir(filepath.Dir(dir)) - assert.NilError(t, err) - - file, err := os.ReadFile(baseDir + "/test/best_practices/disallow_bind_mounts.yaml") - if err != nil { - t.Log(err) - } - policies, _, err := yamlutils.GetPolicy(file) - if err != nil { - t.Log(err) - } - - policy := policies[0] - policy.SetAnnotations(map[string]string{ - kyverno.PodControllersAnnotation: controllers, - }) - - spec := policy.GetSpec() - rule := spec.Rules[0].DeepCopy() - rule.ExcludeResources.Kinds = []string{"Pod"} - rule.ExcludeResources.Namespaces = []string{"test"} - spec.Rules[0] = *rule - - rulePatches, errs := GenerateRulePatches(spec, controllers) - if len(errs) != 0 { - t.Log(errs) - } - - expectedPatches := [][]byte{ - []byte(`{"path":"/spec/rules/1","op":"add","value":{"name":"autogen-cronjob-validate-hostPath","match":{"resources":{"kinds":["CronJob"]}},"exclude":{"resources":{"kinds":["CronJob"],"namespaces":["test"]}},"validate":{"message":"Host path volumes are not allowed","pattern":{"spec":{"jobTemplate":{"spec":{"template":{"spec":{"=(volumes)":[{"X(hostPath)":"null"}]}}}}}}}}}`), - } - - assert.DeepEqual(t, rulePatches, expectedPatches) -} - -func Test_CronJobAndDeployment(t *testing.T) { - controllers := strings.Join([]string{PodControllerCronJob, "Deployment"}, ",") - dir, err := os.Getwd() - baseDir := filepath.Dir(filepath.Dir(dir)) - assert.NilError(t, err) - file, err := os.ReadFile(baseDir + "/test/best_practices/disallow_bind_mounts.yaml") - if err != nil { - t.Log(err) - } - policies, _, err := yamlutils.GetPolicy(file) - if err != nil { - t.Log(err) - } - - policy := policies[0] - policy.SetAnnotations(map[string]string{ - kyverno.PodControllersAnnotation: controllers, - }) - - rulePatches, errs := GenerateRulePatches(policy.GetSpec(), controllers) - if len(errs) != 0 { - t.Log(errs) - } - - expectedPatches := [][]byte{ - []byte(`{"path":"/spec/rules/1","op":"add","value":{"name":"autogen-validate-hostPath","match":{"resources":{"kinds":["Deployment"]}},"validate":{"message":"Host path volumes are not allowed","pattern":{"spec":{"template":{"spec":{"=(volumes)":[{"X(hostPath)":"null"}]}}}}}}}`), - []byte(`{"path":"/spec/rules/2","op":"add","value":{"name":"autogen-cronjob-validate-hostPath","match":{"resources":{"kinds":["CronJob"]}},"validate":{"message":"Host path volumes are not allowed","pattern":{"spec":{"jobTemplate":{"spec":{"template":{"spec":{"=(volumes)":[{"X(hostPath)":"null"}]}}}}}}}}}`), - } - - assert.DeepEqual(t, rulePatches, expectedPatches) -} - func TestUpdateGenRuleByte(t *testing.T) { tests := []struct { pbyte []byte @@ -595,74 +349,6 @@ func TestUpdateGenRuleByte(t *testing.T) { } } -func Test_UpdateVariablePath(t *testing.T) { - dir, err := os.Getwd() - baseDir := filepath.Dir(filepath.Dir(dir)) - assert.NilError(t, err) - file, err := os.ReadFile(baseDir + "/test/best_practices/select-secrets.yaml") - if err != nil { - t.Log(err) - } - policies, _, err := yamlutils.GetPolicy(file) - if err != nil { - t.Log(err) - } - - policy := policies[0] - - rulePatches, errs := GenerateRulePatches(policy.GetSpec(), PodControllers) - if len(errs) != 0 { - t.Log(errs) - } - expectedPatches := [][]byte{ - []byte(`{"path":"/spec/rules/1","op":"add","value":{"name":"autogen-select-secrets-from-volumes","match":{"resources":{"kinds":["DaemonSet","Deployment","Job","StatefulSet","ReplicaSet","ReplicationController"]}},"context":[{"name":"volsecret","apiCall":{"urlPath":"/api/v1/namespaces/{{request.object.spec.template.metadata.namespace}}/secrets/{{request.object.spec.template.spec.volumes[0].secret.secretName}}","jmesPath":"metadata.labels.foo"}}],"preconditions":[{"key":"{{ request.operation }}","operator":"Equals","value":"CREATE"}],"validate":{"message":"The Secret named {{request.object.spec.template.spec.volumes[0].secret.secretName}} is restricted and may not be used.","pattern":{"spec":{"template":{"spec":{"containers":[{"image":"registry.domain.com/*"}]}}}}}}}`), - []byte(`{"path":"/spec/rules/2","op":"add","value":{"name":"autogen-cronjob-select-secrets-from-volumes","match":{"resources":{"kinds":["CronJob"]}},"context":[{"name":"volsecret","apiCall":{"urlPath":"/api/v1/namespaces/{{request.object.spec.template.metadata.namespace}}/secrets/{{request.object.spec.jobTemplate.spec.template.spec.volumes[0].secret.secretName}}","jmesPath":"metadata.labels.foo"}}],"preconditions":[{"key":"{{ request.operation }}","operator":"Equals","value":"CREATE"}],"validate":{"message":"The Secret named {{request.object.spec.jobTemplate.spec.template.spec.volumes[0].secret.secretName}} is restricted and may not be used.","pattern":{"spec":{"jobTemplate":{"spec":{"template":{"spec":{"containers":[{"image":"registry.domain.com/*"}]}}}}}}}}}`), - } - - for i, ep := range expectedPatches { - assert.Equal(t, string(rulePatches[i]), string(ep), - fmt.Sprintf("unexpected patch: %s\nexpected: %s", rulePatches[i], ep)) - } -} - -func Test_Deny(t *testing.T) { - dir, err := os.Getwd() - baseDir := filepath.Dir(filepath.Dir(dir)) - assert.NilError(t, err) - file, err := os.ReadFile(baseDir + "/test/policy/deny/policy.yaml") - if err != nil { - t.Log(err) - } - policies, _, err := yamlutils.GetPolicy(file) - if err != nil { - t.Log(err) - } - - policy := policies[0] - spec := policy.GetSpec() - spec.Rules[0].MatchResources.Any = kyverno.ResourceFilters{ - { - ResourceDescription: kyverno.ResourceDescription{ - Kinds: []string{"Pod"}, - }, - }, - } - - rulePatches, errs := GenerateRulePatches(spec, PodControllers) - if len(errs) != 0 { - t.Log(errs) - } - expectedPatches := [][]byte{ - []byte(`{"path":"/spec/rules/1","op":"add","value":{"name":"autogen-disallow-mount-containerd-sock","match":{"any":[{"resources":{"kinds":["DaemonSet","Deployment","Job","StatefulSet","ReplicaSet","ReplicationController"]}}],"resources":{"kinds":["Pod"]}},"validate":{"foreach":[{"list":"request.object.spec.template.spec.volumes[]","deny":{"conditions":{"any":[{"key":"{{ path_canonicalize(element.hostPath.path) }}","operator":"Equals","value":"/var/run/containerd/containerd.sock"},{"key":"{{ path_canonicalize(element.hostPath.path) }}","operator":"Equals","value":"/run/containerd/containerd.sock"},{"key":"{{ path_canonicalize(element.hostPath.path) }}","operator":"Equals","value":"\\var\\run\\containerd\\containerd.sock"}]}}}]}}}`), - []byte(`{"path":"/spec/rules/2","op":"add","value":{"name":"autogen-cronjob-disallow-mount-containerd-sock","match":{"any":[{"resources":{"kinds":["CronJob"]}}],"resources":{"kinds":["Pod"]}},"validate":{"foreach":[{"list":"request.object.spec.jobTemplate.spec.template.spec.volumes[]","deny":{"conditions":{"any":[{"key":"{{ path_canonicalize(element.hostPath.path) }}","operator":"Equals","value":"/var/run/containerd/containerd.sock"},{"key":"{{ path_canonicalize(element.hostPath.path) }}","operator":"Equals","value":"/run/containerd/containerd.sock"},{"key":"{{ path_canonicalize(element.hostPath.path) }}","operator":"Equals","value":"\\var\\run\\containerd\\containerd.sock"}]}}}]}}}`), - } - - for i, ep := range expectedPatches { - assert.Equal(t, string(rulePatches[i]), string(ep), - fmt.Sprintf("unexpected patch: %s\nexpected: %s", rulePatches[i], ep)) - } -} - func Test_ComputeRules(t *testing.T) { intPtr := func(i int) *int { return &i } testCases := []struct { diff --git a/pkg/autogen/rule.go b/pkg/autogen/rule.go index 6b19134c5e..6d121f6eb7 100644 --- a/pkg/autogen/rule.go +++ b/pkg/autogen/rule.go @@ -304,12 +304,15 @@ func updateGenRuleByte(pbyte []byte, kind string) (obj []byte) { if kind == "Pod" { obj = []byte(strings.ReplaceAll(string(pbyte), "request.object.spec", "request.object.spec.template.spec")) obj = []byte(strings.ReplaceAll(string(obj), "request.oldObject.spec", "request.oldObject.spec.template.spec")) + obj = []byte(strings.ReplaceAll(string(obj), "request.object.metadata", "request.object.spec.template.metadata")) + obj = []byte(strings.ReplaceAll(string(obj), "request.oldObject.metadata", "request.oldObject.spec.template.metadata")) } if kind == "Cronjob" { obj = []byte(strings.ReplaceAll(string(pbyte), "request.object.spec", "request.object.spec.jobTemplate.spec.template.spec")) obj = []byte(strings.ReplaceAll(string(obj), "request.oldObject.spec", "request.oldObject.spec.jobTemplate.spec.template.spec")) + obj = []byte(strings.ReplaceAll(string(obj), "request.object.metadata", "request.object.spec.jobTemplate.spec.template.metadata")) + obj = []byte(strings.ReplaceAll(string(obj), "request.oldObject.metadata", "request.oldObject.spec.jobTemplate.spec.template.metadata")) } - obj = []byte(strings.ReplaceAll(string(obj), "request.object.metadata", "request.object.spec.template.metadata")) return obj } diff --git a/test/conformance/kuttl/autogen/conditions/01-policy.yaml b/test/conformance/kuttl/autogen/conditions/01-policy.yaml new file mode 100644 index 0000000000..b088ed7601 --- /dev/null +++ b/test/conformance/kuttl/autogen/conditions/01-policy.yaml @@ -0,0 +1,6 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +apply: +- policy.yaml +assert: +- policy-assert.yaml diff --git a/test/conformance/kuttl/autogen/conditions/README.md b/test/conformance/kuttl/autogen/conditions/README.md new file mode 100644 index 0000000000..e52cebc4c0 --- /dev/null +++ b/test/conformance/kuttl/autogen/conditions/README.md @@ -0,0 +1,11 @@ +## Description + +The policy should contain autogen rules with deny conditions correctly adjusted. + +## Expected Behavior + +The policy contains autogen rules with deny conditions correctly adjusted. + +## Related Issue(s) + +- https://github.com/kyverno/kyverno/issues/7566 diff --git a/test/conformance/kuttl/autogen/conditions/policy-assert.yaml b/test/conformance/kuttl/autogen/conditions/policy-assert.yaml new file mode 100644 index 0000000000..4d70890e58 --- /dev/null +++ b/test/conformance/kuttl/autogen/conditions/policy-assert.yaml @@ -0,0 +1,49 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: allowed-annotations +spec: {} +status: + autogen: + rules: + - match: + any: + - resources: + kinds: + - DaemonSet + - Deployment + - Job + - StatefulSet + - ReplicaSet + - ReplicationController + name: autogen-allowed-fluxcd-annotations + validate: + deny: + conditions: + all: + - key: '{{ request.object.spec.template.metadata.annotations.keys(@)[?contains(@, ''fluxcd.io/'')] }}' + operator: AnyNotIn + value: + - fluxcd.io/cow + - fluxcd.io/dog + message: The only approved FluxCD annotations are `fluxcd.io/cow` and `fluxcd.io/dog`. + - match: + any: + - resources: + kinds: + - CronJob + name: autogen-cronjob-allowed-fluxcd-annotations + validate: + deny: + conditions: + all: + - key: '{{ request.object.spec.jobTemplate.spec.template.metadata.annotations.keys(@)[?contains(@, ''fluxcd.io/'')] }}' + operator: AnyNotIn + value: + - fluxcd.io/cow + - fluxcd.io/dog + message: The only approved FluxCD annotations are `fluxcd.io/cow` and `fluxcd.io/dog`. + conditions: + - reason: Succeeded + status: "True" + type: Ready diff --git a/test/conformance/kuttl/autogen/conditions/policy.yaml b/test/conformance/kuttl/autogen/conditions/policy.yaml new file mode 100644 index 0000000000..e0d1a7d0ef --- /dev/null +++ b/test/conformance/kuttl/autogen/conditions/policy.yaml @@ -0,0 +1,24 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: allowed-annotations +spec: + background: true + rules: + - match: + any: + - resources: + kinds: + - Pod + name: allowed-fluxcd-annotations + validate: + deny: + conditions: + all: + - key: '{{ request.object.metadata.annotations.keys(@)[?contains(@, ''fluxcd.io/'')] }}' + operator: AnyNotIn + value: + - fluxcd.io/cow + - fluxcd.io/dog + message: The only approved FluxCD annotations are `fluxcd.io/cow` and `fluxcd.io/dog`. + validationFailureAction: Enforce