in pilot/pkg/networking/core/v1alpha3/gateway.go [324:474]
func (configgen *ConfigGeneratorImpl) buildGatewayHTTPRouteConfig(node *model.Proxy, push *model.PushContext,
routeName string) *route.RouteConfiguration {
if node.MergedGateway == nil {
log.Warnf("buildGatewayRoutes: no gateways for router %v", node.ID)
return &route.RouteConfiguration{
Name: routeName,
VirtualHosts: []*route.VirtualHost{},
ValidateClusters: proto.BoolFalse,
}
}
merged := node.MergedGateway
log.Debugf("buildGatewayRoutes: gateways after merging: %v", merged)
// make sure that there is some server listening on this port
if _, ok := merged.ServersByRouteName[routeName]; !ok {
log.Warnf("Gateway missing for route %s. This is normal if gateway was recently deleted.", routeName)
// This can happen when a gateway has recently been deleted. Envoy will still request route
// information due to the draining of listeners, so we should not return an error.
return nil
}
servers := merged.ServersByRouteName[routeName]
// When this is true, we add alt-svc header to the response to tell the client
// that HTTP/3 over QUIC is available on the same port for this host. This is
// very important for discovering HTTP/3 services
_, isH3DiscoveryNeeded := merged.HTTP3AdvertisingRoutes[routeName]
gatewayRoutes := make(map[string]map[string][]*route.Route)
gatewayVirtualServices := make(map[string][]config.Config)
vHostDedupMap := make(map[host.Name]*route.VirtualHost)
for _, server := range servers {
gatewayName := merged.GatewayNameForServer[server]
port := int(server.Port.Number)
var virtualServices []config.Config
var exists bool
if virtualServices, exists = gatewayVirtualServices[gatewayName]; !exists {
virtualServices = push.VirtualServicesForGateway(node.ConfigNamespace, gatewayName)
gatewayVirtualServices[gatewayName] = virtualServices
}
for _, virtualService := range virtualServices {
virtualServiceHosts := host.NewNames(virtualService.Spec.(*networking.VirtualService).Hosts)
serverHosts := host.NamesForNamespace(server.Hosts, virtualService.Namespace)
// We have two cases here:
// 1. virtualService hosts are 1.foo.com, 2.foo.com, 3.foo.com and server hosts are ns/*.foo.com
// 2. virtualService hosts are *.foo.com, and server hosts are ns/1.foo.com, ns/2.foo.com, ns/3.foo.com
intersectingHosts := serverHosts.Intersection(virtualServiceHosts)
if len(intersectingHosts) == 0 {
continue
}
// Make sure we can obtain services which are visible to this virtualService as much as possible.
nameToServiceMap := buildNameToServiceMapForHTTPRoutes(node, push, virtualService)
var routes []*route.Route
var exists bool
var err error
if _, exists = gatewayRoutes[gatewayName]; !exists {
gatewayRoutes[gatewayName] = make(map[string][]*route.Route)
}
vskey := virtualService.Name + "/" + virtualService.Namespace
if routes, exists = gatewayRoutes[gatewayName][vskey]; !exists {
hashByDestination := istio_route.GetConsistentHashForVirtualService(push, node, virtualService, nameToServiceMap)
routes, err = istio_route.BuildHTTPRoutesForVirtualService(node, virtualService, nameToServiceMap,
hashByDestination, port, map[string]bool{gatewayName: true}, isH3DiscoveryNeeded, push.Mesh)
if err != nil {
log.Debugf("%s omitting routes for virtual service %v/%v due to error: %v", node.ID, virtualService.Namespace, virtualService.Name, err)
continue
}
gatewayRoutes[gatewayName][vskey] = routes
}
for _, hostname := range intersectingHosts {
if vHost, exists := vHostDedupMap[hostname]; exists {
vHost.Routes = append(vHost.Routes, routes...)
if server.Tls != nil && server.Tls.HttpsRedirect {
vHost.RequireTls = route.VirtualHost_ALL
}
} else {
newVHost := &route.VirtualHost{
Name: util.DomainName(string(hostname), port),
Domains: buildGatewayVirtualHostDomains(string(hostname), port),
Routes: routes,
IncludeRequestAttemptCount: true,
}
if server.Tls != nil && server.Tls.HttpsRedirect {
newVHost.RequireTls = route.VirtualHost_ALL
}
vHostDedupMap[hostname] = newVHost
}
}
}
// check all hostname in vHostDedupMap and if is not exist with HttpsRedirect set to true
// create VirtualHost to redirect
for _, hostname := range server.Hosts {
if !server.GetTls().GetHttpsRedirect() {
continue
}
if vHost, exists := vHostDedupMap[host.Name(hostname)]; exists {
vHost.RequireTls = route.VirtualHost_ALL
continue
}
newVHost := &route.VirtualHost{
Name: util.DomainName(hostname, port),
Domains: buildGatewayVirtualHostDomains(hostname, port),
IncludeRequestAttemptCount: true,
RequireTls: route.VirtualHost_ALL,
}
vHostDedupMap[host.Name(hostname)] = newVHost
}
}
var virtualHosts []*route.VirtualHost
if len(vHostDedupMap) == 0 {
port := int(servers[0].Port.Number)
log.Warnf("constructed http route config for route %s on port %d with no vhosts; Setting up a default 404 vhost", routeName, port)
virtualHosts = []*route.VirtualHost{{
Name: util.DomainName("blackhole", port),
Domains: []string{"*"},
// Empty route list will cause Envoy to 404 NR any requests
Routes: []*route.Route{},
}}
} else {
virtualHosts = make([]*route.VirtualHost, 0, len(vHostDedupMap))
vHostDedupMap = collapseDuplicateRoutes(vHostDedupMap)
for _, v := range vHostDedupMap {
v.Routes = istio_route.CombineVHostRoutes(v.Routes)
virtualHosts = append(virtualHosts, v)
}
}
util.SortVirtualHosts(virtualHosts)
routeCfg := &route.RouteConfiguration{
// Retain the routeName as its used by EnvoyFilter patching logic
Name: routeName,
VirtualHosts: virtualHosts,
ValidateClusters: proto.BoolFalse,
}
return routeCfg
}