istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/networking/core/route/route_cache.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 route
    16  
    17  import (
    18  	"fmt"
    19  	"math/big"
    20  	"strconv"
    21  	"strings"
    22  
    23  	networking "istio.io/api/networking/v1alpha3"
    24  	"istio.io/istio/pilot/pkg/model"
    25  	"istio.io/istio/pkg/config"
    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  // Cache includes the variables that can influence a Route Configuration.
    36  // Implements XdsCacheEntry interface.
    37  type Cache struct {
    38  	RouteName string
    39  
    40  	ProxyVersion string
    41  	// proxy cluster ID
    42  	ClusterID string
    43  	// proxy dns domain
    44  	DNSDomain string
    45  	// DNSCapture indicates whether the workload has enabled dns capture
    46  	DNSCapture bool
    47  	// DNSAutoAllocate indicates whether the workload should have auto allocated addresses for ServiceEntry
    48  	// This allows resolving ServiceEntries, which is especially useful for distinguishing TCP traffic
    49  	// This depends on DNSCapture.
    50  	DNSAutoAllocate bool
    51  	// AllowAny indicates if the proxy should allow all outbound traffic or only known registries
    52  	AllowAny bool
    53  
    54  	ListenerPort            int
    55  	Services                []*model.Service
    56  	VirtualServices         []config.Config
    57  	DelegateVirtualServices []model.ConfigHash
    58  	DestinationRules        []*model.ConsolidatedDestRule
    59  	EnvoyFilterKeys         []string
    60  }
    61  
    62  func (r *Cache) Type() string {
    63  	return model.RDSType
    64  }
    65  
    66  func (r *Cache) Cacheable() bool {
    67  	if r == nil {
    68  		return false
    69  	}
    70  	if r.ListenerPort == 0 {
    71  		return false
    72  	}
    73  
    74  	for _, config := range r.VirtualServices {
    75  		vs := config.Spec.(*networking.VirtualService)
    76  		for _, httpRoute := range vs.Http {
    77  			for _, match := range httpRoute.Match {
    78  				// if vs has source match, not cacheable
    79  				if len(match.SourceLabels) > 0 || match.SourceNamespace != "" {
    80  					return false
    81  				}
    82  			}
    83  		}
    84  	}
    85  
    86  	return true
    87  }
    88  
    89  func extractNamespaceForKubernetesService(hostname string) (string, error) {
    90  	ih := strings.Index(hostname, ".svc.")
    91  	if ih < 0 {
    92  		return "", fmt.Errorf("hostname is a not a Kubernetes name, missing .svc: %v", hostname)
    93  	}
    94  	nsI := strings.Index(hostname, ".")
    95  	if nsI+1 >= len(hostname) || nsI+1 > ih {
    96  		// Invalid domain
    97  		return "", fmt.Errorf("hostname is a not a Kubernetes name, missing namespace: %v", hostname)
    98  	}
    99  	ns := hostname[nsI+1 : ih]
   100  	if len(ns) == 0 {
   101  		return "", fmt.Errorf("namespace not found")
   102  	}
   103  	return ns, nil
   104  }
   105  
   106  func (r *Cache) DependentConfigs() []model.ConfigHash {
   107  	size := len(r.Services) + len(r.VirtualServices) + len(r.DelegateVirtualServices) + len(r.EnvoyFilterKeys)
   108  	for _, mergedDR := range r.DestinationRules {
   109  		size += len(mergedDR.GetFrom())
   110  	}
   111  	configs := make([]model.ConfigHash, 0, size)
   112  	for _, svc := range r.Services {
   113  		configs = append(configs, model.ConfigKey{
   114  			Kind:      kind.ServiceEntry,
   115  			Name:      string(svc.Hostname),
   116  			Namespace: svc.Attributes.Namespace,
   117  		}.HashCode())
   118  		for _, alias := range svc.Attributes.Aliases {
   119  			configs = append(configs, model.ConfigKey{Kind: kind.ServiceEntry, Name: alias.Hostname.String(), Namespace: alias.Namespace}.HashCode())
   120  		}
   121  	}
   122  	for _, vs := range r.VirtualServices {
   123  		for _, cfg := range model.VirtualServiceDependencies(vs) {
   124  			configs = append(configs, cfg.HashCode())
   125  		}
   126  	}
   127  	// add delegate virtual services to dependent configs
   128  	// so that we can clear the rds cache when delegate virtual services are updated
   129  	configs = append(configs, r.DelegateVirtualServices...)
   130  	for _, mergedDR := range r.DestinationRules {
   131  		for _, dr := range mergedDR.GetFrom() {
   132  			configs = append(configs, model.ConfigKey{Kind: kind.DestinationRule, Name: dr.Name, Namespace: dr.Namespace}.HashCode())
   133  		}
   134  	}
   135  
   136  	for _, efKey := range r.EnvoyFilterKeys {
   137  		items := strings.Split(efKey, "/")
   138  		configs = append(configs, model.ConfigKey{Kind: kind.EnvoyFilter, Name: items[1], Namespace: items[0]}.HashCode())
   139  	}
   140  	return configs
   141  }
   142  
   143  func (r *Cache) Key() any {
   144  	// nolint: gosec
   145  	// Not security sensitive code
   146  	h := hash.New()
   147  
   148  	h.WriteString(r.RouteName)
   149  	h.Write(Separator)
   150  	h.WriteString(r.ProxyVersion)
   151  	h.Write(Separator)
   152  	h.WriteString(r.ClusterID)
   153  	h.Write(Separator)
   154  	h.WriteString(r.DNSDomain)
   155  	h.Write(Separator)
   156  	h.WriteString(strconv.FormatBool(r.DNSCapture))
   157  	h.Write(Separator)
   158  	h.WriteString(strconv.FormatBool(r.DNSAutoAllocate))
   159  	h.Write(Separator)
   160  	h.WriteString(strconv.FormatBool(r.AllowAny))
   161  	h.Write(Separator)
   162  
   163  	for _, svc := range r.Services {
   164  		h.WriteString(string(svc.Hostname))
   165  		h.Write(Slash)
   166  		h.WriteString(svc.Attributes.Namespace)
   167  		h.Write(Separator)
   168  	}
   169  	h.Write(Separator)
   170  
   171  	for _, vs := range r.VirtualServices {
   172  		for _, cfg := range model.VirtualServiceDependencies(vs) {
   173  			h.WriteString(cfg.Kind.String())
   174  			h.Write(Slash)
   175  			h.WriteString(cfg.Name)
   176  			h.Write(Slash)
   177  			h.WriteString(cfg.Namespace)
   178  			h.Write(Separator)
   179  		}
   180  	}
   181  	h.Write(Separator)
   182  
   183  	for _, vs := range r.DelegateVirtualServices {
   184  		h.Write(hashToBytes(vs))
   185  		h.Write(Separator)
   186  	}
   187  	h.Write(Separator)
   188  
   189  	for _, mergedDR := range r.DestinationRules {
   190  		for _, dr := range mergedDR.GetFrom() {
   191  			h.WriteString(dr.Name)
   192  			h.Write(Slash)
   193  			h.WriteString(dr.Namespace)
   194  			h.Write(Separator)
   195  		}
   196  	}
   197  	h.Write(Separator)
   198  
   199  	for _, efk := range r.EnvoyFilterKeys {
   200  		h.WriteString(efk)
   201  		h.Write(Separator)
   202  	}
   203  	h.Write(Separator)
   204  
   205  	return h.Sum64()
   206  }
   207  
   208  func hashToBytes(number model.ConfigHash) []byte {
   209  	big := new(big.Int)
   210  	big.SetUint64(uint64(number))
   211  	return big.Bytes()
   212  }