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 }