istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/validation/agent/validation_test.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package agent 16 17 import ( 18 "strings" 19 "testing" 20 "time" 21 22 "github.com/hashicorp/go-multierror" 23 "google.golang.org/protobuf/proto" 24 "google.golang.org/protobuf/types/known/durationpb" 25 26 meshconfig "istio.io/api/mesh/v1alpha1" 27 networking "istio.io/api/networking/v1alpha3" 28 ) 29 30 func TestValidateFQDN(t *testing.T) { 31 tests := []struct { 32 fqdn string 33 valid bool 34 name string 35 }{ 36 { 37 fqdn: strings.Repeat("x", 256), 38 valid: false, 39 name: "long FQDN", 40 }, 41 { 42 fqdn: "", 43 valid: false, 44 name: "empty FQDN", 45 }, 46 { 47 fqdn: "istio.io", 48 valid: true, 49 name: "standard FQDN", 50 }, 51 { 52 fqdn: "istio.io.", 53 valid: true, 54 name: "unambiguous FQDN", 55 }, 56 { 57 fqdn: "istio-pilot.istio-system.svc.cluster.local", 58 valid: true, 59 name: "standard kubernetes FQDN", 60 }, 61 { 62 fqdn: "istio-pilot.istio-system.svc.cluster.local.", 63 valid: true, 64 name: "unambiguous kubernetes FQDN", 65 }, 66 } 67 for _, tt := range tests { 68 tt := tt 69 t.Run(tt.name, func(t *testing.T) { 70 err := ValidateFQDN(tt.fqdn) 71 valid := err == nil 72 if valid != tt.valid { 73 t.Errorf("Expected valid=%v, got valid=%v for %v", tt.valid, valid, tt.fqdn) 74 } 75 }) 76 77 } 78 } 79 80 func TestValidatePort(t *testing.T) { 81 ports := map[int]bool{ 82 0: false, 83 65536: false, 84 -1: false, 85 100: true, 86 1000: true, 87 65535: true, 88 } 89 for port, valid := range ports { 90 if got := ValidatePort(port); (got == nil) != valid { 91 t.Errorf("Failed: got valid=%t but wanted valid=%t: %v for %d", got == nil, valid, got, port) 92 } 93 } 94 } 95 96 func TestValidateControlPlaneAuthPolicy(t *testing.T) { 97 cases := []struct { 98 name string 99 policy meshconfig.AuthenticationPolicy 100 isValid bool 101 }{ 102 { 103 name: "invalid policy", 104 policy: -1, 105 isValid: false, 106 }, 107 { 108 name: "valid policy", 109 policy: 0, 110 isValid: true, 111 }, 112 { 113 name: "valid policy", 114 policy: 1, 115 isValid: true, 116 }, 117 { 118 name: "invalid policy", 119 policy: 2, 120 isValid: false, 121 }, 122 { 123 name: "invalid policy", 124 policy: 100, 125 isValid: false, 126 }, 127 } 128 for _, c := range cases { 129 t.Run(c.name, func(t *testing.T) { 130 if got := ValidateControlPlaneAuthPolicy(c.policy); (got == nil) != c.isValid { 131 t.Errorf("got valid=%v but wanted valid=%v: %v", got == nil, c.isValid, got) 132 } 133 }) 134 } 135 } 136 137 func TestValidateProxyAddress(t *testing.T) { 138 addresses := map[string]bool{ 139 "istio-pilot:80": true, 140 "istio-pilot": false, 141 "isti..:80": false, 142 "10.0.0.100:9090": true, 143 "10.0.0.100": false, 144 "istio-pilot:port": false, 145 "istio-pilot:100000": false, 146 "[2001:db8::100]:80": true, 147 "[2001:db8::10::20]:80": false, 148 "[2001:db8::100]": false, 149 "[2001:db8::100]:port": false, 150 "2001:db8::100:80": false, 151 } 152 for addr, valid := range addresses { 153 if got := ValidateProxyAddress(addr); (got == nil) != valid { 154 t.Errorf("Failed: got valid=%t but wanted valid=%t: %v for %s", got == nil, valid, got, addr) 155 } 156 } 157 } 158 159 func TestValidateDuration(t *testing.T) { 160 type durationCheck struct { 161 duration *durationpb.Duration 162 isValid bool 163 } 164 165 checks := []durationCheck{ 166 { 167 duration: &durationpb.Duration{Seconds: 1}, 168 isValid: true, 169 }, 170 { 171 duration: &durationpb.Duration{Seconds: 1, Nanos: -1}, 172 isValid: false, 173 }, 174 { 175 duration: &durationpb.Duration{Seconds: -11, Nanos: -1}, 176 isValid: false, 177 }, 178 { 179 duration: &durationpb.Duration{Nanos: 1}, 180 isValid: false, 181 }, 182 { 183 duration: &durationpb.Duration{Seconds: 1, Nanos: 1}, 184 isValid: false, 185 }, 186 } 187 188 for _, check := range checks { 189 if got := ValidateDuration(check.duration); (got == nil) != check.isValid { 190 t.Errorf("Failed: got valid=%t but wanted valid=%t: %v for %v", got == nil, check.isValid, got, check.duration) 191 } 192 } 193 } 194 195 func TestValidateDrainDuration(t *testing.T) { 196 type ParentDrainTime struct { 197 Drain *durationpb.Duration 198 Valid bool 199 } 200 201 combinations := []ParentDrainTime{ 202 { 203 Drain: &durationpb.Duration{Seconds: 1}, 204 Valid: true, 205 }, 206 { 207 Drain: &durationpb.Duration{Seconds: 1, Nanos: 1000000}, 208 Valid: false, 209 }, 210 { 211 Drain: &durationpb.Duration{Seconds: -1}, 212 Valid: false, 213 }, 214 } 215 for _, combo := range combinations { 216 if got := ValidateDrainDuration(combo.Drain); (got == nil) != combo.Valid { 217 t.Errorf("Failed: got valid=%t but wanted valid=%t: %v for Drain:%v", 218 got == nil, combo.Valid, got, combo.Drain) 219 } 220 } 221 } 222 223 func TestValidateMeshConfigProxyConfig(t *testing.T) { 224 valid := &meshconfig.ProxyConfig{ 225 ConfigPath: "/etc/istio/proxy", 226 BinaryPath: "/usr/local/bin/envoy", 227 DiscoveryAddress: "istio-pilot.istio-system:15010", 228 ProxyAdminPort: 15000, 229 DrainDuration: durationpb.New(45 * time.Second), 230 ClusterName: &meshconfig.ProxyConfig_ServiceCluster{ServiceCluster: "istio-proxy"}, 231 StatsdUdpAddress: "istio-statsd-prom-bridge.istio-system:9125", 232 EnvoyMetricsService: &meshconfig.RemoteService{Address: "metrics-service.istio-system:15000"}, 233 EnvoyAccessLogService: &meshconfig.RemoteService{Address: "accesslog-service.istio-system:15000"}, 234 ControlPlaneAuthPolicy: meshconfig.AuthenticationPolicy_MUTUAL_TLS, 235 Tracing: nil, 236 StatusPort: 15020, 237 PrivateKeyProvider: nil, 238 } 239 240 modify := func(config *meshconfig.ProxyConfig, fieldSetter func(*meshconfig.ProxyConfig)) *meshconfig.ProxyConfig { 241 clone := proto.Clone(config).(*meshconfig.ProxyConfig) 242 fieldSetter(clone) 243 return clone 244 } 245 246 cases := []struct { 247 name string 248 in *meshconfig.ProxyConfig 249 isValid bool 250 }{ 251 { 252 name: "empty proxy config", 253 in: &meshconfig.ProxyConfig{}, 254 isValid: false, 255 }, 256 { 257 name: "valid proxy config", 258 in: valid, 259 isValid: true, 260 }, 261 { 262 name: "config path invalid", 263 in: modify(valid, func(c *meshconfig.ProxyConfig) { c.ConfigPath = "" }), 264 isValid: false, 265 }, 266 { 267 name: "binary path invalid", 268 in: modify(valid, func(c *meshconfig.ProxyConfig) { c.BinaryPath = "" }), 269 isValid: false, 270 }, 271 { 272 name: "discovery address invalid", 273 in: modify(valid, func(c *meshconfig.ProxyConfig) { c.DiscoveryAddress = "10.0.0.100" }), 274 isValid: false, 275 }, 276 { 277 name: "proxy admin port invalid", 278 in: modify(valid, func(c *meshconfig.ProxyConfig) { c.ProxyAdminPort = 0 }), 279 isValid: false, 280 }, 281 { 282 name: "proxy admin port invalid", 283 in: modify(valid, func(c *meshconfig.ProxyConfig) { c.ProxyAdminPort = 65536 }), 284 isValid: false, 285 }, 286 { 287 name: "validate status port", 288 in: modify(valid, func(c *meshconfig.ProxyConfig) { c.StatusPort = 0 }), 289 isValid: false, 290 }, 291 { 292 name: "validate vstatus port", 293 in: modify(valid, func(c *meshconfig.ProxyConfig) { c.StatusPort = 65536 }), 294 isValid: false, 295 }, 296 { 297 name: "drain duration invalid", 298 in: modify(valid, func(c *meshconfig.ProxyConfig) { c.DrainDuration = durationpb.New(-1 * time.Second) }), 299 isValid: false, 300 }, 301 { 302 name: "service cluster invalid", 303 in: modify(valid, func(c *meshconfig.ProxyConfig) { 304 c.ClusterName = &meshconfig.ProxyConfig_ServiceCluster{ServiceCluster: ""} 305 }), 306 isValid: false, 307 }, 308 { 309 name: "statsd udp address invalid", 310 in: modify(valid, func(c *meshconfig.ProxyConfig) { c.StatsdUdpAddress = "10.0.0.100" }), 311 isValid: false, 312 }, 313 { 314 name: "envoy metrics service address invalid", 315 in: modify(valid, func(c *meshconfig.ProxyConfig) { 316 c.EnvoyMetricsService = &meshconfig.RemoteService{Address: "metrics-service.istio-system"} 317 }), 318 isValid: false, 319 }, 320 { 321 name: "envoy access log service address invalid", 322 in: modify(valid, func(c *meshconfig.ProxyConfig) { 323 c.EnvoyAccessLogService = &meshconfig.RemoteService{Address: "accesslog-service.istio-system"} 324 }), 325 isValid: false, 326 }, 327 { 328 name: "control plane auth policy invalid", 329 in: modify(valid, func(c *meshconfig.ProxyConfig) { c.ControlPlaneAuthPolicy = -1 }), 330 isValid: false, 331 }, 332 { 333 name: "zipkin address is valid", 334 in: modify(valid, 335 func(c *meshconfig.ProxyConfig) { 336 c.Tracing = &meshconfig.Tracing{ 337 Tracer: &meshconfig.Tracing_Zipkin_{ 338 Zipkin: &meshconfig.Tracing_Zipkin{ 339 Address: "zipkin.istio-system:9411", 340 }, 341 }, 342 } 343 }, 344 ), 345 isValid: true, 346 }, 347 { 348 name: "zipkin address with $(HOST_IP) is valid", 349 in: modify(valid, 350 func(c *meshconfig.ProxyConfig) { 351 c.Tracing = &meshconfig.Tracing{ 352 Tracer: &meshconfig.Tracing_Zipkin_{ 353 Zipkin: &meshconfig.Tracing_Zipkin{ 354 Address: "$(HOST_IP):9411", 355 }, 356 }, 357 } 358 }, 359 ), 360 isValid: true, 361 }, 362 { 363 name: "zipkin config invalid", 364 in: modify(valid, 365 func(c *meshconfig.ProxyConfig) { 366 c.Tracing = &meshconfig.Tracing{ 367 Tracer: &meshconfig.Tracing_Zipkin_{ 368 Zipkin: &meshconfig.Tracing_Zipkin{ 369 Address: "10.0.0.100", 370 }, 371 }, 372 } 373 }, 374 ), 375 isValid: false, 376 }, 377 { 378 name: "lightstep config is valid", 379 in: modify(valid, 380 func(c *meshconfig.ProxyConfig) { 381 c.Tracing = &meshconfig.Tracing{ 382 Tracer: &meshconfig.Tracing_Lightstep_{ 383 Lightstep: &meshconfig.Tracing_Lightstep{ 384 Address: "collector.lightstep:8080", 385 AccessToken: "abcdefg1234567", 386 }, 387 }, 388 } 389 }, 390 ), 391 isValid: true, 392 }, 393 { 394 name: "lightstep address invalid", 395 in: modify(valid, 396 func(c *meshconfig.ProxyConfig) { 397 c.Tracing = &meshconfig.Tracing{ 398 Tracer: &meshconfig.Tracing_Lightstep_{ 399 Lightstep: &meshconfig.Tracing_Lightstep{ 400 Address: "10.0.0.100", 401 AccessToken: "abcdefg1234567", 402 }, 403 }, 404 } 405 }, 406 ), 407 isValid: false, 408 }, 409 { 410 name: "lightstep address empty but lightstep access token is not", 411 in: modify(valid, 412 func(c *meshconfig.ProxyConfig) { 413 c.Tracing = &meshconfig.Tracing{ 414 Tracer: &meshconfig.Tracing_Lightstep_{ 415 Lightstep: &meshconfig.Tracing_Lightstep{ 416 Address: "", 417 AccessToken: "abcdefg1234567", 418 }, 419 }, 420 } 421 }, 422 ), 423 isValid: false, 424 }, 425 { 426 name: "lightstep address is valid but access token is empty", 427 in: modify(valid, 428 func(c *meshconfig.ProxyConfig) { 429 c.Tracing = &meshconfig.Tracing{ 430 Tracer: &meshconfig.Tracing_Lightstep_{ 431 Lightstep: &meshconfig.Tracing_Lightstep{ 432 Address: "collector.lightstep:8080", 433 AccessToken: "", 434 }, 435 }, 436 } 437 }, 438 ), 439 isValid: false, 440 }, 441 { 442 name: "lightstep access token empty but lightstep address is not", 443 in: modify(valid, 444 func(c *meshconfig.ProxyConfig) { 445 c.Tracing = &meshconfig.Tracing{ 446 Tracer: &meshconfig.Tracing_Lightstep_{ 447 Lightstep: &meshconfig.Tracing_Lightstep{ 448 Address: "10.0.0.100", 449 AccessToken: "", 450 }, 451 }, 452 } 453 }, 454 ), 455 isValid: false, 456 }, 457 { 458 name: "lightstep address and lightstep token both empty", 459 in: modify(valid, 460 func(c *meshconfig.ProxyConfig) { 461 c.Tracing = &meshconfig.Tracing{ 462 Tracer: &meshconfig.Tracing_Lightstep_{ 463 Lightstep: &meshconfig.Tracing_Lightstep{ 464 Address: "", 465 AccessToken: "", 466 }, 467 }, 468 } 469 }, 470 ), 471 isValid: false, 472 }, 473 { 474 name: "datadog without address", 475 in: modify(valid, 476 func(c *meshconfig.ProxyConfig) { 477 c.Tracing = &meshconfig.Tracing{ 478 Tracer: &meshconfig.Tracing_Datadog_{ 479 Datadog: &meshconfig.Tracing_Datadog{}, 480 }, 481 } 482 }, 483 ), 484 isValid: false, 485 }, 486 { 487 name: "datadog with correct address", 488 in: modify(valid, 489 func(c *meshconfig.ProxyConfig) { 490 c.Tracing = &meshconfig.Tracing{ 491 Tracer: &meshconfig.Tracing_Datadog_{ 492 Datadog: &meshconfig.Tracing_Datadog{ 493 Address: "datadog-agent:8126", 494 }, 495 }, 496 } 497 }, 498 ), 499 isValid: true, 500 }, 501 { 502 name: "datadog with invalid address", 503 in: modify(valid, 504 func(c *meshconfig.ProxyConfig) { 505 c.Tracing = &meshconfig.Tracing{ 506 Tracer: &meshconfig.Tracing_Datadog_{ 507 Datadog: &meshconfig.Tracing_Datadog{ 508 Address: "address-missing-port-number", 509 }, 510 }, 511 } 512 }, 513 ), 514 isValid: false, 515 }, 516 { 517 name: "custom tags with a literal value", 518 in: modify(valid, 519 func(c *meshconfig.ProxyConfig) { 520 c.Tracing = &meshconfig.Tracing{ 521 CustomTags: map[string]*meshconfig.Tracing_CustomTag{ 522 "clusterID": { 523 Type: &meshconfig.Tracing_CustomTag_Literal{ 524 Literal: &meshconfig.Tracing_Literal{ 525 Value: "cluster1", 526 }, 527 }, 528 }, 529 }, 530 } 531 }, 532 ), 533 isValid: true, 534 }, 535 { 536 name: "custom tags with a nil value", 537 in: modify(valid, 538 func(c *meshconfig.ProxyConfig) { 539 c.Tracing = &meshconfig.Tracing{ 540 CustomTags: map[string]*meshconfig.Tracing_CustomTag{ 541 "clusterID": nil, 542 }, 543 } 544 }, 545 ), 546 isValid: false, 547 }, 548 { 549 name: "private key provider with empty provider", 550 in: modify(valid, 551 func(c *meshconfig.ProxyConfig) { 552 c.PrivateKeyProvider = &meshconfig.PrivateKeyProvider{} 553 }, 554 ), 555 isValid: false, 556 }, 557 { 558 name: "private key provider with cryptomb without poll_delay", 559 in: modify(valid, 560 func(c *meshconfig.ProxyConfig) { 561 c.PrivateKeyProvider = &meshconfig.PrivateKeyProvider{ 562 Provider: &meshconfig.PrivateKeyProvider_Cryptomb{ 563 Cryptomb: &meshconfig.PrivateKeyProvider_CryptoMb{}, 564 }, 565 } 566 }, 567 ), 568 isValid: false, 569 }, 570 { 571 name: "private key provider with cryptomb zero poll_delay", 572 in: modify(valid, 573 func(c *meshconfig.ProxyConfig) { 574 c.PrivateKeyProvider = &meshconfig.PrivateKeyProvider{ 575 Provider: &meshconfig.PrivateKeyProvider_Cryptomb{ 576 Cryptomb: &meshconfig.PrivateKeyProvider_CryptoMb{ 577 PollDelay: &durationpb.Duration{ 578 Seconds: 0, 579 Nanos: 0, 580 }, 581 }, 582 }, 583 } 584 }, 585 ), 586 isValid: false, 587 }, 588 { 589 name: "private key provider with cryptomb", 590 in: modify(valid, 591 func(c *meshconfig.ProxyConfig) { 592 c.PrivateKeyProvider = &meshconfig.PrivateKeyProvider{ 593 Provider: &meshconfig.PrivateKeyProvider_Cryptomb{ 594 Cryptomb: &meshconfig.PrivateKeyProvider_CryptoMb{ 595 PollDelay: &durationpb.Duration{ 596 Seconds: 0, 597 Nanos: 10000, 598 }, 599 }, 600 }, 601 } 602 }, 603 ), 604 isValid: true, 605 }, 606 { 607 name: "private key provider with qat without poll_delay", 608 in: modify(valid, 609 func(c *meshconfig.ProxyConfig) { 610 c.PrivateKeyProvider = &meshconfig.PrivateKeyProvider{ 611 Provider: &meshconfig.PrivateKeyProvider_Qat{ 612 Qat: &meshconfig.PrivateKeyProvider_QAT{}, 613 }, 614 } 615 }, 616 ), 617 isValid: false, 618 }, 619 { 620 name: "private key provider with qat zero poll_delay", 621 in: modify(valid, 622 func(c *meshconfig.ProxyConfig) { 623 c.PrivateKeyProvider = &meshconfig.PrivateKeyProvider{ 624 Provider: &meshconfig.PrivateKeyProvider_Qat{ 625 Qat: &meshconfig.PrivateKeyProvider_QAT{ 626 PollDelay: &durationpb.Duration{ 627 Seconds: 0, 628 Nanos: 0, 629 }, 630 }, 631 }, 632 } 633 }, 634 ), 635 isValid: false, 636 }, 637 { 638 name: "private key provider with qat", 639 in: modify(valid, 640 func(c *meshconfig.ProxyConfig) { 641 c.PrivateKeyProvider = &meshconfig.PrivateKeyProvider{ 642 Provider: &meshconfig.PrivateKeyProvider_Qat{ 643 Qat: &meshconfig.PrivateKeyProvider_QAT{ 644 PollDelay: &durationpb.Duration{ 645 Seconds: 0, 646 Nanos: 1000, 647 }, 648 }, 649 }, 650 } 651 }, 652 ), 653 isValid: true, 654 }, 655 } 656 for _, c := range cases { 657 t.Run(c.name, func(t *testing.T) { 658 if got := ValidateMeshConfigProxyConfig(c.in); (got == nil) != c.isValid { 659 if c.isValid { 660 t.Errorf("got error %v, wanted none", got) 661 } else { 662 t.Error("got no error, wanted one") 663 } 664 } 665 }) 666 } 667 668 invalid := &meshconfig.ProxyConfig{ 669 ConfigPath: "", 670 BinaryPath: "", 671 DiscoveryAddress: "10.0.0.100", 672 ProxyAdminPort: 0, 673 DrainDuration: durationpb.New(-1 * time.Second), 674 ClusterName: &meshconfig.ProxyConfig_ServiceCluster{ServiceCluster: ""}, 675 StatsdUdpAddress: "10.0.0.100", 676 EnvoyMetricsService: &meshconfig.RemoteService{Address: "metrics-service"}, 677 EnvoyAccessLogService: &meshconfig.RemoteService{Address: "accesslog-service"}, 678 ControlPlaneAuthPolicy: -1, 679 StatusPort: 0, 680 Tracing: &meshconfig.Tracing{ 681 Tracer: &meshconfig.Tracing_Zipkin_{ 682 Zipkin: &meshconfig.Tracing_Zipkin{ 683 Address: "10.0.0.100", 684 }, 685 }, 686 }, 687 } 688 689 err := ValidateMeshConfigProxyConfig(invalid) 690 if err == nil { 691 t.Errorf("expected an error on invalid proxy mesh config: %v", invalid) 692 } else { 693 switch err := err.(type) { 694 case *multierror.Error: 695 // each field must cause an error in the field 696 if len(err.Errors) != 12 { 697 t.Errorf("expected an error for each field %v", err) 698 } 699 default: 700 t.Errorf("expected a multi error as output") 701 } 702 } 703 } 704 705 func TestValidateTLS(t *testing.T) { 706 testCases := []struct { 707 name string 708 tls *networking.ClientTLSSettings 709 valid bool 710 }{ 711 { 712 name: "SIMPLE: Credential Name set correctly", 713 tls: &networking.ClientTLSSettings{ 714 Mode: networking.ClientTLSSettings_SIMPLE, 715 CredentialName: "some credential", 716 ClientCertificate: "", 717 PrivateKey: "", 718 CaCertificates: "", 719 }, 720 valid: true, 721 }, 722 { 723 name: "SIMPLE CredentialName set with ClientCertificate specified", 724 tls: &networking.ClientTLSSettings{ 725 Mode: networking.ClientTLSSettings_SIMPLE, 726 CredentialName: "credential", 727 ClientCertificate: "cert", 728 PrivateKey: "", 729 CaCertificates: "", 730 }, 731 valid: false, 732 }, 733 { 734 name: "SIMPLE: CredentialName set with PrivateKey specified", 735 tls: &networking.ClientTLSSettings{ 736 Mode: networking.ClientTLSSettings_SIMPLE, 737 CredentialName: "credential", 738 ClientCertificate: "", 739 PrivateKey: "key", 740 CaCertificates: "", 741 }, 742 valid: false, 743 }, 744 { 745 name: "SIMPLE: CredentialName set with CACertficiates specified", 746 tls: &networking.ClientTLSSettings{ 747 Mode: networking.ClientTLSSettings_SIMPLE, 748 CredentialName: "credential", 749 ClientCertificate: "", 750 PrivateKey: "", 751 CaCertificates: "ca", 752 }, 753 valid: false, 754 }, 755 { 756 name: "MUTUAL: Credential Name set correctly", 757 tls: &networking.ClientTLSSettings{ 758 Mode: networking.ClientTLSSettings_MUTUAL, 759 CredentialName: "some credential", 760 ClientCertificate: "", 761 PrivateKey: "", 762 CaCertificates: "", 763 }, 764 valid: true, 765 }, 766 { 767 name: "MUTUAL CredentialName set with ClientCertificate specified", 768 tls: &networking.ClientTLSSettings{ 769 Mode: networking.ClientTLSSettings_MUTUAL, 770 CredentialName: "credential", 771 ClientCertificate: "cert", 772 PrivateKey: "", 773 CaCertificates: "", 774 }, 775 valid: false, 776 }, 777 { 778 name: "MUTUAL: CredentialName set with PrivateKey specified", 779 tls: &networking.ClientTLSSettings{ 780 Mode: networking.ClientTLSSettings_MUTUAL, 781 CredentialName: "credential", 782 ClientCertificate: "", 783 PrivateKey: "key", 784 CaCertificates: "", 785 }, 786 valid: false, 787 }, 788 { 789 name: "MUTUAL: CredentialName set with CACertficiates specified", 790 tls: &networking.ClientTLSSettings{ 791 Mode: networking.ClientTLSSettings_MUTUAL, 792 CredentialName: "credential", 793 ClientCertificate: "", 794 PrivateKey: "", 795 CaCertificates: "ca", 796 }, 797 valid: false, 798 }, 799 { 800 name: "MUTUAL: CredentialName set with CACRL specified", 801 tls: &networking.ClientTLSSettings{ 802 Mode: networking.ClientTLSSettings_MUTUAL, 803 CredentialName: "credential", 804 ClientCertificate: "", 805 PrivateKey: "", 806 CaCrl: "ca", 807 }, 808 valid: false, 809 }, 810 { 811 name: "MUTUAL: CredentialName not set with ClientCertificate and Key specified", 812 tls: &networking.ClientTLSSettings{ 813 Mode: networking.ClientTLSSettings_MUTUAL, 814 ClientCertificate: "cert", 815 PrivateKey: "key", 816 }, 817 valid: true, 818 }, 819 { 820 name: "MUTUAL: CredentialName not set with ClientCertificate specified and Key missing", 821 tls: &networking.ClientTLSSettings{ 822 Mode: networking.ClientTLSSettings_MUTUAL, 823 ClientCertificate: "cert", 824 PrivateKey: "", 825 }, 826 valid: false, 827 }, 828 { 829 name: "MUTUAL: CredentialName not set with ClientCertificate missing and Key specified", 830 tls: &networking.ClientTLSSettings{ 831 Mode: networking.ClientTLSSettings_MUTUAL, 832 ClientCertificate: "", 833 PrivateKey: "key", 834 }, 835 valid: false, 836 }, 837 } 838 839 for _, tc := range testCases { 840 if got := ValidateTLS(tc.tls); (got == nil) != tc.valid { 841 t.Errorf("ValidateTLS(%q) => got valid=%v, want valid=%v", 842 tc.name, got == nil, tc.valid) 843 } 844 } 845 } 846 847 func TestValidateWildcardDomain(t *testing.T) { 848 tests := []struct { 849 name string 850 in string 851 out string 852 }{ 853 {"empty", "", "empty"}, 854 {"too long", strings.Repeat("x", 256), "too long"}, 855 {"happy", strings.Repeat("x", 63), ""}, 856 {"wildcard", "*", ""}, 857 {"wildcard multi-segment", "*.bar.com", ""}, 858 {"wildcard single segment", "*foo", ""}, 859 {"wildcard prefix", "*foo.bar.com", ""}, 860 {"wildcard prefix dash", "*-foo.bar.com", ""}, 861 {"bad wildcard", "foo.*.com", "invalid"}, 862 {"bad wildcard", "foo*.bar.com", "invalid"}, 863 {"IP address", "1.1.1.1", "invalid"}, 864 } 865 for _, tt := range tests { 866 t.Run(tt.name, func(t *testing.T) { 867 err := ValidateWildcardDomain(tt.in) 868 if err == nil && tt.out != "" { 869 t.Fatalf("ValidateWildcardDomain(%v) = nil, wanted %q", tt.in, tt.out) 870 } else if err != nil && tt.out == "" { 871 t.Fatalf("ValidateWildcardDomain(%v) = %v, wanted nil", tt.in, err) 872 } else if err != nil && !strings.Contains(err.Error(), tt.out) { 873 t.Fatalf("ValidateWildcardDomain(%v) = %v, wanted %q", tt.in, err, tt.out) 874 } 875 }) 876 } 877 } 878 879 func TestValidateTrustDomain(t *testing.T) { 880 tests := []struct { 881 name string 882 in string 883 err string 884 }{ 885 {"empty", "", "empty"}, 886 {"happy", strings.Repeat("x", 63), ""}, 887 {"multi-segment", "foo.bar.com", ""}, 888 {"middle dash", "f-oo.bar.com", ""}, 889 {"trailing dot", "foo.bar.com.", ""}, 890 {"prefix dash", "-foo.bar.com", "invalid"}, 891 {"forward slash separated", "foo/bar/com", "invalid"}, 892 {"colon separated", "foo:bar:com", "invalid"}, 893 } 894 for _, tt := range tests { 895 t.Run(tt.name, func(t *testing.T) { 896 err := ValidateTrustDomain(tt.in) 897 if err == nil && tt.err != "" { 898 t.Fatalf("ValidateTrustDomain(%v) = nil, wanted %q", tt.in, tt.err) 899 } else if err != nil && tt.err == "" { 900 t.Fatalf("ValidateTrustDomain(%v) = %v, wanted nil", tt.in, err) 901 } else if err != nil && !strings.Contains(err.Error(), tt.err) { 902 t.Fatalf("ValidateTrustDomain(%v) = %v, wanted %q", tt.in, err, tt.err) 903 } 904 }) 905 } 906 } 907 908 func TestValidateConnectTimeout(t *testing.T) { 909 type durationCheck struct { 910 duration *durationpb.Duration 911 isValid bool 912 } 913 914 checks := []durationCheck{ 915 { 916 duration: &durationpb.Duration{Seconds: 1}, 917 isValid: true, 918 }, 919 { 920 duration: &durationpb.Duration{Seconds: 31}, 921 isValid: true, 922 }, 923 { 924 duration: &durationpb.Duration{Seconds: 999999999}, 925 isValid: false, 926 }, 927 } 928 929 for _, check := range checks { 930 if got := ValidateConnectTimeout(check.duration); (got == nil) != check.isValid { 931 t.Errorf("Failed: got valid=%t but wanted valid=%t: %v for %v", got == nil, check.isValid, got, check.duration) 932 } 933 } 934 } 935 936 func TestValidateProtocolDetectionTimeout(t *testing.T) { 937 type durationCheck struct { 938 duration *durationpb.Duration 939 isValid bool 940 } 941 942 checks := []durationCheck{ 943 { 944 duration: &durationpb.Duration{Seconds: 1}, 945 isValid: true, 946 }, 947 { 948 duration: &durationpb.Duration{Nanos: 99999}, 949 isValid: false, 950 }, 951 { 952 duration: &durationpb.Duration{Nanos: 0}, 953 isValid: true, 954 }, 955 } 956 957 for _, check := range checks { 958 if got := ValidateProtocolDetectionTimeout(check.duration); (got == nil) != check.isValid { 959 t.Errorf("Failed: got valid=%t but wanted valid=%t: %v for %v", got == nil, check.isValid, got, check.duration) 960 } 961 } 962 } 963 964 func TestValidateMeshConfig(t *testing.T) { 965 if _, err := ValidateMeshConfig(&meshconfig.MeshConfig{}); err == nil { 966 t.Error("expected an error on an empty mesh config") 967 } 968 969 invalid := &meshconfig.MeshConfig{ 970 ProxyListenPort: 0, 971 ConnectTimeout: durationpb.New(-1 * time.Second), 972 DefaultConfig: &meshconfig.ProxyConfig{}, 973 TrustDomain: "", 974 TrustDomainAliases: []string{"a.$b", "a/b", ""}, 975 ExtensionProviders: []*meshconfig.MeshConfig_ExtensionProvider{ 976 { 977 Name: "default", 978 Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyExtAuthzHttp{ 979 EnvoyExtAuthzHttp: &meshconfig.MeshConfig_ExtensionProvider_EnvoyExternalAuthorizationHttpProvider{ 980 Service: "foo/ext-authz", 981 Port: 999999, 982 }, 983 }, 984 }, 985 }, 986 MeshMTLS: &meshconfig.MeshConfig_TLSConfig{ 987 EcdhCurves: []string{"P-256"}, 988 }, 989 TlsDefaults: &meshconfig.MeshConfig_TLSConfig{ 990 EcdhCurves: []string{"P-256", "P-256", "invalid"}, 991 }, 992 } 993 994 warning, err := ValidateMeshConfig(invalid) 995 if err == nil { 996 t.Errorf("expected an error on invalid proxy mesh config: %v", invalid) 997 } else { 998 wantErrors := []string{ 999 "invalid proxy listen port", 1000 "invalid connect timeout", 1001 "config path must be set", 1002 "binary path must be set", 1003 "oneof service cluster or tracing service name must be specified", 1004 "invalid drain duration: duration must be greater than 1ms", 1005 "discovery address must be set to the proxy discovery service", 1006 "invalid proxy admin port", 1007 "invalid status port", 1008 "trustDomain: empty domain name not allowed", 1009 "trustDomainAliases[0]", 1010 "trustDomainAliases[1]", 1011 "trustDomainAliases[2]", 1012 "mesh TLS does not support ECDH curves configuration", 1013 } 1014 switch err := err.(type) { 1015 case *multierror.Error: 1016 // each field must cause an error in the field 1017 if len(err.Errors) != len(wantErrors) { 1018 t.Errorf("expected %d errors but found %v", len(wantErrors), err) 1019 } else { 1020 for i := 0; i < len(wantErrors); i++ { 1021 if !strings.HasPrefix(err.Errors[i].Error(), wantErrors[i]) { 1022 t.Errorf("expected error %q at index %d but found %q", wantErrors[i], i, err.Errors[i]) 1023 } 1024 } 1025 } 1026 default: 1027 t.Errorf("expected a multi error as output") 1028 } 1029 } 1030 if warning == nil { 1031 t.Errorf("expected a warning on invalid proxy mesh config: %v", invalid) 1032 } else { 1033 wantWarnings := []string{ 1034 "detected unrecognized ECDH curves", 1035 "detected duplicate ECDH curves", 1036 } 1037 switch warn := warning.(type) { 1038 case *multierror.Error: 1039 // each field must cause an error in the field 1040 if len(warn.Errors) != len(wantWarnings) { 1041 t.Errorf("expected %d warnings but found %v", len(wantWarnings), warn) 1042 } else { 1043 for i := 0; i < len(wantWarnings); i++ { 1044 if !strings.HasPrefix(warn.Errors[i].Error(), wantWarnings[i]) { 1045 t.Errorf("expected warning %q at index %d but found %q", wantWarnings[i], i, warn.Errors[i]) 1046 } 1047 } 1048 } 1049 default: 1050 t.Errorf("expected a multi error as output") 1051 } 1052 } 1053 } 1054 1055 func TestValidateLocalityLbSetting(t *testing.T) { 1056 cases := []struct { 1057 name string 1058 in *networking.LocalityLoadBalancerSetting 1059 outlier *networking.OutlierDetection 1060 err bool 1061 warn bool 1062 }{ 1063 { 1064 name: "valid mesh config without LocalityLoadBalancerSetting", 1065 in: nil, 1066 outlier: nil, 1067 err: false, 1068 warn: false, 1069 }, 1070 1071 { 1072 name: "invalid LocalityLoadBalancerSetting_Distribute total weight > 100", 1073 in: &networking.LocalityLoadBalancerSetting{ 1074 Distribute: []*networking.LocalityLoadBalancerSetting_Distribute{ 1075 { 1076 From: "a/b/c", 1077 To: map[string]uint32{ 1078 "a/b/c": 80, 1079 "a/b1": 25, 1080 }, 1081 }, 1082 }, 1083 }, 1084 outlier: &networking.OutlierDetection{}, 1085 err: true, 1086 warn: false, 1087 }, 1088 { 1089 name: "invalid LocalityLoadBalancerSetting_Distribute total weight < 100", 1090 in: &networking.LocalityLoadBalancerSetting{ 1091 Distribute: []*networking.LocalityLoadBalancerSetting_Distribute{ 1092 { 1093 From: "a/b/c", 1094 To: map[string]uint32{ 1095 "a/b/c": 80, 1096 "a/b1": 15, 1097 }, 1098 }, 1099 }, 1100 }, 1101 outlier: &networking.OutlierDetection{}, 1102 err: true, 1103 warn: false, 1104 }, 1105 { 1106 name: "invalid LocalityLoadBalancerSetting_Distribute weight = 0", 1107 in: &networking.LocalityLoadBalancerSetting{ 1108 Distribute: []*networking.LocalityLoadBalancerSetting_Distribute{ 1109 { 1110 From: "a/b/c", 1111 To: map[string]uint32{ 1112 "a/b/c": 0, 1113 "a/b1": 100, 1114 }, 1115 }, 1116 }, 1117 }, 1118 outlier: &networking.OutlierDetection{}, 1119 err: true, 1120 warn: false, 1121 }, 1122 { 1123 name: "invalid LocalityLoadBalancerSetting specify both distribute and failover", 1124 in: &networking.LocalityLoadBalancerSetting{ 1125 Distribute: []*networking.LocalityLoadBalancerSetting_Distribute{ 1126 { 1127 From: "a/b/c", 1128 To: map[string]uint32{ 1129 "a/b/c": 80, 1130 "a/b1": 20, 1131 }, 1132 }, 1133 }, 1134 Failover: []*networking.LocalityLoadBalancerSetting_Failover{ 1135 { 1136 From: "region1", 1137 To: "region2", 1138 }, 1139 }, 1140 }, 1141 outlier: &networking.OutlierDetection{}, 1142 err: true, 1143 warn: false, 1144 }, 1145 1146 { 1147 name: "invalid failover src and dst have same region", 1148 in: &networking.LocalityLoadBalancerSetting{ 1149 Failover: []*networking.LocalityLoadBalancerSetting_Failover{ 1150 { 1151 From: "region1", 1152 To: "region1", 1153 }, 1154 }, 1155 }, 1156 outlier: &networking.OutlierDetection{}, 1157 err: true, 1158 warn: false, 1159 }, 1160 { 1161 name: "invalid failover src contain '*' wildcard", 1162 in: &networking.LocalityLoadBalancerSetting{ 1163 Failover: []*networking.LocalityLoadBalancerSetting_Failover{ 1164 { 1165 From: "*", 1166 To: "region2", 1167 }, 1168 }, 1169 }, 1170 outlier: &networking.OutlierDetection{}, 1171 err: true, 1172 warn: false, 1173 }, 1174 { 1175 name: "invalid failover dst contain '*' wildcard", 1176 in: &networking.LocalityLoadBalancerSetting{ 1177 Failover: []*networking.LocalityLoadBalancerSetting_Failover{ 1178 { 1179 From: "region1", 1180 To: "*", 1181 }, 1182 }, 1183 }, 1184 outlier: &networking.OutlierDetection{}, 1185 err: true, 1186 warn: false, 1187 }, 1188 { 1189 name: "invalid failover src contain '/' separator", 1190 in: &networking.LocalityLoadBalancerSetting{ 1191 Failover: []*networking.LocalityLoadBalancerSetting_Failover{ 1192 { 1193 From: "region1/zone1", 1194 To: "region2", 1195 }, 1196 }, 1197 }, 1198 outlier: &networking.OutlierDetection{}, 1199 err: true, 1200 warn: false, 1201 }, 1202 { 1203 name: "invalid failover dst contain '/' separator", 1204 in: &networking.LocalityLoadBalancerSetting{ 1205 Failover: []*networking.LocalityLoadBalancerSetting_Failover{ 1206 { 1207 From: "region1", 1208 To: "region2/zone1", 1209 }, 1210 }, 1211 }, 1212 outlier: &networking.OutlierDetection{}, 1213 err: true, 1214 warn: false, 1215 }, 1216 { 1217 name: "failover priority provided without outlier detection policy", 1218 in: &networking.LocalityLoadBalancerSetting{ 1219 FailoverPriority: []string{ 1220 "topology.istio.io/network", 1221 "topology.kubernetes.io/region", 1222 "topology.kubernetes.io/zone", 1223 "topology.istio.io/subzone", 1224 }, 1225 }, 1226 outlier: nil, 1227 err: false, 1228 warn: true, 1229 }, 1230 { 1231 name: "failover provided without outlier detection policy", 1232 in: &networking.LocalityLoadBalancerSetting{ 1233 Failover: []*networking.LocalityLoadBalancerSetting_Failover{ 1234 { 1235 From: "us-east", 1236 To: "eu-west", 1237 }, 1238 { 1239 From: "us-west", 1240 To: "eu-east", 1241 }, 1242 }, 1243 }, 1244 outlier: nil, 1245 err: false, 1246 warn: true, 1247 }, 1248 { 1249 name: "invalid LocalityLoadBalancerSetting specify both failoverPriority and region in failover", 1250 in: &networking.LocalityLoadBalancerSetting{ 1251 Failover: []*networking.LocalityLoadBalancerSetting_Failover{ 1252 { 1253 From: "region1", 1254 To: "region2", 1255 }, 1256 }, 1257 FailoverPriority: []string{"topology.kubernetes.io/region"}, 1258 }, 1259 outlier: &networking.OutlierDetection{}, 1260 err: true, 1261 warn: false, 1262 }, 1263 } 1264 1265 for _, c := range cases { 1266 v := ValidateLocalityLbSetting(c.in, c.outlier) 1267 warn, err := v.Unwrap() 1268 if (err != nil) != c.err { 1269 t.Errorf("ValidateLocalityLbSetting failed on %v: got err=%v but wanted err=%v: %v", 1270 c.name, err != nil, c.err, err) 1271 } 1272 if (warn != nil) != c.warn { 1273 t.Errorf("ValidateLocalityLbSetting failed on %v: got warn=%v but wanted warn=%v: %v", 1274 c.name, warn != nil, c.warn, warn) 1275 } 1276 } 1277 } 1278 1279 func TestValidateLocalities(t *testing.T) { 1280 cases := []struct { 1281 name string 1282 localities []string 1283 valid bool 1284 }{ 1285 { 1286 name: "multi wildcard locality", 1287 localities: []string{"*/zone/*"}, 1288 valid: false, 1289 }, 1290 { 1291 name: "wildcard not in suffix", 1292 localities: []string{"*/zone"}, 1293 valid: false, 1294 }, 1295 { 1296 name: "explicit wildcard region overlap", 1297 localities: []string{"*", "a/b/c"}, 1298 valid: false, 1299 }, 1300 { 1301 name: "implicit wildcard region overlap", 1302 localities: []string{"a", "a/b/c"}, 1303 valid: false, 1304 }, 1305 { 1306 name: "explicit wildcard zone overlap", 1307 localities: []string{"a/*", "a/b/c"}, 1308 valid: false, 1309 }, 1310 { 1311 name: "implicit wildcard zone overlap", 1312 localities: []string{"a/b", "a/b/c"}, 1313 valid: false, 1314 }, 1315 { 1316 name: "explicit wildcard subzone overlap", 1317 localities: []string{"a/b/*", "a/b/c"}, 1318 valid: false, 1319 }, 1320 { 1321 name: "implicit wildcard subzone overlap", 1322 localities: []string{"a/b", "a/b/c"}, 1323 valid: false, 1324 }, 1325 { 1326 name: "valid localities", 1327 localities: []string{"a1/*", "a2/*", "a3/b3/c3", "a4/b4", "a5/b5/*"}, 1328 valid: true, 1329 }, 1330 } 1331 for _, c := range cases { 1332 t.Run(c.name, func(t *testing.T) { 1333 err := validateLocalities(c.localities) 1334 if !c.valid && err == nil { 1335 t.Errorf("expect invalid localities") 1336 } 1337 1338 if c.valid && err != nil { 1339 t.Errorf("expect valid localities. but got err %v", err) 1340 } 1341 }) 1342 } 1343 } 1344 1345 func TestValidationIPAddress(t *testing.T) { 1346 tests := []struct { 1347 name string 1348 addr string 1349 ok bool 1350 }{ 1351 { 1352 name: "valid ipv4 address", 1353 addr: "1.1.1.1", 1354 ok: true, 1355 }, 1356 { 1357 name: "invalid ipv4 address", 1358 addr: "1.1.A.1", 1359 ok: false, 1360 }, 1361 { 1362 name: "valid ipv6 subnet", 1363 addr: "2001:1::1", 1364 ok: true, 1365 }, 1366 { 1367 name: "invalid ipv6 address", 1368 addr: "2001:1:G::1", 1369 ok: false, 1370 }, 1371 } 1372 for _, tt := range tests { 1373 if err := ValidateIPAddress(tt.addr); err != nil { 1374 if tt.ok { 1375 t.Errorf("test: \"%s\" expected to succeed but failed with error: %+v", tt.name, err) 1376 } 1377 } else { 1378 if !tt.ok { 1379 t.Errorf("test: \"%s\" expected to fail but succeeded", tt.name) 1380 } 1381 } 1382 } 1383 } 1384 1385 func TestValidationIPSubnet(t *testing.T) { 1386 tests := []struct { 1387 name string 1388 subnet string 1389 ok bool 1390 }{ 1391 { 1392 name: "valid ipv4 subnet", 1393 subnet: "1.1.1.1/24", 1394 ok: true, 1395 }, 1396 { 1397 name: "invalid ipv4 subnet", 1398 subnet: "1.1.1.1/48", 1399 ok: false, 1400 }, 1401 { 1402 name: "valid ipv6 subnet", 1403 subnet: "2001:1::1/64", 1404 ok: true, 1405 }, 1406 { 1407 name: "invalid ipv6 subnet", 1408 subnet: "2001:1::1/132", 1409 ok: false, 1410 }, 1411 } 1412 1413 for _, tt := range tests { 1414 if err := ValidateIPSubnet(tt.subnet); err != nil { 1415 if tt.ok { 1416 t.Errorf("test: \"%s\" expected to succeed but failed with error: %+v", tt.name, err) 1417 } 1418 } else { 1419 if !tt.ok { 1420 t.Errorf("test: \"%s\" expected to fail but succeeded", tt.name) 1421 } 1422 } 1423 } 1424 } 1425 1426 func TestServiceSettings(t *testing.T) { 1427 cases := []struct { 1428 name string 1429 hosts []string 1430 valid bool 1431 }{ 1432 { 1433 name: "good", 1434 hosts: []string{ 1435 "*.foo.bar", 1436 "bar.baz.svc.cluster.local", 1437 }, 1438 valid: true, 1439 }, 1440 { 1441 name: "bad", 1442 hosts: []string{ 1443 "foo.bar.*", 1444 }, 1445 valid: false, 1446 }, 1447 } 1448 1449 for _, c := range cases { 1450 t.Run(c.name, func(t *testing.T) { 1451 m := meshconfig.MeshConfig{ 1452 ServiceSettings: []*meshconfig.MeshConfig_ServiceSettings{ 1453 { 1454 Hosts: c.hosts, 1455 }, 1456 }, 1457 } 1458 1459 if got := validateServiceSettings(&m); (got == nil) != c.valid { 1460 t.Errorf("got(%v) != want(%v)\n", got, c.valid) 1461 } 1462 }) 1463 } 1464 } 1465 1466 func TestValidateMeshNetworks(t *testing.T) { 1467 testcases := []struct { 1468 name string 1469 mn *meshconfig.MeshNetworks 1470 valid bool 1471 }{ 1472 { 1473 name: "Empty MeshNetworks", 1474 mn: &meshconfig.MeshNetworks{}, 1475 valid: true, 1476 }, 1477 { 1478 name: "Valid MeshNetworks", 1479 mn: &meshconfig.MeshNetworks{ 1480 Networks: map[string]*meshconfig.Network{ 1481 "n1": { 1482 Endpoints: []*meshconfig.Network_NetworkEndpoints{ 1483 { 1484 Ne: &meshconfig.Network_NetworkEndpoints_FromRegistry{ 1485 FromRegistry: "Kubernetes", 1486 }, 1487 }, 1488 }, 1489 Gateways: []*meshconfig.Network_IstioNetworkGateway{ 1490 { 1491 Gw: &meshconfig.Network_IstioNetworkGateway_RegistryServiceName{ 1492 RegistryServiceName: "istio-ingressgateway.istio-system.svc.cluster.local", 1493 }, 1494 Port: 80, 1495 }, 1496 }, 1497 }, 1498 "n2": { 1499 Endpoints: []*meshconfig.Network_NetworkEndpoints{ 1500 { 1501 Ne: &meshconfig.Network_NetworkEndpoints_FromRegistry{ 1502 FromRegistry: "cluster1", 1503 }, 1504 }, 1505 }, 1506 Gateways: []*meshconfig.Network_IstioNetworkGateway{ 1507 { 1508 Gw: &meshconfig.Network_IstioNetworkGateway_RegistryServiceName{ 1509 RegistryServiceName: "istio-ingressgateway.istio-system.svc.cluster.local", 1510 }, 1511 Port: 443, 1512 }, 1513 }, 1514 }, 1515 }, 1516 }, 1517 valid: true, 1518 }, 1519 { 1520 name: "Invalid Gateway Address", 1521 mn: &meshconfig.MeshNetworks{ 1522 Networks: map[string]*meshconfig.Network{ 1523 "n1": { 1524 Endpoints: []*meshconfig.Network_NetworkEndpoints{ 1525 { 1526 Ne: &meshconfig.Network_NetworkEndpoints_FromRegistry{ 1527 FromRegistry: "Kubernetes", 1528 }, 1529 }, 1530 }, 1531 Gateways: []*meshconfig.Network_IstioNetworkGateway{ 1532 { 1533 Gw: &meshconfig.Network_IstioNetworkGateway_Address{ 1534 Address: "1nv@lidhostname", 1535 }, 1536 Port: 80, 1537 }, 1538 }, 1539 }, 1540 }, 1541 }, 1542 valid: false, 1543 }, 1544 { 1545 name: "Invalid registry name", 1546 mn: &meshconfig.MeshNetworks{ 1547 Networks: map[string]*meshconfig.Network{ 1548 "n1": { 1549 Endpoints: []*meshconfig.Network_NetworkEndpoints{ 1550 { 1551 Ne: &meshconfig.Network_NetworkEndpoints_FromRegistry{ 1552 FromRegistry: "cluster.local", 1553 }, 1554 }, 1555 }, 1556 Gateways: []*meshconfig.Network_IstioNetworkGateway{ 1557 { 1558 Gw: &meshconfig.Network_IstioNetworkGateway_RegistryServiceName{ 1559 RegistryServiceName: "istio-ingressgateway.istio-system.svc.cluster.local", 1560 }, 1561 Port: 80, 1562 }, 1563 }, 1564 }, 1565 }, 1566 }, 1567 valid: false, 1568 }, 1569 } 1570 1571 for _, tc := range testcases { 1572 t.Run(tc.name, func(t *testing.T) { 1573 err := ValidateMeshNetworks(tc.mn) 1574 if err != nil && tc.valid { 1575 t.Errorf("error not expected on valid meshnetworks: %v", tc.mn) 1576 } 1577 if err == nil && !tc.valid { 1578 t.Errorf("expected an error on invalid meshnetworks: %v", tc.mn) 1579 } 1580 }) 1581 } 1582 }