Skip to content

Commit 29ad76c

Browse files
sauravzgAgraVator
authored andcommitted
Allow injecting bootstrap info into xDS Filter API for config parsing (grpc#12724)
Extend the xDS Filter API to support injecting bootstrap information into filters during configuration parsing. This allows filters to access context information (e.g., allowed gRPC services) from the resource loading layer during configuration validation and parsing. - Update `Filter.Provider.parseFilterConfig` and `parseFilterConfigOverride` to accept a `FilterContext` parameter. - Introduce `BootstrapInfoGrpcServiceContextProvider` to encapsulate bootstrap info for context resolution. - Update `XdsListenerResource` and `XdsRouteConfigureResource` to construct and pass `FilterContext` during configuration parsing. - Update sub-filters (`FaultFilter`, `RbacFilter`, `GcpAuthenticationFilter`, `RouterFilter`) to match the updated `FilterContext` signature. Known Gaps & Limitations: 1. **MetricHolder**: Propagation of `MetricHolder` is not supported with this approach currently and is planned for support in a later phase. 2. **NameResolverRegistry**: Propagation is deferred for consistency. While it could be passed from `XdsNameResolver` on the client side, there is no equivalent mechanism on the server side. To ensure consistent behavior, `DefaultRegistry` is used when validating schemes and creating channels.
1 parent 8b09684 commit 29ad76c

14 files changed

Lines changed: 228 additions & 118 deletions

xds/src/main/java/io/grpc/xds/FaultFilter.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ public FaultFilter newInstance(String name) {
104104
}
105105

106106
@Override
107-
public ConfigOrError<FaultConfig> parseFilterConfig(Message rawProtoMessage, int depth) {
107+
public ConfigOrError<FaultConfig> parseFilterConfig(
108+
Message rawProtoMessage, FilterConfigParseContext context, int depth) {
108109
HTTPFault httpFaultProto;
109110
if (!(rawProtoMessage instanceof Any)) {
110111
return ConfigOrError.fromError("Invalid config type: " + rawProtoMessage.getClass());
@@ -120,8 +121,8 @@ public ConfigOrError<FaultConfig> parseFilterConfig(Message rawProtoMessage, int
120121

121122
@Override
122123
public ConfigOrError<FaultConfig> parseFilterConfigOverride(
123-
Message rawProtoMessage, int depth) {
124-
return parseFilterConfig(rawProtoMessage, depth);
124+
Message rawProtoMessage, FilterConfigParseContext context, int depth) {
125+
return parseFilterConfig(rawProtoMessage, context, depth);
125126
}
126127

127128
private static ConfigOrError<FaultConfig> parseHttpFault(HTTPFault httpFault) {

xds/src/main/java/io/grpc/xds/Filter.java

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,14 @@
1616

1717
package io.grpc.xds;
1818

19+
20+
import com.google.auto.value.AutoValue;
1921
import com.google.common.base.MoreObjects;
2022
import com.google.protobuf.Message;
2123
import io.grpc.ClientInterceptor;
2224
import io.grpc.ServerInterceptor;
25+
import io.grpc.xds.client.Bootstrapper.BootstrapInfo;
26+
import io.grpc.xds.client.Bootstrapper.ServerInfo;
2327
import java.io.Closeable;
2428
import java.util.Objects;
2529
import java.util.concurrent.ScheduledExecutorService;
@@ -93,33 +97,34 @@ default boolean isServerFilter() {
9397
* Parses the top-level filter config from raw proto message. The message may be either a {@link
9498
* com.google.protobuf.Any} or a {@link com.google.protobuf.Struct}.
9599
*/
96-
ConfigOrError<? extends FilterConfig> parseFilterConfig(Message rawProtoMessage, int depth);
100+
ConfigOrError<? extends FilterConfig> parseFilterConfig(
101+
Message rawProtoMessage, FilterConfigParseContext context, int depth);
97102

98103
/**
99104
* Parses the per-filter override filter config from raw proto message. The message may be
100105
* either a {@link com.google.protobuf.Any} or a {@link com.google.protobuf.Struct}.
101106
*/
102107
ConfigOrError<? extends FilterConfig> parseFilterConfigOverride(
103-
Message rawProtoMessage, int depth);
108+
Message rawProtoMessage, FilterConfigParseContext context, int depth);
104109
}
105110

106111
class Parser {
107112
private static final int MAX_RECURSION_DEPTH = 8;
108113

109114
static ConfigOrError<? extends FilterConfig> parseFilterConfig(
110-
Provider provider, Message rawProtoMessage, int depth) {
115+
Provider provider, Message rawProtoMessage, FilterConfigParseContext context, int depth) {
111116
if (depth >= MAX_RECURSION_DEPTH) {
112117
return ConfigOrError.fromError("Maximum recursion depth of 8 exceeded");
113118
}
114-
return provider.parseFilterConfig(rawProtoMessage, depth);
119+
return provider.parseFilterConfig(rawProtoMessage, context, depth);
115120
}
116121

117122
static ConfigOrError<? extends FilterConfig> parseFilterConfigOverride(
118-
Provider provider, Message rawProtoMessage, int depth) {
123+
Provider provider, Message rawProtoMessage, FilterConfigParseContext context, int depth) {
119124
if (depth >= MAX_RECURSION_DEPTH) {
120125
return ConfigOrError.fromError("Maximum recursion depth of 8 exceeded");
121126
}
122-
return provider.parseFilterConfigOverride(rawProtoMessage, depth);
127+
return provider.parseFilterConfigOverride(rawProtoMessage, context, depth);
123128
}
124129
}
125130

@@ -146,6 +151,27 @@ default ServerInterceptor buildServerInterceptor(
146151
@Override
147152
default void close() {}
148153

154+
/** Context carrying dynamic metadata for a filter. */
155+
@AutoValue
156+
abstract static class FilterConfigParseContext {
157+
abstract BootstrapInfo bootstrapInfo();
158+
159+
abstract ServerInfo serverInfo();
160+
161+
static Builder builder() {
162+
return new AutoValue_Filter_FilterConfigParseContext.Builder();
163+
}
164+
165+
@AutoValue.Builder
166+
abstract static class Builder {
167+
abstract Builder bootstrapInfo(BootstrapInfo info);
168+
169+
abstract Builder serverInfo(ServerInfo info);
170+
171+
abstract FilterConfigParseContext build();
172+
}
173+
}
174+
149175
/** Filter config with instance name. */
150176
final class NamedFilterConfig {
151177
// filter instance name

xds/src/main/java/io/grpc/xds/GcpAuthenticationFilter.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public GcpAuthenticationFilter newInstance(String name) {
8787

8888
@Override
8989
public ConfigOrError<GcpAuthenticationConfig> parseFilterConfig(
90-
Message rawProtoMessage, int depth) {
90+
Message rawProtoMessage, FilterConfigParseContext context, int depth) {
9191
GcpAuthnFilterConfig gcpAuthnProto;
9292
if (!(rawProtoMessage instanceof Any)) {
9393
return ConfigOrError.fromError("Invalid config type: " + rawProtoMessage.getClass());
@@ -122,8 +122,8 @@ public ConfigOrError<GcpAuthenticationConfig> parseFilterConfig(
122122

123123
@Override
124124
public ConfigOrError<GcpAuthenticationConfig> parseFilterConfigOverride(
125-
Message rawProtoMessage, int depth) {
126-
return parseFilterConfig(rawProtoMessage, depth);
125+
Message rawProtoMessage, FilterConfigParseContext context, int depth) {
126+
return parseFilterConfig(rawProtoMessage, context, depth);
127127
}
128128
}
129129

xds/src/main/java/io/grpc/xds/RbacFilter.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ public RbacFilter newInstance(String name) {
9494
}
9595

9696
@Override
97-
public ConfigOrError<RbacConfig> parseFilterConfig(Message rawProtoMessage, int depth) {
97+
public ConfigOrError<RbacConfig> parseFilterConfig(
98+
Message rawProtoMessage, FilterConfigParseContext context, int depth) {
9899
RBAC rbacProto;
99100
if (!(rawProtoMessage instanceof Any)) {
100101
return ConfigOrError.fromError("Invalid config type: " + rawProtoMessage.getClass());
@@ -109,7 +110,8 @@ public ConfigOrError<RbacConfig> parseFilterConfig(Message rawProtoMessage, int
109110
}
110111

111112
@Override
112-
public ConfigOrError<RbacConfig> parseFilterConfigOverride(Message rawProtoMessage, int depth) {
113+
public ConfigOrError<RbacConfig> parseFilterConfigOverride(
114+
Message rawProtoMessage, FilterConfigParseContext context, int depth) {
113115
RBACPerRoute rbacPerRoute;
114116
if (!(rawProtoMessage instanceof Any)) {
115117
return ConfigOrError.fromError("Invalid config type: " + rawProtoMessage.getClass());

xds/src/main/java/io/grpc/xds/RouterFilter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,13 @@ public RouterFilter newInstance(String name) {
6262

6363
@Override
6464
public ConfigOrError<? extends FilterConfig> parseFilterConfig(
65-
Message rawProtoMessage, int depth) {
65+
Message rawProtoMessage, FilterConfigParseContext context, int depth) {
6666
return ConfigOrError.fromConfig(ROUTER_CONFIG);
6767
}
6868

6969
@Override
7070
public ConfigOrError<? extends FilterConfig> parseFilterConfigOverride(
71-
Message rawProtoMessage, int depth) {
71+
Message rawProtoMessage, FilterConfigParseContext context, int depth) {
7272
return ConfigOrError.fromError("Router Filter should not have override config");
7373
}
7474
}

xds/src/main/java/io/grpc/xds/XdsListenerResource.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ static io.grpc.xds.HttpConnectionManager parseHttpConnectionManager(
527527
"HttpConnectionManager contains duplicate HttpFilter: " + filterName);
528528
}
529529
StructOrError<Filter.FilterConfig> filterConfig =
530-
parseHttpFilter(httpFilter, filterRegistry, isForClient);
530+
parseHttpFilter(httpFilter, filterRegistry, isForClient, args);
531531
if ((i == proto.getHttpFiltersCount() - 1)
532532
&& (filterConfig == null || !isTerminalFilter(filterConfig.getStruct()))) {
533533
throw new ResourceInvalidException("The last HttpFilter must be a terminal filter: "
@@ -581,7 +581,8 @@ private static boolean isTerminalFilter(Filter.FilterConfig filterConfig) {
581581
@Nullable // Returns null if the filter is optional but not supported.
582582
static StructOrError<Filter.FilterConfig> parseHttpFilter(
583583
io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter
584-
httpFilter, FilterRegistry filterRegistry, boolean isForClient) {
584+
httpFilter, FilterRegistry filterRegistry, boolean isForClient,
585+
XdsResourceType.Args args) {
585586
String filterName = httpFilter.getName();
586587
boolean isOptional = httpFilter.getIsOptional();
587588
if (!httpFilter.hasTypedConfig()) {
@@ -616,8 +617,12 @@ static StructOrError<Filter.FilterConfig> parseHttpFilter(
616617
"HttpFilter [" + filterName + "](" + typeUrl + ") is required but unsupported for " + (
617618
isForClient ? "client" : "server"));
618619
}
620+
Filter.FilterConfigParseContext filterContext = Filter.FilterConfigParseContext.builder()
621+
.bootstrapInfo(args.getBootstrapInfo())
622+
.serverInfo(args.getServerInfo())
623+
.build();
619624
ConfigOrError<? extends FilterConfig> filterConfig =
620-
Filter.Parser.parseFilterConfig(provider, rawConfig, 0);
625+
Filter.Parser.parseFilterConfig(provider, rawConfig, filterContext, 0);
621626
if (filterConfig.errorDetail != null) {
622627
return StructOrError.fromError(
623628
"Invalid filter config for HttpFilter [" + filterName + "]: " + filterConfig.errorDetail);

xds/src/main/java/io/grpc/xds/XdsRouteConfigureResource.java

Lines changed: 18 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
import io.envoyproxy.envoy.config.route.v3.ClusterSpecifierPlugin;
3838
import io.envoyproxy.envoy.config.route.v3.RetryPolicy.RetryBackOff;
3939
import io.envoyproxy.envoy.config.route.v3.RouteConfiguration;
40-
import io.envoyproxy.envoy.type.v3.FractionalPercent;
4140
import io.grpc.Status;
4241
import io.grpc.internal.GrpcUtil;
4342
import io.grpc.xds.ClusterSpecifierPlugin.NamedPluginConfig;
@@ -199,7 +198,7 @@ private static StructOrError<VirtualHost> parseVirtualHost(
199198
routes.add(route.getStruct());
200199
}
201200
StructOrError<Map<String, Filter.FilterConfig>> overrideConfigs =
202-
parseOverrideFilterConfigs(proto.getTypedPerFilterConfigMap(), filterRegistry);
201+
parseOverrideFilterConfigs(proto.getTypedPerFilterConfigMap(), filterRegistry, args);
203202
if (overrideConfigs.getErrorDetail() != null) {
204203
return StructOrError.fromError(
205204
"VirtualHost [" + proto.getName() + "] contains invalid HttpFilter config: "
@@ -211,7 +210,12 @@ private static StructOrError<VirtualHost> parseVirtualHost(
211210

212211
@VisibleForTesting
213212
static StructOrError<Map<String, FilterConfig>> parseOverrideFilterConfigs(
214-
Map<String, Any> rawFilterConfigMap, FilterRegistry filterRegistry) {
213+
Map<String, Any> rawFilterConfigMap, FilterRegistry filterRegistry,
214+
XdsResourceType.Args args) {
215+
Filter.FilterConfigParseContext context = Filter.FilterConfigParseContext.builder()
216+
.bootstrapInfo(args.getBootstrapInfo())
217+
.serverInfo(args.getServerInfo())
218+
.build();
215219
Map<String, FilterConfig> overrideConfigs = new HashMap<>();
216220
for (String name : rawFilterConfigMap.keySet()) {
217221
Any anyConfig = rawFilterConfigMap.get(name);
@@ -255,7 +259,7 @@ static StructOrError<Map<String, FilterConfig>> parseOverrideFilterConfigs(
255259
"HttpFilter [" + name + "](" + typeUrl + ") is required but unsupported");
256260
}
257261
ConfigOrError<? extends Filter.FilterConfig> filterConfig =
258-
Filter.Parser.parseFilterConfigOverride(provider, rawConfig, 0);
262+
Filter.Parser.parseFilterConfigOverride(provider, rawConfig, context, 0);
259263
if (filterConfig.errorDetail != null) {
260264
return StructOrError.fromError(
261265
"Invalid filter config for HttpFilter [" + name + "]: " + filterConfig.errorDetail);
@@ -282,7 +286,7 @@ static StructOrError<Route> parseRoute(
282286
}
283287

284288
StructOrError<Map<String, FilterConfig>> overrideConfigsOrError =
285-
parseOverrideFilterConfigs(proto.getTypedPerFilterConfigMap(), filterRegistry);
289+
parseOverrideFilterConfigs(proto.getTypedPerFilterConfigMap(), filterRegistry, args);
286290
if (overrideConfigsOrError.getErrorDetail() != null) {
287291
return StructOrError.fromError(
288292
"Route [" + proto.getName() + "] contains invalid HttpFilter config: "
@@ -338,12 +342,12 @@ static StructOrError<RouteMatch> parseRouteMatch(
338342

339343
FractionMatcher fractionMatch = null;
340344
if (proto.hasRuntimeFraction()) {
341-
StructOrError<FractionMatcher> parsedFraction =
342-
parseFractionMatcher(proto.getRuntimeFraction().getDefaultValue());
343-
if (parsedFraction.getErrorDetail() != null) {
344-
return StructOrError.fromError(parsedFraction.getErrorDetail());
345+
try {
346+
fractionMatch =
347+
MatcherParser.parseFractionMatcher(proto.getRuntimeFraction().getDefaultValue());
348+
} catch (IllegalArgumentException e) {
349+
return StructOrError.fromError(e.getMessage());
345350
}
346-
fractionMatch = parsedFraction.getStruct();
347351
}
348352

349353
List<HeaderMatcher> headerMatchers = new ArrayList<>();
@@ -384,26 +388,7 @@ static StructOrError<PathMatcher> parsePathMatcher(
384388
}
385389
}
386390

387-
private static StructOrError<FractionMatcher> parseFractionMatcher(FractionalPercent proto) {
388-
int numerator = proto.getNumerator();
389-
int denominator = 0;
390-
switch (proto.getDenominator()) {
391-
case HUNDRED:
392-
denominator = 100;
393-
break;
394-
case TEN_THOUSAND:
395-
denominator = 10_000;
396-
break;
397-
case MILLION:
398-
denominator = 1_000_000;
399-
break;
400-
case UNRECOGNIZED:
401-
default:
402-
return StructOrError.fromError(
403-
"Unrecognized fractional percent denominator: " + proto.getDenominator());
404-
}
405-
return StructOrError.fromStruct(FractionMatcher.create(numerator, denominator));
406-
}
391+
407392

408393
@VisibleForTesting
409394
static StructOrError<HeaderMatcher> parseHeaderMatcher(
@@ -497,7 +482,7 @@ static StructOrError<RouteAction> parseRouteAction(
497482
for (io.envoyproxy.envoy.config.route.v3.WeightedCluster.ClusterWeight clusterWeight
498483
: clusterWeights) {
499484
StructOrError<ClusterWeight> clusterWeightOrError =
500-
parseClusterWeight(clusterWeight, filterRegistry);
485+
parseClusterWeight(clusterWeight, filterRegistry, args);
501486
if (clusterWeightOrError.getErrorDetail() != null) {
502487
return StructOrError.fromError("RouteAction contains invalid ClusterWeight: "
503488
+ clusterWeightOrError.getErrorDetail());
@@ -606,9 +591,9 @@ private static StructOrError<VirtualHost.Route.RouteAction.RetryPolicy> parseRet
606591
@VisibleForTesting
607592
static StructOrError<VirtualHost.Route.RouteAction.ClusterWeight> parseClusterWeight(
608593
io.envoyproxy.envoy.config.route.v3.WeightedCluster.ClusterWeight proto,
609-
FilterRegistry filterRegistry) {
594+
FilterRegistry filterRegistry, XdsResourceType.Args args) {
610595
StructOrError<Map<String, Filter.FilterConfig>> overrideConfigs =
611-
parseOverrideFilterConfigs(proto.getTypedPerFilterConfigMap(), filterRegistry);
596+
parseOverrideFilterConfigs(proto.getTypedPerFilterConfigMap(), filterRegistry, args);
612597
if (overrideConfigs.getErrorDetail() != null) {
613598
return StructOrError.fromError(
614599
"ClusterWeight [" + proto.getName() + "] contains invalid HttpFilter config: "

0 commit comments

Comments
 (0)