istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/networking/core/cluster_builder_test.go (about)

     1  // Copyright Istio Authors. All Rights Reserved.
     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 core
    16  
    17  import (
    18  	"encoding/json"
    19  	"fmt"
    20  	"reflect"
    21  	"sort"
    22  	"strings"
    23  	"sync"
    24  	"testing"
    25  	"time"
    26  
    27  	cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
    28  	core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    29  	endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
    30  	tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
    31  	http "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3"
    32  	"github.com/google/go-cmp/cmp"
    33  	"google.golang.org/protobuf/testing/protocmp"
    34  	"google.golang.org/protobuf/types/known/durationpb"
    35  	"google.golang.org/protobuf/types/known/structpb"
    36  	wrappers "google.golang.org/protobuf/types/known/wrapperspb"
    37  
    38  	meshconfig "istio.io/api/mesh/v1alpha1"
    39  	networking "istio.io/api/networking/v1alpha3"
    40  	"istio.io/api/type/v1beta1"
    41  	"istio.io/istio/pilot/pkg/features"
    42  	"istio.io/istio/pilot/pkg/model"
    43  	"istio.io/istio/pilot/pkg/networking/util"
    44  	authn_model "istio.io/istio/pilot/pkg/security/model"
    45  	"istio.io/istio/pilot/pkg/serviceregistry/provider"
    46  	"istio.io/istio/pilot/pkg/xds/endpoints"
    47  	xdsfilters "istio.io/istio/pilot/pkg/xds/filters"
    48  	v3 "istio.io/istio/pilot/pkg/xds/v3"
    49  	"istio.io/istio/pilot/test/xdstest"
    50  	istiocluster "istio.io/istio/pkg/cluster"
    51  	"istio.io/istio/pkg/config"
    52  	"istio.io/istio/pkg/config/constants"
    53  	"istio.io/istio/pkg/config/host"
    54  	"istio.io/istio/pkg/config/labels"
    55  	"istio.io/istio/pkg/config/protocol"
    56  	"istio.io/istio/pkg/config/schema/gvk"
    57  	"istio.io/istio/pkg/network"
    58  	"istio.io/istio/pkg/security"
    59  	"istio.io/istio/pkg/test"
    60  	"istio.io/istio/pkg/test/util/assert"
    61  )
    62  
    63  func TestApplyDestinationRule(t *testing.T) {
    64  	servicePort := model.PortList{
    65  		&model.Port{
    66  			Name:     "default",
    67  			Port:     8080,
    68  			Protocol: protocol.HTTP,
    69  		},
    70  		&model.Port{
    71  			Name:     "auto",
    72  			Port:     9090,
    73  			Protocol: protocol.Unsupported,
    74  		},
    75  	}
    76  	http2ServicePort := model.PortList{
    77  		&model.Port{
    78  			Name:     "default",
    79  			Port:     8080,
    80  			Protocol: protocol.HTTP2,
    81  		},
    82  		&model.Port{
    83  			Name:     "auto",
    84  			Port:     9090,
    85  			Protocol: protocol.Unsupported,
    86  		},
    87  	}
    88  	service := &model.Service{
    89  		Hostname:   host.Name("foo.default.svc.cluster.local"),
    90  		Ports:      servicePort,
    91  		Resolution: model.ClientSideLB,
    92  		Attributes: model.ServiceAttributes{
    93  			Namespace: TestServiceNamespace,
    94  		},
    95  	}
    96  	http2Service := &model.Service{
    97  		Hostname:   host.Name("foo.default.svc.cluster.local"),
    98  		Ports:      http2ServicePort,
    99  		Resolution: model.ClientSideLB,
   100  		Attributes: model.ServiceAttributes{
   101  			Namespace: TestServiceNamespace,
   102  		},
   103  	}
   104  
   105  	cases := []struct {
   106  		name                   string
   107  		cluster                *cluster.Cluster
   108  		clusterMode            ClusterMode
   109  		service                *model.Service
   110  		port                   *model.Port
   111  		proxyView              model.ProxyView
   112  		destRule               *networking.DestinationRule
   113  		meshConfig             *meshconfig.MeshConfig
   114  		expectedSubsetClusters []*cluster.Cluster
   115  	}{
   116  		// TODO(ramaraochavali): Add more tests to cover additional conditions.
   117  		{
   118  			name:                   "nil destination rule",
   119  			cluster:                &cluster.Cluster{},
   120  			clusterMode:            DefaultClusterMode,
   121  			service:                &model.Service{},
   122  			port:                   &model.Port{},
   123  			proxyView:              model.ProxyViewAll,
   124  			destRule:               nil,
   125  			expectedSubsetClusters: []*cluster.Cluster{},
   126  		},
   127  		{
   128  			name:        "destination rule with subsets",
   129  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
   130  			clusterMode: DefaultClusterMode,
   131  			service:     service,
   132  			port:        servicePort[0],
   133  			proxyView:   model.ProxyViewAll,
   134  			destRule: &networking.DestinationRule{
   135  				Host: "foo.default.svc.cluster.local",
   136  				Subsets: []*networking.Subset{
   137  					{
   138  						Name:   "foobar",
   139  						Labels: map[string]string{"foo": "bar"},
   140  					},
   141  				},
   142  			},
   143  			expectedSubsetClusters: []*cluster.Cluster{
   144  				{
   145  					Name:                 "outbound|8080|foobar|foo.default.svc.cluster.local",
   146  					ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS},
   147  					EdsClusterConfig: &cluster.Cluster_EdsClusterConfig{
   148  						ServiceName: "outbound|8080|foobar|foo.default.svc.cluster.local",
   149  					},
   150  				},
   151  			},
   152  		},
   153  		{
   154  			name:        "destination rule with pass through subsets",
   155  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STATIC}},
   156  			clusterMode: DefaultClusterMode,
   157  			service:     service,
   158  			port:        servicePort[0],
   159  			proxyView:   model.ProxyViewAll,
   160  			destRule: &networking.DestinationRule{
   161  				Host: "foo.default.svc.cluster.local",
   162  				Subsets: []*networking.Subset{
   163  					{
   164  						Name:   "foobar",
   165  						Labels: map[string]string{"foo": "bar"},
   166  						TrafficPolicy: &networking.TrafficPolicy{
   167  							LoadBalancer: &networking.LoadBalancerSettings{
   168  								LbPolicy: &networking.LoadBalancerSettings_Simple{Simple: networking.LoadBalancerSettings_PASSTHROUGH},
   169  							},
   170  						},
   171  					},
   172  				},
   173  			},
   174  			expectedSubsetClusters: []*cluster.Cluster{
   175  				{
   176  					Name:                 "outbound|8080|foobar|foo.default.svc.cluster.local",
   177  					ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_ORIGINAL_DST},
   178  				},
   179  			},
   180  		},
   181  		{
   182  			name: "destination rule static with pass",
   183  			cluster: &cluster.Cluster{
   184  				Name:                 "foo",
   185  				ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STATIC},
   186  				LoadAssignment:       &endpoint.ClusterLoadAssignment{},
   187  			},
   188  			clusterMode: DefaultClusterMode,
   189  			service:     service,
   190  			port:        servicePort[0],
   191  			proxyView:   model.ProxyViewAll,
   192  			destRule: &networking.DestinationRule{
   193  				Host: "foo.default.svc.cluster.local",
   194  				TrafficPolicy: &networking.TrafficPolicy{
   195  					LoadBalancer: &networking.LoadBalancerSettings{
   196  						LbPolicy: &networking.LoadBalancerSettings_Simple{Simple: networking.LoadBalancerSettings_PASSTHROUGH},
   197  					},
   198  				},
   199  			},
   200  		},
   201  		{
   202  			name:        "destination rule with subsets for SniDnat cluster",
   203  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
   204  			clusterMode: SniDnatClusterMode,
   205  			service:     service,
   206  			port:        servicePort[0],
   207  			proxyView:   model.ProxyViewAll,
   208  			destRule: &networking.DestinationRule{
   209  				Host: "foo.default.svc.cluster.local",
   210  				Subsets: []*networking.Subset{
   211  					{
   212  						Name:   "foobar",
   213  						Labels: map[string]string{"foo": "bar"},
   214  					},
   215  				},
   216  			},
   217  			expectedSubsetClusters: []*cluster.Cluster{
   218  				{
   219  					Name:                 "outbound_.8080_.foobar_.foo.default.svc.cluster.local",
   220  					ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS},
   221  					EdsClusterConfig: &cluster.Cluster_EdsClusterConfig{
   222  						ServiceName: "outbound_.8080_.foobar_.foo.default.svc.cluster.local",
   223  					},
   224  				},
   225  			},
   226  		},
   227  		{
   228  			name:        "destination rule with subset traffic policy",
   229  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
   230  			clusterMode: DefaultClusterMode,
   231  			service:     service,
   232  			port:        servicePort[0],
   233  			proxyView:   model.ProxyViewAll,
   234  			destRule: &networking.DestinationRule{
   235  				Host: "foo.default.svc.cluster.local",
   236  				Subsets: []*networking.Subset{
   237  					{
   238  						Name:   "foobar",
   239  						Labels: map[string]string{"foo": "bar"},
   240  						TrafficPolicy: &networking.TrafficPolicy{
   241  							ConnectionPool: &networking.ConnectionPoolSettings{
   242  								Http: &networking.ConnectionPoolSettings_HTTPSettings{
   243  									MaxRetries: 10,
   244  								},
   245  							},
   246  						},
   247  					},
   248  				},
   249  			},
   250  			expectedSubsetClusters: []*cluster.Cluster{
   251  				{
   252  					Name:                 "outbound|8080|foobar|foo.default.svc.cluster.local",
   253  					ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS},
   254  					EdsClusterConfig: &cluster.Cluster_EdsClusterConfig{
   255  						ServiceName: "outbound|8080|foobar|foo.default.svc.cluster.local",
   256  					},
   257  					CircuitBreakers: &cluster.CircuitBreakers{
   258  						Thresholds: []*cluster.CircuitBreakers_Thresholds{
   259  							{
   260  								MaxRetries: &wrappers.UInt32Value{
   261  									Value: 10,
   262  								},
   263  							},
   264  						},
   265  					},
   266  				},
   267  			},
   268  		},
   269  		{
   270  			name:        "destination rule with subset traffic policy and alt statname",
   271  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
   272  			clusterMode: DefaultClusterMode,
   273  			service:     service,
   274  			port:        servicePort[0],
   275  			proxyView:   model.ProxyViewAll,
   276  			destRule: &networking.DestinationRule{
   277  				Host: "foo.default.svc.cluster.local",
   278  				Subsets: []*networking.Subset{
   279  					{
   280  						Name:   "foobar",
   281  						Labels: map[string]string{"foo": "bar"},
   282  						TrafficPolicy: &networking.TrafficPolicy{
   283  							ConnectionPool: &networking.ConnectionPoolSettings{
   284  								Http: &networking.ConnectionPoolSettings_HTTPSettings{
   285  									MaxRetries: 10,
   286  								},
   287  							},
   288  						},
   289  					},
   290  				},
   291  			},
   292  			meshConfig: &meshconfig.MeshConfig{
   293  				OutboundClusterStatName: "%SERVICE%_%SUBSET_NAME%_%SERVICE_PORT_NAME%_%SERVICE_PORT%",
   294  				InboundTrafficPolicy:    &meshconfig.MeshConfig_InboundTrafficPolicy{},
   295  				EnableAutoMtls: &wrappers.BoolValue{
   296  					Value: false,
   297  				},
   298  			},
   299  			expectedSubsetClusters: []*cluster.Cluster{
   300  				{
   301  					Name:                 "outbound|8080|foobar|foo.default.svc.cluster.local",
   302  					ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS},
   303  					EdsClusterConfig: &cluster.Cluster_EdsClusterConfig{
   304  						ServiceName: "outbound|8080|foobar|foo.default.svc.cluster.local",
   305  					},
   306  					CircuitBreakers: &cluster.CircuitBreakers{
   307  						Thresholds: []*cluster.CircuitBreakers_Thresholds{
   308  							{
   309  								MaxRetries: &wrappers.UInt32Value{
   310  									Value: 10,
   311  								},
   312  							},
   313  						},
   314  					},
   315  					AltStatName: "foo.default.svc.cluster.local_foobar_default_8080",
   316  				},
   317  			},
   318  		},
   319  		{
   320  			name:        "destination rule with use client protocol traffic policy",
   321  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
   322  			clusterMode: DefaultClusterMode,
   323  			service:     service,
   324  			port:        servicePort[0],
   325  			proxyView:   model.ProxyViewAll,
   326  			destRule: &networking.DestinationRule{
   327  				Host: "foo.default.svc.cluster.local",
   328  				TrafficPolicy: &networking.TrafficPolicy{
   329  					ConnectionPool: &networking.ConnectionPoolSettings{
   330  						Http: &networking.ConnectionPoolSettings_HTTPSettings{
   331  							MaxRetries:        10,
   332  							UseClientProtocol: true,
   333  						},
   334  					},
   335  				},
   336  			},
   337  			expectedSubsetClusters: []*cluster.Cluster{},
   338  		},
   339  		{
   340  			name:        "destination rule with maxRequestsPerConnection",
   341  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
   342  			clusterMode: DefaultClusterMode,
   343  			service:     service,
   344  			port:        servicePort[0],
   345  			proxyView:   model.ProxyViewAll,
   346  			destRule: &networking.DestinationRule{
   347  				Host: "foo.default.svc.cluster.local",
   348  				TrafficPolicy: &networking.TrafficPolicy{
   349  					ConnectionPool: &networking.ConnectionPoolSettings{
   350  						Http: &networking.ConnectionPoolSettings_HTTPSettings{
   351  							MaxRetries:               10,
   352  							MaxRequestsPerConnection: 10,
   353  						},
   354  					},
   355  				},
   356  			},
   357  			expectedSubsetClusters: []*cluster.Cluster{},
   358  		},
   359  		{
   360  			name:        "destination rule with maxConcurrentStreams",
   361  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
   362  			clusterMode: DefaultClusterMode,
   363  			service:     http2Service,
   364  			port:        http2ServicePort[0],
   365  			proxyView:   model.ProxyViewAll,
   366  			destRule: &networking.DestinationRule{
   367  				Host: "foo.default.svc.cluster.local",
   368  				TrafficPolicy: &networking.TrafficPolicy{
   369  					ConnectionPool: &networking.ConnectionPoolSettings{
   370  						Http: &networking.ConnectionPoolSettings_HTTPSettings{
   371  							MaxRetries:           10,
   372  							MaxConcurrentStreams: 10,
   373  						},
   374  					},
   375  				},
   376  			},
   377  			expectedSubsetClusters: []*cluster.Cluster{},
   378  		},
   379  		{
   380  			name:        "subset without labels in both",
   381  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STRICT_DNS}},
   382  			clusterMode: DefaultClusterMode,
   383  			service: &model.Service{
   384  				Hostname:   host.Name("foo.example.com"),
   385  				Ports:      servicePort,
   386  				Resolution: model.DNSLB,
   387  				Attributes: model.ServiceAttributes{
   388  					Namespace: TestServiceNamespace,
   389  				},
   390  			},
   391  			port:      servicePort[0],
   392  			proxyView: model.ProxyViewAll,
   393  			destRule: &networking.DestinationRule{
   394  				Host:    "foo.example.com",
   395  				Subsets: []*networking.Subset{{Name: "v1"}},
   396  			},
   397  			expectedSubsetClusters: []*cluster.Cluster{{
   398  				Name:                 "outbound|8080|v1|foo.example.com",
   399  				ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STRICT_DNS},
   400  			}},
   401  		},
   402  		{
   403  			name:        "subset without labels in dest rule",
   404  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STRICT_DNS}},
   405  			clusterMode: DefaultClusterMode,
   406  			service: &model.Service{
   407  				Hostname:   host.Name("foo.example.com"),
   408  				Ports:      servicePort,
   409  				Resolution: model.DNSLB,
   410  				Attributes: model.ServiceAttributes{
   411  					Namespace: TestServiceNamespace,
   412  					Labels:    map[string]string{"foo": "bar"},
   413  				},
   414  			},
   415  			port:      servicePort[0],
   416  			proxyView: model.ProxyViewAll,
   417  			destRule: &networking.DestinationRule{
   418  				Host:    "foo.example.com",
   419  				Subsets: []*networking.Subset{{Name: "v1"}},
   420  			},
   421  			expectedSubsetClusters: []*cluster.Cluster{{
   422  				Name:                 "outbound|8080|v1|foo.example.com",
   423  				ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STRICT_DNS},
   424  			}},
   425  		},
   426  		{
   427  			name:        "subset with labels in both",
   428  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STRICT_DNS}},
   429  			clusterMode: DefaultClusterMode,
   430  			service: &model.Service{
   431  				Hostname:   host.Name("foo.example.com"),
   432  				Ports:      servicePort,
   433  				Resolution: model.DNSLB,
   434  				Attributes: model.ServiceAttributes{
   435  					Namespace: TestServiceNamespace,
   436  					Labels:    map[string]string{"foo": "bar"},
   437  				},
   438  			},
   439  			port:      servicePort[0],
   440  			proxyView: model.ProxyViewAll,
   441  			destRule: &networking.DestinationRule{
   442  				Host: "foo.example.com",
   443  				Subsets: []*networking.Subset{{
   444  					Name:   "v1",
   445  					Labels: map[string]string{"foo": "bar"},
   446  				}},
   447  			},
   448  			expectedSubsetClusters: []*cluster.Cluster{{
   449  				Name:                 "outbound|8080|v1|foo.example.com",
   450  				ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STRICT_DNS},
   451  			}},
   452  		},
   453  		{
   454  			name:        "subset with labels in both, not matching",
   455  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STRICT_DNS}},
   456  			clusterMode: DefaultClusterMode,
   457  			service: &model.Service{
   458  				Hostname:   host.Name("foo.example.com"),
   459  				Ports:      servicePort,
   460  				Resolution: model.DNSLB,
   461  				Attributes: model.ServiceAttributes{
   462  					Namespace: TestServiceNamespace,
   463  					Labels:    map[string]string{"foo": "bar"},
   464  				},
   465  			},
   466  			port:      servicePort[0],
   467  			proxyView: model.ProxyViewAll,
   468  			destRule: &networking.DestinationRule{
   469  				Host: "foo.example.com",
   470  				Subsets: []*networking.Subset{{
   471  					Name:   "v1",
   472  					Labels: map[string]string{"foo": "not-match"},
   473  				}},
   474  			},
   475  			expectedSubsetClusters: []*cluster.Cluster{},
   476  		},
   477  		{
   478  			name:        "subset without labels in both and resolution of DNS_ROUND_ROBIN",
   479  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_LOGICAL_DNS}},
   480  			clusterMode: DefaultClusterMode,
   481  			service: &model.Service{
   482  				Hostname:   host.Name("foo.example.com"),
   483  				Ports:      servicePort,
   484  				Resolution: model.DNSRoundRobinLB,
   485  				Attributes: model.ServiceAttributes{
   486  					Namespace: TestServiceNamespace,
   487  				},
   488  			},
   489  			port:      servicePort[0],
   490  			proxyView: model.ProxyViewAll,
   491  			destRule: &networking.DestinationRule{
   492  				Host:    "foo.example.com",
   493  				Subsets: []*networking.Subset{{Name: "v1"}},
   494  			},
   495  			expectedSubsetClusters: []*cluster.Cluster{{
   496  				Name:                 "outbound|8080|v1|foo.example.com",
   497  				ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_LOGICAL_DNS},
   498  			}},
   499  		},
   500  		{
   501  			name:        "subset without labels in dest rule and a resolution of DNS_ROUND_ROBIN",
   502  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_LOGICAL_DNS}},
   503  			clusterMode: DefaultClusterMode,
   504  			service: &model.Service{
   505  				Hostname:   host.Name("foo.example.com"),
   506  				Ports:      servicePort,
   507  				Resolution: model.DNSRoundRobinLB,
   508  				Attributes: model.ServiceAttributes{
   509  					Namespace: TestServiceNamespace,
   510  					Labels:    map[string]string{"foo": "bar"},
   511  				},
   512  			},
   513  			port:      servicePort[0],
   514  			proxyView: model.ProxyViewAll,
   515  			destRule: &networking.DestinationRule{
   516  				Host:    "foo.example.com",
   517  				Subsets: []*networking.Subset{{Name: "v1"}},
   518  			},
   519  			expectedSubsetClusters: []*cluster.Cluster{{
   520  				Name:                 "outbound|8080|v1|foo.example.com",
   521  				ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_LOGICAL_DNS},
   522  			}},
   523  		},
   524  		{
   525  			name:        "subset with labels in both",
   526  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_LOGICAL_DNS}},
   527  			clusterMode: DefaultClusterMode,
   528  			service: &model.Service{
   529  				Hostname:   host.Name("foo.example.com"),
   530  				Ports:      servicePort,
   531  				Resolution: model.DNSRoundRobinLB,
   532  				Attributes: model.ServiceAttributes{
   533  					Namespace: TestServiceNamespace,
   534  					Labels:    map[string]string{"foo": "bar"},
   535  				},
   536  			},
   537  			port:      servicePort[0],
   538  			proxyView: model.ProxyViewAll,
   539  			destRule: &networking.DestinationRule{
   540  				Host: "foo.example.com",
   541  				Subsets: []*networking.Subset{{
   542  					Name:   "v1",
   543  					Labels: map[string]string{"foo": "bar"},
   544  				}},
   545  			},
   546  			expectedSubsetClusters: []*cluster.Cluster{{
   547  				Name:                 "outbound|8080|v1|foo.example.com",
   548  				ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_LOGICAL_DNS},
   549  			}},
   550  		},
   551  		{
   552  			name:        "subset with labels in both, not matching",
   553  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_LOGICAL_DNS}},
   554  			clusterMode: DefaultClusterMode,
   555  			service: &model.Service{
   556  				Hostname:   host.Name("foo.example.com"),
   557  				Ports:      servicePort,
   558  				Resolution: model.DNSRoundRobinLB,
   559  				Attributes: model.ServiceAttributes{
   560  					Namespace: TestServiceNamespace,
   561  					Labels:    map[string]string{"foo": "bar"},
   562  				},
   563  			},
   564  			port:      servicePort[0],
   565  			proxyView: model.ProxyViewAll,
   566  			destRule: &networking.DestinationRule{
   567  				Host: "foo.example.com",
   568  				Subsets: []*networking.Subset{{
   569  					Name:   "v1",
   570  					Labels: map[string]string{"foo": "not-match"},
   571  				}},
   572  			},
   573  			expectedSubsetClusters: []*cluster.Cluster{},
   574  		},
   575  		{
   576  			name:        "destination rule with tls mode SIMPLE",
   577  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
   578  			clusterMode: DefaultClusterMode,
   579  			service:     service,
   580  			port:        servicePort[0],
   581  			proxyView:   model.ProxyViewAll,
   582  			destRule: &networking.DestinationRule{
   583  				Host: "foo.default.svc.cluster.local",
   584  				TrafficPolicy: &networking.TrafficPolicy{
   585  					Tls: &networking.ClientTLSSettings{Mode: networking.ClientTLSSettings_SIMPLE},
   586  				},
   587  			},
   588  			expectedSubsetClusters: []*cluster.Cluster{},
   589  		},
   590  		{
   591  			name:        "destination rule with tls mode MUTUAL",
   592  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
   593  			clusterMode: DefaultClusterMode,
   594  			service:     service,
   595  			port:        servicePort[0],
   596  			proxyView:   model.ProxyViewAll,
   597  			destRule: &networking.DestinationRule{
   598  				Host: "foo.default.svc.cluster.local",
   599  				TrafficPolicy: &networking.TrafficPolicy{
   600  					Tls: &networking.ClientTLSSettings{Mode: networking.ClientTLSSettings_MUTUAL},
   601  				},
   602  			},
   603  			expectedSubsetClusters: []*cluster.Cluster{},
   604  		},
   605  		{
   606  			name:        "destination rule with tls mode ISTIO_MUTUAL",
   607  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
   608  			clusterMode: DefaultClusterMode,
   609  			service:     service,
   610  			port:        servicePort[0],
   611  			proxyView:   model.ProxyViewAll,
   612  			destRule: &networking.DestinationRule{
   613  				Host: "foo.default.svc.cluster.local",
   614  				TrafficPolicy: &networking.TrafficPolicy{
   615  					Tls: &networking.ClientTLSSettings{Mode: networking.ClientTLSSettings_ISTIO_MUTUAL},
   616  				},
   617  			},
   618  			expectedSubsetClusters: []*cluster.Cluster{},
   619  		},
   620  		{
   621  			name:        "port level destination rule with tls mode SIMPLE",
   622  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
   623  			clusterMode: DefaultClusterMode,
   624  			service:     service,
   625  			port:        servicePort[0],
   626  			proxyView:   model.ProxyViewAll,
   627  			destRule: &networking.DestinationRule{
   628  				Host: "foo.default.svc.cluster.local",
   629  				TrafficPolicy: &networking.TrafficPolicy{
   630  					PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{
   631  						{
   632  							Port: &networking.PortSelector{Number: uint32(servicePort[0].Port)},
   633  							Tls:  &networking.ClientTLSSettings{Mode: networking.ClientTLSSettings_SIMPLE},
   634  						},
   635  					},
   636  				},
   637  			},
   638  			expectedSubsetClusters: []*cluster.Cluster{},
   639  		},
   640  		{
   641  			name:        "port level destination rule with tls mode MUTUAL",
   642  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
   643  			clusterMode: DefaultClusterMode,
   644  			service:     service,
   645  			port:        servicePort[0],
   646  			proxyView:   model.ProxyViewAll,
   647  			destRule: &networking.DestinationRule{
   648  				Host: "foo.default.svc.cluster.local",
   649  				TrafficPolicy: &networking.TrafficPolicy{
   650  					PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{
   651  						{
   652  							Port: &networking.PortSelector{Number: uint32(servicePort[0].Port)},
   653  							Tls:  &networking.ClientTLSSettings{Mode: networking.ClientTLSSettings_MUTUAL},
   654  						},
   655  					},
   656  				},
   657  			},
   658  			expectedSubsetClusters: []*cluster.Cluster{},
   659  		},
   660  		{
   661  			name:        "port level destination rule with tls mode ISTIO_MUTUAL",
   662  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
   663  			clusterMode: DefaultClusterMode,
   664  			service:     service,
   665  			port:        servicePort[0],
   666  			proxyView:   model.ProxyViewAll,
   667  			destRule: &networking.DestinationRule{
   668  				Host: "foo.default.svc.cluster.local",
   669  				TrafficPolicy: &networking.TrafficPolicy{
   670  					PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{
   671  						{
   672  							Port: &networking.PortSelector{Number: uint32(servicePort[0].Port)},
   673  							Tls:  &networking.ClientTLSSettings{Mode: networking.ClientTLSSettings_ISTIO_MUTUAL},
   674  						},
   675  					},
   676  				},
   677  			},
   678  			expectedSubsetClusters: []*cluster.Cluster{},
   679  		},
   680  		{
   681  			name:        "subset destination rule with tls mode SIMPLE",
   682  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
   683  			clusterMode: DefaultClusterMode,
   684  			service:     service,
   685  			port:        servicePort[0],
   686  			proxyView:   model.ProxyViewAll,
   687  			destRule: &networking.DestinationRule{
   688  				Host: "foo.default.svc.cluster.local",
   689  				Subsets: []*networking.Subset{
   690  					{
   691  						Name: "v1",
   692  						TrafficPolicy: &networking.TrafficPolicy{
   693  							Tls: &networking.ClientTLSSettings{Mode: networking.ClientTLSSettings_SIMPLE},
   694  						},
   695  					},
   696  				},
   697  			},
   698  			expectedSubsetClusters: []*cluster.Cluster{{
   699  				Name:                 "outbound|8080|v1|foo.default.svc.cluster.local",
   700  				ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS},
   701  				EdsClusterConfig:     &cluster.Cluster_EdsClusterConfig{ServiceName: "outbound|8080|v1|foo.default.svc.cluster.local"},
   702  			}},
   703  		},
   704  		{
   705  			name:        "subset destination rule with tls mode MUTUAL",
   706  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
   707  			clusterMode: DefaultClusterMode,
   708  			service:     service,
   709  			port:        servicePort[0],
   710  			proxyView:   model.ProxyViewAll,
   711  			destRule: &networking.DestinationRule{
   712  				Host: "foo.default.svc.cluster.local",
   713  				Subsets: []*networking.Subset{
   714  					{
   715  						Name: "v1",
   716  						TrafficPolicy: &networking.TrafficPolicy{
   717  							Tls: &networking.ClientTLSSettings{Mode: networking.ClientTLSSettings_MUTUAL},
   718  						},
   719  					},
   720  				},
   721  			},
   722  			expectedSubsetClusters: []*cluster.Cluster{{
   723  				Name:                 "outbound|8080|v1|foo.default.svc.cluster.local",
   724  				ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS},
   725  				EdsClusterConfig:     &cluster.Cluster_EdsClusterConfig{ServiceName: "outbound|8080|v1|foo.default.svc.cluster.local"},
   726  			}},
   727  		},
   728  		{
   729  			name:        "subset destination rule with tls mode MUTUAL",
   730  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
   731  			clusterMode: DefaultClusterMode,
   732  			service:     service,
   733  			port:        servicePort[0],
   734  			proxyView:   model.ProxyViewAll,
   735  			destRule: &networking.DestinationRule{
   736  				Host: "foo.default.svc.cluster.local",
   737  				Subsets: []*networking.Subset{
   738  					{
   739  						Name: "v1",
   740  						TrafficPolicy: &networking.TrafficPolicy{
   741  							Tls: &networking.ClientTLSSettings{Mode: networking.ClientTLSSettings_ISTIO_MUTUAL},
   742  						},
   743  					},
   744  				},
   745  			},
   746  			expectedSubsetClusters: []*cluster.Cluster{{
   747  				Name:                 "outbound|8080|v1|foo.default.svc.cluster.local",
   748  				ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS},
   749  				EdsClusterConfig:     &cluster.Cluster_EdsClusterConfig{ServiceName: "outbound|8080|v1|foo.default.svc.cluster.local"},
   750  			}},
   751  		},
   752  	}
   753  
   754  	for _, tt := range cases {
   755  		t.Run(tt.name, func(t *testing.T) {
   756  			instances := []*model.ServiceInstance{
   757  				{
   758  					Service:     tt.service,
   759  					ServicePort: tt.port,
   760  					Endpoint: &model.IstioEndpoint{
   761  						ServicePortName: tt.port.Name,
   762  						Address:         "192.168.1.1",
   763  						EndpointPort:    10001,
   764  						Locality: model.Locality{
   765  							ClusterID: "",
   766  							Label:     "region1/zone1/subzone1",
   767  						},
   768  						Labels:  tt.service.Attributes.Labels,
   769  						TLSMode: model.IstioMutualTLSModeLabel,
   770  					},
   771  				},
   772  			}
   773  
   774  			var cfg *config.Config
   775  			if tt.destRule != nil {
   776  				cfg = &config.Config{
   777  					Meta: config.Meta{
   778  						GroupVersionKind: gvk.DestinationRule,
   779  						Name:             "acme",
   780  						Namespace:        "default",
   781  					},
   782  					Spec: tt.destRule,
   783  				}
   784  			}
   785  			cg := NewConfigGenTest(t, TestOptions{
   786  				Instances:      instances,
   787  				ConfigPointers: []*config.Config{cfg},
   788  				Services:       []*model.Service{tt.service},
   789  				MeshConfig:     tt.meshConfig,
   790  			})
   791  			proxy := cg.SetupProxy(nil)
   792  			cb := NewClusterBuilder(proxy, &model.PushRequest{Push: cg.PushContext()}, nil)
   793  
   794  			tt.cluster.CommonLbConfig = &cluster.Cluster_CommonLbConfig{}
   795  
   796  			ec := newClusterWrapper(tt.cluster)
   797  			// Set cluster wrapping with HTTP2 options if port protocol is HTTP2
   798  			if tt.port.Protocol == protocol.HTTP2 {
   799  				setH2Options(ec)
   800  			}
   801  			destRule := proxy.SidecarScope.DestinationRule(model.TrafficDirectionOutbound, proxy, tt.service.Hostname)
   802  			eb := endpoints.NewCDSEndpointBuilder(proxy, cb.req.Push, tt.cluster.Name,
   803  				model.TrafficDirectionOutbound, "", tt.service.Hostname, tt.port.Port,
   804  				tt.service, destRule)
   805  			subsetClusters := cb.applyDestinationRule(ec, tt.clusterMode, tt.service, tt.port, eb, destRule.GetRule(), nil)
   806  			if len(subsetClusters) != len(tt.expectedSubsetClusters) {
   807  				t.Fatalf("Unexpected subset clusters want %v, got %v. keys=%v",
   808  					len(tt.expectedSubsetClusters), len(subsetClusters), xdstest.MapKeys(xdstest.ExtractClusters(subsetClusters)))
   809  			}
   810  			if len(tt.expectedSubsetClusters) > 0 {
   811  				compareClusters(t, tt.expectedSubsetClusters[0], subsetClusters[0])
   812  			}
   813  			// Validate that use client protocol configures cluster correctly.
   814  			if tt.destRule != nil && tt.destRule.TrafficPolicy != nil && tt.destRule.TrafficPolicy.GetConnectionPool().GetHttp().GetUseClientProtocol() {
   815  				if ec.httpProtocolOptions == nil {
   816  					t.Errorf("Expected cluster %s to have http protocol options but not found", tt.cluster.Name)
   817  				}
   818  				if ec.httpProtocolOptions.UpstreamProtocolOptions == nil &&
   819  					ec.httpProtocolOptions.GetUseDownstreamProtocolConfig() == nil {
   820  					t.Errorf("Expected cluster %s to have downstream protocol options but not found", tt.cluster.Name)
   821  				}
   822  			}
   823  
   824  			// Validate that max requests per connection configures cluster correctly.
   825  			if tt.destRule != nil && tt.destRule.TrafficPolicy != nil && tt.destRule.TrafficPolicy.GetConnectionPool().GetHttp().GetMaxRequestsPerConnection() > 0 {
   826  				if ec.httpProtocolOptions == nil {
   827  					t.Errorf("Expected cluster %s to have http protocol options but not found", tt.cluster.Name)
   828  				}
   829  				if ec.httpProtocolOptions.CommonHttpProtocolOptions == nil {
   830  					t.Errorf("Expected cluster %s to have common http protocol options but not found", tt.cluster.Name)
   831  				}
   832  				if ec.httpProtocolOptions.CommonHttpProtocolOptions.MaxRequestsPerConnection.GetValue() !=
   833  					uint32(tt.destRule.TrafficPolicy.GetConnectionPool().GetHttp().MaxRequestsPerConnection) {
   834  					t.Errorf("Unexpected max_requests_per_connection found")
   835  				}
   836  			}
   837  
   838  			if tt.destRule.GetTrafficPolicy().GetConnectionPool().GetHttp().GetMaxConcurrentStreams() > 0 {
   839  				if ec.httpProtocolOptions == nil {
   840  					t.Errorf("Expected cluster %s to have http protocol options but not found", tt.cluster.Name)
   841  				}
   842  				if ec.httpProtocolOptions.GetExplicitHttpConfig() == nil {
   843  					t.Errorf("Expected cluster %s to have explicit http config but not found", tt.cluster.Name)
   844  				}
   845  				if ec.httpProtocolOptions.GetExplicitHttpConfig().GetHttp2ProtocolOptions() == nil {
   846  					t.Errorf("Expected cluster %s to have HTTP2 protocol options but not found", tt.cluster.Name)
   847  				}
   848  				if ec.httpProtocolOptions.GetExplicitHttpConfig().GetHttp2ProtocolOptions().GetMaxConcurrentStreams().GetValue() !=
   849  					uint32(tt.destRule.TrafficPolicy.GetConnectionPool().GetHttp().MaxConcurrentStreams) {
   850  					t.Errorf("Unexpected max_concurrent_streams found")
   851  				}
   852  			}
   853  
   854  			// Validate that alpn_override is correctly configured on cluster given a TLS mode.
   855  			if tt.destRule.GetTrafficPolicy().GetTls() != nil {
   856  				verifyALPNOverride(t, tt.cluster.Metadata, tt.destRule.TrafficPolicy.Tls.Mode)
   857  			}
   858  			if len(tt.destRule.GetSubsets()) > 0 {
   859  				for _, c := range subsetClusters {
   860  					var subsetName string
   861  					if tt.clusterMode == DefaultClusterMode {
   862  						subsetName = strings.Split(c.Name, "|")[2]
   863  					} else {
   864  						subsetName = strings.Split(c.Name, ".")[2]
   865  					}
   866  					for _, subset := range tt.destRule.Subsets {
   867  						if subset.Name == subsetName {
   868  							if subset.GetTrafficPolicy().GetTls() != nil {
   869  								verifyALPNOverride(t, c.Metadata, subset.TrafficPolicy.Tls.Mode)
   870  							}
   871  						}
   872  					}
   873  				}
   874  			}
   875  
   876  			// Validate that ORIGINAL_DST cluster does not have load assignments
   877  			for _, subset := range subsetClusters {
   878  				if subset.GetType() == cluster.Cluster_ORIGINAL_DST && subset.GetLoadAssignment() != nil {
   879  					t.Errorf("Passthrough subsets should not have load assignments")
   880  				}
   881  			}
   882  			if ec.cluster.GetType() == cluster.Cluster_ORIGINAL_DST && ec.cluster.GetLoadAssignment() != nil {
   883  				t.Errorf("Passthrough should not have load assignments")
   884  			}
   885  		})
   886  	}
   887  }
   888  
   889  func compareClusters(t *testing.T, ec *cluster.Cluster, gc *cluster.Cluster) {
   890  	// TODO(ramaraochavali): Expand the comparison to more fields.
   891  	t.Helper()
   892  	if ec.Name != gc.Name {
   893  		t.Errorf("Unexpected cluster name want %s, got %s", ec.Name, gc.Name)
   894  	}
   895  	if ec.GetType() != gc.GetType() {
   896  		t.Errorf("Unexpected cluster discovery type want %v, got %v", ec.GetType(), gc.GetType())
   897  	}
   898  	if ec.GetType() == cluster.Cluster_EDS && ec.EdsClusterConfig.ServiceName != gc.EdsClusterConfig.ServiceName {
   899  		t.Errorf("Unexpected service name in EDS config want %v, got %v", ec.EdsClusterConfig.ServiceName, gc.EdsClusterConfig.ServiceName)
   900  	}
   901  	if ec.CircuitBreakers != nil {
   902  		if ec.CircuitBreakers.Thresholds[0].MaxRetries.Value != gc.CircuitBreakers.Thresholds[0].MaxRetries.Value {
   903  			t.Errorf("Unexpected circuit breaker thresholds want %v, got %v", ec.CircuitBreakers.Thresholds[0].MaxRetries, gc.CircuitBreakers.Thresholds[0].MaxRetries)
   904  		}
   905  	}
   906  	if ec.AltStatName != "" {
   907  		if ec.AltStatName != gc.AltStatName {
   908  			t.Errorf("Unexpected alt stat name want %s, got %s", ec.AltStatName, gc.AltStatName)
   909  		}
   910  	}
   911  }
   912  
   913  func verifyALPNOverride(t *testing.T, md *core.Metadata, tlsMode networking.ClientTLSSettings_TLSmode) {
   914  	istio, ok := md.FilterMetadata[util.IstioMetadataKey]
   915  	if tlsMode == networking.ClientTLSSettings_SIMPLE || tlsMode == networking.ClientTLSSettings_MUTUAL {
   916  		if !ok {
   917  			t.Errorf("Istio metadata not found")
   918  		}
   919  		alpnOverride, found := istio.Fields[util.AlpnOverrideMetadataKey]
   920  		if found {
   921  			if alpnOverride.GetStringValue() != "false" {
   922  				t.Errorf("alpn_override:%s tlsMode:%s, should be false for either TLS mode SIMPLE or MUTUAL", alpnOverride, tlsMode)
   923  			}
   924  		} else {
   925  			t.Errorf("alpn_override metadata should be written for either TLS mode SIMPLE or MUTUAL")
   926  		}
   927  	} else if ok {
   928  		alpnOverride, found := istio.Fields[util.AlpnOverrideMetadataKey]
   929  		if found {
   930  			t.Errorf("alpn_override:%s tlsMode:%s, alpn_override metadata should not be written if TLS mode is neither SIMPLE nor MUTUAL",
   931  				alpnOverride.GetStringValue(), tlsMode)
   932  		}
   933  	}
   934  }
   935  
   936  func TestApplyEdsConfig(t *testing.T) {
   937  	cases := []struct {
   938  		name      string
   939  		cluster   *cluster.Cluster
   940  		edsConfig *cluster.Cluster_EdsClusterConfig
   941  	}{
   942  		{
   943  			name:      "non eds type of cluster",
   944  			cluster:   &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STRICT_DNS}},
   945  			edsConfig: nil,
   946  		},
   947  		{
   948  			name:    "eds type of cluster",
   949  			cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
   950  			edsConfig: &cluster.Cluster_EdsClusterConfig{
   951  				ServiceName: "foo",
   952  				EdsConfig: &core.ConfigSource{
   953  					ConfigSourceSpecifier: &core.ConfigSource_Ads{
   954  						Ads: &core.AggregatedConfigSource{},
   955  					},
   956  					InitialFetchTimeout: durationpb.New(0),
   957  					ResourceApiVersion:  core.ApiVersion_V3,
   958  				},
   959  			},
   960  		},
   961  	}
   962  
   963  	for _, tt := range cases {
   964  		t.Run(tt.name, func(t *testing.T) {
   965  			maybeApplyEdsConfig(tt.cluster)
   966  			if !reflect.DeepEqual(tt.cluster.EdsClusterConfig, tt.edsConfig) {
   967  				t.Errorf("Unexpected Eds config in cluster. want %v, got %v", tt.edsConfig, tt.cluster.EdsClusterConfig)
   968  			}
   969  		})
   970  	}
   971  }
   972  
   973  func TestBuildDefaultCluster(t *testing.T) {
   974  	servicePort := &model.Port{
   975  		Name:     "default",
   976  		Port:     8080,
   977  		Protocol: protocol.HTTP,
   978  	}
   979  
   980  	cases := []struct {
   981  		name            string
   982  		clusterName     string
   983  		discovery       cluster.Cluster_DiscoveryType
   984  		endpoints       []*endpoint.LocalityLbEndpoints
   985  		direction       model.TrafficDirection
   986  		external        bool
   987  		expectedCluster *cluster.Cluster
   988  	}{
   989  		{
   990  			name:        "default EDS cluster",
   991  			clusterName: "foo",
   992  			discovery:   cluster.Cluster_EDS,
   993  			endpoints:   nil,
   994  			direction:   model.TrafficDirectionOutbound,
   995  			external:    false,
   996  			expectedCluster: &cluster.Cluster{
   997  				Name:                 "foo",
   998  				ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS},
   999  				CommonLbConfig:       &cluster.Cluster_CommonLbConfig{},
  1000  				ConnectTimeout:       &durationpb.Duration{Seconds: 10, Nanos: 1},
  1001  				CircuitBreakers: &cluster.CircuitBreakers{
  1002  					Thresholds: []*cluster.CircuitBreakers_Thresholds{getDefaultCircuitBreakerThresholds()},
  1003  				},
  1004  				Filters:  []*cluster.Filter{xdsfilters.TCPClusterMx},
  1005  				LbPolicy: defaultLBAlgorithm(),
  1006  				Metadata: &core.Metadata{
  1007  					FilterMetadata: map[string]*structpb.Struct{
  1008  						util.IstioMetadataKey: {
  1009  							Fields: map[string]*structpb.Value{
  1010  								"services": {Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{Values: []*structpb.Value{
  1011  									{Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{
  1012  										"host": {
  1013  											Kind: &structpb.Value_StringValue{
  1014  												StringValue: "host",
  1015  											},
  1016  										},
  1017  										"name": {
  1018  											Kind: &structpb.Value_StringValue{
  1019  												StringValue: "svc",
  1020  											},
  1021  										},
  1022  										"namespace": {
  1023  											Kind: &structpb.Value_StringValue{
  1024  												StringValue: "default",
  1025  											},
  1026  										},
  1027  									}}}},
  1028  								}}}},
  1029  							},
  1030  						},
  1031  					},
  1032  				},
  1033  				EdsClusterConfig: &cluster.Cluster_EdsClusterConfig{
  1034  					ServiceName: "foo",
  1035  					EdsConfig: &core.ConfigSource{
  1036  						ConfigSourceSpecifier: &core.ConfigSource_Ads{
  1037  							Ads: &core.AggregatedConfigSource{},
  1038  						},
  1039  						InitialFetchTimeout: durationpb.New(0),
  1040  						ResourceApiVersion:  core.ApiVersion_V3,
  1041  					},
  1042  				},
  1043  			},
  1044  		},
  1045  		{
  1046  			name:            "static cluster with no endpoints",
  1047  			clusterName:     "foo",
  1048  			discovery:       cluster.Cluster_STATIC,
  1049  			endpoints:       nil,
  1050  			direction:       model.TrafficDirectionOutbound,
  1051  			external:        false,
  1052  			expectedCluster: nil,
  1053  		},
  1054  		{
  1055  			name:            "strict DNS cluster with no endpoints",
  1056  			clusterName:     "foo",
  1057  			discovery:       cluster.Cluster_STRICT_DNS,
  1058  			endpoints:       nil,
  1059  			direction:       model.TrafficDirectionOutbound,
  1060  			external:        false,
  1061  			expectedCluster: nil,
  1062  		},
  1063  		{
  1064  			name:        "static cluster with endpoints",
  1065  			clusterName: "foo",
  1066  			discovery:   cluster.Cluster_STATIC,
  1067  			endpoints: []*endpoint.LocalityLbEndpoints{
  1068  				{
  1069  					Locality: &core.Locality{
  1070  						Region:  "region1",
  1071  						Zone:    "zone1",
  1072  						SubZone: "subzone1",
  1073  					},
  1074  					LbEndpoints: []*endpoint.LbEndpoint{},
  1075  					LoadBalancingWeight: &wrappers.UInt32Value{
  1076  						Value: 1,
  1077  					},
  1078  					Priority: 0,
  1079  				},
  1080  			},
  1081  			direction: model.TrafficDirectionOutbound,
  1082  			external:  false,
  1083  			expectedCluster: &cluster.Cluster{
  1084  				Name:                 "foo",
  1085  				ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STATIC},
  1086  				CommonLbConfig:       &cluster.Cluster_CommonLbConfig{},
  1087  				ConnectTimeout:       &durationpb.Duration{Seconds: 10, Nanos: 1},
  1088  				Filters:              []*cluster.Filter{xdsfilters.TCPClusterMx},
  1089  				LbPolicy:             defaultLBAlgorithm(),
  1090  				LoadAssignment: &endpoint.ClusterLoadAssignment{
  1091  					ClusterName: "foo",
  1092  					Endpoints: []*endpoint.LocalityLbEndpoints{
  1093  						{
  1094  							Locality: &core.Locality{
  1095  								Region:  "region1",
  1096  								Zone:    "zone1",
  1097  								SubZone: "subzone1",
  1098  							},
  1099  							LbEndpoints: []*endpoint.LbEndpoint{},
  1100  							LoadBalancingWeight: &wrappers.UInt32Value{
  1101  								Value: 1,
  1102  							},
  1103  							Priority: 0,
  1104  						},
  1105  					},
  1106  				},
  1107  				CircuitBreakers: &cluster.CircuitBreakers{
  1108  					Thresholds: []*cluster.CircuitBreakers_Thresholds{getDefaultCircuitBreakerThresholds()},
  1109  				},
  1110  				Metadata: &core.Metadata{
  1111  					FilterMetadata: map[string]*structpb.Struct{
  1112  						util.IstioMetadataKey: {Fields: map[string]*structpb.Value{
  1113  							"services": {Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{Values: []*structpb.Value{
  1114  								{Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{
  1115  									"host": {
  1116  										Kind: &structpb.Value_StringValue{
  1117  											StringValue: "host",
  1118  										},
  1119  									},
  1120  									"name": {
  1121  										Kind: &structpb.Value_StringValue{
  1122  											StringValue: "svc",
  1123  										},
  1124  									},
  1125  									"namespace": {
  1126  										Kind: &structpb.Value_StringValue{
  1127  											StringValue: "default",
  1128  										},
  1129  									},
  1130  								}}}},
  1131  							}}}},
  1132  						}},
  1133  					},
  1134  				},
  1135  			},
  1136  		},
  1137  	}
  1138  
  1139  	for _, tt := range cases {
  1140  		t.Run(tt.name, func(t *testing.T) {
  1141  			mesh := testMesh()
  1142  			cg := NewConfigGenTest(t, TestOptions{MeshConfig: mesh})
  1143  			proxy := cg.SetupProxy(nil)
  1144  			cb := NewClusterBuilder(proxy, &model.PushRequest{Push: cg.PushContext()}, nil)
  1145  			service := &model.Service{
  1146  				Ports: model.PortList{
  1147  					servicePort,
  1148  				},
  1149  				Hostname:     "host",
  1150  				MeshExternal: false,
  1151  				Attributes:   model.ServiceAttributes{Name: "svc", Namespace: "default"},
  1152  			}
  1153  			defaultCluster := cb.buildCluster(tt.clusterName, tt.discovery, tt.endpoints, tt.direction, servicePort, service, nil, "")
  1154  			eb := endpoints.NewCDSEndpointBuilder(proxy, cb.req.Push, tt.clusterName,
  1155  				tt.direction, "", service.Hostname, servicePort.Port,
  1156  				service, nil)
  1157  			if defaultCluster != nil {
  1158  				_ = cb.applyDestinationRule(defaultCluster, DefaultClusterMode, service, servicePort, eb, nil, nil)
  1159  			}
  1160  
  1161  			if diff := cmp.Diff(defaultCluster.build(), tt.expectedCluster, protocmp.Transform()); diff != "" {
  1162  				t.Errorf("Unexpected default cluster, diff: %v", diff)
  1163  			}
  1164  		})
  1165  	}
  1166  }
  1167  
  1168  func TestClusterDnsLookupFamily(t *testing.T) {
  1169  	servicePort := &model.Port{
  1170  		Name:     "default",
  1171  		Port:     8080,
  1172  		Protocol: protocol.HTTP,
  1173  	}
  1174  
  1175  	endpoints := []*endpoint.LocalityLbEndpoints{
  1176  		{
  1177  			Locality: &core.Locality{
  1178  				Region:  "region1",
  1179  				Zone:    "zone1",
  1180  				SubZone: "subzone1",
  1181  			},
  1182  			LbEndpoints: []*endpoint.LbEndpoint{},
  1183  			LoadBalancingWeight: &wrappers.UInt32Value{
  1184  				Value: 1,
  1185  			},
  1186  			Priority: 0,
  1187  		},
  1188  	}
  1189  
  1190  	cases := []struct {
  1191  		name           string
  1192  		clusterName    string
  1193  		discovery      cluster.Cluster_DiscoveryType
  1194  		proxy          *model.Proxy
  1195  		dualStack      bool
  1196  		expectedFamily cluster.Cluster_DnsLookupFamily
  1197  	}{
  1198  		{
  1199  			name:           "all ipv4, dual stack disabled",
  1200  			clusterName:    "foo",
  1201  			discovery:      cluster.Cluster_STRICT_DNS,
  1202  			proxy:          getProxy(),
  1203  			dualStack:      false,
  1204  			expectedFamily: cluster.Cluster_V4_ONLY,
  1205  		},
  1206  		{
  1207  			name:           "all ipv4, dual stack enabled",
  1208  			clusterName:    "foo",
  1209  			discovery:      cluster.Cluster_STRICT_DNS,
  1210  			proxy:          getProxy(),
  1211  			dualStack:      true,
  1212  			expectedFamily: cluster.Cluster_V4_ONLY,
  1213  		},
  1214  		{
  1215  			name:           "all ipv6, dual stack disabled",
  1216  			clusterName:    "foo",
  1217  			discovery:      cluster.Cluster_STRICT_DNS,
  1218  			proxy:          getIPv6Proxy(),
  1219  			dualStack:      false,
  1220  			expectedFamily: cluster.Cluster_V6_ONLY,
  1221  		},
  1222  		{
  1223  			name:           "all ipv6, dual stack enabled",
  1224  			clusterName:    "foo",
  1225  			discovery:      cluster.Cluster_STRICT_DNS,
  1226  			proxy:          getIPv6Proxy(),
  1227  			dualStack:      true,
  1228  			expectedFamily: cluster.Cluster_V6_ONLY,
  1229  		},
  1230  		{
  1231  			name:           "ipv4 and ipv6, dual stack disabled",
  1232  			clusterName:    "foo",
  1233  			discovery:      cluster.Cluster_STRICT_DNS,
  1234  			proxy:          &dualStackProxy,
  1235  			dualStack:      false,
  1236  			expectedFamily: cluster.Cluster_V4_ONLY,
  1237  		},
  1238  		{
  1239  			name:           "ipv4 and ipv6, dual stack enabled",
  1240  			clusterName:    "foo",
  1241  			discovery:      cluster.Cluster_STRICT_DNS,
  1242  			proxy:          &dualStackProxy,
  1243  			dualStack:      true,
  1244  			expectedFamily: cluster.Cluster_ALL,
  1245  		},
  1246  	}
  1247  	for _, tt := range cases {
  1248  		t.Run(tt.name, func(t *testing.T) {
  1249  			test.SetForTest(t, &features.EnableDualStack, tt.dualStack)
  1250  			mesh := testMesh()
  1251  			cg := NewConfigGenTest(t, TestOptions{MeshConfig: mesh})
  1252  			cb := NewClusterBuilder(cg.SetupProxy(tt.proxy), &model.PushRequest{Push: cg.PushContext()}, nil)
  1253  			service := &model.Service{
  1254  				Ports: model.PortList{
  1255  					servicePort,
  1256  				},
  1257  				Hostname:     "host",
  1258  				MeshExternal: false,
  1259  				Attributes:   model.ServiceAttributes{Name: "svc", Namespace: "default"},
  1260  			}
  1261  			defaultCluster := cb.buildCluster(tt.clusterName, tt.discovery, endpoints, model.TrafficDirectionOutbound, servicePort, service, nil, "")
  1262  
  1263  			if defaultCluster.build().DnsLookupFamily != tt.expectedFamily {
  1264  				t.Errorf("Unexpected DnsLookupFamily, got: %v, want: %v", defaultCluster.build().DnsLookupFamily, tt.expectedFamily)
  1265  			}
  1266  		})
  1267  	}
  1268  }
  1269  
  1270  func TestBuildLocalityLbEndpoints(t *testing.T) {
  1271  	proxy := &model.Proxy{
  1272  		Metadata: &model.NodeMetadata{
  1273  			ClusterID:            "cluster-1",
  1274  			RequestedNetworkView: []string{"nw-0", "nw-1"},
  1275  		},
  1276  	}
  1277  	servicePort := &model.Port{
  1278  		Name:     "default",
  1279  		Port:     8080,
  1280  		Protocol: protocol.HTTP,
  1281  	}
  1282  	service := &model.Service{
  1283  		Hostname: host.Name("*.example.org"),
  1284  		Ports:    model.PortList{servicePort},
  1285  		Attributes: model.ServiceAttributes{
  1286  			Name:      "TestService",
  1287  			Namespace: "test-ns",
  1288  		},
  1289  	}
  1290  
  1291  	buildMetadata := func(networkID network.ID, tlsMode, workloadname, namespace string,
  1292  		clusterID istiocluster.ID, lbls labels.Instance,
  1293  	) *core.Metadata {
  1294  		newmeta := &core.Metadata{}
  1295  		util.AppendLbEndpointMetadata(&model.EndpointMetadata{
  1296  			Network:      networkID,
  1297  			TLSMode:      tlsMode,
  1298  			WorkloadName: workloadname,
  1299  			Namespace:    namespace,
  1300  			ClusterID:    clusterID,
  1301  			Labels:       lbls,
  1302  		}, newmeta)
  1303  		return newmeta
  1304  	}
  1305  
  1306  	cases := []struct {
  1307  		name      string
  1308  		mesh      *meshconfig.MeshConfig
  1309  		labels    labels.Instance
  1310  		instances []*model.ServiceInstance
  1311  		expected  []*endpoint.LocalityLbEndpoints
  1312  	}{
  1313  		{
  1314  			name: "basics",
  1315  			mesh: testMesh(),
  1316  			instances: []*model.ServiceInstance{
  1317  				{
  1318  					Service:     service,
  1319  					ServicePort: servicePort,
  1320  					Endpoint: &model.IstioEndpoint{
  1321  						Address:      "192.168.1.1",
  1322  						EndpointPort: 10001,
  1323  						WorkloadName: "workload-1",
  1324  						Namespace:    "namespace-1",
  1325  						Locality: model.Locality{
  1326  							ClusterID: "cluster-1",
  1327  							Label:     "region1/zone1/subzone1",
  1328  						},
  1329  						LbWeight: 30,
  1330  						Network:  "nw-0",
  1331  					},
  1332  				},
  1333  				{
  1334  					Service:     service,
  1335  					ServicePort: servicePort,
  1336  					Endpoint: &model.IstioEndpoint{
  1337  						Address:      "192.168.1.2",
  1338  						EndpointPort: 10001,
  1339  						WorkloadName: "workload-2",
  1340  						Namespace:    "namespace-2",
  1341  						Locality: model.Locality{
  1342  							ClusterID: "cluster-2",
  1343  							Label:     "region1/zone1/subzone1",
  1344  						},
  1345  						LbWeight: 30,
  1346  						Network:  "nw-1",
  1347  					},
  1348  				},
  1349  				{
  1350  					Service:     service,
  1351  					ServicePort: servicePort,
  1352  					Endpoint: &model.IstioEndpoint{
  1353  						Address:      "192.168.1.3",
  1354  						EndpointPort: 10001,
  1355  						WorkloadName: "workload-3",
  1356  						Namespace:    "namespace-3",
  1357  						Locality: model.Locality{
  1358  							ClusterID: "cluster-3",
  1359  							Label:     "region2/zone1/subzone1",
  1360  						},
  1361  						LbWeight: 40,
  1362  						Network:  "",
  1363  					},
  1364  				},
  1365  				{
  1366  					Service:     service,
  1367  					ServicePort: servicePort,
  1368  					Endpoint: &model.IstioEndpoint{
  1369  						Address:      "192.168.1.4",
  1370  						EndpointPort: 10001,
  1371  						WorkloadName: "workload-1",
  1372  						Namespace:    "namespace-1",
  1373  						Locality: model.Locality{
  1374  							ClusterID: "cluster-1",
  1375  							Label:     "region1/zone1/subzone1",
  1376  						},
  1377  						LbWeight: 30,
  1378  						Network:  "filtered-out",
  1379  					},
  1380  				},
  1381  			},
  1382  			expected: []*endpoint.LocalityLbEndpoints{
  1383  				{
  1384  					Locality: &core.Locality{
  1385  						Region:  "region1",
  1386  						Zone:    "zone1",
  1387  						SubZone: "subzone1",
  1388  					},
  1389  					LoadBalancingWeight: &wrappers.UInt32Value{
  1390  						Value: 60,
  1391  					},
  1392  					LbEndpoints: []*endpoint.LbEndpoint{
  1393  						{
  1394  							HostIdentifier: &endpoint.LbEndpoint_Endpoint{
  1395  								Endpoint: &endpoint.Endpoint{
  1396  									Address: &core.Address{
  1397  										Address: &core.Address_SocketAddress{
  1398  											SocketAddress: &core.SocketAddress{
  1399  												Address: "192.168.1.1",
  1400  												PortSpecifier: &core.SocketAddress_PortValue{
  1401  													PortValue: 10001,
  1402  												},
  1403  											},
  1404  										},
  1405  									},
  1406  								},
  1407  							},
  1408  							Metadata: buildMetadata("nw-0", "", "workload-1", "namespace-1", "cluster-1", map[string]string{}),
  1409  							LoadBalancingWeight: &wrappers.UInt32Value{
  1410  								Value: 30,
  1411  							},
  1412  						},
  1413  						{
  1414  							HostIdentifier: &endpoint.LbEndpoint_Endpoint{
  1415  								Endpoint: &endpoint.Endpoint{
  1416  									Address: &core.Address{
  1417  										Address: &core.Address_SocketAddress{
  1418  											SocketAddress: &core.SocketAddress{
  1419  												Address: "192.168.1.2",
  1420  												PortSpecifier: &core.SocketAddress_PortValue{
  1421  													PortValue: 10001,
  1422  												},
  1423  											},
  1424  										},
  1425  									},
  1426  								},
  1427  							},
  1428  							Metadata: buildMetadata("nw-1", "", "workload-2", "namespace-2", "cluster-2", map[string]string{}),
  1429  							LoadBalancingWeight: &wrappers.UInt32Value{
  1430  								Value: 30,
  1431  							},
  1432  						},
  1433  					},
  1434  				},
  1435  				{
  1436  					Locality: &core.Locality{
  1437  						Region:  "region2",
  1438  						Zone:    "zone1",
  1439  						SubZone: "subzone1",
  1440  					},
  1441  					LoadBalancingWeight: &wrappers.UInt32Value{
  1442  						Value: 40,
  1443  					},
  1444  					LbEndpoints: []*endpoint.LbEndpoint{
  1445  						{
  1446  							HostIdentifier: &endpoint.LbEndpoint_Endpoint{
  1447  								Endpoint: &endpoint.Endpoint{
  1448  									Address: &core.Address{
  1449  										Address: &core.Address_SocketAddress{
  1450  											SocketAddress: &core.SocketAddress{
  1451  												Address: "192.168.1.3",
  1452  												PortSpecifier: &core.SocketAddress_PortValue{
  1453  													PortValue: 10001,
  1454  												},
  1455  											},
  1456  										},
  1457  									},
  1458  								},
  1459  							},
  1460  							Metadata: buildMetadata("", "", "workload-3", "namespace-3", "cluster-3", map[string]string{}),
  1461  							LoadBalancingWeight: &wrappers.UInt32Value{
  1462  								Value: 40,
  1463  							},
  1464  						},
  1465  					},
  1466  				},
  1467  			},
  1468  		},
  1469  		{
  1470  			name: "cluster local",
  1471  			mesh: withClusterLocalHosts(testMesh(), "*.example.org"),
  1472  			instances: []*model.ServiceInstance{
  1473  				{
  1474  					Service:     service,
  1475  					ServicePort: servicePort,
  1476  					Endpoint: &model.IstioEndpoint{
  1477  						Address:      "192.168.1.1",
  1478  						EndpointPort: 10001,
  1479  						Locality: model.Locality{
  1480  							ClusterID: "cluster-1",
  1481  							Label:     "region1/zone1/subzone1",
  1482  						},
  1483  						LbWeight: 30,
  1484  					},
  1485  				},
  1486  				{
  1487  					Service:     service,
  1488  					ServicePort: servicePort,
  1489  					Endpoint: &model.IstioEndpoint{
  1490  						Address:      "192.168.1.2",
  1491  						EndpointPort: 10001,
  1492  						Locality: model.Locality{
  1493  							ClusterID: "cluster-2",
  1494  							Label:     "region1/zone1/subzone1",
  1495  						},
  1496  						LbWeight: 30,
  1497  					},
  1498  				},
  1499  			},
  1500  			expected: []*endpoint.LocalityLbEndpoints{
  1501  				{
  1502  					Locality: &core.Locality{
  1503  						Region:  "region1",
  1504  						Zone:    "zone1",
  1505  						SubZone: "subzone1",
  1506  					},
  1507  					LoadBalancingWeight: &wrappers.UInt32Value{
  1508  						Value: 30,
  1509  					},
  1510  					LbEndpoints: []*endpoint.LbEndpoint{
  1511  						{
  1512  							HostIdentifier: &endpoint.LbEndpoint_Endpoint{
  1513  								Endpoint: &endpoint.Endpoint{
  1514  									Address: &core.Address{
  1515  										Address: &core.Address_SocketAddress{
  1516  											SocketAddress: &core.SocketAddress{
  1517  												Address: "192.168.1.1",
  1518  												PortSpecifier: &core.SocketAddress_PortValue{
  1519  													PortValue: 10001,
  1520  												},
  1521  											},
  1522  										},
  1523  									},
  1524  								},
  1525  							},
  1526  							Metadata: buildMetadata("", "", "", "", "cluster-1", map[string]string{}),
  1527  							LoadBalancingWeight: &wrappers.UInt32Value{
  1528  								Value: 30,
  1529  							},
  1530  						},
  1531  					},
  1532  				},
  1533  			},
  1534  		},
  1535  		{
  1536  			name:   "subset cluster endpoints with labels",
  1537  			mesh:   testMesh(),
  1538  			labels: labels.Instance{"version": "v1"},
  1539  			instances: []*model.ServiceInstance{
  1540  				{
  1541  					Service:     service,
  1542  					ServicePort: servicePort,
  1543  					Endpoint: &model.IstioEndpoint{
  1544  						Address:      "192.168.1.1",
  1545  						EndpointPort: 10001,
  1546  						WorkloadName: "workload-1",
  1547  						Namespace:    "namespace-1",
  1548  						Labels: map[string]string{
  1549  							"version": "v1",
  1550  							"app":     "example",
  1551  						},
  1552  						Locality: model.Locality{
  1553  							ClusterID: "cluster-1",
  1554  							Label:     "region1/zone1/subzone1",
  1555  						},
  1556  						LbWeight: 30,
  1557  						Network:  "nw-0",
  1558  					},
  1559  				},
  1560  				{
  1561  					Service:     service,
  1562  					ServicePort: servicePort,
  1563  					Endpoint: &model.IstioEndpoint{
  1564  						Address:      "192.168.1.2",
  1565  						EndpointPort: 10001,
  1566  						WorkloadName: "workload-2",
  1567  						Namespace:    "namespace-2",
  1568  						Labels: map[string]string{
  1569  							"version": "v2",
  1570  							"app":     "example",
  1571  						},
  1572  						Locality: model.Locality{
  1573  							ClusterID: "cluster-2",
  1574  							Label:     "region1/zone1/subzone1",
  1575  						},
  1576  						LbWeight: 30,
  1577  						Network:  "nw-1",
  1578  					},
  1579  				},
  1580  				{
  1581  					Service:     service,
  1582  					ServicePort: servicePort,
  1583  					Endpoint: &model.IstioEndpoint{
  1584  						Address:      "192.168.1.3",
  1585  						EndpointPort: 10001,
  1586  						WorkloadName: "workload-3",
  1587  						Namespace:    "namespace-3",
  1588  						Labels: map[string]string{
  1589  							"version": "v3",
  1590  							"app":     "example",
  1591  						},
  1592  						Locality: model.Locality{
  1593  							ClusterID: "cluster-3",
  1594  							Label:     "region2/zone1/subzone1",
  1595  						},
  1596  						LbWeight: 40,
  1597  						Network:  "",
  1598  					},
  1599  				},
  1600  				{
  1601  					Service:     service,
  1602  					ServicePort: servicePort,
  1603  					Endpoint: &model.IstioEndpoint{
  1604  						Address:      "192.168.1.4",
  1605  						EndpointPort: 10001,
  1606  						WorkloadName: "workload-1",
  1607  						Namespace:    "namespace-1",
  1608  						Labels: map[string]string{
  1609  							"version": "v4",
  1610  							"app":     "example",
  1611  						},
  1612  						Locality: model.Locality{
  1613  							ClusterID: "cluster-1",
  1614  							Label:     "region1/zone1/subzone1",
  1615  						},
  1616  						LbWeight: 30,
  1617  						Network:  "filtered-out",
  1618  					},
  1619  				},
  1620  			},
  1621  			expected: []*endpoint.LocalityLbEndpoints{
  1622  				{
  1623  					Locality: &core.Locality{
  1624  						Region:  "region1",
  1625  						Zone:    "zone1",
  1626  						SubZone: "subzone1",
  1627  					},
  1628  					LoadBalancingWeight: &wrappers.UInt32Value{
  1629  						Value: 30,
  1630  					},
  1631  					LbEndpoints: []*endpoint.LbEndpoint{
  1632  						{
  1633  							HostIdentifier: &endpoint.LbEndpoint_Endpoint{
  1634  								Endpoint: &endpoint.Endpoint{
  1635  									Address: &core.Address{
  1636  										Address: &core.Address_SocketAddress{
  1637  											SocketAddress: &core.SocketAddress{
  1638  												Address: "192.168.1.1",
  1639  												PortSpecifier: &core.SocketAddress_PortValue{
  1640  													PortValue: 10001,
  1641  												},
  1642  											},
  1643  										},
  1644  									},
  1645  								},
  1646  							},
  1647  							Metadata: buildMetadata("nw-0", "", "workload-1", "namespace-1", "cluster-1", map[string]string{
  1648  								"version": "v1",
  1649  								"app":     "example",
  1650  							}),
  1651  							LoadBalancingWeight: &wrappers.UInt32Value{
  1652  								Value: 30,
  1653  							},
  1654  						},
  1655  					},
  1656  				},
  1657  			},
  1658  		},
  1659  	}
  1660  
  1661  	sortEndpoints := func(endpoints []*endpoint.LocalityLbEndpoints) {
  1662  		sort.SliceStable(endpoints, func(i, j int) bool {
  1663  			if strings.Compare(endpoints[i].Locality.Region, endpoints[j].Locality.Region) < 0 {
  1664  				return true
  1665  			}
  1666  			if strings.Compare(endpoints[i].Locality.Zone, endpoints[j].Locality.Zone) < 0 {
  1667  				return true
  1668  			}
  1669  			return strings.Compare(endpoints[i].Locality.SubZone, endpoints[j].Locality.SubZone) < 0
  1670  		})
  1671  	}
  1672  
  1673  	for _, tt := range cases {
  1674  		for _, resolution := range []model.Resolution{model.DNSLB, model.DNSRoundRobinLB} {
  1675  			t.Run(fmt.Sprintf("%s_%s", tt.name, resolution), func(t *testing.T) {
  1676  				service.Resolution = resolution
  1677  				cg := NewConfigGenTest(t, TestOptions{
  1678  					MeshConfig: tt.mesh,
  1679  					Services:   []*model.Service{service},
  1680  					Instances:  tt.instances,
  1681  				})
  1682  
  1683  				cb := NewClusterBuilder(cg.SetupProxy(proxy), &model.PushRequest{Push: cg.PushContext()}, nil)
  1684  				eb := endpoints.NewCDSEndpointBuilder(
  1685  					proxy, cb.req.Push,
  1686  					"outbound|8080|v1|foo.com",
  1687  					model.TrafficDirectionOutbound, "v1", "foo.com", 8080,
  1688  					service, drWithLabels(tt.labels),
  1689  				)
  1690  				actual := eb.FromServiceEndpoints()
  1691  				sortEndpoints(actual)
  1692  				if v := cmp.Diff(tt.expected, actual, protocmp.Transform()); v != "" {
  1693  					t.Fatalf("Expected (-) != actual (+):\n%s", v)
  1694  				}
  1695  			})
  1696  		}
  1697  	}
  1698  }
  1699  
  1700  func drWithLabels(lbls labels.Instance) *model.ConsolidatedDestRule {
  1701  	return model.ConvertConsolidatedDestRule(&config.Config{
  1702  		Meta: config.Meta{},
  1703  		Spec: &networking.DestinationRule{
  1704  			Subsets: []*networking.Subset{{
  1705  				Name:   "v1",
  1706  				Labels: lbls,
  1707  			}},
  1708  		},
  1709  	})
  1710  }
  1711  
  1712  func TestConcurrentBuildLocalityLbEndpoints(t *testing.T) {
  1713  	test.SetForTest(t, &features.CanonicalServiceForMeshExternalServiceEntry, true)
  1714  	proxy := &model.Proxy{
  1715  		Metadata: &model.NodeMetadata{
  1716  			ClusterID:            "cluster-1",
  1717  			RequestedNetworkView: []string{"nw-0", "nw-1"},
  1718  		},
  1719  	}
  1720  	servicePort := &model.Port{
  1721  		Name:     "default",
  1722  		Port:     8080,
  1723  		Protocol: protocol.HTTP,
  1724  	}
  1725  	service := &model.Service{
  1726  		Hostname: host.Name("*.example.org"),
  1727  		Ports:    model.PortList{servicePort},
  1728  		Attributes: model.ServiceAttributes{
  1729  			Name:      "TestService",
  1730  			Namespace: "test-ns",
  1731  			Labels:    map[string]string{"service.istio.io/canonical-name": "example-service"},
  1732  		},
  1733  		MeshExternal: true,
  1734  		Resolution:   model.DNSLB,
  1735  	}
  1736  	dr := drWithLabels(labels.Instance{"version": "v1"})
  1737  
  1738  	buildMetadata := func(networkID network.ID, tlsMode, workloadname, namespace string,
  1739  		clusterID istiocluster.ID, lbls labels.Instance,
  1740  	) *core.Metadata {
  1741  		newmeta := &core.Metadata{}
  1742  		util.AppendLbEndpointMetadata(&model.EndpointMetadata{
  1743  			Network:      networkID,
  1744  			TLSMode:      tlsMode,
  1745  			WorkloadName: workloadname,
  1746  			Namespace:    namespace,
  1747  			ClusterID:    clusterID,
  1748  			Labels:       lbls,
  1749  		}, newmeta)
  1750  		return newmeta
  1751  	}
  1752  
  1753  	instances := []*model.ServiceInstance{
  1754  		{
  1755  			Service:     service,
  1756  			ServicePort: servicePort,
  1757  			Endpoint: &model.IstioEndpoint{
  1758  				Address:      "192.168.1.1",
  1759  				EndpointPort: 10001,
  1760  				WorkloadName: "workload-1",
  1761  				Namespace:    "namespace-1",
  1762  				Labels: map[string]string{
  1763  					"version": "v1",
  1764  					"app":     "example",
  1765  				},
  1766  				Locality: model.Locality{
  1767  					ClusterID: "cluster-1",
  1768  					Label:     "region1/zone1/subzone1",
  1769  				},
  1770  				LbWeight: 30,
  1771  				Network:  "nw-0",
  1772  			},
  1773  		},
  1774  		{
  1775  			Service:     service,
  1776  			ServicePort: servicePort,
  1777  			Endpoint: &model.IstioEndpoint{
  1778  				Address:      "192.168.1.2",
  1779  				EndpointPort: 10001,
  1780  				WorkloadName: "workload-2",
  1781  				Namespace:    "namespace-2",
  1782  				Labels: map[string]string{
  1783  					"version": "v2",
  1784  					"app":     "example",
  1785  				},
  1786  				Locality: model.Locality{
  1787  					ClusterID: "cluster-2",
  1788  					Label:     "region1/zone1/subzone1",
  1789  				},
  1790  				LbWeight: 30,
  1791  				Network:  "nw-1",
  1792  			},
  1793  		},
  1794  		{
  1795  			Service:     service,
  1796  			ServicePort: servicePort,
  1797  			Endpoint: &model.IstioEndpoint{
  1798  				Address:      "192.168.1.3",
  1799  				EndpointPort: 10001,
  1800  				WorkloadName: "workload-3",
  1801  				Namespace:    "namespace-3",
  1802  				Labels: map[string]string{
  1803  					"version": "v3",
  1804  					"app":     "example",
  1805  				},
  1806  				Locality: model.Locality{
  1807  					ClusterID: "cluster-3",
  1808  					Label:     "region2/zone1/subzone1",
  1809  				},
  1810  				LbWeight: 40,
  1811  				Network:  "",
  1812  			},
  1813  		},
  1814  		{
  1815  			Service:     service,
  1816  			ServicePort: servicePort,
  1817  			Endpoint: &model.IstioEndpoint{
  1818  				Address:      "192.168.1.4",
  1819  				EndpointPort: 10001,
  1820  				WorkloadName: "workload-1",
  1821  				Namespace:    "namespace-1",
  1822  				Labels: map[string]string{
  1823  					"version": "v4",
  1824  					"app":     "example",
  1825  				},
  1826  				Locality: model.Locality{
  1827  					ClusterID: "cluster-1",
  1828  					Label:     "region1/zone1/subzone1",
  1829  				},
  1830  				LbWeight: 30,
  1831  				Network:  "filtered-out",
  1832  			},
  1833  		},
  1834  	}
  1835  
  1836  	updatedLbls := labels.Instance{
  1837  		"app":                                "example",
  1838  		model.IstioCanonicalServiceLabelName: "example-service",
  1839  	}
  1840  	expected := []*endpoint.LocalityLbEndpoints{
  1841  		{
  1842  			Locality: &core.Locality{
  1843  				Region:  "region1",
  1844  				Zone:    "zone1",
  1845  				SubZone: "subzone1",
  1846  			},
  1847  			LoadBalancingWeight: &wrappers.UInt32Value{
  1848  				Value: 30,
  1849  			},
  1850  			LbEndpoints: []*endpoint.LbEndpoint{
  1851  				{
  1852  					HostIdentifier: &endpoint.LbEndpoint_Endpoint{
  1853  						Endpoint: &endpoint.Endpoint{
  1854  							Address: &core.Address{
  1855  								Address: &core.Address_SocketAddress{
  1856  									SocketAddress: &core.SocketAddress{
  1857  										Address: "192.168.1.1",
  1858  										PortSpecifier: &core.SocketAddress_PortValue{
  1859  											PortValue: 10001,
  1860  										},
  1861  									},
  1862  								},
  1863  							},
  1864  						},
  1865  					},
  1866  					Metadata: buildMetadata("nw-0", "", "workload-1", "test-ns", "cluster-1", updatedLbls),
  1867  					LoadBalancingWeight: &wrappers.UInt32Value{
  1868  						Value: 30,
  1869  					},
  1870  				},
  1871  			},
  1872  		},
  1873  	}
  1874  
  1875  	sortEndpoints := func(endpoints []*endpoint.LocalityLbEndpoints) {
  1876  		sort.SliceStable(endpoints, func(i, j int) bool {
  1877  			if strings.Compare(endpoints[i].Locality.Region, endpoints[j].Locality.Region) < 0 {
  1878  				return true
  1879  			}
  1880  			if strings.Compare(endpoints[i].Locality.Zone, endpoints[j].Locality.Zone) < 0 {
  1881  				return true
  1882  			}
  1883  			return strings.Compare(endpoints[i].Locality.SubZone, endpoints[j].Locality.SubZone) < 0
  1884  		})
  1885  	}
  1886  
  1887  	cg := NewConfigGenTest(t, TestOptions{
  1888  		MeshConfig: testMesh(),
  1889  		Services:   []*model.Service{service},
  1890  		Instances:  instances,
  1891  	})
  1892  
  1893  	cb := NewClusterBuilder(cg.SetupProxy(proxy), &model.PushRequest{Push: cg.PushContext()}, nil)
  1894  	wg := sync.WaitGroup{}
  1895  	wg.Add(5)
  1896  	var actual []*endpoint.LocalityLbEndpoints
  1897  	mu := sync.Mutex{}
  1898  	for i := 0; i < 5; i++ {
  1899  		go func() {
  1900  			eb := endpoints.NewCDSEndpointBuilder(
  1901  				proxy, cb.req.Push,
  1902  				"outbound|8080|v1|foo.com",
  1903  				model.TrafficDirectionOutbound, "v1", "foo.com", 8080,
  1904  				service, dr,
  1905  			)
  1906  			eps := eb.FromServiceEndpoints()
  1907  			mu.Lock()
  1908  			actual = eps
  1909  			mu.Unlock()
  1910  			wg.Done()
  1911  		}()
  1912  	}
  1913  	wg.Wait()
  1914  	sortEndpoints(actual)
  1915  	if v := cmp.Diff(expected, actual, protocmp.Transform()); v != "" {
  1916  		t.Fatalf("Expected (-) != actual (+):\n%s", v)
  1917  	}
  1918  }
  1919  
  1920  func TestBuildPassthroughClusters(t *testing.T) {
  1921  	cases := []struct {
  1922  		name         string
  1923  		ips          []string
  1924  		ipv4Expected bool
  1925  		ipv6Expected bool
  1926  	}{
  1927  		{
  1928  			name:         "both ipv4 and ipv6",
  1929  			ips:          []string{"6.6.6.6", "::1"},
  1930  			ipv4Expected: true,
  1931  			ipv6Expected: true,
  1932  		},
  1933  		{
  1934  			name:         "ipv4 only",
  1935  			ips:          []string{"6.6.6.6"},
  1936  			ipv4Expected: true,
  1937  			ipv6Expected: false,
  1938  		},
  1939  		{
  1940  			name:         "ipv6 only",
  1941  			ips:          []string{"::1"},
  1942  			ipv4Expected: false,
  1943  			ipv6Expected: true,
  1944  		},
  1945  	}
  1946  	for _, tt := range cases {
  1947  		t.Run(tt.name, func(t *testing.T) {
  1948  			proxy := &model.Proxy{IPAddresses: tt.ips}
  1949  			cg := NewConfigGenTest(t, TestOptions{})
  1950  
  1951  			cb := NewClusterBuilder(cg.SetupProxy(proxy), &model.PushRequest{Push: cg.PushContext()}, nil)
  1952  			clusters := cb.buildInboundPassthroughClusters()
  1953  
  1954  			var hasIpv4, hasIpv6 bool
  1955  			for _, c := range clusters {
  1956  				hasIpv4 = hasIpv4 || c.Name == util.InboundPassthroughClusterIpv4
  1957  				hasIpv6 = hasIpv6 || c.Name == util.InboundPassthroughClusterIpv6
  1958  			}
  1959  			if hasIpv4 != tt.ipv4Expected {
  1960  				t.Errorf("Unexpected Ipv4 Passthrough Cluster, want %v got %v", tt.ipv4Expected, hasIpv4)
  1961  			}
  1962  			if hasIpv6 != tt.ipv6Expected {
  1963  				t.Errorf("Unexpected Ipv6 Passthrough Cluster, want %v got %v", tt.ipv6Expected, hasIpv6)
  1964  			}
  1965  
  1966  			passthrough := xdstest.ExtractCluster(util.InboundPassthroughClusterIpv4, clusters)
  1967  			if passthrough == nil {
  1968  				passthrough = xdstest.ExtractCluster(util.InboundPassthroughClusterIpv6, clusters)
  1969  			}
  1970  			// Validate that Passthrough Cluster LB Policy is set correctly.
  1971  			if passthrough.GetType() != cluster.Cluster_ORIGINAL_DST || passthrough.GetLbPolicy() != cluster.Cluster_CLUSTER_PROVIDED {
  1972  				t.Errorf("Unexpected Discovery type or Lb policy, got Discovery type: %v, Lb Policy: %v", passthrough.GetType(), passthrough.GetLbPolicy())
  1973  			}
  1974  		})
  1975  	}
  1976  }
  1977  
  1978  func newTestCluster() *clusterWrapper {
  1979  	return newClusterWrapper(&cluster.Cluster{
  1980  		Name: "test-cluster",
  1981  	})
  1982  }
  1983  
  1984  func newH2TestCluster() *clusterWrapper {
  1985  	mc := newClusterWrapper(&cluster.Cluster{
  1986  		Name: "test-cluster",
  1987  	})
  1988  	setH2Options(mc)
  1989  	return mc
  1990  }
  1991  
  1992  func newDownstreamTestCluster() *clusterWrapper {
  1993  	cb := NewClusterBuilder(newSidecarProxy(), nil, model.DisabledCache{})
  1994  	mc := newClusterWrapper(&cluster.Cluster{
  1995  		Name: "test-cluster",
  1996  	})
  1997  	cb.setUseDownstreamProtocol(mc)
  1998  	return mc
  1999  }
  2000  
  2001  func newSidecarProxy() *model.Proxy {
  2002  	return &model.Proxy{Type: model.SidecarProxy, Metadata: &model.NodeMetadata{}}
  2003  }
  2004  
  2005  func newGatewayProxy() *model.Proxy {
  2006  	return &model.Proxy{Type: model.Router, Metadata: &model.NodeMetadata{}}
  2007  }
  2008  
  2009  // Helper function to extract TLS context from a cluster
  2010  func getTLSContext(t *testing.T, c *cluster.Cluster) *tls.UpstreamTlsContext {
  2011  	t.Helper()
  2012  	if c.TransportSocket == nil {
  2013  		return nil
  2014  	}
  2015  	tlsContext := &tls.UpstreamTlsContext{}
  2016  	err := c.TransportSocket.GetTypedConfig().UnmarshalTo(tlsContext)
  2017  	if err != nil {
  2018  		t.Fatalf("Failed to unmarshall tls context: %v", err)
  2019  	}
  2020  	return tlsContext
  2021  }
  2022  
  2023  func TestShouldH2Upgrade(t *testing.T) {
  2024  	tests := []struct {
  2025  		name           string
  2026  		clusterName    string
  2027  		port           *model.Port
  2028  		mesh           *meshconfig.MeshConfig
  2029  		connectionPool *networking.ConnectionPoolSettings
  2030  
  2031  		upgrade bool
  2032  	}{
  2033  		{
  2034  			name:        "mesh upgrade - dr default",
  2035  			clusterName: "bar",
  2036  			port:        &model.Port{Protocol: protocol.HTTP},
  2037  			mesh:        &meshconfig.MeshConfig{H2UpgradePolicy: meshconfig.MeshConfig_UPGRADE},
  2038  			connectionPool: &networking.ConnectionPoolSettings{
  2039  				Http: &networking.ConnectionPoolSettings_HTTPSettings{
  2040  					H2UpgradePolicy: networking.ConnectionPoolSettings_HTTPSettings_DEFAULT,
  2041  				},
  2042  			},
  2043  			upgrade: true,
  2044  		},
  2045  		{
  2046  			name:        "mesh default - dr upgrade non http port",
  2047  			clusterName: "bar",
  2048  			port:        &model.Port{Protocol: protocol.Unsupported},
  2049  			mesh:        &meshconfig.MeshConfig{},
  2050  			connectionPool: &networking.ConnectionPoolSettings{
  2051  				Http: &networking.ConnectionPoolSettings_HTTPSettings{
  2052  					H2UpgradePolicy: networking.ConnectionPoolSettings_HTTPSettings_UPGRADE,
  2053  				},
  2054  			},
  2055  			upgrade: true,
  2056  		},
  2057  		{
  2058  			name:        "mesh no_upgrade - dr default",
  2059  			clusterName: "bar",
  2060  			port:        &model.Port{Protocol: protocol.HTTP},
  2061  			mesh:        &meshconfig.MeshConfig{H2UpgradePolicy: meshconfig.MeshConfig_DO_NOT_UPGRADE},
  2062  			connectionPool: &networking.ConnectionPoolSettings{
  2063  				Http: &networking.ConnectionPoolSettings_HTTPSettings{
  2064  					H2UpgradePolicy: networking.ConnectionPoolSettings_HTTPSettings_DEFAULT,
  2065  				},
  2066  			},
  2067  			upgrade: false,
  2068  		},
  2069  		{
  2070  			name:        "mesh no_upgrade - dr upgrade",
  2071  			clusterName: "bar",
  2072  			port:        &model.Port{Protocol: protocol.HTTP},
  2073  			mesh:        &meshconfig.MeshConfig{H2UpgradePolicy: meshconfig.MeshConfig_DO_NOT_UPGRADE},
  2074  			connectionPool: &networking.ConnectionPoolSettings{
  2075  				Http: &networking.ConnectionPoolSettings_HTTPSettings{
  2076  					H2UpgradePolicy: networking.ConnectionPoolSettings_HTTPSettings_UPGRADE,
  2077  				},
  2078  			},
  2079  			upgrade: true,
  2080  		},
  2081  		{
  2082  			name:        "mesh upgrade - dr no_upgrade",
  2083  			clusterName: "bar",
  2084  			port:        &model.Port{Protocol: protocol.HTTP},
  2085  			mesh:        &meshconfig.MeshConfig{H2UpgradePolicy: meshconfig.MeshConfig_UPGRADE},
  2086  			connectionPool: &networking.ConnectionPoolSettings{
  2087  				Http: &networking.ConnectionPoolSettings_HTTPSettings{
  2088  					H2UpgradePolicy: networking.ConnectionPoolSettings_HTTPSettings_DO_NOT_UPGRADE,
  2089  				},
  2090  			},
  2091  			upgrade: false,
  2092  		},
  2093  		{
  2094  			name:        "non-http",
  2095  			clusterName: "bar",
  2096  			port:        &model.Port{Protocol: protocol.Unsupported},
  2097  			mesh:        &meshconfig.MeshConfig{H2UpgradePolicy: meshconfig.MeshConfig_UPGRADE},
  2098  			connectionPool: &networking.ConnectionPoolSettings{
  2099  				Http: &networking.ConnectionPoolSettings_HTTPSettings{
  2100  					H2UpgradePolicy: networking.ConnectionPoolSettings_HTTPSettings_DEFAULT,
  2101  				},
  2102  			},
  2103  			upgrade: false,
  2104  		},
  2105  	}
  2106  
  2107  	for _, test := range tests {
  2108  		t.Run(test.name, func(t *testing.T) {
  2109  			upgrade := shouldH2Upgrade(test.clusterName, test.port, test.mesh, test.connectionPool)
  2110  
  2111  			if upgrade != test.upgrade {
  2112  				t.Fatalf("got: %t, want: %t (%v, %v)", upgrade, test.upgrade, test.mesh.H2UpgradePolicy, test.connectionPool.Http.H2UpgradePolicy)
  2113  			}
  2114  		})
  2115  	}
  2116  }
  2117  
  2118  // nolint
  2119  func TestIsHttp2Cluster(t *testing.T) {
  2120  	tests := []struct {
  2121  		name           string
  2122  		cluster        *clusterWrapper
  2123  		isHttp2Cluster bool // revive:disable-line
  2124  	}{
  2125  		{
  2126  			name:           "with no h2 options",
  2127  			cluster:        newTestCluster(),
  2128  			isHttp2Cluster: false,
  2129  		},
  2130  		{
  2131  			name:           "with h2 options",
  2132  			cluster:        newH2TestCluster(),
  2133  			isHttp2Cluster: true,
  2134  		},
  2135  		{
  2136  			name:           "with downstream config and h2 options",
  2137  			cluster:        newDownstreamTestCluster(),
  2138  			isHttp2Cluster: false,
  2139  		},
  2140  	}
  2141  
  2142  	cb := NewClusterBuilder(newSidecarProxy(), nil, model.DisabledCache{})
  2143  
  2144  	for _, test := range tests {
  2145  		t.Run(test.name, func(t *testing.T) {
  2146  			isHttp2Cluster := cb.isHttp2Cluster(test.cluster) // revive:disable-line
  2147  			if isHttp2Cluster != test.isHttp2Cluster {
  2148  				t.Errorf("got: %t, want: %t", isHttp2Cluster, test.isHttp2Cluster)
  2149  			}
  2150  		})
  2151  	}
  2152  }
  2153  
  2154  func TestApplyDestinationRuleOSCACert(t *testing.T) {
  2155  	servicePort := model.PortList{
  2156  		&model.Port{
  2157  			Name:     "default",
  2158  			Port:     8080,
  2159  			Protocol: protocol.HTTP,
  2160  		},
  2161  		&model.Port{
  2162  			Name:     "auto",
  2163  			Port:     9090,
  2164  			Protocol: protocol.Unsupported,
  2165  		},
  2166  	}
  2167  	service := &model.Service{
  2168  		Hostname:   host.Name("foo.default.svc.cluster.local"),
  2169  		Ports:      servicePort,
  2170  		Resolution: model.ClientSideLB,
  2171  		Attributes: model.ServiceAttributes{
  2172  			Namespace: TestServiceNamespace,
  2173  		},
  2174  	}
  2175  
  2176  	cases := []struct {
  2177  		name                      string
  2178  		cluster                   *cluster.Cluster
  2179  		clusterMode               ClusterMode
  2180  		service                   *model.Service
  2181  		port                      *model.Port
  2182  		proxyView                 model.ProxyView
  2183  		destRule                  *networking.DestinationRule
  2184  		expectedCaCertificateName string
  2185  		enableVerifyCertAtClient  bool
  2186  	}{
  2187  		{
  2188  			name:        "VerifyCertAtClient set and destination rule with empty string CaCertificates",
  2189  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
  2190  			clusterMode: DefaultClusterMode,
  2191  			service:     service,
  2192  			port:        servicePort[0],
  2193  			proxyView:   model.ProxyViewAll,
  2194  			destRule: &networking.DestinationRule{
  2195  				Host: "foo.default.svc.cluster.local",
  2196  				TrafficPolicy: &networking.TrafficPolicy{
  2197  					ConnectionPool: &networking.ConnectionPoolSettings{
  2198  						Http: &networking.ConnectionPoolSettings_HTTPSettings{
  2199  							MaxRetries:        10,
  2200  							UseClientProtocol: true,
  2201  						},
  2202  					},
  2203  					Tls: &networking.ClientTLSSettings{
  2204  						CaCertificates: "",
  2205  						Mode:           networking.ClientTLSSettings_SIMPLE,
  2206  					},
  2207  				},
  2208  			},
  2209  			expectedCaCertificateName: "system",
  2210  			enableVerifyCertAtClient:  true,
  2211  		},
  2212  		{
  2213  			name:        "VerifyCertAtClient set and destination rule with CaCertificates",
  2214  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
  2215  			clusterMode: DefaultClusterMode,
  2216  			service:     service,
  2217  			port:        servicePort[0],
  2218  			proxyView:   model.ProxyViewAll,
  2219  			destRule: &networking.DestinationRule{
  2220  				Host: "foo.default.svc.cluster.local",
  2221  				TrafficPolicy: &networking.TrafficPolicy{
  2222  					ConnectionPool: &networking.ConnectionPoolSettings{
  2223  						Http: &networking.ConnectionPoolSettings_HTTPSettings{
  2224  							MaxRetries:        10,
  2225  							UseClientProtocol: true,
  2226  						},
  2227  					},
  2228  					Tls: &networking.ClientTLSSettings{
  2229  						CaCertificates: "root-cert.pem",
  2230  						Mode:           networking.ClientTLSSettings_SIMPLE,
  2231  					},
  2232  				},
  2233  			},
  2234  			expectedCaCertificateName: "root-cert.pem",
  2235  			enableVerifyCertAtClient:  true,
  2236  		},
  2237  		{
  2238  			name:        "VerifyCertAtClient set and destination rule without CaCertificates",
  2239  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
  2240  			clusterMode: DefaultClusterMode,
  2241  			service:     service,
  2242  			port:        servicePort[0],
  2243  			proxyView:   model.ProxyViewAll,
  2244  			destRule: &networking.DestinationRule{
  2245  				Host: "foo.default.svc.cluster.local",
  2246  				TrafficPolicy: &networking.TrafficPolicy{
  2247  					ConnectionPool: &networking.ConnectionPoolSettings{
  2248  						Http: &networking.ConnectionPoolSettings_HTTPSettings{
  2249  							MaxRetries:        10,
  2250  							UseClientProtocol: true,
  2251  						},
  2252  					},
  2253  					Tls: &networking.ClientTLSSettings{
  2254  						Mode: networking.ClientTLSSettings_SIMPLE,
  2255  					},
  2256  				},
  2257  			},
  2258  			expectedCaCertificateName: "system",
  2259  			enableVerifyCertAtClient:  true,
  2260  		},
  2261  		{
  2262  			name:        "VerifyCertAtClient false and destination rule without CaCertificates",
  2263  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
  2264  			clusterMode: DefaultClusterMode,
  2265  			service:     service,
  2266  			port:        servicePort[0],
  2267  			proxyView:   model.ProxyViewAll,
  2268  			destRule: &networking.DestinationRule{
  2269  				Host: "foo.default.svc.cluster.local",
  2270  				TrafficPolicy: &networking.TrafficPolicy{
  2271  					ConnectionPool: &networking.ConnectionPoolSettings{
  2272  						Http: &networking.ConnectionPoolSettings_HTTPSettings{
  2273  							MaxRetries:        10,
  2274  							UseClientProtocol: true,
  2275  						},
  2276  					},
  2277  					Tls: &networking.ClientTLSSettings{
  2278  						Mode: networking.ClientTLSSettings_SIMPLE,
  2279  					},
  2280  				},
  2281  			},
  2282  			expectedCaCertificateName: "",
  2283  			enableVerifyCertAtClient:  false,
  2284  		},
  2285  		{
  2286  			name:        "VerifyCertAtClient false and destination rule with CaCertificates",
  2287  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
  2288  			clusterMode: DefaultClusterMode,
  2289  			service:     service,
  2290  			port:        servicePort[0],
  2291  			proxyView:   model.ProxyViewAll,
  2292  			destRule: &networking.DestinationRule{
  2293  				Host: "foo.default.svc.cluster.local",
  2294  				TrafficPolicy: &networking.TrafficPolicy{
  2295  					ConnectionPool: &networking.ConnectionPoolSettings{
  2296  						Http: &networking.ConnectionPoolSettings_HTTPSettings{
  2297  							MaxRetries:        10,
  2298  							UseClientProtocol: true,
  2299  						},
  2300  					},
  2301  					Tls: &networking.ClientTLSSettings{
  2302  						CaCertificates: "root-cert.pem",
  2303  						Mode:           networking.ClientTLSSettings_SIMPLE,
  2304  					},
  2305  				},
  2306  			},
  2307  			expectedCaCertificateName: "root-cert.pem",
  2308  			enableVerifyCertAtClient:  false,
  2309  		},
  2310  	}
  2311  
  2312  	for _, tt := range cases {
  2313  		t.Run(tt.name, func(t *testing.T) {
  2314  			test.SetForTest(t, &features.VerifyCertAtClient, tt.enableVerifyCertAtClient)
  2315  
  2316  			var cfg *config.Config
  2317  			if tt.destRule != nil {
  2318  				cfg = &config.Config{
  2319  					Meta: config.Meta{
  2320  						GroupVersionKind: gvk.DestinationRule,
  2321  						Name:             "acme",
  2322  						Namespace:        "default",
  2323  					},
  2324  					Spec: tt.destRule,
  2325  				}
  2326  			}
  2327  			cg := NewConfigGenTest(t, TestOptions{
  2328  				ConfigPointers: []*config.Config{cfg},
  2329  				Services:       []*model.Service{tt.service},
  2330  			})
  2331  			proxy := cg.SetupProxy(nil)
  2332  			cb := NewClusterBuilder(proxy, &model.PushRequest{Push: cg.PushContext()}, nil)
  2333  
  2334  			tt.cluster.CommonLbConfig = &cluster.Cluster_CommonLbConfig{}
  2335  
  2336  			ec := newClusterWrapper(tt.cluster)
  2337  			destRule := proxy.SidecarScope.DestinationRule(model.TrafficDirectionOutbound, proxy, tt.service.Hostname)
  2338  
  2339  			eb := endpoints.NewCDSEndpointBuilder(proxy, cb.req.Push, tt.cluster.Name,
  2340  				model.TrafficDirectionOutbound, "", service.Hostname, tt.port.Port,
  2341  				service, destRule)
  2342  
  2343  			// ACT
  2344  			_ = cb.applyDestinationRule(ec, tt.clusterMode, tt.service, tt.port, eb, destRule.GetRule(), nil)
  2345  
  2346  			byteArray, err := config.ToJSON(destRule.GetRule().Spec)
  2347  			if err != nil {
  2348  				t.Errorf("Could not parse destination rule: %v", err)
  2349  			}
  2350  			dr := &networking.DestinationRule{}
  2351  			err = json.Unmarshal(byteArray, dr)
  2352  			if err != nil {
  2353  				t.Errorf("Could not unmarshal destination rule: %v", err)
  2354  			}
  2355  			ca := dr.TrafficPolicy.Tls.CaCertificates
  2356  			if ca != tt.expectedCaCertificateName {
  2357  				t.Errorf("%v: got unexpected caCertitifcates field. Expected (%v), received (%v)", tt.name, tt.expectedCaCertificateName, ca)
  2358  			}
  2359  		})
  2360  	}
  2361  }
  2362  
  2363  func TestApplyTCPKeepalive(t *testing.T) {
  2364  	cases := []struct {
  2365  		name           string
  2366  		mesh           *meshconfig.MeshConfig
  2367  		connectionPool *networking.ConnectionPoolSettings
  2368  		wantConnOpts   *cluster.UpstreamConnectionOptions
  2369  	}{
  2370  		{
  2371  			name:           "no tcp alive",
  2372  			mesh:           &meshconfig.MeshConfig{},
  2373  			connectionPool: &networking.ConnectionPoolSettings{},
  2374  			wantConnOpts:   nil,
  2375  		},
  2376  		{
  2377  			name: "destination rule tcp alive",
  2378  			mesh: &meshconfig.MeshConfig{},
  2379  			connectionPool: &networking.ConnectionPoolSettings{
  2380  				Tcp: &networking.ConnectionPoolSettings_TCPSettings{
  2381  					TcpKeepalive: &networking.ConnectionPoolSettings_TCPSettings_TcpKeepalive{
  2382  						Time: &durationpb.Duration{Seconds: 10},
  2383  					},
  2384  				},
  2385  			},
  2386  			wantConnOpts: &cluster.UpstreamConnectionOptions{
  2387  				TcpKeepalive: &core.TcpKeepalive{
  2388  					KeepaliveTime: &wrappers.UInt32Value{Value: uint32(10)},
  2389  				},
  2390  			},
  2391  		},
  2392  		{
  2393  			name: "mesh tcp alive",
  2394  			mesh: &meshconfig.MeshConfig{
  2395  				TcpKeepalive: &networking.ConnectionPoolSettings_TCPSettings_TcpKeepalive{
  2396  					Time: &durationpb.Duration{Seconds: 10},
  2397  				},
  2398  			},
  2399  			connectionPool: &networking.ConnectionPoolSettings{},
  2400  			wantConnOpts: &cluster.UpstreamConnectionOptions{
  2401  				TcpKeepalive: &core.TcpKeepalive{
  2402  					KeepaliveTime: &wrappers.UInt32Value{Value: uint32(10)},
  2403  				},
  2404  			},
  2405  		},
  2406  	}
  2407  
  2408  	for _, tt := range cases {
  2409  		t.Run(tt.name, func(t *testing.T) {
  2410  			cg := NewConfigGenTest(t, TestOptions{})
  2411  			proxy := cg.SetupProxy(nil)
  2412  			cb := NewClusterBuilder(proxy, &model.PushRequest{Push: cg.PushContext()}, nil)
  2413  			mc := &clusterWrapper{
  2414  				cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
  2415  			}
  2416  
  2417  			cb.applyConnectionPool(tt.mesh, mc, tt.connectionPool)
  2418  
  2419  			if !reflect.DeepEqual(tt.wantConnOpts, mc.cluster.UpstreamConnectionOptions) {
  2420  				t.Errorf("unexpected tcp keepalive settings, want %v, got %v", tt.wantConnOpts,
  2421  					mc.cluster.UpstreamConnectionOptions)
  2422  			}
  2423  		})
  2424  	}
  2425  }
  2426  
  2427  func TestApplyConnectionPool(t *testing.T) {
  2428  	cases := []struct {
  2429  		name                string
  2430  		cluster             *cluster.Cluster
  2431  		httpProtocolOptions *http.HttpProtocolOptions
  2432  		connectionPool      *networking.ConnectionPoolSettings
  2433  		expectedHTTPPOpt    *http.HttpProtocolOptions
  2434  	}{
  2435  		{
  2436  			name:    "only update IdleTimeout",
  2437  			cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
  2438  			httpProtocolOptions: &http.HttpProtocolOptions{
  2439  				CommonHttpProtocolOptions: &core.HttpProtocolOptions{
  2440  					IdleTimeout: &durationpb.Duration{
  2441  						Seconds: 10,
  2442  					},
  2443  					MaxRequestsPerConnection: &wrappers.UInt32Value{Value: 10},
  2444  				},
  2445  			},
  2446  			connectionPool: &networking.ConnectionPoolSettings{
  2447  				Http: &networking.ConnectionPoolSettings_HTTPSettings{
  2448  					IdleTimeout: &durationpb.Duration{
  2449  						Seconds: 22,
  2450  					},
  2451  				},
  2452  			},
  2453  			expectedHTTPPOpt: &http.HttpProtocolOptions{
  2454  				CommonHttpProtocolOptions: &core.HttpProtocolOptions{
  2455  					IdleTimeout: &durationpb.Duration{
  2456  						Seconds: 22,
  2457  					},
  2458  					MaxRequestsPerConnection: &wrappers.UInt32Value{Value: 10},
  2459  				},
  2460  			},
  2461  		},
  2462  		{
  2463  			name:    "set TCP idle timeout",
  2464  			cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
  2465  			httpProtocolOptions: &http.HttpProtocolOptions{
  2466  				CommonHttpProtocolOptions: &core.HttpProtocolOptions{
  2467  					MaxRequestsPerConnection: &wrappers.UInt32Value{Value: 10},
  2468  				},
  2469  			},
  2470  			connectionPool: &networking.ConnectionPoolSettings{
  2471  				Tcp: &networking.ConnectionPoolSettings_TCPSettings{
  2472  					IdleTimeout: &durationpb.Duration{
  2473  						Seconds: 10,
  2474  					},
  2475  				},
  2476  				Http: &networking.ConnectionPoolSettings_HTTPSettings{
  2477  					IdleTimeout: nil,
  2478  				},
  2479  			},
  2480  			expectedHTTPPOpt: &http.HttpProtocolOptions{
  2481  				CommonHttpProtocolOptions: &core.HttpProtocolOptions{
  2482  					IdleTimeout: &durationpb.Duration{
  2483  						Seconds: 10,
  2484  					},
  2485  					MaxRequestsPerConnection: &wrappers.UInt32Value{Value: 10},
  2486  				},
  2487  			},
  2488  		},
  2489  		{
  2490  			name:    "ignore TCP idle timeout when HTTP idle timeout is specified",
  2491  			cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
  2492  			httpProtocolOptions: &http.HttpProtocolOptions{
  2493  				CommonHttpProtocolOptions: &core.HttpProtocolOptions{
  2494  					MaxRequestsPerConnection: &wrappers.UInt32Value{Value: 10},
  2495  				},
  2496  			},
  2497  			connectionPool: &networking.ConnectionPoolSettings{
  2498  				Tcp: &networking.ConnectionPoolSettings_TCPSettings{
  2499  					IdleTimeout: &durationpb.Duration{
  2500  						Seconds: 10,
  2501  					},
  2502  				},
  2503  				Http: &networking.ConnectionPoolSettings_HTTPSettings{
  2504  					IdleTimeout: &durationpb.Duration{
  2505  						Seconds: 20,
  2506  					},
  2507  				},
  2508  			},
  2509  			expectedHTTPPOpt: &http.HttpProtocolOptions{
  2510  				CommonHttpProtocolOptions: &core.HttpProtocolOptions{
  2511  					IdleTimeout: &durationpb.Duration{
  2512  						Seconds: 20,
  2513  					},
  2514  					MaxRequestsPerConnection: &wrappers.UInt32Value{Value: 10},
  2515  				},
  2516  			},
  2517  		},
  2518  		{
  2519  			name:    "only update MaxRequestsPerConnection ",
  2520  			cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
  2521  			httpProtocolOptions: &http.HttpProtocolOptions{
  2522  				CommonHttpProtocolOptions: &core.HttpProtocolOptions{
  2523  					IdleTimeout: &durationpb.Duration{
  2524  						Seconds: 10,
  2525  					},
  2526  					MaxRequestsPerConnection: &wrappers.UInt32Value{Value: 10},
  2527  				},
  2528  			},
  2529  			connectionPool: &networking.ConnectionPoolSettings{
  2530  				Http: &networking.ConnectionPoolSettings_HTTPSettings{
  2531  					MaxRequestsPerConnection: 22,
  2532  				},
  2533  			},
  2534  			expectedHTTPPOpt: &http.HttpProtocolOptions{
  2535  				CommonHttpProtocolOptions: &core.HttpProtocolOptions{
  2536  					IdleTimeout: &durationpb.Duration{
  2537  						Seconds: 10,
  2538  					},
  2539  					MaxRequestsPerConnection: &wrappers.UInt32Value{Value: 22},
  2540  				},
  2541  			},
  2542  		},
  2543  		{
  2544  			name:    "update multiple fields",
  2545  			cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
  2546  			httpProtocolOptions: &http.HttpProtocolOptions{
  2547  				CommonHttpProtocolOptions: &core.HttpProtocolOptions{
  2548  					IdleTimeout: &durationpb.Duration{
  2549  						Seconds: 10,
  2550  					},
  2551  					MaxRequestsPerConnection: &wrappers.UInt32Value{Value: 10},
  2552  				},
  2553  			},
  2554  			connectionPool: &networking.ConnectionPoolSettings{
  2555  				Http: &networking.ConnectionPoolSettings_HTTPSettings{
  2556  					IdleTimeout: &durationpb.Duration{
  2557  						Seconds: 22,
  2558  					},
  2559  					MaxRequestsPerConnection: 22,
  2560  				},
  2561  				Tcp: &networking.ConnectionPoolSettings_TCPSettings{
  2562  					MaxConnectionDuration: &durationpb.Duration{
  2563  						Seconds: 500,
  2564  					},
  2565  				},
  2566  			},
  2567  			expectedHTTPPOpt: &http.HttpProtocolOptions{
  2568  				CommonHttpProtocolOptions: &core.HttpProtocolOptions{
  2569  					IdleTimeout: &durationpb.Duration{
  2570  						Seconds: 22,
  2571  					},
  2572  					MaxRequestsPerConnection: &wrappers.UInt32Value{Value: 22},
  2573  					MaxConnectionDuration: &durationpb.Duration{
  2574  						Seconds: 500,
  2575  					},
  2576  				},
  2577  			},
  2578  		},
  2579  	}
  2580  
  2581  	for _, tt := range cases {
  2582  		t.Run(tt.name, func(t *testing.T) {
  2583  			cg := NewConfigGenTest(t, TestOptions{})
  2584  			proxy := cg.SetupProxy(nil)
  2585  			cb := NewClusterBuilder(proxy, &model.PushRequest{Push: cg.PushContext()}, nil)
  2586  			mc := &clusterWrapper{
  2587  				cluster:             tt.cluster,
  2588  				httpProtocolOptions: tt.httpProtocolOptions,
  2589  			}
  2590  
  2591  			opts := buildClusterOpts{
  2592  				mesh:    cb.req.Push.Mesh,
  2593  				mutable: mc,
  2594  			}
  2595  			cb.applyConnectionPool(opts.mesh, opts.mutable, tt.connectionPool)
  2596  			// assert httpProtocolOptions
  2597  			assert.Equal(t, opts.mutable.httpProtocolOptions.CommonHttpProtocolOptions.IdleTimeout,
  2598  				tt.expectedHTTPPOpt.CommonHttpProtocolOptions.IdleTimeout)
  2599  			assert.Equal(t, opts.mutable.httpProtocolOptions.CommonHttpProtocolOptions.MaxRequestsPerConnection,
  2600  				tt.expectedHTTPPOpt.CommonHttpProtocolOptions.MaxRequestsPerConnection)
  2601  			assert.Equal(t, opts.mutable.httpProtocolOptions.CommonHttpProtocolOptions.MaxConnectionDuration,
  2602  				tt.expectedHTTPPOpt.CommonHttpProtocolOptions.MaxConnectionDuration)
  2603  		})
  2604  	}
  2605  }
  2606  
  2607  func TestBuildExternalSDSClusters(t *testing.T) {
  2608  	proxy := &model.Proxy{
  2609  		Metadata: &model.NodeMetadata{
  2610  			Raw: map[string]any{
  2611  				security.CredentialMetaDataName: "true",
  2612  			},
  2613  		},
  2614  	}
  2615  
  2616  	cases := []struct {
  2617  		name         string
  2618  		expectedName string
  2619  		expectedPath string
  2620  	}{
  2621  		{
  2622  			name:         "uds",
  2623  			expectedName: security.SDSExternalClusterName,
  2624  			expectedPath: security.CredentialNameSocketPath,
  2625  		},
  2626  	}
  2627  	for _, tt := range cases {
  2628  		t.Run(tt.name, func(t *testing.T) {
  2629  			cg := NewConfigGenTest(t, TestOptions{})
  2630  			cb := NewClusterBuilder(cg.SetupProxy(proxy), &model.PushRequest{Push: cg.PushContext()}, nil)
  2631  			cluster := cb.buildExternalSDSCluster(security.CredentialNameSocketPath)
  2632  			path := cluster.LoadAssignment.Endpoints[0].LbEndpoints[0].GetEndpoint().Address.GetPipe().Path
  2633  			anyOptions := cluster.TypedExtensionProtocolOptions[v3.HttpProtocolOptionsType]
  2634  			if anyOptions == nil {
  2635  				t.Errorf("cluster has no httpProtocolOptions")
  2636  			}
  2637  			if cluster.Name != tt.expectedName {
  2638  				t.Errorf("Unexpected cluster name, got: %v, want: %v", cluster.Name, tt.expectedName)
  2639  			}
  2640  			if path != tt.expectedPath {
  2641  				t.Errorf("Unexpected path, got: %v, want: %v", path, tt.expectedPath)
  2642  			}
  2643  		})
  2644  	}
  2645  }
  2646  
  2647  func TestInsecureSkipVerify(t *testing.T) {
  2648  	servicePort := model.PortList{
  2649  		&model.Port{
  2650  			Name:     "default",
  2651  			Port:     8080,
  2652  			Protocol: protocol.HTTP,
  2653  		},
  2654  		&model.Port{
  2655  			Name:     "auto",
  2656  			Port:     9090,
  2657  			Protocol: protocol.Unsupported,
  2658  		},
  2659  	}
  2660  
  2661  	service := &model.Service{
  2662  		Hostname:   host.Name("foo.default.svc.cluster.local"),
  2663  		Ports:      servicePort,
  2664  		Resolution: model.ClientSideLB,
  2665  		Attributes: model.ServiceAttributes{
  2666  			Namespace:       TestServiceNamespace,
  2667  			ServiceRegistry: provider.External,
  2668  		},
  2669  	}
  2670  
  2671  	cases := []struct {
  2672  		name                     string
  2673  		cluster                  *cluster.Cluster
  2674  		clusterMode              ClusterMode
  2675  		service                  *model.Service
  2676  		port                     *model.Port
  2677  		proxyView                model.ProxyView
  2678  		destRule                 *networking.DestinationRule
  2679  		serviceAcct              []string // SE SAN values
  2680  		enableAutoSni            bool
  2681  		enableVerifyCertAtClient bool
  2682  		expectTLSContext         *tls.UpstreamTlsContext
  2683  	}{
  2684  		{
  2685  			name:        "With tls mode simple, InsecureSkipVerify is not specified and ca cert is supplied",
  2686  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
  2687  			clusterMode: DefaultClusterMode,
  2688  			service:     service,
  2689  			port:        servicePort[0],
  2690  			proxyView:   model.ProxyViewAll,
  2691  			destRule: &networking.DestinationRule{
  2692  				Host: "foo.default.svc.cluster.local",
  2693  				TrafficPolicy: &networking.TrafficPolicy{
  2694  					Tls: &networking.ClientTLSSettings{
  2695  						Mode:            networking.ClientTLSSettings_SIMPLE,
  2696  						CaCertificates:  constants.RootCertFilename,
  2697  						Sni:             "foo.default.svc.cluster.local",
  2698  						SubjectAltNames: []string{"foo.default.svc.cluster.local"},
  2699  					},
  2700  				},
  2701  			},
  2702  			enableAutoSni:            false,
  2703  			enableVerifyCertAtClient: false,
  2704  			expectTLSContext: &tls.UpstreamTlsContext{
  2705  				CommonTlsContext: &tls.CommonTlsContext{
  2706  					TlsParams: &tls.TlsParameters{
  2707  						// if not specified, envoy use TLSv1_2 as default for client.
  2708  						TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3,
  2709  						TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2,
  2710  					},
  2711  					ValidationContextType: &tls.CommonTlsContext_CombinedValidationContext{
  2712  						CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{
  2713  							DefaultValidationContext: &tls.CertificateValidationContext{MatchSubjectAltNames: util.StringToExactMatch([]string{"foo.default.svc.cluster.local"})},
  2714  							ValidationContextSdsSecretConfig: &tls.SdsSecretConfig{
  2715  								Name: "file-root:" + constants.RootCertFilename,
  2716  								SdsConfig: &core.ConfigSource{
  2717  									ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{
  2718  										ApiConfigSource: &core.ApiConfigSource{
  2719  											ApiType:                   core.ApiConfigSource_GRPC,
  2720  											SetNodeOnFirstMessageOnly: true,
  2721  											TransportApiVersion:       core.ApiVersion_V3,
  2722  											GrpcServices: []*core.GrpcService{
  2723  												{
  2724  													TargetSpecifier: &core.GrpcService_EnvoyGrpc_{
  2725  														EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"},
  2726  													},
  2727  												},
  2728  											},
  2729  										},
  2730  									},
  2731  									ResourceApiVersion: core.ApiVersion_V3,
  2732  								},
  2733  							},
  2734  						},
  2735  					},
  2736  				},
  2737  				Sni: "foo.default.svc.cluster.local",
  2738  			},
  2739  		},
  2740  		{
  2741  			name:        "With tls mode simple, InsecureSkipVerify is set false and ca cert is supplied",
  2742  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
  2743  			clusterMode: DefaultClusterMode,
  2744  			service:     service,
  2745  			port:        servicePort[0],
  2746  			proxyView:   model.ProxyViewAll,
  2747  			destRule: &networking.DestinationRule{
  2748  				Host: "foo.default.svc.cluster.local",
  2749  				TrafficPolicy: &networking.TrafficPolicy{
  2750  					Tls: &networking.ClientTLSSettings{
  2751  						Mode:               networking.ClientTLSSettings_SIMPLE,
  2752  						CaCertificates:     constants.RootCertFilename,
  2753  						Sni:                "foo.default.svc.cluster.local",
  2754  						SubjectAltNames:    []string{"foo.default.svc.cluster.local"},
  2755  						InsecureSkipVerify: &wrappers.BoolValue{Value: false},
  2756  					},
  2757  				},
  2758  			},
  2759  			enableAutoSni:            false,
  2760  			enableVerifyCertAtClient: false,
  2761  			expectTLSContext: &tls.UpstreamTlsContext{
  2762  				CommonTlsContext: &tls.CommonTlsContext{
  2763  					TlsParams: &tls.TlsParameters{
  2764  						// if not specified, envoy use TLSv1_2 as default for client.
  2765  						TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3,
  2766  						TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2,
  2767  					},
  2768  					ValidationContextType: &tls.CommonTlsContext_CombinedValidationContext{
  2769  						CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{
  2770  							DefaultValidationContext: &tls.CertificateValidationContext{MatchSubjectAltNames: util.StringToExactMatch([]string{"foo.default.svc.cluster.local"})},
  2771  							ValidationContextSdsSecretConfig: &tls.SdsSecretConfig{
  2772  								Name: "file-root:" + constants.RootCertFilename,
  2773  								SdsConfig: &core.ConfigSource{
  2774  									ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{
  2775  										ApiConfigSource: &core.ApiConfigSource{
  2776  											ApiType:                   core.ApiConfigSource_GRPC,
  2777  											SetNodeOnFirstMessageOnly: true,
  2778  											TransportApiVersion:       core.ApiVersion_V3,
  2779  											GrpcServices: []*core.GrpcService{
  2780  												{
  2781  													TargetSpecifier: &core.GrpcService_EnvoyGrpc_{
  2782  														EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"},
  2783  													},
  2784  												},
  2785  											},
  2786  										},
  2787  									},
  2788  									ResourceApiVersion: core.ApiVersion_V3,
  2789  								},
  2790  							},
  2791  						},
  2792  					},
  2793  				},
  2794  				Sni: "foo.default.svc.cluster.local",
  2795  			},
  2796  		},
  2797  		{
  2798  			name:        "With tls mode simple, InsecureSkipVerify is set true and env VERIFY_CERTIFICATE_AT_CLIENT is true",
  2799  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
  2800  			clusterMode: DefaultClusterMode,
  2801  			service:     service,
  2802  			port:        servicePort[0],
  2803  			proxyView:   model.ProxyViewAll,
  2804  			destRule: &networking.DestinationRule{
  2805  				Host: "foo.default.svc.cluster.local",
  2806  				TrafficPolicy: &networking.TrafficPolicy{
  2807  					Tls: &networking.ClientTLSSettings{
  2808  						Mode:               networking.ClientTLSSettings_SIMPLE,
  2809  						Sni:                "foo.default.svc.cluster.local",
  2810  						SubjectAltNames:    []string{"foo.default.svc.cluster.local"},
  2811  						InsecureSkipVerify: &wrappers.BoolValue{Value: true},
  2812  					},
  2813  				},
  2814  			},
  2815  			enableAutoSni:            false,
  2816  			enableVerifyCertAtClient: true,
  2817  			expectTLSContext: &tls.UpstreamTlsContext{
  2818  				CommonTlsContext: &tls.CommonTlsContext{
  2819  					TlsParams: &tls.TlsParameters{
  2820  						// if not specified, envoy use TLSv1_2 as default for client.
  2821  						TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3,
  2822  						TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2,
  2823  					},
  2824  					ValidationContextType: &tls.CommonTlsContext_ValidationContext{},
  2825  				},
  2826  				Sni: "foo.default.svc.cluster.local",
  2827  			},
  2828  		},
  2829  		{
  2830  			name:        "With tls mode simple, InsecureSkipVerify is set true and env VERIFY_CERTIFICATE_AT_CLIENT is true and AUTO_SNI is true",
  2831  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
  2832  			clusterMode: DefaultClusterMode,
  2833  			service:     service,
  2834  			port:        servicePort[0],
  2835  			proxyView:   model.ProxyViewAll,
  2836  			destRule: &networking.DestinationRule{
  2837  				Host: "foo.default.svc.cluster.local",
  2838  				TrafficPolicy: &networking.TrafficPolicy{
  2839  					Tls: &networking.ClientTLSSettings{
  2840  						Mode:               networking.ClientTLSSettings_SIMPLE,
  2841  						SubjectAltNames:    []string{"foo.default.svc.cluster.local"},
  2842  						InsecureSkipVerify: &wrappers.BoolValue{Value: true},
  2843  					},
  2844  				},
  2845  			},
  2846  			enableAutoSni:            true,
  2847  			enableVerifyCertAtClient: true,
  2848  			expectTLSContext: &tls.UpstreamTlsContext{
  2849  				CommonTlsContext: &tls.CommonTlsContext{
  2850  					TlsParams: &tls.TlsParameters{
  2851  						// if not specified, envoy use TLSv1_2 as default for client.
  2852  						TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3,
  2853  						TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2,
  2854  					},
  2855  					ValidationContextType: &tls.CommonTlsContext_ValidationContext{},
  2856  				},
  2857  			},
  2858  		},
  2859  		{
  2860  			name:        "With tls mode simple and CredentialName, InsecureSkipVerify is set true and env VERIFY_CERTIFICATE_AT_CLIENT is true",
  2861  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
  2862  			clusterMode: DefaultClusterMode,
  2863  			service:     service,
  2864  			port:        servicePort[0],
  2865  			proxyView:   model.ProxyViewAll,
  2866  			destRule: &networking.DestinationRule{
  2867  				Host: "foo.default.svc.cluster.local",
  2868  				TrafficPolicy: &networking.TrafficPolicy{
  2869  					Tls: &networking.ClientTLSSettings{
  2870  						Mode:               networking.ClientTLSSettings_SIMPLE,
  2871  						CredentialName:     "ca-cert",
  2872  						Sni:                "foo.default.svc.cluster.local",
  2873  						SubjectAltNames:    []string{"foo.default.svc.cluster.local"},
  2874  						InsecureSkipVerify: &wrappers.BoolValue{Value: true},
  2875  					},
  2876  				},
  2877  				WorkloadSelector: &v1beta1.WorkloadSelector{},
  2878  			},
  2879  			enableAutoSni:            false,
  2880  			enableVerifyCertAtClient: true,
  2881  			expectTLSContext: &tls.UpstreamTlsContext{
  2882  				CommonTlsContext: &tls.CommonTlsContext{
  2883  					TlsParams: &tls.TlsParameters{
  2884  						// if not specified, envoy use TLSv1_2 as default for client.
  2885  						TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3,
  2886  						TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2,
  2887  					},
  2888  				},
  2889  				Sni: "foo.default.svc.cluster.local",
  2890  			},
  2891  		},
  2892  		{
  2893  			name:        "With tls mode mutual, InsecureSkipVerify is not specified and ca cert is supplied",
  2894  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
  2895  			clusterMode: DefaultClusterMode,
  2896  			service:     service,
  2897  			port:        servicePort[0],
  2898  			proxyView:   model.ProxyViewAll,
  2899  			destRule: &networking.DestinationRule{
  2900  				Host: "foo.default.svc.cluster.local",
  2901  				TrafficPolicy: &networking.TrafficPolicy{
  2902  					Tls: &networking.ClientTLSSettings{
  2903  						Mode:              networking.ClientTLSSettings_MUTUAL,
  2904  						ClientCertificate: "cert",
  2905  						PrivateKey:        "key",
  2906  						CaCertificates:    constants.RootCertFilename,
  2907  						Sni:               "foo.default.svc.cluster.local",
  2908  						SubjectAltNames:   []string{"foo.default.svc.cluster.local"},
  2909  					},
  2910  				},
  2911  			},
  2912  			enableAutoSni:            false,
  2913  			enableVerifyCertAtClient: false,
  2914  			expectTLSContext: &tls.UpstreamTlsContext{
  2915  				CommonTlsContext: &tls.CommonTlsContext{
  2916  					TlsParams: &tls.TlsParameters{
  2917  						// if not specified, envoy use TLSv1_2 as default for client.
  2918  						TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3,
  2919  						TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2,
  2920  					},
  2921  					TlsCertificateSdsSecretConfigs: []*tls.SdsSecretConfig{
  2922  						{
  2923  							Name: "file-cert:cert~key",
  2924  							SdsConfig: &core.ConfigSource{
  2925  								ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{
  2926  									ApiConfigSource: &core.ApiConfigSource{
  2927  										ApiType:                   core.ApiConfigSource_GRPC,
  2928  										SetNodeOnFirstMessageOnly: true,
  2929  										TransportApiVersion:       core.ApiVersion_V3,
  2930  										GrpcServices: []*core.GrpcService{
  2931  											{
  2932  												TargetSpecifier: &core.GrpcService_EnvoyGrpc_{
  2933  													EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"},
  2934  												},
  2935  											},
  2936  										},
  2937  									},
  2938  								},
  2939  								ResourceApiVersion: core.ApiVersion_V3,
  2940  							},
  2941  						},
  2942  					},
  2943  					ValidationContextType: &tls.CommonTlsContext_CombinedValidationContext{
  2944  						CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{
  2945  							DefaultValidationContext: &tls.CertificateValidationContext{MatchSubjectAltNames: util.StringToExactMatch([]string{"foo.default.svc.cluster.local"})},
  2946  							ValidationContextSdsSecretConfig: &tls.SdsSecretConfig{
  2947  								Name: "file-root:" + constants.RootCertFilename,
  2948  								SdsConfig: &core.ConfigSource{
  2949  									ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{
  2950  										ApiConfigSource: &core.ApiConfigSource{
  2951  											ApiType:                   core.ApiConfigSource_GRPC,
  2952  											SetNodeOnFirstMessageOnly: true,
  2953  											TransportApiVersion:       core.ApiVersion_V3,
  2954  											GrpcServices: []*core.GrpcService{
  2955  												{
  2956  													TargetSpecifier: &core.GrpcService_EnvoyGrpc_{
  2957  														EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"},
  2958  													},
  2959  												},
  2960  											},
  2961  										},
  2962  									},
  2963  									ResourceApiVersion: core.ApiVersion_V3,
  2964  								},
  2965  							},
  2966  						},
  2967  					},
  2968  				},
  2969  				Sni: "foo.default.svc.cluster.local",
  2970  			},
  2971  		},
  2972  		{
  2973  			name:        "With tls mode mutual, InsecureSkipVerify is set false and ca cert is supplied",
  2974  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
  2975  			clusterMode: DefaultClusterMode,
  2976  			service:     service,
  2977  			port:        servicePort[0],
  2978  			proxyView:   model.ProxyViewAll,
  2979  			destRule: &networking.DestinationRule{
  2980  				Host: "foo.default.svc.cluster.local",
  2981  				TrafficPolicy: &networking.TrafficPolicy{
  2982  					Tls: &networking.ClientTLSSettings{
  2983  						Mode:               networking.ClientTLSSettings_MUTUAL,
  2984  						ClientCertificate:  "cert",
  2985  						PrivateKey:         "key",
  2986  						CaCertificates:     constants.RootCertFilename,
  2987  						Sni:                "foo.default.svc.cluster.local",
  2988  						SubjectAltNames:    []string{"foo.default.svc.cluster.local"},
  2989  						InsecureSkipVerify: &wrappers.BoolValue{Value: false},
  2990  					},
  2991  				},
  2992  			},
  2993  			enableAutoSni:            false,
  2994  			enableVerifyCertAtClient: false,
  2995  			expectTLSContext: &tls.UpstreamTlsContext{
  2996  				CommonTlsContext: &tls.CommonTlsContext{
  2997  					TlsParams: &tls.TlsParameters{
  2998  						// if not specified, envoy use TLSv1_2 as default for client.
  2999  						TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3,
  3000  						TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2,
  3001  					},
  3002  					TlsCertificateSdsSecretConfigs: []*tls.SdsSecretConfig{
  3003  						{
  3004  							Name: "file-cert:cert~key",
  3005  							SdsConfig: &core.ConfigSource{
  3006  								ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{
  3007  									ApiConfigSource: &core.ApiConfigSource{
  3008  										ApiType:                   core.ApiConfigSource_GRPC,
  3009  										SetNodeOnFirstMessageOnly: true,
  3010  										TransportApiVersion:       core.ApiVersion_V3,
  3011  										GrpcServices: []*core.GrpcService{
  3012  											{
  3013  												TargetSpecifier: &core.GrpcService_EnvoyGrpc_{
  3014  													EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"},
  3015  												},
  3016  											},
  3017  										},
  3018  									},
  3019  								},
  3020  								ResourceApiVersion: core.ApiVersion_V3,
  3021  							},
  3022  						},
  3023  					},
  3024  					ValidationContextType: &tls.CommonTlsContext_CombinedValidationContext{
  3025  						CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{
  3026  							DefaultValidationContext: &tls.CertificateValidationContext{MatchSubjectAltNames: util.StringToExactMatch([]string{"foo.default.svc.cluster.local"})},
  3027  							ValidationContextSdsSecretConfig: &tls.SdsSecretConfig{
  3028  								Name: "file-root:" + constants.RootCertFilename,
  3029  								SdsConfig: &core.ConfigSource{
  3030  									ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{
  3031  										ApiConfigSource: &core.ApiConfigSource{
  3032  											ApiType:                   core.ApiConfigSource_GRPC,
  3033  											SetNodeOnFirstMessageOnly: true,
  3034  											TransportApiVersion:       core.ApiVersion_V3,
  3035  											GrpcServices: []*core.GrpcService{
  3036  												{
  3037  													TargetSpecifier: &core.GrpcService_EnvoyGrpc_{
  3038  														EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"},
  3039  													},
  3040  												},
  3041  											},
  3042  										},
  3043  									},
  3044  									ResourceApiVersion: core.ApiVersion_V3,
  3045  								},
  3046  							},
  3047  						},
  3048  					},
  3049  				},
  3050  				Sni: "foo.default.svc.cluster.local",
  3051  			},
  3052  		},
  3053  		{
  3054  			name:        "With tls mode mutual, InsecureSkipVerify is set true and env VERIFY_CERTIFICATE_AT_CLIENT is true",
  3055  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
  3056  			clusterMode: DefaultClusterMode,
  3057  			service:     service,
  3058  			port:        servicePort[0],
  3059  			proxyView:   model.ProxyViewAll,
  3060  			destRule: &networking.DestinationRule{
  3061  				Host: "foo.default.svc.cluster.local",
  3062  				TrafficPolicy: &networking.TrafficPolicy{
  3063  					Tls: &networking.ClientTLSSettings{
  3064  						Mode:               networking.ClientTLSSettings_MUTUAL,
  3065  						ClientCertificate:  "cert",
  3066  						PrivateKey:         "key",
  3067  						Sni:                "foo.default.svc.cluster.local",
  3068  						SubjectAltNames:    []string{"foo.default.svc.cluster.local"},
  3069  						InsecureSkipVerify: &wrappers.BoolValue{Value: true},
  3070  					},
  3071  				},
  3072  			},
  3073  			enableAutoSni:            false,
  3074  			enableVerifyCertAtClient: true,
  3075  			expectTLSContext: &tls.UpstreamTlsContext{
  3076  				CommonTlsContext: &tls.CommonTlsContext{
  3077  					TlsParams: &tls.TlsParameters{
  3078  						// if not specified, envoy use TLSv1_2 as default for client.
  3079  						TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3,
  3080  						TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2,
  3081  					},
  3082  					TlsCertificateSdsSecretConfigs: []*tls.SdsSecretConfig{
  3083  						{
  3084  							Name: "file-cert:cert~key",
  3085  							SdsConfig: &core.ConfigSource{
  3086  								ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{
  3087  									ApiConfigSource: &core.ApiConfigSource{
  3088  										ApiType:                   core.ApiConfigSource_GRPC,
  3089  										SetNodeOnFirstMessageOnly: true,
  3090  										TransportApiVersion:       core.ApiVersion_V3,
  3091  										GrpcServices: []*core.GrpcService{
  3092  											{
  3093  												TargetSpecifier: &core.GrpcService_EnvoyGrpc_{
  3094  													EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"},
  3095  												},
  3096  											},
  3097  										},
  3098  									},
  3099  								},
  3100  								ResourceApiVersion: core.ApiVersion_V3,
  3101  							},
  3102  						},
  3103  					},
  3104  					ValidationContextType: &tls.CommonTlsContext_ValidationContext{},
  3105  				},
  3106  				Sni: "foo.default.svc.cluster.local",
  3107  			},
  3108  		},
  3109  		{
  3110  			name:        "With tls mode mutual, InsecureSkipVerify is set true and env VERIFY_CERTIFICATE_AT_CLIENT is true and AUTO_SNI is true",
  3111  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
  3112  			clusterMode: DefaultClusterMode,
  3113  			service:     service,
  3114  			port:        servicePort[0],
  3115  			proxyView:   model.ProxyViewAll,
  3116  			destRule: &networking.DestinationRule{
  3117  				Host: "foo.default.svc.cluster.local",
  3118  				TrafficPolicy: &networking.TrafficPolicy{
  3119  					Tls: &networking.ClientTLSSettings{
  3120  						Mode:               networking.ClientTLSSettings_MUTUAL,
  3121  						ClientCertificate:  "cert",
  3122  						PrivateKey:         "key",
  3123  						SubjectAltNames:    []string{"foo.default.svc.cluster.local"},
  3124  						InsecureSkipVerify: &wrappers.BoolValue{Value: true},
  3125  					},
  3126  				},
  3127  			},
  3128  			enableAutoSni:            true,
  3129  			enableVerifyCertAtClient: true,
  3130  			expectTLSContext: &tls.UpstreamTlsContext{
  3131  				CommonTlsContext: &tls.CommonTlsContext{
  3132  					TlsParams: &tls.TlsParameters{
  3133  						// if not specified, envoy use TLSv1_2 as default for client.
  3134  						TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3,
  3135  						TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2,
  3136  					},
  3137  					TlsCertificateSdsSecretConfigs: []*tls.SdsSecretConfig{
  3138  						{
  3139  							Name: "file-cert:cert~key",
  3140  							SdsConfig: &core.ConfigSource{
  3141  								ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{
  3142  									ApiConfigSource: &core.ApiConfigSource{
  3143  										ApiType:                   core.ApiConfigSource_GRPC,
  3144  										SetNodeOnFirstMessageOnly: true,
  3145  										TransportApiVersion:       core.ApiVersion_V3,
  3146  										GrpcServices: []*core.GrpcService{
  3147  											{
  3148  												TargetSpecifier: &core.GrpcService_EnvoyGrpc_{
  3149  													EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"},
  3150  												},
  3151  											},
  3152  										},
  3153  									},
  3154  								},
  3155  								ResourceApiVersion: core.ApiVersion_V3,
  3156  							},
  3157  						},
  3158  					},
  3159  					ValidationContextType: &tls.CommonTlsContext_ValidationContext{},
  3160  				},
  3161  			},
  3162  		},
  3163  		{
  3164  			name:        "With tls mode mutual and CredentialName, InsecureSkipVerify is set true and env VERIFY_CERTIFICATE_AT_CLIENT is true",
  3165  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
  3166  			clusterMode: DefaultClusterMode,
  3167  			service:     service,
  3168  			port:        servicePort[0],
  3169  			proxyView:   model.ProxyViewAll,
  3170  			destRule: &networking.DestinationRule{
  3171  				Host: "foo.default.svc.cluster.local",
  3172  				TrafficPolicy: &networking.TrafficPolicy{
  3173  					Tls: &networking.ClientTLSSettings{
  3174  						Mode:               networking.ClientTLSSettings_MUTUAL,
  3175  						CredentialName:     "server-cert",
  3176  						Sni:                "foo.default.svc.cluster.local",
  3177  						SubjectAltNames:    []string{"foo.default.svc.cluster.local"},
  3178  						InsecureSkipVerify: &wrappers.BoolValue{Value: true},
  3179  					},
  3180  				},
  3181  				WorkloadSelector: &v1beta1.WorkloadSelector{},
  3182  			},
  3183  			enableAutoSni:            false,
  3184  			enableVerifyCertAtClient: true,
  3185  			expectTLSContext: &tls.UpstreamTlsContext{
  3186  				CommonTlsContext: &tls.CommonTlsContext{
  3187  					TlsParams: &tls.TlsParameters{
  3188  						// if not specified, envoy use TLSv1_2 as default for client.
  3189  						TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3,
  3190  						TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2,
  3191  					},
  3192  					TlsCertificateSdsSecretConfigs: []*tls.SdsSecretConfig{
  3193  						{
  3194  							Name: "kubernetes://server-cert",
  3195  							SdsConfig: &core.ConfigSource{
  3196  								ConfigSourceSpecifier: &core.ConfigSource_Ads{
  3197  									Ads: &core.AggregatedConfigSource{},
  3198  								},
  3199  								ResourceApiVersion: core.ApiVersion_V3,
  3200  							},
  3201  						},
  3202  					},
  3203  				},
  3204  				Sni: "foo.default.svc.cluster.local",
  3205  			},
  3206  		},
  3207  		{
  3208  			name:        "With tls mode istio mutual, InsecureSkipVerify is set true",
  3209  			cluster:     &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}},
  3210  			clusterMode: DefaultClusterMode,
  3211  			service:     service,
  3212  			port:        servicePort[0],
  3213  			proxyView:   model.ProxyViewAll,
  3214  			destRule: &networking.DestinationRule{
  3215  				Host: "foo.default.svc.cluster.local",
  3216  				TrafficPolicy: &networking.TrafficPolicy{
  3217  					Tls: &networking.ClientTLSSettings{
  3218  						Mode:               networking.ClientTLSSettings_ISTIO_MUTUAL,
  3219  						Sni:                "foo.default.svc.cluster.local",
  3220  						SubjectAltNames:    []string{"foo.default.svc.cluster.local"},
  3221  						InsecureSkipVerify: &wrappers.BoolValue{Value: true},
  3222  					},
  3223  				},
  3224  			},
  3225  			enableAutoSni:            false,
  3226  			enableVerifyCertAtClient: true,
  3227  			expectTLSContext: &tls.UpstreamTlsContext{
  3228  				CommonTlsContext: &tls.CommonTlsContext{
  3229  					TlsParams: &tls.TlsParameters{
  3230  						// if not specified, envoy use TLSv1_2 as default for client.
  3231  						TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3,
  3232  						TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2,
  3233  					},
  3234  					TlsCertificateSdsSecretConfigs: []*tls.SdsSecretConfig{
  3235  						{
  3236  							Name: authn_model.SDSDefaultResourceName,
  3237  							SdsConfig: &core.ConfigSource{
  3238  								ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{
  3239  									ApiConfigSource: &core.ApiConfigSource{
  3240  										ApiType:                   core.ApiConfigSource_GRPC,
  3241  										SetNodeOnFirstMessageOnly: true,
  3242  										TransportApiVersion:       core.ApiVersion_V3,
  3243  										GrpcServices: []*core.GrpcService{
  3244  											{
  3245  												TargetSpecifier: &core.GrpcService_EnvoyGrpc_{
  3246  													EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"},
  3247  												},
  3248  											},
  3249  										},
  3250  									},
  3251  								},
  3252  								ResourceApiVersion:  core.ApiVersion_V3,
  3253  								InitialFetchTimeout: durationpb.New(time.Second * 0),
  3254  							},
  3255  						},
  3256  					},
  3257  					ValidationContextType: &tls.CommonTlsContext_CombinedValidationContext{
  3258  						CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{
  3259  							DefaultValidationContext: &tls.CertificateValidationContext{MatchSubjectAltNames: util.StringToExactMatch([]string{"foo.default.svc.cluster.local"})},
  3260  							ValidationContextSdsSecretConfig: &tls.SdsSecretConfig{
  3261  								Name: authn_model.SDSRootResourceName,
  3262  								SdsConfig: &core.ConfigSource{
  3263  									ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{
  3264  										ApiConfigSource: &core.ApiConfigSource{
  3265  											ApiType:                   core.ApiConfigSource_GRPC,
  3266  											SetNodeOnFirstMessageOnly: true,
  3267  											TransportApiVersion:       core.ApiVersion_V3,
  3268  											GrpcServices: []*core.GrpcService{
  3269  												{
  3270  													TargetSpecifier: &core.GrpcService_EnvoyGrpc_{
  3271  														EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"},
  3272  													},
  3273  												},
  3274  											},
  3275  										},
  3276  									},
  3277  									ResourceApiVersion:  core.ApiVersion_V3,
  3278  									InitialFetchTimeout: durationpb.New(time.Second * 0),
  3279  								},
  3280  							},
  3281  						},
  3282  					},
  3283  					AlpnProtocols: util.ALPNInMeshWithMxc,
  3284  				},
  3285  				Sni: "foo.default.svc.cluster.local",
  3286  			},
  3287  		},
  3288  	}
  3289  
  3290  	for _, tc := range cases {
  3291  		t.Run(tc.name, func(t *testing.T) {
  3292  			test.SetForTest(t, &features.EnableAutoSni, tc.enableAutoSni)
  3293  			test.SetForTest(t, &features.VerifyCertAtClient, tc.enableVerifyCertAtClient)
  3294  
  3295  			targets := []model.ServiceTarget{
  3296  				{
  3297  					Service: tc.service,
  3298  					Port: model.ServiceInstancePort{
  3299  						ServicePort: tc.port,
  3300  						TargetPort:  10001,
  3301  					},
  3302  				},
  3303  			}
  3304  
  3305  			var cfg *config.Config
  3306  			if tc.destRule != nil {
  3307  				cfg = &config.Config{
  3308  					Meta: config.Meta{
  3309  						GroupVersionKind: gvk.DestinationRule,
  3310  						Name:             "acme",
  3311  						Namespace:        "default",
  3312  					},
  3313  					Spec: tc.destRule,
  3314  				}
  3315  			}
  3316  
  3317  			cg := NewConfigGenTest(t, TestOptions{
  3318  				ConfigPointers: []*config.Config{cfg},
  3319  				Services:       []*model.Service{tc.service},
  3320  			})
  3321  
  3322  			cg.MemRegistry.WantGetProxyServiceTargets = targets
  3323  			proxy := cg.SetupProxy(nil)
  3324  			cb := NewClusterBuilder(proxy, &model.PushRequest{Push: cg.PushContext()}, nil)
  3325  			ec := newClusterWrapper(tc.cluster)
  3326  			tc.cluster.CommonLbConfig = &cluster.Cluster_CommonLbConfig{}
  3327  			destRule := proxy.SidecarScope.DestinationRule(model.TrafficDirectionOutbound, proxy, tc.service.Hostname)
  3328  			eb := endpoints.NewCDSEndpointBuilder(proxy, cb.req.Push, tc.cluster.Name,
  3329  				model.TrafficDirectionOutbound, "", service.Hostname, tc.port.Port,
  3330  				service, destRule)
  3331  
  3332  			_ = cb.applyDestinationRule(ec, tc.clusterMode, tc.service, tc.port, eb, destRule.GetRule(), tc.serviceAcct)
  3333  
  3334  			result := getTLSContext(t, ec.cluster)
  3335  			if diff := cmp.Diff(result, tc.expectTLSContext, protocmp.Transform()); diff != "" {
  3336  				t.Errorf("got diff: `%v", diff)
  3337  			}
  3338  
  3339  			if tc.enableAutoSni {
  3340  				if tc.destRule.GetTrafficPolicy().GetTls().Sni == "" {
  3341  					assert.Equal(t, ec.httpProtocolOptions.UpstreamHttpProtocolOptions.AutoSni, true)
  3342  				}
  3343  
  3344  				if tc.destRule.GetTrafficPolicy().GetTls().GetInsecureSkipVerify().GetValue() {
  3345  					assert.Equal(t, ec.httpProtocolOptions.UpstreamHttpProtocolOptions.AutoSanValidation, false)
  3346  				} else if tc.enableVerifyCertAtClient && len(tc.destRule.GetTrafficPolicy().GetTls().SubjectAltNames) == 0 {
  3347  					assert.Equal(t, ec.httpProtocolOptions.UpstreamHttpProtocolOptions.AutoSanValidation, true)
  3348  				}
  3349  			}
  3350  		})
  3351  	}
  3352  }