From e00596a55145747b57c4245f9a885cd49400a628 Mon Sep 17 00:00:00 2001 From: Mariam Fahmy Date: Thu, 29 Aug 2024 16:09:30 +0300 Subject: [PATCH] fix: match wildcard names for generateExisting policies (#10945) * fix: match wildcard names for generateExisting policies Signed-off-by: Mariam Fahmy * fix chainsaw test Signed-off-by: Mariam Fahmy * chore: add unit tests Signed-off-by: Mariam Fahmy --------- Signed-off-by: Mariam Fahmy --- pkg/policy/utils.go | 18 ++++- pkg/policy/utils_test.go | 72 +++++++++++++++++++ .../README.md | 16 +++++ .../chainsaw-test.yaml | 27 +++++++ .../existing-resources.yaml | 9 +++ .../generated-resources.yaml | 11 +++ .../permissions.yaml | 19 +++++ .../policy-ready.yaml | 9 +++ .../policy.yaml | 21 ++++++ 9 files changed, 199 insertions(+), 3 deletions(-) create mode 100644 test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/README.md create mode 100755 test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/chainsaw-test.yaml create mode 100644 test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/existing-resources.yaml create mode 100644 test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/generated-resources.yaml create mode 100644 test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/permissions.yaml create mode 100644 test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/policy-ready.yaml create mode 100644 test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/policy.yaml diff --git a/pkg/policy/utils.go b/pkg/policy/utils.go index bf8b6e08aa..d9b81f15b1 100644 --- a/pkg/policy/utils.go +++ b/pkg/policy/utils.go @@ -2,16 +2,28 @@ package policy import ( kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" + "github.com/kyverno/kyverno/ext/wildcard" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) func resourceMatches(match kyvernov1.ResourceDescription, res unstructured.Unstructured, isNamespacedPolicy bool) bool { - if match.Name != "" && res.GetName() != match.Name { + if match.Name != "" && !wildcard.Match(match.Name, res.GetName()) { return false } - if len(match.Names) > 0 && !contains(match.Names, res.GetName()) { - return false + + if len(match.Names) > 0 { + isMatch := false + for _, name := range match.Names { + if wildcard.Match(name, res.GetName()) { + isMatch = true + break + } + } + if !isMatch { + return false + } } + if !isNamespacedPolicy && len(match.Namespaces) > 0 && !contains(match.Namespaces, res.GetNamespace()) { return false } diff --git a/pkg/policy/utils_test.go b/pkg/policy/utils_test.go index 29522591d2..61e44a8b4b 100644 --- a/pkg/policy/utils_test.go +++ b/pkg/policy/utils_test.go @@ -51,6 +51,78 @@ func Test_resourceMatches(t *testing.T) { isNamespacedPolicy: false, want: false, }, + { + name: "Matching resource with a wildcard name", + match: kyverno.ResourceDescription{ + Kinds: []string{"Pod"}, + Name: "my-*", + }, + res: unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": map[string]interface{}{ + "name": "my-pod", + }, + }, + }, + isNamespacedPolicy: false, + want: true, + }, + { + name: "Non-matching resource with a wildcard name", + match: kyverno.ResourceDescription{ + Kinds: []string{"Pod"}, + Name: "my-*", + }, + res: unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": map[string]interface{}{ + "name": "test-pod", + }, + }, + }, + isNamespacedPolicy: false, + want: false, + }, + { + name: "Matching resource with multiple wildcard names", + match: kyverno.ResourceDescription{ + Kinds: []string{"Pod"}, + Names: []string{"my-*", "test-pod"}, + }, + res: unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": map[string]interface{}{ + "name": "my-pod", + }, + }, + }, + isNamespacedPolicy: false, + want: true, + }, + { + name: "Non-matching resource with multiple wildcard names", + match: kyverno.ResourceDescription{ + Kinds: []string{"Pod"}, + Names: []string{"my-*", "test-pod"}, + }, + res: unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": map[string]interface{}{ + "name": "pod", + }, + }, + }, + isNamespacedPolicy: false, + want: false, + }, { name: "Matching resource based on its namespace", match: kyverno.ResourceDescription{ diff --git a/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/README.md b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/README.md new file mode 100644 index 0000000000..056e570072 --- /dev/null +++ b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/README.md @@ -0,0 +1,16 @@ +## Description + +This test makes sure that a generate existing policy that matches wildcard names in the `match` block works as expected. The policy should only generate resources for the existing resources that match the wildcard name. + + +## Expected Behavior + +1. Create two Namespaces: `tst-home-dev` and `tst-mobile-dev`. + +2. Create a policy that generates a ServiceAccount for all existing namespaces whose name matches the wildcard `tst-*`. + +3. Two ServiceAccounts are generated in `tst-home-dev` and `tst-mobile-dev`. + +## Reference Issue(s) + +#10886 diff --git a/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/chainsaw-test.yaml b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/chainsaw-test.yaml new file mode 100755 index 0000000000..9da2148934 --- /dev/null +++ b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/chainsaw-test.yaml @@ -0,0 +1,27 @@ +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + creationTimestamp: null + name: existing-with-wildcard-name-matching +spec: + steps: + - name: step-01 + try: + - apply: + file: permissions.yaml + - apply: + file: existing-resources.yaml + - name: step-02 + try: + - apply: + file: policy.yaml + - assert: + file: policy-ready.yaml + - name: step-03 + try: + - sleep: + duration: 3s + - name: step-04 + try: + - assert: + file: generated-resources.yaml diff --git a/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/existing-resources.yaml b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/existing-resources.yaml new file mode 100644 index 0000000000..651fd66e69 --- /dev/null +++ b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/existing-resources.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: tst-home-dev +--- +apiVersion: v1 +kind: Namespace +metadata: + name: tst-mobile-dev diff --git a/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/generated-resources.yaml b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/generated-resources.yaml new file mode 100644 index 0000000000..a8983ef514 --- /dev/null +++ b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/generated-resources.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: home + namespace: tst-home-dev +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: mobile + namespace: tst-mobile-dev \ No newline at end of file diff --git a/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/permissions.yaml b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/permissions.yaml new file mode 100644 index 0000000000..3dd1e4a58a --- /dev/null +++ b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/permissions.yaml @@ -0,0 +1,19 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: kyverno:serviceaccount + labels: + rbac.kyverno.io/aggregate-to-background-controller: "true" + rbac.kyverno.io/aggregate-to-reports-controller: "true" + rbac.kyverno.io/aggregate-to-admission-controller: "true" +rules: +- apiGroups: + - '' + resources: + - serviceaccounts + verbs: + - get + - watch + - list + - delete + - create diff --git a/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/policy-ready.yaml b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/policy-ready.yaml new file mode 100644 index 0000000000..eb9ec703d0 --- /dev/null +++ b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/policy-ready.yaml @@ -0,0 +1,9 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: create-default-serviceaccount +status: + conditions: + - reason: Succeeded + status: "True" + type: Ready \ No newline at end of file diff --git a/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/policy.yaml b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/policy.yaml new file mode 100644 index 0000000000..cab086c785 --- /dev/null +++ b/test/conformance/chainsaw/generate/clusterpolicy/standard/existing/existing-with-wildcard-name-matching/policy.yaml @@ -0,0 +1,21 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: create-default-serviceaccount +spec: + rules: + - name: default-service-account + match: + any: + - resources: + kinds: + - Namespace + names: + - "tst-*" + generate: + generateExisting: true + synchronize: false + apiVersion: v1 + kind: ServiceAccount + namespace: "{{request.object.metadata.name}}" + name: "{{to_lower(request.object.metadata.name | to_string(@) | split(@, '-') | [1])}}"