From c128fb45accbacb47cbc7b220bde16c27f528ebb Mon Sep 17 00:00:00 2001 From: Dipen Sompura Date: Thu, 28 May 2026 09:38:39 +0530 Subject: [PATCH] feat: add per-namespace webhook support using namespaceSelector and WEBHOOK_PREFIX Configure namespace-scoped operator installs to set WEBHOOK_PREFIX, use prefixed webhook configuration names, and scope webhook entries with namespaceSelector. Add chart template tests for cluster-wide and namespace-scoped rendering. Related to cloudnative-pg/cloudnative-pg#10792 Closes #886 Signed-off-by: Dipen Sompura Co-authored-by: Cursor --- .../cloudnative-pg/templates/deployment.yaml | 2 + .../mutatingwebhookconfiguration.yaml | 36 ++++++++ .../validatingwebhookconfiguration.yaml | 44 ++++++++++ .../test/webhook-prefix_test.sh | 85 +++++++++++++++++++ 4 files changed, 167 insertions(+) create mode 100755 charts/cloudnative-pg/test/webhook-prefix_test.sh diff --git a/charts/cloudnative-pg/templates/deployment.yaml b/charts/cloudnative-pg/templates/deployment.yaml index c17e6a7493..bae10a0922 100644 --- a/charts/cloudnative-pg/templates/deployment.yaml +++ b/charts/cloudnative-pg/templates/deployment.yaml @@ -92,6 +92,8 @@ spec: {{- if not .Values.config.clusterWide }} - name: WATCH_NAMESPACE value: "{{ include "cloudnative-pg.namespace" . }}" + - name: WEBHOOK_PREFIX + value: "{{ include "cloudnative-pg.namespace" . }}-" {{- end }} {{- if .Values.additionalEnv }} {{- tpl (.Values.additionalEnv | toYaml) . | nindent 8 }} diff --git a/charts/cloudnative-pg/templates/mutatingwebhookconfiguration.yaml b/charts/cloudnative-pg/templates/mutatingwebhookconfiguration.yaml index c58628a60e..879b7d6f3f 100644 --- a/charts/cloudnative-pg/templates/mutatingwebhookconfiguration.yaml +++ b/charts/cloudnative-pg/templates/mutatingwebhookconfiguration.yaml @@ -21,7 +21,11 @@ apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: +{{ if .Values.config.clusterWide }} name: cnpg-mutating-webhook-configuration +{{ else }} + name: {{ include "cloudnative-pg.namespace" . }}-cnpg-mutating-webhook-configuration +{{ end }} {{- with .Values.commonAnnotations }} annotations: {{- toYaml . | nindent 4 }} @@ -31,6 +35,14 @@ metadata: webhooks: - admissionReviewVersions: - v1 +{{ if not .Values.config.clusterWide }} + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: In + values: + - {{ include "cloudnative-pg.namespace" . }} +{{ end }} clientConfig: service: name: {{ .Values.service.name }} @@ -52,6 +64,14 @@ webhooks: sideEffects: None - admissionReviewVersions: - v1 +{{ if not .Values.config.clusterWide }} + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: In + values: + - {{ include "cloudnative-pg.namespace" . }} +{{ end }} clientConfig: service: name: {{ .Values.service.name }} @@ -73,6 +93,14 @@ webhooks: sideEffects: None - admissionReviewVersions: - v1 +{{ if not .Values.config.clusterWide }} + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: In + values: + - {{ include "cloudnative-pg.namespace" . }} +{{ end }} clientConfig: service: name: {{ .Values.service.name }} @@ -94,6 +122,14 @@ webhooks: sideEffects: None - admissionReviewVersions: - v1 +{{ if not .Values.config.clusterWide }} + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: In + values: + - {{ include "cloudnative-pg.namespace" . }} +{{ end }} clientConfig: service: name: {{ .Values.service.name }} diff --git a/charts/cloudnative-pg/templates/validatingwebhookconfiguration.yaml b/charts/cloudnative-pg/templates/validatingwebhookconfiguration.yaml index c171c911d8..63dbba4f6c 100644 --- a/charts/cloudnative-pg/templates/validatingwebhookconfiguration.yaml +++ b/charts/cloudnative-pg/templates/validatingwebhookconfiguration.yaml @@ -21,7 +21,11 @@ apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: +{{ if .Values.config.clusterWide }} name: cnpg-validating-webhook-configuration +{{ else }} + name: {{ include "cloudnative-pg.namespace" . }}-cnpg-validating-webhook-configuration +{{ end }} labels: {{- include "cloudnative-pg.labels" . | nindent 4 }} {{- with .Values.rbac.annotations }} @@ -31,6 +35,14 @@ metadata: webhooks: - admissionReviewVersions: - v1 +{{ if not .Values.config.clusterWide }} + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: In + values: + - {{ include "cloudnative-pg.namespace" . }} +{{ end }} clientConfig: service: name: {{ .Values.service.name }} @@ -52,6 +64,14 @@ webhooks: sideEffects: None - admissionReviewVersions: - v1 +{{ if not .Values.config.clusterWide }} + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: In + values: + - {{ include "cloudnative-pg.namespace" . }} +{{ end }} clientConfig: service: name: {{ .Values.service.name }} @@ -73,6 +93,14 @@ webhooks: sideEffects: None - admissionReviewVersions: - v1 +{{ if not .Values.config.clusterWide }} + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: In + values: + - {{ include "cloudnative-pg.namespace" . }} +{{ end }} clientConfig: service: name: {{ .Values.service.name }} @@ -94,6 +122,14 @@ webhooks: sideEffects: None - admissionReviewVersions: - v1 +{{ if not .Values.config.clusterWide }} + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: In + values: + - {{ include "cloudnative-pg.namespace" . }} +{{ end }} clientConfig: service: name: {{ .Values.service.name }} @@ -115,6 +151,14 @@ webhooks: sideEffects: None - admissionReviewVersions: - v1 +{{ if not .Values.config.clusterWide }} + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: In + values: + - {{ include "cloudnative-pg.namespace" . }} +{{ end }} clientConfig: service: name: {{ .Values.service.name }} diff --git a/charts/cloudnative-pg/test/webhook-prefix_test.sh b/charts/cloudnative-pg/test/webhook-prefix_test.sh new file mode 100755 index 0000000000..878a415a84 --- /dev/null +++ b/charts/cloudnative-pg/test/webhook-prefix_test.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +# +# Copyright © contributors to CloudNativePG, established as +# CloudNativePG a Series of LF Projects, LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +set -euo pipefail + +CHART_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +KUBE_VERSION="${KUBE_VERSION:-1.29.0}" +NAMESPACE="${NAMESPACE:-single-install}" + +helm_template() { + helm template test "${CHART_DIR}" \ + --kube-version "${KUBE_VERSION}" \ + --namespace "${NAMESPACE}" \ + --set monitoring.grafanaDashboard.create=false \ + "$@" +} + +assert_contains() { + local haystack="$1" + local needle="$2" + local message="$3" + + if ! printf '%s' "${haystack}" | grep -Fq -- "${needle}"; then + echo "assertion failed: ${message}" >&2 + echo "expected to find: ${needle}" >&2 + exit 1 + fi +} + +assert_not_contains() { + local haystack="$1" + local needle="$2" + local message="$3" + + if printf '%s' "${haystack}" | grep -Fq -- "${needle}"; then + echo "assertion failed: ${message}" >&2 + echo "expected to not find: ${needle}" >&2 + exit 1 + fi +} + +deployment_ns="$(helm_template --set config.clusterWide=false --show-only templates/deployment.yaml)" +assert_contains "${deployment_ns}" 'name: WEBHOOK_PREFIX' "namespace-scoped deployment sets WEBHOOK_PREFIX" +assert_contains "${deployment_ns}" "value: \"${NAMESPACE}-\"" "namespace-scoped deployment prefixes WEBHOOK_PREFIX with namespace" +assert_contains "${deployment_ns}" 'name: WATCH_NAMESPACE' "namespace-scoped deployment sets WATCH_NAMESPACE" + +deployment_cluster="$(helm_template --set config.clusterWide=true --show-only templates/deployment.yaml)" +assert_not_contains "${deployment_cluster}" 'name: WEBHOOK_PREFIX' "cluster-wide deployment does not set WEBHOOK_PREFIX" +assert_not_contains "${deployment_cluster}" 'name: WATCH_NAMESPACE' "cluster-wide deployment does not set WATCH_NAMESPACE" + +mutating_ns="$(helm_template --set config.clusterWide=false --show-only templates/mutatingwebhookconfiguration.yaml)" +assert_contains "${mutating_ns}" "name: ${NAMESPACE}-cnpg-mutating-webhook-configuration" "namespace-scoped mutating webhook uses prefixed name" +assert_contains "${mutating_ns}" "kubernetes.io/metadata.name" "namespace-scoped mutating webhook sets namespaceSelector" +assert_contains "${mutating_ns}" " - ${NAMESPACE}" "namespace-scoped mutating webhook targets release namespace" + +mutating_cluster="$(helm_template --set config.clusterWide=true --show-only templates/mutatingwebhookconfiguration.yaml)" +assert_contains "${mutating_cluster}" "name: cnpg-mutating-webhook-configuration" "cluster-wide mutating webhook keeps default name" +assert_not_contains "${mutating_cluster}" "namespaceSelector:" "cluster-wide mutating webhook has no namespaceSelector" + +validating_ns="$(helm_template --set config.clusterWide=false --show-only templates/validatingwebhookconfiguration.yaml)" +assert_contains "${validating_ns}" "name: ${NAMESPACE}-cnpg-validating-webhook-configuration" "namespace-scoped validating webhook uses prefixed name" +assert_contains "${validating_ns}" "kubernetes.io/metadata.name" "namespace-scoped validating webhook sets namespaceSelector" + +validating_cluster="$(helm_template --set config.clusterWide=true --show-only templates/validatingwebhookconfiguration.yaml)" +assert_contains "${validating_cluster}" "name: cnpg-validating-webhook-configuration" "cluster-wide validating webhook keeps default name" +assert_not_contains "${validating_cluster}" "namespaceSelector:" "cluster-wide validating webhook has no namespaceSelector" + +echo "webhook prefix chart tests passed"