sigs.k8s.io/controller-runtime@v0.18.2/pkg/manager/manager.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 manager 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "net" 24 "net/http" 25 "time" 26 27 "github.com/go-logr/logr" 28 coordinationv1 "k8s.io/api/coordination/v1" 29 corev1 "k8s.io/api/core/v1" 30 "k8s.io/apimachinery/pkg/api/meta" 31 "k8s.io/apimachinery/pkg/runtime" 32 "k8s.io/client-go/rest" 33 "k8s.io/client-go/tools/leaderelection/resourcelock" 34 "k8s.io/client-go/tools/record" 35 "k8s.io/utils/ptr" 36 metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" 37 38 "sigs.k8s.io/controller-runtime/pkg/cache" 39 "sigs.k8s.io/controller-runtime/pkg/client" 40 "sigs.k8s.io/controller-runtime/pkg/cluster" 41 "sigs.k8s.io/controller-runtime/pkg/config" 42 "sigs.k8s.io/controller-runtime/pkg/healthz" 43 intrec "sigs.k8s.io/controller-runtime/pkg/internal/recorder" 44 "sigs.k8s.io/controller-runtime/pkg/leaderelection" 45 "sigs.k8s.io/controller-runtime/pkg/log" 46 "sigs.k8s.io/controller-runtime/pkg/recorder" 47 "sigs.k8s.io/controller-runtime/pkg/webhook" 48 ) 49 50 // Manager initializes shared dependencies such as Caches and Clients, and provides them to Runnables. 51 // A Manager is required to create Controllers. 52 type Manager interface { 53 // Cluster holds a variety of methods to interact with a cluster. 54 cluster.Cluster 55 56 // Add will set requested dependencies on the component, and cause the component to be 57 // started when Start is called. 58 // Depending on if a Runnable implements LeaderElectionRunnable interface, a Runnable can be run in either 59 // non-leaderelection mode (always running) or leader election mode (managed by leader election if enabled). 60 Add(Runnable) error 61 62 // Elected is closed when this manager is elected leader of a group of 63 // managers, either because it won a leader election or because no leader 64 // election was configured. 65 Elected() <-chan struct{} 66 67 // AddMetricsServerExtraHandler adds an extra handler served on path to the http server that serves metrics. 68 // Might be useful to register some diagnostic endpoints e.g. pprof. 69 // 70 // Note that these endpoints are meant to be sensitive and shouldn't be exposed publicly. 71 // 72 // If the simple path -> handler mapping offered here is not enough, 73 // a new http server/listener should be added as Runnable to the manager via Add method. 74 AddMetricsServerExtraHandler(path string, handler http.Handler) error 75 76 // AddHealthzCheck allows you to add Healthz checker 77 AddHealthzCheck(name string, check healthz.Checker) error 78 79 // AddReadyzCheck allows you to add Readyz checker 80 AddReadyzCheck(name string, check healthz.Checker) error 81 82 // Start starts all registered Controllers and blocks until the context is cancelled. 83 // Returns an error if there is an error starting any controller. 84 // 85 // If LeaderElection is used, the binary must be exited immediately after this returns, 86 // otherwise components that need leader election might continue to run after the leader 87 // lock was lost. 88 Start(ctx context.Context) error 89 90 // GetWebhookServer returns a webhook.Server 91 GetWebhookServer() webhook.Server 92 93 // GetLogger returns this manager's logger. 94 GetLogger() logr.Logger 95 96 // GetControllerOptions returns controller global configuration options. 97 GetControllerOptions() config.Controller 98 } 99 100 // Options are the arguments for creating a new Manager. 101 type Options struct { 102 // Scheme is the scheme used to resolve runtime.Objects to GroupVersionKinds / Resources. 103 // Defaults to the kubernetes/client-go scheme.Scheme, but it's almost always better 104 // to pass your own scheme in. See the documentation in pkg/scheme for more information. 105 // 106 // If set, the Scheme will be used to create the default Client and Cache. 107 Scheme *runtime.Scheme 108 109 // MapperProvider provides the rest mapper used to map go types to Kubernetes APIs. 110 // 111 // If set, the RESTMapper returned by this function is used to create the RESTMapper 112 // used by the Client and Cache. 113 MapperProvider func(c *rest.Config, httpClient *http.Client) (meta.RESTMapper, error) 114 115 // Cache is the cache.Options that will be used to create the default Cache. 116 // By default, the cache will watch and list requested objects in all namespaces. 117 Cache cache.Options 118 119 // NewCache is the function that will create the cache to be used 120 // by the manager. If not set this will use the default new cache function. 121 // 122 // When using a custom NewCache, the Cache options will be passed to the 123 // NewCache function. 124 // 125 // NOTE: LOW LEVEL PRIMITIVE! 126 // Only use a custom NewCache if you know what you are doing. 127 NewCache cache.NewCacheFunc 128 129 // Client is the client.Options that will be used to create the default Client. 130 // By default, the client will use the cache for reads and direct calls for writes. 131 Client client.Options 132 133 // NewClient is the func that creates the client to be used by the manager. 134 // If not set this will create a Client backed by a Cache for read operations 135 // and a direct Client for write operations. 136 // 137 // When using a custom NewClient, the Client options will be passed to the 138 // NewClient function. 139 // 140 // NOTE: LOW LEVEL PRIMITIVE! 141 // Only use a custom NewClient if you know what you are doing. 142 NewClient client.NewClientFunc 143 144 // Logger is the logger that should be used by this manager. 145 // If none is set, it defaults to log.Log global logger. 146 Logger logr.Logger 147 148 // LeaderElection determines whether or not to use leader election when 149 // starting the manager. 150 LeaderElection bool 151 152 // LeaderElectionResourceLock determines which resource lock to use for leader election, 153 // defaults to "leases". Change this value only if you know what you are doing. 154 // 155 // If you are using `configmaps`/`endpoints` resource lock and want to migrate to "leases", 156 // you might do so by migrating to the respective multilock first ("configmapsleases" or "endpointsleases"), 157 // which will acquire a leader lock on both resources. 158 // After all your users have migrated to the multilock, you can go ahead and migrate to "leases". 159 // Please also keep in mind, that users might skip versions of your controller. 160 // 161 // Note: before controller-runtime version v0.7, it was set to "configmaps". 162 // And from v0.7 to v0.11, the default was "configmapsleases", which was 163 // used to migrate from configmaps to leases. 164 // Since the default was "configmapsleases" for over a year, spanning five minor releases, 165 // any actively maintained operators are very likely to have a released version that uses 166 // "configmapsleases". Therefore defaulting to "leases" should be safe since v0.12. 167 // 168 // So, what do you have to do when you are updating your controller-runtime dependency 169 // from a lower version to v0.12 or newer? 170 // - If your operator matches at least one of these conditions: 171 // - the LeaderElectionResourceLock in your operator has already been explicitly set to "leases" 172 // - the old controller-runtime version is between v0.7.0 and v0.11.x and the 173 // LeaderElectionResourceLock wasn't set or was set to "leases"/"configmapsleases"/"endpointsleases" 174 // feel free to update controller-runtime to v0.12 or newer. 175 // - Otherwise, you may have to take these steps: 176 // 1. update controller-runtime to v0.12 or newer in your go.mod 177 // 2. set LeaderElectionResourceLock to "configmapsleases" (or "endpointsleases") 178 // 3. package your operator and upgrade it in all your clusters 179 // 4. only if you have finished 3, you can remove the LeaderElectionResourceLock to use the default "leases" 180 // Otherwise, your operator might end up with multiple running instances that 181 // each acquired leadership through different resource locks during upgrades and thus 182 // act on the same resources concurrently. 183 LeaderElectionResourceLock string 184 185 // LeaderElectionNamespace determines the namespace in which the leader 186 // election resource will be created. 187 LeaderElectionNamespace string 188 189 // LeaderElectionID determines the name of the resource that leader election 190 // will use for holding the leader lock. 191 LeaderElectionID string 192 193 // LeaderElectionConfig can be specified to override the default configuration 194 // that is used to build the leader election client. 195 LeaderElectionConfig *rest.Config 196 197 // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily 198 // when the Manager ends. This requires the binary to immediately end when the 199 // Manager is stopped, otherwise this setting is unsafe. Setting this significantly 200 // speeds up voluntary leader transitions as the new leader doesn't have to wait 201 // LeaseDuration time first. 202 LeaderElectionReleaseOnCancel bool 203 204 // LeaderElectionResourceLockInterface allows to provide a custom resourcelock.Interface that was created outside 205 // of the controller-runtime. If this value is set the options LeaderElectionID, LeaderElectionNamespace, 206 // LeaderElectionResourceLock, LeaseDuration, RenewDeadline and RetryPeriod will be ignored. This can be useful if you 207 // want to use a locking mechanism that is currently not supported, like a MultiLock across two Kubernetes clusters. 208 LeaderElectionResourceLockInterface resourcelock.Interface 209 210 // LeaseDuration is the duration that non-leader candidates will 211 // wait to force acquire leadership. This is measured against time of 212 // last observed ack. Default is 15 seconds. 213 LeaseDuration *time.Duration 214 215 // RenewDeadline is the duration that the acting controlplane will retry 216 // refreshing leadership before giving up. Default is 10 seconds. 217 RenewDeadline *time.Duration 218 219 // RetryPeriod is the duration the LeaderElector clients should wait 220 // between tries of actions. Default is 2 seconds. 221 RetryPeriod *time.Duration 222 223 // Metrics are the metricsserver.Options that will be used to create the metricsserver.Server. 224 Metrics metricsserver.Options 225 226 // HealthProbeBindAddress is the TCP address that the controller should bind to 227 // for serving health probes 228 // It can be set to "0" or "" to disable serving the health probe. 229 HealthProbeBindAddress string 230 231 // Readiness probe endpoint name, defaults to "readyz" 232 ReadinessEndpointName string 233 234 // Liveness probe endpoint name, defaults to "healthz" 235 LivenessEndpointName string 236 237 // PprofBindAddress is the TCP address that the controller should bind to 238 // for serving pprof. 239 // It can be set to "" or "0" to disable the pprof serving. 240 // Since pprof may contain sensitive information, make sure to protect it 241 // before exposing it to public. 242 PprofBindAddress string 243 244 // WebhookServer is an externally configured webhook.Server. By default, 245 // a Manager will create a server via webhook.NewServer with default settings. 246 // If this is set, the Manager will use this server instead. 247 WebhookServer webhook.Server 248 249 // BaseContext is the function that provides Context values to Runnables 250 // managed by the Manager. If a BaseContext function isn't provided, Runnables 251 // will receive a new Background Context instead. 252 BaseContext BaseContextFunc 253 254 // EventBroadcaster records Events emitted by the manager and sends them to the Kubernetes API 255 // Use this to customize the event correlator and spam filter 256 // 257 // Deprecated: using this may cause goroutine leaks if the lifetime of your manager or controllers 258 // is shorter than the lifetime of your process. 259 EventBroadcaster record.EventBroadcaster 260 261 // GracefulShutdownTimeout is the duration given to runnable to stop before the manager actually returns on stop. 262 // To disable graceful shutdown, set to time.Duration(0) 263 // To use graceful shutdown without timeout, set to a negative duration, e.G. time.Duration(-1) 264 // The graceful shutdown is skipped for safety reasons in case the leader election lease is lost. 265 GracefulShutdownTimeout *time.Duration 266 267 // Controller contains global configuration options for controllers 268 // registered within this manager. 269 // +optional 270 Controller config.Controller 271 272 // makeBroadcaster allows deferring the creation of the broadcaster to 273 // avoid leaking goroutines if we never call Start on this manager. It also 274 // returns whether or not this is a "owned" broadcaster, and as such should be 275 // stopped with the manager. 276 makeBroadcaster intrec.EventBroadcasterProducer 277 278 // Dependency injection for testing 279 newRecorderProvider func(config *rest.Config, httpClient *http.Client, scheme *runtime.Scheme, logger logr.Logger, makeBroadcaster intrec.EventBroadcasterProducer) (*intrec.Provider, error) 280 newResourceLock func(config *rest.Config, recorderProvider recorder.Provider, options leaderelection.Options) (resourcelock.Interface, error) 281 newMetricsServer func(options metricsserver.Options, config *rest.Config, httpClient *http.Client) (metricsserver.Server, error) 282 newHealthProbeListener func(addr string) (net.Listener, error) 283 newPprofListener func(addr string) (net.Listener, error) 284 } 285 286 // BaseContextFunc is a function used to provide a base Context to Runnables 287 // managed by a Manager. 288 type BaseContextFunc func() context.Context 289 290 // Runnable allows a component to be started. 291 // It's very important that Start blocks until 292 // it's done running. 293 type Runnable interface { 294 // Start starts running the component. The component will stop running 295 // when the context is closed. Start blocks until the context is closed or 296 // an error occurs. 297 Start(context.Context) error 298 } 299 300 // RunnableFunc implements Runnable using a function. 301 // It's very important that the given function block 302 // until it's done running. 303 type RunnableFunc func(context.Context) error 304 305 // Start implements Runnable. 306 func (r RunnableFunc) Start(ctx context.Context) error { 307 return r(ctx) 308 } 309 310 // LeaderElectionRunnable knows if a Runnable needs to be run in the leader election mode. 311 type LeaderElectionRunnable interface { 312 // NeedLeaderElection returns true if the Runnable needs to be run in the leader election mode. 313 // e.g. controllers need to be run in leader election mode, while webhook server doesn't. 314 NeedLeaderElection() bool 315 } 316 317 // New returns a new Manager for creating Controllers. 318 // Note that if ContentType in the given config is not set, "application/vnd.kubernetes.protobuf" 319 // will be used for all built-in resources of Kubernetes, and "application/json" is for other types 320 // including all CRD resources. 321 func New(config *rest.Config, options Options) (Manager, error) { 322 if config == nil { 323 return nil, errors.New("must specify Config") 324 } 325 // Set default values for options fields 326 options = setOptionsDefaults(options) 327 328 cluster, err := cluster.New(config, func(clusterOptions *cluster.Options) { 329 clusterOptions.Scheme = options.Scheme 330 clusterOptions.MapperProvider = options.MapperProvider 331 clusterOptions.Logger = options.Logger 332 clusterOptions.NewCache = options.NewCache 333 clusterOptions.NewClient = options.NewClient 334 clusterOptions.Cache = options.Cache 335 clusterOptions.Client = options.Client 336 clusterOptions.EventBroadcaster = options.EventBroadcaster //nolint:staticcheck 337 }) 338 if err != nil { 339 return nil, err 340 } 341 342 config = rest.CopyConfig(config) 343 if config.UserAgent == "" { 344 config.UserAgent = rest.DefaultKubernetesUserAgent() 345 } 346 347 // Create the recorder provider to inject event recorders for the components. 348 // TODO(directxman12): the log for the event provider should have a context (name, tags, etc) specific 349 // to the particular controller that it's being injected into, rather than a generic one like is here. 350 recorderProvider, err := options.newRecorderProvider(config, cluster.GetHTTPClient(), cluster.GetScheme(), options.Logger.WithName("events"), options.makeBroadcaster) 351 if err != nil { 352 return nil, err 353 } 354 355 // Create the resource lock to enable leader election) 356 var leaderConfig *rest.Config 357 var leaderRecorderProvider *intrec.Provider 358 359 if options.LeaderElectionConfig == nil { 360 leaderConfig = rest.CopyConfig(config) 361 leaderRecorderProvider = recorderProvider 362 } else { 363 leaderConfig = rest.CopyConfig(options.LeaderElectionConfig) 364 scheme := cluster.GetScheme() 365 err := corev1.AddToScheme(scheme) 366 if err != nil { 367 return nil, err 368 } 369 err = coordinationv1.AddToScheme(scheme) 370 if err != nil { 371 return nil, err 372 } 373 httpClient, err := rest.HTTPClientFor(options.LeaderElectionConfig) 374 if err != nil { 375 return nil, err 376 } 377 leaderRecorderProvider, err = options.newRecorderProvider(leaderConfig, httpClient, scheme, options.Logger.WithName("events"), options.makeBroadcaster) 378 if err != nil { 379 return nil, err 380 } 381 } 382 383 var resourceLock resourcelock.Interface 384 if options.LeaderElectionResourceLockInterface != nil && options.LeaderElection { 385 resourceLock = options.LeaderElectionResourceLockInterface 386 } else { 387 resourceLock, err = options.newResourceLock(leaderConfig, leaderRecorderProvider, leaderelection.Options{ 388 LeaderElection: options.LeaderElection, 389 LeaderElectionResourceLock: options.LeaderElectionResourceLock, 390 LeaderElectionID: options.LeaderElectionID, 391 LeaderElectionNamespace: options.LeaderElectionNamespace, 392 }) 393 if err != nil { 394 return nil, err 395 } 396 } 397 398 // Create the metrics server. 399 metricsServer, err := options.newMetricsServer(options.Metrics, config, cluster.GetHTTPClient()) 400 if err != nil { 401 return nil, err 402 } 403 404 // Create health probes listener. This will throw an error if the bind 405 // address is invalid or already in use. 406 healthProbeListener, err := options.newHealthProbeListener(options.HealthProbeBindAddress) 407 if err != nil { 408 return nil, err 409 } 410 411 // Create pprof listener. This will throw an error if the bind 412 // address is invalid or already in use. 413 pprofListener, err := options.newPprofListener(options.PprofBindAddress) 414 if err != nil { 415 return nil, fmt.Errorf("failed to new pprof listener: %w", err) 416 } 417 418 errChan := make(chan error, 1) 419 runnables := newRunnables(options.BaseContext, errChan) 420 return &controllerManager{ 421 stopProcedureEngaged: ptr.To(int64(0)), 422 cluster: cluster, 423 runnables: runnables, 424 errChan: errChan, 425 recorderProvider: recorderProvider, 426 resourceLock: resourceLock, 427 metricsServer: metricsServer, 428 controllerConfig: options.Controller, 429 logger: options.Logger, 430 elected: make(chan struct{}), 431 webhookServer: options.WebhookServer, 432 leaderElectionID: options.LeaderElectionID, 433 leaseDuration: *options.LeaseDuration, 434 renewDeadline: *options.RenewDeadline, 435 retryPeriod: *options.RetryPeriod, 436 healthProbeListener: healthProbeListener, 437 readinessEndpointName: options.ReadinessEndpointName, 438 livenessEndpointName: options.LivenessEndpointName, 439 pprofListener: pprofListener, 440 gracefulShutdownTimeout: *options.GracefulShutdownTimeout, 441 internalProceduresStop: make(chan struct{}), 442 leaderElectionStopped: make(chan struct{}), 443 leaderElectionReleaseOnCancel: options.LeaderElectionReleaseOnCancel, 444 }, nil 445 } 446 447 // defaultHealthProbeListener creates the default health probes listener bound to the given address. 448 func defaultHealthProbeListener(addr string) (net.Listener, error) { 449 if addr == "" || addr == "0" { 450 return nil, nil 451 } 452 453 ln, err := net.Listen("tcp", addr) 454 if err != nil { 455 return nil, fmt.Errorf("error listening on %s: %w", addr, err) 456 } 457 return ln, nil 458 } 459 460 // defaultPprofListener creates the default pprof listener bound to the given address. 461 func defaultPprofListener(addr string) (net.Listener, error) { 462 if addr == "" || addr == "0" { 463 return nil, nil 464 } 465 466 ln, err := net.Listen("tcp", addr) 467 if err != nil { 468 return nil, fmt.Errorf("error listening on %s: %w", addr, err) 469 } 470 return ln, nil 471 } 472 473 // defaultBaseContext is used as the BaseContext value in Options if one 474 // has not already been set. 475 func defaultBaseContext() context.Context { 476 return context.Background() 477 } 478 479 // setOptionsDefaults set default values for Options fields. 480 func setOptionsDefaults(options Options) Options { 481 // Allow newResourceLock to be mocked 482 if options.newResourceLock == nil { 483 options.newResourceLock = leaderelection.NewResourceLock 484 } 485 486 // Allow newRecorderProvider to be mocked 487 if options.newRecorderProvider == nil { 488 options.newRecorderProvider = intrec.NewProvider 489 } 490 491 // This is duplicated with pkg/cluster, we need it here 492 // for the leader election and there to provide the user with 493 // an EventBroadcaster 494 if options.EventBroadcaster == nil { 495 // defer initialization to avoid leaking by default 496 options.makeBroadcaster = func() (record.EventBroadcaster, bool) { 497 return record.NewBroadcaster(), true 498 } 499 } else { 500 options.makeBroadcaster = func() (record.EventBroadcaster, bool) { 501 return options.EventBroadcaster, false 502 } 503 } 504 505 if options.newMetricsServer == nil { 506 options.newMetricsServer = metricsserver.NewServer 507 } 508 leaseDuration, renewDeadline, retryPeriod := defaultLeaseDuration, defaultRenewDeadline, defaultRetryPeriod 509 if options.LeaseDuration == nil { 510 options.LeaseDuration = &leaseDuration 511 } 512 513 if options.RenewDeadline == nil { 514 options.RenewDeadline = &renewDeadline 515 } 516 517 if options.RetryPeriod == nil { 518 options.RetryPeriod = &retryPeriod 519 } 520 521 if options.ReadinessEndpointName == "" { 522 options.ReadinessEndpointName = defaultReadinessEndpoint 523 } 524 525 if options.LivenessEndpointName == "" { 526 options.LivenessEndpointName = defaultLivenessEndpoint 527 } 528 529 if options.newHealthProbeListener == nil { 530 options.newHealthProbeListener = defaultHealthProbeListener 531 } 532 533 if options.newPprofListener == nil { 534 options.newPprofListener = defaultPprofListener 535 } 536 537 if options.GracefulShutdownTimeout == nil { 538 gracefulShutdownTimeout := defaultGracefulShutdownPeriod 539 options.GracefulShutdownTimeout = &gracefulShutdownTimeout 540 } 541 542 if options.Logger.GetSink() == nil { 543 options.Logger = log.Log 544 } 545 546 if options.BaseContext == nil { 547 options.BaseContext = defaultBaseContext 548 } 549 550 if options.WebhookServer == nil { 551 options.WebhookServer = webhook.NewServer(webhook.Options{}) 552 } 553 554 return options 555 }