From 9a8bc10f51df62106556a0f58d1142f4b955ce57 Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Fri, 18 Jun 2021 11:57:36 -0700 Subject: [PATCH] xds: unify client and server handling HttpConnectionManager (#8228) Enables parsing HttpConnectionManager filter for the server side TCP listener, with the same codepath for handling it on the client side. Major changes include: - Remodeled LdsUpdate with HttpConnectionManager. Now LdsUpdate is an oneof of HttpConnectionManager (for client side) or Listener (for server side). Each of Listener's FiliterChain contains an HttpConnectionManager (required). Refactored code for validating and parsing the TCP Listener (for server side), put it into ClientXdsClient. The common part of validating/parsing HttpConnectionManager is reused/shared for client side. - Included the name of FilterChain in the parsed form. As specified by the API, each FilterChain has a unique name. If the name is not provided by the control plane, a UUID is used. FilterChain names can be used for bookkeeping a set of FilterChain easily (e.g., used as map key). - Added methods isSupportedOnClients() and isSupportedOnServers() to the Filter interface. Parsing the top-level HttpFilter requires knowing if the HttpFilter implementation is supported for the target usage (client-side or server-side). Note, parsing override HttpFilter configs does not need to know whether the config is used for an HttpFilter that is only supported for the client-side or server side. - Added a new kind of Route: Route with non-forwarding action. Updated the XdsNameResolver being able to handle Route with non-forwarding action: if such a Route is matched to an RPC, that RPC is failed. Note, it is possible that XdsNameResolver receives xDS updates with all Routes with non-forwarding action. That is, the service config will not reference any cluster. Such case can be handled by cluster_manager LB policy's LB config parser: the parser returns the error to Channel and the Channel will handle it as error service config. --- .../java/io/grpc/xds/ClientXdsClient.java | 481 +++++++++---- .../io/grpc/xds/EnvoyServerProtoData.java | 282 ++------ .../io/grpc/xds/HttpConnectionManager.java | 68 ++ .../main/java/io/grpc/xds/RouterFilter.java | 11 +- .../main/java/io/grpc/xds/VirtualHost.java | 15 +- xds/src/main/java/io/grpc/xds/XdsClient.java | 95 +-- .../xds/XdsClientWrapperForServerSds.java | 2 +- .../java/io/grpc/xds/XdsNameResolver.java | 27 +- .../io/grpc/xds/ClientXdsClientDataTest.java | 633 +++++++++++------- .../io/grpc/xds/ClientXdsClientTestBase.java | 215 ++++-- .../io/grpc/xds/ClientXdsClientV2Test.java | 15 +- .../io/grpc/xds/ClientXdsClientV3Test.java | 54 +- ...lusterManagerLoadBalancerProviderTest.java | 25 +- .../io/grpc/xds/EnvoyServerProtoDataTest.java | 185 ----- .../java/io/grpc/xds/FaultFilterTest.java | 29 + .../io/grpc/xds/FilterChainMatchTest.java | 542 ++++++++------- .../XdsClientWrapperForServerSdsTestMisc.java | 3 +- .../java/io/grpc/xds/XdsNameResolverTest.java | 112 +++- .../io/grpc/xds/XdsSdsClientServerTest.java | 13 +- .../java/io/grpc/xds/XdsServerTestHelper.java | 21 +- 20 files changed, 1483 insertions(+), 1345 deletions(-) create mode 100644 xds/src/main/java/io/grpc/xds/HttpConnectionManager.java delete mode 100644 xds/src/test/java/io/grpc/xds/EnvoyServerProtoDataTest.java diff --git a/xds/src/main/java/io/grpc/xds/ClientXdsClient.java b/xds/src/main/java/io/grpc/xds/ClientXdsClient.java index c037d6c416..b14d340471 100644 --- a/xds/src/main/java/io/grpc/xds/ClientXdsClient.java +++ b/xds/src/main/java/io/grpc/xds/ClientXdsClient.java @@ -18,7 +18,6 @@ package io.grpc.xds; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import static io.grpc.xds.EnvoyServerProtoData.TRANSPORT_SOCKET_NAME_TLS; import com.github.udpa.udpa.type.v1.TypedStruct; import com.google.common.annotations.VisibleForTesting; @@ -43,11 +42,13 @@ import io.envoyproxy.envoy.config.core.v3.HttpProtocolOptions; import io.envoyproxy.envoy.config.core.v3.RoutingPriority; import io.envoyproxy.envoy.config.core.v3.SocketAddress; import io.envoyproxy.envoy.config.core.v3.SocketAddress.PortSpecifierCase; +import io.envoyproxy.envoy.config.core.v3.TrafficDirection; import io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment; import io.envoyproxy.envoy.config.listener.v3.Listener; import io.envoyproxy.envoy.config.route.v3.RouteConfiguration; import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager; import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.Rds; +import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext; import io.envoyproxy.envoy.type.v3.FractionalPercent; import io.envoyproxy.envoy.type.v3.FractionalPercent.DenominatorType; import io.grpc.Context; @@ -60,10 +61,16 @@ import io.grpc.internal.TimeProvider; import io.grpc.xds.Endpoints.DropOverload; import io.grpc.xds.Endpoints.LbEndpoint; import io.grpc.xds.Endpoints.LocalityLbEndpoints; +import io.grpc.xds.EnvoyServerProtoData.CidrRange; +import io.grpc.xds.EnvoyServerProtoData.ConnectionSourceType; +import io.grpc.xds.EnvoyServerProtoData.FilterChain; +import io.grpc.xds.EnvoyServerProtoData.FilterChainMatch; import io.grpc.xds.EnvoyServerProtoData.UpstreamTlsContext; +import io.grpc.xds.Filter.ClientInterceptorBuilder; import io.grpc.xds.Filter.ConfigOrError; import io.grpc.xds.Filter.FilterConfig; import io.grpc.xds.Filter.NamedFilterConfig; +import io.grpc.xds.Filter.ServerInterceptorBuilder; import io.grpc.xds.LoadStatsManager2.ClusterDropStats; import io.grpc.xds.LoadStatsManager2.ClusterLocalityStats; import io.grpc.xds.VirtualHost.Route; @@ -76,8 +83,10 @@ import io.grpc.xds.XdsLogger.XdsLogLevel; import io.grpc.xds.internal.Matchers.FractionMatcher; import io.grpc.xds.internal.Matchers.HeaderMatcher; import java.net.InetSocketAddress; +import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -85,6 +94,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.UUID; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; @@ -97,6 +107,7 @@ final class ClientXdsClient extends AbstractXdsClient { // Longest time to wait, since the subscription to some resource, for concluding its absence. @VisibleForTesting static final int INITIAL_RESOURCE_FETCH_TIMEOUT_SEC = 15; + private static final String TRANSPORT_SOCKET_NAME_TLS = "envoy.transport_sockets.tls"; @VisibleForTesting static final long DEFAULT_RING_HASH_LB_POLICY_MIN_RING_SIZE = 1024L; @VisibleForTesting @@ -131,6 +142,7 @@ final class ClientXdsClient extends AbstractXdsClient { private static final String TYPE_URL_FILTER_CONFIG = "type.googleapis.com/envoy.config.route.v3.FilterConfig"; + private final FilterRegistry filterRegistry = FilterRegistry.getDefaultRegistry(); private final Map ldsResourceSubscribers = new HashMap<>(); private final Map rdsResourceSubscribers = new HashMap<>(); private final Map cdsResourceSubscribers = new HashMap<>(); @@ -182,9 +194,11 @@ final class ClientXdsClient extends AbstractXdsClient { LdsUpdate ldsUpdate; try { if (listener.hasApiListener()) { - ldsUpdate = processClientSideListener(listener, enableFaultInjection && isResourceV3); + ldsUpdate = processClientSideListener( + listener, retainedRdsResources, enableFaultInjection && isResourceV3); } else { - ldsUpdate = processServerSideListener(listener); + ldsUpdate = processServerSideListener( + listener, retainedRdsResources, enableFaultInjection && isResourceV3); } } catch (ResourceInvalidException e) { errors.add( @@ -194,9 +208,6 @@ final class ClientXdsClient extends AbstractXdsClient { // LdsUpdate parsed successfully. parsedResources.put(listenerName, new ParsedResource(ldsUpdate, resource)); - if (ldsUpdate.rdsName != null) { - retainedRdsResources.add(ldsUpdate.rdsName); - } } getLogger().log(XdsLogLevel.INFO, "Received LDS Response version {0} nonce {1}. Parsed resources: {2}", @@ -216,7 +227,8 @@ final class ClientXdsClient extends AbstractXdsClient { } } - private static LdsUpdate processClientSideListener(Listener listener, boolean parseFilter) + private LdsUpdate processClientSideListener( + Listener listener, Set rdsResources, boolean parseHttpFilter) throws ResourceInvalidException { // Unpack HttpConnectionManager from the Listener. HttpConnectionManager hcm; @@ -228,86 +240,262 @@ final class ClientXdsClient extends AbstractXdsClient { throw new ResourceInvalidException( "Could not parse HttpConnectionManager config from ApiListener", e); } + return LdsUpdate.forApiListener(parseHttpConnectionManager( + hcm, rdsResources, filterRegistry, parseHttpFilter, true /* isForClient */)); + } + private LdsUpdate processServerSideListener( + Listener proto, Set rdsResources, boolean parseHttpFilter) + throws ResourceInvalidException { + return LdsUpdate.forTcpListener(parseServerSideListener( + proto, rdsResources, tlsContextManager, filterRegistry, parseHttpFilter)); + } + + @VisibleForTesting + static EnvoyServerProtoData.Listener parseServerSideListener( + Listener proto, Set rdsResources, TlsContextManager tlsContextManager, + FilterRegistry filterRegistry, boolean parseHttpFilter) throws ResourceInvalidException { + if (!proto.getTrafficDirection().equals(TrafficDirection.INBOUND)) { + throw new ResourceInvalidException( + "Listener " + proto.getName() + " with invalid traffic direction: " + + proto.getTrafficDirection()); + } + if (!proto.getListenerFiltersList().isEmpty()) { + throw new ResourceInvalidException( + "Listener " + proto.getName() + " cannot have listener_filters"); + } + if (proto.hasUseOriginalDst()) { + throw new ResourceInvalidException( + "Listener " + proto.getName() + " cannot have use_original_dst set to true"); + } + + String address = null; + if (proto.getAddress().hasSocketAddress()) { + SocketAddress socketAddress = proto.getAddress().getSocketAddress(); + address = socketAddress.getAddress(); + switch (socketAddress.getPortSpecifierCase()) { + case NAMED_PORT: + address = address + ":" + socketAddress.getNamedPort(); + break; + case PORT_VALUE: + address = address + ":" + socketAddress.getPortValue(); + break; + default: + // noop + } + } + + List filterChains = new ArrayList<>(); + for (io.envoyproxy.envoy.config.listener.v3.FilterChain fc : proto.getFilterChainsList()) { + filterChains.add( + parseFilterChain(fc, rdsResources, tlsContextManager, filterRegistry, parseHttpFilter)); + } + FilterChain defaultFilterChain = null; + if (proto.hasDefaultFilterChain()) { + defaultFilterChain = parseFilterChain( + proto.getDefaultFilterChain(), rdsResources, tlsContextManager, filterRegistry, + parseHttpFilter); + } + + return new EnvoyServerProtoData.Listener( + proto.getName(), address, Collections.unmodifiableList(filterChains), defaultFilterChain); + } + + @VisibleForTesting + static FilterChain parseFilterChain( + io.envoyproxy.envoy.config.listener.v3.FilterChain proto, Set rdsResources, + TlsContextManager tlsContextManager, FilterRegistry filterRegistry, boolean parseHttpFilters) + throws ResourceInvalidException { + io.grpc.xds.HttpConnectionManager httpConnectionManager = null; + HashSet uniqueNames = new HashSet<>(); + for (io.envoyproxy.envoy.config.listener.v3.Filter filter : proto.getFiltersList()) { + if (!uniqueNames.add(filter.getName())) { + throw new ResourceInvalidException( + "FilterChain " + proto.getName() + " with duplicated filter: " + filter.getName()); + } + if (!filter.hasTypedConfig()) { + throw new ResourceInvalidException( + "FilterChain " + proto.getName() + " contains filter " + filter.getName() + + " without typed_config"); + } + Any any = filter.getTypedConfig(); + // HttpConnectionManager is the only supported network filter at the moment. + if (!any.getTypeUrl().equals(TYPE_URL_HTTP_CONNECTION_MANAGER)) { + throw new ResourceInvalidException( + "FilterChain " + proto.getName() + " contains filter " + filter.getName() + + " with unsupported typed_config type " + any.getTypeUrl()); + } + if (httpConnectionManager == null) { + HttpConnectionManager hcmProto; + try { + hcmProto = any.unpack(HttpConnectionManager.class); + } catch (InvalidProtocolBufferException e) { + throw new ResourceInvalidException("FilterChain " + proto.getName() + " with filter " + + filter.getName() + " failed to unpack message", e); + } + httpConnectionManager = parseHttpConnectionManager( + hcmProto, rdsResources, filterRegistry, parseHttpFilters, false /* isForClient */); + } + } + if (httpConnectionManager == null) { + throw new ResourceInvalidException("FilterChain " + proto.getName() + + " missing required HttpConnectionManager filter"); + } + + EnvoyServerProtoData.DownstreamTlsContext downstreamTlsContext = null; + if (TRANSPORT_SOCKET_NAME_TLS.equals(proto.getTransportSocket().getName())) { + DownstreamTlsContext downstreamTlsContextProto; + try { + downstreamTlsContextProto = + proto.getTransportSocket().getTypedConfig().unpack(DownstreamTlsContext.class); + } catch (InvalidProtocolBufferException e) { + throw new ResourceInvalidException("FilterChain " + proto.getName() + + " failed to unpack message", e); + } + downstreamTlsContext = + EnvoyServerProtoData.DownstreamTlsContext.fromEnvoyProtoDownstreamTlsContext( + downstreamTlsContextProto); + } + + String name = proto.getName(); + if (name.isEmpty()) { + name = UUID.randomUUID().toString(); + } + return new FilterChain( + name, + parseFilterChainMatch(proto.getFilterChainMatch()), + httpConnectionManager, + downstreamTlsContext, + tlsContextManager + ); + } + + private static FilterChainMatch parseFilterChainMatch( + io.envoyproxy.envoy.config.listener.v3.FilterChainMatch proto) + throws ResourceInvalidException { + List prefixRanges = new ArrayList<>(); + List sourcePrefixRanges = new ArrayList<>(); + try { + for (io.envoyproxy.envoy.config.core.v3.CidrRange range : proto.getPrefixRangesList()) { + prefixRanges.add(new CidrRange(range.getAddressPrefix(), range.getPrefixLen().getValue())); + } + for (io.envoyproxy.envoy.config.core.v3.CidrRange range + : proto.getSourcePrefixRangesList()) { + sourcePrefixRanges.add( + new CidrRange(range.getAddressPrefix(), range.getPrefixLen().getValue())); + } + } catch (UnknownHostException e) { + throw new ResourceInvalidException("Failed to create CidrRange", e); + } + ConnectionSourceType sourceType; + switch (proto.getSourceType()) { + case ANY: + sourceType = ConnectionSourceType.ANY; + break; + case EXTERNAL: + sourceType = ConnectionSourceType.EXTERNAL; + break; + case SAME_IP_OR_LOOPBACK: + sourceType = ConnectionSourceType.SAME_IP_OR_LOOPBACK; + break; + default: + throw new ResourceInvalidException("Unknown source-type: " + proto.getSourceType()); + } + return new FilterChainMatch( + proto.getDestinationPort().getValue(), + prefixRanges, + proto.getApplicationProtocolsList(), + sourcePrefixRanges, + sourceType, + proto.getSourcePortsList(), + proto.getServerNamesList(), + proto.getTransportProtocol()); + } + + @VisibleForTesting + static io.grpc.xds.HttpConnectionManager parseHttpConnectionManager( + HttpConnectionManager proto, Set rdsResources, FilterRegistry filterRegistry, + boolean parseHttpFilter, boolean isForClient) throws ResourceInvalidException { + if (proto.getXffNumTrustedHops() != 0) { + throw new ResourceInvalidException( + "HttpConnectionManager with xff_num_trusted_hops unsupported"); + } // Obtain max_stream_duration from Http Protocol Options. long maxStreamDuration = 0; - if (hcm.hasCommonHttpProtocolOptions()) { - HttpProtocolOptions options = hcm.getCommonHttpProtocolOptions(); + if (proto.hasCommonHttpProtocolOptions()) { + HttpProtocolOptions options = proto.getCommonHttpProtocolOptions(); if (options.hasMaxStreamDuration()) { maxStreamDuration = Durations.toNanos(options.getMaxStreamDuration()); } } - // Parse filters. - List filterChain = null; - if (parseFilter) { - filterChain = new ArrayList<>(); - List - httpFilters = hcm.getHttpFiltersList(); + // Parse http filters. + List filterConfigs = null; + if (parseHttpFilter) { + filterConfigs = new ArrayList<>(); + Set names = new HashSet<>(); for (io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter - httpFilter : httpFilters) { + httpFilter : proto.getHttpFiltersList()) { String filterName = httpFilter.getName(); - StructOrError filterConfig = parseHttpFilter(httpFilter); + if (!names.add(filterName)) { + throw new ResourceInvalidException( + "HttpConnectionManager contains duplicate HttpFilter: " + filterName); + } + StructOrError filterConfig = + parseHttpFilter(httpFilter, filterRegistry, isForClient); if (filterConfig == null) { continue; } if (filterConfig.getErrorDetail() != null) { throw new ResourceInvalidException( - "HttpConnectionManager contains invalid HttpFault filter: " + "HttpConnectionManager contains invalid HttpFilter: " + filterConfig.getErrorDetail()); } - filterChain.add(new NamedFilterConfig(filterName, filterConfig.struct)); + filterConfigs.add(new NamedFilterConfig(filterName, filterConfig.struct)); } } - // Parse RDS info. - if (hcm.hasRouteConfig()) { - // Found inlined route_config. Parse it to find the cluster_name. + // Parse inlined RouteConfiguration or RDS. + if (proto.hasRouteConfig()) { List virtualHosts = new ArrayList<>(); for (io.envoyproxy.envoy.config.route.v3.VirtualHost virtualHostProto - : hcm.getRouteConfig().getVirtualHostsList()) { - StructOrError virtualHost = parseVirtualHost(virtualHostProto, parseFilter); + : proto.getRouteConfig().getVirtualHostsList()) { + StructOrError virtualHost = + parseVirtualHost(virtualHostProto, filterRegistry, parseHttpFilter); if (virtualHost.getErrorDetail() != null) { - throw new ResourceInvalidException("HttpConnectionManager contains invalid virtual host: " - + virtualHost.getErrorDetail()); + throw new ResourceInvalidException( + "HttpConnectionManager contains invalid virtual host: " + + virtualHost.getErrorDetail()); } virtualHosts.add(virtualHost.getStruct()); } - return new LdsUpdate(maxStreamDuration, virtualHosts, filterChain); + return io.grpc.xds.HttpConnectionManager.forVirtualHosts( + maxStreamDuration, virtualHosts, filterConfigs); } - - if (hcm.hasRds()) { - // Found RDS. - Rds rds = hcm.getRds(); + if (proto.hasRds()) { + Rds rds = proto.getRds(); if (!rds.hasConfigSource()) { - throw new ResourceInvalidException("HttpConnectionManager missing config_source for RDS."); + throw new ResourceInvalidException( + "HttpConnectionManager contains invalid RDS: missing config_source"); } if (!rds.getConfigSource().hasAds()) { throw new ResourceInvalidException( - "HttpConnectionManager ConfigSource for RDS does not specify ADS."); + "HttpConnectionManager contains invalid RDS: must specify ADS"); } - return new LdsUpdate(maxStreamDuration, rds.getRouteConfigName(), filterChain); + // Collect the RDS resource referenced by this HttpConnectionManager. + rdsResources.add(rds.getRouteConfigName()); + return io.grpc.xds.HttpConnectionManager.forRdsName( + maxStreamDuration, rds.getRouteConfigName(), filterConfigs); } - throw new ResourceInvalidException( - "HttpConnectionManager neither has inlined route_config nor RDS."); - } - - private LdsUpdate processServerSideListener(Listener listener) - throws ResourceInvalidException { - StructOrError convertedListener = - parseServerSideListener(listener, tlsContextManager); - if (convertedListener.getErrorDetail() != null) { - throw new ResourceInvalidException(convertedListener.getErrorDetail()); - } - return new LdsUpdate(convertedListener.getStruct()); + "HttpConnectionManager neither has inlined route_config nor RDS"); } @VisibleForTesting @Nullable // Returns null if the filter is optional but not supported. static StructOrError parseHttpFilter( io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter - httpFilter) { + httpFilter, FilterRegistry filterRegistry, boolean isForClient) { String filterName = httpFilter.getName(); boolean isOptional = httpFilter.getIsOptional(); if (!httpFilter.hasTypedConfig()) { @@ -318,36 +506,12 @@ final class ClientXdsClient extends AbstractXdsClient { "HttpFilter [" + filterName + "] is not optional and has no typed config"); } } - return parseRawFilterConfig(filterName, httpFilter.getTypedConfig(), isOptional, false); - } - - @Nullable // Returns null if the filter should be ignored. - private static StructOrError parseRawFilterConfig( - String filterName, Any anyConfig, Boolean isOptional, boolean isOverrideConfig) { - checkArgument( - isOptional != null || isOverrideConfig, "isOptional can't be null for top-level config"); - String typeUrl = anyConfig.getTypeUrl(); - if (isOverrideConfig) { - isOptional = false; - if (typeUrl.equals(TYPE_URL_FILTER_CONFIG)) { - io.envoyproxy.envoy.config.route.v3.FilterConfig filterConfig; - try { - filterConfig = - anyConfig.unpack(io.envoyproxy.envoy.config.route.v3.FilterConfig.class); - } catch (InvalidProtocolBufferException e) { - return StructOrError.fromError( - "HttpFilter [" + filterName + "] contains invalid proto: " + e); - } - isOptional = filterConfig.getIsOptional(); - anyConfig = filterConfig.getConfig(); - typeUrl = anyConfig.getTypeUrl(); - } - } - Message rawConfig = anyConfig; - if (anyConfig.getTypeUrl().equals(TYPE_URL_TYPED_STRUCT)) { + Message rawConfig = httpFilter.getTypedConfig(); + String typeUrl = httpFilter.getTypedConfig().getTypeUrl(); + if (typeUrl.equals(TYPE_URL_TYPED_STRUCT)) { TypedStruct typedStruct; try { - typedStruct = anyConfig.unpack(TypedStruct.class); + typedStruct = httpFilter.getTypedConfig().unpack(TypedStruct.class); } catch (InvalidProtocolBufferException e) { return StructOrError.fromError( "HttpFilter [" + filterName + "] contains invalid proto: " + e); @@ -355,18 +519,18 @@ final class ClientXdsClient extends AbstractXdsClient { typeUrl = typedStruct.getTypeUrl(); rawConfig = typedStruct.getValue(); } - Filter filter = FilterRegistry.getDefaultRegistry().get(typeUrl); - if (filter == null) { + Filter filter = filterRegistry.get(typeUrl); + if ((isForClient && !(filter instanceof ClientInterceptorBuilder)) + || (!isForClient && !(filter instanceof ServerInterceptorBuilder))) { if (isOptional) { return null; } else { return StructOrError.fromError( - "HttpFilter [" + filterName + "] is not optional and has an unsupported config type: " - + typeUrl); + "HttpFilter [" + filterName + "](" + typeUrl + ") is required but unsupported for " + + (isForClient ? "client" : "server")); } } - ConfigOrError filterConfig = isOverrideConfig - ? filter.parseFilterConfigOverride(rawConfig) : filter.parseFilterConfig(rawConfig); + ConfigOrError filterConfig = filter.parseFilterConfig(rawConfig); if (filterConfig.errorDetail != null) { return StructOrError.fromError( "Invalid filter config for HttpFilter [" + filterName + "]: " + filterConfig.errorDetail); @@ -374,25 +538,13 @@ final class ClientXdsClient extends AbstractXdsClient { return StructOrError.fromStruct(filterConfig.config); } - @VisibleForTesting static StructOrError parseServerSideListener( - Listener listener, TlsContextManager tlsContextManager) { - try { - return StructOrError.fromStruct( - EnvoyServerProtoData.Listener.fromEnvoyProtoListener(listener, tlsContextManager)); - } catch (InvalidProtocolBufferException e) { - return StructOrError.fromError( - "Failed to unpack Listener " + listener.getName() + ":" + e.getMessage()); - } catch (IllegalArgumentException e) { - return StructOrError.fromError(e.getMessage()); - } - } - private static StructOrError parseVirtualHost( - io.envoyproxy.envoy.config.route.v3.VirtualHost proto, boolean parseFilter) { + io.envoyproxy.envoy.config.route.v3.VirtualHost proto, FilterRegistry filterRegistry, + boolean parseHttpFilter) { String name = proto.getName(); List routes = new ArrayList<>(proto.getRoutesCount()); for (io.envoyproxy.envoy.config.route.v3.Route routeProto : proto.getRoutesList()) { - StructOrError route = parseRoute(routeProto, parseFilter); + StructOrError route = parseRoute(routeProto, filterRegistry, parseHttpFilter); if (route == null) { continue; } @@ -402,12 +554,12 @@ final class ClientXdsClient extends AbstractXdsClient { } routes.add(route.getStruct()); } - if (!parseFilter) { + if (!parseHttpFilter) { return StructOrError.fromStruct(VirtualHost.create( name, proto.getDomainsList(), routes, new HashMap())); } StructOrError> overrideConfigs = - parseOverrideFilterConfigs(proto.getTypedPerFilterConfigMap()); + parseOverrideFilterConfigs(proto.getTypedPerFilterConfigMap(), filterRegistry); if (overrideConfigs.errorDetail != null) { return StructOrError.fromError( "VirtualHost [" + proto.getName() + "] contains invalid HttpFilter config: " @@ -419,18 +571,52 @@ final class ClientXdsClient extends AbstractXdsClient { @VisibleForTesting static StructOrError> parseOverrideFilterConfigs( - Map rawFilterConfigMap) { + Map rawFilterConfigMap, FilterRegistry filterRegistry) { Map overrideConfigs = new HashMap<>(); for (String name : rawFilterConfigMap.keySet()) { Any anyConfig = rawFilterConfigMap.get(name); - StructOrError filterConfig = parseRawFilterConfig(name, anyConfig, null, true); - if (filterConfig == null) { - continue; + String typeUrl = anyConfig.getTypeUrl(); + boolean isOptional = false; + if (typeUrl.equals(TYPE_URL_FILTER_CONFIG)) { + io.envoyproxy.envoy.config.route.v3.FilterConfig filterConfig; + try { + filterConfig = + anyConfig.unpack(io.envoyproxy.envoy.config.route.v3.FilterConfig.class); + } catch (InvalidProtocolBufferException e) { + return StructOrError.fromError( + "FilterConfig [" + name + "] contains invalid proto: " + e); + } + isOptional = filterConfig.getIsOptional(); + anyConfig = filterConfig.getConfig(); + typeUrl = anyConfig.getTypeUrl(); } + Message rawConfig = anyConfig; + if (typeUrl.equals(TYPE_URL_TYPED_STRUCT)) { + TypedStruct typedStruct; + try { + typedStruct = anyConfig.unpack(TypedStruct.class); + } catch (InvalidProtocolBufferException e) { + return StructOrError.fromError( + "FilterConfig [" + name + "] contains invalid proto: " + e); + } + typeUrl = typedStruct.getTypeUrl(); + rawConfig = typedStruct.getValue(); + } + Filter filter = filterRegistry.get(typeUrl); + if (filter == null) { + if (isOptional) { + continue; + } + return StructOrError.fromError( + "HttpFilter [" + name + "](" + typeUrl + ") is required but unsupported"); + } + ConfigOrError filterConfig = + filter.parseFilterConfigOverride(rawConfig); if (filterConfig.errorDetail != null) { - return StructOrError.fromError(filterConfig.errorDetail); + return StructOrError.fromError( + "Invalid filter config for HttpFilter [" + name + "]: " + filterConfig.errorDetail); } - overrideConfigs.put(name, filterConfig.struct); + overrideConfigs.put(name, filterConfig.config); } return StructOrError.fromStruct(overrideConfigs); } @@ -438,51 +624,55 @@ final class ClientXdsClient extends AbstractXdsClient { @VisibleForTesting @Nullable static StructOrError parseRoute( - io.envoyproxy.envoy.config.route.v3.Route proto, boolean parseFilter) { + io.envoyproxy.envoy.config.route.v3.Route proto, FilterRegistry filterRegistry, + boolean parseHttpFilter) { StructOrError routeMatch = parseRouteMatch(proto.getMatch()); if (routeMatch == null) { return null; } if (routeMatch.getErrorDetail() != null) { return StructOrError.fromError( - "Invalid route [" + proto.getName() + "]: " + routeMatch.getErrorDetail()); + "Route [" + proto.getName() + "] contains invalid RouteMatch: " + + routeMatch.getErrorDetail()); + } + + Map overrideConfigs = Collections.emptyMap(); + if (parseHttpFilter) { + StructOrError> overrideConfigsOrError = + parseOverrideFilterConfigs(proto.getTypedPerFilterConfigMap(), filterRegistry); + if (overrideConfigsOrError.errorDetail != null) { + return StructOrError.fromError( + "Route [" + proto.getName() + "] contains invalid HttpFilter config: " + + overrideConfigsOrError.errorDetail); + } + overrideConfigs = overrideConfigsOrError.struct; } - StructOrError routeAction; switch (proto.getActionCase()) { case ROUTE: - routeAction = parseRouteAction(proto.getRoute(), parseFilter); - break; + StructOrError routeAction = + parseRouteAction(proto.getRoute(), filterRegistry, parseHttpFilter); + if (routeAction == null) { + return null; + } + if (routeAction.errorDetail != null) { + return StructOrError.fromError( + "Route [" + proto.getName() + "] contains invalid RouteAction: " + + routeAction.getErrorDetail()); + } + return StructOrError.fromStruct( + Route.forAction(routeMatch.struct, routeAction.struct, overrideConfigs)); + case NON_FORWARDING_ACTION: + return StructOrError.fromStruct( + Route.forNonForwardingAction(routeMatch.struct, overrideConfigs)); case REDIRECT: - return StructOrError.fromError("Unsupported action type: redirect"); case DIRECT_RESPONSE: - return StructOrError.fromError("Unsupported action type: direct_response"); case FILTER_ACTION: - return StructOrError.fromError("Unsupported action type: filter_action"); case ACTION_NOT_SET: default: - return StructOrError.fromError("Unknown action type: " + proto.getActionCase()); + return StructOrError.fromError( + "Route [" + proto.getName() + "] with unknown action type: " + proto.getActionCase()); } - if (routeAction == null) { - return null; - } - if (routeAction.getErrorDetail() != null) { - return StructOrError.fromError( - "Invalid route [" + proto.getName() + "]: " + routeAction.getErrorDetail()); - } - if (!parseFilter) { - return StructOrError.fromStruct(Route.create( - routeMatch.getStruct(), routeAction.getStruct(), new HashMap())); - } - StructOrError> overrideConfigs = - parseOverrideFilterConfigs(proto.getTypedPerFilterConfigMap()); - if (overrideConfigs.errorDetail != null) { - return StructOrError.fromError( - "Route [" + proto.getName() + "] contains invalid HttpFilter config: " - + overrideConfigs.errorDetail); - } - return StructOrError.fromStruct(Route.create( - routeMatch.getStruct(), routeAction.getStruct(), overrideConfigs.struct)); } @VisibleForTesting @@ -605,10 +795,16 @@ final class ClientXdsClient extends AbstractXdsClient { } } + /** + * Parses the RouteAction config. The returned result may contain a (parsed form) + * {@link RouteAction} or an error message. Returns {@code null} if the RouteAction + * should be ignored. + */ @VisibleForTesting @Nullable static StructOrError parseRouteAction( - io.envoyproxy.envoy.config.route.v3.RouteAction proto, boolean parseFilter) { + io.envoyproxy.envoy.config.route.v3.RouteAction proto, FilterRegistry filterRegistry, + boolean parseHttpFilter) { Long timeoutNano = null; if (proto.hasMaxStreamDuration()) { io.envoyproxy.envoy.config.route.v3.RouteAction.MaxStreamDuration maxStreamDuration @@ -667,7 +863,7 @@ final class ClientXdsClient extends AbstractXdsClient { for (io.envoyproxy.envoy.config.route.v3.WeightedCluster.ClusterWeight clusterWeight : clusterWeights) { StructOrError clusterWeightOrError = - parseClusterWeight(clusterWeight, parseFilter); + parseClusterWeight(clusterWeight, filterRegistry, parseHttpFilter); if (clusterWeightOrError.getErrorDetail() != null) { return StructOrError.fromError("RouteAction contains invalid ClusterWeight: " + clusterWeightOrError.getErrorDetail()); @@ -687,13 +883,13 @@ final class ClientXdsClient extends AbstractXdsClient { @VisibleForTesting static StructOrError parseClusterWeight( io.envoyproxy.envoy.config.route.v3.WeightedCluster.ClusterWeight proto, - boolean parseFilter) { - if (!parseFilter) { + FilterRegistry filterRegistry, boolean parseHttpFilter) { + if (!parseHttpFilter) { return StructOrError.fromStruct(ClusterWeight.create( proto.getName(), proto.getWeight().getValue(), new HashMap())); } StructOrError> overrideConfigs = - parseOverrideFilterConfigs(proto.getTypedPerFilterConfigMap()); + parseOverrideFilterConfigs(proto.getTypedPerFilterConfigMap(), filterRegistry); if (overrideConfigs.errorDetail != null) { return StructOrError.fromError( "ClusterWeight [" + proto.getName() + "] contains invalid HttpFilter config: " @@ -728,7 +924,8 @@ final class ClientXdsClient extends AbstractXdsClient { RdsUpdate rdsUpdate; boolean isResourceV3 = resource.getTypeUrl().equals(ResourceType.RDS.typeUrl()); try { - rdsUpdate = processRouteConfiguration(routeConfig, enableFaultInjection && isResourceV3); + rdsUpdate = processRouteConfiguration( + routeConfig, filterRegistry, enableFaultInjection && isResourceV3); } catch (ResourceInvalidException e) { errors.add( "RDS response RouteConfiguration '" + routeConfigName + "' validation error: " + e @@ -750,11 +947,13 @@ final class ClientXdsClient extends AbstractXdsClient { } private static RdsUpdate processRouteConfiguration( - RouteConfiguration routeConfig, boolean parseFilter) throws ResourceInvalidException { + RouteConfiguration routeConfig, FilterRegistry filterRegistry, boolean parseHttpFilter) + throws ResourceInvalidException { List virtualHosts = new ArrayList<>(routeConfig.getVirtualHostsCount()); for (io.envoyproxy.envoy.config.route.v3.VirtualHost virtualHostProto : routeConfig.getVirtualHostsList()) { - StructOrError virtualHost = parseVirtualHost(virtualHostProto, parseFilter); + StructOrError virtualHost = + parseVirtualHost(virtualHostProto, filterRegistry, parseHttpFilter); if (virtualHost.getErrorDetail() != null) { throw new ResourceInvalidException( "RouteConfiguration contains invalid virtual host: " + virtualHost.getErrorDetail()); diff --git a/xds/src/main/java/io/grpc/xds/EnvoyServerProtoData.java b/xds/src/main/java/io/grpc/xds/EnvoyServerProtoData.java index b8a5bfd290..266bf5360c 100644 --- a/xds/src/main/java/io/grpc/xds/EnvoyServerProtoData.java +++ b/xds/src/main/java/io/grpc/xds/EnvoyServerProtoData.java @@ -16,24 +16,16 @@ package io.grpc.xds; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; -import com.google.protobuf.Any; -import com.google.protobuf.InvalidProtocolBufferException; -import io.envoyproxy.envoy.config.core.v3.Address; -import io.envoyproxy.envoy.config.core.v3.SocketAddress; -import io.envoyproxy.envoy.config.core.v3.TrafficDirection; -import io.envoyproxy.envoy.config.listener.v3.Filter; -import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager; -import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter; import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CommonTlsContext; import io.grpc.Internal; import io.grpc.xds.internal.sds.SslContextProviderSupplier; import java.net.InetAddress; import java.net.UnknownHostException; -import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Objects; import javax.annotation.Nullable; @@ -45,8 +37,6 @@ import javax.annotation.Nullable; @Internal public final class EnvoyServerProtoData { - static final String TRANSPORT_SOCKET_NAME_TLS = "envoy.transport_sockets.tls"; - // Prevent instantiation. private EnvoyServerProtoData() { } @@ -156,21 +146,11 @@ public final class EnvoyServerProtoData { private final InetAddress addressPrefix; private final int prefixLen; - @VisibleForTesting - CidrRange(String addressPrefix, int prefixLen) throws InvalidProtocolBufferException { - try { - this.addressPrefix = InetAddress.getByName(addressPrefix); - } catch (UnknownHostException e) { - throw new InvalidProtocolBufferException(e.getMessage()); - } + CidrRange(String addressPrefix, int prefixLen) throws UnknownHostException { + this.addressPrefix = InetAddress.getByName(addressPrefix); this.prefixLen = prefixLen; } - static CidrRange fromEnvoyProtoCidrRange( - io.envoyproxy.envoy.config.core.v3.CidrRange proto) throws InvalidProtocolBufferException { - return new CidrRange(proto.getAddressPrefix(), proto.getPrefixLen().getValue()); - } - public InetAddress getAddressPrefix() { return addressPrefix; } @@ -251,50 +231,6 @@ public final class EnvoyServerProtoData { this.transportProtocol = transportProtocol; } - static FilterChainMatch fromEnvoyProtoFilterChainMatch( - io.envoyproxy.envoy.config.listener.v3.FilterChainMatch proto) - throws InvalidProtocolBufferException { - List prefixRanges = new ArrayList<>(); - for (io.envoyproxy.envoy.config.core.v3.CidrRange range : proto.getPrefixRangesList()) { - prefixRanges.add(CidrRange.fromEnvoyProtoCidrRange(range)); - } - List sourcePrefixRanges = new ArrayList<>(); - for (io.envoyproxy.envoy.config.core.v3.CidrRange range : proto.getSourcePrefixRangesList()) { - sourcePrefixRanges.add(CidrRange.fromEnvoyProtoCidrRange(range)); - } - List applicationProtocols = new ArrayList<>(); - for (String appProtocol : proto.getApplicationProtocolsList()) { - applicationProtocols.add(appProtocol); - } - ConnectionSourceType sourceType = null; - switch (proto.getSourceType()) { - case ANY: - sourceType = ConnectionSourceType.ANY; - break; - case EXTERNAL: - sourceType = ConnectionSourceType.EXTERNAL; - break; - case SAME_IP_OR_LOOPBACK: - sourceType = ConnectionSourceType.SAME_IP_OR_LOOPBACK; - break; - default: - throw new InvalidProtocolBufferException("Unknown source-type:" + proto.getSourceType()); - } - List serverNames = new ArrayList<>(); - for (String serverName : proto.getServerNamesList()) { - serverNames.add(serverName); - } - return new FilterChainMatch( - proto.getDestinationPort().getValue(), - prefixRanges, - applicationProtocols, - sourcePrefixRanges, - sourceType, - proto.getSourcePortsList(), - serverNames, - proto.getTransportProtocol()); - } - public int getDestinationPort() { return destinationPort; } @@ -378,116 +314,43 @@ public final class EnvoyServerProtoData { * Corresponds to Envoy proto message {@link io.envoyproxy.envoy.api.v2.listener.FilterChain}. */ static final class FilterChain { + // Unique name for the FilterChain. + private final String name; // TODO(sanjaypujare): flatten structure by moving FilterChainMatch class members here. private final FilterChainMatch filterChainMatch; + private final HttpConnectionManager httpConnectionManager; @Nullable private final SslContextProviderSupplier sslContextProviderSupplier; - @VisibleForTesting FilterChain( - FilterChainMatch filterChainMatch, @Nullable DownstreamTlsContext downstreamTlsContext, + String name, + FilterChainMatch filterChainMatch, + HttpConnectionManager httpConnectionManager, + @Nullable DownstreamTlsContext downstreamTlsContext, TlsContextManager tlsContextManager) { SslContextProviderSupplier sslContextProviderSupplier1 = downstreamTlsContext == null ? null : new SslContextProviderSupplier(downstreamTlsContext, tlsContextManager); + this.name = checkNotNull(name, "name"); + // TODO(chengyuanzhang): enforce non-null, change tests to use a default/empty + // FilterChainMatch instead of null, as that's how the proto is converted. this.filterChainMatch = filterChainMatch; this.sslContextProviderSupplier = sslContextProviderSupplier1; + this.httpConnectionManager = checkNotNull(httpConnectionManager, "httpConnectionManager"); } - static FilterChain fromEnvoyProtoFilterChain( - io.envoyproxy.envoy.config.listener.v3.FilterChain proto, - TlsContextManager tlsContextManager, boolean isDefaultFilterChain) - throws InvalidProtocolBufferException { - if (!isDefaultFilterChain && proto.getFiltersList().isEmpty()) { - throw new IllegalArgumentException( - "filerChain " + proto.getName() + " has to have envoy.http_connection_manager"); - } - HashSet uniqueNames = new HashSet<>(); - for (Filter filter : proto.getFiltersList()) { - if (!uniqueNames.add(filter.getName())) { - throw new IllegalArgumentException( - "filerChain " + proto.getName() + " has non-unique filter name:" + filter.getName()); - } - validateFilter(filter); - } - return new FilterChain( - FilterChainMatch.fromEnvoyProtoFilterChainMatch(proto.getFilterChainMatch()), - getTlsContextFromFilterChain(proto), - tlsContextManager - ); - } - - private static void validateFilter(Filter filter) - throws InvalidProtocolBufferException, IllegalArgumentException { - if (filter.hasConfigDiscovery()) { - throw new IllegalArgumentException( - "filter " + filter.getName() + " with config_discovery not supported"); - } - if (!filter.hasTypedConfig()) { - throw new IllegalArgumentException( - "filter " + filter.getName() + " expected to have typed_config"); - } - Any any = filter.getTypedConfig(); - if (!any.getTypeUrl().equals(ClientXdsClient.TYPE_URL_HTTP_CONNECTION_MANAGER)) { - throw new IllegalArgumentException( - "filter " + filter.getName() + " with unsupported typed_config type:" + any - .getTypeUrl()); - } - validateHttpConnectionManager(any.unpack(HttpConnectionManager.class)); - } - - private static void validateHttpConnectionManager(HttpConnectionManager hcm) - throws IllegalArgumentException { - List httpFilters = hcm.getHttpFiltersList(); - HashSet uniqueNames = new HashSet<>(); - for (HttpFilter httpFilter : httpFilters) { - String httpFilterName = httpFilter.getName(); - if (!uniqueNames.add(httpFilterName)) { - throw new IllegalArgumentException( - "http-connection-manager has non-unique http-filter name:" + httpFilterName); - } - if (!httpFilter.getIsOptional()) { - if (httpFilter.hasConfigDiscovery()) { - throw new IllegalArgumentException( - "http-connection-manager http-filter " + httpFilterName - + " uses config-discovery which is unsupported"); - } - if (httpFilter.hasTypedConfig()) { - Any any = httpFilter.getTypedConfig(); - if (!any.getTypeUrl() - .equals("type.googleapis.com/envoy.extensions.filters.http.router.v3.Router")) { - throw new IllegalArgumentException( - "http-connection-manager http-filter " + httpFilterName - + " has unsupported typed-config type:" + any.getTypeUrl()); - } - } else { - throw new IllegalArgumentException( - "http-connection-manager http-filter " - + httpFilterName - + " should have typed-config type " - + "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"); - } - } - } - } - - @Nullable - private static DownstreamTlsContext getTlsContextFromFilterChain( - io.envoyproxy.envoy.config.listener.v3.FilterChain filterChain) - throws InvalidProtocolBufferException { - if (filterChain.hasTransportSocket() - && TRANSPORT_SOCKET_NAME_TLS.equals(filterChain.getTransportSocket().getName())) { - Any any = filterChain.getTransportSocket().getTypedConfig(); - return DownstreamTlsContext.fromEnvoyProtoDownstreamTlsContext( - io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext.parseFrom( - any.getValue())); - } - return null; + String getName() { + return name; } public FilterChainMatch getFilterChainMatch() { return filterChainMatch; } + HttpConnectionManager getHttpConnectionManager() { + return httpConnectionManager; + } + + @Nullable public SslContextProviderSupplier getSslContextProviderSupplier() { return sslContextProviderSupplier; } @@ -501,21 +364,26 @@ public final class EnvoyServerProtoData { return false; } FilterChain that = (FilterChain) o; - return java.util.Objects.equals(filterChainMatch, that.filterChainMatch) - && java.util.Objects.equals(sslContextProviderSupplier, that.sslContextProviderSupplier); + return Objects.equals(name, that.name) + && Objects.equals(filterChainMatch, that.filterChainMatch) + && Objects.equals(httpConnectionManager, that.httpConnectionManager) + && Objects.equals(sslContextProviderSupplier, that.sslContextProviderSupplier); } @Override public int hashCode() { - return java.util.Objects.hash(filterChainMatch, sslContextProviderSupplier); + return Objects.hash( + name, filterChainMatch, httpConnectionManager, sslContextProviderSupplier); } @Override public String toString() { - return "FilterChain{" - + "filterChainMatch=" + filterChainMatch - + ", sslContextProviderSupplier=" + sslContextProviderSupplier - + '}'; + return MoreObjects.toStringHelper(this) + .add("name", name) + .add("filterChainMatch", filterChainMatch) + .add("httpConnectionManager", httpConnectionManager) + .add("sslContextProviderSupplier", sslContextProviderSupplier) + .toString(); } } @@ -528,73 +396,22 @@ public final class EnvoyServerProtoData { @Nullable private final String address; private final List filterChains; + @Nullable private final FilterChain defaultFilterChain; - @VisibleForTesting - Listener(String name, String address, - List filterChains, FilterChain defaultFilterChain) { - this.name = name; + Listener(String name, @Nullable String address, + List filterChains, @Nullable FilterChain defaultFilterChain) { + this.name = checkNotNull(name, "name"); this.address = address; - this.filterChains = Collections.unmodifiableList(filterChains); + this.filterChains = Collections.unmodifiableList(checkNotNull(filterChains, "filterChains")); this.defaultFilterChain = defaultFilterChain; } - private static String convertEnvoyAddressToString(Address proto) { - if (proto.hasSocketAddress()) { - SocketAddress socketAddress = proto.getSocketAddress(); - String address = socketAddress.getAddress(); - switch (socketAddress.getPortSpecifierCase()) { - case NAMED_PORT: - return address + ":" + socketAddress.getNamedPort(); - case PORT_VALUE: - return address + ":" + socketAddress.getPortValue(); - default: - return address; - } - } - return null; - } - - static Listener fromEnvoyProtoListener(io.envoyproxy.envoy.config.listener.v3.Listener proto, - TlsContextManager tlsContextManager) - throws InvalidProtocolBufferException { - if (!proto.getTrafficDirection().equals(TrafficDirection.INBOUND)) { - throw new IllegalArgumentException("Listener " + proto.getName() + " is not INBOUND"); - } - if (!proto.getListenerFiltersList().isEmpty()) { - throw new IllegalArgumentException( - "Listener " + proto.getName() + " cannot have listener_filters"); - } - if (proto.hasUseOriginalDst()) { - throw new IllegalArgumentException( - "Listener " + proto.getName() + " cannot have use_original_dst set to true"); - } - List filterChains = validateAndSelectFilterChains(proto.getFilterChainsList(), - tlsContextManager); - return new Listener( - proto.getName(), - convertEnvoyAddressToString(proto.getAddress()), - filterChains, FilterChain - .fromEnvoyProtoFilterChain(proto.getDefaultFilterChain(), tlsContextManager, true)); - } - - private static List validateAndSelectFilterChains( - List inputFilterChains, - TlsContextManager tlsContextManager) - throws InvalidProtocolBufferException { - List filterChains = new ArrayList<>(inputFilterChains.size()); - for (io.envoyproxy.envoy.config.listener.v3.FilterChain filterChain : - inputFilterChains) { - filterChains - .add(FilterChain.fromEnvoyProtoFilterChain(filterChain, tlsContextManager, false)); - } - return filterChains; - } - public String getName() { return name; } + @Nullable public String getAddress() { return address; } @@ -603,6 +420,7 @@ public final class EnvoyServerProtoData { return filterChains; } + @Nullable public FilterChain getDefaultFilterChain() { return defaultFilterChain; } @@ -629,18 +447,12 @@ public final class EnvoyServerProtoData { @Override public String toString() { - return "Listener{" - + "name='" - + name - + '\'' - + ", address='" - + address - + '\'' - + ", filterChains=" - + filterChains - + ", defaultFilterChain=" - + defaultFilterChain - + '}'; + return MoreObjects.toStringHelper(this) + .add("name", name) + .add("address", address) + .add("filterChains", filterChains) + .add("defaultFilterChain", defaultFilterChain) + .toString(); } } } diff --git a/xds/src/main/java/io/grpc/xds/HttpConnectionManager.java b/xds/src/main/java/io/grpc/xds/HttpConnectionManager.java new file mode 100644 index 0000000000..8128a42fb0 --- /dev/null +++ b/xds/src/main/java/io/grpc/xds/HttpConnectionManager.java @@ -0,0 +1,68 @@ +/* + * Copyright 2021 The gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.grpc.xds; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.auto.value.AutoValue; +import com.google.common.collect.ImmutableList; +import io.grpc.xds.Filter.NamedFilterConfig; +import java.util.List; +import javax.annotation.Nullable; + +/** + * HttpConnectionManager is a network filter for proxying HTTP requests. + */ +@AutoValue +abstract class HttpConnectionManager { + // Total number of nanoseconds to keep alive an HTTP request/response stream. + abstract long httpMaxStreamDurationNano(); + + // Name of the route configuration to be used for RDS resource discovery. + @Nullable + abstract String rdsName(); + + // List of virtual hosts that make up the route table. + @Nullable + abstract ImmutableList virtualHosts(); + + // List of http filter configs. Null if HttpFilter support is not enabled. + @Nullable + abstract ImmutableList httpFilterConfigs(); + + static HttpConnectionManager forRdsName(long httpMaxStreamDurationNano, String rdsName, + @Nullable List httpFilterConfigs) { + checkNotNull(rdsName, "rdsName"); + return create(httpMaxStreamDurationNano, rdsName, null, httpFilterConfigs); + } + + static HttpConnectionManager forVirtualHosts(long httpMaxStreamDurationNano, + List virtualHosts, @Nullable List httpFilterConfigs) { + checkNotNull(virtualHosts, "virtualHosts"); + return create(httpMaxStreamDurationNano, null, virtualHosts, + httpFilterConfigs); + } + + private static HttpConnectionManager create(long httpMaxStreamDurationNano, + @Nullable String rdsName, @Nullable List virtualHosts, + @Nullable List httpFilterConfigs) { + return new AutoValue_HttpConnectionManager( + httpMaxStreamDurationNano, rdsName, + virtualHosts == null ? null : ImmutableList.copyOf(virtualHosts), + httpFilterConfigs == null ? null : ImmutableList.copyOf(httpFilterConfigs)); + } +} diff --git a/xds/src/main/java/io/grpc/xds/RouterFilter.java b/xds/src/main/java/io/grpc/xds/RouterFilter.java index 18fb8becae..7f1adf86a6 100644 --- a/xds/src/main/java/io/grpc/xds/RouterFilter.java +++ b/xds/src/main/java/io/grpc/xds/RouterFilter.java @@ -19,14 +19,16 @@ package io.grpc.xds; import com.google.protobuf.Message; import io.grpc.ClientInterceptor; import io.grpc.LoadBalancer.PickSubchannelArgs; +import io.grpc.ServerInterceptor; import io.grpc.xds.Filter.ClientInterceptorBuilder; +import io.grpc.xds.Filter.ServerInterceptorBuilder; import java.util.concurrent.ScheduledExecutorService; import javax.annotation.Nullable; /** * Router filter implementation. Currently this filter does not parse any field in the config. */ -enum RouterFilter implements Filter, ClientInterceptorBuilder { +enum RouterFilter implements Filter, ClientInterceptorBuilder, ServerInterceptorBuilder { INSTANCE; static final String TYPE_URL = @@ -66,4 +68,11 @@ enum RouterFilter implements Filter, ClientInterceptorBuilder { ScheduledExecutorService scheduler) { return null; } + + @Nullable + @Override + public ServerInterceptor buildServerInterceptor( + FilterConfig config, @Nullable Filter.FilterConfig overrideConfig) { + return null; + } } diff --git a/xds/src/main/java/io/grpc/xds/VirtualHost.java b/xds/src/main/java/io/grpc/xds/VirtualHost.java index 59ca702d67..ebce2b1b0f 100644 --- a/xds/src/main/java/io/grpc/xds/VirtualHost.java +++ b/xds/src/main/java/io/grpc/xds/VirtualHost.java @@ -57,12 +57,23 @@ abstract class VirtualHost { abstract static class Route { abstract RouteMatch routeMatch(); + @Nullable abstract RouteAction routeAction(); abstract ImmutableMap filterConfigOverrides(); - static Route create( - RouteMatch routeMatch, RouteAction routeAction, + static Route forAction(RouteMatch routeMatch, RouteAction routeAction, + Map filterConfigOverrides) { + return create(routeMatch, routeAction, filterConfigOverrides); + } + + static Route forNonForwardingAction(RouteMatch routeMatch, + Map filterConfigOverrides) { + return create(routeMatch, null, filterConfigOverrides); + } + + private static Route create( + RouteMatch routeMatch, @Nullable RouteAction routeAction, Map filterConfigOverrides) { return new AutoValue_VirtualHost_Route( routeMatch, routeAction, ImmutableMap.copyOf(filterConfigOverrides)); diff --git a/xds/src/main/java/io/grpc/xds/XdsClient.java b/xds/src/main/java/io/grpc/xds/XdsClient.java index 1c2238c1e1..6b6be57f04 100644 --- a/xds/src/main/java/io/grpc/xds/XdsClient.java +++ b/xds/src/main/java/io/grpc/xds/XdsClient.java @@ -20,7 +20,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.auto.value.AutoValue; import com.google.common.base.MoreObjects; -import com.google.common.base.MoreObjects.ToStringHelper; import com.google.common.collect.ImmutableList; import com.google.protobuf.Any; import io.grpc.Status; @@ -29,7 +28,6 @@ import io.grpc.xds.Endpoints.DropOverload; import io.grpc.xds.Endpoints.LocalityLbEndpoints; import io.grpc.xds.EnvoyServerProtoData.Listener; import io.grpc.xds.EnvoyServerProtoData.UpstreamTlsContext; -import io.grpc.xds.Filter.NamedFilterConfig; import io.grpc.xds.LoadStatsManager2.ClusterDropStats; import io.grpc.xds.LoadStatsManager2.ClusterLocalityStats; import java.util.ArrayList; @@ -48,92 +46,25 @@ import javax.annotation.Nullable; */ abstract class XdsClient { - static final class LdsUpdate implements ResourceUpdate { - // Total number of nanoseconds to keep alive an HTTP request/response stream. - final long httpMaxStreamDurationNano; - // The name of the route configuration to be used for RDS resource discovery. + @AutoValue + abstract static class LdsUpdate implements ResourceUpdate { + // Http level api listener configuration. @Nullable - final String rdsName; - // The list virtual hosts that make up the route table. + abstract HttpConnectionManager httpConnectionManager(); + + // Tcp level listener configuration. @Nullable - final List virtualHosts; - // Filter instance names. Null if HttpFilter support is not enabled. - @Nullable final List filterChain; - // Server side Listener. - @Nullable - final Listener listener; + abstract Listener listener(); - LdsUpdate( - long httpMaxStreamDurationNano, String rdsName, - @Nullable List filterChain) { - this(httpMaxStreamDurationNano, rdsName, null, filterChain); + static LdsUpdate forApiListener(HttpConnectionManager httpConnectionManager) { + checkNotNull(httpConnectionManager, "httpConnectionManager"); + return new AutoValue_XdsClient_LdsUpdate(httpConnectionManager, null); } - LdsUpdate( - long httpMaxStreamDurationNano, List virtualHosts, - @Nullable List filterChain) { - this(httpMaxStreamDurationNano, null, virtualHosts, filterChain); + static LdsUpdate forTcpListener(Listener listener) { + checkNotNull(listener, "listener"); + return new AutoValue_XdsClient_LdsUpdate(null, listener); } - - private LdsUpdate( - long httpMaxStreamDurationNano, @Nullable String rdsName, - @Nullable List virtualHosts, @Nullable List filterChain) { - this.httpMaxStreamDurationNano = httpMaxStreamDurationNano; - this.rdsName = rdsName; - this.virtualHosts = virtualHosts == null - ? null : Collections.unmodifiableList(new ArrayList<>(virtualHosts)); - this.filterChain = filterChain == null ? null : Collections.unmodifiableList(filterChain); - this.listener = null; - } - - LdsUpdate(Listener listener) { - this.listener = listener; - this.httpMaxStreamDurationNano = 0L; - this.rdsName = null; - this.filterChain = null; - this.virtualHosts = null; - } - - @Override - public int hashCode() { - return Objects.hash( - httpMaxStreamDurationNano, rdsName, virtualHosts, filterChain, listener); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - LdsUpdate that = (LdsUpdate) o; - return httpMaxStreamDurationNano == that.httpMaxStreamDurationNano - && Objects.equals(rdsName, that.rdsName) - && Objects.equals(virtualHosts, that.virtualHosts) - && Objects.equals(filterChain, that.filterChain) - && Objects.equals(listener, that.listener); - } - - @Override - public String toString() { - ToStringHelper toStringHelper = MoreObjects.toStringHelper(this); - toStringHelper.add("httpMaxStreamDurationNano", httpMaxStreamDurationNano); - if (rdsName != null) { - toStringHelper.add("rdsName", rdsName); - } else { - toStringHelper.add("virtualHosts", virtualHosts); - } - if (filterChain != null) { - toStringHelper.add("filterChain", filterChain); - } - if (listener != null) { - toStringHelper.add("listener", listener); - } - return toStringHelper.toString(); - } - } static final class RdsUpdate implements ResourceUpdate { diff --git a/xds/src/main/java/io/grpc/xds/XdsClientWrapperForServerSds.java b/xds/src/main/java/io/grpc/xds/XdsClientWrapperForServerSds.java index 2cfbb325ad..819459d5c3 100644 --- a/xds/src/main/java/io/grpc/xds/XdsClientWrapperForServerSds.java +++ b/xds/src/main/java/io/grpc/xds/XdsClientWrapperForServerSds.java @@ -114,7 +114,7 @@ public final class XdsClientWrapperForServerSds { new XdsClient.LdsResourceWatcher() { @Override public void onChanged(XdsClient.LdsUpdate update) { - releaseOldSuppliers(curListener.getAndSet(update.listener)); + releaseOldSuppliers(curListener.getAndSet(update.listener())); reportSuccess(); } diff --git a/xds/src/main/java/io/grpc/xds/XdsNameResolver.java b/xds/src/main/java/io/grpc/xds/XdsNameResolver.java index c88d71857b..0bcc572257 100644 --- a/xds/src/main/java/io/grpc/xds/XdsNameResolver.java +++ b/xds/src/main/java/io/grpc/xds/XdsNameResolver.java @@ -359,6 +359,10 @@ final class XdsNameResolver extends NameResolver { return Result.forError( Status.UNAVAILABLE.withDescription("Could not find xDS route matching RPC")); } + if (selectedRoute.routeAction() == null) { + return Result.forError(Status.UNAVAILABLE.withDescription( + "Could not route RPC to Route with non-forwarding action")); + } RouteAction action = selectedRoute.routeAction(); if (action.cluster() != null) { cluster = action.cluster(); @@ -648,14 +652,17 @@ final class XdsNameResolver extends NameResolver { return; } logger.log(XdsLogLevel.INFO, "Receive LDS resource update: {0}", update); - List virtualHosts = update.virtualHosts; - String rdsName = update.rdsName; + HttpConnectionManager httpConnectionManager = update.httpConnectionManager(); + List virtualHosts = httpConnectionManager.virtualHosts(); + String rdsName = httpConnectionManager.rdsName(); cleanUpRouteDiscoveryState(); if (virtualHosts != null) { - updateRoutes(virtualHosts, update.httpMaxStreamDurationNano, update.filterChain); + updateRoutes(virtualHosts, httpConnectionManager.httpMaxStreamDurationNano(), + httpConnectionManager.httpFilterConfigs()); } else { routeDiscoveryState = new RouteDiscoveryState( - rdsName, update.httpMaxStreamDurationNano, update.filterChain); + rdsName, httpConnectionManager.httpMaxStreamDurationNano(), + httpConnectionManager.httpFilterConfigs()); logger.log(XdsLogLevel.INFO, "Start watching RDS resource {0}", rdsName); xdsClient.watchRdsResource(rdsName, routeDiscoveryState); } @@ -739,11 +746,13 @@ final class XdsNameResolver extends NameResolver { Set clusters = new HashSet<>(); for (Route route : routes) { RouteAction action = route.routeAction(); - if (action.cluster() != null) { - clusters.add(action.cluster()); - } else if (action.weightedClusters() != null) { - for (ClusterWeight weighedCluster : action.weightedClusters()) { - clusters.add(weighedCluster.name()); + if (action != null) { + if (action.cluster() != null) { + clusters.add(action.cluster()); + } else if (action.weightedClusters() != null) { + for (ClusterWeight weighedCluster : action.weightedClusters()) { + clusters.add(weighedCluster.name()); + } } } } diff --git a/xds/src/test/java/io/grpc/xds/ClientXdsClientDataTest.java b/xds/src/test/java/io/grpc/xds/ClientXdsClientDataTest.java index 913e1693f7..9ddce56387 100644 --- a/xds/src/test/java/io/grpc/xds/ClientXdsClientDataTest.java +++ b/xds/src/test/java/io/grpc/xds/ClientXdsClientDataTest.java @@ -35,7 +35,7 @@ import io.envoyproxy.envoy.config.cluster.v3.Cluster.RingHashLbConfig.HashFuncti import io.envoyproxy.envoy.config.core.v3.Address; import io.envoyproxy.envoy.config.core.v3.AggregatedConfigSource; import io.envoyproxy.envoy.config.core.v3.ConfigSource; -import io.envoyproxy.envoy.config.core.v3.ExtensionConfigSource; +import io.envoyproxy.envoy.config.core.v3.HttpProtocolOptions; import io.envoyproxy.envoy.config.core.v3.Locality; import io.envoyproxy.envoy.config.core.v3.RuntimeFractionalPercent; import io.envoyproxy.envoy.config.core.v3.SocketAddress; @@ -47,8 +47,14 @@ import io.envoyproxy.envoy.config.listener.v3.FilterChain; import io.envoyproxy.envoy.config.listener.v3.FilterChainMatch; import io.envoyproxy.envoy.config.listener.v3.Listener; import io.envoyproxy.envoy.config.listener.v3.ListenerFilter; +import io.envoyproxy.envoy.config.rbac.v3.Permission; +import io.envoyproxy.envoy.config.rbac.v3.Policy; +import io.envoyproxy.envoy.config.rbac.v3.Principal; +import io.envoyproxy.envoy.config.rbac.v3.RBAC; +import io.envoyproxy.envoy.config.rbac.v3.RBAC.Action; import io.envoyproxy.envoy.config.route.v3.DirectResponseAction; import io.envoyproxy.envoy.config.route.v3.FilterAction; +import io.envoyproxy.envoy.config.route.v3.NonForwardingAction; import io.envoyproxy.envoy.config.route.v3.RedirectAction; import io.envoyproxy.envoy.config.route.v3.RouteAction.HashPolicy.ConnectionProperties; import io.envoyproxy.envoy.config.route.v3.RouteAction.HashPolicy.FilterState; @@ -57,21 +63,23 @@ import io.envoyproxy.envoy.config.route.v3.RouteAction.HashPolicy.QueryParameter import io.envoyproxy.envoy.config.route.v3.RouteAction.MaxStreamDuration; import io.envoyproxy.envoy.config.route.v3.WeightedCluster; import io.envoyproxy.envoy.extensions.filters.common.fault.v3.FaultDelay; +import io.envoyproxy.envoy.extensions.filters.http.fault.v3.FaultAbort; import io.envoyproxy.envoy.extensions.filters.http.fault.v3.HTTPFault; +import io.envoyproxy.envoy.extensions.filters.http.rbac.v3.RBACPerRoute; +import io.envoyproxy.envoy.extensions.filters.http.router.v3.Router; import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager; import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter; +import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.Rds; import io.envoyproxy.envoy.type.matcher.v3.RegexMatchAndSubstitute; import io.envoyproxy.envoy.type.matcher.v3.RegexMatcher; import io.envoyproxy.envoy.type.matcher.v3.RegexMatcher.GoogleRE2; import io.envoyproxy.envoy.type.v3.FractionalPercent; import io.envoyproxy.envoy.type.v3.FractionalPercent.DenominatorType; import io.envoyproxy.envoy.type.v3.Int64Range; -import io.grpc.Status.Code; import io.grpc.xds.ClientXdsClient.ResourceInvalidException; import io.grpc.xds.ClientXdsClient.StructOrError; import io.grpc.xds.Endpoints.LbEndpoint; import io.grpc.xds.Endpoints.LocalityLbEndpoints; -import io.grpc.xds.FaultConfig.FaultAbort; import io.grpc.xds.Filter.FilterConfig; import io.grpc.xds.VirtualHost.Route; import io.grpc.xds.VirtualHost.Route.RouteAction; @@ -100,6 +108,7 @@ public class ClientXdsClientDataTest { @SuppressWarnings("deprecation") // https://github.com/grpc/grpc-java/issues/7467 @Rule public final ExpectedException thrown = ExpectedException.none(); + private final FilterRegistry filterRegistry = FilterRegistry.newRegistry(); @Test public void parseRoute_withRouteAction() { @@ -113,17 +122,36 @@ public class ClientXdsClientDataTest { io.envoyproxy.envoy.config.route.v3.RouteAction.newBuilder() .setCluster("cluster-foo")) .build(); - StructOrError struct = ClientXdsClient.parseRoute(proto, false); + StructOrError struct = ClientXdsClient.parseRoute(proto, filterRegistry, false); assertThat(struct.getErrorDetail()).isNull(); assertThat(struct.getStruct()) .isEqualTo( - Route.create( + Route.forAction( RouteMatch.create(PathMatcher.fromPath("/service/method", false), Collections.emptyList(), null), RouteAction.forCluster("cluster-foo", Collections.emptyList(), null), ImmutableMap.of())); } + @Test + public void parseRoute_withNonForwardingAction() { + io.envoyproxy.envoy.config.route.v3.Route proto = + io.envoyproxy.envoy.config.route.v3.Route.newBuilder() + .setName("route-blade") + .setMatch( + io.envoyproxy.envoy.config.route.v3.RouteMatch.newBuilder() + .setPath("/service/method")) + .setNonForwardingAction(NonForwardingAction.getDefaultInstance()) + .build(); + StructOrError struct = ClientXdsClient.parseRoute(proto, filterRegistry, false); + assertThat(struct.getStruct()) + .isEqualTo( + Route.forNonForwardingAction( + RouteMatch.create(PathMatcher.fromPath("/service/method", false), + Collections.emptyList(), null), + ImmutableMap.of())); + } + @Test public void parseRoute_withUnsupportedActionTypes() { StructOrError res; @@ -133,9 +161,10 @@ public class ClientXdsClientDataTest { .setMatch(io.envoyproxy.envoy.config.route.v3.RouteMatch.newBuilder().setPath("")) .setRedirect(RedirectAction.getDefaultInstance()) .build(); - res = ClientXdsClient.parseRoute(redirectRoute, false); + res = ClientXdsClient.parseRoute(redirectRoute, filterRegistry, false); assertThat(res.getStruct()).isNull(); - assertThat(res.getErrorDetail()).isEqualTo("Unsupported action type: redirect"); + assertThat(res.getErrorDetail()) + .isEqualTo("Route [route-blade] with unknown action type: REDIRECT"); io.envoyproxy.envoy.config.route.v3.Route directResponseRoute = io.envoyproxy.envoy.config.route.v3.Route.newBuilder() @@ -143,9 +172,10 @@ public class ClientXdsClientDataTest { .setMatch(io.envoyproxy.envoy.config.route.v3.RouteMatch.newBuilder().setPath("")) .setDirectResponse(DirectResponseAction.getDefaultInstance()) .build(); - res = ClientXdsClient.parseRoute(directResponseRoute, false); + res = ClientXdsClient.parseRoute(directResponseRoute, filterRegistry, false); assertThat(res.getStruct()).isNull(); - assertThat(res.getErrorDetail()).isEqualTo("Unsupported action type: direct_response"); + assertThat(res.getErrorDetail()) + .isEqualTo("Route [route-blade] with unknown action type: DIRECT_RESPONSE"); io.envoyproxy.envoy.config.route.v3.Route filterRoute = io.envoyproxy.envoy.config.route.v3.Route.newBuilder() @@ -153,9 +183,10 @@ public class ClientXdsClientDataTest { .setMatch(io.envoyproxy.envoy.config.route.v3.RouteMatch.newBuilder().setPath("")) .setFilterAction(FilterAction.getDefaultInstance()) .build(); - res = ClientXdsClient.parseRoute(filterRoute, false); + res = ClientXdsClient.parseRoute(filterRoute, filterRegistry, false); assertThat(res.getStruct()).isNull(); - assertThat(res.getErrorDetail()).isEqualTo("Unsupported action type: filter_action"); + assertThat(res.getErrorDetail()) + .isEqualTo("Route [route-blade] with unknown action type: FILTER_ACTION"); } @Test @@ -173,7 +204,7 @@ public class ClientXdsClientDataTest { io.envoyproxy.envoy.config.route.v3.RouteAction.newBuilder() .setCluster("cluster-foo")) .build(); - assertThat(ClientXdsClient.parseRoute(proto, false)).isNull(); + assertThat(ClientXdsClient.parseRoute(proto, filterRegistry, false)).isNull(); } @Test @@ -188,7 +219,7 @@ public class ClientXdsClientDataTest { io.envoyproxy.envoy.config.route.v3.RouteAction.newBuilder() .setClusterHeader("cluster header")) // cluster_header action not supported .build(); - assertThat(ClientXdsClient.parseRoute(proto, false)).isNull(); + assertThat(ClientXdsClient.parseRoute(proto, filterRegistry, false)).isNull(); } @Test @@ -367,7 +398,8 @@ public class ClientXdsClientDataTest { io.envoyproxy.envoy.config.route.v3.RouteAction.newBuilder() .setCluster("cluster-foo") .build(); - StructOrError struct = ClientXdsClient.parseRouteAction(proto, false); + StructOrError struct = + ClientXdsClient.parseRouteAction(proto, filterRegistry, false); assertThat(struct.getErrorDetail()).isNull(); assertThat(struct.getStruct().cluster()).isEqualTo("cluster-foo"); assertThat(struct.getStruct().weightedClusters()).isNull(); @@ -389,7 +421,8 @@ public class ClientXdsClientDataTest { .setName("cluster-bar") .setWeight(UInt32Value.newBuilder().setValue(70)))) .build(); - StructOrError struct = ClientXdsClient.parseRouteAction(proto, false); + StructOrError struct = + ClientXdsClient.parseRouteAction(proto, filterRegistry, false); assertThat(struct.getErrorDetail()).isNull(); assertThat(struct.getStruct().cluster()).isNull(); assertThat(struct.getStruct().weightedClusters()).containsExactly( @@ -407,7 +440,8 @@ public class ClientXdsClientDataTest { .setGrpcTimeoutHeaderMax(Durations.fromSeconds(5L)) .setMaxStreamDuration(Durations.fromMillis(20L))) .build(); - StructOrError struct = ClientXdsClient.parseRouteAction(proto, false); + StructOrError struct = + ClientXdsClient.parseRouteAction(proto, filterRegistry, false); assertThat(struct.getStruct().timeoutNano()).isEqualTo(TimeUnit.SECONDS.toNanos(5L)); } @@ -420,7 +454,8 @@ public class ClientXdsClientDataTest { MaxStreamDuration.newBuilder() .setMaxStreamDuration(Durations.fromSeconds(5L))) .build(); - StructOrError struct = ClientXdsClient.parseRouteAction(proto, false); + StructOrError struct = + ClientXdsClient.parseRouteAction(proto, filterRegistry, false); assertThat(struct.getStruct().timeoutNano()).isEqualTo(TimeUnit.SECONDS.toNanos(5L)); } @@ -430,7 +465,8 @@ public class ClientXdsClientDataTest { io.envoyproxy.envoy.config.route.v3.RouteAction.newBuilder() .setCluster("cluster-foo") .build(); - StructOrError struct = ClientXdsClient.parseRouteAction(proto, false); + StructOrError struct = + ClientXdsClient.parseRouteAction(proto, filterRegistry, false); assertThat(struct.getStruct().timeoutNano()).isNull(); } @@ -465,7 +501,8 @@ public class ClientXdsClientDataTest { .setQueryParameter( QueryParameter.newBuilder().setName("param"))) // unsupported .build(); - StructOrError struct = ClientXdsClient.parseRouteAction(proto, false); + StructOrError struct = + ClientXdsClient.parseRouteAction(proto, filterRegistry, false); List policies = struct.getStruct().hashPolicies(); assertThat(policies).hasSize(2); assertThat(policies.get(0).type()).isEqualTo(HashPolicy.Type.HEADER); @@ -485,39 +522,12 @@ public class ClientXdsClientDataTest { .setName("cluster-foo") .setWeight(UInt32Value.newBuilder().setValue(30)) .build(); - ClusterWeight clusterWeight = ClientXdsClient.parseClusterWeight(proto, false).getStruct(); + ClusterWeight clusterWeight = + ClientXdsClient.parseClusterWeight(proto, filterRegistry, false).getStruct(); assertThat(clusterWeight.name()).isEqualTo("cluster-foo"); assertThat(clusterWeight.weight()).isEqualTo(30); } - @Test - public void parseFaultAbort_withHttpStatus() { - io.envoyproxy.envoy.extensions.filters.http.fault.v3.FaultAbort proto = - io.envoyproxy.envoy.extensions.filters.http.fault.v3.FaultAbort.newBuilder() - .setPercentage(FractionalPercent.newBuilder() - .setNumerator(100).setDenominator(DenominatorType.TEN_THOUSAND)) - .setHttpStatus(400).build(); - FaultAbort res = FaultFilter.parseFaultAbort(proto).config; - assertThat(res.percent().numerator()).isEqualTo(100); - assertThat(res.percent().denominatorType()) - .isEqualTo(FaultConfig.FractionalPercent.DenominatorType.TEN_THOUSAND); - assertThat(res.status().getCode()).isEqualTo(Code.INTERNAL); - } - - @Test - public void parseFaultAbort_withGrpcStatus() { - io.envoyproxy.envoy.extensions.filters.http.fault.v3.FaultAbort proto = - io.envoyproxy.envoy.extensions.filters.http.fault.v3.FaultAbort.newBuilder() - .setPercentage(FractionalPercent.newBuilder() - .setNumerator(600).setDenominator(DenominatorType.MILLION)) - .setGrpcStatus(Code.DEADLINE_EXCEEDED.value()).build(); - FaultAbort faultAbort = FaultFilter.parseFaultAbort(proto).config; - assertThat(faultAbort.percent().numerator()).isEqualTo(600); - assertThat(faultAbort.percent().denominatorType()) - .isEqualTo(FaultConfig.FractionalPercent.DenominatorType.MILLION); - assertThat(faultAbort.status().getCode()).isEqualTo(Code.DEADLINE_EXCEEDED); - } - @Test public void parseLocalityLbEndpoints_withHealthyEndpoints() { io.envoyproxy.envoy.config.endpoint.v3.LocalityLbEndpoints proto = @@ -637,7 +647,7 @@ public class ClientXdsClientDataTest { .setIsOptional(true) .setTypedConfig(Any.pack(StringValue.of("unsupported"))) .build(); - assertThat(ClientXdsClient.parseHttpFilter(httpFilter)).isNull(); + assertThat(ClientXdsClient.parseHttpFilter(httpFilter, filterRegistry, true)).isNull(); } @Test @@ -647,13 +657,181 @@ public class ClientXdsClientDataTest { .setName("unsupported.filter") .setTypedConfig(Any.pack(StringValue.of("string value"))) .build(); - assertThat(ClientXdsClient.parseHttpFilter(httpFilter).getErrorDetail()).isEqualTo( - "HttpFilter [unsupported.filter] is not optional and has an unsupported config type: " - + "type.googleapis.com/google.protobuf.StringValue"); + assertThat(ClientXdsClient.parseHttpFilter(httpFilter, filterRegistry, true) + .getErrorDetail()).isEqualTo( + "HttpFilter [unsupported.filter]" + + "(type.googleapis.com/google.protobuf.StringValue) is required but unsupported " + + "for client"); + } + + @Test + public void parseHttpFilter_routerFilterForClient() { + filterRegistry.register(RouterFilter.INSTANCE); + HttpFilter httpFilter = + HttpFilter.newBuilder() + .setIsOptional(false) + .setName("envoy.router") + .setTypedConfig(Any.pack(Router.getDefaultInstance())) + .build(); + FilterConfig config = ClientXdsClient.parseHttpFilter( + httpFilter, filterRegistry, true /* isForClient */).getStruct(); + assertThat(config.typeUrl()).isEqualTo(RouterFilter.TYPE_URL); + } + + @Test + public void parseHttpFilter_routerFilterForServer() { + filterRegistry.register(RouterFilter.INSTANCE); + HttpFilter httpFilter = + HttpFilter.newBuilder() + .setIsOptional(false) + .setName("envoy.router") + .setTypedConfig(Any.pack(Router.getDefaultInstance())) + .build(); + FilterConfig config = ClientXdsClient.parseHttpFilter( + httpFilter, filterRegistry, false /* isForClient */).getStruct(); + assertThat(config.typeUrl()).isEqualTo(RouterFilter.TYPE_URL); + } + + @Test + public void parseHttpFilter_faultConfigForClient() { + filterRegistry.register(FaultFilter.INSTANCE); + HttpFilter httpFilter = + HttpFilter.newBuilder() + .setIsOptional(false) + .setName("envoy.fault") + .setTypedConfig( + Any.pack( + HTTPFault.newBuilder() + .setDelay( + FaultDelay.newBuilder() + .setFixedDelay(Durations.fromNanos(1234L))) + .setAbort( + FaultAbort.newBuilder() + .setHttpStatus(300) + .setPercentage( + FractionalPercent.newBuilder() + .setNumerator(10) + .setDenominator(DenominatorType.HUNDRED))) + .build())) + .build(); + FilterConfig config = ClientXdsClient.parseHttpFilter( + httpFilter, filterRegistry, true /* isForClient */).getStruct(); + assertThat(config).isInstanceOf(FaultConfig.class); + } + + @Test + public void parseHttpFilter_faultConfigUnsupportedForServer() { + filterRegistry.register(FaultFilter.INSTANCE); + HttpFilter httpFilter = + HttpFilter.newBuilder() + .setIsOptional(false) + .setName("envoy.fault") + .setTypedConfig( + Any.pack( + HTTPFault.newBuilder() + .setDelay( + FaultDelay.newBuilder() + .setFixedDelay(Durations.fromNanos(1234L))) + .setAbort( + FaultAbort.newBuilder() + .setHttpStatus(300) + .setPercentage( + FractionalPercent.newBuilder() + .setNumerator(10) + .setDenominator(DenominatorType.HUNDRED))) + .build())) + .build(); + StructOrError config = + ClientXdsClient.parseHttpFilter(httpFilter, filterRegistry, false /* isForClient */); + assertThat(config.getErrorDetail()).isEqualTo( + "HttpFilter [envoy.fault](" + FaultFilter.TYPE_URL + ") is required but " + + "unsupported for server"); + } + + @Test + public void parseHttpFilter_rbacConfigForServer() { + filterRegistry.register(RbacFilter.INSTANCE); + HttpFilter httpFilter = + HttpFilter.newBuilder() + .setIsOptional(false) + .setName("envoy.auth") + .setTypedConfig( + Any.pack( + io.envoyproxy.envoy.extensions.filters.http.rbac.v3.RBAC.newBuilder() + .setRules( + RBAC.newBuilder() + .setAction(Action.ALLOW) + .putPolicies( + "allow-all", + Policy.newBuilder() + .addPrincipals(Principal.newBuilder().setAny(true)) + .addPermissions(Permission.newBuilder().setAny(true)) + .build()) + .build()) + .build())) + .build(); + FilterConfig config = ClientXdsClient.parseHttpFilter( + httpFilter, filterRegistry, false /* isForClient */).getStruct(); + assertThat(config).isInstanceOf(RbacConfig.class); + } + + @Test + public void parseHttpFilter_rbacConfigUnsupportedForClient() { + filterRegistry.register(RbacFilter.INSTANCE); + HttpFilter httpFilter = + HttpFilter.newBuilder() + .setIsOptional(false) + .setName("envoy.auth") + .setTypedConfig( + Any.pack( + io.envoyproxy.envoy.extensions.filters.http.rbac.v3.RBAC.newBuilder() + .setRules( + RBAC.newBuilder() + .setAction(Action.ALLOW) + .putPolicies( + "allow-all", + Policy.newBuilder() + .addPrincipals(Principal.newBuilder().setAny(true)) + .addPermissions(Permission.newBuilder().setAny(true)) + .build()) + .build()) + .build())) + .build(); + StructOrError config = + ClientXdsClient.parseHttpFilter(httpFilter, filterRegistry, true /* isForClient */); + assertThat(config.getErrorDetail()).isEqualTo( + "HttpFilter [envoy.auth](" + RbacFilter.TYPE_URL + ") is required but " + + "unsupported for client"); + } + + @Test + public void parseOverrideRbacFilterConfig() { + filterRegistry.register(RbacFilter.INSTANCE); + RBACPerRoute rbacPerRoute = + RBACPerRoute.newBuilder() + .setRbac( + io.envoyproxy.envoy.extensions.filters.http.rbac.v3.RBAC.newBuilder() + .setRules( + RBAC.newBuilder() + .setAction(Action.ALLOW) + .putPolicies( + "allow-all", + Policy.newBuilder() + .addPrincipals(Principal.newBuilder().setAny(true)) + .addPermissions(Permission.newBuilder().setAny(true)) + .build()))) + .build(); + Map configOverrides = ImmutableMap.of("envoy.auth", Any.pack(rbacPerRoute)); + Map parsedConfigs = + ClientXdsClient.parseOverrideFilterConfigs(configOverrides, filterRegistry).getStruct(); + assertThat(parsedConfigs).hasSize(1); + assertThat(parsedConfigs).containsKey("envoy.auth"); + assertThat(parsedConfigs.get("envoy.auth")).isInstanceOf(RbacConfig.class); } @Test public void parseOverrideFilterConfigs_unsupportedButOptional() { + filterRegistry.register(FaultFilter.INSTANCE); HTTPFault httpFault = HTTPFault.newBuilder() .setDelay(FaultDelay.newBuilder().setFixedDelay(Durations.fromNanos(3000))) .build(); @@ -665,13 +843,14 @@ public class ClientXdsClientDataTest { .setIsOptional(true).setConfig(Any.pack(StringValue.of("string value"))) .build())); Map parsedConfigs = - ClientXdsClient.parseOverrideFilterConfigs(configOverrides).getStruct(); + ClientXdsClient.parseOverrideFilterConfigs(configOverrides, filterRegistry).getStruct(); assertThat(parsedConfigs).hasSize(1); assertThat(parsedConfigs).containsKey("envoy.fault"); } @Test public void parseOverrideFilterConfigs_unsupportedAndRequired() { + filterRegistry.register(FaultFilter.INSTANCE); HTTPFault httpFault = HTTPFault.newBuilder() .setDelay(FaultDelay.newBuilder().setFixedDelay(Durations.fromNanos(3000))) .build(); @@ -682,20 +861,66 @@ public class ClientXdsClientDataTest { Any.pack(io.envoyproxy.envoy.config.route.v3.FilterConfig.newBuilder() .setIsOptional(false).setConfig(Any.pack(StringValue.of("string value"))) .build())); - assertThat(ClientXdsClient.parseOverrideFilterConfigs(configOverrides).getErrorDetail()) - .isEqualTo( - "HttpFilter [unsupported.filter] is not optional and has an unsupported config type: " - + "type.googleapis.com/google.protobuf.StringValue"); + assertThat(ClientXdsClient.parseOverrideFilterConfigs(configOverrides, filterRegistry) + .getErrorDetail()).isEqualTo( + "HttpFilter [unsupported.filter]" + + "(type.googleapis.com/google.protobuf.StringValue) is required but unsupported"); configOverrides = ImmutableMap.of( "envoy.fault", Any.pack(httpFault), "unsupported.filter", Any.pack(StringValue.of("string value"))); - assertThat(ClientXdsClient.parseOverrideFilterConfigs(configOverrides).getErrorDetail()) - .isEqualTo( - "HttpFilter [unsupported.filter] is not optional and has an unsupported config type: " - + "type.googleapis.com/google.protobuf.StringValue"); + assertThat(ClientXdsClient.parseOverrideFilterConfigs(configOverrides, filterRegistry) + .getErrorDetail()).isEqualTo( + "HttpFilter [unsupported.filter]" + + "(type.googleapis.com/google.protobuf.StringValue) is required but unsupported"); + } + + @Test + public void parseHttpConnectionManager_xffNumTrustedHopsUnsupported() + throws ResourceInvalidException { + HttpConnectionManager hcm = + HttpConnectionManager.newBuilder() + .setXffNumTrustedHops(2) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("HttpConnectionManager with xff_num_trusted_hops unsupported"); + ClientXdsClient.parseHttpConnectionManager( + hcm, new HashSet(), filterRegistry, false /* does not matter */, + true /* does not matter */); + } + + @Test + public void parseHttpConnectionManager_missingRdsAndInlinedRouteConfiguration() + throws ResourceInvalidException { + HttpConnectionManager hcm = + HttpConnectionManager.newBuilder() + .setCommonHttpProtocolOptions( + HttpProtocolOptions.newBuilder() + .setMaxStreamDuration(Durations.fromNanos(1000L))) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("HttpConnectionManager neither has inlined route_config nor RDS"); + ClientXdsClient.parseHttpConnectionManager( + hcm, new HashSet(), filterRegistry, false /* does not matter */, + true /* does not matter */); + } + + @Test + public void parseHttpConnectionManager_duplicateHttpFilters() throws ResourceInvalidException { + HttpConnectionManager hcm = + HttpConnectionManager.newBuilder() + .addHttpFilters( + HttpFilter.newBuilder().setName("envoy.filter.foo").setIsOptional(true)) + .addHttpFilters( + HttpFilter.newBuilder().setName("envoy.filter.foo").setIsOptional(true)) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("HttpConnectionManager contains duplicate HttpFilter: envoy.filter.foo"); + ClientXdsClient.parseHttpConnectionManager( + hcm, new HashSet(), filterRegistry, true /* parseHttpFilter */, + true /* does not matter */); } @Test @@ -773,250 +998,162 @@ public class ClientXdsClientDataTest { } @Test - public void parseServerSideListener_invalidTrafficDirection() { + public void parseServerSideListener_invalidTrafficDirection() throws ResourceInvalidException { Listener listener = Listener.newBuilder() .setName("listener1") .setTrafficDirection(TrafficDirection.OUTBOUND) .build(); - StructOrError struct = - ClientXdsClient.parseServerSideListener(listener, null); - assertThat(struct.getErrorDetail()).isEqualTo("Listener listener1 is not INBOUND"); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("Listener listener1 with invalid traffic direction: OUTBOUND"); + ClientXdsClient.parseServerSideListener( + listener, new HashSet(), null, filterRegistry, true /* does not matter */); } @Test - public void parseServerSideListener_listenerFiltersPresent() { + public void parseServerSideListener_listenerFiltersPresent() throws ResourceInvalidException { Listener listener = Listener.newBuilder() .setName("listener1") .setTrafficDirection(TrafficDirection.INBOUND) .addListenerFilters(ListenerFilter.newBuilder().build()) .build(); - StructOrError struct = - ClientXdsClient.parseServerSideListener(listener, null); - assertThat(struct.getErrorDetail()) - .isEqualTo("Listener listener1 cannot have listener_filters"); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("Listener listener1 cannot have listener_filters"); + ClientXdsClient.parseServerSideListener( + listener, new HashSet(), null, filterRegistry, true /* does not matter */); } @Test - public void parseServerSideListener_useOriginalDst() { + public void parseServerSideListener_useOriginalDst() throws ResourceInvalidException { Listener listener = Listener.newBuilder() .setName("listener1") .setTrafficDirection(TrafficDirection.INBOUND) .setUseOriginalDst(BoolValue.of(true)) .build(); - StructOrError struct = - ClientXdsClient.parseServerSideListener(listener, null); - assertThat(struct.getErrorDetail()) - .isEqualTo("Listener listener1 cannot have use_original_dst set to true"); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("Listener listener1 cannot have use_original_dst set to true"); + ClientXdsClient.parseServerSideListener( + listener, new HashSet(), null, filterRegistry, true /* does not matter */); } @Test - public void parseServerSideListener_noHcm() { - Listener listener = - Listener.newBuilder() - .setName("listener1") - .setTrafficDirection(TrafficDirection.INBOUND) - .addFilterChains(FilterChain.newBuilder().build()) - .build(); - StructOrError struct = - ClientXdsClient.parseServerSideListener(listener, null); - assertThat(struct.getErrorDetail()) - .isEqualTo("filerChain has to have envoy.http_connection_manager"); - } - - @Test - public void parseServerSideListener_duplicateFilterName() { + public void parseFilterChain_noHcm() throws ResourceInvalidException { FilterChain filterChain = - buildFilterChain( - Filter.newBuilder() - .setName("envoy.http_connection_manager") - .setTypedConfig(Any.pack(HttpConnectionManager.getDefaultInstance())) - .build(), - Filter.newBuilder() - .setName("envoy.http_connection_manager") - .setTypedConfig(Any.pack(HttpConnectionManager.getDefaultInstance())) - .build()); - Listener listener = - Listener.newBuilder() - .setName("listener1") - .setTrafficDirection(TrafficDirection.INBOUND) - .addFilterChains(filterChain) + FilterChain.newBuilder() + .setName("filter-chain-foo") + .setFilterChainMatch(FilterChainMatch.getDefaultInstance()) + .setTransportSocket(TransportSocket.getDefaultInstance()) .build(); - StructOrError struct = - ClientXdsClient.parseServerSideListener(listener, null); - assertThat(struct.getErrorDetail()) - .isEqualTo("filerChain has non-unique filter name:envoy.http_connection_manager"); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage( + "FilterChain filter-chain-foo missing required HttpConnectionManager filter"); + ClientXdsClient.parseFilterChain( + filterChain, new HashSet(), null, filterRegistry, true /* does not matter */); } @Test - public void parseServerSideListener_configDiscoveryFilter() { - Filter filter = - Filter.newBuilder() - .setName("envoy.http_connection_manager") - .setConfigDiscovery(ExtensionConfigSource.newBuilder().build()) + public void parseFilterChain_duplicateFilter() throws ResourceInvalidException { + Filter filter = buildHttpConnectionManagerFilter( + HttpFilter.newBuilder().setName("http-filter-foo").setIsOptional(true).build()); + FilterChain filterChain = + FilterChain.newBuilder() + .setName("filter-chain-foo") + .setFilterChainMatch(FilterChainMatch.getDefaultInstance()) + .setTransportSocket(TransportSocket.getDefaultInstance()) + .addAllFilters(Arrays.asList(filter, filter)) .build(); - FilterChain filterChain = buildFilterChain(filter); - Listener listener = - Listener.newBuilder() - .setName("listener1") - .setTrafficDirection(TrafficDirection.INBOUND) - .addFilterChains(filterChain) - .build(); - StructOrError struct = - ClientXdsClient.parseServerSideListener(listener, null); - assertThat(struct.getErrorDetail()) - .isEqualTo("filter envoy.http_connection_manager with config_discovery not supported"); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage( + "FilterChain filter-chain-foo with duplicated filter: envoy.http_connection_manager"); + ClientXdsClient.parseFilterChain( + filterChain, new HashSet(), null, filterRegistry, true /* does not matter */); } @Test - public void parseServerSideListener_expectTypedConfigFilter() { + public void parseFilterChain_filterMissingTypedConfig() throws ResourceInvalidException { Filter filter = Filter.newBuilder().setName("envoy.http_connection_manager").build(); - FilterChain filterChain = buildFilterChain(filter); - Listener listener = - Listener.newBuilder() - .setName("listener1") - .setTrafficDirection(TrafficDirection.INBOUND) - .addFilterChains(filterChain) + FilterChain filterChain = + FilterChain.newBuilder() + .setName("filter-chain-foo") + .setFilterChainMatch(FilterChainMatch.getDefaultInstance()) + .setTransportSocket(TransportSocket.getDefaultInstance()) + .addFilters(filter) .build(); - StructOrError struct = - ClientXdsClient.parseServerSideListener(listener, null); - assertThat(struct.getErrorDetail()) - .isEqualTo("filter envoy.http_connection_manager expected to have typed_config"); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage( + "FilterChain filter-chain-foo contains filter envoy.http_connection_manager " + + "without typed_config"); + ClientXdsClient.parseFilterChain( + filterChain, new HashSet(), null, filterRegistry, true /* does not matter */); } @Test - public void parseServerSideListener_wrongTypeUrl() { + public void parseFilterChain_unsupportedFilter() throws ResourceInvalidException { Filter filter = Filter.newBuilder() - .setName("envoy.http_connection_manager") - .setTypedConfig(Any.newBuilder().setTypeUrl("badTypeUrl")) + .setName("unsupported") + .setTypedConfig(Any.newBuilder().setTypeUrl("unsupported-type-url")) .build(); - FilterChain filterChain = buildFilterChain(filter); - Listener listener = - Listener.newBuilder() - .setName("listener1") - .setTrafficDirection(TrafficDirection.INBOUND) - .addFilterChains(filterChain) + FilterChain filterChain = + FilterChain.newBuilder() + .setName("filter-chain-foo") + .setFilterChainMatch(FilterChainMatch.getDefaultInstance()) + .setTransportSocket(TransportSocket.getDefaultInstance()) + .addFilters(filter) .build(); - StructOrError struct = - ClientXdsClient.parseServerSideListener(listener, null); - assertThat(struct.getErrorDetail()) - .isEqualTo( - "filter envoy.http_connection_manager with unsupported typed_config type:badTypeUrl"); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage( + "FilterChain filter-chain-foo contains filter unsupported with unsupported " + + "typed_config type unsupported-type-url"); + ClientXdsClient.parseFilterChain( + filterChain, new HashSet(), null, filterRegistry, true /* does not matter */); } @Test - public void parseServerSideListener_duplicateHttpFilter() { - Filter filter = - buildHttpConnectionManager( - "envoy.http_connection_manager", - HttpFilter.newBuilder().setName("hf").setIsOptional(true).build(), - HttpFilter.newBuilder().setName("hf").setIsOptional(true).build()); - FilterChain filterChain = buildFilterChain(filter); - Listener listener = - Listener.newBuilder() - .setName("listener1") - .setTrafficDirection(TrafficDirection.INBOUND) - .addFilterChains(filterChain) + public void parseFilterChain_noName_generatedUuid() throws ResourceInvalidException { + FilterChain filterChain1 = + FilterChain.newBuilder() + .setFilterChainMatch(FilterChainMatch.getDefaultInstance()) + .addFilters(buildHttpConnectionManagerFilter( + HttpFilter.newBuilder() + .setName("http-filter-foo") + .setIsOptional(true) + .build())) .build(); - StructOrError struct = - ClientXdsClient.parseServerSideListener(listener, null); - assertThat(struct.getErrorDetail()) - .isEqualTo("http-connection-manager has non-unique http-filter name:hf"); + FilterChain filterChain2 = + FilterChain.newBuilder() + .setFilterChainMatch(FilterChainMatch.getDefaultInstance()) + .addFilters(buildHttpConnectionManagerFilter( + HttpFilter.newBuilder() + .setName("http-filter-bar") + .setIsOptional(true) + .build())) + .build(); + + EnvoyServerProtoData.FilterChain parsedFilterChain1 = ClientXdsClient.parseFilterChain( + filterChain1, new HashSet(), null, filterRegistry, true /* does not matter */); + EnvoyServerProtoData.FilterChain parsedFilterChain2 = ClientXdsClient.parseFilterChain( + filterChain2, new HashSet(), null, filterRegistry, true /* does not matter */); + assertThat(parsedFilterChain1.getName()).isNotEqualTo(parsedFilterChain2.getName()); } - @Test - public void parseServerSideListener_configDiscoveryHttpFilter() { - Filter filter = - buildHttpConnectionManager( - "envoy.http_connection_manager", - HttpFilter.newBuilder() - .setName("envoy.router") - .setConfigDiscovery(ExtensionConfigSource.newBuilder().build()) - .build()); - FilterChain filterChain = buildFilterChain(filter); - Listener listener = - Listener.newBuilder() - .setName("listener1") - .setTrafficDirection(TrafficDirection.INBOUND) - .addFilterChains(filterChain) - .build(); - StructOrError struct = - ClientXdsClient.parseServerSideListener(listener, null); - assertThat(struct.getErrorDetail()) - .isEqualTo( - "http-connection-manager http-filter envoy.router uses " - + "config-discovery which is unsupported"); - } - - @Test - public void parseServerSideListener_badTypeUrlHttpFilter() { - HTTPFault fault = HTTPFault.newBuilder().build(); - Filter filter = - buildHttpConnectionManager( - "envoy.http_connection_manager", - HttpFilter.newBuilder() - .setName("envoy.router") - .setTypedConfig(Any.pack(fault, "type.googleapis.com")) - .build()); - FilterChain filterChain = buildFilterChain(filter); - Listener listener = - Listener.newBuilder() - .setName("listener1") - .setTrafficDirection(TrafficDirection.INBOUND) - .addFilterChains(filterChain) - .build(); - StructOrError struct = - ClientXdsClient.parseServerSideListener(listener, null); - assertThat(struct.getErrorDetail()) - .isEqualTo( - "http-connection-manager http-filter envoy.router has unsupported typed-config type:" - + "type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault"); - } - - @Test - public void parseServerSideListener_missingTypeUrlHttpFilter() { - Filter filter = - buildHttpConnectionManager( - "envoy.http_connection_manager", - HttpFilter.newBuilder().setName("envoy.filters.http.router").build()); - FilterChain filterChain = buildFilterChain(filter); - Listener listener = - Listener.newBuilder() - .setName("listener1") - .setTrafficDirection(TrafficDirection.INBOUND) - .addFilterChains(filterChain) - .build(); - StructOrError struct = - ClientXdsClient.parseServerSideListener(listener, null); - assertThat(struct.getErrorDetail()) - .isEqualTo( - "http-connection-manager http-filter envoy.filters.http.router should have " - + "typed-config type " - + "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"); - } - - static Filter buildHttpConnectionManager(String name, HttpFilter... httpFilters) { + private static Filter buildHttpConnectionManagerFilter(HttpFilter... httpFilters) { return Filter.newBuilder() - .setName(name) + .setName("envoy.http_connection_manager") .setTypedConfig( Any.pack( HttpConnectionManager.newBuilder() + .setRds( + Rds.newBuilder() + .setRouteConfigName("route-config.googleapis.com") + .setConfigSource( + ConfigSource.newBuilder() + .setAds(AggregatedConfigSource.getDefaultInstance()))) .addAllHttpFilters(Arrays.asList(httpFilters)) .build(), "type.googleapis.com")) .build(); } - - static FilterChain buildFilterChain(Filter... filters) { - return FilterChain.newBuilder() - .setFilterChainMatch( - FilterChainMatch.newBuilder() - .build()) - .setTransportSocket(TransportSocket.getDefaultInstance()) - .addAllFilters(Arrays.asList(filters)) - .build(); - } } diff --git a/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java b/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java index 8536711884..f998396d03 100644 --- a/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java +++ b/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java @@ -38,7 +38,6 @@ import com.google.common.collect.Iterables; import com.google.protobuf.Any; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.Message; -import io.envoyproxy.envoy.config.listener.v3.Listener; import io.envoyproxy.envoy.config.route.v3.FilterConfig; import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.SdsSecretConfig; import io.grpc.BindableService; @@ -61,6 +60,7 @@ import io.grpc.xds.Endpoints.DropOverload; import io.grpc.xds.Endpoints.LbEndpoint; import io.grpc.xds.Endpoints.LocalityLbEndpoints; import io.grpc.xds.EnvoyProtoData.Node; +import io.grpc.xds.EnvoyServerProtoData.FilterChain; import io.grpc.xds.FaultConfig.FractionalPercent.DenominatorType; import io.grpc.xds.LoadStatsManager2.ClusterDropStats; import io.grpc.xds.XdsClient.CdsResourceWatcher; @@ -182,9 +182,10 @@ public abstract class ClientXdsClientTestBase { private static final int VHOST_SIZE = 2; // LDS test resources. - private final Any testListenerVhosts = Any.pack(mf.buildListener(LDS_RESOURCE, + private final Any testListenerVhosts = Any.pack(mf.buildListenerWithApiListener(LDS_RESOURCE, mf.buildRouteConfiguration("do not care", mf.buildOpaqueVirtualHosts(VHOST_SIZE)))); - private final Any testListenerRds = Any.pack(mf.buildListenerForRds(LDS_RESOURCE, RDS_RESOURCE)); + private final Any testListenerRds = + Any.pack(mf.buildListenerWithApiListenerForRds(LDS_RESOURCE, RDS_RESOURCE)); // RDS test resources. private final Any testRouteConfig = @@ -420,7 +421,7 @@ public abstract class ClientXdsClientTestBase { public void ldsResourceNotFound() { DiscoveryRpcCall call = startResourceWatcher(LDS, LDS_RESOURCE, ldsResourceWatcher); - Any listener = Any.pack(mf.buildListener("bar.googleapis.com", + Any listener = Any.pack(mf.buildListenerWithApiListener("bar.googleapis.com", mf.buildRouteConfiguration("route-bar.googleapis.com", mf.buildOpaqueVirtualHosts(1)))); call.sendResponse(LDS, listener, VERSION_1, "0000"); @@ -494,9 +495,9 @@ public abstract class ClientXdsClientTestBase { // LDS -> {A, B, C}, version 1 ImmutableMap resourcesV1 = ImmutableMap.of( - "A", Any.pack(mf.buildListenerForRds("A", "A.1")), - "B", Any.pack(mf.buildListenerForRds("B", "B.1")), - "C", Any.pack(mf.buildListenerForRds("C", "C.1"))); + "A", Any.pack(mf.buildListenerWithApiListenerForRds("A", "A.1")), + "B", Any.pack(mf.buildListenerWithApiListenerForRds("B", "B.1")), + "C", Any.pack(mf.buildListenerWithApiListenerForRds("C", "C.1"))); call.sendResponse(LDS, resourcesV1.values().asList(), VERSION_1, "0000"); // {A, B, C} -> ACK, version 1 verifyResourceMetadataAcked(LDS, "A", resourcesV1.get("A"), VERSION_1, TIME_INCREMENT); @@ -507,8 +508,8 @@ public abstract class ClientXdsClientTestBase { // LDS -> {A, B}, version 2 // Failed to parse endpoint B ImmutableMap resourcesV2 = ImmutableMap.of( - "A", Any.pack(mf.buildListenerForRds("A", "A.2")), - "B", Any.pack(mf.buildListenerInvalid("B"))); + "A", Any.pack(mf.buildListenerWithApiListenerForRds("A", "A.2")), + "B", Any.pack(mf.buildListenerWithApiListenerInvalid("B"))); call.sendResponse(LDS, resourcesV2.values().asList(), VERSION_2, "0001"); // {A, B} -> NACK, version 1, rejected version 2, rejected reason: Failed to parse B // {C} -> ACK, version 1 @@ -522,8 +523,8 @@ public abstract class ClientXdsClientTestBase { // LDS -> {B, C} version 3 ImmutableMap resourcesV3 = ImmutableMap.of( - "B", Any.pack(mf.buildListenerForRds("B", "B.3")), - "C", Any.pack(mf.buildListenerForRds("C", "C.3"))); + "B", Any.pack(mf.buildListenerWithApiListenerForRds("B", "B.3")), + "C", Any.pack(mf.buildListenerWithApiListenerForRds("C", "C.3"))); call.sendResponse(LDS, resourcesV3.values().asList(), VERSION_3, "0002"); // {A} -> NACK, version 1, rejected version 2, rejected reason: Failed to parse B // {B, C} -> ACK, version 3 @@ -542,7 +543,8 @@ public abstract class ClientXdsClientTestBase { call.sendResponse(LDS, testListenerVhosts, VERSION_1, "0000"); call.verifyRequest(LDS, LDS_RESOURCE, VERSION_1, "0000", NODE); verify(ldsResourceWatcher).onChanged(ldsUpdateCaptor.capture()); - assertThat(ldsUpdateCaptor.getValue().virtualHosts).hasSize(VHOST_SIZE); + assertThat(ldsUpdateCaptor.getValue().httpConnectionManager().virtualHosts()) + .hasSize(VHOST_SIZE); assertThat(fakeClock.getPendingTasks(LDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER)).isEmpty(); verifyResourceMetadataAcked(LDS, LDS_RESOURCE, testListenerVhosts, VERSION_1, TIME_INCREMENT); verifySubscribedResourcesMetadataSizes(1, 0, 0, 0); @@ -556,7 +558,8 @@ public abstract class ClientXdsClientTestBase { // Client sends an ACK LDS request. call.verifyRequest(LDS, LDS_RESOURCE, VERSION_1, "0000", NODE); verify(ldsResourceWatcher).onChanged(ldsUpdateCaptor.capture()); - assertThat(ldsUpdateCaptor.getValue().rdsName).isEqualTo(RDS_RESOURCE); + assertThat(ldsUpdateCaptor.getValue().httpConnectionManager().rdsName()) + .isEqualTo(RDS_RESOURCE); assertThat(fakeClock.getPendingTasks(LDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER)).isEmpty(); verifyResourceMetadataAcked(LDS, LDS_RESOURCE, testListenerRds, VERSION_1, TIME_INCREMENT); verifySubscribedResourcesMetadataSizes(1, 0, 0, 0); @@ -573,7 +576,8 @@ public abstract class ClientXdsClientTestBase { LdsResourceWatcher watcher = mock(LdsResourceWatcher.class); xdsClient.watchLdsResource(LDS_RESOURCE, watcher); verify(watcher).onChanged(ldsUpdateCaptor.capture()); - assertThat(ldsUpdateCaptor.getValue().rdsName).isEqualTo(RDS_RESOURCE); + assertThat(ldsUpdateCaptor.getValue().httpConnectionManager().rdsName()) + .isEqualTo(RDS_RESOURCE); call.verifyNoMoreRequest(); verifyResourceMetadataAcked(LDS, LDS_RESOURCE, testListenerRds, VERSION_1, TIME_INCREMENT); verifySubscribedResourcesMetadataSizes(1, 0, 0, 0); @@ -602,14 +606,16 @@ public abstract class ClientXdsClientTestBase { call.sendResponse(LDS, testListenerVhosts, VERSION_1, "0000"); call.verifyRequest(LDS, LDS_RESOURCE, VERSION_1, "0000", NODE); verify(ldsResourceWatcher).onChanged(ldsUpdateCaptor.capture()); - assertThat(ldsUpdateCaptor.getValue().virtualHosts).hasSize(VHOST_SIZE); + assertThat(ldsUpdateCaptor.getValue().httpConnectionManager().virtualHosts()) + .hasSize(VHOST_SIZE); verifyResourceMetadataAcked(LDS, LDS_RESOURCE, testListenerVhosts, VERSION_1, TIME_INCREMENT); // Updated LDS response. call.sendResponse(LDS, testListenerRds, VERSION_2, "0001"); call.verifyRequest(LDS, LDS_RESOURCE, VERSION_2, "0001", NODE); verify(ldsResourceWatcher, times(2)).onChanged(ldsUpdateCaptor.capture()); - assertThat(ldsUpdateCaptor.getValue().rdsName).isEqualTo(RDS_RESOURCE); + assertThat(ldsUpdateCaptor.getValue().httpConnectionManager().rdsName()) + .isEqualTo(RDS_RESOURCE); verifyResourceMetadataAcked(LDS, LDS_RESOURCE, testListenerRds, VERSION_2, TIME_INCREMENT * 2); verifySubscribedResourcesMetadataSizes(1, 0, 0, 0); } @@ -619,7 +625,7 @@ public abstract class ClientXdsClientTestBase { Assume.assumeTrue(useProtocolV3()); DiscoveryRpcCall call = startResourceWatcher(LDS, LDS_RESOURCE, ldsResourceWatcher); Any listener = Any.pack( - mf.buildListener( + mf.buildListenerWithApiListener( LDS_RESOURCE, mf.buildRouteConfiguration( "do not care", @@ -657,9 +663,10 @@ public abstract class ClientXdsClientTestBase { verifySubscribedResourcesMetadataSizes(1, 0, 0, 0); LdsUpdate ldsUpdate = ldsUpdateCaptor.getValue(); - assertThat(ldsUpdate.virtualHosts).hasSize(2); - assertThat(ldsUpdate.filterChain.get(0).name).isEqualTo("envoy.fault"); - FaultConfig faultConfig = (FaultConfig) ldsUpdate.virtualHosts.get(0) + assertThat(ldsUpdate.httpConnectionManager().virtualHosts()).hasSize(2); + assertThat(ldsUpdate.httpConnectionManager().httpFilterConfigs().get(0).name) + .isEqualTo("envoy.fault"); + FaultConfig faultConfig = (FaultConfig) ldsUpdate.httpConnectionManager().virtualHosts().get(0) .filterConfigOverrides().get("envoy.fault"); assertThat(faultConfig.faultDelay().delayNanos()).isEqualTo(300); assertThat(faultConfig.faultDelay().percent().numerator()).isEqualTo(1000); @@ -667,7 +674,7 @@ public abstract class ClientXdsClientTestBase { .isEqualTo(DenominatorType.MILLION); assertThat(faultConfig.faultAbort()).isNull(); assertThat(faultConfig.maxActiveFaults()).isEqualTo(100); - faultConfig = (FaultConfig) ldsUpdate.virtualHosts.get(1) + faultConfig = (FaultConfig) ldsUpdate.httpConnectionManager().virtualHosts().get(1) .filterConfigOverrides().get("envoy.fault"); assertThat(faultConfig.faultDelay()).isNull(); assertThat(faultConfig.faultAbort().status().getCode()).isEqualTo(Status.Code.UNAVAILABLE); @@ -686,7 +693,8 @@ public abstract class ClientXdsClientTestBase { call.sendResponse(LDS, testListenerVhosts, VERSION_1, "0000"); call.verifyRequest(LDS, LDS_RESOURCE, VERSION_1, "0000", NODE); verify(ldsResourceWatcher).onChanged(ldsUpdateCaptor.capture()); - assertThat(ldsUpdateCaptor.getValue().virtualHosts).hasSize(VHOST_SIZE); + assertThat(ldsUpdateCaptor.getValue().httpConnectionManager().virtualHosts()) + .hasSize(VHOST_SIZE); verifyResourceMetadataAcked(LDS, LDS_RESOURCE, testListenerVhosts, VERSION_1, TIME_INCREMENT); verifySubscribedResourcesMetadataSizes(1, 0, 0, 0); @@ -721,19 +729,22 @@ public abstract class ClientXdsClientTestBase { verifyResourceMetadataDoesNotExist(LDS, ldsResourceTwo); verifySubscribedResourcesMetadataSizes(2, 0, 0, 0); - Any listenerTwo = Any.pack(mf.buildListenerForRds(ldsResourceTwo, RDS_RESOURCE)); + Any listenerTwo = Any.pack(mf.buildListenerWithApiListenerForRds(ldsResourceTwo, RDS_RESOURCE)); call.sendResponse(LDS, ImmutableList.of(testListenerVhosts, listenerTwo), VERSION_1, "0000"); // ldsResourceWatcher called with listenerVhosts. verify(ldsResourceWatcher).onChanged(ldsUpdateCaptor.capture()); - assertThat(ldsUpdateCaptor.getValue().virtualHosts).hasSize(VHOST_SIZE); + assertThat(ldsUpdateCaptor.getValue().httpConnectionManager().virtualHosts()) + .hasSize(VHOST_SIZE); // watcher1 called with listenerTwo. verify(watcher1).onChanged(ldsUpdateCaptor.capture()); - assertThat(ldsUpdateCaptor.getValue().rdsName).isEqualTo(RDS_RESOURCE); - assertThat(ldsUpdateCaptor.getValue().virtualHosts).isNull(); + assertThat(ldsUpdateCaptor.getValue().httpConnectionManager().rdsName()) + .isEqualTo(RDS_RESOURCE); + assertThat(ldsUpdateCaptor.getValue().httpConnectionManager().virtualHosts()).isNull(); // watcher2 called with listenerTwo. verify(watcher2).onChanged(ldsUpdateCaptor.capture()); - assertThat(ldsUpdateCaptor.getValue().rdsName).isEqualTo(RDS_RESOURCE); - assertThat(ldsUpdateCaptor.getValue().virtualHosts).isNull(); + assertThat(ldsUpdateCaptor.getValue().httpConnectionManager().rdsName()) + .isEqualTo(RDS_RESOURCE); + assertThat(ldsUpdateCaptor.getValue().httpConnectionManager().virtualHosts()).isNull(); // Metadata of both listeners is stored. verifyResourceMetadataAcked(LDS, LDS_RESOURCE, testListenerVhosts, VERSION_1, TIME_INCREMENT); verifyResourceMetadataAcked(LDS, ldsResourceTwo, listenerTwo, VERSION_1, TIME_INCREMENT); @@ -933,7 +944,7 @@ public abstract class ClientXdsClientTestBase { } @Test - public void rdsResourceDeletedByLds() { + public void rdsResourceDeletedByLdsApiListener() { xdsClient.watchLdsResource(LDS_RESOURCE, ldsResourceWatcher); xdsClient.watchRdsResource(RDS_RESOURCE, rdsResourceWatcher); verifyResourceMetadataRequested(LDS, LDS_RESOURCE); @@ -943,7 +954,8 @@ public abstract class ClientXdsClientTestBase { DiscoveryRpcCall call = resourceDiscoveryCalls.poll(); call.sendResponse(LDS, testListenerRds, VERSION_1, "0000"); verify(ldsResourceWatcher).onChanged(ldsUpdateCaptor.capture()); - assertThat(ldsUpdateCaptor.getValue().rdsName).isEqualTo(RDS_RESOURCE); + assertThat(ldsUpdateCaptor.getValue().httpConnectionManager().rdsName()) + .isEqualTo(RDS_RESOURCE); verifyResourceMetadataAcked(LDS, LDS_RESOURCE, testListenerRds, VERSION_1, TIME_INCREMENT); verifyResourceMetadataRequested(RDS, RDS_RESOURCE); verifySubscribedResourcesMetadataSizes(1, 0, 1, 0); @@ -957,7 +969,8 @@ public abstract class ClientXdsClientTestBase { call.sendResponse(LDS, testListenerVhosts, VERSION_2, "0001"); verify(ldsResourceWatcher, times(2)).onChanged(ldsUpdateCaptor.capture()); - assertThat(ldsUpdateCaptor.getValue().virtualHosts).hasSize(VHOST_SIZE); + assertThat(ldsUpdateCaptor.getValue().httpConnectionManager().virtualHosts()) + .hasSize(VHOST_SIZE); verify(rdsResourceWatcher).onResourceDoesNotExist(RDS_RESOURCE); verifyResourceMetadataDoesNotExist(RDS, RDS_RESOURCE); verifyResourceMetadataAcked( @@ -965,6 +978,68 @@ public abstract class ClientXdsClientTestBase { verifySubscribedResourcesMetadataSizes(1, 0, 1, 0); } + @Test + public void rdsResourcesDeletedByLdsTcpListener() { + Assume.assumeTrue(useProtocolV3()); + xdsClient.watchLdsResource(LISTENER_RESOURCE, ldsResourceWatcher); + xdsClient.watchRdsResource(RDS_RESOURCE, rdsResourceWatcher); + verifyResourceMetadataRequested(LDS, LISTENER_RESOURCE); + verifyResourceMetadataRequested(RDS, RDS_RESOURCE); + verifySubscribedResourcesMetadataSizes(1, 0, 1, 0); + + Message hcmFilter = mf.buildHttpConnectionManagerFilter( + RDS_RESOURCE, null, Collections.emptyList()); + Message downstreamTlsContext = CommonTlsContextTestsUtil.buildTestDownstreamTlsContext( + "google-sds-config-default", "ROOTCA"); + Message filterChain = mf.buildFilterChain( + Collections.emptyList(), downstreamTlsContext, hcmFilter); + Any packedListener = + Any.pack(mf.buildListenerWithFilterChain(LISTENER_RESOURCE, 7000, "0.0.0.0", filterChain)); + + // Simulates receiving the requested LDS resource as a TCP listener with a filter chain + // referencing RDS_RESOURCE. + DiscoveryRpcCall call = resourceDiscoveryCalls.poll(); + call.sendResponse(LDS, packedListener, VERSION_1, "0000"); + verify(ldsResourceWatcher).onChanged(ldsUpdateCaptor.capture()); + + assertThat(ldsUpdateCaptor.getValue().listener().getFilterChains()).hasSize(1); + FilterChain parsedFilterChain = Iterables.getOnlyElement( + ldsUpdateCaptor.getValue().listener().getFilterChains()); + assertThat(parsedFilterChain.getHttpConnectionManager().rdsName()).isEqualTo(RDS_RESOURCE); + verifyResourceMetadataAcked(LDS, LISTENER_RESOURCE, packedListener, VERSION_1, TIME_INCREMENT); + verifyResourceMetadataRequested(RDS, RDS_RESOURCE); + verifySubscribedResourcesMetadataSizes(1, 0, 1, 0); + + // Simulates receiving the requested RDS resource. + call.sendResponse(RDS, testRouteConfig, VERSION_1, "0000"); + verify(rdsResourceWatcher).onChanged(rdsUpdateCaptor.capture()); + assertThat(rdsUpdateCaptor.getValue().virtualHosts).hasSize(VHOST_SIZE); + verifyResourceMetadataAcked(RDS, RDS_RESOURCE, testRouteConfig, VERSION_1, TIME_INCREMENT * 2); + + // Simulates receiving an updated version of the requested LDS resource as a TCP listener + // with a filter chain containing inlined RouteConfiguration. + hcmFilter = mf.buildHttpConnectionManagerFilter( + null, + mf.buildRouteConfiguration( + "route-bar.googleapis.com", mf.buildOpaqueVirtualHosts(VHOST_SIZE)), + Collections.emptyList()); + filterChain = mf.buildFilterChain( + Collections.emptyList(), downstreamTlsContext, hcmFilter); + packedListener = + Any.pack(mf.buildListenerWithFilterChain(LISTENER_RESOURCE, 7000, "0.0.0.0", filterChain)); + call.sendResponse(LDS, packedListener, VERSION_2, "0001"); + verify(ldsResourceWatcher, times(2)).onChanged(ldsUpdateCaptor.capture()); + assertThat(ldsUpdateCaptor.getValue().listener().getFilterChains()).hasSize(1); + parsedFilterChain = Iterables.getOnlyElement( + ldsUpdateCaptor.getValue().listener().getFilterChains()); + assertThat(parsedFilterChain.getHttpConnectionManager().virtualHosts()).hasSize(VHOST_SIZE); + verify(rdsResourceWatcher).onResourceDoesNotExist(RDS_RESOURCE); + verifyResourceMetadataDoesNotExist(RDS, RDS_RESOURCE); + verifyResourceMetadataAcked( + LDS, LISTENER_RESOURCE, packedListener, VERSION_2, TIME_INCREMENT * 3); + verifySubscribedResourcesMetadataSizes(1, 0, 1, 0); + } + @Test public void multipleRdsWatchers() { String rdsResourceTwo = "route-bar.googleapis.com"; @@ -1873,7 +1948,7 @@ public abstract class ClientXdsClientTestBase { call.verifyRequest(EDS, EDS_RESOURCE, "", "", NODE); List listeners = ImmutableList.of( - Any.pack(mf.buildListener(LDS_RESOURCE, + Any.pack(mf.buildListenerWithApiListener(LDS_RESOURCE, mf.buildRouteConfiguration("do not care", mf.buildOpaqueVirtualHosts(2))))); call.sendResponse(LDS, listeners, "63", "3242"); call.verifyRequest(LDS, LDS_RESOURCE, "63", "3242", NODE); @@ -2018,36 +2093,34 @@ public abstract class ClientXdsClientTestBase { } @Test - public void serverSideListenerFound() throws InvalidProtocolBufferException { + public void serverSideListenerFound() { Assume.assumeTrue(useProtocolV3()); ClientXdsClientTestBase.DiscoveryRpcCall call = startResourceWatcher(LDS, LISTENER_RESOURCE, ldsResourceWatcher); + Message hcmFilter = mf.buildHttpConnectionManagerFilter( + "route-foo.googleapis.com", null, Collections.emptyList()); + Message downstreamTlsContext = CommonTlsContextTestsUtil.buildTestDownstreamTlsContext( + "google-sds-config-default", "ROOTCA"); + Message filterChain = mf.buildFilterChain( + Collections.emptyList(), downstreamTlsContext, hcmFilter); Message listener = - mf.buildListenerWithFilterChain( - LISTENER_RESOURCE, 7000, "0.0.0.0", "google-sds-config-default", "ROOTCA"); + mf.buildListenerWithFilterChain(LISTENER_RESOURCE, 7000, "0.0.0.0", filterChain); List listeners = ImmutableList.of(Any.pack(listener)); call.sendResponse(ResourceType.LDS, listeners, "0", "0000"); // Client sends an ACK LDS request. call.verifyRequest( ResourceType.LDS, Collections.singletonList(LISTENER_RESOURCE), "0", "0000", NODE); verify(ldsResourceWatcher).onChanged(ldsUpdateCaptor.capture()); - assertThat(ldsUpdateCaptor.getValue().listener) - .isEqualTo(EnvoyServerProtoData.Listener - .fromEnvoyProtoListener((Listener) listener, tlsContextManager)); - - listener = - mf.buildListenerWithFilterChain( - LISTENER_RESOURCE, 7000, "0.0.0.0", "CERT2", "ROOTCA2"); - listeners = ImmutableList.of(Any.pack(listener)); - call.sendResponse(ResourceType.LDS, listeners, "1", "0001"); - - // Client sends an ACK LDS request. - call.verifyRequest( - ResourceType.LDS, Collections.singletonList(LISTENER_RESOURCE), "1", "0001", NODE); - verify(ldsResourceWatcher, times(2)).onChanged(ldsUpdateCaptor.capture()); - assertThat(ldsUpdateCaptor.getValue().listener) - .isEqualTo(EnvoyServerProtoData.Listener - .fromEnvoyProtoListener((Listener) listener, tlsContextManager)); + EnvoyServerProtoData.Listener parsedListener = ldsUpdateCaptor.getValue().listener(); + assertThat(parsedListener.getName()).isEqualTo(LISTENER_RESOURCE); + assertThat(parsedListener.getAddress()).isEqualTo("0.0.0.0:7000"); + assertThat(parsedListener.getDefaultFilterChain()).isNull(); + assertThat(parsedListener.getFilterChains()).hasSize(1); + FilterChain parsedFilterChain = Iterables.getOnlyElement(parsedListener.getFilterChains()); + assertThat(parsedFilterChain.getFilterChainMatch().getApplicationProtocols()).isEmpty(); + assertThat(parsedFilterChain.getHttpConnectionManager().rdsName()) + .isEqualTo("route-foo.googleapis.com"); + assertThat(parsedFilterChain.getHttpConnectionManager().httpFilterConfigs()).isEmpty(); assertThat(fakeClock.getPendingTasks(LDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER)).isEmpty(); } @@ -2057,18 +2130,14 @@ public abstract class ClientXdsClientTestBase { Assume.assumeTrue(useProtocolV3()); ClientXdsClientTestBase.DiscoveryRpcCall call = startResourceWatcher(LDS, LISTENER_RESOURCE, ldsResourceWatcher); - final Message filterChainInbound = - mf.buildFilterChain( - Arrays.asList("managed-mtls"), - CommonTlsContextTestsUtil.buildTestDownstreamTlsContext( - "google-sds-config-default", "ROOTCA"), - mf.buildTestFilter("envoy.http_connection_manager")); - Message listener = - mf.buildListenerWithFilterChain( - "grpc/server?xds.resource.listening_address=0.0.0.0:8000", - 7000, - "0.0.0.0", - filterChainInbound); + Message hcmFilter = mf.buildHttpConnectionManagerFilter( + "route-foo.googleapis.com", null, Collections.emptyList()); + Message downstreamTlsContext = CommonTlsContextTestsUtil.buildTestDownstreamTlsContext( + "google-sds-config-default", "ROOTCA"); + Message filterChain = mf.buildFilterChain( + Collections.singletonList("managed-mtls"), downstreamTlsContext, hcmFilter); + Message listener = mf.buildListenerWithFilterChain( + "grpc/server?xds.resource.listening_address=0.0.0.0:8000", 7000, "0.0.0.0", filterChain); List listeners = ImmutableList.of(Any.pack(listener)); call.sendResponse(ResourceType.LDS, listeners, "0", "0000"); // Client sends an ACK LDS request. @@ -2162,16 +2231,18 @@ public abstract class ClientXdsClientTestBase { /** Throws {@link InvalidProtocolBufferException} on {@link Any#unpack(Class)}. */ protected static final Any FAILING_ANY = Any.newBuilder().setTypeUrl("fake").build(); - protected final Message buildListener(String name, Message routeConfiguration) { - return buildListener(name, routeConfiguration, Collections.emptyList()); + protected final Message buildListenerWithApiListener(String name, Message routeConfiguration) { + return buildListenerWithApiListener( + name, routeConfiguration, Collections.emptyList()); } - protected abstract Message buildListener( + protected abstract Message buildListenerWithApiListener( String name, Message routeConfiguration, List httpFilters); - protected abstract Message buildListenerForRds(String name, String rdsResourceName); + protected abstract Message buildListenerWithApiListenerForRds( + String name, String rdsResourceName); - protected abstract Message buildListenerInvalid(String name); + protected abstract Message buildListenerWithApiListenerInvalid(String name); protected abstract Message buildHttpFilter( String name, @Nullable Any typedConfig, boolean isOptional); @@ -2239,9 +2310,7 @@ public abstract class ClientXdsClientTestBase { protected abstract Message buildListenerWithFilterChain( String name, int portValue, String address, Message... filterChains); - protected abstract Message buildListenerWithFilterChain( - String name, int portValue, String address, String certName, String validationContextName); - - protected abstract Message buildTestFilter(String name); + protected abstract Message buildHttpConnectionManagerFilter( + @Nullable String rdsName, @Nullable Message routeConfig, List httpFilters); } } diff --git a/xds/src/test/java/io/grpc/xds/ClientXdsClientV2Test.java b/xds/src/test/java/io/grpc/xds/ClientXdsClientV2Test.java index 27ef2ba9eb..8edeb97f70 100644 --- a/xds/src/test/java/io/grpc/xds/ClientXdsClientV2Test.java +++ b/xds/src/test/java/io/grpc/xds/ClientXdsClientV2Test.java @@ -254,7 +254,7 @@ public class ClientXdsClientV2Test extends ClientXdsClientTestBase { @SuppressWarnings("unchecked") @Override - protected Message buildListener( + protected Message buildListenerWithApiListener( String name, Message routeConfiguration, List httpFilters) { return Listener.newBuilder() .setName(name) @@ -270,7 +270,7 @@ public class ClientXdsClientV2Test extends ClientXdsClientTestBase { } @Override - protected Message buildListenerForRds(String name, String rdsResourceName) { + protected Message buildListenerWithApiListenerForRds(String name, String rdsResourceName) { return Listener.newBuilder() .setName(name) .setAddress(Address.getDefaultInstance()) @@ -289,7 +289,7 @@ public class ClientXdsClientV2Test extends ClientXdsClientTestBase { } @Override - protected Message buildListenerInvalid(String name) { + protected Message buildListenerWithApiListenerInvalid(String name) { return Listener.newBuilder() .setName(name) .setAddress(Address.getDefaultInstance()) @@ -629,13 +629,8 @@ public class ClientXdsClientV2Test extends ClientXdsClientTestBase { } @Override - protected Message buildListenerWithFilterChain( - String name, int portValue, String address, String certName, String validationContextName) { - throw new UnsupportedOperationException(); - } - - @Override - protected Message buildTestFilter(String name) { + protected Message buildHttpConnectionManagerFilter( + @Nullable String rdsName, @Nullable Message routeConfig, List httpFilters) { throw new UnsupportedOperationException(); } } diff --git a/xds/src/test/java/io/grpc/xds/ClientXdsClientV3Test.java b/xds/src/test/java/io/grpc/xds/ClientXdsClientV3Test.java index a68decba88..031ae716df 100644 --- a/xds/src/test/java/io/grpc/xds/ClientXdsClientV3Test.java +++ b/xds/src/test/java/io/grpc/xds/ClientXdsClientV3Test.java @@ -98,7 +98,6 @@ import io.grpc.Context.CancellationListener; import io.grpc.Status; import io.grpc.stub.StreamObserver; import io.grpc.xds.AbstractXdsClient.ResourceType; -import io.grpc.xds.internal.sds.CommonTlsContextTestsUtil; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -264,7 +263,7 @@ public class ClientXdsClientV3Test extends ClientXdsClientTestBase { @SuppressWarnings("unchecked") @Override - protected Message buildListener( + protected Message buildListenerWithApiListener( String name, Message routeConfiguration, List httpFilters) { return Listener.newBuilder() .setName(name) @@ -280,7 +279,7 @@ public class ClientXdsClientV3Test extends ClientXdsClientTestBase { } @Override - protected Message buildListenerForRds(String name, String rdsResourceName) { + protected Message buildListenerWithApiListenerForRds(String name, String rdsResourceName) { return Listener.newBuilder() .setName(name) .setAddress(Address.getDefaultInstance()) @@ -299,7 +298,7 @@ public class ClientXdsClientV3Test extends ClientXdsClientTestBase { } @Override - protected Message buildListenerInvalid(String name) { + protected Message buildListenerWithApiListenerInvalid(String name) { return Listener.newBuilder() .setName(name) .setAddress(Address.getDefaultInstance()) @@ -700,40 +699,33 @@ public class ClientXdsClientV3Test extends ClientXdsClientTestBase { return Listener.newBuilder() .setName(name) .setAddress(listenerAddress) - .setDefaultFilterChain(FilterChain.getDefaultInstance()) .addAllFilterChains(Arrays.asList(filterChainsArray)) .setTrafficDirection(TrafficDirection.INBOUND) .build(); } @Override - protected Listener buildListenerWithFilterChain( - String name, int portValue, String address, String certName, String validationContextName) { - FilterChain filterChain = - buildFilterChain( - Arrays.asList(), - CommonTlsContextTestsUtil.buildTestDownstreamTlsContext( - certName, validationContextName), - buildTestFilter("envoy.http_connection_manager")); - io.envoyproxy.envoy.config.core.v3.Address listenerAddress = - io.envoyproxy.envoy.config.core.v3.Address.newBuilder() - .setSocketAddress( - SocketAddress.newBuilder().setPortValue(portValue).setAddress(address)) - .build(); - return Listener.newBuilder() - .setName(name) - .setAddress(listenerAddress) - .setDefaultFilterChain(FilterChain.getDefaultInstance()) - .addAllFilterChains(Arrays.asList(filterChain)) - .setTrafficDirection(TrafficDirection.INBOUND) - .build(); - } - - @Override - protected Filter buildTestFilter(String name) { + protected Message buildHttpConnectionManagerFilter( + @Nullable String rdsName, @Nullable Message routeConfig, List httpFilters) { + HttpConnectionManager.Builder hcmBuilder = HttpConnectionManager.newBuilder(); + if (rdsName != null) { + hcmBuilder.setRds( + Rds.newBuilder() + .setRouteConfigName(rdsName) + .setConfigSource( + ConfigSource.newBuilder() + .setAds(AggregatedConfigSource.getDefaultInstance()))); + } + if (routeConfig != null) { + hcmBuilder.setRouteConfig((RouteConfiguration) routeConfig); + } + for (Message httpFilter : httpFilters) { + hcmBuilder.addHttpFilters((HttpFilter) httpFilter); + } return Filter.newBuilder() - .setName(name) - .setTypedConfig(Any.pack(HttpConnectionManager.getDefaultInstance())) + .setName("envoy.http_connection_manager") + .setTypedConfig( + Any.pack(hcmBuilder.build(), "type.googleapis.com")) .build(); } } diff --git a/xds/src/test/java/io/grpc/xds/ClusterManagerLoadBalancerProviderTest.java b/xds/src/test/java/io/grpc/xds/ClusterManagerLoadBalancerProviderTest.java index dfef96e545..515f6fef3e 100644 --- a/xds/src/test/java/io/grpc/xds/ClusterManagerLoadBalancerProviderTest.java +++ b/xds/src/test/java/io/grpc/xds/ClusterManagerLoadBalancerProviderTest.java @@ -23,6 +23,8 @@ import io.grpc.LoadBalancer.Helper; import io.grpc.LoadBalancerProvider; import io.grpc.LoadBalancerRegistry; import io.grpc.NameResolver.ConfigOrError; +import io.grpc.Status; +import io.grpc.Status.Code; import io.grpc.internal.JsonParser; import io.grpc.internal.ServiceConfigUtil.PolicySelection; import io.grpc.xds.ClusterManagerLoadBalancerProvider.ClusterManagerConfig; @@ -36,11 +38,12 @@ import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class ClusterManagerLoadBalancerProviderTest { + private final LoadBalancerRegistry lbRegistry = new LoadBalancerRegistry(); + private final ClusterManagerLoadBalancerProvider provider = + new ClusterManagerLoadBalancerProvider(lbRegistry); + @Test - public void parseClusterManagerLoadBalancingPolicyConfig() throws IOException { - LoadBalancerRegistry lbRegistry = new LoadBalancerRegistry(); - ClusterManagerLoadBalancerProvider provider = - new ClusterManagerLoadBalancerProvider(lbRegistry); + public void parseLoadBalancingConfig_valid() throws IOException { final Object fooConfig = new Object(); LoadBalancerProvider lbProviderFoo = new LoadBalancerProvider() { @Override @@ -136,6 +139,20 @@ public class ClusterManagerLoadBalancerProviderTest { new PolicySelection(lbProviderBar, barConfig)); } + @Test + public void parseLoadBalancingPolicyConfig_emptyChildPolicy() throws IOException { + String clusterManagerConfigJson = "{\n" + + " \"childPolicy\": {}\n" + + "}"; + @SuppressWarnings("unchecked") + Map rawLbConfigMap = (Map) JsonParser.parse(clusterManagerConfigJson); + ConfigOrError configOrError = provider.parseLoadBalancingPolicyConfig(rawLbConfigMap); + Status error = configOrError.getError(); + assertThat(error.getCode()).isEqualTo(Code.INTERNAL); + assertThat(error.getDescription()) + .startsWith("No child policy provided for cluster_manager LB policy"); + } + @Test public void registered() { LoadBalancerProvider provider = diff --git a/xds/src/test/java/io/grpc/xds/EnvoyServerProtoDataTest.java b/xds/src/test/java/io/grpc/xds/EnvoyServerProtoDataTest.java deleted file mode 100644 index 5e641e8a65..0000000000 --- a/xds/src/test/java/io/grpc/xds/EnvoyServerProtoDataTest.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2020 The gRPC Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.grpc.xds; - -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.mock; - -import com.google.protobuf.Any; -import com.google.protobuf.InvalidProtocolBufferException; -import com.google.protobuf.UInt32Value; -import io.envoyproxy.envoy.config.core.v3.Address; -import io.envoyproxy.envoy.config.core.v3.CidrRange; -import io.envoyproxy.envoy.config.core.v3.SocketAddress; -import io.envoyproxy.envoy.config.core.v3.TrafficDirection; -import io.envoyproxy.envoy.config.core.v3.TransportSocket; -import io.envoyproxy.envoy.config.listener.v3.Filter; -import io.envoyproxy.envoy.config.listener.v3.FilterChain; -import io.envoyproxy.envoy.config.listener.v3.FilterChainMatch; -import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CommonTlsContext; -import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.SdsSecretConfig; -import io.grpc.xds.EnvoyServerProtoData.DownstreamTlsContext; -import io.grpc.xds.EnvoyServerProtoData.Listener; -import io.grpc.xds.internal.sds.CommonTlsContextTestsUtil; -import io.grpc.xds.internal.sds.SslContextProviderSupplier; -import java.util.Arrays; -import java.util.List; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** - * Unit tests for {@link EnvoyServerProtoData}. - */ -@RunWith(JUnit4.class) -public class EnvoyServerProtoDataTest { - - @Test - public void listener_convertFromListenerProto() throws InvalidProtocolBufferException { - Address address = - Address.newBuilder() - .setSocketAddress( - SocketAddress.newBuilder().setPortValue(8000).setAddress("10.2.1.34").build()) - .build(); - io.envoyproxy.envoy.config.listener.v3.Listener listener = - io.envoyproxy.envoy.config.listener.v3.Listener.newBuilder() - .setName("8000") - .setAddress(address) - .addFilterChains(createInFilter()) - .setDefaultFilterChain(createDefaultFilterChain()) - .setTrafficDirection(TrafficDirection.INBOUND) - .build(); - - Listener xdsListener = Listener.fromEnvoyProtoListener(listener, mock(TlsContextManager.class)); - assertThat(xdsListener.getName()).isEqualTo("8000"); - assertThat(xdsListener.getAddress()).isEqualTo("10.2.1.34:8000"); - List filterChains = xdsListener.getFilterChains(); - assertThat(filterChains).isNotNull(); - assertThat(filterChains.size()).isEqualTo(1); - - EnvoyServerProtoData.FilterChain inFilter = filterChains.get(0); - assertThat(inFilter).isNotNull(); - EnvoyServerProtoData.FilterChainMatch inFilterChainMatch = inFilter.getFilterChainMatch(); - assertThat(inFilterChainMatch).isNotNull(); - assertThat(inFilterChainMatch.getDestinationPort()).isEqualTo(8000); - assertThat(inFilterChainMatch.getApplicationProtocols()) - .containsExactlyElementsIn(Arrays.asList("managed-mtls", "h2")); - assertThat(inFilterChainMatch.getServerNames()) - .containsExactlyElementsIn(Arrays.asList("server1", "server2")); - assertThat(inFilterChainMatch.getTransportProtocol()).isEqualTo("tls"); - assertThat(inFilterChainMatch.getPrefixRanges()) - .containsExactly(new EnvoyServerProtoData.CidrRange("10.20.0.15", 32)); - assertThat(inFilterChainMatch.getSourcePrefixRanges()) - .containsExactly(new EnvoyServerProtoData.CidrRange("10.30.3.0", 24)); - assertThat(inFilterChainMatch.getConnectionSourceType()) - .isEqualTo(EnvoyServerProtoData.ConnectionSourceType.EXTERNAL); - assertThat(inFilterChainMatch.getSourcePorts()).containsExactly(200, 300); - SslContextProviderSupplier sslContextProviderSupplier = inFilter - .getSslContextProviderSupplier(); - assertThat(sslContextProviderSupplier.getTlsContext()).isInstanceOf(DownstreamTlsContext.class); - DownstreamTlsContext inFilterTlsContext = (DownstreamTlsContext) sslContextProviderSupplier - .getTlsContext(); - assertThat(inFilterTlsContext.getCommonTlsContext()).isNotNull(); - CommonTlsContext commonTlsContext = inFilterTlsContext.getCommonTlsContext(); - List tlsCertSdsConfigs = commonTlsContext - .getTlsCertificateSdsSecretConfigsList(); - assertThat(tlsCertSdsConfigs).hasSize(1); - assertThat(tlsCertSdsConfigs.get(0).getName()).isEqualTo("google-sds-config-default"); - - EnvoyServerProtoData.FilterChain defaultFilter = xdsListener.getDefaultFilterChain(); - assertThat(defaultFilter).isNotNull(); - EnvoyServerProtoData.FilterChainMatch defaultFilterChainMatch = - defaultFilter.getFilterChainMatch(); - assertThat(defaultFilterChainMatch).isNotNull(); - assertThat(defaultFilterChainMatch.getDestinationPort()).isEqualTo(8001); - assertThat(defaultFilterChainMatch.getPrefixRanges()) - .containsExactly(new EnvoyServerProtoData.CidrRange("10.20.0.16", 30)); - } - - private static FilterChain createInFilter() { - FilterChain filterChain = - FilterChain.newBuilder() - .setFilterChainMatch( - FilterChainMatch.newBuilder() - .setDestinationPort(UInt32Value.of(8000)) - .addAllServerNames(Arrays.asList("server1", "server2")) - .setTransportProtocol("tls") - .addAllApplicationProtocols(Arrays.asList("managed-mtls", "h2")) - .addPrefixRanges(CidrRange.newBuilder() - .setAddressPrefix("10.20.0.15") - .setPrefixLen(UInt32Value.of(32)) - .build()) - .addSourcePrefixRanges( - CidrRange.newBuilder() - .setAddressPrefix("10.30.3.0") - .setPrefixLen(UInt32Value.of(24)) - .build()) - .setSourceType(FilterChainMatch.ConnectionSourceType.EXTERNAL) - .addSourcePorts(200) - .addSourcePorts(300) - .build()) - .setTransportSocket(TransportSocket.newBuilder().setName("envoy.transport_sockets.tls") - .setTypedConfig( - Any.pack(CommonTlsContextTestsUtil.buildTestDownstreamTlsContext( - "google-sds-config-default", "ROOTCA"))) - .build()) - .addFilters(Filter.newBuilder() - .setName("envoy.http_connection_manager") - .setTypedConfig(Any.newBuilder() - .setTypeUrl( - "type.googleapis.com/" - + "envoy.extensions.filters.network.http_connection_manager" - + ".v3.HttpConnectionManager")) - .build()) - .build(); - return filterChain; - } - - private static FilterChain createDefaultFilterChain() { - FilterChain filterChain = - FilterChain.newBuilder() - .setFilterChainMatch( - FilterChainMatch.newBuilder() - .setDestinationPort(UInt32Value.of(8001)) - .addPrefixRanges( - CidrRange.newBuilder() - .setAddressPrefix("10.20.0.16") - .setPrefixLen(UInt32Value.of(30)) - .build()) - .build()) - .setTransportSocket( - TransportSocket.newBuilder() - .setName("envoy.transport_sockets.tls") - .setTypedConfig( - Any.pack( - CommonTlsContextTestsUtil.buildTestDownstreamTlsContext( - "google-sds-config-default", "ROOTCA"))) - .build()) - .addFilters( - Filter.newBuilder() - .setName("envoy.http_connection_manager") - .setTypedConfig( - Any.newBuilder() - .setTypeUrl( - "type.googleapis.com/" - + "envoy.extensions.filters.network.http_connection_manager" - + ".v3.HttpConnectionManager")) - .build()) - .build(); - return filterChain; - } -} diff --git a/xds/src/test/java/io/grpc/xds/FaultFilterTest.java b/xds/src/test/java/io/grpc/xds/FaultFilterTest.java index 92e53dd248..f85f29ec0a 100644 --- a/xds/src/test/java/io/grpc/xds/FaultFilterTest.java +++ b/xds/src/test/java/io/grpc/xds/FaultFilterTest.java @@ -24,6 +24,7 @@ import io.envoyproxy.envoy.extensions.filters.http.fault.v3.FaultAbort.HeaderAbo import io.envoyproxy.envoy.extensions.filters.http.fault.v3.HTTPFault; import io.envoyproxy.envoy.type.v3.FractionalPercent; import io.envoyproxy.envoy.type.v3.FractionalPercent.DenominatorType; +import io.grpc.Status.Code; import io.grpc.internal.GrpcUtil; import org.junit.Test; import org.junit.runner.RunWith; @@ -59,4 +60,32 @@ public class FaultFilterTest { assertThat(faultAbort.percent().denominatorType()) .isEqualTo(FaultConfig.FractionalPercent.DenominatorType.HUNDRED); } + + @Test + public void parseFaultAbort_withHttpStatus() { + io.envoyproxy.envoy.extensions.filters.http.fault.v3.FaultAbort proto = + io.envoyproxy.envoy.extensions.filters.http.fault.v3.FaultAbort.newBuilder() + .setPercentage(FractionalPercent.newBuilder() + .setNumerator(100).setDenominator(DenominatorType.TEN_THOUSAND)) + .setHttpStatus(400).build(); + FaultConfig.FaultAbort res = FaultFilter.parseFaultAbort(proto).config; + assertThat(res.percent().numerator()).isEqualTo(100); + assertThat(res.percent().denominatorType()) + .isEqualTo(FaultConfig.FractionalPercent.DenominatorType.TEN_THOUSAND); + assertThat(res.status().getCode()).isEqualTo(Code.INTERNAL); + } + + @Test + public void parseFaultAbort_withGrpcStatus() { + io.envoyproxy.envoy.extensions.filters.http.fault.v3.FaultAbort proto = + io.envoyproxy.envoy.extensions.filters.http.fault.v3.FaultAbort.newBuilder() + .setPercentage(FractionalPercent.newBuilder() + .setNumerator(600).setDenominator(DenominatorType.MILLION)) + .setGrpcStatus(Code.DEADLINE_EXCEEDED.value()).build(); + FaultConfig.FaultAbort faultAbort = FaultFilter.parseFaultAbort(proto).config; + assertThat(faultAbort.percent().numerator()).isEqualTo(600); + assertThat(faultAbort.percent().denominatorType()) + .isEqualTo(FaultConfig.FractionalPercent.DenominatorType.MILLION); + assertThat(faultAbort.status().getCode()).isEqualTo(Code.DEADLINE_EXCEEDED); + } } diff --git a/xds/src/test/java/io/grpc/xds/FilterChainMatchTest.java b/xds/src/test/java/io/grpc/xds/FilterChainMatchTest.java index dfe80cd339..f4d6f83a5b 100644 --- a/xds/src/test/java/io/grpc/xds/FilterChainMatchTest.java +++ b/xds/src/test/java/io/grpc/xds/FilterChainMatchTest.java @@ -21,18 +21,8 @@ import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.google.protobuf.Any; -import com.google.protobuf.InvalidProtocolBufferException; -import com.google.protobuf.UInt32Value; -import io.envoyproxy.envoy.config.core.v3.Address; -import io.envoyproxy.envoy.config.core.v3.CidrRange; -import io.envoyproxy.envoy.config.core.v3.SocketAddress; -import io.envoyproxy.envoy.config.core.v3.TrafficDirection; -import io.envoyproxy.envoy.config.core.v3.TransportSocket; -import io.envoyproxy.envoy.config.listener.v3.Filter; -import io.envoyproxy.envoy.config.listener.v3.FilterChain; -import io.envoyproxy.envoy.config.listener.v3.FilterChainMatch; -import io.grpc.xds.EnvoyServerProtoData.DownstreamTlsContext; +import io.grpc.xds.Filter.NamedFilterConfig; +import io.grpc.xds.XdsClient.LdsUpdate; import io.grpc.xds.internal.sds.CommonTlsContextTestsUtil; import io.grpc.xds.internal.sds.SslContextProviderSupplier; import io.netty.channel.Channel; @@ -41,6 +31,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.Arrays; +import java.util.Collections; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -56,6 +47,9 @@ public class FilterChainMatchTest { private static final int PORT = 7000; private static final String LOCAL_IP = "10.1.2.3"; // dest private static final String REMOTE_IP = "10.4.2.3"; // source + private static final HttpConnectionManager HTTP_CONNECTION_MANAGER = + HttpConnectionManager.forRdsName( + 10L, "route-config", Collections.emptyList()); @Mock private Channel channel; @Mock private TlsContextManager tlsContextManager; @@ -77,13 +71,13 @@ public class FilterChainMatchTest { xdsClientWrapperForServerSds.shutdown(); } - private DownstreamTlsContext getDownstreamTlsContext() { + private EnvoyServerProtoData.DownstreamTlsContext getDownstreamTlsContext() { SslContextProviderSupplier sslContextProviderSupplier = xdsClientWrapperForServerSds.getSslContextProviderSupplier(channel); if (sslContextProviderSupplier != null) { EnvoyServerProtoData.BaseTlsContext tlsContext = sslContextProviderSupplier.getTlsContext(); - assertThat(tlsContext).isInstanceOf(DownstreamTlsContext.class); - return (DownstreamTlsContext) tlsContext; + assertThat(tlsContext).isInstanceOf(EnvoyServerProtoData.DownstreamTlsContext.class); + return (EnvoyServerProtoData.DownstreamTlsContext) tlsContext; } return null; } @@ -101,15 +95,16 @@ public class FilterChainMatchTest { Arrays.asList(), Arrays.asList(), null); - DownstreamTlsContext tlsContext = + EnvoyServerProtoData.DownstreamTlsContext tlsContext = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"); - EnvoyServerProtoData.FilterChain filterChain = - new EnvoyServerProtoData.FilterChain(filterChainMatch, tlsContext, tlsContextManager); + EnvoyServerProtoData.FilterChain filterChain = new EnvoyServerProtoData.FilterChain( + "filter-chain-foo", filterChainMatch, HTTP_CONNECTION_MANAGER, tlsContext, + tlsContextManager); EnvoyServerProtoData.Listener listener = new EnvoyServerProtoData.Listener("listener1", LOCAL_IP, Arrays.asList(filterChain), null); - XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener); + LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); registeredWatcher.onChanged(listenerUpdate); - DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); + EnvoyServerProtoData.DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); assertThat(tlsContext1).isSameInstanceAs(tlsContext); } @@ -126,43 +121,44 @@ public class FilterChainMatchTest { Arrays.asList(), Arrays.asList(), null); - DownstreamTlsContext tlsContext = + EnvoyServerProtoData.DownstreamTlsContext tlsContext = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"); - EnvoyServerProtoData.FilterChain filterChain = - new EnvoyServerProtoData.FilterChain(filterChainMatch, tlsContext, tlsContextManager); - DownstreamTlsContext defaultTlsContext = + EnvoyServerProtoData.FilterChain filterChain = new EnvoyServerProtoData.FilterChain( + "filter-chain-foo", filterChainMatch, HTTP_CONNECTION_MANAGER, tlsContext, + tlsContextManager); + EnvoyServerProtoData.DownstreamTlsContext defaultTlsContext = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT2", "VA2"); - EnvoyServerProtoData.FilterChain defaultFilterChain = - new EnvoyServerProtoData.FilterChain(null, defaultTlsContext, tlsContextManager); - EnvoyServerProtoData.Listener listener = - new EnvoyServerProtoData.Listener("listener1", LOCAL_IP, Arrays.asList(filterChain), - defaultFilterChain); - XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener); + EnvoyServerProtoData.FilterChain defaultFilterChain = new EnvoyServerProtoData.FilterChain( + "filter-chain-bar", null, HTTP_CONNECTION_MANAGER, defaultTlsContext, + tlsContextManager); + EnvoyServerProtoData.Listener listener = new EnvoyServerProtoData.Listener( + "listener1", LOCAL_IP, Arrays.asList(filterChain), defaultFilterChain); + LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); registeredWatcher.onChanged(listenerUpdate); - DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); + EnvoyServerProtoData.DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); assertThat(tlsContext1).isSameInstanceAs(defaultTlsContext); } @Test public void defaultFilterChain() throws UnknownHostException { setupChannel(LOCAL_IP, REMOTE_IP, 15000); - DownstreamTlsContext tlsContext = + EnvoyServerProtoData.DownstreamTlsContext tlsContext = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"); - EnvoyServerProtoData.FilterChain filterChain = - new EnvoyServerProtoData.FilterChain(null, tlsContext, tlsContextManager); + EnvoyServerProtoData.FilterChain filterChain = new EnvoyServerProtoData.FilterChain( + "filter-chain-foo", null, HTTP_CONNECTION_MANAGER, tlsContext, tlsContextManager); EnvoyServerProtoData.Listener listener = new EnvoyServerProtoData.Listener( "listener1", LOCAL_IP, Arrays.asList(), filterChain); - XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener); + LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); registeredWatcher.onChanged(listenerUpdate); - DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); + EnvoyServerProtoData.DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); assertThat(tlsContext1).isSameInstanceAs(tlsContext); } @Test public void destPortFails_returnDefaultFilterChain() throws UnknownHostException { setupChannel(LOCAL_IP, REMOTE_IP, 15000); - DownstreamTlsContext tlsContextWithDestPort = + EnvoyServerProtoData.DownstreamTlsContext tlsContextWithDestPort = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"); EnvoyServerProtoData.FilterChainMatch filterChainMatchWithDestPort = new EnvoyServerProtoData.FilterChainMatch( @@ -175,26 +171,28 @@ public class FilterChainMatchTest { Arrays.asList(), null); EnvoyServerProtoData.FilterChain filterChainWithDestPort = - new EnvoyServerProtoData.FilterChain(filterChainMatchWithDestPort, tlsContextWithDestPort, - tlsContextManager); - DownstreamTlsContext tlsContextForDefaultFilterChain = + new EnvoyServerProtoData.FilterChain( + "filter-chain-foo", filterChainMatchWithDestPort, HTTP_CONNECTION_MANAGER, + tlsContextWithDestPort, tlsContextManager); + EnvoyServerProtoData.DownstreamTlsContext tlsContextForDefaultFilterChain = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT2", "VA2"); EnvoyServerProtoData.FilterChain defaultFilterChain = - new EnvoyServerProtoData.FilterChain(null, tlsContextForDefaultFilterChain, - tlsContextManager); + new EnvoyServerProtoData.FilterChain( + "filter-chain-bar", null, HTTP_CONNECTION_MANAGER, + tlsContextForDefaultFilterChain, tlsContextManager); EnvoyServerProtoData.Listener listener = new EnvoyServerProtoData.Listener( "listener1", LOCAL_IP, Arrays.asList(filterChainWithDestPort), defaultFilterChain); - XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener); + LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); registeredWatcher.onChanged(listenerUpdate); - DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); + EnvoyServerProtoData.DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); assertThat(tlsContext1).isSameInstanceAs(tlsContextForDefaultFilterChain); } @Test - public void destPrefixRangeMatch() throws UnknownHostException, InvalidProtocolBufferException { + public void destPrefixRangeMatch() throws UnknownHostException { setupChannel(LOCAL_IP, REMOTE_IP, 15000); - DownstreamTlsContext tlsContextMatch = + EnvoyServerProtoData.DownstreamTlsContext tlsContextMatch = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"); EnvoyServerProtoData.FilterChainMatch filterChainMatchWithMatch = new EnvoyServerProtoData.FilterChainMatch( @@ -206,28 +204,28 @@ public class FilterChainMatchTest { Arrays.asList(), Arrays.asList(), null); - EnvoyServerProtoData.FilterChain filterChainWithMatch = - new EnvoyServerProtoData.FilterChain(filterChainMatchWithMatch, tlsContextMatch, - tlsContextManager); - DownstreamTlsContext tlsContextForDefaultFilterChain = + EnvoyServerProtoData.FilterChain filterChainWithMatch = new EnvoyServerProtoData.FilterChain( + "filter-chain-foo", filterChainMatchWithMatch, HTTP_CONNECTION_MANAGER, + tlsContextMatch, tlsContextManager); + EnvoyServerProtoData.DownstreamTlsContext tlsContextForDefaultFilterChain = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT2", "VA2"); - EnvoyServerProtoData.FilterChain defaultFilterChain = - new EnvoyServerProtoData.FilterChain(null, tlsContextForDefaultFilterChain, - tlsContextManager); + EnvoyServerProtoData.FilterChain defaultFilterChain = new EnvoyServerProtoData.FilterChain( + "filter-chain-bar", null, HTTP_CONNECTION_MANAGER, + tlsContextForDefaultFilterChain, tlsContextManager); EnvoyServerProtoData.Listener listener = new EnvoyServerProtoData.Listener( "listener1", LOCAL_IP, Arrays.asList(filterChainWithMatch), defaultFilterChain); - XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener); + LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); registeredWatcher.onChanged(listenerUpdate); - DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); + EnvoyServerProtoData.DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); assertThat(tlsContext1).isSameInstanceAs(tlsContextMatch); } @Test public void destPrefixRangeMismatch_returnDefaultFilterChain() - throws UnknownHostException, InvalidProtocolBufferException { + throws UnknownHostException { setupChannel(LOCAL_IP, REMOTE_IP, 15000); - DownstreamTlsContext tlsContextMismatch = + EnvoyServerProtoData.DownstreamTlsContext tlsContextMismatch = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"); // 10.2.2.0/24 doesn't match LOCAL_IP EnvoyServerProtoData.FilterChainMatch filterChainMatchWithMismatch = @@ -241,27 +239,28 @@ public class FilterChainMatchTest { Arrays.asList(), null); EnvoyServerProtoData.FilterChain filterChainWithMismatch = - new EnvoyServerProtoData.FilterChain(filterChainMatchWithMismatch, tlsContextMismatch, - tlsContextManager); - DownstreamTlsContext tlsContextForDefaultFilterChain = + new EnvoyServerProtoData.FilterChain( + "filter-chain-foo", filterChainMatchWithMismatch, HTTP_CONNECTION_MANAGER, + tlsContextMismatch, tlsContextManager); + EnvoyServerProtoData.DownstreamTlsContext tlsContextForDefaultFilterChain = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT2", "VA2"); - EnvoyServerProtoData.FilterChain defaultFilterChain = - new EnvoyServerProtoData.FilterChain(null, tlsContextForDefaultFilterChain, - tlsContextManager); + EnvoyServerProtoData.FilterChain defaultFilterChain = new EnvoyServerProtoData.FilterChain( + "filter-chain-bar", null, HTTP_CONNECTION_MANAGER, + tlsContextForDefaultFilterChain, tlsContextManager); EnvoyServerProtoData.Listener listener = new EnvoyServerProtoData.Listener( "listener1", LOCAL_IP, Arrays.asList(filterChainWithMismatch), defaultFilterChain); - XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener); + LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); registeredWatcher.onChanged(listenerUpdate); - DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); + EnvoyServerProtoData.DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); assertThat(tlsContext1).isSameInstanceAs(tlsContextForDefaultFilterChain); } @Test public void dest0LengthPrefixRange() - throws UnknownHostException, InvalidProtocolBufferException { + throws UnknownHostException { setupChannel(LOCAL_IP, REMOTE_IP, 15000); - DownstreamTlsContext tlsContext0Length = + EnvoyServerProtoData.DownstreamTlsContext tlsContext0Length = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"); // 10.2.2.0/24 doesn't match LOCAL_IP EnvoyServerProtoData.FilterChainMatch filterChainMatch0Length = @@ -274,28 +273,28 @@ public class FilterChainMatchTest { Arrays.asList(), Arrays.asList(), null); - EnvoyServerProtoData.FilterChain filterChain0Length = - new EnvoyServerProtoData.FilterChain(filterChainMatch0Length, tlsContext0Length, - tlsContextManager); - DownstreamTlsContext tlsContextForDefaultFilterChain = + EnvoyServerProtoData.FilterChain filterChain0Length = new EnvoyServerProtoData.FilterChain( + "filter-chain-foo", filterChainMatch0Length, HTTP_CONNECTION_MANAGER, + tlsContext0Length, tlsContextManager); + EnvoyServerProtoData.DownstreamTlsContext tlsContextForDefaultFilterChain = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT2", "VA2"); - EnvoyServerProtoData.FilterChain defaultFilterChain = - new EnvoyServerProtoData.FilterChain(null, tlsContextForDefaultFilterChain, - tlsContextManager); + EnvoyServerProtoData.FilterChain defaultFilterChain = new EnvoyServerProtoData.FilterChain( + "filter-chain-bar", null, HTTP_CONNECTION_MANAGER, + tlsContextForDefaultFilterChain, tlsContextManager); EnvoyServerProtoData.Listener listener = new EnvoyServerProtoData.Listener( "listener1", LOCAL_IP, Arrays.asList(filterChain0Length), defaultFilterChain); - XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener); + LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); registeredWatcher.onChanged(listenerUpdate); - DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); + EnvoyServerProtoData.DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); assertThat(tlsContext1).isSameInstanceAs(tlsContext0Length); } @Test public void destPrefixRange_moreSpecificWins() - throws UnknownHostException, InvalidProtocolBufferException { + throws UnknownHostException { setupChannel(LOCAL_IP, REMOTE_IP, 15000); - DownstreamTlsContext tlsContextLessSpecific = + EnvoyServerProtoData.DownstreamTlsContext tlsContextLessSpecific = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"); EnvoyServerProtoData.FilterChainMatch filterChainMatchLessSpecific = new EnvoyServerProtoData.FilterChainMatch( @@ -308,10 +307,11 @@ public class FilterChainMatchTest { Arrays.asList(), null); EnvoyServerProtoData.FilterChain filterChainLessSpecific = - new EnvoyServerProtoData.FilterChain(filterChainMatchLessSpecific, tlsContextLessSpecific, - tlsContextManager); + new EnvoyServerProtoData.FilterChain( + "filter-chain-foo", filterChainMatchLessSpecific, HTTP_CONNECTION_MANAGER, + tlsContextLessSpecific, tlsContextManager); - DownstreamTlsContext tlsContextMoreSpecific = + EnvoyServerProtoData.DownstreamTlsContext tlsContextMoreSpecific = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT2", "VA2"); EnvoyServerProtoData.FilterChainMatch filterChainMatchMoreSpecific = new EnvoyServerProtoData.FilterChainMatch( @@ -324,27 +324,29 @@ public class FilterChainMatchTest { Arrays.asList(), null); EnvoyServerProtoData.FilterChain filterChainMoreSpecific = - new EnvoyServerProtoData.FilterChain(filterChainMatchMoreSpecific, tlsContextMoreSpecific, + new EnvoyServerProtoData.FilterChain( + "filter-chain-bar", filterChainMatchMoreSpecific, HTTP_CONNECTION_MANAGER, + tlsContextMoreSpecific, tlsContextManager); - EnvoyServerProtoData.FilterChain defaultFilterChain = - new EnvoyServerProtoData.FilterChain(null, null, tlsContextManager); + EnvoyServerProtoData.FilterChain defaultFilterChain = new EnvoyServerProtoData.FilterChain( + "filter-chain-baz", null, HTTP_CONNECTION_MANAGER, null, tlsContextManager); EnvoyServerProtoData.Listener listener = new EnvoyServerProtoData.Listener( "listener1", LOCAL_IP, Arrays.asList(filterChainLessSpecific, filterChainMoreSpecific), defaultFilterChain); - XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener); + LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); registeredWatcher.onChanged(listenerUpdate); - DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); + EnvoyServerProtoData.DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); assertThat(tlsContext1).isSameInstanceAs(tlsContextMoreSpecific); } @Test public void destPrefixRange_emptyListLessSpecific() - throws UnknownHostException, InvalidProtocolBufferException { + throws UnknownHostException { setupChannel(LOCAL_IP, REMOTE_IP, 15000); - DownstreamTlsContext tlsContextLessSpecific = + EnvoyServerProtoData.DownstreamTlsContext tlsContextLessSpecific = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"); EnvoyServerProtoData.FilterChainMatch filterChainMatchLessSpecific = new EnvoyServerProtoData.FilterChainMatch( @@ -357,10 +359,11 @@ public class FilterChainMatchTest { Arrays.asList(), null); EnvoyServerProtoData.FilterChain filterChainLessSpecific = - new EnvoyServerProtoData.FilterChain(filterChainMatchLessSpecific, tlsContextLessSpecific, - tlsContextManager); + new EnvoyServerProtoData.FilterChain( + "filter-chain-foo", filterChainMatchLessSpecific, HTTP_CONNECTION_MANAGER, + tlsContextLessSpecific, tlsContextManager); - DownstreamTlsContext tlsContextMoreSpecific = + EnvoyServerProtoData.DownstreamTlsContext tlsContextMoreSpecific = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT2", "VA2"); EnvoyServerProtoData.FilterChainMatch filterChainMatchMoreSpecific = new EnvoyServerProtoData.FilterChainMatch( @@ -373,27 +376,29 @@ public class FilterChainMatchTest { Arrays.asList(), null); EnvoyServerProtoData.FilterChain filterChainMoreSpecific = - new EnvoyServerProtoData.FilterChain(filterChainMatchMoreSpecific, tlsContextMoreSpecific, + new EnvoyServerProtoData.FilterChain( + "filter-chain-bar", filterChainMatchMoreSpecific, HTTP_CONNECTION_MANAGER, + tlsContextMoreSpecific, tlsContextManager); - EnvoyServerProtoData.FilterChain defaultFilterChain = - new EnvoyServerProtoData.FilterChain(null, null, tlsContextManager); + EnvoyServerProtoData.FilterChain defaultFilterChain = new EnvoyServerProtoData.FilterChain( + "filter-chain-baz", null, HTTP_CONNECTION_MANAGER, null, tlsContextManager); EnvoyServerProtoData.Listener listener = new EnvoyServerProtoData.Listener( "listener1", LOCAL_IP, Arrays.asList(filterChainLessSpecific, filterChainMoreSpecific), defaultFilterChain); - XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener); + LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); registeredWatcher.onChanged(listenerUpdate); - DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); + EnvoyServerProtoData.DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); assertThat(tlsContext1).isSameInstanceAs(tlsContextMoreSpecific); } @Test public void destPrefixRangeIpv6_moreSpecificWins() - throws UnknownHostException, InvalidProtocolBufferException { + throws UnknownHostException { setupChannel("FE80:0000:0000:0000:0202:B3FF:FE1E:8329", "2001:DB8::8:800:200C:417A", 15000); - DownstreamTlsContext tlsContextLessSpecific = + EnvoyServerProtoData.DownstreamTlsContext tlsContextLessSpecific = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"); EnvoyServerProtoData.FilterChainMatch filterChainMatchLessSpecific = new EnvoyServerProtoData.FilterChainMatch( @@ -406,10 +411,11 @@ public class FilterChainMatchTest { Arrays.asList(), null); EnvoyServerProtoData.FilterChain filterChainLessSpecific = - new EnvoyServerProtoData.FilterChain(filterChainMatchLessSpecific, tlsContextLessSpecific, - tlsContextManager); + new EnvoyServerProtoData.FilterChain( + "filter-chain-foo", filterChainMatchLessSpecific, HTTP_CONNECTION_MANAGER, + tlsContextLessSpecific, tlsContextManager); - DownstreamTlsContext tlsContextMoreSpecific = + EnvoyServerProtoData.DownstreamTlsContext tlsContextMoreSpecific = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT2", "VA2"); EnvoyServerProtoData.FilterChainMatch filterChainMatchMoreSpecific = new EnvoyServerProtoData.FilterChainMatch( @@ -422,27 +428,28 @@ public class FilterChainMatchTest { Arrays.asList(), null); EnvoyServerProtoData.FilterChain filterChainMoreSpecific = - new EnvoyServerProtoData.FilterChain(filterChainMatchMoreSpecific, tlsContextMoreSpecific, - tlsContextManager); - EnvoyServerProtoData.FilterChain defaultFilterChain = - new EnvoyServerProtoData.FilterChain(null, null, tlsContextManager); + new EnvoyServerProtoData.FilterChain( + "filter-chain-bar", filterChainMatchMoreSpecific, HTTP_CONNECTION_MANAGER, + tlsContextMoreSpecific, tlsContextManager); + EnvoyServerProtoData.FilterChain defaultFilterChain = new EnvoyServerProtoData.FilterChain( + "filter-chain-baz", null, HTTP_CONNECTION_MANAGER, null, tlsContextManager); EnvoyServerProtoData.Listener listener = new EnvoyServerProtoData.Listener( "listener1", "FE80:0000:0000:0000:0202:B3FF:FE1E:8329", Arrays.asList(filterChainLessSpecific, filterChainMoreSpecific), defaultFilterChain); - XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener); + LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); registeredWatcher.onChanged(listenerUpdate); - DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); + EnvoyServerProtoData.DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); assertThat(tlsContext1).isSameInstanceAs(tlsContextMoreSpecific); } @Test public void destPrefixRange_moreSpecificWith2Wins() - throws UnknownHostException, InvalidProtocolBufferException { + throws UnknownHostException { setupChannel(LOCAL_IP, REMOTE_IP, 15000); - DownstreamTlsContext tlsContextMoreSpecificWith2 = + EnvoyServerProtoData.DownstreamTlsContext tlsContextMoreSpecificWith2 = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"); EnvoyServerProtoData.FilterChainMatch filterChainMatchMoreSpecificWith2 = new EnvoyServerProtoData.FilterChainMatch( @@ -458,9 +465,10 @@ public class FilterChainMatchTest { null); EnvoyServerProtoData.FilterChain filterChainMoreSpecificWith2 = new EnvoyServerProtoData.FilterChain( - filterChainMatchMoreSpecificWith2, tlsContextMoreSpecificWith2, tlsContextManager); + "filter-chain-foo", filterChainMatchMoreSpecificWith2, HTTP_CONNECTION_MANAGER, + tlsContextMoreSpecificWith2, tlsContextManager); - DownstreamTlsContext tlsContextLessSpecific = + EnvoyServerProtoData.DownstreamTlsContext tlsContextLessSpecific = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT2", "VA2"); EnvoyServerProtoData.FilterChainMatch filterChainMatchLessSpecific = new EnvoyServerProtoData.FilterChainMatch( @@ -473,26 +481,27 @@ public class FilterChainMatchTest { Arrays.asList(), null); EnvoyServerProtoData.FilterChain filterChainLessSpecific = - new EnvoyServerProtoData.FilterChain(filterChainMatchLessSpecific, tlsContextLessSpecific, - tlsContextManager); - EnvoyServerProtoData.FilterChain defaultFilterChain = - new EnvoyServerProtoData.FilterChain(null, null, tlsContextManager); + new EnvoyServerProtoData.FilterChain( + "filter-chain-bar", filterChainMatchLessSpecific, HTTP_CONNECTION_MANAGER, + tlsContextLessSpecific, tlsContextManager); + EnvoyServerProtoData.FilterChain defaultFilterChain = new EnvoyServerProtoData.FilterChain( + "filter-chain-baz", null, HTTP_CONNECTION_MANAGER, null, tlsContextManager); EnvoyServerProtoData.Listener listener = new EnvoyServerProtoData.Listener( "listener1", LOCAL_IP, Arrays.asList(filterChainMoreSpecificWith2, filterChainLessSpecific), defaultFilterChain); - XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener); + LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); registeredWatcher.onChanged(listenerUpdate); - DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); + EnvoyServerProtoData.DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); assertThat(tlsContext1).isSameInstanceAs(tlsContextMoreSpecificWith2); } @Test public void sourceTypeMismatch_returnDefaultFilterChain() throws UnknownHostException { setupChannel(LOCAL_IP, REMOTE_IP, 15000); - DownstreamTlsContext tlsContextMismatch = + EnvoyServerProtoData.DownstreamTlsContext tlsContextMismatch = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"); EnvoyServerProtoData.FilterChainMatch filterChainMatchWithMismatch = new EnvoyServerProtoData.FilterChainMatch( @@ -505,26 +514,27 @@ public class FilterChainMatchTest { Arrays.asList(), null); EnvoyServerProtoData.FilterChain filterChainWithMismatch = - new EnvoyServerProtoData.FilterChain(filterChainMatchWithMismatch, tlsContextMismatch, - tlsContextManager); - DownstreamTlsContext tlsContextForDefaultFilterChain = + new EnvoyServerProtoData.FilterChain( + "filter-chain-foo", filterChainMatchWithMismatch, HTTP_CONNECTION_MANAGER, + tlsContextMismatch, tlsContextManager); + EnvoyServerProtoData.DownstreamTlsContext tlsContextForDefaultFilterChain = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT2", "VA2"); - EnvoyServerProtoData.FilterChain defaultFilterChain = - new EnvoyServerProtoData.FilterChain(null, tlsContextForDefaultFilterChain, - tlsContextManager); + EnvoyServerProtoData.FilterChain defaultFilterChain = new EnvoyServerProtoData.FilterChain( + "filter-chain-bar", null, HTTP_CONNECTION_MANAGER,tlsContextForDefaultFilterChain, + tlsContextManager); EnvoyServerProtoData.Listener listener = new EnvoyServerProtoData.Listener( "listener1", LOCAL_IP, Arrays.asList(filterChainWithMismatch), defaultFilterChain); - XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener); + LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); registeredWatcher.onChanged(listenerUpdate); - DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); + EnvoyServerProtoData.DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); assertThat(tlsContext1).isSameInstanceAs(tlsContextForDefaultFilterChain); } @Test public void sourceTypeLocal() throws UnknownHostException { setupChannel(LOCAL_IP, LOCAL_IP, 15000); - DownstreamTlsContext tlsContextMatch = + EnvoyServerProtoData.DownstreamTlsContext tlsContextMatch = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"); EnvoyServerProtoData.FilterChainMatch filterChainMatchWithMatch = new EnvoyServerProtoData.FilterChainMatch( @@ -536,28 +546,28 @@ public class FilterChainMatchTest { Arrays.asList(), Arrays.asList(), null); - EnvoyServerProtoData.FilterChain filterChainWithMatch = - new EnvoyServerProtoData.FilterChain(filterChainMatchWithMatch, tlsContextMatch, - tlsContextManager); - DownstreamTlsContext tlsContextForDefaultFilterChain = + EnvoyServerProtoData.FilterChain filterChainWithMatch = new EnvoyServerProtoData.FilterChain( + "filter-chain-foo", filterChainMatchWithMatch, HTTP_CONNECTION_MANAGER, tlsContextMatch, + tlsContextManager); + EnvoyServerProtoData.DownstreamTlsContext tlsContextForDefaultFilterChain = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT2", "VA2"); - EnvoyServerProtoData.FilterChain defaultFilterChain = - new EnvoyServerProtoData.FilterChain(null, tlsContextForDefaultFilterChain, - tlsContextManager); + EnvoyServerProtoData.FilterChain defaultFilterChain = new EnvoyServerProtoData.FilterChain( + "filter-chain-bar", null, HTTP_CONNECTION_MANAGER, tlsContextForDefaultFilterChain, + tlsContextManager); EnvoyServerProtoData.Listener listener = new EnvoyServerProtoData.Listener( "listener1", LOCAL_IP, Arrays.asList(filterChainWithMatch), defaultFilterChain); - XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener); + LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); registeredWatcher.onChanged(listenerUpdate); - DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); + EnvoyServerProtoData.DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); assertThat(tlsContext1).isSameInstanceAs(tlsContextMatch); } @Test public void sourcePrefixRange_moreSpecificWith2Wins() - throws UnknownHostException, InvalidProtocolBufferException { + throws UnknownHostException { setupChannel(LOCAL_IP, REMOTE_IP, 15000); - DownstreamTlsContext tlsContextMoreSpecificWith2 = + EnvoyServerProtoData.DownstreamTlsContext tlsContextMoreSpecificWith2 = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"); EnvoyServerProtoData.FilterChainMatch filterChainMatchMoreSpecificWith2 = new EnvoyServerProtoData.FilterChainMatch( @@ -573,9 +583,10 @@ public class FilterChainMatchTest { null); EnvoyServerProtoData.FilterChain filterChainMoreSpecificWith2 = new EnvoyServerProtoData.FilterChain( - filterChainMatchMoreSpecificWith2, tlsContextMoreSpecificWith2, tlsContextManager); + "filter-chain-foo", filterChainMatchMoreSpecificWith2, HTTP_CONNECTION_MANAGER, + tlsContextMoreSpecificWith2, tlsContextManager); - DownstreamTlsContext tlsContextLessSpecific = + EnvoyServerProtoData.DownstreamTlsContext tlsContextLessSpecific = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT2", "VA2"); EnvoyServerProtoData.FilterChainMatch filterChainMatchLessSpecific = new EnvoyServerProtoData.FilterChainMatch( @@ -588,27 +599,28 @@ public class FilterChainMatchTest { Arrays.asList(), null); EnvoyServerProtoData.FilterChain filterChainLessSpecific = - new EnvoyServerProtoData.FilterChain(filterChainMatchLessSpecific, tlsContextLessSpecific, - tlsContextManager); - EnvoyServerProtoData.FilterChain defaultFilterChain = - new EnvoyServerProtoData.FilterChain(null, null, tlsContextManager); + new EnvoyServerProtoData.FilterChain( + "filter-chain-bar", filterChainMatchLessSpecific, HTTP_CONNECTION_MANAGER, + tlsContextLessSpecific, tlsContextManager); + EnvoyServerProtoData.FilterChain defaultFilterChain = new EnvoyServerProtoData.FilterChain( + "filter-chain-baz", null, HTTP_CONNECTION_MANAGER, null, tlsContextManager); EnvoyServerProtoData.Listener listener = new EnvoyServerProtoData.Listener( "listener1", LOCAL_IP, Arrays.asList(filterChainMoreSpecificWith2, filterChainLessSpecific), defaultFilterChain); - XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener); + LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); registeredWatcher.onChanged(listenerUpdate); - DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); + EnvoyServerProtoData.DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); assertThat(tlsContext1).isSameInstanceAs(tlsContextMoreSpecificWith2); } @Test public void sourcePrefixRange_2Matchers_expectException() - throws UnknownHostException, InvalidProtocolBufferException { + throws UnknownHostException { setupChannel(LOCAL_IP, REMOTE_IP, 15000); - DownstreamTlsContext tlsContext1 = + EnvoyServerProtoData.DownstreamTlsContext tlsContext1 = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"); EnvoyServerProtoData.FilterChainMatch filterChainMatch1 = new EnvoyServerProtoData.FilterChainMatch( @@ -622,10 +634,11 @@ public class FilterChainMatchTest { Arrays.asList(), Arrays.asList(), null); - EnvoyServerProtoData.FilterChain filterChain1 = - new EnvoyServerProtoData.FilterChain(filterChainMatch1, tlsContext1, tlsContextManager); + EnvoyServerProtoData.FilterChain filterChain1 = new EnvoyServerProtoData.FilterChain( + "filter-chain-foo", filterChainMatch1, HTTP_CONNECTION_MANAGER, tlsContext1, + tlsContextManager); - DownstreamTlsContext tlsContext2 = + EnvoyServerProtoData.DownstreamTlsContext tlsContext2 = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT2", "VA2"); EnvoyServerProtoData.FilterChainMatch filterChainMatch2 = new EnvoyServerProtoData.FilterChainMatch( @@ -637,14 +650,15 @@ public class FilterChainMatchTest { Arrays.asList(), Arrays.asList(), null); - EnvoyServerProtoData.FilterChain filterChain2 = - new EnvoyServerProtoData.FilterChain(filterChainMatch2, tlsContext2, tlsContextManager); - EnvoyServerProtoData.FilterChain defaultFilterChain = - new EnvoyServerProtoData.FilterChain(null, null, null); + EnvoyServerProtoData.FilterChain filterChain2 = new EnvoyServerProtoData.FilterChain( + "filter-chain-bar", filterChainMatch2, HTTP_CONNECTION_MANAGER, tlsContext2, + tlsContextManager); + EnvoyServerProtoData.FilterChain defaultFilterChain = new EnvoyServerProtoData.FilterChain( + "filter-chain-baz", null, HTTP_CONNECTION_MANAGER, null, null); EnvoyServerProtoData.Listener listener = new EnvoyServerProtoData.Listener( "listener1", LOCAL_IP, Arrays.asList(filterChain1, filterChain2), defaultFilterChain); - XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener); + LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); registeredWatcher.onChanged(listenerUpdate); try { xdsClientWrapperForServerSds.getSslContextProviderSupplier(channel); @@ -656,9 +670,9 @@ public class FilterChainMatchTest { @Test public void sourcePortMatch_exactMatchWinsOverEmptyList() - throws UnknownHostException, InvalidProtocolBufferException { + throws UnknownHostException { setupChannel(LOCAL_IP, REMOTE_IP, 15000); - DownstreamTlsContext tlsContextEmptySourcePorts = + EnvoyServerProtoData.DownstreamTlsContext tlsContextEmptySourcePorts = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"); EnvoyServerProtoData.FilterChainMatch filterChainMatchEmptySourcePorts = new EnvoyServerProtoData.FilterChainMatch( @@ -674,9 +688,10 @@ public class FilterChainMatchTest { null); EnvoyServerProtoData.FilterChain filterChainEmptySourcePorts = new EnvoyServerProtoData.FilterChain( - filterChainMatchEmptySourcePorts, tlsContextEmptySourcePorts, tlsContextManager); + "filter-chain-foo", filterChainMatchEmptySourcePorts, HTTP_CONNECTION_MANAGER, + tlsContextEmptySourcePorts, tlsContextManager); - DownstreamTlsContext tlsContextSourcePortMatch = + EnvoyServerProtoData.DownstreamTlsContext tlsContextSourcePortMatch = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT2", "VA2"); EnvoyServerProtoData.FilterChainMatch filterChainMatchSourcePortMatch = new EnvoyServerProtoData.FilterChainMatch( @@ -690,18 +705,19 @@ public class FilterChainMatchTest { null); EnvoyServerProtoData.FilterChain filterChainSourcePortMatch = new EnvoyServerProtoData.FilterChain( - filterChainMatchSourcePortMatch, tlsContextSourcePortMatch, tlsContextManager); - EnvoyServerProtoData.FilterChain defaultFilterChain = - new EnvoyServerProtoData.FilterChain(null, null, tlsContextManager); + "filter-chain-bar", filterChainMatchSourcePortMatch, HTTP_CONNECTION_MANAGER, + tlsContextSourcePortMatch, tlsContextManager); + EnvoyServerProtoData.FilterChain defaultFilterChain = new EnvoyServerProtoData.FilterChain( + "filter-chain-baz", null, HTTP_CONNECTION_MANAGER, null, tlsContextManager); EnvoyServerProtoData.Listener listener = new EnvoyServerProtoData.Listener( "listener1", LOCAL_IP, Arrays.asList(filterChainEmptySourcePorts, filterChainSourcePortMatch), defaultFilterChain); - XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener); + LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); registeredWatcher.onChanged(listenerUpdate); - DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); + EnvoyServerProtoData.DownstreamTlsContext tlsContext1 = getDownstreamTlsContext(); assertThat(tlsContext1).isSameInstanceAs(tlsContextSourcePortMatch); } @@ -713,19 +729,19 @@ public class FilterChainMatchTest { * source-prefix range. - 5th step: out of 2 one with matching source port gets picked */ @Test - public void filterChain_5stepMatch() throws UnknownHostException, InvalidProtocolBufferException { + public void filterChain_5stepMatch() throws UnknownHostException { setupChannel(LOCAL_IP, REMOTE_IP, 15000); - DownstreamTlsContext tlsContext1 = + EnvoyServerProtoData.DownstreamTlsContext tlsContext1 = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"); - DownstreamTlsContext tlsContext2 = + EnvoyServerProtoData.DownstreamTlsContext tlsContext2 = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT2", "VA2"); - DownstreamTlsContext tlsContext3 = + EnvoyServerProtoData.DownstreamTlsContext tlsContext3 = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT3", "VA3"); - DownstreamTlsContext tlsContext4 = + EnvoyServerProtoData.DownstreamTlsContext tlsContext4 = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT4", "VA4"); - DownstreamTlsContext tlsContext5 = + EnvoyServerProtoData.DownstreamTlsContext tlsContext5 = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT5", "VA5"); - DownstreamTlsContext tlsContext6 = + EnvoyServerProtoData.DownstreamTlsContext tlsContext6 = CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT6", "VA6"); // has dest port and specific prefix ranges: gets eliminated in step 1 @@ -739,8 +755,9 @@ public class FilterChainMatchTest { Arrays.asList(), Arrays.asList(), null); - EnvoyServerProtoData.FilterChain filterChain1 = - new EnvoyServerProtoData.FilterChain(filterChainMatch1, tlsContext1, tlsContextManager); + EnvoyServerProtoData.FilterChain filterChain1 = new EnvoyServerProtoData.FilterChain( + "filter-chain-1", filterChainMatch1, HTTP_CONNECTION_MANAGER, tlsContext1, + tlsContextManager); // next 5 use prefix range: 4 with prefixLen of 30 and last one with 29 @@ -755,8 +772,9 @@ public class FilterChainMatchTest { Arrays.asList(), Arrays.asList(), null); - EnvoyServerProtoData.FilterChain filterChain2 = - new EnvoyServerProtoData.FilterChain(filterChainMatch2, tlsContext2, tlsContextManager); + EnvoyServerProtoData.FilterChain filterChain2 = new EnvoyServerProtoData.FilterChain( + "filter-chain-2", filterChainMatch2, HTTP_CONNECTION_MANAGER, tlsContext2, + tlsContextManager); // has prefix ranges with one not matching and source type local: gets eliminated in step 3 EnvoyServerProtoData.FilterChainMatch filterChainMatch3 = @@ -771,8 +789,9 @@ public class FilterChainMatchTest { Arrays.asList(), Arrays.asList(), null); - EnvoyServerProtoData.FilterChain filterChain3 = - new EnvoyServerProtoData.FilterChain(filterChainMatch3, tlsContext3, tlsContextManager); + EnvoyServerProtoData.FilterChain filterChain3 = new EnvoyServerProtoData.FilterChain( + "filter-chain-3", filterChainMatch3, HTTP_CONNECTION_MANAGER, tlsContext3, + tlsContextManager); // has prefix ranges with both matching and source type external but non matching source port: // gets eliminated in step 5 @@ -789,7 +808,9 @@ public class FilterChainMatchTest { Arrays.asList(), null); EnvoyServerProtoData.FilterChain filterChain4 = - new EnvoyServerProtoData.FilterChain(filterChainMatch4, tlsContext4, tlsContextManager); + new EnvoyServerProtoData.FilterChain( + "filter-chain-4", filterChainMatch4, HTTP_CONNECTION_MANAGER, tlsContext4, + tlsContextManager); // has prefix ranges with both matching and source type external and matching source port: this // gets selected @@ -808,7 +829,9 @@ public class FilterChainMatchTest { Arrays.asList(), null); EnvoyServerProtoData.FilterChain filterChain5 = - new EnvoyServerProtoData.FilterChain(filterChainMatch5, tlsContext5, tlsContextManager); + new EnvoyServerProtoData.FilterChain( + "filter-chain-5", filterChainMatch5, HTTP_CONNECTION_MANAGER, tlsContext5, + tlsContextManager); // has prefix range with prefixLen of 29: gets eliminated in step 2 EnvoyServerProtoData.FilterChainMatch filterChainMatch6 = @@ -822,10 +845,12 @@ public class FilterChainMatchTest { Arrays.asList(), null); EnvoyServerProtoData.FilterChain filterChain6 = - new EnvoyServerProtoData.FilterChain(filterChainMatch6, tlsContext6, tlsContextManager); + new EnvoyServerProtoData.FilterChain( + "filter-chain-6", filterChainMatch6, HTTP_CONNECTION_MANAGER, tlsContext6, + tlsContextManager); - EnvoyServerProtoData.FilterChain defaultFilterChain = - new EnvoyServerProtoData.FilterChain(null, null, tlsContextManager); + EnvoyServerProtoData.FilterChain defaultFilterChain = new EnvoyServerProtoData.FilterChain( + "filter-chain-7", null, HTTP_CONNECTION_MANAGER, null, tlsContextManager); EnvoyServerProtoData.Listener listener = new EnvoyServerProtoData.Listener( "listener1", @@ -833,112 +858,77 @@ public class FilterChainMatchTest { Arrays.asList( filterChain1, filterChain2, filterChain3, filterChain4, filterChain5, filterChain6), defaultFilterChain); - XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener); + LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); registeredWatcher.onChanged(listenerUpdate); - DownstreamTlsContext tlsContextPicked = getDownstreamTlsContext(); + EnvoyServerProtoData.DownstreamTlsContext tlsContextPicked = getDownstreamTlsContext(); assertThat(tlsContextPicked).isSameInstanceAs(tlsContext5); } @Test public void filterChainMatch_unsupportedMatchers() - throws InvalidProtocolBufferException, UnknownHostException { + throws UnknownHostException { setupChannel(LOCAL_IP, REMOTE_IP, 15000); - io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext tlsContext1 = - CommonTlsContextTestsUtil.buildTestDownstreamTlsContext( - "CERT1", "ROOTCA"); - io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext tlsContext2 = - CommonTlsContextTestsUtil.buildTestDownstreamTlsContext( - "CERT2", "ROOTCA"); - io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext tlsContext3 = - CommonTlsContextTestsUtil.buildTestDownstreamTlsContext( - "CERT3", "ROOTCA"); + EnvoyServerProtoData.DownstreamTlsContext tlsContext1 = + CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "ROOTCA"); + EnvoyServerProtoData.DownstreamTlsContext tlsContext2 = + CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT2", "ROOTCA"); + EnvoyServerProtoData.DownstreamTlsContext tlsContext3 = + CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT3", "ROOTCA"); - FilterChainMatch filterChainMatch1 = - FilterChainMatch.newBuilder() - .addAllServerNames(Arrays.asList("server1", "server2")) - .setTransportProtocol("tls") - .addAllApplicationProtocols(Arrays.asList("managed-mtls", "h2")) - .addPrefixRanges(CidrRange.newBuilder() - .setAddressPrefix("10.1.0.0") - .setPrefixLen(UInt32Value.of(16)) - .build()) - .build(); + EnvoyServerProtoData.FilterChainMatch filterChainMatch1 = + new EnvoyServerProtoData.FilterChainMatch( + 0 /* destinationPort */, + Collections.singletonList( + new EnvoyServerProtoData.CidrRange("10.1.0.0", 16)) /* prefixRange */, + Arrays.asList("managed-mtls", "h2") /* applicationProtocol */, + Collections.emptyList() /* sourcePrefixRanges */, + EnvoyServerProtoData.ConnectionSourceType.ANY /* sourceType */, + Collections.emptyList() /* sourcePorts */, + Arrays.asList("server1", "server2") /* serverNames */, + "tls" /* transportProtocol */); - FilterChainMatch filterChainMatch2 = - FilterChainMatch.newBuilder() - .addPrefixRanges(CidrRange.newBuilder() - .setAddressPrefix("10.0.0.0") - .setPrefixLen(UInt32Value.of(8)) - .build()) - .build(); + EnvoyServerProtoData.FilterChainMatch filterChainMatch2 = + new EnvoyServerProtoData.FilterChainMatch( + 0 /* destinationPort */, + Collections.singletonList( + new EnvoyServerProtoData.CidrRange("10.0.0.0", 8)) /* prefixRange */, + Collections.emptyList() /* applicationProtocol */, + Collections.emptyList() /* sourcePrefixRanges */, + EnvoyServerProtoData.ConnectionSourceType.ANY /* sourceType */, + Collections.emptyList() /* sourcePorts */, + Collections.emptyList() /* serverNames */, + "" /* transportProtocol */); - FilterChain filterChain1 = - FilterChain.newBuilder() - .setFilterChainMatch(filterChainMatch1) - .setTransportSocket(TransportSocket.newBuilder().setName("envoy.transport_sockets.tls") - .setTypedConfig(Any.pack(tlsContext1)) - .build()) - .addFilters(Filter.newBuilder() - .setName("envoy.http_connection_manager") - .setTypedConfig(Any.newBuilder() - .setTypeUrl( - "type.googleapis.com/" - + "envoy.extensions.filters.network.http_connection_manager" - + ".v3.HttpConnectionManager")) - .build()) - .build(); - FilterChain filterChain2 = - FilterChain.newBuilder() - .setFilterChainMatch(filterChainMatch2) - .setTransportSocket(TransportSocket.newBuilder().setName("envoy.transport_sockets.tls") - .setTypedConfig(Any.pack(tlsContext2)) - .build()) - .addFilters(Filter.newBuilder() - .setName("envoy.http_connection_manager") - .setTypedConfig(Any.newBuilder() - .setTypeUrl( - "type.googleapis.com/" - + "envoy.extensions.filters.network.http_connection_manager" - + ".v3.HttpConnectionManager")) - .build()) - .build(); - FilterChain defaultFilterChain = - FilterChain.newBuilder() - .setTransportSocket(TransportSocket.newBuilder().setName("envoy.transport_sockets.tls") - .setTypedConfig(Any.pack(tlsContext3)) - .build()) - .addFilters(Filter.newBuilder() - .setName("envoy.http_connection_manager") - .setTypedConfig(Any.newBuilder() - .setTypeUrl( - "type.googleapis.com/" - + "envoy.extensions.filters.network.http_connection_manager" - + ".v3.HttpConnectionManager")) - .build()) - .build(); - Address address = - Address.newBuilder() - .setSocketAddress( - SocketAddress.newBuilder().setPortValue(8000).setAddress("10.2.1.34").build()) - .build(); - io.envoyproxy.envoy.config.listener.v3.Listener listener = - io.envoyproxy.envoy.config.listener.v3.Listener.newBuilder() - .setName("8000") - .setAddress(address) - .addFilterChains(filterChain1) - .addFilterChains(filterChain2) - .setDefaultFilterChain(defaultFilterChain) - .setTrafficDirection(TrafficDirection.INBOUND) - .build(); + EnvoyServerProtoData.FilterChainMatch defaultFilterChainMatch = + new EnvoyServerProtoData.FilterChainMatch( + 0 /* destinationPort */, + Collections.emptyList() /* prefixRange */, + Collections.emptyList() /* applicationProtocol */, + Collections.emptyList() /* sourcePrefixRanges */, + EnvoyServerProtoData.ConnectionSourceType.ANY /* sourceType */, + Collections.emptyList() /* sourcePorts */, + Collections.emptyList() /* serverNames */, + "" /* transportProtocol */); - EnvoyServerProtoData.Listener xdsListener = EnvoyServerProtoData.Listener - .fromEnvoyProtoListener(listener, mock(TlsContextManager.class)); - XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(xdsListener); + EnvoyServerProtoData.FilterChain filterChain1 = new EnvoyServerProtoData.FilterChain( + "filter-chain-foo", filterChainMatch1, HTTP_CONNECTION_MANAGER, tlsContext1, + mock(TlsContextManager.class)); + EnvoyServerProtoData.FilterChain filterChain2 = new EnvoyServerProtoData.FilterChain( + "filter-chain-bar", filterChainMatch2, HTTP_CONNECTION_MANAGER, tlsContext2, + mock(TlsContextManager.class)); + + EnvoyServerProtoData.FilterChain defaultFilterChain = new EnvoyServerProtoData.FilterChain( + "filter-chain-baz", defaultFilterChainMatch, HTTP_CONNECTION_MANAGER, tlsContext3, + mock(TlsContextManager.class)); + + EnvoyServerProtoData.Listener listener = new EnvoyServerProtoData.Listener( + "", "10.2.1.34:8000", Arrays.asList(filterChain1, filterChain2), defaultFilterChain); + LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); registeredWatcher.onChanged(listenerUpdate); - DownstreamTlsContext tlsContextPicked = getDownstreamTlsContext(); + EnvoyServerProtoData.DownstreamTlsContext tlsContextPicked = getDownstreamTlsContext(); // assert defaultFilterChain match - assertThat(tlsContextPicked.getCommonTlsContext().getTlsCertificateSdsSecretConfigsList().get(0) - .getName()).isEqualTo("CERT3"); + assertThat(tlsContextPicked.getCommonTlsContext().getTlsCertificateSdsSecretConfigsList() + .get(0).getName()).isEqualTo("CERT3"); } private void setupChannel(String localIp, String remoteIp, int remotePort) diff --git a/xds/src/test/java/io/grpc/xds/XdsClientWrapperForServerSdsTestMisc.java b/xds/src/test/java/io/grpc/xds/XdsClientWrapperForServerSdsTestMisc.java index 75d7b76dbb..a8566a682b 100644 --- a/xds/src/test/java/io/grpc/xds/XdsClientWrapperForServerSdsTestMisc.java +++ b/xds/src/test/java/io/grpc/xds/XdsClientWrapperForServerSdsTestMisc.java @@ -31,6 +31,7 @@ import io.grpc.Status; import io.grpc.StatusException; import io.grpc.inprocess.InProcessSocketAddress; import io.grpc.xds.EnvoyServerProtoData.DownstreamTlsContext; +import io.grpc.xds.XdsClient.LdsUpdate; import io.grpc.xds.internal.sds.CommonTlsContextTestsUtil; import io.grpc.xds.internal.sds.SslContextProvider; import io.grpc.xds.internal.sds.SslContextProviderSupplier; @@ -131,7 +132,7 @@ public class XdsClientWrapperForServerSdsTestMisc { "10.1.2.3", Collections.emptyList(), null); - XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener); + LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); registeredWatcher.onChanged(listenerUpdate); DownstreamTlsContext tlsContext = getDownstreamTlsContext(); assertThat(tlsContext).isNull(); diff --git a/xds/src/test/java/io/grpc/xds/XdsNameResolverTest.java b/xds/src/test/java/io/grpc/xds/XdsNameResolverTest.java index 75f81da558..6a3e4cfb98 100644 --- a/xds/src/test/java/io/grpc/xds/XdsNameResolverTest.java +++ b/xds/src/test/java/io/grpc/xds/XdsNameResolverTest.java @@ -202,11 +202,11 @@ public class XdsNameResolverTest { @SuppressWarnings("unchecked") @Test public void resolving_ldsResourceUpdateRdsName() { - Route route1 = Route.create(RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), + Route route1 = Route.forAction(RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), RouteAction.forCluster( cluster1, Collections.emptyList(), TimeUnit.SECONDS.toNanos(15L)), ImmutableMap.of()); - Route route2 = Route.create(RouteMatch.withPathExactOnly(call2.getFullMethodNameForPath()), + Route route2 = Route.forAction(RouteMatch.withPathExactOnly(call2.getFullMethodNameForPath()), RouteAction.forCluster( cluster2, Collections.emptyList(), TimeUnit.SECONDS.toNanos(20L)), ImmutableMap.of()); @@ -260,7 +260,7 @@ public class XdsNameResolverTest { @SuppressWarnings("unchecked") @Test public void resolving_ldsResourceRevokedAndAddedBack() { - Route route = Route.create(RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), + Route route = Route.forAction(RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), RouteAction.forCluster( cluster1, Collections.emptyList(), TimeUnit.SECONDS.toNanos(15L)), ImmutableMap.of()); @@ -299,7 +299,7 @@ public class XdsNameResolverTest { @SuppressWarnings("unchecked") @Test public void resolving_rdsResourceRevokedAndAddedBack() { - Route route = Route.create(RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), + Route route = Route.forAction(RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), RouteAction.forCluster( cluster1, Collections.emptyList(), TimeUnit.SECONDS.toNanos(15L)), ImmutableMap.of()); @@ -373,11 +373,11 @@ public class XdsNameResolverTest { } private List buildUnmatchedVirtualHosts() { - Route route1 = Route.create(RouteMatch.withPathExactOnly(call2.getFullMethodNameForPath()), + Route route1 = Route.forAction(RouteMatch.withPathExactOnly(call2.getFullMethodNameForPath()), RouteAction.forCluster( cluster2, Collections.emptyList(), TimeUnit.SECONDS.toNanos(15L)), ImmutableMap.of()); - Route route2 = Route.create(RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), + Route route2 = Route.forAction(RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), RouteAction.forCluster( cluster1, Collections.emptyList(), TimeUnit.SECONDS.toNanos(15L)), ImmutableMap.of()); @@ -394,7 +394,7 @@ public class XdsNameResolverTest { public void resolved_noTimeout() { resolver.start(mockListener); FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient(); - Route route = Route.create(RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), + Route route = Route.forAction(RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), RouteAction.forCluster( cluster1, Collections.emptyList(), null), // per-route timeout unset ImmutableMap.of()); @@ -412,7 +412,7 @@ public class XdsNameResolverTest { public void resolved_fallbackToHttpMaxStreamDurationAsTimeout() { resolver.start(mockListener); FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient(); - Route route = Route.create(RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), + Route route = Route.forAction(RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), RouteAction.forCluster( cluster1, Collections.emptyList(), null), // per-route timeout unset ImmutableMap.of()); @@ -436,7 +436,7 @@ public class XdsNameResolverTest { } @Test - public void resolved_simpleCallFailedToRoute() { + public void resolved_simpleCallFailedToRoute_noMatchingRoute() { InternalConfigSelector configSelector = resolveToClusters(); CallInfo call = new CallInfo("FooService", "barMethod"); Result selectResult = configSelector.selectConfig( @@ -448,13 +448,48 @@ public class XdsNameResolverTest { verifyNoMoreInteractions(mockListener); } + @SuppressWarnings("unchecked") + @Test + public void resolved_simpleCallFailedToRoute_routeWithNonForwardingAction() { + resolver.start(mockListener); + FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient(); + xdsClient.deliverLdsUpdate( + Arrays.asList( + Route.forNonForwardingAction( + RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), + ImmutableMap.of()), + Route.forAction( + RouteMatch.withPathExactOnly(call2.getFullMethodNameForPath()), + RouteAction.forCluster(cluster2, Collections.emptyList(), + TimeUnit.SECONDS.toNanos(15L)), + ImmutableMap.of()))); + verify(mockListener).onResult(resolutionResultCaptor.capture()); + ResolutionResult result = resolutionResultCaptor.getValue(); + assertThat(result.getAddresses()).isEmpty(); + assertServiceConfigForLoadBalancingConfig( + Collections.singletonList(cluster2), + (Map) result.getServiceConfig().getConfig()); + assertThat(result.getAttributes().get(InternalXdsAttributes.XDS_CLIENT_POOL)).isNotNull(); + assertThat(result.getAttributes().get(InternalXdsAttributes.CALL_COUNTER_PROVIDER)).isNotNull(); + InternalConfigSelector configSelector = result.getAttributes().get(InternalConfigSelector.KEY); + // Simulates making a call1 RPC. + Result selectResult = configSelector.selectConfig( + new PickSubchannelArgsImpl(call1.methodDescriptor, new Metadata(), CallOptions.DEFAULT)); + Status status = selectResult.getStatus(); + assertThat(status.isOk()).isFalse(); + assertThat(status.getCode()).isEqualTo(Code.UNAVAILABLE); + assertThat(status.getDescription()) + .isEqualTo("Could not route RPC to Route with non-forwarding action"); + verifyNoMoreInteractions(mockListener); + } + @Test public void resolved_rpcHashingByHeader_withoutSubstitution() { resolver.start(mockListener); FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient(); xdsClient.deliverLdsUpdate( Collections.singletonList( - Route.create( + Route.forAction( RouteMatch.withPathExactOnly( "/" + TestMethodDescriptors.voidMethod().getFullMethodName()), RouteAction.forCluster(cluster1, Collections.singletonList(HashPolicy.forHeader( @@ -485,7 +520,7 @@ public class XdsNameResolverTest { FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient(); xdsClient.deliverLdsUpdate( Collections.singletonList( - Route.create( + Route.forAction( RouteMatch.withPathExactOnly( "/" + TestMethodDescriptors.voidMethod().getFullMethodName()), RouteAction.forCluster(cluster1, Collections.singletonList(HashPolicy.forHeader( @@ -522,7 +557,7 @@ public class XdsNameResolverTest { FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient(); xdsClient.deliverLdsUpdate( Collections.singletonList( - Route.create( + Route.forAction( RouteMatch.withPathExactOnly( "/" + TestMethodDescriptors.voidMethod().getFullMethodName()), RouteAction.forCluster(cluster1, Collections.singletonList( @@ -553,7 +588,7 @@ public class XdsNameResolverTest { xdsClient = (FakeXdsClient) resolver.getXdsClient(); xdsClient.deliverLdsUpdate( Collections.singletonList( - Route.create( + Route.forAction( RouteMatch.withPathExactOnly( "/" + TestMethodDescriptors.voidMethod().getFullMethodName()), RouteAction.forCluster(cluster1, Collections.singletonList( @@ -584,13 +619,13 @@ public class XdsNameResolverTest { FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient(); xdsClient.deliverLdsUpdate( Arrays.asList( - Route.create( + Route.forAction( RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), RouteAction.forCluster( "another-cluster", Collections.emptyList(), TimeUnit.SECONDS.toNanos(20L)), ImmutableMap.of()), - Route.create( + Route.forAction( RouteMatch.withPathExactOnly(call2.getFullMethodNameForPath()), RouteAction.forCluster( cluster2, Collections.emptyList(), TimeUnit.SECONDS.toNanos(15L)), @@ -623,13 +658,13 @@ public class XdsNameResolverTest { FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient(); xdsClient.deliverLdsUpdate( Arrays.asList( - Route.create( + Route.forAction( RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), RouteAction.forCluster( "another-cluster", Collections.emptyList(), TimeUnit.SECONDS.toNanos(20L)), ImmutableMap.of()), - Route.create( + Route.forAction( RouteMatch.withPathExactOnly(call2.getFullMethodNameForPath()), RouteAction.forCluster(cluster2, Collections.emptyList(), TimeUnit.SECONDS.toNanos(15L)), @@ -658,12 +693,12 @@ public class XdsNameResolverTest { FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient(); xdsClient.deliverLdsUpdate( Arrays.asList( - Route.create( + Route.forAction( RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), RouteAction.forCluster("another-cluster", Collections.emptyList(), TimeUnit.SECONDS.toNanos(20L)), ImmutableMap.of()), - Route.create( + Route.forAction( RouteMatch.withPathExactOnly(call2.getFullMethodNameForPath()), RouteAction.forCluster(cluster2, Collections.emptyList(), TimeUnit.SECONDS.toNanos(15L)), @@ -677,12 +712,12 @@ public class XdsNameResolverTest { xdsClient.deliverLdsUpdate( Arrays.asList( - Route.create( + Route.forAction( RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), RouteAction.forCluster("another-cluster", Collections.emptyList(), TimeUnit.SECONDS.toNanos(15L)), ImmutableMap.of()), - Route.create( + Route.forAction( RouteMatch.withPathExactOnly(call2.getFullMethodNameForPath()), RouteAction.forCluster(cluster2, Collections.emptyList(), TimeUnit.SECONDS.toNanos(15L)), @@ -698,19 +733,19 @@ public class XdsNameResolverTest { FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient(); xdsClient.deliverLdsUpdate( Collections.singletonList( - Route.create( + Route.forAction( RouteMatch.withPathExactOnly(call2.getFullMethodNameForPath()), RouteAction.forCluster(cluster2, Collections.emptyList(), TimeUnit.SECONDS.toNanos(15L)), ImmutableMap.of()))); xdsClient.deliverLdsUpdate( Arrays.asList( - Route.create( + Route.forAction( RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), RouteAction.forCluster(cluster1, Collections.emptyList(), TimeUnit.SECONDS.toNanos(15L)), ImmutableMap.of()), - Route.create( + Route.forAction( RouteMatch.withPathExactOnly(call2.getFullMethodNameForPath()), RouteAction.forCluster(cluster2, Collections.emptyList(), TimeUnit.SECONDS.toNanos(15L)), @@ -727,7 +762,7 @@ public class XdsNameResolverTest { FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient(); xdsClient.deliverLdsUpdate( Collections.singletonList( - Route.create( + Route.forAction( RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), RouteAction.forWeightedClusters( Arrays.asList( @@ -790,12 +825,12 @@ public class XdsNameResolverTest { FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient(); xdsClient.deliverLdsUpdate( Arrays.asList( - Route.create( + Route.forAction( RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), RouteAction.forCluster(cluster1, Collections.emptyList(), TimeUnit.SECONDS.toNanos(15L)), ImmutableMap.of()), - Route.create( + Route.forAction( RouteMatch.withPathExactOnly(call2.getFullMethodNameForPath()), RouteAction.forCluster(cluster2, Collections.emptyList(), TimeUnit.SECONDS.toNanos(15L)), @@ -1591,7 +1626,8 @@ public class XdsNameResolverTest { } void deliverLdsUpdate(long httpMaxStreamDurationNano, List virtualHosts) { - ldsWatcher.onChanged(new LdsUpdate(httpMaxStreamDurationNano, virtualHosts, null)); + ldsWatcher.onChanged(LdsUpdate.forApiListener(HttpConnectionManager.forVirtualHosts( + httpMaxStreamDurationNano, virtualHosts, null))); } void deliverLdsUpdate(final List routes) { @@ -1599,7 +1635,8 @@ public class XdsNameResolverTest { VirtualHost.create( "virtual-host", Collections.singletonList(AUTHORITY), routes, ImmutableMap.of()); - ldsWatcher.onChanged(new LdsUpdate(0L, Collections.singletonList(virtualHost), null)); + ldsWatcher.onChanged(LdsUpdate.forApiListener(HttpConnectionManager.forVirtualHosts( + 0L, Collections.singletonList(virtualHost), null))); } void deliverLdsUpdateWithFaultInjection( @@ -1625,7 +1662,7 @@ public class XdsNameResolverTest { overrideConfig = routeFaultConfig == null ? ImmutableMap.of() : ImmutableMap.of(FAULT_FILTER_INSTANCE_NAME, routeFaultConfig); - Route route = Route.create( + Route route = Route.forAction( RouteMatch.create( PathMatcher.fromPrefix("/", false), Collections.emptyList(), null), RouteAction.forWeightedClusters( @@ -1642,7 +1679,8 @@ public class XdsNameResolverTest { Collections.singletonList(AUTHORITY), Collections.singletonList(route), overrideConfig); - ldsWatcher.onChanged(new LdsUpdate(0L, Collections.singletonList(virtualHost), filterChain)); + ldsWatcher.onChanged(LdsUpdate.forApiListener(HttpConnectionManager.forVirtualHosts( + 0L, Collections.singletonList(virtualHost), filterChain))); } void deliverLdsUpdateWithNoRouterFilter() { @@ -1651,8 +1689,8 @@ public class XdsNameResolverTest { Collections.singletonList(AUTHORITY), Collections.emptyList(), Collections.emptyMap()); - ldsWatcher.onChanged(new LdsUpdate( - 0L, Collections.singletonList(virtualHost), ImmutableList.of())); + ldsWatcher.onChanged(LdsUpdate.forApiListener(HttpConnectionManager.forVirtualHosts( + 0L, Collections.singletonList(virtualHost), ImmutableList.of()))); } void deliverLdsUpdateForRdsNameWithFaultInjection( @@ -1664,11 +1702,13 @@ public class XdsNameResolverTest { ImmutableList filterChain = ImmutableList.of( new NamedFilterConfig(FAULT_FILTER_INSTANCE_NAME, httpFilterFaultConfig), new NamedFilterConfig(ROUTER_FILTER_INSTANCE_NAME, RouterFilter.ROUTER_CONFIG)); - ldsWatcher.onChanged(new LdsUpdate(0L, rdsName, filterChain)); + ldsWatcher.onChanged(LdsUpdate.forApiListener(HttpConnectionManager.forRdsName( + 0L, rdsName, filterChain))); } void deliverLdsUpdateForRdsName(String rdsName) { - ldsWatcher.onChanged(new LdsUpdate(0, rdsName, null)); + ldsWatcher.onChanged(LdsUpdate.forApiListener(HttpConnectionManager.forRdsName( + 0, rdsName, null))); } void deliverLdsResourceNotFound() { @@ -1690,7 +1730,7 @@ public class XdsNameResolverTest { overrideConfig = routFaultConfig == null ? ImmutableMap.of() : ImmutableMap.of(FAULT_FILTER_INSTANCE_NAME, routFaultConfig); - Route route = Route.create( + Route route = Route.forAction( RouteMatch.create( PathMatcher.fromPrefix("/", false), Collections.emptyList(), null), RouteAction.forWeightedClusters( diff --git a/xds/src/test/java/io/grpc/xds/XdsSdsClientServerTest.java b/xds/src/test/java/io/grpc/xds/XdsSdsClientServerTest.java index 2d7a2fbce1..e7690cf4af 100644 --- a/xds/src/test/java/io/grpc/xds/XdsSdsClientServerTest.java +++ b/xds/src/test/java/io/grpc/xds/XdsSdsClientServerTest.java @@ -48,6 +48,8 @@ import io.grpc.testing.protobuf.SimpleResponse; import io.grpc.testing.protobuf.SimpleServiceGrpc; import io.grpc.xds.EnvoyServerProtoData.DownstreamTlsContext; import io.grpc.xds.EnvoyServerProtoData.UpstreamTlsContext; +import io.grpc.xds.Filter.NamedFilterConfig; +import io.grpc.xds.XdsClient.LdsUpdate; import io.grpc.xds.internal.sds.CommonTlsContextTestsUtil; import io.grpc.xds.internal.sds.SslContextProviderSupplier; import io.grpc.xds.internal.sds.TlsContextManagerImpl; @@ -59,6 +61,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; @@ -336,7 +339,7 @@ public class XdsSdsClientServerTest { TlsContextManager tlsContextManager) { EnvoyServerProtoData.Listener listener = buildListener("listener1", "0.0.0.0", tlsContext, tlsContextManager); - XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener); + LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); registeredWatcher.onChanged(listenerUpdate); } @@ -366,8 +369,12 @@ public class XdsSdsClientServerTest { Arrays.asList(), Arrays.asList(), null); - EnvoyServerProtoData.FilterChain defaultFilterChain = - new EnvoyServerProtoData.FilterChain(filterChainMatch, tlsContext, tlsContextManager); + // HttpConnectionManager currently not used for server side. + HttpConnectionManager httpConnectionManager = HttpConnectionManager.forRdsName( + 0L, "does not matter", Collections.emptyList()); + EnvoyServerProtoData.FilterChain defaultFilterChain = new EnvoyServerProtoData.FilterChain( + "filter-chain-foo", filterChainMatch, httpConnectionManager, tlsContext, + tlsContextManager); EnvoyServerProtoData.Listener listener = new EnvoyServerProtoData.Listener(name, address, Arrays.asList(defaultFilterChain), null); return listener; diff --git a/xds/src/test/java/io/grpc/xds/XdsServerTestHelper.java b/xds/src/test/java/io/grpc/xds/XdsServerTestHelper.java index a960818059..faa3ccac7f 100644 --- a/xds/src/test/java/io/grpc/xds/XdsServerTestHelper.java +++ b/xds/src/test/java/io/grpc/xds/XdsServerTestHelper.java @@ -23,9 +23,12 @@ import static org.mockito.Mockito.when; import io.grpc.InsecureChannelCredentials; import io.grpc.internal.ObjectPool; +import io.grpc.xds.Filter.NamedFilterConfig; +import io.grpc.xds.XdsClient.LdsUpdate; import java.io.IOException; import java.net.ServerSocket; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import javax.annotation.Nullable; @@ -121,7 +124,7 @@ class XdsServerTestHelper { EnvoyServerProtoData.DownstreamTlsContext tlsContext, TlsContextManager tlsContextManager) { EnvoyServerProtoData.Listener listener = buildTestListener("listener1", "10.1.2.3", Arrays.asList(), tlsContext, null, tlsContextManager); - XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener); + LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); registeredWatcher.onChanged(listenerUpdate); } @@ -132,7 +135,7 @@ class XdsServerTestHelper { TlsContextManager tlsContextManager) { EnvoyServerProtoData.Listener listener = buildTestListener("listener1", "10.1.2.3", sourcePorts, tlsContext, tlsContextForDefaultFilterChain, tlsContextManager); - XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener); + LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); registeredWatcher.onChanged(listenerUpdate); } @@ -158,11 +161,15 @@ class XdsServerTestHelper { sourcePorts, Arrays.asList(), null); - EnvoyServerProtoData.FilterChain filterChain1 = - new EnvoyServerProtoData.FilterChain(filterChainMatch1, tlsContext, tlsContextManager); - EnvoyServerProtoData.FilterChain defaultFilterChain = - new EnvoyServerProtoData.FilterChain(null, tlsContextForDefaultFilterChain, - tlsContextManager); + // HttpConnectionManager currently not used for server side. + HttpConnectionManager httpConnectionManager = HttpConnectionManager.forRdsName( + 0L, "does not matter", Collections.emptyList()); + EnvoyServerProtoData.FilterChain filterChain1 = new EnvoyServerProtoData.FilterChain( + "filter-chain-foo", filterChainMatch1, httpConnectionManager, tlsContext, + tlsContextManager); + EnvoyServerProtoData.FilterChain defaultFilterChain = new EnvoyServerProtoData.FilterChain( + "filter-chain-bar", null, httpConnectionManager, tlsContextForDefaultFilterChain, + tlsContextManager); EnvoyServerProtoData.Listener listener = new EnvoyServerProtoData.Listener( name, address, Arrays.asList(filterChain1), defaultFilterChain);