xref: /aosp_15_r20/external/grpc-grpc/test/cpp/end2end/xds/xds_fault_injection_end2end_test.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1 // Copyright 2017 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 
16 #include <string>
17 #include <vector>
18 
19 #include <gmock/gmock.h>
20 #include <gtest/gtest.h>
21 
22 #include "src/core/client_channel/backup_poller.h"
23 #include "src/core/lib/config/config_vars.h"
24 #include "src/proto/grpc/testing/xds/v3/cluster.grpc.pb.h"
25 #include "src/proto/grpc/testing/xds/v3/fault.grpc.pb.h"
26 #include "src/proto/grpc/testing/xds/v3/http_connection_manager.grpc.pb.h"
27 #include "src/proto/grpc/testing/xds/v3/router.grpc.pb.h"
28 #include "test/core/util/test_config.h"
29 #include "test/cpp/end2end/xds/xds_end2end_test_lib.h"
30 
31 namespace grpc {
32 namespace testing {
33 namespace {
34 
35 using ::envoy::config::cluster::v3::RoutingPriority;
36 using ::envoy::extensions::filters::http::fault::v3::HTTPFault;
37 using ::envoy::extensions::filters::network::http_connection_manager::v3::
38     HttpFilter;
39 using ::envoy::type::v3::FractionalPercent;
40 
41 class FaultInjectionTest : public XdsEnd2endTest {
42  public:
43   // Builds a Listener with Fault Injection filter config. If the http_fault
44   // is nullptr, then assign an empty filter config. This filter config is
45   // required to enable the fault injection features.
BuildListenerWithFaultInjection(const HTTPFault & http_fault=HTTPFault ())46   static Listener BuildListenerWithFaultInjection(
47       const HTTPFault& http_fault = HTTPFault()) {
48     HttpConnectionManager http_connection_manager;
49     Listener listener;
50     listener.set_name(kServerName);
51     HttpFilter* fault_filter = http_connection_manager.add_http_filters();
52     fault_filter->set_name("envoy.fault");
53     fault_filter->mutable_typed_config()->PackFrom(http_fault);
54     HttpFilter* router_filter = http_connection_manager.add_http_filters();
55     router_filter->set_name("router");
56     router_filter->mutable_typed_config()->PackFrom(
57         envoy::extensions::filters::http::router::v3::Router());
58     listener.mutable_api_listener()->mutable_api_listener()->PackFrom(
59         http_connection_manager);
60     return listener;
61   }
62 
BuildRouteConfigurationWithFaultInjection(const HTTPFault & http_fault)63   RouteConfiguration BuildRouteConfigurationWithFaultInjection(
64       const HTTPFault& http_fault) {
65     // Package as Any
66     google::protobuf::Any filter_config;
67     filter_config.PackFrom(http_fault);
68     // Plug into the RouteConfiguration
69     RouteConfiguration new_route_config = default_route_config_;
70     auto* config_map = new_route_config.mutable_virtual_hosts(0)
71                            ->mutable_routes(0)
72                            ->mutable_typed_per_filter_config();
73     (*config_map)["envoy.fault"] = std::move(filter_config);
74     return new_route_config;
75   }
76 
SetFilterConfig(HTTPFault & http_fault)77   void SetFilterConfig(HTTPFault& http_fault) {
78     switch (GetParam().filter_config_setup()) {
79       case XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute: {
80         Listener listener = BuildListenerWithFaultInjection();
81         RouteConfiguration route =
82             BuildRouteConfigurationWithFaultInjection(http_fault);
83         SetListenerAndRouteConfiguration(balancer_.get(), listener, route);
84         break;
85       }
86       case XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInListener: {
87         Listener listener = BuildListenerWithFaultInjection(http_fault);
88         SetListenerAndRouteConfiguration(balancer_.get(), listener,
89                                          default_route_config_);
90       }
91     };
92   }
93 };
94 
95 // Run with all combinations of RDS disabled/enabled and the HTTP filter
96 // config in the Listener vs. in the Route.
97 INSTANTIATE_TEST_SUITE_P(
98     XdsTest, FaultInjectionTest,
99     ::testing::Values(
100         XdsTestType(), XdsTestType().set_enable_rds_testing(),
101         XdsTestType().set_filter_config_setup(
102             XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute),
103         XdsTestType().set_enable_rds_testing().set_filter_config_setup(
104             XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute)),
105     &XdsTestType::Name);
106 
107 // Test to ensure the most basic fault injection config works.
TEST_P(FaultInjectionTest,XdsFaultInjectionAlwaysAbort)108 TEST_P(FaultInjectionTest, XdsFaultInjectionAlwaysAbort) {
109   const uint32_t kAbortPercentagePerHundred = 100;
110   // Create an EDS resource
111   EdsResourceArgs args({{"locality0", {MakeNonExistantEndpoint()}}});
112   balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
113   // Construct the fault injection filter config
114   HTTPFault http_fault;
115   auto* abort_percentage = http_fault.mutable_abort()->mutable_percentage();
116   abort_percentage->set_numerator(kAbortPercentagePerHundred);
117   abort_percentage->set_denominator(FractionalPercent::HUNDRED);
118   http_fault.mutable_abort()->set_grpc_status(
119       static_cast<uint32_t>(StatusCode::ABORTED));
120   // Config fault injection via different setup
121   SetFilterConfig(http_fault);
122   // Fire several RPCs, and expect all of them to be aborted.
123   for (size_t i = 0; i < 5; ++i) {
124     CheckRpcSendFailure(DEBUG_LOCATION, StatusCode::ABORTED, "Fault injected",
125                         RpcOptions().set_wait_for_ready(true));
126   }
127 }
128 
129 // Without the listener config, the fault injection won't be enabled.
TEST_P(FaultInjectionTest,XdsFaultInjectionWithoutListenerFilter)130 TEST_P(FaultInjectionTest, XdsFaultInjectionWithoutListenerFilter) {
131   CreateAndStartBackends(1);
132   const uint32_t kAbortPercentagePerHundred = 100;
133   // Create an EDS resource
134   EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
135   balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
136   // Construct the fault injection filter config
137   HTTPFault http_fault;
138   auto* abort_percentage = http_fault.mutable_abort()->mutable_percentage();
139   abort_percentage->set_numerator(kAbortPercentagePerHundred);
140   abort_percentage->set_denominator(FractionalPercent::HUNDRED);
141   http_fault.mutable_abort()->set_grpc_status(
142       static_cast<uint32_t>(StatusCode::ABORTED));
143   // Turn on fault injection
144   RouteConfiguration route =
145       BuildRouteConfigurationWithFaultInjection(http_fault);
146   SetListenerAndRouteConfiguration(balancer_.get(), default_listener_, route);
147   // Fire several RPCs, and expect all of them to be pass.
148   CheckRpcSendOk(DEBUG_LOCATION, 5, RpcOptions().set_wait_for_ready(true));
149 }
150 
TEST_P(FaultInjectionTest,XdsFaultInjectionPercentageAbort)151 TEST_P(FaultInjectionTest, XdsFaultInjectionPercentageAbort) {
152   CreateAndStartBackends(1);
153   const uint32_t kAbortPercentagePerHundred = 50;
154   const double kAbortRate = kAbortPercentagePerHundred / 100.0;
155   const double kErrorTolerance = 0.1;
156   const size_t kNumRpcs = ComputeIdealNumRpcs(kAbortRate, kErrorTolerance);
157   // Create an EDS resource
158   EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
159   balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
160   // Construct the fault injection filter config
161   HTTPFault http_fault;
162   auto* abort_percentage = http_fault.mutable_abort()->mutable_percentage();
163   abort_percentage->set_numerator(kAbortPercentagePerHundred);
164   abort_percentage->set_denominator(FractionalPercent::HUNDRED);
165   http_fault.mutable_abort()->set_grpc_status(
166       static_cast<uint32_t>(StatusCode::ABORTED));
167   // Config fault injection via different setup
168   SetFilterConfig(http_fault);
169   // Send kNumRpcs RPCs and count the aborts.
170   size_t num_aborted = SendRpcsAndCountFailuresWithMessage(
171       DEBUG_LOCATION, kNumRpcs, StatusCode::ABORTED, "Fault injected");
172   // The abort rate should be roughly equal to the expectation.
173   const double seen_abort_rate = static_cast<double>(num_aborted) / kNumRpcs;
174   EXPECT_THAT(seen_abort_rate,
175               ::testing::DoubleNear(kAbortRate, kErrorTolerance));
176 }
177 
TEST_P(FaultInjectionTest,XdsFaultInjectionPercentageAbortViaHeaders)178 TEST_P(FaultInjectionTest, XdsFaultInjectionPercentageAbortViaHeaders) {
179   CreateAndStartBackends(1);
180   const uint32_t kAbortPercentageCap = 100;
181   const uint32_t kAbortPercentage = 50;
182   const double kAbortRate = kAbortPercentage / 100.0;
183   const double kErrorTolerance = 0.1;
184   const size_t kNumRpcs = ComputeIdealNumRpcs(kAbortRate, kErrorTolerance);
185   // Create an EDS resource
186   EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
187   balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
188   // Construct the fault injection filter config
189   HTTPFault http_fault;
190   http_fault.mutable_abort()->mutable_header_abort();
191   http_fault.mutable_abort()->mutable_percentage()->set_numerator(
192       kAbortPercentageCap);
193   // Config fault injection via different setup
194   SetFilterConfig(http_fault);
195   // Send kNumRpcs RPCs and count the aborts.
196   std::vector<std::pair<std::string, std::string>> metadata = {
197       {"x-envoy-fault-abort-grpc-request", "10"},
198       {"x-envoy-fault-abort-percentage", std::to_string(kAbortPercentage)},
199   };
200   size_t num_aborted = SendRpcsAndCountFailuresWithMessage(
201       DEBUG_LOCATION, kNumRpcs, StatusCode::ABORTED, "Fault injected",
202       RpcOptions().set_metadata(metadata));
203   // The abort rate should be roughly equal to the expectation.
204   const double seen_abort_rate = static_cast<double>(num_aborted) / kNumRpcs;
205   EXPECT_THAT(seen_abort_rate,
206               ::testing::DoubleNear(kAbortRate, kErrorTolerance));
207 }
208 
TEST_P(FaultInjectionTest,XdsFaultInjectionPercentageDelay)209 TEST_P(FaultInjectionTest, XdsFaultInjectionPercentageDelay) {
210   CreateAndStartBackends(1);
211   const auto kRpcTimeout = grpc_core::Duration::Seconds(10);
212   const auto kFixedDelay = grpc_core::Duration::Seconds(20);
213   const uint32_t kDelayPercentagePerHundred = 50;
214   const double kDelayRate = kDelayPercentagePerHundred / 100.0;
215   const double kErrorTolerance = 0.1;
216   const size_t kNumRpcs = ComputeIdealNumRpcs(kDelayRate, kErrorTolerance);
217   const size_t kMaxConcurrentRequests = kNumRpcs;
218   // Create an EDS resource
219   EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
220   balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
221   // Loosen the max concurrent request limit
222   Cluster cluster = default_cluster_;
223   auto* threshold = cluster.mutable_circuit_breakers()->add_thresholds();
224   threshold->set_priority(RoutingPriority::DEFAULT);
225   threshold->mutable_max_requests()->set_value(kMaxConcurrentRequests);
226   balancer_->ads_service()->SetCdsResource(cluster);
227   // Construct the fault injection filter config
228   HTTPFault http_fault;
229   auto* delay_percentage = http_fault.mutable_delay()->mutable_percentage();
230   delay_percentage->set_numerator(kDelayPercentagePerHundred);
231   delay_percentage->set_denominator(FractionalPercent::HUNDRED);
232   SetProtoDuration(kFixedDelay,
233                    http_fault.mutable_delay()->mutable_fixed_delay());
234   // Config fault injection via different setup
235   SetFilterConfig(http_fault);
236   // Make sure channel is connected.  This avoids flakiness caused by
237   // having multiple queued RPCs proceed in parallel when the name
238   // resolution response is returned to the channel.
239   channel_->WaitForConnected(grpc_timeout_milliseconds_to_deadline(15000));
240   // Send kNumRpcs RPCs and count the delays.
241   RpcOptions rpc_options =
242       RpcOptions().set_timeout(kRpcTimeout).set_skip_cancelled_check(true);
243   std::vector<ConcurrentRpc> rpcs =
244       SendConcurrentRpcs(DEBUG_LOCATION, stub_.get(), kNumRpcs, rpc_options);
245   size_t num_delayed = 0;
246   for (auto& rpc : rpcs) {
247     if (rpc.status.error_code() == StatusCode::OK) continue;
248     EXPECT_EQ(StatusCode::DEADLINE_EXCEEDED, rpc.status.error_code());
249     ++num_delayed;
250   }
251   // The delay rate should be roughly equal to the expectation.
252   const double seen_delay_rate = static_cast<double>(num_delayed) / kNumRpcs;
253   EXPECT_THAT(seen_delay_rate,
254               ::testing::DoubleNear(kDelayRate, kErrorTolerance));
255 }
256 
TEST_P(FaultInjectionTest,XdsFaultInjectionPercentageDelayViaHeaders)257 TEST_P(FaultInjectionTest, XdsFaultInjectionPercentageDelayViaHeaders) {
258   CreateAndStartBackends(1);
259   const auto kRpcTimeout = grpc_core::Duration::Seconds(10);
260   const auto kFixedDelay = grpc_core::Duration::Seconds(20);
261   const uint32_t kDelayPercentageCap = 100;
262   const uint32_t kDelayPercentage = 50;
263   const double kDelayRate = kDelayPercentage / 100.0;
264   const double kErrorTolerance = 0.1;
265   const size_t kNumRpcs = ComputeIdealNumRpcs(kDelayRate, kErrorTolerance);
266   const size_t kMaxConcurrentRequests = kNumRpcs;
267   // Create an EDS resource
268   EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
269   balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
270   // Loosen the max concurrent request limit
271   Cluster cluster = default_cluster_;
272   auto* threshold = cluster.mutable_circuit_breakers()->add_thresholds();
273   threshold->set_priority(RoutingPriority::DEFAULT);
274   threshold->mutable_max_requests()->set_value(kMaxConcurrentRequests);
275   balancer_->ads_service()->SetCdsResource(cluster);
276   // Construct the fault injection filter config
277   HTTPFault http_fault;
278   http_fault.mutable_delay()->mutable_header_delay();
279   http_fault.mutable_delay()->mutable_percentage()->set_numerator(
280       kDelayPercentageCap);
281   // Config fault injection via different setup
282   SetFilterConfig(http_fault);
283   // Make sure channel is connected.  This avoids flakiness caused by
284   // having multiple queued RPCs proceed in parallel when the name
285   // resolution response is returned to the channel.
286   channel_->WaitForConnected(grpc_timeout_milliseconds_to_deadline(15000));
287   // Send kNumRpcs RPCs and count the delays.
288   std::vector<std::pair<std::string, std::string>> metadata = {
289       {"x-envoy-fault-delay-request",
290        std::to_string(kFixedDelay.millis() * grpc_test_slowdown_factor())},
291       {"x-envoy-fault-delay-request-percentage",
292        std::to_string(kDelayPercentage)},
293   };
294   RpcOptions rpc_options = RpcOptions()
295                                .set_metadata(metadata)
296                                .set_timeout(kRpcTimeout)
297                                .set_skip_cancelled_check(true);
298   std::vector<ConcurrentRpc> rpcs =
299       SendConcurrentRpcs(DEBUG_LOCATION, stub_.get(), kNumRpcs, rpc_options);
300   size_t num_delayed = 0;
301   for (auto& rpc : rpcs) {
302     if (rpc.status.error_code() == StatusCode::OK) continue;
303     EXPECT_EQ(StatusCode::DEADLINE_EXCEEDED, rpc.status.error_code());
304     ++num_delayed;
305   }
306   // The delay rate should be roughly equal to the expectation.
307   const double seen_delay_rate = static_cast<double>(num_delayed) / kNumRpcs;
308   EXPECT_THAT(seen_delay_rate,
309               ::testing::DoubleNear(kDelayRate, kErrorTolerance));
310 }
311 
TEST_P(FaultInjectionTest,XdsFaultInjectionAbortAfterDelayForStreamCall)312 TEST_P(FaultInjectionTest, XdsFaultInjectionAbortAfterDelayForStreamCall) {
313   CreateAndStartBackends(1);
314   const auto kRpcTimeout = grpc_core::Duration::Seconds(30);
315   const auto kFixedDelay = grpc_core::Duration::Seconds(1);
316   // Create an EDS resource
317   EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
318   balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
319   // Construct the fault injection filter config
320   HTTPFault http_fault;
321   auto* abort_percentage = http_fault.mutable_abort()->mutable_percentage();
322   abort_percentage->set_numerator(100);  // Always inject ABORT!
323   abort_percentage->set_denominator(FractionalPercent::HUNDRED);
324   http_fault.mutable_abort()->set_grpc_status(
325       static_cast<uint32_t>(StatusCode::ABORTED));
326   auto* delay_percentage = http_fault.mutable_delay()->mutable_percentage();
327   delay_percentage->set_numerator(100);  // Always inject DELAY!
328   delay_percentage->set_denominator(FractionalPercent::HUNDRED);
329   SetProtoDuration(kFixedDelay,
330                    http_fault.mutable_delay()->mutable_fixed_delay());
331   // Config fault injection via different setup
332   SetFilterConfig(http_fault);
333   // Send a stream RPC and check its status code
334   ClientContext context;
335   context.set_deadline(
336       grpc_timeout_milliseconds_to_deadline(kRpcTimeout.millis()));
337   auto stream = stub_->BidiStream(&context);
338   stream->WritesDone();
339   auto status = stream->Finish();
340   EXPECT_EQ(StatusCode::ABORTED, status.error_code())
341       << status.error_message() << ", " << status.error_details() << ", "
342       << context.debug_error_string();
343 }
344 
TEST_P(FaultInjectionTest,XdsFaultInjectionAlwaysDelayPercentageAbort)345 TEST_P(FaultInjectionTest, XdsFaultInjectionAlwaysDelayPercentageAbort) {
346   CreateAndStartBackends(1);
347   const auto kRpcTimeout = grpc_core::Duration::Seconds(30);
348   const auto kFixedDelay = grpc_core::Duration::Seconds(1);
349   const uint32_t kAbortPercentagePerHundred = 50;
350   const double kAbortRate = kAbortPercentagePerHundred / 100.0;
351   const double kErrorTolerance = 0.1;
352   const size_t kNumRpcs = ComputeIdealNumRpcs(kAbortRate, kErrorTolerance);
353   const size_t kMaxConcurrentRequests = kNumRpcs;
354   // Create an EDS resource
355   EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
356   balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
357   // Loosen the max concurrent request limit
358   Cluster cluster = default_cluster_;
359   auto* threshold = cluster.mutable_circuit_breakers()->add_thresholds();
360   threshold->set_priority(RoutingPriority::DEFAULT);
361   threshold->mutable_max_requests()->set_value(kMaxConcurrentRequests);
362   balancer_->ads_service()->SetCdsResource(cluster);
363   // Construct the fault injection filter config
364   HTTPFault http_fault;
365   auto* abort_percentage = http_fault.mutable_abort()->mutable_percentage();
366   abort_percentage->set_numerator(kAbortPercentagePerHundred);
367   abort_percentage->set_denominator(FractionalPercent::HUNDRED);
368   http_fault.mutable_abort()->set_grpc_status(
369       static_cast<uint32_t>(StatusCode::ABORTED));
370   auto* delay_percentage = http_fault.mutable_delay()->mutable_percentage();
371   delay_percentage->set_numerator(1000000);  // Always inject DELAY!
372   delay_percentage->set_denominator(FractionalPercent::MILLION);
373   SetProtoDuration(kFixedDelay,
374                    http_fault.mutable_delay()->mutable_fixed_delay());
375   // Config fault injection via different setup
376   SetFilterConfig(http_fault);
377   // Make sure channel is connected.  This avoids flakiness caused by
378   // having multiple queued RPCs proceed in parallel when the name
379   // resolution response is returned to the channel.
380   channel_->WaitForConnected(grpc_timeout_milliseconds_to_deadline(15000));
381   // Send kNumRpcs RPCs and count the aborts.
382   int num_aborted = 0;
383   RpcOptions rpc_options = RpcOptions().set_timeout(kRpcTimeout);
384   std::vector<ConcurrentRpc> rpcs =
385       SendConcurrentRpcs(DEBUG_LOCATION, stub_.get(), kNumRpcs, rpc_options);
386   for (auto& rpc : rpcs) {
387     EXPECT_GE(rpc.elapsed_time, kFixedDelay * grpc_test_slowdown_factor());
388     if (rpc.status.error_code() == StatusCode::OK) continue;
389     EXPECT_EQ("Fault injected", rpc.status.error_message());
390     ++num_aborted;
391   }
392   // The abort rate should be roughly equal to the expectation.
393   const double seen_abort_rate = static_cast<double>(num_aborted) / kNumRpcs;
394   EXPECT_THAT(seen_abort_rate,
395               ::testing::DoubleNear(kAbortRate, kErrorTolerance));
396 }
397 
398 // This test and the above test apply different denominators to delay and
399 // abort. This ensures that we are using the right denominator for each
400 // injected fault in our code.
TEST_P(FaultInjectionTest,XdsFaultInjectionAlwaysDelayPercentageAbortSwitchDenominator)401 TEST_P(FaultInjectionTest,
402        XdsFaultInjectionAlwaysDelayPercentageAbortSwitchDenominator) {
403   CreateAndStartBackends(1);
404   const auto kRpcTimeout = grpc_core::Duration::Seconds(30);
405   const auto kFixedDelay = grpc_core::Duration::Seconds(1);
406   const uint32_t kAbortPercentagePerMillion = 500000;
407   const double kAbortRate = kAbortPercentagePerMillion / 1000000.0;
408   const double kErrorTolerance = 0.1;
409   const size_t kNumRpcs = ComputeIdealNumRpcs(kAbortRate, kErrorTolerance);
410   const size_t kMaxConcurrentRequests = kNumRpcs;
411   // Create an EDS resource
412   EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
413   balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
414   // Loosen the max concurrent request limit
415   Cluster cluster = default_cluster_;
416   auto* threshold = cluster.mutable_circuit_breakers()->add_thresholds();
417   threshold->set_priority(RoutingPriority::DEFAULT);
418   threshold->mutable_max_requests()->set_value(kMaxConcurrentRequests);
419   balancer_->ads_service()->SetCdsResource(cluster);
420   // Construct the fault injection filter config
421   HTTPFault http_fault;
422   auto* abort_percentage = http_fault.mutable_abort()->mutable_percentage();
423   abort_percentage->set_numerator(kAbortPercentagePerMillion);
424   abort_percentage->set_denominator(FractionalPercent::MILLION);
425   http_fault.mutable_abort()->set_grpc_status(
426       static_cast<uint32_t>(StatusCode::ABORTED));
427   auto* delay_percentage = http_fault.mutable_delay()->mutable_percentage();
428   delay_percentage->set_numerator(100);  // Always inject DELAY!
429   delay_percentage->set_denominator(FractionalPercent::HUNDRED);
430   SetProtoDuration(kFixedDelay,
431                    http_fault.mutable_delay()->mutable_fixed_delay());
432   // Config fault injection via different setup
433   SetFilterConfig(http_fault);
434   // Make sure channel is connected.  This avoids flakiness caused by
435   // having multiple queued RPCs proceed in parallel when the name
436   // resolution response is returned to the channel.
437   channel_->WaitForConnected(grpc_timeout_milliseconds_to_deadline(15000));
438   // Send kNumRpcs RPCs and count the aborts.
439   int num_aborted = 0;
440   RpcOptions rpc_options = RpcOptions().set_timeout(kRpcTimeout);
441   std::vector<ConcurrentRpc> rpcs =
442       SendConcurrentRpcs(DEBUG_LOCATION, stub_.get(), kNumRpcs, rpc_options);
443   for (auto& rpc : rpcs) {
444     EXPECT_GE(rpc.elapsed_time, kFixedDelay * grpc_test_slowdown_factor());
445     if (rpc.status.error_code() == StatusCode::OK) continue;
446     EXPECT_EQ("Fault injected", rpc.status.error_message());
447     ++num_aborted;
448   }
449   // The abort rate should be roughly equal to the expectation.
450   const double seen_abort_rate = static_cast<double>(num_aborted) / kNumRpcs;
451   EXPECT_THAT(seen_abort_rate,
452               ::testing::DoubleNear(kAbortRate, kErrorTolerance));
453 }
454 
TEST_P(FaultInjectionTest,XdsFaultInjectionMaxFault)455 TEST_P(FaultInjectionTest, XdsFaultInjectionMaxFault) {
456   CreateAndStartBackends(1);
457   const auto kRpcTimeout = grpc_core::Duration::Seconds(4);
458   const auto kFixedDelay = grpc_core::Duration::Seconds(20);
459   const uint32_t kMaxFault = 10;
460   const uint32_t kNumRpcs = 30;  // kNumRpcs should be bigger than kMaxFault
461   const uint32_t kAlwaysDelayPercentage = 100;
462   // Create an EDS resource
463   EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
464   balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
465   // Construct the fault injection filter config
466   HTTPFault http_fault;
467   auto* delay_percentage = http_fault.mutable_delay()->mutable_percentage();
468   delay_percentage->set_numerator(
469       kAlwaysDelayPercentage);  // Always inject DELAY!
470   delay_percentage->set_denominator(FractionalPercent::HUNDRED);
471   SetProtoDuration(kFixedDelay,
472                    http_fault.mutable_delay()->mutable_fixed_delay());
473   http_fault.mutable_max_active_faults()->set_value(kMaxFault);
474   // Config fault injection via different setup
475   SetFilterConfig(http_fault);
476   // Make sure channel is connected.  This avoids flakiness caused by
477   // having multiple queued RPCs proceed in parallel when the name
478   // resolution response is returned to the channel.
479   channel_->WaitForConnected(grpc_timeout_milliseconds_to_deadline(15000));
480   // Sends a batch of long running RPCs with long timeout to consume all
481   // active faults quota.
482   int num_delayed = 0;
483   RpcOptions rpc_options = RpcOptions().set_timeout(kRpcTimeout);
484   std::vector<ConcurrentRpc> rpcs =
485       SendConcurrentRpcs(DEBUG_LOCATION, stub_.get(), kNumRpcs, rpc_options);
486   for (auto& rpc : rpcs) {
487     if (rpc.status.error_code() == StatusCode::OK) continue;
488     EXPECT_EQ(StatusCode::DEADLINE_EXCEEDED, rpc.status.error_code());
489     ++num_delayed;
490   }
491   // Only kMaxFault number of RPC should be fault injected.
492   EXPECT_EQ(kMaxFault, num_delayed);
493   // Conduct one more round of RPCs after previous calls are finished. The goal
494   // is to validate if the max fault counter is restored to zero.
495   num_delayed = 0;
496   rpcs = SendConcurrentRpcs(DEBUG_LOCATION, stub_.get(), kNumRpcs, rpc_options);
497   for (auto& rpc : rpcs) {
498     if (rpc.status.error_code() == StatusCode::OK) continue;
499     EXPECT_EQ(StatusCode::DEADLINE_EXCEEDED, rpc.status.error_code());
500     ++num_delayed;
501   }
502   // Only kMaxFault number of RPC should be fault injected. If the max fault
503   // isn't restored to zero, none of the new RPCs will be fault injected.
504   EXPECT_EQ(kMaxFault, num_delayed);
505 }
506 
TEST_P(FaultInjectionTest,XdsFaultInjectionBidiStreamDelayOk)507 TEST_P(FaultInjectionTest, XdsFaultInjectionBidiStreamDelayOk) {
508   CreateAndStartBackends(1);
509   const auto kRpcTimeout = grpc_core::Duration::Seconds(20);
510   const auto kFixedDelay = grpc_core::Duration::Seconds(1);
511   const uint32_t kDelayPercentagePerHundred = 100;
512   // Create an EDS resource
513   EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
514   balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
515   // Construct the fault injection filter config
516   HTTPFault http_fault;
517   auto* delay_percentage = http_fault.mutable_delay()->mutable_percentage();
518   delay_percentage->set_numerator(kDelayPercentagePerHundred);
519   delay_percentage->set_denominator(FractionalPercent::HUNDRED);
520   SetProtoDuration(kFixedDelay,
521                    http_fault.mutable_delay()->mutable_fixed_delay());
522   // Config fault injection via different setup
523   SetFilterConfig(http_fault);
524   ClientContext context;
525   context.set_deadline(
526       grpc_timeout_milliseconds_to_deadline(kRpcTimeout.millis()));
527   auto stream = stub_->BidiStream(&context);
528   stream->WritesDone();
529   auto status = stream->Finish();
530   EXPECT_TRUE(status.ok()) << status.error_message() << ", "
531                            << status.error_details() << ", "
532                            << context.debug_error_string();
533 }
534 
535 // This case catches a bug in the retry code that was triggered by a bad
536 // interaction with the FI code.  See https://github.com/grpc/grpc/pull/27217
537 // for description.
TEST_P(FaultInjectionTest,XdsFaultInjectionBidiStreamDelayError)538 TEST_P(FaultInjectionTest, XdsFaultInjectionBidiStreamDelayError) {
539   CreateAndStartBackends(1);
540   const auto kRpcTimeout = grpc_core::Duration::Seconds(10);
541   const auto kFixedDelay = grpc_core::Duration::Seconds(30);
542   const uint32_t kDelayPercentagePerHundred = 100;
543   // Create an EDS resource
544   EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
545   balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
546   // Construct the fault injection filter config
547   HTTPFault http_fault;
548   auto* delay_percentage = http_fault.mutable_delay()->mutable_percentage();
549   delay_percentage->set_numerator(kDelayPercentagePerHundred);
550   delay_percentage->set_denominator(FractionalPercent::HUNDRED);
551   SetProtoDuration(kFixedDelay,
552                    http_fault.mutable_delay()->mutable_fixed_delay());
553   // Config fault injection via different setup
554   SetFilterConfig(http_fault);
555   ClientContext context;
556   context.set_deadline(
557       grpc_timeout_milliseconds_to_deadline(kRpcTimeout.millis()));
558   auto stream = stub_->BidiStream(&context);
559   stream->WritesDone();
560   auto status = stream->Finish();
561   EXPECT_EQ(StatusCode::DEADLINE_EXCEEDED, status.error_code())
562       << status.error_message() << ", " << status.error_details() << ", "
563       << context.debug_error_string();
564 }
565 
566 }  // namespace
567 }  // namespace testing
568 }  // namespace grpc
569 
main(int argc,char ** argv)570 int main(int argc, char** argv) {
571   grpc::testing::TestEnvironment env(&argc, argv);
572   ::testing::InitGoogleTest(&argc, argv);
573   // Make the backup poller poll very frequently in order to pick up
574   // updates from all the subchannels's FDs.
575   grpc_core::ConfigVars::Overrides overrides;
576   overrides.client_channel_backup_poll_interval_ms = 1;
577   grpc_core::ConfigVars::SetOverrides(overrides);
578 #if TARGET_OS_IPHONE
579   // Workaround Apple CFStream bug
580   grpc_core::SetEnv("grpc_cfstream", "0");
581 #endif
582   grpc_init();
583   const auto result = RUN_ALL_TESTS();
584   grpc_shutdown();
585   return result;
586 }
587