controller.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  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. "sync"
  16. "time"
  17. "k8s.io/apimachinery/pkg/runtime"
  18. "k8s.io/apimachinery/pkg/util/clock"
  19. utilruntime "k8s.io/apimachinery/pkg/util/runtime"
  20. "k8s.io/apimachinery/pkg/util/wait"
  21. )
  22. // Config contains all the settings for a Controller.
  23. type Config struct {
  24. // The queue for your objects; either a FIFO or
  25. // a DeltaFIFO. Your Process() function should accept
  26. // the output of this Queue's Pop() method.
  27. Queue
  28. // Something that can list and watch your objects.
  29. ListerWatcher
  30. // Something that can process your objects.
  31. Process ProcessFunc
  32. // The type of your objects.
  33. ObjectType runtime.Object
  34. // Reprocess everything at least this often.
  35. // Note that if it takes longer for you to clear the queue than this
  36. // period, you will end up processing items in the order determined
  37. // by FIFO.Replace(). Currently, this is random. If this is a
  38. // problem, we can change that replacement policy to append new
  39. // things to the end of the queue instead of replacing the entire
  40. // queue.
  41. FullResyncPeriod time.Duration
  42. // ShouldResync, if specified, is invoked when the controller's reflector determines the next
  43. // periodic sync should occur. If this returns true, it means the reflector should proceed with
  44. // the resync.
  45. ShouldResync ShouldResyncFunc
  46. // If true, when Process() returns an error, re-enqueue the object.
  47. // TODO: add interface to let you inject a delay/backoff or drop
  48. // the object completely if desired. Pass the object in
  49. // question to this interface as a parameter.
  50. RetryOnError bool
  51. }
  52. // ShouldResyncFunc is a type of function that indicates if a reflector should perform a
  53. // resync or not. It can be used by a shared informer to support multiple event handlers with custom
  54. // resync periods.
  55. type ShouldResyncFunc func() bool
  56. // ProcessFunc processes a single object.
  57. type ProcessFunc func(obj interface{}) error
  58. // Controller is a generic controller framework.
  59. type controller struct {
  60. config Config
  61. reflector *Reflector
  62. reflectorMutex sync.RWMutex
  63. clock clock.Clock
  64. }
  65. type Controller interface {
  66. Run(stopCh <-chan struct{})
  67. HasSynced() bool
  68. LastSyncResourceVersion() string
  69. }
  70. // New makes a new Controller from the given Config.
  71. func New(c *Config) Controller {
  72. ctlr := &controller{
  73. config: *c,
  74. clock: &clock.RealClock{},
  75. }
  76. return ctlr
  77. }
  78. // Run begins processing items, and will continue until a value is sent down stopCh.
  79. // It's an error to call Run more than once.
  80. // Run blocks; call via go.
  81. func (c *controller) Run(stopCh <-chan struct{}) {
  82. defer utilruntime.HandleCrash()
  83. go func() {
  84. <-stopCh
  85. c.config.Queue.Close()
  86. }()
  87. r := NewReflector(
  88. c.config.ListerWatcher,
  89. c.config.ObjectType,
  90. c.config.Queue,
  91. c.config.FullResyncPeriod,
  92. )
  93. r.ShouldResync = c.config.ShouldResync
  94. r.clock = c.clock
  95. c.reflectorMutex.Lock()
  96. c.reflector = r
  97. c.reflectorMutex.Unlock()
  98. var wg wait.Group
  99. defer wg.Wait()
  100. wg.StartWithChannel(stopCh, r.Run)
  101. wait.Until(c.processLoop, time.Second, stopCh)
  102. }
  103. // Returns true once this controller has completed an initial resource listing
  104. func (c *controller) HasSynced() bool {
  105. return c.config.Queue.HasSynced()
  106. }
  107. func (c *controller) LastSyncResourceVersion() string {
  108. if c.reflector == nil {
  109. return ""
  110. }
  111. return c.reflector.LastSyncResourceVersion()
  112. }
  113. // processLoop drains the work queue.
  114. // TODO: Consider doing the processing in parallel. This will require a little thought
  115. // to make sure that we don't end up processing the same object multiple times
  116. // concurrently.
  117. //
  118. // TODO: Plumb through the stopCh here (and down to the queue) so that this can
  119. // actually exit when the controller is stopped. Or just give up on this stuff
  120. // ever being stoppable. Converting this whole package to use Context would
  121. // also be helpful.
  122. func (c *controller) processLoop() {
  123. for {
  124. obj, err := c.config.Queue.Pop(PopProcessFunc(c.config.Process))
  125. if err != nil {
  126. if err == FIFOClosedError {
  127. return
  128. }
  129. if c.config.RetryOnError {
  130. // This is the safe way to re-enqueue.
  131. c.config.Queue.AddIfNotPresent(obj)
  132. }
  133. }
  134. }
  135. }
  136. // ResourceEventHandler can handle notifications for events that happen to a
  137. // resource. The events are informational only, so you can't return an
  138. // error.
  139. // * OnAdd is called when an object is added.
  140. // * OnUpdate is called when an object is modified. Note that oldObj is the
  141. // last known state of the object-- it is possible that several changes
  142. // were combined together, so you can't use this to see every single
  143. // change. OnUpdate is also called when a re-list happens, and it will
  144. // get called even if nothing changed. This is useful for periodically
  145. // evaluating or syncing something.
  146. // * OnDelete will get the final state of the item if it is known, otherwise
  147. // it will get an object of type DeletedFinalStateUnknown. This can
  148. // happen if the watch is closed and misses the delete event and we don't
  149. // notice the deletion until the subsequent re-list.
  150. type ResourceEventHandler interface {
  151. OnAdd(obj interface{})
  152. OnUpdate(oldObj, newObj interface{})
  153. OnDelete(obj interface{})
  154. }
  155. // ResourceEventHandlerFuncs is an adaptor to let you easily specify as many or
  156. // as few of the notification functions as you want while still implementing
  157. // ResourceEventHandler.
  158. type ResourceEventHandlerFuncs struct {
  159. AddFunc func(obj interface{})
  160. UpdateFunc func(oldObj, newObj interface{})
  161. DeleteFunc func(obj interface{})
  162. }
  163. // OnAdd calls AddFunc if it's not nil.
  164. func (r ResourceEventHandlerFuncs) OnAdd(obj interface{}) {
  165. if r.AddFunc != nil {
  166. r.AddFunc(obj)
  167. }
  168. }
  169. // OnUpdate calls UpdateFunc if it's not nil.
  170. func (r ResourceEventHandlerFuncs) OnUpdate(oldObj, newObj interface{}) {
  171. if r.UpdateFunc != nil {
  172. r.UpdateFunc(oldObj, newObj)
  173. }
  174. }
  175. // OnDelete calls DeleteFunc if it's not nil.
  176. func (r ResourceEventHandlerFuncs) OnDelete(obj interface{}) {
  177. if r.DeleteFunc != nil {
  178. r.DeleteFunc(obj)
  179. }
  180. }
  181. // FilteringResourceEventHandler applies the provided filter to all events coming
  182. // in, ensuring the appropriate nested handler method is invoked. An object
  183. // that starts passing the filter after an update is considered an add, and an
  184. // object that stops passing the filter after an update is considered a delete.
  185. type FilteringResourceEventHandler struct {
  186. FilterFunc func(obj interface{}) bool
  187. Handler ResourceEventHandler
  188. }
  189. // OnAdd calls the nested handler only if the filter succeeds
  190. func (r FilteringResourceEventHandler) OnAdd(obj interface{}) {
  191. if !r.FilterFunc(obj) {
  192. return
  193. }
  194. r.Handler.OnAdd(obj)
  195. }
  196. // OnUpdate ensures the proper handler is called depending on whether the filter matches
  197. func (r FilteringResourceEventHandler) OnUpdate(oldObj, newObj interface{}) {
  198. newer := r.FilterFunc(newObj)
  199. older := r.FilterFunc(oldObj)
  200. switch {
  201. case newer && older:
  202. r.Handler.OnUpdate(oldObj, newObj)
  203. case newer && !older:
  204. r.Handler.OnAdd(newObj)
  205. case !newer && older:
  206. r.Handler.OnDelete(oldObj)
  207. default:
  208. // do nothing
  209. }
  210. }
  211. // OnDelete calls the nested handler only if the filter succeeds
  212. func (r FilteringResourceEventHandler) OnDelete(obj interface{}) {
  213. if !r.FilterFunc(obj) {
  214. return
  215. }
  216. r.Handler.OnDelete(obj)
  217. }
  218. // DeletionHandlingMetaNamespaceKeyFunc checks for
  219. // DeletedFinalStateUnknown objects before calling
  220. // MetaNamespaceKeyFunc.
  221. func DeletionHandlingMetaNamespaceKeyFunc(obj interface{}) (string, error) {
  222. if d, ok := obj.(DeletedFinalStateUnknown); ok {
  223. return d.Key, nil
  224. }
  225. return MetaNamespaceKeyFunc(obj)
  226. }
  227. // NewInformer returns a Store and a controller for populating the store
  228. // while also providing event notifications. You should only used the returned
  229. // Store for Get/List operations; Add/Modify/Deletes will cause the event
  230. // notifications to be faulty.
  231. //
  232. // Parameters:
  233. // * lw is list and watch functions for the source of the resource you want to
  234. // be informed of.
  235. // * objType is an object of the type that you expect to receive.
  236. // * resyncPeriod: if non-zero, will re-list this often (you will get OnUpdate
  237. // calls, even if nothing changed). Otherwise, re-list will be delayed as
  238. // long as possible (until the upstream source closes the watch or times out,
  239. // or you stop the controller).
  240. // * h is the object you want notifications sent to.
  241. //
  242. func NewInformer(
  243. lw ListerWatcher,
  244. objType runtime.Object,
  245. resyncPeriod time.Duration,
  246. h ResourceEventHandler,
  247. ) (Store, Controller) {
  248. // This will hold the client state, as we know it.
  249. clientState := NewStore(DeletionHandlingMetaNamespaceKeyFunc)
  250. // This will hold incoming changes. Note how we pass clientState in as a
  251. // KeyLister, that way resync operations will result in the correct set
  252. // of update/delete deltas.
  253. fifo := NewDeltaFIFO(MetaNamespaceKeyFunc, clientState)
  254. cfg := &Config{
  255. Queue: fifo,
  256. ListerWatcher: lw,
  257. ObjectType: objType,
  258. FullResyncPeriod: resyncPeriod,
  259. RetryOnError: false,
  260. Process: func(obj interface{}) error {
  261. // from oldest to newest
  262. for _, d := range obj.(Deltas) {
  263. switch d.Type {
  264. case Sync, Added, Updated:
  265. if old, exists, err := clientState.Get(d.Object); err == nil && exists {
  266. if err := clientState.Update(d.Object); err != nil {
  267. return err
  268. }
  269. h.OnUpdate(old, d.Object)
  270. } else {
  271. if err := clientState.Add(d.Object); err != nil {
  272. return err
  273. }
  274. h.OnAdd(d.Object)
  275. }
  276. case Deleted:
  277. if err := clientState.Delete(d.Object); err != nil {
  278. return err
  279. }
  280. h.OnDelete(d.Object)
  281. }
  282. }
  283. return nil
  284. },
  285. }
  286. return clientState, New(cfg)
  287. }
  288. // NewIndexerInformer returns a Indexer and a controller for populating the index
  289. // while also providing event notifications. You should only used the returned
  290. // Index for Get/List operations; Add/Modify/Deletes will cause the event
  291. // notifications to be faulty.
  292. //
  293. // Parameters:
  294. // * lw is list and watch functions for the source of the resource you want to
  295. // be informed of.
  296. // * objType is an object of the type that you expect to receive.
  297. // * resyncPeriod: if non-zero, will re-list this often (you will get OnUpdate
  298. // calls, even if nothing changed). Otherwise, re-list will be delayed as
  299. // long as possible (until the upstream source closes the watch or times out,
  300. // or you stop the controller).
  301. // * h is the object you want notifications sent to.
  302. // * indexers is the indexer for the received object type.
  303. //
  304. func NewIndexerInformer(
  305. lw ListerWatcher,
  306. objType runtime.Object,
  307. resyncPeriod time.Duration,
  308. h ResourceEventHandler,
  309. indexers Indexers,
  310. ) (Indexer, Controller) {
  311. // This will hold the client state, as we know it.
  312. clientState := NewIndexer(DeletionHandlingMetaNamespaceKeyFunc, indexers)
  313. // This will hold incoming changes. Note how we pass clientState in as a
  314. // KeyLister, that way resync operations will result in the correct set
  315. // of update/delete deltas.
  316. fifo := NewDeltaFIFO(MetaNamespaceKeyFunc, clientState)
  317. cfg := &Config{
  318. Queue: fifo,
  319. ListerWatcher: lw,
  320. ObjectType: objType,
  321. FullResyncPeriod: resyncPeriod,
  322. RetryOnError: false,
  323. Process: func(obj interface{}) error {
  324. // from oldest to newest
  325. for _, d := range obj.(Deltas) {
  326. switch d.Type {
  327. case Sync, Added, Updated:
  328. if old, exists, err := clientState.Get(d.Object); err == nil && exists {
  329. if err := clientState.Update(d.Object); err != nil {
  330. return err
  331. }
  332. h.OnUpdate(old, d.Object)
  333. } else {
  334. if err := clientState.Add(d.Object); err != nil {
  335. return err
  336. }
  337. h.OnAdd(d.Object)
  338. }
  339. case Deleted:
  340. if err := clientState.Delete(d.Object); err != nil {
  341. return err
  342. }
  343. h.OnDelete(d.Object)
  344. }
  345. }
  346. return nil
  347. },
  348. }
  349. return clientState, New(cfg)
  350. }