Skip to content

Commit e870584

Browse files
authored
xds: gate HttpFilter parsing by env flag
1 parent da86eea commit e870584

File tree

3 files changed

+55
-77
lines changed

3 files changed

+55
-77
lines changed

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

Lines changed: 47 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ final class ClientXdsClient extends AbstractXdsClient {
9696
@VisibleForTesting
9797
static final String AGGREGATE_CLUSTER_TYPE_NAME = "envoy.clusters.aggregate";
9898
private static final String HTTP_FAULT_FILTER_NAME = "envoy.fault";
99+
@VisibleForTesting
100+
static boolean enableFaultInjection =
101+
Boolean.parseBoolean(System.getenv("GRPC_XDS_EXPERIMENTAL_FAULT_INJECTION"));
102+
99103
private static final String TYPE_URL_HTTP_CONNECTION_MANAGER_V2 =
100104
"type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2"
101105
+ ".HttpConnectionManager";
@@ -184,22 +188,26 @@ protected void handleLdsResponse(String versionInfo, List<Any> resources, String
184188
}
185189
boolean hasFaultInjection = false;
186190
HttpFault httpFault = null;
187-
List<HttpFilter> httpFilters = hcm.getHttpFiltersList();
188-
for (HttpFilter httpFilter : httpFilters) {
189-
if (HTTP_FAULT_FILTER_NAME.equals(httpFilter.getName())) {
190-
hasFaultInjection = true;
191-
if (httpFilter.hasTypedConfig()) {
192-
StructOrError<HttpFault> httpFaultOrError =
193-
decodeFaultFilterConfig(httpFilter.getTypedConfig());
194-
if (httpFaultOrError.getErrorDetail() != null) {
195-
nackResponse(ResourceType.LDS, nonce,
196-
"Listener " + listenerName + " contains invalid HttpFault filter: "
197-
+ httpFaultOrError.getErrorDetail());
198-
return;
191+
if (enableFaultInjection) {
192+
List<HttpFilter> httpFilters = hcm.getHttpFiltersList();
193+
for (HttpFilter httpFilter : httpFilters) {
194+
if (HTTP_FAULT_FILTER_NAME.equals(httpFilter.getName())) {
195+
hasFaultInjection = true;
196+
if (httpFilter.hasTypedConfig()) {
197+
StructOrError<HttpFault> httpFaultOrError =
198+
decodeFaultFilterConfig(httpFilter.getTypedConfig());
199+
if (httpFaultOrError != null) {
200+
if (httpFaultOrError.getErrorDetail() != null) {
201+
nackResponse(ResourceType.LDS, nonce,
202+
"Listener " + listenerName + " contains invalid HttpFault filter: "
203+
+ httpFaultOrError.getErrorDetail());
204+
return;
205+
}
206+
httpFault = httpFaultOrError.getStruct();
207+
}
199208
}
200-
httpFault = httpFaultOrError.getStruct();
209+
break;
201210
}
202-
break;
203211
}
204212
}
205213
if (hcm.hasRouteConfig()) {
@@ -271,12 +279,14 @@ private static StructOrError<VirtualHost> parseVirtualHost(
271279
if (filterConfigMap.containsKey(HTTP_FAULT_FILTER_NAME)) {
272280
Any rawFaultFilterConfig = filterConfigMap.get(HTTP_FAULT_FILTER_NAME);
273281
StructOrError<HttpFault> httpFaultOrError = decodeFaultFilterConfig(rawFaultFilterConfig);
274-
if (httpFaultOrError.getErrorDetail() != null) {
275-
return StructOrError.fromError(
276-
"Virtual host [" + name + "] contains invalid HttpFault filter : "
277-
+ httpFaultOrError.getErrorDetail());
282+
if (httpFaultOrError != null) {
283+
if (httpFaultOrError.getErrorDetail() != null) {
284+
return StructOrError.fromError(
285+
"Virtual host [" + name + "] contains invalid HttpFault filter : "
286+
+ httpFaultOrError.getErrorDetail());
287+
}
288+
httpFault = httpFaultOrError.getStruct();
278289
}
279-
httpFault = httpFaultOrError.getStruct();
280290
}
281291
return StructOrError.fromStruct(VirtualHost.create(
282292
name, proto.getDomainsList(), routes, httpFault));
@@ -322,12 +332,14 @@ static StructOrError<Route> parseRoute(io.envoyproxy.envoy.config.route.v3.Route
322332
if (filterConfigMap.containsKey(HTTP_FAULT_FILTER_NAME)) {
323333
Any rawFaultFilterConfig = filterConfigMap.get(HTTP_FAULT_FILTER_NAME);
324334
StructOrError<HttpFault> httpFaultOrError = decodeFaultFilterConfig(rawFaultFilterConfig);
325-
if (httpFaultOrError.getErrorDetail() != null) {
326-
return StructOrError.fromError(
327-
"Route [" + proto.getName() + "] contains invalid HttpFault filter: "
328-
+ httpFaultOrError.getErrorDetail());
335+
if (httpFaultOrError != null) {
336+
if (httpFaultOrError.getErrorDetail() != null) {
337+
return StructOrError.fromError(
338+
"Route [" + proto.getName() + "] contains invalid HttpFault filter: "
339+
+ httpFaultOrError.getErrorDetail());
340+
}
341+
httpFault = httpFaultOrError.getStruct();
329342
}
330-
httpFault = httpFaultOrError.getStruct();
331343
}
332344
return StructOrError.fromStruct(Route.create(
333345
routeMatch.getStruct(), routeAction.getStruct(), httpFault));
@@ -508,22 +520,24 @@ static StructOrError<ClusterWeight> parseClusterWeight(
508520
if (filterConfigMap.containsKey(HTTP_FAULT_FILTER_NAME)) {
509521
Any rawFaultFilterConfig = filterConfigMap.get(HTTP_FAULT_FILTER_NAME);
510522
StructOrError<HttpFault> httpFaultOrError = decodeFaultFilterConfig(rawFaultFilterConfig);
511-
if (httpFaultOrError.getErrorDetail() != null) {
512-
return StructOrError.fromError(
513-
"ClusterWeight [" + proto.getName() + "] contains invalid HttpFault filter: "
514-
+ httpFaultOrError.getErrorDetail());
523+
if (httpFaultOrError != null) {
524+
if (httpFaultOrError.getErrorDetail() != null) {
525+
return StructOrError.fromError(
526+
"ClusterWeight [" + proto.getName() + "] contains invalid HttpFault filter: "
527+
+ httpFaultOrError.getErrorDetail());
528+
}
529+
httpFault = httpFaultOrError.getStruct();
515530
}
516-
httpFault = httpFaultOrError.getStruct();
517531
}
518532
return StructOrError.fromStruct(
519533
ClusterWeight.create(proto.getName(), proto.getWeight().getValue(), httpFault));
520534
}
521535

536+
@Nullable
522537
private static StructOrError<HttpFault> decodeFaultFilterConfig(Any rawFaultFilterConfig) {
523-
if (rawFaultFilterConfig.getTypeUrl().equals(
524-
"type.googleapis.com/envoy.config.filter.http.fault.v2.HTTPFault")) {
525-
rawFaultFilterConfig = rawFaultFilterConfig.toBuilder().setTypeUrl(
526-
"type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault").build();
538+
if (!rawFaultFilterConfig.getTypeUrl().equals(
539+
"type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault")) {
540+
return null;
527541
}
528542
HTTPFault httpFaultProto;
529543
try {

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
import java.util.concurrent.atomic.AtomicBoolean;
7474
import javax.annotation.Nullable;
7575
import org.junit.After;
76+
import org.junit.Assume;
7677
import org.junit.Before;
7778
import org.junit.Rule;
7879
import org.junit.Test;
@@ -172,9 +173,12 @@ public boolean shouldAccept(Runnable command) {
172173

173174
private ManagedChannel channel;
174175
private ClientXdsClient xdsClient;
176+
private boolean originalEnableFaultInjection;
175177

176178
@Before
177179
public void setUp() throws IOException {
180+
originalEnableFaultInjection = ClientXdsClient.enableFaultInjection;
181+
ClientXdsClient.enableFaultInjection = true;
178182
MockitoAnnotations.initMocks(this);
179183
when(backoffPolicyProvider.get()).thenReturn(backoffPolicy1, backoffPolicy2);
180184
when(backoffPolicy1.nextBackoffNanos()).thenReturn(10L, 100L);
@@ -207,6 +211,7 @@ public void setUp() throws IOException {
207211

208212
@After
209213
public void tearDown() {
214+
ClientXdsClient.enableFaultInjection = originalEnableFaultInjection;
210215
xdsClient.shutdown();
211216
channel.shutdown(); // channel not owned by XdsClient
212217
assertThat(adsEnded.get()).isTrue();
@@ -333,6 +338,7 @@ public void ldsResourceUpdated() {
333338

334339
@Test
335340
public void ldsResourceUpdate_withFaultInjection() {
341+
Assume.assumeTrue(useProtocolV3());
336342
DiscoveryRpcCall call =
337343
startResourceWatcher(ResourceType.LDS, LDS_RESOURCE, ldsResourceWatcher);
338344
List<Any> listeners = ImmutableList.of(

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

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,6 @@
6868
import io.envoyproxy.envoy.api.v2.route.RouteMatch;
6969
import io.envoyproxy.envoy.api.v2.route.VirtualHost;
7070
import io.envoyproxy.envoy.config.cluster.aggregate.v2alpha.ClusterConfig;
71-
import io.envoyproxy.envoy.config.filter.fault.v2.FaultDelay;
72-
import io.envoyproxy.envoy.config.filter.fault.v2.FaultDelay.HeaderDelay;
73-
import io.envoyproxy.envoy.config.filter.http.fault.v2.FaultAbort;
74-
import io.envoyproxy.envoy.config.filter.http.fault.v2.FaultAbort.HeaderAbort;
75-
import io.envoyproxy.envoy.config.filter.http.fault.v2.HTTPFault;
7671
import io.envoyproxy.envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager;
7772
import io.envoyproxy.envoy.config.filter.network.http_connection_manager.v2.HttpFilter;
7873
import io.envoyproxy.envoy.config.filter.network.http_connection_manager.v2.Rds;
@@ -279,52 +274,15 @@ protected Message buildListenerForRds(String name, String rdsResourceName) {
279274

280275
@Override
281276
protected Message buildHttpFilter(String name, @Nullable Any typedConfig) {
282-
HttpFilter.Builder builder = HttpFilter.newBuilder().setName(name);
283-
if (typedConfig != null) {
284-
builder.setTypedConfig(typedConfig);
285-
}
286-
return builder.build();
277+
throw new UnsupportedOperationException();
287278
}
288279

289280
@Override
290281
protected Any buildHttpFaultTypedConfig(
291282
@Nullable Long delayNanos, @Nullable Integer delayRate, String upstreamCluster,
292283
List<String> downstreamNodes, @Nullable Integer maxActiveFaults, @Nullable Status status,
293284
@Nullable Integer httpCode, @Nullable Integer abortRate) {
294-
HTTPFault.Builder builder = HTTPFault.newBuilder();
295-
if (delayRate != null) {
296-
FaultDelay.Builder delayBuilder
297-
= FaultDelay.newBuilder();
298-
delayBuilder.setPercentage(
299-
FractionalPercent.newBuilder()
300-
.setNumerator(delayRate).setDenominator(DenominatorType.MILLION));
301-
if (delayNanos != null) {
302-
delayBuilder.setFixedDelay(Durations.fromNanos(delayNanos));
303-
} else {
304-
delayBuilder.setHeaderDelay(HeaderDelay.newBuilder());
305-
}
306-
builder.setDelay(delayBuilder);
307-
}
308-
if (abortRate != null) {
309-
FaultAbort.Builder abortBuilder = FaultAbort.newBuilder();
310-
abortBuilder.setPercentage(
311-
FractionalPercent.newBuilder()
312-
.setNumerator(abortRate).setDenominator(DenominatorType.MILLION));
313-
if (status != null) {
314-
throw new UnsupportedOperationException();
315-
} else if (httpCode != null) {
316-
abortBuilder.setHttpStatus(httpCode);
317-
} else {
318-
abortBuilder.setHeaderAbort(HeaderAbort.newBuilder());
319-
}
320-
builder.setAbort(abortBuilder);
321-
}
322-
builder.setUpstreamCluster(upstreamCluster);
323-
builder.addAllDownstreamNodes(downstreamNodes);
324-
if (maxActiveFaults != null) {
325-
builder.setMaxActiveFaults(UInt32Value.of(maxActiveFaults));
326-
}
327-
return Any.pack(builder.build());
285+
throw new UnsupportedOperationException();
328286
}
329287

330288
@Override

0 commit comments

Comments
 (0)