Skip to content
Open
Show file tree
Hide file tree
Changes from all 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);

/**
* 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