istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/serviceregistry/serviceentry/store.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 serviceentry
    16  
    17  import (
    18  	"k8s.io/apimachinery/pkg/types"
    19  
    20  	"istio.io/istio/pilot/pkg/model"
    21  	"istio.io/istio/pkg/util/sets"
    22  )
    23  
    24  type hostPort struct {
    25  	host instancesKey
    26  	port int
    27  }
    28  
    29  // stores all the service instances from SE, WLE and pods
    30  type serviceInstancesStore struct {
    31  	ip2instance map[string][]*model.ServiceInstance
    32  	// service instances by hostname -> config
    33  	instances map[instancesKey]map[configKey][]*model.ServiceInstance
    34  	// instances only for serviceentry
    35  	instancesBySE map[types.NamespacedName]map[configKey][]*model.ServiceInstance
    36  	// instancesByHostAndPort tells whether the host has instances.
    37  	// This is used to validate that we only have one instance for DNS_ROUNDROBIN_LB.
    38  	instancesByHostAndPort sets.Set[hostPort]
    39  }
    40  
    41  func (s *serviceInstancesStore) getByIP(ip string) []*model.ServiceInstance {
    42  	return s.ip2instance[ip]
    43  }
    44  
    45  func (s *serviceInstancesStore) getAll() []*model.ServiceInstance {
    46  	all := []*model.ServiceInstance{}
    47  	for _, instances := range s.ip2instance {
    48  		all = append(all, instances...)
    49  	}
    50  	return all
    51  }
    52  
    53  func (s *serviceInstancesStore) getByKey(key instancesKey) []*model.ServiceInstance {
    54  	all := []*model.ServiceInstance{}
    55  	for _, instances := range s.instances[key] {
    56  		all = append(all, instances...)
    57  	}
    58  	return all
    59  }
    60  
    61  // deleteInstanceKeys deletes all instances with the given configKey and instanceKey
    62  // Note: as a convenience, this takes a []ServiceInstance instead of []instanceKey, as most callers have this format
    63  // However, this function only operates on the instance keys
    64  func (s *serviceInstancesStore) deleteInstanceKeys(key configKey, instances []*model.ServiceInstance) {
    65  	for _, i := range instances {
    66  		ikey := makeInstanceKey(i)
    67  		s.instancesByHostAndPort.Delete(hostPort{ikey, i.ServicePort.Port})
    68  		oldInstances := s.instances[ikey][key]
    69  		delete(s.instances[ikey], key)
    70  		if len(s.instances[ikey]) == 0 {
    71  			delete(s.instances, ikey)
    72  		}
    73  		delete(s.ip2instance, i.Endpoint.Address)
    74  		// Cleanup stale IPs, if the IPs changed
    75  		for _, oi := range oldInstances {
    76  			s.instancesByHostAndPort.Delete(hostPort{ikey, oi.ServicePort.Port})
    77  			delete(s.ip2instance, oi.Endpoint.Address)
    78  		}
    79  	}
    80  }
    81  
    82  // addInstances add the instances to the store.
    83  func (s *serviceInstancesStore) addInstances(key configKey, instances []*model.ServiceInstance) {
    84  	for _, instance := range instances {
    85  		ikey := makeInstanceKey(instance)
    86  		hostPort := hostPort{ikey, instance.ServicePort.Port}
    87  		// For DNSRoundRobinLB resolution type, check if service instances already exist and do not add
    88  		// if it already exist. This can happen if two Service Entries are created with same host name,
    89  		// resolution as DNS_ROUND_ROBIN and with same/different endpoints.
    90  		if instance.Service.Resolution == model.DNSRoundRobinLB &&
    91  			s.instancesByHostAndPort.Contains(hostPort) {
    92  			log.Debugf("skipping service %s from service entry %s with DnsRoundRobinLB. A service entry with the same host "+
    93  				"already exists. Only one locality lb end point is allowed for DnsRoundRobinLB services.",
    94  				ikey.hostname, key.name+"/"+key.namespace)
    95  			continue
    96  		}
    97  		if _, f := s.instances[ikey]; !f {
    98  			s.instances[ikey] = map[configKey][]*model.ServiceInstance{}
    99  		}
   100  		s.instancesByHostAndPort.Insert(hostPort)
   101  		s.instances[ikey][key] = append(s.instances[ikey][key], instance)
   102  		if instance.Endpoint.Address != "" {
   103  			s.ip2instance[instance.Endpoint.Address] = append(s.ip2instance[instance.Endpoint.Address], instance)
   104  		}
   105  	}
   106  }
   107  
   108  func (s *serviceInstancesStore) updateInstances(key configKey, instances []*model.ServiceInstance) {
   109  	// first delete
   110  	s.deleteInstanceKeys(key, instances)
   111  
   112  	// second add
   113  	s.addInstances(key, instances)
   114  }
   115  
   116  func (s *serviceInstancesStore) getServiceEntryInstances(key types.NamespacedName) map[configKey][]*model.ServiceInstance {
   117  	return s.instancesBySE[key]
   118  }
   119  
   120  func (s *serviceInstancesStore) updateServiceEntryInstances(key types.NamespacedName, instances map[configKey][]*model.ServiceInstance) {
   121  	s.instancesBySE[key] = instances
   122  }
   123  
   124  func (s *serviceInstancesStore) updateServiceEntryInstancesPerConfig(key types.NamespacedName, cKey configKey, instances []*model.ServiceInstance) {
   125  	if s.instancesBySE[key] == nil {
   126  		s.instancesBySE[key] = map[configKey][]*model.ServiceInstance{}
   127  	}
   128  	s.instancesBySE[key][cKey] = instances
   129  }
   130  
   131  func (s *serviceInstancesStore) deleteServiceEntryInstances(key types.NamespacedName, cKey configKey) {
   132  	delete(s.instancesBySE[key], cKey)
   133  	if len(s.instancesBySE[key]) == 0 {
   134  		delete(s.instancesBySE, key)
   135  	}
   136  }
   137  
   138  func (s *serviceInstancesStore) deleteAllServiceEntryInstances(key types.NamespacedName) {
   139  	delete(s.instancesBySE, key)
   140  }
   141  
   142  // stores all the services converted from serviceEntries
   143  type serviceStore struct {
   144  	// services keeps track of all services - mainly used to return from Services() to avoid reconversion.
   145  	servicesBySE   map[types.NamespacedName][]*model.Service
   146  	allocateNeeded bool
   147  }
   148  
   149  // getAllServices return all the services.
   150  func (s *serviceStore) getAllServices() []*model.Service {
   151  	var out []*model.Service
   152  	for _, svcs := range s.servicesBySE {
   153  		out = append(out, svcs...)
   154  	}
   155  	return model.SortServicesByCreationTime(out)
   156  }
   157  
   158  func (s *serviceStore) getServices(key types.NamespacedName) []*model.Service {
   159  	return s.servicesBySE[key]
   160  }
   161  
   162  func (s *serviceStore) deleteServices(key types.NamespacedName) {
   163  	delete(s.servicesBySE, key)
   164  }
   165  
   166  func (s *serviceStore) updateServices(key types.NamespacedName, services []*model.Service) {
   167  	s.servicesBySE[key] = services
   168  	s.allocateNeeded = true
   169  }