From 2e485948ed21e20787eab315a1273a439bf83f13 Mon Sep 17 00:00:00 2001 From: Nick Zeolla Date: Tue, 20 Oct 2026 10:15:15 +0100 Subject: [PATCH] add-WATCH_NAMESPACE-env-var-with-RBAC-scoping --- internal/cmd/operator/main.go | 1 + internal/cnpgi/operator/manager.go | 28 +++++++++++++++++ internal/cnpgi/operator/manager_test.go | 40 +++++++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 internal/cnpgi/operator/manager_test.go diff --git a/internal/cmd/operator/main.go b/internal/cmd/operator/main.go index 33570543c..dc5635a05 100644 --- a/internal/cmd/operator/main.go +++ b/internal/cmd/operator/main.go @@ -102,6 +102,7 @@ func NewCmd() *cobra.Command { _ = viper.BindPFlag("server-address", cmd.Flags().Lookup("server-address")) _ = viper.BindEnv("sidecar-image", "SIDECAR_IMAGE") + _ = viper.BindEnv("watch-namespace", "WATCH_NAMESPACE") return cmd } diff --git a/internal/cnpgi/operator/manager.go b/internal/cnpgi/operator/manager.go index d6daf0acf..b09a0f7b7 100644 --- a/internal/cnpgi/operator/manager.go +++ b/internal/cnpgi/operator/manager.go @@ -22,6 +22,7 @@ package operator import ( "context" "crypto/tls" + "strings" // +kubebuilder:scaffold:imports "github.com/cloudnative-pg/machinery/pkg/log" @@ -30,6 +31,7 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/metrics/filters" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" @@ -51,6 +53,20 @@ func init() { // +kubebuilder:scaffold:scheme } +// parseWatchNamespaces parses the comma-separated list of namespaces +// contained in the WATCH_NAMESPACE environment variable, ignoring blank +// entries. An empty result means that all the namespaces are watched. +func parseWatchNamespaces(value string) []string { + var namespaces []string + for _, namespace := range strings.Split(value, ",") { + if namespace = strings.TrimSpace(namespace); namespace != "" { + namespaces = append(namespaces, namespace) + } + } + + return namespaces +} + // Start starts the manager func Start(ctx context.Context) error { setupLog := log.FromContext(ctx) @@ -100,8 +116,20 @@ func Start(ctx context.Context) error { metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization } + // When WATCH_NAMESPACE is set, the informer cache is restricted to the + // listed namespaces, so the plugin only needs namespaced RBAC there. + var cacheOptions cache.Options + if namespaces := parseWatchNamespaces(viper.GetString("watch-namespace")); len(namespaces) > 0 { + setupLog.Info("restricting to watched namespaces", "namespaces", namespaces) + cacheOptions.DefaultNamespaces = make(map[string]cache.Config, len(namespaces)) + for _, namespace := range namespaces { + cacheOptions.DefaultNamespaces[namespace] = cache.Config{} + } + } + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme, + Cache: cacheOptions, Metrics: metricsServerOptions, WebhookServer: webhookServer, HealthProbeBindAddress: viper.GetString("health-probe-bind-address"), diff --git a/internal/cnpgi/operator/manager_test.go b/internal/cnpgi/operator/manager_test.go new file mode 100644 index 000000000..973fb1610 --- /dev/null +++ b/internal/cnpgi/operator/manager_test.go @@ -0,0 +1,40 @@ +/* +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 +*/ + +package operator + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("parseWatchNamespaces", func() { + It("returns no namespaces when the value is empty", func() { + Expect(parseWatchNamespaces("")).To(BeEmpty()) + Expect(parseWatchNamespaces(" , ,")).To(BeEmpty()) + }) + + It("parses a single namespace", func() { + Expect(parseWatchNamespaces("cnpg")).To(ConsistOf("cnpg")) + }) + + It("parses a comma-separated list, trimming blanks", func() { + Expect(parseWatchNamespaces(" one, two ,,three")).To(Equal([]string{"one", "two", "three"})) + }) +})