From 6ec2d73943132e9533f3704f78ebd42f3a40aa81 Mon Sep 17 00:00:00 2001 From: Per Goncalves da Silva Date: Mon, 7 Jul 2025 10:40:45 +0200 Subject: [PATCH] :sparkles: Add NamespaceSelector to generated webhook configs Signed-off-by: Per Goncalves da Silva --- .../registryv1/generators/generators.go | 27 ++++++++++++ .../registryv1/generators/generators_test.go | 42 ++++++++++++++++++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/internal/operator-controller/rukpak/render/registryv1/generators/generators.go b/internal/operator-controller/rukpak/render/registryv1/generators/generators.go index 7ae8de895..3fdbf943c 100644 --- a/internal/operator-controller/rukpak/render/registryv1/generators/generators.go +++ b/internal/operator-controller/rukpak/render/registryv1/generators/generators.go @@ -13,6 +13,7 @@ import ( corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/utils/ptr" @@ -29,6 +30,8 @@ import ( const ( tlsCrtPath = "tls.crt" tlsKeyPath = "tls.key" + + labelKubernetesNamespaceMetadataName = "kubernetes.io/metadata.name" ) // volume mount name -> mount path @@ -291,6 +294,7 @@ func BundleValidatingWebhookResourceGenerator(rv1 *bundle.RegistryV1, opts rende //nolint:prealloc var objs []client.Object + for _, wh := range rv1.CSV.Spec.WebhookDefinitions { if wh.Type != v1alpha1.ValidatingAdmissionWebhook { continue @@ -318,6 +322,9 @@ func BundleValidatingWebhookResourceGenerator(rv1 *bundle.RegistryV1, opts rende Port: &wh.ContainerPort, }, }, + // It is safe to create a namespace selector even for cluster scoped CRs. A webhook + // is never skipped for cluster scoped CRs. + NamespaceSelector: getWebhookNamespaceSelector(opts.TargetNamespaces), }, ), ) @@ -367,6 +374,9 @@ func BundleMutatingWebhookResourceGenerator(rv1 *bundle.RegistryV1, opts render. }, }, ReinvocationPolicy: wh.ReinvocationPolicy, + // It is safe to create a namespace selector even for cluster scoped CRs. A webhook + // is never skipped for cluster scoped CRs. + NamespaceSelector: getWebhookNamespaceSelector(opts.TargetNamespaces), }, ), ) @@ -535,3 +545,20 @@ func addCertVolumesToDeployment(dep *appsv1.Deployment, certSecretInfo render.Ce ) } } + +// getWebhookNamespaceSelector returns a label selector that matches any namespace in targetNamespaces. +// If targetNamespaces is empty, nil, or includes "" (signifying all namespaces) nil is returned. +func getWebhookNamespaceSelector(targetNamespaces []string) *metav1.LabelSelector { + if len(targetNamespaces) > 0 && !slices.Contains(targetNamespaces, "") { + return &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: labelKubernetesNamespaceMetadataName, + Operator: metav1.LabelSelectorOpIn, + Values: targetNamespaces, + }, + }, + } + } + return nil +} diff --git a/internal/operator-controller/rukpak/render/registryv1/generators/generators_test.go b/internal/operator-controller/rukpak/render/registryv1/generators/generators_test.go index bf48b2aec..9b0e58ec3 100644 --- a/internal/operator-controller/rukpak/render/registryv1/generators/generators_test.go +++ b/internal/operator-controller/rukpak/render/registryv1/generators/generators_test.go @@ -1507,7 +1507,7 @@ func Test_BundleValidatingWebhookResourceGenerator_Succeeds(t *testing.T) { }, opts: render.Options{ InstallNamespace: "install-namespace", - TargetNamespaces: []string{"watch-namespace-one", "watch-namespace-two"}, + TargetNamespaces: []string{""}, }, expectedResources: []client.Object{ &admissionregistrationv1.ValidatingWebhookConfiguration{ @@ -1554,6 +1554,7 @@ func Test_BundleValidatingWebhookResourceGenerator_Succeeds(t *testing.T) { Port: ptr.To(int32(443)), }, }, + // No NamespaceSelector is set targetNamespaces = []string{""} (AllNamespaces install mode) }, }, }, @@ -1647,6 +1648,15 @@ func Test_BundleValidatingWebhookResourceGenerator_Succeeds(t *testing.T) { Port: ptr.To(int32(443)), }, }, + NamespaceSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "kubernetes.io/metadata.name", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"watch-namespace-one", "watch-namespace-two"}, + }, + }, + }, }, }, }, @@ -1694,6 +1704,15 @@ func Test_BundleValidatingWebhookResourceGenerator_Succeeds(t *testing.T) { Port: ptr.To(int32(443)), }, }, + NamespaceSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "kubernetes.io/metadata.name", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"watch-namespace-one", "watch-namespace-two"}, + }, + }, + }, }, }, }, @@ -1772,7 +1791,7 @@ func Test_BundleMutatingWebhookResourceGenerator_Succeeds(t *testing.T) { }, opts: render.Options{ InstallNamespace: "install-namespace", - TargetNamespaces: []string{"watch-namespace-one", "watch-namespace-two"}, + TargetNamespaces: []string{""}, }, expectedResources: []client.Object{ &admissionregistrationv1.MutatingWebhookConfiguration{ @@ -1820,6 +1839,7 @@ func Test_BundleMutatingWebhookResourceGenerator_Succeeds(t *testing.T) { Port: ptr.To(int32(443)), }, }, + // No NamespaceSelector is set targetNamespaces = []string{""} (AllNamespaces install mode) }, }, }, @@ -1915,6 +1935,15 @@ func Test_BundleMutatingWebhookResourceGenerator_Succeeds(t *testing.T) { Port: ptr.To(int32(443)), }, }, + NamespaceSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "kubernetes.io/metadata.name", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"watch-namespace-one", "watch-namespace-two"}, + }, + }, + }, }, }, }, @@ -1962,6 +1991,15 @@ func Test_BundleMutatingWebhookResourceGenerator_Succeeds(t *testing.T) { Port: ptr.To(int32(443)), }, }, + NamespaceSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "kubernetes.io/metadata.name", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"watch-namespace-one", "watch-namespace-two"}, + }, + }, + }, }, }, },