mirror of https://github.com/grpc/grpc-node.git
grpc-js-xds: Remove env var protection for routing feature
This commit is contained in:
parent
c3aeb94b8c
commit
abfe46b99d
|
@ -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');
|
||||
*/
|
|
@ -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<string>();
|
||||
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<string, {child_policy: LoadBalancingConfig[]}>();
|
||||
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<string>();
|
||||
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<string, {child_policy: LoadBalancingConfig[]}>();
|
||||
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) {
|
||||
|
|
|
@ -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<RouteConfiguration__Output> {
|
|||
}
|
||||
|
||||
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<string>) {
|
||||
|
|
Loading…
Reference in New Issue