knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/controller/controller.go (about) 1 /* 2 Copyright 2018 The Knative 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 https://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 controller 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "sync" 24 "time" 25 26 "github.com/google/uuid" 27 "golang.org/x/sync/errgroup" 28 29 "go.uber.org/zap" 30 "go.uber.org/zap/zapcore" 31 apierrors "k8s.io/apimachinery/pkg/api/errors" 32 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 33 "k8s.io/apimachinery/pkg/runtime/schema" 34 "k8s.io/apimachinery/pkg/types" 35 "k8s.io/apimachinery/pkg/util/runtime" 36 "k8s.io/apimachinery/pkg/util/wait" 37 "k8s.io/client-go/tools/cache" 38 "k8s.io/client-go/tools/record" 39 "k8s.io/client-go/util/workqueue" 40 41 "knative.dev/pkg/kmeta" 42 kle "knative.dev/pkg/leaderelection" 43 "knative.dev/pkg/logging" 44 "knative.dev/pkg/logging/logkey" 45 "knative.dev/pkg/reconciler" 46 "knative.dev/pkg/tracker" 47 ) 48 49 const ( 50 // DefaultResyncPeriod is the default duration that is used when no 51 // resync period is associated with a controllers initialization context. 52 DefaultResyncPeriod = 10 * time.Hour 53 ) 54 55 // DefaultThreadsPerController is the number of threads to use 56 // when processing the controller's workqueue. Controller binaries 57 // may adjust this process-wide default. For finer control, invoke 58 // Run on the controller directly. 59 // TODO rename the const to Concurrency and deprecated this 60 var DefaultThreadsPerController = 2 61 62 // Reconciler is the interface that controller implementations are expected 63 // to implement, so that the shared controller.Impl can drive work through it. 64 type Reconciler interface { 65 Reconcile(ctx context.Context, key string) error 66 } 67 68 // PassNew makes it simple to create an UpdateFunc for use with 69 // cache.ResourceEventHandlerFuncs that can delegate the same methods 70 // as AddFunc/DeleteFunc but passing through only the second argument 71 // (which is the "new" object). 72 func PassNew(f func(interface{})) func(interface{}, interface{}) { 73 return func(first, second interface{}) { 74 f(second) 75 } 76 } 77 78 // HandleAll wraps the provided handler function into a cache.ResourceEventHandler 79 // that sends all events to the given handler. For Updates, only the new object 80 // is forwarded. 81 func HandleAll(h func(interface{})) cache.ResourceEventHandler { 82 return cache.ResourceEventHandlerFuncs{ 83 AddFunc: h, 84 UpdateFunc: PassNew(h), 85 DeleteFunc: h, 86 } 87 } 88 89 // Filter makes it simple to create FilterFunc's for use with 90 // cache.FilteringResourceEventHandler that filter based on the 91 // schema.GroupVersionKind of the controlling resources. 92 // 93 // Deprecated: Use FilterGroupVersionKind or FilterGroupKind instead 94 func Filter(gvk schema.GroupVersionKind) func(obj interface{}) bool { 95 return FilterGroupVersionKind(gvk) 96 } 97 98 // FilterGroupVersionKind makes it simple to create FilterFunc's for use with 99 // cache.FilteringResourceEventHandler that filter based on the 100 // schema.GroupVersionKind of the controlling resources. 101 // 102 // Deprecated: Use FilterControllerGVK instead. 103 func FilterGroupVersionKind(gvk schema.GroupVersionKind) func(obj interface{}) bool { 104 return FilterControllerGVK(gvk) 105 } 106 107 // FilterControllerGVK makes it simple to create FilterFunc's for use with 108 // cache.FilteringResourceEventHandler that filter based on the 109 // schema.GroupVersionKind of the controlling resources. 110 func FilterControllerGVK(gvk schema.GroupVersionKind) func(obj interface{}) bool { 111 return func(obj interface{}) bool { 112 object, ok := obj.(metav1.Object) 113 if !ok { 114 return false 115 } 116 117 owner := metav1.GetControllerOf(object) 118 return owner != nil && 119 owner.APIVersion == gvk.GroupVersion().String() && 120 owner.Kind == gvk.Kind 121 } 122 } 123 124 // FilterGroupKind makes it simple to create FilterFunc's for use with 125 // cache.FilteringResourceEventHandler that filter based on the 126 // schema.GroupKind of the controlling resources. 127 // 128 // Deprecated: Use FilterControllerGK instead 129 func FilterGroupKind(gk schema.GroupKind) func(obj interface{}) bool { 130 return FilterControllerGK(gk) 131 } 132 133 // FilterControllerGK makes it simple to create FilterFunc's for use with 134 // cache.FilteringResourceEventHandler that filter based on the 135 // schema.GroupKind of the controlling resources. 136 func FilterControllerGK(gk schema.GroupKind) func(obj interface{}) bool { 137 return func(obj interface{}) bool { 138 object, ok := obj.(metav1.Object) 139 if !ok { 140 return false 141 } 142 143 owner := metav1.GetControllerOf(object) 144 if owner == nil { 145 return false 146 } 147 148 ownerGV, err := schema.ParseGroupVersion(owner.APIVersion) 149 return err == nil && 150 ownerGV.Group == gk.Group && 151 owner.Kind == gk.Kind 152 } 153 } 154 155 // FilterController makes it simple to create FilterFunc's for use with 156 // cache.FilteringResourceEventHandler that filter based on the 157 // controlling resource. 158 func FilterController(r kmeta.OwnerRefable) func(obj interface{}) bool { 159 return FilterControllerGK(r.GetGroupVersionKind().GroupKind()) 160 } 161 162 // FilterWithName makes it simple to create FilterFunc's for use with 163 // cache.FilteringResourceEventHandler that filter based on a name. 164 func FilterWithName(name string) func(obj interface{}) bool { 165 return func(obj interface{}) bool { 166 if object, ok := obj.(metav1.Object); ok { 167 return name == object.GetName() 168 } 169 return false 170 } 171 } 172 173 // FilterWithNameAndNamespace makes it simple to create FilterFunc's for use with 174 // cache.FilteringResourceEventHandler that filter based on a namespace and a name. 175 func FilterWithNameAndNamespace(namespace, name string) func(obj interface{}) bool { 176 return func(obj interface{}) bool { 177 if object, ok := obj.(metav1.Object); ok { 178 return name == object.GetName() && 179 namespace == object.GetNamespace() 180 } 181 return false 182 } 183 } 184 185 // Impl is our core controller implementation. It handles queuing and feeding work 186 // from the queue to an implementation of Reconciler. 187 type Impl struct { 188 // Name is the unique name for this controller workqueue within this process. 189 // This is used for surfacing metrics, and per-controller leader election. 190 Name string 191 192 // Reconciler is the workhorse of this controller, it is fed the keys 193 // from the workqueue to process. Public for testing. 194 Reconciler Reconciler 195 196 // workQueue is a rate-limited two-lane work queue. 197 // This is used to queue work to be processed instead of performing it as 198 // soon as a change happens. This means we can ensure we only process a 199 // fixed amount of resources at a time, and makes it easy to ensure we are 200 // never processing the same item simultaneously in two different workers. 201 // The slow queue is used for global resync and other background processes 202 // which are not required to complete at the highest priority. 203 workQueue *twoLaneRateLimitingQueue 204 205 // Concurrency - The number of workers to use when processing the controller's workqueue. 206 Concurrency int 207 208 // Sugared logger is easier to use but is not as performant as the 209 // raw logger. In performance critical paths, call logger.Desugar() 210 // and use the returned raw logger instead. In addition to the 211 // performance benefits, raw logger also preserves type-safety at 212 // the expense of slightly greater verbosity. 213 logger *zap.SugaredLogger 214 215 // Tracker allows reconcilers to associate a reference with particular key, 216 // such that when the reference changes the key is queued for reconciliation. 217 Tracker tracker.Interface 218 } 219 220 // ControllerOptions encapsulates options for creating a new controller, 221 // including throttling and stats behavior. 222 type ControllerOptions struct { 223 WorkQueueName string 224 Logger *zap.SugaredLogger 225 RateLimiter workqueue.TypedRateLimiter[any] 226 Concurrency int 227 } 228 229 // NewContext instantiates an instance of our controller that will feed work to the 230 // provided Reconciler as it is enqueued. 231 func NewContext(ctx context.Context, r Reconciler, options ControllerOptions) *Impl { 232 if options.RateLimiter == nil { 233 options.RateLimiter = workqueue.DefaultTypedControllerRateLimiter[any]() 234 } 235 if options.Concurrency == 0 { 236 options.Concurrency = DefaultThreadsPerController 237 } 238 i := &Impl{ 239 Name: options.WorkQueueName, 240 Reconciler: r, 241 workQueue: newTwoLaneWorkQueue(options.WorkQueueName, options.RateLimiter), 242 logger: options.Logger, 243 Concurrency: options.Concurrency, 244 } 245 246 if t := GetTracker(ctx); t != nil { 247 i.Tracker = t 248 } else { 249 i.Tracker = tracker.New(i.EnqueueKey, GetTrackerLease(ctx)) 250 } 251 252 return i 253 } 254 255 // WorkQueue permits direct access to the work queue. 256 func (c *Impl) WorkQueue() workqueue.TypedRateLimitingInterface[any] { 257 return c.workQueue 258 } 259 260 // EnqueueAfter takes a resource, converts it into a namespace/name string, 261 // and passes it to EnqueueKey. 262 func (c *Impl) EnqueueAfter(obj interface{}, after time.Duration) { 263 object, err := kmeta.DeletionHandlingAccessor(obj) 264 if err != nil { 265 c.logger.Errorw("EnqueueAfter", zap.Error(err)) 266 return 267 } 268 c.EnqueueKeyAfter(types.NamespacedName{Namespace: object.GetNamespace(), Name: object.GetName()}, after) 269 } 270 271 // EnqueueSlowKey takes a resource, converts it into a namespace/name string, 272 // and enqueues that key in the slow lane. 273 func (c *Impl) EnqueueSlowKey(key types.NamespacedName) { 274 c.workQueue.AddSlow(key) 275 276 if logger := c.logger.Desugar(); logger.Core().Enabled(zapcore.DebugLevel) { 277 logger.Debug(fmt.Sprintf("Adding to the slow queue %s (depth(total/slow): %d/%d)", 278 safeKey(key), c.workQueue.Len(), c.workQueue.SlowLen()), 279 zap.String(logkey.Key, key.String())) 280 } 281 } 282 283 // EnqueueSlow extracts namespaced name from the object and enqueues it on the slow 284 // work queue. 285 func (c *Impl) EnqueueSlow(obj interface{}) { 286 object, err := kmeta.DeletionHandlingAccessor(obj) 287 if err != nil { 288 c.logger.Errorw("EnqueueSlow", zap.Error(err)) 289 return 290 } 291 key := types.NamespacedName{Namespace: object.GetNamespace(), Name: object.GetName()} 292 c.EnqueueSlowKey(key) 293 } 294 295 // Enqueue takes a resource, converts it into a namespace/name string, 296 // and passes it to EnqueueKey. 297 func (c *Impl) Enqueue(obj interface{}) { 298 object, err := kmeta.DeletionHandlingAccessor(obj) 299 if err != nil { 300 c.logger.Errorw("Enqueue", zap.Error(err)) 301 return 302 } 303 c.EnqueueKey(types.NamespacedName{Namespace: object.GetNamespace(), Name: object.GetName()}) 304 } 305 306 // EnqueueSentinel returns a Enqueue method which will always enqueue a 307 // predefined key instead of the object key. 308 func (c *Impl) EnqueueSentinel(k types.NamespacedName) func(interface{}) { 309 return func(interface{}) { 310 c.EnqueueKey(k) 311 } 312 } 313 314 // EnqueueControllerOf takes a resource, identifies its controller resource, 315 // converts it into a namespace/name string, and passes that to EnqueueKey. 316 func (c *Impl) EnqueueControllerOf(obj interface{}) { 317 object, err := kmeta.DeletionHandlingAccessor(obj) 318 if err != nil { 319 c.logger.Error(err) 320 return 321 } 322 323 // If we can determine the controller ref of this object, then 324 // add that object to our workqueue. 325 if owner := metav1.GetControllerOf(object); owner != nil { 326 c.EnqueueKey(types.NamespacedName{Namespace: object.GetNamespace(), Name: owner.Name}) 327 } 328 } 329 330 // EnqueueLabelOfNamespaceScopedResource returns with an Enqueue func that 331 // takes a resource, identifies its controller resource through given namespace 332 // and name labels, converts it into a namespace/name string, and passes that 333 // to EnqueueKey. The controller resource must be of namespace-scoped. 334 func (c *Impl) EnqueueLabelOfNamespaceScopedResource(namespaceLabel, nameLabel string) func(obj interface{}) { 335 return func(obj interface{}) { 336 object, err := kmeta.DeletionHandlingAccessor(obj) 337 if err != nil { 338 c.logger.Error(err) 339 return 340 } 341 342 labels := object.GetLabels() 343 controllerKey, ok := labels[nameLabel] 344 if !ok { 345 c.logger.Debugf("Object %s/%s does not have a referring name label %s", 346 object.GetNamespace(), object.GetName(), nameLabel) 347 return 348 } 349 350 if namespaceLabel != "" { 351 controllerNamespace, ok := labels[namespaceLabel] 352 if !ok { 353 c.logger.Debugf("Object %s/%s does not have a referring namespace label %s", 354 object.GetNamespace(), object.GetName(), namespaceLabel) 355 return 356 } 357 358 c.EnqueueKey(types.NamespacedName{Namespace: controllerNamespace, Name: controllerKey}) 359 return 360 } 361 362 // Pass through namespace of the object itself if no namespace label specified. 363 // This is for the scenario that object and the parent resource are of same namespace, 364 // e.g. to enqueue the revision of an endpoint. 365 c.EnqueueKey(types.NamespacedName{Namespace: object.GetNamespace(), Name: controllerKey}) 366 } 367 } 368 369 // EnqueueLabelOfClusterScopedResource returns with an Enqueue func 370 // that takes a resource, identifies its controller resource through 371 // given name label, and passes it to EnqueueKey. 372 // The controller resource must be of cluster-scoped. 373 func (c *Impl) EnqueueLabelOfClusterScopedResource(nameLabel string) func(obj interface{}) { 374 return func(obj interface{}) { 375 object, err := kmeta.DeletionHandlingAccessor(obj) 376 if err != nil { 377 c.logger.Error(err) 378 return 379 } 380 381 labels := object.GetLabels() 382 controllerKey, ok := labels[nameLabel] 383 if !ok { 384 c.logger.Debugf("Object %s/%s does not have a referring name label %s", 385 object.GetNamespace(), object.GetName(), nameLabel) 386 return 387 } 388 389 c.EnqueueKey(types.NamespacedName{Namespace: "", Name: controllerKey}) 390 } 391 } 392 393 // EnqueueNamespaceOf takes a resource, and enqueues the Namespace to which it belongs. 394 func (c *Impl) EnqueueNamespaceOf(obj interface{}) { 395 object, err := kmeta.DeletionHandlingAccessor(obj) 396 if err != nil { 397 c.logger.Errorw("EnqueueNamespaceOf", zap.Error(err)) 398 return 399 } 400 c.EnqueueKey(types.NamespacedName{Name: object.GetNamespace()}) 401 } 402 403 // EnqueueKey takes a namespace/name string and puts it onto the work queue. 404 func (c *Impl) EnqueueKey(key types.NamespacedName) { 405 c.workQueue.Add(key) 406 407 if logger := c.logger.Desugar(); logger.Core().Enabled(zapcore.DebugLevel) { 408 logger.Debug(fmt.Sprintf("Adding to queue %s (depth: %d)", safeKey(key), c.workQueue.Len()), 409 zap.String(logkey.Key, key.String())) 410 } 411 } 412 413 // MaybeEnqueueBucketKey takes a Bucket and namespace/name string and puts it onto 414 // the slow work queue. 415 func (c *Impl) MaybeEnqueueBucketKey(bkt reconciler.Bucket, key types.NamespacedName) { 416 if bkt.Has(key) { 417 c.EnqueueSlowKey(key) 418 } 419 } 420 421 // EnqueueKeyAfter takes a namespace/name string and schedules its execution in 422 // the work queue after given delay. 423 func (c *Impl) EnqueueKeyAfter(key types.NamespacedName, delay time.Duration) { 424 c.workQueue.AddAfter(key, delay) 425 426 if logger := c.logger.Desugar(); logger.Core().Enabled(zapcore.DebugLevel) { 427 logger.Debug(fmt.Sprintf("Adding to queue %s (delay: %v, depth: %d)", safeKey(key), delay, c.workQueue.Len()), 428 zap.String(logkey.Key, key.String())) 429 } 430 } 431 432 // Run runs the controller with it's configured Concurrency 433 func (c *Impl) Run(ctx context.Context) error { 434 return c.RunContext(ctx, c.Concurrency) 435 } 436 437 // RunContext starts the controller's worker threads, the number of which is threadiness. 438 // If the context has been decorated for LeaderElection, then an elector is built and run. 439 // It then blocks until the context is cancelled, at which point it shuts down its 440 // internal work queue and waits for workers to finish processing their current 441 // work items. 442 func (c *Impl) RunContext(ctx context.Context, threadiness int) error { 443 sg := sync.WaitGroup{} 444 defer func() { 445 c.workQueue.ShutDown() 446 for c.workQueue.Len() > 0 { 447 time.Sleep(time.Millisecond * 100) 448 } 449 sg.Wait() 450 runtime.HandleCrash() 451 }() 452 453 if la, ok := c.Reconciler.(reconciler.LeaderAware); ok { 454 // Build and execute an elector. 455 le, err := kle.BuildElector(ctx, la, c.Name, c.MaybeEnqueueBucketKey) 456 if err != nil { 457 return err 458 } 459 if ib, ok := le.(kle.ElectorWithInitialBuckets); ok { 460 for _, b := range ib.InitialBuckets() { 461 // No need to provide an enq function since the controller 462 // is not processing items 463 la.Promote(b, nil) 464 } 465 } 466 sg.Add(1) 467 go func() { 468 defer sg.Done() 469 le.Run(ctx) 470 }() 471 } 472 473 // Launch workers to process resources that get enqueued to our workqueue. 474 c.logger.Infow("Starting controller and workers", zap.Int("threadiness", threadiness)) 475 for range threadiness { 476 sg.Add(1) 477 go func() { 478 defer sg.Done() 479 for c.processNextWorkItem() { 480 } 481 }() 482 } 483 484 c.logger.Info("Started workers") 485 <-ctx.Done() 486 c.logger.Info("Shutting down workers") 487 488 return nil 489 } 490 491 // processNextWorkItem will read a single work item off the workqueue and 492 // attempt to process it, by calling Reconcile on our Reconciler. 493 func (c *Impl) processNextWorkItem() bool { 494 obj, shutdown := c.workQueue.Get() 495 if shutdown { 496 return false 497 } 498 key := obj.(types.NamespacedName) 499 keyStr := safeKey(key) 500 501 c.logger.Debugf("Processing from queue %s (depth: %d)", safeKey(key), c.workQueue.Len()) 502 503 startTime := time.Now() 504 505 var err error 506 defer func() { 507 // We call Done here so the workqueue knows we have finished 508 // processing this item. We also must remember to call Forget if 509 // reconcile succeeds. If a transient error occurs, we do not call 510 // Forget and put the item back to the queue with an increased 511 // delay. 512 c.workQueue.Done(key) 513 }() 514 515 // Embed the key into the logger and attach that to the context we pass 516 // to the Reconciler. 517 logger := c.logger.With(zap.String(logkey.TraceID, uuid.NewString()), zap.String(logkey.Key, keyStr)) 518 ctx := logging.WithLogger(context.Background(), logger) 519 520 // Run Reconcile, passing it the namespace/name string of the 521 // resource to be synced. 522 if err = c.Reconciler.Reconcile(ctx, keyStr); err != nil { 523 c.handleErr(logger, err, key, startTime) 524 return true 525 } 526 527 // Finally, if no error occurs we Forget this item so it does not 528 // have any delay when another change happens. 529 c.workQueue.Forget(key) 530 logger.Infow("Reconcile succeeded", zap.Duration("duration", time.Since(startTime))) 531 532 return true 533 } 534 535 func (c *Impl) handleErr(logger *zap.SugaredLogger, err error, key types.NamespacedName, startTime time.Time) { 536 // Check if we should skip this key or if the queue is shutting down. 537 // We check shutdown here since controller Run might have exited by now 538 // (since while this item was being processed, queue.Len==0). 539 if IsSkipKey(err) || c.workQueue.ShuttingDown() { 540 c.workQueue.Forget(key) 541 return 542 } 543 544 if ok, delay := IsRequeueKey(err); ok { 545 c.workQueue.AddAfter(key, delay) 546 logger.Debugf("Requeuing key %s (by request) after %v (depth: %d)", safeKey(key), delay, c.workQueue.Len()) 547 return 548 } 549 550 // Conflict errors are expected, requeue to retry 551 if apierrors.IsConflict(err) { 552 logger.Debugw("Reconcile conflict", zap.Duration("duration", time.Since(startTime))) 553 c.workQueue.AddRateLimited(key) 554 return 555 } 556 557 logger.Errorw("Reconcile error", zap.Duration("duration", time.Since(startTime)), zap.Error(err)) 558 559 // Re-queue the key if it's a transient error. 560 if !IsPermanentError(err) { 561 c.workQueue.AddRateLimited(key) 562 logger.Debugf("Requeuing key %s due to non-permanent error (depth: %d)", safeKey(key), c.workQueue.Len()) 563 return 564 } 565 566 c.workQueue.Forget(key) 567 } 568 569 // GlobalResync enqueues into the slow lane all objects from the passed SharedInformer 570 func (c *Impl) GlobalResync(si cache.SharedInformer) { 571 alwaysTrue := func(interface{}) bool { return true } 572 c.FilteredGlobalResync(alwaysTrue, si) 573 } 574 575 // FilteredGlobalResync enqueues all objects from the 576 // SharedInformer that pass the filter function in to the slow queue. 577 func (c *Impl) FilteredGlobalResync(f func(interface{}) bool, si cache.SharedInformer) { 578 if c.workQueue.ShuttingDown() { 579 return 580 } 581 list := si.GetStore().List() 582 for _, obj := range list { 583 if f(obj) { 584 c.EnqueueSlow(obj) 585 } 586 } 587 } 588 589 // NewSkipKey returns a new instance of skipKeyError. 590 // Users can return this type of error to indicate that the key was skipped. 591 func NewSkipKey(key string) error { 592 return skipKeyError{key: key} 593 } 594 595 // skipKeyError is an error that indicates a key was skipped. 596 // We should not re-queue keys when it returns this error from Reconcile. 597 type skipKeyError struct { 598 key string 599 } 600 601 var _ error = skipKeyError{} 602 603 // Error implements the Error() interface of error. 604 func (err skipKeyError) Error() string { 605 return fmt.Sprintf("skipped key: %q", err.key) 606 } 607 608 // IsSkipKey returns true if the given error is a skipKeyError. 609 func IsSkipKey(err error) bool { 610 return errors.Is(err, skipKeyError{}) 611 } 612 613 // Is implements the Is() interface of error. It returns whether the target 614 // error can be treated as equivalent to a permanentError. 615 func (skipKeyError) Is(target error) bool { 616 _, ok := target.(skipKeyError) 617 return ok 618 } 619 620 // NewPermanentError returns a new instance of permanentError. 621 // Users can wrap an error as permanentError with this in reconcile 622 // when they do not expect the key to get re-queued. 623 func NewPermanentError(err error) error { 624 return permanentError{e: err} 625 } 626 627 // permanentError is an error that is considered not transient. 628 // We should not re-queue keys when it returns with thus error in reconcile. 629 type permanentError struct { 630 e error 631 } 632 633 // IsPermanentError returns true if the given error is a permanentError or 634 // wraps a permanentError. 635 func IsPermanentError(err error) bool { 636 return errors.Is(err, permanentError{}) 637 } 638 639 // Is implements the Is() interface of error. It returns whether the target 640 // error can be treated as equivalent to a permanentError. 641 func (permanentError) Is(target error) bool { 642 _, ok := target.(permanentError) 643 return ok 644 } 645 646 var _ error = permanentError{} 647 648 // Error implements the Error() interface of error. 649 func (err permanentError) Error() string { 650 if err.e == nil { 651 return "" 652 } 653 654 return err.e.Error() 655 } 656 657 // Unwrap implements the Unwrap() interface of error. It returns the error 658 // wrapped inside permanentError. 659 func (err permanentError) Unwrap() error { 660 return err.e 661 } 662 663 // NewRequeueImmediately returns a new instance of requeueKeyError. 664 // Users can return this type of error to immediately requeue a key. 665 func NewRequeueImmediately() error { 666 return requeueKeyError{} 667 } 668 669 // NewRequeueAfter returns a new instance of requeueKeyError. 670 // Users can return this type of error to requeue a key after a delay. 671 func NewRequeueAfter(dur time.Duration) error { 672 return requeueKeyError{duration: dur} 673 } 674 675 // requeueKeyError is an error that indicates the reconciler wants to reprocess 676 // the key after a particular duration (possibly zero). 677 // We should re-queue keys with the desired duration when this is returned by Reconcile. 678 type requeueKeyError struct { 679 duration time.Duration 680 } 681 682 var _ error = requeueKeyError{} 683 684 // Error implements the Error() interface of error. 685 func (err requeueKeyError) Error() string { 686 return fmt.Sprintf("requeue after: %s", err.duration) 687 } 688 689 // IsRequeueKey returns true if the given error is a requeueKeyError. 690 func IsRequeueKey(err error) (bool, time.Duration) { 691 rqe := requeueKeyError{} 692 if errors.As(err, &rqe) { 693 return true, rqe.duration 694 } 695 return false, 0 696 } 697 698 // Is implements the Is() interface of error. It returns whether the target 699 // error can be treated as equivalent to a requeueKeyError. 700 func (requeueKeyError) Is(target error) bool { 701 _, ok := target.(requeueKeyError) 702 return ok 703 } 704 705 // Informer is the group of methods that a type must implement to be passed to 706 // StartInformers. 707 type Informer interface { 708 Run(<-chan struct{}) 709 HasSynced() bool 710 } 711 712 // StartInformers kicks off all of the passed informers and then waits for all 713 // of them to synchronize. 714 func StartInformers(stopCh <-chan struct{}, informers ...Informer) error { 715 for _, informer := range informers { 716 go informer.Run(stopCh) 717 } 718 719 for i, informer := range informers { 720 if ok := cache.WaitForCacheSync(stopCh, informer.HasSynced); !ok { 721 return fmt.Errorf("failed to wait for cache at index %d to sync", i) 722 } 723 } 724 return nil 725 } 726 727 // RunInformers kicks off all of the passed informers and then waits for all of 728 // them to synchronize. Returned function will wait for all informers to finish. 729 func RunInformers(stopCh <-chan struct{}, informers ...Informer) (func(), error) { 730 var wg sync.WaitGroup 731 wg.Add(len(informers)) 732 for _, informer := range informers { 733 go func() { 734 defer wg.Done() 735 informer.Run(stopCh) 736 }() 737 } 738 739 for i, informer := range informers { 740 if ok := WaitForCacheSyncQuick(stopCh, informer.HasSynced); !ok { 741 return wg.Wait, fmt.Errorf("failed to wait for cache at index %d to sync", i) 742 } 743 } 744 return wg.Wait, nil 745 } 746 747 // WaitForCacheSyncQuick is the same as cache.WaitForCacheSync but with a much reduced 748 // check-rate for the sync period. 749 func WaitForCacheSyncQuick(stopCh <-chan struct{}, cacheSyncs ...cache.InformerSynced) bool { 750 err := wait.PollUntilContextCancel(wait.ContextForChannel(stopCh), time.Millisecond, true, 751 func(context.Context) (bool, error) { 752 for _, syncFunc := range cacheSyncs { 753 if !syncFunc() { 754 return false, nil 755 } 756 } 757 return true, nil 758 }, 759 ) 760 return err == nil 761 } 762 763 // StartAll kicks off all of the passed controllers with DefaultThreadsPerController. 764 func StartAll(ctx context.Context, controllers ...*Impl) error { 765 eg, egCtx := errgroup.WithContext(ctx) 766 767 // Start all of the controllers. 768 for _, controller := range controllers { 769 c := controller 770 eg.Go(func() error { 771 return c.Run(egCtx) 772 }) 773 } 774 return eg.Wait() 775 } 776 777 // This is attached to contexts passed to controller constructors to associate 778 // a resync period. 779 type resyncPeriodKey struct{} 780 781 // WithResyncPeriod associates the given resync period with the given context in 782 // the context that is returned. 783 func WithResyncPeriod(ctx context.Context, resync time.Duration) context.Context { 784 return context.WithValue(ctx, resyncPeriodKey{}, resync) 785 } 786 787 // GetResyncPeriod returns the resync period associated with the given context. 788 // When none is specified a default resync period is used. 789 func GetResyncPeriod(ctx context.Context) time.Duration { 790 rp := ctx.Value(resyncPeriodKey{}) 791 if rp == nil { 792 return DefaultResyncPeriod 793 } 794 return rp.(time.Duration) 795 } 796 797 // GetTrackerLease fetches the tracker lease from the controller context. 798 func GetTrackerLease(ctx context.Context) time.Duration { 799 return 3 * GetResyncPeriod(ctx) 800 } 801 802 // trackerKey is used to associate tracker.Interface with contexts. 803 type trackerKey struct{} 804 805 // WithTracker attaches the given tracker.Interface to the provided context 806 // in the returned context. 807 func WithTracker(ctx context.Context, t tracker.Interface) context.Context { 808 return context.WithValue(ctx, trackerKey{}, t) 809 } 810 811 // GetTracker attempts to look up the tracker.Interface on a given context. 812 // It may return null if none is found. 813 func GetTracker(ctx context.Context) tracker.Interface { 814 untyped := ctx.Value(trackerKey{}) 815 if untyped == nil { 816 return nil 817 } 818 return untyped.(tracker.Interface) 819 } 820 821 // erKey is used to associate record.EventRecorders with contexts. 822 type erKey struct{} 823 824 // WithEventRecorder attaches the given record.EventRecorder to the provided context 825 // in the returned context. 826 func WithEventRecorder(ctx context.Context, er record.EventRecorder) context.Context { 827 return context.WithValue(ctx, erKey{}, er) 828 } 829 830 // GetEventRecorder attempts to look up the record.EventRecorder on a given context. 831 // It may return null if none is found. 832 func GetEventRecorder(ctx context.Context) record.EventRecorder { 833 untyped := ctx.Value(erKey{}) 834 if untyped == nil { 835 return nil 836 } 837 return untyped.(record.EventRecorder) 838 } 839 840 func safeKey(key types.NamespacedName) string { 841 if key.Namespace == "" { 842 return key.Name 843 } 844 return key.String() 845 }