|
|
|
@ -13,8 +13,10 @@ |
|
|
|
|
// limitations under the License.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#include <algorithm> |
|
|
|
|
#include <memory> |
|
|
|
|
#include <string> |
|
|
|
|
#include <type_traits> |
|
|
|
|
#include <vector> |
|
|
|
|
|
|
|
|
|
#include <gmock/gmock.h> |
|
|
|
@ -24,9 +26,11 @@ |
|
|
|
|
|
|
|
|
|
#include "src/core/client_channel/backup_poller.h" |
|
|
|
|
#include "src/core/lib/config/config_vars.h" |
|
|
|
|
#include "src/proto/grpc/testing/xds/v3/listener.pb.h" |
|
|
|
|
#include "test/core/util/resolve_localhost_ip46.h" |
|
|
|
|
#include "test/core/util/scoped_env_var.h" |
|
|
|
|
#include "test/cpp/end2end/xds/xds_end2end_test_lib.h" |
|
|
|
|
#include "test/cpp/end2end/xds/xds_server.h" |
|
|
|
|
|
|
|
|
|
namespace grpc { |
|
|
|
|
namespace testing { |
|
|
|
@ -167,7 +171,21 @@ INSTANTIATE_TEST_SUITE_P(XdsTest, GlobalXdsClientTest, |
|
|
|
|
XdsTestType::kBootstrapFromEnvVar)), |
|
|
|
|
&XdsTestType::Name); |
|
|
|
|
|
|
|
|
|
TEST_P(GlobalXdsClientTest, MultipleChannelsShareXdsClient) { |
|
|
|
|
TEST_P(GlobalXdsClientTest, MultipleChannelsSameTargetShareXdsClient) { |
|
|
|
|
CreateAndStartBackends(1); |
|
|
|
|
EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}}); |
|
|
|
|
balancer_->ads_service()->SetEdsResource(BuildEdsResource(args)); |
|
|
|
|
WaitForAllBackends(DEBUG_LOCATION); |
|
|
|
|
// Create second channel and tell it to connect to the same server.
|
|
|
|
|
auto channel2 = CreateChannel(/*failover_timeout_ms=*/0, kServerName); |
|
|
|
|
channel2->GetState(/*try_to_connect=*/true); |
|
|
|
|
ASSERT_TRUE(channel2->WaitForConnected(grpc_timeout_seconds_to_deadline(1))); |
|
|
|
|
// Make sure there's only one client connected.
|
|
|
|
|
EXPECT_EQ(1UL, balancer_->ads_service()->clients().size()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TEST_P(GlobalXdsClientTest, |
|
|
|
|
MultipleChannelsDifferentTargetDoNotShareXdsClient) { |
|
|
|
|
CreateAndStartBackends(1); |
|
|
|
|
const char* kNewServerName = "new-server.example.com"; |
|
|
|
|
Listener listener = default_listener_; |
|
|
|
@ -181,8 +199,8 @@ TEST_P(GlobalXdsClientTest, MultipleChannelsShareXdsClient) { |
|
|
|
|
auto channel2 = CreateChannel(/*failover_timeout_ms=*/0, kNewServerName); |
|
|
|
|
channel2->GetState(/*try_to_connect=*/true); |
|
|
|
|
ASSERT_TRUE(channel2->WaitForConnected(grpc_timeout_seconds_to_deadline(1))); |
|
|
|
|
// Make sure there's only one client connected.
|
|
|
|
|
EXPECT_EQ(1UL, balancer_->ads_service()->clients().size()); |
|
|
|
|
// Make sure there are two clients connected.
|
|
|
|
|
EXPECT_EQ(2UL, balancer_->ads_service()->clients().size()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TEST_P( |
|
|
|
@ -219,64 +237,62 @@ TEST_P( |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Tests that the NACK for multiple bad LDS resources includes both errors.
|
|
|
|
|
// This needs to be in GlobalXdsClientTest because the only way to request
|
|
|
|
|
// two LDS resources in the same XdsClient is for two channels to share
|
|
|
|
|
// the same XdsClient.
|
|
|
|
|
// This needs to use xDS server as this is the only scenario when XdsClient
|
|
|
|
|
// is shared.
|
|
|
|
|
TEST_P(GlobalXdsClientTest, MultipleBadLdsResources) { |
|
|
|
|
CreateAndStartBackends(1); |
|
|
|
|
constexpr char kServerName2[] = "server.other.com"; |
|
|
|
|
constexpr char kServerName3[] = "server.another.com"; |
|
|
|
|
auto listener = default_listener_; |
|
|
|
|
listener.clear_api_listener(); |
|
|
|
|
balancer_->ads_service()->SetLdsResource(listener); |
|
|
|
|
listener.set_name(kServerName2); |
|
|
|
|
balancer_->ads_service()->SetLdsResource(listener); |
|
|
|
|
listener = default_listener_; |
|
|
|
|
listener.set_name(kServerName3); |
|
|
|
|
SetListenerAndRouteConfiguration(balancer_.get(), listener, |
|
|
|
|
default_route_config_); |
|
|
|
|
EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}}); |
|
|
|
|
CreateBackends(2, true); |
|
|
|
|
EdsResourceArgs args({{"locality0", CreateEndpointsForBackends(0, 2)}}); |
|
|
|
|
balancer_->ads_service()->SetEdsResource(BuildEdsResource(args)); |
|
|
|
|
const auto response_state = WaitForLdsNack(DEBUG_LOCATION); |
|
|
|
|
auto listener = default_server_listener_; |
|
|
|
|
listener.clear_address(); |
|
|
|
|
listener.set_name(GetServerListenerName(backends_[0]->port())); |
|
|
|
|
balancer_->ads_service()->SetLdsResource(listener); |
|
|
|
|
backends_[0]->Start(); |
|
|
|
|
auto response_state = WaitForLdsNack(DEBUG_LOCATION); |
|
|
|
|
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; |
|
|
|
|
EXPECT_EQ(response_state->error_message, |
|
|
|
|
"xDS response validation errors: [" |
|
|
|
|
"resource index 0: server.example.com: " |
|
|
|
|
"INVALID_ARGUMENT: Listener has neither address nor ApiListener]"); |
|
|
|
|
// Need to create a second channel to subscribe to a second LDS resource.
|
|
|
|
|
auto channel2 = CreateChannel(0, kServerName2); |
|
|
|
|
auto stub2 = grpc::testing::EchoTestService::NewStub(channel2); |
|
|
|
|
{ |
|
|
|
|
ClientContext context; |
|
|
|
|
EchoRequest request; |
|
|
|
|
request.set_message(kRequestMessage); |
|
|
|
|
EchoResponse response; |
|
|
|
|
grpc::Status status = stub2->Echo(&context, request, &response); |
|
|
|
|
EXPECT_FALSE(status.ok()); |
|
|
|
|
// Wait for second NACK to be reported to xDS server.
|
|
|
|
|
const auto response_state = WaitForLdsNack(DEBUG_LOCATION); |
|
|
|
|
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; |
|
|
|
|
EXPECT_EQ( |
|
|
|
|
response_state->error_message, |
|
|
|
|
"xDS response validation errors: [" |
|
|
|
|
"resource index 0: server.other.com: " |
|
|
|
|
"INVALID_ARGUMENT: Listener has neither address nor ApiListener; " |
|
|
|
|
"resource index 1: server.example.com: " |
|
|
|
|
"INVALID_ARGUMENT: Listener has neither address nor ApiListener]"); |
|
|
|
|
} |
|
|
|
|
// Now start a new channel with a third server name, this one with a
|
|
|
|
|
// valid resource.
|
|
|
|
|
auto channel3 = CreateChannel(0, kServerName3); |
|
|
|
|
auto stub3 = grpc::testing::EchoTestService::NewStub(channel3); |
|
|
|
|
{ |
|
|
|
|
ClientContext context; |
|
|
|
|
EchoRequest request; |
|
|
|
|
request.set_message(kRequestMessage); |
|
|
|
|
EchoResponse response; |
|
|
|
|
grpc::Status status = stub3->Echo(&context, request, &response); |
|
|
|
|
EXPECT_TRUE(status.ok()) << "code=" << status.error_code() |
|
|
|
|
<< " message=" << status.error_message(); |
|
|
|
|
} |
|
|
|
|
EXPECT_EQ( |
|
|
|
|
response_state->error_message, |
|
|
|
|
absl::StrFormat( |
|
|
|
|
"xDS response validation errors: [" |
|
|
|
|
"resource index 0: " |
|
|
|
|
"grpc/server?xds.resource.listening_address=127.0.0.1:%lu: " |
|
|
|
|
"INVALID_ARGUMENT: Listener has neither address nor ApiListener]", |
|
|
|
|
backends_[0]->port())); |
|
|
|
|
listener = default_server_listener_; |
|
|
|
|
listener.clear_address(); |
|
|
|
|
listener.set_name(GetServerListenerName(backends_[1]->port())); |
|
|
|
|
balancer_->ads_service()->SetLdsResource(listener); |
|
|
|
|
backends_[1]->Start(); |
|
|
|
|
constexpr absl::string_view kMessageFormat = |
|
|
|
|
"xDS response validation errors: [" |
|
|
|
|
"resource index 0: " |
|
|
|
|
"grpc/server?xds.resource.listening_address=127.0.0.1:%d: " |
|
|
|
|
"INVALID_ARGUMENT: Listener has neither address nor " |
|
|
|
|
"ApiListener; " |
|
|
|
|
"resource index 1: " |
|
|
|
|
"grpc/server?xds.resource.listening_address=127.0.0.1:%d: " |
|
|
|
|
"INVALID_ARGUMENT: Listener has neither address nor " |
|
|
|
|
"ApiListener" |
|
|
|
|
"]"; |
|
|
|
|
const std::string expected_message1 = absl::StrFormat( |
|
|
|
|
kMessageFormat, backends_[0]->port(), backends_[1]->port()); |
|
|
|
|
const std::string expected_message2 = absl::StrFormat( |
|
|
|
|
kMessageFormat, backends_[1]->port(), backends_[0]->port()); |
|
|
|
|
response_state = WaitForNack( |
|
|
|
|
DEBUG_LOCATION, [&]() -> absl::optional<AdsServiceImpl::ResponseState> { |
|
|
|
|
auto response = balancer_->ads_service()->lds_response_state(); |
|
|
|
|
if (response.has_value() && |
|
|
|
|
response->state == AdsServiceImpl::ResponseState::NACKED) { |
|
|
|
|
if (response->error_message == expected_message1 || |
|
|
|
|
response->error_message == expected_message2) { |
|
|
|
|
return response; |
|
|
|
|
} |
|
|
|
|
gpr_log(GPR_INFO, "non-matching NACK message: %s", |
|
|
|
|
response->error_message.c_str()); |
|
|
|
|
} |
|
|
|
|
return absl::nullopt; |
|
|
|
|
}); |
|
|
|
|
EXPECT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Tests that we don't trigger does-not-exist callbacks for a resource
|
|
|
|
|