sigs.k8s.io/cluster-api@v1.7.1/main.go (about)

     1  /*
     2  Copyright 2019 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // main is the main package for the Cluster API Core Provider.
    18  package main
    19  
    20  import (
    21  	"context"
    22  	"errors"
    23  	"flag"
    24  	"fmt"
    25  	"os"
    26  	goruntime "runtime"
    27  	"time"
    28  
    29  	"github.com/spf13/pflag"
    30  	corev1 "k8s.io/api/core/v1"
    31  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    32  	"k8s.io/apimachinery/pkg/labels"
    33  	"k8s.io/apimachinery/pkg/runtime"
    34  	"k8s.io/apimachinery/pkg/selection"
    35  	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
    36  	"k8s.io/client-go/tools/leaderelection/resourcelock"
    37  	cliflag "k8s.io/component-base/cli/flag"
    38  	"k8s.io/component-base/logs"
    39  	logsv1 "k8s.io/component-base/logs/api/v1"
    40  	_ "k8s.io/component-base/logs/json/register"
    41  	"k8s.io/klog/v2"
    42  	ctrl "sigs.k8s.io/controller-runtime"
    43  	"sigs.k8s.io/controller-runtime/pkg/cache"
    44  	"sigs.k8s.io/controller-runtime/pkg/client"
    45  	"sigs.k8s.io/controller-runtime/pkg/controller"
    46  	"sigs.k8s.io/controller-runtime/pkg/webhook"
    47  
    48  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    49  	"sigs.k8s.io/cluster-api/api/v1beta1/index"
    50  	"sigs.k8s.io/cluster-api/controllers"
    51  	"sigs.k8s.io/cluster-api/controllers/remote"
    52  	addonsv1 "sigs.k8s.io/cluster-api/exp/addons/api/v1beta1"
    53  	addonscontrollers "sigs.k8s.io/cluster-api/exp/addons/controllers"
    54  	addonswebhooks "sigs.k8s.io/cluster-api/exp/addons/webhooks"
    55  	expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
    56  	expcontrollers "sigs.k8s.io/cluster-api/exp/controllers"
    57  	ipamv1 "sigs.k8s.io/cluster-api/exp/ipam/api/v1beta1"
    58  	expipamwebhooks "sigs.k8s.io/cluster-api/exp/ipam/webhooks"
    59  	runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1alpha1"
    60  	runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog"
    61  	runtimecontrollers "sigs.k8s.io/cluster-api/exp/runtime/controllers"
    62  	runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
    63  	expwebhooks "sigs.k8s.io/cluster-api/exp/webhooks"
    64  	"sigs.k8s.io/cluster-api/feature"
    65  	addonsv1alpha3 "sigs.k8s.io/cluster-api/internal/apis/core/exp/addons/v1alpha3"
    66  	addonsv1alpha4 "sigs.k8s.io/cluster-api/internal/apis/core/exp/addons/v1alpha4"
    67  	expv1alpha3 "sigs.k8s.io/cluster-api/internal/apis/core/exp/v1alpha3"
    68  	expv1alpha4 "sigs.k8s.io/cluster-api/internal/apis/core/exp/v1alpha4"
    69  	clusterv1alpha3 "sigs.k8s.io/cluster-api/internal/apis/core/v1alpha3"
    70  	clusterv1alpha4 "sigs.k8s.io/cluster-api/internal/apis/core/v1alpha4"
    71  	runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client"
    72  	runtimeregistry "sigs.k8s.io/cluster-api/internal/runtime/registry"
    73  	runtimewebhooks "sigs.k8s.io/cluster-api/internal/webhooks/runtime"
    74  	"sigs.k8s.io/cluster-api/util/flags"
    75  	"sigs.k8s.io/cluster-api/version"
    76  	"sigs.k8s.io/cluster-api/webhooks"
    77  )
    78  
    79  var (
    80  	catalog        = runtimecatalog.New()
    81  	scheme         = runtime.NewScheme()
    82  	setupLog       = ctrl.Log.WithName("setup")
    83  	controllerName = "cluster-api-controller-manager"
    84  
    85  	// flags.
    86  	enableLeaderElection        bool
    87  	leaderElectionLeaseDuration time.Duration
    88  	leaderElectionRenewDeadline time.Duration
    89  	leaderElectionRetryPeriod   time.Duration
    90  	watchFilterValue            string
    91  	watchNamespace              string
    92  	profilerAddress             string
    93  	enableContentionProfiling   bool
    94  	syncPeriod                  time.Duration
    95  	restConfigQPS               float32
    96  	restConfigBurst             int
    97  	webhookPort                 int
    98  	webhookCertDir              string
    99  	healthAddr                  string
   100  	tlsOptions                  = flags.TLSOptions{}
   101  	diagnosticsOptions          = flags.DiagnosticsOptions{}
   102  	logOptions                  = logs.NewOptions()
   103  	// core Cluster API specific flags.
   104  	clusterTopologyConcurrency     int
   105  	clusterCacheTrackerConcurrency int
   106  	clusterClassConcurrency        int
   107  	clusterConcurrency             int
   108  	extensionConfigConcurrency     int
   109  	machineConcurrency             int
   110  	machineSetConcurrency          int
   111  	machineDeploymentConcurrency   int
   112  	machinePoolConcurrency         int
   113  	clusterResourceSetConcurrency  int
   114  	machineHealthCheckConcurrency  int
   115  	nodeDrainClientTimeout         time.Duration
   116  )
   117  
   118  func init() {
   119  	_ = clientgoscheme.AddToScheme(scheme)
   120  	_ = apiextensionsv1.AddToScheme(scheme)
   121  
   122  	_ = clusterv1alpha3.AddToScheme(scheme)
   123  	_ = clusterv1alpha4.AddToScheme(scheme)
   124  	_ = clusterv1.AddToScheme(scheme)
   125  
   126  	_ = expv1alpha3.AddToScheme(scheme)
   127  	_ = expv1alpha4.AddToScheme(scheme)
   128  	_ = expv1.AddToScheme(scheme)
   129  
   130  	_ = addonsv1alpha3.AddToScheme(scheme)
   131  	_ = addonsv1alpha4.AddToScheme(scheme)
   132  	_ = addonsv1.AddToScheme(scheme)
   133  
   134  	_ = runtimev1.AddToScheme(scheme)
   135  
   136  	_ = ipamv1.AddToScheme(scheme)
   137  
   138  	// Register the RuntimeHook types into the catalog.
   139  	_ = runtimehooksv1.AddToCatalog(catalog)
   140  }
   141  
   142  // InitFlags initializes the flags.
   143  func InitFlags(fs *pflag.FlagSet) {
   144  	logsv1.AddFlags(logOptions, fs)
   145  
   146  	fs.BoolVar(&enableLeaderElection, "leader-elect", false,
   147  		"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.")
   148  
   149  	fs.DurationVar(&leaderElectionLeaseDuration, "leader-elect-lease-duration", 15*time.Second,
   150  		"Interval at which non-leader candidates will wait to force acquire leadership (duration string)")
   151  
   152  	fs.DurationVar(&leaderElectionRenewDeadline, "leader-elect-renew-deadline", 10*time.Second,
   153  		"Duration that the leading controller manager will retry refreshing leadership before giving up (duration string)")
   154  
   155  	fs.DurationVar(&leaderElectionRetryPeriod, "leader-elect-retry-period", 2*time.Second,
   156  		"Duration the LeaderElector clients should wait between tries of actions (duration string)")
   157  
   158  	fs.StringVar(&watchNamespace, "namespace", "",
   159  		"Namespace that the controller watches to reconcile cluster-api objects. If unspecified, the controller watches for cluster-api objects across all namespaces.")
   160  
   161  	fs.StringVar(&watchFilterValue, "watch-filter", "",
   162  		fmt.Sprintf("Label value that the controller watches to reconcile cluster-api objects. Label key is always %s. If unspecified, the controller watches for all cluster-api objects.", clusterv1.WatchLabel))
   163  
   164  	fs.StringVar(&profilerAddress, "profiler-address", "",
   165  		"Bind address to expose the pprof profiler (e.g. localhost:6060)")
   166  
   167  	fs.BoolVar(&enableContentionProfiling, "contention-profiling", false,
   168  		"Enable block profiling")
   169  
   170  	fs.IntVar(&clusterTopologyConcurrency, "clustertopology-concurrency", 10,
   171  		"Number of clusters to process simultaneously")
   172  
   173  	fs.IntVar(&clusterClassConcurrency, "clusterclass-concurrency", 10,
   174  		"Number of ClusterClasses to process simultaneously")
   175  
   176  	fs.IntVar(&clusterConcurrency, "cluster-concurrency", 10,
   177  		"Number of clusters to process simultaneously")
   178  
   179  	fs.IntVar(&clusterCacheTrackerConcurrency, "clustercachetracker-concurrency", 10,
   180  		"Number of clusters to process simultaneously")
   181  
   182  	fs.IntVar(&extensionConfigConcurrency, "extensionconfig-concurrency", 10,
   183  		"Number of extension configs to process simultaneously")
   184  
   185  	fs.IntVar(&machineConcurrency, "machine-concurrency", 10,
   186  		"Number of machines to process simultaneously")
   187  
   188  	fs.IntVar(&machineSetConcurrency, "machineset-concurrency", 10,
   189  		"Number of machine sets to process simultaneously")
   190  
   191  	fs.IntVar(&machineDeploymentConcurrency, "machinedeployment-concurrency", 10,
   192  		"Number of machine deployments to process simultaneously")
   193  
   194  	fs.IntVar(&machinePoolConcurrency, "machinepool-concurrency", 10,
   195  		"Number of machine pools to process simultaneously")
   196  
   197  	fs.IntVar(&clusterResourceSetConcurrency, "clusterresourceset-concurrency", 10,
   198  		"Number of cluster resource sets to process simultaneously")
   199  
   200  	fs.IntVar(&machineHealthCheckConcurrency, "machinehealthcheck-concurrency", 10,
   201  		"Number of machine health checks to process simultaneously")
   202  
   203  	fs.DurationVar(&syncPeriod, "sync-period", 10*time.Minute,
   204  		"The minimum interval at which watched resources are reconciled (e.g. 15m)")
   205  
   206  	fs.Float32Var(&restConfigQPS, "kube-api-qps", 20,
   207  		"Maximum queries per second from the controller client to the Kubernetes API server. Defaults to 20")
   208  
   209  	fs.IntVar(&restConfigBurst, "kube-api-burst", 30,
   210  		"Maximum number of queries that should be allowed in one burst from the controller client to the Kubernetes API server. Default 30")
   211  
   212  	fs.DurationVar(&nodeDrainClientTimeout, "node-drain-client-timeout-duration", time.Second*10,
   213  		"The timeout of the client used for draining nodes. Defaults to 10s")
   214  
   215  	fs.IntVar(&webhookPort, "webhook-port", 9443,
   216  		"Webhook Server port")
   217  
   218  	fs.StringVar(&webhookCertDir, "webhook-cert-dir", "/tmp/k8s-webhook-server/serving-certs/",
   219  		"Webhook cert dir, only used when webhook-port is specified.")
   220  
   221  	fs.StringVar(&healthAddr, "health-addr", ":9440",
   222  		"The address the health endpoint binds to.")
   223  
   224  	flags.AddDiagnosticsOptions(fs, &diagnosticsOptions)
   225  	flags.AddTLSOptions(fs, &tlsOptions)
   226  
   227  	feature.MutableGates.AddFlag(fs)
   228  }
   229  
   230  // Add RBAC for the authorized diagnostics endpoint.
   231  // +kubebuilder:rbac:groups=authentication.k8s.io,resources=tokenreviews,verbs=create
   232  // +kubebuilder:rbac:groups=authorization.k8s.io,resources=subjectaccessreviews,verbs=create
   233  
   234  func main() {
   235  	InitFlags(pflag.CommandLine)
   236  	pflag.CommandLine.SetNormalizeFunc(cliflag.WordSepNormalizeFunc)
   237  	pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
   238  	// Set log level 2 as default.
   239  	if err := pflag.CommandLine.Set("v", "2"); err != nil {
   240  		setupLog.Error(err, "failed to set default log level")
   241  		os.Exit(1)
   242  	}
   243  	pflag.Parse()
   244  
   245  	if err := logsv1.ValidateAndApply(logOptions, nil); err != nil {
   246  		setupLog.Error(err, "unable to start manager")
   247  		os.Exit(1)
   248  	}
   249  
   250  	// klog.Background will automatically use the right logger.
   251  	ctrl.SetLogger(klog.Background())
   252  
   253  	restConfig := ctrl.GetConfigOrDie()
   254  	restConfig.QPS = restConfigQPS
   255  	restConfig.Burst = restConfigBurst
   256  	restConfig.UserAgent = remote.DefaultClusterAPIUserAgent(controllerName)
   257  
   258  	if nodeDrainClientTimeout <= 0 {
   259  		setupLog.Error(errors.New("node drain client timeout must be greater than zero"), "unable to start manager")
   260  		os.Exit(1)
   261  	}
   262  
   263  	minVer := version.MinimumKubernetesVersion
   264  	if feature.Gates.Enabled(feature.ClusterTopology) {
   265  		minVer = version.MinimumKubernetesVersionClusterTopology
   266  	}
   267  
   268  	if err := version.CheckKubernetesVersion(restConfig, minVer); err != nil {
   269  		setupLog.Error(err, "unable to start manager")
   270  		os.Exit(1)
   271  	}
   272  
   273  	tlsOptionOverrides, err := flags.GetTLSOptionOverrideFuncs(tlsOptions)
   274  	if err != nil {
   275  		setupLog.Error(err, "unable to add TLS settings to the webhook server")
   276  		os.Exit(1)
   277  	}
   278  
   279  	diagnosticsOpts := flags.GetDiagnosticsOptions(diagnosticsOptions)
   280  
   281  	var watchNamespaces map[string]cache.Config
   282  	if watchNamespace != "" {
   283  		watchNamespaces = map[string]cache.Config{
   284  			watchNamespace: {},
   285  		}
   286  	}
   287  
   288  	if enableContentionProfiling {
   289  		goruntime.SetBlockProfileRate(1)
   290  	}
   291  
   292  	req, _ := labels.NewRequirement(clusterv1.ClusterNameLabel, selection.Exists, nil)
   293  	clusterSecretCacheSelector := labels.NewSelector().Add(*req)
   294  
   295  	ctrlOptions := ctrl.Options{
   296  		Scheme:                     scheme,
   297  		LeaderElection:             enableLeaderElection,
   298  		LeaderElectionID:           "controller-leader-election-capi",
   299  		LeaseDuration:              &leaderElectionLeaseDuration,
   300  		RenewDeadline:              &leaderElectionRenewDeadline,
   301  		RetryPeriod:                &leaderElectionRetryPeriod,
   302  		LeaderElectionResourceLock: resourcelock.LeasesResourceLock,
   303  		HealthProbeBindAddress:     healthAddr,
   304  		PprofBindAddress:           profilerAddress,
   305  		Metrics:                    diagnosticsOpts,
   306  		Cache: cache.Options{
   307  			DefaultNamespaces: watchNamespaces,
   308  			SyncPeriod:        &syncPeriod,
   309  			ByObject: map[client.Object]cache.ByObject{
   310  				// Note: Only Secrets with the cluster name label are cached.
   311  				// The default client of the manager won't use the cache for secrets at all (see Client.Cache.DisableFor).
   312  				// The cached secrets will only be used by the secretCachingClient we create below.
   313  				&corev1.Secret{}: {
   314  					Label: clusterSecretCacheSelector,
   315  				},
   316  			},
   317  		},
   318  		Client: client.Options{
   319  			Cache: &client.CacheOptions{
   320  				DisableFor: []client.Object{
   321  					&corev1.ConfigMap{},
   322  					&corev1.Secret{},
   323  				},
   324  			},
   325  		},
   326  		WebhookServer: webhook.NewServer(
   327  			webhook.Options{
   328  				Port:    webhookPort,
   329  				CertDir: webhookCertDir,
   330  				TLSOpts: tlsOptionOverrides,
   331  			},
   332  		),
   333  	}
   334  
   335  	mgr, err := ctrl.NewManager(restConfig, ctrlOptions)
   336  	if err != nil {
   337  		setupLog.Error(err, "unable to start manager")
   338  		os.Exit(1)
   339  	}
   340  
   341  	// Setup the context that's going to be used in controllers and for the manager.
   342  	ctx := ctrl.SetupSignalHandler()
   343  
   344  	setupChecks(mgr)
   345  	setupIndexes(ctx, mgr)
   346  	tracker := setupReconcilers(ctx, mgr)
   347  	setupWebhooks(mgr, tracker)
   348  
   349  	setupLog.Info("starting manager", "version", version.Get().String())
   350  	if err := mgr.Start(ctx); err != nil {
   351  		setupLog.Error(err, "problem running manager")
   352  		os.Exit(1)
   353  	}
   354  }
   355  
   356  func setupChecks(mgr ctrl.Manager) {
   357  	if err := mgr.AddReadyzCheck("webhook", mgr.GetWebhookServer().StartedChecker()); err != nil {
   358  		setupLog.Error(err, "unable to create ready check")
   359  		os.Exit(1)
   360  	}
   361  
   362  	if err := mgr.AddHealthzCheck("webhook", mgr.GetWebhookServer().StartedChecker()); err != nil {
   363  		setupLog.Error(err, "unable to create health check")
   364  		os.Exit(1)
   365  	}
   366  }
   367  
   368  func setupIndexes(ctx context.Context, mgr ctrl.Manager) {
   369  	if err := index.AddDefaultIndexes(ctx, mgr); err != nil {
   370  		setupLog.Error(err, "unable to setup indexes")
   371  		os.Exit(1)
   372  	}
   373  }
   374  
   375  func setupReconcilers(ctx context.Context, mgr ctrl.Manager) webhooks.ClusterCacheTrackerReader {
   376  	secretCachingClient, err := client.New(mgr.GetConfig(), client.Options{
   377  		HTTPClient: mgr.GetHTTPClient(),
   378  		Cache: &client.CacheOptions{
   379  			Reader: mgr.GetCache(),
   380  		},
   381  	})
   382  	if err != nil {
   383  		setupLog.Error(err, "unable to create secret caching client")
   384  		os.Exit(1)
   385  	}
   386  
   387  	// Set up a ClusterCacheTracker and ClusterCacheReconciler to provide to controllers
   388  	// requiring a connection to a remote cluster
   389  	tracker, err := remote.NewClusterCacheTracker(
   390  		mgr,
   391  		remote.ClusterCacheTrackerOptions{
   392  			SecretCachingClient: secretCachingClient,
   393  			ControllerName:      controllerName,
   394  			Log:                 &ctrl.Log,
   395  			Indexes:             []remote.Index{remote.NodeProviderIDIndex},
   396  		},
   397  	)
   398  	if err != nil {
   399  		setupLog.Error(err, "unable to create cluster cache tracker")
   400  		os.Exit(1)
   401  	}
   402  
   403  	if err := (&remote.ClusterCacheReconciler{
   404  		Client:           mgr.GetClient(),
   405  		Tracker:          tracker,
   406  		WatchFilterValue: watchFilterValue,
   407  	}).SetupWithManager(ctx, mgr, concurrency(clusterCacheTrackerConcurrency)); err != nil {
   408  		setupLog.Error(err, "unable to create controller", "controller", "ClusterCacheReconciler")
   409  		os.Exit(1)
   410  	}
   411  
   412  	var runtimeClient runtimeclient.Client
   413  	if feature.Gates.Enabled(feature.RuntimeSDK) {
   414  		// This is the creation of the runtimeClient for the controllers, embedding a shared catalog and registry instance.
   415  		runtimeClient = runtimeclient.New(runtimeclient.Options{
   416  			Catalog:  catalog,
   417  			Registry: runtimeregistry.New(),
   418  			Client:   mgr.GetClient(),
   419  		})
   420  	}
   421  
   422  	unstructuredCachingClient, err := client.New(mgr.GetConfig(), client.Options{
   423  		HTTPClient: mgr.GetHTTPClient(),
   424  		Cache: &client.CacheOptions{
   425  			Reader:       mgr.GetCache(),
   426  			Unstructured: true,
   427  		},
   428  	})
   429  	if err != nil {
   430  		setupLog.Error(err, "unable to create unstructured caching client")
   431  		os.Exit(1)
   432  	}
   433  
   434  	if feature.Gates.Enabled(feature.ClusterTopology) {
   435  		if err := (&controllers.ClusterClassReconciler{
   436  			Client:                    mgr.GetClient(),
   437  			RuntimeClient:             runtimeClient,
   438  			UnstructuredCachingClient: unstructuredCachingClient,
   439  			WatchFilterValue:          watchFilterValue,
   440  		}).SetupWithManager(ctx, mgr, concurrency(clusterClassConcurrency)); err != nil {
   441  			setupLog.Error(err, "unable to create controller", "controller", "ClusterClass")
   442  			os.Exit(1)
   443  		}
   444  
   445  		if err := (&controllers.ClusterTopologyReconciler{
   446  			Client:                    mgr.GetClient(),
   447  			APIReader:                 mgr.GetAPIReader(),
   448  			RuntimeClient:             runtimeClient,
   449  			Tracker:                   tracker,
   450  			UnstructuredCachingClient: unstructuredCachingClient,
   451  			WatchFilterValue:          watchFilterValue,
   452  		}).SetupWithManager(ctx, mgr, concurrency(clusterTopologyConcurrency)); err != nil {
   453  			setupLog.Error(err, "unable to create controller", "controller", "ClusterTopology")
   454  			os.Exit(1)
   455  		}
   456  
   457  		if err := (&controllers.MachineDeploymentTopologyReconciler{
   458  			Client:           mgr.GetClient(),
   459  			APIReader:        mgr.GetAPIReader(),
   460  			WatchFilterValue: watchFilterValue,
   461  		}).SetupWithManager(ctx, mgr, controller.Options{}); err != nil {
   462  			setupLog.Error(err, "unable to create controller", "controller", "MachineDeploymentTopology")
   463  			os.Exit(1)
   464  		}
   465  
   466  		if err := (&controllers.MachineSetTopologyReconciler{
   467  			Client:           mgr.GetClient(),
   468  			APIReader:        mgr.GetAPIReader(),
   469  			WatchFilterValue: watchFilterValue,
   470  		}).SetupWithManager(ctx, mgr, controller.Options{}); err != nil {
   471  			setupLog.Error(err, "unable to create controller", "controller", "MachineSetTopology")
   472  			os.Exit(1)
   473  		}
   474  	}
   475  
   476  	if feature.Gates.Enabled(feature.RuntimeSDK) {
   477  		if err = (&runtimecontrollers.ExtensionConfigReconciler{
   478  			Client:           mgr.GetClient(),
   479  			APIReader:        mgr.GetAPIReader(),
   480  			RuntimeClient:    runtimeClient,
   481  			WatchFilterValue: watchFilterValue,
   482  		}).SetupWithManager(ctx, mgr, concurrency(extensionConfigConcurrency)); err != nil {
   483  			setupLog.Error(err, "unable to create controller", "controller", "ExtensionConfig")
   484  			os.Exit(1)
   485  		}
   486  	}
   487  
   488  	if err := (&controllers.ClusterReconciler{
   489  		Client:                    mgr.GetClient(),
   490  		UnstructuredCachingClient: unstructuredCachingClient,
   491  		APIReader:                 mgr.GetAPIReader(),
   492  		WatchFilterValue:          watchFilterValue,
   493  	}).SetupWithManager(ctx, mgr, concurrency(clusterConcurrency)); err != nil {
   494  		setupLog.Error(err, "unable to create controller", "controller", "Cluster")
   495  		os.Exit(1)
   496  	}
   497  	if err := (&controllers.MachineReconciler{
   498  		Client:                    mgr.GetClient(),
   499  		UnstructuredCachingClient: unstructuredCachingClient,
   500  		APIReader:                 mgr.GetAPIReader(),
   501  		Tracker:                   tracker,
   502  		WatchFilterValue:          watchFilterValue,
   503  		NodeDrainClientTimeout:    nodeDrainClientTimeout,
   504  	}).SetupWithManager(ctx, mgr, concurrency(machineConcurrency)); err != nil {
   505  		setupLog.Error(err, "unable to create controller", "controller", "Machine")
   506  		os.Exit(1)
   507  	}
   508  	if err := (&controllers.MachineSetReconciler{
   509  		Client:                    mgr.GetClient(),
   510  		UnstructuredCachingClient: unstructuredCachingClient,
   511  		APIReader:                 mgr.GetAPIReader(),
   512  		Tracker:                   tracker,
   513  		WatchFilterValue:          watchFilterValue,
   514  	}).SetupWithManager(ctx, mgr, concurrency(machineSetConcurrency)); err != nil {
   515  		setupLog.Error(err, "unable to create controller", "controller", "MachineSet")
   516  		os.Exit(1)
   517  	}
   518  	if err := (&controllers.MachineDeploymentReconciler{
   519  		Client:                    mgr.GetClient(),
   520  		UnstructuredCachingClient: unstructuredCachingClient,
   521  		APIReader:                 mgr.GetAPIReader(),
   522  		WatchFilterValue:          watchFilterValue,
   523  	}).SetupWithManager(ctx, mgr, concurrency(machineDeploymentConcurrency)); err != nil {
   524  		setupLog.Error(err, "unable to create controller", "controller", "MachineDeployment")
   525  		os.Exit(1)
   526  	}
   527  
   528  	if feature.Gates.Enabled(feature.MachinePool) {
   529  		if err := (&expcontrollers.MachinePoolReconciler{
   530  			Client:           mgr.GetClient(),
   531  			APIReader:        mgr.GetAPIReader(),
   532  			Tracker:          tracker,
   533  			WatchFilterValue: watchFilterValue,
   534  		}).SetupWithManager(ctx, mgr, concurrency(machinePoolConcurrency)); err != nil {
   535  			setupLog.Error(err, "unable to create controller", "controller", "MachinePool")
   536  			os.Exit(1)
   537  		}
   538  	}
   539  
   540  	if feature.Gates.Enabled(feature.ClusterResourceSet) {
   541  		if err := (&addonscontrollers.ClusterResourceSetReconciler{
   542  			Client:           mgr.GetClient(),
   543  			Tracker:          tracker,
   544  			WatchFilterValue: watchFilterValue,
   545  		}).SetupWithManager(ctx, mgr, concurrency(clusterResourceSetConcurrency)); err != nil {
   546  			setupLog.Error(err, "unable to create controller", "controller", "ClusterResourceSet")
   547  			os.Exit(1)
   548  		}
   549  		if err := (&addonscontrollers.ClusterResourceSetBindingReconciler{
   550  			Client:           mgr.GetClient(),
   551  			WatchFilterValue: watchFilterValue,
   552  		}).SetupWithManager(ctx, mgr, concurrency(clusterResourceSetConcurrency)); err != nil {
   553  			setupLog.Error(err, "unable to create controller", "controller", "ClusterResourceSetBinding")
   554  			os.Exit(1)
   555  		}
   556  	}
   557  
   558  	if err := (&controllers.MachineHealthCheckReconciler{
   559  		Client:           mgr.GetClient(),
   560  		Tracker:          tracker,
   561  		WatchFilterValue: watchFilterValue,
   562  	}).SetupWithManager(ctx, mgr, concurrency(machineHealthCheckConcurrency)); err != nil {
   563  		setupLog.Error(err, "unable to create controller", "controller", "MachineHealthCheck")
   564  		os.Exit(1)
   565  	}
   566  
   567  	return tracker
   568  }
   569  
   570  func setupWebhooks(mgr ctrl.Manager, tracker webhooks.ClusterCacheTrackerReader) {
   571  	// NOTE: ClusterClass and managed topologies are behind ClusterTopology feature gate flag; the webhook
   572  	// is going to prevent creating or updating new objects in case the feature flag is disabled.
   573  	if err := (&webhooks.ClusterClass{Client: mgr.GetClient()}).SetupWebhookWithManager(mgr); err != nil {
   574  		setupLog.Error(err, "unable to create webhook", "webhook", "ClusterClass")
   575  		os.Exit(1)
   576  	}
   577  
   578  	// NOTE: ClusterClass and managed topologies are behind ClusterTopology feature gate flag; the webhook
   579  	// is going to prevent usage of Cluster.Topology in case the feature flag is disabled.
   580  	if err := (&webhooks.Cluster{Client: mgr.GetClient(), ClusterCacheTrackerReader: tracker}).SetupWebhookWithManager(mgr); err != nil {
   581  		setupLog.Error(err, "unable to create webhook", "webhook", "Cluster")
   582  		os.Exit(1)
   583  	}
   584  
   585  	if err := (&webhooks.Machine{}).SetupWebhookWithManager(mgr); err != nil {
   586  		setupLog.Error(err, "unable to create webhook", "webhook", "Machine")
   587  		os.Exit(1)
   588  	}
   589  
   590  	if err := (&webhooks.MachineSet{}).SetupWebhookWithManager(mgr); err != nil {
   591  		setupLog.Error(err, "unable to create webhook", "webhook", "MachineSet")
   592  		os.Exit(1)
   593  	}
   594  
   595  	if err := (&webhooks.MachineDeployment{}).SetupWebhookWithManager(mgr); err != nil {
   596  		setupLog.Error(err, "unable to create webhook", "webhook", "MachineDeployment")
   597  		os.Exit(1)
   598  	}
   599  
   600  	// NOTE: MachinePool is behind MachinePool feature gate flag; the webhook
   601  	// is going to prevent creating or updating new objects in case the feature flag is disabled
   602  	if err := (&expwebhooks.MachinePool{}).SetupWebhookWithManager(mgr); err != nil {
   603  		setupLog.Error(err, "unable to create webhook", "webhook", "MachinePool")
   604  		os.Exit(1)
   605  	}
   606  
   607  	// NOTE: ClusterResourceSet is behind ClusterResourceSet feature gate flag; the webhook
   608  	// is going to prevent creating or updating new objects in case the feature flag is disabled
   609  	if err := (&addonswebhooks.ClusterResourceSet{}).SetupWebhookWithManager(mgr); err != nil {
   610  		setupLog.Error(err, "unable to create webhook", "webhook", "ClusterResourceSet")
   611  		os.Exit(1)
   612  	}
   613  	// NOTE: ClusterResourceSetBinding is behind ClusterResourceSet feature gate flag; the webhook
   614  	// is going to prevent creating or updating new objects in case the feature flag is disabled
   615  	if err := (&addonswebhooks.ClusterResourceSetBinding{}).SetupWebhookWithManager(mgr); err != nil {
   616  		setupLog.Error(err, "unable to create webhook", "webhook", "ClusterResourceSetBinding")
   617  		os.Exit(1)
   618  	}
   619  
   620  	if err := (&webhooks.MachineHealthCheck{}).SetupWebhookWithManager(mgr); err != nil {
   621  		setupLog.Error(err, "unable to create webhook", "webhook", "MachineHealthCheck")
   622  		os.Exit(1)
   623  	}
   624  
   625  	// NOTE: ExtensionConfig is behind the RuntimeSDK feature gate flag. The webhook will prevent creating or updating
   626  	// new objects if the feature flag is disabled.
   627  	if err := (&runtimewebhooks.ExtensionConfig{}).SetupWebhookWithManager(mgr); err != nil {
   628  		setupLog.Error(err, "unable to create webhook", "webhook", "ExtensionConfig")
   629  		os.Exit(1)
   630  	}
   631  
   632  	if err := (&expipamwebhooks.IPAddress{
   633  		// We are using GetAPIReader here to avoid caching all IPAddressClaims
   634  		Client: mgr.GetAPIReader(),
   635  	}).SetupWebhookWithManager(mgr); err != nil {
   636  		setupLog.Error(err, "unable to create webhook", "webhook", "IPAddress")
   637  		os.Exit(1)
   638  	}
   639  	if err := (&expipamwebhooks.IPAddressClaim{}).SetupWebhookWithManager(mgr); err != nil {
   640  		setupLog.Error(err, "unable to create webhook", "webhook", "IPAddressClaim")
   641  		os.Exit(1)
   642  	}
   643  }
   644  
   645  func concurrency(c int) controller.Options {
   646  	return controller.Options{MaxConcurrentReconciles: c}
   647  }