xref: /aosp_15_r20/external/grpc-grpc/test/core/xds/xds_lb_policy_registry_test.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
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