sigs.k8s.io/cluster-api-provider-aws@v1.5.5/main.go (about) 1 /* 2 Copyright 2018 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 package main 18 19 import ( 20 "context" 21 "errors" 22 "flag" 23 "fmt" 24 "math/rand" 25 "net/http" 26 _ "net/http/pprof" 27 "os" 28 "time" 29 30 "github.com/spf13/pflag" 31 "k8s.io/apimachinery/pkg/runtime" 32 cgscheme "k8s.io/client-go/kubernetes/scheme" 33 "k8s.io/client-go/tools/leaderelection/resourcelock" 34 cgrecord "k8s.io/client-go/tools/record" 35 "k8s.io/klog/v2" 36 "k8s.io/klog/v2/klogr" 37 ctrl "sigs.k8s.io/controller-runtime" 38 "sigs.k8s.io/controller-runtime/pkg/controller" 39 40 // +kubebuilder:scaffold:imports 41 infrav1alpha3 "sigs.k8s.io/cluster-api-provider-aws/api/v1alpha3" 42 infrav1alpha4 "sigs.k8s.io/cluster-api-provider-aws/api/v1alpha4" 43 infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1" 44 eksbootstrapv1alpha3 "sigs.k8s.io/cluster-api-provider-aws/bootstrap/eks/api/v1alpha3" 45 eksbootstrapv1alpha4 "sigs.k8s.io/cluster-api-provider-aws/bootstrap/eks/api/v1alpha4" 46 eksbootstrapv1 "sigs.k8s.io/cluster-api-provider-aws/bootstrap/eks/api/v1beta1" 47 eksbootstrapcontrollers "sigs.k8s.io/cluster-api-provider-aws/bootstrap/eks/controllers" 48 "sigs.k8s.io/cluster-api-provider-aws/controllers" 49 ekscontrolplanev1alpha3 "sigs.k8s.io/cluster-api-provider-aws/controlplane/eks/api/v1alpha3" 50 ekscontrolplanev1alpha4 "sigs.k8s.io/cluster-api-provider-aws/controlplane/eks/api/v1alpha4" 51 ekscontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/controlplane/eks/api/v1beta1" 52 ekscontrolplanecontrollers "sigs.k8s.io/cluster-api-provider-aws/controlplane/eks/controllers" 53 expinfrav1alpha3 "sigs.k8s.io/cluster-api-provider-aws/exp/api/v1alpha3" 54 expinfrav1alpha4 "sigs.k8s.io/cluster-api-provider-aws/exp/api/v1alpha4" 55 expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/exp/api/v1beta1" 56 "sigs.k8s.io/cluster-api-provider-aws/exp/controlleridentitycreator" 57 expcontrollers "sigs.k8s.io/cluster-api-provider-aws/exp/controllers" 58 "sigs.k8s.io/cluster-api-provider-aws/exp/instancestate" 59 "sigs.k8s.io/cluster-api-provider-aws/feature" 60 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/endpoints" 61 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/scope" 62 "sigs.k8s.io/cluster-api-provider-aws/pkg/record" 63 "sigs.k8s.io/cluster-api-provider-aws/version" 64 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 65 expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" 66 ) 67 68 var ( 69 scheme = runtime.NewScheme() 70 setupLog = ctrl.Log.WithName("setup") 71 ) 72 73 func init() { 74 _ = eksbootstrapv1.AddToScheme(scheme) 75 _ = eksbootstrapv1alpha3.AddToScheme(scheme) 76 _ = eksbootstrapv1alpha4.AddToScheme(scheme) 77 _ = cgscheme.AddToScheme(scheme) 78 _ = clusterv1.AddToScheme(scheme) 79 _ = expclusterv1.AddToScheme(scheme) 80 _ = ekscontrolplanev1.AddToScheme(scheme) 81 _ = ekscontrolplanev1alpha3.AddToScheme(scheme) 82 _ = ekscontrolplanev1alpha4.AddToScheme(scheme) 83 _ = infrav1.AddToScheme(scheme) 84 _ = infrav1alpha3.AddToScheme(scheme) 85 _ = expinfrav1alpha3.AddToScheme(scheme) 86 _ = infrav1alpha4.AddToScheme(scheme) 87 _ = expinfrav1alpha4.AddToScheme(scheme) 88 _ = expinfrav1.AddToScheme(scheme) 89 // +kubebuilder:scaffold:scheme 90 } 91 92 var ( 93 metricsBindAddr string 94 enableLeaderElection bool 95 leaderElectionNamespace string 96 watchNamespace string 97 watchFilterValue string 98 profilerAddress string 99 awsClusterConcurrency int 100 instanceStateConcurrency int 101 awsMachineConcurrency int 102 syncPeriod time.Duration 103 webhookPort int 104 webhookCertDir string 105 healthAddr string 106 serviceEndpoints string 107 108 // maxEKSSyncPeriod is the maximum allowed duration for the sync-period flag when using EKS. It is set to 10 minutes 109 // because during resync it will create a new AWS auth token which can a maximum life of 15 minutes and this ensures 110 // the token (and kubeconfig secret) is refreshed before token expiration. 111 maxEKSSyncPeriod = time.Minute * 10 112 errMaxSyncPeriodExceeded = errors.New("sync period greater than maximum allowed") 113 errEKSInvalidFlags = errors.New("invalid EKS flag combination") 114 ) 115 116 func main() { 117 klog.InitFlags(nil) 118 119 rand.Seed(time.Now().UnixNano()) 120 initFlags(pflag.CommandLine) 121 pflag.CommandLine.AddGoFlagSet(flag.CommandLine) 122 pflag.Parse() 123 124 ctrl.SetLogger(klogr.New()) 125 126 if watchNamespace != "" { 127 setupLog.Info("Watching cluster-api objects only in namespace for reconciliation", "namespace", watchNamespace) 128 } 129 130 if profilerAddress != "" { 131 setupLog.Info("Profiler listening for requests", "profiler-address", profilerAddress) 132 go func() { 133 setupLog.Error(http.ListenAndServe(profilerAddress, nil), "listen and serve error") 134 }() 135 } 136 137 // Machine and cluster operations can create enough events to trigger the event recorder spam filter 138 // Setting the burst size higher ensures all events will be recorded and submitted to the API 139 broadcaster := cgrecord.NewBroadcasterWithCorrelatorOptions(cgrecord.CorrelatorOptions{ 140 BurstSize: 100, 141 }) 142 143 ctx := ctrl.SetupSignalHandler() 144 145 restConfig := ctrl.GetConfigOrDie() 146 restConfig.UserAgent = "cluster-api-provider-aws-controller" 147 mgr, err := ctrl.NewManager(restConfig, ctrl.Options{ 148 Scheme: scheme, 149 MetricsBindAddress: metricsBindAddr, 150 LeaderElection: enableLeaderElection, 151 LeaderElectionResourceLock: resourcelock.LeasesResourceLock, 152 LeaderElectionID: "controller-leader-elect-capa", 153 LeaderElectionNamespace: leaderElectionNamespace, 154 SyncPeriod: &syncPeriod, 155 Namespace: watchNamespace, 156 EventBroadcaster: broadcaster, 157 Port: webhookPort, 158 CertDir: webhookCertDir, 159 HealthProbeBindAddress: healthAddr, 160 }) 161 if err != nil { 162 setupLog.Error(err, "unable to start manager") 163 os.Exit(1) 164 } 165 166 // Initialize event recorder. 167 record.InitFromRecorder(mgr.GetEventRecorderFor("aws-controller")) 168 169 setupLog.V(1).Info(fmt.Sprintf("feature gates: %+v\n", feature.Gates)) 170 171 externalResourceGC := false 172 if feature.Gates.Enabled(feature.ExternalResourceGC) { 173 setupLog.Info("enabling external resource garbage collection") 174 externalResourceGC = true 175 } 176 177 // Parse service endpoints. 178 AWSServiceEndpoints, err := endpoints.ParseFlag(serviceEndpoints) 179 if err != nil { 180 setupLog.Error(err, "unable to parse service endpoints", "controller", "AWSCluster") 181 os.Exit(1) 182 } 183 184 if err = (&controllers.AWSMachineReconciler{ 185 Client: mgr.GetClient(), 186 Log: ctrl.Log.WithName("controllers").WithName("AWSMachine"), 187 Recorder: mgr.GetEventRecorderFor("awsmachine-controller"), 188 Endpoints: AWSServiceEndpoints, 189 WatchFilterValue: watchFilterValue, 190 }).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: awsMachineConcurrency, RecoverPanic: true}); err != nil { 191 setupLog.Error(err, "unable to create controller", "controller", "AWSMachine") 192 os.Exit(1) 193 } 194 if err = (&controllers.AWSClusterReconciler{ 195 Client: mgr.GetClient(), 196 Recorder: mgr.GetEventRecorderFor("awscluster-controller"), 197 Endpoints: AWSServiceEndpoints, 198 WatchFilterValue: watchFilterValue, 199 ExternalResourceGC: externalResourceGC, 200 }).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: awsClusterConcurrency, RecoverPanic: true}); err != nil { 201 setupLog.Error(err, "unable to create controller", "controller", "AWSCluster") 202 os.Exit(1) 203 } 204 enableGates(ctx, mgr, AWSServiceEndpoints, externalResourceGC) 205 206 if err = (&infrav1.AWSMachineTemplate{}).SetupWebhookWithManager(mgr); err != nil { 207 setupLog.Error(err, "unable to create webhook", "webhook", "AWSMachineTemplate") 208 os.Exit(1) 209 } 210 if err = (&infrav1.AWSCluster{}).SetupWebhookWithManager(mgr); err != nil { 211 setupLog.Error(err, "unable to create webhook", "webhook", "AWSCluster") 212 os.Exit(1) 213 } 214 if err = (&infrav1.AWSClusterTemplate{}).SetupWebhookWithManager(mgr); err != nil { 215 setupLog.Error(err, "unable to create webhook", "webhook", "AWSClusterTemplate") 216 os.Exit(1) 217 } 218 if err = (&infrav1.AWSClusterControllerIdentity{}).SetupWebhookWithManager(mgr); err != nil { 219 setupLog.Error(err, "unable to create webhook", "webhook", "AWSClusterControllerIdentity") 220 os.Exit(1) 221 } 222 if err = (&infrav1.AWSClusterRoleIdentity{}).SetupWebhookWithManager(mgr); err != nil { 223 setupLog.Error(err, "unable to create webhook", "webhook", "AWSClusterRoleIdentity") 224 os.Exit(1) 225 } 226 if err = (&infrav1.AWSClusterStaticIdentity{}).SetupWebhookWithManager(mgr); err != nil { 227 setupLog.Error(err, "unable to create webhook", "webhook", "AWSClusterStaticIdentity") 228 os.Exit(1) 229 } 230 if err = (&infrav1.AWSMachine{}).SetupWebhookWithManager(mgr); err != nil { 231 setupLog.Error(err, "unable to create webhook", "webhook", "AWSMachine") 232 os.Exit(1) 233 } 234 if feature.Gates.Enabled(feature.EKS) { 235 setupLog.Info("enabling EKS webhooks") 236 if err := (&ekscontrolplanev1.AWSManagedControlPlane{}).SetupWebhookWithManager(mgr); err != nil { 237 setupLog.Error(err, "unable to create webhook", "webhook", "AWSManagedControlPlane") 238 os.Exit(1) 239 } 240 if feature.Gates.Enabled(feature.EKSFargate) { 241 if err = (&expinfrav1.AWSFargateProfile{}).SetupWebhookWithManager(mgr); err != nil { 242 setupLog.Error(err, "unable to create webhook", "webhook", "AWSFargateProfile") 243 os.Exit(1) 244 } 245 } 246 if feature.Gates.Enabled(feature.MachinePool) { 247 if err = (&expinfrav1.AWSManagedMachinePool{}).SetupWebhookWithManager(mgr); err != nil { 248 setupLog.Error(err, "unable to create webhook", "webhook", "AWSManagedMachinePool") 249 os.Exit(1) 250 } 251 } 252 } 253 if feature.Gates.Enabled(feature.MachinePool) { 254 setupLog.Info("enabling webhook for AWSMachinePool") 255 if err = (&expinfrav1.AWSMachinePool{}).SetupWebhookWithManager(mgr); err != nil { 256 setupLog.Error(err, "unable to create webhook", "webhook", "AWSMachinePool") 257 os.Exit(1) 258 } 259 } 260 261 // +kubebuilder:scaffold:builder 262 263 if err := mgr.AddReadyzCheck("webhook", mgr.GetWebhookServer().StartedChecker()); err != nil { 264 setupLog.Error(err, "unable to create ready check") 265 os.Exit(1) 266 } 267 268 if err := mgr.AddHealthzCheck("webhook", mgr.GetWebhookServer().StartedChecker()); err != nil { 269 setupLog.Error(err, "unable to create health check") 270 os.Exit(1) 271 } 272 273 setupLog.Info("starting manager", "version", version.Get().String()) 274 if err := mgr.Start(ctx); err != nil { 275 setupLog.Error(err, "problem running manager") 276 os.Exit(1) 277 } 278 } 279 280 func enableGates(ctx context.Context, mgr ctrl.Manager, awsServiceEndpoints []scope.ServiceEndpoint, externalResourceGC bool) { 281 if feature.Gates.Enabled(feature.EKS) { 282 setupLog.Info("enabling EKS controllers") 283 284 if syncPeriod > maxEKSSyncPeriod { 285 setupLog.Error(errMaxSyncPeriodExceeded, "failed to enable EKS", "max-sync-period", maxEKSSyncPeriod, "syn-period", syncPeriod) 286 os.Exit(1) 287 } 288 289 enableIAM := feature.Gates.Enabled(feature.EKSEnableIAM) 290 allowAddRoles := feature.Gates.Enabled(feature.EKSAllowAddRoles) 291 setupLog.V(2).Info("EKS IAM role creation", "enabled", enableIAM) 292 setupLog.V(2).Info("EKS IAM additional roles", "enabled", allowAddRoles) 293 if allowAddRoles && !enableIAM { 294 setupLog.Error(errEKSInvalidFlags, "cannot use EKSAllowAddRoles flag without EKSEnableIAM") 295 os.Exit(1) 296 } 297 298 setupLog.V(2).Info("enabling EKS control plane controller") 299 if err := (&ekscontrolplanecontrollers.AWSManagedControlPlaneReconciler{ 300 Client: mgr.GetClient(), 301 EnableIAM: enableIAM, 302 AllowAdditionalRoles: allowAddRoles, 303 Endpoints: awsServiceEndpoints, 304 WatchFilterValue: watchFilterValue, 305 ExternalResourceGC: externalResourceGC, 306 }).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: awsClusterConcurrency, RecoverPanic: true}); err != nil { 307 setupLog.Error(err, "unable to create controller", "controller", "AWSManagedControlPlane") 308 os.Exit(1) 309 } 310 311 setupLog.V(2).Info("enabling EKS bootstrap controller") 312 if err := (&eksbootstrapcontrollers.EKSConfigReconciler{ 313 Client: mgr.GetClient(), 314 WatchFilterValue: watchFilterValue, 315 }).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: awsClusterConcurrency, RecoverPanic: true}); err != nil { 316 setupLog.Error(err, "unable to create controller", "controller", "EKSConfig") 317 os.Exit(1) 318 } 319 320 if feature.Gates.Enabled(feature.EKSFargate) { 321 setupLog.V(2).Info("enabling EKS fargate profile controller") 322 if err := (&expcontrollers.AWSFargateProfileReconciler{ 323 Client: mgr.GetClient(), 324 Recorder: mgr.GetEventRecorderFor("awsfargateprofile-reconciler"), 325 EnableIAM: enableIAM, 326 Endpoints: awsServiceEndpoints, 327 WatchFilterValue: watchFilterValue, 328 }).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: awsClusterConcurrency, RecoverPanic: true}); err != nil { 329 setupLog.Error(err, "unable to create controller", "controller", "AWSFargateProfile") 330 } 331 } 332 333 if feature.Gates.Enabled(feature.MachinePool) { 334 setupLog.V(2).Info("enabling EKS managed machine pool controller") 335 if err := (&expcontrollers.AWSManagedMachinePoolReconciler{ 336 AllowAdditionalRoles: allowAddRoles, 337 Client: mgr.GetClient(), 338 EnableIAM: enableIAM, 339 Endpoints: awsServiceEndpoints, 340 Recorder: mgr.GetEventRecorderFor("awsmanagedmachinepool-reconciler"), 341 WatchFilterValue: watchFilterValue, 342 }).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: instanceStateConcurrency, RecoverPanic: true}); err != nil { 343 setupLog.Error(err, "unable to create controller", "controller", "AWSManagedMachinePool") 344 os.Exit(1) 345 } 346 } 347 } 348 if feature.Gates.Enabled(feature.MachinePool) { 349 setupLog.V(2).Info("enabling machine pool controller") 350 if err := (&expcontrollers.AWSMachinePoolReconciler{ 351 Client: mgr.GetClient(), 352 Recorder: mgr.GetEventRecorderFor("awsmachinepool-controller"), 353 WatchFilterValue: watchFilterValue, 354 }).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: instanceStateConcurrency, RecoverPanic: true}); err != nil { 355 setupLog.Error(err, "unable to create controller", "controller", "AWSMachinePool") 356 os.Exit(1) 357 } 358 } 359 if feature.Gates.Enabled(feature.EventBridgeInstanceState) { 360 setupLog.Info("EventBridge notifications enabled. enabling AWSInstanceStateController") 361 if err := (&instancestate.AwsInstanceStateReconciler{ 362 Client: mgr.GetClient(), 363 Log: ctrl.Log.WithName("controllers").WithName("AWSInstanceStateController"), 364 Endpoints: awsServiceEndpoints, 365 WatchFilterValue: watchFilterValue, 366 }).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: instanceStateConcurrency, RecoverPanic: true}); err != nil { 367 setupLog.Error(err, "unable to create controller", "controller", "AWSInstanceStateController") 368 os.Exit(1) 369 } 370 } 371 if feature.Gates.Enabled(feature.AutoControllerIdentityCreator) { 372 setupLog.Info("AutoControllerIdentityCreator enabled") 373 if err := (&controlleridentitycreator.AWSControllerIdentityReconciler{ 374 Client: mgr.GetClient(), 375 Log: ctrl.Log.WithName("controllers").WithName("AWSControllerIdentity"), 376 Endpoints: awsServiceEndpoints, 377 WatchFilterValue: watchFilterValue, 378 }).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: awsClusterConcurrency, RecoverPanic: true}); err != nil { 379 setupLog.Error(err, "unable to create controller", "controller", "AWSControllerIdentity") 380 os.Exit(1) 381 } 382 } 383 384 if feature.Gates.Enabled(feature.BootstrapFormatIgnition) { 385 setupLog.Info("Enabling Ignition support for machine bootstrap data") 386 } 387 } 388 func initFlags(fs *pflag.FlagSet) { 389 fs.StringVar( 390 &metricsBindAddr, 391 "metrics-bind-addr", 392 "localhost:8080", 393 "The address the metric endpoint binds to.", 394 ) 395 396 fs.BoolVar( 397 &enableLeaderElection, 398 "leader-elect", 399 false, 400 "Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.", 401 ) 402 403 fs.StringVar( 404 &watchNamespace, 405 "namespace", 406 "", 407 "Namespace that the controller watches to reconcile cluster-api objects. If unspecified, the controller watches for cluster-api objects across all namespaces.", 408 ) 409 410 fs.StringVar( 411 &leaderElectionNamespace, 412 "leader-elect-namespace", 413 "", 414 "Namespace that the controller performs leader election in. If unspecified, the controller will discover which namespace it is running in.", 415 ) 416 417 fs.StringVar( 418 &profilerAddress, 419 "profiler-address", 420 "", 421 "Bind address to expose the pprof profiler (e.g. localhost:6060)", 422 ) 423 424 fs.IntVar(&awsClusterConcurrency, 425 "awscluster-concurrency", 426 5, 427 "Number of AWSClusters to process simultaneously", 428 ) 429 430 fs.IntVar(&instanceStateConcurrency, 431 "instance-state-concurrency", 432 5, 433 "Number of concurrent watches for instance state changes", 434 ) 435 436 fs.IntVar(&awsMachineConcurrency, 437 "awsmachine-concurrency", 438 10, 439 "Number of AWSMachines to process simultaneously", 440 ) 441 442 fs.DurationVar(&syncPeriod, 443 "sync-period", 444 10*time.Minute, 445 fmt.Sprintf("The minimum interval at which watched resources are reconciled. If EKS is enabled the maximum allowed is %s", maxEKSSyncPeriod), 446 ) 447 448 fs.IntVar(&webhookPort, 449 "webhook-port", 450 9443, 451 "Webhook Server port.", 452 ) 453 454 fs.StringVar(&webhookCertDir, "webhook-cert-dir", "/tmp/k8s-webhook-server/serving-certs/", 455 "Webhook cert dir, only used when webhook-port is specified.") 456 457 fs.StringVar(&healthAddr, 458 "health-addr", 459 ":9440", 460 "The address the health endpoint binds to.", 461 ) 462 463 fs.StringVar(&serviceEndpoints, 464 "service-endpoints", 465 "", 466 "Set custom AWS service endpoins in semi-colon separated format: ${SigningRegion1}:${ServiceID1}=${URL},${ServiceID2}=${URL};${SigningRegion2}...", 467 ) 468 469 fs.StringVar( 470 &watchFilterValue, 471 "watch-filter", 472 "", 473 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), 474 ) 475 476 feature.MutableGates.AddFlag(fs) 477 }