github.com/nginxinc/kubernetes-ingress@v1.12.5/internal/configs/virtualserver_test.go (about) 1 package configs 2 3 import ( 4 "errors" 5 "fmt" 6 "reflect" 7 "testing" 8 9 "github.com/google/go-cmp/cmp" 10 "github.com/nginxinc/kubernetes-ingress/internal/configs/version2" 11 "github.com/nginxinc/kubernetes-ingress/internal/k8s/secrets" 12 "github.com/nginxinc/kubernetes-ingress/internal/nginx" 13 conf_v1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1" 14 api_v1 "k8s.io/api/core/v1" 15 meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 16 "k8s.io/apimachinery/pkg/runtime" 17 ) 18 19 func createPointerFromBool(b bool) *bool { 20 return &b 21 } 22 23 func TestVirtualServerExString(t *testing.T) { 24 tests := []struct { 25 input *VirtualServerEx 26 expected string 27 }{ 28 { 29 input: &VirtualServerEx{ 30 VirtualServer: &conf_v1.VirtualServer{ 31 ObjectMeta: meta_v1.ObjectMeta{ 32 Name: "cafe", 33 Namespace: "default", 34 }, 35 }, 36 }, 37 expected: "default/cafe", 38 }, 39 { 40 input: &VirtualServerEx{}, 41 expected: "VirtualServerEx has no VirtualServer", 42 }, 43 { 44 input: nil, 45 expected: "<nil>", 46 }, 47 } 48 49 for _, test := range tests { 50 result := test.input.String() 51 if result != test.expected { 52 t.Errorf("VirtualServerEx.String() returned %v but expected %v", result, test.expected) 53 } 54 } 55 } 56 57 func TestGenerateEndpointsKey(t *testing.T) { 58 serviceNamespace := "default" 59 serviceName := "test" 60 var port uint16 = 80 61 62 tests := []struct { 63 subselector map[string]string 64 expected string 65 }{ 66 { 67 subselector: nil, 68 expected: "default/test:80", 69 }, 70 { 71 subselector: map[string]string{"version": "v1"}, 72 expected: "default/test_version=v1:80", 73 }, 74 } 75 76 for _, test := range tests { 77 result := GenerateEndpointsKey(serviceNamespace, serviceName, test.subselector, port) 78 if result != test.expected { 79 t.Errorf("GenerateEndpointsKey() returned %q but expected %q", result, test.expected) 80 } 81 82 } 83 } 84 85 func TestUpstreamNamerForVirtualServer(t *testing.T) { 86 virtualServer := conf_v1.VirtualServer{ 87 ObjectMeta: meta_v1.ObjectMeta{ 88 Name: "cafe", 89 Namespace: "default", 90 }, 91 } 92 upstreamNamer := newUpstreamNamerForVirtualServer(&virtualServer) 93 upstream := "test" 94 95 expected := "vs_default_cafe_test" 96 97 result := upstreamNamer.GetNameForUpstream(upstream) 98 if result != expected { 99 t.Errorf("GetNameForUpstream() returned %q but expected %q", result, expected) 100 } 101 } 102 103 func TestUpstreamNamerForVirtualServerRoute(t *testing.T) { 104 virtualServer := conf_v1.VirtualServer{ 105 ObjectMeta: meta_v1.ObjectMeta{ 106 Name: "cafe", 107 Namespace: "default", 108 }, 109 } 110 virtualServerRoute := conf_v1.VirtualServerRoute{ 111 ObjectMeta: meta_v1.ObjectMeta{ 112 Name: "coffee", 113 Namespace: "default", 114 }, 115 } 116 upstreamNamer := newUpstreamNamerForVirtualServerRoute(&virtualServer, &virtualServerRoute) 117 upstream := "test" 118 119 expected := "vs_default_cafe_vsr_default_coffee_test" 120 121 result := upstreamNamer.GetNameForUpstream(upstream) 122 if result != expected { 123 t.Errorf("GetNameForUpstream() returned %q but expected %q", result, expected) 124 } 125 } 126 127 func TestVariableNamerSafeNsName(t *testing.T) { 128 virtualServer := conf_v1.VirtualServer{ 129 ObjectMeta: meta_v1.ObjectMeta{ 130 Name: "cafe-test", 131 Namespace: "default", 132 }, 133 } 134 135 expected := "default_cafe_test" 136 137 variableNamer := newVariableNamer(&virtualServer) 138 139 if variableNamer.safeNsName != expected { 140 t.Errorf( 141 "newVariableNamer() returned variableNamer with safeNsName=%q but expected %q", 142 variableNamer.safeNsName, 143 expected, 144 ) 145 } 146 } 147 148 func TestVariableNamer(t *testing.T) { 149 virtualServer := conf_v1.VirtualServer{ 150 ObjectMeta: meta_v1.ObjectMeta{ 151 Name: "cafe", 152 Namespace: "default", 153 }, 154 } 155 variableNamer := newVariableNamer(&virtualServer) 156 157 // GetNameForSplitClientVariable() 158 index := 0 159 160 expected := "$vs_default_cafe_splits_0" 161 162 result := variableNamer.GetNameForSplitClientVariable(index) 163 if result != expected { 164 t.Errorf("GetNameForSplitClientVariable() returned %q but expected %q", result, expected) 165 } 166 167 // GetNameForVariableForMatchesRouteMap() 168 matchesIndex := 1 169 matchIndex := 2 170 conditionIndex := 3 171 172 expected = "$vs_default_cafe_matches_1_match_2_cond_3" 173 174 result = variableNamer.GetNameForVariableForMatchesRouteMap(matchesIndex, matchIndex, conditionIndex) 175 if result != expected { 176 t.Errorf("GetNameForVariableForMatchesRouteMap() returned %q but expected %q", result, expected) 177 } 178 179 // GetNameForVariableForMatchesRouteMainMap() 180 matchesIndex = 2 181 182 expected = "$vs_default_cafe_matches_2" 183 184 result = variableNamer.GetNameForVariableForMatchesRouteMainMap(matchesIndex) 185 if result != expected { 186 t.Errorf("GetNameForVariableForMatchesRouteMainMap() returned %q but expected %q", result, expected) 187 } 188 } 189 190 func TestGenerateVirtualServerConfig(t *testing.T) { 191 virtualServerEx := VirtualServerEx{ 192 VirtualServer: &conf_v1.VirtualServer{ 193 ObjectMeta: meta_v1.ObjectMeta{ 194 Name: "cafe", 195 Namespace: "default", 196 }, 197 Spec: conf_v1.VirtualServerSpec{ 198 Host: "cafe.example.com", 199 Upstreams: []conf_v1.Upstream{ 200 { 201 Name: "tea", 202 Service: "tea-svc", 203 Port: 80, 204 }, 205 { 206 Name: "tea-latest", 207 Service: "tea-svc", 208 Subselector: map[string]string{"version": "v1"}, 209 Port: 80, 210 }, 211 { 212 Name: "coffee", 213 Service: "coffee-svc", 214 Port: 80, 215 }, 216 }, 217 Routes: []conf_v1.Route{ 218 { 219 Path: "/tea", 220 Action: &conf_v1.Action{ 221 Pass: "tea", 222 }, 223 }, 224 { 225 Path: "/tea-latest", 226 Action: &conf_v1.Action{ 227 Pass: "tea-latest", 228 }, 229 }, 230 { 231 Path: "/coffee", 232 Route: "default/coffee", 233 }, 234 { 235 Path: "/subtea", 236 Route: "default/subtea", 237 }, 238 { 239 Path: "/coffee-errorpage", 240 Action: &conf_v1.Action{ 241 Pass: "coffee", 242 }, 243 ErrorPages: []conf_v1.ErrorPage{ 244 { 245 Codes: []int{401, 403}, 246 Redirect: &conf_v1.ErrorPageRedirect{ 247 ActionRedirect: conf_v1.ActionRedirect{ 248 URL: "http://nginx.com", 249 Code: 301, 250 }, 251 }, 252 }, 253 }, 254 }, 255 { 256 Path: "/coffee-errorpage-subroute", 257 Route: "default/subcoffee", 258 ErrorPages: []conf_v1.ErrorPage{ 259 { 260 Codes: []int{401, 403}, 261 Redirect: &conf_v1.ErrorPageRedirect{ 262 ActionRedirect: conf_v1.ActionRedirect{ 263 URL: "http://nginx.com", 264 Code: 301, 265 }, 266 }, 267 }, 268 }, 269 }, 270 }, 271 }, 272 }, 273 Endpoints: map[string][]string{ 274 "default/tea-svc:80": { 275 "10.0.0.20:80", 276 }, 277 "default/tea-svc_version=v1:80": { 278 "10.0.0.30:80", 279 }, 280 "default/coffee-svc:80": { 281 "10.0.0.40:80", 282 }, 283 "default/sub-tea-svc_version=v1:80": { 284 "10.0.0.50:80", 285 }, 286 }, 287 VirtualServerRoutes: []*conf_v1.VirtualServerRoute{ 288 { 289 ObjectMeta: meta_v1.ObjectMeta{ 290 Name: "coffee", 291 Namespace: "default", 292 }, 293 Spec: conf_v1.VirtualServerRouteSpec{ 294 Host: "cafe.example.com", 295 Upstreams: []conf_v1.Upstream{ 296 { 297 Name: "coffee", 298 Service: "coffee-svc", 299 Port: 80, 300 }, 301 }, 302 Subroutes: []conf_v1.Route{ 303 { 304 Path: "/coffee", 305 Action: &conf_v1.Action{ 306 Pass: "coffee", 307 }, 308 }, 309 }, 310 }, 311 }, 312 { 313 ObjectMeta: meta_v1.ObjectMeta{ 314 Name: "subtea", 315 Namespace: "default", 316 }, 317 Spec: conf_v1.VirtualServerRouteSpec{ 318 Host: "cafe.example.com", 319 Upstreams: []conf_v1.Upstream{ 320 { 321 Name: "subtea", 322 Service: "sub-tea-svc", 323 Port: 80, 324 Subselector: map[string]string{"version": "v1"}, 325 }, 326 }, 327 Subroutes: []conf_v1.Route{ 328 { 329 Path: "/subtea", 330 Action: &conf_v1.Action{ 331 Pass: "subtea", 332 }, 333 }, 334 }, 335 }, 336 }, 337 { 338 ObjectMeta: meta_v1.ObjectMeta{ 339 Name: "subcoffee", 340 Namespace: "default", 341 }, 342 Spec: conf_v1.VirtualServerRouteSpec{ 343 Host: "cafe.example.com", 344 Upstreams: []conf_v1.Upstream{ 345 { 346 Name: "coffee", 347 Service: "coffee-svc", 348 Port: 80, 349 }, 350 }, 351 Subroutes: []conf_v1.Route{ 352 { 353 Path: "/coffee-errorpage-subroute", 354 Action: &conf_v1.Action{ 355 Pass: "coffee", 356 }, 357 }, 358 { 359 Path: "/coffee-errorpage-subroute-defined", 360 Action: &conf_v1.Action{ 361 Pass: "coffee", 362 }, 363 ErrorPages: []conf_v1.ErrorPage{ 364 { 365 Codes: []int{502, 503}, 366 Return: &conf_v1.ErrorPageReturn{ 367 ActionReturn: conf_v1.ActionReturn{ 368 Code: 200, 369 Type: "text/plain", 370 Body: "All Good", 371 }, 372 }, 373 }, 374 }, 375 }, 376 }, 377 }, 378 }, 379 }, 380 } 381 382 baseCfgParams := ConfigParams{ 383 ServerTokens: "off", 384 Keepalive: 16, 385 ServerSnippets: []string{"# server snippet"}, 386 ProxyProtocol: true, 387 SetRealIPFrom: []string{"0.0.0.0/0"}, 388 RealIPHeader: "X-Real-IP", 389 RealIPRecursive: true, 390 } 391 392 expected := version2.VirtualServerConfig{ 393 Upstreams: []version2.Upstream{ 394 { 395 UpstreamLabels: version2.UpstreamLabels{ 396 Service: "tea-svc", 397 ResourceType: "virtualserver", 398 ResourceName: "cafe", 399 ResourceNamespace: "default", 400 }, 401 Name: "vs_default_cafe_tea", 402 Servers: []version2.UpstreamServer{ 403 { 404 Address: "10.0.0.20:80", 405 }, 406 }, 407 Keepalive: 16, 408 }, 409 { 410 UpstreamLabels: version2.UpstreamLabels{ 411 Service: "tea-svc", 412 ResourceType: "virtualserver", 413 ResourceName: "cafe", 414 ResourceNamespace: "default", 415 }, 416 Name: "vs_default_cafe_tea-latest", 417 Servers: []version2.UpstreamServer{ 418 { 419 Address: "10.0.0.30:80", 420 }, 421 }, 422 Keepalive: 16, 423 }, 424 { 425 UpstreamLabels: version2.UpstreamLabels{ 426 Service: "coffee-svc", 427 ResourceType: "virtualserver", 428 ResourceName: "cafe", 429 ResourceNamespace: "default", 430 }, 431 Name: "vs_default_cafe_coffee", 432 Servers: []version2.UpstreamServer{ 433 { 434 Address: "10.0.0.40:80", 435 }, 436 }, 437 Keepalive: 16, 438 }, 439 { 440 UpstreamLabels: version2.UpstreamLabels{ 441 Service: "coffee-svc", 442 ResourceType: "virtualserverroute", 443 ResourceName: "coffee", 444 ResourceNamespace: "default", 445 }, 446 Name: "vs_default_cafe_vsr_default_coffee_coffee", 447 Servers: []version2.UpstreamServer{ 448 { 449 Address: "10.0.0.40:80", 450 }, 451 }, 452 Keepalive: 16, 453 }, 454 { 455 UpstreamLabels: version2.UpstreamLabels{ 456 Service: "sub-tea-svc", 457 ResourceType: "virtualserverroute", 458 ResourceName: "subtea", 459 ResourceNamespace: "default", 460 }, 461 Name: "vs_default_cafe_vsr_default_subtea_subtea", 462 Servers: []version2.UpstreamServer{ 463 { 464 Address: "10.0.0.50:80", 465 }, 466 }, 467 Keepalive: 16, 468 }, 469 { 470 UpstreamLabels: version2.UpstreamLabels{ 471 Service: "coffee-svc", 472 ResourceType: "virtualserverroute", 473 ResourceName: "subcoffee", 474 ResourceNamespace: "default", 475 }, 476 Name: "vs_default_cafe_vsr_default_subcoffee_coffee", 477 Servers: []version2.UpstreamServer{ 478 { 479 Address: "10.0.0.40:80", 480 }, 481 }, 482 Keepalive: 16, 483 }, 484 }, 485 HTTPSnippets: []string{}, 486 LimitReqZones: []version2.LimitReqZone{}, 487 Server: version2.Server{ 488 ServerName: "cafe.example.com", 489 StatusZone: "cafe.example.com", 490 VSNamespace: "default", 491 VSName: "cafe", 492 ProxyProtocol: true, 493 ServerTokens: "off", 494 SetRealIPFrom: []string{"0.0.0.0/0"}, 495 RealIPHeader: "X-Real-IP", 496 RealIPRecursive: true, 497 Snippets: []string{"# server snippet"}, 498 TLSPassthrough: true, 499 Locations: []version2.Location{ 500 { 501 Path: "/tea", 502 ProxyPass: "http://vs_default_cafe_tea", 503 ProxyNextUpstream: "error timeout", 504 ProxyNextUpstreamTimeout: "0s", 505 ProxyNextUpstreamTries: 0, 506 HasKeepalive: true, 507 ProxySSLName: "tea-svc.default.svc", 508 ProxyPassRequestHeaders: true, 509 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 510 ServiceName: "tea-svc", 511 }, 512 { 513 Path: "/tea-latest", 514 ProxyPass: "http://vs_default_cafe_tea-latest", 515 ProxyNextUpstream: "error timeout", 516 ProxyNextUpstreamTimeout: "0s", 517 ProxyNextUpstreamTries: 0, 518 HasKeepalive: true, 519 ProxySSLName: "tea-svc.default.svc", 520 ProxyPassRequestHeaders: true, 521 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 522 ServiceName: "tea-svc", 523 }, 524 // Order changes here because we generate first all the VS Routes and then all the VSR Subroutes (separated for loops) 525 { 526 Path: "/coffee-errorpage", 527 ProxyPass: "http://vs_default_cafe_coffee", 528 ProxyNextUpstream: "error timeout", 529 ProxyNextUpstreamTimeout: "0s", 530 ProxyNextUpstreamTries: 0, 531 HasKeepalive: true, 532 ProxyInterceptErrors: true, 533 ErrorPages: []version2.ErrorPage{ 534 { 535 Name: "http://nginx.com", 536 Codes: "401 403", 537 ResponseCode: 301, 538 }, 539 }, 540 ProxySSLName: "coffee-svc.default.svc", 541 ProxyPassRequestHeaders: true, 542 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 543 ServiceName: "coffee-svc", 544 }, 545 { 546 Path: "/coffee", 547 ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee", 548 ProxyNextUpstream: "error timeout", 549 ProxyNextUpstreamTimeout: "0s", 550 ProxyNextUpstreamTries: 0, 551 HasKeepalive: true, 552 ProxySSLName: "coffee-svc.default.svc", 553 ProxyPassRequestHeaders: true, 554 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 555 ServiceName: "coffee-svc", 556 IsVSR: true, 557 VSRName: "coffee", 558 VSRNamespace: "default", 559 }, 560 { 561 Path: "/subtea", 562 ProxyPass: "http://vs_default_cafe_vsr_default_subtea_subtea", 563 ProxyNextUpstream: "error timeout", 564 ProxyNextUpstreamTimeout: "0s", 565 ProxyNextUpstreamTries: 0, 566 HasKeepalive: true, 567 ProxySSLName: "sub-tea-svc.default.svc", 568 ProxyPassRequestHeaders: true, 569 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 570 ServiceName: "sub-tea-svc", 571 IsVSR: true, 572 VSRName: "subtea", 573 VSRNamespace: "default", 574 }, 575 576 { 577 Path: "/coffee-errorpage-subroute", 578 ProxyPass: "http://vs_default_cafe_vsr_default_subcoffee_coffee", 579 ProxyNextUpstream: "error timeout", 580 ProxyNextUpstreamTimeout: "0s", 581 ProxyNextUpstreamTries: 0, 582 HasKeepalive: true, 583 ProxyInterceptErrors: true, 584 ErrorPages: []version2.ErrorPage{ 585 { 586 Name: "http://nginx.com", 587 Codes: "401 403", 588 ResponseCode: 301, 589 }, 590 }, 591 ProxySSLName: "coffee-svc.default.svc", 592 ProxyPassRequestHeaders: true, 593 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 594 ServiceName: "coffee-svc", 595 IsVSR: true, 596 VSRName: "subcoffee", 597 VSRNamespace: "default", 598 }, 599 { 600 Path: "/coffee-errorpage-subroute-defined", 601 ProxyPass: "http://vs_default_cafe_vsr_default_subcoffee_coffee", 602 ProxyNextUpstream: "error timeout", 603 ProxyNextUpstreamTimeout: "0s", 604 ProxyNextUpstreamTries: 0, 605 HasKeepalive: true, 606 ProxyInterceptErrors: true, 607 ErrorPages: []version2.ErrorPage{ 608 { 609 Name: "@error_page_0_0", 610 Codes: "502 503", 611 ResponseCode: 200, 612 }, 613 }, 614 ProxySSLName: "coffee-svc.default.svc", 615 ProxyPassRequestHeaders: true, 616 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 617 ServiceName: "coffee-svc", 618 IsVSR: true, 619 VSRName: "subcoffee", 620 VSRNamespace: "default", 621 }, 622 }, 623 ErrorPageLocations: []version2.ErrorPageLocation{ 624 { 625 Name: "@error_page_0_0", 626 DefaultType: "text/plain", 627 Return: &version2.Return{ 628 Text: "All Good", 629 }, 630 }, 631 }, 632 }, 633 } 634 635 isPlus := false 636 isResolverConfigured := false 637 vsc := newVirtualServerConfigurator( 638 &baseCfgParams, 639 isPlus, 640 isResolverConfigured, 641 &StaticConfigParams{TLSPassthrough: true}, 642 ) 643 644 result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil) 645 if diff := cmp.Diff(expected, result); diff != "" { 646 t.Errorf("GenerateVirtualServerConfig() mismatch (-want +got):\n%s", diff) 647 } 648 649 if len(warnings) != 0 { 650 t.Errorf("GenerateVirtualServerConfig returned warnings: %v", vsc.warnings) 651 } 652 } 653 654 func TestGenerateVirtualServerConfigWithSpiffeCerts(t *testing.T) { 655 virtualServerEx := VirtualServerEx{ 656 VirtualServer: &conf_v1.VirtualServer{ 657 ObjectMeta: meta_v1.ObjectMeta{ 658 Name: "cafe", 659 Namespace: "default", 660 }, 661 Spec: conf_v1.VirtualServerSpec{ 662 Host: "cafe.example.com", 663 Upstreams: []conf_v1.Upstream{ 664 { 665 Name: "tea", 666 Service: "tea-svc", 667 Port: 80, 668 }, 669 }, 670 Routes: []conf_v1.Route{ 671 { 672 Path: "/tea", 673 Action: &conf_v1.Action{ 674 Pass: "tea", 675 }, 676 }, 677 }, 678 }, 679 }, 680 Endpoints: map[string][]string{ 681 "default/tea-svc:80": { 682 "10.0.0.20:80", 683 }, 684 }, 685 } 686 687 baseCfgParams := ConfigParams{ 688 ServerTokens: "off", 689 Keepalive: 16, 690 ServerSnippets: []string{"# server snippet"}, 691 ProxyProtocol: true, 692 SetRealIPFrom: []string{"0.0.0.0/0"}, 693 RealIPHeader: "X-Real-IP", 694 RealIPRecursive: true, 695 } 696 697 expected := version2.VirtualServerConfig{ 698 Upstreams: []version2.Upstream{ 699 { 700 UpstreamLabels: version2.UpstreamLabels{ 701 Service: "tea-svc", 702 ResourceType: "virtualserver", 703 ResourceName: "cafe", 704 ResourceNamespace: "default", 705 }, 706 Name: "vs_default_cafe_tea", 707 Servers: []version2.UpstreamServer{ 708 { 709 Address: "10.0.0.20:80", 710 }, 711 }, 712 Keepalive: 16, 713 }, 714 }, 715 HTTPSnippets: []string{}, 716 LimitReqZones: []version2.LimitReqZone{}, 717 Server: version2.Server{ 718 ServerName: "cafe.example.com", 719 StatusZone: "cafe.example.com", 720 VSNamespace: "default", 721 VSName: "cafe", 722 ProxyProtocol: true, 723 ServerTokens: "off", 724 SetRealIPFrom: []string{"0.0.0.0/0"}, 725 RealIPHeader: "X-Real-IP", 726 RealIPRecursive: true, 727 Snippets: []string{"# server snippet"}, 728 TLSPassthrough: true, 729 Locations: []version2.Location{ 730 { 731 Path: "/tea", 732 ProxyPass: "https://vs_default_cafe_tea", 733 ProxyNextUpstream: "error timeout", 734 ProxyNextUpstreamTimeout: "0s", 735 ProxyNextUpstreamTries: 0, 736 HasKeepalive: true, 737 ProxySSLName: "tea-svc.default.svc", 738 ProxyPassRequestHeaders: true, 739 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 740 ServiceName: "tea-svc", 741 }, 742 }, 743 }, 744 SpiffeCerts: true, 745 } 746 747 isPlus := false 748 isResolverConfigured := false 749 staticConfigParams := &StaticConfigParams{TLSPassthrough: true, NginxServiceMesh: true} 750 vsc := newVirtualServerConfigurator(&baseCfgParams, isPlus, isResolverConfigured, staticConfigParams) 751 752 result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil) 753 if diff := cmp.Diff(expected, result); diff != "" { 754 t.Errorf("GenerateVirtualServerConfig() mismatch (-want +got):\n%s", diff) 755 } 756 757 if len(warnings) != 0 { 758 t.Errorf("GenerateVirtualServerConfig returned warnings: %v", vsc.warnings) 759 } 760 } 761 762 func TestGenerateVirtualServerConfigForVirtualServerWithSplits(t *testing.T) { 763 virtualServerEx := VirtualServerEx{ 764 VirtualServer: &conf_v1.VirtualServer{ 765 ObjectMeta: meta_v1.ObjectMeta{ 766 Name: "cafe", 767 Namespace: "default", 768 }, 769 Spec: conf_v1.VirtualServerSpec{ 770 Host: "cafe.example.com", 771 Upstreams: []conf_v1.Upstream{ 772 { 773 Name: "tea-v1", 774 Service: "tea-svc-v1", 775 Port: 80, 776 }, 777 { 778 Name: "tea-v2", 779 Service: "tea-svc-v2", 780 Port: 80, 781 }, 782 }, 783 Routes: []conf_v1.Route{ 784 { 785 Path: "/tea", 786 Splits: []conf_v1.Split{ 787 { 788 Weight: 90, 789 Action: &conf_v1.Action{ 790 Pass: "tea-v1", 791 }, 792 }, 793 { 794 Weight: 10, 795 Action: &conf_v1.Action{ 796 Pass: "tea-v2", 797 }, 798 }, 799 }, 800 }, 801 { 802 Path: "/coffee", 803 Route: "default/coffee", 804 }, 805 }, 806 }, 807 }, 808 Endpoints: map[string][]string{ 809 "default/tea-svc-v1:80": { 810 "10.0.0.20:80", 811 }, 812 "default/tea-svc-v2:80": { 813 "10.0.0.21:80", 814 }, 815 "default/coffee-svc-v1:80": { 816 "10.0.0.30:80", 817 }, 818 "default/coffee-svc-v2:80": { 819 "10.0.0.31:80", 820 }, 821 }, 822 VirtualServerRoutes: []*conf_v1.VirtualServerRoute{ 823 { 824 ObjectMeta: meta_v1.ObjectMeta{ 825 Name: "coffee", 826 Namespace: "default", 827 }, 828 Spec: conf_v1.VirtualServerRouteSpec{ 829 Host: "cafe.example.com", 830 Upstreams: []conf_v1.Upstream{ 831 { 832 Name: "coffee-v1", 833 Service: "coffee-svc-v1", 834 Port: 80, 835 }, 836 { 837 Name: "coffee-v2", 838 Service: "coffee-svc-v2", 839 Port: 80, 840 }, 841 }, 842 Subroutes: []conf_v1.Route{ 843 { 844 Path: "/coffee", 845 Splits: []conf_v1.Split{ 846 { 847 Weight: 40, 848 Action: &conf_v1.Action{ 849 Pass: "coffee-v1", 850 }, 851 }, 852 { 853 Weight: 60, 854 Action: &conf_v1.Action{ 855 Pass: "coffee-v2", 856 }, 857 }, 858 }, 859 }, 860 }, 861 }, 862 }, 863 }, 864 } 865 866 baseCfgParams := ConfigParams{} 867 868 expected := version2.VirtualServerConfig{ 869 Upstreams: []version2.Upstream{ 870 { 871 Name: "vs_default_cafe_tea-v1", 872 UpstreamLabels: version2.UpstreamLabels{ 873 Service: "tea-svc-v1", 874 ResourceType: "virtualserver", 875 ResourceName: "cafe", 876 ResourceNamespace: "default", 877 }, 878 Servers: []version2.UpstreamServer{ 879 { 880 Address: "10.0.0.20:80", 881 }, 882 }, 883 }, 884 { 885 Name: "vs_default_cafe_tea-v2", 886 UpstreamLabels: version2.UpstreamLabels{ 887 Service: "tea-svc-v2", 888 ResourceType: "virtualserver", 889 ResourceName: "cafe", 890 ResourceNamespace: "default", 891 }, 892 Servers: []version2.UpstreamServer{ 893 { 894 Address: "10.0.0.21:80", 895 }, 896 }, 897 }, 898 { 899 Name: "vs_default_cafe_vsr_default_coffee_coffee-v1", 900 UpstreamLabels: version2.UpstreamLabels{ 901 Service: "coffee-svc-v1", 902 ResourceType: "virtualserverroute", 903 ResourceName: "coffee", 904 ResourceNamespace: "default", 905 }, 906 Servers: []version2.UpstreamServer{ 907 { 908 Address: "10.0.0.30:80", 909 }, 910 }, 911 }, 912 { 913 Name: "vs_default_cafe_vsr_default_coffee_coffee-v2", 914 UpstreamLabels: version2.UpstreamLabels{ 915 Service: "coffee-svc-v2", 916 ResourceType: "virtualserverroute", 917 ResourceName: "coffee", 918 ResourceNamespace: "default", 919 }, 920 Servers: []version2.UpstreamServer{ 921 { 922 Address: "10.0.0.31:80", 923 }, 924 }, 925 }, 926 }, 927 SplitClients: []version2.SplitClient{ 928 { 929 Source: "$request_id", 930 Variable: "$vs_default_cafe_splits_0", 931 Distributions: []version2.Distribution{ 932 { 933 Weight: "90%", 934 Value: "/internal_location_splits_0_split_0", 935 }, 936 { 937 Weight: "10%", 938 Value: "/internal_location_splits_0_split_1", 939 }, 940 }, 941 }, 942 { 943 Source: "$request_id", 944 Variable: "$vs_default_cafe_splits_1", 945 Distributions: []version2.Distribution{ 946 { 947 Weight: "40%", 948 Value: "/internal_location_splits_1_split_0", 949 }, 950 { 951 Weight: "60%", 952 Value: "/internal_location_splits_1_split_1", 953 }, 954 }, 955 }, 956 }, 957 HTTPSnippets: []string{}, 958 LimitReqZones: []version2.LimitReqZone{}, 959 Server: version2.Server{ 960 ServerName: "cafe.example.com", 961 StatusZone: "cafe.example.com", 962 VSNamespace: "default", 963 VSName: "cafe", 964 InternalRedirectLocations: []version2.InternalRedirectLocation{ 965 { 966 Path: "/tea", 967 Destination: "$vs_default_cafe_splits_0", 968 }, 969 { 970 Path: "/coffee", 971 Destination: "$vs_default_cafe_splits_1", 972 }, 973 }, 974 Locations: []version2.Location{ 975 { 976 Path: "/internal_location_splits_0_split_0", 977 ProxyPass: "http://vs_default_cafe_tea-v1$request_uri", 978 ProxyNextUpstream: "error timeout", 979 ProxyNextUpstreamTimeout: "0s", 980 ProxyNextUpstreamTries: 0, 981 Internal: true, 982 ProxySSLName: "tea-svc-v1.default.svc", 983 ProxyPassRequestHeaders: true, 984 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 985 ServiceName: "tea-svc-v1", 986 }, 987 { 988 Path: "/internal_location_splits_0_split_1", 989 ProxyPass: "http://vs_default_cafe_tea-v2$request_uri", 990 ProxyNextUpstream: "error timeout", 991 ProxyNextUpstreamTimeout: "0s", 992 ProxyNextUpstreamTries: 0, 993 Internal: true, 994 ProxySSLName: "tea-svc-v2.default.svc", 995 ProxyPassRequestHeaders: true, 996 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 997 ServiceName: "tea-svc-v2", 998 }, 999 { 1000 Path: "/internal_location_splits_1_split_0", 1001 ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee-v1$request_uri", 1002 ProxyNextUpstream: "error timeout", 1003 ProxyNextUpstreamTimeout: "0s", 1004 ProxyNextUpstreamTries: 0, 1005 Internal: true, 1006 ProxySSLName: "coffee-svc-v1.default.svc", 1007 ProxyPassRequestHeaders: true, 1008 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 1009 ServiceName: "coffee-svc-v1", 1010 IsVSR: true, 1011 VSRName: "coffee", 1012 VSRNamespace: "default", 1013 }, 1014 { 1015 Path: "/internal_location_splits_1_split_1", 1016 ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee-v2$request_uri", 1017 ProxyNextUpstream: "error timeout", 1018 ProxyNextUpstreamTimeout: "0s", 1019 ProxyNextUpstreamTries: 0, 1020 Internal: true, 1021 ProxySSLName: "coffee-svc-v2.default.svc", 1022 ProxyPassRequestHeaders: true, 1023 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 1024 ServiceName: "coffee-svc-v2", 1025 IsVSR: true, 1026 VSRName: "coffee", 1027 VSRNamespace: "default", 1028 }, 1029 }, 1030 }, 1031 } 1032 1033 isPlus := false 1034 isResolverConfigured := false 1035 vsc := newVirtualServerConfigurator(&baseCfgParams, isPlus, isResolverConfigured, &StaticConfigParams{}) 1036 1037 result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil) 1038 if diff := cmp.Diff(expected, result); diff != "" { 1039 t.Errorf("GenerateVirtualServerConfig() mismatch (-want +got):\n%s", diff) 1040 } 1041 1042 if len(warnings) != 0 { 1043 t.Errorf("GenerateVirtualServerConfig returned warnings: %v", vsc.warnings) 1044 } 1045 } 1046 1047 func TestGenerateVirtualServerConfigForVirtualServerWithMatches(t *testing.T) { 1048 virtualServerEx := VirtualServerEx{ 1049 VirtualServer: &conf_v1.VirtualServer{ 1050 ObjectMeta: meta_v1.ObjectMeta{ 1051 Name: "cafe", 1052 Namespace: "default", 1053 }, 1054 Spec: conf_v1.VirtualServerSpec{ 1055 Host: "cafe.example.com", 1056 Upstreams: []conf_v1.Upstream{ 1057 { 1058 Name: "tea-v1", 1059 Service: "tea-svc-v1", 1060 Port: 80, 1061 }, 1062 { 1063 Name: "tea-v2", 1064 Service: "tea-svc-v2", 1065 Port: 80, 1066 }, 1067 }, 1068 Routes: []conf_v1.Route{ 1069 { 1070 Path: "/tea", 1071 Matches: []conf_v1.Match{ 1072 { 1073 Conditions: []conf_v1.Condition{ 1074 { 1075 Header: "x-version", 1076 Value: "v2", 1077 }, 1078 }, 1079 Action: &conf_v1.Action{ 1080 Pass: "tea-v2", 1081 }, 1082 }, 1083 }, 1084 Action: &conf_v1.Action{ 1085 Pass: "tea-v1", 1086 }, 1087 }, 1088 { 1089 Path: "/coffee", 1090 Route: "default/coffee", 1091 }, 1092 }, 1093 }, 1094 }, 1095 Endpoints: map[string][]string{ 1096 "default/tea-svc-v1:80": { 1097 "10.0.0.20:80", 1098 }, 1099 "default/tea-svc-v2:80": { 1100 "10.0.0.21:80", 1101 }, 1102 "default/coffee-svc-v1:80": { 1103 "10.0.0.30:80", 1104 }, 1105 "default/coffee-svc-v2:80": { 1106 "10.0.0.31:80", 1107 }, 1108 }, 1109 VirtualServerRoutes: []*conf_v1.VirtualServerRoute{ 1110 { 1111 ObjectMeta: meta_v1.ObjectMeta{ 1112 Name: "coffee", 1113 Namespace: "default", 1114 }, 1115 Spec: conf_v1.VirtualServerRouteSpec{ 1116 Host: "cafe.example.com", 1117 Upstreams: []conf_v1.Upstream{ 1118 { 1119 Name: "coffee-v1", 1120 Service: "coffee-svc-v1", 1121 Port: 80, 1122 }, 1123 { 1124 Name: "coffee-v2", 1125 Service: "coffee-svc-v2", 1126 Port: 80, 1127 }, 1128 }, 1129 Subroutes: []conf_v1.Route{ 1130 { 1131 Path: "/coffee", 1132 Matches: []conf_v1.Match{ 1133 { 1134 Conditions: []conf_v1.Condition{ 1135 { 1136 Argument: "version", 1137 Value: "v2", 1138 }, 1139 }, 1140 Action: &conf_v1.Action{ 1141 Pass: "coffee-v2", 1142 }, 1143 }, 1144 }, 1145 Action: &conf_v1.Action{ 1146 Pass: "coffee-v1", 1147 }, 1148 }, 1149 }, 1150 }, 1151 }, 1152 }, 1153 } 1154 1155 baseCfgParams := ConfigParams{} 1156 1157 expected := version2.VirtualServerConfig{ 1158 Upstreams: []version2.Upstream{ 1159 { 1160 UpstreamLabels: version2.UpstreamLabels{ 1161 Service: "tea-svc-v1", 1162 ResourceType: "virtualserver", 1163 ResourceName: "cafe", 1164 ResourceNamespace: "default", 1165 }, 1166 Name: "vs_default_cafe_tea-v1", 1167 Servers: []version2.UpstreamServer{ 1168 { 1169 Address: "10.0.0.20:80", 1170 }, 1171 }, 1172 }, 1173 { 1174 Name: "vs_default_cafe_tea-v2", 1175 UpstreamLabels: version2.UpstreamLabels{ 1176 Service: "tea-svc-v2", 1177 ResourceType: "virtualserver", 1178 ResourceName: "cafe", 1179 ResourceNamespace: "default", 1180 }, 1181 Servers: []version2.UpstreamServer{ 1182 { 1183 Address: "10.0.0.21:80", 1184 }, 1185 }, 1186 }, 1187 { 1188 Name: "vs_default_cafe_vsr_default_coffee_coffee-v1", 1189 UpstreamLabels: version2.UpstreamLabels{ 1190 Service: "coffee-svc-v1", 1191 ResourceType: "virtualserverroute", 1192 ResourceName: "coffee", 1193 ResourceNamespace: "default", 1194 }, 1195 Servers: []version2.UpstreamServer{ 1196 { 1197 Address: "10.0.0.30:80", 1198 }, 1199 }, 1200 }, 1201 { 1202 Name: "vs_default_cafe_vsr_default_coffee_coffee-v2", 1203 UpstreamLabels: version2.UpstreamLabels{ 1204 Service: "coffee-svc-v2", 1205 ResourceType: "virtualserverroute", 1206 ResourceName: "coffee", 1207 ResourceNamespace: "default", 1208 }, 1209 Servers: []version2.UpstreamServer{ 1210 { 1211 Address: "10.0.0.31:80", 1212 }, 1213 }, 1214 }, 1215 }, 1216 Maps: []version2.Map{ 1217 { 1218 Source: "$http_x_version", 1219 Variable: "$vs_default_cafe_matches_0_match_0_cond_0", 1220 Parameters: []version2.Parameter{ 1221 { 1222 Value: `"v2"`, 1223 Result: "1", 1224 }, 1225 { 1226 Value: "default", 1227 Result: "0", 1228 }, 1229 }, 1230 }, 1231 { 1232 Source: "$vs_default_cafe_matches_0_match_0_cond_0", 1233 Variable: "$vs_default_cafe_matches_0", 1234 Parameters: []version2.Parameter{ 1235 { 1236 Value: "~^1", 1237 Result: "/internal_location_matches_0_match_0", 1238 }, 1239 { 1240 Value: "default", 1241 Result: "/internal_location_matches_0_default", 1242 }, 1243 }, 1244 }, 1245 { 1246 Source: "$arg_version", 1247 Variable: "$vs_default_cafe_matches_1_match_0_cond_0", 1248 Parameters: []version2.Parameter{ 1249 { 1250 Value: `"v2"`, 1251 Result: "1", 1252 }, 1253 { 1254 Value: "default", 1255 Result: "0", 1256 }, 1257 }, 1258 }, 1259 { 1260 Source: "$vs_default_cafe_matches_1_match_0_cond_0", 1261 Variable: "$vs_default_cafe_matches_1", 1262 Parameters: []version2.Parameter{ 1263 { 1264 Value: "~^1", 1265 Result: "/internal_location_matches_1_match_0", 1266 }, 1267 { 1268 Value: "default", 1269 Result: "/internal_location_matches_1_default", 1270 }, 1271 }, 1272 }, 1273 }, 1274 HTTPSnippets: []string{}, 1275 LimitReqZones: []version2.LimitReqZone{}, 1276 Server: version2.Server{ 1277 ServerName: "cafe.example.com", 1278 StatusZone: "cafe.example.com", 1279 VSNamespace: "default", 1280 VSName: "cafe", 1281 InternalRedirectLocations: []version2.InternalRedirectLocation{ 1282 { 1283 Path: "/tea", 1284 Destination: "$vs_default_cafe_matches_0", 1285 }, 1286 { 1287 Path: "/coffee", 1288 Destination: "$vs_default_cafe_matches_1", 1289 }, 1290 }, 1291 Locations: []version2.Location{ 1292 { 1293 Path: "/internal_location_matches_0_match_0", 1294 ProxyPass: "http://vs_default_cafe_tea-v2$request_uri", 1295 ProxyNextUpstream: "error timeout", 1296 ProxyNextUpstreamTimeout: "0s", 1297 ProxyNextUpstreamTries: 0, 1298 Internal: true, 1299 ProxySSLName: "tea-svc-v2.default.svc", 1300 ProxyPassRequestHeaders: true, 1301 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 1302 ServiceName: "tea-svc-v2", 1303 }, 1304 { 1305 Path: "/internal_location_matches_0_default", 1306 ProxyPass: "http://vs_default_cafe_tea-v1$request_uri", 1307 ProxyNextUpstream: "error timeout", 1308 ProxyNextUpstreamTimeout: "0s", 1309 ProxyNextUpstreamTries: 0, 1310 Internal: true, 1311 ProxySSLName: "tea-svc-v1.default.svc", 1312 ProxyPassRequestHeaders: true, 1313 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 1314 ServiceName: "tea-svc-v1", 1315 }, 1316 { 1317 Path: "/internal_location_matches_1_match_0", 1318 ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee-v2$request_uri", 1319 ProxyNextUpstream: "error timeout", 1320 ProxyNextUpstreamTimeout: "0s", 1321 ProxyNextUpstreamTries: 0, 1322 Internal: true, 1323 ProxySSLName: "coffee-svc-v2.default.svc", 1324 ProxyPassRequestHeaders: true, 1325 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 1326 ServiceName: "coffee-svc-v2", 1327 IsVSR: true, 1328 VSRName: "coffee", 1329 VSRNamespace: "default", 1330 }, 1331 { 1332 Path: "/internal_location_matches_1_default", 1333 ProxyPass: "http://vs_default_cafe_vsr_default_coffee_coffee-v1$request_uri", 1334 ProxyNextUpstream: "error timeout", 1335 ProxyNextUpstreamTimeout: "0s", 1336 ProxyNextUpstreamTries: 0, 1337 Internal: true, 1338 ProxySSLName: "coffee-svc-v1.default.svc", 1339 ProxyPassRequestHeaders: true, 1340 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 1341 ServiceName: "coffee-svc-v1", 1342 IsVSR: true, 1343 VSRName: "coffee", 1344 VSRNamespace: "default", 1345 }, 1346 }, 1347 }, 1348 } 1349 1350 isPlus := false 1351 isResolverConfigured := false 1352 vsc := newVirtualServerConfigurator(&baseCfgParams, isPlus, isResolverConfigured, &StaticConfigParams{}) 1353 1354 result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil) 1355 if diff := cmp.Diff(expected, result); diff != "" { 1356 t.Errorf("GenerateVirtualServerConfig() mismatch (-want +got):\n%s", diff) 1357 } 1358 1359 if len(warnings) != 0 { 1360 t.Errorf("GenerateVirtualServerConfig returned warnings: %v", vsc.warnings) 1361 } 1362 } 1363 1364 func TestGenerateVirtualServerConfigForVirtualServerWithReturns(t *testing.T) { 1365 virtualServerEx := VirtualServerEx{ 1366 VirtualServer: &conf_v1.VirtualServer{ 1367 ObjectMeta: meta_v1.ObjectMeta{ 1368 Name: "returns", 1369 Namespace: "default", 1370 }, 1371 Spec: conf_v1.VirtualServerSpec{ 1372 Host: "example.com", 1373 Routes: []conf_v1.Route{ 1374 { 1375 Path: "/return", 1376 Action: &conf_v1.Action{ 1377 Return: &conf_v1.ActionReturn{ 1378 Body: "hello 0", 1379 }, 1380 }, 1381 }, 1382 { 1383 Path: "/splits-with-return", 1384 Splits: []conf_v1.Split{ 1385 { 1386 Weight: 90, 1387 Action: &conf_v1.Action{ 1388 Return: &conf_v1.ActionReturn{ 1389 Body: "hello 1", 1390 }, 1391 }, 1392 }, 1393 { 1394 Weight: 10, 1395 Action: &conf_v1.Action{ 1396 Return: &conf_v1.ActionReturn{ 1397 Body: "hello 2", 1398 }, 1399 }, 1400 }, 1401 }, 1402 }, 1403 { 1404 Path: "/matches-with-return", 1405 Matches: []conf_v1.Match{ 1406 { 1407 Conditions: []conf_v1.Condition{ 1408 { 1409 Header: "x-version", 1410 Value: "v2", 1411 }, 1412 }, 1413 Action: &conf_v1.Action{ 1414 Return: &conf_v1.ActionReturn{ 1415 Body: "hello 3", 1416 }, 1417 }, 1418 }, 1419 }, 1420 Action: &conf_v1.Action{ 1421 Return: &conf_v1.ActionReturn{ 1422 Body: "hello 4", 1423 }, 1424 }, 1425 }, 1426 { 1427 Path: "/more", 1428 Route: "default/more-returns", 1429 }, 1430 }, 1431 }, 1432 }, 1433 VirtualServerRoutes: []*conf_v1.VirtualServerRoute{ 1434 { 1435 ObjectMeta: meta_v1.ObjectMeta{ 1436 Name: "more-returns", 1437 Namespace: "default", 1438 }, 1439 Spec: conf_v1.VirtualServerRouteSpec{ 1440 Host: "example.com", 1441 Subroutes: []conf_v1.Route{ 1442 { 1443 Path: "/more/return", 1444 Action: &conf_v1.Action{ 1445 Return: &conf_v1.ActionReturn{ 1446 Body: "hello 5", 1447 }, 1448 }, 1449 }, 1450 { 1451 Path: "/more/splits-with-return", 1452 Splits: []conf_v1.Split{ 1453 { 1454 Weight: 90, 1455 Action: &conf_v1.Action{ 1456 Return: &conf_v1.ActionReturn{ 1457 Body: "hello 6", 1458 }, 1459 }, 1460 }, 1461 { 1462 Weight: 10, 1463 Action: &conf_v1.Action{ 1464 Return: &conf_v1.ActionReturn{ 1465 Body: "hello 7", 1466 }, 1467 }, 1468 }, 1469 }, 1470 }, 1471 { 1472 Path: "/more/matches-with-return", 1473 Matches: []conf_v1.Match{ 1474 { 1475 Conditions: []conf_v1.Condition{ 1476 { 1477 Header: "x-version", 1478 Value: "v2", 1479 }, 1480 }, 1481 Action: &conf_v1.Action{ 1482 Return: &conf_v1.ActionReturn{ 1483 Body: "hello 8", 1484 }, 1485 }, 1486 }, 1487 }, 1488 Action: &conf_v1.Action{ 1489 Return: &conf_v1.ActionReturn{ 1490 Body: "hello 9", 1491 }, 1492 }, 1493 }, 1494 }, 1495 }, 1496 }, 1497 }, 1498 } 1499 1500 baseCfgParams := ConfigParams{} 1501 1502 expected := version2.VirtualServerConfig{ 1503 Maps: []version2.Map{ 1504 { 1505 Source: "$http_x_version", 1506 Variable: "$vs_default_returns_matches_0_match_0_cond_0", 1507 Parameters: []version2.Parameter{ 1508 { 1509 Value: `"v2"`, 1510 Result: "1", 1511 }, 1512 { 1513 Value: "default", 1514 Result: "0", 1515 }, 1516 }, 1517 }, 1518 { 1519 Source: "$vs_default_returns_matches_0_match_0_cond_0", 1520 Variable: "$vs_default_returns_matches_0", 1521 Parameters: []version2.Parameter{ 1522 { 1523 Value: "~^1", 1524 Result: "/internal_location_matches_0_match_0", 1525 }, 1526 { 1527 Value: "default", 1528 Result: "/internal_location_matches_0_default", 1529 }, 1530 }, 1531 }, 1532 { 1533 Source: "$http_x_version", 1534 Variable: "$vs_default_returns_matches_1_match_0_cond_0", 1535 Parameters: []version2.Parameter{ 1536 { 1537 Value: `"v2"`, 1538 Result: "1", 1539 }, 1540 { 1541 Value: "default", 1542 Result: "0", 1543 }, 1544 }, 1545 }, 1546 { 1547 Source: "$vs_default_returns_matches_1_match_0_cond_0", 1548 Variable: "$vs_default_returns_matches_1", 1549 Parameters: []version2.Parameter{ 1550 { 1551 Value: "~^1", 1552 Result: "/internal_location_matches_1_match_0", 1553 }, 1554 { 1555 Value: "default", 1556 Result: "/internal_location_matches_1_default", 1557 }, 1558 }, 1559 }, 1560 }, 1561 SplitClients: []version2.SplitClient{ 1562 { 1563 Source: "$request_id", 1564 Variable: "$vs_default_returns_splits_0", 1565 Distributions: []version2.Distribution{ 1566 { 1567 Weight: "90%", 1568 Value: "/internal_location_splits_0_split_0", 1569 }, 1570 { 1571 Weight: "10%", 1572 Value: "/internal_location_splits_0_split_1", 1573 }, 1574 }, 1575 }, 1576 { 1577 Source: "$request_id", 1578 Variable: "$vs_default_returns_splits_1", 1579 Distributions: []version2.Distribution{ 1580 { 1581 Weight: "90%", 1582 Value: "/internal_location_splits_1_split_0", 1583 }, 1584 { 1585 Weight: "10%", 1586 Value: "/internal_location_splits_1_split_1", 1587 }, 1588 }, 1589 }, 1590 }, 1591 HTTPSnippets: []string{}, 1592 LimitReqZones: []version2.LimitReqZone{}, 1593 Server: version2.Server{ 1594 ServerName: "example.com", 1595 StatusZone: "example.com", 1596 VSNamespace: "default", 1597 VSName: "returns", 1598 InternalRedirectLocations: []version2.InternalRedirectLocation{ 1599 { 1600 Path: "/splits-with-return", 1601 Destination: "$vs_default_returns_splits_0", 1602 }, 1603 { 1604 Path: "/matches-with-return", 1605 Destination: "$vs_default_returns_matches_0", 1606 }, 1607 { 1608 Path: "/more/splits-with-return", 1609 Destination: "$vs_default_returns_splits_1", 1610 }, 1611 { 1612 Path: "/more/matches-with-return", 1613 Destination: "$vs_default_returns_matches_1", 1614 }, 1615 }, 1616 ReturnLocations: []version2.ReturnLocation{ 1617 { 1618 Name: "@return_0", 1619 DefaultType: "text/plain", 1620 Return: version2.Return{ 1621 Code: 0, 1622 Text: "hello 0", 1623 }, 1624 }, 1625 { 1626 Name: "@return_1", 1627 DefaultType: "text/plain", 1628 Return: version2.Return{ 1629 Code: 0, 1630 Text: "hello 1", 1631 }, 1632 }, 1633 { 1634 Name: "@return_2", 1635 DefaultType: "text/plain", 1636 Return: version2.Return{ 1637 Code: 0, 1638 Text: "hello 2", 1639 }, 1640 }, 1641 { 1642 Name: "@return_3", 1643 DefaultType: "text/plain", 1644 Return: version2.Return{ 1645 Code: 0, 1646 Text: "hello 3", 1647 }, 1648 }, 1649 { 1650 Name: "@return_4", 1651 DefaultType: "text/plain", 1652 Return: version2.Return{ 1653 Code: 0, 1654 Text: "hello 4", 1655 }, 1656 }, 1657 { 1658 Name: "@return_5", 1659 DefaultType: "text/plain", 1660 Return: version2.Return{ 1661 Code: 0, 1662 Text: "hello 5", 1663 }, 1664 }, 1665 { 1666 Name: "@return_6", 1667 DefaultType: "text/plain", 1668 Return: version2.Return{ 1669 Code: 0, 1670 Text: "hello 6", 1671 }, 1672 }, 1673 { 1674 Name: "@return_7", 1675 DefaultType: "text/plain", 1676 Return: version2.Return{ 1677 Code: 0, 1678 Text: "hello 7", 1679 }, 1680 }, 1681 { 1682 Name: "@return_8", 1683 DefaultType: "text/plain", 1684 Return: version2.Return{ 1685 Code: 0, 1686 Text: "hello 8", 1687 }, 1688 }, 1689 { 1690 Name: "@return_9", 1691 DefaultType: "text/plain", 1692 Return: version2.Return{ 1693 Code: 0, 1694 Text: "hello 9", 1695 }, 1696 }, 1697 }, 1698 Locations: []version2.Location{ 1699 { 1700 Path: "/return", 1701 ProxyInterceptErrors: true, 1702 ErrorPages: []version2.ErrorPage{ 1703 { 1704 Name: "@return_0", 1705 Codes: "418", 1706 ResponseCode: 200, 1707 }, 1708 }, 1709 InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock", 1710 }, 1711 { 1712 Path: "/internal_location_splits_0_split_0", 1713 ProxyInterceptErrors: true, 1714 ErrorPages: []version2.ErrorPage{ 1715 { 1716 Name: "@return_1", 1717 Codes: "418", 1718 ResponseCode: 200, 1719 }, 1720 }, 1721 InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock", 1722 }, 1723 { 1724 Path: "/internal_location_splits_0_split_1", 1725 ProxyInterceptErrors: true, 1726 ErrorPages: []version2.ErrorPage{ 1727 { 1728 Name: "@return_2", 1729 Codes: "418", 1730 ResponseCode: 200, 1731 }, 1732 }, 1733 InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock", 1734 }, 1735 { 1736 Path: "/internal_location_matches_0_match_0", 1737 ProxyInterceptErrors: true, 1738 ErrorPages: []version2.ErrorPage{ 1739 { 1740 Name: "@return_3", 1741 Codes: "418", 1742 ResponseCode: 200, 1743 }, 1744 }, 1745 InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock", 1746 }, 1747 { 1748 Path: "/internal_location_matches_0_default", 1749 ProxyInterceptErrors: true, 1750 ErrorPages: []version2.ErrorPage{ 1751 { 1752 Name: "@return_4", 1753 Codes: "418", 1754 ResponseCode: 200, 1755 }, 1756 }, 1757 InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock", 1758 }, 1759 { 1760 Path: "/more/return", 1761 ProxyInterceptErrors: true, 1762 ErrorPages: []version2.ErrorPage{ 1763 { 1764 Name: "@return_5", 1765 Codes: "418", 1766 ResponseCode: 200, 1767 }, 1768 }, 1769 InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock", 1770 }, 1771 { 1772 Path: "/internal_location_splits_1_split_0", 1773 ProxyInterceptErrors: true, 1774 ErrorPages: []version2.ErrorPage{ 1775 { 1776 Name: "@return_6", 1777 Codes: "418", 1778 ResponseCode: 200, 1779 }, 1780 }, 1781 InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock", 1782 }, 1783 { 1784 Path: "/internal_location_splits_1_split_1", 1785 ProxyInterceptErrors: true, 1786 ErrorPages: []version2.ErrorPage{ 1787 { 1788 Name: "@return_7", 1789 Codes: "418", 1790 ResponseCode: 200, 1791 }, 1792 }, 1793 InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock", 1794 }, 1795 { 1796 Path: "/internal_location_matches_1_match_0", 1797 ProxyInterceptErrors: true, 1798 ErrorPages: []version2.ErrorPage{ 1799 { 1800 Name: "@return_8", 1801 Codes: "418", 1802 ResponseCode: 200, 1803 }, 1804 }, 1805 InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock", 1806 }, 1807 { 1808 Path: "/internal_location_matches_1_default", 1809 ProxyInterceptErrors: true, 1810 ErrorPages: []version2.ErrorPage{ 1811 { 1812 Name: "@return_9", 1813 Codes: "418", 1814 ResponseCode: 200, 1815 }, 1816 }, 1817 InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock", 1818 }, 1819 }, 1820 }, 1821 } 1822 1823 isPlus := false 1824 isResolverConfigured := false 1825 vsc := newVirtualServerConfigurator(&baseCfgParams, isPlus, isResolverConfigured, &StaticConfigParams{}) 1826 1827 result, warnings := vsc.GenerateVirtualServerConfig(&virtualServerEx, nil) 1828 if !reflect.DeepEqual(result, expected) { 1829 t.Errorf("GenerateVirtualServerConfig returned \n%+v but expected \n%+v", result, expected) 1830 } 1831 1832 if len(warnings) != 0 { 1833 t.Errorf("GenerateVirtualServerConfig returned warnings: %v", vsc.warnings) 1834 } 1835 } 1836 1837 func TestGeneratePolicies(t *testing.T) { 1838 ownerDetails := policyOwnerDetails{ 1839 owner: nil, // nil is OK for the unit test 1840 ownerNamespace: "default", 1841 vsNamespace: "default", 1842 vsName: "test", 1843 } 1844 ingressMTLSCertPath := "/etc/nginx/secrets/default-ingress-mtls-secret" 1845 policyOpts := policyOptions{ 1846 tls: true, 1847 secretRefs: map[string]*secrets.SecretReference{ 1848 "default/ingress-mtls-secret": { 1849 Secret: &api_v1.Secret{ 1850 Type: secrets.SecretTypeCA, 1851 }, 1852 Path: ingressMTLSCertPath, 1853 }, 1854 "default/egress-mtls-secret": { 1855 Secret: &api_v1.Secret{ 1856 Type: api_v1.SecretTypeTLS, 1857 }, 1858 Path: "/etc/nginx/secrets/default-egress-mtls-secret", 1859 }, 1860 "default/egress-trusted-ca-secret": { 1861 Secret: &api_v1.Secret{ 1862 Type: secrets.SecretTypeCA, 1863 }, 1864 Path: "/etc/nginx/secrets/default-egress-trusted-ca-secret", 1865 }, 1866 "default/jwt-secret": { 1867 Secret: &api_v1.Secret{ 1868 Type: secrets.SecretTypeJWK, 1869 }, 1870 Path: "/etc/nginx/secrets/default-jwt-secret", 1871 }, 1872 "default/oidc-secret": { 1873 Secret: &api_v1.Secret{ 1874 Type: secrets.SecretTypeOIDC, 1875 Data: map[string][]byte{ 1876 "client-secret": []byte("super_secret_123"), 1877 }, 1878 }, 1879 }, 1880 }, 1881 apResources: map[string]string{ 1882 "default/logconf": "/etc/nginx/waf/nac-logconfs/default-logconf", 1883 "default/dataguard-alarm": "/etc/nginx/waf/nac-policies/default-dataguard-alarm", 1884 }, 1885 } 1886 1887 tests := []struct { 1888 policyRefs []conf_v1.PolicyReference 1889 policies map[string]*conf_v1.Policy 1890 policyOpts policyOptions 1891 context string 1892 expected policiesCfg 1893 msg string 1894 }{ 1895 { 1896 policyRefs: []conf_v1.PolicyReference{ 1897 { 1898 Name: "allow-policy", 1899 Namespace: "default", 1900 }, 1901 }, 1902 policies: map[string]*conf_v1.Policy{ 1903 "default/allow-policy": { 1904 Spec: conf_v1.PolicySpec{ 1905 AccessControl: &conf_v1.AccessControl{ 1906 Allow: []string{"127.0.0.1"}, 1907 }, 1908 }, 1909 }, 1910 }, 1911 expected: policiesCfg{ 1912 Allow: []string{"127.0.0.1"}, 1913 }, 1914 msg: "explicit reference", 1915 }, 1916 { 1917 policyRefs: []conf_v1.PolicyReference{ 1918 { 1919 Name: "allow-policy", 1920 }, 1921 }, 1922 policies: map[string]*conf_v1.Policy{ 1923 "default/allow-policy": { 1924 Spec: conf_v1.PolicySpec{ 1925 AccessControl: &conf_v1.AccessControl{ 1926 Allow: []string{"127.0.0.1"}, 1927 }, 1928 }, 1929 }, 1930 }, 1931 expected: policiesCfg{ 1932 Allow: []string{"127.0.0.1"}, 1933 }, 1934 msg: "implicit reference", 1935 }, 1936 { 1937 policyRefs: []conf_v1.PolicyReference{ 1938 { 1939 Name: "allow-policy-1", 1940 }, 1941 { 1942 Name: "allow-policy-2", 1943 }, 1944 }, 1945 policies: map[string]*conf_v1.Policy{ 1946 "default/allow-policy-1": { 1947 Spec: conf_v1.PolicySpec{ 1948 AccessControl: &conf_v1.AccessControl{ 1949 Allow: []string{"127.0.0.1"}, 1950 }, 1951 }, 1952 }, 1953 "default/allow-policy-2": { 1954 Spec: conf_v1.PolicySpec{ 1955 AccessControl: &conf_v1.AccessControl{ 1956 Allow: []string{"127.0.0.2"}, 1957 }, 1958 }, 1959 }, 1960 }, 1961 expected: policiesCfg{ 1962 Allow: []string{"127.0.0.1", "127.0.0.2"}, 1963 }, 1964 msg: "merging", 1965 }, 1966 { 1967 policyRefs: []conf_v1.PolicyReference{ 1968 { 1969 Name: "rateLimit-policy", 1970 Namespace: "default", 1971 }, 1972 }, 1973 policies: map[string]*conf_v1.Policy{ 1974 "default/rateLimit-policy": { 1975 Spec: conf_v1.PolicySpec{ 1976 RateLimit: &conf_v1.RateLimit{ 1977 Key: "test", 1978 ZoneSize: "10M", 1979 Rate: "10r/s", 1980 LogLevel: "notice", 1981 }, 1982 }, 1983 }, 1984 }, 1985 expected: policiesCfg{ 1986 LimitReqZones: []version2.LimitReqZone{ 1987 { 1988 Key: "test", 1989 ZoneSize: "10M", 1990 Rate: "10r/s", 1991 ZoneName: "pol_rl_default_rateLimit-policy_default_test", 1992 }, 1993 }, 1994 LimitReqOptions: version2.LimitReqOptions{ 1995 LogLevel: "notice", 1996 RejectCode: 503, 1997 }, 1998 LimitReqs: []version2.LimitReq{ 1999 { 2000 ZoneName: "pol_rl_default_rateLimit-policy_default_test", 2001 }, 2002 }, 2003 }, 2004 msg: "rate limit reference", 2005 }, 2006 { 2007 policyRefs: []conf_v1.PolicyReference{ 2008 { 2009 Name: "rateLimit-policy", 2010 Namespace: "default", 2011 }, 2012 { 2013 Name: "rateLimit-policy2", 2014 Namespace: "default", 2015 }, 2016 }, 2017 policies: map[string]*conf_v1.Policy{ 2018 "default/rateLimit-policy": { 2019 Spec: conf_v1.PolicySpec{ 2020 RateLimit: &conf_v1.RateLimit{ 2021 Key: "test", 2022 ZoneSize: "10M", 2023 Rate: "10r/s", 2024 }, 2025 }, 2026 }, 2027 "default/rateLimit-policy2": { 2028 Spec: conf_v1.PolicySpec{ 2029 RateLimit: &conf_v1.RateLimit{ 2030 Key: "test2", 2031 ZoneSize: "20M", 2032 Rate: "20r/s", 2033 }, 2034 }, 2035 }, 2036 }, 2037 expected: policiesCfg{ 2038 LimitReqZones: []version2.LimitReqZone{ 2039 { 2040 Key: "test", 2041 ZoneSize: "10M", 2042 Rate: "10r/s", 2043 ZoneName: "pol_rl_default_rateLimit-policy_default_test", 2044 }, 2045 { 2046 Key: "test2", 2047 ZoneSize: "20M", 2048 Rate: "20r/s", 2049 ZoneName: "pol_rl_default_rateLimit-policy2_default_test", 2050 }, 2051 }, 2052 LimitReqOptions: version2.LimitReqOptions{ 2053 LogLevel: "error", 2054 RejectCode: 503, 2055 }, 2056 LimitReqs: []version2.LimitReq{ 2057 { 2058 ZoneName: "pol_rl_default_rateLimit-policy_default_test", 2059 }, 2060 { 2061 ZoneName: "pol_rl_default_rateLimit-policy2_default_test", 2062 }, 2063 }, 2064 }, 2065 msg: "multi rate limit reference", 2066 }, 2067 { 2068 policyRefs: []conf_v1.PolicyReference{ 2069 { 2070 Name: "jwt-policy", 2071 Namespace: "default", 2072 }, 2073 }, 2074 policies: map[string]*conf_v1.Policy{ 2075 "default/jwt-policy": { 2076 ObjectMeta: meta_v1.ObjectMeta{ 2077 Name: "jwt-policy", 2078 Namespace: "default", 2079 }, 2080 Spec: conf_v1.PolicySpec{ 2081 JWTAuth: &conf_v1.JWTAuth{ 2082 Realm: "My Test API", 2083 Secret: "jwt-secret", 2084 }, 2085 }, 2086 }, 2087 }, 2088 expected: policiesCfg{ 2089 JWTAuth: &version2.JWTAuth{ 2090 Secret: "/etc/nginx/secrets/default-jwt-secret", 2091 Realm: "My Test API", 2092 }, 2093 }, 2094 msg: "jwt reference", 2095 }, 2096 { 2097 policyRefs: []conf_v1.PolicyReference{ 2098 { 2099 Name: "ingress-mtls-policy", 2100 Namespace: "default", 2101 }, 2102 }, 2103 policies: map[string]*conf_v1.Policy{ 2104 "default/ingress-mtls-policy": { 2105 ObjectMeta: meta_v1.ObjectMeta{ 2106 Name: "ingress-mtls-policy", 2107 Namespace: "default", 2108 }, 2109 Spec: conf_v1.PolicySpec{ 2110 IngressMTLS: &conf_v1.IngressMTLS{ 2111 ClientCertSecret: "ingress-mtls-secret", 2112 VerifyClient: "off", 2113 }, 2114 }, 2115 }, 2116 }, 2117 context: "spec", 2118 expected: policiesCfg{ 2119 IngressMTLS: &version2.IngressMTLS{ 2120 ClientCert: ingressMTLSCertPath, 2121 VerifyClient: "off", 2122 VerifyDepth: 1, 2123 }, 2124 }, 2125 msg: "ingressMTLS reference", 2126 }, 2127 { 2128 policyRefs: []conf_v1.PolicyReference{ 2129 { 2130 Name: "egress-mtls-policy", 2131 Namespace: "default", 2132 }, 2133 }, 2134 policies: map[string]*conf_v1.Policy{ 2135 "default/egress-mtls-policy": { 2136 Spec: conf_v1.PolicySpec{ 2137 EgressMTLS: &conf_v1.EgressMTLS{ 2138 TLSSecret: "egress-mtls-secret", 2139 ServerName: true, 2140 SessionReuse: createPointerFromBool(false), 2141 TrustedCertSecret: "egress-trusted-ca-secret", 2142 }, 2143 }, 2144 }, 2145 }, 2146 context: "route", 2147 expected: policiesCfg{ 2148 EgressMTLS: &version2.EgressMTLS{ 2149 Certificate: "/etc/nginx/secrets/default-egress-mtls-secret", 2150 CertificateKey: "/etc/nginx/secrets/default-egress-mtls-secret", 2151 Ciphers: "DEFAULT", 2152 Protocols: "TLSv1 TLSv1.1 TLSv1.2", 2153 ServerName: true, 2154 SessionReuse: false, 2155 VerifyDepth: 1, 2156 VerifyServer: false, 2157 TrustedCert: "/etc/nginx/secrets/default-egress-trusted-ca-secret", 2158 SSLName: "$proxy_host", 2159 }, 2160 }, 2161 msg: "egressMTLS reference", 2162 }, 2163 { 2164 policyRefs: []conf_v1.PolicyReference{ 2165 { 2166 Name: "oidc-policy", 2167 Namespace: "default", 2168 }, 2169 }, 2170 policies: map[string]*conf_v1.Policy{ 2171 "default/oidc-policy": { 2172 ObjectMeta: meta_v1.ObjectMeta{ 2173 Name: "oidc-policy", 2174 Namespace: "default", 2175 }, 2176 Spec: conf_v1.PolicySpec{ 2177 OIDC: &conf_v1.OIDC{ 2178 AuthEndpoint: "http://example.com/auth", 2179 TokenEndpoint: "http://example.com/token", 2180 JWKSURI: "http://example.com/jwks", 2181 ClientID: "client-id", 2182 ClientSecret: "oidc-secret", 2183 Scope: "scope", 2184 RedirectURI: "/redirect", 2185 }, 2186 }, 2187 }, 2188 }, 2189 expected: policiesCfg{ 2190 OIDC: true, 2191 }, 2192 msg: "oidc reference", 2193 }, 2194 { 2195 policyRefs: []conf_v1.PolicyReference{ 2196 { 2197 Name: "waf-policy", 2198 Namespace: "default", 2199 }, 2200 }, 2201 policies: map[string]*conf_v1.Policy{ 2202 "default/waf-policy": { 2203 Spec: conf_v1.PolicySpec{ 2204 WAF: &conf_v1.WAF{ 2205 Enable: true, 2206 ApPolicy: "default/dataguard-alarm", 2207 SecurityLog: &conf_v1.SecurityLog{ 2208 Enable: true, 2209 ApLogConf: "default/logconf", 2210 LogDest: "syslog:server=127.0.0.1:514", 2211 }, 2212 }, 2213 }, 2214 }, 2215 }, 2216 context: "route", 2217 expected: policiesCfg{ 2218 WAF: &version2.WAF{ 2219 Enable: "on", 2220 ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm", 2221 ApSecurityLogEnable: true, 2222 ApLogConf: "/etc/nginx/waf/nac-logconfs/default-logconf syslog:server=127.0.0.1:514", 2223 }, 2224 }, 2225 msg: "WAF reference", 2226 }, 2227 } 2228 2229 vsc := newVirtualServerConfigurator(&ConfigParams{}, false, false, &StaticConfigParams{}) 2230 2231 for _, test := range tests { 2232 result := vsc.generatePolicies(ownerDetails, test.policyRefs, test.policies, test.context, policyOpts) 2233 if diff := cmp.Diff(test.expected, result); diff != "" { 2234 t.Errorf("generatePolicies() '%v' mismatch (-want +got):\n%s", test.msg, diff) 2235 } 2236 if len(vsc.warnings) > 0 { 2237 t.Errorf("generatePolicies() returned unexpected warnings %v for the case of %s", vsc.warnings, test.msg) 2238 } 2239 } 2240 } 2241 2242 func TestGeneratePoliciesFails(t *testing.T) { 2243 ownerDetails := policyOwnerDetails{ 2244 owner: nil, // nil is OK for the unit test 2245 ownerNamespace: "default", 2246 vsNamespace: "default", 2247 vsName: "test", 2248 } 2249 2250 dryRunOverride := true 2251 rejectCodeOverride := 505 2252 2253 tests := []struct { 2254 policyRefs []conf_v1.PolicyReference 2255 policies map[string]*conf_v1.Policy 2256 policyOpts policyOptions 2257 trustedCAFileName string 2258 context string 2259 oidcPolCfg *oidcPolicyCfg 2260 expected policiesCfg 2261 expectedWarnings Warnings 2262 expectedOidc *oidcPolicyCfg 2263 msg string 2264 }{ 2265 { 2266 policyRefs: []conf_v1.PolicyReference{ 2267 { 2268 Name: "allow-policy", 2269 Namespace: "default", 2270 }, 2271 }, 2272 policies: map[string]*conf_v1.Policy{}, 2273 policyOpts: policyOptions{}, 2274 expected: policiesCfg{ 2275 ErrorReturn: &version2.Return{ 2276 Code: 500, 2277 }, 2278 }, 2279 expectedWarnings: Warnings{ 2280 nil: { 2281 "Policy default/allow-policy is missing or invalid", 2282 }, 2283 }, 2284 expectedOidc: &oidcPolicyCfg{}, 2285 msg: "missing policy", 2286 }, 2287 { 2288 policyRefs: []conf_v1.PolicyReference{ 2289 { 2290 Name: "allow-policy", 2291 }, 2292 { 2293 Name: "deny-policy", 2294 }, 2295 }, 2296 policies: map[string]*conf_v1.Policy{ 2297 "default/allow-policy": { 2298 Spec: conf_v1.PolicySpec{ 2299 AccessControl: &conf_v1.AccessControl{ 2300 Allow: []string{"127.0.0.1"}, 2301 }, 2302 }, 2303 }, 2304 "default/deny-policy": { 2305 Spec: conf_v1.PolicySpec{ 2306 AccessControl: &conf_v1.AccessControl{ 2307 Deny: []string{"127.0.0.2"}, 2308 }, 2309 }, 2310 }, 2311 }, 2312 policyOpts: policyOptions{}, 2313 expected: policiesCfg{ 2314 Allow: []string{"127.0.0.1"}, 2315 Deny: []string{"127.0.0.2"}, 2316 }, 2317 expectedWarnings: Warnings{ 2318 nil: { 2319 "AccessControl policy (or policies) with deny rules is overridden by policy (or policies) with allow rules", 2320 }, 2321 }, 2322 expectedOidc: &oidcPolicyCfg{}, 2323 msg: "conflicting policies", 2324 }, 2325 { 2326 policyRefs: []conf_v1.PolicyReference{ 2327 { 2328 Name: "rateLimit-policy", 2329 Namespace: "default", 2330 }, 2331 { 2332 Name: "rateLimit-policy2", 2333 Namespace: "default", 2334 }, 2335 }, 2336 policies: map[string]*conf_v1.Policy{ 2337 "default/rateLimit-policy": { 2338 Spec: conf_v1.PolicySpec{ 2339 RateLimit: &conf_v1.RateLimit{ 2340 Key: "test", 2341 ZoneSize: "10M", 2342 Rate: "10r/s", 2343 }, 2344 }, 2345 }, 2346 "default/rateLimit-policy2": { 2347 Spec: conf_v1.PolicySpec{ 2348 RateLimit: &conf_v1.RateLimit{ 2349 Key: "test2", 2350 ZoneSize: "20M", 2351 Rate: "20r/s", 2352 DryRun: &dryRunOverride, 2353 LogLevel: "info", 2354 RejectCode: &rejectCodeOverride, 2355 }, 2356 }, 2357 }, 2358 }, 2359 policyOpts: policyOptions{}, 2360 expected: policiesCfg{ 2361 LimitReqZones: []version2.LimitReqZone{ 2362 { 2363 Key: "test", 2364 ZoneSize: "10M", 2365 Rate: "10r/s", 2366 ZoneName: "pol_rl_default_rateLimit-policy_default_test", 2367 }, 2368 { 2369 Key: "test2", 2370 ZoneSize: "20M", 2371 Rate: "20r/s", 2372 ZoneName: "pol_rl_default_rateLimit-policy2_default_test", 2373 }, 2374 }, 2375 LimitReqOptions: version2.LimitReqOptions{ 2376 LogLevel: "error", 2377 RejectCode: 503, 2378 }, 2379 LimitReqs: []version2.LimitReq{ 2380 { 2381 ZoneName: "pol_rl_default_rateLimit-policy_default_test", 2382 }, 2383 { 2384 ZoneName: "pol_rl_default_rateLimit-policy2_default_test", 2385 }, 2386 }, 2387 }, 2388 expectedWarnings: Warnings{ 2389 nil: { 2390 `RateLimit policy default/rateLimit-policy2 with limit request option dryRun='true' is overridden to dryRun='false' by the first policy reference in this context`, 2391 `RateLimit policy default/rateLimit-policy2 with limit request option logLevel='info' is overridden to logLevel='error' by the first policy reference in this context`, 2392 `RateLimit policy default/rateLimit-policy2 with limit request option rejectCode='505' is overridden to rejectCode='503' by the first policy reference in this context`, 2393 }, 2394 }, 2395 expectedOidc: &oidcPolicyCfg{}, 2396 msg: "rate limit policy limit request option override", 2397 }, 2398 { 2399 policyRefs: []conf_v1.PolicyReference{ 2400 { 2401 Name: "jwt-policy", 2402 Namespace: "default", 2403 }, 2404 }, 2405 policies: map[string]*conf_v1.Policy{ 2406 "default/jwt-policy": { 2407 ObjectMeta: meta_v1.ObjectMeta{ 2408 Name: "jwt-policy", 2409 Namespace: "default", 2410 }, 2411 Spec: conf_v1.PolicySpec{ 2412 JWTAuth: &conf_v1.JWTAuth{ 2413 Realm: "test", 2414 Secret: "jwt-secret", 2415 }, 2416 }, 2417 }, 2418 }, 2419 policyOpts: policyOptions{ 2420 secretRefs: map[string]*secrets.SecretReference{ 2421 "default/jwt-secret": { 2422 Secret: &api_v1.Secret{ 2423 Type: secrets.SecretTypeJWK, 2424 }, 2425 Error: errors.New("secret is invalid"), 2426 }, 2427 }, 2428 }, 2429 expected: policiesCfg{ 2430 ErrorReturn: &version2.Return{ 2431 Code: 500, 2432 }, 2433 }, 2434 expectedWarnings: Warnings{ 2435 nil: { 2436 `JWT policy default/jwt-policy references an invalid secret default/jwt-secret: secret is invalid`, 2437 }, 2438 }, 2439 expectedOidc: &oidcPolicyCfg{}, 2440 msg: "jwt reference missing secret", 2441 }, 2442 { 2443 policyRefs: []conf_v1.PolicyReference{ 2444 { 2445 Name: "jwt-policy", 2446 Namespace: "default", 2447 }, 2448 }, 2449 policies: map[string]*conf_v1.Policy{ 2450 "default/jwt-policy": { 2451 ObjectMeta: meta_v1.ObjectMeta{ 2452 Name: "jwt-policy", 2453 Namespace: "default", 2454 }, 2455 Spec: conf_v1.PolicySpec{ 2456 JWTAuth: &conf_v1.JWTAuth{ 2457 Realm: "test", 2458 Secret: "jwt-secret", 2459 }, 2460 }, 2461 }, 2462 }, 2463 policyOpts: policyOptions{ 2464 secretRefs: map[string]*secrets.SecretReference{ 2465 "default/jwt-secret": { 2466 Secret: &api_v1.Secret{ 2467 Type: secrets.SecretTypeCA, 2468 }, 2469 }, 2470 }, 2471 }, 2472 expected: policiesCfg{ 2473 ErrorReturn: &version2.Return{ 2474 Code: 500, 2475 }, 2476 }, 2477 expectedWarnings: Warnings{ 2478 nil: { 2479 `JWT policy default/jwt-policy references a secret default/jwt-secret of a wrong type 'nginx.org/ca', must be 'nginx.org/jwk'`, 2480 }, 2481 }, 2482 expectedOidc: &oidcPolicyCfg{}, 2483 msg: "jwt references wrong secret type", 2484 }, 2485 { 2486 policyRefs: []conf_v1.PolicyReference{ 2487 { 2488 Name: "jwt-policy", 2489 Namespace: "default", 2490 }, 2491 { 2492 Name: "jwt-policy2", 2493 Namespace: "default", 2494 }, 2495 }, 2496 policies: map[string]*conf_v1.Policy{ 2497 "default/jwt-policy": { 2498 ObjectMeta: meta_v1.ObjectMeta{ 2499 Name: "jwt-policy", 2500 Namespace: "default", 2501 }, 2502 Spec: conf_v1.PolicySpec{ 2503 JWTAuth: &conf_v1.JWTAuth{ 2504 Realm: "test", 2505 Secret: "jwt-secret", 2506 }, 2507 }, 2508 }, 2509 "default/jwt-policy2": { 2510 ObjectMeta: meta_v1.ObjectMeta{ 2511 Name: "jwt-policy2", 2512 Namespace: "default", 2513 }, 2514 Spec: conf_v1.PolicySpec{ 2515 JWTAuth: &conf_v1.JWTAuth{ 2516 Realm: "test", 2517 Secret: "jwt-secret2", 2518 }, 2519 }, 2520 }, 2521 }, 2522 policyOpts: policyOptions{ 2523 secretRefs: map[string]*secrets.SecretReference{ 2524 "default/jwt-secret": { 2525 Secret: &api_v1.Secret{ 2526 Type: secrets.SecretTypeJWK, 2527 }, 2528 Path: "/etc/nginx/secrets/default-jwt-secret", 2529 }, 2530 "default/jwt-secret2": { 2531 Secret: &api_v1.Secret{ 2532 Type: secrets.SecretTypeJWK, 2533 }, 2534 Path: "/etc/nginx/secrets/default-jwt-secret2", 2535 }, 2536 }, 2537 }, 2538 expected: policiesCfg{ 2539 JWTAuth: &version2.JWTAuth{ 2540 Secret: "/etc/nginx/secrets/default-jwt-secret", 2541 Realm: "test", 2542 }, 2543 }, 2544 expectedWarnings: Warnings{ 2545 nil: { 2546 `Multiple jwt policies in the same context is not valid. JWT policy default/jwt-policy2 will be ignored`, 2547 }, 2548 }, 2549 expectedOidc: &oidcPolicyCfg{}, 2550 msg: "multi jwt reference", 2551 }, 2552 { 2553 policyRefs: []conf_v1.PolicyReference{ 2554 { 2555 Name: "ingress-mtls-policy", 2556 Namespace: "default", 2557 }, 2558 }, 2559 policies: map[string]*conf_v1.Policy{ 2560 "default/ingress-mtls-policy": { 2561 ObjectMeta: meta_v1.ObjectMeta{ 2562 Name: "ingress-mtls-policy", 2563 Namespace: "default", 2564 }, 2565 Spec: conf_v1.PolicySpec{ 2566 IngressMTLS: &conf_v1.IngressMTLS{ 2567 ClientCertSecret: "ingress-mtls-secret", 2568 }, 2569 }, 2570 }, 2571 }, 2572 policyOpts: policyOptions{ 2573 tls: true, 2574 secretRefs: map[string]*secrets.SecretReference{ 2575 "default/ingress-mtls-secret": { 2576 Error: errors.New("secret is invalid"), 2577 }, 2578 }, 2579 }, 2580 context: "spec", 2581 expected: policiesCfg{ 2582 ErrorReturn: &version2.Return{ 2583 Code: 500, 2584 }, 2585 }, 2586 expectedWarnings: Warnings{ 2587 nil: { 2588 `IngressMTLS policy "default/ingress-mtls-policy" references an invalid secret default/ingress-mtls-secret: secret is invalid`, 2589 }, 2590 }, 2591 expectedOidc: &oidcPolicyCfg{}, 2592 msg: "ingress mtls reference an invalid secret", 2593 }, 2594 { 2595 policyRefs: []conf_v1.PolicyReference{ 2596 { 2597 Name: "ingress-mtls-policy", 2598 Namespace: "default", 2599 }, 2600 }, 2601 policies: map[string]*conf_v1.Policy{ 2602 "default/ingress-mtls-policy": { 2603 ObjectMeta: meta_v1.ObjectMeta{ 2604 Name: "ingress-mtls-policy", 2605 Namespace: "default", 2606 }, 2607 Spec: conf_v1.PolicySpec{ 2608 IngressMTLS: &conf_v1.IngressMTLS{ 2609 ClientCertSecret: "ingress-mtls-secret", 2610 }, 2611 }, 2612 }, 2613 }, 2614 policyOpts: policyOptions{ 2615 tls: true, 2616 secretRefs: map[string]*secrets.SecretReference{ 2617 "default/ingress-mtls-secret": { 2618 Secret: &api_v1.Secret{ 2619 Type: api_v1.SecretTypeTLS, 2620 }, 2621 }, 2622 }, 2623 }, 2624 context: "spec", 2625 expected: policiesCfg{ 2626 ErrorReturn: &version2.Return{ 2627 Code: 500, 2628 }, 2629 }, 2630 expectedWarnings: Warnings{ 2631 nil: { 2632 `IngressMTLS policy default/ingress-mtls-policy references a secret default/ingress-mtls-secret of a wrong type 'kubernetes.io/tls', must be 'nginx.org/ca'`, 2633 }, 2634 }, 2635 expectedOidc: &oidcPolicyCfg{}, 2636 msg: "ingress mtls references wrong secret type", 2637 }, 2638 { 2639 policyRefs: []conf_v1.PolicyReference{ 2640 { 2641 Name: "ingress-mtls-policy", 2642 Namespace: "default", 2643 }, 2644 { 2645 Name: "ingress-mtls-policy2", 2646 Namespace: "default", 2647 }, 2648 }, 2649 policies: map[string]*conf_v1.Policy{ 2650 "default/ingress-mtls-policy": { 2651 ObjectMeta: meta_v1.ObjectMeta{ 2652 Name: "ingress-mtls-policy", 2653 Namespace: "default", 2654 }, 2655 Spec: conf_v1.PolicySpec{ 2656 IngressMTLS: &conf_v1.IngressMTLS{ 2657 ClientCertSecret: "ingress-mtls-secret", 2658 }, 2659 }, 2660 }, 2661 "default/ingress-mtls-policy2": { 2662 Spec: conf_v1.PolicySpec{ 2663 IngressMTLS: &conf_v1.IngressMTLS{ 2664 ClientCertSecret: "ingress-mtls-secret2", 2665 }, 2666 }, 2667 }, 2668 }, 2669 policyOpts: policyOptions{ 2670 tls: true, 2671 secretRefs: map[string]*secrets.SecretReference{ 2672 "default/ingress-mtls-secret": { 2673 Secret: &api_v1.Secret{ 2674 Type: secrets.SecretTypeCA, 2675 }, 2676 Path: "/etc/nginx/secrets/default-ingress-mtls-secret", 2677 }, 2678 }, 2679 }, 2680 context: "spec", 2681 expected: policiesCfg{ 2682 IngressMTLS: &version2.IngressMTLS{ 2683 ClientCert: "/etc/nginx/secrets/default-ingress-mtls-secret", 2684 VerifyClient: "on", 2685 VerifyDepth: 1, 2686 }, 2687 }, 2688 expectedWarnings: Warnings{ 2689 nil: { 2690 `Multiple ingressMTLS policies are not allowed. IngressMTLS policy default/ingress-mtls-policy2 will be ignored`, 2691 }, 2692 }, 2693 expectedOidc: &oidcPolicyCfg{}, 2694 msg: "multi ingress mtls", 2695 }, 2696 { 2697 policyRefs: []conf_v1.PolicyReference{ 2698 { 2699 Name: "ingress-mtls-policy", 2700 Namespace: "default", 2701 }, 2702 }, 2703 policies: map[string]*conf_v1.Policy{ 2704 "default/ingress-mtls-policy": { 2705 ObjectMeta: meta_v1.ObjectMeta{ 2706 Name: "ingress-mtls-policy", 2707 Namespace: "default", 2708 }, 2709 Spec: conf_v1.PolicySpec{ 2710 IngressMTLS: &conf_v1.IngressMTLS{ 2711 ClientCertSecret: "ingress-mtls-secret", 2712 }, 2713 }, 2714 }, 2715 }, 2716 policyOpts: policyOptions{ 2717 tls: true, 2718 secretRefs: map[string]*secrets.SecretReference{ 2719 "default/ingress-mtls-secret": { 2720 Secret: &api_v1.Secret{ 2721 Type: secrets.SecretTypeCA, 2722 }, 2723 Path: "/etc/nginx/secrets/default-ingress-mtls-secret", 2724 }, 2725 }, 2726 }, 2727 context: "route", 2728 expected: policiesCfg{ 2729 ErrorReturn: &version2.Return{ 2730 Code: 500, 2731 }, 2732 }, 2733 expectedWarnings: Warnings{ 2734 nil: { 2735 `IngressMTLS policy default/ingress-mtls-policy is not allowed in the route context`, 2736 }, 2737 }, 2738 expectedOidc: &oidcPolicyCfg{}, 2739 msg: "ingress mtls in the wrong context", 2740 }, 2741 { 2742 policyRefs: []conf_v1.PolicyReference{ 2743 { 2744 Name: "ingress-mtls-policy", 2745 Namespace: "default", 2746 }, 2747 }, 2748 policies: map[string]*conf_v1.Policy{ 2749 "default/ingress-mtls-policy": { 2750 ObjectMeta: meta_v1.ObjectMeta{ 2751 Name: "ingress-mtls-policy", 2752 Namespace: "default", 2753 }, 2754 Spec: conf_v1.PolicySpec{ 2755 IngressMTLS: &conf_v1.IngressMTLS{ 2756 ClientCertSecret: "ingress-mtls-secret", 2757 }, 2758 }, 2759 }, 2760 }, 2761 policyOpts: policyOptions{ 2762 tls: false, 2763 secretRefs: map[string]*secrets.SecretReference{ 2764 "default/ingress-mtls-secret": { 2765 Secret: &api_v1.Secret{ 2766 Type: secrets.SecretTypeCA, 2767 }, 2768 Path: "/etc/nginx/secrets/default-ingress-mtls-secret", 2769 }, 2770 }, 2771 }, 2772 context: "route", 2773 expected: policiesCfg{ 2774 ErrorReturn: &version2.Return{ 2775 Code: 500, 2776 }, 2777 }, 2778 expectedWarnings: Warnings{ 2779 nil: { 2780 `TLS must be enabled in VirtualServer for IngressMTLS policy default/ingress-mtls-policy`, 2781 }, 2782 }, 2783 expectedOidc: &oidcPolicyCfg{}, 2784 msg: "ingress mtls missing TLS config", 2785 }, 2786 { 2787 policyRefs: []conf_v1.PolicyReference{ 2788 { 2789 Name: "egress-mtls-policy", 2790 Namespace: "default", 2791 }, 2792 { 2793 Name: "egress-mtls-policy2", 2794 Namespace: "default", 2795 }, 2796 }, 2797 policies: map[string]*conf_v1.Policy{ 2798 "default/egress-mtls-policy": { 2799 ObjectMeta: meta_v1.ObjectMeta{ 2800 Name: "egress-mtls-policy", 2801 Namespace: "default", 2802 }, 2803 Spec: conf_v1.PolicySpec{ 2804 EgressMTLS: &conf_v1.EgressMTLS{ 2805 TLSSecret: "egress-mtls-secret", 2806 }, 2807 }, 2808 }, 2809 "default/egress-mtls-policy2": { 2810 ObjectMeta: meta_v1.ObjectMeta{ 2811 Name: "egress-mtls-policy2", 2812 Namespace: "default", 2813 }, 2814 Spec: conf_v1.PolicySpec{ 2815 EgressMTLS: &conf_v1.EgressMTLS{ 2816 TLSSecret: "egress-mtls-secret2", 2817 }, 2818 }, 2819 }, 2820 }, 2821 policyOpts: policyOptions{ 2822 secretRefs: map[string]*secrets.SecretReference{ 2823 "default/egress-mtls-secret": { 2824 Secret: &api_v1.Secret{ 2825 Type: api_v1.SecretTypeTLS, 2826 }, 2827 Path: "/etc/nginx/secrets/default-egress-mtls-secret", 2828 }, 2829 }, 2830 }, 2831 context: "route", 2832 expected: policiesCfg{ 2833 EgressMTLS: &version2.EgressMTLS{ 2834 Certificate: "/etc/nginx/secrets/default-egress-mtls-secret", 2835 CertificateKey: "/etc/nginx/secrets/default-egress-mtls-secret", 2836 VerifyServer: false, 2837 VerifyDepth: 1, 2838 Ciphers: "DEFAULT", 2839 Protocols: "TLSv1 TLSv1.1 TLSv1.2", 2840 SessionReuse: true, 2841 SSLName: "$proxy_host", 2842 }, 2843 }, 2844 expectedWarnings: Warnings{ 2845 nil: { 2846 `Multiple egressMTLS policies in the same context is not valid. EgressMTLS policy default/egress-mtls-policy2 will be ignored`, 2847 }, 2848 }, 2849 expectedOidc: &oidcPolicyCfg{}, 2850 msg: "multi egress mtls", 2851 }, 2852 { 2853 policyRefs: []conf_v1.PolicyReference{ 2854 { 2855 Name: "egress-mtls-policy", 2856 Namespace: "default", 2857 }, 2858 }, 2859 policies: map[string]*conf_v1.Policy{ 2860 "default/egress-mtls-policy": { 2861 ObjectMeta: meta_v1.ObjectMeta{ 2862 Name: "egress-mtls-policy", 2863 Namespace: "default", 2864 }, 2865 Spec: conf_v1.PolicySpec{ 2866 EgressMTLS: &conf_v1.EgressMTLS{ 2867 TrustedCertSecret: "egress-trusted-secret", 2868 SSLName: "foo.com", 2869 }, 2870 }, 2871 }, 2872 }, 2873 policyOpts: policyOptions{ 2874 secretRefs: map[string]*secrets.SecretReference{ 2875 "default/egress-trusted-secret": { 2876 Secret: &api_v1.Secret{ 2877 Type: secrets.SecretTypeCA, 2878 }, 2879 Error: errors.New("secret is invalid"), 2880 }, 2881 }, 2882 }, 2883 context: "route", 2884 expected: policiesCfg{ 2885 ErrorReturn: &version2.Return{ 2886 Code: 500, 2887 }, 2888 }, 2889 expectedWarnings: Warnings{ 2890 nil: { 2891 `EgressMTLS policy default/egress-mtls-policy references an invalid secret default/egress-trusted-secret: secret is invalid`, 2892 }, 2893 }, 2894 expectedOidc: &oidcPolicyCfg{}, 2895 msg: "egress mtls referencing an invalid CA secret", 2896 }, 2897 { 2898 policyRefs: []conf_v1.PolicyReference{ 2899 { 2900 Name: "egress-mtls-policy", 2901 Namespace: "default", 2902 }, 2903 }, 2904 policies: map[string]*conf_v1.Policy{ 2905 "default/egress-mtls-policy": { 2906 ObjectMeta: meta_v1.ObjectMeta{ 2907 Name: "egress-mtls-policy", 2908 Namespace: "default", 2909 }, 2910 Spec: conf_v1.PolicySpec{ 2911 EgressMTLS: &conf_v1.EgressMTLS{ 2912 TLSSecret: "egress-mtls-secret", 2913 SSLName: "foo.com", 2914 }, 2915 }, 2916 }, 2917 }, 2918 policyOpts: policyOptions{ 2919 secretRefs: map[string]*secrets.SecretReference{ 2920 "default/egress-mtls-secret": { 2921 Secret: &api_v1.Secret{ 2922 Type: secrets.SecretTypeCA, 2923 }, 2924 }, 2925 }, 2926 }, 2927 context: "route", 2928 expected: policiesCfg{ 2929 ErrorReturn: &version2.Return{ 2930 Code: 500, 2931 }, 2932 }, 2933 expectedWarnings: Warnings{ 2934 nil: { 2935 `EgressMTLS policy default/egress-mtls-policy references a secret default/egress-mtls-secret of a wrong type 'nginx.org/ca', must be 'kubernetes.io/tls'`, 2936 }, 2937 }, 2938 expectedOidc: &oidcPolicyCfg{}, 2939 msg: "egress mtls referencing wrong secret type", 2940 }, 2941 { 2942 policyRefs: []conf_v1.PolicyReference{ 2943 { 2944 Name: "egress-mtls-policy", 2945 Namespace: "default", 2946 }, 2947 }, 2948 policies: map[string]*conf_v1.Policy{ 2949 "default/egress-mtls-policy": { 2950 ObjectMeta: meta_v1.ObjectMeta{ 2951 Name: "egress-mtls-policy", 2952 Namespace: "default", 2953 }, 2954 Spec: conf_v1.PolicySpec{ 2955 EgressMTLS: &conf_v1.EgressMTLS{ 2956 TrustedCertSecret: "egress-trusted-secret", 2957 SSLName: "foo.com", 2958 }, 2959 }, 2960 }, 2961 }, 2962 policyOpts: policyOptions{ 2963 secretRefs: map[string]*secrets.SecretReference{ 2964 "default/egress-trusted-secret": { 2965 Secret: &api_v1.Secret{ 2966 Type: api_v1.SecretTypeTLS, 2967 }, 2968 }, 2969 }, 2970 }, 2971 context: "route", 2972 expected: policiesCfg{ 2973 ErrorReturn: &version2.Return{ 2974 Code: 500, 2975 }, 2976 }, 2977 expectedWarnings: Warnings{ 2978 nil: { 2979 `EgressMTLS policy default/egress-mtls-policy references a secret default/egress-trusted-secret of a wrong type 'kubernetes.io/tls', must be 'nginx.org/ca'`, 2980 }, 2981 }, 2982 expectedOidc: &oidcPolicyCfg{}, 2983 msg: "egress trusted secret referencing wrong secret type", 2984 }, 2985 { 2986 policyRefs: []conf_v1.PolicyReference{ 2987 { 2988 Name: "egress-mtls-policy", 2989 Namespace: "default", 2990 }, 2991 }, 2992 policies: map[string]*conf_v1.Policy{ 2993 "default/egress-mtls-policy": { 2994 ObjectMeta: meta_v1.ObjectMeta{ 2995 Name: "egress-mtls-policy", 2996 Namespace: "default", 2997 }, 2998 Spec: conf_v1.PolicySpec{ 2999 EgressMTLS: &conf_v1.EgressMTLS{ 3000 TLSSecret: "egress-mtls-secret", 3001 SSLName: "foo.com", 3002 }, 3003 }, 3004 }, 3005 }, 3006 policyOpts: policyOptions{ 3007 secretRefs: map[string]*secrets.SecretReference{ 3008 "default/egress-mtls-secret": { 3009 Secret: &api_v1.Secret{ 3010 Type: api_v1.SecretTypeTLS, 3011 }, 3012 Error: errors.New("secret is invalid"), 3013 }, 3014 }, 3015 }, 3016 context: "route", 3017 expected: policiesCfg{ 3018 ErrorReturn: &version2.Return{ 3019 Code: 500, 3020 }, 3021 }, 3022 expectedWarnings: Warnings{ 3023 nil: { 3024 `EgressMTLS policy default/egress-mtls-policy references an invalid secret default/egress-mtls-secret: secret is invalid`, 3025 }, 3026 }, 3027 expectedOidc: &oidcPolicyCfg{}, 3028 msg: "egress mtls referencing missing tls secret", 3029 }, 3030 { 3031 policyRefs: []conf_v1.PolicyReference{ 3032 { 3033 Name: "oidc-policy", 3034 Namespace: "default", 3035 }, 3036 }, 3037 policies: map[string]*conf_v1.Policy{ 3038 "default/oidc-policy": { 3039 ObjectMeta: meta_v1.ObjectMeta{ 3040 Name: "oidc-policy", 3041 Namespace: "default", 3042 }, 3043 Spec: conf_v1.PolicySpec{ 3044 OIDC: &conf_v1.OIDC{ 3045 ClientSecret: "oidc-secret", 3046 }, 3047 }, 3048 }, 3049 }, 3050 policyOpts: policyOptions{ 3051 secretRefs: map[string]*secrets.SecretReference{ 3052 "default/oidc-secret": { 3053 Secret: &api_v1.Secret{ 3054 Type: secrets.SecretTypeOIDC, 3055 }, 3056 Error: errors.New("secret is invalid"), 3057 }, 3058 }, 3059 }, 3060 context: "route", 3061 expected: policiesCfg{ 3062 ErrorReturn: &version2.Return{ 3063 Code: 500, 3064 }, 3065 }, 3066 expectedWarnings: Warnings{ 3067 nil: { 3068 `OIDC policy default/oidc-policy references an invalid secret default/oidc-secret: secret is invalid`, 3069 }, 3070 }, 3071 expectedOidc: &oidcPolicyCfg{}, 3072 msg: "oidc referencing missing oidc secret", 3073 }, 3074 { 3075 policyRefs: []conf_v1.PolicyReference{ 3076 { 3077 Name: "oidc-policy", 3078 Namespace: "default", 3079 }, 3080 }, 3081 policies: map[string]*conf_v1.Policy{ 3082 "default/oidc-policy": { 3083 ObjectMeta: meta_v1.ObjectMeta{ 3084 Name: "oidc-policy", 3085 Namespace: "default", 3086 }, 3087 Spec: conf_v1.PolicySpec{ 3088 OIDC: &conf_v1.OIDC{ 3089 ClientSecret: "oidc-secret", 3090 AuthEndpoint: "http://foo.com/bar", 3091 TokenEndpoint: "http://foo.com/bar", 3092 JWKSURI: "http://foo.com/bar", 3093 }, 3094 }, 3095 }, 3096 }, 3097 policyOpts: policyOptions{ 3098 secretRefs: map[string]*secrets.SecretReference{ 3099 "default/oidc-secret": { 3100 Secret: &api_v1.Secret{ 3101 Type: api_v1.SecretTypeTLS, 3102 }, 3103 }, 3104 }, 3105 }, 3106 context: "spec", 3107 expected: policiesCfg{ 3108 ErrorReturn: &version2.Return{ 3109 Code: 500, 3110 }, 3111 }, 3112 expectedWarnings: Warnings{ 3113 nil: { 3114 `OIDC policy default/oidc-policy references a secret default/oidc-secret of a wrong type 'kubernetes.io/tls', must be 'nginx.org/oidc'`, 3115 }, 3116 }, 3117 expectedOidc: &oidcPolicyCfg{}, 3118 msg: "oidc secret referencing wrong secret type", 3119 }, 3120 { 3121 policyRefs: []conf_v1.PolicyReference{ 3122 { 3123 Name: "oidc-policy-2", 3124 Namespace: "default", 3125 }, 3126 }, 3127 policies: map[string]*conf_v1.Policy{ 3128 "default/oidc-policy-1": { 3129 ObjectMeta: meta_v1.ObjectMeta{ 3130 Name: "oidc-policy-1", 3131 Namespace: "default", 3132 }, 3133 Spec: conf_v1.PolicySpec{ 3134 OIDC: &conf_v1.OIDC{ 3135 ClientID: "foo", 3136 ClientSecret: "oidc-secret", 3137 AuthEndpoint: "https://foo.com/auth", 3138 TokenEndpoint: "https://foo.com/token", 3139 JWKSURI: "https://foo.com/certs", 3140 }, 3141 }, 3142 }, 3143 "default/oidc-policy-2": { 3144 ObjectMeta: meta_v1.ObjectMeta{ 3145 Name: "oidc-policy-2", 3146 Namespace: "default", 3147 }, 3148 Spec: conf_v1.PolicySpec{ 3149 OIDC: &conf_v1.OIDC{ 3150 ClientID: "foo", 3151 ClientSecret: "oidc-secret", 3152 AuthEndpoint: "https://bar.com/auth", 3153 TokenEndpoint: "https://bar.com/token", 3154 JWKSURI: "https://bar.com/certs", 3155 }, 3156 }, 3157 }, 3158 }, 3159 policyOpts: policyOptions{ 3160 secretRefs: map[string]*secrets.SecretReference{ 3161 "default/oidc-secret": { 3162 Secret: &api_v1.Secret{ 3163 Type: secrets.SecretTypeOIDC, 3164 Data: map[string][]byte{ 3165 "client-secret": []byte("super_secret_123"), 3166 }, 3167 }, 3168 }, 3169 }, 3170 }, 3171 context: "route", 3172 oidcPolCfg: &oidcPolicyCfg{ 3173 oidc: &version2.OIDC{ 3174 AuthEndpoint: "https://foo.com/auth", 3175 TokenEndpoint: "https://foo.com/token", 3176 JwksURI: "https://foo.com/certs", 3177 ClientID: "foo", 3178 ClientSecret: "super_secret_123", 3179 RedirectURI: "/_codexch", 3180 Scope: "openid", 3181 }, 3182 key: "default/oidc-policy-1", 3183 }, 3184 expected: policiesCfg{ 3185 ErrorReturn: &version2.Return{ 3186 Code: 500, 3187 }, 3188 }, 3189 expectedWarnings: Warnings{ 3190 nil: { 3191 `Only one oidc policy is allowed in a VirtualServer and its VirtualServerRoutes. Can't use default/oidc-policy-2. Use default/oidc-policy-1`, 3192 }, 3193 }, 3194 expectedOidc: &oidcPolicyCfg{ 3195 oidc: &version2.OIDC{ 3196 AuthEndpoint: "https://foo.com/auth", 3197 TokenEndpoint: "https://foo.com/token", 3198 JwksURI: "https://foo.com/certs", 3199 ClientID: "foo", 3200 ClientSecret: "super_secret_123", 3201 RedirectURI: "/_codexch", 3202 Scope: "openid", 3203 }, 3204 key: "default/oidc-policy-1", 3205 }, 3206 msg: "multiple oidc policies", 3207 }, 3208 { 3209 policyRefs: []conf_v1.PolicyReference{ 3210 { 3211 Name: "oidc-policy", 3212 Namespace: "default", 3213 }, 3214 { 3215 Name: "oidc-policy2", 3216 Namespace: "default", 3217 }, 3218 }, 3219 policies: map[string]*conf_v1.Policy{ 3220 "default/oidc-policy": { 3221 ObjectMeta: meta_v1.ObjectMeta{ 3222 Name: "oidc-policy", 3223 Namespace: "default", 3224 }, 3225 Spec: conf_v1.PolicySpec{ 3226 OIDC: &conf_v1.OIDC{ 3227 ClientSecret: "oidc-secret", 3228 AuthEndpoint: "https://foo.com/auth", 3229 TokenEndpoint: "https://foo.com/token", 3230 JWKSURI: "https://foo.com/certs", 3231 ClientID: "foo", 3232 }, 3233 }, 3234 }, 3235 "default/oidc-policy2": { 3236 ObjectMeta: meta_v1.ObjectMeta{ 3237 Name: "oidc-policy2", 3238 Namespace: "default", 3239 }, 3240 Spec: conf_v1.PolicySpec{ 3241 OIDC: &conf_v1.OIDC{ 3242 ClientSecret: "oidc-secret", 3243 AuthEndpoint: "https://bar.com/auth", 3244 TokenEndpoint: "https://bar.com/token", 3245 JWKSURI: "https://bar.com/certs", 3246 ClientID: "bar", 3247 }, 3248 }, 3249 }, 3250 }, 3251 policyOpts: policyOptions{ 3252 secretRefs: map[string]*secrets.SecretReference{ 3253 "default/oidc-secret": { 3254 Secret: &api_v1.Secret{ 3255 Type: secrets.SecretTypeOIDC, 3256 Data: map[string][]byte{ 3257 "client-secret": []byte("super_secret_123"), 3258 }, 3259 }, 3260 }, 3261 }, 3262 }, 3263 context: "route", 3264 expected: policiesCfg{ 3265 OIDC: true, 3266 }, 3267 expectedWarnings: Warnings{ 3268 nil: { 3269 `Multiple oidc policies in the same context is not valid. OIDC policy default/oidc-policy2 will be ignored`, 3270 }, 3271 }, 3272 expectedOidc: &oidcPolicyCfg{ 3273 &version2.OIDC{ 3274 AuthEndpoint: "https://foo.com/auth", 3275 TokenEndpoint: "https://foo.com/token", 3276 JwksURI: "https://foo.com/certs", 3277 ClientID: "foo", 3278 ClientSecret: "super_secret_123", 3279 RedirectURI: "/_codexch", 3280 Scope: "openid", 3281 }, 3282 "default/oidc-policy", 3283 }, 3284 msg: "multi oidc", 3285 }, 3286 { 3287 policyRefs: []conf_v1.PolicyReference{ 3288 { 3289 Name: "waf-policy", 3290 Namespace: "default", 3291 }, 3292 { 3293 Name: "waf-policy2", 3294 Namespace: "default", 3295 }, 3296 }, 3297 policies: map[string]*conf_v1.Policy{ 3298 "default/waf-policy": { 3299 ObjectMeta: meta_v1.ObjectMeta{ 3300 Name: "waf-policy", 3301 Namespace: "default", 3302 }, 3303 Spec: conf_v1.PolicySpec{ 3304 WAF: &conf_v1.WAF{ 3305 Enable: true, 3306 ApPolicy: "default/dataguard-alarm", 3307 }, 3308 }, 3309 }, 3310 "default/waf-policy2": { 3311 ObjectMeta: meta_v1.ObjectMeta{ 3312 Name: "waf-policy2", 3313 Namespace: "default", 3314 }, 3315 Spec: conf_v1.PolicySpec{ 3316 WAF: &conf_v1.WAF{ 3317 Enable: true, 3318 ApPolicy: "default/dataguard-alarm", 3319 }, 3320 }, 3321 }, 3322 }, 3323 policyOpts: policyOptions{ 3324 apResources: map[string]string{ 3325 "default/logconf": "/etc/nginx/waf/nac-logconfs/default-logconf", 3326 "default/dataguard-alarm": "/etc/nginx/waf/nac-policies/default-dataguard-alarm", 3327 }, 3328 }, 3329 context: "route", 3330 expected: policiesCfg{ 3331 WAF: &version2.WAF{ 3332 Enable: "on", 3333 ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm", 3334 }, 3335 }, 3336 expectedWarnings: Warnings{ 3337 nil: { 3338 `Multiple WAF policies in the same context is not valid. WAF policy default/waf-policy2 will be ignored`, 3339 }, 3340 }, 3341 expectedOidc: &oidcPolicyCfg{}, 3342 msg: "multi waf", 3343 }, 3344 } 3345 3346 for _, test := range tests { 3347 vsc := newVirtualServerConfigurator(&ConfigParams{}, false, false, &StaticConfigParams{}) 3348 3349 if test.oidcPolCfg != nil { 3350 vsc.oidcPolCfg = test.oidcPolCfg 3351 } 3352 3353 result := vsc.generatePolicies(ownerDetails, test.policyRefs, test.policies, test.context, test.policyOpts) 3354 if diff := cmp.Diff(test.expected, result); diff != "" { 3355 t.Errorf("generatePolicies() '%v' mismatch (-want +got):\n%s", test.msg, diff) 3356 } 3357 if !reflect.DeepEqual(vsc.warnings, test.expectedWarnings) { 3358 t.Errorf( 3359 "generatePolicies() returned warnings of \n%v but expected \n%v for the case of %s", 3360 vsc.warnings, 3361 test.expectedWarnings, 3362 test.msg, 3363 ) 3364 } 3365 if diff := cmp.Diff(test.expectedOidc.oidc, vsc.oidcPolCfg.oidc); diff != "" { 3366 t.Errorf("generatePolicies() '%v' mismatch (-want +got):\n%s", test.msg, diff) 3367 } 3368 if diff := cmp.Diff(test.expectedOidc.key, vsc.oidcPolCfg.key); diff != "" { 3369 t.Errorf("generatePolicies() '%v' mismatch (-want +got):\n%s", test.msg, diff) 3370 } 3371 } 3372 } 3373 3374 func TestRemoveDuplicates(t *testing.T) { 3375 tests := []struct { 3376 rlz []version2.LimitReqZone 3377 expected []version2.LimitReqZone 3378 }{ 3379 { 3380 rlz: []version2.LimitReqZone{ 3381 {ZoneName: "test"}, 3382 {ZoneName: "test"}, 3383 {ZoneName: "test2"}, 3384 {ZoneName: "test3"}, 3385 }, 3386 expected: []version2.LimitReqZone{ 3387 {ZoneName: "test"}, 3388 {ZoneName: "test2"}, 3389 {ZoneName: "test3"}, 3390 }, 3391 }, 3392 { 3393 rlz: []version2.LimitReqZone{ 3394 {ZoneName: "test"}, 3395 {ZoneName: "test"}, 3396 {ZoneName: "test2"}, 3397 {ZoneName: "test3"}, 3398 {ZoneName: "test3"}, 3399 }, 3400 expected: []version2.LimitReqZone{ 3401 {ZoneName: "test"}, 3402 {ZoneName: "test2"}, 3403 {ZoneName: "test3"}, 3404 }, 3405 }, 3406 } 3407 for _, test := range tests { 3408 result := removeDuplicateLimitReqZones(test.rlz) 3409 if !reflect.DeepEqual(result, test.expected) { 3410 t.Errorf("removeDuplicateLimitReqZones() returned \n%v, but expected \n%v", result, test.expected) 3411 } 3412 } 3413 } 3414 3415 func TestAddPoliciesCfgToLocations(t *testing.T) { 3416 cfg := policiesCfg{ 3417 Allow: []string{"127.0.0.1"}, 3418 Deny: []string{"127.0.0.2"}, 3419 ErrorReturn: &version2.Return{ 3420 Code: 400, 3421 }, 3422 } 3423 3424 locations := []version2.Location{ 3425 { 3426 Path: "/", 3427 }, 3428 } 3429 3430 expectedLocations := []version2.Location{ 3431 { 3432 Path: "/", 3433 Allow: []string{"127.0.0.1"}, 3434 Deny: []string{"127.0.0.2"}, 3435 PoliciesErrorReturn: &version2.Return{ 3436 Code: 400, 3437 }, 3438 }, 3439 } 3440 3441 addPoliciesCfgToLocations(cfg, locations) 3442 if !reflect.DeepEqual(locations, expectedLocations) { 3443 t.Errorf("addPoliciesCfgToLocations() returned \n%+v but expected \n%+v", locations, expectedLocations) 3444 } 3445 } 3446 3447 func TestGenerateUpstream(t *testing.T) { 3448 name := "test-upstream" 3449 upstream := conf_v1.Upstream{Service: name, Port: 80} 3450 endpoints := []string{ 3451 "192.168.10.10:8080", 3452 } 3453 cfgParams := ConfigParams{ 3454 LBMethod: "random", 3455 MaxFails: 1, 3456 MaxConns: 0, 3457 FailTimeout: "10s", 3458 Keepalive: 21, 3459 UpstreamZoneSize: "256k", 3460 } 3461 3462 expected := version2.Upstream{ 3463 Name: "test-upstream", 3464 UpstreamLabels: version2.UpstreamLabels{ 3465 Service: "test-upstream", 3466 }, 3467 Servers: []version2.UpstreamServer{ 3468 { 3469 Address: "192.168.10.10:8080", 3470 }, 3471 }, 3472 MaxFails: 1, 3473 MaxConns: 0, 3474 FailTimeout: "10s", 3475 LBMethod: "random", 3476 Keepalive: 21, 3477 UpstreamZoneSize: "256k", 3478 } 3479 3480 vsc := newVirtualServerConfigurator(&cfgParams, false, false, &StaticConfigParams{}) 3481 result := vsc.generateUpstream(nil, name, upstream, false, endpoints) 3482 if !reflect.DeepEqual(result, expected) { 3483 t.Errorf("generateUpstream() returned %v but expected %v", result, expected) 3484 } 3485 3486 if len(vsc.warnings) != 0 { 3487 t.Errorf("generateUpstream returned warnings for %v", upstream) 3488 } 3489 } 3490 3491 func TestGenerateUpstreamWithKeepalive(t *testing.T) { 3492 name := "test-upstream" 3493 noKeepalive := 0 3494 keepalive := 32 3495 endpoints := []string{ 3496 "192.168.10.10:8080", 3497 } 3498 3499 tests := []struct { 3500 upstream conf_v1.Upstream 3501 cfgParams *ConfigParams 3502 expected version2.Upstream 3503 msg string 3504 }{ 3505 { 3506 conf_v1.Upstream{Keepalive: &keepalive, Service: name, Port: 80}, 3507 &ConfigParams{Keepalive: 21}, 3508 version2.Upstream{ 3509 Name: "test-upstream", 3510 UpstreamLabels: version2.UpstreamLabels{ 3511 Service: "test-upstream", 3512 }, 3513 Servers: []version2.UpstreamServer{ 3514 { 3515 Address: "192.168.10.10:8080", 3516 }, 3517 }, 3518 Keepalive: 32, 3519 }, 3520 "upstream keepalive set, configparam set", 3521 }, 3522 { 3523 conf_v1.Upstream{Service: name, Port: 80}, 3524 &ConfigParams{Keepalive: 21}, 3525 version2.Upstream{ 3526 Name: "test-upstream", 3527 UpstreamLabels: version2.UpstreamLabels{ 3528 Service: "test-upstream", 3529 }, 3530 Servers: []version2.UpstreamServer{ 3531 { 3532 Address: "192.168.10.10:8080", 3533 }, 3534 }, 3535 Keepalive: 21, 3536 }, 3537 "upstream keepalive not set, configparam set", 3538 }, 3539 { 3540 conf_v1.Upstream{Keepalive: &noKeepalive, Service: name, Port: 80}, 3541 &ConfigParams{Keepalive: 21}, 3542 version2.Upstream{ 3543 Name: "test-upstream", 3544 UpstreamLabels: version2.UpstreamLabels{ 3545 Service: "test-upstream", 3546 }, 3547 Servers: []version2.UpstreamServer{ 3548 { 3549 Address: "192.168.10.10:8080", 3550 }, 3551 }, 3552 }, 3553 "upstream keepalive set to 0, configparam set", 3554 }, 3555 } 3556 3557 for _, test := range tests { 3558 vsc := newVirtualServerConfigurator(test.cfgParams, false, false, &StaticConfigParams{}) 3559 result := vsc.generateUpstream(nil, name, test.upstream, false, endpoints) 3560 if !reflect.DeepEqual(result, test.expected) { 3561 t.Errorf("generateUpstream() returned %v but expected %v for the case of %v", result, test.expected, test.msg) 3562 } 3563 3564 if len(vsc.warnings) != 0 { 3565 t.Errorf("generateUpstream() returned warnings for %v", test.upstream) 3566 } 3567 } 3568 } 3569 3570 func TestGenerateUpstreamForExternalNameService(t *testing.T) { 3571 name := "test-upstream" 3572 endpoints := []string{"example.com"} 3573 upstream := conf_v1.Upstream{Service: name} 3574 cfgParams := ConfigParams{} 3575 3576 expected := version2.Upstream{ 3577 Name: name, 3578 UpstreamLabels: version2.UpstreamLabels{ 3579 Service: "test-upstream", 3580 }, 3581 Servers: []version2.UpstreamServer{ 3582 { 3583 Address: "example.com", 3584 }, 3585 }, 3586 Resolve: true, 3587 } 3588 3589 vsc := newVirtualServerConfigurator(&cfgParams, true, true, &StaticConfigParams{}) 3590 result := vsc.generateUpstream(nil, name, upstream, true, endpoints) 3591 if !reflect.DeepEqual(result, expected) { 3592 t.Errorf("generateUpstream() returned %v but expected %v", result, expected) 3593 } 3594 3595 if len(vsc.warnings) != 0 { 3596 t.Errorf("generateUpstream() returned warnings for %v", upstream) 3597 } 3598 } 3599 3600 func TestGenerateProxyPass(t *testing.T) { 3601 tests := []struct { 3602 tlsEnabled bool 3603 upstreamName string 3604 internal bool 3605 expected string 3606 }{ 3607 { 3608 tlsEnabled: false, 3609 upstreamName: "test-upstream", 3610 internal: false, 3611 expected: "http://test-upstream", 3612 }, 3613 { 3614 tlsEnabled: true, 3615 upstreamName: "test-upstream", 3616 internal: false, 3617 expected: "https://test-upstream", 3618 }, 3619 { 3620 tlsEnabled: false, 3621 upstreamName: "test-upstream", 3622 internal: true, 3623 expected: "http://test-upstream$request_uri", 3624 }, 3625 { 3626 tlsEnabled: true, 3627 upstreamName: "test-upstream", 3628 internal: true, 3629 expected: "https://test-upstream$request_uri", 3630 }, 3631 } 3632 3633 for _, test := range tests { 3634 result := generateProxyPass(test.tlsEnabled, test.upstreamName, test.internal, nil) 3635 if result != test.expected { 3636 t.Errorf("generateProxyPass(%v, %v, %v) returned %v but expected %v", test.tlsEnabled, test.upstreamName, test.internal, result, test.expected) 3637 } 3638 } 3639 } 3640 3641 func TestGenerateProxyPassProtocol(t *testing.T) { 3642 tests := []struct { 3643 upstream conf_v1.Upstream 3644 expected string 3645 }{ 3646 { 3647 upstream: conf_v1.Upstream{}, 3648 expected: "http", 3649 }, 3650 { 3651 upstream: conf_v1.Upstream{ 3652 TLS: conf_v1.UpstreamTLS{ 3653 Enable: true, 3654 }, 3655 }, 3656 expected: "https", 3657 }, 3658 } 3659 3660 for _, test := range tests { 3661 result := generateProxyPassProtocol(test.upstream.TLS.Enable) 3662 if result != test.expected { 3663 t.Errorf("generateProxyPassProtocol(%v) returned %v but expected %v", test.upstream.TLS.Enable, result, test.expected) 3664 } 3665 } 3666 } 3667 3668 func TestGenerateString(t *testing.T) { 3669 tests := []struct { 3670 inputS string 3671 expected string 3672 }{ 3673 { 3674 inputS: "http_404", 3675 expected: "http_404", 3676 }, 3677 { 3678 inputS: "", 3679 expected: "error timeout", 3680 }, 3681 } 3682 3683 for _, test := range tests { 3684 result := generateString(test.inputS, "error timeout") 3685 if result != test.expected { 3686 t.Errorf("generateString() return %v but expected %v", result, test.expected) 3687 } 3688 } 3689 } 3690 3691 func TestGenerateSnippets(t *testing.T) { 3692 tests := []struct { 3693 enableSnippets bool 3694 s string 3695 defaultS []string 3696 expected []string 3697 }{ 3698 { 3699 true, 3700 "test", 3701 []string{}, 3702 []string{"test"}, 3703 }, 3704 { 3705 true, 3706 "", 3707 []string{"default"}, 3708 []string{"default"}, 3709 }, 3710 { 3711 true, 3712 "test\none\ntwo", 3713 []string{}, 3714 []string{"test", "one", "two"}, 3715 }, 3716 { 3717 false, 3718 "test", 3719 nil, 3720 nil, 3721 }, 3722 } 3723 for _, test := range tests { 3724 result := generateSnippets(test.enableSnippets, test.s, test.defaultS) 3725 if !reflect.DeepEqual(result, test.expected) { 3726 t.Errorf("generateSnippets() return %v, but expected %v", result, test.expected) 3727 } 3728 } 3729 } 3730 3731 func TestGenerateBuffer(t *testing.T) { 3732 tests := []struct { 3733 inputS *conf_v1.UpstreamBuffers 3734 expected string 3735 }{ 3736 { 3737 inputS: nil, 3738 expected: "8 4k", 3739 }, 3740 { 3741 inputS: &conf_v1.UpstreamBuffers{Number: 8, Size: "16K"}, 3742 expected: "8 16K", 3743 }, 3744 } 3745 3746 for _, test := range tests { 3747 result := generateBuffers(test.inputS, "8 4k") 3748 if result != test.expected { 3749 t.Errorf("generateBuffer() return %v but expected %v", result, test.expected) 3750 } 3751 } 3752 } 3753 3754 func TestGenerateLocationForProxying(t *testing.T) { 3755 cfgParams := ConfigParams{ 3756 ProxyConnectTimeout: "30s", 3757 ProxyReadTimeout: "31s", 3758 ProxySendTimeout: "32s", 3759 ClientMaxBodySize: "1m", 3760 ProxyMaxTempFileSize: "1024m", 3761 ProxyBuffering: true, 3762 ProxyBuffers: "8 4k", 3763 ProxyBufferSize: "4k", 3764 LocationSnippets: []string{"# location snippet"}, 3765 } 3766 path := "/" 3767 upstreamName := "test-upstream" 3768 vsLocSnippets := []string{"# vs location snippet"} 3769 3770 expected := version2.Location{ 3771 Path: "/", 3772 Snippets: vsLocSnippets, 3773 ProxyConnectTimeout: "30s", 3774 ProxyReadTimeout: "31s", 3775 ProxySendTimeout: "32s", 3776 ClientMaxBodySize: "1m", 3777 ProxyMaxTempFileSize: "1024m", 3778 ProxyBuffering: true, 3779 ProxyBuffers: "8 4k", 3780 ProxyBufferSize: "4k", 3781 ProxyPass: "http://test-upstream", 3782 ProxyNextUpstream: "error timeout", 3783 ProxyNextUpstreamTimeout: "0s", 3784 ProxyNextUpstreamTries: 0, 3785 ProxyPassRequestHeaders: true, 3786 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 3787 ServiceName: "", 3788 IsVSR: false, 3789 VSRName: "", 3790 VSRNamespace: "", 3791 } 3792 3793 result := generateLocationForProxying(path, upstreamName, conf_v1.Upstream{}, &cfgParams, nil, false, 0, "", nil, "", vsLocSnippets, false, "", "") 3794 if diff := cmp.Diff(expected, result); diff != "" { 3795 t.Errorf("generateLocationForProxying() mismatch (-want +got):\n%s", diff) 3796 } 3797 } 3798 3799 func TestGenerateReturnBlock(t *testing.T) { 3800 tests := []struct { 3801 text string 3802 code int 3803 defaultCode int 3804 expected *version2.Return 3805 }{ 3806 { 3807 text: "Hello World!", 3808 code: 0, // Not set 3809 defaultCode: 200, 3810 expected: &version2.Return{ 3811 Code: 200, 3812 Text: "Hello World!", 3813 }, 3814 }, 3815 { 3816 text: "Hello World!", 3817 code: 400, 3818 defaultCode: 200, 3819 expected: &version2.Return{ 3820 Code: 400, 3821 Text: "Hello World!", 3822 }, 3823 }, 3824 } 3825 3826 for _, test := range tests { 3827 result := generateReturnBlock(test.text, test.code, test.defaultCode) 3828 if !reflect.DeepEqual(result, test.expected) { 3829 t.Errorf("generateReturnBlock() returned %v but expected %v", result, test.expected) 3830 } 3831 } 3832 } 3833 3834 func TestGenerateLocationForReturn(t *testing.T) { 3835 tests := []struct { 3836 actionReturn *conf_v1.ActionReturn 3837 expectedLocation version2.Location 3838 expectedReturnLocation *version2.ReturnLocation 3839 msg string 3840 }{ 3841 { 3842 actionReturn: &conf_v1.ActionReturn{ 3843 Body: "hello", 3844 }, 3845 3846 expectedLocation: version2.Location{ 3847 Path: "/", 3848 Snippets: []string{"# location snippet"}, 3849 ErrorPages: []version2.ErrorPage{ 3850 { 3851 Name: "@return_1", 3852 Codes: "418", 3853 ResponseCode: 200, 3854 }, 3855 }, 3856 ProxyInterceptErrors: true, 3857 InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock", 3858 }, 3859 expectedReturnLocation: &version2.ReturnLocation{ 3860 Name: "@return_1", 3861 DefaultType: "text/plain", 3862 Return: version2.Return{ 3863 Code: 0, 3864 Text: "hello", 3865 }, 3866 }, 3867 msg: "return without code and type", 3868 }, 3869 { 3870 actionReturn: &conf_v1.ActionReturn{ 3871 Code: 400, 3872 Type: "text/html", 3873 Body: "hello", 3874 }, 3875 3876 expectedLocation: version2.Location{ 3877 Path: "/", 3878 Snippets: []string{"# location snippet"}, 3879 ErrorPages: []version2.ErrorPage{ 3880 { 3881 Name: "@return_1", 3882 Codes: "418", 3883 ResponseCode: 400, 3884 }, 3885 }, 3886 ProxyInterceptErrors: true, 3887 InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock", 3888 }, 3889 expectedReturnLocation: &version2.ReturnLocation{ 3890 Name: "@return_1", 3891 DefaultType: "text/html", 3892 Return: version2.Return{ 3893 Code: 0, 3894 Text: "hello", 3895 }, 3896 }, 3897 msg: "return with all fields defined", 3898 }, 3899 } 3900 path := "/" 3901 snippets := []string{"# location snippet"} 3902 returnLocationIndex := 1 3903 3904 for _, test := range tests { 3905 location, returnLocation := generateLocationForReturn(path, snippets, test.actionReturn, returnLocationIndex) 3906 if !reflect.DeepEqual(location, test.expectedLocation) { 3907 t.Errorf("generateLocationForReturn() returned \n%+v but expected \n%+v for the case of %s", 3908 location, test.expectedLocation, test.msg) 3909 } 3910 if !reflect.DeepEqual(returnLocation, test.expectedReturnLocation) { 3911 t.Errorf("generateLocationForReturn() returned \n%+v but expected \n%+v for the case of %s", 3912 returnLocation, test.expectedReturnLocation, test.msg) 3913 } 3914 } 3915 } 3916 3917 func TestGenerateLocationForRedirect(t *testing.T) { 3918 tests := []struct { 3919 redirect *conf_v1.ActionRedirect 3920 expected version2.Location 3921 msg string 3922 }{ 3923 { 3924 redirect: &conf_v1.ActionRedirect{ 3925 URL: "http://nginx.org", 3926 }, 3927 3928 expected: version2.Location{ 3929 Path: "/", 3930 Snippets: []string{"# location snippet"}, 3931 ErrorPages: []version2.ErrorPage{ 3932 { 3933 Name: "http://nginx.org", 3934 Codes: "418", 3935 ResponseCode: 301, 3936 }, 3937 }, 3938 ProxyInterceptErrors: true, 3939 InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock", 3940 }, 3941 msg: "redirect without code", 3942 }, 3943 { 3944 redirect: &conf_v1.ActionRedirect{ 3945 Code: 302, 3946 URL: "http://nginx.org", 3947 }, 3948 3949 expected: version2.Location{ 3950 Path: "/", 3951 Snippets: []string{"# location snippet"}, 3952 ErrorPages: []version2.ErrorPage{ 3953 { 3954 Name: "http://nginx.org", 3955 Codes: "418", 3956 ResponseCode: 302, 3957 }, 3958 }, 3959 ProxyInterceptErrors: true, 3960 InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock", 3961 }, 3962 msg: "redirect with all fields defined", 3963 }, 3964 } 3965 3966 for _, test := range tests { 3967 result := generateLocationForRedirect("/", []string{"# location snippet"}, test.redirect) 3968 if !reflect.DeepEqual(result, test.expected) { 3969 t.Errorf("generateLocationForReturn() returned \n%+v but expected \n%+v for the case of %s", 3970 result, test.expected, test.msg) 3971 } 3972 } 3973 } 3974 3975 func TestGenerateSSLConfig(t *testing.T) { 3976 tests := []struct { 3977 inputTLS *conf_v1.TLS 3978 inputSecretRefs map[string]*secrets.SecretReference 3979 inputCfgParams *ConfigParams 3980 expectedSSL *version2.SSL 3981 expectedWarnings Warnings 3982 msg string 3983 }{ 3984 { 3985 inputTLS: nil, 3986 inputSecretRefs: map[string]*secrets.SecretReference{}, 3987 inputCfgParams: &ConfigParams{}, 3988 expectedSSL: nil, 3989 expectedWarnings: Warnings{}, 3990 msg: "no TLS field", 3991 }, 3992 { 3993 inputTLS: &conf_v1.TLS{ 3994 Secret: "", 3995 }, 3996 inputSecretRefs: map[string]*secrets.SecretReference{}, 3997 inputCfgParams: &ConfigParams{}, 3998 expectedSSL: nil, 3999 expectedWarnings: Warnings{}, 4000 msg: "TLS field with empty secret", 4001 }, 4002 { 4003 inputTLS: &conf_v1.TLS{ 4004 Secret: "secret", 4005 }, 4006 inputCfgParams: &ConfigParams{}, 4007 inputSecretRefs: map[string]*secrets.SecretReference{ 4008 "default/secret": { 4009 Error: errors.New("secret doesn't exist"), 4010 }, 4011 }, 4012 expectedSSL: &version2.SSL{ 4013 HTTP2: false, 4014 RejectHandshake: true, 4015 }, 4016 expectedWarnings: Warnings{ 4017 nil: []string{"TLS secret secret is invalid: secret doesn't exist"}, 4018 }, 4019 msg: "secret doesn't exist in the cluster with HTTPS", 4020 }, 4021 { 4022 inputTLS: &conf_v1.TLS{ 4023 Secret: "secret", 4024 }, 4025 inputCfgParams: &ConfigParams{}, 4026 inputSecretRefs: map[string]*secrets.SecretReference{ 4027 "default/secret": { 4028 Secret: &api_v1.Secret{ 4029 Type: secrets.SecretTypeCA, 4030 }, 4031 }, 4032 }, 4033 expectedSSL: &version2.SSL{ 4034 HTTP2: false, 4035 RejectHandshake: true, 4036 }, 4037 expectedWarnings: Warnings{ 4038 nil: []string{"TLS secret secret is of a wrong type 'nginx.org/ca', must be 'kubernetes.io/tls'"}, 4039 }, 4040 msg: "wrong secret type", 4041 }, 4042 { 4043 inputTLS: &conf_v1.TLS{ 4044 Secret: "secret", 4045 }, 4046 inputSecretRefs: map[string]*secrets.SecretReference{ 4047 "default/secret": { 4048 Secret: &api_v1.Secret{ 4049 Type: api_v1.SecretTypeTLS, 4050 }, 4051 Path: "secret.pem", 4052 }, 4053 }, 4054 inputCfgParams: &ConfigParams{}, 4055 expectedSSL: &version2.SSL{ 4056 HTTP2: false, 4057 Certificate: "secret.pem", 4058 CertificateKey: "secret.pem", 4059 RejectHandshake: false, 4060 }, 4061 expectedWarnings: Warnings{}, 4062 msg: "normal case with HTTPS", 4063 }, 4064 } 4065 4066 namespace := "default" 4067 4068 for _, test := range tests { 4069 vsc := newVirtualServerConfigurator(&ConfigParams{}, false, false, &StaticConfigParams{}) 4070 4071 // it is ok to use nil as the owner 4072 result := vsc.generateSSLConfig(nil, test.inputTLS, namespace, test.inputSecretRefs, test.inputCfgParams) 4073 if !reflect.DeepEqual(result, test.expectedSSL) { 4074 t.Errorf("generateSSLConfig() returned %v but expected %v for the case of %s", result, test.expectedSSL, test.msg) 4075 } 4076 if !reflect.DeepEqual(vsc.warnings, test.expectedWarnings) { 4077 t.Errorf("generateSSLConfig() returned warnings of \n%v but expected \n%v for the case of %s", vsc.warnings, test.expectedWarnings, test.msg) 4078 } 4079 } 4080 } 4081 4082 func TestGenerateRedirectConfig(t *testing.T) { 4083 tests := []struct { 4084 inputTLS *conf_v1.TLS 4085 expected *version2.TLSRedirect 4086 msg string 4087 }{ 4088 { 4089 inputTLS: nil, 4090 expected: nil, 4091 msg: "no TLS field", 4092 }, 4093 { 4094 inputTLS: &conf_v1.TLS{ 4095 Secret: "secret", 4096 Redirect: nil, 4097 }, 4098 expected: nil, 4099 msg: "no redirect field", 4100 }, 4101 { 4102 inputTLS: &conf_v1.TLS{ 4103 Secret: "secret", 4104 Redirect: &conf_v1.TLSRedirect{Enable: false}, 4105 }, 4106 expected: nil, 4107 msg: "redirect disabled", 4108 }, 4109 { 4110 inputTLS: &conf_v1.TLS{ 4111 Secret: "secret", 4112 Redirect: &conf_v1.TLSRedirect{ 4113 Enable: true, 4114 }, 4115 }, 4116 expected: &version2.TLSRedirect{ 4117 Code: 301, 4118 BasedOn: "$scheme", 4119 }, 4120 msg: "normal case with defaults", 4121 }, 4122 { 4123 inputTLS: &conf_v1.TLS{ 4124 Secret: "secret", 4125 Redirect: &conf_v1.TLSRedirect{ 4126 Enable: true, 4127 BasedOn: "x-forwarded-proto", 4128 }, 4129 }, 4130 expected: &version2.TLSRedirect{ 4131 Code: 301, 4132 BasedOn: "$http_x_forwarded_proto", 4133 }, 4134 msg: "normal case with BasedOn set", 4135 }, 4136 } 4137 4138 for _, test := range tests { 4139 result := generateTLSRedirectConfig(test.inputTLS) 4140 if !reflect.DeepEqual(result, test.expected) { 4141 t.Errorf("generateTLSRedirectConfig() returned %v but expected %v for the case of %s", result, test.expected, test.msg) 4142 } 4143 } 4144 } 4145 4146 func TestGenerateTLSRedirectBasedOn(t *testing.T) { 4147 tests := []struct { 4148 basedOn string 4149 expected string 4150 }{ 4151 { 4152 basedOn: "scheme", 4153 expected: "$scheme", 4154 }, 4155 { 4156 basedOn: "x-forwarded-proto", 4157 expected: "$http_x_forwarded_proto", 4158 }, 4159 { 4160 basedOn: "", 4161 expected: "$scheme", 4162 }, 4163 } 4164 for _, test := range tests { 4165 result := generateTLSRedirectBasedOn(test.basedOn) 4166 if result != test.expected { 4167 t.Errorf("generateTLSRedirectBasedOn(%v) returned %v but expected %v", test.basedOn, result, test.expected) 4168 } 4169 } 4170 } 4171 4172 func TestCreateUpstreamsForPlus(t *testing.T) { 4173 virtualServerEx := VirtualServerEx{ 4174 VirtualServer: &conf_v1.VirtualServer{ 4175 ObjectMeta: meta_v1.ObjectMeta{ 4176 Name: "cafe", 4177 Namespace: "default", 4178 }, 4179 Spec: conf_v1.VirtualServerSpec{ 4180 Host: "cafe.example.com", 4181 Upstreams: []conf_v1.Upstream{ 4182 { 4183 Name: "tea", 4184 Service: "tea-svc", 4185 Port: 80, 4186 }, 4187 { 4188 Name: "test", 4189 Service: "test-svc", 4190 Port: 80, 4191 }, 4192 { 4193 Name: "subselector-test", 4194 Service: "test-svc", 4195 Subselector: map[string]string{"vs": "works"}, 4196 Port: 80, 4197 }, 4198 { 4199 Name: "external", 4200 Service: "external-svc", 4201 Port: 80, 4202 }, 4203 }, 4204 Routes: []conf_v1.Route{ 4205 { 4206 Path: "/tea", 4207 Action: &conf_v1.Action{ 4208 Pass: "tea", 4209 }, 4210 }, 4211 { 4212 Path: "/coffee", 4213 Route: "default/coffee", 4214 }, 4215 { 4216 Path: "/external", 4217 Action: &conf_v1.Action{ 4218 Pass: "external", 4219 }, 4220 }, 4221 }, 4222 }, 4223 }, 4224 Endpoints: map[string][]string{ 4225 "default/tea-svc:80": { 4226 "10.0.0.20:80", 4227 }, 4228 "default/test-svc:80": {}, 4229 "default/test-svc_vs=works:80": { 4230 "10.0.0.30:80", 4231 }, 4232 "default/coffee-svc:80": { 4233 "10.0.0.40:80", 4234 }, 4235 "default/test-svc_vsr=works:80": { 4236 "10.0.0.50:80", 4237 }, 4238 "default/external-svc:80": { 4239 "example.com:80", 4240 }, 4241 }, 4242 ExternalNameSvcs: map[string]bool{ 4243 "default/external-svc": true, 4244 }, 4245 VirtualServerRoutes: []*conf_v1.VirtualServerRoute{ 4246 { 4247 ObjectMeta: meta_v1.ObjectMeta{ 4248 Name: "coffee", 4249 Namespace: "default", 4250 }, 4251 Spec: conf_v1.VirtualServerRouteSpec{ 4252 Host: "cafe.example.com", 4253 Upstreams: []conf_v1.Upstream{ 4254 { 4255 Name: "coffee", 4256 Service: "coffee-svc", 4257 Port: 80, 4258 }, 4259 { 4260 Name: "subselector-test", 4261 Service: "test-svc", 4262 Subselector: map[string]string{"vsr": "works"}, 4263 Port: 80, 4264 }, 4265 }, 4266 Subroutes: []conf_v1.Route{ 4267 { 4268 Path: "/coffee", 4269 Action: &conf_v1.Action{ 4270 Pass: "coffee", 4271 }, 4272 }, 4273 { 4274 Path: "/coffee/sub", 4275 Action: &conf_v1.Action{ 4276 Pass: "subselector-test", 4277 }, 4278 }, 4279 }, 4280 }, 4281 }, 4282 }, 4283 } 4284 4285 expected := []version2.Upstream{ 4286 { 4287 Name: "vs_default_cafe_tea", 4288 UpstreamLabels: version2.UpstreamLabels{ 4289 Service: "tea-svc", 4290 ResourceType: "virtualserver", 4291 ResourceNamespace: "default", 4292 ResourceName: "cafe", 4293 }, 4294 Servers: []version2.UpstreamServer{ 4295 { 4296 Address: "10.0.0.20:80", 4297 }, 4298 }, 4299 }, 4300 { 4301 Name: "vs_default_cafe_test", 4302 UpstreamLabels: version2.UpstreamLabels{ 4303 Service: "test-svc", 4304 ResourceType: "virtualserver", 4305 ResourceNamespace: "default", 4306 ResourceName: "cafe", 4307 }, 4308 Servers: nil, 4309 }, 4310 { 4311 Name: "vs_default_cafe_subselector-test", 4312 UpstreamLabels: version2.UpstreamLabels{ 4313 Service: "test-svc", 4314 ResourceType: "virtualserver", 4315 ResourceNamespace: "default", 4316 ResourceName: "cafe", 4317 }, 4318 Servers: []version2.UpstreamServer{ 4319 { 4320 Address: "10.0.0.30:80", 4321 }, 4322 }, 4323 }, 4324 { 4325 Name: "vs_default_cafe_vsr_default_coffee_coffee", 4326 UpstreamLabels: version2.UpstreamLabels{ 4327 Service: "coffee-svc", 4328 ResourceType: "virtualserverroute", 4329 ResourceNamespace: "default", 4330 ResourceName: "coffee", 4331 }, 4332 Servers: []version2.UpstreamServer{ 4333 { 4334 Address: "10.0.0.40:80", 4335 }, 4336 }, 4337 }, 4338 { 4339 Name: "vs_default_cafe_vsr_default_coffee_subselector-test", 4340 UpstreamLabels: version2.UpstreamLabels{ 4341 Service: "test-svc", 4342 ResourceType: "virtualserverroute", 4343 ResourceNamespace: "default", 4344 ResourceName: "coffee", 4345 }, 4346 Servers: []version2.UpstreamServer{ 4347 { 4348 Address: "10.0.0.50:80", 4349 }, 4350 }, 4351 }, 4352 } 4353 4354 result := createUpstreamsForPlus(&virtualServerEx, &ConfigParams{}, &StaticConfigParams{}) 4355 if !reflect.DeepEqual(result, expected) { 4356 t.Errorf("createUpstreamsForPlus returned \n%v but expected \n%v", result, expected) 4357 } 4358 } 4359 4360 func TestCreateUpstreamServersConfigForPlus(t *testing.T) { 4361 upstream := version2.Upstream{ 4362 Servers: []version2.UpstreamServer{ 4363 { 4364 Address: "10.0.0.20:80", 4365 }, 4366 }, 4367 MaxFails: 21, 4368 MaxConns: 16, 4369 FailTimeout: "30s", 4370 SlowStart: "50s", 4371 } 4372 4373 expected := nginx.ServerConfig{ 4374 MaxFails: 21, 4375 MaxConns: 16, 4376 FailTimeout: "30s", 4377 SlowStart: "50s", 4378 } 4379 4380 result := createUpstreamServersConfigForPlus(upstream) 4381 if !reflect.DeepEqual(result, expected) { 4382 t.Errorf("createUpstreamServersConfigForPlus returned %v but expected %v", result, expected) 4383 } 4384 } 4385 4386 func TestCreateUpstreamServersConfigForPlusNoUpstreams(t *testing.T) { 4387 noUpstream := version2.Upstream{} 4388 expected := nginx.ServerConfig{} 4389 4390 result := createUpstreamServersConfigForPlus(noUpstream) 4391 if !reflect.DeepEqual(result, expected) { 4392 t.Errorf("createUpstreamServersConfigForPlus returned %v but expected %v", result, expected) 4393 } 4394 } 4395 4396 func TestGenerateSplits(t *testing.T) { 4397 originalPath := "/path" 4398 splits := []conf_v1.Split{ 4399 { 4400 Weight: 90, 4401 Action: &conf_v1.Action{ 4402 Proxy: &conf_v1.ActionProxy{ 4403 Upstream: "coffee-v1", 4404 RewritePath: "/rewrite", 4405 }, 4406 }, 4407 }, 4408 { 4409 Weight: 9, 4410 Action: &conf_v1.Action{ 4411 Pass: "coffee-v2", 4412 }, 4413 }, 4414 { 4415 Weight: 1, 4416 Action: &conf_v1.Action{ 4417 Return: &conf_v1.ActionReturn{ 4418 Body: "hello", 4419 }, 4420 }, 4421 }, 4422 } 4423 4424 virtualServer := conf_v1.VirtualServer{ 4425 ObjectMeta: meta_v1.ObjectMeta{ 4426 Name: "cafe", 4427 Namespace: "default", 4428 }, 4429 } 4430 upstreamNamer := newUpstreamNamerForVirtualServer(&virtualServer) 4431 variableNamer := newVariableNamer(&virtualServer) 4432 scIndex := 1 4433 cfgParams := ConfigParams{} 4434 crUpstreams := map[string]conf_v1.Upstream{ 4435 "vs_default_cafe_coffee-v1": { 4436 Service: "coffee-v1", 4437 }, 4438 "vs_default_cafe_coffee-v2": { 4439 Service: "coffee-v2", 4440 }, 4441 } 4442 locSnippet := "# location snippet" 4443 enableSnippets := true 4444 errorPages := []conf_v1.ErrorPage{ 4445 { 4446 Codes: []int{400, 500}, 4447 Return: &conf_v1.ErrorPageReturn{ 4448 ActionReturn: conf_v1.ActionReturn{ 4449 Code: 200, 4450 Type: "application/json", 4451 Body: `{\"message\": \"ok\"}`, 4452 }, 4453 Headers: []conf_v1.Header{ 4454 { 4455 Name: "Set-Cookie", 4456 Value: "cookie1=value", 4457 }, 4458 }, 4459 }, 4460 Redirect: nil, 4461 }, 4462 { 4463 Codes: []int{500, 502}, 4464 Return: nil, 4465 Redirect: &conf_v1.ErrorPageRedirect{ 4466 ActionRedirect: conf_v1.ActionRedirect{ 4467 URL: "http://nginx.com", 4468 Code: 301, 4469 }, 4470 }, 4471 }, 4472 } 4473 4474 expectedSplitClient := version2.SplitClient{ 4475 Source: "$request_id", 4476 Variable: "$vs_default_cafe_splits_1", 4477 Distributions: []version2.Distribution{ 4478 { 4479 Weight: "90%", 4480 Value: "/internal_location_splits_1_split_0", 4481 }, 4482 { 4483 Weight: "9%", 4484 Value: "/internal_location_splits_1_split_1", 4485 }, 4486 { 4487 Weight: "1%", 4488 Value: "/internal_location_splits_1_split_2", 4489 }, 4490 }, 4491 } 4492 expectedLocations := []version2.Location{ 4493 { 4494 Path: "/internal_location_splits_1_split_0", 4495 ProxyPass: "http://vs_default_cafe_coffee-v1", 4496 Rewrites: []string{ 4497 "^ $request_uri", 4498 fmt.Sprintf(`"^%v(.*)$" "/rewrite$1" break`, originalPath), 4499 }, 4500 ProxyNextUpstream: "error timeout", 4501 ProxyNextUpstreamTimeout: "0s", 4502 ProxyNextUpstreamTries: 0, 4503 ProxyInterceptErrors: true, 4504 Internal: true, 4505 ErrorPages: []version2.ErrorPage{ 4506 { 4507 Name: "@error_page_0_0", 4508 Codes: "400 500", 4509 ResponseCode: 200, 4510 }, 4511 { 4512 Name: "http://nginx.com", 4513 Codes: "500 502", 4514 ResponseCode: 301, 4515 }, 4516 }, 4517 ProxySSLName: "coffee-v1.default.svc", 4518 ProxyPassRequestHeaders: true, 4519 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 4520 Snippets: []string{locSnippet}, 4521 ServiceName: "coffee-v1", 4522 IsVSR: true, 4523 VSRName: "coffee", 4524 VSRNamespace: "default", 4525 }, 4526 { 4527 Path: "/internal_location_splits_1_split_1", 4528 ProxyPass: "http://vs_default_cafe_coffee-v2$request_uri", 4529 ProxyNextUpstream: "error timeout", 4530 ProxyNextUpstreamTimeout: "0s", 4531 ProxyNextUpstreamTries: 0, 4532 ProxyInterceptErrors: true, 4533 Internal: true, 4534 ErrorPages: []version2.ErrorPage{ 4535 { 4536 Name: "@error_page_0_0", 4537 Codes: "400 500", 4538 ResponseCode: 200, 4539 }, 4540 { 4541 Name: "http://nginx.com", 4542 Codes: "500 502", 4543 ResponseCode: 301, 4544 }, 4545 }, 4546 ProxySSLName: "coffee-v2.default.svc", 4547 ProxyPassRequestHeaders: true, 4548 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 4549 Snippets: []string{locSnippet}, 4550 ServiceName: "coffee-v2", 4551 IsVSR: true, 4552 VSRName: "coffee", 4553 VSRNamespace: "default", 4554 }, 4555 { 4556 Path: "/internal_location_splits_1_split_2", 4557 ProxyInterceptErrors: true, 4558 ErrorPages: []version2.ErrorPage{ 4559 { 4560 Name: "@return_1", 4561 Codes: "418", 4562 ResponseCode: 200, 4563 }, 4564 }, 4565 InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock", 4566 }, 4567 } 4568 expectedReturnLocations := []version2.ReturnLocation{ 4569 { 4570 Name: "@return_1", 4571 DefaultType: "text/plain", 4572 Return: version2.Return{ 4573 Code: 0, 4574 Text: "hello", 4575 }, 4576 }, 4577 } 4578 returnLocationIndex := 1 4579 4580 resultSplitClient, resultLocations, resultReturnLocations := generateSplits( 4581 splits, 4582 upstreamNamer, 4583 crUpstreams, 4584 variableNamer, 4585 scIndex, 4586 &cfgParams, 4587 errorPages, 4588 0, 4589 originalPath, 4590 locSnippet, 4591 enableSnippets, 4592 returnLocationIndex, 4593 true, 4594 "coffee", 4595 "default", 4596 ) 4597 if !reflect.DeepEqual(resultSplitClient, expectedSplitClient) { 4598 t.Errorf("generateSplits() returned \n%+v but expected \n%+v", resultSplitClient, expectedSplitClient) 4599 } 4600 if !reflect.DeepEqual(resultLocations, expectedLocations) { 4601 t.Errorf("generateSplits() returned \n%+v but expected \n%+v", resultLocations, expectedLocations) 4602 } 4603 if !reflect.DeepEqual(resultReturnLocations, expectedReturnLocations) { 4604 t.Errorf("generateSplits() returned \n%+v but expected \n%+v", resultReturnLocations, expectedReturnLocations) 4605 } 4606 } 4607 4608 func TestGenerateDefaultSplitsConfig(t *testing.T) { 4609 route := conf_v1.Route{ 4610 Path: "/", 4611 Splits: []conf_v1.Split{ 4612 { 4613 Weight: 90, 4614 Action: &conf_v1.Action{ 4615 Pass: "coffee-v1", 4616 }, 4617 }, 4618 { 4619 Weight: 10, 4620 Action: &conf_v1.Action{ 4621 Pass: "coffee-v2", 4622 }, 4623 }, 4624 }, 4625 } 4626 virtualServer := conf_v1.VirtualServer{ 4627 ObjectMeta: meta_v1.ObjectMeta{ 4628 Name: "cafe", 4629 Namespace: "default", 4630 }, 4631 } 4632 upstreamNamer := newUpstreamNamerForVirtualServer(&virtualServer) 4633 variableNamer := newVariableNamer(&virtualServer) 4634 index := 1 4635 4636 expected := routingCfg{ 4637 SplitClients: []version2.SplitClient{ 4638 { 4639 Source: "$request_id", 4640 Variable: "$vs_default_cafe_splits_1", 4641 Distributions: []version2.Distribution{ 4642 { 4643 Weight: "90%", 4644 Value: "/internal_location_splits_1_split_0", 4645 }, 4646 { 4647 Weight: "10%", 4648 Value: "/internal_location_splits_1_split_1", 4649 }, 4650 }, 4651 }, 4652 }, 4653 Locations: []version2.Location{ 4654 { 4655 Path: "/internal_location_splits_1_split_0", 4656 ProxyPass: "http://vs_default_cafe_coffee-v1$request_uri", 4657 ProxyNextUpstream: "error timeout", 4658 ProxyNextUpstreamTimeout: "0s", 4659 ProxyNextUpstreamTries: 0, 4660 Internal: true, 4661 ProxySSLName: "coffee-v1.default.svc", 4662 ProxyPassRequestHeaders: true, 4663 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 4664 ServiceName: "coffee-v1", 4665 IsVSR: true, 4666 VSRName: "coffee", 4667 VSRNamespace: "default", 4668 }, 4669 { 4670 Path: "/internal_location_splits_1_split_1", 4671 ProxyPass: "http://vs_default_cafe_coffee-v2$request_uri", 4672 ProxyNextUpstream: "error timeout", 4673 ProxyNextUpstreamTimeout: "0s", 4674 ProxyNextUpstreamTries: 0, 4675 Internal: true, 4676 ProxySSLName: "coffee-v2.default.svc", 4677 ProxyPassRequestHeaders: true, 4678 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 4679 ServiceName: "coffee-v2", 4680 IsVSR: true, 4681 VSRName: "coffee", 4682 VSRNamespace: "default", 4683 }, 4684 }, 4685 InternalRedirectLocation: version2.InternalRedirectLocation{ 4686 Path: "/", 4687 Destination: "$vs_default_cafe_splits_1", 4688 }, 4689 } 4690 4691 cfgParams := ConfigParams{} 4692 locSnippet := "" 4693 enableSnippets := false 4694 crUpstreams := map[string]conf_v1.Upstream{ 4695 "vs_default_cafe_coffee-v1": { 4696 Service: "coffee-v1", 4697 }, 4698 "vs_default_cafe_coffee-v2": { 4699 Service: "coffee-v2", 4700 }, 4701 } 4702 4703 result := generateDefaultSplitsConfig(route, upstreamNamer, crUpstreams, variableNamer, index, &cfgParams, 4704 route.ErrorPages, 0, "", locSnippet, enableSnippets, 0, true, "coffee", "default") 4705 if !reflect.DeepEqual(result, expected) { 4706 t.Errorf("generateDefaultSplitsConfig() returned \n%+v but expected \n%+v", result, expected) 4707 } 4708 } 4709 4710 func TestGenerateMatchesConfig(t *testing.T) { 4711 route := conf_v1.Route{ 4712 Path: "/", 4713 Matches: []conf_v1.Match{ 4714 { 4715 Conditions: []conf_v1.Condition{ 4716 { 4717 Header: "x-version", 4718 Value: "v1", 4719 }, 4720 { 4721 Cookie: "user", 4722 Value: "john", 4723 }, 4724 { 4725 Argument: "answer", 4726 Value: "yes", 4727 }, 4728 { 4729 Variable: "$request_method", 4730 Value: "GET", 4731 }, 4732 }, 4733 Action: &conf_v1.Action{ 4734 Pass: "coffee-v1", 4735 }, 4736 }, 4737 { 4738 Conditions: []conf_v1.Condition{ 4739 { 4740 Header: "x-version", 4741 Value: "v2", 4742 }, 4743 { 4744 Cookie: "user", 4745 Value: "paul", 4746 }, 4747 { 4748 Argument: "answer", 4749 Value: "no", 4750 }, 4751 { 4752 Variable: "$request_method", 4753 Value: "POST", 4754 }, 4755 }, 4756 Splits: []conf_v1.Split{ 4757 { 4758 Weight: 90, 4759 Action: &conf_v1.Action{ 4760 Pass: "coffee-v1", 4761 }, 4762 }, 4763 { 4764 Weight: 10, 4765 Action: &conf_v1.Action{ 4766 Pass: "coffee-v2", 4767 }, 4768 }, 4769 }, 4770 }, 4771 }, 4772 Action: &conf_v1.Action{ 4773 Pass: "tea", 4774 }, 4775 } 4776 virtualServer := conf_v1.VirtualServer{ 4777 ObjectMeta: meta_v1.ObjectMeta{ 4778 Name: "cafe", 4779 Namespace: "default", 4780 }, 4781 } 4782 errorPages := []conf_v1.ErrorPage{ 4783 { 4784 Codes: []int{400, 500}, 4785 Return: &conf_v1.ErrorPageReturn{ 4786 ActionReturn: conf_v1.ActionReturn{ 4787 Code: 200, 4788 Type: "application/json", 4789 Body: `{\"message\": \"ok\"}`, 4790 }, 4791 Headers: []conf_v1.Header{ 4792 { 4793 Name: "Set-Cookie", 4794 Value: "cookie1=value", 4795 }, 4796 }, 4797 }, 4798 Redirect: nil, 4799 }, 4800 { 4801 Codes: []int{500, 502}, 4802 Return: nil, 4803 Redirect: &conf_v1.ErrorPageRedirect{ 4804 ActionRedirect: conf_v1.ActionRedirect{ 4805 URL: "http://nginx.com", 4806 Code: 301, 4807 }, 4808 }, 4809 }, 4810 } 4811 upstreamNamer := newUpstreamNamerForVirtualServer(&virtualServer) 4812 variableNamer := newVariableNamer(&virtualServer) 4813 index := 1 4814 scIndex := 2 4815 4816 expected := routingCfg{ 4817 Maps: []version2.Map{ 4818 { 4819 Source: "$http_x_version", 4820 Variable: "$vs_default_cafe_matches_1_match_0_cond_0", 4821 Parameters: []version2.Parameter{ 4822 { 4823 Value: `"v1"`, 4824 Result: "$vs_default_cafe_matches_1_match_0_cond_1", 4825 }, 4826 { 4827 Value: "default", 4828 Result: "0", 4829 }, 4830 }, 4831 }, 4832 { 4833 Source: "$cookie_user", 4834 Variable: "$vs_default_cafe_matches_1_match_0_cond_1", 4835 Parameters: []version2.Parameter{ 4836 { 4837 Value: `"john"`, 4838 Result: "$vs_default_cafe_matches_1_match_0_cond_2", 4839 }, 4840 { 4841 Value: "default", 4842 Result: "0", 4843 }, 4844 }, 4845 }, 4846 { 4847 Source: "$arg_answer", 4848 Variable: "$vs_default_cafe_matches_1_match_0_cond_2", 4849 Parameters: []version2.Parameter{ 4850 { 4851 Value: `"yes"`, 4852 Result: "$vs_default_cafe_matches_1_match_0_cond_3", 4853 }, 4854 { 4855 Value: "default", 4856 Result: "0", 4857 }, 4858 }, 4859 }, 4860 { 4861 Source: "$request_method", 4862 Variable: "$vs_default_cafe_matches_1_match_0_cond_3", 4863 Parameters: []version2.Parameter{ 4864 { 4865 Value: `"GET"`, 4866 Result: "1", 4867 }, 4868 { 4869 Value: "default", 4870 Result: "0", 4871 }, 4872 }, 4873 }, 4874 { 4875 Source: "$http_x_version", 4876 Variable: "$vs_default_cafe_matches_1_match_1_cond_0", 4877 Parameters: []version2.Parameter{ 4878 { 4879 Value: `"v2"`, 4880 Result: "$vs_default_cafe_matches_1_match_1_cond_1", 4881 }, 4882 { 4883 Value: "default", 4884 Result: "0", 4885 }, 4886 }, 4887 }, 4888 { 4889 Source: "$cookie_user", 4890 Variable: "$vs_default_cafe_matches_1_match_1_cond_1", 4891 Parameters: []version2.Parameter{ 4892 { 4893 Value: `"paul"`, 4894 Result: "$vs_default_cafe_matches_1_match_1_cond_2", 4895 }, 4896 { 4897 Value: "default", 4898 Result: "0", 4899 }, 4900 }, 4901 }, 4902 { 4903 Source: "$arg_answer", 4904 Variable: "$vs_default_cafe_matches_1_match_1_cond_2", 4905 Parameters: []version2.Parameter{ 4906 { 4907 Value: `"no"`, 4908 Result: "$vs_default_cafe_matches_1_match_1_cond_3", 4909 }, 4910 { 4911 Value: "default", 4912 Result: "0", 4913 }, 4914 }, 4915 }, 4916 { 4917 Source: "$request_method", 4918 Variable: "$vs_default_cafe_matches_1_match_1_cond_3", 4919 Parameters: []version2.Parameter{ 4920 { 4921 Value: `"POST"`, 4922 Result: "1", 4923 }, 4924 { 4925 Value: "default", 4926 Result: "0", 4927 }, 4928 }, 4929 }, 4930 { 4931 Source: "$vs_default_cafe_matches_1_match_0_cond_0$vs_default_cafe_matches_1_match_1_cond_0", 4932 Variable: "$vs_default_cafe_matches_1", 4933 Parameters: []version2.Parameter{ 4934 { 4935 Value: "~^1", 4936 Result: "/internal_location_matches_1_match_0", 4937 }, 4938 { 4939 Value: "~^01", 4940 Result: "$vs_default_cafe_splits_2", 4941 }, 4942 { 4943 Value: "default", 4944 Result: "/internal_location_matches_1_default", 4945 }, 4946 }, 4947 }, 4948 }, 4949 Locations: []version2.Location{ 4950 { 4951 Path: "/internal_location_matches_1_match_0", 4952 ProxyPass: "http://vs_default_cafe_coffee-v1$request_uri", 4953 ProxyNextUpstream: "error timeout", 4954 ProxyNextUpstreamTimeout: "0s", 4955 ProxyNextUpstreamTries: 0, 4956 ProxyInterceptErrors: true, 4957 Internal: true, 4958 ErrorPages: []version2.ErrorPage{ 4959 { 4960 Name: "@error_page_2_0", 4961 Codes: "400 500", 4962 ResponseCode: 200, 4963 }, 4964 { 4965 Name: "http://nginx.com", 4966 Codes: "500 502", 4967 ResponseCode: 301, 4968 }, 4969 }, 4970 ProxySSLName: "coffee-v1.default.svc", 4971 ProxyPassRequestHeaders: true, 4972 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 4973 ServiceName: "coffee-v1", 4974 IsVSR: false, 4975 VSRName: "", 4976 VSRNamespace: "", 4977 }, 4978 { 4979 Path: "/internal_location_splits_2_split_0", 4980 ProxyPass: "http://vs_default_cafe_coffee-v1$request_uri", 4981 ProxyNextUpstream: "error timeout", 4982 ProxyNextUpstreamTimeout: "0s", 4983 ProxyNextUpstreamTries: 0, 4984 ProxyInterceptErrors: true, 4985 Internal: true, 4986 ErrorPages: []version2.ErrorPage{ 4987 { 4988 Name: "@error_page_2_0", 4989 Codes: "400 500", 4990 ResponseCode: 200, 4991 }, 4992 { 4993 Name: "http://nginx.com", 4994 Codes: "500 502", 4995 ResponseCode: 301, 4996 }, 4997 }, 4998 ProxySSLName: "coffee-v1.default.svc", 4999 ProxyPassRequestHeaders: true, 5000 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 5001 ServiceName: "coffee-v1", 5002 IsVSR: false, 5003 VSRName: "", 5004 VSRNamespace: "", 5005 }, 5006 { 5007 Path: "/internal_location_splits_2_split_1", 5008 ProxyPass: "http://vs_default_cafe_coffee-v2$request_uri", 5009 ProxyNextUpstream: "error timeout", 5010 ProxyNextUpstreamTimeout: "0s", 5011 ProxyNextUpstreamTries: 0, 5012 ProxyInterceptErrors: true, 5013 Internal: true, 5014 ErrorPages: []version2.ErrorPage{ 5015 { 5016 Name: "@error_page_2_0", 5017 Codes: "400 500", 5018 ResponseCode: 200, 5019 }, 5020 { 5021 Name: "http://nginx.com", 5022 Codes: "500 502", 5023 ResponseCode: 301, 5024 }, 5025 }, 5026 ProxySSLName: "coffee-v2.default.svc", 5027 ProxyPassRequestHeaders: true, 5028 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 5029 ServiceName: "coffee-v2", 5030 IsVSR: false, 5031 VSRName: "", 5032 VSRNamespace: "", 5033 }, 5034 { 5035 Path: "/internal_location_matches_1_default", 5036 ProxyPass: "http://vs_default_cafe_tea$request_uri", 5037 ProxyNextUpstream: "error timeout", 5038 ProxyNextUpstreamTimeout: "0s", 5039 ProxyNextUpstreamTries: 0, 5040 ProxyInterceptErrors: true, 5041 Internal: true, 5042 ErrorPages: []version2.ErrorPage{ 5043 { 5044 Name: "@error_page_2_0", 5045 Codes: "400 500", 5046 ResponseCode: 200, 5047 }, 5048 { 5049 Name: "http://nginx.com", 5050 Codes: "500 502", 5051 ResponseCode: 301, 5052 }, 5053 }, 5054 ProxySSLName: "tea.default.svc", 5055 ProxyPassRequestHeaders: true, 5056 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 5057 ServiceName: "tea", 5058 IsVSR: false, 5059 VSRName: "", 5060 VSRNamespace: "", 5061 }, 5062 }, 5063 InternalRedirectLocation: version2.InternalRedirectLocation{ 5064 Path: "/", 5065 Destination: "$vs_default_cafe_matches_1", 5066 }, 5067 SplitClients: []version2.SplitClient{ 5068 { 5069 Source: "$request_id", 5070 Variable: "$vs_default_cafe_splits_2", 5071 Distributions: []version2.Distribution{ 5072 { 5073 Weight: "90%", 5074 Value: "/internal_location_splits_2_split_0", 5075 }, 5076 { 5077 Weight: "10%", 5078 Value: "/internal_location_splits_2_split_1", 5079 }, 5080 }, 5081 }, 5082 }, 5083 } 5084 5085 cfgParams := ConfigParams{} 5086 enableSnippets := false 5087 locSnippets := "" 5088 crUpstreams := map[string]conf_v1.Upstream{ 5089 "vs_default_cafe_coffee-v1": {Service: "coffee-v1"}, 5090 "vs_default_cafe_coffee-v2": {Service: "coffee-v2"}, 5091 "vs_default_cafe_tea": {Service: "tea"}, 5092 } 5093 5094 result := generateMatchesConfig( 5095 route, 5096 upstreamNamer, 5097 crUpstreams, 5098 variableNamer, 5099 index, 5100 scIndex, 5101 &cfgParams, 5102 errorPages, 5103 2, 5104 locSnippets, 5105 enableSnippets, 5106 0, 5107 false, 5108 "", 5109 "", 5110 ) 5111 if !reflect.DeepEqual(result, expected) { 5112 t.Errorf("generateMatchesConfig() returned \n%+v but expected \n%+v", result, expected) 5113 } 5114 } 5115 5116 func TestGenerateMatchesConfigWithMultipleSplits(t *testing.T) { 5117 route := conf_v1.Route{ 5118 Path: "/", 5119 Matches: []conf_v1.Match{ 5120 { 5121 Conditions: []conf_v1.Condition{ 5122 { 5123 Header: "x-version", 5124 Value: "v1", 5125 }, 5126 }, 5127 Splits: []conf_v1.Split{ 5128 { 5129 Weight: 30, 5130 Action: &conf_v1.Action{ 5131 Pass: "coffee-v1", 5132 }, 5133 }, 5134 { 5135 Weight: 70, 5136 Action: &conf_v1.Action{ 5137 Pass: "coffee-v2", 5138 }, 5139 }, 5140 }, 5141 }, 5142 { 5143 Conditions: []conf_v1.Condition{ 5144 { 5145 Header: "x-version", 5146 Value: "v2", 5147 }, 5148 }, 5149 Splits: []conf_v1.Split{ 5150 { 5151 Weight: 90, 5152 Action: &conf_v1.Action{ 5153 Pass: "coffee-v2", 5154 }, 5155 }, 5156 { 5157 Weight: 10, 5158 Action: &conf_v1.Action{ 5159 Pass: "coffee-v1", 5160 }, 5161 }, 5162 }, 5163 }, 5164 }, 5165 Splits: []conf_v1.Split{ 5166 { 5167 Weight: 99, 5168 Action: &conf_v1.Action{ 5169 Pass: "coffee-v1", 5170 }, 5171 }, 5172 { 5173 Weight: 1, 5174 Action: &conf_v1.Action{ 5175 Pass: "coffee-v2", 5176 }, 5177 }, 5178 }, 5179 } 5180 virtualServer := conf_v1.VirtualServer{ 5181 ObjectMeta: meta_v1.ObjectMeta{ 5182 Name: "cafe", 5183 Namespace: "default", 5184 }, 5185 } 5186 upstreamNamer := newUpstreamNamerForVirtualServer(&virtualServer) 5187 variableNamer := newVariableNamer(&virtualServer) 5188 index := 1 5189 scIndex := 2 5190 errorPages := []conf_v1.ErrorPage{ 5191 { 5192 Codes: []int{400, 500}, 5193 Return: &conf_v1.ErrorPageReturn{ 5194 ActionReturn: conf_v1.ActionReturn{ 5195 Code: 200, 5196 Type: "application/json", 5197 Body: `{\"message\": \"ok\"}`, 5198 }, 5199 Headers: []conf_v1.Header{ 5200 { 5201 Name: "Set-Cookie", 5202 Value: "cookie1=value", 5203 }, 5204 }, 5205 }, 5206 Redirect: nil, 5207 }, 5208 { 5209 Codes: []int{500, 502}, 5210 Return: nil, 5211 Redirect: &conf_v1.ErrorPageRedirect{ 5212 ActionRedirect: conf_v1.ActionRedirect{ 5213 URL: "http://nginx.com", 5214 Code: 301, 5215 }, 5216 }, 5217 }, 5218 } 5219 5220 expected := routingCfg{ 5221 Maps: []version2.Map{ 5222 { 5223 Source: "$http_x_version", 5224 Variable: "$vs_default_cafe_matches_1_match_0_cond_0", 5225 Parameters: []version2.Parameter{ 5226 { 5227 Value: `"v1"`, 5228 Result: "1", 5229 }, 5230 { 5231 Value: "default", 5232 Result: "0", 5233 }, 5234 }, 5235 }, 5236 { 5237 Source: "$http_x_version", 5238 Variable: "$vs_default_cafe_matches_1_match_1_cond_0", 5239 Parameters: []version2.Parameter{ 5240 { 5241 Value: `"v2"`, 5242 Result: "1", 5243 }, 5244 { 5245 Value: "default", 5246 Result: "0", 5247 }, 5248 }, 5249 }, 5250 { 5251 Source: "$vs_default_cafe_matches_1_match_0_cond_0$vs_default_cafe_matches_1_match_1_cond_0", 5252 Variable: "$vs_default_cafe_matches_1", 5253 Parameters: []version2.Parameter{ 5254 { 5255 Value: "~^1", 5256 Result: "$vs_default_cafe_splits_2", 5257 }, 5258 { 5259 Value: "~^01", 5260 Result: "$vs_default_cafe_splits_3", 5261 }, 5262 { 5263 Value: "default", 5264 Result: "$vs_default_cafe_splits_4", 5265 }, 5266 }, 5267 }, 5268 }, 5269 Locations: []version2.Location{ 5270 { 5271 Path: "/internal_location_splits_2_split_0", 5272 ProxyPass: "http://vs_default_cafe_coffee-v1$request_uri", 5273 ProxyNextUpstream: "error timeout", 5274 ProxyNextUpstreamTimeout: "0s", 5275 ProxyNextUpstreamTries: 0, 5276 Internal: true, 5277 ErrorPages: []version2.ErrorPage{ 5278 { 5279 Name: "@error_page_0_0", 5280 Codes: "400 500", 5281 ResponseCode: 200, 5282 }, 5283 { 5284 Name: "http://nginx.com", 5285 Codes: "500 502", 5286 ResponseCode: 301, 5287 }, 5288 }, 5289 ProxyInterceptErrors: true, 5290 ProxySSLName: "coffee-v1.default.svc", 5291 ProxyPassRequestHeaders: true, 5292 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 5293 ServiceName: "coffee-v1", 5294 IsVSR: true, 5295 VSRName: "coffee", 5296 VSRNamespace: "default", 5297 }, 5298 { 5299 Path: "/internal_location_splits_2_split_1", 5300 ProxyPass: "http://vs_default_cafe_coffee-v2$request_uri", 5301 ProxyNextUpstream: "error timeout", 5302 ProxyNextUpstreamTimeout: "0s", 5303 ProxyNextUpstreamTries: 0, 5304 Internal: true, 5305 ErrorPages: []version2.ErrorPage{ 5306 { 5307 Name: "@error_page_0_0", 5308 Codes: "400 500", 5309 ResponseCode: 200, 5310 }, 5311 { 5312 Name: "http://nginx.com", 5313 Codes: "500 502", 5314 ResponseCode: 301, 5315 }, 5316 }, 5317 ProxyInterceptErrors: true, 5318 ProxySSLName: "coffee-v2.default.svc", 5319 ProxyPassRequestHeaders: true, 5320 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 5321 ServiceName: "coffee-v2", 5322 IsVSR: true, 5323 VSRName: "coffee", 5324 VSRNamespace: "default", 5325 }, 5326 { 5327 Path: "/internal_location_splits_3_split_0", 5328 ProxyPass: "http://vs_default_cafe_coffee-v2$request_uri", 5329 ProxyNextUpstream: "error timeout", 5330 ProxyNextUpstreamTimeout: "0s", 5331 ProxyNextUpstreamTries: 0, 5332 Internal: true, 5333 ErrorPages: []version2.ErrorPage{ 5334 { 5335 Name: "@error_page_0_0", 5336 Codes: "400 500", 5337 ResponseCode: 200, 5338 }, 5339 { 5340 Name: "http://nginx.com", 5341 Codes: "500 502", 5342 ResponseCode: 301, 5343 }, 5344 }, 5345 ProxyInterceptErrors: true, 5346 ProxySSLName: "coffee-v2.default.svc", 5347 ProxyPassRequestHeaders: true, 5348 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 5349 ServiceName: "coffee-v2", 5350 IsVSR: true, 5351 VSRName: "coffee", 5352 VSRNamespace: "default", 5353 }, 5354 { 5355 Path: "/internal_location_splits_3_split_1", 5356 ProxyPass: "http://vs_default_cafe_coffee-v1$request_uri", 5357 ProxyNextUpstream: "error timeout", 5358 ProxyNextUpstreamTimeout: "0s", 5359 ProxyNextUpstreamTries: 0, 5360 Internal: true, 5361 ErrorPages: []version2.ErrorPage{ 5362 { 5363 Name: "@error_page_0_0", 5364 Codes: "400 500", 5365 ResponseCode: 200, 5366 }, 5367 { 5368 Name: "http://nginx.com", 5369 Codes: "500 502", 5370 ResponseCode: 301, 5371 }, 5372 }, 5373 ProxyInterceptErrors: true, 5374 ProxySSLName: "coffee-v1.default.svc", 5375 ProxyPassRequestHeaders: true, 5376 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 5377 ServiceName: "coffee-v1", 5378 IsVSR: true, 5379 VSRName: "coffee", 5380 VSRNamespace: "default", 5381 }, 5382 { 5383 Path: "/internal_location_splits_4_split_0", 5384 ProxyPass: "http://vs_default_cafe_coffee-v1$request_uri", 5385 ProxyNextUpstream: "error timeout", 5386 ProxyNextUpstreamTimeout: "0s", 5387 ProxyNextUpstreamTries: 0, 5388 Internal: true, 5389 ErrorPages: []version2.ErrorPage{ 5390 { 5391 Name: "@error_page_0_0", 5392 Codes: "400 500", 5393 ResponseCode: 200, 5394 }, 5395 { 5396 Name: "http://nginx.com", 5397 Codes: "500 502", 5398 ResponseCode: 301, 5399 }, 5400 }, 5401 ProxyInterceptErrors: true, 5402 ProxySSLName: "coffee-v1.default.svc", 5403 ProxyPassRequestHeaders: true, 5404 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 5405 ServiceName: "coffee-v1", 5406 IsVSR: true, 5407 VSRName: "coffee", 5408 VSRNamespace: "default", 5409 }, 5410 { 5411 Path: "/internal_location_splits_4_split_1", 5412 ProxyPass: "http://vs_default_cafe_coffee-v2$request_uri", 5413 ProxyNextUpstream: "error timeout", 5414 ProxyNextUpstreamTimeout: "0s", 5415 ProxyNextUpstreamTries: 0, 5416 Internal: true, 5417 ErrorPages: []version2.ErrorPage{ 5418 { 5419 Name: "@error_page_0_0", 5420 Codes: "400 500", 5421 ResponseCode: 200, 5422 }, 5423 { 5424 Name: "http://nginx.com", 5425 Codes: "500 502", 5426 ResponseCode: 301, 5427 }, 5428 }, 5429 ProxyInterceptErrors: true, 5430 ProxySSLName: "coffee-v2.default.svc", 5431 ProxyPassRequestHeaders: true, 5432 ProxySetHeaders: []version2.Header{{Name: "Host", Value: "$host"}}, 5433 ServiceName: "coffee-v2", 5434 IsVSR: true, 5435 VSRName: "coffee", 5436 VSRNamespace: "default", 5437 }, 5438 }, 5439 InternalRedirectLocation: version2.InternalRedirectLocation{ 5440 Path: "/", 5441 Destination: "$vs_default_cafe_matches_1", 5442 }, 5443 SplitClients: []version2.SplitClient{ 5444 { 5445 Source: "$request_id", 5446 Variable: "$vs_default_cafe_splits_2", 5447 Distributions: []version2.Distribution{ 5448 { 5449 Weight: "30%", 5450 Value: "/internal_location_splits_2_split_0", 5451 }, 5452 { 5453 Weight: "70%", 5454 Value: "/internal_location_splits_2_split_1", 5455 }, 5456 }, 5457 }, 5458 { 5459 Source: "$request_id", 5460 Variable: "$vs_default_cafe_splits_3", 5461 Distributions: []version2.Distribution{ 5462 { 5463 Weight: "90%", 5464 Value: "/internal_location_splits_3_split_0", 5465 }, 5466 { 5467 Weight: "10%", 5468 Value: "/internal_location_splits_3_split_1", 5469 }, 5470 }, 5471 }, 5472 { 5473 Source: "$request_id", 5474 Variable: "$vs_default_cafe_splits_4", 5475 Distributions: []version2.Distribution{ 5476 { 5477 Weight: "99%", 5478 Value: "/internal_location_splits_4_split_0", 5479 }, 5480 { 5481 Weight: "1%", 5482 Value: "/internal_location_splits_4_split_1", 5483 }, 5484 }, 5485 }, 5486 }, 5487 } 5488 5489 cfgParams := ConfigParams{} 5490 enableSnippets := false 5491 locSnippets := "" 5492 crUpstreams := map[string]conf_v1.Upstream{ 5493 "vs_default_cafe_coffee-v1": {Service: "coffee-v1"}, 5494 "vs_default_cafe_coffee-v2": {Service: "coffee-v2"}, 5495 } 5496 result := generateMatchesConfig( 5497 route, 5498 upstreamNamer, 5499 crUpstreams, 5500 variableNamer, 5501 index, 5502 scIndex, 5503 &cfgParams, 5504 errorPages, 5505 0, 5506 locSnippets, 5507 enableSnippets, 5508 0, 5509 true, 5510 "coffee", 5511 "default", 5512 ) 5513 if !reflect.DeepEqual(result, expected) { 5514 t.Errorf("generateMatchesConfig() returned \n%+v but expected \n%+v", result, expected) 5515 } 5516 } 5517 5518 func TestGenerateValueForMatchesRouteMap(t *testing.T) { 5519 tests := []struct { 5520 input string 5521 expectedValue string 5522 expectedIsNegative bool 5523 }{ 5524 { 5525 input: "default", 5526 expectedValue: `\default`, 5527 expectedIsNegative: false, 5528 }, 5529 { 5530 input: "!default", 5531 expectedValue: `\default`, 5532 expectedIsNegative: true, 5533 }, 5534 { 5535 input: "hostnames", 5536 expectedValue: `\hostnames`, 5537 expectedIsNegative: false, 5538 }, 5539 { 5540 input: "include", 5541 expectedValue: `\include`, 5542 expectedIsNegative: false, 5543 }, 5544 { 5545 input: "volatile", 5546 expectedValue: `\volatile`, 5547 expectedIsNegative: false, 5548 }, 5549 { 5550 input: "abc", 5551 expectedValue: `"abc"`, 5552 expectedIsNegative: false, 5553 }, 5554 { 5555 input: "!abc", 5556 expectedValue: `"abc"`, 5557 expectedIsNegative: true, 5558 }, 5559 { 5560 input: "", 5561 expectedValue: `""`, 5562 expectedIsNegative: false, 5563 }, 5564 { 5565 input: "!", 5566 expectedValue: `""`, 5567 expectedIsNegative: true, 5568 }, 5569 } 5570 5571 for _, test := range tests { 5572 resultValue, resultIsNegative := generateValueForMatchesRouteMap(test.input) 5573 if resultValue != test.expectedValue { 5574 t.Errorf("generateValueForMatchesRouteMap(%q) returned %q but expected %q as the value", test.input, resultValue, test.expectedValue) 5575 } 5576 if resultIsNegative != test.expectedIsNegative { 5577 t.Errorf("generateValueForMatchesRouteMap(%q) returned %v but expected %v as the isNegative", test.input, resultIsNegative, test.expectedIsNegative) 5578 } 5579 } 5580 } 5581 5582 func TestGenerateParametersForMatchesRouteMap(t *testing.T) { 5583 tests := []struct { 5584 inputMatchedValue string 5585 inputSuccessfulResult string 5586 expected []version2.Parameter 5587 }{ 5588 { 5589 inputMatchedValue: "abc", 5590 inputSuccessfulResult: "1", 5591 expected: []version2.Parameter{ 5592 { 5593 Value: `"abc"`, 5594 Result: "1", 5595 }, 5596 { 5597 Value: "default", 5598 Result: "0", 5599 }, 5600 }, 5601 }, 5602 { 5603 inputMatchedValue: "!abc", 5604 inputSuccessfulResult: "1", 5605 expected: []version2.Parameter{ 5606 { 5607 Value: `"abc"`, 5608 Result: "0", 5609 }, 5610 { 5611 Value: "default", 5612 Result: "1", 5613 }, 5614 }, 5615 }, 5616 } 5617 5618 for _, test := range tests { 5619 result := generateParametersForMatchesRouteMap(test.inputMatchedValue, test.inputSuccessfulResult) 5620 if !reflect.DeepEqual(result, test.expected) { 5621 t.Errorf("generateParametersForMatchesRouteMap(%q, %q) returned %v but expected %v", test.inputMatchedValue, test.inputSuccessfulResult, result, test.expected) 5622 } 5623 } 5624 } 5625 5626 func TestGetNameForSourceForMatchesRouteMapFromCondition(t *testing.T) { 5627 tests := []struct { 5628 input conf_v1.Condition 5629 expected string 5630 }{ 5631 { 5632 input: conf_v1.Condition{ 5633 Header: "x-version", 5634 }, 5635 expected: "$http_x_version", 5636 }, 5637 { 5638 input: conf_v1.Condition{ 5639 Cookie: "mycookie", 5640 }, 5641 expected: "$cookie_mycookie", 5642 }, 5643 { 5644 input: conf_v1.Condition{ 5645 Argument: "arg", 5646 }, 5647 expected: "$arg_arg", 5648 }, 5649 { 5650 input: conf_v1.Condition{ 5651 Variable: "$request_method", 5652 }, 5653 expected: "$request_method", 5654 }, 5655 } 5656 5657 for _, test := range tests { 5658 result := getNameForSourceForMatchesRouteMapFromCondition(test.input) 5659 if result != test.expected { 5660 t.Errorf("getNameForSourceForMatchesRouteMapFromCondition() returned %q but expected %q for input %v", result, test.expected, test.input) 5661 } 5662 } 5663 } 5664 5665 func TestGenerateLBMethod(t *testing.T) { 5666 defaultMethod := "random two least_conn" 5667 5668 tests := []struct { 5669 input string 5670 expected string 5671 }{ 5672 { 5673 input: "", 5674 expected: defaultMethod, 5675 }, 5676 { 5677 input: "round_robin", 5678 expected: "", 5679 }, 5680 { 5681 input: "random", 5682 expected: "random", 5683 }, 5684 } 5685 for _, test := range tests { 5686 result := generateLBMethod(test.input, defaultMethod) 5687 if result != test.expected { 5688 t.Errorf("generateLBMethod() returned %q but expected %q for input '%v'", result, test.expected, test.input) 5689 } 5690 } 5691 } 5692 5693 func TestUpstreamHasKeepalive(t *testing.T) { 5694 noKeepalive := 0 5695 keepalive := 32 5696 5697 tests := []struct { 5698 upstream conf_v1.Upstream 5699 cfgParams *ConfigParams 5700 expected bool 5701 msg string 5702 }{ 5703 { 5704 conf_v1.Upstream{}, 5705 &ConfigParams{Keepalive: keepalive}, 5706 true, 5707 "upstream keepalive not set, configparam keepalive set", 5708 }, 5709 { 5710 conf_v1.Upstream{Keepalive: &noKeepalive}, 5711 &ConfigParams{Keepalive: keepalive}, 5712 false, 5713 "upstream keepalive set to 0, configparam keepalive set", 5714 }, 5715 { 5716 conf_v1.Upstream{Keepalive: &keepalive}, 5717 &ConfigParams{Keepalive: noKeepalive}, 5718 true, 5719 "upstream keepalive set, configparam keepalive set to 0", 5720 }, 5721 } 5722 5723 for _, test := range tests { 5724 result := upstreamHasKeepalive(test.upstream, test.cfgParams) 5725 if result != test.expected { 5726 t.Errorf("upstreamHasKeepalive() returned %v, but expected %v for the case of %v", result, test.expected, test.msg) 5727 } 5728 } 5729 } 5730 5731 func TestNewHealthCheckWithDefaults(t *testing.T) { 5732 upstreamName := "test-upstream" 5733 baseCfgParams := &ConfigParams{ 5734 ProxySendTimeout: "5s", 5735 ProxyReadTimeout: "5s", 5736 ProxyConnectTimeout: "5s", 5737 } 5738 expected := &version2.HealthCheck{ 5739 Name: upstreamName, 5740 ProxySendTimeout: "5s", 5741 ProxyReadTimeout: "5s", 5742 ProxyConnectTimeout: "5s", 5743 ProxyPass: fmt.Sprintf("http://%v", upstreamName), 5744 URI: "/", 5745 Interval: "5s", 5746 Jitter: "0s", 5747 Fails: 1, 5748 Passes: 1, 5749 Headers: make(map[string]string), 5750 } 5751 5752 result := newHealthCheckWithDefaults(conf_v1.Upstream{}, upstreamName, baseCfgParams) 5753 5754 if !reflect.DeepEqual(result, expected) { 5755 t.Errorf("newHealthCheckWithDefaults returned \n%v but expected \n%v", result, expected) 5756 } 5757 } 5758 5759 func TestGenerateHealthCheck(t *testing.T) { 5760 upstreamName := "test-upstream" 5761 tests := []struct { 5762 upstream conf_v1.Upstream 5763 upstreamName string 5764 expected *version2.HealthCheck 5765 msg string 5766 }{ 5767 { 5768 5769 upstream: conf_v1.Upstream{ 5770 HealthCheck: &conf_v1.HealthCheck{ 5771 Enable: true, 5772 Path: "/healthz", 5773 Interval: "5s", 5774 Jitter: "2s", 5775 Fails: 3, 5776 Passes: 2, 5777 Port: 8080, 5778 ConnectTimeout: "20s", 5779 SendTimeout: "20s", 5780 ReadTimeout: "20s", 5781 Headers: []conf_v1.Header{ 5782 { 5783 Name: "Host", 5784 Value: "my.service", 5785 }, 5786 { 5787 Name: "User-Agent", 5788 Value: "nginx", 5789 }, 5790 }, 5791 StatusMatch: "! 500", 5792 }, 5793 }, 5794 upstreamName: upstreamName, 5795 expected: &version2.HealthCheck{ 5796 Name: upstreamName, 5797 ProxyConnectTimeout: "20s", 5798 ProxySendTimeout: "20s", 5799 ProxyReadTimeout: "20s", 5800 ProxyPass: fmt.Sprintf("http://%v", upstreamName), 5801 URI: "/healthz", 5802 Interval: "5s", 5803 Jitter: "2s", 5804 Fails: 3, 5805 Passes: 2, 5806 Port: 8080, 5807 Headers: map[string]string{ 5808 "Host": "my.service", 5809 "User-Agent": "nginx", 5810 }, 5811 Match: fmt.Sprintf("%v_match", upstreamName), 5812 }, 5813 msg: "HealthCheck with changed parameters", 5814 }, 5815 { 5816 upstream: conf_v1.Upstream{ 5817 HealthCheck: &conf_v1.HealthCheck{ 5818 Enable: true, 5819 }, 5820 ProxyConnectTimeout: "30s", 5821 ProxyReadTimeout: "30s", 5822 ProxySendTimeout: "30s", 5823 }, 5824 upstreamName: upstreamName, 5825 expected: &version2.HealthCheck{ 5826 Name: upstreamName, 5827 ProxyConnectTimeout: "30s", 5828 ProxyReadTimeout: "30s", 5829 ProxySendTimeout: "30s", 5830 ProxyPass: fmt.Sprintf("http://%v", upstreamName), 5831 URI: "/", 5832 Interval: "5s", 5833 Jitter: "0s", 5834 Fails: 1, 5835 Passes: 1, 5836 Headers: make(map[string]string), 5837 }, 5838 msg: "HealthCheck with default parameters from Upstream", 5839 }, 5840 { 5841 upstream: conf_v1.Upstream{ 5842 HealthCheck: &conf_v1.HealthCheck{ 5843 Enable: true, 5844 }, 5845 }, 5846 upstreamName: upstreamName, 5847 expected: &version2.HealthCheck{ 5848 Name: upstreamName, 5849 ProxyConnectTimeout: "5s", 5850 ProxyReadTimeout: "5s", 5851 ProxySendTimeout: "5s", 5852 ProxyPass: fmt.Sprintf("http://%v", upstreamName), 5853 URI: "/", 5854 Interval: "5s", 5855 Jitter: "0s", 5856 Fails: 1, 5857 Passes: 1, 5858 Headers: make(map[string]string), 5859 }, 5860 msg: "HealthCheck with default parameters from ConfigMap (not defined in Upstream)", 5861 }, 5862 { 5863 upstream: conf_v1.Upstream{}, 5864 upstreamName: upstreamName, 5865 expected: nil, 5866 msg: "HealthCheck not enabled", 5867 }, 5868 { 5869 upstream: conf_v1.Upstream{ 5870 HealthCheck: &conf_v1.HealthCheck{ 5871 Enable: true, 5872 Interval: "1m 5s", 5873 Jitter: "2m 3s", 5874 ConnectTimeout: "1m 10s", 5875 SendTimeout: "1m 20s", 5876 ReadTimeout: "1m 30s", 5877 }, 5878 }, 5879 upstreamName: upstreamName, 5880 expected: &version2.HealthCheck{ 5881 Name: upstreamName, 5882 ProxyConnectTimeout: "1m10s", 5883 ProxySendTimeout: "1m20s", 5884 ProxyReadTimeout: "1m30s", 5885 ProxyPass: fmt.Sprintf("http://%v", upstreamName), 5886 URI: "/", 5887 Interval: "1m5s", 5888 Jitter: "2m3s", 5889 Fails: 1, 5890 Passes: 1, 5891 Headers: make(map[string]string), 5892 }, 5893 msg: "HealthCheck with time parameters have correct format", 5894 }, 5895 } 5896 5897 baseCfgParams := &ConfigParams{ 5898 ProxySendTimeout: "5s", 5899 ProxyReadTimeout: "5s", 5900 ProxyConnectTimeout: "5s", 5901 } 5902 5903 for _, test := range tests { 5904 result := generateHealthCheck(test.upstream, test.upstreamName, baseCfgParams) 5905 if !reflect.DeepEqual(result, test.expected) { 5906 t.Errorf("generateHealthCheck returned \n%v but expected \n%v \n for case: %v", result, test.expected, test.msg) 5907 } 5908 } 5909 } 5910 5911 func TestGenerateEndpointsForUpstream(t *testing.T) { 5912 name := "test" 5913 namespace := "test-namespace" 5914 5915 tests := []struct { 5916 upstream conf_v1.Upstream 5917 vsEx *VirtualServerEx 5918 isPlus bool 5919 isResolverConfigured bool 5920 expected []string 5921 warningsExpected bool 5922 msg string 5923 }{ 5924 { 5925 upstream: conf_v1.Upstream{ 5926 Service: name, 5927 Port: 80, 5928 }, 5929 vsEx: &VirtualServerEx{ 5930 VirtualServer: &conf_v1.VirtualServer{ 5931 ObjectMeta: meta_v1.ObjectMeta{ 5932 Name: name, 5933 Namespace: namespace, 5934 }, 5935 }, 5936 Endpoints: map[string][]string{ 5937 "test-namespace/test:80": {"example.com:80"}, 5938 }, 5939 ExternalNameSvcs: map[string]bool{ 5940 "test-namespace/test": true, 5941 }, 5942 }, 5943 isPlus: true, 5944 isResolverConfigured: true, 5945 expected: []string{"example.com:80"}, 5946 msg: "ExternalName service", 5947 }, 5948 { 5949 upstream: conf_v1.Upstream{ 5950 Service: name, 5951 Port: 80, 5952 }, 5953 vsEx: &VirtualServerEx{ 5954 VirtualServer: &conf_v1.VirtualServer{ 5955 ObjectMeta: meta_v1.ObjectMeta{ 5956 Name: name, 5957 Namespace: namespace, 5958 }, 5959 }, 5960 Endpoints: map[string][]string{ 5961 "test-namespace/test:80": {"example.com:80"}, 5962 }, 5963 ExternalNameSvcs: map[string]bool{ 5964 "test-namespace/test": true, 5965 }, 5966 }, 5967 isPlus: true, 5968 isResolverConfigured: false, 5969 warningsExpected: true, 5970 expected: []string{}, 5971 msg: "ExternalName service without resolver configured", 5972 }, 5973 { 5974 upstream: conf_v1.Upstream{ 5975 Service: name, 5976 Port: 8080, 5977 }, 5978 vsEx: &VirtualServerEx{ 5979 VirtualServer: &conf_v1.VirtualServer{ 5980 ObjectMeta: meta_v1.ObjectMeta{ 5981 Name: name, 5982 Namespace: namespace, 5983 }, 5984 }, 5985 Endpoints: map[string][]string{ 5986 "test-namespace/test:8080": {"192.168.10.10:8080"}, 5987 }, 5988 }, 5989 isPlus: false, 5990 isResolverConfigured: false, 5991 expected: []string{"192.168.10.10:8080"}, 5992 msg: "Service with endpoints", 5993 }, 5994 { 5995 upstream: conf_v1.Upstream{ 5996 Service: name, 5997 Port: 8080, 5998 }, 5999 vsEx: &VirtualServerEx{ 6000 VirtualServer: &conf_v1.VirtualServer{ 6001 ObjectMeta: meta_v1.ObjectMeta{ 6002 Name: name, 6003 Namespace: namespace, 6004 }, 6005 }, 6006 Endpoints: map[string][]string{}, 6007 }, 6008 isPlus: false, 6009 isResolverConfigured: false, 6010 expected: []string{nginx502Server}, 6011 msg: "Service with no endpoints", 6012 }, 6013 { 6014 upstream: conf_v1.Upstream{ 6015 Service: name, 6016 Port: 8080, 6017 }, 6018 vsEx: &VirtualServerEx{ 6019 VirtualServer: &conf_v1.VirtualServer{ 6020 ObjectMeta: meta_v1.ObjectMeta{ 6021 Name: name, 6022 Namespace: namespace, 6023 }, 6024 }, 6025 Endpoints: map[string][]string{}, 6026 }, 6027 isPlus: true, 6028 isResolverConfigured: false, 6029 expected: nil, 6030 msg: "Service with no endpoints", 6031 }, 6032 { 6033 upstream: conf_v1.Upstream{ 6034 Service: name, 6035 Port: 8080, 6036 Subselector: map[string]string{"version": "test"}, 6037 }, 6038 vsEx: &VirtualServerEx{ 6039 VirtualServer: &conf_v1.VirtualServer{ 6040 ObjectMeta: meta_v1.ObjectMeta{ 6041 Name: name, 6042 Namespace: namespace, 6043 }, 6044 }, 6045 Endpoints: map[string][]string{ 6046 "test-namespace/test_version=test:8080": {"192.168.10.10:8080"}, 6047 }, 6048 }, 6049 isPlus: false, 6050 isResolverConfigured: false, 6051 expected: []string{"192.168.10.10:8080"}, 6052 msg: "Upstream with subselector, with a matching endpoint", 6053 }, 6054 { 6055 upstream: conf_v1.Upstream{ 6056 Service: name, 6057 Port: 8080, 6058 Subselector: map[string]string{"version": "test"}, 6059 }, 6060 vsEx: &VirtualServerEx{ 6061 VirtualServer: &conf_v1.VirtualServer{ 6062 ObjectMeta: meta_v1.ObjectMeta{ 6063 Name: name, 6064 Namespace: namespace, 6065 }, 6066 }, 6067 Endpoints: map[string][]string{ 6068 "test-namespace/test:8080": {"192.168.10.10:8080"}, 6069 }, 6070 }, 6071 isPlus: false, 6072 isResolverConfigured: false, 6073 expected: []string{nginx502Server}, 6074 msg: "Upstream with subselector, without a matching endpoint", 6075 }, 6076 } 6077 6078 for _, test := range tests { 6079 vsc := newVirtualServerConfigurator( 6080 &ConfigParams{}, 6081 test.isPlus, 6082 test.isResolverConfigured, 6083 &StaticConfigParams{}, 6084 ) 6085 result := vsc.generateEndpointsForUpstream(test.vsEx.VirtualServer, namespace, test.upstream, test.vsEx) 6086 if !reflect.DeepEqual(result, test.expected) { 6087 t.Errorf("generateEndpointsForUpstream(isPlus=%v, isResolverConfigured=%v) returned %v, but expected %v for case: %v", 6088 test.isPlus, test.isResolverConfigured, result, test.expected, test.msg) 6089 } 6090 6091 if len(vsc.warnings) == 0 && test.warningsExpected { 6092 t.Errorf( 6093 "generateEndpointsForUpstream(isPlus=%v, isResolverConfigured=%v) didn't return any warnings for %v but warnings expected", 6094 test.isPlus, 6095 test.isResolverConfigured, 6096 test.upstream, 6097 ) 6098 } 6099 6100 if len(vsc.warnings) != 0 && !test.warningsExpected { 6101 t.Errorf("generateEndpointsForUpstream(isPlus=%v, isResolverConfigured=%v) returned warnings for %v", 6102 test.isPlus, test.isResolverConfigured, test.upstream) 6103 } 6104 } 6105 } 6106 6107 func TestGenerateSlowStartForPlusWithInCompatibleLBMethods(t *testing.T) { 6108 serviceName := "test-slowstart-with-incompatible-LBMethods" 6109 upstream := conf_v1.Upstream{Service: serviceName, Port: 80, SlowStart: "10s"} 6110 expected := "" 6111 6112 tests := []string{ 6113 "random", 6114 "ip_hash", 6115 "hash 123", 6116 "random two", 6117 "random two least_conn", 6118 "random two least_time=header", 6119 "random two least_time=last_byte", 6120 } 6121 6122 for _, lbMethod := range tests { 6123 vsc := newVirtualServerConfigurator(&ConfigParams{}, true, false, &StaticConfigParams{}) 6124 result := vsc.generateSlowStartForPlus(&conf_v1.VirtualServer{}, upstream, lbMethod) 6125 6126 if !reflect.DeepEqual(result, expected) { 6127 t.Errorf("generateSlowStartForPlus returned %v, but expected %v for lbMethod %v", result, expected, lbMethod) 6128 } 6129 6130 if len(vsc.warnings) == 0 { 6131 t.Errorf("generateSlowStartForPlus returned no warnings for %v but warnings expected", upstream) 6132 } 6133 } 6134 } 6135 6136 func TestGenerateSlowStartForPlus(t *testing.T) { 6137 serviceName := "test-slowstart" 6138 6139 tests := []struct { 6140 upstream conf_v1.Upstream 6141 lbMethod string 6142 expected string 6143 }{ 6144 { 6145 upstream: conf_v1.Upstream{Service: serviceName, Port: 80, SlowStart: "", LBMethod: "least_conn"}, 6146 lbMethod: "least_conn", 6147 expected: "", 6148 }, 6149 { 6150 upstream: conf_v1.Upstream{Service: serviceName, Port: 80, SlowStart: "10s", LBMethod: "least_conn"}, 6151 lbMethod: "least_conn", 6152 expected: "10s", 6153 }, 6154 } 6155 6156 for _, test := range tests { 6157 vsc := newVirtualServerConfigurator(&ConfigParams{}, true, false, &StaticConfigParams{}) 6158 result := vsc.generateSlowStartForPlus(&conf_v1.VirtualServer{}, test.upstream, test.lbMethod) 6159 if !reflect.DeepEqual(result, test.expected) { 6160 t.Errorf("generateSlowStartForPlus returned %v, but expected %v", result, test.expected) 6161 } 6162 6163 if len(vsc.warnings) != 0 { 6164 t.Errorf("generateSlowStartForPlus returned warnings for %v", test.upstream) 6165 } 6166 } 6167 } 6168 6169 func TestCreateEndpointsFromUpstream(t *testing.T) { 6170 ups := version2.Upstream{ 6171 Servers: []version2.UpstreamServer{ 6172 { 6173 Address: "10.0.0.20:80", 6174 }, 6175 { 6176 Address: "10.0.0.30:80", 6177 }, 6178 }, 6179 } 6180 6181 expected := []string{ 6182 "10.0.0.20:80", 6183 "10.0.0.30:80", 6184 } 6185 6186 endpoints := createEndpointsFromUpstream(ups) 6187 if !reflect.DeepEqual(endpoints, expected) { 6188 t.Errorf("createEndpointsFromUpstream returned %v, but expected %v", endpoints, expected) 6189 } 6190 } 6191 6192 func TestGenerateUpstreamWithQueue(t *testing.T) { 6193 serviceName := "test-queue" 6194 6195 tests := []struct { 6196 name string 6197 upstream conf_v1.Upstream 6198 isPlus bool 6199 expected version2.Upstream 6200 msg string 6201 }{ 6202 { 6203 name: "test-upstream-queue", 6204 upstream: conf_v1.Upstream{Service: serviceName, Port: 80, Queue: &conf_v1.UpstreamQueue{ 6205 Size: 10, 6206 Timeout: "10s", 6207 }}, 6208 isPlus: true, 6209 expected: version2.Upstream{ 6210 UpstreamLabels: version2.UpstreamLabels{ 6211 Service: "test-queue", 6212 }, 6213 Name: "test-upstream-queue", 6214 Queue: &version2.Queue{ 6215 Size: 10, 6216 Timeout: "10s", 6217 }, 6218 }, 6219 msg: "upstream queue with size and timeout", 6220 }, 6221 { 6222 name: "test-upstream-queue-with-default-timeout", 6223 upstream: conf_v1.Upstream{ 6224 Service: serviceName, 6225 Port: 80, 6226 Queue: &conf_v1.UpstreamQueue{Size: 10, Timeout: ""}, 6227 }, 6228 isPlus: true, 6229 expected: version2.Upstream{ 6230 UpstreamLabels: version2.UpstreamLabels{ 6231 Service: "test-queue", 6232 }, 6233 Name: "test-upstream-queue-with-default-timeout", 6234 Queue: &version2.Queue{ 6235 Size: 10, 6236 Timeout: "60s", 6237 }, 6238 }, 6239 msg: "upstream queue with only size", 6240 }, 6241 { 6242 name: "test-upstream-queue-nil", 6243 upstream: conf_v1.Upstream{Service: serviceName, Port: 80, Queue: nil}, 6244 isPlus: false, 6245 expected: version2.Upstream{ 6246 UpstreamLabels: version2.UpstreamLabels{ 6247 Service: "test-queue", 6248 }, 6249 Name: "test-upstream-queue-nil", 6250 }, 6251 msg: "upstream queue with nil for OSS", 6252 }, 6253 } 6254 6255 for _, test := range tests { 6256 vsc := newVirtualServerConfigurator(&ConfigParams{}, test.isPlus, false, &StaticConfigParams{}) 6257 result := vsc.generateUpstream(nil, test.name, test.upstream, false, []string{}) 6258 if !reflect.DeepEqual(result, test.expected) { 6259 t.Errorf("generateUpstream() returned %v but expected %v for the case of %v", result, test.expected, test.msg) 6260 } 6261 } 6262 } 6263 6264 func TestGenerateQueueForPlus(t *testing.T) { 6265 tests := []struct { 6266 upstreamQueue *conf_v1.UpstreamQueue 6267 expected *version2.Queue 6268 msg string 6269 }{ 6270 { 6271 upstreamQueue: &conf_v1.UpstreamQueue{Size: 10, Timeout: "10s"}, 6272 expected: &version2.Queue{Size: 10, Timeout: "10s"}, 6273 msg: "upstream queue with size and timeout", 6274 }, 6275 { 6276 upstreamQueue: nil, 6277 expected: nil, 6278 msg: "upstream queue with nil", 6279 }, 6280 { 6281 upstreamQueue: &conf_v1.UpstreamQueue{Size: 10}, 6282 expected: &version2.Queue{Size: 10, Timeout: "60s"}, 6283 msg: "upstream queue with only size", 6284 }, 6285 } 6286 6287 for _, test := range tests { 6288 result := generateQueueForPlus(test.upstreamQueue, "60s") 6289 if !reflect.DeepEqual(result, test.expected) { 6290 t.Errorf("generateQueueForPlus() returned %v but expected %v for the case of %v", result, test.expected, test.msg) 6291 } 6292 } 6293 } 6294 6295 func TestGenerateSessionCookie(t *testing.T) { 6296 tests := []struct { 6297 sc *conf_v1.SessionCookie 6298 expected *version2.SessionCookie 6299 msg string 6300 }{ 6301 { 6302 sc: &conf_v1.SessionCookie{Enable: true, Name: "test"}, 6303 expected: &version2.SessionCookie{Enable: true, Name: "test"}, 6304 msg: "session cookie with name", 6305 }, 6306 { 6307 sc: nil, 6308 expected: nil, 6309 msg: "session cookie with nil", 6310 }, 6311 { 6312 sc: &conf_v1.SessionCookie{Name: "test"}, 6313 expected: nil, 6314 msg: "session cookie not enabled", 6315 }, 6316 } 6317 for _, test := range tests { 6318 result := generateSessionCookie(test.sc) 6319 if !reflect.DeepEqual(result, test.expected) { 6320 t.Errorf("generateSessionCookie() returned %v, but expected %v for the case of: %v", result, test.expected, test.msg) 6321 } 6322 } 6323 } 6324 6325 func TestGeneratePath(t *testing.T) { 6326 tests := []struct { 6327 path string 6328 expected string 6329 }{ 6330 { 6331 path: "/", 6332 expected: "/", 6333 }, 6334 { 6335 path: "=/exact/match", 6336 expected: "=/exact/match", 6337 }, 6338 { 6339 path: `~ *\\.jpg`, 6340 expected: `~ "*\\.jpg"`, 6341 }, 6342 { 6343 path: `~* *\\.PNG`, 6344 expected: `~* "*\\.PNG"`, 6345 }, 6346 } 6347 6348 for _, test := range tests { 6349 result := generatePath(test.path) 6350 if result != test.expected { 6351 t.Errorf("generatePath() returned %v, but expected %v.", result, test.expected) 6352 } 6353 } 6354 } 6355 6356 func TestGenerateErrorPageName(t *testing.T) { 6357 tests := []struct { 6358 routeIndex int 6359 index int 6360 expected string 6361 }{ 6362 { 6363 0, 6364 0, 6365 "@error_page_0_0", 6366 }, 6367 { 6368 0, 6369 1, 6370 "@error_page_0_1", 6371 }, 6372 { 6373 1, 6374 0, 6375 "@error_page_1_0", 6376 }, 6377 } 6378 6379 for _, test := range tests { 6380 result := generateErrorPageName(test.routeIndex, test.index) 6381 if result != test.expected { 6382 t.Errorf("generateErrorPageName(%v, %v) returned %v but expected %v", test.routeIndex, test.index, result, test.expected) 6383 } 6384 } 6385 } 6386 6387 func TestGenerateErrorPageCodes(t *testing.T) { 6388 tests := []struct { 6389 codes []int 6390 expected string 6391 }{ 6392 { 6393 codes: []int{400}, 6394 expected: "400", 6395 }, 6396 { 6397 codes: []int{404, 405, 502}, 6398 expected: "404 405 502", 6399 }, 6400 } 6401 6402 for _, test := range tests { 6403 result := generateErrorPageCodes(test.codes) 6404 if result != test.expected { 6405 t.Errorf("generateErrorPageCodes(%v) returned %v but expected %v", test.codes, result, test.expected) 6406 } 6407 } 6408 } 6409 6410 func TestGenerateErrorPages(t *testing.T) { 6411 tests := []struct { 6412 upstreamName string 6413 errorPages []conf_v1.ErrorPage 6414 expected []version2.ErrorPage 6415 }{ 6416 {}, // empty errorPages 6417 { 6418 "vs_test_test", 6419 []conf_v1.ErrorPage{ 6420 { 6421 Codes: []int{404, 405, 500, 502}, 6422 Return: &conf_v1.ErrorPageReturn{ 6423 ActionReturn: conf_v1.ActionReturn{ 6424 Code: 200, 6425 }, 6426 Headers: nil, 6427 }, 6428 Redirect: nil, 6429 }, 6430 }, 6431 []version2.ErrorPage{ 6432 { 6433 Name: "@error_page_1_0", 6434 Codes: "404 405 500 502", 6435 ResponseCode: 200, 6436 }, 6437 }, 6438 }, 6439 { 6440 "vs_test_test", 6441 []conf_v1.ErrorPage{ 6442 { 6443 Codes: []int{404, 405, 500, 502}, 6444 Return: nil, 6445 Redirect: &conf_v1.ErrorPageRedirect{ 6446 ActionRedirect: conf_v1.ActionRedirect{ 6447 URL: "http://nginx.org", 6448 Code: 302, 6449 }, 6450 }, 6451 }, 6452 }, 6453 []version2.ErrorPage{ 6454 { 6455 Name: "http://nginx.org", 6456 Codes: "404 405 500 502", 6457 ResponseCode: 302, 6458 }, 6459 }, 6460 }, 6461 } 6462 6463 for i, test := range tests { 6464 result := generateErrorPages(i, test.errorPages) 6465 if !reflect.DeepEqual(result, test.expected) { 6466 t.Errorf("generateErrorPages(%v, %v) returned %v but expected %v", test.upstreamName, test.errorPages, result, test.expected) 6467 } 6468 } 6469 } 6470 6471 func TestGenerateErrorPageLocations(t *testing.T) { 6472 tests := []struct { 6473 upstreamName string 6474 errorPages []conf_v1.ErrorPage 6475 expected []version2.ErrorPageLocation 6476 }{ 6477 {}, 6478 { 6479 "vs_test_test", 6480 []conf_v1.ErrorPage{ 6481 { 6482 Codes: []int{404, 405, 500, 502}, 6483 Return: nil, 6484 Redirect: &conf_v1.ErrorPageRedirect{ 6485 ActionRedirect: conf_v1.ActionRedirect{ 6486 URL: "http://nginx.org", 6487 Code: 302, 6488 }, 6489 }, 6490 }, 6491 }, 6492 nil, 6493 }, 6494 { 6495 "vs_test_test", 6496 []conf_v1.ErrorPage{ 6497 { 6498 Codes: []int{404, 405, 500, 502}, 6499 Return: &conf_v1.ErrorPageReturn{ 6500 ActionReturn: conf_v1.ActionReturn{ 6501 Code: 200, 6502 Type: "application/json", 6503 Body: "Hello World", 6504 }, 6505 Headers: []conf_v1.Header{ 6506 { 6507 Name: "HeaderName", 6508 Value: "HeaderValue", 6509 }, 6510 }, 6511 }, 6512 Redirect: nil, 6513 }, 6514 }, 6515 []version2.ErrorPageLocation{ 6516 { 6517 Name: "@error_page_2_0", 6518 DefaultType: "application/json", 6519 Return: &version2.Return{ 6520 Code: 0, 6521 Text: "Hello World", 6522 }, 6523 Headers: []version2.Header{ 6524 { 6525 Name: "HeaderName", 6526 Value: "HeaderValue", 6527 }, 6528 }, 6529 }, 6530 }, 6531 }, 6532 } 6533 6534 for i, test := range tests { 6535 result := generateErrorPageLocations(i, test.errorPages) 6536 if !reflect.DeepEqual(result, test.expected) { 6537 t.Errorf("generateErrorPageLocations(%v, %v) returned %v but expected %v", test.upstreamName, test.errorPages, result, test.expected) 6538 } 6539 } 6540 } 6541 6542 func TestGenerateProxySSLName(t *testing.T) { 6543 result := generateProxySSLName("coffee-v1", "default") 6544 if result != "coffee-v1.default.svc" { 6545 t.Errorf("generateProxySSLName(coffee-v1, default) returned %v but expected coffee-v1.default.svc", result) 6546 } 6547 } 6548 6549 func TestIsTLSEnabled(t *testing.T) { 6550 tests := []struct { 6551 upstream conf_v1.Upstream 6552 spiffeCert bool 6553 expected bool 6554 }{ 6555 { 6556 upstream: conf_v1.Upstream{ 6557 TLS: conf_v1.UpstreamTLS{ 6558 Enable: false, 6559 }, 6560 }, 6561 spiffeCert: false, 6562 expected: false, 6563 }, 6564 { 6565 upstream: conf_v1.Upstream{ 6566 TLS: conf_v1.UpstreamTLS{ 6567 Enable: false, 6568 }, 6569 }, 6570 spiffeCert: true, 6571 expected: true, 6572 }, 6573 { 6574 upstream: conf_v1.Upstream{ 6575 TLS: conf_v1.UpstreamTLS{ 6576 Enable: true, 6577 }, 6578 }, 6579 spiffeCert: true, 6580 expected: true, 6581 }, 6582 { 6583 upstream: conf_v1.Upstream{ 6584 TLS: conf_v1.UpstreamTLS{ 6585 Enable: true, 6586 }, 6587 }, 6588 spiffeCert: false, 6589 expected: true, 6590 }, 6591 } 6592 6593 for _, test := range tests { 6594 result := isTLSEnabled(test.upstream, test.spiffeCert) 6595 if result != test.expected { 6596 t.Errorf("isTLSEnabled(%v, %v) returned %v but expected %v", test.upstream, test.spiffeCert, result, test.expected) 6597 } 6598 } 6599 } 6600 6601 func TestGenerateRewrites(t *testing.T) { 6602 tests := []struct { 6603 path string 6604 proxy *conf_v1.ActionProxy 6605 internal bool 6606 originalPath string 6607 expected []string 6608 }{ 6609 { 6610 proxy: nil, 6611 expected: nil, 6612 }, 6613 { 6614 proxy: &conf_v1.ActionProxy{ 6615 RewritePath: "", 6616 }, 6617 expected: nil, 6618 }, 6619 { 6620 path: "/path", 6621 proxy: &conf_v1.ActionProxy{ 6622 RewritePath: "/rewrite", 6623 }, 6624 expected: nil, 6625 }, 6626 { 6627 path: "/_internal_path", 6628 internal: true, 6629 proxy: &conf_v1.ActionProxy{ 6630 RewritePath: "/rewrite", 6631 }, 6632 originalPath: "/path", 6633 expected: []string{`^ $request_uri`, `"^/path(.*)$" "/rewrite$1" break`}, 6634 }, 6635 { 6636 path: "~/regex", 6637 internal: true, 6638 proxy: &conf_v1.ActionProxy{ 6639 RewritePath: "/rewrite", 6640 }, 6641 originalPath: "/path", 6642 expected: []string{`^ $request_uri`, `"^/path(.*)$" "/rewrite$1" break`}, 6643 }, 6644 { 6645 path: "~/regex", 6646 internal: false, 6647 proxy: &conf_v1.ActionProxy{ 6648 RewritePath: "/rewrite", 6649 }, 6650 expected: []string{`"^/regex" "/rewrite" break`}, 6651 }, 6652 } 6653 6654 for _, test := range tests { 6655 result := generateRewrites(test.path, test.proxy, test.internal, test.originalPath) 6656 if !reflect.DeepEqual(result, test.expected) { 6657 t.Errorf("generateRewrites(%v, %v, %v, %v) returned \n %v but expected \n %v", 6658 test.path, test.proxy, test.internal, test.originalPath, result, test.expected) 6659 } 6660 } 6661 } 6662 6663 func TestGenerateProxyPassRewrite(t *testing.T) { 6664 tests := []struct { 6665 path string 6666 proxy *conf_v1.ActionProxy 6667 internal bool 6668 expected string 6669 }{ 6670 { 6671 expected: "", 6672 }, 6673 { 6674 internal: true, 6675 proxy: &conf_v1.ActionProxy{ 6676 RewritePath: "/rewrite", 6677 }, 6678 expected: "", 6679 }, 6680 { 6681 path: "/path", 6682 proxy: &conf_v1.ActionProxy{ 6683 RewritePath: "/rewrite", 6684 }, 6685 expected: "/rewrite", 6686 }, 6687 { 6688 path: "=/path", 6689 proxy: &conf_v1.ActionProxy{ 6690 RewritePath: "/rewrite", 6691 }, 6692 expected: "/rewrite", 6693 }, 6694 { 6695 path: "~/path", 6696 proxy: &conf_v1.ActionProxy{ 6697 RewritePath: "/rewrite", 6698 }, 6699 expected: "", 6700 }, 6701 } 6702 6703 for _, test := range tests { 6704 result := generateProxyPassRewrite(test.path, test.proxy, test.internal) 6705 if result != test.expected { 6706 t.Errorf("generateProxyPassRewrite(%v, %v, %v) returned %v but expected %v", 6707 test.path, test.proxy, test.internal, result, test.expected) 6708 } 6709 } 6710 } 6711 6712 func TestGenerateProxySetHeaders(t *testing.T) { 6713 tests := []struct { 6714 proxy *conf_v1.ActionProxy 6715 expected []version2.Header 6716 msg string 6717 }{ 6718 { 6719 proxy: nil, 6720 expected: []version2.Header{{Name: "Host", Value: "$host"}}, 6721 msg: "no action proxy", 6722 }, 6723 { 6724 proxy: &conf_v1.ActionProxy{}, 6725 expected: []version2.Header{{Name: "Host", Value: "$host"}}, 6726 msg: "empty action proxy", 6727 }, 6728 { 6729 proxy: &conf_v1.ActionProxy{ 6730 RequestHeaders: &conf_v1.ProxyRequestHeaders{ 6731 Set: []conf_v1.Header{ 6732 { 6733 Name: "Header-Name", 6734 Value: "HeaderValue", 6735 }, 6736 }, 6737 }, 6738 }, 6739 expected: []version2.Header{ 6740 { 6741 Name: "Header-Name", 6742 Value: "HeaderValue", 6743 }, 6744 { 6745 Name: "Host", 6746 Value: "$host", 6747 }, 6748 }, 6749 msg: "set headers without host", 6750 }, 6751 { 6752 proxy: &conf_v1.ActionProxy{ 6753 RequestHeaders: &conf_v1.ProxyRequestHeaders{ 6754 Set: []conf_v1.Header{ 6755 { 6756 Name: "Header-Name", 6757 Value: "HeaderValue", 6758 }, 6759 { 6760 Name: "Host", 6761 Value: "example.com", 6762 }, 6763 }, 6764 }, 6765 }, 6766 expected: []version2.Header{ 6767 { 6768 Name: "Header-Name", 6769 Value: "HeaderValue", 6770 }, 6771 { 6772 Name: "Host", 6773 Value: "example.com", 6774 }, 6775 }, 6776 msg: "set headers with host capitalized", 6777 }, 6778 { 6779 proxy: &conf_v1.ActionProxy{ 6780 RequestHeaders: &conf_v1.ProxyRequestHeaders{ 6781 Set: []conf_v1.Header{ 6782 { 6783 Name: "Header-Name", 6784 Value: "HeaderValue", 6785 }, 6786 { 6787 Name: "hoST", 6788 Value: "example.com", 6789 }, 6790 }, 6791 }, 6792 }, 6793 expected: []version2.Header{ 6794 { 6795 Name: "Header-Name", 6796 Value: "HeaderValue", 6797 }, 6798 { 6799 Name: "hoST", 6800 Value: "example.com", 6801 }, 6802 }, 6803 msg: "set headers with host in mixed case", 6804 }, 6805 { 6806 proxy: &conf_v1.ActionProxy{ 6807 RequestHeaders: &conf_v1.ProxyRequestHeaders{ 6808 Set: []conf_v1.Header{ 6809 { 6810 Name: "Header-Name", 6811 Value: "HeaderValue", 6812 }, 6813 { 6814 Name: "Host", 6815 Value: "one.example.com", 6816 }, 6817 { 6818 Name: "Host", 6819 Value: "two.example.com", 6820 }, 6821 }, 6822 }, 6823 }, 6824 expected: []version2.Header{ 6825 { 6826 Name: "Header-Name", 6827 Value: "HeaderValue", 6828 }, 6829 { 6830 Name: "Host", 6831 Value: "one.example.com", 6832 }, 6833 { 6834 Name: "Host", 6835 Value: "two.example.com", 6836 }, 6837 }, 6838 msg: "set headers with multiple hosts", 6839 }, 6840 } 6841 6842 for _, test := range tests { 6843 result := generateProxySetHeaders(test.proxy) 6844 if diff := cmp.Diff(test.expected, result); diff != "" { 6845 t.Errorf("generateProxySetHeaders() '%v' mismatch (-want +got):\n%s", test.msg, diff) 6846 } 6847 } 6848 } 6849 6850 func TestGenerateProxyPassRequestHeaders(t *testing.T) { 6851 passTrue := true 6852 passFalse := false 6853 tests := []struct { 6854 proxy *conf_v1.ActionProxy 6855 expected bool 6856 }{ 6857 { 6858 proxy: nil, 6859 expected: true, 6860 }, 6861 { 6862 proxy: &conf_v1.ActionProxy{}, 6863 expected: true, 6864 }, 6865 { 6866 proxy: &conf_v1.ActionProxy{ 6867 RequestHeaders: &conf_v1.ProxyRequestHeaders{ 6868 Pass: nil, 6869 }, 6870 }, 6871 expected: true, 6872 }, 6873 { 6874 proxy: &conf_v1.ActionProxy{ 6875 RequestHeaders: &conf_v1.ProxyRequestHeaders{ 6876 Pass: &passTrue, 6877 }, 6878 }, 6879 expected: true, 6880 }, 6881 { 6882 proxy: &conf_v1.ActionProxy{ 6883 RequestHeaders: &conf_v1.ProxyRequestHeaders{ 6884 Pass: &passFalse, 6885 }, 6886 }, 6887 expected: false, 6888 }, 6889 } 6890 6891 for _, test := range tests { 6892 result := generateProxyPassRequestHeaders(test.proxy) 6893 if result != test.expected { 6894 t.Errorf("generateProxyPassRequestHeaders(%v) returned %v but expected %v", test.proxy, result, test.expected) 6895 } 6896 } 6897 } 6898 6899 func TestGenerateProxyHideHeaders(t *testing.T) { 6900 tests := []struct { 6901 proxy *conf_v1.ActionProxy 6902 expected []string 6903 }{ 6904 { 6905 proxy: nil, 6906 expected: nil, 6907 }, 6908 { 6909 proxy: &conf_v1.ActionProxy{ 6910 ResponseHeaders: nil, 6911 }, 6912 }, 6913 { 6914 proxy: &conf_v1.ActionProxy{ 6915 ResponseHeaders: &conf_v1.ProxyResponseHeaders{ 6916 Hide: []string{"Header", "Header-2"}, 6917 }, 6918 }, 6919 expected: []string{"Header", "Header-2"}, 6920 }, 6921 } 6922 6923 for _, test := range tests { 6924 result := generateProxyHideHeaders(test.proxy) 6925 if !reflect.DeepEqual(result, test.expected) { 6926 t.Errorf("generateProxyHideHeaders(%v) returned %v but expected %v", test.proxy, result, test.expected) 6927 } 6928 } 6929 } 6930 6931 func TestGenerateProxyPassHeaders(t *testing.T) { 6932 tests := []struct { 6933 proxy *conf_v1.ActionProxy 6934 expected []string 6935 }{ 6936 { 6937 proxy: nil, 6938 expected: nil, 6939 }, 6940 { 6941 proxy: &conf_v1.ActionProxy{ 6942 ResponseHeaders: nil, 6943 }, 6944 }, 6945 { 6946 proxy: &conf_v1.ActionProxy{ 6947 ResponseHeaders: &conf_v1.ProxyResponseHeaders{ 6948 Pass: []string{"Header", "Header-2"}, 6949 }, 6950 }, 6951 expected: []string{"Header", "Header-2"}, 6952 }, 6953 } 6954 6955 for _, test := range tests { 6956 result := generateProxyPassHeaders(test.proxy) 6957 if !reflect.DeepEqual(result, test.expected) { 6958 t.Errorf("generateProxyPassHeaders(%v) returned %v but expected %v", test.proxy, result, test.expected) 6959 } 6960 } 6961 } 6962 6963 func TestGenerateProxyIgnoreHeaders(t *testing.T) { 6964 tests := []struct { 6965 proxy *conf_v1.ActionProxy 6966 expected string 6967 }{ 6968 { 6969 proxy: nil, 6970 expected: "", 6971 }, 6972 { 6973 proxy: &conf_v1.ActionProxy{ 6974 ResponseHeaders: nil, 6975 }, 6976 expected: "", 6977 }, 6978 { 6979 proxy: &conf_v1.ActionProxy{ 6980 ResponseHeaders: &conf_v1.ProxyResponseHeaders{ 6981 Ignore: []string{"Header", "Header-2"}, 6982 }, 6983 }, 6984 expected: "Header Header-2", 6985 }, 6986 } 6987 6988 for _, test := range tests { 6989 result := generateProxyIgnoreHeaders(test.proxy) 6990 if result != test.expected { 6991 t.Errorf("generateProxyIgnoreHeaders(%v) returned %v but expected %v", test.proxy, result, test.expected) 6992 } 6993 } 6994 } 6995 6996 func TestGenerateProxyAddHeaders(t *testing.T) { 6997 tests := []struct { 6998 proxy *conf_v1.ActionProxy 6999 expected []version2.AddHeader 7000 }{ 7001 { 7002 proxy: nil, 7003 expected: nil, 7004 }, 7005 { 7006 proxy: &conf_v1.ActionProxy{}, 7007 expected: nil, 7008 }, 7009 { 7010 proxy: &conf_v1.ActionProxy{ 7011 ResponseHeaders: &conf_v1.ProxyResponseHeaders{ 7012 Add: []conf_v1.AddHeader{ 7013 { 7014 Header: conf_v1.Header{ 7015 Name: "Header-Name", 7016 Value: "HeaderValue", 7017 }, 7018 Always: true, 7019 }, 7020 { 7021 Header: conf_v1.Header{ 7022 Name: "Server", 7023 Value: "myServer", 7024 }, 7025 Always: false, 7026 }, 7027 }, 7028 }, 7029 }, 7030 expected: []version2.AddHeader{ 7031 { 7032 Header: version2.Header{ 7033 Name: "Header-Name", 7034 Value: "HeaderValue", 7035 }, 7036 Always: true, 7037 }, 7038 { 7039 Header: version2.Header{ 7040 Name: "Server", 7041 Value: "myServer", 7042 }, 7043 Always: false, 7044 }, 7045 }, 7046 }, 7047 } 7048 7049 for _, test := range tests { 7050 result := generateProxyAddHeaders(test.proxy) 7051 if !reflect.DeepEqual(result, test.expected) { 7052 t.Errorf("generateProxyAddHeaders(%v) returned %v but expected %v", test.proxy, result, test.expected) 7053 } 7054 } 7055 } 7056 7057 func TestGetUpstreamResourceLabels(t *testing.T) { 7058 tests := []struct { 7059 owner runtime.Object 7060 expected version2.UpstreamLabels 7061 }{ 7062 { 7063 owner: nil, 7064 expected: version2.UpstreamLabels{}, 7065 }, 7066 { 7067 owner: &conf_v1.VirtualServer{ 7068 ObjectMeta: meta_v1.ObjectMeta{ 7069 Namespace: "namespace", 7070 Name: "name", 7071 }, 7072 }, 7073 expected: version2.UpstreamLabels{ 7074 ResourceNamespace: "namespace", 7075 ResourceName: "name", 7076 ResourceType: "virtualserver", 7077 }, 7078 }, 7079 { 7080 owner: &conf_v1.VirtualServerRoute{ 7081 ObjectMeta: meta_v1.ObjectMeta{ 7082 Namespace: "namespace", 7083 Name: "name", 7084 }, 7085 }, 7086 expected: version2.UpstreamLabels{ 7087 ResourceNamespace: "namespace", 7088 ResourceName: "name", 7089 ResourceType: "virtualserverroute", 7090 }, 7091 }, 7092 } 7093 for _, test := range tests { 7094 result := getUpstreamResourceLabels(test.owner) 7095 if !reflect.DeepEqual(result, test.expected) { 7096 t.Errorf("getUpstreamResourceLabels(%+v) returned %+v but expected %+v", test.owner, result, test.expected) 7097 } 7098 } 7099 } 7100 7101 func TestAddWafConfig(t *testing.T) { 7102 tests := []struct { 7103 wafInput *conf_v1.WAF 7104 polKey string 7105 polNamespace string 7106 apResources map[string]string 7107 wafConfig *version2.WAF 7108 expected *validationResults 7109 msg string 7110 }{ 7111 { 7112 7113 wafInput: &conf_v1.WAF{ 7114 Enable: true, 7115 }, 7116 polKey: "default/waf-policy", 7117 polNamespace: "default", 7118 apResources: map[string]string{}, 7119 wafConfig: &version2.WAF{ 7120 Enable: "on", 7121 }, 7122 expected: &validationResults{isError: false}, 7123 msg: "valid waf config, default App Protect config", 7124 }, 7125 { 7126 7127 wafInput: &conf_v1.WAF{ 7128 Enable: true, 7129 ApPolicy: "dataguard-alarm", 7130 SecurityLog: &conf_v1.SecurityLog{ 7131 Enable: true, 7132 ApLogConf: "logconf", 7133 LogDest: "syslog:server=127.0.0.1:514", 7134 }, 7135 }, 7136 polKey: "default/waf-policy", 7137 polNamespace: "default", 7138 apResources: map[string]string{ 7139 "default/dataguard-alarm": "/etc/nginx/waf/nac-policies/default-dataguard-alarm", 7140 "default/logconf": "/etc/nginx/waf/nac-logconfs/default-logconf", 7141 }, 7142 wafConfig: &version2.WAF{ 7143 ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm", 7144 ApSecurityLogEnable: true, 7145 ApLogConf: "/etc/nginx/waf/nac-logconfs/default-logconf", 7146 }, 7147 expected: &validationResults{isError: false}, 7148 msg: "valid waf config", 7149 }, 7150 { 7151 7152 wafInput: &conf_v1.WAF{ 7153 Enable: true, 7154 ApPolicy: "default/dataguard-alarm", 7155 SecurityLog: &conf_v1.SecurityLog{ 7156 Enable: true, 7157 ApLogConf: "default/logconf", 7158 LogDest: "syslog:server=127.0.0.1:514", 7159 }, 7160 }, 7161 polKey: "default/waf-policy", 7162 polNamespace: "", 7163 apResources: map[string]string{ 7164 "default/dataguard-alarm": "/etc/nginx/waf/nac-policies/default-dataguard-alarm", 7165 }, 7166 wafConfig: &version2.WAF{ 7167 ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm", 7168 ApSecurityLogEnable: true, 7169 ApLogConf: "/etc/nginx/waf/nac-logconfs/default-logconf", 7170 }, 7171 expected: &validationResults{ 7172 isError: true, 7173 warnings: []string{ 7174 `WAF policy default/waf-policy references an invalid or non-existing log config default/logconf`, 7175 }, 7176 }, 7177 msg: "invalid waf config, apLogConf references non-existing log conf", 7178 }, 7179 { 7180 7181 wafInput: &conf_v1.WAF{ 7182 Enable: true, 7183 ApPolicy: "default/dataguard-alarm", 7184 SecurityLog: &conf_v1.SecurityLog{ 7185 Enable: true, 7186 LogDest: "syslog:server=127.0.0.1:514", 7187 }, 7188 }, 7189 polKey: "default/waf-policy", 7190 polNamespace: "", 7191 apResources: map[string]string{ 7192 "default/logconf": "/etc/nginx/waf/nac-logconfs/default-logconf", 7193 }, 7194 wafConfig: &version2.WAF{ 7195 ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm", 7196 ApSecurityLogEnable: true, 7197 ApLogConf: "/etc/nginx/waf/nac-logconfs/default-logconf", 7198 }, 7199 expected: &validationResults{ 7200 isError: true, 7201 warnings: []string{ 7202 `WAF policy default/waf-policy references an invalid or non-existing App Protect policy default/dataguard-alarm`, 7203 }, 7204 }, 7205 msg: "invalid waf config, apLogConf references non-existing ap conf", 7206 }, 7207 { 7208 7209 wafInput: &conf_v1.WAF{ 7210 Enable: true, 7211 ApPolicy: "ns1/dataguard-alarm", 7212 SecurityLog: &conf_v1.SecurityLog{ 7213 Enable: true, 7214 ApLogConf: "ns2/logconf", 7215 LogDest: "syslog:server=127.0.0.1:514", 7216 }, 7217 }, 7218 polKey: "default/waf-policy", 7219 polNamespace: "", 7220 apResources: map[string]string{ 7221 "ns1/dataguard-alarm": "/etc/nginx/waf/nac-policies/ns1-dataguard-alarm", 7222 "ns2/logconf": "/etc/nginx/waf/nac-logconfs/ns2-logconf", 7223 }, 7224 wafConfig: &version2.WAF{ 7225 ApPolicy: "/etc/nginx/waf/nac-policies/ns1-dataguard-alarm", 7226 ApSecurityLogEnable: true, 7227 ApLogConf: "/etc/nginx/waf/nac-logconfs/ns2-logconf", 7228 }, 7229 expected: &validationResults{}, 7230 msg: "valid waf config, cross ns reference", 7231 }, 7232 { 7233 7234 wafInput: &conf_v1.WAF{ 7235 Enable: false, 7236 ApPolicy: "dataguard-alarm", 7237 }, 7238 polKey: "default/waf-policy", 7239 polNamespace: "default", 7240 apResources: map[string]string{ 7241 "default/dataguard-alarm": "/etc/nginx/waf/nac-policies/ns1-dataguard-alarm", 7242 "default/logconf": "/etc/nginx/waf/nac-logconfs/ns2-logconf", 7243 }, 7244 wafConfig: &version2.WAF{ 7245 Enable: "off", 7246 ApPolicy: "/etc/nginx/waf/nac-policies/ns1-dataguard-alarm", 7247 }, 7248 expected: &validationResults{}, 7249 msg: "valid waf config, disable waf", 7250 }, 7251 } 7252 7253 for _, test := range tests { 7254 polCfg := newPoliciesConfig() 7255 result := polCfg.addWAFConfig(test.wafInput, test.polKey, test.polNamespace, test.apResources) 7256 if diff := cmp.Diff(test.expected.warnings, result.warnings); diff != "" { 7257 t.Errorf("policiesCfg.addWAFConfig() '%v' mismatch (-want +got):\n%s", test.msg, diff) 7258 } 7259 } 7260 } 7261 7262 func TestGenerateTime(t *testing.T) { 7263 tests := []struct { 7264 value, expected string 7265 }{ 7266 { 7267 value: "0s", 7268 expected: "0s", 7269 }, 7270 { 7271 value: "0", 7272 expected: "0s", 7273 }, 7274 { 7275 value: "1h", 7276 expected: "1h", 7277 }, 7278 { 7279 value: "1h 30m", 7280 expected: "1h30m", 7281 }, 7282 } 7283 7284 for _, test := range tests { 7285 result := generateTime(test.value) 7286 if result != test.expected { 7287 t.Errorf("generateTime(%q) returned %q but expected %q", test.value, result, test.expected) 7288 } 7289 } 7290 } 7291 7292 func TestGenerateTimeWithDefault(t *testing.T) { 7293 tests := []struct { 7294 value, defaultValue, expected string 7295 }{ 7296 { 7297 value: "1h 30m", 7298 defaultValue: "", 7299 expected: "1h30m", 7300 }, 7301 { 7302 value: "", 7303 defaultValue: "60s", 7304 expected: "60s", 7305 }, 7306 { 7307 value: "", 7308 defaultValue: "test", 7309 expected: "test", 7310 }, 7311 } 7312 7313 for _, test := range tests { 7314 result := generateTimeWithDefault(test.value, test.defaultValue) 7315 if result != test.expected { 7316 t.Errorf("generateTimeWithDefault(%q, %q) returned %q but expected %q", test.value, test.defaultValue, result, test.expected) 7317 } 7318 } 7319 }