[xDS] improve RPC failure status message when aggregate cluster graph has no leaf clusters (#34201)

Old message:
new_cluster_1: UNAVAILABLE: errors validating xds_cluster_resolver LB
policy config: [field:discoveryMechanisms error:must be non-empty]

New message:
new_cluster_1: FAILED_PRECONDITION: aggregate cluster graph has no leaf
clusters
pull/34209/head
Mark D. Roth 2 years ago committed by GitHub
parent fb635a1959
commit 1c54662866
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      src/core/ext/filters/client_channel/lb_policy/xds/cds.cc
  2. 166
      test/cpp/end2end/xds/xds_cluster_type_end2end_test.cc

@ -457,6 +457,10 @@ void CdsLb::OnClusterChanged(
return OnError(name, result.status());
}
if (*result) {
if (discovery_mechanisms.empty()) {
return OnError(name, absl::FailedPreconditionError(
"aggregate cluster graph has no leaf clusters"));
}
// LB policy is configured by aggregate cluster, not by the individual
// underlying cluster that we may be processing an update for.
auto it = watchers_.find(config_->cluster());

@ -171,66 +171,6 @@ TEST_P(AggregateClusterTest, Basic) {
WaitForBackend(DEBUG_LOCATION, 0);
}
TEST_P(AggregateClusterTest, DiamondDependency) {
const char* kNewClusterName1 = "new_cluster_1";
const char* kNewEdsServiceName1 = "new_eds_service_name_1";
const char* kNewClusterName2 = "new_cluster_2";
const char* kNewEdsServiceName2 = "new_eds_service_name_2";
const char* kNewAggregateClusterName = "new_aggregate_cluster";
// Populate new EDS resources.
CreateAndStartBackends(2);
EdsResourceArgs args1({{"locality0", CreateEndpointsForBackends(0, 1)}});
balancer_->ads_service()->SetEdsResource(
BuildEdsResource(args1, kNewEdsServiceName1));
EdsResourceArgs args2({{"locality0", CreateEndpointsForBackends(1, 2)}});
balancer_->ads_service()->SetEdsResource(
BuildEdsResource(args2, kNewEdsServiceName2));
// Populate new CDS resources.
Cluster new_cluster1 = default_cluster_;
new_cluster1.set_name(kNewClusterName1);
new_cluster1.mutable_eds_cluster_config()->set_service_name(
kNewEdsServiceName1);
balancer_->ads_service()->SetCdsResource(new_cluster1);
Cluster new_cluster2 = default_cluster_;
new_cluster2.set_name(kNewClusterName2);
new_cluster2.mutable_eds_cluster_config()->set_service_name(
kNewEdsServiceName2);
balancer_->ads_service()->SetCdsResource(new_cluster2);
// Populate top-level aggregate cluster pointing to kNewClusterName1
// and kNewAggregateClusterName.
auto cluster = default_cluster_;
CustomClusterType* custom_cluster = cluster.mutable_cluster_type();
custom_cluster->set_name("envoy.clusters.aggregate");
ClusterConfig cluster_config;
cluster_config.add_clusters(kNewClusterName1);
cluster_config.add_clusters(kNewAggregateClusterName);
custom_cluster->mutable_typed_config()->PackFrom(cluster_config);
balancer_->ads_service()->SetCdsResource(cluster);
// Populate kNewAggregateClusterName aggregate cluster pointing to
// kNewClusterName1 and kNewClusterName2.
auto aggregate_cluster2 = default_cluster_;
aggregate_cluster2.set_name(kNewAggregateClusterName);
custom_cluster = aggregate_cluster2.mutable_cluster_type();
custom_cluster->set_name("envoy.clusters.aggregate");
cluster_config.Clear();
cluster_config.add_clusters(kNewClusterName1);
cluster_config.add_clusters(kNewClusterName2);
custom_cluster->mutable_typed_config()->PackFrom(cluster_config);
balancer_->ads_service()->SetCdsResource(aggregate_cluster2);
// Wait for traffic to go to backend 0.
WaitForBackend(DEBUG_LOCATION, 0);
// Shutdown backend 0 and wait for all traffic to go to backend 1.
backends_[0]->StopListeningAndSendGoaways();
WaitForBackend(DEBUG_LOCATION, 1);
auto response_state = balancer_->ads_service()->cds_response_state();
ASSERT_TRUE(response_state.has_value());
EXPECT_EQ(response_state->state, AdsServiceImpl::ResponseState::ACKED);
// Bring backend 0 back and ensure all traffic go back to it.
ShutdownBackend(0);
StartBackend(0);
WaitForBackend(DEBUG_LOCATION, 0);
}
// This test covers a bug found in the following scenario:
// 1. P0 reports TRANSIENT_FAILURE, so we start connecting to P1.
// 2. While P1 is still in CONNECTING, P0 goes back to READY, so we
@ -602,6 +542,112 @@ TEST_P(AggregateClusterTest, UpdateOfChildCluster) {
EXPECT_EQ(response_state->state, AdsServiceImpl::ResponseState::ACKED);
}
TEST_P(AggregateClusterTest, DiamondDependency) {
const char* kNewClusterName1 = "new_cluster_1";
const char* kNewEdsServiceName1 = "new_eds_service_name_1";
const char* kNewClusterName2 = "new_cluster_2";
const char* kNewEdsServiceName2 = "new_eds_service_name_2";
const char* kNewAggregateClusterName = "new_aggregate_cluster";
// Populate new EDS resources.
CreateAndStartBackends(2);
EdsResourceArgs args1({{"locality0", CreateEndpointsForBackends(0, 1)}});
balancer_->ads_service()->SetEdsResource(
BuildEdsResource(args1, kNewEdsServiceName1));
EdsResourceArgs args2({{"locality0", CreateEndpointsForBackends(1, 2)}});
balancer_->ads_service()->SetEdsResource(
BuildEdsResource(args2, kNewEdsServiceName2));
// Populate new CDS resources.
Cluster new_cluster1 = default_cluster_;
new_cluster1.set_name(kNewClusterName1);
new_cluster1.mutable_eds_cluster_config()->set_service_name(
kNewEdsServiceName1);
balancer_->ads_service()->SetCdsResource(new_cluster1);
Cluster new_cluster2 = default_cluster_;
new_cluster2.set_name(kNewClusterName2);
new_cluster2.mutable_eds_cluster_config()->set_service_name(
kNewEdsServiceName2);
balancer_->ads_service()->SetCdsResource(new_cluster2);
// Populate top-level aggregate cluster pointing to kNewClusterName1
// and kNewAggregateClusterName.
auto cluster = default_cluster_;
CustomClusterType* custom_cluster = cluster.mutable_cluster_type();
custom_cluster->set_name("envoy.clusters.aggregate");
ClusterConfig cluster_config;
cluster_config.add_clusters(kNewClusterName1);
cluster_config.add_clusters(kNewAggregateClusterName);
custom_cluster->mutable_typed_config()->PackFrom(cluster_config);
balancer_->ads_service()->SetCdsResource(cluster);
// Populate kNewAggregateClusterName aggregate cluster pointing to
// kNewClusterName1 and kNewClusterName2.
auto aggregate_cluster2 = default_cluster_;
aggregate_cluster2.set_name(kNewAggregateClusterName);
custom_cluster = aggregate_cluster2.mutable_cluster_type();
custom_cluster->set_name("envoy.clusters.aggregate");
cluster_config.Clear();
cluster_config.add_clusters(kNewClusterName1);
cluster_config.add_clusters(kNewClusterName2);
custom_cluster->mutable_typed_config()->PackFrom(cluster_config);
balancer_->ads_service()->SetCdsResource(aggregate_cluster2);
// Wait for traffic to go to backend 0.
WaitForBackend(DEBUG_LOCATION, 0);
// Shutdown backend 0 and wait for all traffic to go to backend 1.
backends_[0]->StopListeningAndSendGoaways();
WaitForBackend(DEBUG_LOCATION, 1);
auto response_state = balancer_->ads_service()->cds_response_state();
ASSERT_TRUE(response_state.has_value());
EXPECT_EQ(response_state->state, AdsServiceImpl::ResponseState::ACKED);
// Bring backend 0 back and ensure all traffic go back to it.
ShutdownBackend(0);
StartBackend(0);
WaitForBackend(DEBUG_LOCATION, 0);
}
TEST_P(AggregateClusterTest, DependencyLoopWithNoLeafClusters) {
const char* kNewClusterName1 = "new_cluster_1";
// Default cluster is an aggregate cluster pointing to kNewClusterName1.
auto cluster = default_cluster_;
CustomClusterType* custom_cluster = cluster.mutable_cluster_type();
custom_cluster->set_name("envoy.clusters.aggregate");
ClusterConfig cluster_config;
cluster_config.add_clusters(kNewClusterName1);
custom_cluster->mutable_typed_config()->PackFrom(cluster_config);
balancer_->ads_service()->SetCdsResource(cluster);
// kDefaultClusterName points to the default cluster.
cluster.set_name(kNewClusterName1);
cluster_config.Clear();
cluster_config.add_clusters(kDefaultClusterName);
custom_cluster->mutable_typed_config()->PackFrom(cluster_config);
balancer_->ads_service()->SetCdsResource(cluster);
// RPCs should fail.
CheckRpcSendFailure(DEBUG_LOCATION, StatusCode::UNAVAILABLE,
"new_cluster_1: FAILED_PRECONDITION: "
"aggregate cluster graph has no leaf clusters");
}
TEST_P(AggregateClusterTest, DependencyLoopWithLeafClusters) {
const char* kNewClusterName1 = "new_cluster_1";
// Populate new EDS resource.
CreateAndStartBackends(1);
EdsResourceArgs args1({{"locality0", CreateEndpointsForBackends()}});
balancer_->ads_service()->SetEdsResource(BuildEdsResource(args1));
// Populate new CDS resource.
Cluster new_cluster1 = default_cluster_;
new_cluster1.set_name(kNewClusterName1);
balancer_->ads_service()->SetCdsResource(new_cluster1);
// Populate top-level aggregate cluster pointing to itself and the new
// CDS cluster.
auto cluster = default_cluster_;
CustomClusterType* custom_cluster = cluster.mutable_cluster_type();
custom_cluster->set_name("envoy.clusters.aggregate");
ClusterConfig cluster_config;
cluster_config.add_clusters(kNewClusterName1);
cluster_config.add_clusters(kDefaultClusterName);
custom_cluster->mutable_typed_config()->PackFrom(cluster_config);
balancer_->ads_service()->SetCdsResource(cluster);
// RPCs should work.
CheckRpcSendOk(DEBUG_LOCATION);
}
TEST_P(AggregateClusterTest, RecursionDepthJustBelowMax) {
// Populate EDS resource.
CreateAndStartBackends(1);

Loading…
Cancel
Save