istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/networking/core/cluster_cache.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  	"strconv"
    19  	"strings"
    20  
    21  	core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    22  
    23  	"istio.io/istio/pilot/pkg/model"
    24  	"istio.io/istio/pilot/pkg/networking/util"
    25  	"istio.io/istio/pilot/pkg/xds/endpoints"
    26  	"istio.io/istio/pkg/config/schema/kind"
    27  	"istio.io/istio/pkg/util/hash"
    28  )
    29  
    30  var (
    31  	Separator = []byte{'~'}
    32  	Slash     = []byte{'/'}
    33  )
    34  
    35  // clusterCache includes the variables that can influence a Cluster Configuration.
    36  // Implements XdsCacheEntry interface.
    37  type clusterCache struct {
    38  	clusterName string
    39  
    40  	// proxy related cache fields
    41  	proxyVersion    string         // will be matched by envoyfilter patches
    42  	locality        *core.Locality // identifies the locality the cluster is generated for
    43  	proxyClusterID  string         // identifies the kubernetes cluster a proxy is in
    44  	proxySidecar    bool           // identifies if this proxy is a Sidecar
    45  	hbone           bool
    46  	proxyView       model.ProxyView
    47  	metadataCerts   *metadataCerts // metadata certificates of proxy
    48  	endpointBuilder *endpoints.EndpointBuilder
    49  
    50  	// service attributes
    51  	http2          bool // http2 identifies if the cluster is for an http2 service
    52  	downstreamAuto bool
    53  	supportsIPv4   bool
    54  
    55  	// dependent configs
    56  	service         *model.Service
    57  	destinationRule *model.ConsolidatedDestRule
    58  	envoyFilterKeys []string
    59  	peerAuthVersion string   // identifies the versions of all peer authentications
    60  	serviceAccounts []string // contains all the service accounts associated with the service
    61  }
    62  
    63  func (t *clusterCache) Type() string {
    64  	return model.CDSType
    65  }
    66  
    67  func (t *clusterCache) Key() any {
    68  	// nolint: gosec
    69  	// Not security sensitive code
    70  	h := hash.New()
    71  	h.WriteString(t.clusterName)
    72  	h.Write(Separator)
    73  	h.WriteString(t.proxyVersion)
    74  	h.Write(Separator)
    75  	h.WriteString(util.LocalityToString(t.locality))
    76  	h.Write(Separator)
    77  	h.WriteString(t.proxyClusterID)
    78  	h.Write(Separator)
    79  	h.WriteString(strconv.FormatBool(t.proxySidecar))
    80  	h.Write(Separator)
    81  	h.WriteString(strconv.FormatBool(t.http2))
    82  	h.Write(Separator)
    83  	h.WriteString(strconv.FormatBool(t.downstreamAuto))
    84  	h.Write(Separator)
    85  	h.WriteString(strconv.FormatBool(t.supportsIPv4))
    86  	h.Write(Separator)
    87  	h.WriteString(strconv.FormatBool(t.hbone))
    88  	h.Write(Separator)
    89  
    90  	if t.proxyView != nil {
    91  		h.WriteString(t.proxyView.String())
    92  	}
    93  	h.Write(Separator)
    94  
    95  	if t.metadataCerts != nil {
    96  		h.WriteString(t.metadataCerts.String())
    97  	}
    98  	h.Write(Separator)
    99  
   100  	if t.service != nil {
   101  		h.WriteString(string(t.service.Hostname))
   102  		h.Write(Slash)
   103  		h.WriteString(t.service.Attributes.Namespace)
   104  	}
   105  	h.Write(Separator)
   106  
   107  	for _, dr := range t.destinationRule.GetFrom() {
   108  		h.WriteString(dr.Name)
   109  		h.Write(Slash)
   110  		h.WriteString(dr.Namespace)
   111  	}
   112  	h.Write(Separator)
   113  
   114  	for _, efk := range t.envoyFilterKeys {
   115  		h.WriteString(efk)
   116  		h.Write(Separator)
   117  	}
   118  	h.Write(Separator)
   119  
   120  	h.WriteString(t.peerAuthVersion)
   121  	h.Write(Separator)
   122  
   123  	for _, sa := range t.serviceAccounts {
   124  		h.WriteString(sa)
   125  		h.Write(Separator)
   126  	}
   127  	h.Write(Separator)
   128  
   129  	if t.endpointBuilder != nil {
   130  		t.endpointBuilder.WriteHash(h)
   131  	}
   132  
   133  	return h.Sum64()
   134  }
   135  
   136  func (t *clusterCache) DependentConfigs() []model.ConfigHash {
   137  	drs := t.destinationRule.GetFrom()
   138  	configs := make([]model.ConfigHash, 0, len(drs)+1+len(t.envoyFilterKeys))
   139  	if t.destinationRule != nil {
   140  		for _, dr := range drs {
   141  			configs = append(configs, model.ConfigKey{Kind: kind.DestinationRule, Name: dr.Name, Namespace: dr.Namespace}.HashCode())
   142  		}
   143  	}
   144  	if t.service != nil {
   145  		configs = append(configs, model.ConfigKey{Kind: kind.ServiceEntry, Name: string(t.service.Hostname), Namespace: t.service.Attributes.Namespace}.HashCode())
   146  	}
   147  	for _, efKey := range t.envoyFilterKeys {
   148  		items := strings.Split(efKey, "/")
   149  		configs = append(configs, model.ConfigKey{Kind: kind.EnvoyFilter, Name: items[1], Namespace: items[0]}.HashCode())
   150  	}
   151  
   152  	// For now, this matches EndpointBuilder's DependentConfigs. No need to duplicate them.
   153  
   154  	return configs
   155  }
   156  
   157  func (t *clusterCache) Cacheable() bool {
   158  	return true
   159  }
   160  
   161  // cacheStats keeps track of cache usage stats.
   162  type cacheStats struct {
   163  	hits, miss int
   164  }
   165  
   166  func (c cacheStats) empty() bool {
   167  	return c.hits == 0 && c.miss == 0
   168  }
   169  
   170  func (c cacheStats) merge(other cacheStats) cacheStats {
   171  	return cacheStats{
   172  		hits: c.hits + other.hits,
   173  		miss: c.miss + other.miss,
   174  	}
   175  }
   176  
   177  func buildClusterKey(service *model.Service, port *model.Port, cb *ClusterBuilder, proxy *model.Proxy, efKeys []string) clusterCache {
   178  	clusterName := model.BuildSubsetKey(model.TrafficDirectionOutbound, "", service.Hostname, port.Port)
   179  	dr := proxy.SidecarScope.DestinationRule(model.TrafficDirectionOutbound, proxy, service.Hostname)
   180  	var eb *endpoints.EndpointBuilder
   181  	if service.Resolution == model.DNSLB || service.Resolution == model.DNSRoundRobinLB {
   182  		eb = endpoints.NewCDSEndpointBuilder(
   183  			proxy,
   184  			cb.req.Push,
   185  			clusterName,
   186  			model.TrafficDirectionOutbound, "", service.Hostname, port.Port,
   187  			service, dr,
   188  		)
   189  	}
   190  	return clusterCache{
   191  		clusterName:     clusterName,
   192  		proxyVersion:    cb.proxyVersion,
   193  		locality:        cb.locality,
   194  		proxyClusterID:  cb.clusterID,
   195  		proxySidecar:    cb.sidecarProxy(),
   196  		proxyView:       cb.proxyView,
   197  		hbone:           cb.sendHbone,
   198  		http2:           port.Protocol.IsHTTP2(),
   199  		downstreamAuto:  cb.sidecarProxy() && port.Protocol.IsUnsupported(),
   200  		supportsIPv4:    cb.supportsIPv4,
   201  		service:         service,
   202  		destinationRule: dr,
   203  		envoyFilterKeys: efKeys,
   204  		metadataCerts:   cb.metadataCerts,
   205  		peerAuthVersion: cb.req.Push.AuthnPolicies.GetVersion(),
   206  		serviceAccounts: cb.req.Push.ServiceAccounts(service.Hostname, service.Attributes.Namespace),
   207  		endpointBuilder: eb,
   208  	}
   209  }