listwatch.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  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. "context"
  16. "time"
  17. "k8s.io/apimachinery/pkg/api/meta"
  18. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  19. "k8s.io/apimachinery/pkg/fields"
  20. "k8s.io/apimachinery/pkg/runtime"
  21. "k8s.io/apimachinery/pkg/util/wait"
  22. "k8s.io/apimachinery/pkg/watch"
  23. restclient "k8s.io/client-go/rest"
  24. "k8s.io/client-go/tools/pager"
  25. )
  26. // ListerWatcher is any object that knows how to perform an initial list and start a watch on a resource.
  27. type ListerWatcher interface {
  28. // List should return a list type object; the Items field will be extracted, and the
  29. // ResourceVersion field will be used to start the watch in the right place.
  30. List(options metav1.ListOptions) (runtime.Object, error)
  31. // Watch should begin a watch at the specified version.
  32. Watch(options metav1.ListOptions) (watch.Interface, error)
  33. }
  34. // ListFunc knows how to list resources
  35. type ListFunc func(options metav1.ListOptions) (runtime.Object, error)
  36. // WatchFunc knows how to watch resources
  37. type WatchFunc func(options metav1.ListOptions) (watch.Interface, error)
  38. // ListWatch knows how to list and watch a set of apiserver resources. It satisfies the ListerWatcher interface.
  39. // It is a convenience function for users of NewReflector, etc.
  40. // ListFunc and WatchFunc must not be nil
  41. type ListWatch struct {
  42. ListFunc ListFunc
  43. WatchFunc WatchFunc
  44. // DisableChunking requests no chunking for this list watcher.
  45. DisableChunking bool
  46. }
  47. // Getter interface knows how to access Get method from RESTClient.
  48. type Getter interface {
  49. Get() *restclient.Request
  50. }
  51. // NewListWatchFromClient creates a new ListWatch from the specified client, resource, namespace and field selector.
  52. func NewListWatchFromClient(c Getter, resource string, namespace string, fieldSelector fields.Selector) *ListWatch {
  53. optionsModifier := func(options *metav1.ListOptions) {
  54. options.FieldSelector = fieldSelector.String()
  55. }
  56. return NewFilteredListWatchFromClient(c, resource, namespace, optionsModifier)
  57. }
  58. // NewFilteredListWatchFromClient creates a new ListWatch from the specified client, resource, namespace, and option modifier.
  59. // Option modifier is a function takes a ListOptions and modifies the consumed ListOptions. Provide customized modifier function
  60. // to apply modification to ListOptions with a field selector, a label selector, or any other desired options.
  61. func NewFilteredListWatchFromClient(c Getter, resource string, namespace string, optionsModifier func(options *metav1.ListOptions)) *ListWatch {
  62. listFunc := func(options metav1.ListOptions) (runtime.Object, error) {
  63. optionsModifier(&options)
  64. return c.Get().
  65. Namespace(namespace).
  66. Resource(resource).
  67. VersionedParams(&options, metav1.ParameterCodec).
  68. Do().
  69. Get()
  70. }
  71. watchFunc := func(options metav1.ListOptions) (watch.Interface, error) {
  72. options.Watch = true
  73. optionsModifier(&options)
  74. return c.Get().
  75. Namespace(namespace).
  76. Resource(resource).
  77. VersionedParams(&options, metav1.ParameterCodec).
  78. Watch()
  79. }
  80. return &ListWatch{ListFunc: listFunc, WatchFunc: watchFunc}
  81. }
  82. func timeoutFromListOptions(options metav1.ListOptions) time.Duration {
  83. if options.TimeoutSeconds != nil {
  84. return time.Duration(*options.TimeoutSeconds) * time.Second
  85. }
  86. return 0
  87. }
  88. // List a set of apiserver resources
  89. func (lw *ListWatch) List(options metav1.ListOptions) (runtime.Object, error) {
  90. if !lw.DisableChunking {
  91. return pager.New(pager.SimplePageFunc(lw.ListFunc)).List(context.TODO(), options)
  92. }
  93. return lw.ListFunc(options)
  94. }
  95. // Watch a set of apiserver resources
  96. func (lw *ListWatch) Watch(options metav1.ListOptions) (watch.Interface, error) {
  97. return lw.WatchFunc(options)
  98. }
  99. // ListWatchUntil checks the provided conditions against the items returned by the list watcher, returning wait.ErrWaitTimeout
  100. // if timeout is exceeded without all conditions returning true, or an error if an error occurs.
  101. // TODO: check for watch expired error and retry watch from latest point? Same issue exists for Until.
  102. func ListWatchUntil(timeout time.Duration, lw ListerWatcher, conditions ...watch.ConditionFunc) (*watch.Event, error) {
  103. if len(conditions) == 0 {
  104. return nil, nil
  105. }
  106. list, err := lw.List(metav1.ListOptions{})
  107. if err != nil {
  108. return nil, err
  109. }
  110. initialItems, err := meta.ExtractList(list)
  111. if err != nil {
  112. return nil, err
  113. }
  114. // use the initial items as simulated "adds"
  115. var lastEvent *watch.Event
  116. currIndex := 0
  117. passedConditions := 0
  118. for _, condition := range conditions {
  119. // check the next condition against the previous event and short circuit waiting for the next watch
  120. if lastEvent != nil {
  121. done, err := condition(*lastEvent)
  122. if err != nil {
  123. return lastEvent, err
  124. }
  125. if done {
  126. passedConditions = passedConditions + 1
  127. continue
  128. }
  129. }
  130. ConditionSucceeded:
  131. for currIndex < len(initialItems) {
  132. lastEvent = &watch.Event{Type: watch.Added, Object: initialItems[currIndex]}
  133. currIndex++
  134. done, err := condition(*lastEvent)
  135. if err != nil {
  136. return lastEvent, err
  137. }
  138. if done {
  139. passedConditions = passedConditions + 1
  140. break ConditionSucceeded
  141. }
  142. }
  143. }
  144. if passedConditions == len(conditions) {
  145. return lastEvent, nil
  146. }
  147. remainingConditions := conditions[passedConditions:]
  148. metaObj, err := meta.ListAccessor(list)
  149. if err != nil {
  150. return nil, err
  151. }
  152. currResourceVersion := metaObj.GetResourceVersion()
  153. watchInterface, err := lw.Watch(metav1.ListOptions{ResourceVersion: currResourceVersion})
  154. if err != nil {
  155. return nil, err
  156. }
  157. evt, err := watch.Until(timeout, watchInterface, remainingConditions...)
  158. if err == watch.ErrWatchClosed {
  159. // present a consistent error interface to callers
  160. err = wait.ErrWaitTimeout
  161. }
  162. return evt, err
  163. }