sigs.k8s.io/controller-runtime@v0.18.2/pkg/controller/controller.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 controller 18 19 import ( 20 "context" 21 "fmt" 22 "time" 23 24 "github.com/go-logr/logr" 25 "k8s.io/client-go/util/workqueue" 26 "k8s.io/klog/v2" 27 28 "sigs.k8s.io/controller-runtime/pkg/internal/controller" 29 "sigs.k8s.io/controller-runtime/pkg/manager" 30 "sigs.k8s.io/controller-runtime/pkg/ratelimiter" 31 "sigs.k8s.io/controller-runtime/pkg/reconcile" 32 "sigs.k8s.io/controller-runtime/pkg/source" 33 ) 34 35 // Options are the arguments for creating a new Controller. 36 type Options struct { 37 // MaxConcurrentReconciles is the maximum number of concurrent Reconciles which can be run. Defaults to 1. 38 MaxConcurrentReconciles int 39 40 // CacheSyncTimeout refers to the time limit set to wait for syncing caches. 41 // Defaults to 2 minutes if not set. 42 CacheSyncTimeout time.Duration 43 44 // RecoverPanic indicates whether the panic caused by reconcile should be recovered. 45 // Defaults to the Controller.RecoverPanic setting from the Manager if unset. 46 RecoverPanic *bool 47 48 // NeedLeaderElection indicates whether the controller needs to use leader election. 49 // Defaults to true, which means the controller will use leader election. 50 NeedLeaderElection *bool 51 52 // Reconciler reconciles an object 53 Reconciler reconcile.Reconciler 54 55 // RateLimiter is used to limit how frequently requests may be queued. 56 // Defaults to MaxOfRateLimiter which has both overall and per-item rate limiting. 57 // The overall is a token bucket and the per-item is exponential. 58 RateLimiter ratelimiter.RateLimiter 59 60 // NewQueue constructs the queue for this controller once the controller is ready to start. 61 // With NewQueue a custom queue implementation can be used, e.g. a priority queue to prioritize with which 62 // priority/order objects are reconciled (e.g. to reconcile objects with changes first). 63 // This is a func because the standard Kubernetes work queues start themselves immediately, which 64 // leads to goroutine leaks if something calls controller.New repeatedly. 65 // The NewQueue func gets the controller name and the RateLimiter option (defaulted if necessary) passed in. 66 // NewQueue defaults to NewRateLimitingQueueWithConfig. 67 // 68 // NOTE: LOW LEVEL PRIMITIVE! 69 // Only use a custom NewQueue if you know what you are doing. 70 NewQueue func(controllerName string, rateLimiter ratelimiter.RateLimiter) workqueue.RateLimitingInterface 71 72 // LogConstructor is used to construct a logger used for this controller and passed 73 // to each reconciliation via the context field. 74 LogConstructor func(request *reconcile.Request) logr.Logger 75 } 76 77 // Controller implements a Kubernetes API. A Controller manages a work queue fed reconcile.Requests 78 // from source.Sources. Work is performed through the reconcile.Reconciler for each enqueued item. 79 // Work typically is reads and writes Kubernetes objects to make the system state match the state specified 80 // in the object Spec. 81 type Controller interface { 82 // Reconciler is called to reconcile an object by Namespace/Name 83 reconcile.Reconciler 84 85 // Watch watches the provided Source. 86 Watch(src source.Source) error 87 88 // Start starts the controller. Start blocks until the context is closed or a 89 // controller has an error starting. 90 Start(ctx context.Context) error 91 92 // GetLogger returns this controller logger prefilled with basic information. 93 GetLogger() logr.Logger 94 } 95 96 // New returns a new Controller registered with the Manager. The Manager will ensure that shared Caches have 97 // been synced before the Controller is Started. 98 func New(name string, mgr manager.Manager, options Options) (Controller, error) { 99 c, err := NewUnmanaged(name, mgr, options) 100 if err != nil { 101 return nil, err 102 } 103 104 // Add the controller as a Manager components 105 return c, mgr.Add(c) 106 } 107 108 // NewUnmanaged returns a new controller without adding it to the manager. The 109 // caller is responsible for starting the returned controller. 110 func NewUnmanaged(name string, mgr manager.Manager, options Options) (Controller, error) { 111 if options.Reconciler == nil { 112 return nil, fmt.Errorf("must specify Reconciler") 113 } 114 115 if len(name) == 0 { 116 return nil, fmt.Errorf("must specify Name for Controller") 117 } 118 119 if options.LogConstructor == nil { 120 log := mgr.GetLogger().WithValues( 121 "controller", name, 122 ) 123 options.LogConstructor = func(req *reconcile.Request) logr.Logger { 124 log := log 125 if req != nil { 126 log = log.WithValues( 127 "object", klog.KRef(req.Namespace, req.Name), 128 "namespace", req.Namespace, "name", req.Name, 129 ) 130 } 131 return log 132 } 133 } 134 135 if options.MaxConcurrentReconciles <= 0 { 136 if mgr.GetControllerOptions().MaxConcurrentReconciles > 0 { 137 options.MaxConcurrentReconciles = mgr.GetControllerOptions().MaxConcurrentReconciles 138 } else { 139 options.MaxConcurrentReconciles = 1 140 } 141 } 142 143 if options.CacheSyncTimeout == 0 { 144 if mgr.GetControllerOptions().CacheSyncTimeout != 0 { 145 options.CacheSyncTimeout = mgr.GetControllerOptions().CacheSyncTimeout 146 } else { 147 options.CacheSyncTimeout = 2 * time.Minute 148 } 149 } 150 151 if options.RateLimiter == nil { 152 options.RateLimiter = workqueue.DefaultControllerRateLimiter() 153 } 154 155 if options.NewQueue == nil { 156 options.NewQueue = func(controllerName string, rateLimiter ratelimiter.RateLimiter) workqueue.RateLimitingInterface { 157 return workqueue.NewRateLimitingQueueWithConfig(rateLimiter, workqueue.RateLimitingQueueConfig{ 158 Name: controllerName, 159 }) 160 } 161 } 162 163 if options.RecoverPanic == nil { 164 options.RecoverPanic = mgr.GetControllerOptions().RecoverPanic 165 } 166 167 if options.NeedLeaderElection == nil { 168 options.NeedLeaderElection = mgr.GetControllerOptions().NeedLeaderElection 169 } 170 171 // Create controller with dependencies set 172 return &controller.Controller{ 173 Do: options.Reconciler, 174 RateLimiter: options.RateLimiter, 175 NewQueue: options.NewQueue, 176 MaxConcurrentReconciles: options.MaxConcurrentReconciles, 177 CacheSyncTimeout: options.CacheSyncTimeout, 178 Name: name, 179 LogConstructor: options.LogConstructor, 180 RecoverPanic: options.RecoverPanic, 181 LeaderElected: options.NeedLeaderElection, 182 }, nil 183 } 184 185 // ReconcileIDFromContext gets the reconcileID from the current context. 186 var ReconcileIDFromContext = controller.ReconcileIDFromContext