xds: Add route to filterchain (#4610)

* Added RDS Information from LDS in filter chain
This commit is contained in:
Zach Reyes 2021-08-05 17:28:06 -04:00 committed by GitHub
parent 6ba56c814b
commit 74370577fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 820 additions and 196 deletions

View File

@ -30,6 +30,7 @@ import (
v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
v3httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
wrapperspb "github.com/golang/protobuf/ptypes/wrappers"
@ -87,7 +88,20 @@ var listenerWithFilterChains = &v3listenerpb.Listener{
{
Name: "filter-1",
ConfigType: &v3listenerpb.Filter_TypedConfig{
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{}),
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
RouteConfig: &v3routepb.RouteConfiguration{
Name: "routeName",
VirtualHosts: []*v3routepb.VirtualHost{{
Domains: []string{"lds.target.good:3333"},
Routes: []*v3routepb.Route{{
Match: &v3routepb.RouteMatch{
PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"},
},
Action: &v3routepb.Route_NonForwardingAction{},
}}}}},
},
}),
},
},
},

View File

@ -199,7 +199,20 @@ func DefaultServerListener(host string, port uint32, secLevel SecurityLevel) *v3
{
Name: "filter-1",
ConfigType: &v3listenerpb.Filter_TypedConfig{
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{}),
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
RouteConfig: &v3routepb.RouteConfiguration{
Name: "routeName",
VirtualHosts: []*v3routepb.VirtualHost{{
Domains: []string{"lds.target.good:3333"},
Routes: []*v3routepb.Route{{
Match: &v3routepb.RouteMatch{
PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"},
},
Action: &v3routepb.Route_NonForwardingAction{},
}}}}},
},
}),
},
},
},
@ -230,7 +243,20 @@ func DefaultServerListener(host string, port uint32, secLevel SecurityLevel) *v3
{
Name: "filter-1",
ConfigType: &v3listenerpb.Filter_TypedConfig{
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{}),
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
RouteConfig: &v3routepb.RouteConfiguration{
Name: "routeName",
VirtualHosts: []*v3routepb.VirtualHost{{
Domains: []string{"lds.target.good:3333"},
Routes: []*v3routepb.Route{{
Match: &v3routepb.RouteMatch{
PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"},
},
Action: &v3routepb.Route_NonForwardingAction{},
}}}}},
},
}),
},
},
},

View File

@ -294,6 +294,25 @@ type HashPolicy struct {
RegexSubstitution string
}
// RouteAction is the action of the route from a received RDS response.
type RouteAction int
const (
// RouteActionUnsupported are routing types currently unsupported by grpc.
// According to A36, "A Route with an inappropriate action causes RPCs
// matching that route to fail."
RouteActionUnsupported RouteAction = iota
// RouteActionRoute is the expected route type on the client side. Route
// represents routing a request to some upstream cluster. On the client
// side, if an RPC matches to a route that is not RouteActionRoute, the RPC
// will fail according to A36.
RouteActionRoute
// RouteActionNonForwardingAction is the expected route type on the server
// side. NonForwardingAction represents when a route will generate a
// response directly, without forwarding to an upstream host.
RouteActionNonForwardingAction
)
// Route is both a specification of how to match a request as well as an
// indication of the action to take upon match.
type Route struct {
@ -321,6 +340,8 @@ type Route struct {
// unused if the matching WeightedCluster contains an override for that
// filter.
HTTPFilterConfigOverride map[string]httpfilter.FilterConfig
RouteAction RouteAction
}
// WeightedCluster contains settings for an xds RouteAction.WeightedCluster.

View File

@ -24,8 +24,10 @@ import (
"net"
v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
v3httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes"
"google.golang.org/grpc/xds/internal/version"
)
@ -54,6 +56,15 @@ type FilterChain struct {
SecurityCfg *SecurityConfig
// HTTPFilters represent the HTTP Filters that comprise this FilterChain.
HTTPFilters []HTTPFilter
// RouteConfigName is the route configuration name for this FilterChain.
//
// Only one of RouteConfigName and InlineRouteConfig is set.
RouteConfigName string
// InlineRouteConfig is the inline route configuration (RDS response)
// returned for this filter chain.
//
// Only one of RouteConfigName and InlineRouteConfig is set.
InlineRouteConfig *RouteConfigUpdate
}
// SourceType specifies the connection source IP match type.
@ -109,6 +120,11 @@ type FilterChainManager struct {
dstPrefixes []*destPrefixEntry
def *FilterChain // Default filter chain, if specified.
// RouteConfigNames are the route configuration names which need to be
// dynamically queried for RDS Configuration for any FilterChains which
// specify to load RDS Configuration dynamically.
RouteConfigNames map[string]bool
}
// destPrefixEntry is the value type of the map indexed on destination prefixes.
@ -158,7 +174,10 @@ type sourcePrefixEntry struct {
// create a FilterChainManager.
func NewFilterChainManager(lis *v3listenerpb.Listener) (*FilterChainManager, error) {
// Parse all the filter chains and build the internal data structures.
fci := &FilterChainManager{dstPrefixMap: make(map[string]*destPrefixEntry)}
fci := &FilterChainManager{
dstPrefixMap: make(map[string]*destPrefixEntry),
RouteConfigNames: make(map[string]bool),
}
if err := fci.addFilterChains(lis.GetFilterChains()); err != nil {
return nil, err
}
@ -187,7 +206,7 @@ func NewFilterChainManager(lis *v3listenerpb.Listener) (*FilterChainManager, err
var def *FilterChain
if dfc := lis.GetDefaultFilterChain(); dfc != nil {
var err error
if def, err = filterChainFromProto(dfc); err != nil {
if def, err = fci.filterChainFromProto(dfc); err != nil {
return nil, err
}
}
@ -368,7 +387,7 @@ func (fci *FilterChainManager) addFilterChainsForSourcePorts(srcEntry *sourcePre
srcPorts = append(srcPorts, int(port))
}
fc, err := filterChainFromProto(fcProto)
fc, err := fci.filterChainFromProto(fcProto)
if err != nil {
return err
}
@ -391,13 +410,19 @@ func (fci *FilterChainManager) addFilterChainsForSourcePorts(srcEntry *sourcePre
}
// filterChainFromProto extracts the relevant information from the FilterChain
// proto and stores it in our internal representation.
func filterChainFromProto(fc *v3listenerpb.FilterChain) (*FilterChain, error) {
httpFilters, err := processNetworkFilters(fc.GetFilters())
// proto and stores it in our internal representation. It also persists any
// RouteNames which need to be queried dynamically via RDS.
func (fci *FilterChainManager) filterChainFromProto(fc *v3listenerpb.FilterChain) (*FilterChain, error) {
filterChain, err := processNetworkFilters(fc.GetFilters())
if err != nil {
return nil, err
}
filterChain := &FilterChain{HTTPFilters: httpFilters}
// These route names will be dynamically queried via RDS in the wrapped
// listener, which receives the LDS response, if specified for the filter
// chain.
if filterChain.RouteConfigName != "" {
fci.RouteConfigNames[filterChain.RouteConfigName] = true
}
// If the transport_socket field is not specified, it means that the control
// plane has not sent us any security config. This is fine and the server
// will use the fallback credentials configured as part of the
@ -435,6 +460,93 @@ func filterChainFromProto(fc *v3listenerpb.FilterChain) (*FilterChain, error) {
return filterChain, nil
}
func processNetworkFilters(filters []*v3listenerpb.Filter) (*FilterChain, error) {
filterChain := &FilterChain{}
seenNames := make(map[string]bool, len(filters))
seenHCM := false
for _, filter := range filters {
name := filter.GetName()
if name == "" {
return nil, fmt.Errorf("network filters {%+v} is missing name field in filter: {%+v}", filters, filter)
}
if seenNames[name] {
return nil, fmt.Errorf("network filters {%+v} has duplicate filter name %q", filters, name)
}
seenNames[name] = true
// Network filters have a oneof field named `config_type` where we
// only support `TypedConfig` variant.
switch typ := filter.GetConfigType().(type) {
case *v3listenerpb.Filter_TypedConfig:
// The typed_config field has an `anypb.Any` proto which could
// directly contain the serialized bytes of the actual filter
// configuration, or it could be encoded as a `TypedStruct`.
// TODO: Add support for `TypedStruct`.
tc := filter.GetTypedConfig()
// The only network filter that we currently support is the v3
// HttpConnectionManager. So, we can directly check the type_url
// and unmarshal the config.
// TODO: Implement a registry of supported network filters (like
// we have for HTTP filters), when we have to support network
// filters other than HttpConnectionManager.
if tc.GetTypeUrl() != version.V3HTTPConnManagerURL {
return nil, fmt.Errorf("network filters {%+v} has unsupported network filter %q in filter {%+v}", filters, tc.GetTypeUrl(), filter)
}
hcm := &v3httppb.HttpConnectionManager{}
if err := ptypes.UnmarshalAny(tc, hcm); err != nil {
return nil, fmt.Errorf("network filters {%+v} failed unmarshaling of network filter {%+v}: %v", filters, filter, err)
}
// "Any filters after HttpConnectionManager should be ignored during
// connection processing but still be considered for validity.
// HTTPConnectionManager must have valid http_filters." - A36
filters, err := processHTTPFilters(hcm.GetHttpFilters(), true)
if err != nil {
return nil, fmt.Errorf("network filters {%+v} had invalid server side HTTP Filters {%+v}", filters, hcm.GetHttpFilters())
}
if !seenHCM {
// TODO: Implement terminal filter logic, as per A36.
filterChain.HTTPFilters = filters
seenHCM = true
switch hcm.RouteSpecifier.(type) {
case *v3httppb.HttpConnectionManager_Rds:
if hcm.GetRds().GetConfigSource().GetAds() == nil {
return nil, fmt.Errorf("ConfigSource is not ADS: %+v", hcm)
}
name := hcm.GetRds().GetRouteConfigName()
if name == "" {
return nil, fmt.Errorf("empty route_config_name: %+v", hcm)
}
filterChain.RouteConfigName = name
case *v3httppb.HttpConnectionManager_RouteConfig:
// "RouteConfiguration validation logic inherits all
// previous validations made for client-side usage as RDS
// does not distinguish between client-side and
// server-side." - A36
// Can specify v3 here, as will never get to this function
// if v2.
routeU, err := generateRDSUpdateFromRouteConfiguration(hcm.GetRouteConfig(), nil, false)
if err != nil {
return nil, fmt.Errorf("failed to parse inline RDS resp: %v", err)
}
filterChain.InlineRouteConfig = &routeU
case nil:
// No-op, as no route specifier is a valid configuration on
// the server side.
default:
return nil, fmt.Errorf("unsupported type %T for RouteSpecifier", hcm.RouteSpecifier)
}
}
default:
return nil, fmt.Errorf("network filters {%+v} has unsupported config_type %T in filter %s", filters, typ, filter.GetName())
}
}
if !seenHCM {
return nil, fmt.Errorf("network filters {%+v} missing HttpConnectionManager filter", filters)
}
return filterChain, nil
}
// FilterChainLookupParams wraps parameters to be passed to Lookup.
type FilterChainLookupParams struct {
// IsUnspecified indicates whether the server is listening on a wildcard

View File

@ -28,6 +28,7 @@ import (
v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
v3httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
"github.com/google/go-cmp/cmp"
@ -41,11 +42,30 @@ import (
)
var (
routeConfig = &v3routepb.RouteConfiguration{
Name: "routeName",
VirtualHosts: []*v3routepb.VirtualHost{{
Domains: []string{"lds.target.good:3333"},
Routes: []*v3routepb.Route{{
Match: &v3routepb.RouteMatch{
PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"},
},
Action: &v3routepb.Route_NonForwardingAction{},
}}}}}
inlineRouteConfig = &RouteConfigUpdate{
VirtualHosts: []*VirtualHost{{
Domains: []string{"lds.target.good:3333"},
Routes: []*Route{{Prefix: newStringP("/"), RouteAction: RouteActionNonForwardingAction}},
}}}
emptyValidNetworkFilters = []*v3listenerpb.Filter{
{
Name: "filter-1",
ConfigType: &v3listenerpb.Filter_TypedConfig{
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{}),
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
RouteConfig: routeConfig,
},
}),
},
},
}
@ -249,8 +269,7 @@ func TestNewFilterChainImpl_Failure_OverlappingMatchingRules(t *testing.T) {
const wantErr = "multiple filter chains with overlapping matching rules are defined"
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
_, err := NewFilterChainManager(test.lis)
if err == nil || !strings.Contains(err.Error(), wantErr) {
if _, err := NewFilterChainManager(test.lis); err == nil || !strings.Contains(err.Error(), wantErr) {
t.Fatalf("NewFilterChainManager() returned err: %v, wantErr: %s", err, wantErr)
}
})
@ -417,6 +436,323 @@ func TestNewFilterChainImpl_Failure_BadSecurityConfig(t *testing.T) {
}
}
// TestNewFilterChainImpl_Success_RouteUpdate tests the construction of the
// filter chain with valid HTTP Filters present.
func TestNewFilterChainImpl_Success_RouteUpdate(t *testing.T) {
tests := []struct {
name string
lis *v3listenerpb.Listener
wantFC *FilterChainManager
}{
{
name: "rds",
lis: &v3listenerpb.Listener{
FilterChains: []*v3listenerpb.FilterChain{
{
Name: "filter-chain-1",
Filters: []*v3listenerpb.Filter{
{
Name: "hcm",
ConfigType: &v3listenerpb.Filter_TypedConfig{
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
Rds: &v3httppb.Rds{
ConfigSource: &v3corepb.ConfigSource{
ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{Ads: &v3corepb.AggregatedConfigSource{}},
},
RouteConfigName: "route-1",
},
},
}),
},
},
},
},
},
DefaultFilterChain: &v3listenerpb.FilterChain{
Filters: []*v3listenerpb.Filter{
{
Name: "hcm",
ConfigType: &v3listenerpb.Filter_TypedConfig{
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
Rds: &v3httppb.Rds{
ConfigSource: &v3corepb.ConfigSource{
ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{Ads: &v3corepb.AggregatedConfigSource{}},
},
RouteConfigName: "route-1",
},
},
}),
},
},
},
},
},
wantFC: &FilterChainManager{
dstPrefixMap: map[string]*destPrefixEntry{
unspecifiedPrefixMapKey: {
srcTypeArr: [3]*sourcePrefixes{
{
srcPrefixMap: map[string]*sourcePrefixEntry{
unspecifiedPrefixMapKey: {
srcPortMap: map[int]*FilterChain{
0: {
RouteConfigName: "route-1",
},
},
},
},
},
},
},
},
def: &FilterChain{
RouteConfigName: "route-1",
},
RouteConfigNames: map[string]bool{"route-1": true},
},
},
{
name: "inline route config",
lis: &v3listenerpb.Listener{
FilterChains: []*v3listenerpb.FilterChain{
{
Name: "filter-chain-1",
Filters: []*v3listenerpb.Filter{
{
Name: "hcm",
ConfigType: &v3listenerpb.Filter_TypedConfig{
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
RouteConfig: routeConfig,
},
}),
},
},
},
},
},
DefaultFilterChain: &v3listenerpb.FilterChain{
Filters: []*v3listenerpb.Filter{
{
Name: "hcm",
ConfigType: &v3listenerpb.Filter_TypedConfig{
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
RouteConfig: routeConfig,
},
}),
},
},
},
},
},
wantFC: &FilterChainManager{
dstPrefixMap: map[string]*destPrefixEntry{
unspecifiedPrefixMapKey: {
srcTypeArr: [3]*sourcePrefixes{
{
srcPrefixMap: map[string]*sourcePrefixEntry{
unspecifiedPrefixMapKey: {
srcPortMap: map[int]*FilterChain{
0: {
InlineRouteConfig: inlineRouteConfig,
},
},
},
},
},
},
},
},
def: &FilterChain{
InlineRouteConfig: inlineRouteConfig,
},
},
},
// two rds tests whether the Filter Chain Manager successfully persists
// the two RDS names that need to be dynamically queried.
{
name: "two rds",
lis: &v3listenerpb.Listener{
FilterChains: []*v3listenerpb.FilterChain{
{
Name: "filter-chain-1",
Filters: []*v3listenerpb.Filter{
{
Name: "hcm",
ConfigType: &v3listenerpb.Filter_TypedConfig{
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
Rds: &v3httppb.Rds{
ConfigSource: &v3corepb.ConfigSource{
ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{Ads: &v3corepb.AggregatedConfigSource{}},
},
RouteConfigName: "route-1",
},
},
}),
},
},
},
},
},
DefaultFilterChain: &v3listenerpb.FilterChain{
Filters: []*v3listenerpb.Filter{
{
Name: "hcm",
ConfigType: &v3listenerpb.Filter_TypedConfig{
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
Rds: &v3httppb.Rds{
ConfigSource: &v3corepb.ConfigSource{
ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{Ads: &v3corepb.AggregatedConfigSource{}},
},
RouteConfigName: "route-2",
},
},
}),
},
},
},
},
},
wantFC: &FilterChainManager{
dstPrefixMap: map[string]*destPrefixEntry{
unspecifiedPrefixMapKey: {
srcTypeArr: [3]*sourcePrefixes{
{
srcPrefixMap: map[string]*sourcePrefixEntry{
unspecifiedPrefixMapKey: {
srcPortMap: map[int]*FilterChain{
0: {
RouteConfigName: "route-1",
},
},
},
},
},
},
},
},
def: &FilterChain{
RouteConfigName: "route-2",
},
RouteConfigNames: map[string]bool{
"route-1": true,
"route-2": true,
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
gotFC, err := NewFilterChainManager(test.lis)
if err != nil {
t.Fatalf("NewFilterChainManager() returned err: %v, wantErr: nil", err)
}
if !cmp.Equal(gotFC, test.wantFC, cmp.AllowUnexported(FilterChainManager{}, destPrefixEntry{}, sourcePrefixes{}, sourcePrefixEntry{}), cmpOpts) {
t.Fatalf("NewFilterChainManager() returned %+v, want: %+v", gotFC, test.wantFC)
}
})
}
}
// TestNewFilterChainImpl_Failure_BadRouteUpdate verifies cases where the Route
// Update in the filter chain are invalid.
func TestNewFilterChainImpl_Failure_BadRouteUpdate(t *testing.T) {
tests := []struct {
name string
lis *v3listenerpb.Listener
wantErr string
}{
{
name: "not-ads",
lis: &v3listenerpb.Listener{
FilterChains: []*v3listenerpb.FilterChain{
{
Name: "filter-chain-1",
Filters: []*v3listenerpb.Filter{
{
Name: "hcm",
ConfigType: &v3listenerpb.Filter_TypedConfig{
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
Rds: &v3httppb.Rds{
RouteConfigName: "route-1",
},
},
}),
},
},
},
},
},
DefaultFilterChain: &v3listenerpb.FilterChain{
Filters: []*v3listenerpb.Filter{
{
Name: "hcm",
ConfigType: &v3listenerpb.Filter_TypedConfig{
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
Rds: &v3httppb.Rds{
RouteConfigName: "route-1",
},
},
}),
},
},
},
},
},
wantErr: "ConfigSource is not ADS",
},
{
name: "unsupported-route-specifier",
lis: &v3listenerpb.Listener{
FilterChains: []*v3listenerpb.FilterChain{
{
Name: "filter-chain-1",
Filters: []*v3listenerpb.Filter{
{
Name: "hcm",
ConfigType: &v3listenerpb.Filter_TypedConfig{
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
RouteSpecifier: &v3httppb.HttpConnectionManager_ScopedRoutes{},
}),
},
},
},
},
},
DefaultFilterChain: &v3listenerpb.FilterChain{
Filters: []*v3listenerpb.Filter{
{
Name: "hcm",
ConfigType: &v3listenerpb.Filter_TypedConfig{
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
RouteSpecifier: &v3httppb.HttpConnectionManager_ScopedRoutes{},
}),
},
},
},
},
},
wantErr: "unsupported type",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
_, err := NewFilterChainManager(test.lis)
if err == nil || !strings.Contains(err.Error(), test.wantErr) {
t.Fatalf("NewFilterChainManager() returned err: %v, wantErr: %s", err, test.wantErr)
}
})
}
}
// TestNewFilterChainImpl_Failure_BadHTTPFilters verifies cases where the HTTP
// Filters in the filter chain are invalid.
func TestNewFilterChainImpl_Failure_BadHTTPFilters(t *testing.T) {
@ -513,6 +849,9 @@ func TestNewFilterChainImpl_Success_HTTPFilters(t *testing.T) {
HttpFilters: []*v3httppb.HttpFilter{
validServerSideHTTPFilter1,
},
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
RouteConfig: routeConfig,
},
}),
},
},
@ -528,6 +867,9 @@ func TestNewFilterChainImpl_Success_HTTPFilters(t *testing.T) {
HttpFilters: []*v3httppb.HttpFilter{
validServerSideHTTPFilter1,
},
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
RouteConfig: routeConfig,
},
}),
},
},
@ -548,7 +890,9 @@ func TestNewFilterChainImpl_Success_HTTPFilters(t *testing.T) {
Filter: serverOnlyHTTPFilter{},
Config: filterConfig{Cfg: serverOnlyCustomFilterConfig},
},
}},
},
InlineRouteConfig: inlineRouteConfig,
},
},
},
},
@ -556,13 +900,14 @@ func TestNewFilterChainImpl_Success_HTTPFilters(t *testing.T) {
},
},
},
def: &FilterChain{HTTPFilters: []HTTPFilter{
{
def: &FilterChain{
HTTPFilters: []HTTPFilter{{
Name: "serverOnlyCustomFilter",
Filter: serverOnlyHTTPFilter{},
Config: filterConfig{Cfg: serverOnlyCustomFilterConfig},
},
}},
}},
InlineRouteConfig: inlineRouteConfig,
},
},
},
{
@ -580,6 +925,9 @@ func TestNewFilterChainImpl_Success_HTTPFilters(t *testing.T) {
validServerSideHTTPFilter1,
validServerSideHTTPFilter2,
},
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
RouteConfig: routeConfig,
},
}),
},
},
@ -596,6 +944,9 @@ func TestNewFilterChainImpl_Success_HTTPFilters(t *testing.T) {
validServerSideHTTPFilter1,
validServerSideHTTPFilter2,
},
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
RouteConfig: routeConfig,
},
}),
},
},
@ -621,7 +972,9 @@ func TestNewFilterChainImpl_Success_HTTPFilters(t *testing.T) {
Filter: serverOnlyHTTPFilter{},
Config: filterConfig{Cfg: serverOnlyCustomFilterConfig},
},
}},
},
InlineRouteConfig: inlineRouteConfig,
},
},
},
},
@ -641,6 +994,7 @@ func TestNewFilterChainImpl_Success_HTTPFilters(t *testing.T) {
Config: filterConfig{Cfg: serverOnlyCustomFilterConfig},
},
},
InlineRouteConfig: inlineRouteConfig,
},
},
},
@ -661,6 +1015,9 @@ func TestNewFilterChainImpl_Success_HTTPFilters(t *testing.T) {
validServerSideHTTPFilter1,
validServerSideHTTPFilter2,
},
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
RouteConfig: routeConfig,
},
}),
},
},
@ -671,6 +1028,9 @@ func TestNewFilterChainImpl_Success_HTTPFilters(t *testing.T) {
HttpFilters: []*v3httppb.HttpFilter{
validServerSideHTTPFilter1,
},
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
RouteConfig: routeConfig,
},
}),
},
},
@ -687,6 +1047,9 @@ func TestNewFilterChainImpl_Success_HTTPFilters(t *testing.T) {
validServerSideHTTPFilter1,
validServerSideHTTPFilter2,
},
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
RouteConfig: routeConfig,
},
}),
},
},
@ -697,6 +1060,9 @@ func TestNewFilterChainImpl_Success_HTTPFilters(t *testing.T) {
HttpFilters: []*v3httppb.HttpFilter{
validServerSideHTTPFilter1,
},
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
RouteConfig: routeConfig,
},
}),
},
},
@ -722,7 +1088,9 @@ func TestNewFilterChainImpl_Success_HTTPFilters(t *testing.T) {
Filter: serverOnlyHTTPFilter{},
Config: filterConfig{Cfg: serverOnlyCustomFilterConfig},
},
}},
},
InlineRouteConfig: inlineRouteConfig,
},
},
},
},
@ -742,6 +1110,7 @@ func TestNewFilterChainImpl_Success_HTTPFilters(t *testing.T) {
Config: filterConfig{Cfg: serverOnlyCustomFilterConfig},
},
},
InlineRouteConfig: inlineRouteConfig,
},
},
},
@ -789,7 +1158,7 @@ func TestNewFilterChainImpl_Success_SecurityConfig(t *testing.T) {
srcPrefixMap: map[string]*sourcePrefixEntry{
unspecifiedPrefixMapKey: {
srcPortMap: map[int]*FilterChain{
0: {},
0: {InlineRouteConfig: inlineRouteConfig},
},
},
},
@ -797,7 +1166,7 @@ func TestNewFilterChainImpl_Success_SecurityConfig(t *testing.T) {
},
},
},
def: &FilterChain{},
def: &FilterChain{InlineRouteConfig: inlineRouteConfig},
},
},
{
@ -851,6 +1220,7 @@ func TestNewFilterChainImpl_Success_SecurityConfig(t *testing.T) {
IdentityInstanceName: "identityPluginInstance",
IdentityCertName: "identityCertName",
},
InlineRouteConfig: inlineRouteConfig,
},
},
},
@ -864,6 +1234,7 @@ func TestNewFilterChainImpl_Success_SecurityConfig(t *testing.T) {
IdentityInstanceName: "defaultIdentityPluginInstance",
IdentityCertName: "defaultIdentityCertName",
},
InlineRouteConfig: inlineRouteConfig,
},
},
},
@ -936,6 +1307,7 @@ func TestNewFilterChainImpl_Success_SecurityConfig(t *testing.T) {
IdentityCertName: "identityCertName",
RequireClientCert: true,
},
InlineRouteConfig: inlineRouteConfig,
},
},
},
@ -952,6 +1324,7 @@ func TestNewFilterChainImpl_Success_SecurityConfig(t *testing.T) {
IdentityCertName: "defaultIdentityCertName",
RequireClientCert: true,
},
InlineRouteConfig: inlineRouteConfig,
},
},
},
@ -982,7 +1355,7 @@ func TestNewFilterChainImpl_Success_UnsupportedMatchFields(t *testing.T) {
srcPrefixMap: map[string]*sourcePrefixEntry{
unspecifiedPrefixMapKey: {
srcPortMap: map[int]*FilterChain{
0: {},
0: {InlineRouteConfig: inlineRouteConfig},
},
},
},
@ -1018,7 +1391,7 @@ func TestNewFilterChainImpl_Success_UnsupportedMatchFields(t *testing.T) {
dstPrefixMap: map[string]*destPrefixEntry{
unspecifiedPrefixMapKey: unspecifiedEntry,
},
def: &FilterChain{},
def: &FilterChain{InlineRouteConfig: inlineRouteConfig},
},
},
{
@ -1047,7 +1420,7 @@ func TestNewFilterChainImpl_Success_UnsupportedMatchFields(t *testing.T) {
net: ipNetFromCIDR("192.168.2.2/16"),
},
},
def: &FilterChain{},
def: &FilterChain{InlineRouteConfig: inlineRouteConfig},
},
},
{
@ -1076,7 +1449,7 @@ func TestNewFilterChainImpl_Success_UnsupportedMatchFields(t *testing.T) {
net: ipNetFromCIDR("192.168.2.2/16"),
},
},
def: &FilterChain{},
def: &FilterChain{InlineRouteConfig: inlineRouteConfig},
},
},
{
@ -1105,7 +1478,7 @@ func TestNewFilterChainImpl_Success_UnsupportedMatchFields(t *testing.T) {
net: ipNetFromCIDR("192.168.2.2/16"),
},
},
def: &FilterChain{},
def: &FilterChain{InlineRouteConfig: inlineRouteConfig},
},
},
}
@ -1175,7 +1548,7 @@ func TestNewFilterChainImpl_Success_AllCombinations(t *testing.T) {
srcPrefixMap: map[string]*sourcePrefixEntry{
unspecifiedPrefixMapKey: {
srcPortMap: map[int]*FilterChain{
0: {},
0: {InlineRouteConfig: inlineRouteConfig},
},
},
},
@ -1191,7 +1564,7 @@ func TestNewFilterChainImpl_Success_AllCombinations(t *testing.T) {
srcPrefixMap: map[string]*sourcePrefixEntry{
unspecifiedPrefixMapKey: {
srcPortMap: map[int]*FilterChain{
0: {},
0: {InlineRouteConfig: inlineRouteConfig},
},
},
},
@ -1207,7 +1580,7 @@ func TestNewFilterChainImpl_Success_AllCombinations(t *testing.T) {
srcPrefixMap: map[string]*sourcePrefixEntry{
unspecifiedPrefixMapKey: {
srcPortMap: map[int]*FilterChain{
0: {},
0: {InlineRouteConfig: inlineRouteConfig},
},
},
},
@ -1221,7 +1594,7 @@ func TestNewFilterChainImpl_Success_AllCombinations(t *testing.T) {
srcPrefixMap: map[string]*sourcePrefixEntry{
unspecifiedPrefixMapKey: {
srcPortMap: map[int]*FilterChain{
0: {},
0: {InlineRouteConfig: inlineRouteConfig},
},
},
},
@ -1235,7 +1608,7 @@ func TestNewFilterChainImpl_Success_AllCombinations(t *testing.T) {
srcPrefixMap: map[string]*sourcePrefixEntry{
unspecifiedPrefixMapKey: {
srcPortMap: map[int]*FilterChain{
0: {},
0: {InlineRouteConfig: inlineRouteConfig},
},
},
},
@ -1243,7 +1616,7 @@ func TestNewFilterChainImpl_Success_AllCombinations(t *testing.T) {
},
},
},
def: &FilterChain{},
def: &FilterChain{InlineRouteConfig: inlineRouteConfig},
},
},
{
@ -1273,7 +1646,7 @@ func TestNewFilterChainImpl_Success_AllCombinations(t *testing.T) {
srcPrefixMap: map[string]*sourcePrefixEntry{
unspecifiedPrefixMapKey: {
srcPortMap: map[int]*FilterChain{
0: {},
0: {InlineRouteConfig: inlineRouteConfig},
},
},
},
@ -1289,7 +1662,7 @@ func TestNewFilterChainImpl_Success_AllCombinations(t *testing.T) {
srcPrefixMap: map[string]*sourcePrefixEntry{
unspecifiedPrefixMapKey: {
srcPortMap: map[int]*FilterChain{
0: {},
0: {InlineRouteConfig: inlineRouteConfig},
},
},
},
@ -1297,7 +1670,7 @@ func TestNewFilterChainImpl_Success_AllCombinations(t *testing.T) {
},
},
},
def: &FilterChain{},
def: &FilterChain{InlineRouteConfig: inlineRouteConfig},
},
},
{
@ -1327,7 +1700,7 @@ func TestNewFilterChainImpl_Success_AllCombinations(t *testing.T) {
"10.0.0.0/8": {
net: ipNetFromCIDR("10.0.0.0/8"),
srcPortMap: map[int]*FilterChain{
0: {},
0: {InlineRouteConfig: inlineRouteConfig},
},
},
},
@ -1342,7 +1715,7 @@ func TestNewFilterChainImpl_Success_AllCombinations(t *testing.T) {
"192.168.0.0/16": {
net: ipNetFromCIDR("192.168.0.0/16"),
srcPortMap: map[int]*FilterChain{
0: {},
0: {InlineRouteConfig: inlineRouteConfig},
},
},
},
@ -1350,7 +1723,7 @@ func TestNewFilterChainImpl_Success_AllCombinations(t *testing.T) {
},
},
},
def: &FilterChain{},
def: &FilterChain{InlineRouteConfig: inlineRouteConfig},
},
},
{
@ -1381,9 +1754,9 @@ func TestNewFilterChainImpl_Success_AllCombinations(t *testing.T) {
srcPrefixMap: map[string]*sourcePrefixEntry{
unspecifiedPrefixMapKey: {
srcPortMap: map[int]*FilterChain{
1: {},
2: {},
3: {},
1: {InlineRouteConfig: inlineRouteConfig},
2: {InlineRouteConfig: inlineRouteConfig},
3: {InlineRouteConfig: inlineRouteConfig},
},
},
},
@ -1400,9 +1773,9 @@ func TestNewFilterChainImpl_Success_AllCombinations(t *testing.T) {
"192.168.0.0/16": {
net: ipNetFromCIDR("192.168.0.0/16"),
srcPortMap: map[int]*FilterChain{
1: {},
2: {},
3: {},
1: {InlineRouteConfig: inlineRouteConfig},
2: {InlineRouteConfig: inlineRouteConfig},
3: {InlineRouteConfig: inlineRouteConfig},
},
},
},
@ -1410,7 +1783,7 @@ func TestNewFilterChainImpl_Success_AllCombinations(t *testing.T) {
},
},
},
def: &FilterChain{},
def: &FilterChain{InlineRouteConfig: inlineRouteConfig},
},
},
{
@ -1484,7 +1857,7 @@ func TestNewFilterChainImpl_Success_AllCombinations(t *testing.T) {
srcPrefixMap: map[string]*sourcePrefixEntry{
unspecifiedPrefixMapKey: {
srcPortMap: map[int]*FilterChain{
0: {},
0: {InlineRouteConfig: inlineRouteConfig},
},
},
},
@ -1498,7 +1871,7 @@ func TestNewFilterChainImpl_Success_AllCombinations(t *testing.T) {
srcPrefixMap: map[string]*sourcePrefixEntry{
unspecifiedPrefixMapKey: {
srcPortMap: map[int]*FilterChain{
0: {},
0: {InlineRouteConfig: inlineRouteConfig},
},
},
},
@ -1512,7 +1885,7 @@ func TestNewFilterChainImpl_Success_AllCombinations(t *testing.T) {
srcPrefixMap: map[string]*sourcePrefixEntry{
unspecifiedPrefixMapKey: {
srcPortMap: map[int]*FilterChain{
0: {},
0: {InlineRouteConfig: inlineRouteConfig},
},
},
},
@ -1532,7 +1905,7 @@ func TestNewFilterChainImpl_Success_AllCombinations(t *testing.T) {
srcTypeArr: [3]*sourcePrefixes{},
},
},
def: &FilterChain{},
def: &FilterChain{InlineRouteConfig: inlineRouteConfig},
},
},
}
@ -1804,7 +2177,10 @@ func TestLookup_Successes(t *testing.T) {
IsUnspecifiedListener: true,
DestAddr: net.IPv4(10, 1, 1, 1),
},
wantFC: &FilterChain{SecurityCfg: &SecurityConfig{IdentityInstanceName: "default"}},
wantFC: &FilterChain{
SecurityCfg: &SecurityConfig{IdentityInstanceName: "default"},
InlineRouteConfig: inlineRouteConfig,
},
},
{
desc: "unspecified destination match",
@ -1815,7 +2191,10 @@ func TestLookup_Successes(t *testing.T) {
SourceAddr: net.IPv4(10, 1, 1, 1),
SourcePort: 1,
},
wantFC: &FilterChain{SecurityCfg: &SecurityConfig{IdentityInstanceName: "unspecified-dest-and-source-prefix"}},
wantFC: &FilterChain{
SecurityCfg: &SecurityConfig{IdentityInstanceName: "unspecified-dest-and-source-prefix"},
InlineRouteConfig: inlineRouteConfig,
},
},
{
desc: "wildcard destination match v4",
@ -1826,7 +2205,10 @@ func TestLookup_Successes(t *testing.T) {
SourceAddr: net.IPv4(10, 1, 1, 1),
SourcePort: 1,
},
wantFC: &FilterChain{SecurityCfg: &SecurityConfig{IdentityInstanceName: "wildcard-prefixes-v4"}},
wantFC: &FilterChain{
SecurityCfg: &SecurityConfig{IdentityInstanceName: "wildcard-prefixes-v4"},
InlineRouteConfig: inlineRouteConfig,
},
},
{
desc: "wildcard source match v6",
@ -1837,7 +2219,10 @@ func TestLookup_Successes(t *testing.T) {
SourceAddr: net.ParseIP("2001:68::2"),
SourcePort: 1,
},
wantFC: &FilterChain{SecurityCfg: &SecurityConfig{IdentityInstanceName: "wildcard-source-prefix-v6"}},
wantFC: &FilterChain{
SecurityCfg: &SecurityConfig{IdentityInstanceName: "wildcard-source-prefix-v6"},
InlineRouteConfig: inlineRouteConfig,
},
},
{
desc: "specific destination and wildcard source type match",
@ -1848,7 +2233,10 @@ func TestLookup_Successes(t *testing.T) {
SourceAddr: net.IPv4(192, 168, 100, 1),
SourcePort: 80,
},
wantFC: &FilterChain{SecurityCfg: &SecurityConfig{IdentityInstanceName: "specific-destination-prefix-unspecified-source-type"}},
wantFC: &FilterChain{
SecurityCfg: &SecurityConfig{IdentityInstanceName: "specific-destination-prefix-unspecified-source-type"},
InlineRouteConfig: inlineRouteConfig,
},
},
{
desc: "specific destination and source type match",
@ -1859,7 +2247,10 @@ func TestLookup_Successes(t *testing.T) {
SourceAddr: net.IPv4(10, 1, 1, 1),
SourcePort: 80,
},
wantFC: &FilterChain{SecurityCfg: &SecurityConfig{IdentityInstanceName: "specific-destination-prefix-specific-source-type"}},
wantFC: &FilterChain{
SecurityCfg: &SecurityConfig{IdentityInstanceName: "specific-destination-prefix-specific-source-type"},
InlineRouteConfig: inlineRouteConfig,
},
},
{
desc: "specific destination source type and source prefix",
@ -1870,7 +2261,10 @@ func TestLookup_Successes(t *testing.T) {
SourceAddr: net.IPv4(192, 168, 92, 100),
SourcePort: 70,
},
wantFC: &FilterChain{SecurityCfg: &SecurityConfig{IdentityInstanceName: "specific-destination-prefix-specific-source-type-specific-source-prefix"}},
wantFC: &FilterChain{
SecurityCfg: &SecurityConfig{IdentityInstanceName: "specific-destination-prefix-specific-source-type-specific-source-prefix"},
InlineRouteConfig: inlineRouteConfig,
},
},
{
desc: "specific destination source type source prefix and source port",
@ -1881,7 +2275,10 @@ func TestLookup_Successes(t *testing.T) {
SourceAddr: net.IPv4(192, 168, 92, 100),
SourcePort: 80,
},
wantFC: &FilterChain{SecurityCfg: &SecurityConfig{IdentityInstanceName: "specific-destination-prefix-specific-source-type-specific-source-prefix-specific-source-port"}},
wantFC: &FilterChain{
SecurityCfg: &SecurityConfig{IdentityInstanceName: "specific-destination-prefix-specific-source-type-specific-source-prefix-specific-source-port"},
InlineRouteConfig: inlineRouteConfig,
},
},
}
@ -1918,6 +2315,8 @@ func (fci *FilterChainManager) Equal(other *FilterChainManager) bool {
// TODO: Support comparing dstPrefixes slice?
case !cmp.Equal(fci.def, other.def, cmpopts.EquateEmpty(), protocmp.Transform()):
return false
case !cmp.Equal(fci.RouteConfigNames, other.RouteConfigNames, cmpopts.EquateEmpty()):
return false
}
return true
}

View File

@ -493,7 +493,7 @@ func (s) TestUnmarshalListener_ClientSide(t *testing.T) {
InlineRouteConfig: &RouteConfigUpdate{
VirtualHosts: []*VirtualHost{{
Domains: []string{v3LDSTarget},
Routes: []*Route{{Prefix: newStringP("/"), WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}}}},
Routes: []*Route{{Prefix: newStringP("/"), WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}}, RouteAction: RouteActionRoute}},
}}},
MaxStreamDuration: time.Second,
Raw: v3LisWithInlineRoute,
@ -563,11 +563,30 @@ func (s) TestUnmarshalListener_ServerSide(t *testing.T) {
)
var (
routeConfig = &v3routepb.RouteConfiguration{
Name: "routeName",
VirtualHosts: []*v3routepb.VirtualHost{{
Domains: []string{"lds.target.good:3333"},
Routes: []*v3routepb.Route{{
Match: &v3routepb.RouteMatch{
PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"},
},
Action: &v3routepb.Route_NonForwardingAction{},
}}}}}
inlineRouteConfig = &RouteConfigUpdate{
VirtualHosts: []*VirtualHost{{
Domains: []string{"lds.target.good:3333"},
Routes: []*Route{{Prefix: newStringP("/"), RouteAction: RouteActionNonForwardingAction}},
}}}
emptyValidNetworkFilters = []*v3listenerpb.Filter{
{
Name: "filter-1",
ConfigType: &v3listenerpb.Filter_TypedConfig{
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{}),
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
RouteConfig: routeConfig,
},
}),
},
},
}
@ -806,13 +825,21 @@ func (s) TestUnmarshalListener_ServerSide(t *testing.T) {
{
Name: "name",
ConfigType: &v3listenerpb.Filter_TypedConfig{
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{}),
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
RouteConfig: routeConfig,
},
}),
},
},
{
Name: "name",
ConfigType: &v3listenerpb.Filter_TypedConfig{
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{}),
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
RouteConfig: routeConfig,
},
}),
},
},
},
@ -1051,7 +1078,7 @@ func (s) TestUnmarshalListener_ServerSide(t *testing.T) {
srcPrefixMap: map[string]*sourcePrefixEntry{
unspecifiedPrefixMapKey: {
srcPortMap: map[int]*FilterChain{
0: {},
0: {InlineRouteConfig: inlineRouteConfig},
},
},
},
@ -1144,6 +1171,7 @@ func (s) TestUnmarshalListener_ServerSide(t *testing.T) {
IdentityInstanceName: "identityPluginInstance",
IdentityCertName: "identityCertName",
},
InlineRouteConfig: inlineRouteConfig,
},
},
},
@ -1157,6 +1185,7 @@ func (s) TestUnmarshalListener_ServerSide(t *testing.T) {
IdentityInstanceName: "defaultIdentityPluginInstance",
IdentityCertName: "defaultIdentityCertName",
},
InlineRouteConfig: inlineRouteConfig,
},
},
},
@ -1192,6 +1221,7 @@ func (s) TestUnmarshalListener_ServerSide(t *testing.T) {
IdentityCertName: "identityCertName",
RequireClientCert: true,
},
InlineRouteConfig: inlineRouteConfig,
},
},
},
@ -1208,6 +1238,7 @@ func (s) TestUnmarshalListener_ServerSide(t *testing.T) {
IdentityCertName: "defaultIdentityCertName",
RequireClientCert: true,
},
InlineRouteConfig: inlineRouteConfig,
},
},
},

View File

@ -76,6 +76,7 @@ func (s) TestRDSGenerateRDSUpdateFromRouteConfiguration(t *testing.T) {
Routes: []*Route{{
Prefix: newStringP("/"),
WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}},
RouteAction: RouteActionRoute,
}},
HTTPFilterConfigOverride: cfgs,
}},
@ -178,7 +179,10 @@ func (s) TestRDSGenerateRDSUpdateFromRouteConfiguration(t *testing.T) {
VirtualHosts: []*VirtualHost{
{
Domains: []string{ldsTarget},
Routes: []*Route{{Prefix: newStringP("/"), CaseInsensitive: true, WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}}}},
Routes: []*Route{{Prefix: newStringP("/"),
CaseInsensitive: true,
WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}},
RouteAction: RouteActionRoute}},
},
},
},
@ -220,11 +224,15 @@ func (s) TestRDSGenerateRDSUpdateFromRouteConfiguration(t *testing.T) {
VirtualHosts: []*VirtualHost{
{
Domains: []string{uninterestingDomain},
Routes: []*Route{{Prefix: newStringP(""), WeightedClusters: map[string]WeightedCluster{uninterestingClusterName: {Weight: 1}}}},
Routes: []*Route{{Prefix: newStringP(""),
WeightedClusters: map[string]WeightedCluster{uninterestingClusterName: {Weight: 1}},
RouteAction: RouteActionRoute}},
},
{
Domains: []string{ldsTarget},
Routes: []*Route{{Prefix: newStringP(""), WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}}}},
Routes: []*Route{{Prefix: newStringP(""),
WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}},
RouteAction: RouteActionRoute}},
},
},
},
@ -254,7 +262,9 @@ func (s) TestRDSGenerateRDSUpdateFromRouteConfiguration(t *testing.T) {
VirtualHosts: []*VirtualHost{
{
Domains: []string{ldsTarget},
Routes: []*Route{{Prefix: newStringP("/"), WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}}}},
Routes: []*Route{{Prefix: newStringP("/"),
WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}},
RouteAction: RouteActionRoute}},
},
},
},
@ -331,6 +341,7 @@ func (s) TestRDSGenerateRDSUpdateFromRouteConfiguration(t *testing.T) {
"b": {Weight: 3},
"c": {Weight: 5},
},
RouteAction: RouteActionRoute,
}},
},
},
@ -365,6 +376,7 @@ func (s) TestRDSGenerateRDSUpdateFromRouteConfiguration(t *testing.T) {
Prefix: newStringP("/"),
WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}},
MaxStreamDuration: newDurationP(time.Second),
RouteAction: RouteActionRoute,
}},
},
},
@ -399,6 +411,7 @@ func (s) TestRDSGenerateRDSUpdateFromRouteConfiguration(t *testing.T) {
Prefix: newStringP("/"),
WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}},
MaxStreamDuration: newDurationP(time.Second),
RouteAction: RouteActionRoute,
}},
},
},
@ -433,6 +446,7 @@ func (s) TestRDSGenerateRDSUpdateFromRouteConfiguration(t *testing.T) {
Prefix: newStringP("/"),
WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}},
MaxStreamDuration: newDurationP(0),
RouteAction: RouteActionRoute,
}},
},
},
@ -621,11 +635,15 @@ func (s) TestUnmarshalRouteConfig(t *testing.T) {
VirtualHosts: []*VirtualHost{
{
Domains: []string{uninterestingDomain},
Routes: []*Route{{Prefix: newStringP(""), WeightedClusters: map[string]WeightedCluster{uninterestingClusterName: {Weight: 1}}}},
Routes: []*Route{{Prefix: newStringP(""),
WeightedClusters: map[string]WeightedCluster{uninterestingClusterName: {Weight: 1}},
RouteAction: RouteActionRoute}},
},
{
Domains: []string{ldsTarget},
Routes: []*Route{{Prefix: newStringP(""), WeightedClusters: map[string]WeightedCluster{v2ClusterName: {Weight: 1}}}},
Routes: []*Route{{Prefix: newStringP(""),
WeightedClusters: map[string]WeightedCluster{v2ClusterName: {Weight: 1}},
RouteAction: RouteActionRoute}},
},
},
Raw: v2RouteConfig,
@ -644,11 +662,15 @@ func (s) TestUnmarshalRouteConfig(t *testing.T) {
VirtualHosts: []*VirtualHost{
{
Domains: []string{uninterestingDomain},
Routes: []*Route{{Prefix: newStringP(""), WeightedClusters: map[string]WeightedCluster{uninterestingClusterName: {Weight: 1}}}},
Routes: []*Route{{Prefix: newStringP(""),
WeightedClusters: map[string]WeightedCluster{uninterestingClusterName: {Weight: 1}},
RouteAction: RouteActionRoute}},
},
{
Domains: []string{ldsTarget},
Routes: []*Route{{Prefix: newStringP(""), WeightedClusters: map[string]WeightedCluster{v3ClusterName: {Weight: 1}}}},
Routes: []*Route{{Prefix: newStringP(""),
WeightedClusters: map[string]WeightedCluster{v3ClusterName: {Weight: 1}},
RouteAction: RouteActionRoute}},
},
},
Raw: v3RouteConfig,
@ -667,11 +689,15 @@ func (s) TestUnmarshalRouteConfig(t *testing.T) {
VirtualHosts: []*VirtualHost{
{
Domains: []string{uninterestingDomain},
Routes: []*Route{{Prefix: newStringP(""), WeightedClusters: map[string]WeightedCluster{uninterestingClusterName: {Weight: 1}}}},
Routes: []*Route{{Prefix: newStringP(""),
WeightedClusters: map[string]WeightedCluster{uninterestingClusterName: {Weight: 1}},
RouteAction: RouteActionRoute}},
},
{
Domains: []string{ldsTarget},
Routes: []*Route{{Prefix: newStringP(""), WeightedClusters: map[string]WeightedCluster{v3ClusterName: {Weight: 1}}}},
Routes: []*Route{{Prefix: newStringP(""),
WeightedClusters: map[string]WeightedCluster{v3ClusterName: {Weight: 1}},
RouteAction: RouteActionRoute}},
},
},
Raw: v3RouteConfig,
@ -680,11 +706,15 @@ func (s) TestUnmarshalRouteConfig(t *testing.T) {
VirtualHosts: []*VirtualHost{
{
Domains: []string{uninterestingDomain},
Routes: []*Route{{Prefix: newStringP(""), WeightedClusters: map[string]WeightedCluster{uninterestingClusterName: {Weight: 1}}}},
Routes: []*Route{{Prefix: newStringP(""),
WeightedClusters: map[string]WeightedCluster{uninterestingClusterName: {Weight: 1}},
RouteAction: RouteActionRoute}},
},
{
Domains: []string{ldsTarget},
Routes: []*Route{{Prefix: newStringP(""), WeightedClusters: map[string]WeightedCluster{v2ClusterName: {Weight: 1}}}},
Routes: []*Route{{Prefix: newStringP(""),
WeightedClusters: map[string]WeightedCluster{v2ClusterName: {Weight: 1}},
RouteAction: RouteActionRoute}},
},
},
Raw: v2RouteConfig,
@ -714,11 +744,15 @@ func (s) TestUnmarshalRouteConfig(t *testing.T) {
VirtualHosts: []*VirtualHost{
{
Domains: []string{uninterestingDomain},
Routes: []*Route{{Prefix: newStringP(""), WeightedClusters: map[string]WeightedCluster{uninterestingClusterName: {Weight: 1}}}},
Routes: []*Route{{Prefix: newStringP(""),
WeightedClusters: map[string]WeightedCluster{uninterestingClusterName: {Weight: 1}},
RouteAction: RouteActionRoute}},
},
{
Domains: []string{ldsTarget},
Routes: []*Route{{Prefix: newStringP(""), WeightedClusters: map[string]WeightedCluster{v3ClusterName: {Weight: 1}}}},
Routes: []*Route{{Prefix: newStringP(""),
WeightedClusters: map[string]WeightedCluster{v3ClusterName: {Weight: 1}},
RouteAction: RouteActionRoute}},
},
},
Raw: v3RouteConfig,
@ -727,11 +761,15 @@ func (s) TestUnmarshalRouteConfig(t *testing.T) {
VirtualHosts: []*VirtualHost{
{
Domains: []string{uninterestingDomain},
Routes: []*Route{{Prefix: newStringP(""), WeightedClusters: map[string]WeightedCluster{uninterestingClusterName: {Weight: 1}}}},
Routes: []*Route{{Prefix: newStringP(""),
WeightedClusters: map[string]WeightedCluster{uninterestingClusterName: {Weight: 1}},
RouteAction: RouteActionRoute}},
},
{
Domains: []string{ldsTarget},
Routes: []*Route{{Prefix: newStringP(""), WeightedClusters: map[string]WeightedCluster{v2ClusterName: {Weight: 1}}}},
Routes: []*Route{{Prefix: newStringP(""),
WeightedClusters: map[string]WeightedCluster{v2ClusterName: {Weight: 1}},
RouteAction: RouteActionRoute}},
},
},
Raw: v2RouteConfig,
@ -794,6 +832,7 @@ func (s) TestRoutesProtoToSlice(t *testing.T) {
CaseInsensitive: true,
WeightedClusters: map[string]WeightedCluster{"A": {Weight: 40}, "B": {Weight: 60, HTTPFilterConfigOverride: cfgs}},
HTTPFilterConfigOverride: cfgs,
RouteAction: RouteActionRoute,
}}
}
)
@ -833,6 +872,7 @@ func (s) TestRoutesProtoToSlice(t *testing.T) {
Prefix: newStringP("/"),
CaseInsensitive: true,
WeightedClusters: map[string]WeightedCluster{"A": {Weight: 40}, "B": {Weight: 60}},
RouteAction: RouteActionRoute,
}},
},
{
@ -880,6 +920,7 @@ func (s) TestRoutesProtoToSlice(t *testing.T) {
},
Fraction: newUInt32P(10000),
WeightedClusters: map[string]WeightedCluster{"A": {Weight: 40}, "B": {Weight: 60}},
RouteAction: RouteActionRoute,
}},
wantErr: false,
},
@ -925,6 +966,7 @@ func (s) TestRoutesProtoToSlice(t *testing.T) {
},
Fraction: newUInt32P(10000),
WeightedClusters: map[string]WeightedCluster{"A": {Weight: 40}, "B": {Weight: 60}},
RouteAction: RouteActionRoute,
}},
wantErr: false,
},
@ -959,6 +1001,7 @@ func (s) TestRoutesProtoToSlice(t *testing.T) {
wantRoutes: []*Route{{
Prefix: newStringP("/a/"),
WeightedClusters: map[string]WeightedCluster{"A": {Weight: 40}, "B": {Weight: 60}},
RouteAction: RouteActionRoute,
}},
wantErr: false,
},
@ -1126,6 +1169,7 @@ func (s) TestRoutesProtoToSlice(t *testing.T) {
wantRoutes: []*Route{{
Prefix: newStringP("/a/"),
WeightedClusters: map[string]WeightedCluster{"A": {Weight: 40}, "B": {Weight: 60}},
RouteAction: RouteActionRoute,
}},
wantErr: false,
},
@ -1151,6 +1195,7 @@ func (s) TestRoutesProtoToSlice(t *testing.T) {
wantRoutes: []*Route{{
Prefix: newStringP("/a/"),
WeightedClusters: map[string]WeightedCluster{"A": {Weight: 20}, "B": {Weight: 30}},
RouteAction: RouteActionRoute,
}},
wantErr: false,
},
@ -1206,6 +1251,7 @@ func (s) TestRoutesProtoToSlice(t *testing.T) {
HashPolicies: []*HashPolicy{
{HashPolicyType: HashPolicyTypeChannelID},
},
RouteAction: RouteActionRoute,
}},
wantErr: false,
},
@ -1264,6 +1310,7 @@ func (s) TestRoutesProtoToSlice(t *testing.T) {
{HashPolicyType: HashPolicyTypeHeader,
HeaderName: ":path"},
},
RouteAction: RouteActionRoute,
}},
wantErr: false,
},

View File

@ -111,11 +111,16 @@ func (s) TestRDSHandleResponseWithRouting(t *testing.T) {
VirtualHosts: []*xdsclient.VirtualHost{
{
Domains: []string{uninterestingDomain},
Routes: []*xdsclient.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsclient.WeightedCluster{uninterestingClusterName: {Weight: 1}}}},
Routes: []*xdsclient.Route{{Prefix: newStringP(""),
WeightedClusters: map[string]xdsclient.WeightedCluster{uninterestingClusterName: {Weight: 1}},
RouteAction: xdsclient.RouteActionRoute}},
},
{
Domains: []string{goodLDSTarget1},
Routes: []*xdsclient.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsclient.WeightedCluster{goodClusterName2: {Weight: 1}}}},
Routes: []*xdsclient.Route{{
Prefix: newStringP(""),
WeightedClusters: map[string]xdsclient.WeightedCluster{goodClusterName2: {Weight: 1}},
RouteAction: xdsclient.RouteActionRoute}},
},
},
Raw: marshaledGoodRouteConfig2,
@ -136,11 +141,16 @@ func (s) TestRDSHandleResponseWithRouting(t *testing.T) {
VirtualHosts: []*xdsclient.VirtualHost{
{
Domains: []string{uninterestingDomain},
Routes: []*xdsclient.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsclient.WeightedCluster{uninterestingClusterName: {Weight: 1}}}},
Routes: []*xdsclient.Route{{
Prefix: newStringP(""),
WeightedClusters: map[string]xdsclient.WeightedCluster{uninterestingClusterName: {Weight: 1}},
RouteAction: xdsclient.RouteActionRoute}},
},
{
Domains: []string{goodLDSTarget1},
Routes: []*xdsclient.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsclient.WeightedCluster{goodClusterName1: {Weight: 1}}}},
Routes: []*xdsclient.Route{{Prefix: newStringP(""),
WeightedClusters: map[string]xdsclient.WeightedCluster{goodClusterName1: {Weight: 1}},
RouteAction: xdsclient.RouteActionRoute}},
},
},
Raw: marshaledGoodRouteConfig1,

View File

@ -277,65 +277,6 @@ func processServerSideListener(lis *v3listenerpb.Listener) (*ListenerUpdate, err
return lu, nil
}
func processNetworkFilters(filters []*v3listenerpb.Filter) ([]HTTPFilter, error) {
seenNames := make(map[string]bool, len(filters))
seenHCM := false
var httpFilters []HTTPFilter
for _, filter := range filters {
name := filter.GetName()
if name == "" {
return nil, fmt.Errorf("network filters {%+v} is missing name field in filter: {%+v}", filters, filter)
}
if seenNames[name] {
return nil, fmt.Errorf("network filters {%+v} has duplicate filter name %q", filters, name)
}
seenNames[name] = true
// Network filters have a oneof field named `config_type` where we
// only support `TypedConfig` variant.
switch typ := filter.GetConfigType().(type) {
case *v3listenerpb.Filter_TypedConfig:
// The typed_config field has an `anypb.Any` proto which could
// directly contain the serialized bytes of the actual filter
// configuration, or it could be encoded as a `TypedStruct`.
// TODO: Add support for `TypedStruct`.
tc := filter.GetTypedConfig()
// The only network filter that we currently support is the v3
// HttpConnectionManager. So, we can directly check the type_url
// and unmarshal the config.
// TODO: Implement a registry of supported network filters (like
// we have for HTTP filters), when we have to support network
// filters other than HttpConnectionManager.
if tc.GetTypeUrl() != version.V3HTTPConnManagerURL {
return nil, fmt.Errorf("network filters {%+v} has unsupported network filter %q in filter {%+v}", filters, tc.GetTypeUrl(), filter)
}
hcm := &v3httppb.HttpConnectionManager{}
if err := ptypes.UnmarshalAny(tc, hcm); err != nil {
return nil, fmt.Errorf("network filters {%+v} failed unmarshaling of network filter {%+v}: %v", filters, filter, err)
}
// "Any filters after HttpConnectionManager should be ignored during
// connection processing but still be considered for validity.
// HTTPConnectionManager must have valid http_filters." - A36
filters, err := processHTTPFilters(hcm.GetHttpFilters(), true)
if err != nil {
return nil, fmt.Errorf("network filters {%+v} had invalid server side HTTP Filters {%+v}", filters, hcm.GetHttpFilters())
}
if !seenHCM {
// TODO: Implement terminal filter logic, as per A36.
httpFilters = filters
seenHCM = true
}
default:
return nil, fmt.Errorf("network filters {%+v} has unsupported config_type %T in filter %s", filters, typ, filter.GetName())
}
}
if !seenHCM {
return nil, fmt.Errorf("network filters {%+v} missing HttpConnectionManager filter", filters)
}
return httpFilters, nil
}
// UnmarshalRouteConfig processes resources received in an RDS response,
// validates them, and transforms them into a native struct which contains only
// fields we are interested in. The provided hostname determines the route
@ -491,65 +432,74 @@ func routesProtoToSlice(routes []*v3routepb.Route, logger *grpclog.PrefixLogger,
route.Fraction = &n
}
route.WeightedClusters = make(map[string]WeightedCluster)
action := r.GetRoute()
switch r.GetAction().(type) {
case *v3routepb.Route_Route:
route.WeightedClusters = make(map[string]WeightedCluster)
action := r.GetRoute()
// Hash Policies are only applicable for a Ring Hash LB.
if env.RingHashSupport {
hp, err := hashPoliciesProtoToSlice(action.HashPolicy, logger)
if err != nil {
return nil, err
}
route.HashPolicies = hp
}
switch a := action.GetClusterSpecifier().(type) {
case *v3routepb.RouteAction_Cluster:
route.WeightedClusters[a.Cluster] = WeightedCluster{Weight: 1}
case *v3routepb.RouteAction_WeightedClusters:
wcs := a.WeightedClusters
var totalWeight uint32
for _, c := range wcs.Clusters {
w := c.GetWeight().GetValue()
if w == 0 {
continue
// Hash Policies are only applicable for a Ring Hash LB.
if env.RingHashSupport {
hp, err := hashPoliciesProtoToSlice(action.HashPolicy, logger)
if err != nil {
return nil, err
}
wc := WeightedCluster{Weight: w}
if !v2 {
cfgs, err := processHTTPFilterOverrides(c.GetTypedPerFilterConfig())
if err != nil {
return nil, fmt.Errorf("route %+v, action %+v: %v", r, a, err)
route.HashPolicies = hp
}
switch a := action.GetClusterSpecifier().(type) {
case *v3routepb.RouteAction_Cluster:
route.WeightedClusters[a.Cluster] = WeightedCluster{Weight: 1}
case *v3routepb.RouteAction_WeightedClusters:
wcs := a.WeightedClusters
var totalWeight uint32
for _, c := range wcs.Clusters {
w := c.GetWeight().GetValue()
if w == 0 {
continue
}
wc.HTTPFilterConfigOverride = cfgs
wc := WeightedCluster{Weight: w}
if !v2 {
cfgs, err := processHTTPFilterOverrides(c.GetTypedPerFilterConfig())
if err != nil {
return nil, fmt.Errorf("route %+v, action %+v: %v", r, a, err)
}
wc.HTTPFilterConfigOverride = cfgs
}
route.WeightedClusters[c.GetName()] = wc
totalWeight += w
}
route.WeightedClusters[c.GetName()] = wc
totalWeight += w
// envoy xds doc
// default TotalWeight https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto.html#envoy-v3-api-field-config-route-v3-weightedcluster-total-weight
wantTotalWeight := uint32(100)
if tw := wcs.GetTotalWeight(); tw != nil {
wantTotalWeight = tw.GetValue()
}
if totalWeight != wantTotalWeight {
return nil, fmt.Errorf("route %+v, action %+v, weights of clusters do not add up to total total weight, got: %v, expected total weight from response: %v", r, a, totalWeight, wantTotalWeight)
}
if totalWeight == 0 {
return nil, fmt.Errorf("route %+v, action %+v, has no valid cluster in WeightedCluster action", r, a)
}
case *v3routepb.RouteAction_ClusterHeader:
continue
}
// envoy xds doc
// default TotalWeight https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto.html#envoy-v3-api-field-config-route-v3-weightedcluster-total-weight
wantTotalWeight := uint32(100)
if tw := wcs.GetTotalWeight(); tw != nil {
wantTotalWeight = tw.GetValue()
}
if totalWeight != wantTotalWeight {
return nil, fmt.Errorf("route %+v, action %+v, weights of clusters do not add up to total total weight, got: %v, expected total weight from response: %v", r, a, totalWeight, wantTotalWeight)
}
if totalWeight == 0 {
return nil, fmt.Errorf("route %+v, action %+v, has no valid cluster in WeightedCluster action", r, a)
}
case *v3routepb.RouteAction_ClusterHeader:
continue
}
msd := action.GetMaxStreamDuration()
// Prefer grpc_timeout_header_max, if set.
dur := msd.GetGrpcTimeoutHeaderMax()
if dur == nil {
dur = msd.GetMaxStreamDuration()
}
if dur != nil {
d := dur.AsDuration()
route.MaxStreamDuration = &d
msd := action.GetMaxStreamDuration()
// Prefer grpc_timeout_header_max, if set.
dur := msd.GetGrpcTimeoutHeaderMax()
if dur == nil {
dur = msd.GetMaxStreamDuration()
}
if dur != nil {
d := dur.AsDuration()
route.MaxStreamDuration = &d
}
route.RouteAction = RouteActionRoute
case *v3routepb.Route_NonForwardingAction:
// Expected to be used on server side.
route.RouteAction = RouteActionNonForwardingAction
default:
route.RouteAction = RouteActionUnsupported
}
if !v2 {

View File

@ -32,6 +32,7 @@ import (
v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
v3httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
"google.golang.org/grpc"
@ -688,7 +689,20 @@ func (s) TestHandleListenerUpdate_NoXDSCreds(t *testing.T) {
{
Name: "filter-1",
ConfigType: &v3listenerpb.Filter_TypedConfig{
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{}),
TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
RouteConfig: &v3routepb.RouteConfiguration{
Name: "routeName",
VirtualHosts: []*v3routepb.VirtualHost{{
Domains: []string{"lds.target.good:3333"},
Routes: []*v3routepb.Route{{
Match: &v3routepb.RouteMatch{
PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"},
},
Action: &v3routepb.Route_NonForwardingAction{},
}}}}},
},
}),
},
},
},