Skip to content

Commit 9b61f4a

Browse files
committed
grpc-js-xds: Implement EDS dualstack support
1 parent a114b9f commit 9b61f4a

File tree

3 files changed

+46
-16
lines changed

3 files changed

+46
-16
lines changed

packages/grpc-js-xds/src/environment.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ export const EXPERIMENTAL_FEDERATION = (process.env.GRPC_EXPERIMENTAL_XDS_FEDERA
2222
export const EXPERIMENTAL_CUSTOM_LB_CONFIG = (process.env.GRPC_EXPERIMENTAL_XDS_CUSTOM_LB_CONFIG ?? 'false') === 'true';
2323
export const EXPERIMENTAL_RING_HASH = (process.env.GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH ?? 'false') === 'true';
2424
export const EXPERIMENTAL_PICK_FIRST = (process.env.GRPC_EXPERIMENTAL_PICKFIRST_LB_CONFIG ?? 'false') === 'true';
25+
export const EXPERIMENTAL_DUALSTACK_ENDPOINTS = (process.env.GRPC_EXPERIMENTAL_XDS_DUALSTACK_ENDPOINTS ?? 'false') === 'true';

packages/grpc-js-xds/src/load-balancer-xds-cluster-resolver.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
import { ChannelOptions, LoadBalancingConfig, Metadata, connectivityState, experimental, logVerbosity, status } from "@grpc/grpc-js";
1919
import { registerLoadBalancerType } from "@grpc/grpc-js/build/src/load-balancer";
20-
import { EXPERIMENTAL_OUTLIER_DETECTION } from "./environment";
20+
import { EXPERIMENTAL_DUALSTACK_ENDPOINTS, EXPERIMENTAL_OUTLIER_DETECTION } from "./environment";
2121
import { Locality__Output } from "./generated/envoy/config/core/v3/Locality";
2222
import { ClusterLoadAssignment__Output } from "./generated/envoy/config/endpoint/v3/ClusterLoadAssignment";
2323
import { LocalityEndpoint, PriorityChildRaw } from "./load-balancer-priority";
@@ -40,6 +40,7 @@ import parseLoadBalancingConfig = experimental.parseLoadBalancingConfig;
4040
import UnavailablePicker = experimental.UnavailablePicker;
4141
import { serverConfigEqual, validateXdsServerConfig, XdsServerConfig } from "./xds-bootstrap";
4242
import { EndpointResourceType } from "./xds-resource-type/endpoint-resource-type";
43+
import { SocketAddress__Output } from "./generated/envoy/config/core/v3/SocketAddress";
4344

4445
const TRACER_NAME = 'xds_cluster_resolver';
4546

@@ -175,13 +176,21 @@ function getEdsPriorities(edsUpdate: ClusterLoadAssignment__Output): PriorityEnt
175176
(lbEndpoint) => {
176177
/* The validator in the XdsClient class ensures that each endpoint has
177178
* a socket_address with an IP address and a port_value. */
178-
const socketAddress = lbEndpoint.endpoint!.address!.socket_address!;
179+
let socketAddresses: SocketAddress__Output[];
180+
if (EXPERIMENTAL_DUALSTACK_ENDPOINTS) {
181+
socketAddresses = [
182+
lbEndpoint.endpoint!.address!.socket_address!,
183+
...lbEndpoint.endpoint!.additional_addresses.map(additionalAddress => additionalAddress.address!.socket_address!)
184+
];
185+
} else {
186+
socketAddresses = [lbEndpoint.endpoint!.address!.socket_address!];
187+
}
179188
return {
180189
endpoint: {
181-
addresses: [{
190+
addresses: socketAddresses.map(socketAddress => ({
182191
host: socketAddress.address!,
183-
port: socketAddress.port_value!,
184-
}]
192+
port: socketAddress.port_value!
193+
}))
185194
},
186195
weight: lbEndpoint.load_balancing_weight?.value ?? 1
187196
};

packages/grpc-js-xds/src/xds-resource-type/endpoint-resource-type.ts

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { isIPv4, isIPv6 } from "net";
77
import { Any__Output } from "../generated/google/protobuf/Any";
88
import { EDS_TYPE_URL, decodeSingleResource } from "../resources";
99
import { Watcher, XdsClient } from "../xds-client";
10+
import { EXPERIMENTAL_DUALSTACK_ENDPOINTS } from "../environment";
1011

1112
const TRACER_NAME = 'xds_client';
1213

@@ -39,6 +40,24 @@ export class EndpointResourceType extends XdsResourceType {
3940
return 'envoy.config.endpoint.v3.ClusterLoadAssignment';
4041
}
4142

43+
private validateAddress(socketAddress: SocketAddress__Output, seenAddresses: SocketAddress__Output[]): boolean {
44+
if (socketAddress.port_specifier !== 'port_value') {
45+
trace('EDS validation: socket_address.port_specifier !== "port_value"');
46+
return false;
47+
}
48+
if (!(isIPv4(socketAddress.address) || isIPv6(socketAddress.address))) {
49+
trace('EDS validation: address not a valid IPv4 or IPv6 address: ' + socketAddress.address);
50+
return false;
51+
}
52+
for (const address of seenAddresses) {
53+
if (addressesEqual(socketAddress, address)) {
54+
trace('EDS validation: duplicate address seen: ' + address);
55+
return false;
56+
}
57+
}
58+
return true;
59+
}
60+
4261
private validateResource(message: ClusterLoadAssignment__Output): ClusterLoadAssignment__Output | null {
4362
const seenLocalities: {locality: Locality__Output, priority: number}[] = [];
4463
const seenAddresses: SocketAddress__Output[] = [];
@@ -61,21 +80,22 @@ export class EndpointResourceType extends XdsResourceType {
6180
trace('EDS validation: endpoint socket_address not set');
6281
return null;
6382
}
64-
if (socketAddress.port_specifier !== 'port_value') {
65-
trace('EDS validation: socket_address.port_specifier !== "port_value"');
66-
return null;
67-
}
68-
if (!(isIPv4(socketAddress.address) || isIPv6(socketAddress.address))) {
69-
trace('EDS validation: address not a valid IPv4 or IPv6 address: ' + socketAddress.address);
83+
if (!this.validateAddress(socketAddress, seenAddresses)) {
7084
return null;
7185
}
72-
for (const address of seenAddresses) {
73-
if (addressesEqual(socketAddress, address)) {
74-
trace('EDS validation: duplicate address seen: ' + address);
75-
return null;
86+
seenAddresses.push(socketAddress);
87+
if (EXPERIMENTAL_DUALSTACK_ENDPOINTS && lb.endpoint?.additional_addresses) {
88+
for (const additionalAddress of lb.endpoint.additional_addresses) {
89+
if (!additionalAddress.address?.socket_address) {
90+
trace('EDS validation: endpoint additional_addresses socket_address not set');
91+
return null;
92+
}
93+
if (!this.validateAddress(additionalAddress.address.socket_address, seenAddresses)) {
94+
return null;
95+
}
96+
seenAddresses.push(additionalAddress.address.socket_address);
7697
}
7798
}
78-
seenAddresses.push(socketAddress);
7999
}
80100
priorityTotalWeights.set(endpoint.priority, (priorityTotalWeights.get(endpoint.priority) ?? 0) + (endpoint.load_balancing_weight?.value ?? 0));
81101
}

0 commit comments

Comments
 (0)