From 5e7055b910c5c79c40a3a5f205b90e1874b8adc6 Mon Sep 17 00:00:00 2001 From: Yan Wang <68562925+yanw-bq@users.noreply.github.com> Date: Wed, 26 Apr 2023 10:41:23 -0700 Subject: [PATCH] China endpoint support (#140) * china endpoint support * nit --- driver/failover.h | 18 ++++-- driver/failover_handler.cc | 91 +++++++++++++++++++-------- unit_testing/failover_handler_test.cc | 87 +++++++++++++++++++++++++ unit_testing/test_utils.cc | 28 +++++++++ unit_testing/test_utils.h | 7 +++ 5 files changed, 198 insertions(+), 33 deletions(-) diff --git a/driver/failover.h b/driver/failover.h index 248ed1084..a4ae9e034 100644 --- a/driver/failover.h +++ b/driver/failover.h @@ -192,14 +192,15 @@ class FAILOVER_HANDLER { bool m_is_rds_custom_cluster = false; bool initialized = false; - bool is_dns_pattern_valid(std::string host); - bool is_rds_dns(std::string host); - bool is_rds_proxy_dns(std::string host); - bool is_rds_custom_cluster_dns(std::string host); + static bool is_dns_pattern_valid(std::string host); + static bool is_rds_dns(std::string host); + static bool is_rds_cluster_dns(std::string host); + static bool is_rds_proxy_dns(std::string host); + static bool is_rds_custom_cluster_dns(std::string host); SQLRETURN create_connection_and_initialize_topology(); SQLRETURN reconnect(bool failover_enabled); - std::string get_rds_cluster_host_url(std::string host); - std::string get_rds_instance_host_pattern(std::string host); + static std::string get_rds_cluster_host_url(std::string host); + static std::string get_rds_instance_host_pattern(std::string host); bool is_ipv4(std::string host); bool is_ipv6(std::string host); bool failover_to_reader(const char*& new_error_code, const char*& error_msg); @@ -210,6 +211,11 @@ class FAILOVER_HANDLER { std::shared_ptr metrics_container; std::chrono::steady_clock::time_point invoke_start_time_ms; std::chrono::steady_clock::time_point failover_start_time_ms; + +#ifdef UNIT_TEST_BUILD + // Allows for testing private methods + friend class TEST_UTILS; +#endif }; // ************************************************************************************************ diff --git a/driver/failover_handler.cc b/driver/failover_handler.cc index dc6bf757c..5724d2496 100644 --- a/driver/failover_handler.cc +++ b/driver/failover_handler.cc @@ -42,10 +42,25 @@ const std::regex AURORA_DNS_PATTERN( R"#((.+)\.(proxy-|cluster-|cluster-ro-|cluster-custom-)?([a-zA-Z0-9]+\.[a-zA-Z0-9\-]+\.rds\.amazonaws\.com))#", std::regex_constants::icase); const std::regex AURORA_PROXY_DNS_PATTERN( - R"#((.+)\.(proxy-[a-zA-Z0-9]+\.[a-zA-Z0-9\-]+\.rds\.amazonaws\.com))#", + R"#((.+)\.(proxy-)+([a-zA-Z0-9]+\.[a-zA-Z0-9\-]+\.rds\.amazonaws\.com))#", + std::regex_constants::icase); +const std::regex AURORA_CLUSTER_PATTERN( + R"#((.+)\.(cluster-|cluster-ro-)+([a-zA-Z0-9]+\.[a-zA-Z0-9\-]+\.rds\.amazonaws\.com))#", std::regex_constants::icase); const std::regex AURORA_CUSTOM_CLUSTER_PATTERN( - R"#((.+)\.(cluster-custom-[a-zA-Z0-9]+\.[a-zA-Z0-9\-]+\.rds\.amazonaws\.com))#", + R"#((.+)\.(cluster-custom-)+([a-zA-Z0-9]+\.[a-zA-Z0-9\-]+\.rds\.amazonaws\.com))#", + std::regex_constants::icase); +const std::regex AURORA_CHINA_DNS_PATTERN( + R"#((.+)\.(proxy-|cluster-|cluster-ro-|cluster-custom-)?([a-zA-Z0-9]+\.rds\.[a-zA-Z0-9\-]+\.amazonaws\.com\.cn))#", + std::regex_constants::icase); +const std::regex AURORA_CHINA_PROXY_DNS_PATTERN( + R"#((.+)\.(proxy-)+([a-zA-Z0-9]+\.rds\.[a-zA-Z0-9\-]+\.amazonaws\.com\.cn))#", + std::regex_constants::icase); +const std::regex AURORA_CHINA_CLUSTER_PATTERN( + R"#((.+)\.(cluster-|cluster-ro-)+([a-zA-Z0-9]+\.rds\.[a-zA-Z0-9\-]+\.amazonaws\.com\.cn))#", + std::regex_constants::icase); +const std::regex AURORA_CHINA_CUSTOM_CLUSTER_PATTERN( + R"#((.+)\.(cluster-custom-)+([a-zA-Z0-9]+\.rds\.[a-zA-Z0-9\-]+\.amazonaws\.com\.cn))#", std::regex_constants::icase); const std::regex IPV4_PATTERN( R"#(^(([1-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){1}(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){2}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$)#"); @@ -296,15 +311,19 @@ bool FAILOVER_HANDLER::is_dns_pattern_valid(std::string host) { } bool FAILOVER_HANDLER::is_rds_dns(std::string host) { - return std::regex_match(host, AURORA_DNS_PATTERN); + return std::regex_match(host, AURORA_DNS_PATTERN) || std::regex_match(host, AURORA_CHINA_DNS_PATTERN); +} + +bool FAILOVER_HANDLER::is_rds_cluster_dns(std::string host) { + return std::regex_match(host, AURORA_CLUSTER_PATTERN) || std::regex_match(host, AURORA_CHINA_CLUSTER_PATTERN); } bool FAILOVER_HANDLER::is_rds_proxy_dns(std::string host) { - return std::regex_match(host, AURORA_PROXY_DNS_PATTERN); + return std::regex_match(host, AURORA_PROXY_DNS_PATTERN) || std::regex_match(host, AURORA_CHINA_PROXY_DNS_PATTERN); } bool FAILOVER_HANDLER::is_rds_custom_cluster_dns(std::string host) { - return std::regex_match(host, AURORA_CUSTOM_CLUSTER_PATTERN); + return std::regex_match(host, AURORA_CUSTOM_CLUSTER_PATTERN) || std::regex_match(host, AURORA_CHINA_CUSTOM_CLUSTER_PATTERN); } #if defined(__APPLE__) || defined(__linux__) @@ -314,35 +333,53 @@ bool FAILOVER_HANDLER::is_rds_custom_cluster_dns(std::string host) { #endif std::string FAILOVER_HANDLER::get_rds_cluster_host_url(std::string host) { - std::smatch m; - if (std::regex_search(host, m, AURORA_DNS_PATTERN) && m.size() > 1) { - std::string gr1 = m.size() > 1 ? m.str(1) : std::string(""); - std::string gr2 = m.size() > 2 ? m.str(2) : std::string(""); - std::string gr3 = m.size() > 3 ? m.str(3) : std::string(""); - if (!gr1.empty() && !gr3.empty() && - (strcmp_case_insensitive(gr2.c_str(), "cluster-") == 0 || strcmp_case_insensitive(gr2.c_str(), "cluster-ro-") == 0)) { - std::string result; - result.assign(gr1); - result.append(".cluster-"); - result.append(gr3); - - return result; + auto f = [host](const std::regex pattern) { + std::smatch m; + if (std::regex_search(host, m, pattern) && m.size() > 1) { + std::string gr1 = m.size() > 1 ? m.str(1) : std::string(""); + std::string gr2 = m.size() > 2 ? m.str(2) : std::string(""); + std::string gr3 = m.size() > 3 ? m.str(3) : std::string(""); + if (!gr1.empty() && !gr3.empty() && + (strcmp_case_insensitive(gr2.c_str(), "cluster-") == 0 || strcmp_case_insensitive(gr2.c_str(), "cluster-ro-") == 0)) { + std::string result; + result.assign(gr1); + result.append(".cluster-"); + result.append(gr3); + + return result; + } } + return std::string(); + }; + + auto result = f(AURORA_CLUSTER_PATTERN); + if (!result.empty()) { + return result; } - return ""; + + return f(AURORA_CHINA_CLUSTER_PATTERN); } std::string FAILOVER_HANDLER::get_rds_instance_host_pattern(std::string host) { - std::smatch m; - if (std::regex_search(host, m, AURORA_DNS_PATTERN) && m.size() > 3) { - if (!m.str(3).empty()) { - std::string result("?."); - result.append(m.str(3)); - - return result; + auto f = [host](const std::regex pattern) { + std::smatch m; + if (std::regex_search(host, m, pattern) && m.size() > 3) { + if (!m.str(3).empty()) { + std::string result("?."); + result.append(m.str(3)); + + return result; + } } + return std::string(); + }; + + auto result = f(AURORA_DNS_PATTERN); + if (!result.empty()) { + return result; } - return ""; + + return f(AURORA_CHINA_DNS_PATTERN); } bool FAILOVER_HANDLER::is_failover_enabled() { diff --git a/unit_testing/failover_handler_test.cc b/unit_testing/failover_handler_test.cc index 20b8d7d4f..a78fd847b 100644 --- a/unit_testing/failover_handler_test.cc +++ b/unit_testing/failover_handler_test.cc @@ -38,6 +38,19 @@ using ::testing::AtLeast; using ::testing::Return; using ::testing::StrEq; +namespace { + const std::string US_EAST_REGION_CLUSTER = "database-test-name.cluster-XYZ.us-east-2.rds.amazonaws.com"; + const std::string US_EAST_REGION_CLUSTER_READ_ONLY = "database-test-name.cluster-ro-XYZ.us-east-2.rds.amazonaws.com"; + const std::string US_EAST_REGION_PROXY = "proxy-test-name.proxy-XYZ.us-east-2.rds.amazonaws.com"; + const std::string US_EAST_REGION_CUSTON_DOMAIN = "custom-test-name.cluster-custom-XYZ.us-east-2.rds.amazonaws.com"; + + const std::string CHINA_REGION_CLUSTER = "database-test-name.cluster-XYZ.rds.cn-northwest-1.amazonaws.com.cn"; + const std::string CHINA_REGION_CLUSTER_READ_ONLY = "database-test-name.cluster-ro-XYZ.rds.cn-northwest-1.amazonaws.com.cn"; + const std::string CHINA_REGION_PROXY = "proxy-test-name.proxy-XYZ.rds.cn-northwest-1.amazonaws.com.cn"; + const std::string CHINA_REGION_CUSTON_DOMAIN = "custom-test-name.cluster-custom-XYZ.rds.cn-northwest-1.amazonaws.com.cn"; + +} // namespace + class FailoverHandlerTest : public testing::Test { protected: static std::shared_ptr writer_host; @@ -387,3 +400,77 @@ TEST_F(FailoverHandlerTest, ReconnectWithFailoverSettings) { EXPECT_TRUE(failover_handler.is_failover_enabled()); } + +TEST_F(FailoverHandlerTest, IsRdsDns) { + EXPECT_TRUE(TEST_UTILS::is_rds_dns(US_EAST_REGION_CLUSTER)); + EXPECT_TRUE(TEST_UTILS::is_rds_dns(US_EAST_REGION_CLUSTER_READ_ONLY)); + EXPECT_TRUE(TEST_UTILS::is_rds_dns(US_EAST_REGION_PROXY)); + EXPECT_TRUE(TEST_UTILS::is_rds_dns(US_EAST_REGION_CUSTON_DOMAIN)); + + EXPECT_TRUE(TEST_UTILS::is_rds_dns(CHINA_REGION_CLUSTER)); + EXPECT_TRUE(TEST_UTILS::is_rds_dns(CHINA_REGION_CLUSTER_READ_ONLY)); + EXPECT_TRUE(TEST_UTILS::is_rds_dns(CHINA_REGION_PROXY)); + EXPECT_TRUE(TEST_UTILS::is_rds_dns(CHINA_REGION_CUSTON_DOMAIN)); +} + +TEST_F(FailoverHandlerTest, IsRdsClusterDns) { + EXPECT_TRUE(TEST_UTILS::is_rds_cluster_dns(US_EAST_REGION_CLUSTER)); + EXPECT_TRUE(TEST_UTILS::is_rds_cluster_dns(US_EAST_REGION_CLUSTER_READ_ONLY)); + EXPECT_FALSE(TEST_UTILS::is_rds_cluster_dns(US_EAST_REGION_PROXY)); + EXPECT_FALSE(TEST_UTILS::is_rds_cluster_dns(US_EAST_REGION_CUSTON_DOMAIN)); + + EXPECT_TRUE(TEST_UTILS::is_rds_cluster_dns(CHINA_REGION_CLUSTER)); + EXPECT_TRUE(TEST_UTILS::is_rds_cluster_dns(CHINA_REGION_CLUSTER_READ_ONLY)); + EXPECT_FALSE(TEST_UTILS::is_rds_cluster_dns(CHINA_REGION_PROXY)); + EXPECT_FALSE(TEST_UTILS::is_rds_cluster_dns(CHINA_REGION_CUSTON_DOMAIN)); +} + +TEST_F(FailoverHandlerTest, IsRdsProxyDns) { + EXPECT_FALSE(TEST_UTILS::is_rds_proxy_dns(US_EAST_REGION_CLUSTER)); + EXPECT_FALSE(TEST_UTILS::is_rds_proxy_dns(US_EAST_REGION_CLUSTER_READ_ONLY)); + EXPECT_TRUE(TEST_UTILS::is_rds_proxy_dns(US_EAST_REGION_PROXY)); + EXPECT_FALSE(TEST_UTILS::is_rds_proxy_dns(US_EAST_REGION_CUSTON_DOMAIN)); + + EXPECT_FALSE(TEST_UTILS::is_rds_proxy_dns(CHINA_REGION_CLUSTER)); + EXPECT_FALSE(TEST_UTILS::is_rds_proxy_dns(CHINA_REGION_CLUSTER_READ_ONLY)); + EXPECT_TRUE(TEST_UTILS::is_rds_proxy_dns(CHINA_REGION_PROXY)); + EXPECT_FALSE(TEST_UTILS::is_rds_proxy_dns(CHINA_REGION_CUSTON_DOMAIN)); +} + +TEST_F(FailoverHandlerTest, IsRdsCustomClusterDns) { + EXPECT_FALSE(TEST_UTILS::is_rds_custom_cluster_dns(US_EAST_REGION_CLUSTER)); + EXPECT_FALSE(TEST_UTILS::is_rds_custom_cluster_dns(US_EAST_REGION_CLUSTER_READ_ONLY)); + EXPECT_FALSE(TEST_UTILS::is_rds_custom_cluster_dns(US_EAST_REGION_PROXY)); + EXPECT_TRUE(TEST_UTILS::is_rds_custom_cluster_dns(US_EAST_REGION_CUSTON_DOMAIN)); + + EXPECT_FALSE(TEST_UTILS::is_rds_custom_cluster_dns(CHINA_REGION_CLUSTER)); + EXPECT_FALSE(TEST_UTILS::is_rds_custom_cluster_dns(CHINA_REGION_CLUSTER_READ_ONLY)); + EXPECT_FALSE(TEST_UTILS::is_rds_custom_cluster_dns(CHINA_REGION_PROXY)); + EXPECT_TRUE(TEST_UTILS::is_rds_custom_cluster_dns(CHINA_REGION_CUSTON_DOMAIN)); +} + +TEST_F(FailoverHandlerTest, GetRdsInstanceHostPattern) { + const std::string expected_pattern = "?.XYZ.us-east-2.rds.amazonaws.com"; + EXPECT_EQ(expected_pattern, TEST_UTILS::get_rds_instance_host_pattern(US_EAST_REGION_CLUSTER)); + EXPECT_EQ(expected_pattern, TEST_UTILS::get_rds_instance_host_pattern(US_EAST_REGION_CLUSTER_READ_ONLY)); + EXPECT_EQ(expected_pattern, TEST_UTILS::get_rds_instance_host_pattern(US_EAST_REGION_PROXY)); + EXPECT_EQ(expected_pattern, TEST_UTILS::get_rds_instance_host_pattern(US_EAST_REGION_CUSTON_DOMAIN)); + + const std::string expected_china_pattern = "?.XYZ.rds.cn-northwest-1.amazonaws.com.cn"; + EXPECT_EQ(expected_china_pattern, TEST_UTILS::get_rds_instance_host_pattern(CHINA_REGION_CLUSTER)); + EXPECT_EQ(expected_china_pattern, TEST_UTILS::get_rds_instance_host_pattern(CHINA_REGION_CLUSTER_READ_ONLY)); + EXPECT_EQ(expected_china_pattern, TEST_UTILS::get_rds_instance_host_pattern(CHINA_REGION_PROXY)); + EXPECT_EQ(expected_china_pattern, TEST_UTILS::get_rds_instance_host_pattern(CHINA_REGION_CUSTON_DOMAIN)); +} + +TEST_F(FailoverHandlerTest, GetRdsClusterHostUrl) { + EXPECT_EQ(US_EAST_REGION_CLUSTER, TEST_UTILS::get_rds_cluster_host_url(US_EAST_REGION_CLUSTER)); + EXPECT_EQ(US_EAST_REGION_CLUSTER, TEST_UTILS::get_rds_cluster_host_url(US_EAST_REGION_CLUSTER_READ_ONLY)); + EXPECT_EQ(std::string(), TEST_UTILS::get_rds_cluster_host_url(US_EAST_REGION_PROXY)); + EXPECT_EQ(std::string(), TEST_UTILS::get_rds_cluster_host_url(US_EAST_REGION_CUSTON_DOMAIN)); + + EXPECT_EQ(CHINA_REGION_CLUSTER, TEST_UTILS::get_rds_cluster_host_url(CHINA_REGION_CLUSTER)); + EXPECT_EQ(CHINA_REGION_CLUSTER, TEST_UTILS::get_rds_cluster_host_url(CHINA_REGION_CLUSTER_READ_ONLY)); + EXPECT_EQ(std::string(), TEST_UTILS::get_rds_cluster_host_url(CHINA_REGION_PROXY)); + EXPECT_EQ(std::string(), TEST_UTILS::get_rds_cluster_host_url(CHINA_REGION_CUSTON_DOMAIN)); +} diff --git a/unit_testing/test_utils.cc b/unit_testing/test_utils.cc index dee55161c..5b7b438a0 100644 --- a/unit_testing/test_utils.cc +++ b/unit_testing/test_utils.cc @@ -130,3 +130,31 @@ std::map, Aws::Utils::Json::JsonValue>& TEST bool TEST_UTILS::try_parse_region_from_secret(std::string secret, std::string& region) { return SECRETS_MANAGER_PROXY::try_parse_region_from_secret(secret, region); } + +bool TEST_UTILS::is_dns_pattern_valid(std::string host) { + return FAILOVER_HANDLER::is_dns_pattern_valid(host); +} + +bool TEST_UTILS::is_rds_dns(std::string host) { + return FAILOVER_HANDLER::is_rds_dns(host); +} + +bool TEST_UTILS::is_rds_cluster_dns(std::string host) { + return FAILOVER_HANDLER::is_rds_cluster_dns(host); +} + +bool TEST_UTILS::is_rds_proxy_dns(std::string host) { + return FAILOVER_HANDLER::is_rds_proxy_dns(host); +} + +bool TEST_UTILS::is_rds_custom_cluster_dns(std::string host) { + return FAILOVER_HANDLER::is_rds_custom_cluster_dns(host); +} + +std::string TEST_UTILS::get_rds_cluster_host_url(std::string host) { + return FAILOVER_HANDLER::get_rds_cluster_host_url(host); +} + +std::string TEST_UTILS::get_rds_instance_host_pattern(std::string host) { + return FAILOVER_HANDLER::get_rds_instance_host_pattern(host); +} diff --git a/unit_testing/test_utils.h b/unit_testing/test_utils.h index dac4c68fa..c4567604b 100644 --- a/unit_testing/test_utils.h +++ b/unit_testing/test_utils.h @@ -59,6 +59,13 @@ class TEST_UTILS { static void clear_token_cache(IAM_PROXY* iam_proxy); static std::map, Aws::Utils::Json::JsonValue>& get_secrets_cache(); static bool try_parse_region_from_secret(std::string secret, std::string& region); + static bool is_dns_pattern_valid(std::string host); + static bool is_rds_dns(std::string host); + static bool is_rds_cluster_dns(std::string host); + static bool is_rds_proxy_dns(std::string host); + static bool is_rds_custom_cluster_dns(std::string host); + static std::string get_rds_cluster_host_url(std::string host); + static std::string get_rds_instance_host_pattern(std::string host); }; #endif /* __TESTUTILS_H__ */