Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

xDS: fix WeightedClusters total weight handling #32134

Merged
merged 2 commits into from
Jan 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -688,11 +688,8 @@ XdsResolver::XdsConfigSelector::GetCallConfig(GetCallConfigArgs args) {
[&](const std::vector<
XdsRouteConfigResource::Route::RouteAction::ClusterWeight>&
/*weighted_clusters*/) {
const uint32_t key =
rand() %
entry
.weighted_cluster_state[entry.weighted_cluster_state.size() - 1]
.range_end;
const uint32_t key = absl::Uniform<uint32_t>(
absl::BitGen(), 0, entry.weighted_cluster_state.back().range_end);
// Find the index in weighted clusters corresponding to key.
size_t mid = 0;
size_t start_index = 0;
Expand Down
5 changes: 5 additions & 0 deletions src/core/ext/xds/xds_route_config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <stddef.h>
#include <stdint.h>

#include <limits>
#include <map>
#include <memory>
#include <set>
Expand Down Expand Up @@ -835,6 +836,7 @@ absl::optional<XdsRouteConfigResource::Route::RouteAction> RouteActionParse(
GPR_ASSERT(weighted_clusters_proto != nullptr);
std::vector<XdsRouteConfigResource::Route::RouteAction::ClusterWeight>
action_weighted_clusters;
uint64_t total_weight = 0;
size_t clusters_size;
const envoy_config_route_v3_WeightedCluster_ClusterWeight* const* clusters =
envoy_config_route_v3_WeightedCluster_clusters(weighted_clusters_proto,
Expand Down Expand Up @@ -874,12 +876,15 @@ absl::optional<XdsRouteConfigResource::Route::RouteAction> RouteActionParse(
} else {
cluster.weight = google_protobuf_UInt32Value_value(weight_proto);
if (cluster.weight == 0) continue;
total_weight += cluster.weight;
}
// Add entry to WeightedClusters.
action_weighted_clusters.emplace_back(std::move(cluster));
}
if (action_weighted_clusters.empty()) {
errors->AddError("no valid clusters specified");
} else if (total_weight > std::numeric_limits<uint32_t>::max()) {
errors->AddError("sum of cluster weights exceeds uint32 max");
}
route_action.action = std::move(action_weighted_clusters);
} else if (XdsRlsEnabled() &&
Expand Down
33 changes: 33 additions & 0 deletions test/core/xds/xds_route_config_resource_type_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
// limitations under the License.
//

#include <stdint.h>

#include <algorithm>
#include <limits>
#include <map>
#include <memory>
#include <string>
Expand Down Expand Up @@ -1627,6 +1630,36 @@ TEST_F(WeightedClusterTest, Invalid) {
<< decode_result.resource.status();
}

TEST_F(WeightedClusterTest, TotalWeightExceedsUint32Max) {
RouteConfiguration route_config;
route_config.set_name("foo");
auto* vhost = route_config.add_virtual_hosts();
vhost->add_domains("*");
auto* route_proto = vhost->add_routes();
route_proto->mutable_match()->set_prefix("");
auto* weighted_clusters_proto =
route_proto->mutable_route()->mutable_weighted_clusters();
auto* cluster_weight_proto = weighted_clusters_proto->add_clusters();
cluster_weight_proto->set_name("cluster1");
cluster_weight_proto->mutable_weight()->set_value(
std::numeric_limits<uint32_t>::max());
cluster_weight_proto = weighted_clusters_proto->add_clusters();
cluster_weight_proto->set_name("cluster2");
cluster_weight_proto->mutable_weight()->set_value(1);
std::string serialized_resource;
ASSERT_TRUE(route_config.SerializeToString(&serialized_resource));
auto* resource_type = XdsRouteConfigResourceType::Get();
auto decode_result =
resource_type->Decode(decode_context_, serialized_resource);
EXPECT_EQ(decode_result.resource.status().code(),
absl::StatusCode::kInvalidArgument);
EXPECT_EQ(decode_result.resource.status().message(),
"errors validating RouteConfiguration resource: ["
"field:virtual_hosts[0].routes[0].route.weighted_clusters "
"error:sum of cluster weights exceeds uint32 max]")
<< decode_result.resource.status();
}

//
// RLS tests
//
Expand Down
98 changes: 82 additions & 16 deletions test/cpp/end2end/xds/xds_routing_end2end_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -952,10 +952,6 @@ TEST_P(LdsRdsTest, XdsRoutingWeightedCluster) {
route1->mutable_route()->mutable_weighted_clusters()->add_clusters();
weighted_cluster3->set_name(kNotUsedClusterName);
weighted_cluster3->mutable_weight()->set_value(0);
route1->mutable_route()
->mutable_weighted_clusters()
->mutable_total_weight()
->set_value(kWeight75 + kWeight25);
auto* default_route = new_route_config.mutable_virtual_hosts(0)->add_routes();
default_route->mutable_match()->set_prefix("");
default_route->mutable_route()->set_cluster(kDefaultClusterName);
Expand Down Expand Up @@ -984,6 +980,88 @@ TEST_P(LdsRdsTest, XdsRoutingWeightedCluster) {
::testing::DoubleNear(kWeight25Percent, kErrorTolerance));
}

TEST_P(LdsRdsTest, XdsRoutingWeightedClusterNoIntegerOverflow) {
CreateAndStartBackends(3);
const char* kNewCluster1Name = "new_cluster_1";
const char* kNewEdsService1Name = "new_eds_service_name_1";
const char* kNewCluster2Name = "new_cluster_2";
const char* kNewEdsService2Name = "new_eds_service_name_2";
const size_t kNumEchoRpcs = 10; // RPCs that will go to a fixed backend.
const uint32_t kWeight1 = std::numeric_limits<uint32_t>::max() / 3;
const uint32_t kWeight2 = std::numeric_limits<uint32_t>::max() - kWeight1;
const double kErrorTolerance = 0.05;
const double kWeight1Percent =
static_cast<double>(kWeight1) / std::numeric_limits<uint32_t>::max();
const double kWeight2Percent =
static_cast<double>(kWeight2) / std::numeric_limits<uint32_t>::max();
const size_t kNumEcho1Rpcs =
ComputeIdealNumRpcs(kWeight2Percent, kErrorTolerance);
// Populate new EDS resources.
EdsResourceArgs args({
{"locality0", CreateEndpointsForBackends(0, 1)},
});
EdsResourceArgs args1({
{"locality0", CreateEndpointsForBackends(1, 2)},
});
EdsResourceArgs args2({
{"locality0", CreateEndpointsForBackends(2, 3)},
});
balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
balancer_->ads_service()->SetEdsResource(
BuildEdsResource(args1, kNewEdsService1Name));
balancer_->ads_service()->SetEdsResource(
BuildEdsResource(args2, kNewEdsService2Name));
// Populate new CDS resources.
Cluster new_cluster1 = default_cluster_;
new_cluster1.set_name(kNewCluster1Name);
new_cluster1.mutable_eds_cluster_config()->set_service_name(
kNewEdsService1Name);
balancer_->ads_service()->SetCdsResource(new_cluster1);
Cluster new_cluster2 = default_cluster_;
new_cluster2.set_name(kNewCluster2Name);
new_cluster2.mutable_eds_cluster_config()->set_service_name(
kNewEdsService2Name);
balancer_->ads_service()->SetCdsResource(new_cluster2);
// Populating Route Configurations for LDS.
RouteConfiguration new_route_config = default_route_config_;
auto* route1 = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
route1->mutable_match()->set_prefix("/grpc.testing.EchoTest1Service/");
auto* weighted_cluster1 =
route1->mutable_route()->mutable_weighted_clusters()->add_clusters();
weighted_cluster1->set_name(kNewCluster1Name);
weighted_cluster1->mutable_weight()->set_value(kWeight1);
auto* weighted_cluster2 =
route1->mutable_route()->mutable_weighted_clusters()->add_clusters();
weighted_cluster2->set_name(kNewCluster2Name);
weighted_cluster2->mutable_weight()->set_value(kWeight2);
auto* default_route = new_route_config.mutable_virtual_hosts(0)->add_routes();
default_route->mutable_match()->set_prefix("");
default_route->mutable_route()->set_cluster(kDefaultClusterName);
SetRouteConfiguration(balancer_.get(), new_route_config);
WaitForAllBackends(DEBUG_LOCATION, 0, 1);
WaitForAllBackends(DEBUG_LOCATION, 1, 3, /*check_status=*/nullptr,
WaitForBackendOptions(),
RpcOptions().set_rpc_service(SERVICE_ECHO1));
CheckRpcSendOk(DEBUG_LOCATION, kNumEchoRpcs);
CheckRpcSendOk(DEBUG_LOCATION, kNumEcho1Rpcs,
RpcOptions().set_rpc_service(SERVICE_ECHO1));
// Make sure RPCs all go to the correct backend.
EXPECT_EQ(kNumEchoRpcs, backends_[0]->backend_service()->request_count());
EXPECT_EQ(0, backends_[0]->backend_service1()->request_count());
EXPECT_EQ(0, backends_[1]->backend_service()->request_count());
const int weight1_request_count =
backends_[1]->backend_service1()->request_count();
EXPECT_EQ(0, backends_[2]->backend_service()->request_count());
const int weight2_request_count =
backends_[2]->backend_service1()->request_count();
gpr_log(GPR_INFO, "target1 received %d rpcs and target2 received %d rpcs",
weight1_request_count, weight2_request_count);
EXPECT_THAT(static_cast<double>(weight1_request_count) / kNumEcho1Rpcs,
::testing::DoubleNear(kWeight1Percent, kErrorTolerance));
EXPECT_THAT(static_cast<double>(weight2_request_count) / kNumEcho1Rpcs,
::testing::DoubleNear(kWeight2Percent, kErrorTolerance));
}

TEST_P(LdsRdsTest, RouteActionWeightedTargetDefaultRoute) {
CreateAndStartBackends(3);
const char* kNewCluster1Name = "new_cluster_1";
Expand Down Expand Up @@ -1035,10 +1113,6 @@ TEST_P(LdsRdsTest, RouteActionWeightedTargetDefaultRoute) {
route1->mutable_route()->mutable_weighted_clusters()->add_clusters();
weighted_cluster2->set_name(kNewCluster2Name);
weighted_cluster2->mutable_weight()->set_value(kWeight25);
route1->mutable_route()
->mutable_weighted_clusters()
->mutable_total_weight()
->set_value(kWeight75 + kWeight25);
SetRouteConfiguration(balancer_.get(), new_route_config);
WaitForAllBackends(DEBUG_LOCATION, 1, 3);
CheckRpcSendOk(DEBUG_LOCATION, kNumEchoRpcs);
Expand Down Expand Up @@ -1124,10 +1198,6 @@ TEST_P(LdsRdsTest, XdsRoutingWeightedClusterUpdateWeights) {
route1->mutable_route()->mutable_weighted_clusters()->add_clusters();
weighted_cluster2->set_name(kNewCluster2Name);
weighted_cluster2->mutable_weight()->set_value(kWeight25);
route1->mutable_route()
->mutable_weighted_clusters()
->mutable_total_weight()
->set_value(kWeight75 + kWeight25);
auto* default_route = new_route_config.mutable_virtual_hosts(0)->add_routes();
default_route->mutable_match()->set_prefix("");
default_route->mutable_route()->set_cluster(kDefaultClusterName);
Expand Down Expand Up @@ -1264,10 +1334,6 @@ TEST_P(LdsRdsTest, XdsRoutingWeightedClusterUpdateClusters) {
route1->mutable_route()->mutable_weighted_clusters()->add_clusters();
weighted_cluster2->set_name(kDefaultClusterName);
weighted_cluster2->mutable_weight()->set_value(kWeight25);
route1->mutable_route()
->mutable_weighted_clusters()
->mutable_total_weight()
->set_value(kWeight75 + kWeight25);
auto* default_route = new_route_config.mutable_virtual_hosts(0)->add_routes();
default_route->mutable_match()->set_prefix("");
default_route->mutable_route()->set_cluster(kDefaultClusterName);
Expand Down