|
|
|
@ -82,7 +82,8 @@ |
|
|
|
|
#include "src/core/lib/iomgr/pollset_set.h" |
|
|
|
|
#include "src/core/lib/iomgr/timer.h" |
|
|
|
|
#include "src/core/lib/json/json.h" |
|
|
|
|
#include "src/core/lib/json/json_util.h" |
|
|
|
|
#include "src/core/lib/json/json_args.h" |
|
|
|
|
#include "src/core/lib/json/json_object_loader.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" |
|
|
|
@ -142,25 +143,24 @@ class RlsLbConfig : public LoadBalancingPolicy::Config { |
|
|
|
|
struct RouteLookupConfig { |
|
|
|
|
KeyBuilderMap key_builder_map; |
|
|
|
|
std::string lookup_service; |
|
|
|
|
Duration lookup_service_timeout; |
|
|
|
|
Duration max_age; |
|
|
|
|
Duration stale_age; |
|
|
|
|
Duration lookup_service_timeout = kDefaultLookupServiceTimeout; |
|
|
|
|
Duration max_age = kMaxMaxAge; |
|
|
|
|
Duration stale_age = kMaxMaxAge; |
|
|
|
|
int64_t cache_size_bytes = 0; |
|
|
|
|
std::string default_target; |
|
|
|
|
|
|
|
|
|
static const JsonLoaderInterface* JsonLoader(const JsonArgs&); |
|
|
|
|
void JsonPostLoad(const Json& json, const JsonArgs& args, |
|
|
|
|
ErrorList* errors); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
RlsLbConfig(RouteLookupConfig route_lookup_config, |
|
|
|
|
std::string rls_channel_service_config, Json child_policy_config, |
|
|
|
|
std::string child_policy_config_target_field_name, |
|
|
|
|
RefCountedPtr<LoadBalancingPolicy::Config> |
|
|
|
|
default_child_policy_parsed_config) |
|
|
|
|
: route_lookup_config_(std::move(route_lookup_config)), |
|
|
|
|
rls_channel_service_config_(std::move(rls_channel_service_config)), |
|
|
|
|
child_policy_config_(std::move(child_policy_config)), |
|
|
|
|
child_policy_config_target_field_name_( |
|
|
|
|
std::move(child_policy_config_target_field_name)), |
|
|
|
|
default_child_policy_parsed_config_( |
|
|
|
|
std::move(default_child_policy_parsed_config)) {} |
|
|
|
|
RlsLbConfig() = default; |
|
|
|
|
|
|
|
|
|
RlsLbConfig(const RlsLbConfig&) = delete; |
|
|
|
|
RlsLbConfig& operator=(const RlsLbConfig&) = delete; |
|
|
|
|
|
|
|
|
|
RlsLbConfig(RlsLbConfig&& other) = delete; |
|
|
|
|
RlsLbConfig& operator=(RlsLbConfig&& other) = delete; |
|
|
|
|
|
|
|
|
|
absl::string_view name() const override { return kRls; } |
|
|
|
|
|
|
|
|
@ -193,6 +193,9 @@ class RlsLbConfig : public LoadBalancingPolicy::Config { |
|
|
|
|
return default_child_policy_parsed_config_; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static const JsonLoaderInterface* JsonLoader(const JsonArgs&); |
|
|
|
|
void JsonPostLoad(const Json& json, const JsonArgs&, ErrorList* errors); |
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
RouteLookupConfig route_lookup_config_; |
|
|
|
|
std::string rls_channel_service_config_; |
|
|
|
@ -742,28 +745,32 @@ void RlsLb::ChildPolicyWrapper::Orphan() { |
|
|
|
|
picker_.reset(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_error_handle InsertOrUpdateChildPolicyField(const std::string& field, |
|
|
|
|
const std::string& value, |
|
|
|
|
Json* config) { |
|
|
|
|
bool InsertOrUpdateChildPolicyField(const std::string& field, |
|
|
|
|
const std::string& value, Json* config, |
|
|
|
|
ErrorList* errors) { |
|
|
|
|
if (config->type() != Json::Type::ARRAY) { |
|
|
|
|
return GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"child policy configuration is not an array"); |
|
|
|
|
errors->AddError("is not an array"); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
std::vector<grpc_error_handle> error_list; |
|
|
|
|
for (Json& child_json : *config->mutable_array()) { |
|
|
|
|
bool success = true; |
|
|
|
|
for (size_t i = 0; i < config->array_value().size(); ++i) { |
|
|
|
|
Json& child_json = (*config->mutable_array())[i]; |
|
|
|
|
ScopedField json_field(errors, absl::StrCat("[", i, "]")); |
|
|
|
|
if (child_json.type() != Json::Type::OBJECT) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"child policy item is not an object")); |
|
|
|
|
errors->AddError("is not an object"); |
|
|
|
|
success = false; |
|
|
|
|
} else { |
|
|
|
|
Json::Object& child = *child_json.mutable_object(); |
|
|
|
|
if (child.size() != 1) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"child policy item contains more than one field")); |
|
|
|
|
errors->AddError("child policy object contains more than one field"); |
|
|
|
|
success = false; |
|
|
|
|
} else { |
|
|
|
|
ScopedField json_field( |
|
|
|
|
errors, absl::StrCat("[\"", child.begin()->first, "\"]")); |
|
|
|
|
Json& child_config_json = child.begin()->second; |
|
|
|
|
if (child_config_json.type() != Json::Type::OBJECT) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"child policy item config is not an object")); |
|
|
|
|
errors->AddError("child policy config is not an object"); |
|
|
|
|
success = false; |
|
|
|
|
} else { |
|
|
|
|
Json::Object& child_config = *child_config_json.mutable_object(); |
|
|
|
|
child_config[field] = Json(value); |
|
|
|
@ -771,18 +778,15 @@ grpc_error_handle InsertOrUpdateChildPolicyField(const std::string& field, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return GRPC_ERROR_CREATE_FROM_VECTOR_AND_CPP_STRING( |
|
|
|
|
absl::StrCat("errors when inserting field \"", field, |
|
|
|
|
"\" for child policy"), |
|
|
|
|
&error_list); |
|
|
|
|
return success; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void RlsLb::ChildPolicyWrapper::StartUpdate() { |
|
|
|
|
Json child_policy_config = lb_policy_->config_->child_policy_config(); |
|
|
|
|
grpc_error_handle error = InsertOrUpdateChildPolicyField( |
|
|
|
|
ErrorList errors; |
|
|
|
|
GPR_ASSERT(InsertOrUpdateChildPolicyField( |
|
|
|
|
lb_policy_->config_->child_policy_config_target_field_name(), target_, |
|
|
|
|
&child_policy_config); |
|
|
|
|
GPR_ASSERT(GRPC_ERROR_IS_NONE(error)); |
|
|
|
|
&child_policy_config, &errors)); |
|
|
|
|
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_rls_trace)) { |
|
|
|
|
gpr_log( |
|
|
|
|
GPR_INFO, |
|
|
|
@ -2143,346 +2147,338 @@ void RlsLb::UpdatePickerLocked() { |
|
|
|
|
// RlsLbFactory
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
grpc_error_handle ParseJsonHeaders(size_t idx, const Json& json, |
|
|
|
|
std::string* key, |
|
|
|
|
std::vector<std::string>* headers) { |
|
|
|
|
if (json.type() != Json::Type::OBJECT) { |
|
|
|
|
return GRPC_ERROR_CREATE_FROM_CPP_STRING(absl::StrCat( |
|
|
|
|
"field:headers index:", idx, " error:type should be OBJECT")); |
|
|
|
|
} |
|
|
|
|
std::vector<grpc_error_handle> error_list; |
|
|
|
|
// requiredMatch must not be present.
|
|
|
|
|
if (json.object_value().find("requiredMatch") != json.object_value().end()) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"field:requiredMatch error:must not be present")); |
|
|
|
|
} |
|
|
|
|
// Find key.
|
|
|
|
|
if (ParseJsonObjectField(json.object_value(), "key", key, &error_list) && |
|
|
|
|
key->empty()) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"field:key error:must be non-empty")); |
|
|
|
|
} |
|
|
|
|
// Find headers.
|
|
|
|
|
const Json::Array* headers_json = nullptr; |
|
|
|
|
ParseJsonObjectField(json.object_value(), "names", &headers_json, |
|
|
|
|
&error_list); |
|
|
|
|
if (headers_json != nullptr) { |
|
|
|
|
if (headers_json->empty()) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"field:names error:list is empty")); |
|
|
|
|
} else { |
|
|
|
|
size_t name_idx = 0; |
|
|
|
|
for (const Json& name_json : *headers_json) { |
|
|
|
|
if (name_json.type() != Json::Type::STRING) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING(absl::StrCat( |
|
|
|
|
"field:names index:", name_idx, " error:type should be STRING"))); |
|
|
|
|
} else if (name_json.string_value().empty()) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING( |
|
|
|
|
absl::StrCat("field:names index:", name_idx, |
|
|
|
|
" error:header name must be non-empty"))); |
|
|
|
|
} else { |
|
|
|
|
headers->push_back(name_json.string_value()); |
|
|
|
|
struct GrpcKeyBuilder { |
|
|
|
|
struct Name { |
|
|
|
|
std::string service; |
|
|
|
|
std::string method; |
|
|
|
|
|
|
|
|
|
static const JsonLoaderInterface* JsonLoader(const JsonArgs&) { |
|
|
|
|
static const auto* loader = JsonObjectLoader<Name>() |
|
|
|
|
.Field("service", &Name::service) |
|
|
|
|
.OptionalField("method", &Name::method) |
|
|
|
|
.Finish(); |
|
|
|
|
return loader; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
struct NameMatcher { |
|
|
|
|
std::string key; |
|
|
|
|
std::vector<std::string> names; |
|
|
|
|
absl::optional<bool> required_match; |
|
|
|
|
|
|
|
|
|
static const JsonLoaderInterface* JsonLoader(const JsonArgs&) { |
|
|
|
|
static const auto* loader = |
|
|
|
|
JsonObjectLoader<NameMatcher>() |
|
|
|
|
.Field("key", &NameMatcher::key) |
|
|
|
|
.Field("names", &NameMatcher::names) |
|
|
|
|
.OptionalField("requiredMatch", &NameMatcher::required_match) |
|
|
|
|
.Finish(); |
|
|
|
|
return loader; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void JsonPostLoad(const Json&, const JsonArgs&, ErrorList* errors) { |
|
|
|
|
// key must be non-empty.
|
|
|
|
|
{ |
|
|
|
|
ScopedField field(errors, ".key"); |
|
|
|
|
if (!errors->FieldHasErrors() && key.empty()) { |
|
|
|
|
errors->AddError("must be non-empty"); |
|
|
|
|
} |
|
|
|
|
++name_idx; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return GRPC_ERROR_CREATE_FROM_VECTOR_AND_CPP_STRING( |
|
|
|
|
absl::StrCat("field:headers index:", idx), &error_list); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::string ParseJsonMethodName(size_t idx, const Json& json, |
|
|
|
|
grpc_error_handle* error) { |
|
|
|
|
if (json.type() != Json::Type::OBJECT) { |
|
|
|
|
*error = GRPC_ERROR_CREATE_FROM_CPP_STRING(absl::StrCat( |
|
|
|
|
"field:names index:", idx, " error:type should be OBJECT")); |
|
|
|
|
return ""; |
|
|
|
|
} |
|
|
|
|
std::vector<grpc_error_handle> error_list; |
|
|
|
|
// Find service name.
|
|
|
|
|
absl::string_view service_name; |
|
|
|
|
ParseJsonObjectField(json.object_value(), "service", &service_name, |
|
|
|
|
&error_list); |
|
|
|
|
// Find method name.
|
|
|
|
|
absl::string_view method_name; |
|
|
|
|
ParseJsonObjectField(json.object_value(), "method", &method_name, &error_list, |
|
|
|
|
/*required=*/false); |
|
|
|
|
// Return error, if any.
|
|
|
|
|
*error = GRPC_ERROR_CREATE_FROM_VECTOR_AND_CPP_STRING( |
|
|
|
|
absl::StrCat("field:names index:", idx), &error_list); |
|
|
|
|
// Construct path.
|
|
|
|
|
return absl::StrCat("/", service_name, "/", method_name); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_error_handle ParseGrpcKeybuilder( |
|
|
|
|
size_t idx, const Json& json, RlsLbConfig::KeyBuilderMap* key_builder_map) { |
|
|
|
|
if (json.type() != Json::Type::OBJECT) { |
|
|
|
|
return GRPC_ERROR_CREATE_FROM_CPP_STRING(absl::StrCat( |
|
|
|
|
"field:grpc_keybuilders index:", idx, " error:type should be OBJECT")); |
|
|
|
|
} |
|
|
|
|
std::vector<grpc_error_handle> error_list; |
|
|
|
|
// Parse names.
|
|
|
|
|
std::set<std::string> names; |
|
|
|
|
const Json::Array* names_array = nullptr; |
|
|
|
|
if (ParseJsonObjectField(json.object_value(), "names", &names_array, |
|
|
|
|
&error_list)) { |
|
|
|
|
if (names_array->empty()) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"field:names error:list is empty")); |
|
|
|
|
} else { |
|
|
|
|
size_t name_idx = 0; |
|
|
|
|
for (const Json& name_json : *names_array) { |
|
|
|
|
grpc_error_handle child_error = GRPC_ERROR_NONE; |
|
|
|
|
std::string name = |
|
|
|
|
ParseJsonMethodName(name_idx++, name_json, &child_error); |
|
|
|
|
if (!GRPC_ERROR_IS_NONE(child_error)) { |
|
|
|
|
error_list.push_back(child_error); |
|
|
|
|
} else { |
|
|
|
|
bool inserted = names.insert(name).second; |
|
|
|
|
if (!inserted) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING( |
|
|
|
|
absl::StrCat("field:names error:duplicate entry for ", name))); |
|
|
|
|
// List of header names must be non-empty.
|
|
|
|
|
{ |
|
|
|
|
ScopedField field(errors, ".names"); |
|
|
|
|
if (!errors->FieldHasErrors() && names.empty()) { |
|
|
|
|
errors->AddError("must be non-empty"); |
|
|
|
|
} |
|
|
|
|
// Individual header names must be non-empty.
|
|
|
|
|
for (size_t i = 0; i < names.size(); ++i) { |
|
|
|
|
ScopedField field(errors, absl::StrCat("[", i, "]")); |
|
|
|
|
if (!errors->FieldHasErrors() && names[i].empty()) { |
|
|
|
|
errors->AddError("must be non-empty"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Helper function to check for duplicate keys.
|
|
|
|
|
std::set<std::string> all_keys; |
|
|
|
|
auto duplicate_key_check_func = [&all_keys, |
|
|
|
|
&error_list](const std::string& key) { |
|
|
|
|
auto it = all_keys.find(key); |
|
|
|
|
if (it != all_keys.end()) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING( |
|
|
|
|
absl::StrCat("key \"", key, "\" listed multiple times"))); |
|
|
|
|
} else { |
|
|
|
|
all_keys.insert(key); |
|
|
|
|
// requiredMatch must not be present.
|
|
|
|
|
{ |
|
|
|
|
ScopedField field(errors, ".requiredMatch"); |
|
|
|
|
if (required_match.has_value()) { |
|
|
|
|
errors->AddError("must not be present"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
// Parse headers.
|
|
|
|
|
RlsLbConfig::KeyBuilder key_builder; |
|
|
|
|
const Json::Array* headers_array = nullptr; |
|
|
|
|
ParseJsonObjectField(json.object_value(), "headers", &headers_array, |
|
|
|
|
&error_list, /*required=*/false); |
|
|
|
|
if (headers_array != nullptr) { |
|
|
|
|
size_t header_idx = 0; |
|
|
|
|
for (const Json& header_json : *headers_array) { |
|
|
|
|
std::string key; |
|
|
|
|
std::vector<std::string> headers; |
|
|
|
|
grpc_error_handle child_error = |
|
|
|
|
ParseJsonHeaders(header_idx++, header_json, &key, &headers); |
|
|
|
|
if (!GRPC_ERROR_IS_NONE(child_error)) { |
|
|
|
|
error_list.push_back(child_error); |
|
|
|
|
} else { |
|
|
|
|
duplicate_key_check_func(key); |
|
|
|
|
key_builder.header_keys.emplace(key, std::move(headers)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
struct ExtraKeys { |
|
|
|
|
absl::optional<std::string> host_key; |
|
|
|
|
absl::optional<std::string> service_key; |
|
|
|
|
absl::optional<std::string> method_key; |
|
|
|
|
|
|
|
|
|
static const JsonLoaderInterface* JsonLoader(const JsonArgs&) { |
|
|
|
|
static const auto* loader = |
|
|
|
|
JsonObjectLoader<ExtraKeys>() |
|
|
|
|
.OptionalField("host", &ExtraKeys::host_key) |
|
|
|
|
.OptionalField("service", &ExtraKeys::service_key) |
|
|
|
|
.OptionalField("method", &ExtraKeys::method_key) |
|
|
|
|
.Finish(); |
|
|
|
|
return loader; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void JsonPostLoad(const Json&, const JsonArgs&, ErrorList* errors) { |
|
|
|
|
auto check_field = [&](const std::string& field_name, |
|
|
|
|
absl::optional<std::string>* struct_field) { |
|
|
|
|
ScopedField field(errors, absl::StrCat(".", field_name)); |
|
|
|
|
if (struct_field->has_value() && (*struct_field)->empty()) { |
|
|
|
|
errors->AddError("must be non-empty if set"); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
check_field("host", &host_key); |
|
|
|
|
check_field("service", &service_key); |
|
|
|
|
check_field("method", &method_key); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
std::vector<Name> names; |
|
|
|
|
std::vector<NameMatcher> headers; |
|
|
|
|
ExtraKeys extra_keys; |
|
|
|
|
std::map<std::string /*key*/, std::string /*value*/> constant_keys; |
|
|
|
|
|
|
|
|
|
static const JsonLoaderInterface* JsonLoader(const JsonArgs&) { |
|
|
|
|
static const auto* loader = |
|
|
|
|
JsonObjectLoader<GrpcKeyBuilder>() |
|
|
|
|
.Field("names", &GrpcKeyBuilder::names) |
|
|
|
|
.OptionalField("headers", &GrpcKeyBuilder::headers) |
|
|
|
|
.OptionalField("extraKeys", &GrpcKeyBuilder::extra_keys) |
|
|
|
|
.OptionalField("constantKeys", &GrpcKeyBuilder::constant_keys) |
|
|
|
|
.Finish(); |
|
|
|
|
return loader; |
|
|
|
|
} |
|
|
|
|
// Parse extraKeys.
|
|
|
|
|
const Json::Object* extra_keys = nullptr; |
|
|
|
|
ParseJsonObjectField(json.object_value(), "extraKeys", &extra_keys, |
|
|
|
|
&error_list, /*required=*/false); |
|
|
|
|
if (extra_keys != nullptr) { |
|
|
|
|
std::vector<grpc_error_handle> extra_keys_errors; |
|
|
|
|
if (ParseJsonObjectField(*extra_keys, "host", &key_builder.host_key, |
|
|
|
|
&extra_keys_errors, /*required=*/false) && |
|
|
|
|
key_builder.host_key.empty()) { |
|
|
|
|
extra_keys_errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"field:host error:must be non-empty")); |
|
|
|
|
|
|
|
|
|
void JsonPostLoad(const Json&, const JsonArgs&, ErrorList* errors) { |
|
|
|
|
// The names field must be non-empty.
|
|
|
|
|
{ |
|
|
|
|
ScopedField field(errors, ".names"); |
|
|
|
|
if (!errors->FieldHasErrors() && names.empty()) { |
|
|
|
|
errors->AddError("must be non-empty"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!key_builder.host_key.empty()) { |
|
|
|
|
duplicate_key_check_func(key_builder.host_key); |
|
|
|
|
// Make sure no key in constantKeys is empty.
|
|
|
|
|
if (constant_keys.find("") != constant_keys.end()) { |
|
|
|
|
ScopedField field(errors, ".constantKeys[\"\"]"); |
|
|
|
|
errors->AddError("key must be non-empty"); |
|
|
|
|
} |
|
|
|
|
if (ParseJsonObjectField(*extra_keys, "service", &key_builder.service_key, |
|
|
|
|
&extra_keys_errors, /*required=*/false) && |
|
|
|
|
key_builder.service_key.empty()) { |
|
|
|
|
extra_keys_errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"field:service error:must be non-empty")); |
|
|
|
|
// Check for duplicate keys.
|
|
|
|
|
std::set<absl::string_view> keys_seen; |
|
|
|
|
auto duplicate_key_check_func = [&keys_seen, errors]( |
|
|
|
|
const std::string& key, |
|
|
|
|
const std::string& field_name) { |
|
|
|
|
if (key.empty()) return; // Already generated an error about this.
|
|
|
|
|
ScopedField field(errors, field_name); |
|
|
|
|
auto it = keys_seen.find(key); |
|
|
|
|
if (it != keys_seen.end()) { |
|
|
|
|
errors->AddError(absl::StrCat("duplicate key \"", key, "\"")); |
|
|
|
|
} else { |
|
|
|
|
keys_seen.insert(key); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
for (size_t i = 0; i < headers.size(); ++i) { |
|
|
|
|
NameMatcher& header = headers[i]; |
|
|
|
|
duplicate_key_check_func(header.key, |
|
|
|
|
absl::StrCat(".headers[", i, "].key")); |
|
|
|
|
} |
|
|
|
|
if (!key_builder.service_key.empty()) { |
|
|
|
|
duplicate_key_check_func(key_builder.service_key); |
|
|
|
|
for (const auto& p : constant_keys) { |
|
|
|
|
duplicate_key_check_func( |
|
|
|
|
p.first, absl::StrCat(".constantKeys[\"", p.first, "\"]")); |
|
|
|
|
} |
|
|
|
|
if (ParseJsonObjectField(*extra_keys, "method", &key_builder.method_key, |
|
|
|
|
&extra_keys_errors, /*required=*/false) && |
|
|
|
|
key_builder.method_key.empty()) { |
|
|
|
|
extra_keys_errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"field:method error:must be non-empty")); |
|
|
|
|
if (extra_keys.host_key.has_value()) { |
|
|
|
|
duplicate_key_check_func(*extra_keys.host_key, ".extraKeys.host"); |
|
|
|
|
} |
|
|
|
|
if (!key_builder.method_key.empty()) { |
|
|
|
|
duplicate_key_check_func(key_builder.method_key); |
|
|
|
|
if (extra_keys.service_key.has_value()) { |
|
|
|
|
duplicate_key_check_func(*extra_keys.service_key, ".extraKeys.service"); |
|
|
|
|
} |
|
|
|
|
if (!extra_keys_errors.empty()) { |
|
|
|
|
error_list.push_back( |
|
|
|
|
GRPC_ERROR_CREATE_FROM_VECTOR("field:extraKeys", &extra_keys_errors)); |
|
|
|
|
if (extra_keys.method_key.has_value()) { |
|
|
|
|
duplicate_key_check_func(*extra_keys.method_key, ".extraKeys.method"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Parse constantKeys.
|
|
|
|
|
const Json::Object* constant_keys = nullptr; |
|
|
|
|
ParseJsonObjectField(json.object_value(), "constantKeys", &constant_keys, |
|
|
|
|
&error_list, /*required=*/false); |
|
|
|
|
if (constant_keys != nullptr) { |
|
|
|
|
std::vector<grpc_error_handle> constant_keys_errors; |
|
|
|
|
for (const auto& p : *constant_keys) { |
|
|
|
|
const std::string& key = p.first; |
|
|
|
|
const Json& value = p.second; |
|
|
|
|
if (key.empty()) { |
|
|
|
|
constant_keys_errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"error:keys must be non-empty")); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const JsonLoaderInterface* RlsLbConfig::RouteLookupConfig::JsonLoader( |
|
|
|
|
const JsonArgs&) { |
|
|
|
|
static const auto* loader = |
|
|
|
|
JsonObjectLoader<RouteLookupConfig>() |
|
|
|
|
// Note: Some fields require manual processing and are handled in
|
|
|
|
|
// JsonPostLoad() instead.
|
|
|
|
|
.Field("lookupService", &RouteLookupConfig::lookup_service) |
|
|
|
|
.OptionalField("lookupServiceTimeout", |
|
|
|
|
&RouteLookupConfig::lookup_service_timeout) |
|
|
|
|
.OptionalField("maxAge", &RouteLookupConfig::max_age) |
|
|
|
|
.OptionalField("staleAge", &RouteLookupConfig::stale_age) |
|
|
|
|
.Field("cacheSizeBytes", &RouteLookupConfig::cache_size_bytes) |
|
|
|
|
.OptionalField("defaultTarget", &RouteLookupConfig::default_target) |
|
|
|
|
.Finish(); |
|
|
|
|
return loader; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void RlsLbConfig::RouteLookupConfig::JsonPostLoad(const Json& json, |
|
|
|
|
const JsonArgs& args, |
|
|
|
|
ErrorList* errors) { |
|
|
|
|
// Parse grpcKeybuilders.
|
|
|
|
|
auto grpc_keybuilders = LoadJsonObjectField<std::vector<GrpcKeyBuilder>>( |
|
|
|
|
json.object_value(), args, "grpcKeybuilders", errors); |
|
|
|
|
if (grpc_keybuilders.has_value()) { |
|
|
|
|
ScopedField field(errors, ".grpcKeybuilders"); |
|
|
|
|
for (size_t i = 0; i < grpc_keybuilders->size(); ++i) { |
|
|
|
|
ScopedField field(errors, absl::StrCat("[", i, "]")); |
|
|
|
|
auto& grpc_keybuilder = (*grpc_keybuilders)[i]; |
|
|
|
|
// Construct KeyBuilder.
|
|
|
|
|
RlsLbConfig::KeyBuilder key_builder; |
|
|
|
|
for (const auto& header : grpc_keybuilder.headers) { |
|
|
|
|
key_builder.header_keys.emplace(header.key, header.names); |
|
|
|
|
} |
|
|
|
|
if (grpc_keybuilder.extra_keys.host_key.has_value()) { |
|
|
|
|
key_builder.host_key = std::move(*grpc_keybuilder.extra_keys.host_key); |
|
|
|
|
} |
|
|
|
|
if (grpc_keybuilder.extra_keys.service_key.has_value()) { |
|
|
|
|
key_builder.service_key = |
|
|
|
|
std::move(*grpc_keybuilder.extra_keys.service_key); |
|
|
|
|
} |
|
|
|
|
if (grpc_keybuilder.extra_keys.method_key.has_value()) { |
|
|
|
|
key_builder.method_key = |
|
|
|
|
std::move(*grpc_keybuilder.extra_keys.method_key); |
|
|
|
|
} |
|
|
|
|
key_builder.constant_keys = std::move(grpc_keybuilder.constant_keys); |
|
|
|
|
// Add entries to map.
|
|
|
|
|
for (const auto& name : grpc_keybuilder.names) { |
|
|
|
|
std::string path = absl::StrCat("/", name.service, "/", name.method); |
|
|
|
|
bool inserted = key_builder_map.emplace(path, key_builder).second; |
|
|
|
|
if (!inserted) { |
|
|
|
|
errors->AddError(absl::StrCat("duplicate entry for \"", path, "\"")); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
duplicate_key_check_func(key); |
|
|
|
|
ExtractJsonString(value, key, &key_builder.constant_keys[key], |
|
|
|
|
&constant_keys_errors); |
|
|
|
|
} |
|
|
|
|
if (!constant_keys_errors.empty()) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_VECTOR( |
|
|
|
|
"field:constantKeys", &constant_keys_errors)); |
|
|
|
|
} |
|
|
|
|
// Validate lookupService.
|
|
|
|
|
{ |
|
|
|
|
ScopedField field(errors, ".lookupService"); |
|
|
|
|
if (!errors->FieldHasErrors() && |
|
|
|
|
!CoreConfiguration::Get().resolver_registry().IsValidTarget( |
|
|
|
|
lookup_service)) { |
|
|
|
|
errors->AddError("must be valid gRPC target URI"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Insert key_builder into key_builder_map.
|
|
|
|
|
for (const std::string& name : names) { |
|
|
|
|
bool inserted = key_builder_map->emplace(name, key_builder).second; |
|
|
|
|
if (!inserted) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING( |
|
|
|
|
absl::StrCat("field:names error:duplicate entry for ", name))); |
|
|
|
|
// Clamp maxAge to the max allowed value.
|
|
|
|
|
if (max_age > kMaxMaxAge) max_age = kMaxMaxAge; |
|
|
|
|
// If staleAge is set, then maxAge must also be set.
|
|
|
|
|
if (json.object_value().find("staleAge") != json.object_value().end() && |
|
|
|
|
json.object_value().find("maxAge") == json.object_value().end()) { |
|
|
|
|
ScopedField field(errors, ".maxAge"); |
|
|
|
|
errors->AddError("must be set if staleAge is set"); |
|
|
|
|
} |
|
|
|
|
// Ignore staleAge if greater than or equal to maxAge.
|
|
|
|
|
if (stale_age >= max_age) stale_age = max_age; |
|
|
|
|
// Validate cacheSizeBytes.
|
|
|
|
|
{ |
|
|
|
|
ScopedField field(errors, ".cacheSizeBytes"); |
|
|
|
|
if (!errors->FieldHasErrors() && cache_size_bytes <= 0) { |
|
|
|
|
errors->AddError("must be greater than 0"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return GRPC_ERROR_CREATE_FROM_VECTOR_AND_CPP_STRING( |
|
|
|
|
absl::StrCat("index:", idx), &error_list); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
RlsLbConfig::KeyBuilderMap ParseGrpcKeybuilders( |
|
|
|
|
const Json::Array& key_builder_list, grpc_error_handle* error) { |
|
|
|
|
RlsLbConfig::KeyBuilderMap key_builder_map; |
|
|
|
|
if (key_builder_list.empty()) { |
|
|
|
|
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"field:grpcKeybuilders error:list is empty"); |
|
|
|
|
return key_builder_map; |
|
|
|
|
// Clamp cacheSizeBytes to the max allowed value.
|
|
|
|
|
if (cache_size_bytes > kMaxCacheSizeBytes) { |
|
|
|
|
cache_size_bytes = kMaxCacheSizeBytes; |
|
|
|
|
} |
|
|
|
|
std::vector<grpc_error_handle> error_list; |
|
|
|
|
size_t idx = 0; |
|
|
|
|
for (const Json& key_builder : key_builder_list) { |
|
|
|
|
grpc_error_handle child_error = |
|
|
|
|
ParseGrpcKeybuilder(idx++, key_builder, &key_builder_map); |
|
|
|
|
if (!GRPC_ERROR_IS_NONE(child_error)) error_list.push_back(child_error); |
|
|
|
|
// Validate defaultTarget.
|
|
|
|
|
{ |
|
|
|
|
ScopedField field(errors, ".defaultTarget"); |
|
|
|
|
if (!errors->FieldHasErrors() && |
|
|
|
|
json.object_value().find("defaultTarget") != |
|
|
|
|
json.object_value().end() && |
|
|
|
|
default_target.empty()) { |
|
|
|
|
errors->AddError("must be non-empty if set"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
*error = GRPC_ERROR_CREATE_FROM_VECTOR("field:grpcKeybuilders", &error_list); |
|
|
|
|
return key_builder_map; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
RlsLbConfig::RouteLookupConfig ParseRouteLookupConfig( |
|
|
|
|
const Json::Object& json, grpc_error_handle* error) { |
|
|
|
|
std::vector<grpc_error_handle> error_list; |
|
|
|
|
RlsLbConfig::RouteLookupConfig route_lookup_config; |
|
|
|
|
// Parse grpcKeybuilders.
|
|
|
|
|
const Json::Array* keybuilder_list = nullptr; |
|
|
|
|
ParseJsonObjectField(json, "grpcKeybuilders", &keybuilder_list, &error_list); |
|
|
|
|
if (keybuilder_list != nullptr) { |
|
|
|
|
const JsonLoaderInterface* RlsLbConfig::JsonLoader(const JsonArgs&) { |
|
|
|
|
static const auto* loader = |
|
|
|
|
JsonObjectLoader<RlsLbConfig>() |
|
|
|
|
// Note: Some fields require manual processing and are handled in
|
|
|
|
|
// JsonPostLoad() instead.
|
|
|
|
|
.Field("routeLookupConfig", &RlsLbConfig::route_lookup_config_) |
|
|
|
|
.Field("childPolicyConfigTargetFieldName", |
|
|
|
|
&RlsLbConfig::child_policy_config_target_field_name_) |
|
|
|
|
.Finish(); |
|
|
|
|
return loader; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void RlsLbConfig::JsonPostLoad(const Json& json, const JsonArgs&, |
|
|
|
|
ErrorList* errors) { |
|
|
|
|
// Parse routeLookupChannelServiceConfig.
|
|
|
|
|
auto it = json.object_value().find("routeLookupChannelServiceConfig"); |
|
|
|
|
if (it != json.object_value().end()) { |
|
|
|
|
ScopedField field(errors, ".routeLookupChannelServiceConfig"); |
|
|
|
|
grpc_error_handle child_error = GRPC_ERROR_NONE; |
|
|
|
|
route_lookup_config.key_builder_map = |
|
|
|
|
ParseGrpcKeybuilders(*keybuilder_list, &child_error); |
|
|
|
|
if (!GRPC_ERROR_IS_NONE(child_error)) error_list.push_back(child_error); |
|
|
|
|
} |
|
|
|
|
// Parse lookupService.
|
|
|
|
|
if (ParseJsonObjectField(json, "lookupService", |
|
|
|
|
&route_lookup_config.lookup_service, &error_list)) { |
|
|
|
|
if (!CoreConfiguration::Get().resolver_registry().IsValidTarget( |
|
|
|
|
route_lookup_config.lookup_service)) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"field:lookupService error:must be valid gRPC target URI")); |
|
|
|
|
rls_channel_service_config_ = it->second.Dump(); |
|
|
|
|
auto service_config = MakeRefCounted<ServiceConfigImpl>( |
|
|
|
|
ChannelArgs(), rls_channel_service_config_, it->second, &child_error); |
|
|
|
|
if (!GRPC_ERROR_IS_NONE(child_error)) { |
|
|
|
|
errors->AddError(grpc_error_std_string(child_error)); |
|
|
|
|
GRPC_ERROR_UNREF(child_error); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Parse lookupServiceTimeout.
|
|
|
|
|
route_lookup_config.lookup_service_timeout = kDefaultLookupServiceTimeout; |
|
|
|
|
ParseJsonObjectFieldAsDuration(json, "lookupServiceTimeout", |
|
|
|
|
&route_lookup_config.lookup_service_timeout, |
|
|
|
|
&error_list, /*required=*/false); |
|
|
|
|
// Parse maxAge.
|
|
|
|
|
route_lookup_config.max_age = kMaxMaxAge; |
|
|
|
|
bool max_age_set = ParseJsonObjectFieldAsDuration( |
|
|
|
|
json, "maxAge", &route_lookup_config.max_age, &error_list, |
|
|
|
|
/*required=*/false); |
|
|
|
|
// Clamp maxAge to the max allowed value.
|
|
|
|
|
if (route_lookup_config.max_age > kMaxMaxAge) { |
|
|
|
|
route_lookup_config.max_age = kMaxMaxAge; |
|
|
|
|
} |
|
|
|
|
// Parse staleAge.
|
|
|
|
|
route_lookup_config.stale_age = kMaxMaxAge; |
|
|
|
|
bool stale_age_set = ParseJsonObjectFieldAsDuration( |
|
|
|
|
json, "staleAge", &route_lookup_config.stale_age, &error_list, |
|
|
|
|
/*required=*/false); |
|
|
|
|
// If staleAge is set, then maxAge must also be set.
|
|
|
|
|
if (stale_age_set && !max_age_set) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"field:maxAge error:must be set if staleAge is set")); |
|
|
|
|
} |
|
|
|
|
// Ignore staleAge if greater than or equal to maxAge.
|
|
|
|
|
if (route_lookup_config.stale_age >= route_lookup_config.max_age) { |
|
|
|
|
route_lookup_config.stale_age = route_lookup_config.max_age; |
|
|
|
|
} |
|
|
|
|
// Parse cacheSizeBytes.
|
|
|
|
|
ParseJsonObjectField(json, "cacheSizeBytes", |
|
|
|
|
&route_lookup_config.cache_size_bytes, &error_list); |
|
|
|
|
if (route_lookup_config.cache_size_bytes <= 0) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"field:cacheSizeBytes error:must be greater than 0")); |
|
|
|
|
} |
|
|
|
|
// Clamp cacheSizeBytes to the max allowed value.
|
|
|
|
|
if (route_lookup_config.cache_size_bytes > kMaxCacheSizeBytes) { |
|
|
|
|
route_lookup_config.cache_size_bytes = kMaxCacheSizeBytes; |
|
|
|
|
} |
|
|
|
|
// Parse defaultTarget.
|
|
|
|
|
if (ParseJsonObjectField(json, "defaultTarget", |
|
|
|
|
&route_lookup_config.default_target, &error_list, |
|
|
|
|
/*required=*/false)) { |
|
|
|
|
if (route_lookup_config.default_target.empty()) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"field:defaultTarget error:must be non-empty if set")); |
|
|
|
|
// Validate childPolicyConfigTargetFieldName.
|
|
|
|
|
{ |
|
|
|
|
ScopedField field(errors, ".childPolicyConfigTargetFieldName"); |
|
|
|
|
if (!errors->FieldHasErrors() && |
|
|
|
|
child_policy_config_target_field_name_.empty()) { |
|
|
|
|
errors->AddError("must be non-empty"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
*error = |
|
|
|
|
GRPC_ERROR_CREATE_FROM_VECTOR("field:routeLookupConfig", &error_list); |
|
|
|
|
return route_lookup_config; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_error_handle ValidateChildPolicyList( |
|
|
|
|
const Json& child_policy_list, |
|
|
|
|
const std::string& child_policy_config_target_field_name, |
|
|
|
|
const std::string& default_target, Json* child_policy_config, |
|
|
|
|
RefCountedPtr<LoadBalancingPolicy::Config>* |
|
|
|
|
default_child_policy_parsed_config) { |
|
|
|
|
// Add target to each entry in the config proto.
|
|
|
|
|
*child_policy_config = child_policy_list; |
|
|
|
|
std::string target = |
|
|
|
|
default_target.empty() ? kFakeTargetFieldValue : default_target; |
|
|
|
|
grpc_error_handle error = InsertOrUpdateChildPolicyField( |
|
|
|
|
child_policy_config_target_field_name, target, child_policy_config); |
|
|
|
|
if (!GRPC_ERROR_IS_NONE(error)) return error; |
|
|
|
|
// Parse the config.
|
|
|
|
|
auto parsed_config = |
|
|
|
|
CoreConfiguration::Get().lb_policy_registry().ParseLoadBalancingConfig( |
|
|
|
|
*child_policy_config); |
|
|
|
|
if (!parsed_config.ok()) { |
|
|
|
|
return absl_status_to_grpc_error(parsed_config.status()); |
|
|
|
|
} |
|
|
|
|
// Find the chosen config and return it in JSON form.
|
|
|
|
|
// We remove all non-selected configs, and in the selected config, we leave
|
|
|
|
|
// the target field in place, set to the default value. This slightly
|
|
|
|
|
// optimizes what we need to do later when we update a child policy for a
|
|
|
|
|
// given target.
|
|
|
|
|
for (Json& config : *(child_policy_config->mutable_array())) { |
|
|
|
|
if (config.object_value().begin()->first == (*parsed_config)->name()) { |
|
|
|
|
Json save_config = std::move(config); |
|
|
|
|
child_policy_config->mutable_array()->clear(); |
|
|
|
|
child_policy_config->mutable_array()->push_back(std::move(save_config)); |
|
|
|
|
break; |
|
|
|
|
// Parse childPolicy.
|
|
|
|
|
{ |
|
|
|
|
ScopedField field(errors, ".childPolicy"); |
|
|
|
|
auto it = json.object_value().find("childPolicy"); |
|
|
|
|
if (it == json.object_value().end()) { |
|
|
|
|
errors->AddError("field not present"); |
|
|
|
|
} else { |
|
|
|
|
// Add target to all child policy configs in the list.
|
|
|
|
|
child_policy_config_ = it->second; |
|
|
|
|
std::string target = route_lookup_config_.default_target.empty() |
|
|
|
|
? kFakeTargetFieldValue |
|
|
|
|
: route_lookup_config_.default_target; |
|
|
|
|
if (InsertOrUpdateChildPolicyField(child_policy_config_target_field_name_, |
|
|
|
|
target, &child_policy_config_, |
|
|
|
|
errors)) { |
|
|
|
|
// Parse the config.
|
|
|
|
|
auto parsed_config = |
|
|
|
|
CoreConfiguration::Get() |
|
|
|
|
.lb_policy_registry() |
|
|
|
|
.ParseLoadBalancingConfig(child_policy_config_); |
|
|
|
|
if (!parsed_config.ok()) { |
|
|
|
|
errors->AddError(parsed_config.status().message()); |
|
|
|
|
} else { |
|
|
|
|
// Find the chosen config and return it in JSON form.
|
|
|
|
|
// We remove all non-selected configs, and in the selected config,
|
|
|
|
|
// we leave the target field in place, set to the default value.
|
|
|
|
|
// This slightly optimizes what we need to do later when we update
|
|
|
|
|
// a child policy for a given target.
|
|
|
|
|
for (Json& config : *(child_policy_config_.mutable_array())) { |
|
|
|
|
if (config.object_value().begin()->first == |
|
|
|
|
(*parsed_config)->name()) { |
|
|
|
|
Json save_config = std::move(config); |
|
|
|
|
child_policy_config_.mutable_array()->clear(); |
|
|
|
|
child_policy_config_.mutable_array()->push_back( |
|
|
|
|
std::move(save_config)); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// If default target is set, set the default child config.
|
|
|
|
|
if (!route_lookup_config_.default_target.empty()) { |
|
|
|
|
default_child_policy_parsed_config_ = std::move(*parsed_config); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// If default target is set, return the parsed config.
|
|
|
|
|
if (!default_target.empty()) { |
|
|
|
|
*default_child_policy_parsed_config = std::move(*parsed_config); |
|
|
|
|
} |
|
|
|
|
return GRPC_ERROR_NONE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class RlsLbFactory : public LoadBalancingPolicyFactory { |
|
|
|
@ -2495,83 +2491,9 @@ class RlsLbFactory : public LoadBalancingPolicyFactory { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
absl::StatusOr<RefCountedPtr<LoadBalancingPolicy::Config>> |
|
|
|
|
ParseLoadBalancingConfig(const Json& config) const override { |
|
|
|
|
std::vector<grpc_error_handle> error_list; |
|
|
|
|
// Parse routeLookupConfig.
|
|
|
|
|
RlsLbConfig::RouteLookupConfig route_lookup_config; |
|
|
|
|
const Json::Object* route_lookup_config_json = nullptr; |
|
|
|
|
if (ParseJsonObjectField(config.object_value(), "routeLookupConfig", |
|
|
|
|
&route_lookup_config_json, &error_list)) { |
|
|
|
|
grpc_error_handle child_error = GRPC_ERROR_NONE; |
|
|
|
|
route_lookup_config = |
|
|
|
|
ParseRouteLookupConfig(*route_lookup_config_json, &child_error); |
|
|
|
|
if (!GRPC_ERROR_IS_NONE(child_error)) error_list.push_back(child_error); |
|
|
|
|
} |
|
|
|
|
// Parse routeLookupChannelServiceConfig.
|
|
|
|
|
std::string rls_channel_service_config; |
|
|
|
|
const Json::Object* rls_channel_service_config_json_obj = nullptr; |
|
|
|
|
if (ParseJsonObjectField(config.object_value(), |
|
|
|
|
"routeLookupChannelServiceConfig", |
|
|
|
|
&rls_channel_service_config_json_obj, &error_list, |
|
|
|
|
/*required=*/false)) { |
|
|
|
|
grpc_error_handle child_error = GRPC_ERROR_NONE; |
|
|
|
|
Json rls_channel_service_config_json( |
|
|
|
|
*rls_channel_service_config_json_obj); |
|
|
|
|
rls_channel_service_config = rls_channel_service_config_json.Dump(); |
|
|
|
|
auto service_config = MakeRefCounted<ServiceConfigImpl>( |
|
|
|
|
ChannelArgs(), rls_channel_service_config, |
|
|
|
|
std::move(rls_channel_service_config_json), &child_error); |
|
|
|
|
if (!GRPC_ERROR_IS_NONE(child_error)) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( |
|
|
|
|
"field:routeLookupChannelServiceConfig", &child_error, 1)); |
|
|
|
|
GRPC_ERROR_UNREF(child_error); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Parse childPolicyConfigTargetFieldName.
|
|
|
|
|
std::string child_policy_config_target_field_name; |
|
|
|
|
if (ParseJsonObjectField( |
|
|
|
|
config.object_value(), "childPolicyConfigTargetFieldName", |
|
|
|
|
&child_policy_config_target_field_name, &error_list)) { |
|
|
|
|
if (child_policy_config_target_field_name.empty()) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"field:childPolicyConfigTargetFieldName error:must be non-empty")); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Parse childPolicy.
|
|
|
|
|
Json child_policy_config; |
|
|
|
|
RefCountedPtr<LoadBalancingPolicy::Config> |
|
|
|
|
default_child_policy_parsed_config; |
|
|
|
|
auto it = config.object_value().find("childPolicy"); |
|
|
|
|
if (it == config.object_value().end()) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"field:childPolicy error:does not exist.")); |
|
|
|
|
} else if (it->second.type() != Json::Type::ARRAY) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"field:childPolicy error:type should be ARRAY")); |
|
|
|
|
} else { |
|
|
|
|
grpc_error_handle child_error = ValidateChildPolicyList( |
|
|
|
|
it->second, child_policy_config_target_field_name, |
|
|
|
|
route_lookup_config.default_target, &child_policy_config, |
|
|
|
|
&default_child_policy_parsed_config); |
|
|
|
|
if (!GRPC_ERROR_IS_NONE(child_error)) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( |
|
|
|
|
"field:childPolicy", &child_error, 1)); |
|
|
|
|
GRPC_ERROR_UNREF(child_error); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Return result.
|
|
|
|
|
if (!error_list.empty()) { |
|
|
|
|
grpc_error_handle error = GRPC_ERROR_CREATE_FROM_VECTOR( |
|
|
|
|
"errors parsing RLS LB policy config", &error_list); |
|
|
|
|
std::string error_string = grpc_error_std_string(error); |
|
|
|
|
GRPC_ERROR_UNREF(error); |
|
|
|
|
return absl::InvalidArgumentError(error_string); |
|
|
|
|
} |
|
|
|
|
return MakeRefCounted<RlsLbConfig>( |
|
|
|
|
std::move(route_lookup_config), std::move(rls_channel_service_config), |
|
|
|
|
std::move(child_policy_config), |
|
|
|
|
std::move(child_policy_config_target_field_name), |
|
|
|
|
std::move(default_child_policy_parsed_config)); |
|
|
|
|
ParseLoadBalancingConfig(const Json& json) const override { |
|
|
|
|
return LoadRefCountedFromJson<RlsLbConfig>( |
|
|
|
|
json, JsonArgs(), "errors validing RLS LB policy config"); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
@ -2582,6 +2504,4 @@ void RegisterRlsLbPolicy(CoreConfiguration::Builder* builder) { |
|
|
|
|
absl::make_unique<RlsLbFactory>()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void RlsLbPluginShutdown() {} |
|
|
|
|
|
|
|
|
|
} // namespace grpc_core
|
|
|
|
|