Skip to content

Commit

Permalink
add mesh config for validate_clusters
Browse files Browse the repository at this point in the history
  • Loading branch information
ndhanushkodi committed Aug 19, 2024
1 parent 6f9537c commit fc4f421
Show file tree
Hide file tree
Showing 12 changed files with 2,206 additions and 1,491 deletions.
5 changes: 5 additions & 0 deletions agent/structs/config_entry_mesh.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ type MeshConfigEntry struct {
// MutualTLSMode=permissive in either service-defaults or proxy-defaults.
AllowEnablingPermissiveMutualTLS bool `json:",omitempty" alias:"allow_enabling_permissive_mutual_tls"`

// ValidateClusters is false by default and configures whether Envoy proxies will validate clusters in a route. If
// set to true and any clusters in the route do not exist, the route table will not load. If set to false, the route
// table will load and routing to a non-existent cluster will result in a 404.
ValidateClusters bool `json:",omitempty" alias:"validate_clusters"`

TLS *MeshTLSConfig `json:",omitempty"`

HTTP *MeshHTTPConfig `json:",omitempty"`
Expand Down
10 changes: 10 additions & 0 deletions agent/xds/resources_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,16 @@ func getConnectProxyDiscoChainTests(enterprise bool) []goldenTestCase {
},
alsoRunTestForV2: true,
},
{
name: "connect-proxy-with-chain-and-splitter-and-mesh-validate-clusters",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
cfgSnap := proxycfg.TestConfigSnapshotDiscoveryChain(t, "chain-and-splitter", enterprise, nil, nil)
cfgSnap.ConnectProxy.MeshConfig = &structs.MeshConfigEntry{
ValidateClusters: true,
}
return cfgSnap
},
},
{
name: "connect-proxy-with-grpc-router",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
Expand Down
54 changes: 45 additions & 9 deletions agent/xds/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,19 @@ func (s *ResourceGenerator) routesFromSnapshot(cfgSnap *proxycfg.ConfigSnapshot)
}
}

func meshValidateClusters(cfgSnap *proxycfg.ConfigSnapshot) bool {
validate := false
if mesh := cfgSnap.MeshConfig(); mesh != nil {
validate = mesh.ValidateClusters
}
return validate
}

// routesFromSnapshotConnectProxy returns the xDS API representation of the
// "routes" in the snapshot.
func (s *ResourceGenerator) routesForConnectProxy(cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) {
var resources []proto.Message
validateClusters := meshValidateClusters(cfgSnap)
for uid, chain := range cfgSnap.ConnectProxy.DiscoveryChain {
if chain.Default {
continue
Expand All @@ -77,6 +86,9 @@ func (s *ResourceGenerator) routesForConnectProxy(cfgSnap *proxycfg.ConfigSnapsh
Name: uid.EnvoyID(),
VirtualHosts: []*envoy_route_v3.VirtualHost{virtualHost},
}
if validateClusters {
route.ValidateClusters = response.MakeBoolValue(true)
}
resources = append(resources, route)
}
addressesMap := make(map[string]map[string]string)
Expand Down Expand Up @@ -108,7 +120,7 @@ func (s *ResourceGenerator) routesForConnectProxy(cfgSnap *proxycfg.ConfigSnapsh
}

for routeName, clusters := range addressesMap {
routes, err := s.makeRoutesForAddresses(routeName, clusters)
routes, err := s.makeRoutesForAddresses(routeName, clusters, validateClusters)
if err != nil {
return nil, err
}
Expand All @@ -121,10 +133,10 @@ func (s *ResourceGenerator) routesForConnectProxy(cfgSnap *proxycfg.ConfigSnapsh
return resources, nil
}

func (s *ResourceGenerator) makeRoutesForAddresses(routeName string, addresses map[string]string) ([]proto.Message, error) {
func (s *ResourceGenerator) makeRoutesForAddresses(routeName string, addresses map[string]string, validateClusters bool) ([]proto.Message, error) {
var resources []proto.Message

route, err := makeNamedAddressesRoute(routeName, addresses)
route, err := makeNamedAddressesRoute(routeName, addresses, validateClusters)
if err != nil {
s.Logger.Error("failed to make route", "cluster", "error", err)
return nil, err
Expand Down Expand Up @@ -219,7 +231,8 @@ func (s *ResourceGenerator) makeRoutes(
if resolver.LoadBalancer != nil {
lb = resolver.LoadBalancer
}
route, err := makeNamedDefaultRouteWithLB(clusterName, lb, resolver.RequestTimeout, autoHostRewrite)
validateClusters := meshValidateClusters(cfgSnap)
route, err := makeNamedDefaultRouteWithLB(clusterName, lb, resolver.RequestTimeout, autoHostRewrite, validateClusters)
if err != nil {
s.Logger.Error("failed to make route", "cluster", clusterName, "error", err)
return nil, err
Expand All @@ -229,7 +242,7 @@ func (s *ResourceGenerator) makeRoutes(
// If there is a service-resolver for this service then also setup routes for each subset
for name := range resolver.Subsets {
clusterName = connect.ServiceSNI(svc.Name, name, svc.NamespaceOrDefault(), svc.PartitionOrDefault(), cfgSnap.Datacenter, cfgSnap.Roots.TrustDomain)
route, err := makeNamedDefaultRouteWithLB(clusterName, lb, resolver.RequestTimeout, autoHostRewrite)
route, err := makeNamedDefaultRouteWithLB(clusterName, lb, resolver.RequestTimeout, autoHostRewrite, validateClusters)
if err != nil {
s.Logger.Error("failed to make route", "cluster", clusterName, "error", err)
return nil, err
Expand Down Expand Up @@ -273,13 +286,18 @@ func (s *ResourceGenerator) routesForMeshGateway(cfgSnap *proxycfg.ConfigSnapsho
Name: uid.EnvoyID(),
VirtualHosts: []*envoy_route_v3.VirtualHost{virtualHost},
}
if mesh := cfgSnap.MeshConfig(); mesh != nil {
if mesh.ValidateClusters {
route.ValidateClusters = response.MakeBoolValue(true)
}
}
resources = append(resources, route)
}

return resources, nil
}

func makeNamedDefaultRouteWithLB(clusterName string, lb *structs.LoadBalancer, timeout time.Duration, autoHostRewrite bool) (*envoy_route_v3.RouteConfiguration, error) {
func makeNamedDefaultRouteWithLB(clusterName string, lb *structs.LoadBalancer, timeout time.Duration, autoHostRewrite bool, validateClusters bool) (*envoy_route_v3.RouteConfiguration, error) {
action := makeRouteActionFromName(clusterName)

if err := injectLBToRouteAction(lb, action.Route); err != nil {
Expand All @@ -297,7 +315,7 @@ func makeNamedDefaultRouteWithLB(clusterName string, lb *structs.LoadBalancer, t
action.Route.Timeout = durationpb.New(timeout)
}

return &envoy_route_v3.RouteConfiguration{
route := &envoy_route_v3.RouteConfiguration{
Name: clusterName,
VirtualHosts: []*envoy_route_v3.VirtualHost{
{
Expand All @@ -311,13 +329,20 @@ func makeNamedDefaultRouteWithLB(clusterName string, lb *structs.LoadBalancer, t
},
},
},
}, nil
}
if validateClusters {
route.ValidateClusters = response.MakeBoolValue(true)
}
return route, nil
}

func makeNamedAddressesRoute(routeName string, addresses map[string]string) (*envoy_route_v3.RouteConfiguration, error) {
func makeNamedAddressesRoute(routeName string, addresses map[string]string, validateClusters bool) (*envoy_route_v3.RouteConfiguration, error) {
route := &envoy_route_v3.RouteConfiguration{
Name: routeName,
}
if validateClusters {
route.ValidateClusters = response.MakeBoolValue(true)
}
for clusterName, address := range addresses {
action := makeRouteActionFromName(clusterName)
virtualHost := &envoy_route_v3.VirtualHost{
Expand Down Expand Up @@ -356,6 +381,10 @@ func (s *ResourceGenerator) routesForIngressGateway(cfgSnap *proxycfg.ConfigSnap
defaultRoute := &envoy_route_v3.RouteConfiguration{
Name: listenerKey.RouteName(),
}
validateClusters := meshValidateClusters(cfgSnap)
if validateClusters {
defaultRoute.ValidateClusters = response.MakeBoolValue(true)
}

for _, u := range upstreams {
uid := proxycfg.NewUpstreamID(&u)
Expand Down Expand Up @@ -405,6 +434,9 @@ func (s *ResourceGenerator) routesForIngressGateway(cfgSnap *proxycfg.ConfigSnap
Name: svcRouteName,
VirtualHosts: []*envoy_route_v3.VirtualHost{virtualHost},
}
if validateClusters {
svcRoute.ValidateClusters = response.MakeBoolValue(true)
}
result = append(result, svcRoute)
}
}
Expand Down Expand Up @@ -440,6 +472,10 @@ func (s *ResourceGenerator) routesForAPIGateway(cfgSnap *proxycfg.ConfigSnapshot
listenerRoute := &envoy_route_v3.RouteConfiguration{
Name: readyListener.listenerKey.RouteName(),
}
validateClusters := meshValidateClusters(cfgSnap)
if validateClusters {
listenerRoute.ValidateClusters = response.MakeBoolValue(true)
}

// Consolidate all routes for this listener into the minimum possible set based on hostname matching.
allRoutesForListener := []*structs.HTTPRouteConfigEntry{}
Expand Down
Loading

0 comments on commit fc4f421

Please sign in to comment.