@@ -153,7 +153,7 @@ func CacheTest(createCacheFunc func(config *rest.Config, opts cache.Options) (ca
153
153
Expect (listObj .Items ).NotTo (BeEmpty ())
154
154
hasKubeService := false
155
155
for _ , svc := range listObj .Items {
156
- if svc . Namespace == "default" && svc . Name == "kubernetes" {
156
+ if isKubeService ( & svc ) {
157
157
hasKubeService = true
158
158
break
159
159
}
@@ -300,7 +300,7 @@ func CacheTest(createCacheFunc func(config *rest.Config, opts cache.Options) (ca
300
300
Expect (listObj .Items ).NotTo (BeEmpty ())
301
301
hasKubeService := false
302
302
for _ , svc := range listObj .Items {
303
- if svc . GetNamespace () == "default" && svc . GetName () == "kubernetes" {
303
+ if isKubeService ( & svc ) {
304
304
hasKubeService = true
305
305
break
306
306
}
@@ -472,6 +472,204 @@ func CacheTest(createCacheFunc func(config *rest.Config, opts cache.Options) (ca
472
472
svc := & kcorev1.Service {}
473
473
svcKey := client.ObjectKey {Namespace : "unknown" , Name : "unknown" }
474
474
475
+ By ("verifying that an error is returned" )
476
+ err := informerCache .Get (context .Background (), svcKey , svc )
477
+ Expect (err ).To (HaveOccurred ())
478
+ })
479
+ })
480
+ Context ("with metadata-only objects" , func () {
481
+ It ("should be able to list objects that haven't been watched previously" , func () {
482
+ By ("listing all services in the cluster" )
483
+ listObj := & kmetav1.PartialObjectMetadataList {}
484
+ listObj .SetGroupVersionKind (schema.GroupVersionKind {
485
+ Group : "" ,
486
+ Version : "v1" ,
487
+ Kind : "ServiceList" ,
488
+ })
489
+ err := informerCache .List (context .Background (), listObj )
490
+ Expect (err ).To (Succeed ())
491
+
492
+ By ("verifying that the returned list contains the Kubernetes service" )
493
+ // NB: kubernetes default service is automatically created in testenv.
494
+ Expect (listObj .Items ).NotTo (BeEmpty ())
495
+ hasKubeService := false
496
+ for _ , svc := range listObj .Items {
497
+ if isKubeService (& svc ) {
498
+ hasKubeService = true
499
+ break
500
+ }
501
+ }
502
+ Expect (hasKubeService ).To (BeTrue ())
503
+ })
504
+ It ("should be able to get objects that haven't been watched previously" , func () {
505
+ By ("getting the Kubernetes service" )
506
+ svc := & kmetav1.PartialObjectMetadata {}
507
+ svc .SetGroupVersionKind (schema.GroupVersionKind {
508
+ Group : "" ,
509
+ Version : "v1" ,
510
+ Kind : "Service" ,
511
+ })
512
+ svcKey := client.ObjectKey {Namespace : "default" , Name : "kubernetes" }
513
+ Expect (informerCache .Get (context .Background (), svcKey , svc )).To (Succeed ())
514
+
515
+ By ("verifying that the returned service looks reasonable" )
516
+ Expect (svc .GetName ()).To (Equal ("kubernetes" ))
517
+ Expect (svc .GetNamespace ()).To (Equal ("default" ))
518
+ })
519
+
520
+ It ("should support filtering by labels in a single namespace" , func () {
521
+ By ("listing pods with a particular label" )
522
+ // NB: each pod has a "test-label": <pod-name>
523
+ out := kmetav1.PartialObjectMetadataList {}
524
+ out .SetGroupVersionKind (schema.GroupVersionKind {
525
+ Group : "" ,
526
+ Version : "v1" ,
527
+ Kind : "PodList" ,
528
+ })
529
+ err := informerCache .List (context .Background (), & out ,
530
+ client .InNamespace (testNamespaceTwo ),
531
+ client .MatchingLabels (map [string ]string {"test-label" : "test-pod-2" }))
532
+ Expect (err ).To (Succeed ())
533
+
534
+ By ("verifying the returned pods have the correct label" )
535
+ Expect (out .Items ).NotTo (BeEmpty ())
536
+ Expect (out .Items ).Should (HaveLen (1 ))
537
+ actual := out .Items [0 ]
538
+ Expect (actual .GetLabels ()["test-label" ]).To (Equal ("test-pod-2" ))
539
+ })
540
+
541
+ It ("should support filtering by labels from multiple namespaces" , func () {
542
+ By ("creating another pod with the same label but different namespace" )
543
+ anotherPod := createPod ("test-pod-2" , testNamespaceOne , kcorev1 .RestartPolicyAlways )
544
+ defer deletePod (anotherPod )
545
+
546
+ By ("listing pods with a particular label" )
547
+ // NB: each pod has a "test-label": <pod-name>
548
+ out := kmetav1.PartialObjectMetadataList {}
549
+ out .SetGroupVersionKind (schema.GroupVersionKind {
550
+ Group : "" ,
551
+ Version : "v1" ,
552
+ Kind : "PodList" ,
553
+ })
554
+ labels := map [string ]string {"test-label" : "test-pod-2" }
555
+ err := informerCache .List (context .Background (), & out , client .MatchingLabels (labels ))
556
+ Expect (err ).To (Succeed ())
557
+
558
+ By ("verifying multiple pods with the same label in different namespaces are returned" )
559
+ Expect (out .Items ).NotTo (BeEmpty ())
560
+ Expect (out .Items ).Should (HaveLen (2 ))
561
+ for _ , actual := range out .Items {
562
+ Expect (actual .GetLabels ()["test-label" ]).To (Equal ("test-pod-2" ))
563
+ }
564
+
565
+ })
566
+
567
+ It ("should be able to list objects by namespace" , func () {
568
+ By ("listing pods in test-namespace-1" )
569
+ listObj := & kmetav1.PartialObjectMetadataList {}
570
+ listObj .SetGroupVersionKind (schema.GroupVersionKind {
571
+ Group : "" ,
572
+ Version : "v1" ,
573
+ Kind : "PodList" ,
574
+ })
575
+ err := informerCache .List (context .Background (), listObj , client .InNamespace (testNamespaceOne ))
576
+ Expect (err ).To (Succeed ())
577
+
578
+ By ("verifying that the returned pods are in test-namespace-1" )
579
+ Expect (listObj .Items ).NotTo (BeEmpty ())
580
+ Expect (listObj .Items ).Should (HaveLen (1 ))
581
+ actual := listObj .Items [0 ]
582
+ Expect (actual .GetNamespace ()).To (Equal (testNamespaceOne ))
583
+ })
584
+
585
+ It ("should be able to restrict cache to a namespace" , func () {
586
+ By ("creating a namespaced cache" )
587
+ namespacedCache , err := cache .New (cfg , cache.Options {Namespace : testNamespaceOne })
588
+ Expect (err ).NotTo (HaveOccurred ())
589
+
590
+ By ("running the cache and waiting for it to sync" )
591
+ go func () {
592
+ defer GinkgoRecover ()
593
+ Expect (namespacedCache .Start (stop )).To (Succeed ())
594
+ }()
595
+ Expect (namespacedCache .WaitForCacheSync (stop )).NotTo (BeFalse ())
596
+
597
+ By ("listing pods in all namespaces" )
598
+ out := & kmetav1.PartialObjectMetadataList {}
599
+ out .SetGroupVersionKind (schema.GroupVersionKind {
600
+ Group : "" ,
601
+ Version : "v1" ,
602
+ Kind : "PodList" ,
603
+ })
604
+ Expect (namespacedCache .List (context .Background (), out )).To (Succeed ())
605
+
606
+ By ("verifying the returned pod is from the watched namespace" )
607
+ Expect (out .Items ).NotTo (BeEmpty ())
608
+ Expect (out .Items ).Should (HaveLen (1 ))
609
+ Expect (out .Items [0 ].GetNamespace ()).To (Equal (testNamespaceOne ))
610
+
611
+ By ("listing all namespaces - should still be able to get a cluster-scoped resource" )
612
+ namespaceList := & kmetav1.PartialObjectMetadataList {}
613
+ namespaceList .SetGroupVersionKind (schema.GroupVersionKind {
614
+ Group : "" ,
615
+ Version : "v1" ,
616
+ Kind : "NamespaceList" ,
617
+ })
618
+ Expect (namespacedCache .List (context .Background (), namespaceList )).To (Succeed ())
619
+
620
+ By ("verifying the namespace list is not empty" )
621
+ Expect (namespaceList .Items ).NotTo (BeEmpty ())
622
+ })
623
+
624
+ It ("should deep copy the object unless told otherwise" , func () {
625
+ By ("retrieving a specific pod from the cache" )
626
+ out := & kmetav1.PartialObjectMetadata {}
627
+ out .SetGroupVersionKind (schema.GroupVersionKind {
628
+ Group : "" ,
629
+ Version : "v1" ,
630
+ Kind : "Pod" ,
631
+ })
632
+ uKnownPod2 := & kmetav1.PartialObjectMetadata {}
633
+ knownPod2 .(* kcorev1.Pod ).ObjectMeta .DeepCopyInto (& uKnownPod2 .ObjectMeta )
634
+ uKnownPod2 .SetGroupVersionKind (schema.GroupVersionKind {
635
+ Group : "" ,
636
+ Version : "v1" ,
637
+ Kind : "Pod" ,
638
+ })
639
+
640
+ podKey := client.ObjectKey {Name : "test-pod-2" , Namespace : testNamespaceTwo }
641
+ Expect (informerCache .Get (context .Background (), podKey , out )).To (Succeed ())
642
+
643
+ By ("verifying the retrieved pod is equal to a known pod" )
644
+ Expect (out ).To (Equal (uKnownPod2 ))
645
+
646
+ By ("altering a field in the retrieved pod" )
647
+ out .Labels ["foo" ] = "bar"
648
+
649
+ By ("verifying the pods are no longer equal" )
650
+ Expect (out ).NotTo (Equal (knownPod2 ))
651
+ })
652
+
653
+ It ("should return an error if the object is not found" , func () {
654
+ By ("getting a service that does not exists" )
655
+ svc := & kmetav1.PartialObjectMetadata {}
656
+ svc .SetGroupVersionKind (schema.GroupVersionKind {
657
+ Group : "" ,
658
+ Version : "v1" ,
659
+ Kind : "Service" ,
660
+ })
661
+ svcKey := client.ObjectKey {Namespace : testNamespaceOne , Name : "unknown" }
662
+
663
+ By ("verifying that an error is returned" )
664
+ err := informerCache .Get (context .Background (), svcKey , svc )
665
+ Expect (err ).To (HaveOccurred ())
666
+ Expect (errors .IsNotFound (err )).To (BeTrue ())
667
+ })
668
+ It ("should return an error if getting object in unwatched namespace" , func () {
669
+ By ("getting a service that does not exists" )
670
+ svc := & kcorev1.Service {}
671
+ svcKey := client.ObjectKey {Namespace : "unknown" , Name : "unknown" }
672
+
475
673
By ("verifying that an error is returned" )
476
674
err := informerCache .Get (context .Background (), svcKey , svc )
477
675
Expect (err ).To (HaveOccurred ())
@@ -518,7 +716,6 @@ func CacheTest(createCacheFunc func(config *rest.Config, opts cache.Options) (ca
518
716
Eventually (out ).Should (Receive (Equal (pod )))
519
717
close (done )
520
718
})
521
- // TODO: Add a test for when GVK is not in Scheme. Does code support informer for unstructured object?
522
719
It ("should be able to get an informer by group/version/kind" , func (done Done ) {
523
720
By ("getting an shared index informer for gvk = core/v1/pod" )
524
721
gvk := schema.GroupVersionKind {Group : "" , Version : "v1" , Kind : "Pod" }
@@ -744,6 +941,126 @@ func CacheTest(createCacheFunc func(config *rest.Config, opts cache.Options) (ca
744
941
Expect (errors .IsTimeout (err )).To (BeTrue ())
745
942
})
746
943
})
944
+ Context ("with metadata-only objects" , func () {
945
+ It ("should be able to get informer for the object" , func (done Done ) {
946
+ By ("getting a shared index informer for a pod" )
947
+
948
+ pod := & kcorev1.Pod {
949
+ ObjectMeta : kmetav1.ObjectMeta {
950
+ Name : "informer-obj" ,
951
+ Namespace : "default" ,
952
+ },
953
+ Spec : kcorev1.PodSpec {
954
+ Containers : []kcorev1.Container {
955
+ {
956
+ Name : "nginx" ,
957
+ Image : "nginx" ,
958
+ },
959
+ },
960
+ },
961
+ }
962
+
963
+ podMeta := & kmetav1.PartialObjectMetadata {}
964
+ pod .ObjectMeta .DeepCopyInto (& podMeta .ObjectMeta )
965
+ podMeta .SetGroupVersionKind (schema.GroupVersionKind {
966
+ Group : "" ,
967
+ Version : "v1" ,
968
+ Kind : "Pod" ,
969
+ })
970
+
971
+ sii , err := informerCache .GetInformer (context .TODO (), podMeta )
972
+ Expect (err ).NotTo (HaveOccurred ())
973
+ Expect (sii ).NotTo (BeNil ())
974
+ Expect (sii .HasSynced ()).To (BeTrue ())
975
+
976
+ By ("adding an event handler listening for object creation which sends the object to a channel" )
977
+ out := make (chan interface {})
978
+ addFunc := func (obj interface {}) {
979
+ out <- obj
980
+ }
981
+ sii .AddEventHandler (kcache.ResourceEventHandlerFuncs {AddFunc : addFunc })
982
+
983
+ By ("adding an object" )
984
+ cl , err := client .New (cfg , client.Options {})
985
+ Expect (err ).NotTo (HaveOccurred ())
986
+ Expect (cl .Create (context .Background (), pod )).To (Succeed ())
987
+ defer deletePod (pod )
988
+ // re-copy the result in so that we can match on it properly
989
+ pod .ObjectMeta .DeepCopyInto (& podMeta .ObjectMeta )
990
+ // NB(directxman12): proto doesn't care typemeta, and
991
+ // partialobjectmetadata is proto, so no typemeta
992
+ // TODO(directxman12): we should paper over this in controller-runtime
993
+ podMeta .APIVersion = ""
994
+ podMeta .Kind = ""
995
+
996
+ By ("verifying the object's metadata is received on the channel" )
997
+ Eventually (out ).Should (Receive (Equal (podMeta )))
998
+ close (done )
999
+ }, 3 )
1000
+
1001
+ It ("should be able to index an object field then retrieve objects by that field" , func () {
1002
+ By ("creating the cache" )
1003
+ informer , err := cache .New (cfg , cache.Options {})
1004
+ Expect (err ).NotTo (HaveOccurred ())
1005
+
1006
+ By ("indexing the restartPolicy field of the Pod object before starting" )
1007
+ pod := & kmetav1.PartialObjectMetadata {}
1008
+ pod .SetGroupVersionKind (schema.GroupVersionKind {
1009
+ Group : "" ,
1010
+ Version : "v1" ,
1011
+ Kind : "Pod" ,
1012
+ })
1013
+ indexFunc := func (obj runtime.Object ) []string {
1014
+ metadata := obj .(* kmetav1.PartialObjectMetadata )
1015
+ return []string {metadata .Labels ["test-label" ]}
1016
+ }
1017
+ Expect (informer .IndexField (context .TODO (), pod , "metadata.labels.test-label" , indexFunc )).To (Succeed ())
1018
+
1019
+ By ("running the cache and waiting for it to sync" )
1020
+ go func () {
1021
+ defer GinkgoRecover ()
1022
+ Expect (informer .Start (stop )).To (Succeed ())
1023
+ }()
1024
+ Expect (informer .WaitForCacheSync (stop )).NotTo (BeFalse ())
1025
+
1026
+ By ("listing Pods with restartPolicyOnFailure" )
1027
+ listObj := & kmetav1.PartialObjectMetadataList {}
1028
+ listObj .SetGroupVersionKind (schema.GroupVersionKind {
1029
+ Group : "" ,
1030
+ Version : "v1" ,
1031
+ Kind : "PodList" ,
1032
+ })
1033
+ err = informer .List (context .Background (), listObj ,
1034
+ client.MatchingFields {"metadata.labels.test-label" : "test-pod-3" })
1035
+ Expect (err ).To (Succeed ())
1036
+
1037
+ By ("verifying that the returned pods have correct restart policy" )
1038
+ Expect (listObj .Items ).NotTo (BeEmpty ())
1039
+ Expect (listObj .Items ).Should (HaveLen (1 ))
1040
+ actual := listObj .Items [0 ]
1041
+ Expect (actual .GetName ()).To (Equal ("test-pod-3" ))
1042
+ }, 3 )
1043
+
1044
+ It ("should allow for get informer to be cancelled" , func () {
1045
+ By ("creating a context and cancelling it" )
1046
+ ctx , cancel := context .WithCancel (context .Background ())
1047
+ cancel ()
1048
+
1049
+ By ("getting a shared index informer for a pod with a cancelled context" )
1050
+ pod := & kmetav1.PartialObjectMetadata {}
1051
+ pod .SetName ("informer-obj2" )
1052
+ pod .SetNamespace ("default" )
1053
+ pod .SetGroupVersionKind (schema.GroupVersionKind {
1054
+ Group : "" ,
1055
+ Version : "v1" ,
1056
+ Kind : "Pod" ,
1057
+ })
1058
+ sii , err := informerCache .GetInformer (ctx , pod )
1059
+ Expect (err ).To (HaveOccurred ())
1060
+ Expect (sii ).To (BeNil ())
1061
+ Expect (errors .IsTimeout (err )).To (BeTrue ())
1062
+ })
1063
+ })
747
1064
})
748
1065
})
749
1066
}
@@ -765,3 +1082,9 @@ func ensureNamespace(namespace string, client client.Client) error {
765
1082
}
766
1083
return err
767
1084
}
1085
+
1086
+ //nolint:interfacer
1087
+ func isKubeService (svc kmetav1.Object ) bool {
1088
+ // grumble grumble linters grumble grumble
1089
+ return svc .GetNamespace () == "default" && svc .GetName () == "kubernetes"
1090
+ }
0 commit comments