diff --git a/internal/features/features.go b/internal/features/features.go new file mode 100644 index 000000000..a1c0ddaef --- /dev/null +++ b/internal/features/features.go @@ -0,0 +1,59 @@ +/* +Copyright 2023 The Flux authors + +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. +*/ + +// Package features sets the feature gates that notification-controller supports, +// and their default states. +package features + +import feathelper "github.com/fluxcd/pkg/runtime/features" + +const ( + // CacheSecretsAndConfigMaps controls whether Secrets and ConfigMaps should + // be cached. + // + // When enabled, it will cache both object types, resulting in increased + // memory usage and cluster-wide RBAC permissions (list and watch). + CacheSecretsAndConfigMaps = "CacheSecretsAndConfigMaps" +) + +var features = map[string]bool{ + // CacheSecretsAndConfigMaps + // opt-in from v0.31 + CacheSecretsAndConfigMaps: false, +} + +// FeatureGates contains a list of all supported feature gates and +// their default values. +func FeatureGates() map[string]bool { + return features +} + +// Enabled verifies whether the feature is enabled or not. +// +// This is only a wrapper around the Enabled func in +// pkg/runtime/features, so callers won't need to import both packages +// for checking whether a feature is enabled. +func Enabled(feature string) (bool, error) { + return feathelper.Enabled(feature) +} + +// Disable disables the specified feature. If the feature is not +// present, it's a no-op. +func Disable(feature string) { + if _, ok := features[feature]; ok { + features[feature] = false + } +} diff --git a/main.go b/main.go index 85627d7ab..73eba3119 100644 --- a/main.go +++ b/main.go @@ -25,15 +25,18 @@ import ( prommetrics "github.com/slok/go-http-metrics/metrics/prometheus" "github.com/slok/go-http-metrics/middleware" flag "github.com/spf13/pflag" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" ctrl "sigs.k8s.io/controller-runtime" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" crtlmetrics "sigs.k8s.io/controller-runtime/pkg/metrics" "github.com/fluxcd/pkg/runtime/acl" "github.com/fluxcd/pkg/runtime/client" helper "github.com/fluxcd/pkg/runtime/controller" + feathelper "github.com/fluxcd/pkg/runtime/features" "github.com/fluxcd/pkg/runtime/leaderelection" "github.com/fluxcd/pkg/runtime/logger" "github.com/fluxcd/pkg/runtime/pprof" @@ -41,6 +44,7 @@ import ( apiv1 "github.com/fluxcd/notification-controller/api/v1beta2" "github.com/fluxcd/notification-controller/controllers" + "github.com/fluxcd/notification-controller/internal/features" "github.com/fluxcd/notification-controller/internal/server" // +kubebuilder:scaffold:imports ) @@ -73,6 +77,7 @@ func main() { leaderElectionOptions leaderelection.Options aclOptions acl.Options rateLimiterOptions helper.RateLimiterOptions + featureGates feathelper.FeatureGates ) flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") @@ -83,13 +88,21 @@ func main() { flag.BoolVar(&watchAllNamespaces, "watch-all-namespaces", true, "Watch for custom resources in all namespaces, if set to false it will only watch the runtime namespace.") flag.DurationVar(&rateLimitInterval, "rate-limit-interval", 5*time.Minute, "Interval in which rate limit has effect.") + clientOptions.BindFlags(flag.CommandLine) logOptions.BindFlags(flag.CommandLine) leaderElectionOptions.BindFlags(flag.CommandLine) aclOptions.BindFlags(flag.CommandLine) rateLimiterOptions.BindFlags(flag.CommandLine) + featureGates.BindFlags(flag.CommandLine) + flag.Parse() + if err := featureGates.WithLogger(setupLog).SupportedFeatures(features.FeatureGates()); err != nil { + setupLog.Error(err, "unable to load feature gates") + os.Exit(1) + } + log := logger.NewLogger(logOptions) ctrl.SetLogger(log) @@ -98,6 +111,16 @@ func main() { watchNamespace = os.Getenv("RUNTIME_NAMESPACE") } + var disableCacheFor []ctrlclient.Object + shouldCache, err := features.Enabled(features.CacheSecretsAndConfigMaps) + if err != nil { + setupLog.Error(err, "unable to check feature gate "+features.CacheSecretsAndConfigMaps) + os.Exit(1) + } + if !shouldCache { + disableCacheFor = append(disableCacheFor, &corev1.Secret{}, &corev1.ConfigMap{}) + } + restConfig := client.GetConfigOrDie(clientOptions) mgr, err := ctrl.NewManager(restConfig, ctrl.Options{ Scheme: scheme, @@ -112,6 +135,7 @@ func main() { LeaderElectionID: fmt.Sprintf("%s-leader-election", controllerName), Namespace: watchNamespace, Logger: ctrl.Log, + ClientDisableCacheFor: disableCacheFor, }) if err != nil { setupLog.Error(err, "unable to start manager")