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

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package core
    16  
    17  import (
    18  	"math"
    19  
    20  	cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
    21  	core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    22  	proxyprotocol "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/proxy_protocol/v3"
    23  	http "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3"
    24  	xdstype "github.com/envoyproxy/go-control-plane/envoy/type/v3"
    25  	"google.golang.org/protobuf/proto"
    26  	"google.golang.org/protobuf/types/known/durationpb"
    27  	"google.golang.org/protobuf/types/known/wrapperspb"
    28  
    29  	meshconfig "istio.io/api/mesh/v1alpha1"
    30  	networking "istio.io/api/networking/v1alpha3"
    31  	"istio.io/istio/pilot/pkg/features"
    32  	"istio.io/istio/pilot/pkg/model"
    33  	"istio.io/istio/pilot/pkg/networking/core/loadbalancer"
    34  	"istio.io/istio/pilot/pkg/util/protoconv"
    35  	"istio.io/istio/pkg/config/protocol"
    36  	"istio.io/istio/pkg/log"
    37  )
    38  
    39  // applyTrafficPolicy applies the trafficPolicy defined within destinationRule,
    40  // which can be called for both outbound and inbound cluster, but only connection pool will be applied to inbound cluster.
    41  func (cb *ClusterBuilder) applyTrafficPolicy(opts buildClusterOpts) {
    42  	connectionPool, outlierDetection, loadBalancer, tls, proxyProtocol := selectTrafficPolicyComponents(opts.policy)
    43  	// Connection pool settings are applicable for both inbound and outbound clusters.
    44  	if connectionPool == nil {
    45  		connectionPool = &networking.ConnectionPoolSettings{}
    46  	}
    47  	cb.applyConnectionPool(opts.mesh, opts.mutable, connectionPool)
    48  	if opts.direction != model.TrafficDirectionInbound {
    49  		cb.applyH2Upgrade(opts.mutable, opts.port, opts.mesh, connectionPool)
    50  		applyOutlierDetection(opts.mutable.cluster, outlierDetection)
    51  		applyLoadBalancer(opts.mutable.cluster, loadBalancer, opts.port, cb.locality, cb.proxyLabels, opts.mesh)
    52  		if opts.clusterMode != SniDnatClusterMode {
    53  			autoMTLSEnabled := opts.mesh.GetEnableAutoMtls().Value
    54  			tls, mtlsCtxType := cb.buildUpstreamTLSSettings(tls, opts.serviceAccounts, opts.istioMtlsSni,
    55  				autoMTLSEnabled, opts.meshExternal, opts.serviceMTLSMode)
    56  			cb.applyUpstreamTLSSettings(&opts, tls, mtlsCtxType)
    57  			cb.applyUpstreamProxyProtocol(&opts, proxyProtocol)
    58  		}
    59  	}
    60  
    61  	if opts.mutable.cluster.GetType() == cluster.Cluster_ORIGINAL_DST {
    62  		opts.mutable.cluster.LbPolicy = cluster.Cluster_CLUSTER_PROVIDED
    63  	}
    64  }
    65  
    66  // selectTrafficPolicyComponents returns the components of TrafficPolicy that should be used for given port.
    67  func selectTrafficPolicyComponents(policy *networking.TrafficPolicy) (
    68  	*networking.ConnectionPoolSettings,
    69  	*networking.OutlierDetection,
    70  	*networking.LoadBalancerSettings,
    71  	*networking.ClientTLSSettings,
    72  	*networking.TrafficPolicy_ProxyProtocol,
    73  ) {
    74  	if policy == nil {
    75  		return nil, nil, nil, nil, nil
    76  	}
    77  	connectionPool := policy.ConnectionPool
    78  	outlierDetection := policy.OutlierDetection
    79  	loadBalancer := policy.LoadBalancer
    80  	tls := policy.Tls
    81  	proxyProtocol := policy.ProxyProtocol
    82  
    83  	// Check if CA Certificate should be System CA Certificate
    84  	if features.VerifyCertAtClient && tls != nil && tls.CaCertificates == "" {
    85  		tls.CaCertificates = "system"
    86  	}
    87  
    88  	return connectionPool, outlierDetection, loadBalancer, tls, proxyProtocol
    89  }
    90  
    91  // FIXME: there isn't a way to distinguish between unset values and zero values
    92  func (cb *ClusterBuilder) applyConnectionPool(mesh *meshconfig.MeshConfig,
    93  	mc *clusterWrapper, settings *networking.ConnectionPoolSettings,
    94  ) {
    95  	if settings == nil {
    96  		return
    97  	}
    98  
    99  	threshold := getDefaultCircuitBreakerThresholds()
   100  	var idleTimeout *durationpb.Duration
   101  	var maxRequestsPerConnection uint32
   102  	var maxConcurrentStreams uint32
   103  	var maxConnectionDuration *durationpb.Duration
   104  
   105  	if settings.Http != nil {
   106  		if settings.Http.Http2MaxRequests > 0 {
   107  			// Envoy only applies MaxRequests in HTTP/2 clusters
   108  			threshold.MaxRequests = &wrapperspb.UInt32Value{Value: uint32(settings.Http.Http2MaxRequests)}
   109  		}
   110  		if settings.Http.Http1MaxPendingRequests > 0 {
   111  			// Envoy only applies MaxPendingRequests in HTTP/1.1 clusters
   112  			threshold.MaxPendingRequests = &wrapperspb.UInt32Value{Value: uint32(settings.Http.Http1MaxPendingRequests)}
   113  		}
   114  
   115  		// FIXME: zero is a valid value if explicitly set, otherwise we want to use the default
   116  		if settings.Http.MaxRetries > 0 {
   117  			threshold.MaxRetries = &wrapperspb.UInt32Value{Value: uint32(settings.Http.MaxRetries)}
   118  		}
   119  
   120  		idleTimeout = settings.Http.IdleTimeout
   121  		maxRequestsPerConnection = uint32(settings.Http.MaxRequestsPerConnection)
   122  		maxConcurrentStreams = uint32(settings.Http.MaxConcurrentStreams)
   123  	}
   124  
   125  	cb.applyDefaultConnectionPool(mc.cluster)
   126  	if settings.Tcp != nil {
   127  		if settings.Tcp.ConnectTimeout != nil {
   128  			mc.cluster.ConnectTimeout = settings.Tcp.ConnectTimeout
   129  		}
   130  
   131  		if settings.Tcp.MaxConnections > 0 {
   132  			threshold.MaxConnections = &wrapperspb.UInt32Value{Value: uint32(settings.Tcp.MaxConnections)}
   133  		}
   134  		if settings.Tcp.MaxConnectionDuration != nil {
   135  			maxConnectionDuration = settings.Tcp.MaxConnectionDuration
   136  		}
   137  		if idleTimeout == nil {
   138  			idleTimeout = settings.Tcp.IdleTimeout
   139  		}
   140  	}
   141  	applyTCPKeepalive(mesh, mc.cluster, settings.Tcp)
   142  
   143  	mc.cluster.CircuitBreakers = &cluster.CircuitBreakers{
   144  		Thresholds: []*cluster.CircuitBreakers_Thresholds{threshold},
   145  	}
   146  
   147  	if maxConnectionDuration != nil || idleTimeout != nil || maxRequestsPerConnection > 0 || maxConcurrentStreams > 0 {
   148  		if mc.httpProtocolOptions == nil {
   149  			mc.httpProtocolOptions = &http.HttpProtocolOptions{}
   150  		}
   151  		options := mc.httpProtocolOptions
   152  		if options.CommonHttpProtocolOptions == nil {
   153  			options.CommonHttpProtocolOptions = &core.HttpProtocolOptions{}
   154  		}
   155  		if idleTimeout != nil {
   156  			idleTimeoutDuration := idleTimeout
   157  			options.CommonHttpProtocolOptions.IdleTimeout = idleTimeoutDuration
   158  		}
   159  		if maxRequestsPerConnection > 0 {
   160  			options.CommonHttpProtocolOptions.MaxRequestsPerConnection = &wrapperspb.UInt32Value{Value: maxRequestsPerConnection}
   161  		}
   162  		if maxConnectionDuration != nil {
   163  			options.CommonHttpProtocolOptions.MaxConnectionDuration = maxConnectionDuration
   164  		}
   165  		// Check if cluster is HTTP2
   166  		http2ProtocolOptions := options.GetExplicitHttpConfig().GetHttp2ProtocolOptions()
   167  		if http2ProtocolOptions != nil && maxConcurrentStreams > 0 {
   168  			http2ProtocolOptions.MaxConcurrentStreams = &wrapperspb.UInt32Value{Value: maxConcurrentStreams}
   169  		}
   170  	}
   171  	if settings.Http != nil && settings.Http.UseClientProtocol {
   172  		// Use downstream protocol. If the incoming traffic use HTTP 1.1, the
   173  		// upstream cluster will use HTTP 1.1, if incoming traffic use HTTP2,
   174  		// the upstream cluster will use HTTP2.
   175  		cb.setUseDownstreamProtocol(mc)
   176  	}
   177  }
   178  
   179  // applyH2Upgrade function will upgrade cluster to http2 if specified by configuration.
   180  // applyH2Upgrade can only be called for outbound cluster
   181  func (cb *ClusterBuilder) applyH2Upgrade(mc *clusterWrapper, port *model.Port,
   182  	mesh *meshconfig.MeshConfig, connectionPool *networking.ConnectionPoolSettings,
   183  ) {
   184  	if shouldH2Upgrade(mc.cluster.Name, port, mesh, connectionPool) {
   185  		setH2Options(mc)
   186  	}
   187  }
   188  
   189  // shouldH2Upgrade function returns true if the cluster should be upgraded to http2.
   190  // shouldH2Upgrade can only be called for outbound cluster
   191  func shouldH2Upgrade(clusterName string, port *model.Port, mesh *meshconfig.MeshConfig,
   192  	connectionPool *networking.ConnectionPoolSettings,
   193  ) bool {
   194  	// TODO (mjog)
   195  	// Upgrade if tls.GetMode() == networking.TLSSettings_ISTIO_MUTUAL
   196  	if connectionPool != nil && connectionPool.Http != nil {
   197  		override := connectionPool.Http.H2UpgradePolicy
   198  		// If user wants an upgrade at destination rule/port level that means he is sure that
   199  		// it is a Http port - upgrade in such case. This is useful incase protocol sniffing is
   200  		// enabled and user wants to upgrade/preserve http protocol from client.
   201  		if override == networking.ConnectionPoolSettings_HTTPSettings_UPGRADE {
   202  			log.Debugf("Upgrading cluster: %v (%v %v)", clusterName, mesh.H2UpgradePolicy, override)
   203  			return true
   204  		}
   205  		if override == networking.ConnectionPoolSettings_HTTPSettings_DO_NOT_UPGRADE {
   206  			log.Debugf("Not upgrading cluster: %v (%v %v)", clusterName, mesh.H2UpgradePolicy, override)
   207  			return false
   208  		}
   209  	}
   210  
   211  	// Do not upgrade non-http ports. This also ensures that we are only upgrading
   212  	// named ports so that protocol sniffing does not interfere. Protocol sniffing
   213  	// uses downstream protocol. Therefore if the client upgrades connection to http2,
   214  	// the server will send h2 stream to the application,even though the application only
   215  	// supports http 1.1.
   216  	if port != nil && !port.Protocol.IsHTTP() {
   217  		return false
   218  	}
   219  
   220  	return mesh.H2UpgradePolicy == meshconfig.MeshConfig_UPGRADE
   221  }
   222  
   223  func (cb *ClusterBuilder) applyDefaultConnectionPool(cluster *cluster.Cluster) {
   224  	cluster.ConnectTimeout = proto.Clone(cb.req.Push.Mesh.ConnectTimeout).(*durationpb.Duration)
   225  }
   226  
   227  func applyLoadBalancer(c *cluster.Cluster, lb *networking.LoadBalancerSettings, port *model.Port,
   228  	locality *core.Locality, proxyLabels map[string]string, meshConfig *meshconfig.MeshConfig,
   229  ) {
   230  	// Disable panic threshold when SendUnhealthyEndpoints is enabled as enabling it "may" send traffic to unready
   231  	// end points when load balancer is in panic mode.
   232  	if features.SendUnhealthyEndpoints.Load() {
   233  		c.CommonLbConfig.HealthyPanicThreshold = &xdstype.Percent{Value: 0}
   234  	}
   235  	localityLbSetting := loadbalancer.GetLocalityLbSetting(meshConfig.GetLocalityLbSetting(), lb.GetLocalityLbSetting())
   236  	if localityLbSetting != nil {
   237  		c.CommonLbConfig.LocalityConfigSpecifier = &cluster.Cluster_CommonLbConfig_LocalityWeightedLbConfig_{
   238  			LocalityWeightedLbConfig: &cluster.Cluster_CommonLbConfig_LocalityWeightedLbConfig{},
   239  		}
   240  	}
   241  	// Use locality lb settings from load balancer settings if present, else use mesh wide locality lb settings
   242  	applyLocalityLoadBalancer(locality, proxyLabels, c, localityLbSetting)
   243  
   244  	if c.GetType() == cluster.Cluster_ORIGINAL_DST {
   245  		c.LbPolicy = cluster.Cluster_CLUSTER_PROVIDED
   246  		return
   247  	}
   248  
   249  	// Redis protocol must be defaulted with MAGLEV to benefit from client side sharding.
   250  	if features.EnableRedisFilter && port != nil && port.Protocol == protocol.Redis {
   251  		c.LbPolicy = cluster.Cluster_MAGLEV
   252  		return
   253  	}
   254  
   255  	// DO not do if else here. since lb.GetSimple returns a enum value (not pointer).
   256  	switch lb.GetSimple() {
   257  	// nolint: staticcheck
   258  	case networking.LoadBalancerSettings_LEAST_CONN, networking.LoadBalancerSettings_LEAST_REQUEST:
   259  		applyLeastRequestLoadBalancer(c, lb)
   260  	case networking.LoadBalancerSettings_RANDOM:
   261  		c.LbPolicy = cluster.Cluster_RANDOM
   262  	case networking.LoadBalancerSettings_ROUND_ROBIN:
   263  		applyRoundRobinLoadBalancer(c, lb)
   264  	case networking.LoadBalancerSettings_PASSTHROUGH:
   265  		c.LbPolicy = cluster.Cluster_CLUSTER_PROVIDED
   266  		c.ClusterDiscoveryType = &cluster.Cluster_Type{Type: cluster.Cluster_ORIGINAL_DST}
   267  		// Wipe out any LoadAssignment, if set. This can occur when we have a STATIC Service but PASSTHROUGH traffic policy
   268  		c.LoadAssignment = nil
   269  	default:
   270  		applySimpleDefaultLoadBalancer(c, lb)
   271  	}
   272  
   273  	ApplyRingHashLoadBalancer(c, lb)
   274  }
   275  
   276  func applyLocalityLoadBalancer(locality *core.Locality, proxyLabels map[string]string, cluster *cluster.Cluster,
   277  	localityLB *networking.LocalityLoadBalancerSetting,
   278  ) {
   279  	// Failover should only be applied with outlier detection, or traffic will never failover.
   280  	enableFailover := cluster.OutlierDetection != nil
   281  	if cluster.LoadAssignment != nil {
   282  		// TODO: enable failoverPriority for `STRICT_DNS` cluster type
   283  		loadbalancer.ApplyLocalityLoadBalancer(cluster.LoadAssignment, nil, locality, proxyLabels, localityLB, enableFailover)
   284  	}
   285  }
   286  
   287  // applySimpleDefaultLoadBalancer will set the DefaultLBPolicy and create an LbConfig if used in LoadBalancerSettings
   288  func applySimpleDefaultLoadBalancer(c *cluster.Cluster, loadbalancer *networking.LoadBalancerSettings) {
   289  	c.LbPolicy = defaultLBAlgorithm()
   290  	switch c.LbPolicy {
   291  	case cluster.Cluster_ROUND_ROBIN:
   292  		applyRoundRobinLoadBalancer(c, loadbalancer)
   293  	case cluster.Cluster_LEAST_REQUEST:
   294  		applyLeastRequestLoadBalancer(c, loadbalancer)
   295  	}
   296  }
   297  
   298  func defaultLBAlgorithm() cluster.Cluster_LbPolicy {
   299  	return cluster.Cluster_LEAST_REQUEST
   300  }
   301  
   302  // applyRoundRobinLoadBalancer will set the LbPolicy and create an LbConfig for ROUND_ROBIN if used in LoadBalancerSettings
   303  func applyRoundRobinLoadBalancer(c *cluster.Cluster, loadbalancer *networking.LoadBalancerSettings) {
   304  	c.LbPolicy = cluster.Cluster_ROUND_ROBIN
   305  
   306  	if loadbalancer.GetWarmupDurationSecs() != nil {
   307  		c.LbConfig = &cluster.Cluster_RoundRobinLbConfig_{
   308  			RoundRobinLbConfig: &cluster.Cluster_RoundRobinLbConfig{
   309  				SlowStartConfig: setSlowStartConfig(loadbalancer.GetWarmupDurationSecs()),
   310  			},
   311  		}
   312  	}
   313  }
   314  
   315  // applyLeastRequestLoadBalancer will set the LbPolicy and create an LbConfig for LEAST_REQUEST if used in LoadBalancerSettings
   316  func applyLeastRequestLoadBalancer(c *cluster.Cluster, loadbalancer *networking.LoadBalancerSettings) {
   317  	c.LbPolicy = cluster.Cluster_LEAST_REQUEST
   318  
   319  	if loadbalancer.GetWarmupDurationSecs() != nil {
   320  		c.LbConfig = &cluster.Cluster_LeastRequestLbConfig_{
   321  			LeastRequestLbConfig: &cluster.Cluster_LeastRequestLbConfig{
   322  				SlowStartConfig: setSlowStartConfig(loadbalancer.GetWarmupDurationSecs()),
   323  			},
   324  		}
   325  	}
   326  }
   327  
   328  // setSlowStartConfig will set the warmupDurationSecs for LEAST_REQUEST and ROUND_ROBIN if provided in DestinationRule
   329  func setSlowStartConfig(dur *durationpb.Duration) *cluster.Cluster_SlowStartConfig {
   330  	return &cluster.Cluster_SlowStartConfig{
   331  		SlowStartWindow: dur,
   332  	}
   333  }
   334  
   335  // getDefaultCircuitBreakerThresholds returns a copy of the default circuit breaker thresholds for the given traffic direction.
   336  func getDefaultCircuitBreakerThresholds() *cluster.CircuitBreakers_Thresholds {
   337  	return &cluster.CircuitBreakers_Thresholds{
   338  		// DefaultMaxRetries specifies the default for the Envoy circuit breaker parameter max_retries. This
   339  		// defines the maximum number of parallel retries a given Envoy will allow to the upstream cluster. Envoy defaults
   340  		// this value to 3, however that has shown to be insufficient during periods of pod churn (e.g. rolling updates),
   341  		// where multiple endpoints in a cluster are terminated. In these scenarios the circuit breaker can kick
   342  		// in before Pilot is able to deliver an updated endpoint list to Envoy, leading to client-facing 503s.
   343  		MaxRetries:         &wrapperspb.UInt32Value{Value: math.MaxUint32},
   344  		MaxRequests:        &wrapperspb.UInt32Value{Value: math.MaxUint32},
   345  		MaxConnections:     &wrapperspb.UInt32Value{Value: math.MaxUint32},
   346  		MaxPendingRequests: &wrapperspb.UInt32Value{Value: math.MaxUint32},
   347  		TrackRemaining:     true,
   348  	}
   349  }
   350  
   351  // FIXME: there isn't a way to distinguish between unset values and zero values
   352  func applyOutlierDetection(c *cluster.Cluster, outlier *networking.OutlierDetection) {
   353  	if outlier == nil {
   354  		return
   355  	}
   356  
   357  	out := &cluster.OutlierDetection{}
   358  
   359  	// SuccessRate based outlier detection should be disabled.
   360  	out.EnforcingSuccessRate = &wrapperspb.UInt32Value{Value: 0}
   361  
   362  	if e := outlier.Consecutive_5XxErrors; e != nil {
   363  		v := e.GetValue()
   364  
   365  		out.Consecutive_5Xx = &wrapperspb.UInt32Value{Value: v}
   366  
   367  		if v > 0 {
   368  			v = 100
   369  		}
   370  		out.EnforcingConsecutive_5Xx = &wrapperspb.UInt32Value{Value: v}
   371  	}
   372  	if e := outlier.ConsecutiveGatewayErrors; e != nil {
   373  		v := e.GetValue()
   374  
   375  		out.ConsecutiveGatewayFailure = &wrapperspb.UInt32Value{Value: v}
   376  
   377  		if v > 0 {
   378  			v = 100
   379  		}
   380  		out.EnforcingConsecutiveGatewayFailure = &wrapperspb.UInt32Value{Value: v}
   381  	}
   382  
   383  	if outlier.Interval != nil {
   384  		out.Interval = outlier.Interval
   385  	}
   386  	if outlier.BaseEjectionTime != nil {
   387  		out.BaseEjectionTime = outlier.BaseEjectionTime
   388  	}
   389  	if outlier.MaxEjectionPercent > 0 {
   390  		out.MaxEjectionPercent = &wrapperspb.UInt32Value{Value: uint32(outlier.MaxEjectionPercent)}
   391  	}
   392  
   393  	if outlier.SplitExternalLocalOriginErrors {
   394  		out.SplitExternalLocalOriginErrors = true
   395  		if outlier.ConsecutiveLocalOriginFailures.GetValue() > 0 {
   396  			out.ConsecutiveLocalOriginFailure = &wrapperspb.UInt32Value{Value: outlier.ConsecutiveLocalOriginFailures.Value}
   397  			out.EnforcingConsecutiveLocalOriginFailure = &wrapperspb.UInt32Value{Value: 100}
   398  		}
   399  		// SuccessRate based outlier detection should be disabled.
   400  		out.EnforcingLocalOriginSuccessRate = &wrapperspb.UInt32Value{Value: 0}
   401  	}
   402  
   403  	c.OutlierDetection = out
   404  
   405  	// Disable panic threshold by default as its not typically applicable in k8s environments
   406  	// with few pods per service.
   407  	// To do so, set the healthy_panic_threshold field even if its value is 0 (defaults to 50 in Envoy).
   408  	// FIXME: we can't distinguish between it being unset or being explicitly set to 0
   409  	minHealthPercent := outlier.MinHealthPercent
   410  	if minHealthPercent >= 0 {
   411  		// When we are sending unhealthy endpoints, we should disable Panic Threshold. Otherwise
   412  		// Envoy will send traffic to "Unready" pods when the percentage of healthy hosts fall
   413  		// below minimum health percentage.
   414  		if features.SendUnhealthyEndpoints.Load() {
   415  			minHealthPercent = 0
   416  		}
   417  		c.CommonLbConfig.HealthyPanicThreshold = &xdstype.Percent{Value: float64(minHealthPercent)}
   418  	}
   419  }
   420  
   421  // ApplyRingHashLoadBalancer will set the LbPolicy and create an LbConfig for RING_HASH if  used in LoadBalancerSettings
   422  func ApplyRingHashLoadBalancer(c *cluster.Cluster, lb *networking.LoadBalancerSettings) {
   423  	consistentHash := lb.GetConsistentHash()
   424  	if consistentHash == nil {
   425  		return
   426  	}
   427  
   428  	switch {
   429  	case consistentHash.GetMaglev() != nil:
   430  		c.LbPolicy = cluster.Cluster_MAGLEV
   431  		if consistentHash.GetMaglev().TableSize != 0 {
   432  			c.LbConfig = &cluster.Cluster_MaglevLbConfig_{
   433  				MaglevLbConfig: &cluster.Cluster_MaglevLbConfig{
   434  					TableSize: &wrapperspb.UInt64Value{Value: consistentHash.GetMaglev().TableSize},
   435  				},
   436  			}
   437  		}
   438  	case consistentHash.GetRingHash() != nil:
   439  		c.LbPolicy = cluster.Cluster_RING_HASH
   440  		if consistentHash.GetRingHash().MinimumRingSize != 0 {
   441  			c.LbConfig = &cluster.Cluster_RingHashLbConfig_{
   442  				RingHashLbConfig: &cluster.Cluster_RingHashLbConfig{
   443  					MinimumRingSize: &wrapperspb.UInt64Value{Value: consistentHash.GetRingHash().MinimumRingSize},
   444  				},
   445  			}
   446  		}
   447  	default:
   448  		// Check the deprecated MinimumRingSize.
   449  		// TODO: MinimumRingSize is an int, and zero could potentially
   450  		// be a valid value unable to distinguish between set and unset
   451  		// case currently.
   452  		// 1024 is the default value for envoy.
   453  		minRingSize := &wrapperspb.UInt64Value{Value: 1024}
   454  
   455  		if consistentHash.MinimumRingSize != 0 { // nolint: staticcheck
   456  			minRingSize = &wrapperspb.UInt64Value{Value: consistentHash.GetMinimumRingSize()} // nolint: staticcheck
   457  		}
   458  		c.LbPolicy = cluster.Cluster_RING_HASH
   459  		c.LbConfig = &cluster.Cluster_RingHashLbConfig_{
   460  			RingHashLbConfig: &cluster.Cluster_RingHashLbConfig{
   461  				MinimumRingSize: minRingSize,
   462  			},
   463  		}
   464  	}
   465  }
   466  
   467  func (cb *ClusterBuilder) applyUpstreamProxyProtocol(
   468  	opts *buildClusterOpts,
   469  	proxyProtocol *networking.TrafficPolicy_ProxyProtocol,
   470  ) {
   471  	if proxyProtocol == nil {
   472  		return
   473  	}
   474  	c := opts.mutable
   475  	if c.cluster.TransportSocket != nil {
   476  		// add an upstream proxy protocol wrapper for transportSocket
   477  		c.cluster.TransportSocket = &core.TransportSocket{
   478  			Name: "envoy.transport_sockets.upstream_proxy_protocol",
   479  			ConfigType: &core.TransportSocket_TypedConfig{TypedConfig: protoconv.MessageToAny(&proxyprotocol.ProxyProtocolUpstreamTransport{
   480  				Config:          &core.ProxyProtocolConfig{Version: core.ProxyProtocolConfig_Version(proxyProtocol.Version)},
   481  				TransportSocket: c.cluster.TransportSocket,
   482  			})},
   483  		}
   484  	}
   485  
   486  	// add an upstream proxy protocol wrapper for each transportSocket
   487  	for _, tsm := range c.cluster.TransportSocketMatches {
   488  		tsm.TransportSocket = &core.TransportSocket{
   489  			Name: "envoy.transport_sockets.upstream_proxy_protocol",
   490  			ConfigType: &core.TransportSocket_TypedConfig{TypedConfig: protoconv.MessageToAny(&proxyprotocol.ProxyProtocolUpstreamTransport{
   491  				Config:          &core.ProxyProtocolConfig{Version: core.ProxyProtocolConfig_Version(proxyProtocol.Version)},
   492  				TransportSocket: tsm.TransportSocket,
   493  			})},
   494  		}
   495  	}
   496  }