Skip to content

Commit f3e94ec

Browse files
authored
xds: improve error message when matched route on client is not of type RouteActionRoute (#6248)
1 parent bb41067 commit f3e94ec

File tree

3 files changed

+95
-51
lines changed

3 files changed

+95
-51
lines changed

xds/internal/httpfilter/fault/fault_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ import (
5353
testgrpc "google.golang.org/grpc/interop/grpc_testing"
5454
testpb "google.golang.org/grpc/interop/grpc_testing"
5555

56-
_ "google.golang.org/grpc/xds/internal/balancer" // Register the balancers.
57-
_ "google.golang.org/grpc/xds/internal/resolver" // Register the xds_resolver.
56+
_ "google.golang.org/grpc/xds/internal/balancer" // Register the balancers.
57+
_ "google.golang.org/grpc/xds/internal/httpfilter/router" // Register the router filter.
58+
_ "google.golang.org/grpc/xds/internal/resolver" // Register the xds_resolver.
5859
)
5960

6061
const defaultTestTimeout = 10 * time.Second

xds/internal/resolver/serviceconfig.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ import (
3939
"google.golang.org/grpc/xds/internal/balancer/clustermanager"
4040
"google.golang.org/grpc/xds/internal/balancer/ringhash"
4141
"google.golang.org/grpc/xds/internal/httpfilter"
42-
"google.golang.org/grpc/xds/internal/httpfilter/router"
4342
"google.golang.org/grpc/xds/internal/xdsclient/xdsresource"
4443
)
4544

@@ -121,6 +120,7 @@ type routeCluster struct {
121120

122121
type route struct {
123122
m *xdsresource.CompositeMatcher // converted from route matchers
123+
actionType xdsresource.RouteActionType // holds route action type
124124
clusters wrr.WRR // holds *routeCluster entries
125125
maxStreamDuration time.Duration
126126
// map from filter name to its config
@@ -142,6 +142,7 @@ type configSelector struct {
142142
}
143143

144144
var errNoMatchedRouteFound = status.Errorf(codes.Unavailable, "no matched route was found")
145+
var errUnsupportedClientRouteAction = status.Errorf(codes.Unavailable, "matched route does not have a supported route action type")
145146

146147
func (cs *configSelector) SelectConfig(rpcInfo iresolver.RPCInfo) (*iresolver.RPCConfig, error) {
147148
if cs == nil {
@@ -155,10 +156,15 @@ func (cs *configSelector) SelectConfig(rpcInfo iresolver.RPCInfo) (*iresolver.RP
155156
break
156157
}
157158
}
159+
158160
if rt == nil || rt.clusters == nil {
159161
return nil, errNoMatchedRouteFound
160162
}
161163

164+
if rt.actionType != xdsresource.RouteActionRoute {
165+
return nil, errUnsupportedClientRouteAction
166+
}
167+
162168
cluster, ok := rt.clusters.Next().(*routeCluster)
163169
if !ok {
164170
return nil, status.Errorf(codes.Internal, "error retrieving cluster for match: %v (%T)", cluster, cluster)
@@ -280,11 +286,6 @@ func (cs *configSelector) newInterceptor(rt *route, cluster *routeCluster) (ires
280286
}
281287
interceptors := make([]iresolver.ClientInterceptor, 0, len(cs.httpFilterConfig))
282288
for _, filter := range cs.httpFilterConfig {
283-
if router.IsRouterFilter(filter.Filter) {
284-
// Ignore any filters after the router filter. The router itself
285-
// is currently a nop.
286-
return &interceptorList{interceptors: interceptors}, nil
287-
}
288289
override := cluster.httpFilterConfigOverride[filter.Name] // cluster is highest priority
289290
if override == nil {
290291
override = rt.httpFilterConfigOverride[filter.Name] // route is second priority
@@ -305,7 +306,7 @@ func (cs *configSelector) newInterceptor(rt *route, cluster *routeCluster) (ires
305306
interceptors = append(interceptors, i)
306307
}
307308
}
308-
return nil, fmt.Errorf("error in xds config: no router filter present")
309+
return &interceptorList{interceptors: interceptors}, nil
309310
}
310311

311312
// stop decrements refs of all clusters referenced by this config selector.
@@ -381,6 +382,7 @@ func (r *xdsResolver) newConfigSelector(su serviceUpdate) (*configSelector, erro
381382
if err != nil {
382383
return nil, err
383384
}
385+
cs.routes[i].actionType = rt.ActionType
384386
if rt.MaxStreamDuration == nil {
385387
cs.routes[i].maxStreamDuration = su.ldsConfig.maxStreamDuration
386388
} else {

xds/internal/resolver/xds_resolver_test.go

Lines changed: 83 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,13 @@ const (
7979
defaultTestShortTimeout = 100 * time.Microsecond
8080
)
8181

82-
var target = resolver.Target{URL: *testutils.MustParseURL("xds:///" + targetStr)}
82+
var (
83+
target = resolver.Target{URL: *testutils.MustParseURL("xds:///" + targetStr)}
8384

84-
var routerFilter = xdsresource.HTTPFilter{Name: "rtr", Filter: httpfilter.Get(router.TypeURL)}
85+
routerHTTPFilter = httpfilter.Get(router.TypeURL)
86+
routerConfig, _ = routerHTTPFilter.ParseFilterConfig(testutils.MarshalAny(&v3routerpb.Router{}))
87+
routerFilter = xdsresource.HTTPFilter{Name: "rtr", Filter: routerHTTPFilter, Config: routerConfig}
88+
)
8589

8690
type s struct {
8791
grpctest.Tester
@@ -1802,42 +1806,65 @@ func (s) TestXDSResolverHTTPFilters(t *testing.T) {
18021806
testCases := []struct {
18031807
name string
18041808
ldsFilters []xdsresource.HTTPFilter
1805-
vhOverrides map[string]httpfilter.FilterConfig
1806-
rtOverrides map[string]httpfilter.FilterConfig
1807-
clOverrides map[string]httpfilter.FilterConfig
1809+
rtCfgUpdate xdsresource.RouteConfigUpdate
18081810
rpcRes map[string][][]string
18091811
selectErr string
18101812
newStreamErr string
18111813
}{
1814+
18121815
{
1813-
name: "no router filter",
1816+
name: "route type RouteActionUnsupported invalid for client",
18141817
ldsFilters: []xdsresource.HTTPFilter{
18151818
{Name: "foo", Filter: &filterBuilder{path: &path}, Config: filterCfg{s: "foo1"}},
18161819
},
1820+
rtCfgUpdate: xdsresource.RouteConfigUpdate{
1821+
VirtualHosts: []*xdsresource.VirtualHost{
1822+
{
1823+
Domains: []string{targetStr},
1824+
Routes: []*xdsresource.Route{{
1825+
Prefix: newStringP("1"),
1826+
WeightedClusters: map[string]xdsresource.WeightedCluster{
1827+
"A": {Weight: 1},
1828+
"B": {Weight: 1},
1829+
},
1830+
ActionType: xdsresource.RouteActionUnsupported,
1831+
}},
1832+
},
1833+
},
1834+
},
18171835
rpcRes: map[string][][]string{
18181836
"1": {
18191837
{"build:foo1", "override:foo2", "build:bar1", "override:bar2", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
18201838
},
18211839
},
1822-
selectErr: "no router filter present",
1840+
selectErr: errUnsupportedClientRouteAction.Error(),
18231841
},
18241842
{
1825-
name: "ignored after router filter",
1843+
name: "route type RouteActionNonForwardingAction invalid for client",
18261844
ldsFilters: []xdsresource.HTTPFilter{
18271845
{Name: "foo", Filter: &filterBuilder{path: &path}, Config: filterCfg{s: "foo1"}},
1828-
routerFilter,
1829-
{Name: "foo2", Filter: &filterBuilder{path: &path}, Config: filterCfg{s: "foo2"}},
1846+
},
1847+
rtCfgUpdate: xdsresource.RouteConfigUpdate{
1848+
VirtualHosts: []*xdsresource.VirtualHost{
1849+
{
1850+
Domains: []string{targetStr},
1851+
Routes: []*xdsresource.Route{{
1852+
Prefix: newStringP("1"),
1853+
WeightedClusters: map[string]xdsresource.WeightedCluster{
1854+
"A": {Weight: 1},
1855+
"B": {Weight: 1},
1856+
},
1857+
ActionType: xdsresource.RouteActionNonForwardingAction,
1858+
}},
1859+
},
1860+
},
18301861
},
18311862
rpcRes: map[string][][]string{
18321863
"1": {
1833-
{"build:foo1", "newstream:foo1", "done:foo1"},
1834-
},
1835-
"2": {
1836-
{"build:foo1", "newstream:foo1", "done:foo1"},
1837-
{"build:foo1", "newstream:foo1", "done:foo1"},
1838-
{"build:foo1", "newstream:foo1", "done:foo1"},
1864+
{"build:foo1", "override:foo2", "build:bar1", "override:bar2", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
18391865
},
18401866
},
1867+
selectErr: errUnsupportedClientRouteAction.Error(),
18411868
},
18421869
{
18431870
name: "NewStream error; ensure earlier interceptor Done is still called",
@@ -1846,13 +1873,25 @@ func (s) TestXDSResolverHTTPFilters(t *testing.T) {
18461873
{Name: "bar", Filter: &filterBuilder{path: &path}, Config: filterCfg{s: "bar1", newStreamErr: errors.New("bar newstream err")}},
18471874
routerFilter,
18481875
},
1876+
rtCfgUpdate: xdsresource.RouteConfigUpdate{
1877+
VirtualHosts: []*xdsresource.VirtualHost{
1878+
{
1879+
Domains: []string{targetStr},
1880+
Routes: []*xdsresource.Route{{
1881+
Prefix: newStringP("1"),
1882+
WeightedClusters: map[string]xdsresource.WeightedCluster{
1883+
"A": {Weight: 1},
1884+
"B": {Weight: 1},
1885+
},
1886+
ActionType: xdsresource.RouteActionRoute,
1887+
}},
1888+
},
1889+
},
1890+
},
18491891
rpcRes: map[string][][]string{
18501892
"1": {
18511893
{"build:foo1", "build:bar1", "newstream:foo1", "newstream:bar1" /* <err in bar1 NewStream> */, "done:foo1"},
18521894
},
1853-
"2": {
1854-
{"build:foo1", "build:bar1", "newstream:foo1", "newstream:bar1" /* <err in bar1 NewSteam> */, "done:foo1"},
1855-
},
18561895
},
18571896
newStreamErr: "bar newstream err",
18581897
},
@@ -1863,9 +1902,30 @@ func (s) TestXDSResolverHTTPFilters(t *testing.T) {
18631902
{Name: "bar", Filter: &filterBuilder{path: &path}, Config: filterCfg{s: "bar1"}},
18641903
routerFilter,
18651904
},
1866-
vhOverrides: map[string]httpfilter.FilterConfig{"foo": filterCfg{s: "foo2"}, "bar": filterCfg{s: "bar2"}},
1867-
rtOverrides: map[string]httpfilter.FilterConfig{"foo": filterCfg{s: "foo3"}, "bar": filterCfg{s: "bar3"}},
1868-
clOverrides: map[string]httpfilter.FilterConfig{"foo": filterCfg{s: "foo4"}, "bar": filterCfg{s: "bar4"}},
1905+
rtCfgUpdate: xdsresource.RouteConfigUpdate{
1906+
VirtualHosts: []*xdsresource.VirtualHost{
1907+
{
1908+
Domains: []string{targetStr},
1909+
Routes: []*xdsresource.Route{{
1910+
Prefix: newStringP("1"),
1911+
WeightedClusters: map[string]xdsresource.WeightedCluster{
1912+
"A": {Weight: 1},
1913+
"B": {Weight: 1},
1914+
},
1915+
ActionType: xdsresource.RouteActionRoute,
1916+
}, {
1917+
Prefix: newStringP("2"),
1918+
WeightedClusters: map[string]xdsresource.WeightedCluster{
1919+
"A": {Weight: 1},
1920+
"B": {Weight: 1, HTTPFilterConfigOverride: map[string]httpfilter.FilterConfig{"foo": filterCfg{s: "foo4"}, "bar": filterCfg{s: "bar4"}}},
1921+
},
1922+
HTTPFilterConfigOverride: map[string]httpfilter.FilterConfig{"foo": filterCfg{s: "foo3"}, "bar": filterCfg{s: "bar3"}},
1923+
ActionType: xdsresource.RouteActionRoute,
1924+
}},
1925+
HTTPFilterConfigOverride: map[string]httpfilter.FilterConfig{"foo": filterCfg{s: "foo2"}, "bar": filterCfg{s: "bar2"}},
1926+
},
1927+
},
1928+
},
18691929
rpcRes: map[string][][]string{
18701930
"1": {
18711931
{"build:foo1", "override:foo2", "build:bar1", "override:bar2", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
@@ -1904,26 +1964,7 @@ func (s) TestXDSResolverHTTPFilters(t *testing.T) {
19041964

19051965
// Invoke the watchAPI callback with a good service update and wait for the
19061966
// UpdateState method to be called on the ClientConn.
1907-
xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
1908-
VirtualHosts: []*xdsresource.VirtualHost{
1909-
{
1910-
Domains: []string{targetStr},
1911-
Routes: []*xdsresource.Route{{
1912-
Prefix: newStringP("1"), WeightedClusters: map[string]xdsresource.WeightedCluster{
1913-
"A": {Weight: 1},
1914-
"B": {Weight: 1},
1915-
},
1916-
}, {
1917-
Prefix: newStringP("2"), WeightedClusters: map[string]xdsresource.WeightedCluster{
1918-
"A": {Weight: 1},
1919-
"B": {Weight: 1, HTTPFilterConfigOverride: tc.clOverrides},
1920-
},
1921-
HTTPFilterConfigOverride: tc.rtOverrides,
1922-
}},
1923-
HTTPFilterConfigOverride: tc.vhOverrides,
1924-
},
1925-
},
1926-
}, nil)
1967+
xdsC.InvokeWatchRouteConfigCallback("", tc.rtCfgUpdate, nil)
19271968

19281969
gotState, err := tcc.stateCh.Receive(ctx)
19291970
if err != nil {

0 commit comments

Comments
 (0)