1
16
17 package apiserver
18
19 import (
20 "io"
21 "net/http"
22 "net/http/httptest"
23 "net/http/httputil"
24 "testing"
25
26 "github.com/google/go-cmp/cmp"
27 apiequality "k8s.io/apimachinery/pkg/api/equality"
28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 "k8s.io/apimachinery/pkg/runtime"
30 "k8s.io/apimachinery/pkg/util/sets"
31 "k8s.io/client-go/tools/cache"
32
33 apiregistration "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
34 aggregatorscheme "k8s.io/kube-aggregator/pkg/apiserver/scheme"
35 listers "k8s.io/kube-aggregator/pkg/client/listers/apiregistration/v1"
36 )
37
38 func TestAPIs(t *testing.T) {
39 tests := []struct {
40 name string
41 enabled sets.String
42 apiservices []*apiregistration.APIService
43 expected *metav1.APIGroupList
44 }{
45 {
46 name: "empty",
47 enabled: sets.NewString("v1", "v1beta1"),
48 apiservices: []*apiregistration.APIService{},
49 expected: &metav1.APIGroupList{
50 TypeMeta: metav1.TypeMeta{Kind: "APIGroupList", APIVersion: "v1"},
51 Groups: []metav1.APIGroup{
52 discoveryGroup(sets.NewString("v1", "v1beta1")),
53 },
54 },
55 },
56 {
57 name: "v1 only",
58 enabled: sets.NewString("v1"),
59 apiservices: []*apiregistration.APIService{},
60 expected: &metav1.APIGroupList{
61 TypeMeta: metav1.TypeMeta{Kind: "APIGroupList", APIVersion: "v1"},
62 Groups: []metav1.APIGroup{
63 discoveryGroup(sets.NewString("v1")),
64 },
65 },
66 },
67 {
68 name: "simple add",
69 enabled: sets.NewString("v1", "v1beta1"),
70 apiservices: []*apiregistration.APIService{
71 {
72 ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
73 Spec: apiregistration.APIServiceSpec{
74 Service: &apiregistration.ServiceReference{
75 Namespace: "ns",
76 Name: "api",
77 },
78 Group: "foo",
79 Version: "v1",
80 GroupPriorityMinimum: 11,
81 },
82 Status: apiregistration.APIServiceStatus{
83 Conditions: []apiregistration.APIServiceCondition{
84 {Type: apiregistration.Available, Status: apiregistration.ConditionTrue},
85 },
86 },
87 },
88 {
89 ObjectMeta: metav1.ObjectMeta{Name: "v1.bar"},
90 Spec: apiregistration.APIServiceSpec{
91 Service: &apiregistration.ServiceReference{
92 Namespace: "ns",
93 Name: "api",
94 },
95 Group: "bar",
96 Version: "v1",
97 GroupPriorityMinimum: 10,
98 },
99 Status: apiregistration.APIServiceStatus{
100 Conditions: []apiregistration.APIServiceCondition{
101 {Type: apiregistration.Available, Status: apiregistration.ConditionTrue},
102 },
103 },
104 },
105 },
106 expected: &metav1.APIGroupList{
107 TypeMeta: metav1.TypeMeta{Kind: "APIGroupList", APIVersion: "v1"},
108 Groups: []metav1.APIGroup{
109 discoveryGroup(sets.NewString("v1", "v1beta1")),
110 {
111 Name: "foo",
112 Versions: []metav1.GroupVersionForDiscovery{
113 {
114 GroupVersion: "foo/v1",
115 Version: "v1",
116 },
117 },
118 PreferredVersion: metav1.GroupVersionForDiscovery{
119 GroupVersion: "foo/v1",
120 Version: "v1",
121 },
122 },
123 {
124 Name: "bar",
125 Versions: []metav1.GroupVersionForDiscovery{
126 {
127 GroupVersion: "bar/v1",
128 Version: "v1",
129 },
130 },
131 PreferredVersion: metav1.GroupVersionForDiscovery{
132 GroupVersion: "bar/v1",
133 Version: "v1",
134 },
135 },
136 },
137 },
138 },
139 {
140 name: "sorting",
141 enabled: sets.NewString("v1", "v1beta1"),
142 apiservices: []*apiregistration.APIService{
143 {
144 ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
145 Spec: apiregistration.APIServiceSpec{
146 Service: &apiregistration.ServiceReference{
147 Namespace: "ns",
148 Name: "api",
149 },
150 Group: "foo",
151 Version: "v1",
152 GroupPriorityMinimum: 20,
153 VersionPriority: 10,
154 },
155 Status: apiregistration.APIServiceStatus{
156 Conditions: []apiregistration.APIServiceCondition{
157 {Type: apiregistration.Available, Status: apiregistration.ConditionTrue},
158 },
159 },
160 },
161 {
162 ObjectMeta: metav1.ObjectMeta{Name: "v2.bar"},
163 Spec: apiregistration.APIServiceSpec{
164 Service: &apiregistration.ServiceReference{
165 Namespace: "ns",
166 Name: "api",
167 },
168 Group: "bar",
169 Version: "v2",
170 GroupPriorityMinimum: 11,
171 },
172 Status: apiregistration.APIServiceStatus{
173 Conditions: []apiregistration.APIServiceCondition{
174 {Type: apiregistration.Available, Status: apiregistration.ConditionTrue},
175 },
176 },
177 },
178 {
179 ObjectMeta: metav1.ObjectMeta{Name: "v2.foo"},
180 Spec: apiregistration.APIServiceSpec{
181 Service: &apiregistration.ServiceReference{
182 Namespace: "ns",
183 Name: "api",
184 },
185 Group: "foo",
186 Version: "v2",
187 GroupPriorityMinimum: 1,
188 VersionPriority: 15,
189 },
190 Status: apiregistration.APIServiceStatus{
191 Conditions: []apiregistration.APIServiceCondition{
192 {Type: apiregistration.Available, Status: apiregistration.ConditionTrue},
193 },
194 },
195 },
196 {
197 ObjectMeta: metav1.ObjectMeta{Name: "v1.bar"},
198 Spec: apiregistration.APIServiceSpec{
199 Service: &apiregistration.ServiceReference{
200 Namespace: "ns",
201 Name: "api",
202 },
203 Group: "bar",
204 Version: "v1",
205 GroupPriorityMinimum: 11,
206 },
207 Status: apiregistration.APIServiceStatus{
208 Conditions: []apiregistration.APIServiceCondition{
209 {Type: apiregistration.Available, Status: apiregistration.ConditionTrue},
210 },
211 },
212 },
213 },
214 expected: &metav1.APIGroupList{
215 TypeMeta: metav1.TypeMeta{Kind: "APIGroupList", APIVersion: "v1"},
216 Groups: []metav1.APIGroup{
217 discoveryGroup(sets.NewString("v1", "v1beta1")),
218 {
219 Name: "foo",
220 Versions: []metav1.GroupVersionForDiscovery{
221 {
222 GroupVersion: "foo/v2",
223 Version: "v2",
224 },
225 {
226 GroupVersion: "foo/v1",
227 Version: "v1",
228 },
229 },
230 PreferredVersion: metav1.GroupVersionForDiscovery{
231 GroupVersion: "foo/v2",
232 Version: "v2",
233 },
234 },
235 {
236 Name: "bar",
237 Versions: []metav1.GroupVersionForDiscovery{
238 {
239 GroupVersion: "bar/v2",
240 Version: "v2",
241 },
242 {
243 GroupVersion: "bar/v1",
244 Version: "v1",
245 },
246 },
247 PreferredVersion: metav1.GroupVersionForDiscovery{
248 GroupVersion: "bar/v2",
249 Version: "v2",
250 },
251 },
252 },
253 },
254 },
255 {
256 name: "unavailable service",
257 enabled: sets.NewString("v1", "v1beta1"),
258 apiservices: []*apiregistration.APIService{
259 {
260 ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
261 Spec: apiregistration.APIServiceSpec{
262 Service: &apiregistration.ServiceReference{
263 Namespace: "ns",
264 Name: "api",
265 },
266 Group: "foo",
267 Version: "v1",
268 GroupPriorityMinimum: 11,
269 },
270 Status: apiregistration.APIServiceStatus{
271 Conditions: []apiregistration.APIServiceCondition{
272 {Type: apiregistration.Available, Status: apiregistration.ConditionFalse},
273 },
274 },
275 },
276 },
277 expected: &metav1.APIGroupList{
278 TypeMeta: metav1.TypeMeta{Kind: "APIGroupList", APIVersion: "v1"},
279 Groups: []metav1.APIGroup{
280 discoveryGroup(sets.NewString("v1", "v1beta1")),
281 {
282 Name: "foo",
283 Versions: []metav1.GroupVersionForDiscovery{
284 {
285 GroupVersion: "foo/v1",
286 Version: "v1",
287 },
288 },
289 PreferredVersion: metav1.GroupVersionForDiscovery{
290 GroupVersion: "foo/v1",
291 Version: "v1",
292 },
293 },
294 },
295 },
296 },
297 }
298
299 for _, tc := range tests {
300 indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
301 handler := &apisHandler{
302 codecs: aggregatorscheme.Codecs,
303 lister: listers.NewAPIServiceLister(indexer),
304 discoveryGroup: discoveryGroup(tc.enabled),
305 }
306 for _, o := range tc.apiservices {
307 indexer.Add(o)
308 }
309
310 server := httptest.NewServer(handler)
311 defer server.Close()
312
313 resp, err := http.Get(server.URL + "/apis")
314 if err != nil {
315 t.Errorf("%s: %v", tc.name, err)
316 continue
317 }
318 bytes, err := io.ReadAll(resp.Body)
319 if err != nil {
320 t.Errorf("%s: %v", tc.name, err)
321 continue
322 }
323
324 actual := &metav1.APIGroupList{}
325 if err := runtime.DecodeInto(aggregatorscheme.Codecs.UniversalDecoder(), bytes, actual); err != nil {
326 t.Errorf("%s: %v", tc.name, err)
327 continue
328 }
329 if !apiequality.Semantic.DeepEqual(tc.expected, actual) {
330 t.Errorf("%s: %v", tc.name, cmp.Diff(tc.expected, actual))
331 continue
332 }
333 }
334 }
335
336 func TestAPIGroupMissing(t *testing.T) {
337 indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
338 handler := &apiGroupHandler{
339 codecs: aggregatorscheme.Codecs,
340 lister: listers.NewAPIServiceLister(indexer),
341 groupName: "groupName",
342 delegate: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
343 w.WriteHeader(http.StatusForbidden)
344 }),
345 }
346
347 server := httptest.NewServer(handler)
348 defer server.Close()
349
350
351 resp, err := http.Get(server.URL + "/apis/groupName/foo")
352 if err != nil {
353 t.Fatal(err)
354 }
355 if resp.StatusCode != http.StatusForbidden {
356 t.Fatalf("expected %v, got %v", http.StatusForbidden, resp.StatusCode)
357 }
358
359
360 resp, err = http.Get(server.URL + "/apis/groupName/")
361 if err != nil {
362 t.Fatal(err)
363 }
364 if resp.StatusCode != http.StatusForbidden {
365 t.Fatalf("expected %v, got %v", http.StatusForbidden, resp.StatusCode)
366 }
367
368
369 resp, err = http.Get(server.URL + "/apis/missing")
370 if err != nil {
371 t.Fatal(err)
372 }
373 if resp.StatusCode != http.StatusForbidden {
374 t.Fatalf("expected %v, got %v", http.StatusForbidden, resp.StatusCode)
375 }
376 }
377
378 func TestAPIGroup(t *testing.T) {
379 tests := []struct {
380 name string
381 group string
382 apiservices []*apiregistration.APIService
383 expected *metav1.APIGroup
384 }{
385 {
386 name: "sorting",
387 group: "foo",
388 apiservices: []*apiregistration.APIService{
389 {
390 ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
391 Spec: apiregistration.APIServiceSpec{
392 Service: &apiregistration.ServiceReference{
393 Namespace: "ns",
394 Name: "api",
395 },
396 Group: "foo",
397 Version: "v1",
398 GroupPriorityMinimum: 20,
399 VersionPriority: 10,
400 },
401 Status: apiregistration.APIServiceStatus{
402 Conditions: []apiregistration.APIServiceCondition{
403 {Type: apiregistration.Available, Status: apiregistration.ConditionTrue},
404 },
405 },
406 },
407 {
408 ObjectMeta: metav1.ObjectMeta{Name: "v2.bar"},
409 Spec: apiregistration.APIServiceSpec{
410 Service: &apiregistration.ServiceReference{
411 Namespace: "ns",
412 Name: "api",
413 },
414 Group: "bar",
415 Version: "v2",
416 GroupPriorityMinimum: 11,
417 },
418 Status: apiregistration.APIServiceStatus{
419 Conditions: []apiregistration.APIServiceCondition{
420 {Type: apiregistration.Available, Status: apiregistration.ConditionTrue},
421 },
422 },
423 },
424 {
425 ObjectMeta: metav1.ObjectMeta{Name: "v2.foo"},
426 Spec: apiregistration.APIServiceSpec{
427 Service: &apiregistration.ServiceReference{
428 Namespace: "ns",
429 Name: "api",
430 },
431 Group: "foo",
432 Version: "v2",
433 GroupPriorityMinimum: 1,
434 VersionPriority: 15,
435 },
436 Status: apiregistration.APIServiceStatus{
437 Conditions: []apiregistration.APIServiceCondition{
438 {Type: apiregistration.Available, Status: apiregistration.ConditionTrue},
439 },
440 },
441 },
442 {
443 ObjectMeta: metav1.ObjectMeta{Name: "v1.bar"},
444 Spec: apiregistration.APIServiceSpec{
445 Service: &apiregistration.ServiceReference{
446 Namespace: "ns",
447 Name: "api",
448 },
449 Group: "bar",
450 Version: "v1",
451 GroupPriorityMinimum: 11,
452 },
453 Status: apiregistration.APIServiceStatus{
454 Conditions: []apiregistration.APIServiceCondition{
455 {Type: apiregistration.Available, Status: apiregistration.ConditionTrue},
456 },
457 },
458 },
459 },
460 expected: &metav1.APIGroup{
461 TypeMeta: metav1.TypeMeta{Kind: "APIGroup", APIVersion: "v1"},
462 Name: "foo",
463 Versions: []metav1.GroupVersionForDiscovery{
464 {
465 GroupVersion: "foo/v2",
466 Version: "v2",
467 },
468 {
469 GroupVersion: "foo/v1",
470 Version: "v1",
471 },
472 },
473 PreferredVersion: metav1.GroupVersionForDiscovery{
474 GroupVersion: "foo/v2",
475 Version: "v2",
476 },
477 },
478 },
479 }
480
481 for _, tc := range tests {
482 indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
483 handler := &apiGroupHandler{
484 codecs: aggregatorscheme.Codecs,
485 lister: listers.NewAPIServiceLister(indexer),
486 groupName: "foo",
487 }
488 for _, o := range tc.apiservices {
489 indexer.Add(o)
490 }
491
492 server := httptest.NewServer(handler)
493 defer server.Close()
494
495 resp, err := http.Get(server.URL + "/apis/" + tc.group)
496 if err != nil {
497 t.Errorf("%s: %v", tc.name, err)
498 continue
499 }
500 if resp.StatusCode != http.StatusOK {
501 response, _ := httputil.DumpResponse(resp, true)
502 t.Errorf("%s: %v", tc.name, string(response))
503 continue
504 }
505 bytes, err := io.ReadAll(resp.Body)
506 if err != nil {
507 t.Errorf("%s: %v", tc.name, err)
508 continue
509 }
510
511 actual := &metav1.APIGroup{}
512 if err := runtime.DecodeInto(aggregatorscheme.Codecs.UniversalDecoder(), bytes, actual); err != nil {
513 t.Errorf("%s: %v", tc.name, err)
514 continue
515 }
516 if !apiequality.Semantic.DeepEqual(tc.expected, actual) {
517 t.Errorf("%s: %v", tc.name, cmp.Diff(tc.expected, actual))
518 continue
519 }
520 }
521 }
522
View as plain text