Skip to content

Commit a25472e

Browse files
authored
xds: fix xdsClient resource not exist for invalid resource, fix xdsServerWrapper start on resource not exist (1.42.x backport) (#8690)
* xds: remove filter chain uuid name generator (#8663) Generating a uuid in filterChain breaks the de-duplication detection which causes XdsServer to cycle connections, so removing it. An empty name is now allowed. The name is currently only used for debug purpose. * xds: fix xdsClient resource not exist for invalid resource, fix xdsServerWrapper start on resource not exist (#8660) Fix bugs: 1. Invalid resource at xdsClient, the watcher should have been delivered an error instead of resource not found. 2. If the resource is properly determined to not exist, it shouldn't cause start() to fail. From A36 xDS for Servers: "XdsServer's start must not fail due to transient xDS issues, like missing xDS configuration from the xDS server."
1 parent c818289 commit a25472e

File tree

5 files changed

+64
-38
lines changed

5 files changed

+64
-38
lines changed

xds/src/main/java/io/grpc/xds/ClientXdsClient.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -2077,11 +2077,13 @@ private void handleResourceUpdate(
20772077
}
20782078
retainedResources.add(edsName);
20792079
}
2080-
continue;
2080+
} else if (invalidResources.contains(resourceName)) {
2081+
subscriber.onError(Status.UNAVAILABLE.withDescription(errorDetail));
2082+
} else {
2083+
// For State of the World services, notify watchers when their watched resource is missing
2084+
// from the ADS update.
2085+
subscriber.onAbsent();
20812086
}
2082-
// For State of the World services, notify watchers when their watched resource is missing
2083-
// from the ADS update.
2084-
subscriber.onAbsent();
20852087
}
20862088
}
20872089
// LDS/CDS responses represents the state of the world, RDS/EDS resources not referenced in

xds/src/main/java/io/grpc/xds/XdsServerWrapper.java

-4
Original file line numberDiff line numberDiff line change
@@ -571,10 +571,6 @@ private void handleConfigNotFound(StatusException exception) {
571571
for (SslContextProviderSupplier s: toRelease) {
572572
s.close();
573573
}
574-
if (!initialStarted) {
575-
initialStarted = true;
576-
initialStartFuture.set(exception);
577-
}
578574
if (restartTimer != null) {
579575
restartTimer.cancel();
580576
}

xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java

+34-16
Original file line numberDiff line numberDiff line change
@@ -1555,12 +1555,16 @@ public void cdsResponseErrorHandling_badUpstreamTlsContext() {
15551555
call.sendResponse(CDS, clusters, VERSION_1, "0000");
15561556

15571557
// The response NACKed with errors indicating indices of the failed resources.
1558-
call.verifyRequestNack(CDS, CDS_RESOURCE, "", "0000", NODE, ImmutableList.of(
1559-
"CDS response Cluster 'cluster.googleapis.com' validation error: "
1558+
String errorMsg = "CDS response Cluster 'cluster.googleapis.com' validation error: "
15601559
+ "Cluster cluster.googleapis.com: malformed UpstreamTlsContext: "
15611560
+ "io.grpc.xds.ClientXdsClient$ResourceInvalidException: "
1562-
+ "ca_certificate_provider_instance is required in upstream-tls-context"));
1563-
verifyNoInteractions(cdsResourceWatcher);
1561+
+ "ca_certificate_provider_instance is required in upstream-tls-context";
1562+
call.verifyRequestNack(CDS, CDS_RESOURCE, "", "0000", NODE, ImmutableList.of(errorMsg));
1563+
ArgumentCaptor<Status> captor = ArgumentCaptor.forClass(Status.class);
1564+
verify(cdsResourceWatcher).onError(captor.capture());
1565+
Status errorStatus = captor.getValue();
1566+
assertThat(errorStatus.getCode()).isEqualTo(Status.UNAVAILABLE.getCode());
1567+
assertThat(errorStatus.getDescription()).isEqualTo(errorMsg);
15641568
}
15651569

15661570
/**
@@ -1579,10 +1583,14 @@ public void cdsResponseErrorHandling_badTransportSocketName() {
15791583
call.sendResponse(CDS, clusters, VERSION_1, "0000");
15801584

15811585
// The response NACKed with errors indicating indices of the failed resources.
1582-
call.verifyRequestNack(CDS, CDS_RESOURCE, "", "0000", NODE, ImmutableList.of(
1583-
"CDS response Cluster 'cluster.googleapis.com' validation error: "
1584-
+ "transport-socket with name envoy.transport_sockets.bad not supported."));
1585-
verifyNoInteractions(cdsResourceWatcher);
1586+
String errorMsg = "CDS response Cluster 'cluster.googleapis.com' validation error: "
1587+
+ "transport-socket with name envoy.transport_sockets.bad not supported.";
1588+
call.verifyRequestNack(CDS, CDS_RESOURCE, "", "0000", NODE, ImmutableList.of(errorMsg));
1589+
ArgumentCaptor<Status> captor = ArgumentCaptor.forClass(Status.class);
1590+
verify(cdsResourceWatcher).onError(captor.capture());
1591+
Status errorStatus = captor.getValue();
1592+
assertThat(errorStatus.getCode()).isEqualTo(Status.UNAVAILABLE.getCode());
1593+
assertThat(errorStatus.getDescription()).isEqualTo(errorMsg);
15861594
}
15871595

15881596
@Test
@@ -2429,10 +2437,15 @@ public void serverSideListenerResponseErrorHandling_badDownstreamTlsContext() {
24292437
List<Any> listeners = ImmutableList.of(Any.pack(listener));
24302438
call.sendResponse(ResourceType.LDS, listeners, "0", "0000");
24312439
// The response NACKed with errors indicating indices of the failed resources.
2432-
call.verifyRequestNack(LDS, LISTENER_RESOURCE, "", "0000", NODE, ImmutableList.of(
2433-
"LDS response Listener \'grpc/server?xds.resource.listening_address=0.0.0.0:7000\' "
2434-
+ "validation error: common-tls-context is required in downstream-tls-context"));
2435-
verifyNoInteractions(ldsResourceWatcher);
2440+
String errorMsg = "LDS response Listener \'grpc/server?xds.resource.listening_address="
2441+
+ "0.0.0.0:7000\' validation error: "
2442+
+ "common-tls-context is required in downstream-tls-context";
2443+
call.verifyRequestNack(LDS, LISTENER_RESOURCE, "", "0000", NODE, ImmutableList.of(errorMsg));
2444+
ArgumentCaptor<Status> captor = ArgumentCaptor.forClass(Status.class);
2445+
verify(ldsResourceWatcher).onError(captor.capture());
2446+
Status errorStatus = captor.getValue();
2447+
assertThat(errorStatus.getCode()).isEqualTo(Status.UNAVAILABLE.getCode());
2448+
assertThat(errorStatus.getDescription()).isEqualTo(errorMsg);
24362449
}
24372450

24382451
@Test
@@ -2453,11 +2466,16 @@ public void serverSideListenerResponseErrorHandling_badTransportSocketName() {
24532466
List<Any> listeners = ImmutableList.of(Any.pack(listener));
24542467
call.sendResponse(ResourceType.LDS, listeners, "0", "0000");
24552468
// The response NACKed with errors indicating indices of the failed resources.
2469+
String errorMsg = "LDS response Listener \'grpc/server?xds.resource.listening_address="
2470+
+ "0.0.0.0:7000\' validation error: "
2471+
+ "transport-socket with name envoy.transport_sockets.bad1 not supported.";
24562472
call.verifyRequestNack(LDS, LISTENER_RESOURCE, "", "0000", NODE, ImmutableList.of(
2457-
"LDS response Listener \'grpc/server?xds.resource.listening_address=0.0.0.0:7000\' "
2458-
+ "validation error: "
2459-
+ "transport-socket with name envoy.transport_sockets.bad1 not supported."));
2460-
verifyNoInteractions(ldsResourceWatcher);
2473+
errorMsg));
2474+
ArgumentCaptor<Status> captor = ArgumentCaptor.forClass(Status.class);
2475+
verify(ldsResourceWatcher).onError(captor.capture());
2476+
Status errorStatus = captor.getValue();
2477+
assertThat(errorStatus.getCode()).isEqualTo(Status.UNAVAILABLE.getCode());
2478+
assertThat(errorStatus.getDescription()).isEqualTo(errorMsg);
24612479
}
24622480

24632481
private DiscoveryRpcCall startResourceWatcher(

xds/src/test/java/io/grpc/xds/XdsClientWrapperForServerSdsTestMisc.java

+8-8
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,15 @@
6363
import io.netty.handler.codec.http2.Http2ConnectionDecoder;
6464
import io.netty.handler.codec.http2.Http2ConnectionEncoder;
6565
import io.netty.handler.codec.http2.Http2Settings;
66-
import java.io.IOException;
6766
import java.net.InetAddress;
6867
import java.net.InetSocketAddress;
6968
import java.net.SocketAddress;
7069
import java.util.Arrays;
7170
import java.util.Collections;
72-
import java.util.concurrent.ExecutionException;
7371
import java.util.concurrent.Executors;
7472
import java.util.concurrent.TimeUnit;
73+
import java.util.concurrent.TimeoutException;
74+
7575
import org.junit.Before;
7676
import org.junit.Test;
7777
import org.junit.runner.RunWith;
@@ -190,8 +190,8 @@ public void run() {
190190
try {
191191
start.get(5, TimeUnit.SECONDS);
192192
fail("Start should throw exception");
193-
} catch (ExecutionException ex) {
194-
assertThat(ex.getCause()).isInstanceOf(IOException.class);
193+
} catch (TimeoutException ex) {
194+
assertThat(start.isDone()).isFalse();
195195
}
196196
assertThat(selectorManager.getSelectorToUpdateSelector()).isSameInstanceAs(NO_FILTER_CHAIN);
197197
}
@@ -214,8 +214,8 @@ public void run() {
214214
try {
215215
start.get(5, TimeUnit.SECONDS);
216216
fail("Start should throw exception");
217-
} catch (ExecutionException ex) {
218-
assertThat(ex.getCause()).isInstanceOf(IOException.class);
217+
} catch (TimeoutException ex) {
218+
assertThat(start.isDone()).isFalse();
219219
}
220220
assertThat(selectorManager.getSelectorToUpdateSelector()).isSameInstanceAs(NO_FILTER_CHAIN);
221221
}
@@ -238,8 +238,8 @@ public void run() {
238238
try {
239239
start.get(5, TimeUnit.SECONDS);
240240
fail("Start should throw exception");
241-
} catch (ExecutionException ex) {
242-
assertThat(ex.getCause()).isInstanceOf(IOException.class);
241+
} catch (TimeoutException ex) {
242+
assertThat(start.isDone()).isFalse();
243243
}
244244
assertThat(selectorManager.getSelectorToUpdateSelector()).isSameInstanceAs(NO_FILTER_CHAIN);
245245
}

xds/src/test/java/io/grpc/xds/XdsServerWrapperTest.java

+16-6
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
import java.util.concurrent.ExecutionException;
7575
import java.util.concurrent.Executors;
7676
import java.util.concurrent.TimeUnit;
77+
import java.util.concurrent.TimeoutException;
7778
import java.util.concurrent.atomic.AtomicReference;
7879
import org.junit.Before;
7980
import org.junit.Rule;
@@ -261,9 +262,10 @@ public void run() {
261262
xdsClient.ldsWatcher.onResourceDoesNotExist(ldsResource);
262263
try {
263264
start.get(5000, TimeUnit.MILLISECONDS);
264-
fail("Start should throw exception");
265-
} catch (ExecutionException ex) {
266-
assertThat(ex.getCause()).isInstanceOf(IOException.class);
265+
fail("server should not start() successfully.");
266+
} catch (TimeoutException ex) {
267+
// expect to block here.
268+
assertThat(start.isDone()).isFalse();
267269
}
268270
verify(mockBuilder, times(1)).build();
269271
verify(mockServer, never()).start();
@@ -602,9 +604,10 @@ public void run() {
602604
xdsClient.ldsWatcher.onResourceDoesNotExist(ldsResource);
603605
try {
604606
start.get(5000, TimeUnit.MILLISECONDS);
605-
fail("Start should throw exception");
606-
} catch (ExecutionException ex) {
607-
assertThat(ex.getCause()).isInstanceOf(IOException.class);
607+
fail("server should not start()");
608+
} catch (TimeoutException ex) {
609+
// expect to block here.
610+
assertThat(start.isDone()).isFalse();
608611
}
609612
verify(listener, times(1)).onNotServing(any(StatusException.class));
610613
verify(mockBuilder, times(1)).build();
@@ -627,6 +630,13 @@ public void run() {
627630
assertThat(sslSupplier0.isShutdown()).isTrue();
628631
xdsClient.deliverRdsUpdate("rds",
629632
Collections.singletonList(createVirtualHost("virtual-host-1")));
633+
try {
634+
start.get(5000, TimeUnit.MILLISECONDS);
635+
fail("Start should throw exception");
636+
} catch (ExecutionException ex) {
637+
assertThat(ex.getCause()).isInstanceOf(IOException.class);
638+
assertThat(ex.getCause().getMessage()).isEqualTo("error!");
639+
}
630640
RdsResourceWatcher saveRdsWatcher = xdsClient.rdsWatchers.get("rds");
631641
assertThat(executor.forwardNanos(RETRY_DELAY_NANOS)).isEqualTo(1);
632642
verify(mockBuilder, times(1)).build();

0 commit comments

Comments
 (0)