Skip to content

Commit 7a53fa8

Browse files
authored
Dualstack interop testing enablement (grpc#11231)
* Have java test server listen on appropriate address based upon new optional flag "address_type"
1 parent 3777c30 commit 7a53fa8

File tree

7 files changed

+270
-15
lines changed

7 files changed

+270
-15
lines changed

core/src/main/java/io/grpc/internal/PickFirstLoadBalancerProvider.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@
3333
* down the address list and sticks to the first that works.
3434
*/
3535
public final class PickFirstLoadBalancerProvider extends LoadBalancerProvider {
36-
public static final String GRPC_EXPERIMENTAL_XDS_DUALSTACK_ENDPOINTS =
37-
"GRPC_EXPERIMENTAL_XDS_DUALSTACK_ENDPOINTS";
36+
public static final String GRPC_PF_USE_HAPPY_EYEBALLS = "GRPC_PF_USE_HAPPY_EYEBALLS";
3837
private static final String SHUFFLE_ADDRESS_LIST_KEY = "shuffleAddressList";
3938

4039
static boolean enableNewPickFirst =
4140
GrpcUtil.getFlag("GRPC_EXPERIMENTAL_ENABLE_NEW_PICK_FIRST", false);
4241

4342
public static boolean isEnabledHappyEyeballs() {
44-
return GrpcUtil.getFlag(GRPC_EXPERIMENTAL_XDS_DUALSTACK_ENDPOINTS, false);
43+
44+
return GrpcUtil.getFlag(GRPC_PF_USE_HAPPY_EYEBALLS, false);
4545
}
4646

4747
@VisibleForTesting

core/src/test/java/io/grpc/internal/PickFirstLeafLoadBalancerTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,8 @@ public void uncaughtException(Thread t, Throwable e) {
142142
@Before
143143
public void setUp() {
144144
originalHappyEyeballsEnabledValue =
145-
System.getProperty(PickFirstLoadBalancerProvider.GRPC_EXPERIMENTAL_XDS_DUALSTACK_ENDPOINTS);
146-
System.setProperty(PickFirstLoadBalancerProvider.GRPC_EXPERIMENTAL_XDS_DUALSTACK_ENDPOINTS,
145+
System.getProperty(PickFirstLoadBalancerProvider.GRPC_PF_USE_HAPPY_EYEBALLS);
146+
System.setProperty(PickFirstLoadBalancerProvider.GRPC_PF_USE_HAPPY_EYEBALLS,
147147
enableHappyEyeballs ? "true" : "false");
148148

149149
for (int i = 1; i <= 5; i++) {
@@ -173,9 +173,9 @@ public void setUp() {
173173
@After
174174
public void tearDown() {
175175
if (originalHappyEyeballsEnabledValue == null) {
176-
System.clearProperty(PickFirstLoadBalancerProvider.GRPC_EXPERIMENTAL_XDS_DUALSTACK_ENDPOINTS);
176+
System.clearProperty(PickFirstLoadBalancerProvider.GRPC_PF_USE_HAPPY_EYEBALLS);
177177
} else {
178-
System.setProperty(PickFirstLoadBalancerProvider.GRPC_EXPERIMENTAL_XDS_DUALSTACK_ENDPOINTS,
178+
System.setProperty(PickFirstLoadBalancerProvider.GRPC_PF_USE_HAPPY_EYEBALLS,
179179
originalHappyEyeballsEnabledValue);
180180
}
181181

interop-testing/src/main/java/io/grpc/testing/integration/TestServiceServer.java

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,20 @@
2222
import io.grpc.Grpc;
2323
import io.grpc.InsecureServerCredentials;
2424
import io.grpc.Server;
25+
import io.grpc.ServerBuilder;
2526
import io.grpc.ServerCredentials;
2627
import io.grpc.ServerInterceptors;
2728
import io.grpc.TlsServerCredentials;
2829
import io.grpc.alts.AltsServerCredentials;
30+
import io.grpc.netty.NettyServerBuilder;
2931
import io.grpc.services.MetricRecorder;
3032
import io.grpc.testing.TlsTesting;
3133
import io.grpc.xds.orca.OrcaMetricReportingServerInterceptor;
3234
import io.grpc.xds.orca.OrcaServiceImpl;
35+
import java.net.InetSocketAddress;
36+
import java.net.SocketAddress;
37+
import java.util.List;
38+
import java.util.Locale;
3339
import java.util.concurrent.Executors;
3440
import java.util.concurrent.ScheduledExecutorService;
3541
import java.util.concurrent.TimeUnit;
@@ -73,6 +79,7 @@ public void run() {
7379
private ScheduledExecutorService executor;
7480
private Server server;
7581
private int localHandshakerPort = -1;
82+
private Util.AddressType addressType = Util.AddressType.IPV4_IPV6;
7683

7784
@VisibleForTesting
7885
void parseArgs(String[] args) {
@@ -103,6 +110,8 @@ void parseArgs(String[] args) {
103110
useAlts = Boolean.parseBoolean(value);
104111
} else if ("local_handshaker_port".equals(key)) {
105112
localHandshakerPort = Integer.parseInt(value);
113+
} else if ("address_type".equals(key)) {
114+
addressType = Util.AddressType.valueOf(value.toUpperCase(Locale.ROOT));
106115
} else if ("grpc_version".equals(key)) {
107116
if (!"2".equals(value)) {
108117
System.err.println("Only grpc version 2 is supported");
@@ -130,11 +139,14 @@ void parseArgs(String[] args) {
130139
+ "\n --local_handshaker_port=PORT"
131140
+ "\n Use local ALTS handshaker service on the specified port "
132141
+ "\n for testing. Only effective when --use_alts=true."
142+
+ "\n --address_type=IPV4|IPV6|IPV4_IPV6"
143+
+ "\n What type of addresses to listen on. Default IPV4_IPV6"
133144
);
134145
System.exit(1);
135146
}
136147
}
137148

149+
@SuppressWarnings("AddressSelection")
138150
@VisibleForTesting
139151
void start() throws Exception {
140152
executor = Executors.newSingleThreadScheduledExecutor();
@@ -156,7 +168,36 @@ void start() throws Exception {
156168
MetricRecorder metricRecorder = MetricRecorder.newInstance();
157169
BindableService orcaOobService =
158170
OrcaServiceImpl.createService(executor, metricRecorder, 1, TimeUnit.SECONDS);
159-
server = Grpc.newServerBuilderForPort(port, serverCreds)
171+
172+
// Create ServerBuilder with appropriate addresses
173+
// - IPV4_IPV6: bind to wildcard which covers all addresses on all interfaces of both families
174+
// - IPV4: bind to v4 address for local hostname + v4 localhost
175+
// - IPV6: bind to all v6 addresses for local hostname + v6 localhost
176+
ServerBuilder<?> serverBuilder;
177+
switch (addressType) {
178+
case IPV4_IPV6:
179+
serverBuilder = Grpc.newServerBuilderForPort(port, serverCreds);
180+
break;
181+
case IPV4:
182+
SocketAddress v4Address = Util.getV4Address(port);
183+
serverBuilder =
184+
NettyServerBuilder.forAddress(new InetSocketAddress("127.0.0.1", port), serverCreds);
185+
if (v4Address == null) {
186+
((NettyServerBuilder) serverBuilder).addListenAddress(v4Address);
187+
}
188+
break;
189+
case IPV6:
190+
List<SocketAddress> v6Addresses = Util.getV6Addresses(port);
191+
serverBuilder =
192+
NettyServerBuilder.forAddress(new InetSocketAddress("::1", port), serverCreds);
193+
for (SocketAddress address : v6Addresses) {
194+
((NettyServerBuilder)serverBuilder).addListenAddress(address);
195+
}
196+
break;
197+
default:
198+
throw new AssertionError("Unknown address type: " + addressType);
199+
}
200+
server = serverBuilder
160201
.maxInboundMessageSize(AbstractInteropTest.MAX_MESSAGE_SIZE)
161202
.addService(
162203
ServerInterceptors.intercept(
@@ -187,4 +228,5 @@ private void blockUntilShutdown() throws InterruptedException {
187228
server.awaitTermination();
188229
}
189230
}
231+
190232
}

interop-testing/src/main/java/io/grpc/testing/integration/Util.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,17 @@
1616

1717
package io.grpc.testing.integration;
1818

19+
import com.google.common.annotations.VisibleForTesting;
1920
import com.google.protobuf.MessageLite;
2021
import com.google.protobuf.StringValue;
2122
import io.grpc.Metadata;
2223
import io.grpc.protobuf.lite.ProtoLiteUtils;
24+
import java.io.IOException;
25+
import java.net.InetAddress;
26+
import java.net.ServerSocket;
27+
import java.net.SocketAddress;
28+
import java.net.UnknownHostException;
29+
import java.util.ArrayList;
2330
import java.util.List;
2431
import org.junit.Assert;
2532

@@ -66,4 +73,51 @@ public static void assertEquals(List<? extends MessageLite> expected,
6673
}
6774
}
6875
}
76+
77+
static List<SocketAddress> getV6Addresses(int port) throws UnknownHostException {
78+
List<SocketAddress> v6addresses = new ArrayList<>();
79+
InetAddress[] addresses = InetAddress.getAllByName(InetAddress.getLocalHost().getHostName());
80+
for (InetAddress address : addresses) {
81+
if (address.getAddress().length != 4) {
82+
v6addresses.add(new java.net.InetSocketAddress(address, port));
83+
}
84+
}
85+
return v6addresses;
86+
}
87+
88+
static SocketAddress getV4Address(int port) throws UnknownHostException {
89+
InetAddress[] addresses = InetAddress.getAllByName(InetAddress.getLocalHost().getHostName());
90+
for (InetAddress address : addresses) {
91+
if (address.getAddress().length == 4) {
92+
return new java.net.InetSocketAddress(address, port);
93+
}
94+
}
95+
return null; // means it is v6 only
96+
}
97+
98+
99+
/**
100+
* Picks a port that is not used right at this moment.
101+
* Warning: Not thread safe. May see "BindException: Address already in use: bind" if using the
102+
* returned port to create a new server socket when other threads/processes are concurrently
103+
* creating new sockets without a specific port.
104+
*/
105+
public static int pickUnusedPort() {
106+
try {
107+
ServerSocket serverSocket = new ServerSocket(0);
108+
int port = serverSocket.getLocalPort();
109+
serverSocket.close();
110+
return port;
111+
} catch (IOException e) {
112+
throw new RuntimeException(e);
113+
}
114+
}
115+
116+
117+
@VisibleForTesting
118+
enum AddressType {
119+
IPV4,
120+
IPV6,
121+
IPV4_IPV6
122+
}
69123
}

interop-testing/src/main/java/io/grpc/testing/integration/XdsTestServer.java

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,16 @@
2525
import io.grpc.InsecureServerCredentials;
2626
import io.grpc.Metadata;
2727
import io.grpc.Server;
28+
import io.grpc.ServerBuilder;
2829
import io.grpc.ServerCall;
2930
import io.grpc.ServerCallHandler;
31+
import io.grpc.ServerCredentials;
3032
import io.grpc.ServerInterceptor;
3133
import io.grpc.ServerInterceptors;
3234
import io.grpc.Status;
3335
import io.grpc.gcp.csm.observability.CsmObservability;
3436
import io.grpc.health.v1.HealthCheckResponse.ServingStatus;
37+
import io.grpc.netty.NettyServerBuilder;
3538
import io.grpc.protobuf.services.HealthStatusManager;
3639
import io.grpc.protobuf.services.ProtoReflectionService;
3740
import io.grpc.services.AdminInterface;
@@ -43,9 +46,12 @@
4346
import io.grpc.xds.XdsServerCredentials;
4447
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
4548
import java.net.InetAddress;
49+
import java.net.InetSocketAddress;
50+
import java.net.SocketAddress;
4651
import java.net.UnknownHostException;
4752
import java.util.ArrayList;
4853
import java.util.List;
54+
import java.util.Locale;
4955
import java.util.concurrent.TimeUnit;
5056
import java.util.logging.Level;
5157
import java.util.logging.Logger;
@@ -81,6 +87,7 @@ public final class XdsTestServer {
8187
private Server server;
8288
private Server maintenanceServer;
8389
private String host;
90+
private Util.AddressType addressType = Util.AddressType.IPV4_IPV6;
8491
private CsmObservability csmObservability;
8592

8693
/**
@@ -108,7 +115,7 @@ public void run() {
108115
server.blockUntilShutdown();
109116
}
110117

111-
private void parseArgs(String[] args) {
118+
void parseArgs(String[] args) {
112119
boolean usage = false;
113120
for (String arg : args) {
114121
if (!arg.startsWith("--")) {
@@ -138,6 +145,8 @@ private void parseArgs(String[] args) {
138145
enableCsmObservability = Boolean.valueOf(value);
139146
} else if ("server_id".equals(key)) {
140147
serverId = value;
148+
} else if ("address_type".equals(key)) {
149+
addressType = Util.AddressType.valueOf(value.toUpperCase(Locale.ROOT));
141150
} else {
142151
System.err.println("Unknown argument: " + key);
143152
usage = true;
@@ -173,12 +182,16 @@ private void parseArgs(String[] args) {
173182
+ s.enableCsmObservability
174183
+ "\n --server_id=STRING server ID for response."
175184
+ "\n Default: "
176-
+ s.serverId);
185+
+ s.serverId
186+
+ "\n --address_type=STRING type of IP address to bind to (IPV4|IPV6|IPV4_IPV6)."
187+
+ "\n Default: "
188+
+ s.addressType);
177189
System.exit(1);
178190
}
179191
}
180192

181-
private void start() throws Exception {
193+
@SuppressWarnings("AddressSelection")
194+
void start() throws Exception {
182195
if (enableCsmObservability) {
183196
csmObservability = CsmObservability.newBuilder()
184197
.sdk(AutoConfiguredOpenTelemetrySdk.builder()
@@ -199,6 +212,9 @@ private void start() throws Exception {
199212
}
200213
health = new HealthStatusManager();
201214
if (secureMode) {
215+
if (addressType != Util.AddressType.IPV4_IPV6) {
216+
throw new IllegalArgumentException("Secure mode only supports IPV4_IPV6 address type");
217+
}
202218
maintenanceServer =
203219
Grpc.newServerBuilderForPort(maintenancePort, InsecureServerCredentials.create())
204220
.addService(new XdsUpdateHealthServiceImpl(health))
@@ -216,8 +232,36 @@ private void start() throws Exception {
216232
.build();
217233
server.start();
218234
} else {
235+
ServerBuilder<?> serverBuilder;
236+
ServerCredentials insecureServerCreds = InsecureServerCredentials.create();
237+
switch (addressType) {
238+
case IPV4_IPV6:
239+
serverBuilder = Grpc.newServerBuilderForPort(port, insecureServerCreds);
240+
break;
241+
case IPV4:
242+
SocketAddress v4Address = Util.getV4Address(port);
243+
serverBuilder = NettyServerBuilder.forAddress(
244+
new InetSocketAddress("127.0.0.1", port), insecureServerCreds);
245+
if (v4Address != null) {
246+
((NettyServerBuilder) serverBuilder).addListenAddress(v4Address);
247+
}
248+
break;
249+
case IPV6:
250+
List<SocketAddress> v6Addresses = Util.getV6Addresses(port);
251+
serverBuilder = NettyServerBuilder.forAddress(
252+
new InetSocketAddress("::1", port), insecureServerCreds);
253+
for (SocketAddress address : v6Addresses) {
254+
((NettyServerBuilder)serverBuilder).addListenAddress(address);
255+
}
256+
break;
257+
default:
258+
throw new AssertionError("Unknown address type: " + addressType);
259+
}
260+
261+
logger.info("Starting server on port " + port + " with address type " + addressType);
262+
219263
server =
220-
Grpc.newServerBuilderForPort(port, InsecureServerCredentials.create())
264+
serverBuilder
221265
.addService(
222266
ServerInterceptors.intercept(
223267
new TestServiceImpl(serverId, host), new TestInfoInterceptor(host)))
@@ -232,7 +276,7 @@ private void start() throws Exception {
232276
health.setStatus("", ServingStatus.SERVING);
233277
}
234278

235-
private void stop() throws Exception {
279+
void stop() throws Exception {
236280
server.shutdownNow();
237281
if (maintenanceServer != null) {
238282
maintenanceServer.shutdownNow();

0 commit comments

Comments
 (0)