github.com/nginxinc/kubernetes-ingress@v1.12.5/pkg/apis/configuration/validation/virtualserver_test.go (about) 1 package validation 2 3 import ( 4 "reflect" 5 "testing" 6 7 v1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1" 8 meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 "k8s.io/apimachinery/pkg/util/sets" 10 "k8s.io/apimachinery/pkg/util/validation/field" 11 ) 12 13 func TestValidateVirtualServer(t *testing.T) { 14 virtualServer := v1.VirtualServer{ 15 ObjectMeta: meta_v1.ObjectMeta{ 16 Name: "cafe", 17 Namespace: "default", 18 }, 19 Spec: v1.VirtualServerSpec{ 20 Host: "example.com", 21 TLS: &v1.TLS{ 22 Secret: "abc", 23 }, 24 Upstreams: []v1.Upstream{ 25 { 26 Name: "first", 27 Service: "service-1", 28 LBMethod: "random", 29 Port: 80, 30 MaxFails: createPointerFromInt(8), 31 MaxConns: createPointerFromInt(16), 32 Keepalive: createPointerFromInt(32), 33 }, 34 { 35 Name: "second", 36 Service: "service-2", 37 Port: 80, 38 }, 39 }, 40 Routes: []v1.Route{ 41 { 42 Path: "/first", 43 Action: &v1.Action{ 44 Pass: "first", 45 }, 46 }, 47 { 48 Path: "/second", 49 Action: &v1.Action{ 50 Pass: "second", 51 }, 52 }, 53 }, 54 }, 55 } 56 57 vsv := &VirtualServerValidator{isPlus: false} 58 59 err := vsv.ValidateVirtualServer(&virtualServer) 60 if err != nil { 61 t.Errorf("ValidateVirtualServer() returned error %v for valid input %v", err, virtualServer) 62 } 63 } 64 65 func TestValidateHost(t *testing.T) { 66 validHosts := []string{ 67 "hello", 68 "example.com", 69 "hello-world-1", 70 } 71 72 for _, h := range validHosts { 73 allErrs := validateHost(h, field.NewPath("host")) 74 if len(allErrs) > 0 { 75 t.Errorf("validateHost(%q) returned errors %v for valid input", h, allErrs) 76 } 77 } 78 79 invalidHosts := []string{ 80 "", 81 "*", 82 "..", 83 ".example.com", 84 "-hello-world-1", 85 } 86 87 for _, h := range invalidHosts { 88 allErrs := validateHost(h, field.NewPath("host")) 89 if len(allErrs) == 0 { 90 t.Errorf("validateHost(%q) returned no errors for invalid input", h) 91 } 92 } 93 } 94 95 func TestValidatePolicies(t *testing.T) { 96 tests := []struct { 97 policies []v1.PolicyReference 98 msg string 99 }{ 100 { 101 policies: []v1.PolicyReference{ 102 { 103 Name: "my-policy", 104 }, 105 }, 106 msg: "name without namespace", 107 }, 108 { 109 policies: []v1.PolicyReference{ 110 { 111 Name: "my-policy", 112 Namespace: "nginx-ingress", 113 }, 114 }, 115 msg: "name with namespace", 116 }, 117 { 118 policies: []v1.PolicyReference{ 119 { 120 Name: "my-policy", 121 Namespace: "default", 122 }, 123 { 124 Name: "my-policy", 125 Namespace: "test", 126 }, 127 }, 128 msg: "same name different namespaces", 129 }, 130 } 131 132 for _, test := range tests { 133 allErrs := validatePolicies(test.policies, field.NewPath("policies"), "default") 134 if len(allErrs) > 0 { 135 t.Errorf("validatePolicies() returned errors %v for valid input for the case of %s", allErrs, test.msg) 136 } 137 } 138 } 139 140 func TestValidatePoliciesFails(t *testing.T) { 141 tests := []struct { 142 policies []v1.PolicyReference 143 msg string 144 }{ 145 { 146 policies: []v1.PolicyReference{ 147 { 148 Name: "", 149 }, 150 }, 151 msg: "missing name", 152 }, 153 { 154 policies: []v1.PolicyReference{ 155 { 156 Name: "-invalid", 157 }, 158 }, 159 msg: "invalid name", 160 }, 161 { 162 policies: []v1.PolicyReference{ 163 { 164 Name: "my-policy", 165 Namespace: "-invalid", 166 }, 167 }, 168 msg: "invalid namespace", 169 }, 170 { 171 policies: []v1.PolicyReference{ 172 { 173 Name: "my-policy", 174 Namespace: "default", 175 }, 176 { 177 Name: "my-policy", 178 Namespace: "default", 179 }, 180 }, 181 msg: "duplicated policies", 182 }, 183 { 184 policies: []v1.PolicyReference{ 185 { 186 Name: "my-policy", 187 Namespace: "default", 188 }, 189 { 190 Name: "my-policy", 191 }, 192 }, 193 msg: "duplicated policies with inferred namespace", 194 }, 195 } 196 197 for _, test := range tests { 198 allErrs := validatePolicies(test.policies, field.NewPath("policies"), "default") 199 if len(allErrs) == 0 { 200 t.Errorf("validatePolicies() returned no errors for invalid input for the case of %s", test.msg) 201 } 202 } 203 } 204 205 func TestValidateTLS(t *testing.T) { 206 validTLSes := []*v1.TLS{ 207 nil, 208 { 209 Secret: "", 210 }, 211 { 212 Secret: "my-secret", 213 }, 214 { 215 Secret: "my-secret", 216 Redirect: &v1.TLSRedirect{}, 217 }, 218 { 219 Secret: "my-secret", 220 Redirect: &v1.TLSRedirect{ 221 Enable: true, 222 }, 223 }, 224 { 225 Secret: "my-secret", 226 Redirect: &v1.TLSRedirect{ 227 Enable: true, 228 Code: createPointerFromInt(302), 229 BasedOn: "scheme", 230 }, 231 }, 232 { 233 Secret: "my-secret", 234 Redirect: &v1.TLSRedirect{ 235 Enable: true, 236 Code: createPointerFromInt(307), 237 }, 238 }, 239 } 240 241 for _, tls := range validTLSes { 242 allErrs := validateTLS(tls, field.NewPath("tls")) 243 if len(allErrs) > 0 { 244 t.Errorf("validateTLS() returned errors %v for valid input %v", allErrs, tls) 245 } 246 } 247 248 invalidTLSes := []*v1.TLS{ 249 { 250 Secret: "-", 251 }, 252 { 253 Secret: "a/b", 254 }, 255 { 256 Secret: "my-secret", 257 Redirect: &v1.TLSRedirect{ 258 Enable: true, 259 Code: createPointerFromInt(305), 260 BasedOn: "scheme", 261 }, 262 }, 263 { 264 Secret: "my-secret", 265 Redirect: &v1.TLSRedirect{ 266 Enable: true, 267 Code: createPointerFromInt(301), 268 BasedOn: "invalidScheme", 269 }, 270 }, 271 } 272 273 for _, tls := range invalidTLSes { 274 allErrs := validateTLS(tls, field.NewPath("tls")) 275 if len(allErrs) == 0 { 276 t.Errorf("validateTLS() returned no errors for invalid input %v", tls) 277 } 278 } 279 } 280 281 func TestValidateUpstreams(t *testing.T) { 282 tests := []struct { 283 upstreams []v1.Upstream 284 expectedUpstreamNames sets.String 285 msg string 286 }{ 287 { 288 upstreams: []v1.Upstream{}, 289 expectedUpstreamNames: sets.String{}, 290 msg: "no upstreams", 291 }, 292 { 293 upstreams: []v1.Upstream{ 294 { 295 Name: "upstream1", 296 Service: "test-1", 297 Port: 80, 298 ProxyNextUpstream: "error timeout", 299 ProxyNextUpstreamTimeout: "10s", 300 ProxyNextUpstreamTries: 5, 301 MaxConns: createPointerFromInt(16), 302 }, 303 { 304 Name: "upstream2", 305 Subselector: map[string]string{"version": "test"}, 306 Service: "test-2", 307 Port: 80, 308 ProxyNextUpstream: "error timeout", 309 ProxyNextUpstreamTimeout: "10s", 310 ProxyNextUpstreamTries: 5, 311 }, 312 { 313 Name: "upstream3", 314 Service: "test-3", 315 Port: 80, 316 UseClusterIP: true, 317 }, 318 }, 319 expectedUpstreamNames: map[string]sets.Empty{ 320 "upstream1": {}, 321 "upstream2": {}, 322 "upstream3": {}, 323 }, 324 msg: "2 valid upstreams", 325 }, 326 } 327 328 vsv := &VirtualServerValidator{isPlus: false} 329 330 for _, test := range tests { 331 allErrs, resultUpstreamNames := vsv.validateUpstreams(test.upstreams, field.NewPath("upstreams")) 332 if len(allErrs) > 0 { 333 t.Errorf("validateUpstreams() returned errors %v for valid input for the case of %s", allErrs, test.msg) 334 } 335 if !resultUpstreamNames.Equal(test.expectedUpstreamNames) { 336 t.Errorf("validateUpstreams() returned %v expected %v for the case of %s", resultUpstreamNames, test.expectedUpstreamNames, test.msg) 337 } 338 } 339 } 340 341 func TestValidateUpstreamsFails(t *testing.T) { 342 tests := []struct { 343 upstreams []v1.Upstream 344 expectedUpstreamNames sets.String 345 msg string 346 }{ 347 { 348 upstreams: []v1.Upstream{ 349 { 350 Name: "@upstream1", 351 Service: "test-1", 352 Port: 80, 353 ProxyNextUpstream: "http_502", 354 ProxyNextUpstreamTimeout: "10s", 355 ProxyNextUpstreamTries: 5, 356 }, 357 }, 358 expectedUpstreamNames: sets.String{}, 359 msg: "invalid upstream name", 360 }, 361 { 362 upstreams: []v1.Upstream{ 363 { 364 Name: "upstream1", 365 Service: "@test-1", 366 Port: 80, 367 ProxyNextUpstream: "error timeout", 368 ProxyNextUpstreamTimeout: "10s", 369 ProxyNextUpstreamTries: 5, 370 }, 371 }, 372 expectedUpstreamNames: map[string]sets.Empty{ 373 "upstream1": {}, 374 }, 375 msg: "invalid service", 376 }, 377 { 378 upstreams: []v1.Upstream{ 379 { 380 Name: "upstream1", 381 Service: "test-1", 382 Port: 0, 383 ProxyNextUpstream: "error timeout", 384 ProxyNextUpstreamTimeout: "10s", 385 ProxyNextUpstreamTries: 5, 386 }, 387 }, 388 expectedUpstreamNames: map[string]sets.Empty{ 389 "upstream1": {}, 390 }, 391 msg: "invalid port", 392 }, 393 { 394 upstreams: []v1.Upstream{ 395 { 396 Name: "upstream1", 397 Service: "test-1", 398 Port: 80, 399 ProxyNextUpstream: "error timeout", 400 ProxyNextUpstreamTimeout: "10s", 401 ProxyNextUpstreamTries: 5, 402 }, 403 { 404 Name: "upstream1", 405 Service: "test-2", 406 Port: 80, 407 ProxyNextUpstream: "error timeout", 408 ProxyNextUpstreamTimeout: "10s", 409 ProxyNextUpstreamTries: 5, 410 }, 411 }, 412 expectedUpstreamNames: map[string]sets.Empty{ 413 "upstream1": {}, 414 }, 415 msg: "duplicated upstreams", 416 }, 417 { 418 upstreams: []v1.Upstream{ 419 { 420 Name: "upstream1", 421 Service: "test-1", 422 Port: 80, 423 ProxyNextUpstream: "https_504", 424 ProxyNextUpstreamTimeout: "10s", 425 ProxyNextUpstreamTries: 5, 426 }, 427 }, 428 expectedUpstreamNames: map[string]sets.Empty{ 429 "upstream1": {}, 430 }, 431 msg: "invalid next upstream syntax", 432 }, 433 { 434 upstreams: []v1.Upstream{ 435 { 436 Name: "upstream1", 437 Service: "test-1", 438 Port: 80, 439 ProxyNextUpstream: "http_504", 440 ProxyNextUpstreamTimeout: "-2s", 441 ProxyNextUpstreamTries: 5, 442 }, 443 }, 444 expectedUpstreamNames: map[string]sets.Empty{ 445 "upstream1": {}, 446 }, 447 msg: "invalid upstream timeout value", 448 }, 449 { 450 upstreams: []v1.Upstream{ 451 { 452 Name: "upstream1", 453 Service: "test-1", 454 Port: 80, 455 ProxyNextUpstream: "https_504", 456 ProxyNextUpstreamTimeout: "10s", 457 ProxyNextUpstreamTries: -1, 458 }, 459 }, 460 expectedUpstreamNames: map[string]sets.Empty{ 461 "upstream1": {}, 462 }, 463 msg: "invalid upstream tries value", 464 }, 465 { 466 upstreams: []v1.Upstream{ 467 { 468 Name: "upstream1", 469 Service: "test-1", 470 Port: 80, 471 MaxConns: createPointerFromInt(-1), 472 }, 473 }, 474 expectedUpstreamNames: map[string]sets.Empty{ 475 "upstream1": {}, 476 }, 477 msg: "negative value for MaxConns", 478 }, 479 { 480 upstreams: []v1.Upstream{ 481 { 482 Name: "upstream1", 483 Service: "test-1", 484 Port: 80, 485 ClientMaxBodySize: "7mins", 486 }, 487 }, 488 expectedUpstreamNames: map[string]sets.Empty{ 489 "upstream1": {}, 490 }, 491 msg: "invalid value for ClientMaxBodySize", 492 }, 493 { 494 upstreams: []v1.Upstream{ 495 { 496 Name: "upstream1", 497 Service: "test-1", 498 Port: 80, 499 ProxyBuffers: &v1.UpstreamBuffers{ 500 Number: -1, 501 Size: "1G", 502 }, 503 }, 504 }, 505 expectedUpstreamNames: map[string]sets.Empty{ 506 "upstream1": {}, 507 }, 508 msg: "invalid value for ProxyBuffers", 509 }, 510 { 511 upstreams: []v1.Upstream{ 512 { 513 Name: "upstream1", 514 Service: "test-1", 515 Port: 80, 516 ProxyBufferSize: "1G", 517 }, 518 }, 519 expectedUpstreamNames: map[string]sets.Empty{ 520 "upstream1": {}, 521 }, 522 msg: "invalid value for ProxyBufferSize", 523 }, 524 { 525 upstreams: []v1.Upstream{ 526 { 527 Name: "upstream1", 528 Service: "test-1", 529 Subselector: map[string]string{"\\$invalidkey": "test"}, 530 Port: 80, 531 }, 532 }, 533 expectedUpstreamNames: map[string]sets.Empty{ 534 "upstream1": {}, 535 }, 536 msg: "invalid key for subselector", 537 }, 538 { 539 upstreams: []v1.Upstream{ 540 { 541 Name: "upstream1", 542 Service: "test-1", 543 Subselector: map[string]string{"version": "test=fail"}, 544 Port: 80, 545 }, 546 }, 547 expectedUpstreamNames: map[string]sets.Empty{ 548 "upstream1": {}, 549 }, 550 msg: "invalid value for subselector", 551 }, 552 { 553 upstreams: []v1.Upstream{ 554 { 555 Name: "upstream1", 556 Service: "test-1", 557 Subselector: map[string]string{"version": "test"}, 558 UseClusterIP: true, 559 Port: 80, 560 }, 561 }, 562 expectedUpstreamNames: map[string]sets.Empty{ 563 "upstream1": {}, 564 }, 565 msg: "invalid use of subselector with use-cluster-ip", 566 }, 567 } 568 569 vsv := &VirtualServerValidator{isPlus: false} 570 571 for _, test := range tests { 572 allErrs, resultUpstreamNames := vsv.validateUpstreams(test.upstreams, field.NewPath("upstreams")) 573 if len(allErrs) == 0 { 574 t.Errorf("validateUpstreams() returned no errors for the case of %s", test.msg) 575 } 576 if !resultUpstreamNames.Equal(test.expectedUpstreamNames) { 577 t.Errorf("validateUpstreams() returned %v expected %v for the case of %s", resultUpstreamNames, test.expectedUpstreamNames, test.msg) 578 } 579 } 580 } 581 582 func TestValidateNextUpstream(t *testing.T) { 583 tests := []struct { 584 inputS string 585 }{ 586 { 587 inputS: "error timeout", 588 }, 589 { 590 inputS: "http_404 timeout", 591 }, 592 } 593 for _, test := range tests { 594 allErrs := validateNextUpstream(test.inputS, field.NewPath("next-upstreams")) 595 if len(allErrs) > 0 { 596 t.Errorf("validateNextUpstream(%q) returned errors %v for valid input.", test.inputS, allErrs) 597 } 598 } 599 } 600 601 func TestValidateNextUpstreamFails(t *testing.T) { 602 tests := []struct { 603 inputS string 604 }{ 605 { 606 inputS: "error error", 607 }, 608 { 609 inputS: "https_404", 610 }, 611 } 612 for _, test := range tests { 613 allErrs := validateNextUpstream(test.inputS, field.NewPath("next-upstreams")) 614 if len(allErrs) == 0 { 615 t.Errorf("validateNextUpstream(%q) didn't return errors %v for invalid input.", test.inputS, allErrs) 616 } 617 } 618 } 619 620 func TestValidateDNS1035Label(t *testing.T) { 621 validNames := []string{ 622 "test", 623 "test-123", 624 } 625 626 for _, name := range validNames { 627 allErrs := validateDNS1035Label(name, field.NewPath("name")) 628 if len(allErrs) > 0 { 629 t.Errorf("validateDNS1035Label(%q) returned errors %v for valid input", name, allErrs) 630 } 631 } 632 633 invalidNames := []string{ 634 "", 635 "123", 636 "test.123", 637 } 638 639 for _, name := range invalidNames { 640 allErrs := validateDNS1035Label(name, field.NewPath("name")) 641 if len(allErrs) == 0 { 642 t.Errorf("validateDNS1035Label(%q) returned no errors for invalid input", name) 643 } 644 } 645 } 646 647 func TestValidateVirtualServerRoutes(t *testing.T) { 648 tests := []struct { 649 routes []v1.Route 650 upstreamNames sets.String 651 msg string 652 }{ 653 { 654 routes: []v1.Route{}, 655 upstreamNames: sets.String{}, 656 msg: "no routes", 657 }, 658 { 659 routes: []v1.Route{ 660 { 661 Path: "/", 662 Action: &v1.Action{ 663 Pass: "test", 664 }, 665 }, 666 }, 667 upstreamNames: map[string]sets.Empty{ 668 "test": {}, 669 }, 670 msg: "valid route", 671 }, 672 } 673 674 vsv := &VirtualServerValidator{isPlus: false} 675 676 for _, test := range tests { 677 allErrs := vsv.validateVirtualServerRoutes(test.routes, field.NewPath("routes"), test.upstreamNames, "default") 678 if len(allErrs) > 0 { 679 t.Errorf("validateVirtualServerRoutes() returned errors %v for valid input for the case of %s", allErrs, test.msg) 680 } 681 } 682 } 683 684 func TestValidateVirtualServerRoutesFails(t *testing.T) { 685 tests := []struct { 686 routes []v1.Route 687 upstreamNames sets.String 688 msg string 689 }{ 690 { 691 routes: []v1.Route{ 692 { 693 Path: "/test", 694 Action: &v1.Action{ 695 Pass: "test-1", 696 }, 697 }, 698 { 699 Path: "/test", 700 Action: &v1.Action{ 701 Pass: "test-2", 702 }, 703 }, 704 }, 705 upstreamNames: map[string]sets.Empty{ 706 "test-1": {}, 707 "test-2": {}, 708 }, 709 msg: "duplicated paths", 710 }, 711 712 { 713 routes: []v1.Route{ 714 { 715 Path: "", 716 Action: nil, 717 }, 718 }, 719 upstreamNames: map[string]sets.Empty{}, 720 msg: "invalid route", 721 }, 722 } 723 724 vsv := &VirtualServerValidator{isPlus: false} 725 726 for _, test := range tests { 727 allErrs := vsv.validateVirtualServerRoutes(test.routes, field.NewPath("routes"), test.upstreamNames, "default") 728 if len(allErrs) == 0 { 729 t.Errorf("validateVirtualServerRoutes() returned no errors for the case of %s", test.msg) 730 } 731 } 732 } 733 734 func TestValidateRoute(t *testing.T) { 735 tests := []struct { 736 route v1.Route 737 upstreamNames sets.String 738 isRouteFieldForbidden bool 739 msg string 740 }{ 741 { 742 route: v1.Route{ 743 744 Path: "/", 745 Action: &v1.Action{ 746 Pass: "test", 747 }, 748 }, 749 upstreamNames: map[string]sets.Empty{ 750 "test": {}, 751 }, 752 isRouteFieldForbidden: false, 753 msg: "valid route with upstream", 754 }, 755 { 756 route: v1.Route{ 757 Path: "/", 758 Splits: []v1.Split{ 759 { 760 Weight: 90, 761 Action: &v1.Action{ 762 Pass: "test-1", 763 }, 764 }, 765 { 766 Weight: 10, 767 Action: &v1.Action{ 768 Pass: "test-2", 769 }, 770 }, 771 }, 772 }, 773 upstreamNames: map[string]sets.Empty{ 774 "test-1": {}, 775 "test-2": {}, 776 }, 777 isRouteFieldForbidden: false, 778 msg: "valid upstream with splits", 779 }, 780 { 781 route: v1.Route{ 782 Path: "/", 783 Matches: []v1.Match{ 784 { 785 Conditions: []v1.Condition{ 786 { 787 Header: "x-version", 788 Value: "test-1", 789 }, 790 }, 791 Action: &v1.Action{ 792 Pass: "test-1", 793 }, 794 }, 795 }, 796 Action: &v1.Action{ 797 Pass: "test-2", 798 }, 799 }, 800 upstreamNames: map[string]sets.Empty{ 801 "test-1": {}, 802 "test-2": {}, 803 }, 804 isRouteFieldForbidden: false, 805 msg: "valid action with matches", 806 }, 807 { 808 route: v1.Route{ 809 810 Path: "/", 811 Route: "default/test", 812 }, 813 upstreamNames: map[string]sets.Empty{}, 814 isRouteFieldForbidden: false, 815 msg: "valid route with route", 816 }, 817 } 818 819 vsv := &VirtualServerValidator{isPlus: false} 820 821 for _, test := range tests { 822 allErrs := vsv.validateRoute(test.route, field.NewPath("route"), test.upstreamNames, test.isRouteFieldForbidden, "default") 823 if len(allErrs) > 0 { 824 t.Errorf("validateRoute() returned errors %v for valid input for the case of %s", allErrs, test.msg) 825 } 826 } 827 } 828 829 func TestValidateRouteFails(t *testing.T) { 830 tests := []struct { 831 route v1.Route 832 upstreamNames sets.String 833 isRouteFieldForbidden bool 834 msg string 835 }{ 836 { 837 route: v1.Route{ 838 Path: "", 839 Action: &v1.Action{ 840 Pass: "test", 841 }, 842 }, 843 upstreamNames: map[string]sets.Empty{ 844 "test": {}, 845 }, 846 isRouteFieldForbidden: false, 847 msg: "empty path", 848 }, 849 { 850 route: v1.Route{ 851 Path: "/test", 852 Action: &v1.Action{ 853 Pass: "-test", 854 }, 855 }, 856 upstreamNames: sets.String{}, 857 isRouteFieldForbidden: false, 858 msg: "invalid pass action", 859 }, 860 { 861 route: v1.Route{ 862 Path: "/", 863 Action: &v1.Action{ 864 Pass: "test", 865 }, 866 }, 867 upstreamNames: sets.String{}, 868 isRouteFieldForbidden: false, 869 msg: "non-existing upstream in pass action", 870 }, 871 { 872 route: v1.Route{ 873 Path: "/", 874 Action: &v1.Action{ 875 Pass: "test", 876 }, 877 Splits: []v1.Split{ 878 { 879 Weight: 90, 880 Action: &v1.Action{ 881 Pass: "test-1", 882 }, 883 }, 884 { 885 Weight: 10, 886 Action: &v1.Action{ 887 Pass: "test-2", 888 }, 889 }, 890 }, 891 }, 892 upstreamNames: map[string]sets.Empty{ 893 "test": {}, 894 "test-1": {}, 895 "test-2": {}, 896 }, 897 isRouteFieldForbidden: false, 898 msg: "both action and splits exist", 899 }, 900 { 901 route: v1.Route{ 902 Path: "/", 903 Splits: []v1.Split{ 904 { 905 Weight: 90, 906 Action: &v1.Action{ 907 Pass: "test-1", 908 }, 909 }, 910 { 911 Weight: 10, 912 Action: &v1.Action{ 913 Pass: "test-2", 914 }, 915 }, 916 }, 917 Matches: []v1.Match{ 918 { 919 Conditions: []v1.Condition{ 920 { 921 Header: "x-version", 922 Value: "test-1", 923 }, 924 }, 925 Action: &v1.Action{ 926 Pass: "test-1", 927 }, 928 }, 929 }, 930 Action: &v1.Action{ 931 Pass: "test-2", 932 }, 933 }, 934 upstreamNames: map[string]sets.Empty{ 935 "test-1": {}, 936 "test-2": {}, 937 }, 938 isRouteFieldForbidden: false, 939 msg: "both splits and matches exist", 940 }, 941 { 942 route: v1.Route{ 943 Path: "/", 944 Route: "default/test", 945 }, 946 upstreamNames: map[string]sets.Empty{}, 947 isRouteFieldForbidden: true, 948 msg: "route field exists but is forbidden", 949 }, 950 } 951 952 vsv := &VirtualServerValidator{isPlus: false} 953 954 for _, test := range tests { 955 allErrs := vsv.validateRoute(test.route, field.NewPath("route"), test.upstreamNames, test.isRouteFieldForbidden, "default") 956 if len(allErrs) == 0 { 957 t.Errorf("validateRoute() returned no errors for invalid input for the case of %s", test.msg) 958 } 959 } 960 } 961 962 func TestValidateAction(t *testing.T) { 963 upstreamNames := map[string]sets.Empty{ 964 "test": {}, 965 } 966 tests := []struct { 967 action *v1.Action 968 msg string 969 }{ 970 { 971 action: &v1.Action{ 972 Pass: "test", 973 }, 974 msg: "base pass action", 975 }, 976 { 977 action: &v1.Action{ 978 Redirect: &v1.ActionRedirect{ 979 URL: "http://www.nginx.com", 980 }, 981 }, 982 msg: "base redirect action", 983 }, 984 { 985 action: &v1.Action{ 986 Redirect: &v1.ActionRedirect{ 987 URL: "http://www.nginx.com", 988 Code: 302, 989 }, 990 }, 991 msg: "redirect action with status code set", 992 }, 993 { 994 action: &v1.Action{ 995 Proxy: &v1.ActionProxy{ 996 Upstream: "test", 997 RewritePath: "/rewrite", 998 RequestHeaders: &v1.ProxyRequestHeaders{ 999 Set: []v1.Header{ 1000 { 1001 Name: "Header-Name", 1002 Value: "value", 1003 }, 1004 }, 1005 }, 1006 ResponseHeaders: &v1.ProxyResponseHeaders{ 1007 Hide: []string{"header-name"}, 1008 Pass: []string{"header-name"}, 1009 Ignore: []string{"Expires"}, 1010 Add: []v1.AddHeader{ 1011 { 1012 Header: v1.Header{ 1013 Name: "Header-Name", 1014 Value: "value", 1015 }, 1016 Always: true, 1017 }, 1018 }, 1019 }, 1020 }, 1021 }, 1022 msg: "proxy action with rewritePath, requestHeaders and responseHeaders", 1023 }, 1024 } 1025 1026 vsv := &VirtualServerValidator{isPlus: false} 1027 1028 for _, test := range tests { 1029 allErrs := vsv.validateAction(test.action, field.NewPath("action"), upstreamNames, "", false) 1030 if len(allErrs) > 0 { 1031 t.Errorf("validateAction() returned errors %v for valid input for the case of %s", allErrs, test.msg) 1032 } 1033 } 1034 } 1035 1036 func TestValidateActionFails(t *testing.T) { 1037 upstreamNames := map[string]sets.Empty{} 1038 1039 tests := []struct { 1040 action *v1.Action 1041 msg string 1042 }{ 1043 1044 { 1045 action: &v1.Action{}, 1046 msg: "empty action", 1047 }, 1048 { 1049 action: &v1.Action{ 1050 Redirect: &v1.ActionRedirect{}, 1051 }, 1052 msg: "missing required field url", 1053 }, 1054 { 1055 action: &v1.Action{ 1056 Pass: "test", 1057 Redirect: &v1.ActionRedirect{ 1058 URL: "http://www.nginx.com", 1059 }, 1060 }, 1061 msg: "multiple actions defined", 1062 }, 1063 { 1064 action: &v1.Action{ 1065 Redirect: &v1.ActionRedirect{ 1066 URL: "http://www.nginx.com", 1067 Code: 305, 1068 }, 1069 }, 1070 msg: "redirect action with invalid status code set", 1071 }, 1072 { 1073 action: &v1.Action{ 1074 Proxy: &v1.ActionProxy{ 1075 Upstream: "", 1076 }, 1077 }, 1078 msg: "proxy action with missing upstream field", 1079 }, 1080 } 1081 1082 vsv := &VirtualServerValidator{isPlus: false} 1083 1084 for _, test := range tests { 1085 allErrs := vsv.validateAction(test.action, field.NewPath("action"), upstreamNames, "", false) 1086 if len(allErrs) == 0 { 1087 t.Errorf("validateAction() returned no errors for invalid input for the case of %s", test.msg) 1088 } 1089 } 1090 } 1091 1092 func TestCaptureVariables(t *testing.T) { 1093 tests := []struct { 1094 s string 1095 expected []string 1096 }{ 1097 { 1098 "${scheme}://${host}", 1099 []string{"scheme", "host"}, 1100 }, 1101 { 1102 "http://www.nginx.org", 1103 nil, 1104 }, 1105 { 1106 "${}", 1107 []string{""}, 1108 }, 1109 } 1110 for _, test := range tests { 1111 result := captureVariables(test.s) 1112 if !reflect.DeepEqual(result, test.expected) { 1113 t.Errorf("captureVariables(%s) returned %v but expected %v", test.s, result, test.expected) 1114 } 1115 } 1116 } 1117 1118 func TestValidateRedirectURL(t *testing.T) { 1119 tests := []struct { 1120 redirectURL string 1121 msg string 1122 }{ 1123 { 1124 redirectURL: "http://www.nginx.com", 1125 msg: "base redirect url", 1126 }, 1127 { 1128 redirectURL: "${scheme}://${host}/sorry", 1129 msg: "multi variable redirect url", 1130 }, 1131 { 1132 redirectURL: "${http_x_forwarded_proto}://${host}/sorry", 1133 msg: "x-forwarded-proto redirect url use case", 1134 }, 1135 { 1136 redirectURL: "http://${host}${request_uri}", 1137 msg: "use multi variables, no scheme set", 1138 }, 1139 { 1140 redirectURL: "${scheme}://www.${host}${request_uri}", 1141 msg: "use multi variables", 1142 }, 1143 { 1144 redirectURL: "http://example.com/redirect?source=abc", 1145 msg: "arg variable use", 1146 }, 1147 { 1148 redirectURL: `\"${scheme}://${host}\"`, 1149 msg: "url with escaped quotes", 1150 }, 1151 { 1152 redirectURL: "http://{abc}", 1153 msg: "url with curly braces with no $ prefix", 1154 }, 1155 } 1156 1157 vsv := &VirtualServerValidator{isPlus: false} 1158 1159 for _, test := range tests { 1160 allErrs := vsv.validateRedirectURL(test.redirectURL, field.NewPath("url"), validRedirectVariableNames) 1161 if len(allErrs) > 0 { 1162 t.Errorf("validateRedirectURL(%s) returned errors %v for valid input for the case of %s", test.redirectURL, allErrs, test.msg) 1163 } 1164 } 1165 } 1166 1167 func TestValidateRedirectURLFails(t *testing.T) { 1168 tests := []struct { 1169 redirectURL string 1170 msg string 1171 }{ 1172 1173 { 1174 redirectURL: "", 1175 msg: "url is blank", 1176 }, 1177 { 1178 redirectURL: "/uri", 1179 msg: "url does not start with http://, https:// or ${scheme}://", 1180 }, 1181 { 1182 redirectURL: "$scheme://www.$host.com", 1183 msg: "usage of nginx variable in url without ${}", 1184 }, 1185 { 1186 redirectURL: "${scheme}://www.${invalid}.com", 1187 msg: "invalid nginx variable in url", 1188 }, 1189 { 1190 redirectURL: "${scheme}://www.${{host}.com", 1191 msg: "leading curly brace", 1192 }, 1193 { 1194 redirectURL: "${host.abc}.com", 1195 msg: "multi var in curly brace", 1196 }, 1197 { 1198 redirectURL: "${scheme}://www.${host{host}}.com", 1199 msg: "nested nginx vars", 1200 }, 1201 { 1202 redirectURL: `"${scheme}://${host}"`, 1203 msg: "url in unescaped quotes", 1204 }, 1205 { 1206 redirectURL: `"${scheme}://${host}`, 1207 msg: "url with unescaped quote prefix", 1208 }, 1209 { 1210 redirectURL: `\\"${scheme}://${host}\\"`, 1211 msg: "url with escaped backslash", 1212 }, 1213 { 1214 redirectURL: `${scheme}://${host}$`, 1215 msg: "url with ending $", 1216 }, 1217 { 1218 redirectURL: `http://${}`, 1219 msg: "url containing blank var", 1220 }, 1221 { 1222 redirectURL: `http://${abca`, 1223 msg: "url containing a var without ending }", 1224 }, 1225 } 1226 1227 vsv := &VirtualServerValidator{isPlus: false} 1228 1229 for _, test := range tests { 1230 allErrs := vsv.validateRedirectURL(test.redirectURL, field.NewPath("action"), validRedirectVariableNames) 1231 if len(allErrs) == 0 { 1232 t.Errorf("validateRedirectURL(%s) returned no errors for invalid input for the case of %s", test.redirectURL, test.msg) 1233 } 1234 } 1235 } 1236 1237 func TestValidateRouteField(t *testing.T) { 1238 validRouteFields := []string{ 1239 "coffee", 1240 "default/coffee", 1241 } 1242 1243 for _, rf := range validRouteFields { 1244 allErrs := validateRouteField(rf, field.NewPath("route")) 1245 if len(allErrs) > 0 { 1246 t.Errorf("validRouteField(%q) returned errors %v for valid input", rf, allErrs) 1247 } 1248 } 1249 1250 invalidRouteFields := []string{ 1251 "-", 1252 "/coffee", 1253 "-/coffee", 1254 } 1255 1256 for _, rf := range invalidRouteFields { 1257 allErrs := validateRouteField(rf, field.NewPath("route")) 1258 if len(allErrs) == 0 { 1259 t.Errorf("validRouteField(%q) returned no errors for invalid input", rf) 1260 } 1261 } 1262 } 1263 1264 func TestValdateReferencedUpstream(t *testing.T) { 1265 upstream := "test" 1266 upstreamNames := map[string]sets.Empty{ 1267 "test": {}, 1268 } 1269 1270 allErrs := validateReferencedUpstream(upstream, field.NewPath("upstream"), upstreamNames) 1271 if len(allErrs) > 0 { 1272 t.Errorf("validateReferencedUpstream() returned errors %v for valid input", allErrs) 1273 } 1274 } 1275 1276 func TestValdateUpstreamFails(t *testing.T) { 1277 tests := []struct { 1278 upstream string 1279 upstreamNames sets.String 1280 msg string 1281 }{ 1282 { 1283 upstream: "", 1284 upstreamNames: map[string]sets.Empty{}, 1285 msg: "empty upstream", 1286 }, 1287 { 1288 upstream: "-test", 1289 upstreamNames: map[string]sets.Empty{}, 1290 msg: "invalid upstream", 1291 }, 1292 { 1293 upstream: "test", 1294 upstreamNames: map[string]sets.Empty{}, 1295 msg: "non-existing upstream", 1296 }, 1297 } 1298 1299 for _, test := range tests { 1300 allErrs := validateReferencedUpstream(test.upstream, field.NewPath("upstream"), test.upstreamNames) 1301 if len(allErrs) == 0 { 1302 t.Errorf("validateReferencedUpstream() returned no errors for invalid input for the case of %s", test.msg) 1303 } 1304 } 1305 } 1306 1307 func TestValidateRegexPath(t *testing.T) { 1308 tests := []struct { 1309 regexPath string 1310 msg string 1311 }{ 1312 { 1313 regexPath: "~ ^/foo.*\\.jpg", 1314 msg: "case sensitive regexp", 1315 }, 1316 { 1317 regexPath: "~* ^/Bar.*\\.jpg", 1318 msg: "case insensitive regexp", 1319 }, 1320 { 1321 regexPath: `~ ^/f\"oo.*\\.jpg`, 1322 msg: "regexp with escaped double quotes", 1323 }, 1324 } 1325 1326 for _, test := range tests { 1327 allErrs := validateRegexPath(test.regexPath, field.NewPath("path")) 1328 if len(allErrs) != 0 { 1329 t.Errorf("validateRegexPath(%v) returned errors for valid input for the case of %v", test.regexPath, test.msg) 1330 } 1331 } 1332 } 1333 1334 func TestValidateRegexPathFails(t *testing.T) { 1335 tests := []struct { 1336 regexPath string 1337 msg string 1338 }{ 1339 { 1340 regexPath: "~ [{", 1341 msg: "invalid regexp", 1342 }, 1343 { 1344 regexPath: `~ /foo"`, 1345 msg: "unescaped double quotes", 1346 }, 1347 { 1348 regexPath: `~"`, 1349 msg: "empty regex", 1350 }, 1351 { 1352 regexPath: `~ /foo\`, 1353 msg: "ending in backslash", 1354 }, 1355 } 1356 1357 for _, test := range tests { 1358 allErrs := validateRegexPath(test.regexPath, field.NewPath("path")) 1359 if len(allErrs) == 0 { 1360 t.Errorf("validateRegexPath(%v) returned no errors for invalid input for the case of %v", test.regexPath, test.msg) 1361 } 1362 } 1363 } 1364 1365 func TestValidateRoutePath(t *testing.T) { 1366 validPaths := []string{ 1367 "/", 1368 "~ /^foo.*\\.jpg", 1369 "~* /^Bar.*\\.jpg", 1370 "=/exact/match", 1371 } 1372 1373 for _, path := range validPaths { 1374 allErrs := validateRoutePath(path, field.NewPath("path")) 1375 if len(allErrs) != 0 { 1376 t.Errorf("validateRoutePath(%v) returned errors for valid input", path) 1377 } 1378 } 1379 1380 invalidPaths := []string{ 1381 "", 1382 "invalid", 1383 } 1384 1385 for _, path := range invalidPaths { 1386 allErrs := validateRoutePath(path, field.NewPath("path")) 1387 if len(allErrs) == 0 { 1388 t.Errorf("validateRoutePath(%v) returned no errors for invalid input", path) 1389 } 1390 } 1391 } 1392 1393 func TestValidatePath(t *testing.T) { 1394 validPaths := []string{ 1395 "/", 1396 "/path", 1397 "/a-1/_A/", 1398 } 1399 1400 for _, path := range validPaths { 1401 allErrs := validatePath(path, field.NewPath("path")) 1402 if len(allErrs) > 0 { 1403 t.Errorf("validatePath(%q) returned errors %v for valid input", path, allErrs) 1404 } 1405 } 1406 1407 invalidPaths := []string{ 1408 "", 1409 " /", 1410 "/ ", 1411 "/{", 1412 "/}", 1413 "/abc;", 1414 } 1415 1416 for _, path := range invalidPaths { 1417 allErrs := validatePath(path, field.NewPath("path")) 1418 if len(allErrs) == 0 { 1419 t.Errorf("validatePath(%q) returned no errors for invalid input", path) 1420 } 1421 } 1422 } 1423 1424 func TestValidateSplits(t *testing.T) { 1425 splits := []v1.Split{ 1426 { 1427 Weight: 90, 1428 Action: &v1.Action{ 1429 Pass: "test-1", 1430 }, 1431 }, 1432 { 1433 Weight: 10, 1434 Action: &v1.Action{ 1435 Proxy: &v1.ActionProxy{ 1436 Upstream: "test-2", 1437 }, 1438 }, 1439 }, 1440 } 1441 upstreamNames := map[string]sets.Empty{ 1442 "test-1": {}, 1443 "test-2": {}, 1444 } 1445 1446 vsv := &VirtualServerValidator{isPlus: false} 1447 1448 allErrs := vsv.validateSplits(splits, field.NewPath("splits"), upstreamNames, "") 1449 if len(allErrs) > 0 { 1450 t.Errorf("validateSplits() returned errors %v for valid input", allErrs) 1451 } 1452 } 1453 1454 func TestValidateSplitsFails(t *testing.T) { 1455 tests := []struct { 1456 splits []v1.Split 1457 upstreamNames sets.String 1458 msg string 1459 }{ 1460 { 1461 splits: []v1.Split{ 1462 { 1463 Weight: 90, 1464 Action: &v1.Action{ 1465 Pass: "test-1", 1466 }, 1467 }, 1468 }, 1469 upstreamNames: map[string]sets.Empty{ 1470 "test-1": {}, 1471 }, 1472 msg: "only one split", 1473 }, 1474 { 1475 splits: []v1.Split{ 1476 { 1477 Weight: 123, 1478 Action: &v1.Action{ 1479 Pass: "test-1", 1480 }, 1481 }, 1482 { 1483 Weight: 10, 1484 Action: &v1.Action{ 1485 Pass: "test-2", 1486 }, 1487 }, 1488 }, 1489 upstreamNames: map[string]sets.Empty{ 1490 "test-1": {}, 1491 "test-2": {}, 1492 }, 1493 msg: "invalid weight", 1494 }, 1495 { 1496 splits: []v1.Split{ 1497 { 1498 Weight: 99, 1499 Action: &v1.Action{ 1500 Pass: "test-1", 1501 }, 1502 }, 1503 { 1504 Weight: 99, 1505 Action: &v1.Action{ 1506 Pass: "test-2", 1507 }, 1508 }, 1509 }, 1510 upstreamNames: map[string]sets.Empty{ 1511 "test-1": {}, 1512 "test-2": {}, 1513 }, 1514 msg: "invalid total weight", 1515 }, 1516 { 1517 splits: []v1.Split{ 1518 { 1519 Weight: 90, 1520 Action: &v1.Action{ 1521 Pass: "", 1522 }, 1523 }, 1524 { 1525 Weight: 10, 1526 Action: &v1.Action{ 1527 Pass: "test-2", 1528 }, 1529 }, 1530 }, 1531 upstreamNames: map[string]sets.Empty{ 1532 "test-1": {}, 1533 "test-2": {}, 1534 }, 1535 msg: "invalid action", 1536 }, 1537 { 1538 splits: []v1.Split{ 1539 { 1540 Weight: 90, 1541 Action: &v1.Action{ 1542 Pass: "some-upstream", 1543 }, 1544 }, 1545 { 1546 Weight: 10, 1547 Action: &v1.Action{ 1548 Pass: "test-2", 1549 }, 1550 }, 1551 }, 1552 upstreamNames: map[string]sets.Empty{ 1553 "test-1": {}, 1554 "test-2": {}, 1555 }, 1556 msg: "invalid action with non-existing upstream", 1557 }, 1558 } 1559 1560 vsv := &VirtualServerValidator{isPlus: false} 1561 1562 for _, test := range tests { 1563 allErrs := vsv.validateSplits(test.splits, field.NewPath("splits"), test.upstreamNames, "") 1564 if len(allErrs) == 0 { 1565 t.Errorf("validateSplits() returned no errors for invalid input for the case of %s", test.msg) 1566 } 1567 } 1568 } 1569 1570 func TestValidateCondition(t *testing.T) { 1571 tests := []struct { 1572 condition v1.Condition 1573 msg string 1574 }{ 1575 { 1576 condition: v1.Condition{ 1577 Header: "x-version", 1578 Value: "v1", 1579 }, 1580 msg: "valid header", 1581 }, 1582 { 1583 condition: v1.Condition{ 1584 Cookie: "my_cookie", 1585 Value: "", 1586 }, 1587 msg: "valid cookie", 1588 }, 1589 { 1590 condition: v1.Condition{ 1591 Argument: "arg", 1592 Value: "yes", 1593 }, 1594 msg: "valid argument", 1595 }, 1596 { 1597 condition: v1.Condition{ 1598 Variable: "$request_method", 1599 Value: "POST", 1600 }, 1601 msg: "valid variable", 1602 }, 1603 } 1604 1605 for _, test := range tests { 1606 allErrs := validateCondition(test.condition, field.NewPath("condition")) 1607 if len(allErrs) > 0 { 1608 t.Errorf("validateCondition() returned errors %v for valid input for the case of %s", allErrs, test.msg) 1609 } 1610 } 1611 } 1612 1613 func TestValidateConditionFails(t *testing.T) { 1614 tests := []struct { 1615 condition v1.Condition 1616 msg string 1617 }{ 1618 { 1619 condition: v1.Condition{}, 1620 msg: "empty condition", 1621 }, 1622 { 1623 condition: v1.Condition{ 1624 Header: "x-version", 1625 Cookie: "user", 1626 Argument: "answer", 1627 Variable: "$request_method", 1628 Value: "something", 1629 }, 1630 msg: "invalid condition", 1631 }, 1632 { 1633 condition: v1.Condition{ 1634 Header: "x_version", 1635 }, 1636 msg: "invalid header", 1637 }, 1638 { 1639 condition: v1.Condition{ 1640 Cookie: "my-cookie", 1641 }, 1642 msg: "invalid cookie", 1643 }, 1644 { 1645 condition: v1.Condition{ 1646 Argument: "my-arg", 1647 }, 1648 msg: "invalid argument", 1649 }, 1650 { 1651 condition: v1.Condition{ 1652 Variable: "request_method", 1653 }, 1654 msg: "invalid variable", 1655 }, 1656 } 1657 1658 for _, test := range tests { 1659 allErrs := validateCondition(test.condition, field.NewPath("condition")) 1660 if len(allErrs) == 0 { 1661 t.Errorf("validateCondition() returned no errors for invalid input for the case of %s", test.msg) 1662 } 1663 } 1664 } 1665 1666 func TestIsCookieName(t *testing.T) { 1667 validCookieNames := []string{ 1668 "123", 1669 "my_cookie", 1670 } 1671 1672 for _, name := range validCookieNames { 1673 errs := isCookieName(name) 1674 if len(errs) > 0 { 1675 t.Errorf("isCookieName(%q) returned errors %v for valid input", name, errs) 1676 } 1677 } 1678 1679 invalidCookieNames := []string{ 1680 "", 1681 "my-cookie", 1682 "cookie!", 1683 } 1684 1685 for _, name := range invalidCookieNames { 1686 errs := isCookieName(name) 1687 if len(errs) == 0 { 1688 t.Errorf("isCookieName(%q) returned no errors for invalid input", name) 1689 } 1690 } 1691 } 1692 1693 func TestIsArgumentName(t *testing.T) { 1694 validArgumentNames := []string{ 1695 "123", 1696 "my_arg", 1697 } 1698 1699 for _, name := range validArgumentNames { 1700 errs := isArgumentName(name) 1701 if len(errs) > 0 { 1702 t.Errorf("isArgumentName(%q) returned errors %v for valid input", name, errs) 1703 } 1704 } 1705 1706 invalidArgumentNames := []string{ 1707 "", 1708 "my-arg", 1709 "arg!", 1710 } 1711 1712 for _, name := range invalidArgumentNames { 1713 errs := isArgumentName(name) 1714 if len(errs) == 0 { 1715 t.Errorf("isArgumentName(%q) returned no errors for invalid input", name) 1716 } 1717 } 1718 } 1719 1720 func TestValidateVariableName(t *testing.T) { 1721 validNames := []string{ 1722 "$request_method", 1723 } 1724 1725 for _, name := range validNames { 1726 allErrs := validateVariableName(name, field.NewPath("variable")) 1727 if len(allErrs) > 0 { 1728 t.Errorf("validateVariableName(%q) returned errors %v for valid input", name, allErrs) 1729 } 1730 } 1731 1732 invalidNames := []string{ 1733 "request_method", 1734 "$request_id", 1735 } 1736 1737 for _, name := range invalidNames { 1738 allErrs := validateVariableName(name, field.NewPath("variable")) 1739 if len(allErrs) == 0 { 1740 t.Errorf("validateVariableName(%q) returned no errors for invalid input", name) 1741 } 1742 } 1743 } 1744 1745 func TestValidateMatch(t *testing.T) { 1746 tests := []struct { 1747 match v1.Match 1748 upstreamNames sets.String 1749 msg string 1750 }{ 1751 { 1752 match: v1.Match{ 1753 Conditions: []v1.Condition{ 1754 { 1755 Cookie: "version", 1756 Value: "v1", 1757 }, 1758 }, 1759 Action: &v1.Action{ 1760 Pass: "test", 1761 }, 1762 }, 1763 upstreamNames: map[string]sets.Empty{ 1764 "test": {}, 1765 }, 1766 msg: "valid match with action", 1767 }, 1768 { 1769 match: v1.Match{ 1770 Conditions: []v1.Condition{ 1771 { 1772 Cookie: "version", 1773 Value: "v1", 1774 }, 1775 }, 1776 Splits: []v1.Split{ 1777 { 1778 Weight: 90, 1779 Action: &v1.Action{ 1780 Pass: "test-1", 1781 }, 1782 }, 1783 { 1784 Weight: 10, 1785 Action: &v1.Action{ 1786 Pass: "test-2", 1787 }, 1788 }, 1789 }, 1790 }, 1791 upstreamNames: map[string]sets.Empty{ 1792 "test-1": {}, 1793 "test-2": {}, 1794 }, 1795 msg: "valid match with splits", 1796 }, 1797 } 1798 1799 vsv := &VirtualServerValidator{isPlus: false} 1800 1801 for _, test := range tests { 1802 allErrs := vsv.validateMatch(test.match, field.NewPath("match"), test.upstreamNames, "") 1803 if len(allErrs) > 0 { 1804 t.Errorf("validateMatch() returned errors %v for valid input for the case of %s", allErrs, test.msg) 1805 } 1806 } 1807 } 1808 1809 func TestValidateMatchFails(t *testing.T) { 1810 tests := []struct { 1811 match v1.Match 1812 upstreamNames sets.String 1813 msg string 1814 }{ 1815 { 1816 match: v1.Match{ 1817 Conditions: []v1.Condition{}, 1818 Action: &v1.Action{ 1819 Pass: "test", 1820 }, 1821 }, 1822 upstreamNames: map[string]sets.Empty{ 1823 "test": {}, 1824 }, 1825 msg: "invalid number of conditions", 1826 }, 1827 { 1828 match: v1.Match{ 1829 Conditions: []v1.Condition{ 1830 { 1831 Cookie: "version", 1832 Value: `v1"`, 1833 }, 1834 }, 1835 Action: &v1.Action{ 1836 Pass: "test", 1837 }, 1838 }, 1839 upstreamNames: map[string]sets.Empty{ 1840 "test": {}, 1841 }, 1842 msg: "invalid condition", 1843 }, 1844 { 1845 match: v1.Match{ 1846 Conditions: []v1.Condition{ 1847 { 1848 Cookie: "version", 1849 Value: "v1", 1850 }, 1851 }, 1852 Action: &v1.Action{}, 1853 }, 1854 upstreamNames: map[string]sets.Empty{}, 1855 msg: "invalid action", 1856 }, 1857 { 1858 match: v1.Match{ 1859 Conditions: []v1.Condition{ 1860 { 1861 Cookie: "version", 1862 Value: "v1", 1863 }, 1864 }, 1865 Action: &v1.Action{ 1866 Pass: "test-1", 1867 }, 1868 Splits: []v1.Split{ 1869 { 1870 Weight: 90, 1871 Action: &v1.Action{ 1872 Pass: "test-1", 1873 }, 1874 }, 1875 { 1876 Weight: 10, 1877 Action: &v1.Action{ 1878 Pass: "test-2", 1879 }, 1880 }, 1881 }, 1882 }, 1883 upstreamNames: map[string]sets.Empty{ 1884 "test-1": {}, 1885 "test-2": {}, 1886 }, 1887 msg: "both splits and action are set", 1888 }, 1889 } 1890 1891 vsv := &VirtualServerValidator{isPlus: false} 1892 1893 for _, test := range tests { 1894 allErrs := vsv.validateMatch(test.match, field.NewPath("match"), test.upstreamNames, "") 1895 if len(allErrs) == 0 { 1896 t.Errorf("validateMatch() returned no errors for invalid input for the case of %s", test.msg) 1897 } 1898 } 1899 } 1900 1901 func TestIsValidMatchValue(t *testing.T) { 1902 validValues := []string{ 1903 "abc", 1904 "123", 1905 `\" 1906 abc\"`, 1907 `\"`, 1908 } 1909 1910 for _, value := range validValues { 1911 errs := isValidMatchValue(value) 1912 if len(errs) > 0 { 1913 t.Errorf("isValidMatchValue(%q) returned errors %v for valid input", value, errs) 1914 } 1915 } 1916 1917 invalidValues := []string{ 1918 `"`, 1919 `\`, 1920 `abc"`, 1921 `abc\\\`, 1922 `a"b`, 1923 } 1924 1925 for _, value := range invalidValues { 1926 errs := isValidMatchValue(value) 1927 if len(errs) == 0 { 1928 t.Errorf("isValidMatchValue(%q) returned no errors for invalid input", value) 1929 } 1930 } 1931 } 1932 1933 func TestValidateVirtualServerRoute(t *testing.T) { 1934 virtualServerRoute := v1.VirtualServerRoute{ 1935 ObjectMeta: meta_v1.ObjectMeta{ 1936 Name: "coffee", 1937 Namespace: "default", 1938 }, 1939 Spec: v1.VirtualServerRouteSpec{ 1940 Host: "example.com", 1941 Upstreams: []v1.Upstream{ 1942 { 1943 Name: "first", 1944 Service: "service-1", 1945 Port: 80, 1946 }, 1947 { 1948 Name: "second", 1949 Service: "service-2", 1950 Port: 80, 1951 }, 1952 }, 1953 Subroutes: []v1.Route{ 1954 { 1955 Path: "/test/first", 1956 Action: &v1.Action{ 1957 Pass: "first", 1958 }, 1959 }, 1960 { 1961 Path: "/test/second", 1962 Action: &v1.Action{ 1963 Pass: "second", 1964 }, 1965 }, 1966 }, 1967 }, 1968 } 1969 1970 vsv := &VirtualServerValidator{isPlus: false} 1971 1972 err := vsv.ValidateVirtualServerRoute(&virtualServerRoute) 1973 if err != nil { 1974 t.Errorf("ValidateVirtualServerRoute() returned error %v for valid input %v", err, virtualServerRoute) 1975 } 1976 } 1977 1978 func TestValidateVirtualServerRouteForVirtualServer(t *testing.T) { 1979 virtualServerRoute := v1.VirtualServerRoute{ 1980 ObjectMeta: meta_v1.ObjectMeta{ 1981 Name: "coffee", 1982 Namespace: "default", 1983 }, 1984 Spec: v1.VirtualServerRouteSpec{ 1985 Host: "example.com", 1986 Upstreams: []v1.Upstream{ 1987 { 1988 Name: "first", 1989 Service: "service-1", 1990 Port: 80, 1991 }, 1992 { 1993 Name: "second", 1994 Service: "service-2", 1995 Port: 80, 1996 }, 1997 }, 1998 Subroutes: []v1.Route{ 1999 { 2000 Path: "/test/first", 2001 Action: &v1.Action{ 2002 Pass: "first", 2003 }, 2004 }, 2005 { 2006 Path: "/test/second", 2007 Action: &v1.Action{ 2008 Pass: "second", 2009 }, 2010 }, 2011 }, 2012 }, 2013 } 2014 virtualServerHost := "example.com" 2015 pathPrefix := "/test" 2016 2017 vsv := &VirtualServerValidator{isPlus: false} 2018 2019 err := vsv.ValidateVirtualServerRouteForVirtualServer(&virtualServerRoute, virtualServerHost, pathPrefix) 2020 if err != nil { 2021 t.Errorf("ValidateVirtualServerRouteForVirtualServer() returned error %v for valid input %v", err, virtualServerRoute) 2022 } 2023 } 2024 2025 func TestValidateVirtualServerRouteHost(t *testing.T) { 2026 virtualServerHost := "example.com" 2027 2028 validHost := "example.com" 2029 2030 allErrs := validateVirtualServerRouteHost(validHost, virtualServerHost, field.NewPath("host")) 2031 if len(allErrs) > 0 { 2032 t.Errorf("validateVirtualServerRouteHost() returned errors %v for valid input", allErrs) 2033 } 2034 2035 invalidHost := "foo.example.com" 2036 2037 allErrs = validateVirtualServerRouteHost(invalidHost, virtualServerHost, field.NewPath("host")) 2038 if len(allErrs) == 0 { 2039 t.Errorf("validateVirtualServerRouteHost() returned no errors for invalid input") 2040 } 2041 } 2042 2043 func TestValidateVirtualServerRouteSubroutes(t *testing.T) { 2044 tests := []struct { 2045 routes []v1.Route 2046 upstreamNames sets.String 2047 pathPrefix string 2048 msg string 2049 }{ 2050 { 2051 routes: []v1.Route{}, 2052 upstreamNames: sets.String{}, 2053 pathPrefix: "/", 2054 msg: "no routes", 2055 }, 2056 { 2057 routes: []v1.Route{ 2058 { 2059 Path: "/", 2060 Action: &v1.Action{ 2061 Pass: "test", 2062 }, 2063 }, 2064 }, 2065 upstreamNames: map[string]sets.Empty{ 2066 "test": {}, 2067 }, 2068 pathPrefix: "/", 2069 msg: "valid route", 2070 }, 2071 } 2072 2073 vsv := &VirtualServerValidator{isPlus: false} 2074 2075 for _, test := range tests { 2076 allErrs := vsv.validateVirtualServerRouteSubroutes(test.routes, field.NewPath("subroutes"), test.upstreamNames, 2077 test.pathPrefix, "default") 2078 if len(allErrs) > 0 { 2079 t.Errorf("validateVirtualServerRouteSubroutes() returned errors %v for valid input for the case of %s", allErrs, test.msg) 2080 } 2081 } 2082 } 2083 2084 func TestValidateVirtualServerRouteSubroutesFails(t *testing.T) { 2085 tests := []struct { 2086 routes []v1.Route 2087 upstreamNames sets.String 2088 pathPrefix string 2089 msg string 2090 }{ 2091 { 2092 routes: []v1.Route{ 2093 { 2094 Path: "/test", 2095 Action: &v1.Action{ 2096 Pass: "test-1", 2097 }, 2098 }, 2099 { 2100 Path: "/test", 2101 Action: &v1.Action{ 2102 Pass: "test-2", 2103 }, 2104 }, 2105 }, 2106 upstreamNames: map[string]sets.Empty{ 2107 "test-1": {}, 2108 "test-2": {}, 2109 }, 2110 pathPrefix: "/", 2111 msg: "duplicated paths", 2112 }, 2113 { 2114 routes: []v1.Route{ 2115 { 2116 Path: "", 2117 Action: nil, 2118 }, 2119 }, 2120 upstreamNames: map[string]sets.Empty{}, 2121 pathPrefix: "", 2122 msg: "invalid route", 2123 }, 2124 { 2125 routes: []v1.Route{ 2126 { 2127 Path: "/", 2128 Action: &v1.Action{ 2129 Pass: "test-1", 2130 }, 2131 }, 2132 }, 2133 upstreamNames: map[string]sets.Empty{ 2134 "test-1": {}, 2135 }, 2136 pathPrefix: "/abc", 2137 msg: "invalid prefix", 2138 }, 2139 } 2140 2141 vsv := &VirtualServerValidator{isPlus: false} 2142 2143 for _, test := range tests { 2144 allErrs := vsv.validateVirtualServerRouteSubroutes(test.routes, field.NewPath("subroutes"), test.upstreamNames, 2145 test.pathPrefix, "default") 2146 if len(allErrs) == 0 { 2147 t.Errorf("validateVirtualServerRouteSubroutes() returned no errors for the case of %s", test.msg) 2148 } 2149 } 2150 } 2151 2152 func TestValidateUpstreamLBMethod(t *testing.T) { 2153 tests := []struct { 2154 method string 2155 isPlus bool 2156 }{ 2157 { 2158 method: "round_robin", 2159 isPlus: false, 2160 }, 2161 { 2162 method: "", 2163 isPlus: false, 2164 }, 2165 { 2166 method: "ip_hash", 2167 isPlus: true, 2168 }, 2169 { 2170 method: "", 2171 isPlus: true, 2172 }, 2173 } 2174 2175 for _, test := range tests { 2176 allErrs := validateUpstreamLBMethod(test.method, field.NewPath("lb-method"), test.isPlus) 2177 2178 if len(allErrs) != 0 { 2179 t.Errorf("validateUpstreamLBMethod(%q, %v) returned errors for method %s", test.method, test.isPlus, test.method) 2180 } 2181 } 2182 } 2183 2184 func TestValidateUpstreamLBMethodFails(t *testing.T) { 2185 tests := []struct { 2186 method string 2187 isPlus bool 2188 }{ 2189 { 2190 method: "wrong", 2191 isPlus: false, 2192 }, 2193 { 2194 method: "wrong", 2195 isPlus: true, 2196 }, 2197 } 2198 2199 for _, test := range tests { 2200 allErrs := validateUpstreamLBMethod(test.method, field.NewPath("lb-method"), test.isPlus) 2201 2202 if len(allErrs) == 0 { 2203 t.Errorf("validateUpstreamLBMethod(%q, %v) returned no errors for method %s", test.method, test.isPlus, test.method) 2204 } 2205 } 2206 } 2207 2208 func TestValidatePositiveIntOrZeroFromPointer(t *testing.T) { 2209 tests := []struct { 2210 number *int 2211 msg string 2212 }{ 2213 { 2214 number: nil, 2215 msg: "valid (nil)", 2216 }, 2217 { 2218 number: createPointerFromInt(0), 2219 msg: "valid (0)", 2220 }, 2221 { 2222 number: createPointerFromInt(1), 2223 msg: "valid (1)", 2224 }, 2225 } 2226 2227 for _, test := range tests { 2228 allErrs := validatePositiveIntOrZeroFromPointer(test.number, field.NewPath("int-field")) 2229 2230 if len(allErrs) != 0 { 2231 t.Errorf("validatePositiveIntOrZeroFromPointer returned errors for case: %v", test.msg) 2232 } 2233 } 2234 } 2235 2236 func TestValidatePositiveIntOrZeroFromPointerFails(t *testing.T) { 2237 number := createPointerFromInt(-1) 2238 allErrs := validatePositiveIntOrZeroFromPointer(number, field.NewPath("int-field")) 2239 2240 if len(allErrs) == 0 { 2241 t.Error("validatePositiveIntOrZeroFromPointer returned no errors for case: invalid (-1)") 2242 } 2243 } 2244 2245 func TestValidatePositiveIntOrZero(t *testing.T) { 2246 tests := []struct { 2247 number int 2248 msg string 2249 }{ 2250 { 2251 number: 0, 2252 msg: "valid (0)", 2253 }, 2254 { 2255 number: 1, 2256 msg: "valid (1)", 2257 }, 2258 } 2259 2260 for _, test := range tests { 2261 allErrs := validatePositiveIntOrZero(test.number, field.NewPath("int-field")) 2262 2263 if len(allErrs) != 0 { 2264 t.Errorf("validatePositiveIntOrZero returned errors for case: %v", test.msg) 2265 } 2266 } 2267 } 2268 2269 func TestValidatePositiveIntOrZeroFails(t *testing.T) { 2270 allErrs := validatePositiveIntOrZero(-1, field.NewPath("int-field")) 2271 2272 if len(allErrs) == 0 { 2273 t.Error("validatePositiveIntOrZero returned no errors for case: invalid (-1)") 2274 } 2275 } 2276 2277 func TestValidateBuffer(t *testing.T) { 2278 validbuff := &v1.UpstreamBuffers{Number: 8, Size: "8k"} 2279 allErrs := validateBuffer(validbuff, field.NewPath("buffers-field")) 2280 2281 if len(allErrs) != 0 { 2282 t.Errorf("validateBuffer returned errors %v valid input %v", allErrs, validbuff) 2283 } 2284 2285 invalidbuff := []*v1.UpstreamBuffers{ 2286 { 2287 Number: -8, 2288 Size: "15m", 2289 }, 2290 { 2291 Number: 8, 2292 Size: "15G", 2293 }, 2294 { 2295 Number: 8, 2296 Size: "", 2297 }, 2298 } 2299 for _, test := range invalidbuff { 2300 allErrs = validateBuffer(test, field.NewPath("buffers-field")) 2301 if len(allErrs) == 0 { 2302 t.Errorf("validateBuffer didn't return error for invalid input %v.", test) 2303 } 2304 } 2305 } 2306 2307 func TestValidateUpstreamHealthCheck(t *testing.T) { 2308 hc := &v1.HealthCheck{ 2309 Enable: true, 2310 Path: "/healthz", 2311 Interval: "4s", 2312 Jitter: "2s", 2313 Fails: 3, 2314 Passes: 2, 2315 Port: 8080, 2316 TLS: &v1.UpstreamTLS{ 2317 Enable: true, 2318 }, 2319 ConnectTimeout: "1s", 2320 ReadTimeout: "1s", 2321 SendTimeout: "1s", 2322 Headers: []v1.Header{ 2323 { 2324 Name: "Host", 2325 Value: "my.service", 2326 }, 2327 }, 2328 StatusMatch: "! 500", 2329 } 2330 2331 allErrs := validateUpstreamHealthCheck(hc, field.NewPath("healthCheck")) 2332 2333 if len(allErrs) != 0 { 2334 t.Errorf("validateUpstreamHealthCheck() returned errors for valid input %v", hc) 2335 } 2336 } 2337 2338 func TestValidateUpstreamHealthCheckFails(t *testing.T) { 2339 tests := []struct { 2340 hc *v1.HealthCheck 2341 }{ 2342 { 2343 hc: &v1.HealthCheck{ 2344 Enable: true, 2345 Path: "/healthz//;", 2346 }, 2347 }, 2348 { 2349 hc: &v1.HealthCheck{ 2350 Enable: false, 2351 Path: "/healthz//;", 2352 }, 2353 }, 2354 } 2355 2356 for _, test := range tests { 2357 allErrs := validateUpstreamHealthCheck(test.hc, field.NewPath("healthCheck")) 2358 2359 if len(allErrs) == 0 { 2360 t.Errorf("validateUpstreamHealthCheck() returned no errors for invalid input %v", test.hc) 2361 } 2362 } 2363 } 2364 2365 func TestValidateStatusMatch(t *testing.T) { 2366 tests := []struct { 2367 status string 2368 }{ 2369 { 2370 status: "200", 2371 }, 2372 { 2373 status: "! 500", 2374 }, 2375 { 2376 status: "200 204", 2377 }, 2378 { 2379 status: "! 301 302", 2380 }, 2381 { 2382 status: "200-399", 2383 }, 2384 { 2385 status: "! 400-599", 2386 }, 2387 { 2388 status: "301-303 307", 2389 }, 2390 } 2391 for _, test := range tests { 2392 allErrs := validateStatusMatch(test.status, field.NewPath("statusMatch")) 2393 2394 if len(allErrs) != 0 { 2395 t.Errorf("validateStatusMatch() returned errors %v for valid input %v", allErrs, test.status) 2396 } 2397 } 2398 } 2399 2400 func TestValidateStatusMatchFails(t *testing.T) { 2401 tests := []struct { 2402 status string 2403 msg string 2404 }{ 2405 { 2406 status: "qwe", 2407 msg: "Invalid: no digits", 2408 }, 2409 { 2410 status: "!", 2411 msg: "Invalid: `!` character only", 2412 }, 2413 { 2414 status: "!500", 2415 msg: "Invalid: no space after !", 2416 }, 2417 { 2418 status: "0", 2419 msg: "Invalid: status out of range (below 100)", 2420 }, 2421 { 2422 status: "1000", 2423 msg: "Invalid: status out of range (above 999)", 2424 }, 2425 { 2426 status: "20-600", 2427 msg: "Invalid: code in range is out of range", 2428 }, 2429 { 2430 status: "! 200 ! 500", 2431 msg: "Invalid: 2 exclamation symbols", 2432 }, 2433 { 2434 status: "200 - 500", 2435 msg: "Invalid: range with space around `-`", 2436 }, 2437 { 2438 status: "500-200", 2439 msg: "Invalid: range must be min < max", 2440 }, 2441 { 2442 status: "200-200-400", 2443 msg: "Invalid: range with more than 2 numbers", 2444 }, 2445 } 2446 for _, test := range tests { 2447 allErrs := validateStatusMatch(test.status, field.NewPath("statusMatch")) 2448 2449 if len(allErrs) == 0 { 2450 t.Errorf("validateStatusMatch() returned no errors for case %v", test.msg) 2451 } 2452 } 2453 } 2454 2455 func TestValidateHeader(t *testing.T) { 2456 tests := []struct { 2457 header v1.Header 2458 }{ 2459 { 2460 header: v1.Header{ 2461 Name: "Host", 2462 Value: "my.service", 2463 }, 2464 }, 2465 { 2466 header: v1.Header{ 2467 Name: "Host", 2468 Value: `\"my.service\"`, 2469 }, 2470 }, 2471 } 2472 2473 for _, test := range tests { 2474 allErrs := validateHeader(test.header, field.NewPath("headers")) 2475 2476 if len(allErrs) != 0 { 2477 t.Errorf("validateHeader() returned errors %v for valid input %v", allErrs, test.header) 2478 } 2479 } 2480 } 2481 2482 func TestValidateHeaderFails(t *testing.T) { 2483 tests := []struct { 2484 header v1.Header 2485 msg string 2486 }{ 2487 { 2488 header: v1.Header{ 2489 Name: "12378 qwe ", 2490 Value: "my.service", 2491 }, 2492 msg: "Invalid name with spaces", 2493 }, 2494 { 2495 header: v1.Header{ 2496 Name: "Host", 2497 Value: `"my.service`, 2498 }, 2499 msg: `Invalid value with unescaped '"'`, 2500 }, 2501 { 2502 header: v1.Header{ 2503 Name: "Host", 2504 Value: `my.service\`, 2505 }, 2506 msg: "Invalid value with ending '\\'", 2507 }, 2508 { 2509 header: v1.Header{ 2510 Name: "Host", 2511 Value: "$my.service", 2512 }, 2513 msg: "Invalid value with '$' character", 2514 }, 2515 { 2516 header: v1.Header{ 2517 Name: "Host", 2518 Value: "my.\\$service", 2519 }, 2520 msg: "Invalid value with escaped '$' character", 2521 }, 2522 } 2523 for _, test := range tests { 2524 allErrs := validateHeader(test.header, field.NewPath("headers")) 2525 2526 if len(allErrs) == 0 { 2527 t.Errorf("validateHeader() returned no errors for case: %v", test.msg) 2528 } 2529 } 2530 } 2531 2532 func TestValidateIntFromString(t *testing.T) { 2533 input := "404" 2534 _, errMsg := validateIntFromString(input) 2535 2536 if errMsg != "" { 2537 t.Errorf("validateIntFromString() returned errors %v for valid input %v", errMsg, input) 2538 } 2539 } 2540 2541 func TestValidateIntFromStringFails(t *testing.T) { 2542 input := "not a number" 2543 _, errMsg := validateIntFromString(input) 2544 2545 if errMsg == "" { 2546 t.Errorf("validateIntFromString() returned no errors for invalid input %v", input) 2547 } 2548 } 2549 2550 func TestRejectPlusResourcesInOSS(t *testing.T) { 2551 tests := []struct { 2552 upstream *v1.Upstream 2553 }{ 2554 { 2555 upstream: &v1.Upstream{ 2556 SlowStart: "10s", 2557 }, 2558 }, 2559 { 2560 upstream: &v1.Upstream{ 2561 HealthCheck: &v1.HealthCheck{}, 2562 }, 2563 }, 2564 { 2565 upstream: &v1.Upstream{ 2566 SessionCookie: &v1.SessionCookie{}, 2567 }, 2568 }, 2569 { 2570 upstream: &v1.Upstream{ 2571 Queue: &v1.UpstreamQueue{}, 2572 }, 2573 }, 2574 } 2575 2576 for _, test := range tests { 2577 allErrsOSS := rejectPlusResourcesInOSS(*test.upstream, field.NewPath("upstreams"), false) 2578 2579 if len(allErrsOSS) == 0 { 2580 t.Errorf("rejectPlusResourcesInOSS() returned no errors for upstream: %v", test.upstream) 2581 } 2582 2583 allErrsPlus := rejectPlusResourcesInOSS(*test.upstream, field.NewPath("upstreams"), true) 2584 2585 if len(allErrsPlus) != 0 { 2586 t.Errorf("rejectPlusResourcesInOSS() returned no errors for upstream: %v", test.upstream) 2587 } 2588 } 2589 } 2590 2591 func TestValidateQueue(t *testing.T) { 2592 tests := []struct { 2593 upstreamQueue *v1.UpstreamQueue 2594 msg string 2595 }{ 2596 { 2597 upstreamQueue: &v1.UpstreamQueue{Size: 10, Timeout: "10s"}, 2598 msg: "valid upstream queue with size and timeout", 2599 }, 2600 { 2601 upstreamQueue: nil, 2602 msg: "upstream queue nil", 2603 }, 2604 { 2605 upstreamQueue: nil, 2606 msg: "upstream queue nil in OSS", 2607 }, 2608 } 2609 2610 for _, test := range tests { 2611 allErrs := validateQueue(test.upstreamQueue, field.NewPath("queue")) 2612 if len(allErrs) != 0 { 2613 t.Errorf("validateQueue() returned errors %v for valid input for the case of %s", allErrs, test.msg) 2614 } 2615 } 2616 } 2617 2618 func TestValidateQueueFails(t *testing.T) { 2619 tests := []struct { 2620 upstreamQueue *v1.UpstreamQueue 2621 msg string 2622 }{ 2623 { 2624 upstreamQueue: &v1.UpstreamQueue{Size: -1, Timeout: "10s"}, 2625 msg: "upstream queue with invalid size", 2626 }, 2627 { 2628 upstreamQueue: &v1.UpstreamQueue{Size: 10, Timeout: "-10"}, 2629 msg: "upstream queue with invalid timeout", 2630 }, 2631 } 2632 2633 for _, test := range tests { 2634 allErrs := validateQueue(test.upstreamQueue, field.NewPath("queue")) 2635 if len(allErrs) == 0 { 2636 t.Errorf("validateQueue() returned no errors for invalid input for the case of %s", test.msg) 2637 } 2638 } 2639 } 2640 2641 func TestValidateSessionCookie(t *testing.T) { 2642 tests := []struct { 2643 sc *v1.SessionCookie 2644 msg string 2645 }{ 2646 { 2647 sc: &v1.SessionCookie{Enable: true, Name: "min"}, 2648 msg: "min valid config", 2649 }, 2650 { 2651 sc: &v1.SessionCookie{Enable: true, Name: "test", Expires: "max"}, 2652 msg: "valid config with expires max", 2653 }, 2654 { 2655 sc: &v1.SessionCookie{ 2656 Enable: true, Name: "test", Path: "/tea", Expires: "1", Domain: ".example.com", HTTPOnly: false, Secure: true, 2657 }, 2658 2659 msg: "max valid config", 2660 }, 2661 } 2662 for _, test := range tests { 2663 allErrs := validateSessionCookie(test.sc, field.NewPath("sessionCookie")) 2664 if len(allErrs) != 0 { 2665 t.Errorf("validateSessionCookie() returned errors %v for valid input for the case of: %s", allErrs, test.msg) 2666 } 2667 } 2668 } 2669 2670 func TestValidateSessionCookieFails(t *testing.T) { 2671 tests := []struct { 2672 sc *v1.SessionCookie 2673 msg string 2674 }{ 2675 { 2676 sc: &v1.SessionCookie{Enable: true}, 2677 msg: "missing required field: Name", 2678 }, 2679 { 2680 sc: &v1.SessionCookie{Enable: false}, 2681 msg: "session cookie not enabled", 2682 }, 2683 { 2684 sc: &v1.SessionCookie{Enable: true, Name: "$ecret-Name"}, 2685 msg: "invalid name format", 2686 }, 2687 { 2688 sc: &v1.SessionCookie{Enable: true, Name: "test", Expires: "EGGS"}, 2689 msg: "invalid time format", 2690 }, 2691 { 2692 sc: &v1.SessionCookie{Enable: true, Name: "test", Path: "/ coffee"}, 2693 msg: "invalid path format", 2694 }, 2695 } 2696 for _, test := range tests { 2697 allErrs := validateSessionCookie(test.sc, field.NewPath("sessionCookie")) 2698 if len(allErrs) == 0 { 2699 t.Errorf("validateSessionCookie() returned no errors for invalid input for the case of: %v", test.msg) 2700 } 2701 } 2702 } 2703 2704 func TestValidateRedirectStatusCode(t *testing.T) { 2705 tests := []struct { 2706 code int 2707 }{ 2708 {code: 301}, 2709 {code: 302}, 2710 {code: 307}, 2711 {code: 308}, 2712 } 2713 for _, test := range tests { 2714 allErrs := validateRedirectStatusCode(test.code, field.NewPath("code")) 2715 if len(allErrs) != 0 { 2716 t.Errorf("validateRedirectStatusCode(%v) returned errors %v for valid input", test.code, allErrs) 2717 } 2718 } 2719 } 2720 2721 func TestValidateRedirectStatusCodeFails(t *testing.T) { 2722 tests := []struct { 2723 code int 2724 }{ 2725 {code: 309}, 2726 {code: 299}, 2727 {code: 305}, 2728 } 2729 for _, test := range tests { 2730 allErrs := validateRedirectStatusCode(test.code, field.NewPath("code")) 2731 if len(allErrs) == 0 { 2732 t.Errorf("validateRedirectStatusCode(%v) returned no errors for invalid input", test.code) 2733 } 2734 } 2735 } 2736 2737 func TestIsRegexOrExactMatch(t *testing.T) { 2738 tests := []struct { 2739 path string 2740 expected bool 2741 }{ 2742 { 2743 path: "/path", 2744 expected: false, 2745 }, 2746 { 2747 path: "~ .*\\.jpg", 2748 expected: true, 2749 }, 2750 { 2751 path: "=/exact/match", 2752 expected: true, 2753 }, 2754 } 2755 2756 for _, test := range tests { 2757 result := isRegexOrExactMatch(test.path) 2758 if result != test.expected { 2759 t.Errorf("isRegexOrExactMatch(%v) returned %v but expected %v", test.path, result, test.expected) 2760 } 2761 } 2762 } 2763 2764 func TestValidateEscapedStringWithVariables(t *testing.T) { 2765 specialVariables := []string{"http_"} 2766 variables := map[string]bool{ 2767 "request_uri": true, 2768 "host": true, 2769 } 2770 2771 tests := []struct { 2772 str string 2773 msg string 2774 }{ 2775 { 2776 str: "Hello World", 2777 msg: "single string", 2778 }, 2779 { 2780 str: "${host}${request_uri}", 2781 msg: "string with variables", 2782 }, 2783 { 2784 str: "{abc} %&*()!@#", 2785 msg: "string with symbols", 2786 }, 2787 { 2788 str: "${http_authorization}", 2789 msg: "special variable with name", 2790 }, 2791 { 2792 str: ` 2793 <html> 2794 <body> 2795 <h1>Hello</h1> 2796 </body> 2797 </html>`, 2798 msg: "string with multiple lines", 2799 }, 2800 } 2801 2802 isPlus := false 2803 2804 for _, test := range tests { 2805 allErrs := validateEscapedStringWithVariables(test.str, field.NewPath("body"), specialVariables, variables, isPlus) 2806 if len(allErrs) != 0 { 2807 t.Errorf("validateEscapedStringWithVariables(%v) returned errors %v for valid input for the case of: %v", test.str, allErrs, test.msg) 2808 } 2809 } 2810 } 2811 2812 func TestValidateEscapedStringWithVariablesFails(t *testing.T) { 2813 specialVariables := []string{"http_"} 2814 variables := map[string]bool{ 2815 "request_uri": true, 2816 "host": true, 2817 } 2818 2819 tests := []struct { 2820 str string 2821 msg string 2822 }{ 2823 { 2824 str: "Request to $host", 2825 msg: "invalid variable format", 2826 }, 2827 { 2828 str: "Request to ${host_uri}", 2829 msg: "invalid variable", 2830 }, 2831 { 2832 str: "Request to ${https_authorization}", 2833 msg: "invalid special variable", 2834 }, 2835 { 2836 str: `Request to host failed "`, 2837 msg: "unescaped double quotes", 2838 }, 2839 { 2840 str: "Please access to ${something}.com", 2841 msg: "invalid variable", 2842 }, 2843 } 2844 2845 isPlus := false 2846 2847 for _, test := range tests { 2848 allErrs := validateEscapedStringWithVariables(test.str, field.NewPath("string"), specialVariables, variables, isPlus) 2849 if len(allErrs) == 0 { 2850 t.Errorf("validateEscapedStringWithVariables(%v) returned no errors for invalid input for the case of: %v", test.str, test.msg) 2851 } 2852 } 2853 } 2854 2855 func TestValidateActionReturnType(t *testing.T) { 2856 tests := []struct { 2857 defaultType string 2858 msg string 2859 }{ 2860 { 2861 defaultType: "application/json", 2862 msg: "normal MIME type", 2863 }, 2864 { 2865 defaultType: `\"application/json\"`, 2866 msg: "double quotes escaped", 2867 }, 2868 } 2869 2870 for _, test := range tests { 2871 allErrs := validateActionReturnType(test.defaultType, field.NewPath("type")) 2872 if len(allErrs) != 0 { 2873 t.Errorf("validateActionReturnType(%v) returned errors %v for the case of: %v", test.defaultType, allErrs, test.msg) 2874 } 2875 } 2876 } 2877 2878 func TestValidateActionReturnTypeFails(t *testing.T) { 2879 tests := []struct { 2880 defaultType string 2881 msg string 2882 }{ 2883 { 2884 defaultType: "application/{json}", 2885 msg: "use of forbidden symbols", 2886 }, 2887 { 2888 defaultType: "application;json", 2889 msg: "use of forbidden symbols", 2890 }, 2891 { 2892 defaultType: `"application/json"`, 2893 msg: "double quotes not escaped", 2894 }, 2895 } 2896 2897 for _, test := range tests { 2898 allErrs := validateActionReturnType(test.defaultType, field.NewPath("type")) 2899 if len(allErrs) == 0 { 2900 t.Errorf("validateActionReturnType(%v) returned no errors for the case of: %v", test.defaultType, test.msg) 2901 } 2902 } 2903 } 2904 2905 func TestValidateActionReturn(t *testing.T) { 2906 tests := []*v1.ActionReturn{ 2907 { 2908 Body: "Hello World", 2909 }, 2910 { 2911 Body: "The URI is ${request_uri}", 2912 }, 2913 { 2914 Body: "The header abc is ${http_abc}", 2915 }, 2916 { 2917 Type: "application/json", 2918 Body: "Hello World", 2919 }, 2920 { 2921 Code: 200, 2922 Type: "application/json", 2923 Body: "Hello World", 2924 }, 2925 } 2926 2927 vsv := &VirtualServerValidator{isPlus: false} 2928 2929 for _, test := range tests { 2930 allErrs := vsv.validateActionReturn(test, field.NewPath("return"), returnBodySpecialVariables, returnBodyVariables) 2931 if len(allErrs) != 0 { 2932 t.Errorf("validateActionReturn(%v) returned errors for valid input", test) 2933 } 2934 } 2935 } 2936 2937 func TestValidateActionReturnFails(t *testing.T) { 2938 tests := []*v1.ActionReturn{ 2939 {}, 2940 { 2941 Body: "Hello ${somevar}", 2942 }, 2943 { 2944 Body: "Hello ${http_%}", 2945 }, 2946 { 2947 Code: 301, 2948 Body: "Hello World", 2949 }, 2950 { 2951 Code: 200, 2952 Type: `application/"json"`, 2953 Body: "Hello World", 2954 }, 2955 } 2956 2957 vsv := &VirtualServerValidator{isPlus: false} 2958 2959 for _, test := range tests { 2960 allErrs := vsv.validateActionReturn(test, field.NewPath("return"), returnBodySpecialVariables, returnBodyVariables) 2961 if len(allErrs) == 0 { 2962 t.Errorf("validateActionReturn(%v) returned no errors for invalid input", test) 2963 } 2964 } 2965 } 2966 2967 func TestValidateActionProxy(t *testing.T) { 2968 upstreamNames := map[string]sets.Empty{ 2969 "upstream1": {}, 2970 } 2971 path := "/path" 2972 actionProxy := &v1.ActionProxy{ 2973 Upstream: "upstream1", 2974 RewritePath: "/test", 2975 } 2976 2977 vsv := &VirtualServerValidator{isPlus: false} 2978 2979 allErrs := vsv.validateActionProxy(actionProxy, field.NewPath("proxy"), upstreamNames, path, false) 2980 2981 if len(allErrs) != 0 { 2982 t.Errorf("validateActionProxy(%+v, %v, %v) returned errors for valid input: %v", actionProxy, upstreamNames, path, allErrs) 2983 } 2984 } 2985 2986 func TestValidateActionProxyFails(t *testing.T) { 2987 upstreamNames := map[string]sets.Empty{ 2988 "upstream1": {}, 2989 } 2990 path := "/path" 2991 actionProxy := &v1.ActionProxy{ 2992 Upstream: "", 2993 } 2994 2995 vsv := &VirtualServerValidator{isPlus: false} 2996 2997 allErrs := vsv.validateActionProxy(actionProxy, field.NewPath("proxy"), upstreamNames, path, false) 2998 2999 if len(allErrs) == 0 { 3000 t.Errorf("validateActionProxy(%+v, %v, %v) returned no errors for invalid input", actionProxy, upstreamNames, path) 3001 } 3002 } 3003 3004 func TestValidateActionProxyRewritePath(t *testing.T) { 3005 tests := []string{"/rewrite", "/rewrite", `/$2`} 3006 for _, test := range tests { 3007 allErrs := validateActionProxyRewritePath(test, field.NewPath("rewritePath")) 3008 if len(allErrs) != 0 { 3009 t.Errorf("validateActionProxyRewritePath(%v) returned errors for valid input: %v", test, allErrs) 3010 } 3011 } 3012 } 3013 3014 func TestValidateActionProxyRewritePathFails(t *testing.T) { 3015 tests := []string{`/\d{3}`, `(`, "$request_uri"} 3016 for _, test := range tests { 3017 allErrs := validateActionProxyRewritePath(test, field.NewPath("rewritePath")) 3018 if len(allErrs) == 0 { 3019 t.Errorf("validateActionProxyRewritePath(%v) returned no errors for invalid input", test) 3020 } 3021 } 3022 } 3023 3024 func TestValidateActionProxyRewritePathForRegexp(t *testing.T) { 3025 tests := []string{"/rewrite$1", "test", `/$2`, `\"test\"`} 3026 for _, test := range tests { 3027 allErrs := validateActionProxyRewritePathForRegexp(test, field.NewPath("rewritePath")) 3028 if len(allErrs) != 0 { 3029 t.Errorf("validateActionProxyRewritePathForRegexp(%v) returned errors for valid input: %v", test, allErrs) 3030 } 3031 } 3032 } 3033 3034 func TestValidateActionProxyRewritePathForRegexpFails(t *testing.T) { 3035 tests := []string{"$request_uri", `"test"`, `test\`} 3036 for _, test := range tests { 3037 allErrs := validateActionProxyRewritePathForRegexp(test, field.NewPath("rewritePath")) 3038 if len(allErrs) == 0 { 3039 t.Errorf("validateActionProxyRewritePathForRegexp(%v) returned no errors for invalid input", test) 3040 } 3041 } 3042 } 3043 3044 func TestValidateActionProxyHeader(t *testing.T) { 3045 tests := []struct { 3046 header v1.Header 3047 }{ 3048 { 3049 header: v1.Header{ 3050 Name: "Host", 3051 Value: "my.service", 3052 }, 3053 }, 3054 { 3055 header: v1.Header{ 3056 Name: "Host", 3057 Value: `\"my.service\"`, 3058 }, 3059 }, 3060 { 3061 header: v1.Header{ 3062 Name: "Host", 3063 Value: "${request_uri}", 3064 }, 3065 }, 3066 { 3067 header: v1.Header{ 3068 Name: "Host", 3069 Value: "${http_some_header}", 3070 }, 3071 }, 3072 { 3073 header: v1.Header{ 3074 Name: "Host", 3075 Value: "${request_uri} and ${http_some_header}", 3076 }, 3077 }, 3078 } 3079 3080 vsv := &VirtualServerValidator{isPlus: false} 3081 3082 for _, test := range tests { 3083 allErrs := vsv.validateActionProxyHeader(test.header, field.NewPath("headers")) 3084 3085 if len(allErrs) != 0 { 3086 t.Errorf("validateActionProxyHeader() returned errors %v for valid input %v", allErrs, test.header) 3087 } 3088 } 3089 } 3090 3091 func TestValidateActionProxyHeaderFails(t *testing.T) { 3092 tests := []struct { 3093 header v1.Header 3094 msg string 3095 }{ 3096 { 3097 header: v1.Header{ 3098 Name: "12378 qwe ", 3099 Value: "my.service", 3100 }, 3101 msg: "Invalid name with spaces", 3102 }, 3103 { 3104 header: v1.Header{ 3105 Name: "Host", 3106 Value: `"my.service`, 3107 }, 3108 msg: `Invalid value with unescaped '"'`, 3109 }, 3110 { 3111 header: v1.Header{ 3112 Name: "Host", 3113 Value: `my.service\`, 3114 }, 3115 msg: "Invalid value with ending '\\'", 3116 }, 3117 { 3118 header: v1.Header{ 3119 Name: "Host", 3120 Value: "${realpath_root}", 3121 }, 3122 msg: "Invalid variable", 3123 }, 3124 { 3125 header: v1.Header{ 3126 Name: "Host", 3127 Value: "${sent_http_name}", 3128 }, 3129 msg: "Invalid special variable", 3130 }, 3131 { 3132 header: v1.Header{ 3133 Name: "Host", 3134 Value: "my.\\$service", 3135 }, 3136 msg: "Invalid value with escaped '$' character", 3137 }, 3138 } 3139 3140 vsv := &VirtualServerValidator{isPlus: false} 3141 3142 for _, test := range tests { 3143 allErrs := vsv.validateActionProxyHeader(test.header, field.NewPath("headers")) 3144 3145 if len(allErrs) == 0 { 3146 t.Errorf("validateActionProxyHeader() returned no errors for case: %v", test.msg) 3147 } 3148 } 3149 } 3150 3151 func TestValidateActionProxyRequestHeaders(t *testing.T) { 3152 requestHeaders := &v1.ProxyRequestHeaders{ 3153 Set: []v1.Header{ 3154 { 3155 Name: "Host", 3156 Value: "nginx.org", 3157 }, 3158 { 3159 Name: "scheme", 3160 Value: "${scheme}", 3161 }, 3162 { 3163 Name: "user", 3164 Value: "${http_user}", 3165 }, 3166 }, 3167 } 3168 3169 vsv := &VirtualServerValidator{isPlus: false} 3170 3171 allErrs := vsv.validateActionProxyRequestHeaders(requestHeaders, field.NewPath("requestHeaders")) 3172 if len(allErrs) != 0 { 3173 t.Errorf("validateActionProxyRequestHeaders(%v) returned errors for valid input: %v", requestHeaders, allErrs) 3174 } 3175 } 3176 3177 func TestValidateActionProxyRequestHeadersFails(t *testing.T) { 3178 invalidHeaders := []*v1.ProxyRequestHeaders{ 3179 { 3180 Set: []v1.Header{ 3181 { 3182 Name: "in va lid", 3183 Value: "", 3184 }, 3185 }, 3186 }, 3187 { 3188 Set: []v1.Header{ 3189 { 3190 Name: "Host", 3191 Value: "$var", 3192 }, 3193 }, 3194 }, 3195 { 3196 Set: []v1.Header{ 3197 { 3198 Name: "", 3199 Value: "nginx.org", 3200 }, 3201 }, 3202 }, 3203 { 3204 Set: []v1.Header{ 3205 { 3206 Name: "Host", 3207 Value: "${http_%}", 3208 }, 3209 }, 3210 }, 3211 } 3212 3213 vsv := &VirtualServerValidator{isPlus: false} 3214 3215 for _, headers := range invalidHeaders { 3216 allErrs := vsv.validateActionProxyRequestHeaders(headers, field.NewPath("requestHeaders")) 3217 if len(allErrs) == 0 { 3218 t.Errorf("validateActionProxyRequestHeaders(%v) returned no errors for invalid input", headers) 3219 } 3220 } 3221 } 3222 3223 func TestValidateActionProxyResponseHeaders(t *testing.T) { 3224 tests := []struct { 3225 responseHeaders *v1.ProxyResponseHeaders 3226 }{ 3227 { 3228 responseHeaders: &v1.ProxyResponseHeaders{ 3229 Hide: []string{"Header"}, 3230 Pass: []string{"Header"}, 3231 Ignore: []string{"Expires"}, 3232 Add: []v1.AddHeader{ 3233 { 3234 Header: v1.Header{ 3235 Name: "Host", 3236 Value: "nginx.org", 3237 }, 3238 Always: false, 3239 }, 3240 }, 3241 }, 3242 }, 3243 { 3244 responseHeaders: &v1.ProxyResponseHeaders{ 3245 Hide: []string{"Header"}, 3246 }, 3247 }, 3248 { 3249 responseHeaders: &v1.ProxyResponseHeaders{ 3250 Pass: []string{"Header"}, 3251 }, 3252 }, 3253 { 3254 responseHeaders: &v1.ProxyResponseHeaders{ 3255 Ignore: []string{"Expires"}, 3256 }, 3257 }, 3258 { 3259 responseHeaders: &v1.ProxyResponseHeaders{ 3260 Add: []v1.AddHeader{ 3261 { 3262 Header: v1.Header{ 3263 Name: "Host", 3264 Value: "nginx.org", 3265 }, 3266 Always: false, 3267 }, 3268 { 3269 Header: v1.Header{ 3270 Name: "uri", 3271 Value: "${request_uri}", 3272 }, 3273 }, 3274 { 3275 Header: v1.Header{ 3276 Name: "abc", 3277 Value: "${http_abc}", 3278 }, 3279 }, 3280 }, 3281 }, 3282 }, 3283 } 3284 3285 vsv := &VirtualServerValidator{isPlus: false} 3286 3287 for _, test := range tests { 3288 allErrs := vsv.validateActionProxyResponseHeaders(test.responseHeaders, field.NewPath("responseHeaders")) 3289 if len(allErrs) != 0 { 3290 t.Errorf("validateActionProxyResponseHeaders(%v) returned errors for valid input: %v", test.responseHeaders, allErrs) 3291 } 3292 } 3293 } 3294 3295 func TestValidateActionProxyResponseHeadersFails(t *testing.T) { 3296 tests := []struct { 3297 responseHeaders *v1.ProxyResponseHeaders 3298 msg string 3299 }{ 3300 { 3301 responseHeaders: &v1.ProxyResponseHeaders{ 3302 Hide: []string{""}, 3303 Pass: []string{""}, 3304 Ignore: []string{""}, 3305 Add: []v1.AddHeader{ 3306 { 3307 Header: v1.Header{ 3308 Name: "", 3309 Value: "nginx.org", 3310 }, 3311 }, 3312 }, 3313 }, 3314 msg: "all fields invalid", 3315 }, 3316 { 3317 responseHeaders: &v1.ProxyResponseHeaders{ 3318 Hide: []string{"invalid header"}, 3319 }, 3320 msg: "invalid hide headers", 3321 }, 3322 { 3323 responseHeaders: &v1.ProxyResponseHeaders{ 3324 Pass: []string{"$invalid"}, 3325 }, 3326 msg: "invalid pass headers", 3327 }, 3328 { 3329 responseHeaders: &v1.ProxyResponseHeaders{ 3330 Ignore: []string{"1234 invalid"}, 3331 }, 3332 msg: "invalid ignore headers", 3333 }, 3334 { 3335 responseHeaders: &v1.ProxyResponseHeaders{ 3336 Add: []v1.AddHeader{ 3337 { 3338 Header: v1.Header{ 3339 Name: "$invalid 123", 3340 Value: "nginx.org", 3341 }, 3342 Always: false, 3343 }, 3344 }, 3345 }, 3346 msg: "invalid Add header name", 3347 }, 3348 { 3349 responseHeaders: &v1.ProxyResponseHeaders{ 3350 Add: []v1.AddHeader{ 3351 { 3352 Header: v1.Header{ 3353 Name: "Host", 3354 Value: "${invalid}", 3355 }, 3356 Always: false, 3357 }, 3358 }, 3359 }, 3360 msg: "invalid Add header value", 3361 }, 3362 } 3363 3364 vsv := &VirtualServerValidator{isPlus: false} 3365 3366 for _, test := range tests { 3367 allErrs := vsv.validateActionProxyResponseHeaders(test.responseHeaders, field.NewPath("responseHeaders")) 3368 if len(allErrs) == 0 { 3369 t.Errorf("validateActionProxyResponseHeaders(%v) returned no errors for invalid input for the case of %v", test.responseHeaders, test.msg) 3370 } 3371 } 3372 } 3373 3374 func TestValidateIgnoreHeaders(t *testing.T) { 3375 var ignoreHeaders []string 3376 3377 for header := range validIgnoreHeaders { 3378 ignoreHeaders = append(ignoreHeaders, header) 3379 } 3380 3381 allErrs := validateIgnoreHeaders(ignoreHeaders, field.NewPath("ignoreHeaders")) 3382 if len(allErrs) != 0 { 3383 t.Errorf("validateIgnoreHeaders(%v) returned errors for valid input: %v", ignoreHeaders, allErrs) 3384 } 3385 } 3386 3387 func TestValidateIgnoreHeadersFails(t *testing.T) { 3388 ignoreHeaders := []string{ 3389 "Host", 3390 "Connection", 3391 } 3392 3393 allErrs := validateIgnoreHeaders(ignoreHeaders, field.NewPath("ignoreHeaders")) 3394 if len(allErrs) == 0 { 3395 t.Errorf("validateIgnoreHeaders(%v) returned no errors for invalid input", ignoreHeaders) 3396 } 3397 } 3398 3399 func TestValidateStringNoVariables(t *testing.T) { 3400 tests := []string{ 3401 "string", 3402 "endWith$", 3403 "withNumber$1", 3404 "abcййй", 3405 "abcййй$1", 3406 "", 3407 } 3408 3409 for _, test := range tests { 3410 allErrs := validateStringNoVariables(test, field.NewPath("rewritePath")) 3411 if len(allErrs) != 0 { 3412 t.Errorf("validateStringNoVariables(%v) returned errors for valid input: %v", test, allErrs) 3413 } 3414 } 3415 } 3416 3417 func TestValidateStringNoVariablesFails(t *testing.T) { 3418 tests := []string{ 3419 "$var", 3420 "abcйй$й", 3421 "$$", 3422 } 3423 3424 for _, test := range tests { 3425 allErrs := validateStringNoVariables(test, field.NewPath("rewritePath")) 3426 if len(allErrs) == 0 { 3427 t.Errorf("validateStringNoVariables(%v) returned no errors for invalid input", test) 3428 } 3429 } 3430 } 3431 3432 func TestValidateActionReturnCode(t *testing.T) { 3433 codes := []int{200, 201, 400, 404, 500, 502, 599} 3434 for _, c := range codes { 3435 allErrs := validateActionReturnCode(c, field.NewPath("code")) 3436 if len(allErrs) != 0 { 3437 t.Errorf("validateActionReturnCode(%v) returned errors for valid input: %v", c, allErrs) 3438 } 3439 } 3440 } 3441 3442 func TestValidateActionReturnCodeFails(t *testing.T) { 3443 codes := []int{0, -1, 199, 300, 399, 600, 999} 3444 for _, c := range codes { 3445 allErrs := validateActionReturnCode(c, field.NewPath("code")) 3446 if len(allErrs) == 0 { 3447 t.Errorf("validateActionReturnCode(%v) returned no errors for invalid input", c) 3448 } 3449 } 3450 } 3451 3452 func TestErrorPageHasRequiredFields(t *testing.T) { 3453 tests := []struct { 3454 errorPage v1.ErrorPage 3455 expected bool 3456 }{ 3457 { 3458 errorPage: v1.ErrorPage{ 3459 Codes: nil, 3460 Return: nil, 3461 Redirect: nil, 3462 }, 3463 expected: false, 3464 }, 3465 { 3466 errorPage: v1.ErrorPage{ 3467 Codes: nil, 3468 Return: &v1.ErrorPageReturn{}, 3469 Redirect: &v1.ErrorPageRedirect{}, 3470 }, 3471 expected: false, 3472 }, 3473 { 3474 errorPage: v1.ErrorPage{ 3475 Codes: nil, 3476 Return: &v1.ErrorPageReturn{}, 3477 Redirect: nil, 3478 }, 3479 expected: true, 3480 }, 3481 { 3482 errorPage: v1.ErrorPage{ 3483 Codes: nil, 3484 Return: nil, 3485 Redirect: &v1.ErrorPageRedirect{}, 3486 }, 3487 expected: true, 3488 }, 3489 } 3490 3491 for _, test := range tests { 3492 result := errorPageHasRequiredFields(test.errorPage) 3493 if result != test.expected { 3494 t.Errorf("errorPageHasRequiredFields(%v) returned %v but expected %v", test.errorPage, result, test.expected) 3495 } 3496 } 3497 } 3498 3499 func TestValidateErrorPage(t *testing.T) { 3500 tests := []v1.ErrorPage{ 3501 { 3502 Codes: []int{400, 404}, 3503 Return: &v1.ErrorPageReturn{ 3504 ActionReturn: v1.ActionReturn{ 3505 Body: "Hello World", 3506 }, 3507 }, 3508 Redirect: nil, 3509 }, 3510 { 3511 Codes: []int{400, 404}, 3512 Return: nil, 3513 Redirect: &v1.ErrorPageRedirect{ 3514 ActionRedirect: v1.ActionRedirect{ 3515 URL: "http://nginx.com", 3516 }, 3517 }, 3518 }, 3519 } 3520 3521 vsv := &VirtualServerValidator{isPlus: false} 3522 3523 for _, ep := range tests { 3524 allErrs := vsv.validateErrorPage(ep, field.NewPath("errorPage")) 3525 if len(allErrs) != 0 { 3526 t.Errorf("validateErrorPage(%v) returned errors for valid input: %v", ep, allErrs) 3527 } 3528 } 3529 } 3530 3531 func TestValidateErrorPageFails(t *testing.T) { 3532 tests := []v1.ErrorPage{ 3533 {}, 3534 { 3535 Codes: []int{400, 404}, 3536 Return: &v1.ErrorPageReturn{}, 3537 Redirect: &v1.ErrorPageRedirect{}, 3538 }, 3539 { 3540 Codes: []int{100, 700}, 3541 Return: &v1.ErrorPageReturn{}, 3542 Redirect: nil, 3543 }, 3544 { 3545 Codes: nil, 3546 Return: &v1.ErrorPageReturn{}, 3547 Redirect: nil, 3548 }, 3549 } 3550 3551 vsv := &VirtualServerValidator{isPlus: false} 3552 3553 for _, ep := range tests { 3554 allErrs := vsv.validateErrorPage(ep, field.NewPath("errorPage")) 3555 if len(allErrs) == 0 { 3556 t.Errorf("validateErrorPage(%v) returned no errors for invalid input", ep) 3557 } 3558 } 3559 } 3560 3561 func TestValidateErrorPageReturn(t *testing.T) { 3562 tests := []v1.ErrorPageReturn{ 3563 { 3564 ActionReturn: v1.ActionReturn{ 3565 Code: 200, 3566 Type: "", 3567 Body: "Could not process request, try again", 3568 }, 3569 Headers: nil, 3570 }, 3571 { 3572 ActionReturn: v1.ActionReturn{ 3573 Code: 0, 3574 Type: "", 3575 Body: "Could not process request, try again. Upstream status ${upstream_status}", 3576 }, 3577 Headers: []v1.Header{ 3578 { 3579 Name: "Set-Cookie", 3580 Value: "mycookie=true", 3581 }, 3582 }, 3583 }, 3584 { 3585 ActionReturn: v1.ActionReturn{ 3586 Code: 200, 3587 Type: "application/json", 3588 Body: `{\"message\": \"Could not process request, try again\", \"upstream_status\": \"${upstream_status}\"}`, 3589 }, 3590 Headers: nil, 3591 }, 3592 } 3593 3594 vsv := &VirtualServerValidator{isPlus: false} 3595 3596 for _, epr := range tests { 3597 // FIXME #nosec G601 3598 allErrs := vsv.validateErrorPageReturn(&epr, field.NewPath("return")) 3599 if len(allErrs) != 0 { 3600 t.Errorf("validateErrorPageReturn(%v) returned errors for valid input: %v", epr, allErrs) 3601 } 3602 } 3603 } 3604 3605 func TestValidateErrorPageReturnFails(t *testing.T) { 3606 tests := []struct { 3607 msg string 3608 epr v1.ErrorPageReturn 3609 }{ 3610 { 3611 msg: "empty body", 3612 epr: v1.ErrorPageReturn{ 3613 ActionReturn: v1.ActionReturn{ 3614 Code: 200, 3615 Type: "application/json", 3616 Body: "", 3617 }, 3618 }, 3619 }, 3620 { 3621 msg: "unescaped double quotes", 3622 epr: v1.ErrorPageReturn{ 3623 ActionReturn: v1.ActionReturn{ 3624 Code: 200, 3625 Type: "", 3626 Body: ` "Oops, Could not process request"`, 3627 }, 3628 }, 3629 }, 3630 { 3631 msg: "invalid variable", 3632 epr: v1.ErrorPageReturn{ 3633 ActionReturn: v1.ActionReturn{ 3634 Code: 0, 3635 Type: "", 3636 Body: "Could not process request, response with invalid var: ${invalid}", 3637 }, 3638 }, 3639 }, 3640 { 3641 msg: "invalid cookie name", 3642 epr: v1.ErrorPageReturn{ 3643 ActionReturn: v1.ActionReturn{ 3644 Code: 200, 3645 Type: "application/json", 3646 Body: `{\"message\": \"Could not process request, try again\", \"status\": \"${status}\"}`, 3647 }, 3648 Headers: []v1.Header{ 3649 { 3650 Name: "Set-Cookie$_%^$ -", 3651 Value: "mycookie=true", 3652 }, 3653 }, 3654 }, 3655 }, 3656 } 3657 3658 vsv := &VirtualServerValidator{isPlus: false} 3659 3660 for _, test := range tests { 3661 allErrs := vsv.validateErrorPageReturn(&test.epr, field.NewPath("return")) 3662 if len(allErrs) == 0 { 3663 t.Errorf("validateErrorPageReturn(%v) returned no errors for invalid input for the case of %v", test.epr, test.msg) 3664 } 3665 } 3666 } 3667 3668 func TestValidateErrorPageRedirect(t *testing.T) { 3669 tests := []v1.ErrorPageRedirect{ 3670 { 3671 ActionRedirect: v1.ActionRedirect{ 3672 URL: "http://nginx.com", 3673 Code: 301, 3674 }, 3675 }, 3676 { 3677 ActionRedirect: v1.ActionRedirect{ 3678 URL: "${scheme}://nginx.com", 3679 Code: 302, 3680 }, 3681 }, 3682 } 3683 3684 vsv := &VirtualServerValidator{isPlus: false} 3685 3686 for _, epr := range tests { 3687 // FIXME #nosec G601 3688 allErrs := vsv.validateErrorPageRedirect(&epr, field.NewPath("redirect")) 3689 if len(allErrs) != 0 { 3690 t.Errorf("validateErrorPageRedirect(%v) returned errors for valid input: %v", epr, allErrs) 3691 } 3692 } 3693 } 3694 3695 func TestValidateErrorPageRedirectFails(t *testing.T) { 3696 tests := []v1.ErrorPageRedirect{ 3697 { 3698 ActionRedirect: v1.ActionRedirect{ 3699 URL: "", 3700 Code: 301, 3701 }, 3702 }, 3703 { 3704 ActionRedirect: v1.ActionRedirect{ 3705 URL: `"http://nginx.com"`, 3706 Code: 301, 3707 }, 3708 }, 3709 { 3710 ActionRedirect: v1.ActionRedirect{ 3711 URL: "http://nginx.com", 3712 Code: 100, 3713 }, 3714 }, 3715 { 3716 ActionRedirect: v1.ActionRedirect{ 3717 URL: "$scheme://nginx.com", 3718 Code: 302, 3719 }, 3720 }, 3721 { 3722 ActionRedirect: v1.ActionRedirect{ 3723 URL: "https://${host}.com", 3724 Code: 302, 3725 }, 3726 }, 3727 } 3728 3729 vsv := &VirtualServerValidator{isPlus: false} 3730 3731 for _, epr := range tests { 3732 // FIXME #nosec G601 3733 allErrs := vsv.validateErrorPageRedirect(&epr, field.NewPath("redirect")) 3734 if len(allErrs) == 0 { 3735 t.Errorf("validateErrorPageRedirect(%v) returned no errors for invalid input", epr) 3736 } 3737 } 3738 } 3739 3740 func TestValidateErrorPageHeader(t *testing.T) { 3741 tests := []v1.Header{ 3742 { 3743 Name: "Header-Name", 3744 Value: "", 3745 }, 3746 { 3747 Name: "Header-Name", 3748 Value: "Value", 3749 }, 3750 { 3751 Name: "Header-Name", 3752 Value: "${upstream_status}", 3753 }, 3754 } 3755 3756 vsv := &VirtualServerValidator{isPlus: false} 3757 3758 for _, test := range tests { 3759 allErrs := vsv.validateErrorPageHeader(test, field.NewPath("header")) 3760 if len(allErrs) != 0 { 3761 t.Errorf("validateErrorPageHeader(%v) returned errors for valid input", test) 3762 } 3763 } 3764 } 3765 3766 func TestValidateErrorPageHeaderFails(t *testing.T) { 3767 tests := []v1.Header{ 3768 { 3769 Name: "", 3770 Value: "", 3771 }, 3772 { 3773 Name: "Header-!!#Name", 3774 Value: "", 3775 }, 3776 { 3777 Name: "Header-Name", 3778 Value: "$novar", 3779 }, 3780 { 3781 Name: "Header-Name", 3782 Value: "${invalid_var}", 3783 }, 3784 { 3785 Name: "Header-Name", 3786 Value: `unescaped "`, 3787 }, 3788 } 3789 3790 vsv := &VirtualServerValidator{isPlus: false} 3791 3792 for _, test := range tests { 3793 allErrs := vsv.validateErrorPageHeader(test, field.NewPath("header")) 3794 if len(allErrs) == 0 { 3795 t.Errorf("validateErrorPageHeader(%v) returned no errors for invalid input", test) 3796 } 3797 } 3798 }