diff --git a/internal/features/features.go b/internal/features/features.go new file mode 100644 index 000000000..4f14acc27 --- /dev/null +++ b/internal/features/features.go @@ -0,0 +1,59 @@ +/* +Copyright 2022 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 +// helm-controller supports, and their default states. +package features + +import feathelper "github.com/fluxcd/pkg/runtime/features" + +const ( + // CacheSecretsAndConfigMaps configures the caching of Secrets and ConfigMaps + // by the controller-runtime client. + // + // 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.28 + 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 f7606112f..1d302930b 100644 --- a/main.go +++ b/main.go @@ -28,6 +28,7 @@ import ( 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" @@ -40,10 +41,13 @@ import ( "github.com/fluxcd/pkg/runtime/pprof" "github.com/fluxcd/pkg/runtime/probes" sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" + corev1 "k8s.io/api/core/v1" v2 "github.com/fluxcd/helm-controller/api/v2beta1" "github.com/fluxcd/helm-controller/controllers" + "github.com/fluxcd/helm-controller/internal/features" intkube "github.com/fluxcd/helm-controller/internal/kube" + feathelper "github.com/fluxcd/pkg/runtime/features" // +kubebuilder:scaffold:imports ) @@ -74,6 +78,7 @@ func main() { httpRetry int clientOptions client.Options kubeConfigOpts client.KubeConfigOptions + featureGates feathelper.FeatureGates logOptions logger.Options aclOptions acl.Options leaderElectionOptions leaderelection.Options @@ -96,10 +101,18 @@ func main() { leaderElectionOptions.BindFlags(flag.CommandLine) rateLimiterOptions.BindFlags(flag.CommandLine) kubeConfigOpts.BindFlags(flag.CommandLine) + featureGates.BindFlags(flag.CommandLine) flag.Parse() ctrl.SetLogger(logger.NewLogger(logOptions)) + err := featureGates.WithLogger(setupLog). + SupportedFeatures(features.FeatureGates()) + if err != nil { + setupLog.Error(err, "unable to load feature gates") + os.Exit(1) + } + metricsRecorder := metrics.NewRecorder() crtlmetrics.Registry.MustRegister(metricsRecorder.Collectors()...) @@ -108,6 +121,16 @@ func main() { watchNamespace = os.Getenv("RUNTIME_NAMESPACE") } + disableCacheFor := []ctrlclient.Object{} + shouldCache, err := features.Enabled(features.CacheSecretsAndConfigMaps) + if err != nil { + setupLog.Error(err, "unable to check feature gate CacheSecretsAndConfigMaps") + os.Exit(1) + } + if !shouldCache { + disableCacheFor = append(disableCacheFor, &corev1.Secret{}, &corev1.ConfigMap{}) + } + // set the managedFields owner for resources reconciled from Helm charts kube.ManagedFieldsManager = controllerName @@ -126,6 +149,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")