@ -19,6 +19,7 @@ import (
"google.golang.org/protobuf/types/known/durationpb"
"github.com/hashicorp/consul/agent/connect"
"github.com/hashicorp/consul/agent/consul/discoverychain"
"github.com/hashicorp/consul/agent/proxycfg"
"github.com/hashicorp/consul/agent/structs"
)
@ -36,13 +37,7 @@ func (s *ResourceGenerator) routesFromSnapshot(cfgSnap *proxycfg.ConfigSnapshot)
case structs . ServiceKindIngressGateway :
return s . routesForIngressGateway ( cfgSnap )
case structs . ServiceKindAPIGateway :
// TODO Find a cleaner solution, can't currently pass unexported property types
var err error
cfgSnap . IngressGateway , err = cfgSnap . APIGateway . ToIngress ( cfgSnap . Datacenter )
if err != nil {
return nil , err
}
return s . routesForIngressGateway ( cfgSnap )
return s . routesForAPIGateway ( cfgSnap )
case structs . ServiceKindTerminatingGateway :
return s . routesForTerminatingGateway ( cfgSnap )
case structs . ServiceKindMeshGateway :
@ -430,6 +425,85 @@ func (s *ResourceGenerator) routesForIngressGateway(cfgSnap *proxycfg.ConfigSnap
return result , nil
}
// routesForAPIGateway returns the xDS API representation of the
// "routes" in the snapshot.
func ( s * ResourceGenerator ) routesForAPIGateway ( cfgSnap * proxycfg . ConfigSnapshot ) ( [ ] proto . Message , error ) {
var result [ ] proto . Message
readyUpstreamsList := getReadyUpstreams ( cfgSnap )
for _ , readyUpstreams := range readyUpstreamsList {
listenerCfg := readyUpstreams . listenerCfg
// Do not create any route configuration for TCP listeners
if listenerCfg . Protocol != structs . ListenerProtocolHTTP {
continue
}
routeRef := readyUpstreams . routeReference
listenerKey := readyUpstreams . listenerKey
defaultRoute := & envoy_route_v3 . RouteConfiguration {
Name : listenerKey . RouteName ( ) ,
// ValidateClusters defaults to true when defined statically and false
// when done via RDS. Re-set the reasonable value of true to prevent
// null-routing traffic.
ValidateClusters : makeBoolValue ( true ) ,
}
route , ok := cfgSnap . APIGateway . HTTPRoutes . Get ( routeRef )
if ! ok {
return nil , fmt . Errorf ( "missing route for route reference %s:%s" , routeRef . Name , routeRef . Kind )
}
// Reformat the route here since discovery chains were indexed earlier using the
// specific naming convention in discoverychain.consolidateHTTPRoutes. If we don't
// convert our route to use the same naming convention, we won't find any chains below.
reformatedRoutes := discoverychain . ReformatHTTPRoute ( route , & listenerCfg , cfgSnap . APIGateway . GatewayConfig )
for _ , reformatedRoute := range reformatedRoutes {
reformatedRoute := reformatedRoute
upstream := buildHTTPRouteUpstream ( reformatedRoute , listenerCfg )
uid := proxycfg . NewUpstreamID ( & upstream )
chain := cfgSnap . APIGateway . DiscoveryChain [ uid ]
if chain == nil {
s . Logger . Debug ( "Discovery chain not found for flattened route" , "discovery chain ID" , uid )
continue
}
domains := generateUpstreamAPIsDomains ( listenerKey , upstream , reformatedRoute . Hostnames )
virtualHost , err := s . makeUpstreamRouteForDiscoveryChain ( cfgSnap , uid , chain , domains , false )
if err != nil {
return nil , err
}
injectHeaderManipToVirtualHostAPIGateway ( & reformatedRoute , virtualHost )
defaultRoute . VirtualHosts = append ( defaultRoute . VirtualHosts , virtualHost )
}
if len ( defaultRoute . VirtualHosts ) > 0 {
result = append ( result , defaultRoute )
}
}
return result , nil
}
func buildHTTPRouteUpstream ( route structs . HTTPRouteConfigEntry , listener structs . APIGatewayListener ) structs . Upstream {
return structs . Upstream {
DestinationName : route . GetName ( ) ,
DestinationNamespace : route . NamespaceOrDefault ( ) ,
DestinationPartition : route . PartitionOrDefault ( ) ,
IngressHosts : route . Hostnames ,
LocalBindPort : listener . Port ,
Config : map [ string ] interface { } {
"protocol" : string ( listener . Protocol ) ,
} ,
}
}
func makeHeadersValueOptions ( vals map [ string ] string , add bool ) [ ] * envoy_core_v3 . HeaderValueOption {
opts := make ( [ ] * envoy_core_v3 . HeaderValueOption , 0 , len ( vals ) )
for k , v := range vals {
@ -516,6 +590,11 @@ func generateUpstreamIngressDomains(listenerKey proxycfg.IngressListenerKey, u s
return domains
}
func generateUpstreamAPIsDomains ( listenerKey proxycfg . APIGatewayListenerKey , u structs . Upstream , hosts [ ] string ) [ ] string {
u . IngressHosts = hosts
return generateUpstreamIngressDomains ( listenerKey , u )
}
func ( s * ResourceGenerator ) makeUpstreamRouteForDiscoveryChain (
cfgSnap * proxycfg . ConfigSnapshot ,
uid proxycfg . UpstreamID ,
@ -1019,6 +1098,16 @@ func injectHeaderManipToRoute(dest *structs.ServiceRouteDestination, r *envoy_ro
return nil
}
func injectHeaderManipToVirtualHostAPIGateway ( dest * structs . HTTPRouteConfigEntry , vh * envoy_route_v3 . VirtualHost ) {
for _ , rule := range dest . Rules {
for _ , header := range rule . Filters . Headers {
vh . RequestHeadersToAdd = append ( vh . RequestHeadersToAdd , makeHeadersValueOptions ( header . Add , true ) ... )
vh . RequestHeadersToAdd = append ( vh . RequestHeadersToAdd , makeHeadersValueOptions ( header . Set , false ) ... )
vh . RequestHeadersToRemove = append ( vh . RequestHeadersToRemove , header . Remove ... )
}
}
}
func injectHeaderManipToVirtualHost ( dest * structs . IngressService , vh * envoy_route_v3 . VirtualHost ) error {
if ! dest . RequestHeaders . IsZero ( ) {
vh . RequestHeadersToAdd = append (