github.com/castai/kvisor@v1.7.1-0.20240516114728-b3572a2607b5/cmd/controller/kube/client_test.go (about) 1 package kube 2 3 import ( 4 "context" 5 "fmt" 6 "reflect" 7 "sync" 8 "testing" 9 "time" 10 11 "github.com/castai/kvisor/pkg/logging" 12 "github.com/samber/lo" 13 "github.com/stretchr/testify/require" 14 appsv1 "k8s.io/api/apps/v1" 15 corev1 "k8s.io/api/core/v1" 16 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 17 "k8s.io/apimachinery/pkg/types" 18 "k8s.io/client-go/informers" 19 "k8s.io/client-go/kubernetes/fake" 20 ) 21 22 func TestClient(t *testing.T) { 23 ctx := context.Background() 24 log := logging.NewTestLog() 25 26 t.Run("call event handlers and listeners", func(t *testing.T) { 27 r := require.New(t) 28 dep1 := newTestDeployment() 29 pod1 := newTestPod() 30 clientset := fake.NewSimpleClientset(dep1, pod1) 31 lis := &mockListener{items: map[types.UID]Object{}} 32 informersFactory := informers.NewSharedInformerFactory(clientset, 0) 33 client := NewClient(log, "castai-kvisor", "kvisor", Version{}, clientset) 34 client.RegisterHandlers(informersFactory) 35 client.RegisterPodsHandlers(informersFactory) 36 client.RegisterKubernetesChangeListener(lis) 37 informersFactory.Start(ctx.Done()) 38 informersFactory.WaitForCacheSync(ctx.Done()) 39 40 // Assert added deployment. 41 r.Eventually(func() bool { 42 items := lis.getItems() 43 if len(items) == 2 { 44 return true 45 } 46 return false 47 }, 2*time.Second, 10*time.Millisecond) 48 49 r.NoError(clientset.AppsV1().Deployments(dep1.Namespace).Delete(ctx, dep1.Name, metav1.DeleteOptions{})) 50 51 // Assert deployment is deleted. 52 r.Eventually(func() bool { 53 items := lis.getItems() 54 if len(items) == 1 { 55 item := items[0] 56 if reflect.TypeOf(item) != reflect.TypeOf(&corev1.Pod{}) { 57 t.Fatal("expected to keep pod after deployment delete") 58 } 59 return true 60 } 61 return false 62 }, 2*time.Second, 10*time.Millisecond) 63 }) 64 } 65 66 func aa(ob Object) { 67 fmt.Println(reflect.TypeOf(ob)) 68 } 69 70 type mockListener struct { 71 items map[types.UID]Object 72 mu sync.Mutex 73 } 74 75 func (m *mockListener) RequiredTypes() []reflect.Type { 76 return []reflect.Type{ 77 reflect.TypeOf(&corev1.Pod{}), 78 reflect.TypeOf(&appsv1.Deployment{}), 79 } 80 } 81 82 func (m *mockListener) getItems() []Object { 83 m.mu.Lock() 84 defer m.mu.Unlock() 85 return lo.Values(m.items) 86 } 87 88 func (m *mockListener) OnAdd(obj Object) { 89 m.mu.Lock() 90 defer m.mu.Unlock() 91 m.items[obj.GetUID()] = obj 92 } 93 94 func (m *mockListener) OnDelete(obj Object) { 95 m.mu.Lock() 96 defer m.mu.Unlock() 97 delete(m.items, obj.GetUID()) 98 } 99 100 func (m *mockListener) OnUpdate(obj Object) { 101 m.mu.Lock() 102 defer m.mu.Unlock() 103 m.items[obj.GetUID()] = obj 104 } 105 106 func newTestDeployment() *appsv1.Deployment { 107 return &appsv1.Deployment{ 108 TypeMeta: metav1.TypeMeta{Kind: "Deployment", APIVersion: "v1"}, 109 ObjectMeta: metav1.ObjectMeta{ 110 Name: "nginx-1", 111 Namespace: "default", 112 UID: types.UID("111b56a9-ab5e-4a35-93af-f092e2f63011"), 113 OwnerReferences: []metav1.OwnerReference{ 114 { 115 UID: types.UID("owner"), 116 APIVersion: "v1", 117 Kind: "Node", 118 Controller: lo.ToPtr(true), 119 Name: "node1", 120 }, 121 }, 122 Labels: map[string]string{"subscriber": "test"}, 123 }, 124 Spec: appsv1.DeploymentSpec{ 125 Template: corev1.PodTemplateSpec{ 126 Spec: corev1.PodSpec{ 127 NodeName: "n1", 128 Containers: []corev1.Container{ 129 { 130 Name: "nginx", 131 Image: "nginx:1.23", 132 }, 133 }, 134 }, 135 }, 136 }, 137 Status: appsv1.DeploymentStatus{ 138 Replicas: 1, 139 }, 140 } 141 } 142 143 func newTestPod() *corev1.Pod { 144 return &corev1.Pod{ 145 ObjectMeta: metav1.ObjectMeta{ 146 Name: "p1", 147 Namespace: "default", 148 UID: types.UID("111b56a9-ab5e-4a35-93af-f092e2f63022"), 149 }, 150 } 151 }