in pilot/pkg/networking/core/v1alpha3/httproute.go [264:444]
func BuildSidecarOutboundVirtualHosts(node *model.Proxy, push *model.PushContext,
routeName string,
listenerPort int,
efKeys []string,
xdsCache model.XdsCache,
) ([]*route.VirtualHost, *discovery.Resource, *istio_route.Cache) {
var virtualServices []config.Config
var services []*model.Service
// Get the services from the egress listener. When sniffing is enabled, we send
// route name as foo.bar.com:8080 which is going to match against the wildcard
// egress listener only. A route with sniffing would not have been generated if there
// was a sidecar with explicit port (and hence protocol declaration). A route with
// sniffing is generated only in the case of the catch all egress listener.
egressListener := node.SidecarScope.GetEgressListenerForRDS(listenerPort, routeName)
// We should never be getting a nil egress listener because the code that setup this RDS
// call obviously saw an egress listener
if egressListener == nil {
return nil, nil, nil
}
services = egressListener.Services()
// To maintain correctness, we should only use the virtualservices for
// this listener and not all virtual services accessible to this proxy.
virtualServices = egressListener.VirtualServices()
// When generating RDS for ports created via the SidecarScope, we treat ports as HTTP proxy style ports
// if ports protocol is HTTP_PROXY.
if egressListener.IstioListener != nil && egressListener.IstioListener.Port != nil &&
protocol.Parse(egressListener.IstioListener.Port.Protocol) == protocol.HTTP_PROXY {
listenerPort = 0
}
servicesByName := make(map[host.Name]*model.Service)
for _, svc := range services {
if listenerPort == 0 {
// Take all ports when listen port is 0 (http_proxy or uds)
// Expect virtualServices to resolve to right port
servicesByName[svc.Hostname] = svc
} else if svcPort, exists := svc.Ports.GetByPort(listenerPort); exists {
servicesByName[svc.Hostname] = &model.Service{
Hostname: svc.Hostname,
DefaultAddress: svc.GetAddressForProxy(node),
MeshExternal: svc.MeshExternal,
Resolution: svc.Resolution,
Ports: []*model.Port{svcPort},
Attributes: model.ServiceAttributes{
Namespace: svc.Attributes.Namespace,
ServiceRegistry: svc.Attributes.ServiceRegistry,
},
}
}
}
var routeCache *istio_route.Cache
if listenerPort > 0 {
services = make([]*model.Service, 0, len(servicesByName))
// sort services
for _, svc := range servicesByName {
services = append(services, svc)
}
sort.SliceStable(services, func(i, j int) bool {
return services[i].Hostname <= services[j].Hostname
})
routeCache = &istio_route.Cache{
RouteName: routeName,
ProxyVersion: node.Metadata.IstioVersion,
ClusterID: string(node.Metadata.ClusterID),
DNSDomain: node.DNSDomain,
DNSCapture: bool(node.Metadata.DNSCapture),
DNSAutoAllocate: bool(node.Metadata.DNSAutoAllocate),
AllowAny: util.IsAllowAnyOutbound(node),
ListenerPort: listenerPort,
Services: services,
VirtualServices: virtualServices,
DelegateVirtualServices: push.DelegateVirtualServicesConfigKey(virtualServices),
EnvoyFilterKeys: efKeys,
}
}
// This is hack to keep consistent with previous behavior.
if listenerPort != 80 {
// only select virtualServices that matches a service
virtualServices = selectVirtualServices(virtualServices, servicesByName)
}
// Get list of virtual services bound to the mesh gateway
virtualHostWrappers := istio_route.BuildSidecarVirtualHostWrapper(routeCache, node, push, servicesByName, virtualServices, listenerPort)
resource, exist := xdsCache.Get(routeCache)
if exist && !features.EnableUnsafeAssertions {
return nil, resource, routeCache
}
vHostPortMap := make(map[int][]*route.VirtualHost)
vhosts := sets.Set{}
vhdomains := sets.Set{}
knownFQDN := sets.Set{}
buildVirtualHost := func(hostname string, vhwrapper istio_route.VirtualHostWrapper, svc *model.Service) *route.VirtualHost {
name := util.DomainName(hostname, vhwrapper.Port)
if duplicateVirtualHost(name, vhosts) {
// This means this virtual host has caused duplicate virtual host name.
var msg string
if svc == nil {
msg = fmt.Sprintf("duplicate domain from virtual service: %s", name)
} else {
msg = fmt.Sprintf("duplicate domain from service: %s", name)
}
push.AddMetric(model.DuplicatedDomains, name, node.ID, msg)
return nil
}
var domains []string
var altHosts []string
if svc == nil {
domains = []string{util.IPv6Compliant(hostname), name}
} else {
domains, altHosts = generateVirtualHostDomains(svc, vhwrapper.Port, node)
}
dl := len(domains)
domains = dedupeDomains(domains, vhdomains, altHosts, knownFQDN)
if dl != len(domains) {
var msg string
if svc == nil {
msg = fmt.Sprintf("duplicate domain from virtual service: %s", name)
} else {
msg = fmt.Sprintf("duplicate domain from service: %s", name)
}
// This means this virtual host has caused duplicate virtual host domain.
push.AddMetric(model.DuplicatedDomains, name, node.ID, msg)
}
if len(domains) > 0 {
return &route.VirtualHost{
Name: name,
Domains: domains,
Routes: vhwrapper.Routes,
IncludeRequestAttemptCount: true,
}
}
return nil
}
for _, virtualHostWrapper := range virtualHostWrappers {
for _, svc := range virtualHostWrapper.Services {
name := util.DomainName(string(svc.Hostname), virtualHostWrapper.Port)
knownFQDN.InsertAll(name, string(svc.Hostname))
}
}
for _, virtualHostWrapper := range virtualHostWrappers {
// If none of the routes matched by source, skip this virtual host
if len(virtualHostWrapper.Routes) == 0 {
continue
}
virtualHosts := make([]*route.VirtualHost, 0, len(virtualHostWrapper.VirtualServiceHosts)+len(virtualHostWrapper.Services))
for _, hostname := range virtualHostWrapper.VirtualServiceHosts {
if vhost := buildVirtualHost(hostname, virtualHostWrapper, nil); vhost != nil {
virtualHosts = append(virtualHosts, vhost)
}
}
for _, svc := range virtualHostWrapper.Services {
if vhost := buildVirtualHost(string(svc.Hostname), virtualHostWrapper, svc); vhost != nil {
virtualHosts = append(virtualHosts, vhost)
}
}
vHostPortMap[virtualHostWrapper.Port] = append(vHostPortMap[virtualHostWrapper.Port], virtualHosts...)
}
var out []*route.VirtualHost
if listenerPort == 0 {
out = mergeAllVirtualHosts(vHostPortMap)
} else {
out = vHostPortMap[listenerPort]
}
return out, nil, routeCache
}