From abfe46b99dd9b541734ec3be154d290c749fa15f Mon Sep 17 00:00:00 2001 From: Michael Lumish Date: Fri, 2 Apr 2021 11:12:41 -0700 Subject: [PATCH] grpc-js-xds: Remove env var protection for routing feature --- packages/grpc-js-xds/src/environment.ts | 8 +- packages/grpc-js-xds/src/resolver-xds.ts | 194 ++++++++---------- .../src/xds-stream-state/rds-state.ts | 79 ++++--- 3 files changed, 123 insertions(+), 158 deletions(-) diff --git a/packages/grpc-js-xds/src/environment.ts b/packages/grpc-js-xds/src/environment.ts index 56d233dd..c2c7f2e0 100644 --- a/packages/grpc-js-xds/src/environment.ts +++ b/packages/grpc-js-xds/src/environment.ts @@ -13,10 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. * - */ - -/** - * Environment variable protection for traffic splitting and routing - * https://github.com/grpc/proposal/blob/master/A28-xds-traffic-splitting-and-routing.md#xds-resolver-and-xds-client - */ -export const GRPC_XDS_EXPERIMENTAL_ROUTING = (process.env.GRPC_XDS_EXPERIMENTAL_ROUTING === 'true'); \ No newline at end of file + */ \ No newline at end of file diff --git a/packages/grpc-js-xds/src/resolver-xds.ts b/packages/grpc-js-xds/src/resolver-xds.ts index c07370b7..102a5234 100644 --- a/packages/grpc-js-xds/src/resolver-xds.ts +++ b/packages/grpc-js-xds/src/resolver-xds.ts @@ -30,7 +30,6 @@ import { Listener__Output } from './generated/envoy/api/v2/Listener'; import { Watcher } from './xds-stream-state/xds-stream-state'; import { RouteConfiguration__Output } from './generated/envoy/api/v2/RouteConfiguration'; import { HttpConnectionManager__Output } from './generated/envoy/config/filter/network/http_connection_manager/v2/HttpConnectionManager'; -import { GRPC_XDS_EXPERIMENTAL_ROUTING } from './environment'; import { CdsLoadBalancingConfig } from './load-balancer-cds'; import { VirtualHost__Output } from './generated/envoy/api/v2/route/VirtualHost'; import { RouteMatch__Output } from './generated/envoy/api/v2/route/RouteMatch'; @@ -288,116 +287,93 @@ class XdsResolver implements Resolver { private handleRouteConfig(routeConfig: RouteConfiguration__Output) { this.latestRouteConfig = routeConfig; - if (GRPC_XDS_EXPERIMENTAL_ROUTING) { - const virtualHost = findVirtualHostForDomain(routeConfig.virtual_hosts, this.target.path); - if (virtualHost === null) { - this.reportResolutionError('No matching route found'); - return; - } - trace('Received virtual host config ' + JSON.stringify(virtualHost, undefined, 2)); - const allConfigClusters = new Set(); - const matchList: {matcher: Matcher, action: RouteAction}[] = []; - for (const route of virtualHost.routes) { - let routeAction: RouteAction; - switch (route.route!.cluster_specifier) { - case 'cluster_header': - continue; - case 'cluster':{ - const cluster = route.route!.cluster!; - allConfigClusters.add(cluster); - routeAction = new SingleClusterRouteAction(cluster); - break; - } - case 'weighted_clusters': { - const weightedClusters: WeightedCluster[] = []; - for (const clusterWeight of route.route!.weighted_clusters!.clusters) { - allConfigClusters.add(clusterWeight.name); - weightedClusters.push({name: clusterWeight.name, weight: clusterWeight.weight?.value ?? 0}); - } - routeAction = new WeightedClusterRouteAction(weightedClusters, route.route!.weighted_clusters!.total_weight?.value ?? 100); - } - } - const routeMatcher = getPredicateForMatcher(route.match!); - matchList.push({matcher: routeMatcher, action: routeAction}); - } - /* Mark clusters that are not in this route config, and remove ones with - * no references */ - for (const [name, refCount] of Array.from(this.clusterRefcounts.entries())) { - if (!allConfigClusters.has(name)) { - refCount.inLastConfig = false; - if (refCount.refCount === 0) { - this.clusterRefcounts.delete(name); - } - } - } - // Add any new clusters from this route config - for (const name of allConfigClusters) { - if (this.clusterRefcounts.has(name)) { - this.clusterRefcounts.get(name)!.inLastConfig = true; - } else { - this.clusterRefcounts.set(name, {inLastConfig: true, refCount: 0}); - } - } - const configSelector: ConfigSelector = (methodName, metadata) => { - for (const {matcher, action} of matchList) { - if (matcher.apply(methodName, metadata)) { - const clusterName = action.getCluster(); - this.refCluster(clusterName); - const onCommitted = () => { - this.unrefCluster(clusterName); - } - return { - methodConfig: {name: []}, - onCommitted: onCommitted, - pickInformation: {cluster: clusterName}, - status: status.OK - }; - } - } - return { - methodConfig: {name: []}, - // cluster won't be used here, but it's set because of some TypeScript weirdness - pickInformation: {cluster: ''}, - status: status.UNAVAILABLE - }; - }; - trace('Created ConfigSelector with configuration:'); - for (const {matcher, action} of matchList) { - trace(matcher.toString()); - trace('=> ' + action.toString()); - } - const clusterConfigMap = new Map(); - for (const clusterName of this.clusterRefcounts.keys()) { - clusterConfigMap.set(clusterName, {child_policy: [new CdsLoadBalancingConfig(clusterName)]}); - } - const lbPolicyConfig = new XdsClusterManagerLoadBalancingConfig(clusterConfigMap); - const serviceConfig: ServiceConfig = { - methodConfig: [], - loadBalancingConfig: [lbPolicyConfig] - } - this.listener.onSuccessfulResolution([], serviceConfig, null, configSelector, {}); - } else { - // !GRPC_XDS_EXPERIMENTAL_ROUTING - for (const virtualHost of routeConfig.virtual_hosts) { - if (virtualHost.domains.indexOf(this.target.path) >= 0) { - const route = virtualHost.routes[virtualHost.routes.length - 1]; - if (route.match?.prefix === '' && route.route?.cluster) { - trace('Reporting RDS update for host ' + uriToString(this.target) + ' with cluster ' + route.route.cluster); - this.listener.onSuccessfulResolution([], { - methodConfig: [], - loadBalancingConfig: [ - new CdsLoadBalancingConfig(route.route.cluster) - ], - }, null, null, {}); - this.hasReportedSuccess = true; - return; - } else { - trace('Discarded matching route with prefix ' + route.match?.prefix + ' and cluster ' + route.route?.cluster); - } - } - } + const virtualHost = findVirtualHostForDomain(routeConfig.virtual_hosts, this.target.path); + if (virtualHost === null) { this.reportResolutionError('No matching route found'); + return; } + trace('Received virtual host config ' + JSON.stringify(virtualHost, undefined, 2)); + const allConfigClusters = new Set(); + const matchList: {matcher: Matcher, action: RouteAction}[] = []; + for (const route of virtualHost.routes) { + let routeAction: RouteAction; + switch (route.route!.cluster_specifier) { + case 'cluster_header': + continue; + case 'cluster':{ + const cluster = route.route!.cluster!; + allConfigClusters.add(cluster); + routeAction = new SingleClusterRouteAction(cluster); + break; + } + case 'weighted_clusters': { + const weightedClusters: WeightedCluster[] = []; + for (const clusterWeight of route.route!.weighted_clusters!.clusters) { + allConfigClusters.add(clusterWeight.name); + weightedClusters.push({name: clusterWeight.name, weight: clusterWeight.weight?.value ?? 0}); + } + routeAction = new WeightedClusterRouteAction(weightedClusters, route.route!.weighted_clusters!.total_weight?.value ?? 100); + } + } + const routeMatcher = getPredicateForMatcher(route.match!); + matchList.push({matcher: routeMatcher, action: routeAction}); + } + /* Mark clusters that are not in this route config, and remove ones with + * no references */ + for (const [name, refCount] of Array.from(this.clusterRefcounts.entries())) { + if (!allConfigClusters.has(name)) { + refCount.inLastConfig = false; + if (refCount.refCount === 0) { + this.clusterRefcounts.delete(name); + } + } + } + // Add any new clusters from this route config + for (const name of allConfigClusters) { + if (this.clusterRefcounts.has(name)) { + this.clusterRefcounts.get(name)!.inLastConfig = true; + } else { + this.clusterRefcounts.set(name, {inLastConfig: true, refCount: 0}); + } + } + const configSelector: ConfigSelector = (methodName, metadata) => { + for (const {matcher, action} of matchList) { + if (matcher.apply(methodName, metadata)) { + const clusterName = action.getCluster(); + this.refCluster(clusterName); + const onCommitted = () => { + this.unrefCluster(clusterName); + } + return { + methodConfig: {name: []}, + onCommitted: onCommitted, + pickInformation: {cluster: clusterName}, + status: status.OK + }; + } + } + return { + methodConfig: {name: []}, + // cluster won't be used here, but it's set because of some TypeScript weirdness + pickInformation: {cluster: ''}, + status: status.UNAVAILABLE + }; + }; + trace('Created ConfigSelector with configuration:'); + for (const {matcher, action} of matchList) { + trace(matcher.toString()); + trace('=> ' + action.toString()); + } + const clusterConfigMap = new Map(); + for (const clusterName of this.clusterRefcounts.keys()) { + clusterConfigMap.set(clusterName, {child_policy: [new CdsLoadBalancingConfig(clusterName)]}); + } + const lbPolicyConfig = new XdsClusterManagerLoadBalancingConfig(clusterConfigMap); + const serviceConfig: ServiceConfig = { + methodConfig: [], + loadBalancingConfig: [lbPolicyConfig] + } + this.listener.onSuccessfulResolution([], serviceConfig, null, configSelector, {}); } private reportResolutionError(reason: string) { diff --git a/packages/grpc-js-xds/src/xds-stream-state/rds-state.ts b/packages/grpc-js-xds/src/xds-stream-state/rds-state.ts index 2ac924d9..8f795e0f 100644 --- a/packages/grpc-js-xds/src/xds-stream-state/rds-state.ts +++ b/packages/grpc-js-xds/src/xds-stream-state/rds-state.ts @@ -16,7 +16,6 @@ */ import { experimental, logVerbosity, StatusObject } from "@grpc/grpc-js"; -import { GRPC_XDS_EXPERIMENTAL_ROUTING } from "../environment"; import { RouteConfiguration__Output } from "../generated/envoy/api/v2/RouteConfiguration"; import { CdsLoadBalancingConfig } from "../load-balancer-cds"; import { Watcher, XdsStreamState } from "./xds-stream-state"; @@ -99,55 +98,51 @@ export class RdsState implements XdsStreamState { } validateResponse(message: RouteConfiguration__Output): boolean { - if (GRPC_XDS_EXPERIMENTAL_ROUTING) { - // https://github.com/grpc/proposal/blob/master/A28-xds-traffic-splitting-and-routing.md#response-validation - for (const virtualHost of message.virtual_hosts) { - for (const domainPattern of virtualHost.domains) { - const starIndex = domainPattern.indexOf('*'); - const lastStarIndex = domainPattern.lastIndexOf('*'); - // A domain pattern can have at most one wildcard * - if (starIndex !== lastStarIndex) { - return false; - } - // A wildcard * can either be absent or at the beginning or end of the pattern - if (!(starIndex === -1 || starIndex === 0 || starIndex === domainPattern.length - 1)) { + // https://github.com/grpc/proposal/blob/master/A28-xds-traffic-splitting-and-routing.md#response-validation + for (const virtualHost of message.virtual_hosts) { + for (const domainPattern of virtualHost.domains) { + const starIndex = domainPattern.indexOf('*'); + const lastStarIndex = domainPattern.lastIndexOf('*'); + // A domain pattern can have at most one wildcard * + if (starIndex !== lastStarIndex) { + return false; + } + // A wildcard * can either be absent or at the beginning or end of the pattern + if (!(starIndex === -1 || starIndex === 0 || starIndex === domainPattern.length - 1)) { + return false; + } + } + for (const route of virtualHost.routes) { + const match = route.match; + if (!match) { + return false; + } + if (SUPPORTED_PATH_SPECIFIERS.indexOf(match.path_specifier) < 0) { + return false; + } + for (const headers of match.headers) { + if (SUPPPORTED_HEADER_MATCH_SPECIFIERS.indexOf(headers.header_match_specifier) < 0) { return false; } } - for (const route of virtualHost.routes) { - const match = route.match; - if (!match) { + if (route.action !== 'route') { + return false; + } + if ((route.route === undefined) || SUPPORTED_CLUSTER_SPECIFIERS.indexOf(route.route.cluster_specifier) < 0) { + return false; + } + if (route.route!.cluster_specifier === 'weighted_clusters') { + let weightSum = 0; + for (const clusterWeight of route.route.weighted_clusters!.clusters) { + weightSum += clusterWeight.weight?.value ?? 0; + } + if (weightSum !== route.route.weighted_clusters!.total_weight?.value ?? 100) { return false; } - if (SUPPORTED_PATH_SPECIFIERS.indexOf(match.path_specifier) < 0) { - return false; - } - for (const headers of match.headers) { - if (SUPPPORTED_HEADER_MATCH_SPECIFIERS.indexOf(headers.header_match_specifier) < 0) { - return false; - } - } - if (route.action !== 'route') { - return false; - } - if ((route.route === undefined) || SUPPORTED_CLUSTER_SPECIFIERS.indexOf(route.route.cluster_specifier) < 0) { - return false; - } - if (route.route!.cluster_specifier === 'weighted_clusters') { - let weightSum = 0; - for (const clusterWeight of route.route.weighted_clusters!.clusters) { - weightSum += clusterWeight.weight?.value ?? 0; - } - if (weightSum !== route.route.weighted_clusters!.total_weight?.value ?? 100) { - return false; - } - } } } - return true; - } else { - return true; } + return true; } private handleMissingNames(allRouteConfigNames: Set) {