k8s.io/client-go@v0.31.1/tools/cache/testing/fake_controller_source.go (about) 1 /* 2 Copyright 2015 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 framework 18 19 import ( 20 "errors" 21 "fmt" 22 "math/rand" 23 "strconv" 24 "sync" 25 26 v1 "k8s.io/api/core/v1" 27 apierrors "k8s.io/apimachinery/pkg/api/errors" 28 "k8s.io/apimachinery/pkg/api/meta" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/runtime" 31 "k8s.io/apimachinery/pkg/types" 32 "k8s.io/apimachinery/pkg/watch" 33 ) 34 35 func NewFakeControllerSource() *FakeControllerSource { 36 return &FakeControllerSource{ 37 Items: map[nnu]runtime.Object{}, 38 Broadcaster: watch.NewBroadcaster(100, watch.WaitIfChannelFull), 39 } 40 } 41 42 func NewFakePVControllerSource() *FakePVControllerSource { 43 return &FakePVControllerSource{ 44 FakeControllerSource{ 45 Items: map[nnu]runtime.Object{}, 46 Broadcaster: watch.NewBroadcaster(100, watch.WaitIfChannelFull), 47 }} 48 } 49 50 func NewFakePVCControllerSource() *FakePVCControllerSource { 51 return &FakePVCControllerSource{ 52 FakeControllerSource{ 53 Items: map[nnu]runtime.Object{}, 54 Broadcaster: watch.NewBroadcaster(100, watch.WaitIfChannelFull), 55 }} 56 } 57 58 // FakeControllerSource implements listing/watching for testing. 59 type FakeControllerSource struct { 60 lock sync.RWMutex 61 Items map[nnu]runtime.Object 62 changes []watch.Event // one change per resourceVersion 63 Broadcaster *watch.Broadcaster 64 lastRV int 65 66 // Set this to simulate an error on List() 67 ListError error 68 } 69 70 type FakePVControllerSource struct { 71 FakeControllerSource 72 } 73 74 type FakePVCControllerSource struct { 75 FakeControllerSource 76 } 77 78 // namespace, name, uid to be used as a key. 79 type nnu struct { 80 namespace, name string 81 uid types.UID 82 } 83 84 // ResetWatch simulates connection problems; creates a new Broadcaster and flushes 85 // the change queue so that clients have to re-list and watch. 86 func (f *FakeControllerSource) ResetWatch() { 87 f.lock.Lock() 88 defer f.lock.Unlock() 89 f.Broadcaster.Shutdown() 90 f.Broadcaster = watch.NewBroadcaster(100, watch.WaitIfChannelFull) 91 f.changes = []watch.Event{} 92 } 93 94 // Add adds an object to the set and sends an add event to watchers. 95 // obj's ResourceVersion is set. 96 func (f *FakeControllerSource) Add(obj runtime.Object) { 97 f.Change(watch.Event{Type: watch.Added, Object: obj}, 1) 98 } 99 100 // Modify updates an object in the set and sends a modified event to watchers. 101 // obj's ResourceVersion is set. 102 func (f *FakeControllerSource) Modify(obj runtime.Object) { 103 f.Change(watch.Event{Type: watch.Modified, Object: obj}, 1) 104 } 105 106 // Delete deletes an object from the set and sends a delete event to watchers. 107 // obj's ResourceVersion is set. 108 func (f *FakeControllerSource) Delete(lastValue runtime.Object) { 109 f.Change(watch.Event{Type: watch.Deleted, Object: lastValue}, 1) 110 } 111 112 // AddDropWatch adds an object to the set but forgets to send an add event to 113 // watchers. 114 // obj's ResourceVersion is set. 115 func (f *FakeControllerSource) AddDropWatch(obj runtime.Object) { 116 f.Change(watch.Event{Type: watch.Added, Object: obj}, 0) 117 } 118 119 // ModifyDropWatch updates an object in the set but forgets to send a modify 120 // event to watchers. 121 // obj's ResourceVersion is set. 122 func (f *FakeControllerSource) ModifyDropWatch(obj runtime.Object) { 123 f.Change(watch.Event{Type: watch.Modified, Object: obj}, 0) 124 } 125 126 // DeleteDropWatch deletes an object from the set but forgets to send a delete 127 // event to watchers. 128 // obj's ResourceVersion is set. 129 func (f *FakeControllerSource) DeleteDropWatch(lastValue runtime.Object) { 130 f.Change(watch.Event{Type: watch.Deleted, Object: lastValue}, 0) 131 } 132 133 func (f *FakeControllerSource) key(accessor metav1.Object) nnu { 134 return nnu{accessor.GetNamespace(), accessor.GetName(), accessor.GetUID()} 135 } 136 137 // Change records the given event (setting the object's resource version) and 138 // sends a watch event with the specified probability. 139 func (f *FakeControllerSource) Change(e watch.Event, watchProbability float64) { 140 f.lock.Lock() 141 defer f.lock.Unlock() 142 143 accessor, err := meta.Accessor(e.Object) 144 if err != nil { 145 panic(err) // this is test code only 146 } 147 148 f.lastRV += 1 149 accessor.SetResourceVersion(strconv.Itoa(f.lastRV)) 150 f.changes = append(f.changes, e) 151 key := f.key(accessor) 152 switch e.Type { 153 case watch.Added, watch.Modified: 154 f.Items[key] = e.Object 155 case watch.Deleted: 156 delete(f.Items, key) 157 } 158 159 if rand.Float64() < watchProbability { 160 f.Broadcaster.Action(e.Type, e.Object) 161 } 162 } 163 164 func (f *FakeControllerSource) getListItemsLocked() ([]runtime.Object, error) { 165 list := make([]runtime.Object, 0, len(f.Items)) 166 for _, obj := range f.Items { 167 // Must make a copy to allow clients to modify the object. 168 // Otherwise, if they make a change and write it back, they 169 // will inadvertently change our canonical copy (in 170 // addition to racing with other clients). 171 list = append(list, obj.DeepCopyObject()) 172 } 173 return list, nil 174 } 175 176 // List returns a list object, with its resource version set. 177 func (f *FakeControllerSource) List(options metav1.ListOptions) (runtime.Object, error) { 178 f.lock.RLock() 179 defer f.lock.RUnlock() 180 181 if f.ListError != nil { 182 return nil, f.ListError 183 } 184 185 list, err := f.getListItemsLocked() 186 if err != nil { 187 return nil, err 188 } 189 listObj := &v1.List{} 190 if err := meta.SetList(listObj, list); err != nil { 191 return nil, err 192 } 193 listAccessor, err := meta.ListAccessor(listObj) 194 if err != nil { 195 return nil, err 196 } 197 listAccessor.SetResourceVersion(strconv.Itoa(f.lastRV)) 198 return listObj, nil 199 } 200 201 // List returns a list object, with its resource version set. 202 func (f *FakePVControllerSource) List(options metav1.ListOptions) (runtime.Object, error) { 203 f.lock.RLock() 204 defer f.lock.RUnlock() 205 list, err := f.FakeControllerSource.getListItemsLocked() 206 if err != nil { 207 return nil, err 208 } 209 listObj := &v1.PersistentVolumeList{} 210 if err := meta.SetList(listObj, list); err != nil { 211 return nil, err 212 } 213 listAccessor, err := meta.ListAccessor(listObj) 214 if err != nil { 215 return nil, err 216 } 217 listAccessor.SetResourceVersion(strconv.Itoa(f.lastRV)) 218 return listObj, nil 219 } 220 221 // List returns a list object, with its resource version set. 222 func (f *FakePVCControllerSource) List(options metav1.ListOptions) (runtime.Object, error) { 223 f.lock.RLock() 224 defer f.lock.RUnlock() 225 list, err := f.FakeControllerSource.getListItemsLocked() 226 if err != nil { 227 return nil, err 228 } 229 listObj := &v1.PersistentVolumeClaimList{} 230 if err := meta.SetList(listObj, list); err != nil { 231 return nil, err 232 } 233 listAccessor, err := meta.ListAccessor(listObj) 234 if err != nil { 235 return nil, err 236 } 237 listAccessor.SetResourceVersion(strconv.Itoa(f.lastRV)) 238 return listObj, nil 239 } 240 241 // Watch returns a watch, which will be pre-populated with all changes 242 // after resourceVersion. 243 func (f *FakeControllerSource) Watch(options metav1.ListOptions) (watch.Interface, error) { 244 f.lock.RLock() 245 defer f.lock.RUnlock() 246 rc, err := strconv.Atoi(options.ResourceVersion) 247 if err != nil { 248 return nil, err 249 } 250 if rc < f.lastRV { 251 // if the change queue was flushed... 252 if len(f.changes) == 0 { 253 return nil, apierrors.NewResourceExpired(fmt.Sprintf("too old resource version: %d (%d)", rc, f.lastRV)) 254 } 255 256 // get the RV of the oldest object in the change queue 257 oldestRV, err := meta.NewAccessor().ResourceVersion(f.changes[0].Object) 258 if err != nil { 259 panic(err) 260 } 261 oldestRC, err := strconv.Atoi(oldestRV) 262 if err != nil { 263 panic(err) 264 } 265 if rc < oldestRC { 266 return nil, apierrors.NewResourceExpired(fmt.Sprintf("too old resource version: %d (%d)", rc, oldestRC)) 267 } 268 269 changes := []watch.Event{} 270 for _, c := range f.changes[rc-oldestRC+1:] { 271 // Must make a copy to allow clients to modify the 272 // object. Otherwise, if they make a change and write 273 // it back, they will inadvertently change the our 274 // canonical copy (in addition to racing with other 275 // clients). 276 changes = append(changes, watch.Event{Type: c.Type, Object: c.Object.DeepCopyObject()}) 277 } 278 return f.Broadcaster.WatchWithPrefix(changes) 279 } else if rc > f.lastRV { 280 return nil, errors.New("resource version in the future not supported by this fake") 281 } 282 return f.Broadcaster.Watch() 283 } 284 285 // Shutdown closes the underlying broadcaster, waiting for events to be 286 // delivered. It's an error to call any method after calling shutdown. This is 287 // enforced by Shutdown() leaving f locked. 288 func (f *FakeControllerSource) Shutdown() { 289 f.lock.Lock() // Purposely no unlock. 290 f.Broadcaster.Shutdown() 291 }