From 42f123efbecb10445aa129d7169495bd688d84c2 Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Wed, 20 Jan 2021 10:12:28 -0800 Subject: [PATCH] Add ability to read xDS bootstrap config from an env var. --- src/core/ext/xds/xds_bootstrap.cc | 73 ++++++++++++++++------------ src/core/ext/xds/xds_bootstrap.h | 19 +++++--- src/core/ext/xds/xds_client.cc | 4 +- test/cpp/end2end/xds_end2end_test.cc | 39 +++++++++++++-- 4 files changed, 92 insertions(+), 43 deletions(-) diff --git a/src/core/ext/xds/xds_bootstrap.cc b/src/core/ext/xds/xds_bootstrap.cc index 95f4c3de1e3..acc9e216d72 100644 --- a/src/core/ext/xds/xds_bootstrap.cc +++ b/src/core/ext/xds/xds_bootstrap.cc @@ -157,38 +157,51 @@ std::unique_ptr ParseJsonAndCreate( } // namespace -std::unique_ptr XdsBootstrap::ReadFromFile( - XdsClient* client, TraceFlag* tracer, const char* fallback_config, - grpc_error** error) { +std::unique_ptr XdsBootstrap::Create(XdsClient* client, + TraceFlag* tracer, + const char* fallback_config, + grpc_error** error) { + // First, try GRPC_XDS_BOOTSTRAP env var. grpc_core::UniquePtr path(gpr_getenv("GRPC_XDS_BOOTSTRAP")); - if (path == nullptr) { - if (fallback_config != nullptr) { - return ParseJsonAndCreate(client, tracer, fallback_config, - "fallback config", error); + if (path != nullptr) { + if (GRPC_TRACE_FLAG_ENABLED(*tracer)) { + gpr_log(GPR_INFO, + "[xds_client %p] Got bootstrap file location from " + "GRPC_XDS_BOOTSTRAP environment variable: %s", + client, path.get()); } - *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Environment variable GRPC_XDS_BOOTSTRAP not defined"); - return nullptr; - } - if (GRPC_TRACE_FLAG_ENABLED(*tracer)) { - gpr_log(GPR_INFO, - "[xds_client %p] Got bootstrap file location from " - "GRPC_XDS_BOOTSTRAP environment variable: %s", - client, path.get()); - } - grpc_slice contents; - *error = grpc_load_file(path.get(), /*add_null_terminator=*/true, &contents); - if (*error != GRPC_ERROR_NONE) return nullptr; - absl::string_view contents_str_view = StringViewFromSlice(contents); - if (GRPC_TRACE_FLAG_ENABLED(*tracer)) { - gpr_log(GPR_DEBUG, "[xds_client %p] Bootstrap file contents: %s", client, - std::string(contents_str_view).c_str()); - } - std::string bootstrap_source = absl::StrCat("file ", path.get()); - auto result = ParseJsonAndCreate(client, tracer, contents_str_view, - bootstrap_source, error); - grpc_slice_unref_internal(contents); - return result; + grpc_slice contents; + *error = + grpc_load_file(path.get(), /*add_null_terminator=*/true, &contents); + if (*error != GRPC_ERROR_NONE) return nullptr; + absl::string_view contents_str_view = StringViewFromSlice(contents); + if (GRPC_TRACE_FLAG_ENABLED(*tracer)) { + gpr_log(GPR_DEBUG, "[xds_client %p] Bootstrap file contents: %s", client, + std::string(contents_str_view).c_str()); + } + std::string bootstrap_source = absl::StrCat("file ", path.get()); + auto result = ParseJsonAndCreate(client, tracer, contents_str_view, + bootstrap_source, error); + grpc_slice_unref_internal(contents); + return result; + } + // Next, try GRPC_XDS_BOOTSTRAP_CONFIG env var. + grpc_core::UniquePtr env_config( + gpr_getenv("GRPC_XDS_BOOTSTRAP_CONFIG")); + if (env_config != nullptr) { + return ParseJsonAndCreate(client, tracer, env_config.get(), + "GRPC_XDS_BOOTSTRAP_CONFIG env var", error); + } + // Finally, try fallback config. + if (fallback_config != nullptr) { + return ParseJsonAndCreate(client, tracer, fallback_config, + "fallback config", error); + } + // No bootstrap config found. + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Environment variables GRPC_XDS_BOOTSTRAP or GRPC_XDS_BOOTSTRAP_CONFIG " + "not defined"); + return nullptr; } XdsBootstrap::XdsBootstrap(Json json, grpc_error** error) { diff --git a/src/core/ext/xds/xds_bootstrap.h b/src/core/ext/xds/xds_bootstrap.h index 5d1f9095858..c4ae4d4457f 100644 --- a/src/core/ext/xds/xds_bootstrap.h +++ b/src/core/ext/xds/xds_bootstrap.h @@ -67,14 +67,19 @@ class XdsBootstrap { bool ShouldUseV3() const; }; - // Normally locates the bootstrap file via an env var. If no env var - // is set, fallback_config will be used instead (if non-null). + // Creates bootstrap object, obtaining the bootstrap JSON as appropriate + // for the environment: + // - If the GRPC_XDS_BOOTSTRAP env var is set, reads the file it specifies + // to obtain the bootstrap JSON. + // - Otherwise, if the GRPC_XDS_BOOTSTRAP_CONFIG env var is set, reads the + // content of that env var to obtain the bootstrap JSON. + // - Otherwise, the JSON will be read from fallback_config (if non-null). // If *error is not GRPC_ERROR_NONE after returning, then there was an - // error reading the file. - static std::unique_ptr ReadFromFile(XdsClient* client, - TraceFlag* tracer, - const char* fallback_config, - grpc_error** error); + // error (e.g., no config found or error reading the file). + static std::unique_ptr Create(XdsClient* client, + TraceFlag* tracer, + const char* fallback_config, + grpc_error** error); // Do not instantiate directly -- use ReadFromFile() above instead. XdsBootstrap(Json json, grpc_error** error); diff --git a/src/core/ext/xds/xds_client.cc b/src/core/ext/xds/xds_client.cc index 5b360eb0bdc..5189dc10afe 100644 --- a/src/core/ext/xds/xds_client.cc +++ b/src/core/ext/xds/xds_client.cc @@ -1734,8 +1734,8 @@ XdsClient::XdsClient(grpc_error** error) : nullptr), request_timeout_(GetRequestTimeout()), interested_parties_(grpc_pollset_set_create()), - bootstrap_(XdsBootstrap::ReadFromFile( - this, &grpc_xds_client_trace, g_fallback_bootstrap_config, error)), + bootstrap_(XdsBootstrap::Create(this, &grpc_xds_client_trace, + g_fallback_bootstrap_config, error)), certificate_provider_store_(MakeOrphanable( bootstrap_ == nullptr ? CertificateProviderStore::PluginDefinitionMap() diff --git a/test/cpp/end2end/xds_end2end_test.cc b/test/cpp/end2end/xds_end2end_test.cc index 2fad87d9d90..026d02c9fdb 100644 --- a/test/cpp/end2end/xds_end2end_test.cc +++ b/test/cpp/end2end/xds_end2end_test.cc @@ -1506,17 +1506,25 @@ class XdsEnd2endTest : public ::testing::TestWithParam { protected: XdsEnd2endTest(size_t num_backends, size_t num_balancers, int client_load_reporting_interval_seconds = 100, - bool use_xds_enabled_server = false) + bool use_xds_enabled_server = false, + bool bootstrap_contents_from_env_var = false) : num_backends_(num_backends), num_balancers_(num_balancers), client_load_reporting_interval_seconds_( client_load_reporting_interval_seconds), - use_xds_enabled_server_(use_xds_enabled_server) {} + use_xds_enabled_server_(use_xds_enabled_server), + bootstrap_contents_from_env_var_(bootstrap_contents_from_env_var) {} void SetUp() override { gpr_setenv("GRPC_XDS_EXPERIMENTAL_V3_SUPPORT", "true"); - gpr_setenv("GRPC_XDS_BOOTSTRAP", - GetParam().use_v2() ? g_bootstrap_file_v2 : g_bootstrap_file_v3); + if (bootstrap_contents_from_env_var_) { + gpr_setenv("GRPC_XDS_BOOTSTRAP_CONFIG", + GetParam().use_v2() ? kBootstrapFileV2 : kBootstrapFileV3); + } else { + gpr_setenv("GRPC_XDS_BOOTSTRAP", GetParam().use_v2() + ? g_bootstrap_file_v2 + : g_bootstrap_file_v3); + } g_port_saver->Reset(); bool localhost_resolves_to_ipv4 = false; bool localhost_resolves_to_ipv6 = false; @@ -1597,6 +1605,8 @@ class XdsEnd2endTest : public ::testing::TestWithParam { // Clear global xDS channel args, since they will go out of scope // when this test object is destroyed. grpc_core::internal::SetXdsChannelArgsForTest(nullptr); + gpr_unsetenv("GRPC_XDS_BOOTSTRAP"); + gpr_unsetenv("GRPC_XDS_BOOTSTRAP_CONFIG"); } void StartAllBackends() { @@ -2272,6 +2282,7 @@ class XdsEnd2endTest : public ::testing::TestWithParam { RouteConfiguration default_route_config_; Cluster default_cluster_; bool use_xds_enabled_server_; + bool bootstrap_contents_from_env_var_; }; class BasicTest : public XdsEnd2endTest { @@ -7833,6 +7844,22 @@ TEST_P(ClientLoadReportingWithDropTest, Vanilla) { kDropRateForThrottle * (1 + kErrorTolerance)))); } +class BootstrapContentsFromEnvVarTest : public XdsEnd2endTest { + public: + BootstrapContentsFromEnvVarTest() : XdsEnd2endTest(4, 1, 100, false, true) {} +}; + +TEST_P(BootstrapContentsFromEnvVarTest, Vanilla) { + SetNextResolution({}); + SetNextResolutionForLbChannelAllBalancers(); + AdsServiceImpl::EdsResourceArgs args({ + {"locality0", GetBackendPorts()}, + }); + balancers_[0]->ads_service()->SetEdsResource( + BuildEdsResource(args, DefaultEdsServiceName())); + WaitForAllBackends(); +} + std::string TestTypeName(const ::testing::TestParamInfo& info) { return info.param.AsString(); } @@ -7967,6 +7994,10 @@ INSTANTIATE_TEST_SUITE_P(XdsTest, ClientLoadReportingWithDropTest, TestType(true, true)), &TestTypeName); +INSTANTIATE_TEST_SUITE_P(XdsTest, BootstrapContentsFromEnvVarTest, + ::testing::Values(TestType(true, false)), + &TestTypeName); + } // namespace } // namespace testing } // namespace grpc