1
16
17 package integration
18
19 import (
20 "context"
21 "fmt"
22 "reflect"
23 "sort"
24 "strings"
25 "testing"
26 "time"
27
28 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
29 "k8s.io/apiextensions-apiserver/test/integration/fixtures"
30 "k8s.io/apimachinery/pkg/api/errors"
31 "k8s.io/apimachinery/pkg/api/meta"
32 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
34 "k8s.io/apimachinery/pkg/runtime"
35 "k8s.io/apimachinery/pkg/types"
36 "k8s.io/apimachinery/pkg/util/wait"
37 "k8s.io/apimachinery/pkg/watch"
38 "k8s.io/client-go/dynamic"
39 )
40
41 func TestServerUp(t *testing.T) {
42 tearDown, _, _, err := fixtures.StartDefaultServerWithClients(t)
43 if err != nil {
44 t.Fatal(err)
45 }
46 defer tearDown()
47 }
48
49 func TestNamespaceScopedCRUD(t *testing.T) {
50 tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
51 if err != nil {
52 t.Fatal(err)
53 }
54 defer tearDown()
55
56 noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
57 noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
58 if err != nil {
59 t.Fatal(err)
60 }
61
62 ns := "not-the-default"
63
64 testSimpleCRUD(t, ns, noxuDefinition, dynamicClient)
65 testFieldSelector(t, ns, noxuDefinition, dynamicClient)
66 }
67
68 func TestClusterScopedCRUD(t *testing.T) {
69 tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
70 if err != nil {
71 t.Fatal(err)
72 }
73 defer tearDown()
74
75 noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.ClusterScoped)
76 noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
77 if err != nil {
78 t.Fatal(err)
79 }
80
81 ns := ""
82 testSimpleCRUD(t, ns, noxuDefinition, dynamicClient)
83 testFieldSelector(t, ns, noxuDefinition, dynamicClient)
84 }
85
86 func testSimpleCRUD(t *testing.T, ns string, noxuDefinition *apiextensionsv1.CustomResourceDefinition, dynamicClient dynamic.Interface) {
87 noxuResourceClients := map[string]dynamic.ResourceInterface{}
88 noxuWatchs := map[string]watch.Interface{}
89 disabledVersions := map[string]bool{}
90 for _, v := range noxuDefinition.Spec.Versions {
91 disabledVersions[v.Name] = !v.Served
92 }
93 for _, v := range noxuDefinition.Spec.Versions {
94 noxuResourceClients[v.Name] = newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, v.Name)
95
96 noxuWatch, err := noxuResourceClients[v.Name].Watch(context.TODO(), metav1.ListOptions{})
97 if disabledVersions[v.Name] {
98 if !errors.IsNotFound(err) {
99 t.Errorf("expected the watch operation fail with NotFound for disabled version %s, got error: %v", v.Name, err)
100 }
101 } else {
102 if err != nil {
103 t.Fatal(err)
104 }
105 noxuWatchs[v.Name] = noxuWatch
106 }
107 }
108 defer func() {
109 for _, w := range noxuWatchs {
110 w.Stop()
111 }
112 }()
113
114 for version, noxuResourceClient := range noxuResourceClients {
115 createdNoxuInstance, err := instantiateVersionedCustomResource(t, fixtures.NewVersionedNoxuInstance(ns, "foo", version), noxuResourceClient, noxuDefinition, version)
116 if disabledVersions[version] {
117 if !errors.IsNotFound(err) {
118 t.Errorf("expected the CR creation fail with NotFound for disabled version %s, got error: %v", version, err)
119 }
120 continue
121 }
122 if err != nil {
123 t.Fatalf("unable to create noxu Instance:%v", err)
124 }
125 if e, a := noxuDefinition.Spec.Group+"/"+version, createdNoxuInstance.GetAPIVersion(); e != a {
126 t.Errorf("expected %v, got %v", e, a)
127 }
128 for watchVersion, noxuWatch := range noxuWatchs {
129 select {
130 case watchEvent := <-noxuWatch.ResultChan():
131 if e, a := watch.Added, watchEvent.Type; e != a {
132 t.Errorf("expected %v, got %v", e, a)
133 break
134 }
135 createdObjectMeta, err := meta.Accessor(watchEvent.Object)
136 if err != nil {
137 t.Fatal(err)
138 }
139
140 if len(createdObjectMeta.GetUID()) == 0 {
141 t.Errorf("missing uuid: %#v", watchEvent.Object)
142 }
143 if e, a := ns, createdObjectMeta.GetNamespace(); e != a {
144 t.Errorf("expected %v, got %v", e, a)
145 }
146 createdTypeMeta, err := meta.TypeAccessor(watchEvent.Object)
147 if err != nil {
148 t.Fatal(err)
149 }
150 if e, a := noxuDefinition.Spec.Group+"/"+watchVersion, createdTypeMeta.GetAPIVersion(); e != a {
151 t.Errorf("expected %v, got %v", e, a)
152 }
153 if e, a := noxuDefinition.Spec.Names.Kind, createdTypeMeta.GetKind(); e != a {
154 t.Errorf("expected %v, got %v", e, a)
155 }
156 case <-time.After(5 * time.Second):
157 t.Errorf("missing watch event")
158 }
159 }
160
161
162 for version2, noxuResourceClient2 := range noxuResourceClients {
163
164 gottenNoxuInstance, err := noxuResourceClient2.Get(context.TODO(), "foo", metav1.GetOptions{})
165
166 if disabledVersions[version2] {
167 if !errors.IsNotFound(err) {
168 t.Errorf("expected the get operation fail with NotFound for disabled version %s, got error: %v", version2, err)
169
170 }
171 } else {
172 if err != nil {
173 t.Fatal(err)
174 }
175
176 if e, a := version2, gottenNoxuInstance.GroupVersionKind().Version; !reflect.DeepEqual(e, a) {
177 t.Errorf("expected %v, got %v", e, a)
178 }
179 }
180
181
182 listWithItem, err := noxuResourceClient2.List(context.TODO(), metav1.ListOptions{})
183 if disabledVersions[version2] {
184 if !errors.IsNotFound(err) {
185 t.Errorf("expected the list operation fail with NotFound for disabled version %s, got error: %v", version2, err)
186
187 }
188 } else {
189 if err != nil {
190 t.Fatal(err)
191 }
192 if e, a := 1, len(listWithItem.Items); e != a {
193 t.Errorf("expected %v, got %v", e, a)
194 }
195 if e, a := version2, listWithItem.GroupVersionKind().Version; !reflect.DeepEqual(e, a) {
196 t.Errorf("expected %v, got %v", e, a)
197 }
198 if e, a := version2, listWithItem.Items[0].GroupVersionKind().Version; !reflect.DeepEqual(e, a) {
199 t.Errorf("expected %v, got %v", e, a)
200 }
201 }
202 }
203
204
205 for version2, noxuResourceClient2 := range noxuResourceClients {
206 var gottenNoxuInstance *unstructured.Unstructured
207 if disabledVersions[version2] {
208 gottenNoxuInstance = &unstructured.Unstructured{}
209 gottenNoxuInstance.SetName("foo")
210 } else {
211 gottenNoxuInstance, err = noxuResourceClient2.Get(context.TODO(), "foo", metav1.GetOptions{})
212 if err != nil {
213 t.Fatal(err)
214 }
215 }
216
217 gottenNoxuInstance.Object["updated"] = version2
218 updatedNoxuInstance, err := noxuResourceClient2.Update(context.TODO(), gottenNoxuInstance, metav1.UpdateOptions{})
219 if disabledVersions[version2] {
220 if !errors.IsNotFound(err) {
221 t.Errorf("expected the update operation fail with NotFound for disabled version %s, got error: %v", version2, err)
222 }
223 } else {
224 if err != nil {
225 t.Fatal(err)
226 }
227
228 if updated, ok := updatedNoxuInstance.Object["updated"]; !ok {
229 t.Errorf("expected string 'updated' field")
230 } else if updated, ok := updated.(string); !ok || updated != version2 {
231 t.Errorf("expected string 'updated' field to equal %q, got %q of type %T", version2, updated, updated)
232 }
233
234 if e, a := version2, updatedNoxuInstance.GroupVersionKind().Version; !reflect.DeepEqual(e, a) {
235 t.Errorf("expected %v, got %v", e, a)
236 }
237
238 for _, noxuWatch := range noxuWatchs {
239 select {
240 case watchEvent := <-noxuWatch.ResultChan():
241 eventMetadata, err := meta.Accessor(watchEvent.Object)
242 if err != nil {
243 t.Fatal(err)
244 }
245
246 if watchEvent.Type != watch.Modified {
247 t.Errorf("expected modified event, got %v", watchEvent.Type)
248 break
249 }
250
251
252 createdMetadata, err := meta.Accessor(createdNoxuInstance)
253 if err != nil {
254 t.Fatal(err)
255 }
256 if e, a := createdMetadata.GetUID(), eventMetadata.GetUID(); e != a {
257 t.Errorf("expected equal UID for (expected) %v, and (actual) %v", createdNoxuInstance, watchEvent.Object)
258 }
259
260 case <-time.After(5 * time.Second):
261 t.Errorf("missing watch event")
262 }
263 }
264 }
265 }
266
267
268 if err := noxuResourceClient.Delete(context.TODO(), "foo", *metav1.NewDeleteOptions(0)); err != nil {
269 t.Fatal(err)
270 }
271
272 listWithoutItem, err := noxuResourceClient.List(context.TODO(), metav1.ListOptions{})
273 if err != nil {
274 t.Fatal(err)
275 }
276 if e, a := 0, len(listWithoutItem.Items); e != a {
277 t.Errorf("expected %v, got %v", e, a)
278 }
279
280 for _, noxuWatch := range noxuWatchs {
281 select {
282 case watchEvent := <-noxuWatch.ResultChan():
283 eventMetadata, err := meta.Accessor(watchEvent.Object)
284 if err != nil {
285 t.Fatal(err)
286 }
287
288 if watchEvent.Type != watch.Deleted {
289 t.Errorf("expected delete event, got %v", watchEvent.Type)
290 break
291 }
292
293
294 createdMetadata, err := meta.Accessor(createdNoxuInstance)
295 if err != nil {
296 t.Fatal(err)
297 }
298 if e, a := createdMetadata.GetUID(), eventMetadata.GetUID(); e != a {
299 t.Errorf("expected equal UID for (expected) %v, and (actual) %v", createdNoxuInstance, watchEvent.Object)
300 }
301
302 case <-time.After(5 * time.Second):
303 t.Errorf("missing watch event")
304 }
305 }
306
307
308 if err := noxuResourceClient.DeleteCollection(context.TODO(), *metav1.NewDeleteOptions(0), metav1.ListOptions{}); err != nil {
309 t.Fatal(err)
310 }
311 }
312 }
313
314 func TestInvalidCRUD(t *testing.T) {
315 tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
316 if err != nil {
317 t.Fatal(err)
318 }
319 defer tearDown()
320
321 noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.ClusterScoped)
322 noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
323 if err != nil {
324 t.Fatal(err)
325 }
326
327 noxuResourceClients := map[string]dynamic.ResourceInterface{}
328 noxuWatchs := map[string]watch.Interface{}
329 disabledVersions := map[string]bool{}
330 for _, v := range noxuDefinition.Spec.Versions {
331 disabledVersions[v.Name] = !v.Served
332 }
333 for _, v := range noxuDefinition.Spec.Versions {
334 noxuResourceClients[v.Name] = newNamespacedCustomResourceVersionedClient("", dynamicClient, noxuDefinition, v.Name)
335
336 noxuWatch, err := noxuResourceClients[v.Name].Watch(context.TODO(), metav1.ListOptions{})
337 if disabledVersions[v.Name] {
338 if !errors.IsNotFound(err) {
339 t.Errorf("expected the watch operation fail with NotFound for disabled version %s, got error: %v", v.Name, err)
340 }
341 } else {
342 if err != nil {
343 t.Fatal(err)
344 }
345 noxuWatchs[v.Name] = noxuWatch
346 }
347 }
348 defer func() {
349 for _, w := range noxuWatchs {
350 w.Stop()
351 }
352 }()
353
354 for version, noxuResourceClient := range noxuResourceClients {
355
356 typelessInstance := &unstructured.Unstructured{}
357 if _, err := noxuResourceClient.Create(context.TODO(), typelessInstance, metav1.CreateOptions{}); !errors.IsBadRequest(err) {
358 t.Errorf("expected badrequest for submitting empty object, got %#v", err)
359 }
360
361 typedNoBodyInstance := &unstructured.Unstructured{
362 Object: map[string]interface{}{
363 "apiVersion": "mygroup.example.com/" + version,
364 "kind": "WishIHadChosenNoxu",
365 },
366 }
367 if _, err := noxuResourceClient.Create(context.TODO(), typedNoBodyInstance, metav1.CreateOptions{}); !errors.IsInvalid(err) {
368 t.Errorf("expected invalid request for submitting malformed object, got %#v", err)
369 }
370 }
371 }
372
373 func testFieldSelector(t *testing.T, ns string, noxuDefinition *apiextensionsv1.CustomResourceDefinition, dynamicClient dynamic.Interface) {
374 noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
375 initialList, err := noxuResourceClient.List(context.TODO(), metav1.ListOptions{})
376 if err != nil {
377 t.Fatal(err)
378 }
379 if e, a := 0, len(initialList.Items); e != a {
380 t.Errorf("expected %v, got %v", e, a)
381 }
382 initialListTypeMeta, err := meta.TypeAccessor(initialList)
383 if err != nil {
384 t.Fatal(err)
385 }
386 if e, a := noxuDefinition.Spec.Group+"/"+noxuDefinition.Spec.Versions[0].Name, initialListTypeMeta.GetAPIVersion(); e != a {
387 t.Errorf("expected %v, got %v", e, a)
388 }
389 if e, a := noxuDefinition.Spec.Names.ListKind, initialListTypeMeta.GetKind(); e != a {
390 t.Errorf("expected %v, got %v", e, a)
391 }
392
393 initialListListMeta, err := meta.ListAccessor(initialList)
394 if err != nil {
395 t.Fatal(err)
396 }
397 noxuWatch, err := noxuResourceClient.Watch(
398 context.TODO(),
399 metav1.ListOptions{
400 ResourceVersion: initialListListMeta.GetResourceVersion(),
401 FieldSelector: "metadata.name=foo",
402 },
403 )
404 if err != nil {
405 t.Fatal(err)
406 }
407 defer noxuWatch.Stop()
408
409 _, err = instantiateCustomResource(t, fixtures.NewNoxuInstance(ns, "bar"), noxuResourceClient, noxuDefinition)
410 if err != nil {
411 t.Fatalf("unable to create noxu Instance:%v", err)
412 }
413 createdNoxuInstanceFoo, err := instantiateCustomResource(t, fixtures.NewNoxuInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
414 if err != nil {
415 t.Fatalf("unable to create noxu Instance:%v", err)
416 }
417
418 select {
419 case watchEvent := <-noxuWatch.ResultChan():
420 if e, a := watch.Added, watchEvent.Type; e != a {
421 t.Errorf("expected %v, got %v", e, a)
422 break
423 }
424 createdObjectMeta, err := meta.Accessor(watchEvent.Object)
425 if err != nil {
426 t.Fatal(err)
427 }
428
429 if len(createdObjectMeta.GetUID()) == 0 {
430 t.Errorf("missing uuid: %#v", watchEvent.Object)
431 }
432 if e, a := ns, createdObjectMeta.GetNamespace(); e != a {
433 t.Errorf("expected %v, got %v", e, a)
434 }
435 if e, a := "foo", createdObjectMeta.GetName(); e != a {
436 t.Errorf("expected %v, got %v", e, a)
437 }
438 createdTypeMeta, err := meta.TypeAccessor(watchEvent.Object)
439 if err != nil {
440 t.Fatal(err)
441 }
442 if e, a := noxuDefinition.Spec.Group+"/"+noxuDefinition.Spec.Versions[0].Name, createdTypeMeta.GetAPIVersion(); e != a {
443 t.Errorf("expected %v, got %v", e, a)
444 }
445 if e, a := noxuDefinition.Spec.Names.Kind, createdTypeMeta.GetKind(); e != a {
446 t.Errorf("expected %v, got %v", e, a)
447 }
448
449 case <-time.After(5 * time.Second):
450 t.Errorf("missing watch event")
451 }
452
453 gottenNoxuInstance, err := noxuResourceClient.Get(context.TODO(), "foo", metav1.GetOptions{})
454 if err != nil {
455 t.Fatal(err)
456 }
457 if e, a := createdNoxuInstanceFoo, gottenNoxuInstance; !reflect.DeepEqual(e, a) {
458 t.Errorf("expected %v, got %v", e, a)
459 }
460
461 listWithItem, err := noxuResourceClient.List(context.TODO(), metav1.ListOptions{FieldSelector: "metadata.name=foo"})
462 if err != nil {
463 t.Fatal(err)
464 }
465 if e, a := 1, len(listWithItem.Items); e != a {
466 t.Errorf("expected %v, got %v", e, a)
467 }
468 if e, a := *createdNoxuInstanceFoo, listWithItem.Items[0]; !reflect.DeepEqual(e, a) {
469 t.Errorf("expected %v, got %v", e, a)
470 }
471
472 if err := noxuResourceClient.Delete(context.TODO(), "bar", metav1.DeleteOptions{}); err != nil {
473 t.Fatal(err)
474 }
475 if err := noxuResourceClient.Delete(context.TODO(), "foo", metav1.DeleteOptions{}); err != nil {
476 t.Fatal(err)
477 }
478
479 listWithoutItem, err := noxuResourceClient.List(context.TODO(), metav1.ListOptions{})
480 if err != nil {
481 t.Fatal(err)
482 }
483 if e, a := 0, len(listWithoutItem.Items); e != a {
484 t.Errorf("expected %v, got %v", e, a)
485 }
486
487 select {
488 case watchEvent := <-noxuWatch.ResultChan():
489 if e, a := watch.Deleted, watchEvent.Type; e != a {
490 t.Errorf("expected %v, got %v", e, a)
491 break
492 }
493 deletedObjectMeta, err := meta.Accessor(watchEvent.Object)
494 if err != nil {
495 t.Fatal(err)
496 }
497
498 createdObjectMeta, err := meta.Accessor(createdNoxuInstanceFoo)
499 if err != nil {
500 t.Fatal(err)
501 }
502 if e, a := createdObjectMeta.GetUID(), deletedObjectMeta.GetUID(); e != a {
503 t.Errorf("expected %v, got %v", e, a)
504 }
505 if e, a := ns, createdObjectMeta.GetNamespace(); e != a {
506 t.Errorf("expected %v, got %v", e, a)
507 }
508 if e, a := "foo", createdObjectMeta.GetName(); e != a {
509 t.Errorf("expected %v, got %v", e, a)
510 }
511
512 case <-time.After(5 * time.Second):
513 t.Errorf("missing watch event")
514 }
515 }
516
517 func TestDiscovery(t *testing.T) {
518 group := "mygroup.example.com"
519 version := "v1beta1"
520
521 tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
522 if err != nil {
523 t.Fatal(err)
524 }
525 defer tearDown()
526
527 scope := apiextensionsv1.NamespaceScoped
528 noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(scope)
529 if _, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient); err != nil {
530 t.Fatal(err)
531 }
532
533
534 resources, err := apiExtensionClient.Discovery().ServerResourcesForGroupVersion(group + "/" + version)
535 if err != nil {
536 t.Fatal(err)
537 }
538
539 if len(resources.APIResources) != 1 {
540 t.Fatalf("Expected exactly the resource \"noxus\" in group version %v/%v via discovery, got: %v", group, version, resources.APIResources)
541 }
542
543 r := resources.APIResources[0]
544 if r.Name != "noxus" {
545 t.Fatalf("Expected exactly the resource \"noxus\" in group version %v/%v via discovery, got: %v", group, version, r.Name)
546 }
547 if r.Kind != "WishIHadChosenNoxu" {
548 t.Fatalf("Expected exactly the kind \"WishIHadChosenNoxu\" in group version %v/%v via discovery, got: %v", group, version, r.Kind)
549 }
550
551 s := []string{"foo", "bar", "abc", "def"}
552 if !reflect.DeepEqual(r.ShortNames, s) {
553 t.Fatalf("Expected exactly the shortnames `foo, bar, abc, def` in group version %v/%v via discovery, got: %v", group, version, r.ShortNames)
554 }
555
556 sort.Strings(r.Verbs)
557 expectedVerbs := []string{"create", "delete", "deletecollection", "get", "list", "patch", "update", "watch"}
558 if !reflect.DeepEqual([]string(r.Verbs), expectedVerbs) {
559 t.Fatalf("Unexpected verbs for resource \"noxus\" in group version %v/%v via discovery: expected=%v got=%v", group, version, expectedVerbs, r.Verbs)
560 }
561
562 if !reflect.DeepEqual(r.Categories, []string{"all"}) {
563 t.Fatalf("Expected exactly the category \"all\" in group version %v/%v via discovery, got: %v", group, version, r.Categories)
564 }
565 }
566
567 func TestNoNamespaceReject(t *testing.T) {
568 tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
569 if err != nil {
570 t.Fatal(err)
571 }
572 defer tearDown()
573
574 noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
575 noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
576 if err != nil {
577 t.Fatal(err)
578 }
579
580 ns := ""
581 noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
582 initialList, err := noxuResourceClient.List(context.TODO(), metav1.ListOptions{})
583 if err != nil {
584 t.Fatal(err)
585 }
586 if e, a := 0, len(initialList.Items); e != a {
587 t.Errorf("expected %v, got %v", e, a)
588 }
589 initialListTypeMeta, err := meta.TypeAccessor(initialList)
590 if err != nil {
591 t.Fatal(err)
592 }
593 if e, a := noxuDefinition.Spec.Group+"/"+noxuDefinition.Spec.Versions[0].Name, initialListTypeMeta.GetAPIVersion(); e != a {
594 t.Errorf("expected %v, got %v", e, a)
595 }
596 if e, a := noxuDefinition.Spec.Names.ListKind, initialListTypeMeta.GetKind(); e != a {
597 t.Errorf("expected %v, got %v", e, a)
598 }
599
600 createdNoxuInstance, err := instantiateCustomResource(t, fixtures.NewNoxuInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
601 if err == nil {
602 t.Fatalf("unexpected non-error: an empty namespace may not be set during creation while creating noxu instance: %v ", createdNoxuInstance)
603 }
604 }
605
606 func TestSameNameDiffNamespace(t *testing.T) {
607 tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
608 if err != nil {
609 t.Fatal(err)
610 }
611 defer tearDown()
612
613 noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
614 noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
615 if err != nil {
616 t.Fatal(err)
617 }
618
619 ns1 := "namespace-1"
620 testSimpleCRUD(t, ns1, noxuDefinition, dynamicClient)
621 ns2 := "namespace-2"
622 testSimpleCRUD(t, ns2, noxuDefinition, dynamicClient)
623
624 }
625
626 func TestPreserveInt(t *testing.T) {
627 tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
628 if err != nil {
629 t.Fatal(err)
630 }
631 defer tearDown()
632
633 noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.ClusterScoped)
634 noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
635 if err != nil {
636 t.Fatal(err)
637 }
638
639 ns := "not-the-default"
640 noxuNamespacedResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
641
642 noxuInstanceToCreate := fixtures.NewNoxuInstance(ns, "foo")
643 createdNoxuInstance, err := noxuNamespacedResourceClient.Create(context.TODO(), noxuInstanceToCreate, metav1.CreateOptions{})
644 if err != nil {
645 t.Fatal(err)
646 }
647
648 originalJSON, err := runtime.Encode(unstructured.UnstructuredJSONScheme, createdNoxuInstance)
649 if err != nil {
650 t.Fatalf("unexpected error: %v", err)
651 }
652
653 gottenNoxuInstance, err := runtime.Decode(unstructured.UnstructuredJSONScheme, originalJSON)
654 if err != nil {
655 t.Fatalf("unexpected error: %v", err)
656 }
657
658
659 unstructuredObj := gottenNoxuInstance.(*unstructured.Unstructured).Object
660 num := unstructuredObj["num"].(map[string]interface{})
661 num1 := num["num1"].(int64)
662 num2 := num["num2"].(int64)
663 if num1 != 9223372036854775807 || num2 != 1000000 {
664 t.Errorf("Expected %v, got %v, %v", `9223372036854775807, 1000000`, num1, num2)
665 }
666 }
667
668 func TestPatch(t *testing.T) {
669 tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
670 if err != nil {
671 t.Fatal(err)
672 }
673 defer tearDown()
674
675 noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.ClusterScoped)
676 noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
677 if err != nil {
678 t.Fatal(err)
679 }
680
681 ns := "not-the-default"
682 noxuNamespacedResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
683
684 t.Logf("Creating foo")
685 noxuInstanceToCreate := fixtures.NewNoxuInstance(ns, "foo")
686 _, err = noxuNamespacedResourceClient.Create(context.TODO(), noxuInstanceToCreate, metav1.CreateOptions{})
687 if err != nil {
688 t.Fatal(err)
689 }
690
691 t.Logf("Patching .num.num2 to 999")
692 patch := []byte(`{"num": {"num2":999}}`)
693 patchedNoxuInstance, err := noxuNamespacedResourceClient.Patch(context.TODO(), "foo", types.MergePatchType, patch, metav1.PatchOptions{})
694 if err != nil {
695 t.Fatalf("unexpected error: %v", err)
696 }
697 expectInt64(t, patchedNoxuInstance.UnstructuredContent(), 999, "num", "num2")
698 rv, found, err := unstructured.NestedString(patchedNoxuInstance.UnstructuredContent(), "metadata", "resourceVersion")
699 if err != nil {
700 t.Fatal(err)
701 }
702 if !found {
703 t.Fatalf("metadata.resourceVersion not found")
704 }
705
706
707 t.Logf("Patching .num.num2 again to 999")
708 patchedNoxuInstance, err = noxuNamespacedResourceClient.Patch(context.TODO(), "foo", types.MergePatchType, patch, metav1.PatchOptions{})
709 if err != nil {
710 t.Fatalf("unexpected error: %v", err)
711 }
712
713 expectInt64(t, patchedNoxuInstance.UnstructuredContent(), 999, "num", "num2")
714 expectString(t, patchedNoxuInstance.UnstructuredContent(), rv, "metadata", "resourceVersion")
715
716
717 t.Logf("Applying empty patch")
718 patchedNoxuInstance, err = noxuNamespacedResourceClient.Patch(context.TODO(), "foo", types.MergePatchType, []byte(`{}`), metav1.PatchOptions{})
719 if err != nil {
720 t.Fatalf("unexpected error: %v", err)
721 }
722
723 expectInt64(t, patchedNoxuInstance.UnstructuredContent(), 999, "num", "num2")
724 expectString(t, patchedNoxuInstance.UnstructuredContent(), rv, "metadata", "resourceVersion")
725
726 originalJSON, err := runtime.Encode(unstructured.UnstructuredJSONScheme, patchedNoxuInstance)
727 if err != nil {
728 t.Fatalf("unexpected error: %v", err)
729 }
730
731 gottenNoxuInstance, err := runtime.Decode(unstructured.UnstructuredJSONScheme, originalJSON)
732 if err != nil {
733 t.Fatalf("unexpected error: %v", err)
734 }
735
736
737 unstructuredObj := gottenNoxuInstance.(*unstructured.Unstructured).Object
738 num := unstructuredObj["num"].(map[string]interface{})
739 num1 := num["num1"].(int64)
740 num2 := num["num2"].(int64)
741 if num1 != 9223372036854775807 || num2 != 999 {
742 t.Errorf("Expected %v, got %v, %v", `9223372036854775807, 999`, num1, num2)
743 }
744 }
745
746 func TestCrossNamespaceListWatch(t *testing.T) {
747 tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
748 if err != nil {
749 t.Fatal(err)
750 }
751 defer tearDown()
752
753 noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
754 noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
755 if err != nil {
756 t.Fatal(err)
757 }
758
759 ns := ""
760 noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
761 initialList, err := noxuResourceClient.List(context.TODO(), metav1.ListOptions{})
762 if err != nil {
763 t.Fatal(err)
764 }
765 if e, a := 0, len(initialList.Items); e != a {
766 t.Errorf("expected %v, got %v", e, a)
767 }
768
769 initialListListMeta, err := meta.ListAccessor(initialList)
770 if err != nil {
771 t.Fatal(err)
772 }
773
774 noxuWatch, err := noxuResourceClient.Watch(context.TODO(), metav1.ListOptions{ResourceVersion: initialListListMeta.GetResourceVersion()})
775 if err != nil {
776 t.Fatal(err)
777 }
778 defer noxuWatch.Stop()
779
780 instances := make(map[string]*unstructured.Unstructured)
781 ns1 := "namespace-1"
782 noxuNamespacedResourceClient1 := newNamespacedCustomResourceClient(ns1, dynamicClient, noxuDefinition)
783 instances[ns1] = createInstanceWithNamespaceHelper(t, ns1, "foo1", noxuNamespacedResourceClient1, noxuDefinition)
784 noxuNamespacesWatch1, err := noxuNamespacedResourceClient1.Watch(context.TODO(), metav1.ListOptions{ResourceVersion: initialListListMeta.GetResourceVersion()})
785 if err != nil {
786 t.Fatalf("Failed to watch namespace: %s, error: %v", ns1, err)
787 }
788 defer noxuNamespacesWatch1.Stop()
789
790 ns2 := "namespace-2"
791 noxuNamespacedResourceClient2 := newNamespacedCustomResourceClient(ns2, dynamicClient, noxuDefinition)
792 instances[ns2] = createInstanceWithNamespaceHelper(t, ns2, "foo2", noxuNamespacedResourceClient2, noxuDefinition)
793 noxuNamespacesWatch2, err := noxuNamespacedResourceClient2.Watch(context.TODO(), metav1.ListOptions{ResourceVersion: initialListListMeta.GetResourceVersion()})
794 if err != nil {
795 t.Fatalf("Failed to watch namespace: %s, error: %v", ns2, err)
796 }
797 defer noxuNamespacesWatch2.Stop()
798
799 createdList, err := noxuResourceClient.List(context.TODO(), metav1.ListOptions{})
800 if err != nil {
801 t.Fatal(err)
802 }
803
804 if e, a := 2, len(createdList.Items); e != a {
805 t.Errorf("expected %v, got %v", e, a)
806 }
807
808 for _, a := range createdList.Items {
809 if e := instances[a.GetNamespace()]; !reflect.DeepEqual(e, &a) {
810 t.Errorf("expected %v, got %v", e, a)
811 }
812 }
813
814 addEvents := 0
815 for addEvents < 2 {
816 select {
817 case watchEvent := <-noxuWatch.ResultChan():
818 if e, a := watch.Added, watchEvent.Type; e != a {
819 t.Fatalf("expected %v, got %v", e, a)
820 }
821 createdObjectMeta, err := meta.Accessor(watchEvent.Object)
822 if err != nil {
823 t.Fatal(err)
824 }
825 if len(createdObjectMeta.GetUID()) == 0 {
826 t.Errorf("missing uuid: %#v", watchEvent.Object)
827 }
828 createdTypeMeta, err := meta.TypeAccessor(watchEvent.Object)
829 if err != nil {
830 t.Fatal(err)
831 }
832 if e, a := noxuDefinition.Spec.Group+"/"+noxuDefinition.Spec.Versions[0].Name, createdTypeMeta.GetAPIVersion(); e != a {
833 t.Errorf("expected %v, got %v", e, a)
834 }
835 if e, a := noxuDefinition.Spec.Names.Kind, createdTypeMeta.GetKind(); e != a {
836 t.Errorf("expected %v, got %v", e, a)
837 }
838 delete(instances, createdObjectMeta.GetNamespace())
839 addEvents++
840 case <-time.After(5 * time.Second):
841 t.Fatalf("missing watch event")
842 }
843 }
844 if e, a := 0, len(instances); e != a {
845 t.Errorf("expected %v, got %v", e, a)
846 }
847
848 checkNamespacesWatchHelper(t, ns1, noxuNamespacesWatch1)
849 checkNamespacesWatchHelper(t, ns2, noxuNamespacesWatch2)
850 }
851
852 func createInstanceWithNamespaceHelper(t *testing.T, ns string, name string, noxuNamespacedResourceClient dynamic.ResourceInterface, noxuDefinition *apiextensionsv1.CustomResourceDefinition) *unstructured.Unstructured {
853 createdInstance, err := instantiateCustomResource(t, fixtures.NewNoxuInstance(ns, name), noxuNamespacedResourceClient, noxuDefinition)
854 if err != nil {
855 t.Fatalf("unable to create noxu Instance:%v", err)
856 }
857 return createdInstance
858 }
859
860 func checkNamespacesWatchHelper(t *testing.T, ns string, namespacedwatch watch.Interface) {
861 namespacedAddEvent := 0
862 for namespacedAddEvent < 2 {
863 select {
864 case watchEvent := <-namespacedwatch.ResultChan():
865
866 if namespacedAddEvent > 0 {
867 t.Fatalf("extra watch event")
868 }
869 if e, a := watch.Added, watchEvent.Type; e != a {
870 t.Fatalf("expected %v, got %v", e, a)
871 }
872 createdObjectMeta, err := meta.Accessor(watchEvent.Object)
873 if err != nil {
874 t.Fatal(err)
875 }
876 if e, a := ns, createdObjectMeta.GetNamespace(); e != a {
877 t.Errorf("expected %v, got %v", e, a)
878 }
879 case <-time.After(5 * time.Second):
880 if namespacedAddEvent != 1 {
881 t.Fatalf("missing watch event")
882 }
883 }
884 namespacedAddEvent++
885 }
886 }
887
888 func TestNameConflict(t *testing.T) {
889 tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
890 if err != nil {
891 t.Fatal(err)
892 }
893 defer tearDown()
894
895 noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
896 noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
897 if err != nil {
898 t.Fatal(err)
899 }
900
901 noxu2Definition := fixtures.NewNoxu2CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
902 _, err = apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Create(context.TODO(), noxu2Definition, metav1.CreateOptions{})
903 if err != nil {
904 t.Fatal(err)
905 }
906
907
908 err = wait.Poll(500*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
909 crd, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), noxu2Definition.Name, metav1.GetOptions{})
910 if err != nil {
911 return false, err
912 }
913
914 for _, condition := range crd.Status.Conditions {
915 if condition.Type == apiextensionsv1.NamesAccepted && condition.Status == apiextensionsv1.ConditionFalse {
916 return true, nil
917 }
918 }
919 return false, nil
920 })
921 if err != nil {
922 t.Fatal(err)
923 }
924
925 err = fixtures.DeleteV1CustomResourceDefinition(noxuDefinition, apiExtensionClient)
926 if err != nil {
927 t.Fatal(err)
928 }
929
930
931 err = wait.Poll(500*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
932 crd, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), noxu2Definition.Name, metav1.GetOptions{})
933 if err != nil {
934 return false, err
935 }
936
937 for _, condition := range crd.Status.Conditions {
938 if condition.Type == apiextensionsv1.NamesAccepted && condition.Status == apiextensionsv1.ConditionTrue {
939 return true, nil
940 }
941 }
942 return false, nil
943 })
944 if err != nil {
945 t.Fatal(err)
946 }
947 }
948
949 func TestStatusGetAndPatch(t *testing.T) {
950 tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
951 if err != nil {
952 t.Fatal(err)
953 }
954 defer tearDown()
955
956 noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.NamespaceScoped)
957 noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
958 if err != nil {
959 t.Fatal(err)
960 }
961
962
963 result := &apiextensionsv1.CustomResourceDefinition{}
964 err = apiExtensionClient.ApiextensionsV1().RESTClient().Get().
965 Resource("customresourcedefinitions").
966 Name(noxuDefinition.Name).
967 SubResource("status").
968 Do(context.TODO()).
969 Into(result)
970 if err != nil {
971 t.Fatal(err)
972 }
973
974
975 _, err = apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().
976 Patch(context.TODO(), noxuDefinition.Name, types.StrategicMergePatchType,
977 []byte(fmt.Sprintf(`{"labels":{"test-label":"dummy"}}`)), metav1.PatchOptions{},
978 "status")
979 if err != nil {
980 t.Fatal(err)
981 }
982 }
983
984 func expectInt64(t *testing.T, obj map[string]interface{}, value int64, pth ...string) {
985 if v, found, err := unstructured.NestedInt64(obj, pth...); err != nil {
986 t.Fatalf("failed to access .%s: %v", strings.Join(pth, "."), err)
987 } else if !found {
988 t.Fatalf("failed to find .%s", strings.Join(pth, "."))
989 } else if v != value {
990 t.Fatalf("wanted %d at .%s, got %d", value, strings.Join(pth, "."), v)
991 }
992 }
993 func expectString(t *testing.T, obj map[string]interface{}, value string, pth ...string) {
994 if v, found, err := unstructured.NestedString(obj, pth...); err != nil {
995 t.Fatalf("failed to access .%s: %v", strings.Join(pth, "."), err)
996 } else if !found {
997 t.Fatalf("failed to find .%s", strings.Join(pth, "."))
998 } else if v != value {
999 t.Fatalf("wanted %q at .%s, got %q", value, strings.Join(pth, "."), v)
1000 }
1001 }
1002
View as plain text