client channel: add tests for authority override precedence (#30172)

* client channel: add tests for authority override precedence

* add unit test

* Automated change: Fix sanity tests

Co-authored-by: markdroth <markdroth@users.noreply.github.com>
pull/30327/head
Mark D. Roth 2 years ago committed by GitHub
parent 1cad82802b
commit 201213c681
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 36
      CMakeLists.txt
  2. 9
      build_autogenerated.yaml
  3. 37
      src/core/ext/filters/client_channel/client_channel.cc
  4. 6
      src/core/ext/filters/client_channel/client_channel.h
  5. 12
      test/core/client_channel/BUILD
  6. 70
      test/core/client_channel/client_channel_test.cc
  7. 87
      test/cpp/end2end/client_lb_end2end_test.cc
  8. 24
      tools/run_tests/generated/tests.json

36
CMakeLists.txt generated

@ -913,6 +913,7 @@ if(gRPC_BUILD_TESTS)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_dependencies(buildtests_cxx client_channel_stress_test)
endif()
add_dependencies(buildtests_cxx client_channel_test)
add_dependencies(buildtests_cxx client_context_test_peer_test)
add_dependencies(buildtests_cxx client_interceptors_end2end_test)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
@ -7844,6 +7845,41 @@ endif()
endif()
if(gRPC_BUILD_TESTS)
add_executable(client_channel_test
test/core/client_channel/client_channel_test.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(client_channel_test
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
${_gRPC_RE2_INCLUDE_DIR}
${_gRPC_SSL_INCLUDE_DIR}
${_gRPC_UPB_GENERATED_DIR}
${_gRPC_UPB_GRPC_GENERATED_DIR}
${_gRPC_UPB_INCLUDE_DIR}
${_gRPC_XXHASH_INCLUDE_DIR}
${_gRPC_ZLIB_INCLUDE_DIR}
third_party/googletest/googletest/include
third_party/googletest/googletest
third_party/googletest/googlemock/include
third_party/googletest/googlemock
${_gRPC_PROTO_GENS_DIR}
)
target_link_libraries(client_channel_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(client_context_test_peer_test
test/cpp/test/client_context_test_peer_test.cc
third_party/googletest/googletest/src/gtest-all.cc

@ -4669,6 +4669,15 @@ targets:
- linux
- posix
- mac
- name: client_channel_test
gtest: true
build: test
language: c++
headers: []
src:
- test/core/client_channel/client_channel_test.cc
deps:
- grpc_test_util
- name: client_context_test_peer_test
gtest: true
build: test

@ -862,17 +862,9 @@ class ClientChannel::ClientChannelControlHelper
args.GetOwnedString(GRPC_ARG_HEALTH_CHECK_SERVICE_NAME);
}
// Construct channel args for subchannel.
// TODO(roth): add a test that default authority overrides work as intended.
ChannelArgs subchannel_args =
args.UnionWith(address.args())
.SetObject(chand_->subchannel_pool_)
// If we haven't already set the default authority arg, add it from
// the channel.
.SetIfUnset(GRPC_ARG_DEFAULT_AUTHORITY, chand_->default_authority_)
// Remove channel args that should not affect subchannel uniqueness.
.Remove(GRPC_ARG_HEALTH_CHECK_SERVICE_NAME)
.Remove(GRPC_ARG_INHIBIT_HEALTH_CHECKING)
.Remove(GRPC_ARG_CHANNELZ_CHANNEL_NODE);
ChannelArgs subchannel_args = ClientChannel::MakeSubchannelArgs(
args, address.args(), chand_->subchannel_pool_,
chand_->default_authority_);
// Create subchannel.
RefCountedPtr<Subchannel> subchannel =
chand_->client_channel_factory_->CreateSubchannel(address.address(),
@ -1078,6 +1070,29 @@ ClientChannel::CreateLoadBalancedCall(
call_dispatch_controller, is_transparent_retry));
}
ChannelArgs ClientChannel::MakeSubchannelArgs(
const ChannelArgs& channel_args, const ChannelArgs& address_args,
const RefCountedPtr<SubchannelPoolInterface>& subchannel_pool,
const std::string& channel_default_authority) {
// Note that we start with the channel-level args and then apply the
// per-address args, so that if a value is present in both, the one
// in the channel-level args is used. This is particularly important
// for the GRPC_ARG_DEFAULT_AUTHORITY arg, which we want to allow
// resolvers to set on a per-address basis only if the application
// did not explicitly set it at the channel level.
return channel_args.UnionWith(address_args)
.SetObject(subchannel_pool)
// If we haven't already set the default authority arg (i.e., it
// was not explicitly set by the application nor overridden by
// the resolver), add it from the channel's default.
.SetIfUnset(GRPC_ARG_DEFAULT_AUTHORITY, channel_default_authority)
// Remove channel args that should not affect subchannel
// uniqueness.
.Remove(GRPC_ARG_HEALTH_CHECK_SERVICE_NAME)
.Remove(GRPC_ARG_INHIBIT_HEALTH_CHECKING)
.Remove(GRPC_ARG_CHANNELZ_CHANNEL_NODE);
}
namespace {
RefCountedPtr<LoadBalancingPolicy::Config> ChooseLbPolicy(

@ -168,6 +168,12 @@ class ClientChannel {
ConfigSelector::CallDispatchController* call_dispatch_controller,
bool is_transparent_retry);
// Exposed for testing only.
static ChannelArgs MakeSubchannelArgs(
const ChannelArgs& channel_args, const ChannelArgs& address_args,
const RefCountedPtr<SubchannelPoolInterface>& subchannel_pool,
const std::string& channel_default_authority);
private:
class CallData;
class ResolverResultHandler;

@ -18,6 +18,18 @@ grpc_package(name = "test/core/client_channel")
licenses(["notice"])
grpc_cc_test(
name = "client_channel_test",
srcs = ["client_channel_test.cc"],
external_deps = ["gtest"],
language = "C++",
deps = [
"//:channel_args",
"//:grpc_client_channel",
"//test/core/util:grpc_test_util",
],
)
grpc_cc_test(
name = "certificate_provider_registry_test",
srcs = ["certificate_provider_registry_test.cc"],

@ -0,0 +1,70 @@
//
// Copyright 2022 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include <grpc/support/port_platform.h>
#include "src/core/ext/filters/client_channel/client_channel.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "src/core/lib/channel/channel_args.h"
#include "test/core/util/test_config.h"
namespace grpc_core {
namespace testing {
namespace {
TEST(MakeSubchannelArgs, UsesChannelDefaultAuthorityByDefault) {
ChannelArgs args = ClientChannel::MakeSubchannelArgs(
ChannelArgs(), ChannelArgs(), nullptr, "foo.example.com");
EXPECT_EQ(args.GetString(GRPC_ARG_DEFAULT_AUTHORITY), "foo.example.com");
}
TEST(MakeSubchannelArgs, DefaultAuthorityFromChannelArgs) {
ChannelArgs args = ClientChannel::MakeSubchannelArgs(
ChannelArgs().Set(GRPC_ARG_DEFAULT_AUTHORITY, "bar.example.com"),
ChannelArgs(), nullptr, "foo.example.com");
EXPECT_EQ(args.GetString(GRPC_ARG_DEFAULT_AUTHORITY), "bar.example.com");
}
TEST(MakeSubchannelArgs, DefaultAuthorityFromResolver) {
ChannelArgs args = ClientChannel::MakeSubchannelArgs(
ChannelArgs(),
ChannelArgs().Set(GRPC_ARG_DEFAULT_AUTHORITY, "bar.example.com"), nullptr,
"foo.example.com");
EXPECT_EQ(args.GetString(GRPC_ARG_DEFAULT_AUTHORITY), "bar.example.com");
}
TEST(MakeSubchannelArgs,
DefaultAuthorityFromChannelArgsOverridesValueFromResolver) {
ChannelArgs args = ClientChannel::MakeSubchannelArgs(
ChannelArgs().Set(GRPC_ARG_DEFAULT_AUTHORITY, "bar.example.com"),
ChannelArgs().Set(GRPC_ARG_DEFAULT_AUTHORITY, "baz.example.com"), nullptr,
"foo.example.com");
EXPECT_EQ(args.GetString(GRPC_ARG_DEFAULT_AUTHORITY), "bar.example.com");
}
} // namespace
} // namespace testing
} // namespace grpc_core
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
grpc::testing::TestEnvironment env(&argc, argv);
auto result = RUN_ALL_TESTS();
return result;
}

@ -156,11 +156,13 @@ class FakeResolverResponseGeneratorWrapper {
const std::vector<int>& ports, const char* service_config_json = nullptr,
const char* attribute_key = nullptr,
std::unique_ptr<grpc_core::ServerAddress::AttributeInterface> attribute =
nullptr) {
nullptr,
const grpc_core::ChannelArgs& per_address_args =
grpc_core::ChannelArgs()) {
grpc_core::ExecCtx exec_ctx;
response_generator_->SetResponse(
BuildFakeResults(ipv6_only_, ports, service_config_json, attribute_key,
std::move(attribute)));
std::move(attribute), per_address_args));
}
void SetNextResolutionUponError(const std::vector<int>& ports) {
@ -184,7 +186,9 @@ class FakeResolverResponseGeneratorWrapper {
const char* service_config_json = nullptr,
const char* attribute_key = nullptr,
std::unique_ptr<grpc_core::ServerAddress::AttributeInterface> attribute =
nullptr) {
nullptr,
const grpc_core::ChannelArgs& per_address_args =
grpc_core::ChannelArgs()) {
grpc_core::Resolver::Result result;
result.addresses = grpc_core::ServerAddressList();
for (const int& port : ports) {
@ -200,8 +204,7 @@ class FakeResolverResponseGeneratorWrapper {
attributes[attribute_key] = attribute->Copy();
}
result.addresses->emplace_back(address.addr, address.len,
grpc_core::ChannelArgs(),
std::move(attributes));
per_address_args, std::move(attributes));
}
if (result.addresses->empty()) {
result.resolution_note = "fake resolver empty address list";
@ -303,7 +306,7 @@ class ClientLbEnd2endTest : public ::testing::Test {
} // else, default to pick first
args.SetPointer(GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR,
response_generator.Get());
return grpc::CreateCustomChannel("fake:///", creds_, args);
return grpc::CreateCustomChannel("fake:default.example.com", creds_, args);
}
Status SendRpc(
@ -602,6 +605,78 @@ TEST_F(ClientLbEnd2endTest, ChannelIdleness) {
EXPECT_EQ(channel->GetState(false), GRPC_CHANNEL_READY);
}
TEST_F(ClientLbEnd2endTest, AuthorityOverrideOnChannel) {
StartServers(1);
// Set authority via channel arg.
auto response_generator = BuildResolverResponseGenerator();
ChannelArguments args;
args.SetString(GRPC_ARG_DEFAULT_AUTHORITY, "foo.example.com");
auto channel = BuildChannel("", response_generator, args);
auto stub = BuildStub(channel);
response_generator.SetNextResolution(GetServersPorts());
// Send an RPC.
EchoRequest request;
request.mutable_param()->set_echo_host_from_authority_header(true);
EchoResponse response;
Status status = SendRpc(stub, &response, /*timeout_ms=*/1000,
/*wait_for_ready=*/false, &request);
EXPECT_TRUE(status.ok()) << "code=" << status.error_code()
<< " message=" << status.error_message();
// Check that the right authority was seen by the server.
EXPECT_EQ("foo.example.com", response.param().host());
}
TEST_F(ClientLbEnd2endTest, AuthorityOverrideFromResolver) {
StartServers(1);
auto response_generator = BuildResolverResponseGenerator();
auto channel = BuildChannel("", response_generator);
auto stub = BuildStub(channel);
// Inject resolver result that sets the per-address authority to a
// different value.
response_generator.SetNextResolution(
GetServersPorts(), /*service_config_json=*/nullptr,
/*attribute_key=*/nullptr, /*attribute=*/nullptr,
grpc_core::ChannelArgs().Set(GRPC_ARG_DEFAULT_AUTHORITY,
"foo.example.com"));
// Send an RPC.
EchoRequest request;
request.mutable_param()->set_echo_host_from_authority_header(true);
EchoResponse response;
Status status = SendRpc(stub, &response, /*timeout_ms=*/1000,
/*wait_for_ready=*/false, &request);
EXPECT_TRUE(status.ok()) << "code=" << status.error_code()
<< " message=" << status.error_message();
// Check that the right authority was seen by the server.
EXPECT_EQ("foo.example.com", response.param().host());
}
TEST_F(ClientLbEnd2endTest, AuthorityOverridePrecedence) {
StartServers(1);
// Set authority via channel arg.
auto response_generator = BuildResolverResponseGenerator();
ChannelArguments args;
args.SetString(GRPC_ARG_DEFAULT_AUTHORITY, "foo.example.com");
auto channel = BuildChannel("", response_generator, args);
auto stub = BuildStub(channel);
// Inject resolver result that sets the per-address authority to a
// different value.
response_generator.SetNextResolution(
GetServersPorts(), /*service_config_json=*/nullptr,
/*attribute_key=*/nullptr, /*attribute=*/nullptr,
grpc_core::ChannelArgs().Set(GRPC_ARG_DEFAULT_AUTHORITY,
"bar.example.com"));
// Send an RPC.
EchoRequest request;
request.mutable_param()->set_echo_host_from_authority_header(true);
EchoResponse response;
Status status = SendRpc(stub, &response, /*timeout_ms=*/1000,
/*wait_for_ready=*/false, &request);
EXPECT_TRUE(status.ok()) << "code=" << status.error_code()
<< " message=" << status.error_message();
// Check that the right authority was seen by the server.
EXPECT_EQ("foo.example.com", response.param().host());
}
//
// pick_first tests
//

@ -2195,6 +2195,30 @@
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": true,
"language": "c++",
"name": "client_channel_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,

Loading…
Cancel
Save