k8s.io/client-go@v0.22.2/discovery/cached/memory/memcache_test.go (about) 1 /* 2 Copyright 2017 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 memory 18 19 import ( 20 "errors" 21 "net/http" 22 "reflect" 23 "sync" 24 "testing" 25 26 errorsutil "k8s.io/apimachinery/pkg/api/errors" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/client-go/discovery/fake" 29 ) 30 31 type resourceMapEntry struct { 32 list *metav1.APIResourceList 33 err error 34 } 35 36 type fakeDiscovery struct { 37 *fake.FakeDiscovery 38 39 lock sync.Mutex 40 groupList *metav1.APIGroupList 41 groupListErr error 42 resourceMap map[string]*resourceMapEntry 43 } 44 45 func (c *fakeDiscovery) ServerResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error) { 46 c.lock.Lock() 47 defer c.lock.Unlock() 48 if rl, ok := c.resourceMap[groupVersion]; ok { 49 return rl.list, rl.err 50 } 51 return nil, errors.New("doesn't exist") 52 } 53 54 func (c *fakeDiscovery) ServerGroups() (*metav1.APIGroupList, error) { 55 c.lock.Lock() 56 defer c.lock.Unlock() 57 if c.groupList == nil { 58 return nil, errors.New("doesn't exist") 59 } 60 return c.groupList, c.groupListErr 61 } 62 63 func TestClient(t *testing.T) { 64 fake := &fakeDiscovery{ 65 groupList: &metav1.APIGroupList{ 66 Groups: []metav1.APIGroup{{ 67 Name: "astronomy", 68 Versions: []metav1.GroupVersionForDiscovery{{ 69 GroupVersion: "astronomy/v8beta1", 70 Version: "v8beta1", 71 }}, 72 }}, 73 }, 74 resourceMap: map[string]*resourceMapEntry{ 75 "astronomy/v8beta1": { 76 list: &metav1.APIResourceList{ 77 GroupVersion: "astronomy/v8beta1", 78 APIResources: []metav1.APIResource{{ 79 Name: "dwarfplanets", 80 SingularName: "dwarfplanet", 81 Namespaced: true, 82 Kind: "DwarfPlanet", 83 ShortNames: []string{"dp"}, 84 }}, 85 }, 86 }, 87 }, 88 } 89 90 c := NewMemCacheClient(fake) 91 if c.Fresh() { 92 t.Errorf("Expected not fresh.") 93 } 94 g, err := c.ServerGroups() 95 if err != nil { 96 t.Errorf("Unexpected error: %v", err) 97 } 98 if e, a := fake.groupList, g; !reflect.DeepEqual(e, a) { 99 t.Errorf("Expected %#v, got %#v", e, a) 100 } 101 if !c.Fresh() { 102 t.Errorf("Expected fresh.") 103 } 104 c.Invalidate() 105 if c.Fresh() { 106 t.Errorf("Expected not fresh.") 107 } 108 109 g, err = c.ServerGroups() 110 if err != nil { 111 t.Errorf("Unexpected error: %v", err) 112 } 113 if e, a := fake.groupList, g; !reflect.DeepEqual(e, a) { 114 t.Errorf("Expected %#v, got %#v", e, a) 115 } 116 if !c.Fresh() { 117 t.Errorf("Expected fresh.") 118 } 119 r, err := c.ServerResourcesForGroupVersion("astronomy/v8beta1") 120 if err != nil { 121 t.Errorf("Unexpected error: %v", err) 122 } 123 if e, a := fake.resourceMap["astronomy/v8beta1"].list, r; !reflect.DeepEqual(e, a) { 124 t.Errorf("Expected %#v, got %#v", e, a) 125 } 126 127 fake.lock.Lock() 128 fake.resourceMap = map[string]*resourceMapEntry{ 129 "astronomy/v8beta1": { 130 list: &metav1.APIResourceList{ 131 GroupVersion: "astronomy/v8beta1", 132 APIResources: []metav1.APIResource{{ 133 Name: "stars", 134 SingularName: "star", 135 Namespaced: true, 136 Kind: "Star", 137 ShortNames: []string{"s"}, 138 }}, 139 }, 140 }, 141 } 142 fake.lock.Unlock() 143 144 c.Invalidate() 145 r, err = c.ServerResourcesForGroupVersion("astronomy/v8beta1") 146 if err != nil { 147 t.Errorf("Unexpected error: %v", err) 148 } 149 if e, a := fake.resourceMap["astronomy/v8beta1"].list, r; !reflect.DeepEqual(e, a) { 150 t.Errorf("Expected %#v, got %#v", e, a) 151 } 152 } 153 154 func TestServerGroupsFails(t *testing.T) { 155 fake := &fakeDiscovery{ 156 groupList: &metav1.APIGroupList{ 157 Groups: []metav1.APIGroup{{ 158 Name: "astronomy", 159 Versions: []metav1.GroupVersionForDiscovery{{ 160 GroupVersion: "astronomy/v8beta1", 161 Version: "v8beta1", 162 }}, 163 }}, 164 }, 165 groupListErr: errors.New("some error"), 166 resourceMap: map[string]*resourceMapEntry{ 167 "astronomy/v8beta1": { 168 list: &metav1.APIResourceList{ 169 GroupVersion: "astronomy/v8beta1", 170 APIResources: []metav1.APIResource{{ 171 Name: "dwarfplanets", 172 SingularName: "dwarfplanet", 173 Namespaced: true, 174 Kind: "DwarfPlanet", 175 ShortNames: []string{"dp"}, 176 }}, 177 }, 178 }, 179 }, 180 } 181 182 c := NewMemCacheClient(fake) 183 if c.Fresh() { 184 t.Errorf("Expected not fresh.") 185 } 186 _, err := c.ServerGroups() 187 if err == nil { 188 t.Errorf("Expected error") 189 } 190 if c.Fresh() { 191 t.Errorf("Expected not fresh.") 192 } 193 fake.lock.Lock() 194 fake.groupListErr = nil 195 fake.lock.Unlock() 196 r, err := c.ServerResourcesForGroupVersion("astronomy/v8beta1") 197 if err != nil { 198 t.Errorf("Unexpected error: %v", err) 199 } 200 if e, a := fake.resourceMap["astronomy/v8beta1"].list, r; !reflect.DeepEqual(e, a) { 201 t.Errorf("Expected %#v, got %#v", e, a) 202 } 203 if !c.Fresh() { 204 t.Errorf("Expected not fresh.") 205 } 206 } 207 208 func TestPartialPermanentFailure(t *testing.T) { 209 fake := &fakeDiscovery{ 210 groupList: &metav1.APIGroupList{ 211 Groups: []metav1.APIGroup{ 212 { 213 Name: "astronomy", 214 Versions: []metav1.GroupVersionForDiscovery{{ 215 GroupVersion: "astronomy/v8beta1", 216 Version: "v8beta1", 217 }}, 218 }, 219 { 220 Name: "astronomy2", 221 Versions: []metav1.GroupVersionForDiscovery{{ 222 GroupVersion: "astronomy2/v8beta1", 223 Version: "v8beta1", 224 }}, 225 }, 226 }, 227 }, 228 resourceMap: map[string]*resourceMapEntry{ 229 "astronomy/v8beta1": { 230 err: errors.New("some permanent error"), 231 }, 232 "astronomy2/v8beta1": { 233 list: &metav1.APIResourceList{ 234 GroupVersion: "astronomy2/v8beta1", 235 APIResources: []metav1.APIResource{{ 236 Name: "dwarfplanets", 237 SingularName: "dwarfplanet", 238 Namespaced: true, 239 Kind: "DwarfPlanet", 240 ShortNames: []string{"dp"}, 241 }}, 242 }, 243 }, 244 }, 245 } 246 247 c := NewMemCacheClient(fake) 248 if c.Fresh() { 249 t.Errorf("Expected not fresh.") 250 } 251 r, err := c.ServerResourcesForGroupVersion("astronomy2/v8beta1") 252 if err != nil { 253 t.Errorf("Unexpected error: %v", err) 254 } 255 if e, a := fake.resourceMap["astronomy2/v8beta1"].list, r; !reflect.DeepEqual(e, a) { 256 t.Errorf("Expected %#v, got %#v", e, a) 257 } 258 _, err = c.ServerResourcesForGroupVersion("astronomy/v8beta1") 259 if err == nil { 260 t.Errorf("Expected error, got nil") 261 } 262 263 fake.lock.Lock() 264 fake.resourceMap["astronomy/v8beta1"] = &resourceMapEntry{ 265 list: &metav1.APIResourceList{ 266 GroupVersion: "astronomy/v8beta1", 267 APIResources: []metav1.APIResource{{ 268 Name: "dwarfplanets", 269 SingularName: "dwarfplanet", 270 Namespaced: true, 271 Kind: "DwarfPlanet", 272 ShortNames: []string{"dp"}, 273 }}, 274 }, 275 err: nil, 276 } 277 fake.lock.Unlock() 278 // We don't retry permanent errors, so it should fail. 279 _, err = c.ServerResourcesForGroupVersion("astronomy/v8beta1") 280 if err == nil { 281 t.Errorf("Expected error, got nil") 282 } 283 c.Invalidate() 284 285 // After Invalidate, we should retry. 286 r, err = c.ServerResourcesForGroupVersion("astronomy/v8beta1") 287 if err != nil { 288 t.Errorf("Unexpected error: %v", err) 289 } 290 if e, a := fake.resourceMap["astronomy/v8beta1"].list, r; !reflect.DeepEqual(e, a) { 291 t.Errorf("Expected %#v, got %#v", e, a) 292 } 293 } 294 295 func TestPartialRetryableFailure(t *testing.T) { 296 fake := &fakeDiscovery{ 297 groupList: &metav1.APIGroupList{ 298 Groups: []metav1.APIGroup{ 299 { 300 Name: "astronomy", 301 Versions: []metav1.GroupVersionForDiscovery{{ 302 GroupVersion: "astronomy/v8beta1", 303 Version: "v8beta1", 304 }}, 305 }, 306 { 307 Name: "astronomy2", 308 Versions: []metav1.GroupVersionForDiscovery{{ 309 GroupVersion: "astronomy2/v8beta1", 310 Version: "v8beta1", 311 }}, 312 }, 313 }, 314 }, 315 resourceMap: map[string]*resourceMapEntry{ 316 "astronomy/v8beta1": { 317 err: &errorsutil.StatusError{ 318 ErrStatus: metav1.Status{ 319 Message: "Some retryable error", 320 Code: int32(http.StatusServiceUnavailable), 321 Reason: metav1.StatusReasonServiceUnavailable, 322 }, 323 }, 324 }, 325 "astronomy2/v8beta1": { 326 list: &metav1.APIResourceList{ 327 GroupVersion: "astronomy2/v8beta1", 328 APIResources: []metav1.APIResource{{ 329 Name: "dwarfplanets", 330 SingularName: "dwarfplanet", 331 Namespaced: true, 332 Kind: "DwarfPlanet", 333 ShortNames: []string{"dp"}, 334 }}, 335 }, 336 }, 337 }, 338 } 339 340 c := NewMemCacheClient(fake) 341 if c.Fresh() { 342 t.Errorf("Expected not fresh.") 343 } 344 r, err := c.ServerResourcesForGroupVersion("astronomy2/v8beta1") 345 if err != nil { 346 t.Errorf("Unexpected error: %v", err) 347 } 348 if e, a := fake.resourceMap["astronomy2/v8beta1"].list, r; !reflect.DeepEqual(e, a) { 349 t.Errorf("Expected %#v, got %#v", e, a) 350 } 351 _, err = c.ServerResourcesForGroupVersion("astronomy/v8beta1") 352 if err == nil { 353 t.Errorf("Expected error, got nil") 354 } 355 356 fake.lock.Lock() 357 fake.resourceMap["astronomy/v8beta1"] = &resourceMapEntry{ 358 list: &metav1.APIResourceList{ 359 GroupVersion: "astronomy/v8beta1", 360 APIResources: []metav1.APIResource{{ 361 Name: "dwarfplanets", 362 SingularName: "dwarfplanet", 363 Namespaced: true, 364 Kind: "DwarfPlanet", 365 ShortNames: []string{"dp"}, 366 }}, 367 }, 368 err: nil, 369 } 370 fake.lock.Unlock() 371 // We should retry retryable error even without Invalidate() being called, 372 // so no error is expected. 373 r, err = c.ServerResourcesForGroupVersion("astronomy/v8beta1") 374 if err != nil { 375 t.Errorf("Expected no error, got %v", err) 376 } 377 if e, a := fake.resourceMap["astronomy/v8beta1"].list, r; !reflect.DeepEqual(e, a) { 378 t.Errorf("Expected %#v, got %#v", e, a) 379 } 380 381 // Check that the last result was cached and we don't retry further. 382 fake.lock.Lock() 383 fake.resourceMap["astronomy/v8beta1"].err = errors.New("some permanent error") 384 fake.lock.Unlock() 385 r, err = c.ServerResourcesForGroupVersion("astronomy/v8beta1") 386 if err != nil { 387 t.Errorf("Expected no error, got %v", err) 388 } 389 if e, a := fake.resourceMap["astronomy/v8beta1"].list, r; !reflect.DeepEqual(e, a) { 390 t.Errorf("Expected %#v, got %#v", e, a) 391 } 392 }