github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/monitor/internal/pod/delete_controller_test.go (about) 1 // +build !windows 2 3 package podmonitor 4 5 import ( 6 "context" 7 "fmt" 8 "testing" 9 "time" 10 11 "github.com/golang/mock/gomock" 12 . "github.com/smartystreets/goconvey/convey" 13 "go.aporeto.io/trireme-lib/common" 14 "go.aporeto.io/trireme-lib/monitor/config" 15 "go.aporeto.io/trireme-lib/monitor/extractors" 16 "go.aporeto.io/trireme-lib/policy/mockpolicy" 17 corev1 "k8s.io/api/core/v1" 18 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 "k8s.io/apimachinery/pkg/types" 20 21 "sigs.k8s.io/controller-runtime/pkg/client" 22 fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" 23 "sigs.k8s.io/controller-runtime/pkg/event" 24 ) 25 26 func TestDeleteControllerFunctionality(t *testing.T) { 27 Convey("Given a fake controller-runtime client and a mock policy resolver", t, func() { 28 ctrl := gomock.NewController(t) 29 defer ctrl.Finish() 30 31 nodeName := "test1" 32 pod1 := &corev1.Pod{ 33 ObjectMeta: metav1.ObjectMeta{ 34 Name: "pod1", 35 Namespace: "default", 36 UID: types.UID("aaaa"), 37 }, 38 Spec: corev1.PodSpec{ 39 NodeName: nodeName, 40 }, 41 } 42 c := fakeclient.NewFakeClient(pod1) 43 eventsCh := make(chan event.GenericEvent) 44 go func() { 45 for { 46 <-eventsCh 47 } 48 }() 49 handler := mockpolicy.NewMockResolver(ctrl) 50 51 pc := &config.ProcessorConfig{ 52 Policy: handler, 53 } 54 55 ctx := context.Background() 56 itemProcessTimeout := 5 * time.Second 57 58 failure := fmt.Errorf("failure") 59 60 Convey("then no destroy events should be sent if there is nothing in the state right now", func() { 61 handler.EXPECT().HandlePUEvent(gomock.Any(), gomock.Any(), common.EventDestroy, gomock.Any()).Return(nil).Times(0) 62 m := make(map[string]DeleteObject) 63 deleteControllerReconcile(ctx, c, nodeName, pc, itemProcessTimeout, m, nil, eventsCh) 64 So(m, ShouldBeEmpty) 65 }) 66 67 Convey("then *no* destroy events should be sent if the pod with the same namespaced name and UID still exists in the Kubernetes API", func() { 68 handler.EXPECT().HandlePUEvent(gomock.Any(), gomock.Any(), common.EventDestroy, gomock.Any()).Return(nil).Times(0) 69 m := make(map[string]DeleteObject) 70 nn := client.ObjectKey{ 71 Name: "pod1", 72 Namespace: "default", 73 } 74 m["aaaa"] = DeleteObject{podUID: "aaaa", sandboxID: "", podName: nn} 75 deleteControllerReconcile(ctx, c, nodeName, pc, itemProcessTimeout, m, nil, eventsCh) 76 So(m, ShouldHaveLength, 1) 77 }) 78 79 Convey("then a destroy event should be sent if the pod with the same namespaced name and UID still exists in the Kubernetes API, but is now on a different node", func() { 80 handler.EXPECT().HandlePUEvent(gomock.Any(), gomock.Any(), common.EventDestroy, gomock.Any()).Return(nil).Times(1) 81 m := make(map[string]DeleteObject) 82 nn := client.ObjectKey{ 83 Name: "pod1", 84 Namespace: "default", 85 } 86 m["aaaa"] = DeleteObject{podUID: "aaaa", sandboxID: "", podName: nn} 87 deleteControllerReconcile(ctx, c, "test2", pc, itemProcessTimeout, m, nil, eventsCh) 88 So(m, ShouldBeEmpty) 89 }) 90 91 Convey("then a destroy event should be sent if the pod with the same namespaced name but *different* UID exists in the Kubernetes API", func() { 92 handler.EXPECT().HandlePUEvent(gomock.Any(), gomock.Any(), common.EventDestroy, gomock.Any()).Return(nil).Times(1) 93 m := make(map[string]DeleteObject) 94 nn := client.ObjectKey{ 95 Name: "pod1", 96 Namespace: "default", 97 } 98 m["bbbb"] = DeleteObject{podUID: "", sandboxID: "", podName: nn} 99 deleteControllerReconcile(ctx, c, nodeName, pc, itemProcessTimeout, m, nil, eventsCh) 100 So(m, ShouldBeEmpty) 101 }) 102 103 Convey("then a destroy event should be sent if the pod does not exist in the Kubernetes API", func() { 104 handler.EXPECT().HandlePUEvent(gomock.Any(), gomock.Any(), common.EventDestroy, gomock.Any()).Return(nil).Times(1) 105 m := make(map[string]DeleteObject) 106 107 nn := client.ObjectKey{ 108 Name: "pod2", 109 Namespace: "default", 110 } 111 m["aaaa"] = DeleteObject{podUID: "", sandboxID: "", podName: nn} 112 deleteControllerReconcile(ctx, c, nodeName, pc, itemProcessTimeout, m, nil, eventsCh) 113 So(m, ShouldBeEmpty) 114 }) 115 116 Convey("then a destroy event should be sent if the pod with the same namespaced name but *different* UID exists in the Kubernetes API, and it should still be removed from the map if it fails", func() { 117 handler.EXPECT().HandlePUEvent(gomock.Any(), gomock.Any(), common.EventDestroy, gomock.Any()).Return(failure).Times(1) 118 m := make(map[string]DeleteObject) 119 120 nn := client.ObjectKey{ 121 Name: "pod1", 122 Namespace: "default", 123 } 124 m["bbbb"] = DeleteObject{podUID: "", sandboxID: "", podName: nn} 125 deleteControllerReconcile(ctx, c, nodeName, pc, itemProcessTimeout, m, nil, eventsCh) 126 So(m, ShouldBeEmpty) 127 }) 128 129 Convey("then a destroy event should be sent if the pod does not exist in the Kubernetes API, and it should still be removed from the map if it fails", func() { 130 handler.EXPECT().HandlePUEvent(gomock.Any(), gomock.Any(), common.EventDestroy, gomock.Any()).Return(failure).Times(1) 131 m := make(map[string]DeleteObject) 132 133 nn := client.ObjectKey{ 134 Name: "pod2", 135 Namespace: "default", 136 } 137 m["aaaa"] = DeleteObject{podUID: "", sandboxID: "", podName: nn} 138 deleteControllerReconcile(ctx, c, nodeName, pc, itemProcessTimeout, m, nil, eventsCh) 139 So(m, ShouldBeEmpty) 140 }) 141 142 sandboxExtractor := func(context.Context, *corev1.Pod) (string, error) { 143 return "different", nil 144 } 145 146 Convey("then a destroy event should be sent if the pod exists in the Kubernetes API, but the sandbox has changed", func() { 147 handler.EXPECT().HandlePUEvent(gomock.Any(), gomock.Any(), common.EventDestroy, gomock.Any()).Return(nil).Times(1) 148 m := make(map[string]DeleteObject) 149 150 nn := client.ObjectKey{ 151 Name: "pod1", 152 Namespace: "default", 153 } 154 m["aaaa"] = DeleteObject{podUID: "aaaa", sandboxID: "sandbox", podName: nn} 155 deleteControllerReconcile(ctx, c, nodeName, pc, itemProcessTimeout, m, sandboxExtractor, eventsCh) 156 So(m, ShouldBeEmpty) 157 }) 158 }) 159 } 160 161 func TestDeleteController(t *testing.T) { 162 Convey("Given a delete controller", t, func() { 163 z := make(chan struct{}) 164 165 nodeName := "test1" 166 testMap := make(map[string]DeleteObject) 167 eventsCh := make(chan event.GenericEvent) 168 go func() { 169 <-eventsCh 170 }() 171 //nolint:unparam 172 reconcileFunc := func(ctx context.Context, c client.Client, nodeName string, pc *config.ProcessorConfig, t time.Duration, m map[string]DeleteObject, s extractors.PodSandboxExtractor, eventsCh chan event.GenericEvent) { 173 for k, v := range m { 174 testMap[k] = v 175 } 176 } 177 178 dc := NewDeleteController(nil, nodeName, nil, nil, eventsCh) 179 dc.deleteCh = make(chan DeleteEvent) 180 dc.reconcileCh = make(chan struct{}) 181 dc.tickerPeriod = 1 * time.Second 182 dc.itemProcessTimeout = 1 * time.Second 183 dc.reconcileFunc = reconcileFunc 184 185 Convey("it should be able to receive delete events, and access them during a reconcile", func() { 186 ev := DeleteEvent{ 187 PodUID: "aaaa", 188 NamespaceName: client.ObjectKey{ 189 Name: "pod1", 190 Namespace: "default", 191 }, 192 } 193 exp := DeleteObject{ 194 podUID: "aaaa", 195 sandboxID: "", 196 podName: client.ObjectKey{ 197 Name: "pod1", 198 Namespace: "default", 199 }, 200 } 201 go func() { 202 dc.GetDeleteCh() <- ev 203 dc.GetReconcileCh() <- struct{}{} 204 close(z) 205 }() 206 207 err := dc.Start(z) 208 So(err, ShouldBeNil) 209 So(testMap, ShouldContainKey, ev.PodUID) 210 So(testMap[ev.PodUID], ShouldResemble, exp) 211 }) 212 213 Convey("it should be able to receive delete events, and access them during a reconcile that was triggered through the ticker", func() { 214 ev := DeleteEvent{ 215 PodUID: "aaaa", 216 NamespaceName: client.ObjectKey{ 217 Name: "pod1", 218 Namespace: "default", 219 }, 220 } 221 exp := DeleteObject{ 222 podUID: "aaaa", 223 sandboxID: "", 224 podName: client.ObjectKey{ 225 Name: "pod1", 226 Namespace: "default", 227 }, 228 } 229 go func() { 230 dc.GetDeleteCh() <- ev 231 // sleeping for twice the ticker period should always trigger the reconcile 232 time.Sleep(dc.tickerPeriod * 2) 233 close(z) 234 }() 235 236 err := dc.Start(z) 237 So(err, ShouldBeNil) 238 So(testMap, ShouldContainKey, ev.PodUID) 239 So(testMap[ev.PodUID], ShouldResemble, exp) 240 }) 241 242 Reset(func() { 243 testMap = make(map[string]DeleteObject) 244 dc = &DeleteController{ 245 client: nil, 246 handler: nil, 247 deleteCh: make(chan DeleteEvent), 248 reconcileCh: make(chan struct{}), 249 tickerPeriod: 1 * time.Second, 250 itemProcessTimeout: 1 * time.Second, 251 reconcileFunc: reconcileFunc, 252 } 253 }) 254 }) 255 }