github.com/nginxinc/kubernetes-ingress@v1.12.5/internal/k8s/controller_test.go (about) 1 package k8s 2 3 import ( 4 "errors" 5 "fmt" 6 "reflect" 7 "sort" 8 "strings" 9 "testing" 10 11 "github.com/google/go-cmp/cmp" 12 "github.com/nginxinc/kubernetes-ingress/internal/configs" 13 "github.com/nginxinc/kubernetes-ingress/internal/configs/version1" 14 "github.com/nginxinc/kubernetes-ingress/internal/configs/version2" 15 "github.com/nginxinc/kubernetes-ingress/internal/k8s/appprotect" 16 "github.com/nginxinc/kubernetes-ingress/internal/k8s/secrets" 17 "github.com/nginxinc/kubernetes-ingress/internal/metrics/collectors" 18 "github.com/nginxinc/kubernetes-ingress/internal/nginx" 19 conf_v1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1" 20 conf_v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" 21 api_v1 "k8s.io/api/core/v1" 22 v1 "k8s.io/api/core/v1" 23 networking "k8s.io/api/networking/v1beta1" 24 meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 26 "k8s.io/apimachinery/pkg/util/intstr" 27 "k8s.io/client-go/kubernetes/fake" 28 29 "k8s.io/client-go/tools/cache" 30 ) 31 32 func TestHasCorrectIngressClass(t *testing.T) { 33 ingressClass := "ing-ctrl" 34 incorrectIngressClass := "gce" 35 emptyClass := "" 36 37 testsWithoutIngressClassOnly := []struct { 38 lbc *LoadBalancerController 39 ing *networking.Ingress 40 expected bool 41 }{ 42 { 43 &LoadBalancerController{ 44 ingressClass: ingressClass, 45 useIngressClassOnly: false, 46 metricsCollector: collectors.NewControllerFakeCollector(), 47 }, 48 &networking.Ingress{ 49 ObjectMeta: meta_v1.ObjectMeta{ 50 Annotations: map[string]string{ingressClassKey: emptyClass}, 51 }, 52 }, 53 true, 54 }, 55 { 56 &LoadBalancerController{ 57 ingressClass: ingressClass, 58 useIngressClassOnly: false, 59 metricsCollector: collectors.NewControllerFakeCollector(), 60 }, 61 &networking.Ingress{ 62 ObjectMeta: meta_v1.ObjectMeta{ 63 Annotations: map[string]string{ingressClassKey: incorrectIngressClass}, 64 }, 65 }, 66 false, 67 }, 68 { 69 &LoadBalancerController{ 70 ingressClass: ingressClass, 71 useIngressClassOnly: false, 72 metricsCollector: collectors.NewControllerFakeCollector(), 73 }, 74 &networking.Ingress{ 75 ObjectMeta: meta_v1.ObjectMeta{ 76 Annotations: map[string]string{ingressClassKey: ingressClass}, 77 }, 78 }, 79 true, 80 }, 81 { 82 &LoadBalancerController{ 83 ingressClass: ingressClass, 84 useIngressClassOnly: false, 85 metricsCollector: collectors.NewControllerFakeCollector(), 86 }, 87 &networking.Ingress{ 88 ObjectMeta: meta_v1.ObjectMeta{ 89 Annotations: map[string]string{}, 90 }, 91 }, 92 true, 93 }, 94 } 95 96 testsWithIngressClassOnly := []struct { 97 lbc *LoadBalancerController 98 ing *networking.Ingress 99 expected bool 100 }{ 101 { 102 &LoadBalancerController{ 103 ingressClass: ingressClass, 104 useIngressClassOnly: true, 105 metricsCollector: collectors.NewControllerFakeCollector(), 106 }, 107 &networking.Ingress{ 108 ObjectMeta: meta_v1.ObjectMeta{ 109 Annotations: map[string]string{ingressClassKey: emptyClass}, 110 }, 111 }, 112 false, 113 }, 114 { 115 &LoadBalancerController{ 116 ingressClass: ingressClass, 117 useIngressClassOnly: true, 118 metricsCollector: collectors.NewControllerFakeCollector(), 119 }, 120 &networking.Ingress{ 121 ObjectMeta: meta_v1.ObjectMeta{ 122 Annotations: map[string]string{ingressClassKey: incorrectIngressClass}, 123 }, 124 }, 125 false, 126 }, 127 { 128 &LoadBalancerController{ 129 ingressClass: ingressClass, 130 useIngressClassOnly: true, 131 metricsCollector: collectors.NewControllerFakeCollector(), 132 }, 133 &networking.Ingress{ 134 ObjectMeta: meta_v1.ObjectMeta{ 135 Annotations: map[string]string{ingressClassKey: ingressClass}, 136 }, 137 }, 138 true, 139 }, 140 { 141 &LoadBalancerController{ 142 ingressClass: ingressClass, 143 useIngressClassOnly: true, 144 metricsCollector: collectors.NewControllerFakeCollector(), 145 }, 146 &networking.Ingress{ 147 ObjectMeta: meta_v1.ObjectMeta{ 148 Annotations: map[string]string{}, 149 }, 150 }, 151 false, 152 }, 153 { 154 &LoadBalancerController{ 155 ingressClass: ingressClass, 156 useIngressClassOnly: true, // always true for k8s >= 1.18 157 metricsCollector: collectors.NewControllerFakeCollector(), 158 }, 159 &networking.Ingress{ 160 Spec: networking.IngressSpec{ 161 IngressClassName: &incorrectIngressClass, 162 }, 163 }, 164 false, 165 }, 166 { 167 &LoadBalancerController{ 168 ingressClass: ingressClass, 169 useIngressClassOnly: true, // always true for k8s >= 1.18 170 metricsCollector: collectors.NewControllerFakeCollector(), 171 }, 172 &networking.Ingress{ 173 Spec: networking.IngressSpec{ 174 IngressClassName: &emptyClass, 175 }, 176 }, 177 false, 178 }, 179 { 180 &LoadBalancerController{ 181 ingressClass: ingressClass, 182 useIngressClassOnly: true, // always true for k8s >= 1.18 183 metricsCollector: collectors.NewControllerFakeCollector(), 184 }, 185 &networking.Ingress{ 186 ObjectMeta: meta_v1.ObjectMeta{ 187 Annotations: map[string]string{ingressClassKey: incorrectIngressClass}, 188 }, 189 Spec: networking.IngressSpec{ 190 IngressClassName: &ingressClass, 191 }, 192 }, 193 false, 194 }, 195 { 196 &LoadBalancerController{ 197 ingressClass: ingressClass, 198 useIngressClassOnly: true, // always true for k8s >= 1.18 199 metricsCollector: collectors.NewControllerFakeCollector(), 200 }, 201 &networking.Ingress{ 202 Spec: networking.IngressSpec{ 203 IngressClassName: &ingressClass, 204 }, 205 }, 206 true, 207 }, 208 } 209 210 for _, test := range testsWithoutIngressClassOnly { 211 if result := test.lbc.HasCorrectIngressClass(test.ing); result != test.expected { 212 classAnnotation := "N/A" 213 if class, exists := test.ing.Annotations[ingressClassKey]; exists { 214 classAnnotation = class 215 } 216 t.Errorf("lbc.HasCorrectIngressClass(ing), lbc.ingressClass=%v, lbc.useIngressClassOnly=%v, ing.Annotations['%v']=%v; got %v, expected %v", 217 test.lbc.ingressClass, test.lbc.useIngressClassOnly, ingressClassKey, classAnnotation, result, test.expected) 218 } 219 } 220 221 for _, test := range testsWithIngressClassOnly { 222 if result := test.lbc.HasCorrectIngressClass(test.ing); result != test.expected { 223 classAnnotation := "N/A" 224 if class, exists := test.ing.Annotations[ingressClassKey]; exists { 225 classAnnotation = class 226 } 227 t.Errorf("lbc.HasCorrectIngressClass(ing), lbc.ingressClass=%v, lbc.useIngressClassOnly=%v, ing.Annotations['%v']=%v; got %v, expected %v", 228 test.lbc.ingressClass, test.lbc.useIngressClassOnly, ingressClassKey, classAnnotation, result, test.expected) 229 } 230 } 231 } 232 233 func deepCopyWithIngressClass(obj interface{}, class string) interface{} { 234 switch obj := obj.(type) { 235 case *conf_v1.VirtualServer: 236 objCopy := obj.DeepCopy() 237 objCopy.Spec.IngressClass = class 238 return objCopy 239 case *conf_v1.VirtualServerRoute: 240 objCopy := obj.DeepCopy() 241 objCopy.Spec.IngressClass = class 242 return objCopy 243 case *conf_v1alpha1.TransportServer: 244 objCopy := obj.DeepCopy() 245 objCopy.Spec.IngressClass = class 246 return objCopy 247 default: 248 panic(fmt.Sprintf("unknown type %T", obj)) 249 } 250 } 251 252 func TestIngressClassForCustomResources(t *testing.T) { 253 ctrl := &LoadBalancerController{ 254 ingressClass: "nginx", 255 useIngressClassOnly: false, 256 } 257 258 ctrlWithUseICOnly := &LoadBalancerController{ 259 ingressClass: "nginx", 260 useIngressClassOnly: true, 261 } 262 263 tests := []struct { 264 lbc *LoadBalancerController 265 objIngressClass string 266 expected bool 267 msg string 268 }{ 269 { 270 lbc: ctrl, 271 objIngressClass: "nginx", 272 expected: true, 273 msg: "Ingress Controller handles a resource that matches its IngressClass", 274 }, 275 { 276 lbc: ctrlWithUseICOnly, 277 objIngressClass: "nginx", 278 expected: true, 279 msg: "Ingress Controller with useIngressClassOnly handles a resource that matches its IngressClass", 280 }, 281 { 282 lbc: ctrl, 283 objIngressClass: "", 284 expected: true, 285 msg: "Ingress Controller handles a resource with an empty IngressClass", 286 }, 287 { 288 lbc: ctrlWithUseICOnly, 289 objIngressClass: "", 290 expected: true, 291 msg: "Ingress Controller with useIngressClassOnly handles a resource with an empty IngressClass", 292 }, 293 { 294 lbc: ctrl, 295 objIngressClass: "gce", 296 expected: false, 297 msg: "Ingress Controller doesn't handle a resource that doesn't match its IngressClass", 298 }, 299 { 300 lbc: ctrlWithUseICOnly, 301 objIngressClass: "gce", 302 expected: false, 303 msg: "Ingress Controller with useIngressClassOnly doesn't handle a resource that doesn't match its IngressClass", 304 }, 305 } 306 307 resources := []interface{}{ 308 &conf_v1.VirtualServer{}, 309 &conf_v1.VirtualServerRoute{}, 310 &conf_v1alpha1.TransportServer{}, 311 } 312 313 for _, r := range resources { 314 for _, test := range tests { 315 obj := deepCopyWithIngressClass(r, test.objIngressClass) 316 317 result := test.lbc.HasCorrectIngressClass(obj) 318 if result != test.expected { 319 t.Errorf("HasCorrectIngressClass() returned %v but expected %v for the case of %q for %T", result, test.expected, test.msg, obj) 320 } 321 } 322 } 323 } 324 325 func TestComparePorts(t *testing.T) { 326 scenarios := []struct { 327 sp v1.ServicePort 328 cp v1.ContainerPort 329 expected bool 330 }{ 331 { 332 // match TargetPort.strval and Protocol 333 v1.ServicePort{ 334 TargetPort: intstr.FromString("name"), 335 Protocol: v1.ProtocolTCP, 336 }, 337 v1.ContainerPort{ 338 Name: "name", 339 Protocol: v1.ProtocolTCP, 340 ContainerPort: 80, 341 }, 342 true, 343 }, 344 { 345 // don't match Name and Protocol 346 v1.ServicePort{ 347 Name: "name", 348 Protocol: v1.ProtocolTCP, 349 }, 350 v1.ContainerPort{ 351 Name: "name", 352 Protocol: v1.ProtocolTCP, 353 ContainerPort: 80, 354 }, 355 false, 356 }, 357 { 358 // TargetPort intval mismatch, don't match by TargetPort.Name 359 v1.ServicePort{ 360 Name: "name", 361 TargetPort: intstr.FromInt(80), 362 }, 363 v1.ContainerPort{ 364 Name: "name", 365 ContainerPort: 81, 366 }, 367 false, 368 }, 369 { 370 // match by TargetPort intval 371 v1.ServicePort{ 372 TargetPort: intstr.IntOrString{ 373 IntVal: 80, 374 }, 375 }, 376 v1.ContainerPort{ 377 ContainerPort: 80, 378 }, 379 true, 380 }, 381 { 382 // Fall back on ServicePort.Port if TargetPort is empty 383 v1.ServicePort{ 384 Name: "name", 385 Port: 80, 386 }, 387 v1.ContainerPort{ 388 Name: "name", 389 ContainerPort: 80, 390 }, 391 true, 392 }, 393 { 394 // TargetPort intval mismatch 395 v1.ServicePort{ 396 TargetPort: intstr.FromInt(80), 397 }, 398 v1.ContainerPort{ 399 ContainerPort: 81, 400 }, 401 false, 402 }, 403 { 404 // don't match empty ports 405 v1.ServicePort{}, 406 v1.ContainerPort{}, 407 false, 408 }, 409 } 410 411 for _, scen := range scenarios { 412 if scen.expected != compareContainerPortAndServicePort(scen.cp, scen.sp) { 413 t.Errorf("Expected: %v, ContainerPort: %v, ServicePort: %v", scen.expected, scen.cp, scen.sp) 414 } 415 } 416 } 417 418 func TestFindProbeForPods(t *testing.T) { 419 pods := []*v1.Pod{ 420 { 421 Spec: v1.PodSpec{ 422 Containers: []v1.Container{ 423 { 424 ReadinessProbe: &v1.Probe{ 425 ProbeHandler: v1.ProbeHandler{ 426 HTTPGet: &v1.HTTPGetAction{ 427 Path: "/", 428 Host: "asdf.com", 429 Port: intstr.IntOrString{ 430 IntVal: 80, 431 }, 432 }, 433 }, 434 PeriodSeconds: 42, 435 }, 436 Ports: []v1.ContainerPort{ 437 { 438 Name: "name", 439 ContainerPort: 80, 440 Protocol: v1.ProtocolTCP, 441 HostIP: "1.2.3.4", 442 }, 443 }, 444 }, 445 }, 446 }, 447 }, 448 } 449 svcPort := v1.ServicePort{ 450 TargetPort: intstr.FromInt(80), 451 } 452 probe := findProbeForPods(pods, &svcPort) 453 if probe == nil || probe.PeriodSeconds != 42 { 454 t.Errorf("ServicePort.TargetPort as int match failed: %+v", probe) 455 } 456 457 svcPort = v1.ServicePort{ 458 TargetPort: intstr.FromString("name"), 459 Protocol: v1.ProtocolTCP, 460 } 461 probe = findProbeForPods(pods, &svcPort) 462 if probe == nil || probe.PeriodSeconds != 42 { 463 t.Errorf("ServicePort.TargetPort as string failed: %+v", probe) 464 } 465 466 svcPort = v1.ServicePort{ 467 TargetPort: intstr.FromInt(80), 468 Protocol: v1.ProtocolTCP, 469 } 470 probe = findProbeForPods(pods, &svcPort) 471 if probe == nil || probe.PeriodSeconds != 42 { 472 t.Errorf("ServicePort.TargetPort as int failed: %+v", probe) 473 } 474 475 svcPort = v1.ServicePort{ 476 Port: 80, 477 } 478 probe = findProbeForPods(pods, &svcPort) 479 if probe == nil || probe.PeriodSeconds != 42 { 480 t.Errorf("ServicePort.Port should match if TargetPort is not set: %+v", probe) 481 } 482 483 svcPort = v1.ServicePort{ 484 TargetPort: intstr.FromString("wrong_name"), 485 } 486 probe = findProbeForPods(pods, &svcPort) 487 if probe != nil { 488 t.Errorf("ServicePort.TargetPort should not have matched string: %+v", probe) 489 } 490 491 svcPort = v1.ServicePort{ 492 TargetPort: intstr.FromInt(22), 493 } 494 probe = findProbeForPods(pods, &svcPort) 495 if probe != nil { 496 t.Errorf("ServicePort.TargetPort should not have matched int: %+v", probe) 497 } 498 499 svcPort = v1.ServicePort{ 500 Port: 22, 501 } 502 probe = findProbeForPods(pods, &svcPort) 503 if probe != nil { 504 t.Errorf("ServicePort.Port mismatch: %+v", probe) 505 } 506 } 507 508 func TestGetServicePortForIngressPort(t *testing.T) { 509 fakeClient := fake.NewSimpleClientset() 510 cnf := configs.NewConfigurator(&nginx.LocalManager{}, &configs.StaticConfigParams{}, &configs.ConfigParams{}, &version1.TemplateExecutor{}, &version2.TemplateExecutor{}, false, false, nil, false, nil, false) 511 lbc := LoadBalancerController{ 512 client: fakeClient, 513 ingressClass: "nginx", 514 configurator: cnf, 515 metricsCollector: collectors.NewControllerFakeCollector(), 516 } 517 svc := v1.Service{ 518 TypeMeta: meta_v1.TypeMeta{}, 519 ObjectMeta: meta_v1.ObjectMeta{ 520 Name: "coffee-svc", 521 Namespace: "default", 522 }, 523 Spec: v1.ServiceSpec{ 524 Ports: []v1.ServicePort{ 525 { 526 Name: "foo", 527 Port: 80, 528 TargetPort: intstr.FromInt(22), 529 }, 530 }, 531 }, 532 Status: v1.ServiceStatus{}, 533 } 534 ingSvcPort := intstr.FromString("foo") 535 svcPort := lbc.getServicePortForIngressPort(ingSvcPort, &svc) 536 if svcPort == nil || svcPort.Port != 80 { 537 t.Errorf("TargetPort string match failed: %+v", svcPort) 538 } 539 540 ingSvcPort = intstr.FromInt(80) 541 svcPort = lbc.getServicePortForIngressPort(ingSvcPort, &svc) 542 if svcPort == nil || svcPort.Port != 80 { 543 t.Errorf("TargetPort int match failed: %+v", svcPort) 544 } 545 546 ingSvcPort = intstr.FromInt(22) 547 svcPort = lbc.getServicePortForIngressPort(ingSvcPort, &svc) 548 if svcPort != nil { 549 t.Errorf("Mismatched ints should not return port: %+v", svcPort) 550 } 551 ingSvcPort = intstr.FromString("bar") 552 svcPort = lbc.getServicePortForIngressPort(ingSvcPort, &svc) 553 if svcPort != nil { 554 t.Errorf("Mismatched strings should not return port: %+v", svcPort) 555 } 556 } 557 558 func TestFormatWarningsMessages(t *testing.T) { 559 warnings := []string{"Test warning", "Test warning 2"} 560 561 expected := "Test warning; Test warning 2" 562 result := formatWarningMessages(warnings) 563 564 if result != expected { 565 t.Errorf("formatWarningMessages(%v) returned %v but expected %v", warnings, result, expected) 566 } 567 } 568 569 func TestGetEndpointsBySubselectedPods(t *testing.T) { 570 boolPointer := func(b bool) *bool { return &b } 571 tests := []struct { 572 desc string 573 targetPort int32 574 svcEps v1.Endpoints 575 expectedEps []podEndpoint 576 }{ 577 { 578 desc: "find one endpoint", 579 targetPort: 80, 580 expectedEps: []podEndpoint{ 581 { 582 Address: "1.2.3.4:80", 583 MeshPodOwner: configs.MeshPodOwner{ 584 OwnerType: "deployment", 585 OwnerName: "deploy-1", 586 }, 587 }, 588 }, 589 }, 590 { 591 desc: "targetPort mismatch", 592 targetPort: 21, 593 expectedEps: nil, 594 }, 595 } 596 597 pods := []*v1.Pod{ 598 { 599 ObjectMeta: meta_v1.ObjectMeta{ 600 OwnerReferences: []meta_v1.OwnerReference{ 601 { 602 Kind: "Deployment", 603 Name: "deploy-1", 604 Controller: boolPointer(true), 605 }, 606 }, 607 }, 608 Status: v1.PodStatus{ 609 PodIP: "1.2.3.4", 610 }, 611 }, 612 } 613 614 svcEps := v1.Endpoints{ 615 Subsets: []v1.EndpointSubset{ 616 { 617 Addresses: []v1.EndpointAddress{ 618 { 619 IP: "1.2.3.4", 620 Hostname: "asdf.com", 621 }, 622 }, 623 Ports: []v1.EndpointPort{ 624 { 625 Port: 80, 626 }, 627 }, 628 }, 629 }, 630 } 631 632 for _, test := range tests { 633 t.Run(test.desc, func(t *testing.T) { 634 gotEndps := getEndpointsBySubselectedPods(test.targetPort, pods, svcEps) 635 if !reflect.DeepEqual(gotEndps, test.expectedEps) { 636 t.Errorf("getEndpointsBySubselectedPods() = %v, want %v", gotEndps, test.expectedEps) 637 } 638 }) 639 } 640 } 641 642 func TestGetStatusFromEventTitle(t *testing.T) { 643 tests := []struct { 644 eventTitle string 645 expected string 646 }{ 647 { 648 eventTitle: "", 649 expected: "", 650 }, 651 { 652 eventTitle: "AddedOrUpdatedWithError", 653 expected: "Invalid", 654 }, 655 { 656 eventTitle: "Rejected", 657 expected: "Invalid", 658 }, 659 { 660 eventTitle: "NoVirtualServersFound", 661 expected: "Invalid", 662 }, 663 { 664 eventTitle: "Missing Secret", 665 expected: "Invalid", 666 }, 667 { 668 eventTitle: "UpdatedWithError", 669 expected: "Invalid", 670 }, 671 { 672 eventTitle: "AddedOrUpdatedWithWarning", 673 expected: "Warning", 674 }, 675 { 676 eventTitle: "UpdatedWithWarning", 677 expected: "Warning", 678 }, 679 { 680 eventTitle: "AddedOrUpdated", 681 expected: "Valid", 682 }, 683 { 684 eventTitle: "Updated", 685 expected: "Valid", 686 }, 687 { 688 eventTitle: "New State", 689 expected: "", 690 }, 691 } 692 693 for _, test := range tests { 694 result := getStatusFromEventTitle(test.eventTitle) 695 if result != test.expected { 696 t.Errorf("getStatusFromEventTitle(%v) returned %v but expected %v", test.eventTitle, result, test.expected) 697 } 698 } 699 } 700 701 func TestGetPolicies(t *testing.T) { 702 validPolicy := &conf_v1.Policy{ 703 ObjectMeta: meta_v1.ObjectMeta{ 704 Name: "valid-policy", 705 Namespace: "default", 706 }, 707 Spec: conf_v1.PolicySpec{ 708 AccessControl: &conf_v1.AccessControl{ 709 Allow: []string{"127.0.0.1"}, 710 }, 711 }, 712 } 713 714 invalidPolicy := &conf_v1.Policy{ 715 ObjectMeta: meta_v1.ObjectMeta{ 716 Name: "invalid-policy", 717 Namespace: "default", 718 }, 719 Spec: conf_v1.PolicySpec{}, 720 } 721 722 lbc := LoadBalancerController{ 723 isNginxPlus: true, 724 policyLister: &cache.FakeCustomStore{ 725 GetByKeyFunc: func(key string) (item interface{}, exists bool, err error) { 726 switch key { 727 case "default/valid-policy": 728 return validPolicy, true, nil 729 case "default/invalid-policy": 730 return invalidPolicy, true, nil 731 case "nginx-ingress/valid-policy": 732 return nil, false, nil 733 default: 734 return nil, false, errors.New("GetByKey error") 735 } 736 }, 737 }, 738 } 739 740 policyRefs := []conf_v1.PolicyReference{ 741 { 742 Name: "valid-policy", 743 // Namespace is implicit here 744 }, 745 { 746 Name: "invalid-policy", 747 Namespace: "default", 748 }, 749 { 750 Name: "valid-policy", // doesn't exist 751 Namespace: "nginx-ingress", 752 }, 753 { 754 Name: "some-policy", // will make lister return error 755 Namespace: "nginx-ingress", 756 }, 757 } 758 759 expectedPolicies := []*conf_v1.Policy{validPolicy} 760 expectedErrors := []error{ 761 errors.New("Policy default/invalid-policy is invalid: spec: Invalid value: \"\": must specify exactly one of: `accessControl`, `rateLimit`, `ingressMTLS`, `egressMTLS`, `jwt`, `oidc`, `waf`"), 762 errors.New("Policy nginx-ingress/valid-policy doesn't exist"), 763 errors.New("Failed to get policy nginx-ingress/some-policy: GetByKey error"), 764 } 765 766 result, errors := lbc.getPolicies(policyRefs, "default") 767 if !reflect.DeepEqual(result, expectedPolicies) { 768 t.Errorf("lbc.getPolicies() returned \n%v but \nexpected %v", result, expectedPolicies) 769 } 770 if diff := cmp.Diff(expectedErrors, errors, cmp.Comparer(errorComparer)); diff != "" { 771 t.Errorf("lbc.getPolicies() mismatch (-want +got):\n%s", diff) 772 } 773 } 774 775 func TestCreatePolicyMap(t *testing.T) { 776 policies := []*conf_v1.Policy{ 777 { 778 ObjectMeta: meta_v1.ObjectMeta{ 779 Name: "policy-1", 780 Namespace: "default", 781 }, 782 }, 783 { 784 ObjectMeta: meta_v1.ObjectMeta{ 785 Name: "policy-2", 786 Namespace: "default", 787 }, 788 }, 789 { 790 ObjectMeta: meta_v1.ObjectMeta{ 791 Name: "policy-1", 792 Namespace: "default", 793 }, 794 }, 795 { 796 ObjectMeta: meta_v1.ObjectMeta{ 797 Name: "policy-1", 798 Namespace: "nginx-ingress", 799 }, 800 }, 801 } 802 803 expected := map[string]*conf_v1.Policy{ 804 "default/policy-1": { 805 ObjectMeta: meta_v1.ObjectMeta{ 806 Name: "policy-1", 807 Namespace: "default", 808 }, 809 }, 810 "default/policy-2": { 811 ObjectMeta: meta_v1.ObjectMeta{ 812 Name: "policy-2", 813 Namespace: "default", 814 }, 815 }, 816 "nginx-ingress/policy-1": { 817 ObjectMeta: meta_v1.ObjectMeta{ 818 Name: "policy-1", 819 Namespace: "nginx-ingress", 820 }, 821 }, 822 } 823 824 result := createPolicyMap(policies) 825 if !reflect.DeepEqual(result, expected) { 826 t.Errorf("createPolicyMap() returned \n%s but expected \n%s", policyMapToString(result), policyMapToString(expected)) 827 } 828 } 829 830 func TestGetPodOwnerTypeAndName(t *testing.T) { 831 tests := []struct { 832 desc string 833 expType string 834 expName string 835 pod *v1.Pod 836 }{ 837 { 838 desc: "deployment", 839 expType: "deployment", 840 expName: "deploy-name", 841 pod: &v1.Pod{ObjectMeta: createTestObjMeta("Deployment", "deploy-name", true)}, 842 }, 843 { 844 desc: "stateful set", 845 expType: "statefulset", 846 expName: "statefulset-name", 847 pod: &v1.Pod{ObjectMeta: createTestObjMeta("StatefulSet", "statefulset-name", true)}, 848 }, 849 { 850 desc: "daemon set", 851 expType: "daemonset", 852 expName: "daemonset-name", 853 pod: &v1.Pod{ObjectMeta: createTestObjMeta("DaemonSet", "daemonset-name", true)}, 854 }, 855 { 856 desc: "replica set with no pod hash", 857 expType: "deployment", 858 expName: "replicaset-name", 859 pod: &v1.Pod{ObjectMeta: createTestObjMeta("ReplicaSet", "replicaset-name", false)}, 860 }, 861 { 862 desc: "replica set with pod hash", 863 expType: "deployment", 864 expName: "replicaset-name", 865 pod: &v1.Pod{ 866 ObjectMeta: createTestObjMeta("ReplicaSet", "replicaset-name-67c6f7c5fd", true), 867 }, 868 }, 869 { 870 desc: "nil controller should use default values", 871 expType: "deployment", 872 expName: "deploy-name", 873 pod: &v1.Pod{ 874 ObjectMeta: meta_v1.ObjectMeta{ 875 OwnerReferences: []meta_v1.OwnerReference{ 876 { 877 Name: "deploy-name", 878 Controller: nil, 879 }, 880 }, 881 }, 882 }, 883 }, 884 } 885 886 for _, test := range tests { 887 t.Run(test.desc, func(t *testing.T) { 888 actualType, actualName := getPodOwnerTypeAndName(test.pod) 889 if actualType != test.expType { 890 t.Errorf("getPodOwnerTypeAndName() returned %s for owner type but expected %s", actualType, test.expType) 891 } 892 if actualName != test.expName { 893 t.Errorf("getPodOwnerTypeAndName() returned %s for owner name but expected %s", actualName, test.expName) 894 } 895 }) 896 } 897 } 898 899 func createTestObjMeta(kind, name string, podHashLabel bool) meta_v1.ObjectMeta { 900 controller := true 901 meta := meta_v1.ObjectMeta{ 902 OwnerReferences: []meta_v1.OwnerReference{ 903 { 904 Kind: kind, 905 Name: name, 906 Controller: &controller, 907 }, 908 }, 909 } 910 if podHashLabel { 911 meta.Labels = map[string]string{ 912 "pod-template-hash": "67c6f7c5fd", 913 } 914 } 915 return meta 916 } 917 918 func policyMapToString(policies map[string]*conf_v1.Policy) string { 919 var keys []string 920 for k := range policies { 921 keys = append(keys, k) 922 } 923 sort.Strings(keys) 924 925 var b strings.Builder 926 927 b.WriteString("[ ") 928 for _, k := range keys { 929 fmt.Fprintf(&b, "%q: '%s/%s', ", k, policies[k].Namespace, policies[k].Name) 930 } 931 b.WriteString("]") 932 933 return b.String() 934 } 935 936 type testResource struct { 937 keyWithKind string 938 } 939 940 func (*testResource) GetObjectMeta() *meta_v1.ObjectMeta { 941 return nil 942 } 943 944 func (t *testResource) GetKeyWithKind() string { 945 return t.keyWithKind 946 } 947 948 func (*testResource) AcquireHost(string) { 949 } 950 951 func (*testResource) ReleaseHost(string) { 952 } 953 954 func (*testResource) Wins(Resource) bool { 955 return false 956 } 957 958 func (*testResource) IsSame(Resource) bool { 959 return false 960 } 961 962 func (*testResource) AddWarning(string) { 963 } 964 965 func (*testResource) IsEqual(Resource) bool { 966 return false 967 } 968 969 func (t *testResource) String() string { 970 return t.keyWithKind 971 } 972 973 func TestRemoveDuplicateResources(t *testing.T) { 974 tests := []struct { 975 resources []Resource 976 expected []Resource 977 }{ 978 { 979 resources: []Resource{ 980 &testResource{keyWithKind: "VirtualServer/ns-1/vs-1"}, 981 &testResource{keyWithKind: "VirtualServer/ns-1/vs-2"}, 982 &testResource{keyWithKind: "VirtualServer/ns-1/vs-2"}, 983 &testResource{keyWithKind: "VirtualServer/ns-1/vs-3"}, 984 }, 985 expected: []Resource{ 986 &testResource{keyWithKind: "VirtualServer/ns-1/vs-1"}, 987 &testResource{keyWithKind: "VirtualServer/ns-1/vs-2"}, 988 &testResource{keyWithKind: "VirtualServer/ns-1/vs-3"}, 989 }, 990 }, 991 { 992 resources: []Resource{ 993 &testResource{keyWithKind: "VirtualServer/ns-2/vs-3"}, 994 &testResource{keyWithKind: "VirtualServer/ns-1/vs-3"}, 995 }, 996 expected: []Resource{ 997 &testResource{keyWithKind: "VirtualServer/ns-2/vs-3"}, 998 &testResource{keyWithKind: "VirtualServer/ns-1/vs-3"}, 999 }, 1000 }, 1001 } 1002 1003 for _, test := range tests { 1004 result := removeDuplicateResources(test.resources) 1005 if !reflect.DeepEqual(result, test.expected) { 1006 t.Errorf("removeDuplicateResources() returned \n%v but expected \n%v", result, test.expected) 1007 } 1008 } 1009 } 1010 1011 func TestFindPoliciesForSecret(t *testing.T) { 1012 jwtPol1 := &conf_v1.Policy{ 1013 ObjectMeta: meta_v1.ObjectMeta{ 1014 Name: "jwt-policy", 1015 Namespace: "default", 1016 }, 1017 Spec: conf_v1.PolicySpec{ 1018 JWTAuth: &conf_v1.JWTAuth{ 1019 Secret: "jwk-secret", 1020 }, 1021 }, 1022 } 1023 1024 jwtPol2 := &conf_v1.Policy{ 1025 ObjectMeta: meta_v1.ObjectMeta{ 1026 Name: "jwt-policy", 1027 Namespace: "ns-1", 1028 }, 1029 Spec: conf_v1.PolicySpec{ 1030 JWTAuth: &conf_v1.JWTAuth{ 1031 Secret: "jwk-secret", 1032 }, 1033 }, 1034 } 1035 1036 ingTLSPol := &conf_v1.Policy{ 1037 ObjectMeta: meta_v1.ObjectMeta{ 1038 Name: "ingress-mtls-policy", 1039 Namespace: "default", 1040 }, 1041 Spec: conf_v1.PolicySpec{ 1042 IngressMTLS: &conf_v1.IngressMTLS{ 1043 ClientCertSecret: "ingress-mtls-secret", 1044 }, 1045 }, 1046 } 1047 egTLSPol := &conf_v1.Policy{ 1048 ObjectMeta: meta_v1.ObjectMeta{ 1049 Name: "egress-mtls-policy", 1050 Namespace: "default", 1051 }, 1052 Spec: conf_v1.PolicySpec{ 1053 EgressMTLS: &conf_v1.EgressMTLS{ 1054 TLSSecret: "egress-mtls-secret", 1055 }, 1056 }, 1057 } 1058 egTLSPol2 := &conf_v1.Policy{ 1059 ObjectMeta: meta_v1.ObjectMeta{ 1060 Name: "egress-trusted-policy", 1061 Namespace: "default", 1062 }, 1063 Spec: conf_v1.PolicySpec{ 1064 EgressMTLS: &conf_v1.EgressMTLS{ 1065 TrustedCertSecret: "egress-trusted-secret", 1066 }, 1067 }, 1068 } 1069 oidcPol := &conf_v1.Policy{ 1070 ObjectMeta: meta_v1.ObjectMeta{ 1071 Name: "oidc-policy", 1072 Namespace: "default", 1073 }, 1074 Spec: conf_v1.PolicySpec{ 1075 OIDC: &conf_v1.OIDC{ 1076 ClientSecret: "oidc-secret", 1077 }, 1078 }, 1079 } 1080 1081 tests := []struct { 1082 policies []*conf_v1.Policy 1083 secretNamespace string 1084 secretName string 1085 expected []*conf_v1.Policy 1086 msg string 1087 }{ 1088 { 1089 policies: []*conf_v1.Policy{jwtPol1}, 1090 secretNamespace: "default", 1091 secretName: "jwk-secret", 1092 expected: []*conf_v1.Policy{jwtPol1}, 1093 msg: "Find policy in default ns", 1094 }, 1095 { 1096 policies: []*conf_v1.Policy{jwtPol2}, 1097 secretNamespace: "default", 1098 secretName: "jwk-secret", 1099 expected: nil, 1100 msg: "Ignore policies in other namespaces", 1101 }, 1102 { 1103 policies: []*conf_v1.Policy{jwtPol1, jwtPol2}, 1104 secretNamespace: "default", 1105 secretName: "jwk-secret", 1106 expected: []*conf_v1.Policy{jwtPol1}, 1107 msg: "Find policy in default ns, ignore other", 1108 }, 1109 { 1110 policies: []*conf_v1.Policy{ingTLSPol}, 1111 secretNamespace: "default", 1112 secretName: "ingress-mtls-secret", 1113 expected: []*conf_v1.Policy{ingTLSPol}, 1114 msg: "Find policy in default ns", 1115 }, 1116 { 1117 policies: []*conf_v1.Policy{jwtPol1, ingTLSPol}, 1118 secretNamespace: "default", 1119 secretName: "ingress-mtls-secret", 1120 expected: []*conf_v1.Policy{ingTLSPol}, 1121 msg: "Find policy in default ns, ignore other types", 1122 }, 1123 { 1124 policies: []*conf_v1.Policy{egTLSPol}, 1125 secretNamespace: "default", 1126 secretName: "egress-mtls-secret", 1127 expected: []*conf_v1.Policy{egTLSPol}, 1128 msg: "Find policy in default ns", 1129 }, 1130 { 1131 policies: []*conf_v1.Policy{jwtPol1, egTLSPol}, 1132 secretNamespace: "default", 1133 secretName: "egress-mtls-secret", 1134 expected: []*conf_v1.Policy{egTLSPol}, 1135 msg: "Find policy in default ns, ignore other types", 1136 }, 1137 { 1138 policies: []*conf_v1.Policy{egTLSPol2}, 1139 secretNamespace: "default", 1140 secretName: "egress-trusted-secret", 1141 expected: []*conf_v1.Policy{egTLSPol2}, 1142 msg: "Find policy in default ns", 1143 }, 1144 { 1145 policies: []*conf_v1.Policy{egTLSPol, egTLSPol2}, 1146 secretNamespace: "default", 1147 secretName: "egress-trusted-secret", 1148 expected: []*conf_v1.Policy{egTLSPol2}, 1149 msg: "Find policy in default ns, ignore other types", 1150 }, 1151 { 1152 policies: []*conf_v1.Policy{oidcPol}, 1153 secretNamespace: "default", 1154 secretName: "oidc-secret", 1155 expected: []*conf_v1.Policy{oidcPol}, 1156 msg: "Find policy in default ns", 1157 }, 1158 { 1159 policies: []*conf_v1.Policy{ingTLSPol, oidcPol}, 1160 secretNamespace: "default", 1161 secretName: "oidc-secret", 1162 expected: []*conf_v1.Policy{oidcPol}, 1163 msg: "Find policy in default ns, ignore other types", 1164 }, 1165 } 1166 for _, test := range tests { 1167 result := findPoliciesForSecret(test.policies, test.secretNamespace, test.secretName) 1168 if diff := cmp.Diff(test.expected, result); diff != "" { 1169 t.Errorf("findPoliciesForSecret() '%v' mismatch (-want +got):\n%s", test.msg, diff) 1170 } 1171 } 1172 } 1173 1174 func errorComparer(e1, e2 error) bool { 1175 if e1 == nil || e2 == nil { 1176 return errors.Is(e1, e2) 1177 } 1178 1179 return e1.Error() == e2.Error() 1180 } 1181 1182 func TestAddJWTSecrets(t *testing.T) { 1183 invalidErr := errors.New("invalid") 1184 validJWKSecret := &v1.Secret{ 1185 ObjectMeta: meta_v1.ObjectMeta{ 1186 Name: "valid-jwk-secret", 1187 Namespace: "default", 1188 }, 1189 Type: secrets.SecretTypeJWK, 1190 } 1191 invalidJWKSecret := &v1.Secret{ 1192 ObjectMeta: meta_v1.ObjectMeta{ 1193 Name: "invalid-jwk-secret", 1194 Namespace: "default", 1195 }, 1196 Type: secrets.SecretTypeJWK, 1197 } 1198 1199 tests := []struct { 1200 policies []*conf_v1.Policy 1201 expectedSecretRefs map[string]*secrets.SecretReference 1202 wantErr bool 1203 msg string 1204 }{ 1205 { 1206 policies: []*conf_v1.Policy{ 1207 { 1208 ObjectMeta: meta_v1.ObjectMeta{ 1209 Name: "jwt-policy", 1210 Namespace: "default", 1211 }, 1212 Spec: conf_v1.PolicySpec{ 1213 JWTAuth: &conf_v1.JWTAuth{ 1214 Secret: "valid-jwk-secret", 1215 Realm: "My API", 1216 }, 1217 }, 1218 }, 1219 }, 1220 expectedSecretRefs: map[string]*secrets.SecretReference{ 1221 "default/valid-jwk-secret": { 1222 Secret: validJWKSecret, 1223 Path: "/etc/nginx/secrets/default-valid-jwk-secret", 1224 }, 1225 }, 1226 wantErr: false, 1227 msg: "test getting valid secret", 1228 }, 1229 { 1230 policies: []*conf_v1.Policy{}, 1231 expectedSecretRefs: map[string]*secrets.SecretReference{}, 1232 wantErr: false, 1233 msg: "test getting valid secret with no policy", 1234 }, 1235 { 1236 policies: []*conf_v1.Policy{ 1237 { 1238 ObjectMeta: meta_v1.ObjectMeta{ 1239 Name: "jwt-policy", 1240 Namespace: "default", 1241 }, 1242 Spec: conf_v1.PolicySpec{ 1243 AccessControl: &conf_v1.AccessControl{ 1244 Allow: []string{"127.0.0.1"}, 1245 }, 1246 }, 1247 }, 1248 }, 1249 expectedSecretRefs: map[string]*secrets.SecretReference{}, 1250 wantErr: false, 1251 msg: "test getting invalid secret with wrong policy", 1252 }, 1253 { 1254 policies: []*conf_v1.Policy{ 1255 { 1256 ObjectMeta: meta_v1.ObjectMeta{ 1257 Name: "jwt-policy", 1258 Namespace: "default", 1259 }, 1260 Spec: conf_v1.PolicySpec{ 1261 JWTAuth: &conf_v1.JWTAuth{ 1262 Secret: "invalid-jwk-secret", 1263 Realm: "My API", 1264 }, 1265 }, 1266 }, 1267 }, 1268 expectedSecretRefs: map[string]*secrets.SecretReference{ 1269 "default/invalid-jwk-secret": { 1270 Secret: invalidJWKSecret, 1271 Error: invalidErr, 1272 }, 1273 }, 1274 wantErr: true, 1275 msg: "test getting invalid secret", 1276 }, 1277 } 1278 1279 lbc := LoadBalancerController{ 1280 secretStore: secrets.NewFakeSecretsStore(map[string]*secrets.SecretReference{ 1281 "default/valid-jwk-secret": { 1282 Secret: validJWKSecret, 1283 Path: "/etc/nginx/secrets/default-valid-jwk-secret", 1284 }, 1285 "default/invalid-jwk-secret": { 1286 Secret: invalidJWKSecret, 1287 Error: invalidErr, 1288 }, 1289 }), 1290 } 1291 1292 for _, test := range tests { 1293 result := make(map[string]*secrets.SecretReference) 1294 1295 err := lbc.addJWTSecretRefs(result, test.policies) 1296 if (err != nil) != test.wantErr { 1297 t.Errorf("addJWTSecretRefs() returned %v, for the case of %v", err, test.msg) 1298 } 1299 1300 if diff := cmp.Diff(test.expectedSecretRefs, result, cmp.Comparer(errorComparer)); diff != "" { 1301 t.Errorf("addJWTSecretRefs() '%v' mismatch (-want +got):\n%s", test.msg, diff) 1302 } 1303 } 1304 } 1305 1306 func TestAddIngressMTLSSecret(t *testing.T) { 1307 invalidErr := errors.New("invalid") 1308 validSecret := &v1.Secret{ 1309 ObjectMeta: meta_v1.ObjectMeta{ 1310 Name: "valid-ingress-mtls-secret", 1311 Namespace: "default", 1312 }, 1313 Type: secrets.SecretTypeCA, 1314 } 1315 invalidSecret := &v1.Secret{ 1316 ObjectMeta: meta_v1.ObjectMeta{ 1317 Name: "invalid-ingress-mtls-secret", 1318 Namespace: "default", 1319 }, 1320 Type: secrets.SecretTypeCA, 1321 } 1322 1323 tests := []struct { 1324 policies []*conf_v1.Policy 1325 expectedSecretRefs map[string]*secrets.SecretReference 1326 wantErr bool 1327 msg string 1328 }{ 1329 { 1330 policies: []*conf_v1.Policy{ 1331 { 1332 ObjectMeta: meta_v1.ObjectMeta{ 1333 Name: "ingress-mtls-policy", 1334 Namespace: "default", 1335 }, 1336 Spec: conf_v1.PolicySpec{ 1337 IngressMTLS: &conf_v1.IngressMTLS{ 1338 ClientCertSecret: "valid-ingress-mtls-secret", 1339 }, 1340 }, 1341 }, 1342 }, 1343 expectedSecretRefs: map[string]*secrets.SecretReference{ 1344 "default/valid-ingress-mtls-secret": { 1345 Secret: validSecret, 1346 Path: "/etc/nginx/secrets/default-valid-ingress-mtls-secret", 1347 }, 1348 }, 1349 wantErr: false, 1350 msg: "test getting valid secret", 1351 }, 1352 { 1353 policies: []*conf_v1.Policy{}, 1354 expectedSecretRefs: map[string]*secrets.SecretReference{}, 1355 wantErr: false, 1356 msg: "test getting valid secret with no policy", 1357 }, 1358 { 1359 policies: []*conf_v1.Policy{ 1360 { 1361 ObjectMeta: meta_v1.ObjectMeta{ 1362 Name: "ingress-mtls-policy", 1363 Namespace: "default", 1364 }, 1365 Spec: conf_v1.PolicySpec{ 1366 AccessControl: &conf_v1.AccessControl{ 1367 Allow: []string{"127.0.0.1"}, 1368 }, 1369 }, 1370 }, 1371 }, 1372 expectedSecretRefs: map[string]*secrets.SecretReference{}, 1373 wantErr: false, 1374 msg: "test getting valid secret with wrong policy", 1375 }, 1376 { 1377 policies: []*conf_v1.Policy{ 1378 { 1379 ObjectMeta: meta_v1.ObjectMeta{ 1380 Name: "ingress-mtls-policy", 1381 Namespace: "default", 1382 }, 1383 Spec: conf_v1.PolicySpec{ 1384 IngressMTLS: &conf_v1.IngressMTLS{ 1385 ClientCertSecret: "invalid-ingress-mtls-secret", 1386 }, 1387 }, 1388 }, 1389 }, 1390 expectedSecretRefs: map[string]*secrets.SecretReference{ 1391 "default/invalid-ingress-mtls-secret": { 1392 Secret: invalidSecret, 1393 Error: invalidErr, 1394 }, 1395 }, 1396 wantErr: true, 1397 msg: "test getting invalid secret", 1398 }, 1399 } 1400 1401 lbc := LoadBalancerController{ 1402 secretStore: secrets.NewFakeSecretsStore(map[string]*secrets.SecretReference{ 1403 "default/valid-ingress-mtls-secret": { 1404 Secret: validSecret, 1405 Path: "/etc/nginx/secrets/default-valid-ingress-mtls-secret", 1406 }, 1407 "default/invalid-ingress-mtls-secret": { 1408 Secret: invalidSecret, 1409 Error: invalidErr, 1410 }, 1411 }), 1412 } 1413 1414 for _, test := range tests { 1415 result := make(map[string]*secrets.SecretReference) 1416 1417 err := lbc.addIngressMTLSSecretRefs(result, test.policies) 1418 if (err != nil) != test.wantErr { 1419 t.Errorf("addIngressMTLSSecretRefs() returned %v, for the case of %v", err, test.msg) 1420 } 1421 1422 if diff := cmp.Diff(test.expectedSecretRefs, result, cmp.Comparer(errorComparer)); diff != "" { 1423 t.Errorf("addIngressMTLSSecretRefs() '%v' mismatch (-want +got):\n%s", test.msg, diff) 1424 } 1425 } 1426 } 1427 1428 func TestAddEgressMTLSSecrets(t *testing.T) { 1429 invalidErr := errors.New("invalid") 1430 validMTLSSecret := &v1.Secret{ 1431 ObjectMeta: meta_v1.ObjectMeta{ 1432 Name: "valid-egress-mtls-secret", 1433 Namespace: "default", 1434 }, 1435 Type: api_v1.SecretTypeTLS, 1436 } 1437 validTrustedSecret := &v1.Secret{ 1438 ObjectMeta: meta_v1.ObjectMeta{ 1439 Name: "valid-egress-trusted-secret", 1440 Namespace: "default", 1441 }, 1442 Type: secrets.SecretTypeCA, 1443 } 1444 invalidMTLSSecret := &v1.Secret{ 1445 ObjectMeta: meta_v1.ObjectMeta{ 1446 Name: "invalid-egress-mtls-secret", 1447 Namespace: "default", 1448 }, 1449 Type: api_v1.SecretTypeTLS, 1450 } 1451 invalidTrustedSecret := &v1.Secret{ 1452 ObjectMeta: meta_v1.ObjectMeta{ 1453 Name: "invalid-egress-trusted-secret", 1454 Namespace: "default", 1455 }, 1456 Type: secrets.SecretTypeCA, 1457 } 1458 1459 tests := []struct { 1460 policies []*conf_v1.Policy 1461 expectedSecretRefs map[string]*secrets.SecretReference 1462 wantErr bool 1463 msg string 1464 }{ 1465 { 1466 policies: []*conf_v1.Policy{ 1467 { 1468 ObjectMeta: meta_v1.ObjectMeta{ 1469 Name: "egress-mtls-policy", 1470 Namespace: "default", 1471 }, 1472 Spec: conf_v1.PolicySpec{ 1473 EgressMTLS: &conf_v1.EgressMTLS{ 1474 TLSSecret: "valid-egress-mtls-secret", 1475 }, 1476 }, 1477 }, 1478 }, 1479 expectedSecretRefs: map[string]*secrets.SecretReference{ 1480 "default/valid-egress-mtls-secret": { 1481 Secret: validMTLSSecret, 1482 Path: "/etc/nginx/secrets/default-valid-egress-mtls-secret", 1483 }, 1484 }, 1485 wantErr: false, 1486 msg: "test getting valid TLS secret", 1487 }, 1488 { 1489 policies: []*conf_v1.Policy{ 1490 { 1491 ObjectMeta: meta_v1.ObjectMeta{ 1492 Name: "egress-egress-trusted-policy", 1493 Namespace: "default", 1494 }, 1495 Spec: conf_v1.PolicySpec{ 1496 EgressMTLS: &conf_v1.EgressMTLS{ 1497 TrustedCertSecret: "valid-egress-trusted-secret", 1498 }, 1499 }, 1500 }, 1501 }, 1502 expectedSecretRefs: map[string]*secrets.SecretReference{ 1503 "default/valid-egress-trusted-secret": { 1504 Secret: validTrustedSecret, 1505 Path: "/etc/nginx/secrets/default-valid-egress-trusted-secret", 1506 }, 1507 }, 1508 wantErr: false, 1509 msg: "test getting valid TrustedCA secret", 1510 }, 1511 { 1512 policies: []*conf_v1.Policy{ 1513 { 1514 ObjectMeta: meta_v1.ObjectMeta{ 1515 Name: "egress-mtls-policy", 1516 Namespace: "default", 1517 }, 1518 Spec: conf_v1.PolicySpec{ 1519 EgressMTLS: &conf_v1.EgressMTLS{ 1520 TLSSecret: "valid-egress-mtls-secret", 1521 TrustedCertSecret: "valid-egress-trusted-secret", 1522 }, 1523 }, 1524 }, 1525 }, 1526 expectedSecretRefs: map[string]*secrets.SecretReference{ 1527 "default/valid-egress-mtls-secret": { 1528 Secret: validMTLSSecret, 1529 Path: "/etc/nginx/secrets/default-valid-egress-mtls-secret", 1530 }, 1531 "default/valid-egress-trusted-secret": { 1532 Secret: validTrustedSecret, 1533 Path: "/etc/nginx/secrets/default-valid-egress-trusted-secret", 1534 }, 1535 }, 1536 wantErr: false, 1537 msg: "test getting valid secrets", 1538 }, 1539 { 1540 policies: []*conf_v1.Policy{}, 1541 expectedSecretRefs: map[string]*secrets.SecretReference{}, 1542 wantErr: false, 1543 msg: "test getting valid secret with no policy", 1544 }, 1545 { 1546 policies: []*conf_v1.Policy{ 1547 { 1548 ObjectMeta: meta_v1.ObjectMeta{ 1549 Name: "ingress-mtls-policy", 1550 Namespace: "default", 1551 }, 1552 Spec: conf_v1.PolicySpec{ 1553 AccessControl: &conf_v1.AccessControl{ 1554 Allow: []string{"127.0.0.1"}, 1555 }, 1556 }, 1557 }, 1558 }, 1559 expectedSecretRefs: map[string]*secrets.SecretReference{}, 1560 wantErr: false, 1561 msg: "test getting valid secret with wrong policy", 1562 }, 1563 { 1564 policies: []*conf_v1.Policy{ 1565 { 1566 ObjectMeta: meta_v1.ObjectMeta{ 1567 Name: "egress-mtls-policy", 1568 Namespace: "default", 1569 }, 1570 Spec: conf_v1.PolicySpec{ 1571 EgressMTLS: &conf_v1.EgressMTLS{ 1572 TLSSecret: "invalid-egress-mtls-secret", 1573 }, 1574 }, 1575 }, 1576 }, 1577 expectedSecretRefs: map[string]*secrets.SecretReference{ 1578 "default/invalid-egress-mtls-secret": { 1579 Secret: invalidMTLSSecret, 1580 Error: invalidErr, 1581 }, 1582 }, 1583 wantErr: true, 1584 msg: "test getting invalid TLS secret", 1585 }, 1586 { 1587 policies: []*conf_v1.Policy{ 1588 { 1589 ObjectMeta: meta_v1.ObjectMeta{ 1590 Name: "egress-mtls-policy", 1591 Namespace: "default", 1592 }, 1593 Spec: conf_v1.PolicySpec{ 1594 EgressMTLS: &conf_v1.EgressMTLS{ 1595 TrustedCertSecret: "invalid-egress-trusted-secret", 1596 }, 1597 }, 1598 }, 1599 }, 1600 expectedSecretRefs: map[string]*secrets.SecretReference{ 1601 "default/invalid-egress-trusted-secret": { 1602 Secret: invalidTrustedSecret, 1603 Error: invalidErr, 1604 }, 1605 }, 1606 wantErr: true, 1607 msg: "test getting invalid TrustedCA secret", 1608 }, 1609 } 1610 1611 lbc := LoadBalancerController{ 1612 secretStore: secrets.NewFakeSecretsStore(map[string]*secrets.SecretReference{ 1613 "default/valid-egress-mtls-secret": { 1614 Secret: validMTLSSecret, 1615 Path: "/etc/nginx/secrets/default-valid-egress-mtls-secret", 1616 }, 1617 "default/valid-egress-trusted-secret": { 1618 Secret: validTrustedSecret, 1619 Path: "/etc/nginx/secrets/default-valid-egress-trusted-secret", 1620 }, 1621 "default/invalid-egress-mtls-secret": { 1622 Secret: invalidMTLSSecret, 1623 Error: invalidErr, 1624 }, 1625 "default/invalid-egress-trusted-secret": { 1626 Secret: invalidTrustedSecret, 1627 Error: invalidErr, 1628 }, 1629 }), 1630 } 1631 1632 for _, test := range tests { 1633 result := make(map[string]*secrets.SecretReference) 1634 1635 err := lbc.addEgressMTLSSecretRefs(result, test.policies) 1636 if (err != nil) != test.wantErr { 1637 t.Errorf("addEgressMTLSSecretRefs() returned %v, for the case of %v", err, test.msg) 1638 } 1639 if diff := cmp.Diff(test.expectedSecretRefs, result, cmp.Comparer(errorComparer)); diff != "" { 1640 t.Errorf("addEgressMTLSSecretRefs() '%v' mismatch (-want +got):\n%s", test.msg, diff) 1641 } 1642 } 1643 } 1644 1645 func TestAddOidcSecret(t *testing.T) { 1646 invalidErr := errors.New("invalid") 1647 validSecret := &v1.Secret{ 1648 ObjectMeta: meta_v1.ObjectMeta{ 1649 Name: "valid-oidc-secret", 1650 Namespace: "default", 1651 }, 1652 Data: map[string][]byte{ 1653 "client-secret": nil, 1654 }, 1655 Type: secrets.SecretTypeOIDC, 1656 } 1657 invalidSecret := &v1.Secret{ 1658 ObjectMeta: meta_v1.ObjectMeta{ 1659 Name: "invalid-oidc-secret", 1660 Namespace: "default", 1661 }, 1662 Type: secrets.SecretTypeOIDC, 1663 } 1664 1665 tests := []struct { 1666 policies []*conf_v1.Policy 1667 expectedSecretRefs map[string]*secrets.SecretReference 1668 wantErr bool 1669 msg string 1670 }{ 1671 { 1672 policies: []*conf_v1.Policy{ 1673 { 1674 ObjectMeta: meta_v1.ObjectMeta{ 1675 Name: "oidc-policy", 1676 Namespace: "default", 1677 }, 1678 Spec: conf_v1.PolicySpec{ 1679 OIDC: &conf_v1.OIDC{ 1680 ClientSecret: "valid-oidc-secret", 1681 }, 1682 }, 1683 }, 1684 }, 1685 expectedSecretRefs: map[string]*secrets.SecretReference{ 1686 "default/valid-oidc-secret": { 1687 Secret: validSecret, 1688 }, 1689 }, 1690 wantErr: false, 1691 msg: "test getting valid secret", 1692 }, 1693 { 1694 policies: []*conf_v1.Policy{}, 1695 expectedSecretRefs: map[string]*secrets.SecretReference{}, 1696 wantErr: false, 1697 msg: "test getting valid secret with no policy", 1698 }, 1699 { 1700 policies: []*conf_v1.Policy{ 1701 { 1702 ObjectMeta: meta_v1.ObjectMeta{ 1703 Name: "oidc-policy", 1704 Namespace: "default", 1705 }, 1706 Spec: conf_v1.PolicySpec{ 1707 AccessControl: &conf_v1.AccessControl{ 1708 Allow: []string{"127.0.0.1"}, 1709 }, 1710 }, 1711 }, 1712 }, 1713 expectedSecretRefs: map[string]*secrets.SecretReference{}, 1714 wantErr: false, 1715 msg: "test getting valid secret with wrong policy", 1716 }, 1717 { 1718 policies: []*conf_v1.Policy{ 1719 { 1720 ObjectMeta: meta_v1.ObjectMeta{ 1721 Name: "oidc-policy", 1722 Namespace: "default", 1723 }, 1724 Spec: conf_v1.PolicySpec{ 1725 OIDC: &conf_v1.OIDC{ 1726 ClientSecret: "invalid-oidc-secret", 1727 }, 1728 }, 1729 }, 1730 }, 1731 expectedSecretRefs: map[string]*secrets.SecretReference{ 1732 "default/invalid-oidc-secret": { 1733 Secret: invalidSecret, 1734 Error: invalidErr, 1735 }, 1736 }, 1737 wantErr: true, 1738 msg: "test getting invalid secret", 1739 }, 1740 } 1741 1742 lbc := LoadBalancerController{ 1743 secretStore: secrets.NewFakeSecretsStore(map[string]*secrets.SecretReference{ 1744 "default/valid-oidc-secret": { 1745 Secret: validSecret, 1746 }, 1747 "default/invalid-oidc-secret": { 1748 Secret: invalidSecret, 1749 Error: invalidErr, 1750 }, 1751 }), 1752 } 1753 1754 for _, test := range tests { 1755 result := make(map[string]*secrets.SecretReference) 1756 1757 err := lbc.addOIDCSecretRefs(result, test.policies) 1758 if (err != nil) != test.wantErr { 1759 t.Errorf("addOIDCSecretRefs() returned %v, for the case of %v", err, test.msg) 1760 } 1761 1762 if diff := cmp.Diff(test.expectedSecretRefs, result, cmp.Comparer(errorComparer)); diff != "" { 1763 t.Errorf("addOIDCSecretRefs() '%v' mismatch (-want +got):\n%s", test.msg, diff) 1764 } 1765 } 1766 } 1767 1768 func TestAddWAFPolicyRefs(t *testing.T) { 1769 apPol := &unstructured.Unstructured{ 1770 Object: map[string]interface{}{ 1771 "metadata": map[string]interface{}{ 1772 "namespace": "default", 1773 "name": "ap-pol", 1774 }, 1775 }, 1776 } 1777 1778 logConf := &unstructured.Unstructured{ 1779 Object: map[string]interface{}{ 1780 "metadata": map[string]interface{}{ 1781 "namespace": "default", 1782 "name": "log-conf", 1783 }, 1784 }, 1785 } 1786 1787 tests := []struct { 1788 policies []*conf_v1.Policy 1789 expectedApPolRefs map[string]*unstructured.Unstructured 1790 expectedLogConfRefs map[string]*unstructured.Unstructured 1791 wantErr bool 1792 msg string 1793 }{ 1794 { 1795 policies: []*conf_v1.Policy{ 1796 { 1797 ObjectMeta: meta_v1.ObjectMeta{ 1798 Name: "waf-pol", 1799 Namespace: "default", 1800 }, 1801 Spec: conf_v1.PolicySpec{ 1802 WAF: &conf_v1.WAF{ 1803 Enable: true, 1804 ApPolicy: "default/ap-pol", 1805 SecurityLog: &conf_v1.SecurityLog{ 1806 Enable: true, 1807 ApLogConf: "log-conf", 1808 }, 1809 }, 1810 }, 1811 }, 1812 }, 1813 expectedApPolRefs: map[string]*unstructured.Unstructured{ 1814 "default/ap-pol": apPol, 1815 }, 1816 expectedLogConfRefs: map[string]*unstructured.Unstructured{ 1817 "default/log-conf": logConf, 1818 }, 1819 wantErr: false, 1820 msg: "base test", 1821 }, 1822 { 1823 policies: []*conf_v1.Policy{ 1824 { 1825 ObjectMeta: meta_v1.ObjectMeta{ 1826 Name: "waf-pol", 1827 Namespace: "default", 1828 }, 1829 Spec: conf_v1.PolicySpec{ 1830 WAF: &conf_v1.WAF{ 1831 Enable: true, 1832 ApPolicy: "non-existing-ap-pol", 1833 }, 1834 }, 1835 }, 1836 }, 1837 wantErr: true, 1838 expectedApPolRefs: make(map[string]*unstructured.Unstructured), 1839 expectedLogConfRefs: make(map[string]*unstructured.Unstructured), 1840 msg: "apPol doesn't exist", 1841 }, 1842 { 1843 policies: []*conf_v1.Policy{ 1844 { 1845 ObjectMeta: meta_v1.ObjectMeta{ 1846 Name: "waf-pol", 1847 Namespace: "default", 1848 }, 1849 Spec: conf_v1.PolicySpec{ 1850 WAF: &conf_v1.WAF{ 1851 Enable: true, 1852 ApPolicy: "ap-pol", 1853 SecurityLog: &conf_v1.SecurityLog{ 1854 Enable: true, 1855 ApLogConf: "non-existing-log-conf", 1856 }, 1857 }, 1858 }, 1859 }, 1860 }, 1861 wantErr: true, 1862 expectedApPolRefs: map[string]*unstructured.Unstructured{ 1863 "default/ap-pol": apPol, 1864 }, 1865 expectedLogConfRefs: make(map[string]*unstructured.Unstructured), 1866 msg: "logConf doesn't exist", 1867 }, 1868 } 1869 1870 lbc := LoadBalancerController{ 1871 appProtectConfiguration: appprotect.NewFakeConfiguration(), 1872 } 1873 lbc.appProtectConfiguration.AddOrUpdatePolicy(apPol) 1874 lbc.appProtectConfiguration.AddOrUpdateLogConf(logConf) 1875 1876 for _, test := range tests { 1877 resApPolicy := make(map[string]*unstructured.Unstructured) 1878 resLogConf := make(map[string]*unstructured.Unstructured) 1879 1880 if err := lbc.addWAFPolicyRefs(resApPolicy, resLogConf, test.policies); (err != nil) != test.wantErr { 1881 t.Errorf("LoadBalancerController.addWAFPolicyRefs() error = %v, wantErr %v", err, test.wantErr) 1882 } 1883 if diff := cmp.Diff(test.expectedApPolRefs, resApPolicy); diff != "" { 1884 t.Errorf("LoadBalancerController.addWAFPolicyRefs() '%v' mismatch (-want +got):\n%s", test.msg, diff) 1885 } 1886 if diff := cmp.Diff(test.expectedLogConfRefs, resLogConf); diff != "" { 1887 t.Errorf("LoadBalancerController.addWAFPolicyRefs() '%v' mismatch (-want +got):\n%s", test.msg, diff) 1888 } 1889 } 1890 } 1891 1892 func TestGetWAFPoliciesForAppProtectPolicy(t *testing.T) { 1893 apPol := &conf_v1.Policy{ 1894 Spec: conf_v1.PolicySpec{ 1895 WAF: &conf_v1.WAF{ 1896 Enable: true, 1897 ApPolicy: "ns1/apPol", 1898 }, 1899 }, 1900 } 1901 1902 apPolNs2 := &conf_v1.Policy{ 1903 ObjectMeta: meta_v1.ObjectMeta{ 1904 Namespace: "ns1", 1905 }, 1906 Spec: conf_v1.PolicySpec{ 1907 WAF: &conf_v1.WAF{ 1908 Enable: true, 1909 ApPolicy: "ns2/apPol", 1910 }, 1911 }, 1912 } 1913 1914 apPolNoNs := &conf_v1.Policy{ 1915 ObjectMeta: meta_v1.ObjectMeta{ 1916 Namespace: "default", 1917 }, 1918 Spec: conf_v1.PolicySpec{ 1919 WAF: &conf_v1.WAF{ 1920 Enable: true, 1921 ApPolicy: "apPol", 1922 }, 1923 }, 1924 } 1925 1926 policies := []*conf_v1.Policy{ 1927 apPol, apPolNs2, apPolNoNs, 1928 } 1929 1930 tests := []struct { 1931 pols []*conf_v1.Policy 1932 key string 1933 want []*conf_v1.Policy 1934 msg string 1935 }{ 1936 { 1937 pols: policies, 1938 key: "ns1/apPol", 1939 want: []*conf_v1.Policy{apPol}, 1940 msg: "WAF pols that ref apPol which has a namepace", 1941 }, 1942 { 1943 pols: policies, 1944 key: "default/apPol", 1945 want: []*conf_v1.Policy{apPolNoNs}, 1946 msg: "WAF pols that ref apPol which has no namepace", 1947 }, 1948 { 1949 pols: policies, 1950 key: "ns2/apPol", 1951 want: []*conf_v1.Policy{apPolNs2}, 1952 msg: "WAF pols that ref apPol which is in another ns", 1953 }, 1954 { 1955 pols: policies, 1956 key: "ns1/apPol-with-no-valid-refs", 1957 want: nil, 1958 msg: "WAF pols where there is no valid ref", 1959 }, 1960 } 1961 for _, test := range tests { 1962 got := getWAFPoliciesForAppProtectPolicy(test.pols, test.key) 1963 if diff := cmp.Diff(test.want, got); diff != "" { 1964 t.Errorf("getWAFPoliciesForAppProtectPolicy() returned unexpected result for the case of: %v (-want +got):\n%s", test.msg, diff) 1965 } 1966 } 1967 } 1968 1969 func TestGetWAFPoliciesForAppProtectLogConf(t *testing.T) { 1970 logConf := &conf_v1.Policy{ 1971 Spec: conf_v1.PolicySpec{ 1972 WAF: &conf_v1.WAF{ 1973 Enable: true, 1974 SecurityLog: &conf_v1.SecurityLog{ 1975 Enable: true, 1976 ApLogConf: "ns1/logConf", 1977 }, 1978 }, 1979 }, 1980 } 1981 1982 logConfNs2 := &conf_v1.Policy{ 1983 ObjectMeta: meta_v1.ObjectMeta{ 1984 Namespace: "ns1", 1985 }, 1986 Spec: conf_v1.PolicySpec{ 1987 WAF: &conf_v1.WAF{ 1988 Enable: true, 1989 SecurityLog: &conf_v1.SecurityLog{ 1990 Enable: true, 1991 ApLogConf: "ns2/logConf", 1992 }, 1993 }, 1994 }, 1995 } 1996 1997 logConfNoNs := &conf_v1.Policy{ 1998 ObjectMeta: meta_v1.ObjectMeta{ 1999 Namespace: "default", 2000 }, 2001 Spec: conf_v1.PolicySpec{ 2002 WAF: &conf_v1.WAF{ 2003 Enable: true, 2004 SecurityLog: &conf_v1.SecurityLog{ 2005 Enable: true, 2006 ApLogConf: "logConf", 2007 }, 2008 }, 2009 }, 2010 } 2011 2012 policies := []*conf_v1.Policy{ 2013 logConf, logConfNs2, logConfNoNs, 2014 } 2015 2016 tests := []struct { 2017 pols []*conf_v1.Policy 2018 key string 2019 want []*conf_v1.Policy 2020 msg string 2021 }{ 2022 { 2023 pols: policies, 2024 key: "ns1/logConf", 2025 want: []*conf_v1.Policy{logConf}, 2026 msg: "WAF pols that ref logConf which has a namepace", 2027 }, 2028 { 2029 pols: policies, 2030 key: "default/logConf", 2031 want: []*conf_v1.Policy{logConfNoNs}, 2032 msg: "WAF pols that ref logConf which has no namepace", 2033 }, 2034 { 2035 pols: policies, 2036 key: "ns2/logConf", 2037 want: []*conf_v1.Policy{logConfNs2}, 2038 msg: "WAF pols that ref logConf which is in another ns", 2039 }, 2040 { 2041 pols: policies, 2042 key: "ns1/logConf-with-no-valid-refs", 2043 want: nil, 2044 msg: "WAF pols where there is no valid logConf ref", 2045 }, 2046 } 2047 for _, test := range tests { 2048 got := getWAFPoliciesForAppProtectLogConf(test.pols, test.key) 2049 if diff := cmp.Diff(test.want, got); diff != "" { 2050 t.Errorf("getWAFPoliciesForAppProtectLogConf() returned unexpected result for the case of: %v (-want +got):\n%s", test.msg, diff) 2051 } 2052 } 2053 } 2054 2055 func TestPreSyncSecrets(t *testing.T) { 2056 lbc := LoadBalancerController{ 2057 isNginxPlus: true, 2058 secretStore: secrets.NewEmptyFakeSecretsStore(), 2059 secretLister: &cache.FakeCustomStore{ 2060 ListFunc: func() []interface{} { 2061 return []interface{}{ 2062 &api_v1.Secret{ 2063 ObjectMeta: meta_v1.ObjectMeta{ 2064 Name: "supported-secret", 2065 Namespace: "default", 2066 }, 2067 Type: api_v1.SecretTypeTLS, 2068 }, 2069 &api_v1.Secret{ 2070 ObjectMeta: meta_v1.ObjectMeta{ 2071 Name: "unsupported-secret", 2072 Namespace: "default", 2073 }, 2074 Type: api_v1.SecretTypeOpaque, 2075 }, 2076 } 2077 }, 2078 }, 2079 } 2080 2081 lbc.preSyncSecrets() 2082 2083 supportedKey := "default/supported-secret" 2084 ref := lbc.secretStore.GetSecret(supportedKey) 2085 if ref.Error != nil { 2086 t.Errorf("GetSecret(%q) returned a reference with an unexpected error %v", supportedKey, ref.Error) 2087 } 2088 2089 unsupportedKey := "default/unsupported-secret" 2090 ref = lbc.secretStore.GetSecret(unsupportedKey) 2091 if ref.Error == nil { 2092 t.Errorf("GetSecret(%q) returned a reference without an expected error", unsupportedKey) 2093 } 2094 }