controller.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package cache
  14. import (
  15. "errors"
  16. "sync"
  17. "time"
  18. "k8s.io/apimachinery/pkg/runtime"
  19. utilruntime "k8s.io/apimachinery/pkg/util/runtime"
  20. "k8s.io/apimachinery/pkg/util/wait"
  21. "k8s.io/utils/clock"
  22. )
  23. // This file implements a low-level controller that is used in
  24. // sharedIndexInformer, which is an implementation of
  25. // SharedIndexInformer. Such informers, in turn, are key components
  26. // in the high level controllers that form the backbone of the
  27. // Kubernetes control plane. Look at those for examples, or the
  28. // example in
  29. // https://github.com/kubernetes/client-go/tree/master/examples/workqueue
  30. // .
  31. // Config contains all the settings for one of these low-level controllers.
  32. type Config struct {
  33. // The queue for your objects - has to be a DeltaFIFO due to
  34. // assumptions in the implementation. Your Process() function
  35. // should accept the output of this Queue's Pop() method.
  36. Queue
  37. // Something that can list and watch your objects.
  38. ListerWatcher
  39. // Something that can process a popped Deltas.
  40. Process ProcessFunc
  41. // ObjectType is an example object of the type this controller is
  42. // expected to handle.
  43. ObjectType runtime.Object
  44. // ObjectDescription is the description to use when logging type-specific information about this controller.
  45. ObjectDescription string
  46. // FullResyncPeriod is the period at which ShouldResync is considered.
  47. FullResyncPeriod time.Duration
  48. // ShouldResync is periodically used by the reflector to determine
  49. // whether to Resync the Queue. If ShouldResync is `nil` or
  50. // returns true, it means the reflector should proceed with the
  51. // resync.
  52. ShouldResync ShouldResyncFunc
  53. // If true, when Process() returns an error, re-enqueue the object.
  54. // TODO: add interface to let you inject a delay/backoff or drop
  55. // the object completely if desired. Pass the object in
  56. // question to this interface as a parameter. This is probably moot
  57. // now that this functionality appears at a higher level.
  58. RetryOnError bool
  59. // Called whenever the ListAndWatch drops the connection with an error.
  60. WatchErrorHandler WatchErrorHandler
  61. // WatchListPageSize is the requested chunk size of initial and relist watch lists.
  62. WatchListPageSize int64
  63. }
  64. // ShouldResyncFunc is a type of function that indicates if a reflector should perform a
  65. // resync or not. It can be used by a shared informer to support multiple event handlers with custom
  66. // resync periods.
  67. type ShouldResyncFunc func() bool
  68. // ProcessFunc processes a single object.
  69. type ProcessFunc func(obj interface{}, isInInitialList bool) error
  70. // `*controller` implements Controller
  71. type controller struct {
  72. config Config
  73. reflector *Reflector
  74. reflectorMutex sync.RWMutex
  75. clock clock.Clock
  76. }
  77. // Controller is a low-level controller that is parameterized by a
  78. // Config and used in sharedIndexInformer.
  79. type Controller interface {
  80. // Run does two things. One is to construct and run a Reflector
  81. // to pump objects/notifications from the Config's ListerWatcher
  82. // to the Config's Queue and possibly invoke the occasional Resync
  83. // on that Queue. The other is to repeatedly Pop from the Queue
  84. // and process with the Config's ProcessFunc. Both of these
  85. // continue until `stopCh` is closed.
  86. Run(stopCh <-chan struct{})
  87. // HasSynced delegates to the Config's Queue
  88. HasSynced() bool
  89. // LastSyncResourceVersion delegates to the Reflector when there
  90. // is one, otherwise returns the empty string
  91. LastSyncResourceVersion() string
  92. }
  93. // New makes a new Controller from the given Config.
  94. func New(c *Config) Controller {
  95. ctlr := &controller{
  96. config: *c,
  97. clock: &clock.RealClock{},
  98. }
  99. return ctlr
  100. }
  101. // Run begins processing items, and will continue until a value is sent down stopCh or it is closed.
  102. // It's an error to call Run more than once.
  103. // Run blocks; call via go.
  104. func (c *controller) Run(stopCh <-chan struct{}) {
  105. defer utilruntime.HandleCrash()
  106. go func() {
  107. <-stopCh
  108. c.config.Queue.Close()
  109. }()
  110. r := NewReflectorWithOptions(
  111. c.config.ListerWatcher,
  112. c.config.ObjectType,
  113. c.config.Queue,
  114. ReflectorOptions{
  115. ResyncPeriod: c.config.FullResyncPeriod,
  116. TypeDescription: c.config.ObjectDescription,
  117. Clock: c.clock,
  118. },
  119. )
  120. r.ShouldResync = c.config.ShouldResync
  121. r.WatchListPageSize = c.config.WatchListPageSize
  122. if c.config.WatchErrorHandler != nil {
  123. r.watchErrorHandler = c.config.WatchErrorHandler
  124. }
  125. c.reflectorMutex.Lock()
  126. c.reflector = r
  127. c.reflectorMutex.Unlock()
  128. var wg wait.Group
  129. wg.StartWithChannel(stopCh, r.Run)
  130. wait.Until(c.processLoop, time.Second, stopCh)
  131. wg.Wait()
  132. }
  133. // Returns true once this controller has completed an initial resource listing
  134. func (c *controller) HasSynced() bool {
  135. return c.config.Queue.HasSynced()
  136. }
  137. func (c *controller) LastSyncResourceVersion() string {
  138. c.reflectorMutex.RLock()
  139. defer c.reflectorMutex.RUnlock()
  140. if c.reflector == nil {
  141. return ""
  142. }
  143. return c.reflector.LastSyncResourceVersion()
  144. }
  145. // processLoop drains the work queue.
  146. // TODO: Consider doing the processing in parallel. This will require a little thought
  147. // to make sure that we don't end up processing the same object multiple times
  148. // concurrently.
  149. //
  150. // TODO: Plumb through the stopCh here (and down to the queue) so that this can
  151. // actually exit when the controller is stopped. Or just give up on this stuff
  152. // ever being stoppable. Converting this whole package to use Context would
  153. // also be helpful.
  154. func (c *controller) processLoop() {
  155. for {
  156. obj, err := c.config.Queue.Pop(PopProcessFunc(c.config.Process))
  157. if err != nil {
  158. if err == ErrFIFOClosed {
  159. return
  160. }
  161. if c.config.RetryOnError {
  162. // This is the safe way to re-enqueue.
  163. c.config.Queue.AddIfNotPresent(obj)
  164. }
  165. }
  166. }
  167. }
  168. // ResourceEventHandler can handle notifications for events that
  169. // happen to a resource. The events are informational only, so you
  170. // can't return an error. The handlers MUST NOT modify the objects
  171. // received; this concerns not only the top level of structure but all
  172. // the data structures reachable from it.
  173. // - OnAdd is called when an object is added.
  174. // - OnUpdate is called when an object is modified. Note that oldObj is the
  175. // last known state of the object-- it is possible that several changes
  176. // were combined together, so you can't use this to see every single
  177. // change. OnUpdate is also called when a re-list happens, and it will
  178. // get called even if nothing changed. This is useful for periodically
  179. // evaluating or syncing something.
  180. // - OnDelete will get the final state of the item if it is known, otherwise
  181. // it will get an object of type DeletedFinalStateUnknown. This can
  182. // happen if the watch is closed and misses the delete event and we don't
  183. // notice the deletion until the subsequent re-list.
  184. type ResourceEventHandler interface {
  185. OnAdd(obj interface{}, isInInitialList bool)
  186. OnUpdate(oldObj, newObj interface{})
  187. OnDelete(obj interface{})
  188. }
  189. // ResourceEventHandlerFuncs is an adaptor to let you easily specify as many or
  190. // as few of the notification functions as you want while still implementing
  191. // ResourceEventHandler. This adapter does not remove the prohibition against
  192. // modifying the objects.
  193. //
  194. // See ResourceEventHandlerDetailedFuncs if your use needs to propagate
  195. // HasSynced.
  196. type ResourceEventHandlerFuncs struct {
  197. AddFunc func(obj interface{})
  198. UpdateFunc func(oldObj, newObj interface{})
  199. DeleteFunc func(obj interface{})
  200. }
  201. // OnAdd calls AddFunc if it's not nil.
  202. func (r ResourceEventHandlerFuncs) OnAdd(obj interface{}, isInInitialList bool) {
  203. if r.AddFunc != nil {
  204. r.AddFunc(obj)
  205. }
  206. }
  207. // OnUpdate calls UpdateFunc if it's not nil.
  208. func (r ResourceEventHandlerFuncs) OnUpdate(oldObj, newObj interface{}) {
  209. if r.UpdateFunc != nil {
  210. r.UpdateFunc(oldObj, newObj)
  211. }
  212. }
  213. // OnDelete calls DeleteFunc if it's not nil.
  214. func (r ResourceEventHandlerFuncs) OnDelete(obj interface{}) {
  215. if r.DeleteFunc != nil {
  216. r.DeleteFunc(obj)
  217. }
  218. }
  219. // ResourceEventHandlerDetailedFuncs is exactly like ResourceEventHandlerFuncs
  220. // except its AddFunc accepts the isInInitialList parameter, for propagating
  221. // HasSynced.
  222. type ResourceEventHandlerDetailedFuncs struct {
  223. AddFunc func(obj interface{}, isInInitialList bool)
  224. UpdateFunc func(oldObj, newObj interface{})
  225. DeleteFunc func(obj interface{})
  226. }
  227. // OnAdd calls AddFunc if it's not nil.
  228. func (r ResourceEventHandlerDetailedFuncs) OnAdd(obj interface{}, isInInitialList bool) {
  229. if r.AddFunc != nil {
  230. r.AddFunc(obj, isInInitialList)
  231. }
  232. }
  233. // OnUpdate calls UpdateFunc if it's not nil.
  234. func (r ResourceEventHandlerDetailedFuncs) OnUpdate(oldObj, newObj interface{}) {
  235. if r.UpdateFunc != nil {
  236. r.UpdateFunc(oldObj, newObj)
  237. }
  238. }
  239. // OnDelete calls DeleteFunc if it's not nil.
  240. func (r ResourceEventHandlerDetailedFuncs) OnDelete(obj interface{}) {
  241. if r.DeleteFunc != nil {
  242. r.DeleteFunc(obj)
  243. }
  244. }
  245. // FilteringResourceEventHandler applies the provided filter to all events coming
  246. // in, ensuring the appropriate nested handler method is invoked. An object
  247. // that starts passing the filter after an update is considered an add, and an
  248. // object that stops passing the filter after an update is considered a delete.
  249. // Like the handlers, the filter MUST NOT modify the objects it is given.
  250. type FilteringResourceEventHandler struct {
  251. FilterFunc func(obj interface{}) bool
  252. Handler ResourceEventHandler
  253. }
  254. // OnAdd calls the nested handler only if the filter succeeds
  255. func (r FilteringResourceEventHandler) OnAdd(obj interface{}, isInInitialList bool) {
  256. if !r.FilterFunc(obj) {
  257. return
  258. }
  259. r.Handler.OnAdd(obj, isInInitialList)
  260. }
  261. // OnUpdate ensures the proper handler is called depending on whether the filter matches
  262. func (r FilteringResourceEventHandler) OnUpdate(oldObj, newObj interface{}) {
  263. newer := r.FilterFunc(newObj)
  264. older := r.FilterFunc(oldObj)
  265. switch {
  266. case newer && older:
  267. r.Handler.OnUpdate(oldObj, newObj)
  268. case newer && !older:
  269. r.Handler.OnAdd(newObj, false)
  270. case !newer && older:
  271. r.Handler.OnDelete(oldObj)
  272. default:
  273. // do nothing
  274. }
  275. }
  276. // OnDelete calls the nested handler only if the filter succeeds
  277. func (r FilteringResourceEventHandler) OnDelete(obj interface{}) {
  278. if !r.FilterFunc(obj) {
  279. return
  280. }
  281. r.Handler.OnDelete(obj)
  282. }
  283. // DeletionHandlingMetaNamespaceKeyFunc checks for
  284. // DeletedFinalStateUnknown objects before calling
  285. // MetaNamespaceKeyFunc.
  286. func DeletionHandlingMetaNamespaceKeyFunc(obj interface{}) (string, error) {
  287. if d, ok := obj.(DeletedFinalStateUnknown); ok {
  288. return d.Key, nil
  289. }
  290. return MetaNamespaceKeyFunc(obj)
  291. }
  292. // NewInformer returns a Store and a controller for populating the store
  293. // while also providing event notifications. You should only used the returned
  294. // Store for Get/List operations; Add/Modify/Deletes will cause the event
  295. // notifications to be faulty.
  296. //
  297. // Parameters:
  298. // - lw is list and watch functions for the source of the resource you want to
  299. // be informed of.
  300. // - objType is an object of the type that you expect to receive.
  301. // - resyncPeriod: if non-zero, will re-list this often (you will get OnUpdate
  302. // calls, even if nothing changed). Otherwise, re-list will be delayed as
  303. // long as possible (until the upstream source closes the watch or times out,
  304. // or you stop the controller).
  305. // - h is the object you want notifications sent to.
  306. func NewInformer(
  307. lw ListerWatcher,
  308. objType runtime.Object,
  309. resyncPeriod time.Duration,
  310. h ResourceEventHandler,
  311. ) (Store, Controller) {
  312. // This will hold the client state, as we know it.
  313. clientState := NewStore(DeletionHandlingMetaNamespaceKeyFunc)
  314. return clientState, newInformer(lw, objType, resyncPeriod, h, clientState, nil)
  315. }
  316. // NewIndexerInformer returns an Indexer and a Controller for populating the index
  317. // while also providing event notifications. You should only used the returned
  318. // Index for Get/List operations; Add/Modify/Deletes will cause the event
  319. // notifications to be faulty.
  320. //
  321. // Parameters:
  322. // - lw is list and watch functions for the source of the resource you want to
  323. // be informed of.
  324. // - objType is an object of the type that you expect to receive.
  325. // - resyncPeriod: if non-zero, will re-list this often (you will get OnUpdate
  326. // calls, even if nothing changed). Otherwise, re-list will be delayed as
  327. // long as possible (until the upstream source closes the watch or times out,
  328. // or you stop the controller).
  329. // - h is the object you want notifications sent to.
  330. // - indexers is the indexer for the received object type.
  331. func NewIndexerInformer(
  332. lw ListerWatcher,
  333. objType runtime.Object,
  334. resyncPeriod time.Duration,
  335. h ResourceEventHandler,
  336. indexers Indexers,
  337. ) (Indexer, Controller) {
  338. // This will hold the client state, as we know it.
  339. clientState := NewIndexer(DeletionHandlingMetaNamespaceKeyFunc, indexers)
  340. return clientState, newInformer(lw, objType, resyncPeriod, h, clientState, nil)
  341. }
  342. // NewTransformingInformer returns a Store and a controller for populating
  343. // the store while also providing event notifications. You should only used
  344. // the returned Store for Get/List operations; Add/Modify/Deletes will cause
  345. // the event notifications to be faulty.
  346. // The given transform function will be called on all objects before they will
  347. // put into the Store and corresponding Add/Modify/Delete handlers will
  348. // be invoked for them.
  349. func NewTransformingInformer(
  350. lw ListerWatcher,
  351. objType runtime.Object,
  352. resyncPeriod time.Duration,
  353. h ResourceEventHandler,
  354. transformer TransformFunc,
  355. ) (Store, Controller) {
  356. // This will hold the client state, as we know it.
  357. clientState := NewStore(DeletionHandlingMetaNamespaceKeyFunc)
  358. return clientState, newInformer(lw, objType, resyncPeriod, h, clientState, transformer)
  359. }
  360. // NewTransformingIndexerInformer returns an Indexer and a controller for
  361. // populating the index while also providing event notifications. You should
  362. // only used the returned Index for Get/List operations; Add/Modify/Deletes
  363. // will cause the event notifications to be faulty.
  364. // The given transform function will be called on all objects before they will
  365. // be put into the Index and corresponding Add/Modify/Delete handlers will
  366. // be invoked for them.
  367. func NewTransformingIndexerInformer(
  368. lw ListerWatcher,
  369. objType runtime.Object,
  370. resyncPeriod time.Duration,
  371. h ResourceEventHandler,
  372. indexers Indexers,
  373. transformer TransformFunc,
  374. ) (Indexer, Controller) {
  375. // This will hold the client state, as we know it.
  376. clientState := NewIndexer(DeletionHandlingMetaNamespaceKeyFunc, indexers)
  377. return clientState, newInformer(lw, objType, resyncPeriod, h, clientState, transformer)
  378. }
  379. // Multiplexes updates in the form of a list of Deltas into a Store, and informs
  380. // a given handler of events OnUpdate, OnAdd, OnDelete
  381. func processDeltas(
  382. // Object which receives event notifications from the given deltas
  383. handler ResourceEventHandler,
  384. clientState Store,
  385. deltas Deltas,
  386. isInInitialList bool,
  387. ) error {
  388. // from oldest to newest
  389. for _, d := range deltas {
  390. obj := d.Object
  391. switch d.Type {
  392. case Sync, Replaced, Added, Updated:
  393. if old, exists, err := clientState.Get(obj); err == nil && exists {
  394. if err := clientState.Update(obj); err != nil {
  395. return err
  396. }
  397. handler.OnUpdate(old, obj)
  398. } else {
  399. if err := clientState.Add(obj); err != nil {
  400. return err
  401. }
  402. handler.OnAdd(obj, isInInitialList)
  403. }
  404. case Deleted:
  405. if err := clientState.Delete(obj); err != nil {
  406. return err
  407. }
  408. handler.OnDelete(obj)
  409. }
  410. }
  411. return nil
  412. }
  413. // newInformer returns a controller for populating the store while also
  414. // providing event notifications.
  415. //
  416. // Parameters
  417. // - lw is list and watch functions for the source of the resource you want to
  418. // be informed of.
  419. // - objType is an object of the type that you expect to receive.
  420. // - resyncPeriod: if non-zero, will re-list this often (you will get OnUpdate
  421. // calls, even if nothing changed). Otherwise, re-list will be delayed as
  422. // long as possible (until the upstream source closes the watch or times out,
  423. // or you stop the controller).
  424. // - h is the object you want notifications sent to.
  425. // - clientState is the store you want to populate
  426. func newInformer(
  427. lw ListerWatcher,
  428. objType runtime.Object,
  429. resyncPeriod time.Duration,
  430. h ResourceEventHandler,
  431. clientState Store,
  432. transformer TransformFunc,
  433. ) Controller {
  434. // This will hold incoming changes. Note how we pass clientState in as a
  435. // KeyLister, that way resync operations will result in the correct set
  436. // of update/delete deltas.
  437. fifo := NewDeltaFIFOWithOptions(DeltaFIFOOptions{
  438. KnownObjects: clientState,
  439. EmitDeltaTypeReplaced: true,
  440. Transformer: transformer,
  441. })
  442. cfg := &Config{
  443. Queue: fifo,
  444. ListerWatcher: lw,
  445. ObjectType: objType,
  446. FullResyncPeriod: resyncPeriod,
  447. RetryOnError: false,
  448. Process: func(obj interface{}, isInInitialList bool) error {
  449. if deltas, ok := obj.(Deltas); ok {
  450. return processDeltas(h, clientState, deltas, isInInitialList)
  451. }
  452. return errors.New("object given as Process argument is not Deltas")
  453. },
  454. }
  455. return New(cfg)
  456. }