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);