github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/monitor/internal/k8s/monitor_test.go (about)

     1  package k8smonitor
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"reflect"
     7  	"testing"
     8  
     9  	"github.com/golang/mock/gomock"
    10  	"go.aporeto.io/enforcerd/trireme-lib/collector"
    11  	"go.aporeto.io/enforcerd/trireme-lib/monitor/config"
    12  	"go.aporeto.io/enforcerd/trireme-lib/monitor/constants"
    13  	"go.aporeto.io/enforcerd/trireme-lib/monitor/external"
    14  	"go.aporeto.io/enforcerd/trireme-lib/monitor/external/mockexternal"
    15  	"go.aporeto.io/enforcerd/trireme-lib/monitor/extractors"
    16  	"go.aporeto.io/enforcerd/trireme-lib/monitor/registerer"
    17  	policy "go.aporeto.io/enforcerd/trireme-lib/policy"
    18  	"go.aporeto.io/enforcerd/trireme-lib/policy/mockpolicy"
    19  	"go.aporeto.io/enforcerd/trireme-lib/utils/cri/mockcri"
    20  	corev1 "k8s.io/api/core/v1"
    21  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    22  	kubernetes "k8s.io/client-go/kubernetes"
    23  	"k8s.io/client-go/kubernetes/fake"
    24  	runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
    25  )
    26  
    27  func TestK8sMonitor_SetupConfig(t *testing.T) {
    28  	type args struct {
    29  		registerer registerer.Registerer
    30  		cfg        interface{}
    31  	}
    32  	tests := []struct {
    33  		name    string
    34  		args    args
    35  		wantErr bool
    36  	}{
    37  		{
    38  			name: "wrong config type",
    39  			args: args{
    40  				registerer: nil,
    41  				cfg:        "wrong type",
    42  			},
    43  			wantErr: true,
    44  		},
    45  		{
    46  			name: "default config is not workable",
    47  			args: args{
    48  				registerer: nil,
    49  				cfg:        nil,
    50  			},
    51  			wantErr: true,
    52  		},
    53  		{
    54  			name: "config: missing metadataExtractor",
    55  			args: args{
    56  				registerer: nil,
    57  				cfg: &Config{
    58  					MetadataExtractor: nil,
    59  				},
    60  			},
    61  			wantErr: true,
    62  		},
    63  		{
    64  			name: "config: missing netclsProgrammer",
    65  			args: args{
    66  				registerer: nil,
    67  				cfg: &Config{
    68  					MetadataExtractor: func(context.Context, *corev1.Pod, string) (*policy.PURuntime, error) {
    69  						return nil, nil
    70  					},
    71  				},
    72  			},
    73  			wantErr: true,
    74  		},
    75  		{
    76  			name: "config: missing sandboxExtractor",
    77  			args: args{
    78  				registerer: nil,
    79  				cfg: &Config{
    80  					MetadataExtractor: func(context.Context, *corev1.Pod, string) (*policy.PURuntime, error) {
    81  						return nil, nil
    82  					},
    83  				},
    84  			},
    85  			wantErr: true,
    86  		},
    87  		{
    88  			name: "config: missing resetNetcls",
    89  			args: args{
    90  				registerer: nil,
    91  				cfg: &Config{
    92  					MetadataExtractor: func(context.Context, *corev1.Pod, string) (*policy.PURuntime, error) {
    93  						return nil, nil
    94  					},
    95  				},
    96  			},
    97  			wantErr: true,
    98  		},
    99  		{
   100  			name: "config: missing CRI runtime service",
   101  			args: args{
   102  				registerer: nil,
   103  				cfg: &Config{
   104  					MetadataExtractor: func(context.Context, *corev1.Pod, string) (*policy.PURuntime, error) {
   105  						return nil, nil
   106  					},
   107  					CRIRuntimeService: nil,
   108  				},
   109  			},
   110  			wantErr: true,
   111  		},
   112  		{
   113  			name: "config: missing CRI runtime service",
   114  			args: args{
   115  				registerer: nil,
   116  				cfg: &Config{
   117  					MetadataExtractor: func(context.Context, *corev1.Pod, string) (*policy.PURuntime, error) {
   118  						return nil, nil
   119  					},
   120  					CRIRuntimeService: nil,
   121  				},
   122  			},
   123  			wantErr: true,
   124  		},
   125  		{
   126  			name: "kubeClient: in-cluster config fails",
   127  			args: args{
   128  				registerer: nil,
   129  				cfg: &Config{
   130  					MetadataExtractor: func(context.Context, *corev1.Pod, string) (*policy.PURuntime, error) {
   131  						return nil, nil
   132  					},
   133  					CRIRuntimeService: mockcri.NewMockExtendedRuntimeService(nil),
   134  					Nodename:          "",
   135  					Kubeconfig:        "",
   136  				},
   137  			},
   138  			wantErr: true,
   139  		},
   140  		{
   141  			name: "kubeClient: non-existent kubeconfig fails",
   142  			args: args{
   143  				registerer: nil,
   144  				cfg: &Config{
   145  					MetadataExtractor: func(context.Context, *corev1.Pod, string) (*policy.PURuntime, error) {
   146  						return nil, nil
   147  					},
   148  					CRIRuntimeService: mockcri.NewMockExtendedRuntimeService(nil),
   149  					Nodename:          "",
   150  					Kubeconfig:        "does-not-exist",
   151  				},
   152  			},
   153  			wantErr: true,
   154  		},
   155  		{
   156  			name: "success",
   157  			args: args{
   158  				registerer: nil,
   159  				cfg: &Config{
   160  					MetadataExtractor: func(context.Context, *corev1.Pod, string) (*policy.PURuntime, error) {
   161  						return nil, nil
   162  					},
   163  					CRIRuntimeService: mockcri.NewMockExtendedRuntimeService(nil),
   164  					Nodename:          "",
   165  					Kubeconfig:        "testdata/kubeconfig",
   166  				},
   167  			},
   168  			wantErr: false,
   169  		},
   170  	}
   171  	for _, tt := range tests {
   172  		t.Run(tt.name, func(t *testing.T) {
   173  			ctx, cancel := context.WithCancel(context.Background())
   174  			m := New(ctx)
   175  			if err := m.SetupConfig(tt.args.registerer, tt.args.cfg); (err != nil) != tt.wantErr {
   176  				t.Errorf("k8sMonitor.SetupConfig() error = %v, wantErr %v", err, tt.wantErr)
   177  			}
   178  			cancel()
   179  		})
   180  	}
   181  }
   182  
   183  func TestK8sMonitor_Resync(t *testing.T) {
   184  	type args struct {
   185  		ctx context.Context
   186  	}
   187  	tests := []struct {
   188  		name    string
   189  		args    args
   190  		wantErr bool
   191  	}{
   192  		{
   193  			name:    "unimplemented",
   194  			wantErr: false,
   195  		},
   196  	}
   197  	for _, tt := range tests {
   198  		t.Run(tt.name, func(t *testing.T) {
   199  			ctx, cancel := context.WithCancel(context.Background())
   200  			m := New(ctx)
   201  			if err := m.Resync(tt.args.ctx); (err != nil) != tt.wantErr {
   202  				t.Errorf("k8sMonitor.Resync() error = %v, wantErr %v", err, tt.wantErr)
   203  			}
   204  			cancel()
   205  		})
   206  	}
   207  }
   208  
   209  func TestK8sMonitor_SetupHandlers(t *testing.T) {
   210  	type args struct {
   211  		c *config.ProcessorConfig
   212  	}
   213  	tests := []struct {
   214  		name string
   215  		args args
   216  	}{
   217  		{
   218  			name: "arguments must be set 1-to-1 in monitor: nil",
   219  			args: args{
   220  				c: nil,
   221  			},
   222  		},
   223  		{
   224  			name: "arguments must be set 1-to-1 in monitor: simple handler",
   225  			args: args{
   226  				c: &config.ProcessorConfig{
   227  					Collector: collector.NewDefaultCollector(),
   228  					Policy:    mockpolicy.NewMockResolver(nil),
   229  					ExternalEventSender: []external.ReceiverRegistration{
   230  						mockexternal.NewMockReceiverRegistration(nil),
   231  					},
   232  				},
   233  			},
   234  		},
   235  	}
   236  	for _, tt := range tests {
   237  		t.Run(tt.name, func(t *testing.T) {
   238  			ctx, cancel := context.WithCancel(context.Background())
   239  			m := New(ctx)
   240  			m.SetupHandlers(tt.args.c)
   241  			if !reflect.DeepEqual(m.handlers, tt.args.c) {
   242  				t.Errorf("m.handlers %v, want %v", m.handlers, tt.args.c)
   243  			}
   244  			cancel()
   245  		})
   246  	}
   247  }
   248  
   249  func TestK8sMonitor_Run(t *testing.T) {
   250  	podTemplate1 := &corev1.Pod{
   251  		ObjectMeta: metav1.ObjectMeta{
   252  			Name:      "my-pod",
   253  			Namespace: "default",
   254  		},
   255  		Spec: corev1.PodSpec{
   256  			NodeName: "test",
   257  		},
   258  	}
   259  	podTemplate2 := &corev1.Pod{
   260  		ObjectMeta: metav1.ObjectMeta{
   261  			Name:      "my-host-network-pod",
   262  			Namespace: "default",
   263  		},
   264  		Spec: corev1.PodSpec{
   265  			HostNetwork: true,
   266  			NodeName:    "test",
   267  		},
   268  	}
   269  	c := fake.NewSimpleClientset(
   270  		podTemplate1.DeepCopy(),
   271  		podTemplate2.DeepCopy(),
   272  	)
   273  	type fields struct {
   274  		collector                collector.EventCollector
   275  		unsetExternalEventSender bool
   276  		kubeClient               kubernetes.Interface
   277  		metadataExtractor        extractors.PodMetadataExtractor
   278  		nodename                 string
   279  	}
   280  	tests := []struct {
   281  		name    string
   282  		fields  fields
   283  		wantErr bool
   284  		prepare func(t *testing.T, mocks *unitTestMonitorMocks)
   285  	}{
   286  		{
   287  			name: "no kubeClient",
   288  			fields: fields{
   289  				kubeClient: nil,
   290  			},
   291  			wantErr: true,
   292  			prepare: func(t *testing.T, mocks *unitTestMonitorMocks) {
   293  			},
   294  		},
   295  		{
   296  			name: "handlers setup is incomplete",
   297  			fields: fields{
   298  				collector:  nil,
   299  				kubeClient: c,
   300  			},
   301  			wantErr: true,
   302  			prepare: func(t *testing.T, mocks *unitTestMonitorMocks) {
   303  			},
   304  		},
   305  		{
   306  			name: "ExternalEventSender is not set",
   307  			fields: fields{
   308  				collector:                collector.NewDefaultCollector(),
   309  				unsetExternalEventSender: true,
   310  				kubeClient:               c,
   311  			},
   312  			wantErr: true,
   313  			prepare: func(t *testing.T, mocks *unitTestMonitorMocks) {
   314  			},
   315  		},
   316  		{
   317  			name: "ReceiverRegistration fails: unavailable",
   318  			fields: fields{
   319  				collector:                collector.NewDefaultCollector(),
   320  				unsetExternalEventSender: false,
   321  				kubeClient:               c,
   322  				nodename:                 "test",
   323  			},
   324  			wantErr: true,
   325  			prepare: func(t *testing.T, mocks *unitTestMonitorMocks) {
   326  				mocks.podCache.EXPECT().SetupInformer(
   327  					gomock.Any(),
   328  					gomock.Eq(c),
   329  					gomock.Eq("test"),
   330  					gomock.AssignableToTypeOf(defaultNeedsUpdate),
   331  				)
   332  				mocks.externalEventSender.EXPECT().SenderName().Return("random").Times(1)
   333  			},
   334  		},
   335  		{
   336  			name: "ReceiverRegistration fails: Register call fails",
   337  			fields: fields{
   338  				collector:                collector.NewDefaultCollector(),
   339  				unsetExternalEventSender: false,
   340  				kubeClient:               c,
   341  				nodename:                 "test",
   342  			},
   343  			wantErr: true,
   344  			prepare: func(t *testing.T, mocks *unitTestMonitorMocks) {
   345  				mocks.podCache.EXPECT().SetupInformer(
   346  					gomock.Any(),
   347  					gomock.Eq(c),
   348  					gomock.Eq("test"),
   349  					gomock.AssignableToTypeOf(defaultNeedsUpdate),
   350  				)
   351  				mocks.externalEventSender.EXPECT().SenderName().Return(constants.MonitorExtSenderName).Times(1)
   352  				mocks.externalEventSender.EXPECT().Register(
   353  					gomock.Eq(constants.K8sMonitorRegistrationName),
   354  					gomock.AssignableToTypeOf(&K8sMonitor{}),
   355  				).Return(fmt.Errorf("error")).Times(1)
   356  			},
   357  		},
   358  		{
   359  			name: "Listing Sandboxes from CRI fails",
   360  			fields: fields{
   361  				collector:                collector.NewDefaultCollector(),
   362  				unsetExternalEventSender: false,
   363  				kubeClient:               c,
   364  				nodename:                 "test",
   365  			},
   366  			wantErr: true,
   367  			prepare: func(t *testing.T, mocks *unitTestMonitorMocks) {
   368  				mocks.externalEventSender.EXPECT().SenderName().Return(constants.MonitorExtSenderName).Times(1)
   369  				mocks.externalEventSender.EXPECT().Register(
   370  					gomock.Eq(constants.K8sMonitorRegistrationName),
   371  					gomock.AssignableToTypeOf(&K8sMonitor{}),
   372  				).Return(nil).Times(1)
   373  				mocks.podCache.EXPECT().SetupInformer(
   374  					gomock.Any(),
   375  					gomock.Eq(c),
   376  					gomock.Eq("test"),
   377  					gomock.AssignableToTypeOf(defaultNeedsUpdate),
   378  				)
   379  				mocks.cri.EXPECT().ListPodSandbox(gomock.Eq(&runtimeapi.PodSandboxFilter{
   380  					State: &runtimeapi.PodSandboxStateValue{
   381  						State: runtimeapi.PodSandboxState_SANDBOX_READY,
   382  					},
   383  				})).Return(nil, fmt.Errorf("error")).Times(1)
   384  			},
   385  		},
   386  		{
   387  			name: "monitor starts successful with empty sandbox list from CRI",
   388  			fields: fields{
   389  				collector:                collector.NewDefaultCollector(),
   390  				unsetExternalEventSender: false,
   391  				kubeClient:               c,
   392  				nodename:                 "test",
   393  			},
   394  			wantErr: false,
   395  			prepare: func(t *testing.T, mocks *unitTestMonitorMocks) {
   396  				mocks.externalEventSender.EXPECT().SenderName().Return(constants.MonitorExtSenderName).Times(1)
   397  				mocks.externalEventSender.EXPECT().Register(
   398  					gomock.Eq(constants.K8sMonitorRegistrationName),
   399  					gomock.AssignableToTypeOf(&K8sMonitor{}),
   400  				).Return(nil).Times(1)
   401  				mocks.podCache.EXPECT().SetupInformer(
   402  					gomock.Any(),
   403  					gomock.Eq(c),
   404  					gomock.Eq("test"),
   405  					gomock.AssignableToTypeOf(defaultNeedsUpdate),
   406  				)
   407  				mocks.cri.EXPECT().ListPodSandbox(gomock.Eq(&runtimeapi.PodSandboxFilter{
   408  					State: &runtimeapi.PodSandboxStateValue{
   409  						State: runtimeapi.PodSandboxState_SANDBOX_READY,
   410  					},
   411  				})).Return(
   412  					[]*runtimeapi.PodSandbox{},
   413  					nil,
   414  				).Times(1)
   415  			},
   416  		},
   417  	}
   418  	for _, tt := range tests {
   419  		t.Run(tt.name, func(t *testing.T) {
   420  			ctrl := gomock.NewController(t)
   421  			m, mocks := newUnitTestMonitor(ctrl)
   422  			m.SenderReady()
   423  			m.handlers.Collector = tt.fields.collector
   424  			if tt.fields.unsetExternalEventSender {
   425  				m.handlers.ExternalEventSender = nil
   426  			}
   427  			m.kubeClient = tt.fields.kubeClient
   428  			m.metadataExtractor = tt.fields.metadataExtractor
   429  			m.nodename = tt.fields.nodename
   430  			tt.prepare(t, mocks)
   431  			if err := m.Run(context.Background()); (err != nil) != tt.wantErr {
   432  				t.Errorf("k8sMonitor.Run() error = %v, wantErr %v", err, tt.wantErr)
   433  			}
   434  			ctrl.Finish()
   435  		})
   436  	}
   437  }