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  }