k8s.io/client-go@v0.31.1/metadata/metadatainformer/informer_test.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package metadatainformer 18 19 import ( 20 "context" 21 "flag" 22 "testing" 23 "time" 24 25 "github.com/google/go-cmp/cmp" 26 "k8s.io/klog/v2" 27 28 "k8s.io/apimachinery/pkg/api/equality" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/runtime" 31 "k8s.io/apimachinery/pkg/runtime/schema" 32 "k8s.io/client-go/metadata/fake" 33 "k8s.io/client-go/tools/cache" 34 ) 35 36 func init() { 37 klog.InitFlags(flag.CommandLine) 38 flag.CommandLine.Lookup("v").Value.Set("5") 39 flag.CommandLine.Lookup("alsologtostderr").Value.Set("true") 40 } 41 42 func TestMetadataSharedInformerFactory(t *testing.T) { 43 scenarios := []struct { 44 name string 45 existingObj *metav1.PartialObjectMetadata 46 gvr schema.GroupVersionResource 47 ns string 48 trigger func(gvr schema.GroupVersionResource, ns string, fakeClient *fake.FakeMetadataClient, testObject *metav1.PartialObjectMetadata) *metav1.PartialObjectMetadata 49 handler func(rcvCh chan<- *metav1.PartialObjectMetadata) *cache.ResourceEventHandlerFuncs 50 }{ 51 // scenario 1 52 { 53 name: "scenario 1: test if adding an object triggers AddFunc", 54 ns: "ns-foo", 55 gvr: schema.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "deployments"}, 56 trigger: func(gvr schema.GroupVersionResource, ns string, fakeClient *fake.FakeMetadataClient, _ *metav1.PartialObjectMetadata) *metav1.PartialObjectMetadata { 57 testObject := newPartialObjectMetadata("extensions/v1beta1", "Deployment", "ns-foo", "name-foo") 58 createdObj, err := fakeClient.Resource(gvr).Namespace(ns).(fake.MetadataClient).CreateFake(testObject, metav1.CreateOptions{}) 59 if err != nil { 60 t.Error(err) 61 } 62 return createdObj 63 }, 64 handler: func(rcvCh chan<- *metav1.PartialObjectMetadata) *cache.ResourceEventHandlerFuncs { 65 return &cache.ResourceEventHandlerFuncs{ 66 AddFunc: func(obj interface{}) { 67 rcvCh <- obj.(*metav1.PartialObjectMetadata) 68 }, 69 } 70 }, 71 }, 72 73 // scenario 2 74 { 75 name: "scenario 2: tests if updating an object triggers UpdateFunc", 76 ns: "ns-foo", 77 gvr: schema.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "deployments"}, 78 existingObj: newPartialObjectMetadata("extensions/v1beta1", "Deployment", "ns-foo", "name-foo"), 79 trigger: func(gvr schema.GroupVersionResource, ns string, fakeClient *fake.FakeMetadataClient, testObject *metav1.PartialObjectMetadata) *metav1.PartialObjectMetadata { 80 if testObject.Annotations == nil { 81 testObject.Annotations = make(map[string]string) 82 } 83 testObject.Annotations["test"] = "updatedName" 84 updatedObj, err := fakeClient.Resource(gvr).Namespace(ns).(fake.MetadataClient).UpdateFake(testObject, metav1.UpdateOptions{}) 85 if err != nil { 86 t.Error(err) 87 } 88 return updatedObj 89 }, 90 handler: func(rcvCh chan<- *metav1.PartialObjectMetadata) *cache.ResourceEventHandlerFuncs { 91 return &cache.ResourceEventHandlerFuncs{ 92 UpdateFunc: func(old, updated interface{}) { 93 rcvCh <- updated.(*metav1.PartialObjectMetadata) 94 }, 95 } 96 }, 97 }, 98 99 // scenario 3 100 { 101 name: "scenario 3: test if deleting an object triggers DeleteFunc", 102 ns: "ns-foo", 103 gvr: schema.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "deployments"}, 104 existingObj: newPartialObjectMetadata("extensions/v1beta1", "Deployment", "ns-foo", "name-foo"), 105 trigger: func(gvr schema.GroupVersionResource, ns string, fakeClient *fake.FakeMetadataClient, testObject *metav1.PartialObjectMetadata) *metav1.PartialObjectMetadata { 106 err := fakeClient.Resource(gvr).Namespace(ns).Delete(context.TODO(), testObject.GetName(), metav1.DeleteOptions{}) 107 if err != nil { 108 t.Error(err) 109 } 110 return testObject 111 }, 112 handler: func(rcvCh chan<- *metav1.PartialObjectMetadata) *cache.ResourceEventHandlerFuncs { 113 return &cache.ResourceEventHandlerFuncs{ 114 DeleteFunc: func(obj interface{}) { 115 rcvCh <- obj.(*metav1.PartialObjectMetadata) 116 }, 117 } 118 }, 119 }, 120 } 121 122 for _, ts := range scenarios { 123 t.Run(ts.name, func(t *testing.T) { 124 // test data 125 timeout := time.Duration(3 * time.Second) 126 ctx, cancel := context.WithTimeout(context.Background(), timeout) 127 defer cancel() 128 scheme := fake.NewTestScheme() 129 metav1.AddMetaToScheme(scheme) 130 informerReciveObjectCh := make(chan *metav1.PartialObjectMetadata, 1) 131 objs := []runtime.Object{} 132 if ts.existingObj != nil { 133 objs = append(objs, ts.existingObj) 134 } 135 fakeClient := fake.NewSimpleMetadataClient(scheme, objs...) 136 target := NewSharedInformerFactory(fakeClient, 0) 137 138 // act 139 informerListerForGvr := target.ForResource(ts.gvr) 140 informerListerForGvr.Informer().AddEventHandler(ts.handler(informerReciveObjectCh)) 141 target.Start(ctx.Done()) 142 if synced := target.WaitForCacheSync(ctx.Done()); !synced[ts.gvr] { 143 t.Fatalf("informer for %s hasn't synced", ts.gvr) 144 } 145 146 testObject := ts.trigger(ts.gvr, ts.ns, fakeClient, ts.existingObj) 147 select { 148 case objFromInformer := <-informerReciveObjectCh: 149 if !equality.Semantic.DeepEqual(testObject, objFromInformer) { 150 t.Fatalf("%v", cmp.Diff(testObject, objFromInformer)) 151 } 152 case <-ctx.Done(): 153 t.Errorf("tested informer haven't received an object, waited %v", timeout) 154 } 155 }) 156 } 157 } 158 159 func newPartialObjectMetadata(apiVersion, kind, namespace, name string) *metav1.PartialObjectMetadata { 160 return &metav1.PartialObjectMetadata{ 161 TypeMeta: metav1.TypeMeta{ 162 APIVersion: apiVersion, 163 Kind: kind, 164 }, 165 ObjectMeta: metav1.ObjectMeta{ 166 Namespace: namespace, 167 Name: name, 168 }, 169 } 170 }