Skip to content

Commit 549adfb

Browse files
authored
local-rate-limit: improving cluster fetching validation (#41246)
Commit Message: local-rate-limit: improving cluster fetching validation Signed-off-by: Adi Suissa-Peleg <[email protected]>
1 parent 153766c commit 549adfb

File tree

6 files changed

+115
-5
lines changed

6 files changed

+115
-5
lines changed

envoy/upstream/cluster_manager.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,16 @@ class ClusterManager {
349349
*/
350350
virtual OptRef<const Cluster> getActiveCluster(const std::string& cluster_name) const PURE;
351351

352+
/**
353+
* Receives a cluster name and returns an active or warming cluster (if found).
354+
* @param cluster_name the name of the cluster.
355+
* @return OptRef<const Cluster> A reference to the cluster if found, and nullopt otherwise.
356+
*
357+
* NOTE: This method is only thread safe on the main thread. It should not be called elsewhere.
358+
*/
359+
virtual OptRef<const Cluster>
360+
getActiveOrWarmingCluster(const std::string& cluster_name) const PURE;
361+
352362
/**
353363
* Returns true iff the given cluster name is known in the cluster-manager
354364
* (either as active or as warming).

source/common/upstream/cluster_manager_impl.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,17 @@ class ClusterManagerImpl : public ClusterManager,
279279
return absl::nullopt;
280280
}
281281

282+
OptRef<const Cluster> getActiveOrWarmingCluster(const std::string& cluster_name) const override {
283+
ASSERT_IS_MAIN_OR_TEST_THREAD();
284+
if (const auto& it = active_clusters_.find(cluster_name); it != active_clusters_.end()) {
285+
return *it->second->cluster_;
286+
}
287+
if (const auto& it = warming_clusters_.find(cluster_name); it != warming_clusters_.end()) {
288+
return *it->second->cluster_;
289+
}
290+
return absl::nullopt;
291+
}
292+
282293
bool hasCluster(const std::string& cluster_name) const override {
283294
ASSERT_IS_MAIN_OR_TEST_THREAD();
284295
return active_clusters_.contains(cluster_name) || warming_clusters_.contains(cluster_name);

source/extensions/filters/common/local_ratelimit/local_ratelimit_impl.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ ShareProviderManagerSharedPtr ShareProviderManager::singleton(Event::Dispatcher&
6565
if (!local_cluster_name.has_value()) {
6666
return nullptr;
6767
}
68-
auto cluster = cm.clusters().getCluster(local_cluster_name.value());
68+
auto cluster = cm.getActiveOrWarmingCluster(local_cluster_name.value());
6969
if (!cluster.has_value()) {
7070
return nullptr;
7171
}

test/common/upstream/cluster_manager_impl_test.cc

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1520,6 +1520,77 @@ TEST_F(ClusterManagerImplTest, OriginalDstInitialization) {
15201520
factory_.tls_.shutdownThread();
15211521
}
15221522

1523+
TEST_F(ClusterManagerImplTest, GetActiveOrWarmingCluster) {
1524+
// Start with a static cluster.
1525+
const std::string bootstrap_yaml = R"EOF(
1526+
static_resources:
1527+
clusters:
1528+
- name: static_cluster
1529+
connect_timeout: 0.250s
1530+
type: static
1531+
lb_policy: round_robin
1532+
load_assignment:
1533+
cluster_name: static_cluster
1534+
endpoints:
1535+
- lb_endpoints:
1536+
- endpoint:
1537+
address:
1538+
socket_address:
1539+
address: 127.0.0.1
1540+
port_value: 11001
1541+
)EOF";
1542+
create(parseBootstrapFromV3Yaml(bootstrap_yaml));
1543+
1544+
// Static cluster should be active.
1545+
EXPECT_NE(absl::nullopt, cluster_manager_->getActiveCluster("static_cluster"));
1546+
EXPECT_NE(absl::nullopt, cluster_manager_->getActiveOrWarmingCluster("static_cluster"));
1547+
EXPECT_EQ(absl::nullopt, cluster_manager_->getActiveOrWarmingCluster("non_existent_cluster"));
1548+
1549+
// Now, add a dynamic cluster. It will start in warming state.
1550+
const std::string warming_cluster_yaml = R"EOF(
1551+
name: warming_cluster
1552+
connect_timeout: 0.250s
1553+
type: EDS
1554+
eds_cluster_config:
1555+
eds_config:
1556+
api_config_source:
1557+
api_type: GRPC
1558+
grpc_services:
1559+
envoy_grpc:
1560+
cluster_name: static_cluster
1561+
)EOF";
1562+
auto warming_cluster_config = parseClusterFromV3Yaml(warming_cluster_yaml);
1563+
1564+
// Mock the cluster creation for the warming cluster.
1565+
std::shared_ptr<MockClusterMockPrioritySet> warming_cluster =
1566+
std::make_shared<NiceMock<MockClusterMockPrioritySet>>();
1567+
warming_cluster->info_->name_ = "warming_cluster";
1568+
std::function<void()> cluster_init_callback;
1569+
EXPECT_CALL(*warming_cluster, initialize(_)).WillOnce(SaveArg<0>(&cluster_init_callback));
1570+
EXPECT_CALL(factory_, clusterFromProto_(ProtoEq(warming_cluster_config), _, true))
1571+
.WillOnce(Return(std::make_pair(warming_cluster, nullptr)));
1572+
1573+
// Add the cluster.
1574+
EXPECT_TRUE(*cluster_manager_->addOrUpdateCluster(warming_cluster_config, "version1"));
1575+
1576+
// The cluster should be in warming, not active.
1577+
EXPECT_EQ(absl::nullopt, cluster_manager_->getActiveCluster("warming_cluster"));
1578+
OptRef<const Cluster> cluster = cluster_manager_->getActiveOrWarmingCluster("warming_cluster");
1579+
EXPECT_NE(absl::nullopt, cluster);
1580+
EXPECT_EQ("warming_cluster", cluster->info()->name());
1581+
1582+
// Finish initialization. This should move it to active.
1583+
cluster_init_callback();
1584+
1585+
// Now the cluster should be active.
1586+
cluster = cluster_manager_->getActiveCluster("warming_cluster");
1587+
EXPECT_NE(absl::nullopt, cluster);
1588+
EXPECT_EQ("warming_cluster", cluster->info()->name());
1589+
cluster = cluster_manager_->getActiveOrWarmingCluster("warming_cluster");
1590+
EXPECT_NE(absl::nullopt, cluster);
1591+
EXPECT_EQ("warming_cluster", cluster->info()->name());
1592+
}
1593+
15231594
TEST_F(ClusterManagerImplTest, UpstreamSocketOptionsPassedToTcpConnPool) {
15241595
createWithBasicStaticCluster();
15251596
NiceMock<MockLoadBalancerContext> context;

test/mocks/upstream/cluster_manager.cc

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,22 @@ MockClusterManager::MockClusterManager()
3838
MockClusterManager::~MockClusterManager() = default;
3939

4040
void MockClusterManager::initializeClusters(const std::vector<std::string>& active_cluster_names,
41-
const std::vector<std::string>&) {
41+
const std::vector<std::string>& warming_cluster_names) {
4242
active_clusters_.clear();
43+
warming_clusters_.clear();
4344
ClusterManager::ClusterInfoMaps info_map;
4445
for (const auto& name : active_cluster_names) {
4546
auto new_cluster = std::make_unique<NiceMock<MockCluster>>();
4647
new_cluster->info_->name_ = name;
4748
info_map.active_clusters_.emplace(name, *new_cluster);
4849
active_clusters_.emplace(name, std::move(new_cluster));
4950
}
50-
51-
// TODO(mattklein123): Add support for warming clusters when needed.
51+
for (const auto& name : warming_cluster_names) {
52+
auto new_cluster = std::make_unique<NiceMock<MockCluster>>();
53+
new_cluster->info_->name_ = name;
54+
info_map.warming_clusters_.emplace(name, *new_cluster);
55+
warming_clusters_.emplace(name, std::move(new_cluster));
56+
}
5257

5358
ON_CALL(*this, clusters()).WillByDefault(Return(info_map));
5459
ON_CALL(*this, getActiveCluster(_))
@@ -58,9 +63,20 @@ void MockClusterManager::initializeClusters(const std::vector<std::string>& acti
5863
}
5964
return absl::nullopt;
6065
}));
66+
ON_CALL(*this, getActiveOrWarmingCluster(_))
67+
.WillByDefault(Invoke([this](const std::string& cluster_name) -> OptRef<const Cluster> {
68+
if (const auto& it = active_clusters_.find(cluster_name); it != active_clusters_.end()) {
69+
return *it->second;
70+
}
71+
if (const auto& it = warming_clusters_.find(cluster_name); it != warming_clusters_.end()) {
72+
return *it->second;
73+
}
74+
return absl::nullopt;
75+
}));
6176
ON_CALL(*this, hasCluster(_))
6277
.WillByDefault(Invoke([this](const std::string& cluster_name) -> bool {
63-
return active_clusters_.find(cluster_name) != active_clusters_.end();
78+
return active_clusters_.find(cluster_name) != active_clusters_.end() ||
79+
warming_clusters_.find(cluster_name) != warming_clusters_.end();
6480
}));
6581
ON_CALL(*this, hasActiveClusters()).WillByDefault(Return(!active_cluster_names.empty()));
6682
}

test/mocks/upstream/cluster_manager.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ class MockClusterManager : public ClusterManager {
4444
(const envoy::config::bootstrap::v3::Bootstrap& bootstrap));
4545
MOCK_METHOD(ClusterInfoMaps, clusters, (), (const));
4646
MOCK_METHOD(OptRef<const Cluster>, getActiveCluster, (const std::string& cluster_name), (const));
47+
MOCK_METHOD(OptRef<const Cluster>, getActiveOrWarmingCluster, (const std::string& cluster_name),
48+
(const));
4749
MOCK_METHOD(bool, hasCluster, (const std::string& cluster_name), (const));
4850
MOCK_METHOD(bool, hasActiveClusters, (), (const));
4951

0 commit comments

Comments
 (0)