|
|
|
@ -38,409 +38,271 @@ |
|
|
|
|
#include "src/core/lib/config/core_configuration.h" |
|
|
|
|
#include "src/core/lib/gprpp/ref_counted_ptr.h" |
|
|
|
|
#include "src/core/lib/json/json.h" |
|
|
|
|
#include "src/core/lib/json/json_util.h" |
|
|
|
|
#include "src/core/lib/json/json_object_loader.h" |
|
|
|
|
#include "src/core/lib/security/certificate_provider/certificate_provider_factory.h" |
|
|
|
|
#include "src/core/lib/security/certificate_provider/certificate_provider_registry.h" |
|
|
|
|
#include "src/core/lib/security/credentials/channel_creds_registry.h" |
|
|
|
|
|
|
|
|
|
namespace grpc_core { |
|
|
|
|
|
|
|
|
|
namespace { |
|
|
|
|
//
|
|
|
|
|
// GrpcXdsBootstrap::GrpcNode::Locality
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
grpc_error_handle ParseChannelCreds(const Json::Object& json, size_t idx, |
|
|
|
|
XdsBootstrap::XdsServer* server) { |
|
|
|
|
std::vector<grpc_error_handle> error_list; |
|
|
|
|
std::string type; |
|
|
|
|
ParseJsonObjectField(json, "type", &type, &error_list); |
|
|
|
|
const Json::Object* config_ptr = nullptr; |
|
|
|
|
ParseJsonObjectField(json, "config", &config_ptr, &error_list, |
|
|
|
|
/*required=*/false); |
|
|
|
|
// Select the first channel creds type that we support.
|
|
|
|
|
if (server->channel_creds_type.empty() && |
|
|
|
|
CoreConfiguration::Get().channel_creds_registry().IsSupported(type)) { |
|
|
|
|
Json config; |
|
|
|
|
if (config_ptr != nullptr) config = *config_ptr; |
|
|
|
|
if (!CoreConfiguration::Get().channel_creds_registry().IsValidConfig( |
|
|
|
|
type, config)) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING(absl::StrCat( |
|
|
|
|
"invalid config for channel creds type \"", type, "\""))); |
|
|
|
|
} |
|
|
|
|
server->channel_creds_type = std::move(type); |
|
|
|
|
server->channel_creds_config = std::move(config); |
|
|
|
|
} |
|
|
|
|
return GRPC_ERROR_CREATE_FROM_VECTOR_AND_CPP_STRING( |
|
|
|
|
absl::StrCat("errors parsing index ", idx), &error_list); |
|
|
|
|
const JsonLoaderInterface* GrpcXdsBootstrap::GrpcNode::Locality::JsonLoader( |
|
|
|
|
const JsonArgs&) { |
|
|
|
|
static const auto* loader = |
|
|
|
|
JsonObjectLoader<Locality>() |
|
|
|
|
.OptionalField("region", &Locality::region) |
|
|
|
|
.OptionalField("zone", &Locality::zone) |
|
|
|
|
.OptionalField("sub_zone", &Locality::sub_zone) |
|
|
|
|
.Finish(); |
|
|
|
|
return loader; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_error_handle ParseChannelCredsArray(const Json::Array& json, |
|
|
|
|
XdsBootstrap::XdsServer* server) { |
|
|
|
|
std::vector<grpc_error_handle> error_list; |
|
|
|
|
for (size_t i = 0; i < json.size(); ++i) { |
|
|
|
|
const Json& child = json.at(i); |
|
|
|
|
if (child.type() != Json::Type::OBJECT) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING( |
|
|
|
|
absl::StrCat("array element ", i, " is not an object"))); |
|
|
|
|
} else { |
|
|
|
|
grpc_error_handle parse_error = |
|
|
|
|
ParseChannelCreds(child.object_value(), i, server); |
|
|
|
|
if (!GRPC_ERROR_IS_NONE(parse_error)) error_list.push_back(parse_error); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (server->channel_creds_type.empty()) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"no known creds type found in \"channel_creds\"")); |
|
|
|
|
} |
|
|
|
|
return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"channel_creds\" array", |
|
|
|
|
&error_list); |
|
|
|
|
//
|
|
|
|
|
// GrpcXdsBootstrap::GrpcNode
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
const JsonLoaderInterface* GrpcXdsBootstrap::GrpcNode::JsonLoader( |
|
|
|
|
const JsonArgs&) { |
|
|
|
|
static const auto* loader = |
|
|
|
|
JsonObjectLoader<GrpcNode>() |
|
|
|
|
.OptionalField("id", &GrpcNode::id_) |
|
|
|
|
.OptionalField("cluster", &GrpcNode::cluster_) |
|
|
|
|
.OptionalField("locality", &GrpcNode::locality_) |
|
|
|
|
.OptionalField("metadata", &GrpcNode::metadata_) |
|
|
|
|
.Finish(); |
|
|
|
|
return loader; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
//
|
|
|
|
|
// GrpcXdsBootstrap::GrpcXdsServer::ChannelCreds
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
const JsonLoaderInterface* |
|
|
|
|
GrpcXdsBootstrap::GrpcXdsServer::ChannelCreds::JsonLoader(const JsonArgs&) { |
|
|
|
|
static const auto* loader = |
|
|
|
|
JsonObjectLoader<ChannelCreds>() |
|
|
|
|
.Field("type", &ChannelCreds::type) |
|
|
|
|
.OptionalField("config", &ChannelCreds::config) |
|
|
|
|
.Finish(); |
|
|
|
|
return loader; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// GrpcXdsBootstrap
|
|
|
|
|
// GrpcXdsBootstrap::GrpcXdsServer
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<GrpcXdsBootstrap> GrpcXdsBootstrap::Create( |
|
|
|
|
absl::string_view json_string, grpc_error_handle* error) { |
|
|
|
|
auto json = Json::Parse(json_string); |
|
|
|
|
if (!json.ok()) { |
|
|
|
|
*error = GRPC_ERROR_CREATE_FROM_CPP_STRING(absl::StrCat( |
|
|
|
|
"Failed to parse bootstrap JSON string: ", json.status().ToString())); |
|
|
|
|
return nullptr; |
|
|
|
|
} |
|
|
|
|
return absl::make_unique<GrpcXdsBootstrap>(std::move(*json), error); |
|
|
|
|
namespace { |
|
|
|
|
|
|
|
|
|
constexpr absl::string_view kServerFeatureXdsV3 = "xds_v3"; |
|
|
|
|
constexpr absl::string_view kServerFeatureIgnoreResourceDeletion = |
|
|
|
|
"ignore_resource_deletion"; |
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
bool GrpcXdsBootstrap::GrpcXdsServer::ShouldUseV3() const { |
|
|
|
|
return server_features_.find(std::string(kServerFeatureXdsV3)) != |
|
|
|
|
server_features_.end(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
GrpcXdsBootstrap::GrpcXdsBootstrap(Json json, grpc_error_handle* error) { |
|
|
|
|
if (json.type() != Json::Type::OBJECT) { |
|
|
|
|
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"malformed JSON in bootstrap file"); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
std::vector<grpc_error_handle> error_list; |
|
|
|
|
auto it = json.mutable_object()->find("xds_servers"); |
|
|
|
|
if (it == json.mutable_object()->end()) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"\"xds_servers\" field not present")); |
|
|
|
|
} else if (it->second.type() != Json::Type::ARRAY) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"\"xds_servers\" field is not an array")); |
|
|
|
|
} else { |
|
|
|
|
grpc_error_handle parse_error = ParseXdsServerList(&it->second, &servers_); |
|
|
|
|
if (!GRPC_ERROR_IS_NONE(parse_error)) error_list.push_back(parse_error); |
|
|
|
|
} |
|
|
|
|
it = json.mutable_object()->find("node"); |
|
|
|
|
if (it != json.mutable_object()->end()) { |
|
|
|
|
if (it->second.type() != Json::Type::OBJECT) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"\"node\" field is not an object")); |
|
|
|
|
} else { |
|
|
|
|
grpc_error_handle parse_error = ParseNode(&it->second); |
|
|
|
|
if (!GRPC_ERROR_IS_NONE(parse_error)) error_list.push_back(parse_error); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (XdsFederationEnabled()) { |
|
|
|
|
it = json.mutable_object()->find("authorities"); |
|
|
|
|
if (it != json.mutable_object()->end()) { |
|
|
|
|
if (it->second.type() != Json::Type::OBJECT) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"\"authorities\" field is not an object")); |
|
|
|
|
} else { |
|
|
|
|
grpc_error_handle parse_error = ParseAuthorities(&it->second); |
|
|
|
|
if (!GRPC_ERROR_IS_NONE(parse_error)) error_list.push_back(parse_error); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
it = json.mutable_object()->find( |
|
|
|
|
"client_default_listener_resource_name_template"); |
|
|
|
|
if (it != json.mutable_object()->end()) { |
|
|
|
|
if (it->second.type() != Json::Type::STRING) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"\"client_default_listener_resource_name_template\" field is not a " |
|
|
|
|
"string")); |
|
|
|
|
} else { |
|
|
|
|
client_default_listener_resource_name_template_ = |
|
|
|
|
std::move(*it->second.mutable_string_value()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
it = json.mutable_object()->find("server_listener_resource_name_template"); |
|
|
|
|
if (it != json.mutable_object()->end()) { |
|
|
|
|
if (it->second.type() != Json::Type::STRING) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"\"server_listener_resource_name_template\" field is not a string")); |
|
|
|
|
} else { |
|
|
|
|
server_listener_resource_name_template_ = |
|
|
|
|
std::move(*it->second.mutable_string_value()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
it = json.mutable_object()->find("certificate_providers"); |
|
|
|
|
if (it != json.mutable_object()->end()) { |
|
|
|
|
if (it->second.type() != Json::Type::OBJECT) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"\"certificate_providers\" field is not an object")); |
|
|
|
|
} else { |
|
|
|
|
grpc_error_handle parse_error = ParseCertificateProviders(&it->second); |
|
|
|
|
if (!GRPC_ERROR_IS_NONE(parse_error)) error_list.push_back(parse_error); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
*error = GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing xds bootstrap file", |
|
|
|
|
&error_list); |
|
|
|
|
bool GrpcXdsBootstrap::GrpcXdsServer::IgnoreResourceDeletion() const { |
|
|
|
|
return server_features_.find(std::string( |
|
|
|
|
kServerFeatureIgnoreResourceDeletion)) != server_features_.end(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_error_handle GrpcXdsBootstrap::ParseXdsServerList( |
|
|
|
|
Json* json, std::vector<XdsServer>* servers) { |
|
|
|
|
std::vector<grpc_error_handle> error_list; |
|
|
|
|
for (size_t i = 0; i < json->mutable_array()->size(); ++i) { |
|
|
|
|
Json& child = json->mutable_array()->at(i); |
|
|
|
|
if (child.type() != Json::Type::OBJECT) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING( |
|
|
|
|
absl::StrCat("array element ", i, " is not an object"))); |
|
|
|
|
} else { |
|
|
|
|
grpc_error_handle parse_error; |
|
|
|
|
servers->emplace_back(XdsServerParse(child, &parse_error)); |
|
|
|
|
if (!GRPC_ERROR_IS_NONE(parse_error)) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING( |
|
|
|
|
absl::StrCat("errors parsing index ", i))); |
|
|
|
|
error_list.push_back(parse_error); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"xds_servers\" array", |
|
|
|
|
&error_list); |
|
|
|
|
bool GrpcXdsBootstrap::GrpcXdsServer::operator==(const XdsServer& other) const { |
|
|
|
|
const auto& o = static_cast<const GrpcXdsServer&>(other); |
|
|
|
|
return (server_uri_ == o.server_uri_ && |
|
|
|
|
channel_creds_.type == o.channel_creds_.type && |
|
|
|
|
channel_creds_.config == o.channel_creds_.config && |
|
|
|
|
server_features_ == o.server_features_); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_error_handle GrpcXdsBootstrap::ParseAuthorities(Json* json) { |
|
|
|
|
std::vector<grpc_error_handle> error_list; |
|
|
|
|
for (auto& p : *(json->mutable_object())) { |
|
|
|
|
if (p.second.type() != Json::Type::OBJECT) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING( |
|
|
|
|
"field:authorities element error: element is not a object")); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
grpc_error_handle parse_error = ParseAuthority(&p.second, p.first); |
|
|
|
|
if (!GRPC_ERROR_IS_NONE(parse_error)) error_list.push_back(parse_error); |
|
|
|
|
} |
|
|
|
|
return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"authorities\"", |
|
|
|
|
&error_list); |
|
|
|
|
const JsonLoaderInterface* GrpcXdsBootstrap::GrpcXdsServer::JsonLoader( |
|
|
|
|
const JsonArgs&) { |
|
|
|
|
static const auto* loader = |
|
|
|
|
JsonObjectLoader<GrpcXdsServer>() |
|
|
|
|
.Field("server_uri", &GrpcXdsServer::server_uri_) |
|
|
|
|
.Finish(); |
|
|
|
|
return loader; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_error_handle GrpcXdsBootstrap::ParseAuthority(Json* json, |
|
|
|
|
const std::string& name) { |
|
|
|
|
std::vector<grpc_error_handle> error_list; |
|
|
|
|
Authority authority; |
|
|
|
|
auto it = |
|
|
|
|
json->mutable_object()->find("client_listener_resource_name_template"); |
|
|
|
|
if (it != json->mutable_object()->end()) { |
|
|
|
|
if (it->second.type() != Json::Type::STRING) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"\"client_listener_resource_name_template\" field is not a string")); |
|
|
|
|
} else { |
|
|
|
|
std::string expected_prefix = absl::StrCat("xdstp://", name, "/"); |
|
|
|
|
if (!absl::StartsWith(it->second.string_value(), expected_prefix)) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING( |
|
|
|
|
absl::StrCat("\"client_listener_resource_name_template\" field " |
|
|
|
|
"must begin with \"", |
|
|
|
|
expected_prefix, "\""))); |
|
|
|
|
} else { |
|
|
|
|
authority.client_listener_resource_name_template = |
|
|
|
|
std::move(*it->second.mutable_string_value()); |
|
|
|
|
void GrpcXdsBootstrap::GrpcXdsServer::JsonPostLoad(const Json& json, |
|
|
|
|
const JsonArgs& args, |
|
|
|
|
ErrorList* errors) { |
|
|
|
|
// Parse "channel_creds".
|
|
|
|
|
auto channel_creds_list = LoadJsonObjectField<std::vector<ChannelCreds>>( |
|
|
|
|
json.object_value(), args, "channel_creds", errors); |
|
|
|
|
if (channel_creds_list.has_value()) { |
|
|
|
|
ScopedField field(errors, ".channel_creds"); |
|
|
|
|
for (size_t i = 0; i < channel_creds_list->size(); ++i) { |
|
|
|
|
ScopedField field(errors, absl::StrCat("[", i, "]")); |
|
|
|
|
auto& creds = (*channel_creds_list)[i]; |
|
|
|
|
// Select the first channel creds type that we support.
|
|
|
|
|
if (channel_creds_.type.empty() && |
|
|
|
|
CoreConfiguration::Get().channel_creds_registry().IsSupported( |
|
|
|
|
creds.type)) { |
|
|
|
|
if (!CoreConfiguration::Get().channel_creds_registry().IsValidConfig( |
|
|
|
|
creds.type, creds.config)) { |
|
|
|
|
errors->AddError(absl::StrCat( |
|
|
|
|
"invalid config for channel creds type \"", creds.type, "\"")); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
channel_creds_.type = std::move(creds.type); |
|
|
|
|
channel_creds_.config = std::move(creds.config); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
it = json->mutable_object()->find("xds_servers"); |
|
|
|
|
if (it != json->mutable_object()->end()) { |
|
|
|
|
if (it->second.type() != Json::Type::ARRAY) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"\"xds_servers\" field is not an array")); |
|
|
|
|
} else { |
|
|
|
|
grpc_error_handle parse_error = |
|
|
|
|
ParseXdsServerList(&it->second, &authority.xds_servers); |
|
|
|
|
if (!GRPC_ERROR_IS_NONE(parse_error)) error_list.push_back(parse_error); |
|
|
|
|
if (channel_creds_.type.empty()) { |
|
|
|
|
errors->AddError("no known creds type found"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (error_list.empty()) { |
|
|
|
|
authorities_[name] = std::move(authority); |
|
|
|
|
// Parse "server_features".
|
|
|
|
|
{ |
|
|
|
|
ScopedField field(errors, ".server_features"); |
|
|
|
|
auto it = json.object_value().find("server_features"); |
|
|
|
|
if (it != json.object_value().end()) { |
|
|
|
|
if (it->second.type() != Json::Type::ARRAY) { |
|
|
|
|
errors->AddError("is not an array"); |
|
|
|
|
} else { |
|
|
|
|
const Json::Array& array = it->second.array_value(); |
|
|
|
|
for (const Json& feature_json : array) { |
|
|
|
|
if (feature_json.type() == Json::Type::STRING && |
|
|
|
|
(feature_json.string_value() == kServerFeatureXdsV3 || |
|
|
|
|
feature_json.string_value() == |
|
|
|
|
kServerFeatureIgnoreResourceDeletion)) { |
|
|
|
|
server_features_.insert(feature_json.string_value()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return GRPC_ERROR_CREATE_FROM_VECTOR_AND_CPP_STRING( |
|
|
|
|
absl::StrCat("errors parsing authority ", name), &error_list); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_error_handle GrpcXdsBootstrap::ParseNode(Json* json) { |
|
|
|
|
std::vector<grpc_error_handle> error_list; |
|
|
|
|
node_ = absl::make_unique<Node>(); |
|
|
|
|
auto it = json->mutable_object()->find("id"); |
|
|
|
|
if (it != json->mutable_object()->end()) { |
|
|
|
|
if (it->second.type() != Json::Type::STRING) { |
|
|
|
|
error_list.push_back( |
|
|
|
|
GRPC_ERROR_CREATE_FROM_STATIC_STRING("\"id\" field is not a string")); |
|
|
|
|
} else { |
|
|
|
|
node_->id = std::move(*it->second.mutable_string_value()); |
|
|
|
|
} |
|
|
|
|
Json GrpcXdsBootstrap::GrpcXdsServer::ToJson() const { |
|
|
|
|
Json::Object channel_creds_json{{"type", channel_creds_.type}}; |
|
|
|
|
if (!channel_creds_.config.empty()) { |
|
|
|
|
channel_creds_json["config"] = channel_creds_.config; |
|
|
|
|
} |
|
|
|
|
it = json->mutable_object()->find("cluster"); |
|
|
|
|
if (it != json->mutable_object()->end()) { |
|
|
|
|
if (it->second.type() != Json::Type::STRING) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"\"cluster\" field is not a string")); |
|
|
|
|
} else { |
|
|
|
|
node_->cluster = std::move(*it->second.mutable_string_value()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
it = json->mutable_object()->find("locality"); |
|
|
|
|
if (it != json->mutable_object()->end()) { |
|
|
|
|
if (it->second.type() != Json::Type::OBJECT) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"\"locality\" field is not an object")); |
|
|
|
|
} else { |
|
|
|
|
grpc_error_handle parse_error = ParseLocality(&it->second); |
|
|
|
|
if (!GRPC_ERROR_IS_NONE(parse_error)) error_list.push_back(parse_error); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
it = json->mutable_object()->find("metadata"); |
|
|
|
|
if (it != json->mutable_object()->end()) { |
|
|
|
|
if (it->second.type() != Json::Type::OBJECT) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"\"metadata\" field is not an object")); |
|
|
|
|
} else { |
|
|
|
|
node_->metadata = std::move(it->second); |
|
|
|
|
Json::Object json{ |
|
|
|
|
{"server_uri", server_uri_}, |
|
|
|
|
{"channel_creds", Json::Array{std::move(channel_creds_json)}}, |
|
|
|
|
}; |
|
|
|
|
if (!server_features_.empty()) { |
|
|
|
|
Json::Array server_features_json; |
|
|
|
|
for (auto& feature : server_features_) { |
|
|
|
|
server_features_json.emplace_back(feature); |
|
|
|
|
} |
|
|
|
|
json["server_features"] = std::move(server_features_json); |
|
|
|
|
} |
|
|
|
|
return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"node\" object", |
|
|
|
|
&error_list); |
|
|
|
|
return json; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_error_handle GrpcXdsBootstrap::ParseLocality(Json* json) { |
|
|
|
|
std::vector<grpc_error_handle> error_list; |
|
|
|
|
auto it = json->mutable_object()->find("region"); |
|
|
|
|
if (it != json->mutable_object()->end()) { |
|
|
|
|
if (it->second.type() != Json::Type::STRING) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"\"region\" field is not a string")); |
|
|
|
|
} else { |
|
|
|
|
node_->locality_region = std::move(*it->second.mutable_string_value()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
it = json->mutable_object()->find("zone"); |
|
|
|
|
if (it != json->mutable_object()->end()) { |
|
|
|
|
if (it->second.type() != Json::Type::STRING) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"\"zone\" field is not a string")); |
|
|
|
|
} else { |
|
|
|
|
node_->locality_zone = std::move(*it->second.mutable_string_value()); |
|
|
|
|
} |
|
|
|
|
//
|
|
|
|
|
// GrpcXdsBootstrap::GrpcAuthority
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
const JsonLoaderInterface* GrpcXdsBootstrap::GrpcAuthority::JsonLoader( |
|
|
|
|
const JsonArgs&) { |
|
|
|
|
static const auto* loader = |
|
|
|
|
JsonObjectLoader<GrpcAuthority>() |
|
|
|
|
.OptionalField( |
|
|
|
|
"client_listener_resource_name_template", |
|
|
|
|
&GrpcAuthority::client_listener_resource_name_template_) |
|
|
|
|
.OptionalField("xds_servers", &GrpcAuthority::servers_) |
|
|
|
|
.Finish(); |
|
|
|
|
return loader; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// GrpcXdsBootstrap
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
absl::StatusOr<std::unique_ptr<GrpcXdsBootstrap>> GrpcXdsBootstrap::Create( |
|
|
|
|
absl::string_view json_string) { |
|
|
|
|
auto json = Json::Parse(json_string); |
|
|
|
|
if (!json.ok()) { |
|
|
|
|
return absl::InvalidArgumentError(absl::StrCat( |
|
|
|
|
"Failed to parse bootstrap JSON string: ", json.status().ToString())); |
|
|
|
|
} |
|
|
|
|
it = json->mutable_object()->find("sub_zone"); |
|
|
|
|
if (it != json->mutable_object()->end()) { |
|
|
|
|
if (it->second.type() != Json::Type::STRING) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"\"sub_zone\" field is not a string")); |
|
|
|
|
} else { |
|
|
|
|
node_->locality_sub_zone = std::move(*it->second.mutable_string_value()); |
|
|
|
|
// Validate JSON.
|
|
|
|
|
class XdsJsonArgs : public JsonArgs { |
|
|
|
|
public: |
|
|
|
|
bool IsEnabled(absl::string_view key) const override { |
|
|
|
|
if (key == "federation") return XdsFederationEnabled(); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"locality\" object", |
|
|
|
|
&error_list); |
|
|
|
|
}; |
|
|
|
|
auto bootstrap = LoadFromJson<GrpcXdsBootstrap>(*json, XdsJsonArgs()); |
|
|
|
|
if (!bootstrap.ok()) return bootstrap.status(); |
|
|
|
|
return absl::make_unique<GrpcXdsBootstrap>(std::move(*bootstrap)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_error_handle GrpcXdsBootstrap::ParseCertificateProviders(Json* json) { |
|
|
|
|
std::vector<grpc_error_handle> error_list; |
|
|
|
|
for (auto& certificate_provider : *(json->mutable_object())) { |
|
|
|
|
if (certificate_provider.second.type() != Json::Type::OBJECT) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING(absl::StrCat( |
|
|
|
|
"element \"", certificate_provider.first, "\" is not an object"))); |
|
|
|
|
} else { |
|
|
|
|
grpc_error_handle parse_error = ParseCertificateProvider( |
|
|
|
|
certificate_provider.first, &certificate_provider.second); |
|
|
|
|
if (!GRPC_ERROR_IS_NONE(parse_error)) error_list.push_back(parse_error); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return GRPC_ERROR_CREATE_FROM_VECTOR( |
|
|
|
|
"errors parsing \"certificate_providers\" object", &error_list); |
|
|
|
|
const JsonLoaderInterface* GrpcXdsBootstrap::JsonLoader(const JsonArgs&) { |
|
|
|
|
static const auto* loader = |
|
|
|
|
JsonObjectLoader<GrpcXdsBootstrap>() |
|
|
|
|
.Field("xds_servers", &GrpcXdsBootstrap::servers_) |
|
|
|
|
.OptionalField("node", &GrpcXdsBootstrap::node_) |
|
|
|
|
.OptionalField("certificate_providers", |
|
|
|
|
&GrpcXdsBootstrap::certificate_providers_) |
|
|
|
|
.OptionalField( |
|
|
|
|
"server_listener_resource_name_template", |
|
|
|
|
&GrpcXdsBootstrap::server_listener_resource_name_template_) |
|
|
|
|
.OptionalField("authorities", &GrpcXdsBootstrap::authorities_, |
|
|
|
|
"federation") |
|
|
|
|
.OptionalField("client_default_listener_resource_name_template", |
|
|
|
|
&GrpcXdsBootstrap:: |
|
|
|
|
client_default_listener_resource_name_template_, |
|
|
|
|
"federation") |
|
|
|
|
.Finish(); |
|
|
|
|
return loader; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_error_handle GrpcXdsBootstrap::ParseCertificateProvider( |
|
|
|
|
const std::string& instance_name, Json* certificate_provider_json) { |
|
|
|
|
std::vector<grpc_error_handle> error_list; |
|
|
|
|
auto it = certificate_provider_json->mutable_object()->find("plugin_name"); |
|
|
|
|
if (it == certificate_provider_json->mutable_object()->end()) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"\"plugin_name\" field not present")); |
|
|
|
|
} else if (it->second.type() != Json::Type::STRING) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"\"plugin_name\" field is not a string")); |
|
|
|
|
} else { |
|
|
|
|
std::string plugin_name = std::move(*(it->second.mutable_string_value())); |
|
|
|
|
// Find config JSON.
|
|
|
|
|
absl::optional<Json> config_json; |
|
|
|
|
it = certificate_provider_json->mutable_object()->find("config"); |
|
|
|
|
if (it != certificate_provider_json->mutable_object()->end()) { |
|
|
|
|
if (it->second.type() != Json::Type::OBJECT) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"\"config\" field is not an object")); |
|
|
|
|
} else { |
|
|
|
|
config_json = it->second; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
// "config" is an optional field, so default to an empty JSON object.
|
|
|
|
|
config_json = Json::Object(); |
|
|
|
|
} |
|
|
|
|
// Try to instantiate the provider.
|
|
|
|
|
CertificateProviderFactory* factory = |
|
|
|
|
CertificateProviderRegistry::LookupCertificateProviderFactory( |
|
|
|
|
plugin_name); |
|
|
|
|
if (factory == nullptr) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING( |
|
|
|
|
absl::StrCat("Unrecognized plugin name: ", plugin_name))); |
|
|
|
|
} else if (config_json.has_value()) { |
|
|
|
|
grpc_error_handle parse_error = GRPC_ERROR_NONE; |
|
|
|
|
RefCountedPtr<CertificateProviderFactory::Config> config = |
|
|
|
|
factory->CreateCertificateProviderConfig(*config_json, &parse_error); |
|
|
|
|
if (!GRPC_ERROR_IS_NONE(parse_error)) { |
|
|
|
|
error_list.push_back(parse_error); |
|
|
|
|
} else { |
|
|
|
|
certificate_providers_.insert( |
|
|
|
|
{instance_name, {std::move(plugin_name), std::move(config)}}); |
|
|
|
|
void GrpcXdsBootstrap::JsonPostLoad(const Json& /*json*/, |
|
|
|
|
const JsonArgs& /*args*/, |
|
|
|
|
ErrorList* errors) { |
|
|
|
|
// Verify that each authority has the right prefix in the
|
|
|
|
|
// client_listener_resource_name_template field.
|
|
|
|
|
{ |
|
|
|
|
ScopedField field(errors, ".authorities"); |
|
|
|
|
for (const auto& p : authorities_) { |
|
|
|
|
const std::string& name = p.first; |
|
|
|
|
const GrpcAuthority& authority = |
|
|
|
|
static_cast<const GrpcAuthority&>(p.second); |
|
|
|
|
ScopedField field( |
|
|
|
|
errors, absl::StrCat("[\"", name, |
|
|
|
|
"\"].client_listener_resource_name_template")); |
|
|
|
|
std::string expected_prefix = absl::StrCat("xdstp://", name, "/"); |
|
|
|
|
if (!authority.client_listener_resource_name_template().empty() && |
|
|
|
|
!absl::StartsWith(authority.client_listener_resource_name_template(), |
|
|
|
|
expected_prefix)) { |
|
|
|
|
errors->AddError( |
|
|
|
|
absl::StrCat("field must begin with \"", expected_prefix, "\"")); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return GRPC_ERROR_CREATE_FROM_VECTOR_AND_CPP_STRING( |
|
|
|
|
absl::StrCat("errors parsing element \"", instance_name, "\""), |
|
|
|
|
&error_list); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::string GrpcXdsBootstrap::ToString() const { |
|
|
|
|
std::vector<std::string> parts; |
|
|
|
|
if (node_ != nullptr) { |
|
|
|
|
parts.push_back(absl::StrFormat( |
|
|
|
|
"node={\n" |
|
|
|
|
" id=\"%s\",\n" |
|
|
|
|
" cluster=\"%s\",\n" |
|
|
|
|
" locality={\n" |
|
|
|
|
" region=\"%s\",\n" |
|
|
|
|
" zone=\"%s\",\n" |
|
|
|
|
" sub_zone=\"%s\"\n" |
|
|
|
|
" },\n" |
|
|
|
|
" metadata=%s,\n" |
|
|
|
|
"},\n", |
|
|
|
|
node_->id, node_->cluster, node_->locality_region, node_->locality_zone, |
|
|
|
|
node_->locality_sub_zone, node_->metadata.Dump())); |
|
|
|
|
if (node_.has_value()) { |
|
|
|
|
parts.push_back( |
|
|
|
|
absl::StrFormat("node={\n" |
|
|
|
|
" id=\"%s\",\n" |
|
|
|
|
" cluster=\"%s\",\n" |
|
|
|
|
" locality={\n" |
|
|
|
|
" region=\"%s\",\n" |
|
|
|
|
" zone=\"%s\",\n" |
|
|
|
|
" sub_zone=\"%s\"\n" |
|
|
|
|
" },\n" |
|
|
|
|
" metadata=%s,\n" |
|
|
|
|
"},\n", |
|
|
|
|
node_->id(), node_->cluster(), node_->locality_region(), |
|
|
|
|
node_->locality_zone(), node_->locality_sub_zone(), |
|
|
|
|
Json{node_->metadata()}.Dump())); |
|
|
|
|
} |
|
|
|
|
parts.push_back( |
|
|
|
|
absl::StrFormat("servers=[\n" |
|
|
|
|
" {\n" |
|
|
|
|
" uri=\"%s\",\n" |
|
|
|
|
" creds_type=%s,\n", |
|
|
|
|
server().server_uri, server().channel_creds_type)); |
|
|
|
|
if (server().channel_creds_config.type() != Json::Type::JSON_NULL) { |
|
|
|
|
parts.push_back(absl::StrFormat(" creds_config=%s,", |
|
|
|
|
server().channel_creds_config.Dump())); |
|
|
|
|
} |
|
|
|
|
if (!server().server_features.empty()) { |
|
|
|
|
parts.push_back(absl::StrCat(" server_features=[", |
|
|
|
|
absl::StrJoin(server().server_features, ", "), |
|
|
|
|
"],\n")); |
|
|
|
|
} |
|
|
|
|
parts.push_back(" }\n],\n"); |
|
|
|
|
absl::StrFormat("servers=[\n%s\n],\n", servers_[0].ToJson().Dump())); |
|
|
|
|
if (!client_default_listener_resource_name_template_.empty()) { |
|
|
|
|
parts.push_back(absl::StrFormat( |
|
|
|
|
"client_default_listener_resource_name_template=\"%s\",\n", |
|
|
|
@ -456,14 +318,14 @@ std::string GrpcXdsBootstrap::ToString() const { |
|
|
|
|
parts.push_back(absl::StrFormat(" %s={\n", entry.first)); |
|
|
|
|
parts.push_back( |
|
|
|
|
absl::StrFormat(" client_listener_resource_name_template=\"%s\",\n", |
|
|
|
|
entry.second.client_listener_resource_name_template)); |
|
|
|
|
parts.push_back( |
|
|
|
|
absl::StrFormat(" servers=[\n" |
|
|
|
|
" {\n" |
|
|
|
|
" uri=\"%s\",\n" |
|
|
|
|
" creds_type=%s,\n", |
|
|
|
|
entry.second.xds_servers[0].server_uri, |
|
|
|
|
entry.second.xds_servers[0].channel_creds_type)); |
|
|
|
|
entry.second.client_listener_resource_name_template())); |
|
|
|
|
if (entry.second.server() != nullptr) { |
|
|
|
|
parts.push_back(absl::StrFormat( |
|
|
|
|
" servers=[\n%s\n],\n", |
|
|
|
|
static_cast<const GrpcXdsServer*>(entry.second.server()) |
|
|
|
|
->ToJson() |
|
|
|
|
.Dump())); |
|
|
|
|
} |
|
|
|
|
parts.push_back(" },\n"); |
|
|
|
|
} |
|
|
|
|
parts.push_back("}\n"); |
|
|
|
@ -481,56 +343,28 @@ std::string GrpcXdsBootstrap::ToString() const { |
|
|
|
|
return absl::StrJoin(parts, ""); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
XdsBootstrap::XdsServer GrpcXdsBootstrap::XdsServerParse( |
|
|
|
|
const Json& json, grpc_error_handle* error) { |
|
|
|
|
std::vector<grpc_error_handle> error_list; |
|
|
|
|
XdsServer server; |
|
|
|
|
ParseJsonObjectField(json.object_value(), "server_uri", &server.server_uri, |
|
|
|
|
&error_list); |
|
|
|
|
const Json::Array* creds_array = nullptr; |
|
|
|
|
ParseJsonObjectField(json.object_value(), "channel_creds", &creds_array, |
|
|
|
|
&error_list); |
|
|
|
|
if (creds_array != nullptr) { |
|
|
|
|
grpc_error_handle parse_error = |
|
|
|
|
ParseChannelCredsArray(*creds_array, &server); |
|
|
|
|
if (!GRPC_ERROR_IS_NONE(parse_error)) error_list.push_back(parse_error); |
|
|
|
|
const XdsBootstrap::Authority* GrpcXdsBootstrap::LookupAuthority( |
|
|
|
|
const std::string& name) const { |
|
|
|
|
auto it = authorities_.find(name); |
|
|
|
|
if (it != authorities_.end()) { |
|
|
|
|
return &it->second; |
|
|
|
|
} |
|
|
|
|
const Json::Array* server_features_array = nullptr; |
|
|
|
|
ParseJsonObjectField(json.object_value(), "server_features", |
|
|
|
|
&server_features_array, &error_list, /*required=*/false); |
|
|
|
|
if (server_features_array != nullptr) { |
|
|
|
|
for (const Json& feature_json : *server_features_array) { |
|
|
|
|
if (feature_json.type() == Json::Type::STRING && |
|
|
|
|
(feature_json.string_value() == |
|
|
|
|
XdsBootstrap::XdsServer::kServerFeatureXdsV3 || |
|
|
|
|
feature_json.string_value() == |
|
|
|
|
XdsBootstrap::XdsServer::kServerFeatureIgnoreResourceDeletion)) { |
|
|
|
|
server.server_features.insert(feature_json.string_value()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
*error = GRPC_ERROR_CREATE_FROM_VECTOR_AND_CPP_STRING( |
|
|
|
|
"errors parsing xds server", &error_list); |
|
|
|
|
return server; |
|
|
|
|
return nullptr; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Json::Object GrpcXdsBootstrap::XdsServerToJson(const XdsServer& server) { |
|
|
|
|
Json::Object channel_creds_json{{"type", server.channel_creds_type}}; |
|
|
|
|
if (server.channel_creds_config.type() != Json::Type::JSON_NULL) { |
|
|
|
|
channel_creds_json["config"] = server.channel_creds_config; |
|
|
|
|
} |
|
|
|
|
Json::Object json{ |
|
|
|
|
{"server_uri", server.server_uri}, |
|
|
|
|
{"channel_creds", Json::Array{std::move(channel_creds_json)}}, |
|
|
|
|
}; |
|
|
|
|
if (!server.server_features.empty()) { |
|
|
|
|
Json::Array server_features_json; |
|
|
|
|
for (auto& feature : server.server_features) { |
|
|
|
|
server_features_json.emplace_back(feature); |
|
|
|
|
const XdsBootstrap::XdsServer* GrpcXdsBootstrap::FindXdsServer( |
|
|
|
|
const XdsBootstrap::XdsServer& server) const { |
|
|
|
|
if (static_cast<const GrpcXdsServer&>(server) == servers_[0]) { |
|
|
|
|
return &servers_[0]; |
|
|
|
|
} |
|
|
|
|
for (auto& p : authorities_) { |
|
|
|
|
const auto* authority_server = |
|
|
|
|
static_cast<const GrpcXdsServer*>(p.second.server()); |
|
|
|
|
if (authority_server != nullptr && *authority_server == server) { |
|
|
|
|
return authority_server; |
|
|
|
|
} |
|
|
|
|
json["server_features"] = std::move(server_features_json); |
|
|
|
|
} |
|
|
|
|
return json; |
|
|
|
|
return nullptr; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} // namespace grpc_core
|
|
|
|
|