1
16
17 package aggregator
18
19 import (
20 "encoding/json"
21 "io"
22 "net/http"
23 "net/http/httptest"
24 "testing"
25 "time"
26
27 "bytes"
28
29 v1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
30 "k8s.io/kube-openapi/pkg/common"
31 "k8s.io/kube-openapi/pkg/validation/spec"
32 )
33
34 func TestBasicPathsMerged(t *testing.T) {
35 mux := http.NewServeMux()
36 delegationHandlers := []http.Handler{
37 &openAPIHandler{
38 openapi: &spec.Swagger{
39 SwaggerProps: spec.SwaggerProps{
40 Paths: &spec.Paths{
41 Paths: map[string]spec.PathItem{
42 "/apis/foo/v1/": {},
43 },
44 },
45 },
46 },
47 },
48 }
49 buildAndRegisterSpecAggregator(delegationHandlers, mux)
50
51 swagger, err := fetchOpenAPI(mux)
52 if err != nil {
53 t.Error(err)
54 }
55 expectPath(t, swagger, "/apis/foo/v1/")
56 expectPath(t, swagger, "/apis/apiregistration.k8s.io/v1/")
57 }
58
59 func TestAddUpdateAPIService(t *testing.T) {
60 mux := http.NewServeMux()
61 var delegationHandlers []http.Handler
62 delegate1 := &openAPIHandler{openapi: &spec.Swagger{
63 SwaggerProps: spec.SwaggerProps{
64 Paths: &spec.Paths{
65 Paths: map[string]spec.PathItem{
66 "/apis/foo/v1": {},
67 },
68 },
69 },
70 }}
71 delegationHandlers = append(delegationHandlers, delegate1)
72 s := buildAndRegisterSpecAggregator(delegationHandlers, mux)
73
74 apiService := &v1.APIService{
75 Spec: v1.APIServiceSpec{
76 Group: "apiservicegroup",
77 Version: "v1",
78 Service: &v1.ServiceReference{Name: "dummy"},
79 },
80 }
81 apiService.Name = "apiservice"
82
83 handler := &openAPIHandler{openapi: &spec.Swagger{
84 SwaggerProps: spec.SwaggerProps{
85 Paths: &spec.Paths{
86 Paths: map[string]spec.PathItem{
87 "/apis/apiservicegroup/v1/path1": {},
88 },
89 },
90 },
91 }}
92
93 if err := s.AddUpdateAPIService(apiService, handler); err != nil {
94 t.Error(err)
95 }
96 if err := s.UpdateAPIServiceSpec(apiService.Name); err != nil {
97 t.Error(err)
98 }
99
100 swagger, err := fetchOpenAPI(mux)
101 if err != nil {
102 t.Error(err)
103 }
104
105 expectPath(t, swagger, "/apis/apiservicegroup/v1/path1")
106 expectPath(t, swagger, "/apis/apiregistration.k8s.io/v1/")
107
108 t.Log("Update APIService OpenAPI")
109 handler.openapi = &spec.Swagger{
110 SwaggerProps: spec.SwaggerProps{
111 Paths: &spec.Paths{
112 Paths: map[string]spec.PathItem{
113 "/apis/apiservicegroup/v1/path2": {},
114 },
115 },
116 },
117 }
118 if err := s.UpdateAPIServiceSpec(apiService.Name); err != nil {
119 t.Error(err)
120 }
121
122 swagger, err = fetchOpenAPI(mux)
123 if err != nil {
124 t.Error(err)
125 }
126
127
128 expectPath(t, swagger, "/apis/apiservicegroup/v1/path2")
129 expectNoPath(t, swagger, "/apis/apiservicegroup/v1/path1")
130 expectPath(t, swagger, "/apis/apiregistration.k8s.io/v1/")
131 }
132
133
134
135
136 func TestAPIServiceOpenAPIServiceMismatch(t *testing.T) {
137 mux := http.NewServeMux()
138 var delegationHandlers []http.Handler
139 delegate1 := &openAPIHandler{openapi: &spec.Swagger{
140 SwaggerProps: spec.SwaggerProps{
141 Paths: &spec.Paths{
142 Paths: map[string]spec.PathItem{
143 "/apis/foo/v1/": {},
144 },
145 },
146 },
147 }}
148 delegationHandlers = append(delegationHandlers, delegate1)
149
150 s := buildAndRegisterSpecAggregator(delegationHandlers, mux)
151
152 apiService := &v1.APIService{
153 Spec: v1.APIServiceSpec{
154 Group: "apiservicegroup",
155 Version: "v1",
156 Service: &v1.ServiceReference{Name: "dummy"},
157 },
158 }
159 apiService.Name = "apiservice"
160
161 apiService2 := &v1.APIService{
162 Spec: v1.APIServiceSpec{
163 Group: "apiservicegroup",
164 Version: "v2",
165 Service: &v1.ServiceReference{Name: "dummy2"},
166 },
167 }
168 apiService2.Name = "apiservice2"
169
170 handler := &openAPIHandler{openapi: &spec.Swagger{
171 SwaggerProps: spec.SwaggerProps{
172 Paths: &spec.Paths{
173 Paths: map[string]spec.PathItem{
174 "/apis/apiservicegroup/v1/": {},
175 "/apis/apiservicegroup/v1beta1/": {},
176 },
177 },
178 },
179 }}
180
181 handler2 := &openAPIHandler{openapi: &spec.Swagger{
182 SwaggerProps: spec.SwaggerProps{
183 Paths: &spec.Paths{
184 Paths: map[string]spec.PathItem{
185 "/apis/a/": {},
186 "/apis/apiservicegroup/v1/": {},
187 "/apis/apiservicegroup/v2/": {},
188 },
189 },
190 },
191 }}
192
193 if err := s.AddUpdateAPIService(apiService, handler); err != nil {
194 t.Error(err)
195 }
196 if err := s.UpdateAPIServiceSpec(apiService.Name); err != nil {
197 t.Error(err)
198 }
199
200 if err := s.AddUpdateAPIService(apiService2, handler2); err != nil {
201 t.Error(err)
202 }
203 if err := s.UpdateAPIServiceSpec(apiService2.Name); err != nil {
204 t.Error(err)
205 }
206
207 swagger, err := fetchOpenAPI(mux)
208 if err != nil {
209 t.Error(err)
210 }
211 expectPath(t, swagger, "/apis/apiservicegroup/v1/")
212 expectPath(t, swagger, "/apis/apiservicegroup/v2/")
213 expectPath(t, swagger, "/apis/apiregistration.k8s.io/v1/")
214 expectNoPath(t, swagger, "/apis/a/")
215 expectNoPath(t, swagger, "/apis/apiservicegroup/v1beta1/")
216
217 t.Logf("Remove APIService %s", apiService.Name)
218 s.RemoveAPIService(apiService.Name)
219
220 swagger, err = fetchOpenAPI(mux)
221 if err != nil {
222 t.Error(err)
223 }
224
225 expectNoPath(t, swagger, "/apis/apiservicegroup/v1")
226 expectPath(t, swagger, "/apis/apiregistration.k8s.io/v1/")
227 expectNoPath(t, swagger, "/apis/a")
228 }
229
230 func TestAddRemoveAPIService(t *testing.T) {
231 mux := http.NewServeMux()
232 var delegationHandlers []http.Handler
233 delegate1 := &openAPIHandler{openapi: &spec.Swagger{
234 SwaggerProps: spec.SwaggerProps{
235 Paths: &spec.Paths{
236 Paths: map[string]spec.PathItem{
237 "/apis/foo/v1/": {},
238 },
239 },
240 },
241 }}
242 delegationHandlers = append(delegationHandlers, delegate1)
243
244 s := buildAndRegisterSpecAggregator(delegationHandlers, mux)
245
246 apiService := &v1.APIService{
247 Spec: v1.APIServiceSpec{
248 Group: "apiservicegroup",
249 Version: "v1",
250 Service: &v1.ServiceReference{Name: "dummy"},
251 },
252 }
253 apiService.Name = "apiservice"
254
255 handler := &openAPIHandler{openapi: &spec.Swagger{
256 SwaggerProps: spec.SwaggerProps{
257 Paths: &spec.Paths{
258 Paths: map[string]spec.PathItem{
259 "/apis/apiservicegroup/v1/": {},
260 },
261 },
262 },
263 }}
264
265 if err := s.AddUpdateAPIService(apiService, handler); err != nil {
266 t.Error(err)
267 }
268 if err := s.UpdateAPIServiceSpec(apiService.Name); err != nil {
269 t.Error(err)
270 }
271
272 swagger, err := fetchOpenAPI(mux)
273 if err != nil {
274 t.Error(err)
275 }
276 expectPath(t, swagger, "/apis/apiservicegroup/v1/")
277 expectPath(t, swagger, "/apis/apiregistration.k8s.io/v1/")
278
279 t.Logf("Remove APIService %s", apiService.Name)
280 s.RemoveAPIService(apiService.Name)
281
282 swagger, err = fetchOpenAPI(mux)
283 if err != nil {
284 t.Error(err)
285 }
286
287 expectNoPath(t, swagger, "/apis/apiservicegroup/v1/")
288 expectPath(t, swagger, "/apis/apiregistration.k8s.io/v1/")
289 }
290
291 func TestUpdateAPIService(t *testing.T) {
292 mux := http.NewServeMux()
293 var delegationHandlers []http.Handler
294 delegate1 := &openAPIHandler{openapi: &spec.Swagger{
295 SwaggerProps: spec.SwaggerProps{
296 Paths: &spec.Paths{
297 Paths: map[string]spec.PathItem{
298 "/apis/foo/v1/": {},
299 },
300 },
301 },
302 }}
303 delegationHandlers = append(delegationHandlers, delegate1)
304
305 s := buildAndRegisterSpecAggregator(delegationHandlers, mux)
306
307 apiService := &v1.APIService{
308 Spec: v1.APIServiceSpec{
309 Group: "apiservicegroup",
310 Version: "v1",
311 Service: &v1.ServiceReference{Name: "dummy"},
312 },
313 }
314 apiService.Name = "apiservice"
315
316 handler := &openAPIHandler{openapi: &spec.Swagger{
317 SwaggerProps: spec.SwaggerProps{
318 Paths: &spec.Paths{
319 Paths: map[string]spec.PathItem{
320 "/apis/apiservicegroup/v1/": {},
321 },
322 },
323 },
324 }}
325
326 handler2 := &openAPIHandler{openapi: &spec.Swagger{
327 SwaggerProps: spec.SwaggerProps{
328 Paths: &spec.Paths{
329 Paths: map[string]spec.PathItem{},
330 },
331 },
332 }}
333
334 if err := s.AddUpdateAPIService(apiService, handler); err != nil {
335 t.Error(err)
336 }
337 if err := s.UpdateAPIServiceSpec(apiService.Name); err != nil {
338 t.Error(err)
339 }
340
341 swagger, err := fetchOpenAPI(mux)
342 if err != nil {
343 t.Error(err)
344 }
345 expectPath(t, swagger, "/apis/apiservicegroup/v1/")
346 expectPath(t, swagger, "/apis/apiregistration.k8s.io/v1/")
347
348 t.Logf("Updating APIService %s", apiService.Name)
349 if err := s.AddUpdateAPIService(apiService, handler2); err != nil {
350 t.Error(err)
351 }
352 if err := s.UpdateAPIServiceSpec(apiService.Name); err != nil {
353 t.Error(err)
354 }
355
356 swagger, err = fetchOpenAPI(mux)
357 if err != nil {
358 t.Error(err)
359 }
360
361 expectNoPath(t, swagger, "/apis/apiservicegroup/v1/")
362 expectPath(t, swagger, "/apis/apiregistration.k8s.io/v1/")
363 }
364
365 func TestFailingAPIServiceSkippedAggregation(t *testing.T) {
366 mux := http.NewServeMux()
367 var delegationHandlers []http.Handler
368 delegate1 := &openAPIHandler{openapi: &spec.Swagger{
369 SwaggerProps: spec.SwaggerProps{
370 Paths: &spec.Paths{
371 Paths: map[string]spec.PathItem{
372 "/apis/foo/v1/": {},
373 },
374 },
375 },
376 }}
377 delegationHandlers = append(delegationHandlers, delegate1)
378
379 s := buildAndRegisterSpecAggregator(delegationHandlers, mux)
380
381 apiServiceFailed := &v1.APIService{
382 Spec: v1.APIServiceSpec{
383 Group: "failed",
384 Version: "v1",
385 Service: &v1.ServiceReference{Name: "dummy"},
386 },
387 }
388 apiServiceFailed.Name = "apiserviceFailed"
389
390 handlerFailed := &openAPIHandler{
391 returnErr: true,
392 openapi: &spec.Swagger{
393 SwaggerProps: spec.SwaggerProps{
394 Paths: &spec.Paths{
395 Paths: map[string]spec.PathItem{
396 "/apis/failed/v1/": {},
397 },
398 },
399 },
400 },
401 }
402
403 apiServiceSuccess := &v1.APIService{
404 Spec: v1.APIServiceSpec{
405 Group: "success",
406 Version: "v1",
407 Service: &v1.ServiceReference{Name: "dummy2"},
408 },
409 }
410 apiServiceSuccess.Name = "apiserviceSuccess"
411
412 handlerSuccess := &openAPIHandler{
413 openapi: &spec.Swagger{
414 SwaggerProps: spec.SwaggerProps{
415 Paths: &spec.Paths{
416 Paths: map[string]spec.PathItem{
417 "/apis/success/v1/": {},
418 },
419 },
420 },
421 },
422 }
423
424 if err := s.AddUpdateAPIService(apiServiceSuccess, handlerSuccess); err != nil {
425 t.Error(err)
426 }
427 if err := s.AddUpdateAPIService(apiServiceFailed, handlerFailed); err != nil {
428 t.Error(err)
429 }
430 if err := s.UpdateAPIServiceSpec(apiServiceSuccess.Name); err != nil {
431 t.Error(err)
432 }
433 err := s.UpdateAPIServiceSpec(apiServiceFailed.Name)
434 if err == nil {
435 t.Errorf("Expected updating failing apiService %s to return error", apiServiceFailed.Name)
436 }
437
438 swagger, err := fetchOpenAPI(mux)
439 if err != nil {
440 t.Error(err)
441 }
442 expectPath(t, swagger, "/apis/foo/v1/")
443 expectNoPath(t, swagger, "/apis/failed/v1/")
444 expectPath(t, swagger, "/apis/success/v1/")
445 }
446
447 func TestAPIServiceFailSuccessTransition(t *testing.T) {
448 mux := http.NewServeMux()
449 var delegationHandlers []http.Handler
450 delegate1 := &openAPIHandler{openapi: &spec.Swagger{
451 SwaggerProps: spec.SwaggerProps{
452 Paths: &spec.Paths{
453 Paths: map[string]spec.PathItem{
454 "/apis/foo/v1/": {},
455 },
456 },
457 },
458 }}
459 delegationHandlers = append(delegationHandlers, delegate1)
460
461 s := buildAndRegisterSpecAggregator(delegationHandlers, mux)
462
463 apiService := &v1.APIService{
464 Spec: v1.APIServiceSpec{
465 Group: "apiservicegroup",
466 Version: "v1",
467 Service: &v1.ServiceReference{Name: "dummy"},
468 },
469 }
470 apiService.Name = "apiservice"
471
472 handler := &openAPIHandler{
473 returnErr: true,
474 openapi: &spec.Swagger{
475 SwaggerProps: spec.SwaggerProps{
476 Paths: &spec.Paths{
477 Paths: map[string]spec.PathItem{
478 "/apis/apiservicegroup/v1/": {},
479 },
480 },
481 },
482 },
483 }
484
485 if err := s.AddUpdateAPIService(apiService, handler); err != nil {
486 t.Error(err)
487 }
488 if err := s.UpdateAPIServiceSpec(apiService.Name); err == nil {
489 t.Errorf("Expected error for when updating spec for failing apiservice")
490 }
491
492 swagger, err := fetchOpenAPI(mux)
493 if err != nil {
494 t.Error(err)
495 }
496 expectPath(t, swagger, "/apis/foo/v1/")
497 expectNoPath(t, swagger, "/apis/apiservicegroup/v1/")
498
499 t.Log("Transition APIService to not return error")
500 handler.returnErr = false
501 err = s.UpdateAPIServiceSpec(apiService.Name)
502 if err != nil {
503 t.Error(err)
504 }
505 swagger, err = fetchOpenAPI(mux)
506 if err != nil {
507 t.Error(err)
508 }
509 expectPath(t, swagger, "/apis/foo/v1/")
510 expectPath(t, swagger, "/apis/apiservicegroup/v1/")
511 }
512
513 func TestFailingAPIServiceDoesNotBlockAdd(t *testing.T) {
514 mux := http.NewServeMux()
515 var delegationHandlers []http.Handler
516 delegate1 := &openAPIHandler{openapi: &spec.Swagger{
517 SwaggerProps: spec.SwaggerProps{
518 Paths: &spec.Paths{
519 Paths: map[string]spec.PathItem{
520 "/apis/foo/v1/": {},
521 },
522 },
523 },
524 }}
525 delegationHandlers = append(delegationHandlers, delegate1)
526
527 s := buildAndRegisterSpecAggregator(delegationHandlers, mux)
528
529 apiServiceFailed := &v1.APIService{
530 Spec: v1.APIServiceSpec{
531 Group: "failed",
532 Version: "v1",
533 Service: &v1.ServiceReference{Name: "dummy"},
534 },
535 }
536 apiServiceFailed.Name = "apiserviceFailed"
537
538
539
540 handlerFailed := &openAPIHandler{
541 delaySeconds: 5,
542 returnErr: true,
543 openapi: &spec.Swagger{
544 SwaggerProps: spec.SwaggerProps{
545 Paths: &spec.Paths{
546 Paths: map[string]spec.PathItem{
547 "/apis/failed/v1/": {},
548 },
549 },
550 },
551 },
552 }
553
554 updateDone := make(chan bool)
555 go func() {
556 if err := s.AddUpdateAPIService(apiServiceFailed, handlerFailed); err != nil {
557 t.Error(err)
558 }
559 close(updateDone)
560 }()
561
562 select {
563 case <-updateDone:
564 case <-time.After(2 * time.Second):
565 t.Errorf("AddUpdateAPIService affected by APIService response time")
566 }
567
568 swagger, err := fetchOpenAPI(mux)
569 if err != nil {
570 t.Error(err)
571 }
572 expectPath(t, swagger, "/apis/foo/v1/")
573 expectNoPath(t, swagger, "/apis/failed/v1/")
574 }
575
576 type openAPIHandler struct {
577 delaySeconds int
578 openapi *spec.Swagger
579 returnErr bool
580 }
581
582 func (o *openAPIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
583 time.Sleep(time.Duration(o.delaySeconds) * time.Second)
584 if o.returnErr {
585 w.WriteHeader(500)
586 return
587 }
588 data, err := json.Marshal(o.openapi)
589 if err != nil {
590 panic(err)
591 }
592 http.ServeContent(w, r, "/openapi/v2", time.Now(), bytes.NewReader(data))
593 return
594 }
595
596 func fetchOpenAPI(mux *http.ServeMux) (*spec.Swagger, error) {
597 server := httptest.NewServer(mux)
598 defer server.Close()
599 client := server.Client()
600
601 req, err := http.NewRequest("GET", server.URL+"/openapi/v2", nil)
602 if err != nil {
603 return nil, err
604 }
605 resp, err := client.Do(req)
606 if err != nil {
607 return nil, err
608 }
609 body, err := io.ReadAll(resp.Body)
610
611 swagger := &spec.Swagger{}
612 if err := swagger.UnmarshalJSON(body); err != nil {
613 return nil, err
614 }
615 return swagger, err
616 }
617
618 func buildAndRegisterSpecAggregator(delegationHandlers []http.Handler, mux common.PathHandler) *specAggregator {
619 downloader := NewDownloader()
620 aggregatorSpec := &spec.Swagger{
621 SwaggerProps: spec.SwaggerProps{
622 Paths: &spec.Paths{
623 Paths: map[string]spec.PathItem{
624 "/apis/apiregistration.k8s.io/v1/": {},
625 },
626 },
627 },
628 }
629 s := buildAndRegisterSpecAggregatorForLocalServices(&downloader, aggregatorSpec, delegationHandlers, mux)
630 return s
631 }
632
633 func expectPath(t *testing.T, swagger *spec.Swagger, path string) {
634 if _, ok := swagger.Paths.Paths[path]; !ok {
635 t.Errorf("Expected path %s to exist in aggregated paths", path)
636 }
637 }
638
639 func expectNoPath(t *testing.T, swagger *spec.Swagger, path string) {
640 if _, ok := swagger.Paths.Paths[path]; ok {
641 t.Errorf("Expected path %s to be omitted in aggregated paths", path)
642 }
643 }
644
View as plain text