pilot/pkg/model/service.go (468 lines of code) (raw):

// Copyright Istio Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // This file describes the abstract model of services (and their instances) as // represented in Istio. This model is independent of the underlying platform // (Kubernetes, Mesos, etc.). Platform specific adapters found populate the // model object with various fields, from the metadata found in the platform. // The platform independent proxy code uses the representation in the model to // generate the configuration files for the Layer 7 proxy sidecar. The proxy // code is specific to individual proxy implementations package model import ( "fmt" "strconv" "strings" "time" ) import ( endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" "github.com/mitchellh/copystructure" "istio.io/api/label" ) import ( "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" "github.com/apache/dubbo-go-pixiu/pkg/cluster" "github.com/apache/dubbo-go-pixiu/pkg/config/constants" "github.com/apache/dubbo-go-pixiu/pkg/config/host" "github.com/apache/dubbo-go-pixiu/pkg/config/labels" "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" "github.com/apache/dubbo-go-pixiu/pkg/config/visibility" "github.com/apache/dubbo-go-pixiu/pkg/network" "github.com/apache/dubbo-go-pixiu/pkg/util/sets" ) // Service describes an Istio service (e.g., catalog.mystore.com:8080) // Each service has a fully qualified domain name (FQDN) and one or more // ports where the service is listening for connections. *Optionally*, a // service can have a single load balancer/virtual IP address associated // with it, such that the DNS queries for the FQDN resolves to the virtual // IP address (a load balancer IP). // // E.g., in kubernetes, a service foo is associated with // foo.default.svc.cluster.local hostname, has a virtual IP of 10.0.1.1 and // listens on ports 80, 8080 type Service struct { // Attributes contains additional attributes associated with the service // used mostly by RBAC for policy enforcement purposes. Attributes ServiceAttributes // Ports is the set of network ports where the service is listening for // connections Ports PortList `json:"ports,omitempty"` // ServiceAccounts specifies the service accounts that run the service. ServiceAccounts []string `json:"serviceAccounts,omitempty"` // CreationTime records the time this service was created, if available. CreationTime time.Time `json:"creationTime,omitempty"` // Name of the service, e.g. "catalog.mystore.com" Hostname host.Name `json:"hostname"` // ClusterVIPs specifies the service address of the load balancer // in each of the clusters where the service resides ClusterVIPs AddressMap `json:"clusterVIPs,omitempty"` // DefaultAddress specifies the default service IP of the load balancer. // Do not access directly. Use GetAddressForProxy DefaultAddress string `json:"defaultAddress,omitempty"` // AutoAllocatedIPv4Address and AutoAllocatedIPv6Address specifies // the automatically allocated IPv4/IPv6 address out of the reserved // Class E subnet (240.240.0.0/16) or reserved Benchmarking IP range // (2001:2::/48) in RFC5180.for service entries with non-wildcard // hostnames. The IPs assigned to services are not // synchronized across istiod replicas as the DNS resolution // for these service entries happens completely inside a pod // whose proxy is managed by one istiod. That said, the algorithm // to allocate IPs is pretty deterministic that at stable state, two // istiods will allocate the exact same set of IPs for a given set of // service entries. AutoAllocatedIPv4Address string `json:"autoAllocatedIPv4Address,omitempty"` AutoAllocatedIPv6Address string `json:"autoAllocatedIPv6Address,omitempty"` // Resolution indicates how the service instances need to be resolved before routing // traffic. Most services in the service registry will use static load balancing wherein // the proxy will decide the service instance that will receive the traffic. Service entries // could either use DNS load balancing (i.e. proxy will query DNS server for the IP of the service) // or use the passthrough model (i.e. proxy will forward the traffic to the network endpoint requested // by the caller) Resolution Resolution // MeshExternal (if true) indicates that the service is external to the mesh. // These services are defined using Istio's ServiceEntry spec. MeshExternal bool // ResourceVersion represents the internal version of this object. ResourceVersion string } func (s *Service) Key() string { if s == nil { return "" } return s.Attributes.Namespace + "/" + string(s.Hostname) } // Resolution indicates how the service instances need to be resolved before routing traffic. type Resolution int const ( // ClientSideLB implies that the proxy will decide the endpoint from its local lb pool ClientSideLB Resolution = iota // DNSLB implies that the proxy will resolve a DNS address and forward to the resolved address DNSLB // Passthrough implies that the proxy should forward traffic to the destination IP requested by the caller Passthrough // DNSRoundRobinLB implies that the proxy will resolve a DNS address and forward to the resolved address DNSRoundRobinLB ) // String converts Resolution in to String. func (resolution Resolution) String() string { switch resolution { case ClientSideLB: return "ClientSide" case DNSLB: return "DNS" case DNSRoundRobinLB: return "DNSRoundRobin" case Passthrough: return "Passthrough" default: return fmt.Sprintf("%d", int(resolution)) } } const ( // IstioDefaultConfigNamespace constant for default namespace IstioDefaultConfigNamespace = "default" // LocalityLabel indicates the region/zone/subzone of an instance. It is used to override the native // registry's value. // // Note: because k8s labels does not support `/`, so we use `.` instead in k8s. LocalityLabel = "istio-locality" // k8s istio-locality label separator k8sSeparator = "." ) const ( // TLSModeLabelShortname name used for determining endpoint level tls transport socket configuration TLSModeLabelShortname = "tlsMode" // DisabledTLSModeLabel implies that this endpoint should receive traffic as is (mostly plaintext) DisabledTLSModeLabel = "disabled" // IstioMutualTLSModeLabel implies that the endpoint is ready to receive Istio mTLS connections. IstioMutualTLSModeLabel = "istio" // IstioCanonicalServiceLabelName is the name of label for the Istio Canonical Service for a workload instance. IstioCanonicalServiceLabelName = "service.istio.io/canonical-name" // IstioCanonicalServiceRevisionLabelName is the name of label for the Istio Canonical Service revision for a workload instance. IstioCanonicalServiceRevisionLabelName = "service.istio.io/canonical-revision" ) // Port represents a network port where a service is listening for // connections. The port should be annotated with the type of protocol // used by the port. type Port struct { // Name ascribes a human readable name for the port object. When a // service has multiple ports, the name field is mandatory Name string `json:"name,omitempty"` // Port number where the service can be reached. Does not necessarily // map to the corresponding port numbers for the instances behind the // service. Port int `json:"port"` // Protocol to be used for the port. Protocol protocol.Instance `json:"protocol,omitempty"` } // PortList is a set of ports type PortList []*Port // TrafficDirection defines whether traffic exists a service instance or enters a service instance type TrafficDirection string const ( // TrafficDirectionInbound indicates inbound traffic TrafficDirectionInbound TrafficDirection = "inbound" // TrafficDirectionOutbound indicates outbound traffic TrafficDirectionOutbound TrafficDirection = "outbound" // trafficDirectionOutboundSrvPrefix the prefix for a DNS SRV type subset key trafficDirectionOutboundSrvPrefix = string(TrafficDirectionOutbound) + "_" // trafficDirectionInboundSrvPrefix the prefix for a DNS SRV type subset key trafficDirectionInboundSrvPrefix = string(TrafficDirectionInbound) + "_" ) // ServiceInstance represents an individual instance of a specific version // of a service. It binds a network endpoint (ip:port), the service // description (which is oblivious to various versions) and a set of labels // that describe the service version associated with this instance. // // Since a ServiceInstance has a single IstioEndpoint, which has a single port, // multiple ServiceInstances are required to represent a workload that listens // on multiple ports. // // The labels associated with a service instance are unique per a network endpoint. // There is one well defined set of labels for each service instance network endpoint. // // For example, the set of service instances associated with catalog.mystore.com // are modeled like this // // --> IstioEndpoint(172.16.0.1:8888), Service(catalog.myservice.com), Labels(foo=bar) // --> IstioEndpoint(172.16.0.2:8888), Service(catalog.myservice.com), Labels(foo=bar) // --> IstioEndpoint(172.16.0.3:8888), Service(catalog.myservice.com), Labels(kitty=cat) // --> IstioEndpoint(172.16.0.4:8888), Service(catalog.myservice.com), Labels(kitty=cat) type ServiceInstance struct { Service *Service `json:"service,omitempty"` ServicePort *Port `json:"servicePort,omitempty"` Endpoint *IstioEndpoint `json:"endpoint,omitempty"` } // DeepCopy creates a copy of ServiceInstance. func (instance *ServiceInstance) DeepCopy() *ServiceInstance { return &ServiceInstance{ Service: instance.Service.DeepCopy(), Endpoint: instance.Endpoint.DeepCopy(), ServicePort: &Port{ Name: instance.ServicePort.Name, Port: instance.ServicePort.Port, Protocol: instance.ServicePort.Protocol, }, } } type workloadKind int const ( // PodKind indicates the workload is from pod PodKind workloadKind = iota // WorkloadEntryKind indicates the workload is from workloadentry WorkloadEntryKind ) func (k workloadKind) String() string { if k == PodKind { return "Pod" } if k == WorkloadEntryKind { return "WorkloadEntry" } return "" } type WorkloadInstance struct { Name string `json:"name,omitempty"` Namespace string `json:"namespace,omitempty"` // Where the workloadInstance come from, valid values are`Pod` or `WorkloadEntry` Kind workloadKind `json:"kind"` Endpoint *IstioEndpoint `json:"endpoint,omitempty"` PortMap map[string]uint32 `json:"portMap,omitempty"` // Can only be selected by service entry of DNS type. DNSServiceEntryOnly bool `json:"dnsServiceEntryOnly,omitempty"` } // DeepCopy creates a copy of WorkloadInstance. func (instance *WorkloadInstance) DeepCopy() *WorkloadInstance { pmap := map[string]uint32{} for k, v := range instance.PortMap { pmap[k] = v } return &WorkloadInstance{ Name: instance.Name, Namespace: instance.Namespace, Kind: instance.Kind, PortMap: pmap, Endpoint: instance.Endpoint.DeepCopy(), } } // WorkloadInstancesEqual is a custom comparison of workload instances based on the fields that we need // i.e. excluding the ports. Returns true if equal, false otherwise. func WorkloadInstancesEqual(first, second *WorkloadInstance) bool { if first.Endpoint == nil || second.Endpoint == nil { return first.Endpoint == second.Endpoint } if first.Endpoint.Address != second.Endpoint.Address { return false } if first.Endpoint.Network != second.Endpoint.Network { return false } if first.Endpoint.TLSMode != second.Endpoint.TLSMode { return false } if !first.Endpoint.Labels.Equals(second.Endpoint.Labels) { return false } if first.Endpoint.ServiceAccount != second.Endpoint.ServiceAccount { return false } if first.Endpoint.Locality != second.Endpoint.Locality { return false } if first.Endpoint.GetLoadBalancingWeight() != second.Endpoint.GetLoadBalancingWeight() { return false } if first.Namespace != second.Namespace { return false } if first.Name != second.Name { return false } if first.Kind != second.Kind { return false } if !portMapEquals(first.PortMap, second.PortMap) { return false } return true } func portMapEquals(a, b map[string]uint32) bool { if len(a) != len(b) { return false } for k, v := range a { if b[k] != v { return false } } return true } // GetLocalityLabelOrDefault returns the locality from the supplied label, or falls back to // the supplied default locality if the supplied label is empty. Because Kubernetes // labels don't support `/`, we replace "." with "/" in the supplied label as a workaround. func GetLocalityLabelOrDefault(label, defaultLabel string) string { if len(label) > 0 { // if there are /'s present we don't need to replace if strings.Contains(label, "/") { return label } // replace "." with "/" return strings.Replace(label, k8sSeparator, "/", -1) } return defaultLabel } // SplitLocalityLabel splits a locality label into region, zone and subzone strings. func SplitLocalityLabel(locality string) (region, zone, subzone string) { items := strings.Split(locality, "/") switch len(items) { case 1: return items[0], "", "" case 2: return items[0], items[1], "" default: return items[0], items[1], items[2] } } // Locality information for an IstioEndpoint type Locality struct { // Label for locality on the endpoint. This is a "/" separated string. Label string // ClusterID where the endpoint is located ClusterID cluster.ID } // Endpoint health status. type HealthStatus int32 const ( // Healthy. Healthy HealthStatus = 0 // Unhealthy. UnHealthy HealthStatus = 1 ) // IstioEndpoint defines a network address (IP:port) associated with an instance of the // service. A service has one or more instances each running in a // container/VM/pod. If a service has multiple ports, then the same // instance IP is expected to be listening on multiple ports (one per each // service port). Note that the port associated with an instance does not // have to be the same as the port associated with the service. Depending // on the network setup (NAT, overlays), this could vary. // // For e.g., if catalog.mystore.com is accessible through port 80 and 8080, // and it maps to an instance with IP 172.16.0.1, such that connections to // port 80 are forwarded to port 55446, and connections to port 8080 are // forwarded to port 33333, // // then internally, we have two endpoint structs for the // service catalog.mystore.com // // --> 172.16.0.1:55446 (with ServicePort pointing to 80) and // --> 172.16.0.1:33333 (with ServicePort pointing to 8080) // // TODO: Investigate removing ServiceInstance entirely. type IstioEndpoint struct { // Labels points to the workload or deployment labels. Labels labels.Instance // Address is the address of the endpoint, using envoy proto. Address string // ServicePortName tracks the name of the port, this is used to select the IstioEndpoint by service port. ServicePortName string // EnvoyEndpoint is a cached LbEndpoint, converted from the data, to // avoid recomputation EnvoyEndpoint *endpoint.LbEndpoint // ServiceAccount holds the associated service account. ServiceAccount string // Network holds the network where this endpoint is present Network network.ID // The locality where the endpoint is present. Locality Locality // EndpointPort is the port where the workload is listening, can be different // from the service port. EndpointPort uint32 // The load balancing weight associated with this endpoint. LbWeight uint32 // TLSMode endpoint is injected with istio sidecar and ready to configure Istio mTLS TLSMode string // Namespace that this endpoint belongs to. This is for telemetry purpose. Namespace string // Name of the workload that this endpoint belongs to. This is for telemetry purpose. WorkloadName string // Specifies the hostname of the Pod, empty for vm workload. HostName string // If specified, the fully qualified Pod hostname will be "<hostname>.<subdomain>.<pod namespace>.svc.<cluster domain>". SubDomain string // The ingress tunnel supportability of this endpoint. // If this endpoint sidecar proxy does not support h2 tunnel, this endpoint will not show up in the EDS clusters // which are generated for h2 tunnel. TunnelAbility networking.TunnelAbility // Determines the discoverability of this endpoint throughout the mesh. DiscoverabilityPolicy EndpointDiscoverabilityPolicy `json:"-"` // Indicatesthe endpoint health status. HealthStatus HealthStatus } // GetLoadBalancingWeight returns the weight for this endpoint, normalized to always be > 0. func (ep *IstioEndpoint) GetLoadBalancingWeight() uint32 { if ep.LbWeight > 0 { return ep.LbWeight } return 1 } // IsDiscoverableFromProxy indicates whether this endpoint is discoverable from the given Proxy. func (ep *IstioEndpoint) IsDiscoverableFromProxy(p *Proxy) bool { if ep == nil || ep.DiscoverabilityPolicy == nil { // If no policy was assigned, default to discoverable mesh-wide. // TODO(nmittler): Will need to re-think this default when cluster.local is actually cluster-local. return true } return ep.DiscoverabilityPolicy.IsDiscoverableFromProxy(ep, p) } // EndpointDiscoverabilityPolicy determines the discoverability of an endpoint throughout the mesh. type EndpointDiscoverabilityPolicy interface { // IsDiscoverableFromProxy indicates whether an endpoint is discoverable from the given Proxy. IsDiscoverableFromProxy(*IstioEndpoint, *Proxy) bool // String returns name of this policy. String() string } type endpointDiscoverabilityPolicyImpl struct { name string f func(*IstioEndpoint, *Proxy) bool } func (p *endpointDiscoverabilityPolicyImpl) IsDiscoverableFromProxy(ep *IstioEndpoint, proxy *Proxy) bool { return p.f(ep, proxy) } func (p *endpointDiscoverabilityPolicyImpl) String() string { return p.name } // AlwaysDiscoverable is an EndpointDiscoverabilityPolicy that allows an endpoint to be discoverable throughout the mesh. var AlwaysDiscoverable EndpointDiscoverabilityPolicy = &endpointDiscoverabilityPolicyImpl{ name: "AlwaysDiscoverable", f: func(*IstioEndpoint, *Proxy) bool { return true }, } // DiscoverableFromSameCluster is an EndpointDiscoverabilityPolicy that only allows an endpoint to be discoverable // from proxies within the same cluster. var DiscoverableFromSameCluster EndpointDiscoverabilityPolicy = &endpointDiscoverabilityPolicyImpl{ name: "DiscoverableFromSameCluster", f: func(ep *IstioEndpoint, p *Proxy) bool { return p.InCluster(ep.Locality.ClusterID) }, } // ServiceAttributes represents a group of custom attributes of the service. type ServiceAttributes struct { // ServiceRegistry indicates the backing service registry system where this service // was sourced from. // TODO: move the ServiceRegistry type from platform.go to model ServiceRegistry provider.ID // Name is "destination.service.name" attribute Name string // Namespace is "destination.service.namespace" attribute Namespace string // Labels applied to the service Labels map[string]string // ExportTo defines the visibility of Service in // a namespace when the namespace is imported. ExportTo map[visibility.Instance]bool // LabelSelectors are the labels used by the service to select workloads. // Applicable to both Kubernetes and ServiceEntries. LabelSelectors map[string]string // For Kubernetes platform // ClusterExternalAddresses is a mapping between a cluster name and the external // address(es) to access the service from outside the cluster. // Used by the aggregator to aggregate the Attributes.ClusterExternalAddresses // for clusters where the service resides ClusterExternalAddresses AddressMap // ClusterExternalPorts is a mapping between a cluster name and the service port // to node port mappings for a given service. When accessing the service via // node port IPs, we need to use the kubernetes assigned node ports of the service // The port that the user provides in the meshNetworks config is the service port. // We translate that to the appropriate node port here. ClusterExternalPorts map[cluster.ID]map[uint32]uint32 } // DeepCopy creates a deep copy of ServiceAttributes, but skips internal mutexes. func (s *ServiceAttributes) DeepCopy() ServiceAttributes { // Nested mutexes are configured to be ignored by copystructure.Copy. // Disabling `go vet` warning since this is actually safe in this case. // nolint: vet return copyInternal(*s).(ServiceAttributes) } // ServiceDiscovery enumerates Istio service instances. // nolint: lll type ServiceDiscovery interface { NetworkGatewaysWatcher // Services list declarations of all services in the system Services() []*Service // GetService retrieves a service by host name if it exists GetService(hostname host.Name) *Service // InstancesByPort retrieves instances for a service on the given ports with labels that match // any of the supplied labels. All instances match an empty tag list. // // For example, consider an example of catalog.mystore.com: // Instances(catalog.myservice.com, 80) -> // --> IstioEndpoint(172.16.0.1:8888), Service(catalog.myservice.com), Labels(foo=bar) // --> IstioEndpoint(172.16.0.2:8888), Service(catalog.myservice.com), Labels(foo=bar) // --> IstioEndpoint(172.16.0.3:8888), Service(catalog.myservice.com), Labels(kitty=cat) // --> IstioEndpoint(172.16.0.4:8888), Service(catalog.myservice.com), Labels(kitty=cat) // // Calling Instances with specific labels returns a trimmed list. // e.g., Instances(catalog.myservice.com, 80, foo=bar) -> // --> IstioEndpoint(172.16.0.1:8888), Service(catalog.myservice.com), Labels(foo=bar) // --> IstioEndpoint(172.16.0.2:8888), Service(catalog.myservice.com), Labels(foo=bar) // // Similar concepts apply for calling this function with a specific // port, hostname and labels. // // Introduced in Istio 0.8. It is only called with 1 port. // CDS (clusters.go) calls it for building 'dnslb' type clusters. // EDS calls it for building the endpoints result. // Consult istio-dev before using this for anything else (except debugging/tools) InstancesByPort(svc *Service, servicePort int, labels labels.Instance) []*ServiceInstance // GetProxyServiceInstances returns the service instances that co-located with a given Proxy // // Co-located generally means running in the same network namespace and security context. // // A Proxy operating as a Sidecar will return a non-empty slice. A stand-alone Proxy // will return an empty slice. // // There are two reasons why this returns multiple ServiceInstances instead of one: // - A ServiceInstance has a single IstioEndpoint which has a single Port. But a Service // may have many ports. So a workload implementing such a Service would need // multiple ServiceInstances, one for each port. // - A single workload may implement multiple logical Services. // // In the second case, multiple services may be implemented by the same physical port number, // though with a different ServicePort and IstioEndpoint for each. If any of these overlapping // services are not HTTP or H2-based, behavior is undefined, since the listener may not be able to // determine the intended destination of a connection without a Host header on the request. GetProxyServiceInstances(*Proxy) []*ServiceInstance GetProxyWorkloadLabels(*Proxy) labels.Instance // GetIstioServiceAccounts returns a list of service accounts looked up from // the specified service hostname and ports. // Deprecated - service account tracking moved to XdsServer, incremental. GetIstioServiceAccounts(svc *Service, ports []int) []string // MCSServices returns information about the services that have been exported/imported via the // Kubernetes Multi-Cluster Services (MCS) ServiceExport API. Only applies to services in // Kubernetes clusters. MCSServices() []MCSServiceInfo } // MCSServiceInfo combines the name of a service with a particular Kubernetes cluster. This // is used for debug information regarding the state of Kubernetes Multi-Cluster Services (MCS). type MCSServiceInfo struct { Cluster cluster.ID Name string Namespace string Exported bool Imported bool ClusterSetVIP string Discoverability map[host.Name]string } // GetNames returns port names func (ports PortList) GetNames() []string { names := make([]string, 0, len(ports)) for _, port := range ports { names = append(names, port.Name) } return names } // Get retrieves a port declaration by name func (ports PortList) Get(name string) (*Port, bool) { for _, port := range ports { if port.Name == name { return port, true } } return nil, false } // GetByPort retrieves a port declaration by port value func (ports PortList) GetByPort(num int) (*Port, bool) { for _, port := range ports { if port.Port == num && port.Protocol != protocol.UDP { return port, true } } return nil, false } // External predicate checks whether the service is external func (s *Service) External() bool { return s.MeshExternal } // BuildSubsetKey generates a unique string referencing service instances for a given service name, a subset and a port. // The proxy queries Pilot with this key to obtain the list of instances in a subset. func BuildSubsetKey(direction TrafficDirection, subsetName string, hostname host.Name, port int) string { return string(direction) + "|" + strconv.Itoa(port) + "|" + subsetName + "|" + string(hostname) } // BuildInboundSubsetKey generates a unique string referencing service instances with port. func BuildInboundSubsetKey(port int) string { return BuildSubsetKey(TrafficDirectionInbound, "", "", port) } // BuildDNSSrvSubsetKey generates a unique string referencing service instances for a given service name, a subset and a port. // The proxy queries Pilot with this key to obtain the list of instances in a subset. // This is used only for the SNI-DNAT router. Do not use for other purposes. // The DNS Srv format of the cluster is also used as the default SNI string for Istio mTLS connections func BuildDNSSrvSubsetKey(direction TrafficDirection, subsetName string, hostname host.Name, port int) string { return string(direction) + "_." + strconv.Itoa(port) + "_." + subsetName + "_." + string(hostname) } // IsValidSubsetKey checks if a string is valid for subset key parsing. func IsValidSubsetKey(s string) bool { return strings.Count(s, "|") == 3 } // IsDNSSrvSubsetKey checks whether the given key is a DNSSrv key (built by BuildDNSSrvSubsetKey). func IsDNSSrvSubsetKey(s string) bool { if strings.HasPrefix(s, trafficDirectionOutboundSrvPrefix) || strings.HasPrefix(s, trafficDirectionInboundSrvPrefix) { return true } return false } // ParseSubsetKey is the inverse of the BuildSubsetKey method func ParseSubsetKey(s string) (direction TrafficDirection, subsetName string, hostname host.Name, port int) { var parts []string dnsSrvMode := false // This could be the DNS srv form of the cluster that uses outbound_.port_.subset_.hostname // Since we do not want every callsite to implement the logic to differentiate between the two forms // we add an alternate parser here. if strings.HasPrefix(s, trafficDirectionOutboundSrvPrefix) || strings.HasPrefix(s, trafficDirectionInboundSrvPrefix) { parts = strings.SplitN(s, ".", 4) dnsSrvMode = true } else { parts = strings.Split(s, "|") } if len(parts) < 4 { return } direction = TrafficDirection(strings.TrimSuffix(parts[0], "_")) port, _ = strconv.Atoi(strings.TrimSuffix(parts[1], "_")) subsetName = parts[2] if dnsSrvMode { subsetName = strings.TrimSuffix(parts[2], "_") } hostname = host.Name(parts[3]) return } // GetAddresses returns a Service's addresses. // This method returns all the VIPs of a service if the ClusterID is explicitly set to "", otherwise only return the VIP // specific to the cluster where the node resides func (s *Service) GetAddresses(node *Proxy) []string { if node.Metadata != nil && node.Metadata.ClusterID == "" { return s.getAllAddresses() } return []string{s.GetAddressForProxy(node)} } // GetAddressForProxy returns a Service's address specific to the cluster where the node resides func (s *Service) GetAddressForProxy(node *Proxy) string { if node.Metadata != nil { if node.Metadata.ClusterID != "" { addresses := s.ClusterVIPs.GetAddressesFor(node.Metadata.ClusterID) if len(addresses) > 0 { return addresses[0] } } if node.Metadata.DNSCapture && node.Metadata.DNSAutoAllocate && s.DefaultAddress == constants.UnspecifiedIP { if node.SupportsIPv4() && s.AutoAllocatedIPv4Address != "" { return s.AutoAllocatedIPv4Address } if node.SupportsIPv6() && s.AutoAllocatedIPv6Address != "" { return s.AutoAllocatedIPv6Address } } } return s.DefaultAddress } // getAllAddresses returns a Service's all addresses. func (s *Service) getAllAddresses() []string { var addresses []string addressMap := s.ClusterVIPs.GetAddresses() for _, clusterAddresses := range addressMap { addresses = append(addresses, clusterAddresses...) } return addresses } // GetTLSModeFromEndpointLabels returns the value of the label // security.istio.io/tlsMode if set. Do not return Enums or constants // from this function as users could provide values other than istio/disabled // and apply custom transport socket matchers here. func GetTLSModeFromEndpointLabels(labels map[string]string) string { if labels != nil { if val, exists := labels[label.SecurityTlsMode.Name]; exists { return val } } return DisabledTLSModeLabel } // GetServiceAccounts returns aggregated list of service accounts of Service plus its instances. func GetServiceAccounts(svc *Service, ports []int, discovery ServiceDiscovery) []string { sa := sets.Set{} instances := make([]*ServiceInstance, 0) // Get the service accounts running service within Kubernetes. This is reflected by the pods that // the service is deployed on, and the service accounts of the pods. for _, port := range ports { svcInstances := discovery.InstancesByPort(svc, port, nil) instances = append(instances, svcInstances...) } for _, si := range instances { if si.Endpoint.ServiceAccount != "" { sa.Insert(si.Endpoint.ServiceAccount) } } sa.InsertAll(svc.ServiceAccounts...) return sa.UnsortedList() } // DeepCopy creates a clone of Service. func (s *Service) DeepCopy() *Service { // nolint: govet out := *s out.Attributes = s.Attributes.DeepCopy() if s.Ports != nil { out.Ports = make(PortList, len(s.Ports)) for i, port := range s.Ports { if port != nil { out.Ports[i] = &Port{ Name: port.Name, Port: port.Port, Protocol: port.Protocol, } } else { out.Ports[i] = nil } } } if s.ServiceAccounts != nil { out.ServiceAccounts = make([]string, len(s.ServiceAccounts)) for i, sa := range s.ServiceAccounts { out.ServiceAccounts[i] = sa } } out.ClusterVIPs = s.ClusterVIPs.DeepCopy() return &out } // DeepCopy creates a clone of IstioEndpoint. func (ep *IstioEndpoint) DeepCopy() *IstioEndpoint { return copyInternal(ep).(*IstioEndpoint) } func copyInternal(v interface{}) interface{} { copied, err := copystructure.Copy(v) if err != nil { // There are 2 locations where errors are generated in copystructure.Copy: // * The reflection walk over the structure fails, which should never happen // * A configurable copy function returns an error. This is only used for copying times, which never returns an error. // Therefore, this should never happen panic(err) } return copied }