sigs.k8s.io/cluster-api@v1.6.3/controlplane/kubeadm/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 Kubeadm Control Plane provider. 18 package main 19 20 import ( 21 "context" 22 "flag" 23 "fmt" 24 "os" 25 goruntime "runtime" 26 "time" 27 28 "github.com/spf13/pflag" 29 appsv1 "k8s.io/api/apps/v1" 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 bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" 50 "sigs.k8s.io/cluster-api/controllers/remote" 51 controlplanev1alpha4 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1alpha4" 52 controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1" 53 kubeadmcontrolplanecontrollers "sigs.k8s.io/cluster-api/controlplane/kubeadm/controllers" 54 "sigs.k8s.io/cluster-api/controlplane/kubeadm/internal/etcd" 55 kcpwebhooks "sigs.k8s.io/cluster-api/controlplane/kubeadm/webhooks" 56 "sigs.k8s.io/cluster-api/feature" 57 controlplanev1alpha3 "sigs.k8s.io/cluster-api/internal/apis/controlplane/kubeadm/v1alpha3" 58 "sigs.k8s.io/cluster-api/util/flags" 59 "sigs.k8s.io/cluster-api/version" 60 ) 61 62 var ( 63 scheme = runtime.NewScheme() 64 setupLog = ctrl.Log.WithName("setup") 65 controllerName = "cluster-api-kubeadm-control-plane-manager" 66 67 // flags. 68 enableLeaderElection bool 69 leaderElectionLeaseDuration time.Duration 70 leaderElectionRenewDeadline time.Duration 71 leaderElectionRetryPeriod time.Duration 72 watchFilterValue string 73 watchNamespace string 74 profilerAddress string 75 enableContentionProfiling bool 76 syncPeriod time.Duration 77 restConfigQPS float32 78 restConfigBurst int 79 webhookPort int 80 webhookCertDir string 81 healthAddr string 82 tlsOptions = flags.TLSOptions{} 83 diagnosticsOptions = flags.DiagnosticsOptions{} 84 logOptions = logs.NewOptions() 85 // KCP specific flags. 86 kubeadmControlPlaneConcurrency int 87 clusterCacheTrackerConcurrency int 88 etcdDialTimeout time.Duration 89 etcdCallTimeout time.Duration 90 ) 91 92 func init() { 93 _ = clientgoscheme.AddToScheme(scheme) 94 _ = clusterv1.AddToScheme(scheme) 95 _ = controlplanev1alpha3.AddToScheme(scheme) 96 _ = controlplanev1alpha4.AddToScheme(scheme) 97 _ = controlplanev1.AddToScheme(scheme) 98 _ = bootstrapv1.AddToScheme(scheme) 99 _ = apiextensionsv1.AddToScheme(scheme) 100 } 101 102 // InitFlags initializes the flags. 103 func InitFlags(fs *pflag.FlagSet) { 104 logsv1.AddFlags(logOptions, fs) 105 106 fs.BoolVar(&enableLeaderElection, "leader-elect", false, 107 "Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.") 108 109 fs.DurationVar(&leaderElectionLeaseDuration, "leader-elect-lease-duration", 1*time.Minute, 110 "Interval at which non-leader candidates will wait to force acquire leadership (duration string)") 111 112 fs.DurationVar(&leaderElectionRenewDeadline, "leader-elect-renew-deadline", 40*time.Second, 113 "Duration that the leading controller manager will retry refreshing leadership before giving up (duration string)") 114 115 fs.DurationVar(&leaderElectionRetryPeriod, "leader-elect-retry-period", 5*time.Second, 116 "Duration the LeaderElector clients should wait between tries of actions (duration string)") 117 118 fs.StringVar(&watchNamespace, "namespace", "", 119 "Namespace that the controller watches to reconcile cluster-api objects. If unspecified, the controller watches for cluster-api objects across all namespaces.") 120 121 fs.StringVar(&watchFilterValue, "watch-filter", "", 122 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)) 123 124 fs.StringVar(&profilerAddress, "profiler-address", "", 125 "Bind address to expose the pprof profiler (e.g. localhost:6060)") 126 127 fs.BoolVar(&enableContentionProfiling, "contention-profiling", false, 128 "Enable block profiling") 129 130 fs.IntVar(&kubeadmControlPlaneConcurrency, "kubeadmcontrolplane-concurrency", 10, 131 "Number of kubeadm control planes to process simultaneously") 132 133 fs.IntVar(&clusterCacheTrackerConcurrency, "clustercachetracker-concurrency", 10, 134 "Number of clusters to process simultaneously") 135 136 fs.DurationVar(&syncPeriod, "sync-period", 10*time.Minute, 137 "The minimum interval at which watched resources are reconciled (e.g. 15m)") 138 139 fs.Float32Var(&restConfigQPS, "kube-api-qps", 20, 140 "Maximum queries per second from the controller client to the Kubernetes API server. Defaults to 20") 141 142 fs.IntVar(&restConfigBurst, "kube-api-burst", 30, 143 "Maximum number of queries that should be allowed in one burst from the controller client to the Kubernetes API server. Default 30") 144 145 fs.IntVar(&webhookPort, "webhook-port", 9443, 146 "Webhook Server port") 147 148 fs.StringVar(&webhookCertDir, "webhook-cert-dir", "/tmp/k8s-webhook-server/serving-certs/", 149 "Webhook cert dir, only used when webhook-port is specified.") 150 151 fs.StringVar(&healthAddr, "health-addr", ":9440", 152 "The address the health endpoint binds to.") 153 154 fs.DurationVar(&etcdDialTimeout, "etcd-dial-timeout-duration", 10*time.Second, 155 "Duration that the etcd client waits at most to establish a connection with etcd") 156 157 fs.DurationVar(&etcdCallTimeout, "etcd-call-timeout-duration", etcd.DefaultCallTimeout, 158 "Duration that the etcd client waits at most for read and write operations to etcd.") 159 160 flags.AddDiagnosticsOptions(fs, &diagnosticsOptions) 161 flags.AddTLSOptions(fs, &tlsOptions) 162 163 feature.MutableGates.AddFlag(fs) 164 } 165 166 // Add RBAC for the authorized diagnostics endpoint. 167 // +kubebuilder:rbac:groups=authentication.k8s.io,resources=tokenreviews,verbs=create 168 // +kubebuilder:rbac:groups=authorization.k8s.io,resources=subjectaccessreviews,verbs=create 169 170 func main() { 171 InitFlags(pflag.CommandLine) 172 pflag.CommandLine.SetNormalizeFunc(cliflag.WordSepNormalizeFunc) 173 pflag.CommandLine.AddGoFlagSet(flag.CommandLine) 174 // Set log level 2 as default. 175 if err := pflag.CommandLine.Set("v", "2"); err != nil { 176 setupLog.Error(err, "failed to set log level: %v") 177 os.Exit(1) 178 } 179 pflag.Parse() 180 181 if err := logsv1.ValidateAndApply(logOptions, nil); err != nil { 182 setupLog.Error(err, "unable to start manager") 183 os.Exit(1) 184 } 185 186 // klog.Background will automatically use the right logger. 187 ctrl.SetLogger(klog.Background()) 188 189 restConfig := ctrl.GetConfigOrDie() 190 restConfig.QPS = restConfigQPS 191 restConfig.Burst = restConfigBurst 192 restConfig.UserAgent = remote.DefaultClusterAPIUserAgent(controllerName) 193 194 tlsOptionOverrides, err := flags.GetTLSOptionOverrideFuncs(tlsOptions) 195 if err != nil { 196 setupLog.Error(err, "unable to add TLS settings to the webhook server") 197 os.Exit(1) 198 } 199 200 diagnosticsOpts := flags.GetDiagnosticsOptions(diagnosticsOptions) 201 202 var watchNamespaces map[string]cache.Config 203 if watchNamespace != "" { 204 watchNamespaces = map[string]cache.Config{ 205 watchNamespace: {}, 206 } 207 } 208 209 if enableContentionProfiling { 210 goruntime.SetBlockProfileRate(1) 211 } 212 213 req, _ := labels.NewRequirement(clusterv1.ClusterNameLabel, selection.Exists, nil) 214 clusterSecretCacheSelector := labels.NewSelector().Add(*req) 215 216 ctrlOptions := ctrl.Options{ 217 Scheme: scheme, 218 LeaderElection: enableLeaderElection, 219 LeaderElectionID: "kubeadm-control-plane-manager-leader-election-capi", 220 LeaseDuration: &leaderElectionLeaseDuration, 221 RenewDeadline: &leaderElectionRenewDeadline, 222 RetryPeriod: &leaderElectionRetryPeriod, 223 LeaderElectionResourceLock: resourcelock.LeasesResourceLock, 224 HealthProbeBindAddress: healthAddr, 225 PprofBindAddress: profilerAddress, 226 Metrics: diagnosticsOpts, 227 Cache: cache.Options{ 228 DefaultNamespaces: watchNamespaces, 229 SyncPeriod: &syncPeriod, 230 ByObject: map[client.Object]cache.ByObject{ 231 // Note: Only Secrets with the cluster name label are cached. 232 // The default client of the manager won't use the cache for secrets at all (see Client.Cache.DisableFor). 233 // The cached secrets will only be used by the secretCachingClient we create below. 234 &corev1.Secret{}: { 235 Label: clusterSecretCacheSelector, 236 }, 237 }, 238 }, 239 Client: client.Options{ 240 Cache: &client.CacheOptions{ 241 DisableFor: []client.Object{ 242 &corev1.ConfigMap{}, 243 &corev1.Secret{}, 244 }, 245 // This config ensures that the default client caches Unstructured objects. 246 // KCP is only using Unstructured to retrieve InfraMachines and InfraMachineTemplates. 247 // As the cache should be used in those cases, caching is configured globally instead of 248 // creating a separate client that caches Unstructured. 249 Unstructured: true, 250 }, 251 }, 252 WebhookServer: webhook.NewServer( 253 webhook.Options{ 254 Port: webhookPort, 255 CertDir: webhookCertDir, 256 TLSOpts: tlsOptionOverrides, 257 }, 258 ), 259 } 260 261 mgr, err := ctrl.NewManager(restConfig, ctrlOptions) 262 if err != nil { 263 setupLog.Error(err, "unable to start manager") 264 os.Exit(1) 265 } 266 267 // Setup the context that's going to be used in controllers and for the manager. 268 ctx := ctrl.SetupSignalHandler() 269 270 setupChecks(mgr) 271 setupReconcilers(ctx, mgr) 272 setupWebhooks(mgr) 273 274 setupLog.Info("starting manager", "version", version.Get().String()) 275 if err := mgr.Start(ctx); err != nil { 276 setupLog.Error(err, "problem running manager") 277 os.Exit(1) 278 } 279 } 280 281 func setupChecks(mgr ctrl.Manager) { 282 if err := mgr.AddReadyzCheck("webhook", mgr.GetWebhookServer().StartedChecker()); err != nil { 283 setupLog.Error(err, "unable to create ready check") 284 os.Exit(1) 285 } 286 287 if err := mgr.AddHealthzCheck("webhook", mgr.GetWebhookServer().StartedChecker()); err != nil { 288 setupLog.Error(err, "unable to create health check") 289 os.Exit(1) 290 } 291 } 292 293 func setupReconcilers(ctx context.Context, mgr ctrl.Manager) { 294 secretCachingClient, err := client.New(mgr.GetConfig(), client.Options{ 295 HTTPClient: mgr.GetHTTPClient(), 296 Cache: &client.CacheOptions{ 297 Reader: mgr.GetCache(), 298 }, 299 }) 300 if err != nil { 301 setupLog.Error(err, "unable to create secret caching client") 302 os.Exit(1) 303 } 304 305 // Set up a ClusterCacheTracker to provide to controllers 306 // requiring a connection to a remote cluster 307 tracker, err := remote.NewClusterCacheTracker(mgr, remote.ClusterCacheTrackerOptions{ 308 SecretCachingClient: secretCachingClient, 309 ControllerName: controllerName, 310 Log: &ctrl.Log, 311 ClientUncachedObjects: []client.Object{ 312 &corev1.ConfigMap{}, 313 &corev1.Secret{}, 314 &corev1.Pod{}, 315 &appsv1.Deployment{}, 316 &appsv1.DaemonSet{}, 317 }, 318 }) 319 if err != nil { 320 setupLog.Error(err, "unable to create cluster cache tracker") 321 os.Exit(1) 322 } 323 if err := (&remote.ClusterCacheReconciler{ 324 Client: mgr.GetClient(), 325 Tracker: tracker, 326 WatchFilterValue: watchFilterValue, 327 }).SetupWithManager(ctx, mgr, concurrency(clusterCacheTrackerConcurrency)); err != nil { 328 setupLog.Error(err, "unable to create controller", "controller", "ClusterCacheReconciler") 329 os.Exit(1) 330 } 331 332 if err := (&kubeadmcontrolplanecontrollers.KubeadmControlPlaneReconciler{ 333 Client: mgr.GetClient(), 334 SecretCachingClient: secretCachingClient, 335 Tracker: tracker, 336 WatchFilterValue: watchFilterValue, 337 EtcdDialTimeout: etcdDialTimeout, 338 EtcdCallTimeout: etcdCallTimeout, 339 }).SetupWithManager(ctx, mgr, concurrency(kubeadmControlPlaneConcurrency)); err != nil { 340 setupLog.Error(err, "unable to create controller", "controller", "KubeadmControlPlane") 341 os.Exit(1) 342 } 343 } 344 345 func setupWebhooks(mgr ctrl.Manager) { 346 if err := (&kcpwebhooks.KubeadmControlPlane{}).SetupWebhookWithManager(mgr); err != nil { 347 setupLog.Error(err, "unable to create webhook", "webhook", "KubeadmControlPlane") 348 os.Exit(1) 349 } 350 351 if err := (&kcpwebhooks.ScaleValidator{ 352 Client: mgr.GetClient(), 353 }).SetupWebhookWithManager(mgr); err != nil { 354 setupLog.Error(err, "unable to create webhook", "webhook", "KubeadmControlPlane scale") 355 os.Exit(1) 356 } 357 358 if err := (&kcpwebhooks.KubeadmControlPlaneTemplate{}).SetupWebhookWithManager(mgr); err != nil { 359 setupLog.Error(err, "unable to create webhook", "webhook", "KubeadmControlPlaneTemplate") 360 os.Exit(1) 361 } 362 } 363 364 func concurrency(c int) controller.Options { 365 return controller.Options{MaxConcurrentReconciles: c} 366 }