1 //
2 //
3 // Copyright 2022 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18
19 #include "src/core/ext/xds/xds_lb_policy_registry.h"
20
21 #include <string>
22
23 #include <google/protobuf/any.pb.h>
24 #include <google/protobuf/duration.pb.h>
25 #include <google/protobuf/struct.pb.h>
26 #include <google/protobuf/wrappers.pb.h>
27
28 #include "absl/status/status.h"
29 #include "absl/status/statusor.h"
30 #include "gmock/gmock.h"
31 #include "gtest/gtest.h"
32 #include "upb/mem/arena.hpp"
33 #include "upb/reflection/def.hpp"
34
35 #include <grpc/grpc.h>
36
37 #include "src/core/ext/xds/xds_bootstrap_grpc.h"
38 #include "src/core/lib/config/core_configuration.h"
39 #include "src/core/lib/gprpp/crash.h"
40 #include "src/core/lib/gprpp/orphanable.h"
41 #include "src/core/lib/gprpp/ref_counted_ptr.h"
42 #include "src/core/lib/gprpp/validation_errors.h"
43 #include "src/core/lib/json/json_writer.h"
44 #include "src/core/load_balancing/lb_policy.h"
45 #include "src/core/load_balancing/lb_policy_factory.h"
46 #include "src/proto/grpc/testing/xds/v3/client_side_weighted_round_robin.pb.h"
47 #include "src/proto/grpc/testing/xds/v3/cluster.pb.h"
48 #include "src/proto/grpc/testing/xds/v3/extension.pb.h"
49 #include "src/proto/grpc/testing/xds/v3/pick_first.pb.h"
50 #include "src/proto/grpc/testing/xds/v3/ring_hash.pb.h"
51 #include "src/proto/grpc/testing/xds/v3/round_robin.pb.h"
52 #include "src/proto/grpc/testing/xds/v3/typed_struct.pb.h"
53 #include "src/proto/grpc/testing/xds/v3/wrr_locality.pb.h"
54 #include "test/core/util/test_config.h"
55
56 namespace grpc_core {
57 namespace testing {
58 namespace {
59
60 using LoadBalancingPolicyProto =
61 ::envoy::config::cluster::v3::LoadBalancingPolicy;
62 using ::envoy::extensions::load_balancing_policies::
63 client_side_weighted_round_robin::v3::ClientSideWeightedRoundRobin;
64 using ::envoy::extensions::load_balancing_policies::pick_first::v3::PickFirst;
65 using ::envoy::extensions::load_balancing_policies::ring_hash::v3::RingHash;
66 using ::envoy::extensions::load_balancing_policies::round_robin::v3::RoundRobin;
67 using ::envoy::extensions::load_balancing_policies::wrr_locality::v3::
68 WrrLocality;
69 using ::xds::type::v3::TypedStruct;
70
71 // Uses XdsLbPolicyRegistry to convert
72 // envoy::config::cluster::v3::LoadBalancingPolicy to gRPC's JSON form.
ConvertXdsPolicy(const LoadBalancingPolicyProto & policy)73 absl::StatusOr<std::string> ConvertXdsPolicy(
74 const LoadBalancingPolicyProto& policy) {
75 std::string serialized_policy = policy.SerializeAsString();
76 upb::Arena arena;
77 upb::DefPool def_pool;
78 XdsResourceType::DecodeContext context = {
79 nullptr, GrpcXdsBootstrap::GrpcXdsServer(), nullptr, def_pool.ptr(),
80 arena.ptr()};
81 auto* upb_policy = envoy_config_cluster_v3_LoadBalancingPolicy_parse(
82 serialized_policy.data(), serialized_policy.size(), arena.ptr());
83 ValidationErrors errors;
84 ValidationErrors::ScopedField field(&errors, ".load_balancing_policy");
85 auto config = XdsLbPolicyRegistry().ConvertXdsLbPolicyConfig(
86 context, upb_policy, &errors);
87 if (!errors.ok()) {
88 return errors.status(absl::StatusCode::kInvalidArgument,
89 "validation errors");
90 }
91 EXPECT_EQ(config.size(), 1);
92 return JsonDump(Json{config[0]});
93 }
94
95 // A gRPC LB policy factory for a custom policy. None of the methods
96 // will actually be used; we just need it to be present in the gRPC LB
97 // policy registry.
98 class CustomLbPolicyFactory : public LoadBalancingPolicyFactory {
99 public:
CreateLoadBalancingPolicy(LoadBalancingPolicy::Args) const100 OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy(
101 LoadBalancingPolicy::Args /*args*/) const override {
102 Crash("unreachable");
103 return nullptr;
104 }
105
name() const106 absl::string_view name() const override { return "test.CustomLb"; }
107
108 absl::StatusOr<RefCountedPtr<LoadBalancingPolicy::Config>>
ParseLoadBalancingConfig(const Json &) const109 ParseLoadBalancingConfig(const Json& /*json*/) const override {
110 return nullptr;
111 }
112 };
113
114 //
115 // RoundRobin
116 //
117
TEST(RoundRobin,Basic)118 TEST(RoundRobin, Basic) {
119 LoadBalancingPolicyProto policy;
120 auto* lb_policy = policy.add_policies();
121 lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
122 RoundRobin());
123 auto result = ConvertXdsPolicy(policy);
124 ASSERT_TRUE(result.ok()) << result.status();
125 EXPECT_EQ(*result, "{\"round_robin\":{}}");
126 }
127
128 //
129 // ClientSideWeightedRoundRobin
130 //
131
TEST(ClientSideWeightedRoundRobinTest,DefaultConfig)132 TEST(ClientSideWeightedRoundRobinTest, DefaultConfig) {
133 LoadBalancingPolicyProto policy;
134 policy.add_policies()
135 ->mutable_typed_extension_config()
136 ->mutable_typed_config()
137 ->PackFrom(ClientSideWeightedRoundRobin());
138 auto result = ConvertXdsPolicy(policy);
139 ASSERT_TRUE(result.ok()) << result.status();
140 EXPECT_EQ(*result, "{\"weighted_round_robin\":{}}");
141 }
142
TEST(ClientSideWeightedRoundRobinTest,FieldsExplicitlySet)143 TEST(ClientSideWeightedRoundRobinTest, FieldsExplicitlySet) {
144 ClientSideWeightedRoundRobin wrr;
145 wrr.mutable_enable_oob_load_report()->set_value(true);
146 wrr.mutable_oob_reporting_period()->set_seconds(1);
147 wrr.mutable_blackout_period()->set_seconds(2);
148 wrr.mutable_weight_expiration_period()->set_seconds(3);
149 wrr.mutable_weight_update_period()->set_seconds(4);
150 wrr.mutable_error_utilization_penalty()->set_value(5.0);
151 LoadBalancingPolicyProto policy;
152 policy.add_policies()
153 ->mutable_typed_extension_config()
154 ->mutable_typed_config()
155 ->PackFrom(wrr);
156 auto result = ConvertXdsPolicy(policy);
157 ASSERT_TRUE(result.ok()) << result.status();
158 EXPECT_EQ(*result,
159 "{\"weighted_round_robin\":{"
160 "\"blackoutPeriod\":\"2.000000000s\","
161 "\"enableOobLoadReport\":true,"
162 "\"errorUtilizationPenalty\":5,"
163 "\"oobReportingPeriod\":\"1.000000000s\","
164 "\"weightExpirationPeriod\":\"3.000000000s\","
165 "\"weightUpdatePeriod\":\"4.000000000s\""
166 "}}");
167 }
168
TEST(ClientSideWeightedRoundRobinTest,InvalidValues)169 TEST(ClientSideWeightedRoundRobinTest, InvalidValues) {
170 ClientSideWeightedRoundRobin wrr;
171 wrr.mutable_oob_reporting_period()->set_seconds(-1);
172 wrr.mutable_blackout_period()->set_seconds(-2);
173 wrr.mutable_weight_expiration_period()->set_seconds(-3);
174 wrr.mutable_weight_update_period()->set_seconds(-4);
175 wrr.mutable_error_utilization_penalty()->set_value(-1);
176 LoadBalancingPolicyProto policy;
177 policy.add_policies()
178 ->mutable_typed_extension_config()
179 ->mutable_typed_config()
180 ->PackFrom(wrr);
181 auto result = ConvertXdsPolicy(policy);
182 EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
183 EXPECT_EQ(result.status().message(),
184 "validation errors: ["
185 "field:load_balancing_policy.policies[0].typed_extension_config"
186 ".typed_config.value[envoy.extensions.load_balancing_policies"
187 ".client_side_weighted_round_robin.v3.ClientSideWeightedRoundRobin]"
188 ".blackout_period.seconds "
189 "error:value must be in the range [0, 315576000000]; "
190 "field:load_balancing_policy.policies[0].typed_extension_config"
191 ".typed_config.value[envoy.extensions.load_balancing_policies"
192 ".client_side_weighted_round_robin.v3.ClientSideWeightedRoundRobin]"
193 ".error_utilization_penalty error:value must be non-negative; "
194 "field:load_balancing_policy.policies[0].typed_extension_config"
195 ".typed_config.value[envoy.extensions.load_balancing_policies"
196 ".client_side_weighted_round_robin.v3.ClientSideWeightedRoundRobin]"
197 ".oob_reporting_period.seconds "
198 "error:value must be in the range [0, 315576000000]; "
199 "field:load_balancing_policy.policies[0].typed_extension_config"
200 ".typed_config.value[envoy.extensions.load_balancing_policies"
201 ".client_side_weighted_round_robin.v3.ClientSideWeightedRoundRobin]"
202 ".weight_expiration_period.seconds "
203 "error:value must be in the range [0, 315576000000]; "
204 "field:load_balancing_policy.policies[0].typed_extension_config"
205 ".typed_config.value[envoy.extensions.load_balancing_policies"
206 ".client_side_weighted_round_robin.v3.ClientSideWeightedRoundRobin]"
207 ".weight_update_period.seconds "
208 "error:value must be in the range [0, 315576000000]]")
209 << result.status();
210 }
211
212 //
213 // RingHash
214 //
215
TEST(RingHashConfig,DefaultConfig)216 TEST(RingHashConfig, DefaultConfig) {
217 LoadBalancingPolicyProto policy;
218 policy.add_policies()
219 ->mutable_typed_extension_config()
220 ->mutable_typed_config()
221 ->PackFrom(RingHash());
222 auto result = ConvertXdsPolicy(policy);
223 ASSERT_TRUE(result.ok()) << result.status();
224 EXPECT_EQ(*result,
225 "{\"ring_hash_experimental\":{"
226 "\"maxRingSize\":8388608,\"minRingSize\":1024}}");
227 }
228
TEST(RingHashConfig,FieldsExplicitlySet)229 TEST(RingHashConfig, FieldsExplicitlySet) {
230 RingHash ring_hash;
231 ring_hash.set_hash_function(RingHash::XX_HASH);
232 ring_hash.mutable_minimum_ring_size()->set_value(1234);
233 ring_hash.mutable_maximum_ring_size()->set_value(4567);
234 LoadBalancingPolicyProto policy;
235 policy.add_policies()
236 ->mutable_typed_extension_config()
237 ->mutable_typed_config()
238 ->PackFrom(ring_hash);
239 auto result = ConvertXdsPolicy(policy);
240 ASSERT_TRUE(result.ok()) << result.status();
241 EXPECT_EQ(*result,
242 "{\"ring_hash_experimental\":{"
243 "\"maxRingSize\":4567,\"minRingSize\":1234}}");
244 }
245
TEST(RingHashConfig,InvalidHashFunction)246 TEST(RingHashConfig, InvalidHashFunction) {
247 RingHash ring_hash;
248 ring_hash.set_hash_function(RingHash::MURMUR_HASH_2);
249 LoadBalancingPolicyProto policy;
250 policy.add_policies()
251 ->mutable_typed_extension_config()
252 ->mutable_typed_config()
253 ->PackFrom(ring_hash);
254 auto result = ConvertXdsPolicy(policy);
255 EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
256 EXPECT_EQ(result.status().message(),
257 "validation errors: ["
258 "field:load_balancing_policy.policies[0].typed_extension_config"
259 ".typed_config.value[envoy.extensions.load_balancing_policies"
260 ".ring_hash.v3.RingHash].hash_function "
261 "error:unsupported value (must be XX_HASH)]")
262 << result.status();
263 }
264
TEST(RingHashConfig,RingSizesTooHigh)265 TEST(RingHashConfig, RingSizesTooHigh) {
266 RingHash ring_hash;
267 ring_hash.mutable_minimum_ring_size()->set_value(8388609);
268 ring_hash.mutable_maximum_ring_size()->set_value(8388609);
269 LoadBalancingPolicyProto policy;
270 policy.add_policies()
271 ->mutable_typed_extension_config()
272 ->mutable_typed_config()
273 ->PackFrom(ring_hash);
274 auto result = ConvertXdsPolicy(policy);
275 EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
276 EXPECT_EQ(result.status().message(),
277 "validation errors: ["
278 "field:load_balancing_policy.policies[0].typed_extension_config"
279 ".typed_config.value[envoy.extensions.load_balancing_policies"
280 ".ring_hash.v3.RingHash].maximum_ring_size "
281 "error:value must be in the range [1, 8388608]; "
282 "field:load_balancing_policy.policies[0].typed_extension_config"
283 ".typed_config.value[envoy.extensions.load_balancing_policies"
284 ".ring_hash.v3.RingHash].minimum_ring_size "
285 "error:value must be in the range [1, 8388608]]")
286 << result.status();
287 }
288
TEST(RingHashConfig,RingSizesTooLow)289 TEST(RingHashConfig, RingSizesTooLow) {
290 RingHash ring_hash;
291 ring_hash.mutable_minimum_ring_size()->set_value(0);
292 ring_hash.mutable_maximum_ring_size()->set_value(0);
293 LoadBalancingPolicyProto policy;
294 policy.add_policies()
295 ->mutable_typed_extension_config()
296 ->mutable_typed_config()
297 ->PackFrom(ring_hash);
298 auto result = ConvertXdsPolicy(policy);
299 EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
300 EXPECT_EQ(result.status().message(),
301 "validation errors: ["
302 "field:load_balancing_policy.policies[0].typed_extension_config"
303 ".typed_config.value[envoy.extensions.load_balancing_policies"
304 ".ring_hash.v3.RingHash].maximum_ring_size "
305 "error:value must be in the range [1, 8388608]; "
306 "field:load_balancing_policy.policies[0].typed_extension_config"
307 ".typed_config.value[envoy.extensions.load_balancing_policies"
308 ".ring_hash.v3.RingHash].minimum_ring_size "
309 "error:value must be in the range [1, 8388608]]")
310 << result.status();
311 }
312
TEST(RingHashConfig,MinRingSizeGreaterThanMaxRingSize)313 TEST(RingHashConfig, MinRingSizeGreaterThanMaxRingSize) {
314 RingHash ring_hash;
315 ring_hash.mutable_minimum_ring_size()->set_value(1000);
316 ring_hash.mutable_maximum_ring_size()->set_value(999);
317 LoadBalancingPolicyProto policy;
318 policy.add_policies()
319 ->mutable_typed_extension_config()
320 ->mutable_typed_config()
321 ->PackFrom(ring_hash);
322 auto result = ConvertXdsPolicy(policy);
323 EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
324 EXPECT_EQ(result.status().message(),
325 "validation errors: ["
326 "field:load_balancing_policy.policies[0].typed_extension_config"
327 ".typed_config.value[envoy.extensions.load_balancing_policies"
328 ".ring_hash.v3.RingHash].minimum_ring_size "
329 "error:cannot be greater than maximum_ring_size]")
330 << result.status();
331 }
332
333 //
334 // WrrLocality
335 //
336
TEST(WrrLocality,RoundRobinChild)337 TEST(WrrLocality, RoundRobinChild) {
338 WrrLocality wrr_locality;
339 wrr_locality.mutable_endpoint_picking_policy()
340 ->add_policies()
341 ->mutable_typed_extension_config()
342 ->mutable_typed_config()
343 ->PackFrom(RoundRobin());
344 LoadBalancingPolicyProto policy;
345 auto* lb_policy = policy.add_policies();
346 lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
347 wrr_locality);
348 auto result = ConvertXdsPolicy(policy);
349 ASSERT_TRUE(result.ok()) << result.status();
350 EXPECT_EQ(*result,
351 "{\"xds_wrr_locality_experimental\":{"
352 "\"childPolicy\":[{\"round_robin\":{}}]}}");
353 }
354
TEST(WrrLocality,MissingEndpointPickingPolicy)355 TEST(WrrLocality, MissingEndpointPickingPolicy) {
356 LoadBalancingPolicyProto policy;
357 auto* lb_policy = policy.add_policies();
358 lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
359 WrrLocality());
360 auto result = ConvertXdsPolicy(policy);
361 EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
362 EXPECT_EQ(result.status().message(),
363 "validation errors: ["
364 "field:load_balancing_policy.policies[0].typed_extension_config"
365 ".typed_config.value[envoy.extensions.load_balancing_policies"
366 ".wrr_locality.v3.WrrLocality].endpoint_picking_policy "
367 "error:field not present]")
368 << result.status();
369 }
370
TEST(WrrLocality,ChildPolicyInvalid)371 TEST(WrrLocality, ChildPolicyInvalid) {
372 RingHash ring_hash;
373 ring_hash.set_hash_function(RingHash::MURMUR_HASH_2);
374 WrrLocality wrr_locality;
375 wrr_locality.mutable_endpoint_picking_policy()
376 ->add_policies()
377 ->mutable_typed_extension_config()
378 ->mutable_typed_config()
379 ->PackFrom(ring_hash);
380 LoadBalancingPolicyProto policy;
381 auto* lb_policy = policy.add_policies();
382 lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
383 wrr_locality);
384 auto result = ConvertXdsPolicy(policy);
385 EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
386 EXPECT_EQ(result.status().message(),
387 "validation errors: ["
388 "field:load_balancing_policy.policies[0].typed_extension_config"
389 ".typed_config.value[envoy.extensions.load_balancing_policies"
390 ".wrr_locality.v3.WrrLocality].endpoint_picking_policy.policies[0]"
391 ".typed_extension_config.typed_config.value["
392 "envoy.extensions.load_balancing_policies.ring_hash.v3.RingHash]"
393 ".hash_function "
394 "error:unsupported value (must be XX_HASH)]")
395 << result.status();
396 }
397
TEST(WrrLocality,NoSupportedChildPolicy)398 TEST(WrrLocality, NoSupportedChildPolicy) {
399 WrrLocality wrr_locality;
400 wrr_locality.mutable_endpoint_picking_policy()
401 ->add_policies()
402 ->mutable_typed_extension_config()
403 ->mutable_typed_config()
404 ->PackFrom(LoadBalancingPolicyProto());
405 LoadBalancingPolicyProto policy;
406 auto* lb_policy = policy.add_policies();
407 lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
408 wrr_locality);
409 auto result = ConvertXdsPolicy(policy);
410 EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
411 EXPECT_EQ(result.status().message(),
412 "validation errors: ["
413 "field:load_balancing_policy.policies[0].typed_extension_config"
414 ".typed_config.value[envoy.extensions.load_balancing_policies"
415 ".wrr_locality.v3.WrrLocality].endpoint_picking_policy "
416 "error:no supported load balancing policy config found]")
417 << result.status();
418 }
419
TEST(WrrLocality,UnsupportedChildPolicyTypeSkipped)420 TEST(WrrLocality, UnsupportedChildPolicyTypeSkipped) {
421 // Create WrrLocality policy and add two policies to its list, an unsupported
422 // type and then a known RoundRobin type. Expect that the unsupported type is
423 // skipped and RoundRobin is selected.
424 WrrLocality wrr_locality;
425 wrr_locality.mutable_endpoint_picking_policy()
426 ->add_policies()
427 ->mutable_typed_extension_config()
428 ->mutable_typed_config()
429 ->PackFrom(LoadBalancingPolicyProto());
430 wrr_locality.mutable_endpoint_picking_policy()
431 ->add_policies()
432 ->mutable_typed_extension_config()
433 ->mutable_typed_config()
434 ->PackFrom(RoundRobin());
435 LoadBalancingPolicyProto policy;
436 auto* lb_policy = policy.add_policies();
437 lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
438 wrr_locality);
439 auto result = ConvertXdsPolicy(policy);
440 EXPECT_EQ(*result,
441 "{\"xds_wrr_locality_experimental\":{"
442 "\"childPolicy\":[{\"round_robin\":{}}]}}");
443 }
444
445 //
446 // PickFirst
447 //
448
TEST(PickFirst,NoShuffle)449 TEST(PickFirst, NoShuffle) {
450 LoadBalancingPolicyProto policy;
451 auto* lb_policy = policy.add_policies();
452 PickFirst pick_first;
453 pick_first.set_shuffle_address_list(false);
454 lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
455 pick_first);
456 auto result = ConvertXdsPolicy(policy);
457 ASSERT_TRUE(result.ok()) << result.status();
458 EXPECT_EQ(*result, "{\"pick_first\":{\"shuffleAddressList\":false}}");
459 }
460
TEST(PickFirst,Shuffle)461 TEST(PickFirst, Shuffle) {
462 LoadBalancingPolicyProto policy;
463 auto* lb_policy = policy.add_policies();
464 PickFirst pick_first;
465 pick_first.set_shuffle_address_list(true);
466 lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
467 pick_first);
468 auto result = ConvertXdsPolicy(policy);
469 ASSERT_TRUE(result.ok()) << result.status();
470 EXPECT_EQ(*result, "{\"pick_first\":{\"shuffleAddressList\":true}}");
471 }
472
TEST(PickFirst,ShuffleOmitted)473 TEST(PickFirst, ShuffleOmitted) {
474 LoadBalancingPolicyProto policy;
475 auto* lb_policy = policy.add_policies();
476 lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
477 PickFirst());
478 auto result = ConvertXdsPolicy(policy);
479 ASSERT_TRUE(result.ok()) << result.status();
480 EXPECT_EQ(*result, "{\"pick_first\":{\"shuffleAddressList\":false}}");
481 }
482
483 //
484 // CustomPolicy
485 //
486
TEST(CustomPolicy,Basic)487 TEST(CustomPolicy, Basic) {
488 TypedStruct typed_struct;
489 typed_struct.set_type_url("type.googleapis.com/test.CustomLb");
490 auto* fields = typed_struct.mutable_value()->mutable_fields();
491 (*fields)["foo"].set_string_value("bar");
492 LoadBalancingPolicyProto policy;
493 auto* lb_policy = policy.add_policies();
494 lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
495 typed_struct);
496 auto result = ConvertXdsPolicy(policy);
497 ASSERT_TRUE(result.ok()) << result.status();
498 EXPECT_EQ(*result, "{\"test.CustomLb\":{\"foo\":\"bar\"}}");
499 }
500
501 //
502 // XdsLbPolicyRegistryTest
503 //
504
TEST(XdsLbPolicyRegistryTest,EmptyLoadBalancingPolicy)505 TEST(XdsLbPolicyRegistryTest, EmptyLoadBalancingPolicy) {
506 auto result = ConvertXdsPolicy(LoadBalancingPolicyProto());
507 EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
508 EXPECT_EQ(result.status().message(),
509 "validation errors: [field:load_balancing_policy "
510 "error:no supported load balancing policy config found]")
511 << result.status();
512 }
513
TEST(XdsLbPolicyRegistryTest,MissingTypedExtensionConfig)514 TEST(XdsLbPolicyRegistryTest, MissingTypedExtensionConfig) {
515 LoadBalancingPolicyProto policy;
516 policy.add_policies();
517 auto result = ConvertXdsPolicy(policy);
518 EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
519 EXPECT_EQ(result.status().message(),
520 "validation errors: ["
521 "field:load_balancing_policy.policies[0].typed_extension_config "
522 "error:field not present]")
523 << result.status();
524 }
525
TEST(XdsLbPolicyRegistryTest,MissingTypedConfig)526 TEST(XdsLbPolicyRegistryTest, MissingTypedConfig) {
527 LoadBalancingPolicyProto policy;
528 policy.add_policies()->mutable_typed_extension_config();
529 auto result = ConvertXdsPolicy(policy);
530 EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
531 EXPECT_EQ(result.status().message(),
532 "validation errors: ["
533 "field:load_balancing_policy.policies[0].typed_extension_config"
534 ".typed_config error:field not present]")
535 << result.status();
536 }
537
538 // This tests that we pass along errors that are generated by
539 // ExtractXdsExtension(). An exhaustive list of error cases caught by
540 // ExtractXdsExtension() are covered in xds_common_types_test.
TEST(XdsLbPolicyRegistryTest,ErrorExtractingExtension)541 TEST(XdsLbPolicyRegistryTest, ErrorExtractingExtension) {
542 TypedStruct typed_struct;
543 typed_struct.set_type_url("type.googleapis.com/");
544 LoadBalancingPolicyProto policy;
545 auto* lb_policy = policy.add_policies();
546 lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
547 typed_struct);
548 auto result = ConvertXdsPolicy(policy);
549 EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
550 EXPECT_EQ(result.status().message(),
551 "validation errors: ["
552 "field:load_balancing_policy.policies[0].typed_extension_config"
553 ".typed_config.value[xds.type.v3.TypedStruct].type_url "
554 "error:invalid value \"type.googleapis.com/\"]")
555 << result.status();
556 }
557
TEST(XdsLbPolicyRegistryTest,NoSupportedType)558 TEST(XdsLbPolicyRegistryTest, NoSupportedType) {
559 LoadBalancingPolicyProto policy;
560 // Unsupported built-in type.
561 policy.add_policies()
562 ->mutable_typed_extension_config()
563 ->mutable_typed_config()
564 ->PackFrom(LoadBalancingPolicyProto());
565 // Unsupported custom type.
566 TypedStruct typed_struct;
567 typed_struct.set_type_url("myorg/foo/bar/test.UnknownLb");
568 policy.add_policies()
569 ->mutable_typed_extension_config()
570 ->mutable_typed_config()
571 ->PackFrom(typed_struct);
572 auto result = ConvertXdsPolicy(policy);
573 EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
574 EXPECT_EQ(result.status().message(),
575 "validation errors: [field:load_balancing_policy "
576 "error:no supported load balancing policy config found]")
577 << result.status();
578 }
579
TEST(XdsLbPolicyRegistryTest,UnsupportedTypesSkipped)580 TEST(XdsLbPolicyRegistryTest, UnsupportedTypesSkipped) {
581 LoadBalancingPolicyProto policy;
582 // Unsupported built-in type.
583 policy.add_policies()
584 ->mutable_typed_extension_config()
585 ->mutable_typed_config()
586 ->PackFrom(LoadBalancingPolicyProto());
587 // Unsupported custom type.
588 TypedStruct typed_struct;
589 typed_struct.set_type_url("myorg/foo/bar/test.UnknownLb");
590 policy.add_policies()
591 ->mutable_typed_extension_config()
592 ->mutable_typed_config()
593 ->PackFrom(typed_struct);
594 // Supported type.
595 policy.add_policies()
596 ->mutable_typed_extension_config()
597 ->mutable_typed_config()
598 ->PackFrom(RoundRobin());
599 auto result = ConvertXdsPolicy(policy);
600 ASSERT_TRUE(result.ok()) << result.status();
601 EXPECT_EQ(*result, "{\"round_robin\":{}}");
602 }
603
604 // Build a recurse load balancing policy that goes beyond the max allowable
605 // depth of 16.
BuildRecursiveLoadBalancingPolicy(int depth)606 LoadBalancingPolicyProto BuildRecursiveLoadBalancingPolicy(int depth) {
607 LoadBalancingPolicyProto policy;
608 if (depth >= 16) {
609 policy.add_policies()
610 ->mutable_typed_extension_config()
611 ->mutable_typed_config()
612 ->PackFrom(RoundRobin());
613 return policy;
614 }
615 WrrLocality wrr_locality;
616 *wrr_locality.mutable_endpoint_picking_policy() =
617 BuildRecursiveLoadBalancingPolicy(depth + 1);
618 policy.add_policies()
619 ->mutable_typed_extension_config()
620 ->mutable_typed_config()
621 ->PackFrom(wrr_locality);
622 return policy;
623 }
624
TEST(XdsLbPolicyRegistryTest,MaxRecursion)625 TEST(XdsLbPolicyRegistryTest, MaxRecursion) {
626 auto result = ConvertXdsPolicy(BuildRecursiveLoadBalancingPolicy(0));
627 EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
628 EXPECT_THAT(std::string(result.status().message()),
629 ::testing::EndsWith("error:exceeded max recursion depth of 16]"));
630 }
631
632 } // namespace
633 } // namespace testing
634 } // namespace grpc_core
635
main(int argc,char ** argv)636 int main(int argc, char** argv) {
637 ::testing::InitGoogleTest(&argc, argv);
638 grpc::testing::TestEnvironment env(&argc, argv);
639 grpc_core::CoreConfiguration::RegisterBuilder(
640 [](grpc_core::CoreConfiguration::Builder* builder) {
641 builder->lb_policy_registry()->RegisterLoadBalancingPolicyFactory(
642 std::make_unique<grpc_core::testing::CustomLbPolicyFactory>());
643 });
644 grpc_init();
645 auto result = RUN_ALL_TESTS();
646 grpc_shutdown();
647 return result;
648 }
649