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  }