1
16
17 package apiutil_test
18
19 import (
20 "context"
21 "fmt"
22 "net/http"
23 "testing"
24
25 _ "github.com/onsi/ginkgo/v2"
26 gmg "github.com/onsi/gomega"
27 "github.com/onsi/gomega/format"
28 gomegatypes "github.com/onsi/gomega/types"
29
30 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
31 apierrors "k8s.io/apimachinery/pkg/api/errors"
32 "k8s.io/apimachinery/pkg/api/meta"
33 "k8s.io/apimachinery/pkg/runtime/schema"
34 "k8s.io/apimachinery/pkg/types"
35 "k8s.io/client-go/discovery"
36 "k8s.io/client-go/kubernetes/scheme"
37 "k8s.io/client-go/rest"
38
39 "sigs.k8s.io/controller-runtime/pkg/client"
40 "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
41 "sigs.k8s.io/controller-runtime/pkg/envtest"
42 )
43
44
45 type countingRoundTripper struct {
46 roundTripper http.RoundTripper
47 requestCount int
48 }
49
50 func newCountingRoundTripper(rt http.RoundTripper) *countingRoundTripper {
51 return &countingRoundTripper{roundTripper: rt}
52 }
53
54
55 func (crt *countingRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
56 crt.requestCount++
57
58 return crt.roundTripper.RoundTrip(r)
59 }
60
61
62 func (crt *countingRoundTripper) GetRequestCount() int {
63 return crt.requestCount
64 }
65
66
67 func (crt *countingRoundTripper) Reset() {
68 crt.requestCount = 0
69 }
70
71 func setupEnvtest(t *testing.T) (*rest.Config, func(t *testing.T)) {
72 t.Log("Setup envtest")
73
74 g := gmg.NewWithT(t)
75 testEnv := &envtest.Environment{
76 CRDDirectoryPaths: []string{"testdata"},
77 }
78
79 cfg, err := testEnv.Start()
80 g.Expect(err).NotTo(gmg.HaveOccurred())
81 g.Expect(cfg).NotTo(gmg.BeNil())
82
83 teardownFunc := func(t *testing.T) {
84 t.Log("Stop envtest")
85 g.Expect(testEnv.Stop()).To(gmg.Succeed())
86 }
87
88 return cfg, teardownFunc
89 }
90
91 func TestLazyRestMapperProvider(t *testing.T) {
92 restCfg, tearDownFn := setupEnvtest(t)
93 defer tearDownFn(t)
94
95 t.Run("LazyRESTMapper should fetch data based on the request", func(t *testing.T) {
96 g := gmg.NewWithT(t)
97
98
99
100
101 httpClient, err := rest.HTTPClientFor(restCfg)
102 g.Expect(err).NotTo(gmg.HaveOccurred())
103
104 crt := newCountingRoundTripper(httpClient.Transport)
105 httpClient.Transport = crt
106
107 lazyRestMapper, err := apiutil.NewDynamicRESTMapper(restCfg, httpClient)
108 g.Expect(err).NotTo(gmg.HaveOccurred())
109
110
111 g.Expect(crt.GetRequestCount()).To(gmg.Equal(0))
112
113 mapping, err := lazyRestMapper.RESTMapping(schema.GroupKind{Group: "apps", Kind: "deployment"}, "v1")
114 g.Expect(err).NotTo(gmg.HaveOccurred())
115 g.Expect(mapping.GroupVersionKind.Kind).To(gmg.Equal("deployment"))
116 g.Expect(crt.GetRequestCount()).To(gmg.Equal(1))
117
118 mappings, err := lazyRestMapper.RESTMappings(schema.GroupKind{Group: "", Kind: "pod"}, "v1")
119 g.Expect(err).NotTo(gmg.HaveOccurred())
120 g.Expect(mappings).To(gmg.HaveLen(1))
121 g.Expect(mappings[0].GroupVersionKind.Kind).To(gmg.Equal("pod"))
122 g.Expect(crt.GetRequestCount()).To(gmg.Equal(2))
123
124 kind, err := lazyRestMapper.KindFor(schema.GroupVersionResource{Group: "networking.k8s.io", Version: "v1", Resource: "ingresses"})
125 g.Expect(err).NotTo(gmg.HaveOccurred())
126 g.Expect(kind.Kind).To(gmg.Equal("Ingress"))
127 g.Expect(crt.GetRequestCount()).To(gmg.Equal(3))
128
129 kinds, err := lazyRestMapper.KindsFor(schema.GroupVersionResource{Group: "authentication.k8s.io", Version: "v1", Resource: "tokenreviews"})
130 g.Expect(err).NotTo(gmg.HaveOccurred())
131 g.Expect(kinds).To(gmg.HaveLen(1))
132 g.Expect(kinds[0].Kind).To(gmg.Equal("TokenReview"))
133 g.Expect(crt.GetRequestCount()).To(gmg.Equal(4))
134
135 resource, err := lazyRestMapper.ResourceFor(schema.GroupVersionResource{Group: "scheduling.k8s.io", Version: "v1", Resource: "priorityclasses"})
136 g.Expect(err).NotTo(gmg.HaveOccurred())
137 g.Expect(resource.Resource).To(gmg.Equal("priorityclasses"))
138 g.Expect(crt.GetRequestCount()).To(gmg.Equal(5))
139
140 resources, err := lazyRestMapper.ResourcesFor(schema.GroupVersionResource{Group: "policy", Version: "v1", Resource: "poddisruptionbudgets"})
141 g.Expect(err).NotTo(gmg.HaveOccurred())
142 g.Expect(resources).To(gmg.HaveLen(1))
143 g.Expect(resources[0].Resource).To(gmg.Equal("poddisruptionbudgets"))
144 g.Expect(crt.GetRequestCount()).To(gmg.Equal(6))
145 })
146
147 t.Run("LazyRESTMapper should cache fetched data and doesn't perform any additional requests", func(t *testing.T) {
148 g := gmg.NewWithT(t)
149
150 httpClient, err := rest.HTTPClientFor(restCfg)
151 g.Expect(err).NotTo(gmg.HaveOccurred())
152
153 crt := newCountingRoundTripper(httpClient.Transport)
154 httpClient.Transport = crt
155
156 lazyRestMapper, err := apiutil.NewDynamicRESTMapper(restCfg, httpClient)
157 g.Expect(err).NotTo(gmg.HaveOccurred())
158
159 g.Expect(crt.GetRequestCount()).To(gmg.Equal(0))
160
161 mapping, err := lazyRestMapper.RESTMapping(schema.GroupKind{Group: "apps", Kind: "deployment"})
162 g.Expect(err).NotTo(gmg.HaveOccurred())
163 g.Expect(mapping.GroupVersionKind.Kind).To(gmg.Equal("deployment"))
164 g.Expect(crt.GetRequestCount()).To(gmg.Equal(3))
165
166
167
168 mapping, err = lazyRestMapper.RESTMapping(schema.GroupKind{Group: "apps", Kind: "deployment"})
169 g.Expect(err).NotTo(gmg.HaveOccurred())
170 g.Expect(mapping.GroupVersionKind.Kind).To(gmg.Equal("deployment"))
171 g.Expect(crt.GetRequestCount()).To(gmg.Equal(3))
172
173 kind, err := lazyRestMapper.KindFor((schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployment"}))
174 g.Expect(err).NotTo(gmg.HaveOccurred())
175 g.Expect(kind.Kind).To(gmg.Equal("Deployment"))
176 g.Expect(crt.GetRequestCount()).To(gmg.Equal(3))
177
178 resource, err := lazyRestMapper.ResourceFor((schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployment"}))
179 g.Expect(err).NotTo(gmg.HaveOccurred())
180 g.Expect(resource.Resource).To(gmg.Equal("deployments"))
181 g.Expect(crt.GetRequestCount()).To(gmg.Equal(3))
182 })
183
184 t.Run("LazyRESTMapper should work correctly with empty versions list", func(t *testing.T) {
185 g := gmg.NewWithT(t)
186
187 httpClient, err := rest.HTTPClientFor(restCfg)
188 g.Expect(err).NotTo(gmg.HaveOccurred())
189
190 crt := newCountingRoundTripper(httpClient.Transport)
191 httpClient.Transport = crt
192
193 lazyRestMapper, err := apiutil.NewDynamicRESTMapper(restCfg, httpClient)
194 g.Expect(err).NotTo(gmg.HaveOccurred())
195
196 g.Expect(crt.GetRequestCount()).To(gmg.Equal(0))
197
198
199
200
201
202
203
204
205
206
207
208 mapping, err := lazyRestMapper.RESTMapping(schema.GroupKind{Group: "crew.example.com", Kind: "driver"})
209 g.Expect(err).NotTo(gmg.HaveOccurred())
210 g.Expect(mapping.GroupVersionKind.Kind).To(gmg.Equal("driver"))
211 g.Expect(crt.GetRequestCount()).To(gmg.Equal(4))
212
213
214 mapping, err = lazyRestMapper.RESTMapping(schema.GroupKind{Group: "crew.example.com", Kind: "driver"})
215 g.Expect(err).NotTo(gmg.HaveOccurred())
216 g.Expect(mapping.GroupVersionKind.Kind).To(gmg.Equal("driver"))
217 g.Expect(crt.GetRequestCount()).To(gmg.Equal(4))
218 })
219
220 t.Run("LazyRESTMapper should work correctly with multiple API group versions", func(t *testing.T) {
221 g := gmg.NewWithT(t)
222
223 httpClient, err := rest.HTTPClientFor(restCfg)
224 g.Expect(err).NotTo(gmg.HaveOccurred())
225
226 crt := newCountingRoundTripper(httpClient.Transport)
227 httpClient.Transport = crt
228
229 lazyRestMapper, err := apiutil.NewDynamicRESTMapper(restCfg, httpClient)
230 g.Expect(err).NotTo(gmg.HaveOccurred())
231
232 g.Expect(crt.GetRequestCount()).To(gmg.Equal(0))
233
234
235
236
237
238 mapping, err := lazyRestMapper.RESTMapping(schema.GroupKind{Group: "crew.example.com", Kind: "driver"}, "v1", "v2")
239 g.Expect(err).NotTo(gmg.HaveOccurred())
240 g.Expect(mapping.GroupVersionKind.Kind).To(gmg.Equal("driver"))
241 g.Expect(crt.GetRequestCount()).To(gmg.Equal(2))
242
243
244 mapping, err = lazyRestMapper.RESTMapping(schema.GroupKind{Group: "crew.example.com", Kind: "driver"}, "v1")
245 g.Expect(err).NotTo(gmg.HaveOccurred())
246 g.Expect(mapping.GroupVersionKind.Kind).To(gmg.Equal("driver"))
247 g.Expect(crt.GetRequestCount()).To(gmg.Equal(2))
248
249 mapping, err = lazyRestMapper.RESTMapping(schema.GroupKind{Group: "crew.example.com", Kind: "driver"})
250 g.Expect(err).NotTo(gmg.HaveOccurred())
251 g.Expect(mapping.GroupVersionKind.Kind).To(gmg.Equal("driver"))
252 g.Expect(crt.GetRequestCount()).To(gmg.Equal(2))
253 })
254
255 t.Run("LazyRESTMapper should work correctly with different API group versions", func(t *testing.T) {
256 g := gmg.NewWithT(t)
257
258 httpClient, err := rest.HTTPClientFor(restCfg)
259 g.Expect(err).NotTo(gmg.HaveOccurred())
260
261 crt := newCountingRoundTripper(httpClient.Transport)
262 httpClient.Transport = crt
263
264 lazyRestMapper, err := apiutil.NewDynamicRESTMapper(restCfg, httpClient)
265 g.Expect(err).NotTo(gmg.HaveOccurred())
266
267 g.Expect(crt.GetRequestCount()).To(gmg.Equal(0))
268
269
270
271
272 mapping, err := lazyRestMapper.RESTMapping(schema.GroupKind{Group: "crew.example.com", Kind: "driver"}, "v1")
273 g.Expect(err).NotTo(gmg.HaveOccurred())
274 g.Expect(mapping.GroupVersionKind.Kind).To(gmg.Equal("driver"))
275 g.Expect(crt.GetRequestCount()).To(gmg.Equal(1))
276
277
278
279
280 mapping, err = lazyRestMapper.RESTMapping(schema.GroupKind{Group: "crew.example.com", Kind: "driver"}, "v2")
281 g.Expect(err).NotTo(gmg.HaveOccurred())
282 g.Expect(mapping.GroupVersionKind.Kind).To(gmg.Equal("driver"))
283 g.Expect(crt.GetRequestCount()).To(gmg.Equal(2))
284
285
286 mapping, err = lazyRestMapper.RESTMapping(schema.GroupKind{Group: "crew.example.com", Kind: "driver"}, "v1")
287 g.Expect(err).NotTo(gmg.HaveOccurred())
288 g.Expect(mapping.GroupVersionKind.Kind).To(gmg.Equal("driver"))
289 g.Expect(crt.GetRequestCount()).To(gmg.Equal(2))
290
291 mapping, err = lazyRestMapper.RESTMapping(schema.GroupKind{Group: "crew.example.com", Kind: "driver"}, "v1", "v2")
292 g.Expect(err).NotTo(gmg.HaveOccurred())
293 g.Expect(mapping.GroupVersionKind.Kind).To(gmg.Equal("driver"))
294 g.Expect(crt.GetRequestCount()).To(gmg.Equal(2))
295 })
296
297 t.Run("LazyRESTMapper should return an error if the group doesn't exist", func(t *testing.T) {
298 g := gmg.NewWithT(t)
299
300
301
302 httpClient, err := rest.HTTPClientFor(restCfg)
303 g.Expect(err).NotTo(gmg.HaveOccurred())
304
305 crt := newCountingRoundTripper(httpClient.Transport)
306 httpClient.Transport = crt
307
308 lazyRestMapper, err := apiutil.NewDynamicRESTMapper(restCfg, httpClient)
309 g.Expect(err).NotTo(gmg.HaveOccurred())
310
311
312
313
314
315 _, err = lazyRestMapper.RESTMapping(schema.GroupKind{Group: "INVALID1"}, "v1")
316 g.Expect(err).To(gmg.HaveOccurred())
317 g.Expect(meta.IsNoMatchError(err)).To(gmg.BeTrue())
318 g.Expect(crt.GetRequestCount()).To(gmg.Equal(1))
319
320 _, err = lazyRestMapper.RESTMappings(schema.GroupKind{Group: "INVALID2"}, "v1")
321 g.Expect(err).To(gmg.HaveOccurred())
322 g.Expect(meta.IsNoMatchError(err)).To(gmg.BeTrue())
323 g.Expect(crt.GetRequestCount()).To(gmg.Equal(2))
324
325 _, err = lazyRestMapper.KindFor(schema.GroupVersionResource{Group: "INVALID3", Version: "v1"})
326 g.Expect(err).To(gmg.HaveOccurred())
327 g.Expect(meta.IsNoMatchError(err)).To(gmg.BeTrue())
328 g.Expect(crt.GetRequestCount()).To(gmg.Equal(3))
329
330 _, err = lazyRestMapper.KindsFor(schema.GroupVersionResource{Group: "INVALID4", Version: "v1"})
331 g.Expect(err).To(gmg.HaveOccurred())
332 g.Expect(meta.IsNoMatchError(err)).To(gmg.BeTrue())
333 g.Expect(crt.GetRequestCount()).To(gmg.Equal(4))
334
335 _, err = lazyRestMapper.ResourceFor(schema.GroupVersionResource{Group: "INVALID5", Version: "v1"})
336 g.Expect(err).To(gmg.HaveOccurred())
337 g.Expect(meta.IsNoMatchError(err)).To(gmg.BeTrue())
338 g.Expect(crt.GetRequestCount()).To(gmg.Equal(5))
339
340 _, err = lazyRestMapper.ResourcesFor(schema.GroupVersionResource{Group: "INVALID6", Version: "v1"})
341 g.Expect(err).To(gmg.HaveOccurred())
342 g.Expect(meta.IsNoMatchError(err)).To(gmg.BeTrue())
343 g.Expect(crt.GetRequestCount()).To(gmg.Equal(6))
344
345
346
347
348
349
350 _, err = lazyRestMapper.RESTMapping(schema.GroupKind{Group: "INVALID7"})
351 g.Expect(err).To(beNoMatchError())
352 g.Expect(crt.GetRequestCount()).To(gmg.Equal(8))
353
354 _, err = lazyRestMapper.RESTMappings(schema.GroupKind{Group: "INVALID8"})
355 g.Expect(err).To(beNoMatchError())
356 g.Expect(crt.GetRequestCount()).To(gmg.Equal(10))
357
358 _, err = lazyRestMapper.KindFor(schema.GroupVersionResource{Group: "INVALID9"})
359 g.Expect(err).To(beNoMatchError())
360 g.Expect(crt.GetRequestCount()).To(gmg.Equal(12))
361
362 _, err = lazyRestMapper.KindsFor(schema.GroupVersionResource{Group: "INVALID10"})
363 g.Expect(err).To(beNoMatchError())
364 g.Expect(crt.GetRequestCount()).To(gmg.Equal(14))
365
366 _, err = lazyRestMapper.ResourceFor(schema.GroupVersionResource{Group: "INVALID11"})
367 g.Expect(err).To(beNoMatchError())
368 g.Expect(crt.GetRequestCount()).To(gmg.Equal(16))
369
370 _, err = lazyRestMapper.ResourcesFor(schema.GroupVersionResource{Group: "INVALID12"})
371 g.Expect(err).To(beNoMatchError())
372 g.Expect(crt.GetRequestCount()).To(gmg.Equal(18))
373 })
374
375 t.Run("LazyRESTMapper should return an error if a resource doesn't exist", func(t *testing.T) {
376 g := gmg.NewWithT(t)
377
378
379
380 httpClient, err := rest.HTTPClientFor(restCfg)
381 g.Expect(err).NotTo(gmg.HaveOccurred())
382
383 crt := newCountingRoundTripper(httpClient.Transport)
384 httpClient.Transport = crt
385
386 lazyRestMapper, err := apiutil.NewDynamicRESTMapper(restCfg, httpClient)
387 g.Expect(err).NotTo(gmg.HaveOccurred())
388
389 _, err = lazyRestMapper.RESTMapping(schema.GroupKind{Group: "apps", Kind: "INVALID"}, "v1")
390 g.Expect(err).To(gmg.HaveOccurred())
391 g.Expect(meta.IsNoMatchError(err)).To(gmg.BeTrue())
392 g.Expect(crt.GetRequestCount()).To(gmg.Equal(1))
393
394 _, err = lazyRestMapper.RESTMappings(schema.GroupKind{Group: "", Kind: "INVALID"}, "v1")
395 g.Expect(err).To(gmg.HaveOccurred())
396 g.Expect(meta.IsNoMatchError(err)).To(gmg.BeTrue())
397 g.Expect(crt.GetRequestCount()).To(gmg.Equal(2))
398
399 _, err = lazyRestMapper.KindFor(schema.GroupVersionResource{Group: "networking.k8s.io", Version: "v1", Resource: "INVALID"})
400 g.Expect(err).To(gmg.HaveOccurred())
401 g.Expect(meta.IsNoMatchError(err)).To(gmg.BeTrue())
402 g.Expect(crt.GetRequestCount()).To(gmg.Equal(3))
403
404 _, err = lazyRestMapper.KindsFor(schema.GroupVersionResource{Group: "authentication.k8s.io", Version: "v1", Resource: "INVALID"})
405 g.Expect(err).To(gmg.HaveOccurred())
406 g.Expect(meta.IsNoMatchError(err)).To(gmg.BeTrue())
407 g.Expect(crt.GetRequestCount()).To(gmg.Equal(4))
408
409 _, err = lazyRestMapper.ResourceFor(schema.GroupVersionResource{Group: "scheduling.k8s.io", Version: "v1", Resource: "INVALID"})
410 g.Expect(err).To(gmg.HaveOccurred())
411 g.Expect(meta.IsNoMatchError(err)).To(gmg.BeTrue())
412 g.Expect(crt.GetRequestCount()).To(gmg.Equal(5))
413
414 _, err = lazyRestMapper.ResourcesFor(schema.GroupVersionResource{Group: "policy", Version: "v1", Resource: "INVALID"})
415 g.Expect(err).To(gmg.HaveOccurred())
416 g.Expect(meta.IsNoMatchError(err)).To(gmg.BeTrue())
417 g.Expect(crt.GetRequestCount()).To(gmg.Equal(6))
418 })
419
420 t.Run("LazyRESTMapper should return an error if the version doesn't exist", func(t *testing.T) {
421 g := gmg.NewWithT(t)
422
423
424
425 httpClient, err := rest.HTTPClientFor(restCfg)
426 g.Expect(err).NotTo(gmg.HaveOccurred())
427
428 crt := newCountingRoundTripper(httpClient.Transport)
429 httpClient.Transport = crt
430
431 lazyRestMapper, err := apiutil.NewDynamicRESTMapper(restCfg, httpClient)
432 g.Expect(err).NotTo(gmg.HaveOccurred())
433
434 _, err = lazyRestMapper.RESTMapping(schema.GroupKind{Group: "apps", Kind: "deployment"}, "INVALID")
435 g.Expect(err).To(gmg.HaveOccurred())
436 g.Expect(meta.IsNoMatchError(err)).To(gmg.BeTrue())
437 g.Expect(crt.GetRequestCount()).To(gmg.Equal(1))
438
439 _, err = lazyRestMapper.RESTMappings(schema.GroupKind{Group: "", Kind: "pod"}, "INVALID")
440 g.Expect(err).To(gmg.HaveOccurred())
441 g.Expect(meta.IsNoMatchError(err)).To(gmg.BeTrue())
442 g.Expect(crt.GetRequestCount()).To(gmg.Equal(2))
443
444 _, err = lazyRestMapper.KindFor(schema.GroupVersionResource{Group: "networking.k8s.io", Version: "INVALID", Resource: "ingresses"})
445 g.Expect(err).To(gmg.HaveOccurred())
446 g.Expect(meta.IsNoMatchError(err)).To(gmg.BeTrue())
447 g.Expect(crt.GetRequestCount()).To(gmg.Equal(3))
448
449 _, err = lazyRestMapper.KindsFor(schema.GroupVersionResource{Group: "authentication.k8s.io", Version: "INVALID", Resource: "tokenreviews"})
450 g.Expect(err).To(gmg.HaveOccurred())
451 g.Expect(meta.IsNoMatchError(err)).To(gmg.BeTrue())
452 g.Expect(crt.GetRequestCount()).To(gmg.Equal(4))
453
454 _, err = lazyRestMapper.ResourceFor(schema.GroupVersionResource{Group: "scheduling.k8s.io", Version: "INVALID", Resource: "priorityclasses"})
455 g.Expect(err).To(gmg.HaveOccurred())
456 g.Expect(meta.IsNoMatchError(err)).To(gmg.BeTrue())
457 g.Expect(crt.GetRequestCount()).To(gmg.Equal(5))
458
459 _, err = lazyRestMapper.ResourcesFor(schema.GroupVersionResource{Group: "policy", Version: "INVALID", Resource: "poddisruptionbudgets"})
460 g.Expect(err).To(gmg.HaveOccurred())
461 g.Expect(meta.IsNoMatchError(err)).To(gmg.BeTrue())
462 g.Expect(crt.GetRequestCount()).To(gmg.Equal(6))
463 })
464
465 t.Run("LazyRESTMapper should work correctly if the version isn't specified", func(t *testing.T) {
466 g := gmg.NewWithT(t)
467
468 httpClient, err := rest.HTTPClientFor(restCfg)
469 g.Expect(err).NotTo(gmg.HaveOccurred())
470
471 lazyRestMapper, err := apiutil.NewDynamicRESTMapper(restCfg, httpClient)
472 g.Expect(err).NotTo(gmg.HaveOccurred())
473
474 kind, err := lazyRestMapper.KindFor(schema.GroupVersionResource{Group: "networking.k8s.io", Resource: "ingress"})
475 g.Expect(err).NotTo(gmg.HaveOccurred())
476 g.Expect(kind.Version).ToNot(gmg.BeEmpty())
477
478 kinds, err := lazyRestMapper.KindsFor(schema.GroupVersionResource{Group: "authentication.k8s.io", Resource: "tokenreviews"})
479 g.Expect(err).NotTo(gmg.HaveOccurred())
480 g.Expect(kinds).ToNot(gmg.BeEmpty())
481 g.Expect(kinds[0].Version).ToNot(gmg.BeEmpty())
482
483 resorce, err := lazyRestMapper.ResourceFor(schema.GroupVersionResource{Group: "scheduling.k8s.io", Resource: "priorityclasses"})
484 g.Expect(err).NotTo(gmg.HaveOccurred())
485 g.Expect(resorce.Version).ToNot(gmg.BeEmpty())
486
487 resorces, err := lazyRestMapper.ResourcesFor(schema.GroupVersionResource{Group: "policy", Resource: "poddisruptionbudgets"})
488 g.Expect(err).NotTo(gmg.HaveOccurred())
489 g.Expect(kinds).ToNot(gmg.BeEmpty())
490 g.Expect(resorces[0].Version).ToNot(gmg.BeEmpty())
491 })
492
493 t.Run("LazyRESTMapper can fetch CRDs if they were created at runtime", func(t *testing.T) {
494 g := gmg.NewWithT(t)
495
496
497
498
499
500
501
502 httpClient, err := rest.HTTPClientFor(restCfg)
503 g.Expect(err).NotTo(gmg.HaveOccurred())
504
505 crt := newCountingRoundTripper(httpClient.Transport)
506 httpClient.Transport = crt
507
508 lazyRestMapper, err := apiutil.NewDynamicRESTMapper(restCfg, httpClient)
509 g.Expect(err).NotTo(gmg.HaveOccurred())
510
511
512 g.Expect(crt.GetRequestCount()).To(gmg.Equal(0))
513
514
515
516
517
518
519
520
521 mapping, err := lazyRestMapper.RESTMapping(schema.GroupKind{Group: "crew.example.com", Kind: "driver"})
522 g.Expect(err).NotTo(gmg.HaveOccurred())
523 g.Expect(mapping.GroupVersionKind.Kind).To(gmg.Equal("driver"))
524 g.Expect(crt.GetRequestCount()).To(gmg.Equal(4))
525
526 s := scheme.Scheme
527 err = apiextensionsv1.AddToScheme(s)
528 g.Expect(err).NotTo(gmg.HaveOccurred())
529
530 c, err := client.New(restCfg, client.Options{Scheme: s})
531 g.Expect(err).NotTo(gmg.HaveOccurred())
532
533
534 createNewCRD(context.TODO(), g, c, "crew.example.com", "Rider", "riders")
535
536
537 g.Eventually(func() error {
538 _, err := lazyRestMapper.RESTMapping(schema.GroupKind{Group: "crew.example.com", Kind: "rider"})
539 return err
540 }).Should(gmg.Succeed())
541
542
543
544
545
546
547
548
549 mapping, err = lazyRestMapper.RESTMapping(schema.GroupKind{Group: "crew.example.com", Kind: "rider"})
550 g.Expect(err).NotTo(gmg.HaveOccurred())
551 g.Expect(mapping.GroupVersionKind.Kind).To(gmg.Equal("rider"))
552 })
553
554 t.Run("LazyRESTMapper should invalidate the group cache if a version is not found", func(t *testing.T) {
555 g := gmg.NewWithT(t)
556 ctx := context.Background()
557
558 httpClient, err := rest.HTTPClientFor(restCfg)
559 g.Expect(err).NotTo(gmg.HaveOccurred())
560
561 crt := newCountingRoundTripper(httpClient.Transport)
562 httpClient.Transport = crt
563
564 lazyRestMapper, err := apiutil.NewDynamicRESTMapper(restCfg, httpClient)
565 g.Expect(err).NotTo(gmg.HaveOccurred())
566
567 s := scheme.Scheme
568 err = apiextensionsv1.AddToScheme(s)
569 g.Expect(err).NotTo(gmg.HaveOccurred())
570
571 c, err := client.New(restCfg, client.Options{Scheme: s})
572 g.Expect(err).NotTo(gmg.HaveOccurred())
573
574
575 group := "inventory.example.com"
576 kind := "Taxi"
577 plural := "taxis"
578 crdName := plural + "." + group
579
580
581 crd := newCRD(ctx, g, c, group, kind, plural)
582 v1alpha1 := crd.Spec.Versions[0]
583 v1alpha1.Name = "v1alpha1"
584 v1alpha1.Storage = false
585 v1alpha1.Served = true
586 v1 := crd.Spec.Versions[0]
587 v1.Name = "v1"
588 v1.Storage = true
589 v1.Served = true
590 crd.Spec.Versions = []apiextensionsv1.CustomResourceDefinitionVersion{v1alpha1, v1}
591 g.Expect(c.Create(ctx, crd)).To(gmg.Succeed())
592 t.Cleanup(func() {
593 g.Expect(c.Delete(ctx, crd)).To(gmg.Succeed())
594 })
595
596
597 discHTTP, err := rest.HTTPClientFor(restCfg)
598 g.Expect(err).NotTo(gmg.HaveOccurred())
599 discClient, err := discovery.NewDiscoveryClientForConfigAndClient(restCfg, discHTTP)
600 g.Expect(err).NotTo(gmg.HaveOccurred())
601 g.Eventually(func(g gmg.Gomega) {
602 _, err = discClient.ServerResourcesForGroupVersion(group + "/v1")
603 g.Expect(err).NotTo(gmg.HaveOccurred())
604 }).Should(gmg.Succeed(), "v1 should be available")
605
606
607 g.Expect(crt.GetRequestCount()).To(gmg.Equal(0))
608
609
610
611
612
613
614
615
616
617 mapping, err := lazyRestMapper.RESTMapping(schema.GroupKind{Group: group, Kind: kind})
618 g.Expect(err).NotTo(gmg.HaveOccurred())
619 g.Expect(mapping.GroupVersionKind.Kind).To(gmg.Equal(kind))
620 g.Expect(crt.GetRequestCount()).To(gmg.Equal(4))
621 crt.Reset()
622
623
624 _, err = lazyRestMapper.RESTMapping(schema.GroupKind{Group: group, Kind: kind}, "v1alpha1")
625 g.Expect(err).NotTo(gmg.HaveOccurred())
626 g.Expect(crt.GetRequestCount()).To(gmg.Equal(0))
627
628
629 g.Expect(c.Get(ctx, types.NamespacedName{Name: crdName}, crd)).To(gmg.Succeed())
630 for _, version := range crd.Spec.Versions {
631 if version.Name == "v1" {
632 v1 = version
633 break
634 }
635 }
636 crd.Spec.Versions = []apiextensionsv1.CustomResourceDefinitionVersion{v1}
637 g.Expect(c.Update(ctx, crd)).To(gmg.Succeed())
638
639
640 g.Eventually(func(g gmg.Gomega) {
641 _, err = discClient.ServerResourcesForGroupVersion(group + "/v1alpha1")
642 g.Expect(apierrors.IsNotFound(err)).To(gmg.BeTrue(), "v1alpha1 should not be available anymore")
643 }).Should(gmg.Succeed())
644
645
646 _, err = lazyRestMapper.RESTMapping(schema.GroupKind{Group: group, Kind: kind}, "v1alpha1")
647 g.Expect(err).NotTo(gmg.HaveOccurred())
648 g.Expect(crt.GetRequestCount()).To(gmg.Equal(0))
649
650
651
652
653
654
655 _, err = lazyRestMapper.RESTMapping(schema.GroupKind{Group: group, Kind: "Limo"})
656 g.Expect(err).To(beNoMatchError())
657 g.Expect(crt.GetRequestCount()).To(gmg.Equal(2))
658 crt.Reset()
659
660
661
662 _, err = lazyRestMapper.RESTMapping(schema.GroupKind{Group: group, Kind: kind}, "v1alpha1")
663 g.Expect(err).To(beNoMatchError())
664 g.Expect(crt.GetRequestCount()).To(gmg.Equal(1))
665
666
667
668 mapping, err = lazyRestMapper.RESTMapping(schema.GroupKind{Group: group, Kind: kind})
669 g.Expect(err).NotTo(gmg.HaveOccurred())
670 g.Expect(mapping.Resource.Version).To(gmg.Equal("v1"))
671 })
672 }
673
674
675 func createNewCRD(ctx context.Context, g gmg.Gomega, c client.Client, group, kind, plural string) *apiextensionsv1.CustomResourceDefinition {
676 newCRD := newCRD(ctx, g, c, group, kind, plural)
677 g.Expect(c.Create(ctx, newCRD)).To(gmg.Succeed())
678
679 return newCRD
680 }
681
682
683 func newCRD(ctx context.Context, g gmg.Gomega, c client.Client, group, kind, plural string) *apiextensionsv1.CustomResourceDefinition {
684 crd := &apiextensionsv1.CustomResourceDefinition{}
685 err := c.Get(ctx, types.NamespacedName{Name: "drivers.crew.example.com"}, crd)
686 g.Expect(err).NotTo(gmg.HaveOccurred())
687 g.Expect(crd.Spec.Names.Kind).To(gmg.Equal("Driver"))
688
689 newCRD := &apiextensionsv1.CustomResourceDefinition{}
690 crd.DeepCopyInto(newCRD)
691 newCRD.Spec.Group = group
692 newCRD.Name = plural + "." + group
693 newCRD.Spec.Names = apiextensionsv1.CustomResourceDefinitionNames{
694 Kind: kind,
695 Plural: plural,
696 }
697 newCRD.ResourceVersion = ""
698
699 return newCRD
700 }
701
702 func beNoMatchError() gomegatypes.GomegaMatcher {
703 return &errorMatcher{
704 checkFunc: meta.IsNoMatchError,
705 message: "NoMatch",
706 }
707 }
708
709 type errorMatcher struct {
710 checkFunc func(error) bool
711 message string
712 }
713
714 func (e *errorMatcher) Match(actual interface{}) (success bool, err error) {
715 if actual == nil {
716 return false, nil
717 }
718
719 actualErr, actualOk := actual.(error)
720 if !actualOk {
721 return false, fmt.Errorf("expected an error-type. got:\n%s", format.Object(actual, 1))
722 }
723
724 return e.checkFunc(actualErr), nil
725 }
726
727 func (e *errorMatcher) FailureMessage(actual interface{}) (message string) {
728 return format.Message(actual, fmt.Sprintf("to be %s error", e.message))
729 }
730
731 func (e *errorMatcher) NegatedFailureMessage(actual interface{}) (message string) {
732 return format.Message(actual, fmt.Sprintf("not to be %s error", e.message))
733 }
734
View as plain text