istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/model/push_context.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 model
    16  
    17  import (
    18  	"cmp"
    19  	"encoding/json"
    20  	"fmt"
    21  	"math"
    22  	"sort"
    23  	"strings"
    24  	"sync"
    25  	"time"
    26  
    27  	"go.uber.org/atomic"
    28  	"k8s.io/apimachinery/pkg/types"
    29  
    30  	extensions "istio.io/api/extensions/v1alpha1"
    31  	meshconfig "istio.io/api/mesh/v1alpha1"
    32  	networking "istio.io/api/networking/v1alpha3"
    33  	"istio.io/api/security/v1beta1"
    34  	"istio.io/istio/pilot/pkg/features"
    35  	"istio.io/istio/pilot/pkg/serviceregistry/provider"
    36  	"istio.io/istio/pkg/cluster"
    37  	"istio.io/istio/pkg/config"
    38  	"istio.io/istio/pkg/config/constants"
    39  	"istio.io/istio/pkg/config/host"
    40  	"istio.io/istio/pkg/config/labels"
    41  	"istio.io/istio/pkg/config/schema/gvk"
    42  	"istio.io/istio/pkg/config/schema/kind"
    43  	"istio.io/istio/pkg/config/security"
    44  	"istio.io/istio/pkg/config/visibility"
    45  	"istio.io/istio/pkg/jwt"
    46  	"istio.io/istio/pkg/monitoring"
    47  	"istio.io/istio/pkg/network"
    48  	"istio.io/istio/pkg/slices"
    49  	"istio.io/istio/pkg/spiffe"
    50  	"istio.io/istio/pkg/util/sets"
    51  	"istio.io/istio/pkg/workloadapi"
    52  	"istio.io/istio/pkg/xds"
    53  )
    54  
    55  // Metrics is an interface for capturing metrics on a per-node basis.
    56  type Metrics interface {
    57  	// AddMetric will add an case to the metric for the given node.
    58  	AddMetric(metric monitoring.Metric, key string, proxyID, msg string)
    59  }
    60  
    61  var _ Metrics = &PushContext{}
    62  
    63  // serviceIndex is an index of all services by various fields for easy access during push.
    64  type serviceIndex struct {
    65  	// privateByNamespace are services that can reachable within the same namespace, with exportTo "."
    66  	privateByNamespace map[string][]*Service
    67  	// public are services reachable within the mesh with exportTo "*"
    68  	public []*Service
    69  	// exportedToNamespace are services that were made visible to this namespace
    70  	// by an exportTo explicitly specifying this namespace.
    71  	exportedToNamespace map[string][]*Service
    72  
    73  	// HostnameAndNamespace has all services, indexed by hostname then namespace.
    74  	HostnameAndNamespace map[host.Name]map[string]*Service `json:"-"`
    75  
    76  	// instancesByPort contains a map of service key and instances by port. It is stored here
    77  	// to avoid recomputations during push. This caches instanceByPort calls with empty labels.
    78  	// Call InstancesByPort directly when instances need to be filtered by actual labels.
    79  	instancesByPort map[string]map[int][]*IstioEndpoint
    80  }
    81  
    82  func newServiceIndex() serviceIndex {
    83  	return serviceIndex{
    84  		public:               []*Service{},
    85  		privateByNamespace:   map[string][]*Service{},
    86  		exportedToNamespace:  map[string][]*Service{},
    87  		HostnameAndNamespace: map[host.Name]map[string]*Service{},
    88  		instancesByPort:      map[string]map[int][]*IstioEndpoint{},
    89  	}
    90  }
    91  
    92  // exportToDefaults contains the default exportTo values.
    93  type exportToDefaults struct {
    94  	service         sets.Set[visibility.Instance]
    95  	virtualService  sets.Set[visibility.Instance]
    96  	destinationRule sets.Set[visibility.Instance]
    97  }
    98  
    99  // virtualServiceIndex is the index of virtual services by various fields.
   100  type virtualServiceIndex struct {
   101  	exportedToNamespaceByGateway map[types.NamespacedName][]config.Config
   102  	// this contains all the virtual services with exportTo "." and current namespace. The keys are namespace,gateway.
   103  	privateByNamespaceAndGateway map[types.NamespacedName][]config.Config
   104  	// This contains all virtual services whose exportTo is "*", keyed by gateway
   105  	publicByGateway map[string][]config.Config
   106  	// root vs namespace/name ->delegate vs virtualservice gvk/namespace/name
   107  	delegates map[ConfigKey][]ConfigKey
   108  
   109  	// This contains destination hosts of virtual services, keyed by gateway's namespace/name,
   110  	// only used when PILOT_FILTER_GATEWAY_CLUSTER_CONFIG is enabled
   111  	destinationsByGateway map[string]sets.String
   112  
   113  	// Map of VS hostname -> referenced hostnames
   114  	referencedDestinations map[string]sets.String
   115  }
   116  
   117  func newVirtualServiceIndex() virtualServiceIndex {
   118  	out := virtualServiceIndex{
   119  		publicByGateway:              map[string][]config.Config{},
   120  		privateByNamespaceAndGateway: map[types.NamespacedName][]config.Config{},
   121  		exportedToNamespaceByGateway: map[types.NamespacedName][]config.Config{},
   122  		delegates:                    map[ConfigKey][]ConfigKey{},
   123  		referencedDestinations:       map[string]sets.String{},
   124  	}
   125  	if features.FilterGatewayClusterConfig {
   126  		out.destinationsByGateway = make(map[string]sets.String)
   127  	}
   128  	return out
   129  }
   130  
   131  // destinationRuleIndex is the index of destination rules by various fields.
   132  type destinationRuleIndex struct {
   133  	//  namespaceLocal contains all public/private dest rules pertaining to a service defined in a given namespace.
   134  	namespaceLocal map[string]*consolidatedDestRules
   135  	//  exportedByNamespace contains all dest rules pertaining to a service exported by a namespace.
   136  	exportedByNamespace map[string]*consolidatedDestRules
   137  	rootNamespaceLocal  *consolidatedDestRules
   138  }
   139  
   140  func newDestinationRuleIndex() destinationRuleIndex {
   141  	return destinationRuleIndex{
   142  		namespaceLocal:      map[string]*consolidatedDestRules{},
   143  		exportedByNamespace: map[string]*consolidatedDestRules{},
   144  	}
   145  }
   146  
   147  // sidecarIndex is the index of sidecar rules
   148  type sidecarIndex struct {
   149  	// user configured sidecars for each namespace if available.
   150  	sidecarsByNamespace map[string][]*SidecarScope
   151  	// the Sidecar for the root namespace (if present). This applies to any namespace without its own Sidecar.
   152  	meshRootSidecarConfig *config.Config
   153  	// meshRootSidecarsByNamespace contains the default sidecar for namespaces that do not have a sidecar.
   154  	// These are converted from root namespace sidecar if it exists.
   155  	// These are lazy-loaded. Access protected by derivedSidecarMutex.
   156  	meshRootSidecarsByNamespace map[string]*SidecarScope
   157  	// defaultSidecarsByNamespace contains the default sidecar for namespaces that do not have a sidecar,
   158  	// These are *always* computed from DefaultSidecarScopeForNamespace i.e. a sidecar that has listeners
   159  	// for all services in the mesh. This will be used if there is no sidecar specified in root namespace.
   160  	// These are lazy-loaded. Access protected by derivedSidecarMutex.
   161  	defaultSidecarsByNamespace map[string]*SidecarScope
   162  	// sidecarsForGatewayByNamespace contains the default sidecar for gateways and waypoints,
   163  	// These are *always* computed from DefaultSidecarScopeForGateway.
   164  	// These are lazy-loaded. Access protected by derivedSidecarMutex.
   165  	sidecarsForGatewayByNamespace map[string]*SidecarScope
   166  
   167  	// mutex to protect derived sidecars i.e. not specified by user.
   168  	derivedSidecarMutex *sync.RWMutex
   169  }
   170  
   171  func newSidecarIndex() sidecarIndex {
   172  	return sidecarIndex{
   173  		sidecarsByNamespace:           map[string][]*SidecarScope{},
   174  		meshRootSidecarsByNamespace:   map[string]*SidecarScope{},
   175  		defaultSidecarsByNamespace:    map[string]*SidecarScope{},
   176  		sidecarsForGatewayByNamespace: map[string]*SidecarScope{},
   177  		derivedSidecarMutex:           &sync.RWMutex{},
   178  	}
   179  }
   180  
   181  // gatewayIndex is the index of gateways by various fields.
   182  type gatewayIndex struct {
   183  	// namespace contains gateways by namespace.
   184  	namespace map[string][]config.Config
   185  	// all contains all gateways.
   186  	all []config.Config
   187  }
   188  
   189  func newGatewayIndex() gatewayIndex {
   190  	return gatewayIndex{
   191  		namespace: map[string][]config.Config{},
   192  		all:       []config.Config{},
   193  	}
   194  }
   195  
   196  type serviceAccountKey struct {
   197  	hostname  host.Name
   198  	namespace string
   199  }
   200  
   201  // PushContext tracks the status of a push - metrics and errors.
   202  // Metrics are reset after a push - at the beginning all
   203  // values are zero, and when push completes the status is reset.
   204  // The struct is exposed in a debug endpoint - fields public to allow
   205  // easy serialization as json.
   206  type PushContext struct {
   207  	proxyStatusMutex sync.RWMutex
   208  	// ProxyStatus is keyed by the error code, and holds a map keyed
   209  	// by the ID.
   210  	ProxyStatus map[string]map[string]ProxyPushStatus
   211  
   212  	// Synthesized from env.Mesh
   213  	exportToDefaults exportToDefaults
   214  
   215  	// ServiceIndex is the index of services by various fields.
   216  	ServiceIndex serviceIndex
   217  
   218  	// serviceAccounts contains a map of hostname and port to service accounts.
   219  	serviceAccounts map[serviceAccountKey][]string
   220  
   221  	// virtualServiceIndex is the index of virtual services by various fields.
   222  	virtualServiceIndex virtualServiceIndex
   223  
   224  	// destinationRuleIndex is the index of destination rules by various fields.
   225  	destinationRuleIndex destinationRuleIndex
   226  
   227  	// gatewayIndex is the index of gateways.
   228  	gatewayIndex gatewayIndex
   229  
   230  	// clusterLocalHosts extracted from the MeshConfig
   231  	clusterLocalHosts ClusterLocalHosts
   232  
   233  	// sidecarIndex stores sidecar resources
   234  	sidecarIndex sidecarIndex
   235  
   236  	// envoy filters for each namespace including global config namespace
   237  	envoyFiltersByNamespace map[string][]*EnvoyFilterWrapper
   238  
   239  	// wasm plugins for each namespace including global config namespace
   240  	wasmPluginsByNamespace map[string][]*WasmPluginWrapper
   241  
   242  	// AuthnPolicies contains Authn policies by namespace.
   243  	AuthnPolicies *AuthenticationPolicies `json:"-"`
   244  
   245  	// AuthzPolicies stores the existing authorization policies in the cluster. Could be nil if there
   246  	// are no authorization policies in the cluster.
   247  	AuthzPolicies *AuthorizationPolicies `json:"-"`
   248  
   249  	// Telemetry stores the existing Telemetry resources for the cluster.
   250  	Telemetry *Telemetries `json:"-"`
   251  
   252  	// ProxyConfig stores the existing ProxyConfig resources for the cluster.
   253  	ProxyConfigs *ProxyConfigs `json:"-"`
   254  
   255  	// The following data is either a global index or used in the inbound path.
   256  	// Namespace specific views do not apply here.
   257  
   258  	// Mesh configuration for the mesh.
   259  	Mesh *meshconfig.MeshConfig `json:"-"`
   260  
   261  	// PushVersion describes the push version this push context was computed for
   262  	PushVersion string
   263  
   264  	// LedgerVersion is the version of the configuration ledger
   265  	LedgerVersion string
   266  
   267  	// JwtKeyResolver holds a reference to the JWT key resolver instance.
   268  	JwtKeyResolver *JwksResolver
   269  
   270  	// GatewayAPIController holds a reference to the gateway API controller.
   271  	GatewayAPIController GatewayController
   272  
   273  	// cache gateways addresses for each network
   274  	// this is mainly used for kubernetes multi-cluster scenario
   275  	networkMgr *NetworkManager
   276  
   277  	Networks *meshconfig.MeshNetworks
   278  
   279  	InitDone        atomic.Bool
   280  	initializeMutex sync.Mutex
   281  	ambientIndex    AmbientIndexes
   282  }
   283  
   284  type consolidatedDestRules struct {
   285  	// Map of dest rule host to the list of namespaces to which this destination rule has been exported to
   286  	exportTo map[host.Name]sets.Set[visibility.Instance]
   287  	// Map of dest rule host and the merged destination rules for that host.
   288  	// Only stores specific non-wildcard destination rules
   289  	specificDestRules map[host.Name][]*ConsolidatedDestRule
   290  	// Map of dest rule host and the merged destination rules for that host.
   291  	// Only stores wildcard destination rules
   292  	wildcardDestRules map[host.Name][]*ConsolidatedDestRule
   293  }
   294  
   295  // ConsolidatedDestRule represents a dr and from which it is consolidated.
   296  type ConsolidatedDestRule struct {
   297  	// rule is merged from the following destinationRules.
   298  	rule *config.Config
   299  	// the original dest rules from which above rule is merged.
   300  	from []types.NamespacedName
   301  }
   302  
   303  // XDSUpdater is used for direct updates of the xDS model and incremental push.
   304  // Pilot uses multiple registries - for example each K8S cluster is a registry
   305  // instance. Each registry is responsible for tracking a set
   306  // of endpoints associated with mesh services, and calling the EDSUpdate on changes.
   307  // A registry may group endpoints for a service in smaller subsets - for example by
   308  // deployment, or to deal with very large number of endpoints for a service. We want
   309  // to avoid passing around large objects - like full list of endpoints for a registry,
   310  // or the full list of endpoints for a service across registries, since it limits
   311  // scalability.
   312  //
   313  // Future optimizations will include grouping the endpoints by labels, gateway or region to
   314  // reduce the time when subsetting or split-horizon is used. This design assumes pilot
   315  // tracks all endpoints in the mesh and they fit in RAM - so limit is few M endpoints.
   316  // It is possible to split the endpoint tracking in future.
   317  type XDSUpdater interface {
   318  	// EDSUpdate is called when the list of endpoints or labels in a Service is changed.
   319  	// For each cluster and hostname, the full list of active endpoints (including empty list)
   320  	// must be sent. The shard name is used as a key - current implementation is using the
   321  	// registry name.
   322  	EDSUpdate(shard ShardKey, hostname string, namespace string, entry []*IstioEndpoint)
   323  
   324  	// EDSCacheUpdate is called when the list of endpoints or labels in a Service is changed.
   325  	// For each cluster and hostname, the full list of active endpoints (including empty list)
   326  	// must be sent. The shard name is used as a key - current implementation is using the
   327  	// registry name.
   328  	// Note: the difference with `EDSUpdate` is that it only update the cache rather than requesting a push
   329  	EDSCacheUpdate(shard ShardKey, hostname string, namespace string, entry []*IstioEndpoint)
   330  
   331  	// SvcUpdate is called when a service definition is updated/deleted.
   332  	SvcUpdate(shard ShardKey, hostname string, namespace string, event Event)
   333  
   334  	// ConfigUpdate is called to notify the XDS server of config updates and request a push.
   335  	// The requests may be collapsed and throttled.
   336  	ConfigUpdate(req *PushRequest)
   337  
   338  	// ProxyUpdate is called to notify the XDS server to send a push to the specified proxy.
   339  	// The requests may be collapsed and throttled.
   340  	ProxyUpdate(clusterID cluster.ID, ip string)
   341  
   342  	// RemoveShard removes all endpoints for the given shard key
   343  	RemoveShard(shardKey ShardKey)
   344  }
   345  
   346  // PushRequest defines a request to push to proxies
   347  // It is used to send updates to the config update debouncer and pass to the PushQueue.
   348  type PushRequest struct {
   349  	// Full determines whether a full push is required or not. If false, an incremental update will be sent.
   350  	// Incremental pushes:
   351  	// * Do not recompute the push context
   352  	// * Do not recompute proxy state (such as ServiceInstances)
   353  	// * Are not reported in standard metrics such as push time
   354  	// As a result, configuration updates should never be incremental. Generally, only EDS will set this, but
   355  	// in the future SDS will as well.
   356  	Full bool
   357  
   358  	// ConfigsUpdated keeps track of configs that have changed.
   359  	// This is used as an optimization to avoid unnecessary pushes to proxies that are scoped with a Sidecar.
   360  	// If this is empty, then all proxies will get an update.
   361  	// Otherwise only proxies depend on these configs will get an update.
   362  	// The kind of resources are defined in pkg/config/schemas.
   363  	ConfigsUpdated sets.Set[ConfigKey]
   364  
   365  	// Push stores the push context to use for the update. This may initially be nil, as we will
   366  	// debounce changes before a PushContext is eventually created.
   367  	Push *PushContext
   368  
   369  	// Start represents the time a push was started. This represents the time of adding to the PushQueue.
   370  	// Note that this does not include time spent debouncing.
   371  	Start time.Time
   372  
   373  	// Reason represents the reason for requesting a push. This should only be a fixed set of values,
   374  	// to avoid unbounded cardinality in metrics. If this is not set, it may be automatically filled in later.
   375  	// There should only be multiple reasons if the push request is the result of two distinct triggers, rather than
   376  	// classifying a single trigger as having multiple reasons.
   377  	Reason ReasonStats
   378  
   379  	// Delta defines the resources that were added or removed as part of this push request.
   380  	// This is set only on requests from the client which change the set of resources they (un)subscribe from.
   381  	Delta ResourceDelta
   382  }
   383  
   384  type ResourceDelta = xds.ResourceDelta
   385  
   386  type ReasonStats map[TriggerReason]int
   387  
   388  func NewReasonStats(reasons ...TriggerReason) ReasonStats {
   389  	ret := make(ReasonStats)
   390  	for _, reason := range reasons {
   391  		ret.Add(reason)
   392  	}
   393  	return ret
   394  }
   395  
   396  func (r ReasonStats) Add(reason TriggerReason) {
   397  	r[reason]++
   398  }
   399  
   400  func (r ReasonStats) Merge(other ReasonStats) {
   401  	for reason, count := range other {
   402  		r[reason] += count
   403  	}
   404  }
   405  
   406  func (r ReasonStats) CopyMerge(other ReasonStats) ReasonStats {
   407  	if len(r) == 0 {
   408  		return other
   409  	}
   410  	if len(other) == 0 {
   411  		return r
   412  	}
   413  
   414  	merged := make(ReasonStats, len(r)+len(other))
   415  	merged.Merge(r)
   416  	merged.Merge(other)
   417  
   418  	return merged
   419  }
   420  
   421  func (r ReasonStats) Count() int {
   422  	var ret int
   423  	for _, count := range r {
   424  		ret += count
   425  	}
   426  	return ret
   427  }
   428  
   429  func (r ReasonStats) Has(reason TriggerReason) bool {
   430  	return r[reason] > 0
   431  }
   432  
   433  type TriggerReason string
   434  
   435  // If adding a new reason, update xds/monitoring.go:triggerMetric
   436  const (
   437  	// EndpointUpdate describes a push triggered by an Endpoint change
   438  	EndpointUpdate TriggerReason = "endpoint"
   439  	// HeadlessEndpointUpdate describes a push triggered by an Endpoint change for headless service
   440  	HeadlessEndpointUpdate TriggerReason = "headlessendpoint"
   441  	// ConfigUpdate describes a push triggered by a config (generally and Istio CRD) change.
   442  	ConfigUpdate TriggerReason = "config"
   443  	// ServiceUpdate describes a push triggered by a Service change
   444  	ServiceUpdate TriggerReason = "service"
   445  	// ProxyUpdate describes a push triggered by a change to an individual proxy (such as label change)
   446  	ProxyUpdate TriggerReason = "proxy"
   447  	// GlobalUpdate describes a push triggered by a change to global config, such as mesh config
   448  	GlobalUpdate TriggerReason = "global"
   449  	// AmbientUpdate describes a push triggered by a change to ambient mesh config
   450  	AmbientUpdate TriggerReason = "ambient"
   451  	// UnknownTrigger describes a push triggered by an unknown reason
   452  	UnknownTrigger TriggerReason = "unknown"
   453  	// DebugTrigger describes a push triggered for debugging
   454  	DebugTrigger TriggerReason = "debug"
   455  	// SecretTrigger describes a push triggered for a Secret change
   456  	SecretTrigger TriggerReason = "secret"
   457  	// NetworksTrigger describes a push triggered for Networks change
   458  	NetworksTrigger TriggerReason = "networks"
   459  	// ProxyRequest describes a push triggered based on proxy request
   460  	ProxyRequest TriggerReason = "proxyrequest"
   461  	// DependentResource describes a push triggered based on a proxy request for a
   462  	// resource that depends on this resource (e.g. a CDS request triggers an EDS response as well)
   463  	// This is mainly used in Delta for now.
   464  	DependentResource TriggerReason = "depdendentresource"
   465  	// NamespaceUpdate describes a push triggered by a Namespace change
   466  	NamespaceUpdate TriggerReason = "namespace"
   467  	// ClusterUpdate describes a push triggered by a Cluster change
   468  	ClusterUpdate TriggerReason = "cluster"
   469  )
   470  
   471  // Merge two update requests together
   472  // Merge behaves similarly to a list append; usage should in the form `a = a.merge(b)`.
   473  // Importantly, Merge may decide to allocate a new PushRequest object or reuse the existing one - both
   474  // inputs should not be used after completion.
   475  func (pr *PushRequest) Merge(other *PushRequest) *PushRequest {
   476  	if pr == nil {
   477  		return other
   478  	}
   479  	if other == nil {
   480  		return pr
   481  	}
   482  
   483  	// Keep the first (older) start time
   484  
   485  	// Merge the two reasons. Note that we shouldn't deduplicate here, or we would under count
   486  	if len(other.Reason) > 0 {
   487  		if pr.Reason == nil {
   488  			pr.Reason = make(map[TriggerReason]int)
   489  		}
   490  		pr.Reason.Merge(other.Reason)
   491  	}
   492  
   493  	// If either is full we need a full push
   494  	pr.Full = pr.Full || other.Full
   495  
   496  	// The other push context is presumed to be later and more up to date
   497  	if other.Push != nil {
   498  		pr.Push = other.Push
   499  	}
   500  
   501  	// Do not merge when any one is empty
   502  	if len(pr.ConfigsUpdated) == 0 || len(other.ConfigsUpdated) == 0 {
   503  		pr.ConfigsUpdated = nil
   504  	} else {
   505  		for conf := range other.ConfigsUpdated {
   506  			pr.ConfigsUpdated.Insert(conf)
   507  		}
   508  	}
   509  
   510  	return pr
   511  }
   512  
   513  // CopyMerge two update requests together. Unlike Merge, this will not mutate either input.
   514  // This should be used when we are modifying a shared PushRequest (typically any time it's in the context
   515  // of a single proxy)
   516  func (pr *PushRequest) CopyMerge(other *PushRequest) *PushRequest {
   517  	if pr == nil {
   518  		return other
   519  	}
   520  	if other == nil {
   521  		return pr
   522  	}
   523  
   524  	var reason ReasonStats
   525  	if len(pr.Reason)+len(other.Reason) > 0 {
   526  		reason = make(ReasonStats)
   527  		reason.Merge(pr.Reason)
   528  		reason.Merge(other.Reason)
   529  	}
   530  	merged := &PushRequest{
   531  		// Keep the first (older) start time
   532  		Start: pr.Start,
   533  
   534  		// If either is full we need a full push
   535  		Full: pr.Full || other.Full,
   536  
   537  		// The other push context is presumed to be later and more up to date
   538  		Push: other.Push,
   539  
   540  		// Merge the two reasons. Note that we shouldn't deduplicate here, or we would under count
   541  		Reason: reason,
   542  	}
   543  
   544  	// Do not merge when any one is empty
   545  	if len(pr.ConfigsUpdated) > 0 && len(other.ConfigsUpdated) > 0 {
   546  		merged.ConfigsUpdated = make(sets.Set[ConfigKey], len(pr.ConfigsUpdated)+len(other.ConfigsUpdated))
   547  		merged.ConfigsUpdated.Merge(pr.ConfigsUpdated)
   548  		merged.ConfigsUpdated.Merge(other.ConfigsUpdated)
   549  	}
   550  
   551  	return merged
   552  }
   553  
   554  func (pr *PushRequest) IsRequest() bool {
   555  	return len(pr.Reason) == 1 && pr.Reason.Has(ProxyRequest)
   556  }
   557  
   558  func (pr *PushRequest) IsProxyUpdate() bool {
   559  	return pr.Reason.Has(ProxyUpdate)
   560  }
   561  
   562  func (pr *PushRequest) PushReason() string {
   563  	if pr.IsRequest() {
   564  		return " request"
   565  	}
   566  	return ""
   567  }
   568  
   569  // ProxyPushStatus represents an event captured during config push to proxies.
   570  // It may contain additional message and the affected proxy.
   571  type ProxyPushStatus struct {
   572  	Proxy   string `json:"proxy,omitempty"`
   573  	Message string `json:"message,omitempty"`
   574  }
   575  
   576  // AddMetric will add an case to the metric.
   577  func (ps *PushContext) AddMetric(metric monitoring.Metric, key string, proxyID, msg string) {
   578  	if ps == nil {
   579  		log.Infof("Metric without context %s %v %s", key, proxyID, msg)
   580  		return
   581  	}
   582  	ps.proxyStatusMutex.Lock()
   583  	defer ps.proxyStatusMutex.Unlock()
   584  
   585  	metricMap, f := ps.ProxyStatus[metric.Name()]
   586  	if !f {
   587  		metricMap = map[string]ProxyPushStatus{}
   588  		ps.ProxyStatus[metric.Name()] = metricMap
   589  	}
   590  	ev := ProxyPushStatus{Message: msg, Proxy: proxyID}
   591  	metricMap[key] = ev
   592  }
   593  
   594  var (
   595  
   596  	// EndpointNoPod tracks endpoints without an associated pod. This is an error condition, since
   597  	// we can't figure out the labels. It may be a transient problem, if endpoint is processed before
   598  	// pod.
   599  	EndpointNoPod = monitoring.NewGauge(
   600  		"endpoint_no_pod",
   601  		"Endpoints without an associated pod.",
   602  	)
   603  
   604  	// ProxyStatusNoService represents proxies not selected by any service
   605  	// This can be normal - for workloads that act only as client, or are not covered by a Service.
   606  	// It can also be an error, for example in cases the Endpoint list of a service was not updated by the time
   607  	// the sidecar calls.
   608  	// Updated by GetProxyServiceTargets
   609  	ProxyStatusNoService = monitoring.NewGauge(
   610  		"pilot_no_ip",
   611  		"Pods not found in the endpoint table, possibly invalid.",
   612  	)
   613  
   614  	// ProxyStatusEndpointNotReady represents proxies found not be ready.
   615  	// Updated by GetProxyServiceTargets. Normal condition when starting
   616  	// an app with readiness, error if it doesn't change to 0.
   617  	ProxyStatusEndpointNotReady = monitoring.NewGauge(
   618  		"pilot_endpoint_not_ready",
   619  		"Endpoint found in unready state.",
   620  	)
   621  
   622  	// ProxyStatusConflictOutboundListenerTCPOverTCP metric tracks number of
   623  	// TCP listeners that conflicted with existing TCP listeners on same port
   624  	ProxyStatusConflictOutboundListenerTCPOverTCP = monitoring.NewGauge(
   625  		"pilot_conflict_outbound_listener_tcp_over_current_tcp",
   626  		"Number of conflicting tcp listeners with current tcp listener.",
   627  	)
   628  
   629  	// ProxyStatusConflictInboundListener tracks cases of multiple inbound
   630  	// listeners - 2 services selecting the same port of the pod.
   631  	ProxyStatusConflictInboundListener = monitoring.NewGauge(
   632  		"pilot_conflict_inbound_listener",
   633  		"Number of conflicting inbound listeners.",
   634  	)
   635  
   636  	// DuplicatedClusters tracks duplicate clusters seen while computing CDS
   637  	DuplicatedClusters = monitoring.NewGauge(
   638  		"pilot_duplicate_envoy_clusters",
   639  		"Duplicate envoy clusters caused by service entries with same hostname",
   640  	)
   641  
   642  	// DNSNoEndpointClusters tracks dns clusters without endpoints
   643  	DNSNoEndpointClusters = monitoring.NewGauge(
   644  		"pilot_dns_cluster_without_endpoints",
   645  		"DNS clusters without endpoints caused by the endpoint field in "+
   646  			"STRICT_DNS type cluster is not set or the corresponding subset cannot select any endpoint",
   647  	)
   648  
   649  	// ProxyStatusClusterNoInstances tracks clusters (services) without workloads.
   650  	ProxyStatusClusterNoInstances = monitoring.NewGauge(
   651  		"pilot_eds_no_instances",
   652  		"Number of clusters without instances.",
   653  	)
   654  
   655  	// DuplicatedDomains tracks rejected VirtualServices due to duplicated hostname.
   656  	DuplicatedDomains = monitoring.NewGauge(
   657  		"pilot_vservice_dup_domain",
   658  		"Virtual services with dup domains.",
   659  	)
   660  
   661  	// DuplicatedSubsets tracks duplicate subsets that we rejected while merging multiple destination rules for same host
   662  	DuplicatedSubsets = monitoring.NewGauge(
   663  		"pilot_destrule_subsets",
   664  		"Duplicate subsets across destination rules for same host",
   665  	)
   666  
   667  	// totalVirtualServices tracks the total number of virtual service
   668  	totalVirtualServices = monitoring.NewGauge(
   669  		"pilot_virt_services",
   670  		"Total virtual services known to pilot.",
   671  	)
   672  
   673  	// LastPushStatus preserves the metrics and data collected during lasts global push.
   674  	// It can be used by debugging tools to inspect the push event. It will be reset after each push with the
   675  	// new version.
   676  	LastPushStatus *PushContext
   677  	// LastPushMutex will protect the LastPushStatus
   678  	LastPushMutex sync.Mutex
   679  
   680  	// All metrics we registered.
   681  	metrics = []monitoring.Metric{
   682  		DNSNoEndpointClusters,
   683  		EndpointNoPod,
   684  		ProxyStatusNoService,
   685  		ProxyStatusEndpointNotReady,
   686  		ProxyStatusConflictOutboundListenerTCPOverTCP,
   687  		ProxyStatusConflictInboundListener,
   688  		DuplicatedClusters,
   689  		ProxyStatusClusterNoInstances,
   690  		DuplicatedDomains,
   691  		DuplicatedSubsets,
   692  	}
   693  )
   694  
   695  // NewPushContext creates a new PushContext structure to track push status.
   696  func NewPushContext() *PushContext {
   697  	return &PushContext{
   698  		ServiceIndex:            newServiceIndex(),
   699  		virtualServiceIndex:     newVirtualServiceIndex(),
   700  		destinationRuleIndex:    newDestinationRuleIndex(),
   701  		sidecarIndex:            newSidecarIndex(),
   702  		envoyFiltersByNamespace: map[string][]*EnvoyFilterWrapper{},
   703  		gatewayIndex:            newGatewayIndex(),
   704  		ProxyStatus:             map[string]map[string]ProxyPushStatus{},
   705  		serviceAccounts:         map[serviceAccountKey][]string{},
   706  	}
   707  }
   708  
   709  // AddPublicServices adds the services to context public services - mainly used in tests.
   710  func (ps *PushContext) AddPublicServices(services []*Service) {
   711  	ps.ServiceIndex.public = append(ps.ServiceIndex.public, services...)
   712  }
   713  
   714  // AddServiceInstances adds instances to the context service instances - mainly used in tests.
   715  func (ps *PushContext) AddServiceInstances(service *Service, instances map[int][]*IstioEndpoint) {
   716  	svcKey := service.Key()
   717  	for port, inst := range instances {
   718  		if _, exists := ps.ServiceIndex.instancesByPort[svcKey]; !exists {
   719  			ps.ServiceIndex.instancesByPort[svcKey] = make(map[int][]*IstioEndpoint)
   720  		}
   721  		ps.ServiceIndex.instancesByPort[svcKey][port] = append(ps.ServiceIndex.instancesByPort[svcKey][port], inst...)
   722  	}
   723  }
   724  
   725  // StatusJSON implements json.Marshaller, with a lock.
   726  func (ps *PushContext) StatusJSON() ([]byte, error) {
   727  	if ps == nil {
   728  		return []byte{'{', '}'}, nil
   729  	}
   730  	ps.proxyStatusMutex.RLock()
   731  	defer ps.proxyStatusMutex.RUnlock()
   732  	return json.MarshalIndent(ps.ProxyStatus, "", "    ")
   733  }
   734  
   735  // OnConfigChange is called when a config change is detected.
   736  func (ps *PushContext) OnConfigChange() {
   737  	LastPushMutex.Lock()
   738  	LastPushStatus = ps
   739  	LastPushMutex.Unlock()
   740  	ps.UpdateMetrics()
   741  }
   742  
   743  // UpdateMetrics will update the prometheus metrics based on the
   744  // current status of the push.
   745  func (ps *PushContext) UpdateMetrics() {
   746  	ps.proxyStatusMutex.RLock()
   747  	defer ps.proxyStatusMutex.RUnlock()
   748  
   749  	for _, pm := range metrics {
   750  		mmap := ps.ProxyStatus[pm.Name()]
   751  		pm.Record(float64(len(mmap)))
   752  	}
   753  }
   754  
   755  // It is called after virtual service short host name is resolved to FQDN
   756  func virtualServiceDestinations(v *networking.VirtualService) map[string]sets.Set[int] {
   757  	if v == nil {
   758  		return nil
   759  	}
   760  
   761  	out := make(map[string]sets.Set[int])
   762  
   763  	addDestination := func(host string, port *networking.PortSelector) {
   764  		// Use the value 0 as a sentinel indicating that one of the destinations
   765  		// in the Virtual Service does not specify a port for this host.
   766  		pn := 0
   767  		if port != nil {
   768  			pn = int(port.Number)
   769  		}
   770  		sets.InsertOrNew(out, host, pn)
   771  	}
   772  
   773  	for _, h := range v.Http {
   774  		for _, r := range h.Route {
   775  			if r.Destination != nil {
   776  				addDestination(r.Destination.Host, r.Destination.GetPort())
   777  			}
   778  		}
   779  		if h.Mirror != nil {
   780  			addDestination(h.Mirror.Host, h.Mirror.GetPort())
   781  		}
   782  		for _, m := range h.Mirrors {
   783  			if m.Destination != nil {
   784  				addDestination(m.Destination.Host, m.Destination.GetPort())
   785  			}
   786  		}
   787  	}
   788  	for _, t := range v.Tcp {
   789  		for _, r := range t.Route {
   790  			if r.Destination != nil {
   791  				addDestination(r.Destination.Host, r.Destination.GetPort())
   792  			}
   793  		}
   794  	}
   795  	for _, t := range v.Tls {
   796  		for _, r := range t.Route {
   797  			if r.Destination != nil {
   798  				addDestination(r.Destination.Host, r.Destination.GetPort())
   799  			}
   800  		}
   801  	}
   802  
   803  	return out
   804  }
   805  
   806  func (ps *PushContext) ExtraWaypointServices(proxy *Proxy) sets.String {
   807  	return ps.extraServicesForProxy(proxy)
   808  }
   809  
   810  // GatewayServices returns the set of services which are referred from the proxy gateways.
   811  func (ps *PushContext) GatewayServices(proxy *Proxy) []*Service {
   812  	svcs := proxy.SidecarScope.services
   813  
   814  	// MergedGateway will be nil when there are no configs in the
   815  	// system during initial installation.
   816  	if proxy.MergedGateway == nil {
   817  		return nil
   818  	}
   819  
   820  	// host set.
   821  	hostsFromGateways := ps.extraServicesForProxy(proxy)
   822  	for _, gw := range proxy.MergedGateway.GatewayNameForServer {
   823  		hostsFromGateways.Merge(ps.virtualServiceIndex.destinationsByGateway[gw])
   824  	}
   825  	log.Debugf("GatewayServices: gateway %v is exposing these hosts:%v", proxy.ID, hostsFromGateways)
   826  
   827  	gwSvcs := make([]*Service, 0, len(svcs))
   828  
   829  	for _, s := range svcs {
   830  		svcHost := string(s.Hostname)
   831  
   832  		if _, ok := hostsFromGateways[svcHost]; ok {
   833  			gwSvcs = append(gwSvcs, s)
   834  		}
   835  	}
   836  
   837  	log.Debugf("GatewayServices: gateways len(services)=%d, len(filtered)=%d", len(svcs), len(gwSvcs))
   838  
   839  	return gwSvcs
   840  }
   841  
   842  func (ps *PushContext) ServicesAttachedToMesh() map[string]sets.String {
   843  	return ps.virtualServiceIndex.referencedDestinations
   844  }
   845  
   846  func (ps *PushContext) ServiceAttachedToGateway(hostname string, proxy *Proxy) bool {
   847  	gw := proxy.MergedGateway
   848  	// MergedGateway will be nil when there are no configs in the
   849  	// system during initial installation.
   850  	if gw == nil {
   851  		return false
   852  	}
   853  	if gw.ContainsAutoPassthroughGateways {
   854  		return true
   855  	}
   856  	for _, g := range gw.GatewayNameForServer {
   857  		if hosts := ps.virtualServiceIndex.destinationsByGateway[g]; hosts != nil {
   858  			if hosts.Contains(hostname) {
   859  				return true
   860  			}
   861  		}
   862  	}
   863  	return ps.extraServicesForProxy(proxy).Contains(hostname)
   864  }
   865  
   866  // wellknownProviders is a list of all known providers.
   867  // This exists
   868  var wellknownProviders = sets.New(
   869  	"envoy_ext_authz_http",
   870  	"envoy_ext_authz_grpc",
   871  	"zipkin",
   872  	"lightstep",
   873  	"datadog",
   874  	"opencensus",
   875  	"stackdriver",
   876  	"prometheus",
   877  	"skywalking",
   878  	"envoy_http_als",
   879  	"envoy_tcp_als",
   880  	"envoy_otel_als",
   881  	"opentelemetry",
   882  	"envoy_file_access_log",
   883  )
   884  
   885  func AssertProvidersHandled(expected int) {
   886  	if expected != len(wellknownProviders) {
   887  		panic(fmt.Sprintf("Not all providers handled; This function handles %v but there are %v known providers", expected, len(wellknownProviders)))
   888  	}
   889  }
   890  
   891  // addHostsFromMeshConfigProvidersHandled contains the number of providers we handle below.
   892  // This is to ensure this stays in sync as new handlers are added
   893  // STOP. DO NOT UPDATE THIS WITHOUT UPDATING extraServicesForProxy.
   894  const addHostsFromMeshConfigProvidersHandled = 14
   895  
   896  // extraServicesForProxy returns a subset of services referred from the proxy gateways, including:
   897  // 1. MeshConfig.ExtensionProviders
   898  // 2. RequestAuthentication.JwtRules.JwksUri
   899  // TODO: include cluster from EnvoyFilter such as global ratelimit [demo](https://istio.io/latest/docs/tasks/policy-enforcement/rate-limit/#global-rate-limit)
   900  func (ps *PushContext) extraServicesForProxy(proxy *Proxy) sets.String {
   901  	hosts := sets.String{}
   902  	// add services from MeshConfig.ExtensionProviders
   903  	for _, prov := range ps.Mesh.ExtensionProviders {
   904  		switch p := prov.Provider.(type) {
   905  		case *meshconfig.MeshConfig_ExtensionProvider_EnvoyExtAuthzHttp:
   906  			hosts.Insert(p.EnvoyExtAuthzHttp.Service)
   907  		case *meshconfig.MeshConfig_ExtensionProvider_EnvoyExtAuthzGrpc:
   908  			hosts.Insert(p.EnvoyExtAuthzGrpc.Service)
   909  		case *meshconfig.MeshConfig_ExtensionProvider_Zipkin:
   910  			hosts.Insert(p.Zipkin.Service)
   911  		//nolint: staticcheck  // Lightstep deprecated
   912  		case *meshconfig.MeshConfig_ExtensionProvider_Lightstep:
   913  			hosts.Insert(p.Lightstep.Service)
   914  		case *meshconfig.MeshConfig_ExtensionProvider_Datadog:
   915  			hosts.Insert(p.Datadog.Service)
   916  		case *meshconfig.MeshConfig_ExtensionProvider_Skywalking:
   917  			hosts.Insert(p.Skywalking.Service)
   918  		case *meshconfig.MeshConfig_ExtensionProvider_Opencensus:
   919  			//nolint: staticcheck
   920  			hosts.Insert(p.Opencensus.Service)
   921  		case *meshconfig.MeshConfig_ExtensionProvider_Opentelemetry:
   922  			hosts.Insert(p.Opentelemetry.Service)
   923  		case *meshconfig.MeshConfig_ExtensionProvider_EnvoyHttpAls:
   924  			hosts.Insert(p.EnvoyHttpAls.Service)
   925  		case *meshconfig.MeshConfig_ExtensionProvider_EnvoyTcpAls:
   926  			hosts.Insert(p.EnvoyTcpAls.Service)
   927  		case *meshconfig.MeshConfig_ExtensionProvider_EnvoyOtelAls:
   928  			hosts.Insert(p.EnvoyOtelAls.Service)
   929  		case *meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLog: // No services
   930  		case *meshconfig.MeshConfig_ExtensionProvider_Prometheus: // No services
   931  		case *meshconfig.MeshConfig_ExtensionProvider_Stackdriver: // No services
   932  		}
   933  	}
   934  	// add services from RequestAuthentication.JwtRules.JwksUri
   935  	if features.JwksFetchMode != jwt.Istiod {
   936  		forWorkload := PolicyMatcherForProxy(proxy)
   937  		jwtPolicies := ps.AuthnPolicies.GetJwtPoliciesForWorkload(forWorkload)
   938  		for _, cfg := range jwtPolicies {
   939  			rules := cfg.Spec.(*v1beta1.RequestAuthentication).JwtRules
   940  			for _, r := range rules {
   941  				if uri := r.GetJwksUri(); len(uri) > 0 {
   942  					jwksInfo, err := security.ParseJwksURI(uri)
   943  					if err == nil {
   944  						hosts.Insert(jwksInfo.Hostname.String())
   945  					}
   946  				}
   947  			}
   948  		}
   949  	}
   950  	return hosts
   951  }
   952  
   953  // servicesExportedToNamespace returns the list of services that are visible to a namespace.
   954  // namespace "" indicates all namespaces
   955  func (ps *PushContext) servicesExportedToNamespace(ns string) []*Service {
   956  	var out []*Service
   957  
   958  	// First add private services and explicitly exportedTo services
   959  	if ns == NamespaceAll {
   960  		out = make([]*Service, 0, len(ps.ServiceIndex.privateByNamespace)+len(ps.ServiceIndex.public))
   961  		for _, privateServices := range ps.ServiceIndex.privateByNamespace {
   962  			out = append(out, privateServices...)
   963  		}
   964  	} else {
   965  		out = make([]*Service, 0, len(ps.ServiceIndex.privateByNamespace[ns])+
   966  			len(ps.ServiceIndex.exportedToNamespace[ns])+len(ps.ServiceIndex.public))
   967  		out = append(out, ps.ServiceIndex.privateByNamespace[ns]...)
   968  		out = append(out, ps.ServiceIndex.exportedToNamespace[ns]...)
   969  	}
   970  
   971  	// Second add public services
   972  	out = append(out, ps.ServiceIndex.public...)
   973  
   974  	return out
   975  }
   976  
   977  // GetAllServices returns the total services within the mesh.
   978  // Note: per proxy services should use SidecarScope.Services.
   979  func (ps *PushContext) GetAllServices() []*Service {
   980  	return ps.servicesExportedToNamespace(NamespaceAll)
   981  }
   982  
   983  // ServiceForHostname returns the service associated with a given hostname following SidecarScope
   984  func (ps *PushContext) ServiceForHostname(proxy *Proxy, hostname host.Name) *Service {
   985  	if proxy != nil && proxy.SidecarScope != nil {
   986  		return proxy.SidecarScope.servicesByHostname[hostname]
   987  	}
   988  
   989  	// SidecarScope shouldn't be null here. If it is, we can't disambiguate the hostname to use for a namespace,
   990  	// so the selection must be undefined.
   991  	for _, service := range ps.ServiceIndex.HostnameAndNamespace[hostname] {
   992  		return service
   993  	}
   994  
   995  	// No service found
   996  	return nil
   997  }
   998  
   999  // IsServiceVisible returns true if the input service is visible to the given namespace.
  1000  func (ps *PushContext) IsServiceVisible(service *Service, namespace string) bool {
  1001  	if service == nil {
  1002  		return false
  1003  	}
  1004  
  1005  	ns := service.Attributes.Namespace
  1006  	if service.Attributes.ExportTo.IsEmpty() {
  1007  		if ps.exportToDefaults.service.Contains(visibility.Private) {
  1008  			return ns == namespace
  1009  		} else if ps.exportToDefaults.service.Contains(visibility.Public) {
  1010  			return true
  1011  		}
  1012  	}
  1013  
  1014  	return service.Attributes.ExportTo.Contains(visibility.Public) ||
  1015  		(service.Attributes.ExportTo.Contains(visibility.Private) && ns == namespace) ||
  1016  		service.Attributes.ExportTo.Contains(visibility.Instance(namespace))
  1017  }
  1018  
  1019  // VirtualServicesForGateway lists all virtual services bound to the specified gateways
  1020  // This replaces store.VirtualServices. Used only by the gateways
  1021  // Sidecars use the egressListener.VirtualServices().
  1022  //
  1023  // Note that for generating the imported virtual services of sidecar egress
  1024  // listener, we don't call this function to copy configs for performance issues.
  1025  // Instead, we pass the virtualServiceIndex directly into SelectVirtualServices
  1026  // function.
  1027  func (ps *PushContext) VirtualServicesForGateway(proxyNamespace, gateway string) []config.Config {
  1028  	name := types.NamespacedName{
  1029  		Namespace: proxyNamespace,
  1030  		Name:      gateway,
  1031  	}
  1032  	res := make([]config.Config, 0, len(ps.virtualServiceIndex.privateByNamespaceAndGateway[name])+
  1033  		len(ps.virtualServiceIndex.exportedToNamespaceByGateway[name])+
  1034  		len(ps.virtualServiceIndex.publicByGateway[gateway]))
  1035  	res = append(res, ps.virtualServiceIndex.privateByNamespaceAndGateway[name]...)
  1036  	res = append(res, ps.virtualServiceIndex.exportedToNamespaceByGateway[name]...)
  1037  	// Favor same-namespace Gateway routes, to give the "consumer override" preference.
  1038  	// We do 2 iterations here to avoid extra allocations.
  1039  	for _, vs := range ps.virtualServiceIndex.publicByGateway[gateway] {
  1040  		if UseGatewaySemantics(vs) && vs.Namespace == proxyNamespace {
  1041  			res = append(res, vs)
  1042  		}
  1043  	}
  1044  	for _, vs := range ps.virtualServiceIndex.publicByGateway[gateway] {
  1045  		if !(UseGatewaySemantics(vs) && vs.Namespace == proxyNamespace) {
  1046  			res = append(res, vs)
  1047  		}
  1048  	}
  1049  
  1050  	return res
  1051  }
  1052  
  1053  // DelegateVirtualServices lists all the delegate virtual services configkeys associated with the provided virtual services
  1054  func (ps *PushContext) DelegateVirtualServices(vses []config.Config) []ConfigHash {
  1055  	var out []ConfigHash
  1056  	for _, vs := range vses {
  1057  		for _, delegate := range ps.virtualServiceIndex.delegates[ConfigKey{Kind: kind.VirtualService, Namespace: vs.Namespace, Name: vs.Name}] {
  1058  			out = append(out, delegate.HashCode())
  1059  		}
  1060  	}
  1061  	return out
  1062  }
  1063  
  1064  // getSidecarScope returns a SidecarScope object associated with the
  1065  // proxy. The SidecarScope object is a semi-processed view of the service
  1066  // registry, and config state associated with the sidecar crd. The scope contains
  1067  // a set of inbound and outbound listeners, services/configs per listener,
  1068  // etc. The sidecar scopes are precomputed in the initSidecarContext
  1069  // function based on the Sidecar API objects in each namespace. If there is
  1070  // no sidecar api object, a default sidecarscope is assigned to the
  1071  // namespace which enables connectivity to all services in the mesh.
  1072  //
  1073  // Callers can check if the sidecarScope is from user generated object or not
  1074  // by checking the sidecarScope.Config field, that contains the user provided config
  1075  func (ps *PushContext) getSidecarScope(proxy *Proxy, workloadLabels labels.Instance) *SidecarScope {
  1076  	// TODO: logic to merge multiple sidecar resources
  1077  	// Currently we assume that there will be only one sidecar config for a namespace.
  1078  	sidecars, hasSidecar := ps.sidecarIndex.sidecarsByNamespace[proxy.ConfigNamespace]
  1079  	switch proxy.Type {
  1080  	case Router, Waypoint:
  1081  		ps.sidecarIndex.derivedSidecarMutex.Lock()
  1082  		defer ps.sidecarIndex.derivedSidecarMutex.Unlock()
  1083  
  1084  		// Gateways always use default sidecar scope.
  1085  		if sc, f := ps.sidecarIndex.defaultSidecarsByNamespace[proxy.ConfigNamespace]; f {
  1086  			return sc
  1087  		}
  1088  
  1089  		if sc, f := ps.sidecarIndex.sidecarsForGatewayByNamespace[proxy.ConfigNamespace]; f {
  1090  			return sc
  1091  		}
  1092  
  1093  		// We need to compute this namespace
  1094  		computed := DefaultSidecarScopeForGateway(ps, proxy.ConfigNamespace)
  1095  		ps.sidecarIndex.sidecarsForGatewayByNamespace[proxy.ConfigNamespace] = computed
  1096  		return computed
  1097  	case SidecarProxy:
  1098  		if hasSidecar {
  1099  			for _, wrapper := range sidecars {
  1100  				if wrapper.Sidecar != nil {
  1101  					sidecar := wrapper.Sidecar
  1102  					// if there is no workload selector, the config applies to all workloads
  1103  					// if there is a workload selector, check for matching workload labels
  1104  					if sidecar.GetWorkloadSelector() != nil {
  1105  						workloadSelector := labels.Instance(sidecar.GetWorkloadSelector().GetLabels())
  1106  						// exclude workload selector that not match
  1107  						if !workloadSelector.SubsetOf(workloadLabels) {
  1108  							continue
  1109  						}
  1110  					}
  1111  
  1112  					// it is guaranteed sidecars with selectors are put in front
  1113  					// and the sidecars are sorted by creation timestamp,
  1114  					// return exact/wildcard matching one directly
  1115  					return wrapper
  1116  				}
  1117  				// this happens at last, it is the default sidecar scope
  1118  				return wrapper
  1119  			}
  1120  		}
  1121  		ps.sidecarIndex.derivedSidecarMutex.Lock()
  1122  		defer ps.sidecarIndex.derivedSidecarMutex.Unlock()
  1123  
  1124  		if ps.sidecarIndex.meshRootSidecarConfig != nil {
  1125  			if sc, exists := ps.sidecarIndex.meshRootSidecarsByNamespace[proxy.ConfigNamespace]; exists {
  1126  				// We have already computed the scope for this namespace, just return it.
  1127  				return sc
  1128  			}
  1129  			// We need to compute this namespace
  1130  			computed := convertToSidecarScope(ps, ps.sidecarIndex.meshRootSidecarConfig, proxy.ConfigNamespace)
  1131  			ps.sidecarIndex.meshRootSidecarsByNamespace[proxy.ConfigNamespace] = computed
  1132  			return computed
  1133  		}
  1134  		if sc, exists := ps.sidecarIndex.defaultSidecarsByNamespace[proxy.ConfigNamespace]; exists {
  1135  			// We have already computed the scope for this namespace, just return it.
  1136  			return sc
  1137  		}
  1138  		// We need to compute this namespace
  1139  		computed := convertToSidecarScope(ps, ps.sidecarIndex.meshRootSidecarConfig, proxy.ConfigNamespace)
  1140  		ps.sidecarIndex.defaultSidecarsByNamespace[proxy.ConfigNamespace] = computed
  1141  		return computed
  1142  	}
  1143  	return nil
  1144  }
  1145  
  1146  // destinationRule returns a destination rule for a service name in a given namespace.
  1147  func (ps *PushContext) destinationRule(proxyNameSpace string, service *Service) []*ConsolidatedDestRule {
  1148  	if service == nil {
  1149  		return nil
  1150  	}
  1151  	// If the proxy config namespace is same as the root config namespace
  1152  	// look for dest rules in the service's namespace first. This hack is needed
  1153  	// because sometimes, istio-system tends to become the root config namespace.
  1154  	// Destination rules are defined here for global purposes. We do not want these
  1155  	// catch all destination rules to be the only dest rule, when processing CDS for
  1156  	// proxies like the istio-ingressgateway or istio-egressgateway.
  1157  	// If there are no service specific dest rules, we will end up picking up the same
  1158  	// rules anyway, later in the code
  1159  
  1160  	// 1. select destination rule from proxy config namespace
  1161  	if proxyNameSpace != ps.Mesh.RootNamespace {
  1162  		// search through the DestinationRules in proxy's namespace first
  1163  		if ps.destinationRuleIndex.namespaceLocal[proxyNameSpace] != nil {
  1164  			if _, drs, ok := MostSpecificHostMatch(service.Hostname,
  1165  				ps.destinationRuleIndex.namespaceLocal[proxyNameSpace].specificDestRules,
  1166  				ps.destinationRuleIndex.namespaceLocal[proxyNameSpace].wildcardDestRules,
  1167  			); ok {
  1168  				return drs
  1169  			}
  1170  		}
  1171  	} else {
  1172  		// If this is a namespace local DR in the same namespace, this must be meant for this proxy, so we do not
  1173  		// need to worry about overriding other DRs with *.local type rules here. If we ignore this, then exportTo=. in
  1174  		// root namespace would always be ignored
  1175  		if _, drs, ok := MostSpecificHostMatch(service.Hostname,
  1176  			ps.destinationRuleIndex.rootNamespaceLocal.specificDestRules,
  1177  			ps.destinationRuleIndex.rootNamespaceLocal.wildcardDestRules,
  1178  		); ok {
  1179  			return drs
  1180  		}
  1181  	}
  1182  
  1183  	// 2. select destination rule from service namespace
  1184  	svcNs := service.Attributes.Namespace
  1185  
  1186  	// This can happen when finding the subset labels for a proxy in root namespace.
  1187  	// Because based on a pure cluster's fqdn, we do not know the service and
  1188  	// construct a fake service without setting Attributes at all.
  1189  	if svcNs == "" {
  1190  		for _, svc := range ps.servicesExportedToNamespace(proxyNameSpace) {
  1191  			if service.Hostname == svc.Hostname && svc.Attributes.Namespace != "" {
  1192  				svcNs = svc.Attributes.Namespace
  1193  				break
  1194  			}
  1195  		}
  1196  	}
  1197  
  1198  	// 3. if no private/public rule matched in the calling proxy's namespace,
  1199  	// check the target service's namespace for exported rules
  1200  	if svcNs != "" {
  1201  		if out := ps.getExportedDestinationRuleFromNamespace(svcNs, service.Hostname, proxyNameSpace); out != nil {
  1202  			return out
  1203  		}
  1204  	}
  1205  
  1206  	// 4. if no public/private rule in calling proxy's namespace matched, and no public rule in the
  1207  	// target service's namespace matched, search for any exported destination rule in the config root namespace
  1208  	if out := ps.getExportedDestinationRuleFromNamespace(ps.Mesh.RootNamespace, service.Hostname, proxyNameSpace); out != nil {
  1209  		return out
  1210  	}
  1211  
  1212  	return nil
  1213  }
  1214  
  1215  func (ps *PushContext) getExportedDestinationRuleFromNamespace(owningNamespace string, hostname host.Name, clientNamespace string) []*ConsolidatedDestRule {
  1216  	if ps.destinationRuleIndex.exportedByNamespace[owningNamespace] != nil {
  1217  		if specificHostname, drs, ok := MostSpecificHostMatch(hostname,
  1218  			ps.destinationRuleIndex.exportedByNamespace[owningNamespace].specificDestRules,
  1219  			ps.destinationRuleIndex.exportedByNamespace[owningNamespace].wildcardDestRules,
  1220  		); ok {
  1221  			// Check if the dest rule for this host is actually exported to the proxy's (client) namespace
  1222  			exportToSet := ps.destinationRuleIndex.exportedByNamespace[owningNamespace].exportTo[specificHostname]
  1223  			if exportToSet.IsEmpty() || exportToSet.Contains(visibility.Public) || exportToSet.Contains(visibility.Instance(clientNamespace)) {
  1224  				return drs
  1225  			}
  1226  		}
  1227  	}
  1228  	return nil
  1229  }
  1230  
  1231  // IsClusterLocal indicates whether the endpoints for the service should only be accessible to clients
  1232  // within the cluster.
  1233  func (ps *PushContext) IsClusterLocal(service *Service) bool {
  1234  	if ps == nil || service == nil {
  1235  		return false
  1236  	}
  1237  	return ps.clusterLocalHosts.IsClusterLocal(service.Hostname)
  1238  }
  1239  
  1240  // InitContext will initialize the data structures used for code generation.
  1241  // This should be called before starting the push, from the thread creating
  1242  // the push context.
  1243  func (ps *PushContext) InitContext(env *Environment, oldPushContext *PushContext, pushReq *PushRequest) error {
  1244  	// Acquire a lock to ensure we don't concurrently initialize the same PushContext.
  1245  	// If this does happen, one thread will block then exit early from InitDone=true
  1246  	ps.initializeMutex.Lock()
  1247  	defer ps.initializeMutex.Unlock()
  1248  	if ps.InitDone.Load() {
  1249  		return nil
  1250  	}
  1251  
  1252  	ps.Mesh = env.Mesh()
  1253  	ps.Networks = env.MeshNetworks()
  1254  	ps.LedgerVersion = env.Version()
  1255  
  1256  	// Must be initialized first as initServiceRegistry/VirtualServices/Destrules
  1257  	// use the default export map.
  1258  	ps.initDefaultExportMaps()
  1259  
  1260  	// create new or incremental update
  1261  	if pushReq == nil || oldPushContext == nil || !oldPushContext.InitDone.Load() || len(pushReq.ConfigsUpdated) == 0 {
  1262  		if err := ps.createNewContext(env); err != nil {
  1263  			return err
  1264  		}
  1265  	} else {
  1266  		if err := ps.updateContext(env, oldPushContext, pushReq); err != nil {
  1267  			return err
  1268  		}
  1269  	}
  1270  
  1271  	ps.networkMgr = env.NetworkManager
  1272  
  1273  	ps.clusterLocalHosts = env.ClusterLocal().GetClusterLocalHosts()
  1274  
  1275  	ps.InitDone.Store(true)
  1276  	return nil
  1277  }
  1278  
  1279  func (ps *PushContext) createNewContext(env *Environment) error {
  1280  	ps.initServiceRegistry(env, nil)
  1281  
  1282  	if err := ps.initKubernetesGateways(env); err != nil {
  1283  		return err
  1284  	}
  1285  
  1286  	ps.initVirtualServices(env)
  1287  
  1288  	ps.initDestinationRules(env)
  1289  	ps.initAuthnPolicies(env)
  1290  
  1291  	ps.initAuthorizationPolicies(env)
  1292  	ps.initTelemetry(env)
  1293  	ps.initProxyConfigs(env)
  1294  	ps.initWasmPlugins(env)
  1295  	ps.initEnvoyFilters(env, nil, nil)
  1296  	ps.initGateways(env)
  1297  	ps.initAmbient(env)
  1298  
  1299  	// Must be initialized in the end
  1300  	ps.initSidecarScopes(env)
  1301  	return nil
  1302  }
  1303  
  1304  func (ps *PushContext) updateContext(
  1305  	env *Environment,
  1306  	oldPushContext *PushContext,
  1307  	pushReq *PushRequest,
  1308  ) error {
  1309  	var servicesChanged, virtualServicesChanged, destinationRulesChanged, gatewayChanged,
  1310  		authnChanged, authzChanged, envoyFiltersChanged, sidecarsChanged, telemetryChanged, gatewayAPIChanged,
  1311  		wasmPluginsChanged, proxyConfigsChanged bool
  1312  
  1313  	changedEnvoyFilters := sets.New[ConfigKey]()
  1314  
  1315  	for conf := range pushReq.ConfigsUpdated {
  1316  		switch conf.Kind {
  1317  		case kind.ServiceEntry, kind.DNSName:
  1318  			servicesChanged = true
  1319  		case kind.DestinationRule:
  1320  			destinationRulesChanged = true
  1321  		case kind.VirtualService:
  1322  			virtualServicesChanged = true
  1323  		case kind.Gateway:
  1324  			gatewayChanged = true
  1325  		case kind.Sidecar:
  1326  			sidecarsChanged = true
  1327  		case kind.WasmPlugin:
  1328  			wasmPluginsChanged = true
  1329  		case kind.EnvoyFilter:
  1330  			envoyFiltersChanged = true
  1331  			if features.OptimizedConfigRebuild {
  1332  				changedEnvoyFilters.Insert(conf)
  1333  			}
  1334  		case kind.AuthorizationPolicy:
  1335  			authzChanged = true
  1336  		case kind.RequestAuthentication,
  1337  			kind.PeerAuthentication:
  1338  			authnChanged = true
  1339  		case kind.HTTPRoute, kind.TCPRoute, kind.TLSRoute, kind.GRPCRoute, kind.GatewayClass, kind.KubernetesGateway, kind.ReferenceGrant:
  1340  			gatewayAPIChanged = true
  1341  			// VS and GW are derived from gatewayAPI, so if it changed we need to update those as well
  1342  			virtualServicesChanged = true
  1343  			gatewayChanged = true
  1344  		case kind.Telemetry:
  1345  			telemetryChanged = true
  1346  		case kind.ProxyConfig:
  1347  			proxyConfigsChanged = true
  1348  		}
  1349  	}
  1350  
  1351  	if servicesChanged {
  1352  		// Services have changed. initialize service registry
  1353  		ps.initServiceRegistry(env, pushReq.ConfigsUpdated)
  1354  	} else {
  1355  		// make sure we copy over things that would be generated in initServiceRegistry
  1356  		ps.ServiceIndex = oldPushContext.ServiceIndex
  1357  		ps.serviceAccounts = oldPushContext.serviceAccounts
  1358  	}
  1359  
  1360  	if servicesChanged || gatewayAPIChanged {
  1361  		// Gateway status depends on services, so recompute if they change as well
  1362  		if err := ps.initKubernetesGateways(env); err != nil {
  1363  			return err
  1364  		}
  1365  	}
  1366  
  1367  	if virtualServicesChanged {
  1368  		ps.initVirtualServices(env)
  1369  	} else {
  1370  		ps.virtualServiceIndex = oldPushContext.virtualServiceIndex
  1371  	}
  1372  
  1373  	if destinationRulesChanged {
  1374  		ps.initDestinationRules(env)
  1375  	} else {
  1376  		ps.destinationRuleIndex = oldPushContext.destinationRuleIndex
  1377  	}
  1378  
  1379  	if authnChanged {
  1380  		ps.initAuthnPolicies(env)
  1381  	} else {
  1382  		ps.AuthnPolicies = oldPushContext.AuthnPolicies
  1383  	}
  1384  
  1385  	if authzChanged {
  1386  		ps.initAuthorizationPolicies(env)
  1387  	} else {
  1388  		ps.AuthzPolicies = oldPushContext.AuthzPolicies
  1389  	}
  1390  
  1391  	if telemetryChanged {
  1392  		ps.initTelemetry(env)
  1393  	} else {
  1394  		ps.Telemetry = oldPushContext.Telemetry
  1395  	}
  1396  
  1397  	if proxyConfigsChanged {
  1398  		ps.initProxyConfigs(env)
  1399  	} else {
  1400  		ps.ProxyConfigs = oldPushContext.ProxyConfigs
  1401  	}
  1402  
  1403  	if wasmPluginsChanged {
  1404  		ps.initWasmPlugins(env)
  1405  	} else {
  1406  		ps.wasmPluginsByNamespace = oldPushContext.wasmPluginsByNamespace
  1407  	}
  1408  
  1409  	if envoyFiltersChanged {
  1410  		ps.initEnvoyFilters(env, changedEnvoyFilters, oldPushContext.envoyFiltersByNamespace)
  1411  	} else {
  1412  		ps.envoyFiltersByNamespace = oldPushContext.envoyFiltersByNamespace
  1413  	}
  1414  
  1415  	if gatewayChanged {
  1416  		ps.initGateways(env)
  1417  	} else {
  1418  		ps.gatewayIndex = oldPushContext.gatewayIndex
  1419  	}
  1420  
  1421  	ps.initAmbient(env)
  1422  
  1423  	// Must be initialized in the end
  1424  	// Sidecars need to be updated if services, virtual services, destination rules, or the sidecar configs change
  1425  	if servicesChanged || virtualServicesChanged || destinationRulesChanged || sidecarsChanged {
  1426  		ps.initSidecarScopes(env)
  1427  	} else {
  1428  		// new ADS connection may insert new entry to computedSidecarsByNamespace/gatewayDefaultSidecarsByNamespace.
  1429  		oldPushContext.sidecarIndex.derivedSidecarMutex.RLock()
  1430  		ps.sidecarIndex = oldPushContext.sidecarIndex
  1431  		oldPushContext.sidecarIndex.derivedSidecarMutex.RUnlock()
  1432  	}
  1433  
  1434  	return nil
  1435  }
  1436  
  1437  // Caches list of services in the registry, and creates a map
  1438  // of hostname to service
  1439  func (ps *PushContext) initServiceRegistry(env *Environment, configsUpdate sets.Set[ConfigKey]) {
  1440  	// Sort the services in order of creation.
  1441  	allServices := SortServicesByCreationTime(env.Services())
  1442  	if features.EnableExternalNameAlias {
  1443  		resolveServiceAliases(allServices, configsUpdate)
  1444  	}
  1445  
  1446  	for _, s := range allServices {
  1447  		portMap := map[string]int{}
  1448  		ports := sets.New[int]()
  1449  		for _, port := range s.Ports {
  1450  			portMap[port.Name] = port.Port
  1451  			ports.Insert(port.Port)
  1452  		}
  1453  
  1454  		svcKey := s.Key()
  1455  		if _, ok := ps.ServiceIndex.instancesByPort[svcKey]; !ok {
  1456  			ps.ServiceIndex.instancesByPort[svcKey] = make(map[int][]*IstioEndpoint)
  1457  		}
  1458  		shards, ok := env.EndpointIndex.ShardsForService(string(s.Hostname), s.Attributes.Namespace)
  1459  		if ok {
  1460  			instancesByPort := shards.CopyEndpoints(portMap, ports)
  1461  			// Iterate over the instances and add them to the service index to avoid overiding the existing port instances.
  1462  			for port, instances := range instancesByPort {
  1463  				ps.ServiceIndex.instancesByPort[svcKey][port] = instances
  1464  			}
  1465  		}
  1466  		if _, f := ps.ServiceIndex.HostnameAndNamespace[s.Hostname]; !f {
  1467  			ps.ServiceIndex.HostnameAndNamespace[s.Hostname] = map[string]*Service{}
  1468  		}
  1469  		// In some scenarios, there may be multiple Services defined for the same hostname due to ServiceEntry allowing
  1470  		// arbitrary hostnames. In these cases, we want to pick the first Service, which is the oldest. This ensures
  1471  		// newly created Services cannot take ownership unexpectedly.
  1472  		// However, the Service is from Kubernetes it should take precedence over ones not. This prevents someone from
  1473  		// "domain squatting" on the hostname before a Kubernetes Service is created.
  1474  		if existing := ps.ServiceIndex.HostnameAndNamespace[s.Hostname][s.Attributes.Namespace]; existing != nil &&
  1475  			!(existing.Attributes.ServiceRegistry != provider.Kubernetes && s.Attributes.ServiceRegistry == provider.Kubernetes) {
  1476  			log.Debugf("Service %s/%s from registry %s ignored by %s/%s/%s", s.Attributes.Namespace, s.Hostname, s.Attributes.ServiceRegistry,
  1477  				existing.Attributes.ServiceRegistry, existing.Attributes.Namespace, existing.Hostname)
  1478  		} else {
  1479  			ps.ServiceIndex.HostnameAndNamespace[s.Hostname][s.Attributes.Namespace] = s
  1480  		}
  1481  
  1482  		ns := s.Attributes.Namespace
  1483  		if s.Attributes.ExportTo.IsEmpty() {
  1484  			if ps.exportToDefaults.service.Contains(visibility.Private) {
  1485  				ps.ServiceIndex.privateByNamespace[ns] = append(ps.ServiceIndex.privateByNamespace[ns], s)
  1486  			} else if ps.exportToDefaults.service.Contains(visibility.Public) {
  1487  				ps.ServiceIndex.public = append(ps.ServiceIndex.public, s)
  1488  			}
  1489  		} else {
  1490  			// if service has exportTo *, make it public and ignore all other exportTos.
  1491  			// if service does not have exportTo *, but has exportTo ~ - i.e. not visible to anyone, ignore all exportTos.
  1492  			// if service has exportTo ., replace with current namespace.
  1493  			if s.Attributes.ExportTo.Contains(visibility.Public) {
  1494  				ps.ServiceIndex.public = append(ps.ServiceIndex.public, s)
  1495  				continue
  1496  			} else if s.Attributes.ExportTo.Contains(visibility.None) {
  1497  				continue
  1498  			}
  1499  			// . or other namespaces
  1500  			for exportTo := range s.Attributes.ExportTo {
  1501  				if exportTo == visibility.Private || string(exportTo) == ns {
  1502  					// exportTo with same namespace is effectively private
  1503  					ps.ServiceIndex.privateByNamespace[ns] = append(ps.ServiceIndex.privateByNamespace[ns], s)
  1504  				} else {
  1505  					// exportTo is a specific target namespace
  1506  					ps.ServiceIndex.exportedToNamespace[string(exportTo)] = append(ps.ServiceIndex.exportedToNamespace[string(exportTo)], s)
  1507  				}
  1508  			}
  1509  		}
  1510  	}
  1511  
  1512  	ps.initServiceAccounts(env, allServices)
  1513  }
  1514  
  1515  // resolveServiceAliases sets the Aliases attributes on all services. The incoming Service's will just have AliasFor set,
  1516  // but in our usage we often need the opposite: for a given service, what are all the aliases?
  1517  // resolveServiceAliases walks this 'graph' of services and updates the Alias field in-place.
  1518  func resolveServiceAliases(allServices []*Service, configsUpdated sets.Set[ConfigKey]) {
  1519  	// rawAlias builds a map of Service -> AliasFor. So this will be ExternalName -> Service.
  1520  	// In an edge case, we can have ExternalName -> ExternalName; we resolve that below.
  1521  	rawAlias := map[NamespacedHostname]host.Name{}
  1522  	for _, s := range allServices {
  1523  		if s.Resolution != Alias {
  1524  			continue
  1525  		}
  1526  		nh := NamespacedHostname{
  1527  			Hostname:  s.Hostname,
  1528  			Namespace: s.Attributes.Namespace,
  1529  		}
  1530  		rawAlias[nh] = host.Name(s.Attributes.K8sAttributes.ExternalName)
  1531  	}
  1532  
  1533  	// unnamespacedRawAlias is like rawAlias but without namespaces.
  1534  	// This is because an `ExternalName` isn't namespaced. If there is a conflict, the behavior is undefined.
  1535  	// This is split from above as a minor optimization to right-size the map
  1536  	unnamespacedRawAlias := make(map[host.Name]host.Name, len(rawAlias))
  1537  	for k, v := range rawAlias {
  1538  		unnamespacedRawAlias[k.Hostname] = v
  1539  	}
  1540  
  1541  	// resolvedAliases builds a map of Alias -> Concrete, fully resolving through multiple hops.
  1542  	// Ex: Alias1 -> Alias2 -> Concrete will flatten to Alias1 -> Concrete.
  1543  	resolvedAliases := make(map[NamespacedHostname]host.Name, len(rawAlias))
  1544  	for alias, referencedService := range rawAlias {
  1545  		// referencedService may be another alias or a concrete service.
  1546  		if _, f := unnamespacedRawAlias[referencedService]; !f {
  1547  			// Common case: alias pointing to a concrete service
  1548  			resolvedAliases[alias] = referencedService
  1549  			continue
  1550  		}
  1551  		// Otherwise, we need to traverse the alias "graph".
  1552  		// In an obscure edge case, a user could make a loop, so we will need to handle that.
  1553  		seen := sets.New(alias.Hostname, referencedService)
  1554  		for {
  1555  			n, f := unnamespacedRawAlias[referencedService]
  1556  			if !f {
  1557  				// The destination we are pointing to is not an alias, so this is the terminal step
  1558  				resolvedAliases[alias] = referencedService
  1559  				break
  1560  			}
  1561  			if seen.InsertContains(n) {
  1562  				// We did a loop!
  1563  				// Kubernetes will make these NXDomain, so we can just treat it like it doesn't exist at all
  1564  				break
  1565  			}
  1566  			referencedService = n
  1567  		}
  1568  	}
  1569  
  1570  	// aliasesForService builds a map of Concrete -> []Aliases
  1571  	// This basically reverses our resolvedAliased map, which is Alias -> Concrete,
  1572  	aliasesForService := map[host.Name][]NamespacedHostname{}
  1573  	for alias, concrete := range resolvedAliases {
  1574  		aliasesForService[concrete] = append(aliasesForService[concrete], alias)
  1575  
  1576  		// We also need to update configsUpdated, such that any "alias" updated also marks the concrete service as updated.
  1577  		aliasKey := ConfigKey{
  1578  			Kind:      kind.ServiceEntry,
  1579  			Name:      alias.Hostname.String(),
  1580  			Namespace: alias.Namespace,
  1581  		}
  1582  		// Alias. We should mark all the concrete services as updated as well.
  1583  		if configsUpdated.Contains(aliasKey) {
  1584  			// We only have the hostname, but we need the namespace...
  1585  			for _, svc := range allServices {
  1586  				if svc.Hostname == concrete {
  1587  					configsUpdated.Insert(ConfigKey{
  1588  						Kind:      kind.ServiceEntry,
  1589  						Name:      concrete.String(),
  1590  						Namespace: svc.Attributes.Namespace,
  1591  					})
  1592  				}
  1593  			}
  1594  		}
  1595  	}
  1596  	// Sort aliases so order is deterministic.
  1597  	for _, v := range aliasesForService {
  1598  		slices.SortFunc(v, func(a, b NamespacedHostname) int {
  1599  			if r := cmp.Compare(a.Namespace, b.Namespace); r != 0 {
  1600  				return r
  1601  			}
  1602  			return cmp.Compare(a.Hostname, b.Hostname)
  1603  		})
  1604  	}
  1605  
  1606  	// Finally, we can traverse all services and update the ones that have aliases
  1607  	for i, s := range allServices {
  1608  		if aliases, f := aliasesForService[s.Hostname]; f {
  1609  			// This service has an alias; set it. We need to make a copy since the underlying Service is shared
  1610  			s = s.DeepCopy()
  1611  			s.Attributes.Aliases = aliases
  1612  			allServices[i] = s
  1613  		}
  1614  	}
  1615  }
  1616  
  1617  // SortServicesByCreationTime sorts the list of services in ascending order by their creation time (if available).
  1618  func SortServicesByCreationTime(services []*Service) []*Service {
  1619  	slices.SortStableFunc(services, func(a, b *Service) int {
  1620  		if r := a.CreationTime.Compare(b.CreationTime); r != 0 {
  1621  			return r
  1622  		}
  1623  		// If creation time is the same, then behavior is nondeterministic. In this case, we can
  1624  		// pick an arbitrary but consistent ordering based on name and namespace, which is unique.
  1625  		// CreationTimestamp is stored in seconds, so this is not uncommon.
  1626  		an := a.Attributes.Name + "." + a.Attributes.Namespace
  1627  		bn := b.Attributes.Name + "." + b.Attributes.Namespace
  1628  		return cmp.Compare(an, bn)
  1629  	})
  1630  	return services
  1631  }
  1632  
  1633  // Caches list of service accounts in the registry
  1634  func (ps *PushContext) initServiceAccounts(env *Environment, services []*Service) {
  1635  	for _, svc := range services {
  1636  		var accounts sets.String
  1637  		// First get endpoint level service accounts
  1638  		shard, f := env.EndpointIndex.ShardsForService(string(svc.Hostname), svc.Attributes.Namespace)
  1639  		if f {
  1640  			shard.RLock()
  1641  			// copy here to reduce the lock time
  1642  			// endpoints could update frequently, so the longer it locks, the more likely it will block other threads.
  1643  			accounts = shard.ServiceAccounts.Copy()
  1644  			shard.RUnlock()
  1645  		}
  1646  		if len(svc.ServiceAccounts) > 0 {
  1647  			if accounts == nil {
  1648  				accounts = sets.New(svc.ServiceAccounts...)
  1649  			} else {
  1650  				accounts = accounts.InsertAll(svc.ServiceAccounts...)
  1651  			}
  1652  		}
  1653  		sa := sets.SortedList(spiffe.ExpandWithTrustDomains(accounts, ps.Mesh.TrustDomainAliases))
  1654  		key := serviceAccountKey{
  1655  			hostname:  svc.Hostname,
  1656  			namespace: svc.Attributes.Namespace,
  1657  		}
  1658  		ps.serviceAccounts[key] = sa
  1659  	}
  1660  }
  1661  
  1662  // Caches list of authentication policies
  1663  func (ps *PushContext) initAuthnPolicies(env *Environment) {
  1664  	ps.AuthnPolicies = initAuthenticationPolicies(env)
  1665  }
  1666  
  1667  // Caches list of virtual services
  1668  func (ps *PushContext) initVirtualServices(env *Environment) {
  1669  	ps.virtualServiceIndex.exportedToNamespaceByGateway = map[types.NamespacedName][]config.Config{}
  1670  	ps.virtualServiceIndex.privateByNamespaceAndGateway = map[types.NamespacedName][]config.Config{}
  1671  	ps.virtualServiceIndex.publicByGateway = map[string][]config.Config{}
  1672  	ps.virtualServiceIndex.referencedDestinations = map[string]sets.String{}
  1673  
  1674  	if features.FilterGatewayClusterConfig {
  1675  		ps.virtualServiceIndex.destinationsByGateway = make(map[string]sets.String)
  1676  	}
  1677  
  1678  	virtualServices := env.List(gvk.VirtualService, NamespaceAll)
  1679  
  1680  	// values returned from ConfigStore.List are immutable.
  1681  	// Therefore, we make a copy
  1682  	vservices := make([]config.Config, len(virtualServices))
  1683  
  1684  	for i := range vservices {
  1685  		vservices[i] = virtualServices[i].DeepCopy()
  1686  	}
  1687  
  1688  	totalVirtualServices.Record(float64(len(virtualServices)))
  1689  
  1690  	// convert all shortnames in virtual services into FQDNs
  1691  	for _, r := range vservices {
  1692  		resolveVirtualServiceShortnames(r.Spec.(*networking.VirtualService), r.Meta)
  1693  	}
  1694  
  1695  	vservices, ps.virtualServiceIndex.delegates = mergeVirtualServicesIfNeeded(vservices, ps.exportToDefaults.virtualService)
  1696  
  1697  	for _, virtualService := range vservices {
  1698  		ns := virtualService.Namespace
  1699  		rule := virtualService.Spec.(*networking.VirtualService)
  1700  		gwNames := getGatewayNames(rule)
  1701  		if len(rule.ExportTo) == 0 {
  1702  			// No exportTo in virtualService. Use the global default
  1703  			// We only honor ., *
  1704  			if ps.exportToDefaults.virtualService.Contains(visibility.Private) {
  1705  				// add to local namespace only
  1706  				private := ps.virtualServiceIndex.privateByNamespaceAndGateway
  1707  				for _, gw := range gwNames {
  1708  					n := types.NamespacedName{Namespace: ns, Name: gw}
  1709  					private[n] = append(private[n], virtualService)
  1710  				}
  1711  			} else if ps.exportToDefaults.virtualService.Contains(visibility.Public) {
  1712  				for _, gw := range gwNames {
  1713  					ps.virtualServiceIndex.publicByGateway[gw] = append(ps.virtualServiceIndex.publicByGateway[gw], virtualService)
  1714  				}
  1715  			}
  1716  		} else {
  1717  			exportToSet := sets.NewWithLength[visibility.Instance](len(rule.ExportTo))
  1718  			for _, e := range rule.ExportTo {
  1719  				exportToSet.Insert(visibility.Instance(e))
  1720  			}
  1721  			// if vs has exportTo ~ - i.e. not visible to anyone, ignore all exportTos
  1722  			// if vs has exportTo *, make public and ignore all other exportTos
  1723  			// if vs has exportTo ., replace with current namespace
  1724  			if exportToSet.Contains(visibility.Public) {
  1725  				for _, gw := range gwNames {
  1726  					ps.virtualServiceIndex.publicByGateway[gw] = append(ps.virtualServiceIndex.publicByGateway[gw], virtualService)
  1727  				}
  1728  			} else if !exportToSet.Contains(visibility.None) {
  1729  				// . or other namespaces
  1730  				for exportTo := range exportToSet {
  1731  					if exportTo == visibility.Private || string(exportTo) == ns {
  1732  						// add to local namespace only
  1733  						for _, gw := range gwNames {
  1734  							n := types.NamespacedName{Namespace: ns, Name: gw}
  1735  							ps.virtualServiceIndex.privateByNamespaceAndGateway[n] = append(ps.virtualServiceIndex.privateByNamespaceAndGateway[n], virtualService)
  1736  						}
  1737  					} else {
  1738  						exported := ps.virtualServiceIndex.exportedToNamespaceByGateway
  1739  						// add to local namespace only
  1740  						for _, gw := range gwNames {
  1741  							n := types.NamespacedName{Namespace: string(exportTo), Name: gw}
  1742  							exported[n] = append(exported[n], virtualService)
  1743  						}
  1744  					}
  1745  				}
  1746  			}
  1747  		}
  1748  
  1749  		if features.FilterGatewayClusterConfig {
  1750  			for _, gw := range gwNames {
  1751  				if gw == constants.IstioMeshGateway {
  1752  					continue
  1753  				}
  1754  				for host := range virtualServiceDestinations(rule) {
  1755  					sets.InsertOrNew(ps.virtualServiceIndex.destinationsByGateway, gw, host)
  1756  				}
  1757  			}
  1758  		}
  1759  
  1760  		// For mesh virtual services, build a map of host -> referenced destinations
  1761  		if features.EnableAmbientWaypoints && (len(rule.Gateways) == 0 || slices.Contains(rule.Gateways, constants.IstioMeshGateway)) {
  1762  			for host := range virtualServiceDestinations(rule) {
  1763  				for _, rhost := range rule.Hosts {
  1764  					if _, f := ps.virtualServiceIndex.referencedDestinations[rhost]; !f {
  1765  						ps.virtualServiceIndex.referencedDestinations[rhost] = sets.New[string]()
  1766  					}
  1767  					ps.virtualServiceIndex.referencedDestinations[rhost].Insert(host)
  1768  				}
  1769  			}
  1770  		}
  1771  	}
  1772  }
  1773  
  1774  var meshGateways = []string{constants.IstioMeshGateway}
  1775  
  1776  func getGatewayNames(vs *networking.VirtualService) []string {
  1777  	if len(vs.Gateways) == 0 {
  1778  		return meshGateways
  1779  	}
  1780  	res := make([]string, 0, len(vs.Gateways))
  1781  	res = append(res, vs.Gateways...)
  1782  	return res
  1783  }
  1784  
  1785  func (ps *PushContext) initDefaultExportMaps() {
  1786  	ps.exportToDefaults.destinationRule = sets.New[visibility.Instance]()
  1787  	if ps.Mesh.DefaultDestinationRuleExportTo != nil {
  1788  		for _, e := range ps.Mesh.DefaultDestinationRuleExportTo {
  1789  			ps.exportToDefaults.destinationRule.Insert(visibility.Instance(e))
  1790  		}
  1791  	} else {
  1792  		// default to *
  1793  		ps.exportToDefaults.destinationRule.Insert(visibility.Public)
  1794  	}
  1795  
  1796  	ps.exportToDefaults.service = sets.New[visibility.Instance]()
  1797  	if ps.Mesh.DefaultServiceExportTo != nil {
  1798  		for _, e := range ps.Mesh.DefaultServiceExportTo {
  1799  			ps.exportToDefaults.service.Insert(visibility.Instance(e))
  1800  		}
  1801  	} else {
  1802  		ps.exportToDefaults.service.Insert(visibility.Public)
  1803  	}
  1804  
  1805  	ps.exportToDefaults.virtualService = sets.New[visibility.Instance]()
  1806  	if ps.Mesh.DefaultVirtualServiceExportTo != nil {
  1807  		for _, e := range ps.Mesh.DefaultVirtualServiceExportTo {
  1808  			ps.exportToDefaults.virtualService.Insert(visibility.Instance(e))
  1809  		}
  1810  	} else {
  1811  		ps.exportToDefaults.virtualService.Insert(visibility.Public)
  1812  	}
  1813  }
  1814  
  1815  // initSidecarScopes synthesizes Sidecar CRDs into objects called
  1816  // SidecarScope.  The SidecarScope object is a semi-processed view of the
  1817  // service registry, and config state associated with the sidecar CRD. The
  1818  // scope contains a set of inbound and outbound listeners, services/configs
  1819  // per listener, etc. The sidecar scopes are precomputed based on the
  1820  // Sidecar API objects in each namespace. If there is no sidecar api object
  1821  // for a namespace, a default sidecarscope is assigned to the namespace
  1822  // which enables connectivity to all services in the mesh.
  1823  //
  1824  // When proxies connect to Pilot, we identify the sidecar scope associated
  1825  // with the proxy and derive listeners/routes/clusters based on the sidecar
  1826  // scope.
  1827  func (ps *PushContext) initSidecarScopes(env *Environment) {
  1828  	rawSidecarConfigs := env.List(gvk.Sidecar, NamespaceAll)
  1829  
  1830  	sortConfigByCreationTime(rawSidecarConfigs)
  1831  
  1832  	sidecarConfigs := make([]config.Config, 0, len(rawSidecarConfigs))
  1833  	for _, sidecarConfig := range rawSidecarConfigs {
  1834  		sidecar := sidecarConfig.Spec.(*networking.Sidecar)
  1835  		// sidecars with selector take preference
  1836  		if sidecar.WorkloadSelector != nil {
  1837  			sidecarConfigs = append(sidecarConfigs, sidecarConfig)
  1838  		}
  1839  	}
  1840  	for _, sidecarConfig := range rawSidecarConfigs {
  1841  		sidecar := sidecarConfig.Spec.(*networking.Sidecar)
  1842  		// sidecars without selector placed behind
  1843  		if sidecar.WorkloadSelector == nil {
  1844  			sidecarConfigs = append(sidecarConfigs, sidecarConfig)
  1845  		}
  1846  	}
  1847  
  1848  	// Hold reference root namespace's sidecar config
  1849  	// Root namespace can have only one sidecar config object
  1850  	// Currently we expect that it has no workloadSelectors
  1851  	var rootNSConfig *config.Config
  1852  	for i, sidecarConfig := range sidecarConfigs {
  1853  		if sidecarConfig.Namespace == ps.Mesh.RootNamespace &&
  1854  			sidecarConfig.Spec.(*networking.Sidecar).WorkloadSelector == nil {
  1855  			rootNSConfig = &sidecarConfigs[i]
  1856  			break
  1857  		}
  1858  	}
  1859  	ps.sidecarIndex.meshRootSidecarConfig = rootNSConfig
  1860  
  1861  	ps.sidecarIndex.sidecarsByNamespace = make(map[string][]*SidecarScope)
  1862  	ps.convertSidecarScopes(sidecarConfigs)
  1863  }
  1864  
  1865  func (ps *PushContext) convertSidecarScopes(sidecarConfigs []config.Config) {
  1866  	if len(sidecarConfigs) == 0 {
  1867  		return
  1868  	}
  1869  	if features.ConvertSidecarScopeConcurrency > 1 {
  1870  		ps.concurrentConvertToSidecarScope(sidecarConfigs)
  1871  	} else {
  1872  		for _, sidecarConfig := range sidecarConfigs {
  1873  			ps.sidecarIndex.sidecarsByNamespace[sidecarConfig.Namespace] = append(ps.sidecarIndex.sidecarsByNamespace[sidecarConfig.Namespace],
  1874  				convertToSidecarScope(ps, &sidecarConfig, sidecarConfig.Namespace))
  1875  		}
  1876  	}
  1877  }
  1878  
  1879  func (ps *PushContext) concurrentConvertToSidecarScope(sidecarConfigs []config.Config) {
  1880  	type taskItem struct {
  1881  		idx int
  1882  		cfg config.Config
  1883  	}
  1884  
  1885  	var wg sync.WaitGroup
  1886  	taskItems := make(chan taskItem)
  1887  	sidecarScopes := make([]*SidecarScope, len(sidecarConfigs))
  1888  	for i := 0; i < features.ConvertSidecarScopeConcurrency; i++ {
  1889  		wg.Add(1)
  1890  		go func() {
  1891  			defer wg.Done()
  1892  			for {
  1893  				item, ok := <-taskItems
  1894  				if !ok {
  1895  					break
  1896  				}
  1897  				sc := convertToSidecarScope(ps, &item.cfg, item.cfg.Namespace)
  1898  				sidecarScopes[item.idx] = sc
  1899  			}
  1900  		}()
  1901  	}
  1902  
  1903  	// note: sidecarScopes order matters and needs to be kept in the same order as sidecarConfigs.
  1904  	// The order indicates priority, see getSidecarScope.
  1905  	for idx, cfg := range sidecarConfigs {
  1906  		taskItems <- taskItem{idx: idx, cfg: cfg}
  1907  	}
  1908  
  1909  	close(taskItems)
  1910  	wg.Wait()
  1911  
  1912  	for _, sc := range sidecarScopes {
  1913  		ps.sidecarIndex.sidecarsByNamespace[sc.Namespace] = append(ps.sidecarIndex.sidecarsByNamespace[sc.Namespace], sc)
  1914  	}
  1915  }
  1916  
  1917  // Split out of DestinationRule expensive conversions - once per push.
  1918  func (ps *PushContext) initDestinationRules(env *Environment) {
  1919  	configs := env.List(gvk.DestinationRule, NamespaceAll)
  1920  
  1921  	// values returned from ConfigStore.List are immutable.
  1922  	// Therefore, we make a copy
  1923  	destRules := make([]config.Config, len(configs))
  1924  	for i := range destRules {
  1925  		destRules[i] = configs[i]
  1926  	}
  1927  
  1928  	ps.setDestinationRules(destRules)
  1929  }
  1930  
  1931  func newConsolidatedDestRules() *consolidatedDestRules {
  1932  	return &consolidatedDestRules{
  1933  		exportTo:          map[host.Name]sets.Set[visibility.Instance]{},
  1934  		specificDestRules: map[host.Name][]*ConsolidatedDestRule{},
  1935  		wildcardDestRules: map[host.Name][]*ConsolidatedDestRule{},
  1936  	}
  1937  }
  1938  
  1939  // Testing Only. This allows tests to inject a config without having the mock.
  1940  func (ps *PushContext) SetDestinationRulesForTesting(configs []config.Config) {
  1941  	ps.setDestinationRules(configs)
  1942  }
  1943  
  1944  // sortConfigBySelectorAndCreationTime sorts the list of config objects based on priority and creation time.
  1945  func sortConfigBySelectorAndCreationTime(configs []config.Config) []config.Config {
  1946  	creationTimeComparator := sortByCreationComparator(configs)
  1947  	// Define a comparator function for sorting configs by priority and creation time
  1948  	comparator := func(i, j int) bool {
  1949  		idr := configs[i].Spec.(*networking.DestinationRule)
  1950  		jdr := configs[j].Spec.(*networking.DestinationRule)
  1951  
  1952  		// Check if one of the configs has priority set to true
  1953  		if idr.GetWorkloadSelector() != nil && jdr.GetWorkloadSelector() == nil {
  1954  			return true
  1955  		} else if idr.GetWorkloadSelector() == nil && jdr.GetWorkloadSelector() != nil {
  1956  			return false
  1957  		}
  1958  
  1959  		// If priority is the same or neither has priority, fallback to creation time ordering
  1960  		return creationTimeComparator(i, j)
  1961  	}
  1962  
  1963  	// Sort the configs using the defined comparator function
  1964  	sort.Slice(configs, comparator)
  1965  	return configs
  1966  }
  1967  
  1968  // setDestinationRules updates internal structures using a set of configs.
  1969  // Split out of DestinationRule expensive conversions, computed once per push.
  1970  // This will not work properly for Sidecars, which will precompute their
  1971  // destination rules on init.
  1972  func (ps *PushContext) setDestinationRules(configs []config.Config) {
  1973  	// Sort by time first. So if two destination rule have top level traffic policies
  1974  	// we take the first one.
  1975  	sortConfigBySelectorAndCreationTime(configs)
  1976  	namespaceLocalDestRules := make(map[string]*consolidatedDestRules)
  1977  	exportedDestRulesByNamespace := make(map[string]*consolidatedDestRules)
  1978  	rootNamespaceLocalDestRules := newConsolidatedDestRules()
  1979  
  1980  	for i := range configs {
  1981  		rule := configs[i].Spec.(*networking.DestinationRule)
  1982  
  1983  		rule.Host = string(ResolveShortnameToFQDN(rule.Host, configs[i].Meta))
  1984  		var exportToSet sets.Set[visibility.Instance]
  1985  
  1986  		// destination rules with workloadSelector should not be exported to other namespaces
  1987  		if rule.GetWorkloadSelector() == nil {
  1988  			exportToSet = sets.NewWithLength[visibility.Instance](len(rule.ExportTo))
  1989  			for _, e := range rule.ExportTo {
  1990  				exportToSet.Insert(visibility.Instance(e))
  1991  			}
  1992  		} else {
  1993  			exportToSet = sets.New[visibility.Instance](visibility.Private)
  1994  		}
  1995  
  1996  		// add only if the dest rule is exported with . or * or explicit exportTo containing this namespace
  1997  		// The global exportTo doesn't matter here (its either . or * - both of which are applicable here)
  1998  		if exportToSet.IsEmpty() || exportToSet.Contains(visibility.Public) || exportToSet.Contains(visibility.Private) ||
  1999  			exportToSet.Contains(visibility.Instance(configs[i].Namespace)) {
  2000  			// Store in an index for the config's namespace
  2001  			// a proxy from this namespace will first look here for the destination rule for a given service
  2002  			// This pool consists of both public/private destination rules.
  2003  			if _, exist := namespaceLocalDestRules[configs[i].Namespace]; !exist {
  2004  				namespaceLocalDestRules[configs[i].Namespace] = newConsolidatedDestRules()
  2005  			}
  2006  			// Merge this destination rule with any public/private dest rules for same host in the same namespace
  2007  			// If there are no duplicates, the dest rule will be added to the list
  2008  			ps.mergeDestinationRule(namespaceLocalDestRules[configs[i].Namespace], configs[i], exportToSet)
  2009  		}
  2010  
  2011  		isPrivateOnly := false
  2012  		// No exportTo in destinationRule. Use the global default
  2013  		// We only honor . and *
  2014  		if exportToSet.IsEmpty() && ps.exportToDefaults.destinationRule.Contains(visibility.Private) {
  2015  			isPrivateOnly = true
  2016  		} else if exportToSet.Len() == 1 && (exportToSet.Contains(visibility.Private) || exportToSet.Contains(visibility.Instance(configs[i].Namespace))) {
  2017  			isPrivateOnly = true
  2018  		}
  2019  
  2020  		if !isPrivateOnly {
  2021  			if _, exist := exportedDestRulesByNamespace[configs[i].Namespace]; !exist {
  2022  				exportedDestRulesByNamespace[configs[i].Namespace] = newConsolidatedDestRules()
  2023  			}
  2024  			// Merge this destination rule with any other exported dest rule for the same host in the same namespace
  2025  			// If there are no duplicates, the dest rule will be added to the list
  2026  			ps.mergeDestinationRule(exportedDestRulesByNamespace[configs[i].Namespace], configs[i], exportToSet)
  2027  		} else if configs[i].Namespace == ps.Mesh.RootNamespace {
  2028  			// Keep track of private root namespace destination rules
  2029  			ps.mergeDestinationRule(rootNamespaceLocalDestRules, configs[i], exportToSet)
  2030  		}
  2031  	}
  2032  
  2033  	ps.destinationRuleIndex.namespaceLocal = namespaceLocalDestRules
  2034  	ps.destinationRuleIndex.exportedByNamespace = exportedDestRulesByNamespace
  2035  	ps.destinationRuleIndex.rootNamespaceLocal = rootNamespaceLocalDestRules
  2036  }
  2037  
  2038  // pre computes all AuthorizationPolicies per namespace
  2039  func (ps *PushContext) initAuthorizationPolicies(env *Environment) {
  2040  	ps.AuthzPolicies = GetAuthorizationPolicies(env)
  2041  }
  2042  
  2043  func (ps *PushContext) initTelemetry(env *Environment) {
  2044  	ps.Telemetry = getTelemetries(env)
  2045  }
  2046  
  2047  func (ps *PushContext) initProxyConfigs(env *Environment) {
  2048  	ps.ProxyConfigs = GetProxyConfigs(env.ConfigStore, env.Mesh())
  2049  }
  2050  
  2051  // pre computes WasmPlugins per namespace
  2052  func (ps *PushContext) initWasmPlugins(env *Environment) {
  2053  	wasmplugins := env.List(gvk.WasmPlugin, NamespaceAll)
  2054  
  2055  	sortConfigByCreationTime(wasmplugins)
  2056  	ps.wasmPluginsByNamespace = map[string][]*WasmPluginWrapper{}
  2057  	for _, plugin := range wasmplugins {
  2058  		if pluginWrapper := convertToWasmPluginWrapper(plugin); pluginWrapper != nil {
  2059  			ps.wasmPluginsByNamespace[plugin.Namespace] = append(ps.wasmPluginsByNamespace[plugin.Namespace], pluginWrapper)
  2060  		}
  2061  	}
  2062  }
  2063  
  2064  // WasmPlugins return the WasmPluginWrappers of a proxy.
  2065  func (ps *PushContext) WasmPlugins(proxy *Proxy) map[extensions.PluginPhase][]*WasmPluginWrapper {
  2066  	return ps.WasmPluginsByListenerInfo(proxy, anyListener, WasmPluginTypeAny)
  2067  }
  2068  
  2069  func (ps *PushContext) WasmPluginsByName(proxy *Proxy, names []types.NamespacedName) []*WasmPluginWrapper {
  2070  	res := make([]*WasmPluginWrapper, 0, len(names))
  2071  	for _, n := range names {
  2072  		if n.Namespace != proxy.ConfigNamespace && n.Namespace != ps.Mesh.RootNamespace {
  2073  			log.Warnf("proxy requested invalid WASM configuration: %v", n)
  2074  			continue
  2075  		}
  2076  		for _, wsm := range ps.wasmPluginsByNamespace[n.Namespace] {
  2077  			if wsm.Name == n.Name {
  2078  				res = append(res, wsm)
  2079  				break
  2080  			}
  2081  		}
  2082  	}
  2083  	return res
  2084  }
  2085  
  2086  // WasmPluginsByListenerInfo return the WasmPluginWrappers which are matched with TrafficSelector in the given proxy.
  2087  func (ps *PushContext) WasmPluginsByListenerInfo(proxy *Proxy, info WasmPluginListenerInfo,
  2088  	pluginType WasmPluginType,
  2089  ) map[extensions.PluginPhase][]*WasmPluginWrapper {
  2090  	if proxy == nil {
  2091  		return nil
  2092  	}
  2093  
  2094  	var lookupInNamespaces []string
  2095  	matchedPlugins := make(map[extensions.PluginPhase][]*WasmPluginWrapper)
  2096  
  2097  	if proxy.ConfigNamespace != ps.Mesh.RootNamespace {
  2098  		// Only check the root namespace if the (workload) namespace is not already the root namespace
  2099  		// to avoid double inclusion.
  2100  		lookupInNamespaces = []string{proxy.ConfigNamespace, ps.Mesh.RootNamespace}
  2101  	} else {
  2102  		lookupInNamespaces = []string{proxy.ConfigNamespace}
  2103  	}
  2104  
  2105  	selectionOpts := PolicyMatcherForProxy(proxy).WithService(info.Service)
  2106  	for _, ns := range lookupInNamespaces {
  2107  		if wasmPlugins, ok := ps.wasmPluginsByNamespace[ns]; ok {
  2108  			for _, plugin := range wasmPlugins {
  2109  				if plugin.MatchListener(selectionOpts, info) && plugin.MatchType(pluginType) {
  2110  					matchedPlugins[plugin.Phase] = append(matchedPlugins[plugin.Phase], plugin)
  2111  				}
  2112  			}
  2113  		}
  2114  	}
  2115  
  2116  	// sort slices by priority
  2117  	for i, slice := range matchedPlugins {
  2118  		sort.SliceStable(slice, func(i, j int) bool {
  2119  			iPriority := int32(math.MinInt32)
  2120  			if prio := slice[i].Priority; prio != nil {
  2121  				iPriority = prio.Value
  2122  			}
  2123  			jPriority := int32(math.MinInt32)
  2124  			if prio := slice[j].Priority; prio != nil {
  2125  				jPriority = prio.Value
  2126  			}
  2127  			return iPriority > jPriority
  2128  		})
  2129  		matchedPlugins[i] = slice
  2130  	}
  2131  
  2132  	return matchedPlugins
  2133  }
  2134  
  2135  // pre computes envoy filters per namespace
  2136  func (ps *PushContext) initEnvoyFilters(env *Environment, changed sets.Set[ConfigKey], previousIndex map[string][]*EnvoyFilterWrapper) {
  2137  	envoyFilterConfigs := env.List(gvk.EnvoyFilter, NamespaceAll)
  2138  	var previous map[ConfigKey]*EnvoyFilterWrapper
  2139  	if features.OptimizedConfigRebuild {
  2140  		previous = make(map[ConfigKey]*EnvoyFilterWrapper)
  2141  		for namespace, nsEnvoyFilters := range previousIndex {
  2142  			for _, envoyFilter := range nsEnvoyFilters {
  2143  				previous[ConfigKey{Kind: kind.EnvoyFilter, Namespace: namespace, Name: envoyFilter.Name}] = envoyFilter
  2144  			}
  2145  		}
  2146  	}
  2147  
  2148  	sort.Slice(envoyFilterConfigs, func(i, j int) bool {
  2149  		ifilter := envoyFilterConfigs[i].Spec.(*networking.EnvoyFilter)
  2150  		jfilter := envoyFilterConfigs[j].Spec.(*networking.EnvoyFilter)
  2151  		if ifilter.Priority != jfilter.Priority {
  2152  			return ifilter.Priority < jfilter.Priority
  2153  		}
  2154  		// If priority is same fallback to name and creation timestamp, else use priority.
  2155  		// If creation time is the same, then behavior is nondeterministic. In this case, we can
  2156  		// pick an arbitrary but consistent ordering based on name and namespace, which is unique.
  2157  		// CreationTimestamp is stored in seconds, so this is not uncommon.
  2158  		if envoyFilterConfigs[i].CreationTimestamp != envoyFilterConfigs[j].CreationTimestamp {
  2159  			return envoyFilterConfigs[i].CreationTimestamp.Before(envoyFilterConfigs[j].CreationTimestamp)
  2160  		}
  2161  		in := envoyFilterConfigs[i].Name + "." + envoyFilterConfigs[i].Namespace
  2162  		jn := envoyFilterConfigs[j].Name + "." + envoyFilterConfigs[j].Namespace
  2163  		return in < jn
  2164  	})
  2165  
  2166  	for _, envoyFilterConfig := range envoyFilterConfigs {
  2167  		var efw *EnvoyFilterWrapper
  2168  		if features.OptimizedConfigRebuild {
  2169  			key := ConfigKey{Kind: kind.EnvoyFilter, Namespace: envoyFilterConfig.Namespace, Name: envoyFilterConfig.Name}
  2170  			if prev, ok := previous[key]; ok && !changed.Contains(key) {
  2171  				// Reuse the previous EnvoyFilterWrapper if it exists and hasn't changed when optimized config rebuild is enabled
  2172  				efw = prev
  2173  			}
  2174  		}
  2175  		// Rebuild the envoy filter in all other cases.
  2176  		if efw == nil {
  2177  			efw = convertToEnvoyFilterWrapper(&envoyFilterConfig)
  2178  		}
  2179  		ps.envoyFiltersByNamespace[envoyFilterConfig.Namespace] = append(ps.envoyFiltersByNamespace[envoyFilterConfig.Namespace], efw)
  2180  	}
  2181  }
  2182  
  2183  // EnvoyFilters return the merged EnvoyFilterWrapper of a proxy
  2184  func (ps *PushContext) EnvoyFilters(proxy *Proxy) *EnvoyFilterWrapper {
  2185  	// this should never happen
  2186  	if proxy == nil {
  2187  		return nil
  2188  	}
  2189  	var matchedEnvoyFilters []*EnvoyFilterWrapper
  2190  	// EnvoyFilters supports inheritance (global ones plus namespace local ones).
  2191  	// First get all the filter configs from the config root namespace
  2192  	// and then add the ones from proxy's own namespace
  2193  	if ps.Mesh.RootNamespace != "" {
  2194  		matchedEnvoyFilters = ps.getMatchedEnvoyFilters(proxy, ps.Mesh.RootNamespace)
  2195  	}
  2196  
  2197  	// To prevent duplicate envoyfilters in case root namespace equals proxy's namespace
  2198  	if proxy.ConfigNamespace != ps.Mesh.RootNamespace {
  2199  		matched := ps.getMatchedEnvoyFilters(proxy, proxy.ConfigNamespace)
  2200  		matchedEnvoyFilters = append(matchedEnvoyFilters, matched...)
  2201  	}
  2202  
  2203  	sort.Slice(matchedEnvoyFilters, func(i, j int) bool {
  2204  		ifilter := matchedEnvoyFilters[i]
  2205  		jfilter := matchedEnvoyFilters[j]
  2206  		if ifilter.Priority != jfilter.Priority {
  2207  			return ifilter.Priority < jfilter.Priority
  2208  		}
  2209  		// Prefer root namespace filters over non-root namespace filters.
  2210  		if ifilter.Namespace != jfilter.Namespace &&
  2211  			(ifilter.Namespace == ps.Mesh.RootNamespace || jfilter.Namespace == ps.Mesh.RootNamespace) {
  2212  			return ifilter.Namespace == ps.Mesh.RootNamespace
  2213  		}
  2214  		if ifilter.creationTime != jfilter.creationTime {
  2215  			return ifilter.creationTime.Before(jfilter.creationTime)
  2216  		}
  2217  		in := ifilter.Name + "." + ifilter.Namespace
  2218  		jn := jfilter.Name + "." + jfilter.Namespace
  2219  		return in < jn
  2220  	})
  2221  	var out *EnvoyFilterWrapper
  2222  	if len(matchedEnvoyFilters) > 0 {
  2223  		out = &EnvoyFilterWrapper{
  2224  			// no need populate workloadSelector, as it is not used later.
  2225  			Patches: make(map[networking.EnvoyFilter_ApplyTo][]*EnvoyFilterConfigPatchWrapper),
  2226  		}
  2227  		// merge EnvoyFilterWrapper
  2228  		for _, efw := range matchedEnvoyFilters {
  2229  			for applyTo, cps := range efw.Patches {
  2230  				for _, cp := range cps {
  2231  					if proxyMatch(proxy, cp) {
  2232  						out.Patches[applyTo] = append(out.Patches[applyTo], cp)
  2233  					}
  2234  				}
  2235  			}
  2236  		}
  2237  	}
  2238  
  2239  	return out
  2240  }
  2241  
  2242  // if there is no workload selector, the config applies to all workloads
  2243  // if there is a workload selector, check for matching workload labels
  2244  func (ps *PushContext) getMatchedEnvoyFilters(proxy *Proxy, namespaces string) []*EnvoyFilterWrapper {
  2245  	matchedEnvoyFilters := make([]*EnvoyFilterWrapper, 0)
  2246  	for _, efw := range ps.envoyFiltersByNamespace[namespaces] {
  2247  		if efw.workloadSelector == nil || efw.workloadSelector.SubsetOf(proxy.Labels) {
  2248  			matchedEnvoyFilters = append(matchedEnvoyFilters, efw)
  2249  		}
  2250  	}
  2251  	return matchedEnvoyFilters
  2252  }
  2253  
  2254  // HasEnvoyFilters checks if an EnvoyFilter exists with the given name at the given namespace.
  2255  func (ps *PushContext) HasEnvoyFilters(name, namespace string) bool {
  2256  	for _, efw := range ps.envoyFiltersByNamespace[namespace] {
  2257  		if efw.Name == name {
  2258  			return true
  2259  		}
  2260  	}
  2261  	return false
  2262  }
  2263  
  2264  // pre computes gateways per namespace
  2265  func (ps *PushContext) initGateways(env *Environment) {
  2266  	gatewayConfigs := env.List(gvk.Gateway, NamespaceAll)
  2267  
  2268  	sortConfigByCreationTime(gatewayConfigs)
  2269  
  2270  	if features.ScopeGatewayToNamespace {
  2271  		ps.gatewayIndex.namespace = make(map[string][]config.Config)
  2272  		for _, gatewayConfig := range gatewayConfigs {
  2273  			if _, exists := ps.gatewayIndex.namespace[gatewayConfig.Namespace]; !exists {
  2274  				ps.gatewayIndex.namespace[gatewayConfig.Namespace] = make([]config.Config, 0)
  2275  			}
  2276  			ps.gatewayIndex.namespace[gatewayConfig.Namespace] = append(ps.gatewayIndex.namespace[gatewayConfig.Namespace], gatewayConfig)
  2277  		}
  2278  	} else {
  2279  		ps.gatewayIndex.all = gatewayConfigs
  2280  	}
  2281  }
  2282  
  2283  func (ps *PushContext) initAmbient(env *Environment) {
  2284  	ps.ambientIndex = env
  2285  }
  2286  
  2287  // InternalGatewayServiceAnnotation represents the hostname of the service a gateway will use. This is
  2288  // only used internally to transfer information from the Kubernetes Gateway API to the Istio Gateway API
  2289  // which does not have a field to represent this.
  2290  // The format is a comma separated list of hostnames. For example, "ingress.istio-system.svc.cluster.local,ingress.example.com"
  2291  // The Gateway will apply to all ServiceInstances of these services, *in the same namespace as the Gateway*.
  2292  const InternalGatewayServiceAnnotation = "internal.istio.io/gateway-service"
  2293  
  2294  type gatewayWithInstances struct {
  2295  	gateway config.Config
  2296  	// If true, ports that are not present in any instance will be used directly (without targetPort translation)
  2297  	// This supports the legacy behavior of selecting gateways by pod label selector
  2298  	legacyGatewaySelector bool
  2299  	instances             []ServiceTarget
  2300  }
  2301  
  2302  func (ps *PushContext) mergeGateways(proxy *Proxy) *MergedGateway {
  2303  	// this should never happen
  2304  	if proxy == nil {
  2305  		return nil
  2306  	}
  2307  	gatewayInstances := make([]gatewayWithInstances, 0)
  2308  
  2309  	var configs []config.Config
  2310  	if features.ScopeGatewayToNamespace {
  2311  		configs = ps.gatewayIndex.namespace[proxy.ConfigNamespace]
  2312  	} else {
  2313  		configs = ps.gatewayIndex.all
  2314  	}
  2315  
  2316  	for _, cfg := range configs {
  2317  		gw := cfg.Spec.(*networking.Gateway)
  2318  		if gwsvcstr, f := cfg.Annotations[InternalGatewayServiceAnnotation]; f {
  2319  			gwsvcs := strings.Split(gwsvcstr, ",")
  2320  			known := sets.New[string](gwsvcs...)
  2321  			matchingInstances := make([]ServiceTarget, 0, len(proxy.ServiceTargets))
  2322  			for _, si := range proxy.ServiceTargets {
  2323  				if _, f := known[string(si.Service.Hostname)]; f && si.Service.Attributes.Namespace == cfg.Namespace {
  2324  					matchingInstances = append(matchingInstances, si)
  2325  				}
  2326  			}
  2327  			// Only if we have a matching instance should we apply the configuration
  2328  			if len(matchingInstances) > 0 {
  2329  				gatewayInstances = append(gatewayInstances, gatewayWithInstances{cfg, false, matchingInstances})
  2330  			}
  2331  		} else if gw.GetSelector() == nil {
  2332  			// no selector. Applies to all workloads asking for the gateway
  2333  			gatewayInstances = append(gatewayInstances, gatewayWithInstances{cfg, true, proxy.ServiceTargets})
  2334  		} else {
  2335  			gatewaySelector := labels.Instance(gw.GetSelector())
  2336  			if gatewaySelector.SubsetOf(proxy.Labels) {
  2337  				gatewayInstances = append(gatewayInstances, gatewayWithInstances{cfg, true, proxy.ServiceTargets})
  2338  			}
  2339  		}
  2340  	}
  2341  
  2342  	if len(gatewayInstances) == 0 {
  2343  		return nil
  2344  	}
  2345  
  2346  	return MergeGateways(gatewayInstances, proxy, ps)
  2347  }
  2348  
  2349  func (ps *PushContext) NetworkManager() *NetworkManager {
  2350  	return ps.networkMgr
  2351  }
  2352  
  2353  // BestEffortInferServiceMTLSMode infers the mTLS mode for the service + port from all authentication
  2354  // policies (both alpha and beta) in the system. The function always returns MTLSUnknown for external service.
  2355  // The result is a best effort. It is because the PeerAuthentication is workload-based, this function is unable
  2356  // to compute the correct service mTLS mode without knowing service to workload binding. For now, this
  2357  // function uses only mesh and namespace level PeerAuthentication and ignore workload & port level policies.
  2358  // This function is used to give a hint for auto-mTLS configuration on client side.
  2359  func (ps *PushContext) BestEffortInferServiceMTLSMode(tp *networking.TrafficPolicy, service *Service, port *Port) MutualTLSMode {
  2360  	if service.MeshExternal {
  2361  		// Only need the authentication mTLS mode when service is not external.
  2362  		return MTLSUnknown
  2363  	}
  2364  
  2365  	// For passthrough traffic (headless service or explicitly defined in DestinationRule), we look at the instances
  2366  	// If ALL instances have a sidecar, we enable TLS, otherwise we disable
  2367  	// TODO(https://github.com/istio/istio/issues/27376) enable mixed deployments
  2368  	// A service with passthrough resolution is always passthrough, regardless of the TrafficPolicy.
  2369  	if service.Resolution == Passthrough || tp.GetLoadBalancer().GetSimple() == networking.LoadBalancerSettings_PASSTHROUGH {
  2370  		instances := ps.ServiceEndpointsByPort(service, port.Port, nil)
  2371  		if len(instances) == 0 {
  2372  			return MTLSDisable
  2373  		}
  2374  		for _, i := range instances {
  2375  			// Infer mTls disabled if any of the endpoint is with tls disabled
  2376  			if i.TLSMode == DisabledTLSModeLabel {
  2377  				return MTLSDisable
  2378  			}
  2379  		}
  2380  	}
  2381  
  2382  	// 2. check mTLS settings from beta policy (i.e PeerAuthentication) at namespace / mesh level.
  2383  	// If the mode is not unknown, use it.
  2384  	if serviceMTLSMode := ps.AuthnPolicies.GetNamespaceMutualTLSMode(service.Attributes.Namespace); serviceMTLSMode != MTLSUnknown {
  2385  		return serviceMTLSMode
  2386  	}
  2387  
  2388  	// Fallback to permissive.
  2389  	return MTLSPermissive
  2390  }
  2391  
  2392  // ServiceEndpointsByPort returns the cached instances by port if it exists.
  2393  func (ps *PushContext) ServiceEndpointsByPort(svc *Service, port int, labels labels.Instance) []*IstioEndpoint {
  2394  	var out []*IstioEndpoint
  2395  	if instances, exists := ps.ServiceIndex.instancesByPort[svc.Key()][port]; exists {
  2396  		// Use cached version of instances by port when labels are empty.
  2397  		if len(labels) == 0 {
  2398  			return instances
  2399  		}
  2400  		// If there are labels,	we will filter instances by pod labels.
  2401  		for _, instance := range instances {
  2402  			// check that one of the input labels is a subset of the labels
  2403  			if labels.SubsetOf(instance.Labels) {
  2404  				out = append(out, instance)
  2405  			}
  2406  		}
  2407  	}
  2408  
  2409  	return out
  2410  }
  2411  
  2412  // ServiceEndpoints returns the cached instances by svc if exists.
  2413  func (ps *PushContext) ServiceEndpoints(svcKey string) map[int][]*IstioEndpoint {
  2414  	if instances, exists := ps.ServiceIndex.instancesByPort[svcKey]; exists {
  2415  		return instances
  2416  	}
  2417  
  2418  	return nil
  2419  }
  2420  
  2421  // initKubernetesGateways initializes Kubernetes gateway-api objects
  2422  func (ps *PushContext) initKubernetesGateways(env *Environment) error {
  2423  	if env.GatewayAPIController != nil {
  2424  		ps.GatewayAPIController = env.GatewayAPIController
  2425  		return env.GatewayAPIController.Reconcile(ps)
  2426  	}
  2427  	return nil
  2428  }
  2429  
  2430  // ReferenceAllowed determines if a given resource (of type `kind` and name `resourceName`) can be
  2431  // accessed by `namespace`, based of specific reference policies.
  2432  // Note: this function only determines if a reference is *explicitly* allowed; the reference may not require
  2433  // explicit authorization to be made at all in most cases. Today, this only is for allowing cross-namespace
  2434  // secret access.
  2435  func (ps *PushContext) ReferenceAllowed(kind config.GroupVersionKind, resourceName string, namespace string) bool {
  2436  	// Currently, only Secret has reference policy, and only implemented by Gateway API controller.
  2437  	switch kind {
  2438  	case gvk.Secret:
  2439  		if ps.GatewayAPIController != nil {
  2440  			return ps.GatewayAPIController.SecretAllowed(resourceName, namespace)
  2441  		}
  2442  	default:
  2443  	}
  2444  	return false
  2445  }
  2446  
  2447  func (ps *PushContext) ServiceAccounts(hostname host.Name, namespace string) []string {
  2448  	return ps.serviceAccounts[serviceAccountKey{
  2449  		hostname:  hostname,
  2450  		namespace: namespace,
  2451  	}]
  2452  }
  2453  
  2454  // SupportsTunnel checks if a given IP address supports tunneling.
  2455  // This currently only accepts workload IPs as arguments; services will always return "false".
  2456  func (ps *PushContext) SupportsTunnel(n network.ID, ip string) bool {
  2457  	// There should be a 1:1 relationship between IP and Workload but the interface doesn't allow this lookup.
  2458  	// We should get 0 or 1 workloads, so just return the first.
  2459  	infos, _ := ps.ambientIndex.AddressInformation(sets.New(n.String() + "/" + ip))
  2460  	for _, wl := range ExtractWorkloadsFromAddresses(infos) {
  2461  		if wl.TunnelProtocol == workloadapi.TunnelProtocol_HBONE {
  2462  			return true
  2463  		}
  2464  	}
  2465  	return false
  2466  }
  2467  
  2468  // WorkloadsForWaypoint returns all workloads associated with a given waypoint identified by it's WaypointKey
  2469  // Used when calculating the workloads which should be configured for a specific waypoint proxy
  2470  func (ps *PushContext) WorkloadsForWaypoint(key WaypointKey) []WorkloadInfo {
  2471  	return ps.ambientIndex.WorkloadsForWaypoint(key)
  2472  }
  2473  
  2474  // ServicesForWaypoint returns all services associated with a given waypoint identified by it's WaypointKey
  2475  // Used when calculating the services which should be configured for a specific waypoint proxy
  2476  func (ps *PushContext) ServicesForWaypoint(key WaypointKey) []ServiceInfo {
  2477  	return ps.ambientIndex.ServicesForWaypoint(key)
  2478  }