@@ -75,6 +75,89 @@ class EdsTest : public testing::Test {
7575 NiceMock<LocalInfo::MockLocalInfo> local_info_;
7676};
7777
78+ class EdsWithHealthCheckUpdateTest : public EdsTest {
79+ protected:
80+ EdsWithHealthCheckUpdateTest () {}
81+
82+ // Build the initial cluster with some endpoints.
83+ void initializeCluster (const std::vector<uint32_t > endpoint_ports,
84+ const bool drain_connections_on_host_removal) {
85+ resetCluster (drain_connections_on_host_removal);
86+
87+ auto health_checker = std::make_shared<MockHealthChecker>();
88+ EXPECT_CALL (*health_checker, start ());
89+ EXPECT_CALL (*health_checker, addHostCheckCompleteCb (_)).Times (2 );
90+ cluster_->setHealthChecker (health_checker);
91+
92+ cluster_load_assignment_ = resources_.Add ();
93+ cluster_load_assignment_->set_cluster_name (" fare" );
94+
95+ for (const auto & port : endpoint_ports) {
96+ addEndpoint (port);
97+ }
98+
99+ VERBOSE_EXPECT_NO_THROW (cluster_->onConfigUpdate (resources_, " " ));
100+
101+ // Make sure the cluster is rebuilt.
102+ EXPECT_EQ (0UL , stats_.counter (" cluster.name.update_no_rebuild" ).value ());
103+ {
104+ auto & hosts = cluster_->prioritySet ().hostSetsPerPriority ()[0 ]->hosts ();
105+ EXPECT_EQ (hosts.size (), 2 );
106+
107+ EXPECT_TRUE (hosts[0 ]->healthFlagGet (Host::HealthFlag::FAILED_ACTIVE_HC));
108+ EXPECT_TRUE (hosts[1 ]->healthFlagGet (Host::HealthFlag::FAILED_ACTIVE_HC));
109+
110+ // Mark the hosts as healthy
111+ hosts[0 ]->healthFlagClear (Host::HealthFlag::FAILED_ACTIVE_HC);
112+ hosts[1 ]->healthFlagClear (Host::HealthFlag::FAILED_ACTIVE_HC);
113+ }
114+ }
115+
116+ void resetCluster (const bool drain_connections_on_host_removal) {
117+ const std::string config = R"EOF(
118+ name: name
119+ connect_timeout: 0.25s
120+ type: EDS
121+ lb_policy: ROUND_ROBIN
122+ drain_connections_on_host_removal: {}
123+ eds_cluster_config:
124+ service_name: fare
125+ eds_config:
126+ api_config_source:
127+ cluster_names:
128+ - eds
129+ refresh_delay: 1s
130+ )EOF" ;
131+ EdsTest::resetCluster (fmt::format (config, drain_connections_on_host_removal));
132+ }
133+
134+ void addEndpoint (const uint32_t port) {
135+ auto * endpoints = cluster_load_assignment_->add_endpoints ();
136+ auto * socket_address = endpoints->add_lb_endpoints ()
137+ ->mutable_endpoint ()
138+ ->mutable_address ()
139+ ->mutable_socket_address ();
140+ socket_address->set_address (" 1.2.3.4" );
141+ socket_address->set_port_value (port);
142+ }
143+
144+ void updateEndpointHealthCheckPortAtIndex (const uint32_t index, const uint32_t port) {
145+ cluster_load_assignment_->mutable_endpoints (index)
146+ ->mutable_lb_endpoints (0 )
147+ ->mutable_endpoint ()
148+ ->mutable_health_check_config ()
149+ ->set_port_value (port);
150+
151+ VERBOSE_EXPECT_NO_THROW (cluster_->onConfigUpdate (resources_, " " ));
152+
153+ // Always rebuild if health check config is changed.
154+ EXPECT_EQ (0UL , stats_.counter (" cluster.name.update_no_rebuild" ).value ());
155+ }
156+
157+ Protobuf::RepeatedPtrField<envoy::api::v2::ClusterLoadAssignment> resources_;
158+ envoy::api::v2::ClusterLoadAssignment* cluster_load_assignment_;
159+ };
160+
78161// Negative test for protoc-gen-validate constraints.
79162TEST_F (EdsTest, ValidateFail) {
80163 Protobuf::RepeatedPtrField<envoy::api::v2::ClusterLoadAssignment> resources;
@@ -1152,6 +1235,84 @@ TEST_F(EdsTest, PriorityAndLocalityWeighted) {
11521235 EXPECT_EQ (1UL , stats_.counter (" cluster.name.update_no_rebuild" ).value ());
11531236}
11541237
1238+ TEST_F (EdsWithHealthCheckUpdateTest, EndpointUpdateHealthCheckConfig) {
1239+ const std::vector<uint32_t > endpoint_ports = {80 , 81 };
1240+ const uint32_t new_health_check_port = 8000 ;
1241+
1242+ // Initialize the cluster with two endpoints without draining connections on host removal.
1243+ initializeCluster (endpoint_ports, false );
1244+
1245+ updateEndpointHealthCheckPortAtIndex (0 , new_health_check_port);
1246+ {
1247+ auto & hosts = cluster_->prioritySet ().hostSetsPerPriority ()[0 ]->hosts ();
1248+ EXPECT_EQ (hosts.size (), 3 );
1249+ // Make sure the first endpoint health check port is updated.
1250+ EXPECT_EQ (new_health_check_port, hosts[0 ]->healthCheckAddress ()->ip ()->port ());
1251+
1252+ EXPECT_NE (new_health_check_port, hosts[1 ]->healthCheckAddress ()->ip ()->port ());
1253+ EXPECT_NE (new_health_check_port, hosts[2 ]->healthCheckAddress ()->ip ()->port ());
1254+ EXPECT_EQ (endpoint_ports[1 ], hosts[1 ]->healthCheckAddress ()->ip ()->port ());
1255+ EXPECT_EQ (endpoint_ports[0 ], hosts[2 ]->healthCheckAddress ()->ip ()->port ());
1256+
1257+ EXPECT_TRUE (hosts[0 ]->healthFlagGet (Host::HealthFlag::FAILED_ACTIVE_HC));
1258+
1259+ // The old hosts are still active. The health checker continues to do health checking to these
1260+ // hosts, until they are removed.
1261+ EXPECT_FALSE (hosts[1 ]->healthFlagGet (Host::HealthFlag::FAILED_ACTIVE_HC));
1262+ EXPECT_FALSE (hosts[2 ]->healthFlagGet (Host::HealthFlag::FAILED_ACTIVE_HC));
1263+ }
1264+
1265+ updateEndpointHealthCheckPortAtIndex (1 , new_health_check_port);
1266+ {
1267+ auto & hosts = cluster_->prioritySet ().hostSetsPerPriority ()[0 ]->hosts ();
1268+ EXPECT_EQ (hosts.size (), 4 );
1269+ EXPECT_EQ (new_health_check_port, hosts[0 ]->healthCheckAddress ()->ip ()->port ());
1270+
1271+ // Make sure the second endpoint health check port is updated.
1272+ EXPECT_EQ (new_health_check_port, hosts[1 ]->healthCheckAddress ()->ip ()->port ());
1273+
1274+ EXPECT_EQ (endpoint_ports[1 ], hosts[2 ]->healthCheckAddress ()->ip ()->port ());
1275+ EXPECT_EQ (endpoint_ports[0 ], hosts[3 ]->healthCheckAddress ()->ip ()->port ());
1276+
1277+ EXPECT_TRUE (hosts[0 ]->healthFlagGet (Host::HealthFlag::FAILED_ACTIVE_HC));
1278+ EXPECT_TRUE (hosts[1 ]->healthFlagGet (Host::HealthFlag::FAILED_ACTIVE_HC));
1279+
1280+ // The old hosts are still active.
1281+ EXPECT_FALSE (hosts[2 ]->healthFlagGet (Host::HealthFlag::FAILED_ACTIVE_HC));
1282+ EXPECT_FALSE (hosts[3 ]->healthFlagGet (Host::HealthFlag::FAILED_ACTIVE_HC));
1283+ }
1284+ }
1285+
1286+ TEST_F (EdsWithHealthCheckUpdateTest, EndpointUpdateHealthCheckConfigWithDrainConnectionsOnRemoval) {
1287+ const std::vector<uint32_t > endpoint_ports = {80 , 81 };
1288+ const uint32_t new_health_check_port = 8000 ;
1289+
1290+ // Initialize the cluster with two endpoints with draining connections on host removal.
1291+ initializeCluster (endpoint_ports, true );
1292+
1293+ updateEndpointHealthCheckPortAtIndex (0 , new_health_check_port);
1294+ {
1295+ auto & hosts = cluster_->prioritySet ().hostSetsPerPriority ()[0 ]->hosts ();
1296+ // Since drain_connections_on_host_removal is set to true, the old hosts are removed
1297+ // immediately.
1298+ EXPECT_EQ (hosts.size (), 2 );
1299+ // Make sure the first endpoint health check port is updated.
1300+ EXPECT_EQ (new_health_check_port, hosts[0 ]->healthCheckAddress ()->ip ()->port ());
1301+
1302+ EXPECT_NE (new_health_check_port, hosts[1 ]->healthCheckAddress ()->ip ()->port ());
1303+ }
1304+
1305+ updateEndpointHealthCheckPortAtIndex (1 , new_health_check_port);
1306+ {
1307+ auto & hosts = cluster_->prioritySet ().hostSetsPerPriority ()[0 ]->hosts ();
1308+ EXPECT_EQ (hosts.size (), 2 );
1309+ EXPECT_EQ (new_health_check_port, hosts[0 ]->healthCheckAddress ()->ip ()->port ());
1310+
1311+ // Make sure the second endpoint health check port is updated.
1312+ EXPECT_EQ (new_health_check_port, hosts[1 ]->healthCheckAddress ()->ip ()->port ());
1313+ }
1314+ }
1315+
11551316// Throw on adding a new resource with an invalid endpoint (since the given address is invalid).
11561317TEST_F (EdsTest, MalformedIP) {
11571318 Protobuf::RepeatedPtrField<envoy::api::v2::ClusterLoadAssignment> resources;
0 commit comments