xDS: custom LB policy support (#31262)

* XdsBootstrap: move two more methods out of the interface

* Automated change: Fix sanity tests

* XdsClient: add unit test

* Automated change: Fix sanity tests

* fix memory leaks

* add helper method

* add unsubscription

* add test for multiple subscriptions

* clang-format

* fix build

* fix flakiness

* add checking for other node fields

* add v2 test

* add response builder

* add test for update from server

* add test for update containing only changed resources

* clang-format

* fix build

* add test for resource not existing upon subscription

* add test for stream closed by server

* add test for multiple watchers for the same resource

* add test for connection failure

* clang-format

* add test for resources wrapped in Resource wrapper message

* add test for resource validation failure

* add test for multiple invalid resources, and fix a case in XdsClient

* add test for validation failure for already-cached resource

* add test for server not resending resources after stream disconnect

* clang-format

* fix XdsClient to report channel errors to newly started watchers

* fix XdsClient to send cached errors/does-not-exists to newly started watchers

* fix watcher to ensure events arrive in the expected order

* fix tests

* clang-format

* add test for multiple resource types

* fix xds_cluster_e2e_test

* Automated change: Fix sanity tests

* cleanup

* add federation tests

* clang-format

* remove now-unnecessary XdsCertificateProviderPluginMapInterface

* code review comments

* simplify XdsResourceType::Decode() API

* XdsClient: add unit tests for XdsClusterResourceType

* add XdsClient with gRPC bootstrap config

* add LB policy tests

* started adding CertificateProvider tests

* update for recent API changes

* fix merge bugs

* xDS resource validation: identify extensions by type_url instead of name

* fix build

* migrate to ValidationErrors

* add xds_common_types_test

* finish TLS tests and add LRS tests

* move ScopedExperimentalEnvVar to its own library and remove redundant e2e tests

* add circuit breaking and outlier detection tests

* add validation to outlier detection LB policy parsing

* clang-format

* Automated change: Fix sanity tests

* fix signedness

* fix sanity

* xDS: implement xds_wrr_locality LB policy and return xDS LB config from XdsClient

* fix unused parameter

* fix sanity

* fix test

* Automated change: Fix sanity tests

* fix aggregate cluster bug

* Automated change: Fix sanity tests

* absl::make_unique -> std::make_unique

* fix sanity

* fix sanity

* iwyu

* iwyu

* update code for XdsResourceTypeImpl changes

* change xDS LB policy registry to be non-global

* change xDS LB policy registry to use ValidationErrors

* bunch o' changes

* clang-format

* generate_projects

* add env var protection

* clang-format

* fix unused parameter error

* iwyu

* remove outdated TODOs

Co-authored-by: markdroth <markdroth@users.noreply.github.com>
revert-31481-reland-try
Mark D. Roth 2 years ago committed by GitHub
parent dfb66b089c
commit f29f861ed3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      CMakeLists.txt
  2. 5
      build_autogenerated.yaml
  3. 8
      src/core/ext/filters/client_channel/lb_policy/ring_hash/ring_hash.cc
  4. 5
      src/core/ext/xds/xds_bootstrap_grpc.h
  5. 168
      src/core/ext/xds/xds_cluster.cc
  6. 2
      src/core/ext/xds/xds_cluster.h
  7. 245
      src/core/ext/xds/xds_lb_policy_registry.cc
  8. 21
      src/core/ext/xds/xds_lb_policy_registry.h
  9. 3
      test/core/xds/BUILD
  10. 147
      test/core/xds/xds_cluster_resource_type_test.cc
  11. 541
      test/core/xds/xds_lb_policy_registry_test.cc

8
CMakeLists.txt generated

@ -20604,6 +20604,10 @@ add_executable(xds_cluster_resource_type_test
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/regex.grpc.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/regex.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/regex.grpc.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/round_robin.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/round_robin.grpc.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/round_robin.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/round_robin.grpc.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/string.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/string.grpc.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/string.pb.h
@ -20616,6 +20620,10 @@ add_executable(xds_cluster_resource_type_test
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/typed_struct.grpc.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/typed_struct.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/typed_struct.grpc.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/wrr_locality.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/wrr_locality.grpc.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/wrr_locality.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/wrr_locality.grpc.pb.h
test/core/xds/xds_cluster_resource_type_test.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc

@ -11308,7 +11308,8 @@ targets:
gtest: true
build: test
language: c++
headers: []
headers:
- test/core/util/scoped_env_var.h
src:
- src/proto/grpc/testing/xds/v3/address.proto
- src/proto/grpc/testing/xds/v3/aggregate_cluster.proto
@ -11320,9 +11321,11 @@ targets:
- src/proto/grpc/testing/xds/v3/outlier_detection.proto
- src/proto/grpc/testing/xds/v3/percent.proto
- src/proto/grpc/testing/xds/v3/regex.proto
- src/proto/grpc/testing/xds/v3/round_robin.proto
- src/proto/grpc/testing/xds/v3/string.proto
- src/proto/grpc/testing/xds/v3/tls.proto
- src/proto/grpc/testing/xds/v3/typed_struct.proto
- src/proto/grpc/testing/xds/v3/wrr_locality.proto
- test/core/xds/xds_cluster_resource_type_test.cc
deps:
- grpc_test_util

@ -82,8 +82,8 @@ UniqueTypeName RequestHashAttributeName() {
const JsonLoaderInterface* RingHashConfig::JsonLoader(const JsonArgs&) {
static const auto* loader =
JsonObjectLoader<RingHashConfig>()
.OptionalField("min_ring_size", &RingHashConfig::min_ring_size)
.OptionalField("max_ring_size", &RingHashConfig::max_ring_size)
.OptionalField("minRingSize", &RingHashConfig::min_ring_size)
.OptionalField("maxRingSize", &RingHashConfig::max_ring_size)
.Finish();
return loader;
}
@ -91,14 +91,14 @@ const JsonLoaderInterface* RingHashConfig::JsonLoader(const JsonArgs&) {
void RingHashConfig::JsonPostLoad(const Json&, const JsonArgs&,
ValidationErrors* errors) {
{
ValidationErrors::ScopedField field(errors, ".min_ring_size");
ValidationErrors::ScopedField field(errors, ".minRingSize");
if (!errors->FieldHasErrors() &&
(min_ring_size == 0 || min_ring_size > 8388608)) {
errors->AddError("must be in the range [1, 8388608]");
}
}
{
ValidationErrors::ScopedField field(errors, ".max_ring_size");
ValidationErrors::ScopedField field(errors, ".maxRingSize");
if (!errors->FieldHasErrors() &&
(max_ring_size == 0 || max_ring_size > 8388608)) {
errors->AddError("must be in the range [1, 8388608]");

@ -33,6 +33,7 @@
#include "src/core/ext/xds/xds_bootstrap.h"
#include "src/core/ext/xds/xds_cluster_specifier_plugin.h"
#include "src/core/ext/xds/xds_http_filters.h"
#include "src/core/ext/xds/xds_lb_policy_registry.h"
#include "src/core/lib/gprpp/validation_errors.h"
#include "src/core/lib/json/json.h"
#include "src/core/lib/json/json_args.h"
@ -157,6 +158,9 @@ class GrpcXdsBootstrap : public XdsBootstrap {
const {
return cluster_specifier_plugin_registry_;
}
const XdsLbPolicyRegistry& lb_policy_registry() const {
return lb_policy_registry_;
}
// Exposed for testing purposes only.
const std::map<std::string, GrpcAuthority>& authorities() const {
@ -172,6 +176,7 @@ class GrpcXdsBootstrap : public XdsBootstrap {
CertificateProviderStore::PluginDefinitionMap certificate_providers_;
XdsHttpFilterRegistry http_filter_registry_;
XdsClusterSpecifierPluginRegistry cluster_specifier_plugin_registry_;
XdsLbPolicyRegistry lb_policy_registry_;
};
} // namespace grpc_core

@ -48,16 +48,31 @@
#include <grpc/support/log.h>
#include "src/core/ext/xds/upb_utils.h"
#include "src/core/ext/xds/xds_client.h"
#include "src/core/ext/xds/xds_common_types.h"
#include "src/core/ext/xds/xds_lb_policy_registry.h"
#include "src/core/ext/xds/xds_resource_type.h"
#include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/gprpp/env.h"
#include "src/core/lib/gprpp/host_port.h"
#include "src/core/lib/gprpp/time.h"
#include "src/core/lib/gprpp/validation_errors.h"
#include "src/core/lib/load_balancing/lb_policy_registry.h"
#include "src/core/lib/matchers/matchers.h"
namespace grpc_core {
// TODO(roth): Remove once custom LB policy support is no longer experimental.
bool XdsCustomLbPolicyEnabled() {
auto value = GetEnv("GRPC_EXPERIMENTAL_XDS_CUSTOM_LB_CONFIG");
if (!value.has_value()) return false;
bool parsed_value;
bool parse_succeeded = gpr_parse_bool_value(value->c_str(), &parsed_value);
return parse_succeeded && parsed_value;
}
//
// XdsClusterResource
//
@ -270,58 +285,39 @@ void AggregateClusterParse(const XdsResourceType::DecodeContext& context,
}
}
absl::StatusOr<XdsClusterResource> CdsResourceParse(
const XdsResourceType::DecodeContext& context,
const envoy_config_cluster_v3_Cluster* cluster) {
XdsClusterResource cds_update;
ValidationErrors errors;
// Check the cluster discovery type.
if (envoy_config_cluster_v3_Cluster_type(cluster) ==
envoy_config_cluster_v3_Cluster_EDS) {
cds_update.cluster_type = XdsClusterResource::ClusterType::EDS;
EdsConfigParse(cluster, &cds_update, &errors);
} else if (envoy_config_cluster_v3_Cluster_type(cluster) ==
envoy_config_cluster_v3_Cluster_LOGICAL_DNS) {
cds_update.cluster_type = XdsClusterResource::ClusterType::LOGICAL_DNS;
LogicalDnsParse(cluster, &cds_update, &errors);
} else if (envoy_config_cluster_v3_Cluster_has_cluster_type(cluster)) {
ValidationErrors::ScopedField field(&errors, ".cluster_type");
const auto* custom_cluster_type =
envoy_config_cluster_v3_Cluster_cluster_type(cluster);
GPR_ASSERT(custom_cluster_type != nullptr);
ValidationErrors::ScopedField field2(&errors, ".typed_config");
const auto* typed_config =
envoy_config_cluster_v3_Cluster_CustomClusterType_typed_config(
custom_cluster_type);
if (typed_config == nullptr) {
errors.AddError("field not present");
} else {
absl::string_view type_url = absl::StripPrefix(
UpbStringToAbsl(google_protobuf_Any_type_url(typed_config)),
"type.googleapis.com/");
if (type_url != "envoy.extensions.clusters.aggregate.v3.ClusterConfig") {
ValidationErrors::ScopedField field(&errors, ".type_url");
errors.AddError(
absl::StrCat("unknown cluster_type extension: ", type_url));
} else {
cds_update.cluster_type = XdsClusterResource::ClusterType::AGGREGATE;
// Retrieve aggregate clusters.
ValidationErrors::ScopedField field(
&errors,
".value[envoy.extensions.clusters.aggregate.v3.ClusterConfig]");
absl::string_view serialized_config =
UpbStringToAbsl(google_protobuf_Any_value(typed_config));
AggregateClusterParse(context, serialized_config, &cds_update, &errors);
void ParseLbPolicyConfig(const XdsResourceType::DecodeContext& context,
const envoy_config_cluster_v3_Cluster* cluster,
XdsClusterResource* cds_update,
ValidationErrors* errors) {
// First, check the new load_balancing_policy field.
if (XdsCustomLbPolicyEnabled()) {
const auto* load_balancing_policy =
envoy_config_cluster_v3_Cluster_load_balancing_policy(cluster);
if (load_balancing_policy != nullptr) {
const auto& registry =
static_cast<const GrpcXdsBootstrap&>(context.client->bootstrap())
.lb_policy_registry();
ValidationErrors::ScopedField field(errors, ".load_balancing_policy");
const size_t original_error_count = errors->size();
cds_update->lb_policy_config = registry.ConvertXdsLbPolicyConfig(
context, load_balancing_policy, errors);
// If there were no conversion errors, validate that the converted config
// parses with the gRPC LB policy registry.
if (original_error_count == errors->size()) {
auto config =
CoreConfiguration::Get()
.lb_policy_registry()
.ParseLoadBalancingConfig(cds_update->lb_policy_config);
if (!config.ok()) errors->AddError(config.status().message());
}
return;
}
} else {
ValidationErrors::ScopedField field(&errors, ".type");
errors.AddError("unknown discovery type");
}
// Check the LB policy.
// Didn't find load_balancing_policy field, so fall back to the old
// lb_policy enum field.
if (envoy_config_cluster_v3_Cluster_lb_policy(cluster) ==
envoy_config_cluster_v3_Cluster_ROUND_ROBIN) {
cds_update.lb_policy_config = {
cds_update->lb_policy_config = {
Json::Object{
{"xds_wrr_locality_experimental",
Json::Object{
@ -342,50 +338,102 @@ absl::StatusOr<XdsClusterResource> CdsResourceParse(
uint64_t min_ring_size = 1024;
uint64_t max_ring_size = 8388608;
if (ring_hash_config != nullptr) {
ValidationErrors::ScopedField field(&errors, ".ring_hash_lb_config");
ValidationErrors::ScopedField field(errors, ".ring_hash_lb_config");
const google_protobuf_UInt64Value* uint64_value =
envoy_config_cluster_v3_Cluster_RingHashLbConfig_maximum_ring_size(
ring_hash_config);
if (uint64_value != nullptr) {
ValidationErrors::ScopedField field(&errors, ".maximum_ring_size");
ValidationErrors::ScopedField field(errors, ".maximum_ring_size");
max_ring_size = google_protobuf_UInt64Value_value(uint64_value);
if (max_ring_size > 8388608 || max_ring_size == 0) {
errors.AddError("must be in the range of 1 to 8388608");
errors->AddError("must be in the range of 1 to 8388608");
}
}
uint64_value =
envoy_config_cluster_v3_Cluster_RingHashLbConfig_minimum_ring_size(
ring_hash_config);
if (uint64_value != nullptr) {
ValidationErrors::ScopedField field(&errors, ".minimum_ring_size");
ValidationErrors::ScopedField field(errors, ".minimum_ring_size");
min_ring_size = google_protobuf_UInt64Value_value(uint64_value);
if (min_ring_size > 8388608 || min_ring_size == 0) {
errors.AddError("must be in the range of 1 to 8388608");
errors->AddError("must be in the range of 1 to 8388608");
}
if (min_ring_size > max_ring_size) {
errors.AddError("cannot be greater than maximum_ring_size");
errors->AddError("cannot be greater than maximum_ring_size");
}
}
if (envoy_config_cluster_v3_Cluster_RingHashLbConfig_hash_function(
ring_hash_config) !=
envoy_config_cluster_v3_Cluster_RingHashLbConfig_XX_HASH) {
ValidationErrors::ScopedField field(&errors, ".hash_function");
errors.AddError("invalid hash function");
ValidationErrors::ScopedField field(errors, ".hash_function");
errors->AddError("invalid hash function");
}
}
cds_update.lb_policy_config = {
cds_update->lb_policy_config = {
Json::Object{
{"ring_hash_experimental",
Json::Object{
{"min_ring_size", min_ring_size},
{"max_ring_size", max_ring_size},
{"minRingSize", min_ring_size},
{"maxRingSize", max_ring_size},
}},
},
};
} else {
ValidationErrors::ScopedField field(&errors, ".lb_policy");
errors.AddError("LB policy is not supported");
ValidationErrors::ScopedField field(errors, ".lb_policy");
errors->AddError("LB policy is not supported");
}
}
absl::StatusOr<XdsClusterResource> CdsResourceParse(
const XdsResourceType::DecodeContext& context,
const envoy_config_cluster_v3_Cluster* cluster) {
XdsClusterResource cds_update;
ValidationErrors errors;
// Check the cluster discovery type.
if (envoy_config_cluster_v3_Cluster_type(cluster) ==
envoy_config_cluster_v3_Cluster_EDS) {
cds_update.cluster_type = XdsClusterResource::ClusterType::EDS;
EdsConfigParse(cluster, &cds_update, &errors);
} else if (envoy_config_cluster_v3_Cluster_type(cluster) ==
envoy_config_cluster_v3_Cluster_LOGICAL_DNS) {
cds_update.cluster_type = XdsClusterResource::ClusterType::LOGICAL_DNS;
LogicalDnsParse(cluster, &cds_update, &errors);
} else if (envoy_config_cluster_v3_Cluster_has_cluster_type(cluster)) {
ValidationErrors::ScopedField field(&errors, ".cluster_type");
const auto* custom_cluster_type =
envoy_config_cluster_v3_Cluster_cluster_type(cluster);
GPR_ASSERT(custom_cluster_type != nullptr);
ValidationErrors::ScopedField field2(&errors, ".typed_config");
const auto* typed_config =
envoy_config_cluster_v3_Cluster_CustomClusterType_typed_config(
custom_cluster_type);
if (typed_config == nullptr) {
errors.AddError("field not present");
} else {
absl::string_view type_url = absl::StripPrefix(
UpbStringToAbsl(google_protobuf_Any_type_url(typed_config)),
"type.googleapis.com/");
if (type_url != "envoy.extensions.clusters.aggregate.v3.ClusterConfig") {
ValidationErrors::ScopedField field(&errors, ".type_url");
errors.AddError(
absl::StrCat("unknown cluster_type extension: ", type_url));
} else {
cds_update.cluster_type = XdsClusterResource::ClusterType::AGGREGATE;
// Retrieve aggregate clusters.
ValidationErrors::ScopedField field(
&errors,
".value[envoy.extensions.clusters.aggregate.v3.ClusterConfig]");
absl::string_view serialized_config =
UpbStringToAbsl(google_protobuf_Any_value(typed_config));
AggregateClusterParse(context, serialized_config, &cds_update, &errors);
}
}
} else {
ValidationErrors::ScopedField field(&errors, ".type");
errors.AddError("unknown discovery type");
}
// Check the LB policy.
ParseLbPolicyConfig(context, cluster, &cds_update, &errors);
// transport_socket
auto* transport_socket =
envoy_config_cluster_v3_Cluster_transport_socket(cluster);

@ -44,6 +44,8 @@
namespace grpc_core {
bool XdsCustomLbPolicyEnabled();
struct XdsClusterResource : public XdsResourceType::ResourceData {
enum ClusterType { EDS, LOGICAL_DNS, AGGREGATE };
ClusterType cluster_type;

@ -19,25 +19,19 @@
#include "src/core/ext/xds/xds_lb_policy_registry.h"
#include <stddef.h>
#include <stdint.h>
#include <string>
#include <utility>
#include <vector>
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/types/optional.h"
#include "absl/types/variant.h"
#include "envoy/config/core/v3/extension.upb.h"
#include "envoy/extensions/load_balancing_policies/ring_hash/v3/ring_hash.upb.h"
#include "envoy/extensions/load_balancing_policies/wrr_locality/v3/wrr_locality.upb.h"
#include "google/protobuf/any.upb.h"
#include "google/protobuf/wrappers.upb.h"
#include <grpc/support/log.h>
#include "src/core/ext/xds/upb_utils.h"
#include "src/core/ext/xds/xds_common_types.h"
#include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/gprpp/validation_errors.h"
@ -47,98 +41,117 @@ namespace grpc_core {
namespace {
class RoundRobinLbPolicyConfigFactory
: public XdsLbPolicyRegistry::ConfigFactory {
public:
Json::Object ConvertXdsLbPolicyConfig(
const XdsLbPolicyRegistry* /*registry*/,
const XdsResourceType::DecodeContext& /*context*/,
absl::string_view /*configuration*/, ValidationErrors* /*errors*/,
int /*recursion_depth*/) override {
return Json::Object{{"round_robin", Json::Object()}};
}
absl::string_view type() override { return Type(); }
static absl::string_view Type() {
return "envoy.extensions.load_balancing_policies.round_robin.v3.RoundRobin";
}
};
class RingHashLbPolicyConfigFactory
: public XdsLbPolicyRegistry::ConfigFactory {
public:
absl::StatusOr<Json::Object> ConvertXdsLbPolicyConfig(
Json::Object ConvertXdsLbPolicyConfig(
const XdsLbPolicyRegistry* /*registry*/,
const XdsResourceType::DecodeContext& context,
absl::string_view configuration, int /* recursion_depth */) override {
absl::string_view configuration, ValidationErrors* errors,
int /*recursion_depth*/) override {
const auto* resource =
envoy_extensions_load_balancing_policies_ring_hash_v3_RingHash_parse(
configuration.data(), configuration.size(), context.arena);
if (resource == nullptr) {
return absl::InvalidArgumentError(
"Can't decode RingHash loadbalancing policy");
errors->AddError("can't decode RingHash LB policy config");
return {};
}
if (envoy_extensions_load_balancing_policies_ring_hash_v3_RingHash_hash_function(
resource) !=
envoy_extensions_load_balancing_policies_ring_hash_v3_RingHash_XX_HASH) {
return absl::InvalidArgumentError(
"Invalid hash function provided for RingHash loadbalancing policy. "
"Only XX_HASH is supported.");
envoy_extensions_load_balancing_policies_ring_hash_v3_RingHash_XX_HASH &&
envoy_extensions_load_balancing_policies_ring_hash_v3_RingHash_hash_function(
resource) !=
envoy_extensions_load_balancing_policies_ring_hash_v3_RingHash_DEFAULT_HASH) {
ValidationErrors::ScopedField field(errors, ".hash_function");
errors->AddError("unsupported value (must be XX_HASH)");
}
Json::Object json;
const auto* min_ring_size =
envoy_extensions_load_balancing_policies_ring_hash_v3_RingHash_minimum_ring_size(
uint64_t max_ring_size = 8388608;
const auto* uint64_value =
envoy_extensions_load_balancing_policies_ring_hash_v3_RingHash_maximum_ring_size(
resource);
if (min_ring_size != nullptr) {
json.emplace("minRingSize",
google_protobuf_UInt64Value_value(min_ring_size));
if (uint64_value != nullptr) {
max_ring_size = google_protobuf_UInt64Value_value(uint64_value);
if (max_ring_size == 0 || max_ring_size > 8388608) {
ValidationErrors::ScopedField field(errors, ".maximum_ring_size");
errors->AddError("value must be in the range [1, 8388608]");
}
const auto* max_ring_size =
envoy_extensions_load_balancing_policies_ring_hash_v3_RingHash_maximum_ring_size(
}
uint64_t min_ring_size = 1024;
uint64_value =
envoy_extensions_load_balancing_policies_ring_hash_v3_RingHash_minimum_ring_size(
resource);
if (max_ring_size != nullptr) {
json.emplace("maxRingSize",
google_protobuf_UInt64Value_value(max_ring_size));
if (uint64_value != nullptr) {
min_ring_size = google_protobuf_UInt64Value_value(uint64_value);
ValidationErrors::ScopedField field(errors, ".minimum_ring_size");
if (min_ring_size == 0 || min_ring_size > 8388608) {
errors->AddError("value must be in the range [1, 8388608]");
}
return Json::Object{{"ring_hash_experimental", std::move(json)}};
if (min_ring_size > max_ring_size) {
errors->AddError("cannot be greater than maximum_ring_size");
}
absl::string_view type() override { return Type(); }
static absl::string_view Type() {
return "envoy.extensions.load_balancing_policies.ring_hash.v3.RingHash";
}
};
class RoundRobinLbPolicyConfigFactory
: public XdsLbPolicyRegistry::ConfigFactory {
public:
absl::StatusOr<Json::Object> ConvertXdsLbPolicyConfig(
const XdsResourceType::DecodeContext& /* context */,
absl::string_view /* configuration */,
int /* recursion_depth */) override {
return Json::Object{{"round_robin", Json::Object()}};
return Json::Object{
{"ring_hash_experimental",
Json::Object{
{"minRingSize", min_ring_size},
{"maxRingSize", max_ring_size},
}},
};
}
absl::string_view type() override { return Type(); }
static absl::string_view Type() {
return "envoy.extensions.load_balancing_policies.round_robin.v3.RoundRobin";
return "envoy.extensions.load_balancing_policies.ring_hash.v3.RingHash";
}
};
class WrrLocalityLbPolicyConfigFactory
: public XdsLbPolicyRegistry::ConfigFactory {
public:
absl::StatusOr<Json::Object> ConvertXdsLbPolicyConfig(
Json::Object ConvertXdsLbPolicyConfig(
const XdsLbPolicyRegistry* registry,
const XdsResourceType::DecodeContext& context,
absl::string_view configuration, int recursion_depth) override {
absl::string_view configuration, ValidationErrors* errors,
int recursion_depth) override {
const auto* resource =
envoy_extensions_load_balancing_policies_wrr_locality_v3_WrrLocality_parse(
configuration.data(), configuration.size(), context.arena);
if (resource == nullptr) {
return absl::InvalidArgumentError(
"Can't decode WrrLocality loadbalancing policy");
errors->AddError("can't decode WrrLocality LB policy config");
return {};
}
ValidationErrors::ScopedField field(errors, ".endpoint_picking_policy");
const auto* endpoint_picking_policy =
envoy_extensions_load_balancing_policies_wrr_locality_v3_WrrLocality_endpoint_picking_policy(
resource);
if (endpoint_picking_policy == nullptr) {
return absl::InvalidArgumentError(
"WrrLocality: endpoint_picking_policy not found");
}
auto child_policy = XdsLbPolicyRegistry::ConvertXdsLbPolicyConfig(
context, endpoint_picking_policy, recursion_depth + 1);
if (!child_policy.ok()) {
return absl::InvalidArgumentError(
absl::StrCat("Error parsing WrrLocality load balancing policy: ",
child_policy.status().message()));
errors->AddError("field not present");
return {};
}
auto child_policy = registry->ConvertXdsLbPolicyConfig(
context, endpoint_picking_policy, errors, recursion_depth + 1);
return Json::Object{
{"xds_wrr_locality_experimental",
Json::Object{{"child_policy", *std::move(child_policy)}}}};
Json::Object{{"childPolicy", std::move(child_policy)}}}};
}
absl::string_view type() override { return Type(); }
@ -155,96 +168,76 @@ class WrrLocalityLbPolicyConfigFactory
// XdsLbPolicyRegistry
//
absl::StatusOr<Json::Array> XdsLbPolicyRegistry::ConvertXdsLbPolicyConfig(
XdsLbPolicyRegistry::XdsLbPolicyRegistry() {
policy_config_factories_.emplace(
RingHashLbPolicyConfigFactory::Type(),
std::make_unique<RingHashLbPolicyConfigFactory>());
policy_config_factories_.emplace(
RoundRobinLbPolicyConfigFactory::Type(),
std::make_unique<RoundRobinLbPolicyConfigFactory>());
policy_config_factories_.emplace(
WrrLocalityLbPolicyConfigFactory::Type(),
std::make_unique<WrrLocalityLbPolicyConfigFactory>());
}
Json::Array XdsLbPolicyRegistry::ConvertXdsLbPolicyConfig(
const XdsResourceType::DecodeContext& context,
const envoy_config_cluster_v3_LoadBalancingPolicy* lb_policy,
int recursion_depth) {
ValidationErrors* errors, int recursion_depth) const {
constexpr int kMaxRecursionDepth = 16;
if (recursion_depth >= kMaxRecursionDepth) {
return absl::InvalidArgumentError(
absl::StrFormat("LoadBalancingPolicy configuration has a recursion "
"depth of more than %d.",
kMaxRecursionDepth));
errors->AddError(
absl::StrCat("exceeded max recursion depth of ", kMaxRecursionDepth));
return {};
}
const size_t original_error_size = errors->size();
size_t size = 0;
const auto* policies =
envoy_config_cluster_v3_LoadBalancingPolicy_policies(lb_policy, &size);
for (size_t i = 0; i < size; ++i) {
absl::StatusOr<Json::Object> policy;
ValidationErrors::ScopedField field(
errors, absl::StrCat(".policies[", i, "].typed_extension_config"));
const auto* typed_extension_config =
envoy_config_cluster_v3_LoadBalancingPolicy_Policy_typed_extension_config(
policies[i]);
if (typed_extension_config == nullptr) {
return absl::InvalidArgumentError(
"Error parsing LoadBalancingPolicy::Policy - Missing "
"typed_extension_config field");
errors->AddError("field not present");
return {};
}
ValidationErrors::ScopedField field2(errors, ".typed_config");
const auto* typed_config =
envoy_config_core_v3_TypedExtensionConfig_typed_config(
typed_extension_config);
if (typed_config == nullptr) {
return absl::InvalidArgumentError(
"Error parsing LoadBalancingPolicy::Policy - Missing "
"TypedExtensionConfig::typed_config field");
}
ValidationErrors errors;
auto extension = ExtractXdsExtension(context, typed_config, &errors);
if (!errors.ok()) {
return errors.status(
"Error parsing "
"LoadBalancingPolicy::Policy::TypedExtensionConfig::typed_config");
}
GPR_ASSERT(extension.has_value());
absl::string_view value =
UpbStringToAbsl(google_protobuf_Any_value(typed_config));
auto config_factory_it =
Get()->policy_config_factories_.find(extension->type);
if (config_factory_it != Get()->policy_config_factories_.end()) {
policy = config_factory_it->second->ConvertXdsLbPolicyConfig(
context, value, recursion_depth);
if (!policy.ok()) {
return absl::InvalidArgumentError(
absl::StrCat("Error parsing "
"LoadBalancingPolicy::Policy::TypedExtensionConfig::"
"typed_config to JSON: ",
policy.status().message()));
}
} else if (absl::holds_alternative<Json>(extension->value)) {
// Custom lb policy config
if (!CoreConfiguration::Get()
.lb_policy_registry()
.LoadBalancingPolicyExists(extension->type, nullptr)) {
// Skip unsupported custom lb policy.
continue;
}
errors->AddError("field not present");
return {};
}
auto extension = ExtractXdsExtension(context, typed_config, errors);
if (!extension.has_value()) return {};
// Check for registered LB policy type.
absl::string_view* serialized_value =
absl::get_if<absl::string_view>(&extension->value);
if (serialized_value != nullptr) {
auto config_factory_it = policy_config_factories_.find(extension->type);
if (config_factory_it != policy_config_factories_.end()) {
return Json::Array{config_factory_it->second->ConvertXdsLbPolicyConfig(
this, context, *serialized_value, errors, recursion_depth)};
}
}
// Check for custom LB policy type.
Json* json = absl::get_if<Json>(&extension->value);
policy = Json::Object{{std::string(extension->type), std::move(*json)}};
} else {
// Unsupported type. Skipping entry.
continue;
if (json != nullptr &&
CoreConfiguration::Get().lb_policy_registry().LoadBalancingPolicyExists(
extension->type, nullptr)) {
return Json::Array{
Json::Object{{std::string(extension->type), std::move(*json)}}};
}
return Json::Array{std::move(policy.value())};
// Unsupported type. Continue to next entry.
}
return absl::InvalidArgumentError(
"No supported load balancing policy config found.");
}
XdsLbPolicyRegistry::XdsLbPolicyRegistry() {
policy_config_factories_.emplace(
RingHashLbPolicyConfigFactory::Type(),
std::make_unique<RingHashLbPolicyConfigFactory>());
policy_config_factories_.emplace(
RoundRobinLbPolicyConfigFactory::Type(),
std::make_unique<RoundRobinLbPolicyConfigFactory>());
policy_config_factories_.emplace(
WrrLocalityLbPolicyConfigFactory::Type(),
std::make_unique<WrrLocalityLbPolicyConfigFactory>());
}
XdsLbPolicyRegistry* XdsLbPolicyRegistry::Get() {
// This is thread-safe since C++11
static XdsLbPolicyRegistry* instance = new XdsLbPolicyRegistry();
return instance;
if (original_error_size == errors->size()) {
errors->AddError("no supported load balancing policy config found");
}
return {};
}
} // namespace grpc_core

@ -22,11 +22,11 @@
#include <map>
#include <memory>
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "envoy/config/cluster/v3/cluster.upb.h"
#include "src/core/ext/xds/xds_resource_type.h"
#include "src/core/lib/gprpp/validation_errors.h"
#include "src/core/lib/json/json.h"
namespace grpc_core {
@ -37,29 +37,28 @@ class XdsLbPolicyRegistry {
public:
class ConfigFactory {
public:
virtual ~ConfigFactory() {}
virtual absl::StatusOr<Json::Object> ConvertXdsLbPolicyConfig(
virtual ~ConfigFactory() = default;
virtual Json::Object ConvertXdsLbPolicyConfig(
const XdsLbPolicyRegistry* registry,
const XdsResourceType::DecodeContext& context,
absl::string_view configuration, int recursion_depth) = 0;
absl::string_view configuration, ValidationErrors* errors,
int recursion_depth) = 0;
virtual absl::string_view type() = 0;
};
XdsLbPolicyRegistry();
// Converts an xDS cluster load balancing policy message to gRPC's JSON
// format. An error is returned if none of the lb policies in the list are
// supported, or if a supported lb policy configuration conversion fails. \a
// recursion_depth indicates the current depth of the tree if lb_policy
// configuration recursively holds other lb policies.
static absl::StatusOr<Json::Array> ConvertXdsLbPolicyConfig(
Json::Array ConvertXdsLbPolicyConfig(
const XdsResourceType::DecodeContext& context,
const envoy_config_cluster_v3_LoadBalancingPolicy* lb_policy,
int recursion_depth = 0);
ValidationErrors* errors, int recursion_depth = 0) const;
private:
XdsLbPolicyRegistry();
static XdsLbPolicyRegistry* Get();
// A map of config factories that goes from the type of the lb policy config
// to the config factory.
std::map<absl::string_view /* Owned by ConfigFactory */,

@ -229,9 +229,12 @@ grpc_cc_test(
"//src/core:grpc_xds_client",
"//src/proto/grpc/testing/xds/v3:aggregate_cluster_proto",
"//src/proto/grpc/testing/xds/v3:cluster_proto",
"//src/proto/grpc/testing/xds/v3:round_robin_proto",
"//src/proto/grpc/testing/xds/v3:tls_proto",
"//src/proto/grpc/testing/xds/v3:typed_struct_proto",
"//src/proto/grpc/testing/xds/v3:wrr_locality_proto",
"//test/core/util:grpc_test_util",
"//test/core/util:scoped_env_var",
],
)

@ -51,14 +51,21 @@
#include "src/proto/grpc/testing/xds/v3/cluster.pb.h"
#include "src/proto/grpc/testing/xds/v3/config_source.pb.h"
#include "src/proto/grpc/testing/xds/v3/endpoint.pb.h"
#include "src/proto/grpc/testing/xds/v3/extension.pb.h"
#include "src/proto/grpc/testing/xds/v3/outlier_detection.pb.h"
#include "src/proto/grpc/testing/xds/v3/round_robin.pb.h"
#include "src/proto/grpc/testing/xds/v3/tls.pb.h"
#include "src/proto/grpc/testing/xds/v3/typed_struct.pb.h"
#include "src/proto/grpc/testing/xds/v3/wrr_locality.pb.h"
#include "test/core/util/scoped_env_var.h"
#include "test/core/util/test_config.h"
using envoy::config::cluster::v3::Cluster;
using envoy::extensions::clusters::aggregate::v3::ClusterConfig;
using envoy::extensions::load_balancing_policies::round_robin::v3::RoundRobin;
using envoy::extensions::load_balancing_policies::wrr_locality::v3::WrrLocality;
using envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext;
using xds::type::v3::TypedStruct;
namespace grpc_core {
namespace testing {
@ -568,7 +575,7 @@ TEST_F(ClusterTypeTest, AggregateClusterUnparseableProto) {
using LbPolicyTest = XdsClusterTest;
TEST_F(LbPolicyTest, LbPolicyRingHash) {
TEST_F(LbPolicyTest, EnumLbPolicyRingHash) {
Cluster cluster;
cluster.set_name("foo");
cluster.set_type(cluster.EDS);
@ -585,10 +592,10 @@ TEST_F(LbPolicyTest, LbPolicyRingHash) {
auto& resource = static_cast<XdsClusterResource&>(**decode_result.resource);
EXPECT_EQ(Json{resource.lb_policy_config}.Dump(),
"[{\"ring_hash_experimental\":{"
"\"max_ring_size\":8388608,\"min_ring_size\":1024}}]");
"\"maxRingSize\":8388608,\"minRingSize\":1024}}]");
}
TEST_F(LbPolicyTest, LbPolicyRingHashSetMinAndMaxRingSize) {
TEST_F(LbPolicyTest, EnumLbPolicyRingHashSetMinAndMaxRingSize) {
Cluster cluster;
cluster.set_name("foo");
cluster.set_type(cluster.EDS);
@ -608,10 +615,10 @@ TEST_F(LbPolicyTest, LbPolicyRingHashSetMinAndMaxRingSize) {
auto& resource = static_cast<XdsClusterResource&>(**decode_result.resource);
EXPECT_EQ(Json{resource.lb_policy_config}.Dump(),
"[{\"ring_hash_experimental\":{"
"\"max_ring_size\":4096,\"min_ring_size\":2048}}]");
"\"maxRingSize\":4096,\"minRingSize\":2048}}]");
}
TEST_F(LbPolicyTest, LbPolicyRingHashSetMinAndMaxRingSizeToZero) {
TEST_F(LbPolicyTest, EnumLbPolicyRingHashSetMinAndMaxRingSizeToZero) {
Cluster cluster;
cluster.set_name("foo");
cluster.set_type(cluster.EDS);
@ -638,7 +645,7 @@ TEST_F(LbPolicyTest, LbPolicyRingHashSetMinAndMaxRingSizeToZero) {
<< decode_result.resource.status();
}
TEST_F(LbPolicyTest, LbPolicyRingHashSetMinAndMaxRingSizeTooLarge) {
TEST_F(LbPolicyTest, EnumLbPolicyRingHashSetMinAndMaxRingSizeTooLarge) {
Cluster cluster;
cluster.set_name("foo");
cluster.set_type(cluster.EDS);
@ -665,7 +672,7 @@ TEST_F(LbPolicyTest, LbPolicyRingHashSetMinAndMaxRingSizeTooLarge) {
<< decode_result.resource.status();
}
TEST_F(LbPolicyTest, LbPolicyRingHashSetMinRingSizeLargerThanMaxRingSize) {
TEST_F(LbPolicyTest, EnumLbPolicyRingHashSetMinRingSizeLargerThanMaxRingSize) {
Cluster cluster;
cluster.set_name("foo");
cluster.set_type(cluster.EDS);
@ -690,7 +697,7 @@ TEST_F(LbPolicyTest, LbPolicyRingHashSetMinRingSizeLargerThanMaxRingSize) {
<< decode_result.resource.status();
}
TEST_F(LbPolicyTest, LbPolicyRingHashUnsupportedHashFunction) {
TEST_F(LbPolicyTest, EnumLbPolicyRingHashUnsupportedHashFunction) {
Cluster cluster;
cluster.set_name("foo");
cluster.set_type(cluster.EDS);
@ -714,7 +721,7 @@ TEST_F(LbPolicyTest, LbPolicyRingHashUnsupportedHashFunction) {
<< decode_result.resource.status();
}
TEST_F(LbPolicyTest, UnsupportedPolicy) {
TEST_F(LbPolicyTest, EnumUnsupportedPolicy) {
Cluster cluster;
cluster.set_name("foo");
cluster.set_type(cluster.EDS);
@ -735,6 +742,128 @@ TEST_F(LbPolicyTest, UnsupportedPolicy) {
<< decode_result.resource.status();
}
TEST_F(LbPolicyTest, LoadBalancingPolicyField) {
ScopedExperimentalEnvVar env_var("GRPC_EXPERIMENTAL_XDS_CUSTOM_LB_CONFIG");
Cluster cluster;
cluster.set_name("foo");
cluster.set_type(cluster.EDS);
cluster.mutable_eds_cluster_config()->mutable_eds_config()->mutable_self();
WrrLocality wrr_locality;
wrr_locality.mutable_endpoint_picking_policy()
->add_policies()
->mutable_typed_extension_config()
->mutable_typed_config()
->PackFrom(RoundRobin());
cluster.mutable_load_balancing_policy()
->add_policies()
->mutable_typed_extension_config()
->mutable_typed_config()
->PackFrom(wrr_locality);
cluster.set_lb_policy(cluster.MAGLEV); // Will be ignored.
std::string serialized_resource;
ASSERT_TRUE(cluster.SerializeToString(&serialized_resource));
auto* resource_type = XdsClusterResourceType::Get();
auto decode_result =
resource_type->Decode(decode_context_, serialized_resource);
ASSERT_TRUE(decode_result.resource.ok()) << decode_result.resource.status();
ASSERT_TRUE(decode_result.name.has_value());
EXPECT_EQ(*decode_result.name, "foo");
auto& resource = static_cast<XdsClusterResource&>(**decode_result.resource);
EXPECT_EQ(Json{resource.lb_policy_config}.Dump(),
"[{\"xds_wrr_locality_experimental\":{"
"\"childPolicy\":[{\"round_robin\":{}}]}}]");
}
TEST_F(LbPolicyTest, LoadBalancingPolicyFieldIgnoredUnlessEnabled) {
Cluster cluster;
cluster.set_name("foo");
cluster.set_type(cluster.EDS);
cluster.mutable_eds_cluster_config()->mutable_eds_config()->mutable_self();
// New field says to use round_robin, but the old enum field says to use
// ring_hash. The env var is not enabled, so we use the old enum field.
cluster.mutable_load_balancing_policy()
->add_policies()
->mutable_typed_extension_config()
->mutable_typed_config()
->PackFrom(RoundRobin());
cluster.set_lb_policy(cluster.RING_HASH);
std::string serialized_resource;
ASSERT_TRUE(cluster.SerializeToString(&serialized_resource));
auto* resource_type = XdsClusterResourceType::Get();
auto decode_result =
resource_type->Decode(decode_context_, serialized_resource);
ASSERT_TRUE(decode_result.resource.ok()) << decode_result.resource.status();
ASSERT_TRUE(decode_result.name.has_value());
EXPECT_EQ(*decode_result.name, "foo");
auto& resource = static_cast<XdsClusterResource&>(**decode_result.resource);
EXPECT_EQ(Json{resource.lb_policy_config}.Dump(),
"[{\"ring_hash_experimental\":{"
"\"maxRingSize\":8388608,\"minRingSize\":1024}}]");
}
// This tests that we're passing along errors from XdsLbPolicyRegistry.
// A complete list of error cases for that class is in
// xds_lb_policy_registry_test.
TEST_F(LbPolicyTest, XdsLbPolicyRegistryConversionFails) {
ScopedExperimentalEnvVar env_var("GRPC_EXPERIMENTAL_XDS_CUSTOM_LB_CONFIG");
Cluster cluster;
cluster.set_name("foo");
cluster.set_type(cluster.EDS);
cluster.mutable_eds_cluster_config()->mutable_eds_config()->mutable_self();
cluster.mutable_load_balancing_policy()
->add_policies()
->mutable_typed_extension_config()
->mutable_typed_config()
->PackFrom(WrrLocality());
std::string serialized_resource;
ASSERT_TRUE(cluster.SerializeToString(&serialized_resource));
auto* resource_type = XdsClusterResourceType::Get();
auto decode_result =
resource_type->Decode(decode_context_, serialized_resource);
ASSERT_TRUE(decode_result.name.has_value());
EXPECT_EQ(*decode_result.name, "foo");
EXPECT_EQ(decode_result.resource.status().code(),
absl::StatusCode::kInvalidArgument);
EXPECT_EQ(decode_result.resource.status().message(),
"errors validating Cluster resource: ["
"field:load_balancing_policy.policies[0].typed_extension_config"
".typed_config.value[envoy.extensions.load_balancing_policies"
".wrr_locality.v3.WrrLocality].endpoint_picking_policy "
"error:field not present]")
<< decode_result.resource.status();
}
TEST_F(LbPolicyTest, ConvertedCustomPolicyFailsValidation) {
ScopedExperimentalEnvVar env_var("GRPC_EXPERIMENTAL_XDS_CUSTOM_LB_CONFIG");
Cluster cluster;
cluster.set_name("foo");
cluster.set_type(cluster.EDS);
cluster.mutable_eds_cluster_config()->mutable_eds_config()->mutable_self();
TypedStruct typed_struct;
typed_struct.set_type_url(
"type.googleapis.com/xds_wrr_locality_experimental");
cluster.mutable_load_balancing_policy()
->add_policies()
->mutable_typed_extension_config()
->mutable_typed_config()
->PackFrom(typed_struct);
std::string serialized_resource;
ASSERT_TRUE(cluster.SerializeToString(&serialized_resource));
auto* resource_type = XdsClusterResourceType::Get();
auto decode_result =
resource_type->Decode(decode_context_, serialized_resource);
ASSERT_TRUE(decode_result.name.has_value());
EXPECT_EQ(*decode_result.name, "foo");
EXPECT_EQ(decode_result.resource.status().code(),
absl::StatusCode::kInvalidArgument);
EXPECT_EQ(decode_result.resource.status().message(),
"errors validating Cluster resource: ["
"field:load_balancing_policy "
"error:errors validating xds_wrr_locality LB policy config: ["
"field:childPolicy error:field not present]]")
<< decode_result.resource.status();
}
//
// TLS config tests
//

@ -27,6 +27,7 @@
#include <google/protobuf/wrappers.pb.h>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "upb/def.hpp"
@ -39,6 +40,7 @@
#include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/gprpp/orphanable.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/gprpp/validation_errors.h"
#include "src/core/lib/load_balancing/lb_policy.h"
#include "src/core/lib/load_balancing/lb_policy_factory.h"
#include "src/core/lib/load_balancing/lb_policy_registry.h"
@ -47,10 +49,8 @@
#include "src/proto/grpc/testing/xds/v3/ring_hash.pb.h"
#include "src/proto/grpc/testing/xds/v3/round_robin.pb.h"
#include "src/proto/grpc/testing/xds/v3/typed_struct.pb.h"
#include "src/proto/grpc/testing/xds/v3/udpa_typed_struct.pb.h"
#include "src/proto/grpc/testing/xds/v3/wrr_locality.pb.h"
#include "test/core/util/test_config.h"
#include "test/cpp/util/config_grpc_cli.h"
namespace grpc_core {
namespace testing {
@ -66,7 +66,7 @@ using ::xds::type::v3::TypedStruct;
// Uses XdsLbPolicyRegistry to convert
// envoy::config::cluster::v3::LoadBalancingPolicy to gRPC's JSON form.
absl::StatusOr<Json::Array> ConvertXdsPolicy(LoadBalancingPolicyProto policy) {
absl::StatusOr<std::string> ConvertXdsPolicy(LoadBalancingPolicyProto policy) {
std::string serialized_policy = policy.SerializeAsString();
upb::Arena arena;
upb::SymbolTable symtab;
@ -75,117 +75,174 @@ absl::StatusOr<Json::Array> ConvertXdsPolicy(LoadBalancingPolicyProto policy) {
nullptr, symtab.ptr(), arena.ptr()};
auto* upb_policy = envoy_config_cluster_v3_LoadBalancingPolicy_parse(
serialized_policy.data(), serialized_policy.size(), arena.ptr());
return XdsLbPolicyRegistry::ConvertXdsLbPolicyConfig(context, upb_policy);
ValidationErrors errors;
ValidationErrors::ScopedField field(&errors, ".load_balancing_policy");
auto config = XdsLbPolicyRegistry().ConvertXdsLbPolicyConfig(
context, upb_policy, &errors);
if (!errors.ok()) return errors.status("validation errors");
EXPECT_EQ(config.size(), 1);
return Json{config[0]}.Dump();
}
TEST(XdsLbPolicyRegistryTest, EmptyLoadBalancingPolicy) {
auto result = ConvertXdsPolicy(LoadBalancingPolicyProto());
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(
std::string(result.status().message()),
::testing::HasSubstr("No supported load balancing policy config found"));
}
// A gRPC LB policy factory for a custom policy. None of the methods
// will actually be used; we just need it to be present in the gRPC LB
// policy registry.
class CustomLbPolicyFactory : public LoadBalancingPolicyFactory {
public:
OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy(
LoadBalancingPolicy::Args /*args*/) const override {
GPR_ASSERT(false);
return nullptr;
}
TEST(XdsLbPolicyRegistryTest, UnsupportedBuiltinType) {
absl::string_view name() const override { return "test.CustomLb"; }
absl::StatusOr<RefCountedPtr<LoadBalancingPolicy::Config>>
ParseLoadBalancingConfig(const Json& /*json*/) const override {
return nullptr;
}
};
//
// RoundRobin
//
TEST(RoundRobin, Basic) {
LoadBalancingPolicyProto policy;
auto* lb_policy = policy.add_policies();
lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
LoadBalancingPolicyProto());
RoundRobin());
auto result = ConvertXdsPolicy(policy);
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(
std::string(result.status().message()),
::testing::HasSubstr("No supported load balancing policy config found"));
ASSERT_TRUE(result.ok()) << result.status();
EXPECT_EQ(*result, "{\"round_robin\":{}}");
}
TEST(XdsLbPolicyRegistryTest, MissingTypedExtensionConfig) {
//
// RingHash
//
TEST(RingHashConfig, DefaultConfig) {
LoadBalancingPolicyProto policy;
policy.add_policies();
policy.add_policies()
->mutable_typed_extension_config()
->mutable_typed_config()
->PackFrom(RingHash());
auto result = ConvertXdsPolicy(policy);
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(std::string(result.status().message()),
::testing::HasSubstr("Error parsing LoadBalancingPolicy::Policy "
"- Missing typed_extension_config field"));
ASSERT_TRUE(result.ok()) << result.status();
EXPECT_EQ(*result,
"{\"ring_hash_experimental\":{"
"\"maxRingSize\":8388608,\"minRingSize\":1024}}");
}
TEST(XdsLbPolicyRegistryTest, MissingTypedConfig) {
TEST(RingHashConfig, FieldsExplicitlySet) {
RingHash ring_hash;
ring_hash.set_hash_function(RingHash::XX_HASH);
ring_hash.mutable_minimum_ring_size()->set_value(1234);
ring_hash.mutable_maximum_ring_size()->set_value(4567);
LoadBalancingPolicyProto policy;
auto* lb_policy = policy.add_policies();
lb_policy->mutable_typed_extension_config();
policy.add_policies()
->mutable_typed_extension_config()
->mutable_typed_config()
->PackFrom(ring_hash);
auto result = ConvertXdsPolicy(policy);
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(
std::string(result.status().message()),
::testing::HasSubstr("Error parsing LoadBalancingPolicy::Policy - "
"Missing TypedExtensionConfig::typed_config field"));
ASSERT_TRUE(result.ok()) << result.status();
EXPECT_EQ(*result,
"{\"ring_hash_experimental\":{"
"\"maxRingSize\":4567,\"minRingSize\":1234}}");
}
TEST(XdsLbPolicyRegistryTest, RingHashInvalidHash) {
TEST(RingHashConfig, InvalidHashFunction) {
RingHash ring_hash;
ring_hash.set_hash_function(RingHash::DEFAULT_HASH);
ring_hash.set_hash_function(RingHash::MURMUR_HASH_2);
LoadBalancingPolicyProto policy;
auto* lb_policy = policy.add_policies();
lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
ring_hash);
policy.add_policies()
->mutable_typed_extension_config()
->mutable_typed_config()
->PackFrom(ring_hash);
auto result = ConvertXdsPolicy(policy);
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(
std::string(result.status().message()),
::testing::HasSubstr("Invalid hash function provided for RingHash "
"loadbalancing policy. Only XX_HASH is supported"));
EXPECT_EQ(result.status().message(),
"validation errors: ["
"field:load_balancing_policy.policies[0].typed_extension_config"
".typed_config.value[envoy.extensions.load_balancing_policies"
".ring_hash.v3.RingHash].hash_function "
"error:unsupported value (must be XX_HASH)]")
<< result.status();
}
TEST(XdsLbPolicyRegistryTest, RingHashRingSizeDefaults) {
TEST(RingHashConfig, RingSizesTooHigh) {
RingHash ring_hash;
ring_hash.set_hash_function(RingHash::XX_HASH);
ring_hash.mutable_minimum_ring_size()->set_value(8388609);
ring_hash.mutable_maximum_ring_size()->set_value(8388609);
LoadBalancingPolicyProto policy;
auto* lb_policy = policy.add_policies();
lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
ring_hash);
policy.add_policies()
->mutable_typed_extension_config()
->mutable_typed_config()
->PackFrom(ring_hash);
auto result = ConvertXdsPolicy(policy);
EXPECT_TRUE(result.ok());
EXPECT_EQ(result->size(), 1);
EXPECT_EQ((*result)[0], Json::Parse("{"
"\"ring_hash_experimental\": {"
"}}")
.value());
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(result.status().message(),
"validation errors: ["
"field:load_balancing_policy.policies[0].typed_extension_config"
".typed_config.value[envoy.extensions.load_balancing_policies"
".ring_hash.v3.RingHash].maximum_ring_size "
"error:value must be in the range [1, 8388608]; "
"field:load_balancing_policy.policies[0].typed_extension_config"
".typed_config.value[envoy.extensions.load_balancing_policies"
".ring_hash.v3.RingHash].minimum_ring_size "
"error:value must be in the range [1, 8388608]]")
<< result.status();
}
TEST(XdsLbPolicyRegistryTest, RingHashRingSizeCustom) {
TEST(RingHashConfig, RingSizesTooLow) {
RingHash ring_hash;
ring_hash.set_hash_function(RingHash::XX_HASH);
ring_hash.mutable_minimum_ring_size()->set_value(1234);
ring_hash.mutable_maximum_ring_size()->set_value(4567);
ring_hash.mutable_minimum_ring_size()->set_value(0);
ring_hash.mutable_maximum_ring_size()->set_value(0);
LoadBalancingPolicyProto policy;
auto* lb_policy = policy.add_policies();
lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
ring_hash);
policy.add_policies()
->mutable_typed_extension_config()
->mutable_typed_config()
->PackFrom(ring_hash);
auto result = ConvertXdsPolicy(policy);
EXPECT_TRUE(result.ok());
EXPECT_EQ(result->size(), 1);
EXPECT_EQ((*result)[0], Json::Parse("{"
"\"ring_hash_experimental\": {"
" \"minRingSize\": 1234,"
" \"maxRingSize\": 4567"
"}}")
.value());
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(result.status().message(),
"validation errors: ["
"field:load_balancing_policy.policies[0].typed_extension_config"
".typed_config.value[envoy.extensions.load_balancing_policies"
".ring_hash.v3.RingHash].maximum_ring_size "
"error:value must be in the range [1, 8388608]; "
"field:load_balancing_policy.policies[0].typed_extension_config"
".typed_config.value[envoy.extensions.load_balancing_policies"
".ring_hash.v3.RingHash].minimum_ring_size "
"error:value must be in the range [1, 8388608]]")
<< result.status();
}
TEST(XdsLbPolicyRegistryTest, RoundRobin) {
TEST(RingHashConfig, MinRingSizeGreaterThanMaxRingSize) {
RingHash ring_hash;
ring_hash.mutable_minimum_ring_size()->set_value(1000);
ring_hash.mutable_maximum_ring_size()->set_value(999);
LoadBalancingPolicyProto policy;
auto* lb_policy = policy.add_policies();
lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
RoundRobin());
policy.add_policies()
->mutable_typed_extension_config()
->mutable_typed_config()
->PackFrom(ring_hash);
auto result = ConvertXdsPolicy(policy);
EXPECT_TRUE(result.ok());
EXPECT_EQ(result->size(), 1);
EXPECT_EQ((*result)[0], Json::Parse("{"
"\"round_robin\": {}"
"}")
.value());
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(result.status().message(),
"validation errors: ["
"field:load_balancing_policy.policies[0].typed_extension_config"
".typed_config.value[envoy.extensions.load_balancing_policies"
".ring_hash.v3.RingHash].minimum_ring_size "
"error:cannot be greater than maximum_ring_size]")
<< result.status();
}
TEST(XdsLbPolicyRegistryTest, WrrLocality) {
//
// WrrLocality
//
TEST(WrrLocality, RoundRobinChild) {
WrrLocality wrr_locality;
wrr_locality.mutable_endpoint_picking_policy()
->add_policies()
@ -197,52 +254,78 @@ TEST(XdsLbPolicyRegistryTest, WrrLocality) {
lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
wrr_locality);
auto result = ConvertXdsPolicy(policy);
EXPECT_TRUE(result.ok());
EXPECT_EQ(result->size(), 1);
EXPECT_EQ((*result)[0], Json::Parse("{"
"\"xds_wrr_locality_experimental\": {"
" \"child_policy\": [{"
" \"round_robin\": {}"
" }]"
"}}")
.value());
ASSERT_TRUE(result.ok()) << result.status();
EXPECT_EQ(*result,
"{\"xds_wrr_locality_experimental\":{"
"\"childPolicy\":[{\"round_robin\":{}}]}}");
}
TEST(XdsLbPolicyRegistryTest, WrrLocalityMissingEndpointPickingPolicy) {
TEST(WrrLocality, MissingEndpointPickingPolicy) {
LoadBalancingPolicyProto policy;
auto* lb_policy = policy.add_policies();
lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
WrrLocality());
auto result = ConvertXdsPolicy(policy);
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(std::string(result.status().message()),
::testing::ContainsRegex(
"Error parsing LoadBalancingPolicy.*WrrLocality: "
"endpoint_picking_policy not found"));
EXPECT_EQ(result.status().message(),
"validation errors: ["
"field:load_balancing_policy.policies[0].typed_extension_config"
".typed_config.value[envoy.extensions.load_balancing_policies"
".wrr_locality.v3.WrrLocality].endpoint_picking_policy "
"error:field not present]")
<< result.status();
}
TEST(XdsLbPolicyRegistryTest, WrrLocalityChildPolicyError) {
TEST(WrrLocality, ChildPolicyInvalid) {
RingHash ring_hash;
ring_hash.set_hash_function(RingHash::MURMUR_HASH_2);
WrrLocality wrr_locality;
wrr_locality.mutable_endpoint_picking_policy()
->add_policies()
->mutable_typed_extension_config()
->mutable_typed_config()
->PackFrom(RingHash());
->PackFrom(ring_hash);
LoadBalancingPolicyProto policy;
auto* lb_policy = policy.add_policies();
lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
wrr_locality);
auto result = ConvertXdsPolicy(policy);
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(std::string(result.status().message()),
::testing::ContainsRegex(
"Error parsing LoadBalancingPolicy.*Error parsing "
"WrrLocality load balancing policy.*Error parsing "
"LoadBalancingPolicy.*Invalid hash function provided for "
"RingHash loadbalancing policy. Only XX_HASH is supported."));
EXPECT_EQ(result.status().message(),
"validation errors: ["
"field:load_balancing_policy.policies[0].typed_extension_config"
".typed_config.value[envoy.extensions.load_balancing_policies"
".wrr_locality.v3.WrrLocality].endpoint_picking_policy.policies[0]"
".typed_extension_config.typed_config.value["
"envoy.extensions.load_balancing_policies.ring_hash.v3.RingHash]"
".hash_function "
"error:unsupported value (must be XX_HASH)]")
<< result.status();
}
TEST(XdsLbPolicyRegistryTest, WrrLocalityUnsupportedTypeSkipped) {
TEST(WrrLocality, NoSupportedChildPolicy) {
WrrLocality wrr_locality;
wrr_locality.mutable_endpoint_picking_policy()
->add_policies()
->mutable_typed_extension_config()
->mutable_typed_config()
->PackFrom(LoadBalancingPolicyProto());
LoadBalancingPolicyProto policy;
auto* lb_policy = policy.add_policies();
lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
wrr_locality);
auto result = ConvertXdsPolicy(policy);
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(result.status().message(),
"validation errors: ["
"field:load_balancing_policy.policies[0].typed_extension_config"
".typed_config.value[envoy.extensions.load_balancing_policies"
".wrr_locality.v3.WrrLocality].endpoint_picking_policy "
"error:no supported load balancing policy config found]")
<< result.status();
}
TEST(WrrLocality, UnsupportedChildPolicyTypeSkipped) {
// Create WrrLocality policy and add two policies to its list, an unsupported
// type and then a known RoundRobin type. Expect that the unsupported type is
// skipped and RoundRobin is selected.
@ -262,234 +345,130 @@ TEST(XdsLbPolicyRegistryTest, WrrLocalityUnsupportedTypeSkipped) {
lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
wrr_locality);
auto result = ConvertXdsPolicy(policy);
EXPECT_TRUE(result.ok());
EXPECT_EQ(result->size(), 1);
EXPECT_EQ((*result)[0], Json::Parse("{"
"\"xds_wrr_locality_experimental\": {"
" \"child_policy\": [{"
" \"round_robin\": {}"
" }]"
"}}")
.value());
EXPECT_EQ(*result,
"{\"xds_wrr_locality_experimental\":{"
"\"childPolicy\":[{\"round_robin\":{}}]}}");
}
class CustomLbPolicyFactory : public LoadBalancingPolicyFactory {
public:
OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy(
LoadBalancingPolicy::Args /* args */) const override {
GPR_ASSERT(false);
return nullptr;
}
absl::string_view name() const override { return "test.CustomLb"; }
absl::StatusOr<RefCountedPtr<LoadBalancingPolicy::Config>>
ParseLoadBalancingConfig(const Json& /*json*/) const override {
return nullptr;
}
};
//
// CustomPolicy
//
TEST(XdsLbPolicyRegistryTest, CustomLbPolicy) {
TEST(CustomPolicy, Basic) {
TypedStruct typed_struct;
typed_struct.set_type_url("type.googleapis.com/test.CustomLb");
auto* fields = typed_struct.mutable_value()->mutable_fields();
(*fields)["foo"].set_string_value("bar");
LoadBalancingPolicyProto policy;
auto* lb_policy = policy.add_policies();
lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
typed_struct);
auto result = ConvertXdsPolicy(policy);
EXPECT_TRUE(result.ok());
EXPECT_EQ(result->size(), 1);
EXPECT_EQ((*result)[0], Json::Parse("{\"test.CustomLb\": {}}").value());
ASSERT_TRUE(result.ok()) << result.status();
EXPECT_EQ(*result, "{\"test.CustomLb\":{\"foo\":\"bar\"}}");
}
TEST(XdsLbPolicyRegistryTest, CustomLbPolicyUdpaTyped) {
::udpa::type::v1::TypedStruct typed_struct;
typed_struct.set_type_url("type.googleapis.com/test.CustomLb");
LoadBalancingPolicyProto policy;
auto* lb_policy = policy.add_policies();
lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
typed_struct);
auto result = ConvertXdsPolicy(policy);
EXPECT_TRUE(result.ok());
EXPECT_EQ(result->size(), 1);
EXPECT_EQ((*result)[0], Json::Parse("{\"test.CustomLb\": {}}").value());
}
//
// XdsLbPolicyRegistryTest
//
TEST(XdsLbPolicyRegistryTest, UnsupportedCustomTypeError) {
TypedStruct typed_struct;
typed_struct.set_type_url("myorg/foo/bar/test.UnknownLb");
LoadBalancingPolicyProto policy;
auto* lb_policy = policy.add_policies();
lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
typed_struct);
auto result = ConvertXdsPolicy(policy);
TEST(XdsLbPolicyRegistryTest, EmptyLoadBalancingPolicy) {
auto result = ConvertXdsPolicy(LoadBalancingPolicyProto());
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(
std::string(result.status().message()),
::testing::HasSubstr("No supported load balancing policy config found"));
EXPECT_EQ(result.status().message(),
"validation errors: [field:load_balancing_policy "
"error:no supported load balancing policy config found]")
<< result.status();
}
TEST(XdsLbPolicyRegistryTest, CustomTypeInvalidUrlMissingSlash) {
TypedStruct typed_struct;
typed_struct.set_type_url("test.UnknownLb");
TEST(XdsLbPolicyRegistryTest, MissingTypedExtensionConfig) {
LoadBalancingPolicyProto policy;
auto* lb_policy = policy.add_policies();
lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
typed_struct);
policy.add_policies();
auto result = ConvertXdsPolicy(policy);
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(result.status().message(),
"Error parsing "
"LoadBalancingPolicy::Policy::TypedExtensionConfig::"
"typed_config: ["
"field:value[xds.type.v3.TypedStruct].type_url "
"error:invalid value \"test.UnknownLb\"]")
"validation errors: ["
"field:load_balancing_policy.policies[0].typed_extension_config "
"error:field not present]")
<< result.status();
}
TEST(XdsLbPolicyRegistryTest, CustomTypeInvalidUrlEmptyType) {
TypedStruct typed_struct;
typed_struct.set_type_url("myorg/");
TEST(XdsLbPolicyRegistryTest, MissingTypedConfig) {
LoadBalancingPolicyProto policy;
auto* lb_policy = policy.add_policies();
lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
typed_struct);
policy.add_policies()->mutable_typed_extension_config();
auto result = ConvertXdsPolicy(policy);
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(result.status().message(),
"Error parsing "
"LoadBalancingPolicy::Policy::TypedExtensionConfig::"
"typed_config: "
"[field:value[xds.type.v3.TypedStruct].type_url "
"error:invalid value \"myorg/\"]")
"validation errors: ["
"field:load_balancing_policy.policies[0].typed_extension_config"
".typed_config error:field not present]")
<< result.status();
}
TEST(XdsLbPolicyRegistryTest, CustomLbPolicyJsonConversion) {
// This tests that we pass along errors that are generated by
// ExtractXdsExtension(). An exhaustive list of error cases caught by
// ExtractXdsExtension() are covered in xds_common_types_test.
TEST(XdsLbPolicyRegistryTest, ErrorExtractingExtension) {
TypedStruct typed_struct;
ASSERT_TRUE(grpc::protobuf::TextFormat::ParseFromString(
R"pb(
type_url: "type.googleapis.com/test.CustomLb"
value {
fields {
key: "key"
value { null_value: NULL_VALUE }
}
fields {
key: "number"
value { number_value: 123 }
}
fields {
key: "string"
value { string_value: "value" }
}
fields {
key: "struct"
value {
struct_value {
fields {
key: "key"
value { null_value: NULL_VALUE }
}
}
}
}
fields {
key: "list"
value {
list_value {
values { null_value: NULL_VALUE }
values { number_value: 234 }
}
}
}
}
)pb",
&typed_struct));
typed_struct.set_type_url("type.googleapis.com/");
LoadBalancingPolicyProto policy;
auto* lb_policy = policy.add_policies();
lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
typed_struct);
auto result = ConvertXdsPolicy(policy);
EXPECT_TRUE(result.ok());
EXPECT_EQ(result->size(), 1);
EXPECT_EQ((*result)[0], Json::Parse(
R"json({
"test.CustomLb":{
"key": null,
"number": 123,
"string": "value",
"struct": {
"key": null
},
"list": [null, 234]
}
})json")
.value());
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(result.status().message(),
"validation errors: ["
"field:load_balancing_policy.policies[0].typed_extension_config"
".typed_config.value[xds.type.v3.TypedStruct].type_url "
"error:invalid value \"type.googleapis.com/\"]")
<< result.status();
}
TEST(XdsLbPolicyRegistryTest, CustomLbPolicyListError) {
TypedStruct typed_struct;
typed_struct.set_type_url("type.googleapis.com/test.CustomLb");
auto* fields = typed_struct.mutable_value()->mutable_fields();
google::protobuf::Value value;
value.mutable_list_value()->add_values();
(*fields)["key"] = value;
TEST(XdsLbPolicyRegistryTest, NoSupportedType) {
LoadBalancingPolicyProto policy;
auto* lb_policy = policy.add_policies();
lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
typed_struct);
// Unsupported built-in type.
policy.add_policies()
->mutable_typed_extension_config()
->mutable_typed_config()
->PackFrom(LoadBalancingPolicyProto());
// Unsupported custom type.
TypedStruct typed_struct;
typed_struct.set_type_url("myorg/foo/bar/test.UnknownLb");
policy.add_policies()
->mutable_typed_extension_config()
->mutable_typed_config()
->PackFrom(typed_struct);
auto result = ConvertXdsPolicy(policy);
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(result.status().message(),
"Error parsing "
"LoadBalancingPolicy::Policy::TypedExtensionConfig::typed_config: ["
"field:value[xds.type.v3.TypedStruct].value[test.CustomLb] "
"error:error encoding google::Protobuf::Struct as JSON: "
"No value set in Value proto]")
"validation errors: [field:load_balancing_policy "
"error:no supported load balancing policy config found]")
<< result.status();
}
TEST(XdsLbPolicyRegistryTest, UnsupportedBuiltInTypeSkipped) {
// Add two policies to list, an unsupported type and then a known RoundRobin
// type. Expect that the unsupported type is skipped and RoundRobin is
// selected.
TEST(XdsLbPolicyRegistryTest, UnsupportedTypesSkipped) {
LoadBalancingPolicyProto policy;
auto* lb_policy = policy.add_policies();
lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
LoadBalancingPolicyProto());
lb_policy = policy.add_policies();
lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
RoundRobin());
auto result = ConvertXdsPolicy(policy);
EXPECT_TRUE(result.ok());
EXPECT_EQ(result->size(), 1);
EXPECT_EQ((*result)[0], Json::Parse("{"
"\"round_robin\": {}"
"}")
.value());
}
TEST(XdsLbPolicyRegistryTest, UnsupportedCustomTypeSkipped) {
// Add two policies to list, an unsupported custom type and then a known
// RoundRobin type. Expect that the unsupported type is skipped and RoundRobin
// is selected.
// Unsupported built-in type.
policy.add_policies()
->mutable_typed_extension_config()
->mutable_typed_config()
->PackFrom(LoadBalancingPolicyProto());
// Unsupported custom type.
TypedStruct typed_struct;
typed_struct.set_type_url("myorg/foo/bar/test.UnknownLb");
LoadBalancingPolicyProto policy;
auto* lb_policy = policy.add_policies();
lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
typed_struct);
lb_policy = policy.add_policies();
lb_policy->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(
RoundRobin());
policy.add_policies()
->mutable_typed_extension_config()
->mutable_typed_config()
->PackFrom(typed_struct);
// Supported type.
policy.add_policies()
->mutable_typed_extension_config()
->mutable_typed_config()
->PackFrom(RoundRobin());
auto result = ConvertXdsPolicy(policy);
EXPECT_TRUE(result.ok());
EXPECT_EQ(result->size(), 1);
EXPECT_EQ((*result)[0], Json::Parse("{"
"\"round_robin\": {}"
"}")
.value());
ASSERT_TRUE(result.ok()) << result.status();
EXPECT_EQ(*result, "{\"round_robin\":{}}");
}
// Build a recurse load balancing policy that goes beyond the max allowable
@ -517,8 +496,7 @@ TEST(XdsLbPolicyRegistryTest, MaxRecursion) {
auto result = ConvertXdsPolicy(BuildRecursiveLoadBalancingPolicy(0));
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(std::string(result.status().message()),
::testing::ContainsRegex("LoadBalancingPolicy configuration has "
"a recursion depth of more than 16"));
::testing::EndsWith("error:exceeded max recursion depth of 16]"));
}
} // namespace
@ -533,7 +511,6 @@ int main(int argc, char** argv) {
builder->lb_policy_registry()->RegisterLoadBalancingPolicyFactory(
std::make_unique<grpc_core::testing::CustomLbPolicyFactory>());
});
grpc_init();
auto result = RUN_ALL_TESTS();
grpc_shutdown();

Loading…
Cancel
Save