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