Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions xds/src/main/java/io/grpc/xds/FaultFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ public FaultFilter newInstance(String name) {
}

@Override
public ConfigOrError<FaultConfig> parseFilterConfig(Message rawProtoMessage) {
public ConfigOrError<FaultConfig> parseFilterConfig(
Message rawProtoMessage, FilterContext context) {
HTTPFault httpFaultProto;
if (!(rawProtoMessage instanceof Any)) {
return ConfigOrError.fromError("Invalid config type: " + rawProtoMessage.getClass());
Expand All @@ -119,8 +120,9 @@ public ConfigOrError<FaultConfig> parseFilterConfig(Message rawProtoMessage) {
}

@Override
public ConfigOrError<FaultConfig> parseFilterConfigOverride(Message rawProtoMessage) {
return parseFilterConfig(rawProtoMessage);
public ConfigOrError<FaultConfig> parseFilterConfigOverride(
Message rawProtoMessage, FilterContext context) {
return parseFilterConfig(rawProtoMessage, context);
}

private static ConfigOrError<FaultConfig> parseHttpFault(HTTPFault httpFault) {
Expand Down
31 changes: 29 additions & 2 deletions xds/src/main/java/io/grpc/xds/Filter.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@

package io.grpc.xds;


import com.google.auto.value.AutoValue;
import com.google.common.base.MoreObjects;
import com.google.protobuf.Message;
import io.grpc.ClientInterceptor;
import io.grpc.ServerInterceptor;
import io.grpc.xds.client.Bootstrapper.BootstrapInfo;
import io.grpc.xds.client.Bootstrapper.ServerInfo;
import java.io.Closeable;
import java.util.Objects;
import java.util.concurrent.ScheduledExecutorService;
Expand Down Expand Up @@ -93,13 +97,15 @@ default boolean isServerFilter() {
* Parses the top-level filter config from raw proto message. The message may be either a {@link
* com.google.protobuf.Any} or a {@link com.google.protobuf.Struct}.
*/
ConfigOrError<? extends FilterConfig> parseFilterConfig(Message rawProtoMessage);
ConfigOrError<? extends FilterConfig> parseFilterConfig(
Message rawProtoMessage, FilterContext context);
Comment thread
kannanjgithub marked this conversation as resolved.
Outdated

/**
* Parses the per-filter override filter config from raw proto message. The message may be
* either a {@link com.google.protobuf.Any} or a {@link com.google.protobuf.Struct}.
*/
ConfigOrError<? extends FilterConfig> parseFilterConfigOverride(Message rawProtoMessage);
ConfigOrError<? extends FilterConfig> parseFilterConfigOverride(
Message rawProtoMessage, FilterContext context);
}

/** Uses the FilterConfigs produced above to produce an HTTP filter interceptor for clients. */
Expand All @@ -125,6 +131,27 @@ default ServerInterceptor buildServerInterceptor(
@Override
default void close() {}

/** Context carrying dynamic metadata for a filter. */
@AutoValue
abstract static class FilterContext {
abstract BootstrapInfo bootstrapInfo();

abstract ServerInfo serverInfo();

static Builder builder() {
return new AutoValue_Filter_FilterContext.Builder();
}

@AutoValue.Builder
abstract static class Builder {
abstract Builder bootstrapInfo(BootstrapInfo info);

abstract Builder serverInfo(ServerInfo info);

abstract FilterContext build();
}
}

/** Filter config with instance name. */
final class NamedFilterConfig {
// filter instance name
Expand Down
7 changes: 4 additions & 3 deletions xds/src/main/java/io/grpc/xds/GcpAuthenticationFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ public GcpAuthenticationFilter newInstance(String name) {
}

@Override
public ConfigOrError<GcpAuthenticationConfig> parseFilterConfig(Message rawProtoMessage) {
public ConfigOrError<GcpAuthenticationConfig> parseFilterConfig(
Message rawProtoMessage, FilterContext context) {
GcpAuthnFilterConfig gcpAuthnProto;
if (!(rawProtoMessage instanceof Any)) {
return ConfigOrError.fromError("Invalid config type: " + rawProtoMessage.getClass());
Expand Down Expand Up @@ -121,8 +122,8 @@ public ConfigOrError<GcpAuthenticationConfig> parseFilterConfig(Message rawProto

@Override
public ConfigOrError<GcpAuthenticationConfig> parseFilterConfigOverride(
Message rawProtoMessage) {
return parseFilterConfig(rawProtoMessage);
Message rawProtoMessage, FilterContext context) {
return parseFilterConfig(rawProtoMessage, context);
}
}

Expand Down
6 changes: 4 additions & 2 deletions xds/src/main/java/io/grpc/xds/RbacFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ public RbacFilter newInstance(String name) {
}

@Override
public ConfigOrError<RbacConfig> parseFilterConfig(Message rawProtoMessage) {
public ConfigOrError<RbacConfig> parseFilterConfig(
Message rawProtoMessage, FilterContext context) {
RBAC rbacProto;
if (!(rawProtoMessage instanceof Any)) {
return ConfigOrError.fromError("Invalid config type: " + rawProtoMessage.getClass());
Expand All @@ -109,7 +110,8 @@ public ConfigOrError<RbacConfig> parseFilterConfig(Message rawProtoMessage) {
}

@Override
public ConfigOrError<RbacConfig> parseFilterConfigOverride(Message rawProtoMessage) {
public ConfigOrError<RbacConfig> parseFilterConfigOverride(
Message rawProtoMessage, FilterContext context) {
RBACPerRoute rbacPerRoute;
if (!(rawProtoMessage instanceof Any)) {
return ConfigOrError.fromError("Invalid config type: " + rawProtoMessage.getClass());
Expand Down
5 changes: 3 additions & 2 deletions xds/src/main/java/io/grpc/xds/RouterFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,14 @@ public RouterFilter newInstance(String name) {
}

@Override
public ConfigOrError<? extends FilterConfig> parseFilterConfig(Message rawProtoMessage) {
public ConfigOrError<? extends FilterConfig> parseFilterConfig(
Message rawProtoMessage, FilterContext context) {
return ConfigOrError.fromConfig(ROUTER_CONFIG);
}

@Override
public ConfigOrError<? extends FilterConfig> parseFilterConfigOverride(
Message rawProtoMessage) {
Message rawProtoMessage, FilterContext context) {
return ConfigOrError.fromError("Router Filter should not have override config");
}
}
Expand Down
13 changes: 10 additions & 3 deletions xds/src/main/java/io/grpc/xds/XdsListenerResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ static io.grpc.xds.HttpConnectionManager parseHttpConnectionManager(
"HttpConnectionManager contains duplicate HttpFilter: " + filterName);
}
StructOrError<Filter.FilterConfig> filterConfig =
parseHttpFilter(httpFilter, filterRegistry, isForClient);
parseHttpFilter(httpFilter, filterRegistry, isForClient, args);
if ((i == proto.getHttpFiltersCount() - 1)
&& (filterConfig == null || !isTerminalFilter(filterConfig.getStruct()))) {
throw new ResourceInvalidException("The last HttpFilter must be a terminal filter: "
Expand Down Expand Up @@ -581,7 +581,8 @@ private static boolean isTerminalFilter(Filter.FilterConfig filterConfig) {
@Nullable // Returns null if the filter is optional but not supported.
static StructOrError<Filter.FilterConfig> parseHttpFilter(
io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter
httpFilter, FilterRegistry filterRegistry, boolean isForClient) {
httpFilter, FilterRegistry filterRegistry, boolean isForClient,
XdsResourceType.Args args) {
String filterName = httpFilter.getName();
boolean isOptional = httpFilter.getIsOptional();
if (!httpFilter.hasTypedConfig()) {
Expand Down Expand Up @@ -616,7 +617,13 @@ static StructOrError<Filter.FilterConfig> parseHttpFilter(
"HttpFilter [" + filterName + "](" + typeUrl + ") is required but unsupported for " + (
isForClient ? "client" : "server"));
}
ConfigOrError<? extends FilterConfig> filterConfig = provider.parseFilterConfig(rawConfig);

Filter.FilterContext filterContext = Filter.FilterContext.builder()
.bootstrapInfo(args.getBootstrapInfo())
.serverInfo(args.getServerInfo())
.build();
ConfigOrError<? extends FilterConfig> filterConfig =
provider.parseFilterConfig(rawConfig, filterContext);
if (filterConfig.errorDetail != null) {
return StructOrError.fromError(
"Invalid filter config for HttpFilter [" + filterName + "]: " + filterConfig.errorDetail);
Expand Down
51 changes: 18 additions & 33 deletions xds/src/main/java/io/grpc/xds/XdsRouteConfigureResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import io.envoyproxy.envoy.config.route.v3.ClusterSpecifierPlugin;
import io.envoyproxy.envoy.config.route.v3.RetryPolicy.RetryBackOff;
import io.envoyproxy.envoy.config.route.v3.RouteConfiguration;
import io.envoyproxy.envoy.type.v3.FractionalPercent;
import io.grpc.Status;
import io.grpc.internal.GrpcUtil;
import io.grpc.xds.ClusterSpecifierPlugin.NamedPluginConfig;
Expand Down Expand Up @@ -198,7 +197,7 @@ private static StructOrError<VirtualHost> parseVirtualHost(
routes.add(route.getStruct());
}
StructOrError<Map<String, Filter.FilterConfig>> overrideConfigs =
parseOverrideFilterConfigs(proto.getTypedPerFilterConfigMap(), filterRegistry);
parseOverrideFilterConfigs(proto.getTypedPerFilterConfigMap(), filterRegistry, args);
if (overrideConfigs.getErrorDetail() != null) {
return StructOrError.fromError(
"VirtualHost [" + proto.getName() + "] contains invalid HttpFilter config: "
Expand All @@ -210,7 +209,12 @@ private static StructOrError<VirtualHost> parseVirtualHost(

@VisibleForTesting
static StructOrError<Map<String, FilterConfig>> parseOverrideFilterConfigs(
Map<String, Any> rawFilterConfigMap, FilterRegistry filterRegistry) {
Map<String, Any> rawFilterConfigMap, FilterRegistry filterRegistry,
XdsResourceType.Args args) {
Filter.FilterContext context = Filter.FilterContext.builder()
.bootstrapInfo(args.getBootstrapInfo())
.serverInfo(args.getServerInfo())
.build();
Map<String, FilterConfig> overrideConfigs = new HashMap<>();
for (String name : rawFilterConfigMap.keySet()) {
Any anyConfig = rawFilterConfigMap.get(name);
Expand Down Expand Up @@ -254,7 +258,7 @@ static StructOrError<Map<String, FilterConfig>> parseOverrideFilterConfigs(
"HttpFilter [" + name + "](" + typeUrl + ") is required but unsupported");
}
ConfigOrError<? extends Filter.FilterConfig> filterConfig =
provider.parseFilterConfigOverride(rawConfig);
provider.parseFilterConfigOverride(rawConfig, context);
if (filterConfig.errorDetail != null) {
return StructOrError.fromError(
"Invalid filter config for HttpFilter [" + name + "]: " + filterConfig.errorDetail);
Expand All @@ -281,7 +285,7 @@ static StructOrError<Route> parseRoute(
}

StructOrError<Map<String, FilterConfig>> overrideConfigsOrError =
parseOverrideFilterConfigs(proto.getTypedPerFilterConfigMap(), filterRegistry);
parseOverrideFilterConfigs(proto.getTypedPerFilterConfigMap(), filterRegistry, args);
if (overrideConfigsOrError.getErrorDetail() != null) {
return StructOrError.fromError(
"Route [" + proto.getName() + "] contains invalid HttpFilter config: "
Expand Down Expand Up @@ -331,12 +335,12 @@ static StructOrError<RouteMatch> parseRouteMatch(

FractionMatcher fractionMatch = null;
if (proto.hasRuntimeFraction()) {
StructOrError<FractionMatcher> parsedFraction =
parseFractionMatcher(proto.getRuntimeFraction().getDefaultValue());
if (parsedFraction.getErrorDetail() != null) {
return StructOrError.fromError(parsedFraction.getErrorDetail());
try {
fractionMatch =
MatcherParser.parseFractionMatcher(proto.getRuntimeFraction().getDefaultValue());
} catch (IllegalArgumentException e) {
return StructOrError.fromError(e.getMessage());
}
fractionMatch = parsedFraction.getStruct();
}

List<HeaderMatcher> headerMatchers = new ArrayList<>();
Expand Down Expand Up @@ -377,26 +381,7 @@ static StructOrError<PathMatcher> parsePathMatcher(
}
}

private static StructOrError<FractionMatcher> parseFractionMatcher(FractionalPercent proto) {
int numerator = proto.getNumerator();
int denominator = 0;
switch (proto.getDenominator()) {
case HUNDRED:
denominator = 100;
break;
case TEN_THOUSAND:
denominator = 10_000;
break;
case MILLION:
denominator = 1_000_000;
break;
case UNRECOGNIZED:
default:
return StructOrError.fromError(
"Unrecognized fractional percent denominator: " + proto.getDenominator());
}
return StructOrError.fromStruct(FractionMatcher.create(numerator, denominator));
}


@VisibleForTesting
static StructOrError<HeaderMatcher> parseHeaderMatcher(
Expand Down Expand Up @@ -490,7 +475,7 @@ static StructOrError<RouteAction> parseRouteAction(
for (io.envoyproxy.envoy.config.route.v3.WeightedCluster.ClusterWeight clusterWeight
: clusterWeights) {
StructOrError<ClusterWeight> clusterWeightOrError =
parseClusterWeight(clusterWeight, filterRegistry);
parseClusterWeight(clusterWeight, filterRegistry, args);
if (clusterWeightOrError.getErrorDetail() != null) {
return StructOrError.fromError("RouteAction contains invalid ClusterWeight: "
+ clusterWeightOrError.getErrorDetail());
Expand Down Expand Up @@ -599,9 +584,9 @@ private static StructOrError<VirtualHost.Route.RouteAction.RetryPolicy> parseRet
@VisibleForTesting
static StructOrError<VirtualHost.Route.RouteAction.ClusterWeight> parseClusterWeight(
io.envoyproxy.envoy.config.route.v3.WeightedCluster.ClusterWeight proto,
FilterRegistry filterRegistry) {
FilterRegistry filterRegistry, XdsResourceType.Args args) {
StructOrError<Map<String, Filter.FilterConfig>> overrideConfigs =
parseOverrideFilterConfigs(proto.getTypedPerFilterConfigMap(), filterRegistry);
parseOverrideFilterConfigs(proto.getTypedPerFilterConfigMap(), filterRegistry, args);
if (overrideConfigs.getErrorDetail() != null) {
return StructOrError.fromError(
"ClusterWeight [" + proto.getName() + "] contains invalid HttpFilter config: "
Expand Down
26 changes: 24 additions & 2 deletions xds/src/test/java/io/grpc/xds/FaultFilterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
import io.envoyproxy.envoy.type.v3.FractionalPercent.DenominatorType;
import io.grpc.Status.Code;
import io.grpc.internal.GrpcUtil;
import io.grpc.xds.client.Bootstrapper.BootstrapInfo;
import io.grpc.xds.client.Bootstrapper.ServerInfo;
import io.grpc.xds.client.EnvoyProtoData.Node;
import java.util.Collections;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
Expand All @@ -45,11 +49,16 @@ public void filterType_clientOnly() {
public void parseFaultAbort_convertHttpStatus() {
Any rawConfig = Any.pack(
HTTPFault.newBuilder().setAbort(FaultAbort.newBuilder().setHttpStatus(404)).build());
FaultConfig faultConfig = FILTER_PROVIDER.parseFilterConfig(rawConfig).config;
FaultConfig faultConfig = FILTER_PROVIDER.parseFilterConfig(
rawConfig, getFilterContext()).config;
assertThat(faultConfig.faultAbort()).isNotNull();
assertThat(faultConfig.faultAbort().status().getCode())
.isEqualTo(GrpcUtil.httpStatusToGrpcStatus(404).getCode());

FaultConfig faultConfigOverride = FILTER_PROVIDER.parseFilterConfigOverride(rawConfig).config;
FaultConfig faultConfigOverride =
FILTER_PROVIDER.parseFilterConfigOverride(
rawConfig, getFilterContext()).config;
assertThat(faultConfigOverride.faultAbort()).isNotNull();
assertThat(faultConfigOverride.faultAbort().status().getCode())
.isEqualTo(GrpcUtil.httpStatusToGrpcStatus(404).getCode());
}
Expand Down Expand Up @@ -95,4 +104,17 @@ public void parseFaultAbort_withGrpcStatus() {
.isEqualTo(FaultConfig.FractionalPercent.DenominatorType.MILLION);
assertThat(faultAbort.status().getCode()).isEqualTo(Code.DEADLINE_EXCEEDED);
}

private static Filter.FilterContext getFilterContext() {
return Filter.FilterContext.builder()
.bootstrapInfo(BootstrapInfo.builder()
.servers(Collections.singletonList(
ServerInfo.create(
"test_target", Collections.emptyMap())))
.node(Node.newBuilder().build())
.build())
.serverInfo(ServerInfo.create(
"test_target", Collections.emptyMap(), false, true, false, false))
.build();
}
}
Loading
Loading