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 }