// Copyright 2017 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.
//
// TODO(roth): Split this file up into a common test framework and a set
// of test files that use that framework. Need to figure out the best
// way to split up the tests. One option would be to split it up by xDS
// resource type; another approach would be to have all of the "core"
// xDS functionality in one file and then move specific features to
// their own files (e.g., mTLS security, fault injection, circuit
// breaking, etc).
# include <deque>
# include <memory>
# include <mutex>
# include <numeric>
# include <set>
# include <sstream>
# include <string>
# include <thread>
# include <vector>
# include <gmock/gmock.h>
# include <gtest/gtest.h>
# include "absl/functional/bind_front.h"
# include "absl/memory/memory.h"
# include "absl/strings/match.h"
# include "absl/strings/str_cat.h"
# include "absl/strings/str_format.h"
# include "absl/strings/str_join.h"
# include "absl/strings/str_replace.h"
# include "absl/types/optional.h"
# include <grpc/grpc.h>
# include <grpc/grpc_security.h>
# include <grpc/support/alloc.h>
# include <grpc/support/log.h>
# include <grpc/support/time.h>
# include <grpcpp/channel.h>
# include <grpcpp/client_context.h>
# include <grpcpp/create_channel.h>
# include <grpcpp/security/tls_certificate_provider.h>
# include <grpcpp/server.h>
# include <grpcpp/server_builder.h>
# include <grpcpp/xds_server_builder.h>
# include "src/core/ext/filters/client_channel/backup_poller.h"
# include "src/core/ext/filters/client_channel/lb_policy/xds/xds_channel_args.h"
# include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h"
# include "src/core/ext/filters/http/client/http_client_filter.h"
# include "src/core/ext/filters/http/server/http_server_filter.h"
# include "src/core/ext/xds/certificate_provider_registry.h"
# include "src/core/ext/xds/xds_api.h"
# include "src/core/ext/xds/xds_channel_args.h"
# include "src/core/ext/xds/xds_client.h"
# include "src/core/ext/xds/xds_listener.h"
# include "src/core/lib/address_utils/parse_address.h"
# include "src/core/lib/address_utils/sockaddr_utils.h"
# include "src/core/lib/channel/channel_args.h"
# include "src/core/lib/gpr/env.h"
# include "src/core/lib/gpr/string.h"
# include "src/core/lib/gpr/time_precise.h"
# include "src/core/lib/gpr/tmpfile.h"
# include "src/core/lib/gprpp/ref_counted_ptr.h"
# include "src/core/lib/gprpp/sync.h"
# include "src/core/lib/gprpp/time.h"
# include "src/core/lib/gprpp/time_util.h"
# include "src/core/lib/iomgr/load_file.h"
# include "src/core/lib/iomgr/sockaddr.h"
# include "src/core/lib/resolver/server_address.h"
# include "src/core/lib/security/credentials/fake/fake_credentials.h"
# include "src/cpp/client/secure_credentials.h"
# include "src/cpp/server/secure_server_credentials.h"
# include "src/proto/grpc/testing/echo.grpc.pb.h"
# include "src/proto/grpc/testing/xds/ads_for_test.grpc.pb.h"
# include "src/proto/grpc/testing/xds/cds_for_test.grpc.pb.h"
# include "src/proto/grpc/testing/xds/eds_for_test.grpc.pb.h"
# include "src/proto/grpc/testing/xds/lds_rds_for_test.grpc.pb.h"
# include "src/proto/grpc/testing/xds/lrs_for_test.grpc.pb.h"
# include "src/proto/grpc/testing/xds/v3/ads.grpc.pb.h"
# include "src/proto/grpc/testing/xds/v3/aggregate_cluster.grpc.pb.h"
# include "src/proto/grpc/testing/xds/v3/cluster.grpc.pb.h"
# include "src/proto/grpc/testing/xds/v3/discovery.grpc.pb.h"
# include "src/proto/grpc/testing/xds/v3/endpoint.grpc.pb.h"
# include "src/proto/grpc/testing/xds/v3/fault.grpc.pb.h"
# include "src/proto/grpc/testing/xds/v3/http_connection_manager.grpc.pb.h"
# include "src/proto/grpc/testing/xds/v3/http_filter_rbac.grpc.pb.h"
# include "src/proto/grpc/testing/xds/v3/listener.grpc.pb.h"
# include "src/proto/grpc/testing/xds/v3/lrs.grpc.pb.h"
# include "src/proto/grpc/testing/xds/v3/route.grpc.pb.h"
# include "src/proto/grpc/testing/xds/v3/router.grpc.pb.h"
# include "src/proto/grpc/testing/xds/v3/tls.grpc.pb.h"
# include "test/core/util/port.h"
# include "test/core/util/test_config.h"
# include "test/cpp/end2end/connection_delay_injector.h"
# include "test/cpp/end2end/xds/xds_end2end_test_lib.h"
# include "test/cpp/util/test_config.h"
# include "test/cpp/util/tls_test_utils.h"
namespace grpc {
namespace testing {
namespace {
using std : : chrono : : system_clock ;
using : : envoy : : config : : cluster : : v3 : : CircuitBreakers ;
using : : envoy : : config : : cluster : : v3 : : CustomClusterType ;
using : : envoy : : config : : cluster : : v3 : : RoutingPriority ;
using : : envoy : : config : : endpoint : : v3 : : HealthStatus ;
using : : envoy : : config : : listener : : v3 : : FilterChainMatch ;
using : : envoy : : config : : rbac : : v3 : : Policy ;
using : : envoy : : config : : rbac : : v3 : : RBAC_Action ;
using : : envoy : : config : : rbac : : v3 : : RBAC_Action_ALLOW ;
using : : envoy : : config : : rbac : : v3 : : RBAC_Action_DENY ;
using : : envoy : : config : : rbac : : v3 : : RBAC_Action_LOG ;
using : : envoy : : extensions : : clusters : : aggregate : : v3 : : ClusterConfig ;
using : : envoy : : extensions : : filters : : http : : fault : : v3 : : HTTPFault ;
using : : envoy : : extensions : : filters : : http : : rbac : : v3 : : RBAC ;
using : : envoy : : extensions : : filters : : http : : rbac : : v3 : : RBACPerRoute ;
using : : envoy : : extensions : : transport_sockets : : tls : : v3 : : DownstreamTlsContext ;
using : : envoy : : extensions : : transport_sockets : : tls : : v3 : : UpstreamTlsContext ;
using : : envoy : : type : : matcher : : v3 : : StringMatcher ;
using : : envoy : : type : : v3 : : FractionalPercent ;
using ClientStats = LrsServiceImpl : : ClientStats ;
using : : grpc : : experimental : : ExternalCertificateVerifier ;
using : : grpc : : experimental : : IdentityKeyCertPair ;
using : : grpc : : experimental : : StaticDataCertificateProvider ;
constexpr char kLbDropType [ ] = " lb " ;
constexpr char kThrottleDropType [ ] = " throttle " ;
constexpr char kClientCertPath [ ] = " src/core/tsi/test_creds/client.pem " ;
constexpr char kClientKeyPath [ ] = " src/core/tsi/test_creds/client.key " ;
constexpr char kBadClientCertPath [ ] = " src/core/tsi/test_creds/badclient.pem " ;
constexpr char kBadClientKeyPath [ ] = " src/core/tsi/test_creds/badclient.key " ;
// Based on StaticDataCertificateProvider, but provides alternate certificates
// if the certificate name is not empty.
class FakeCertificateProvider final : public grpc_tls_certificate_provider {
public :
struct CertData {
std : : string root_certificate ;
grpc_core : : PemKeyCertPairList identity_key_cert_pairs ;
} ;
using CertDataMap = std : : map < std : : string /*cert_name */ , CertData > ;
explicit FakeCertificateProvider ( CertDataMap cert_data_map )
: distributor_ (
grpc_core : : MakeRefCounted < grpc_tls_certificate_distributor > ( ) ) ,
cert_data_map_ ( std : : move ( cert_data_map ) ) {
distributor_ - > SetWatchStatusCallback ( [ this ] ( std : : string cert_name ,
bool root_being_watched ,
bool identity_being_watched ) {
if ( ! root_being_watched & & ! identity_being_watched ) return ;
auto it = cert_data_map_ . find ( cert_name ) ;
if ( it = = cert_data_map_ . end ( ) ) {
grpc_error_handle error =
GRPC_ERROR_CREATE_FROM_CPP_STRING ( absl : : StrCat (
" No certificates available for cert_name \" " , cert_name , " \" " ) ) ;
distributor_ - > SetErrorForCert ( cert_name , GRPC_ERROR_REF ( error ) ,
GRPC_ERROR_REF ( error ) ) ;
GRPC_ERROR_UNREF ( error ) ;
} else {
absl : : optional < std : : string > root_certificate ;
absl : : optional < grpc_core : : PemKeyCertPairList > pem_key_cert_pairs ;
if ( root_being_watched ) {
root_certificate = it - > second . root_certificate ;
}
if ( identity_being_watched ) {
pem_key_cert_pairs = it - > second . identity_key_cert_pairs ;
}
distributor_ - > SetKeyMaterials ( cert_name , std : : move ( root_certificate ) ,
std : : move ( pem_key_cert_pairs ) ) ;
}
} ) ;
}
~ FakeCertificateProvider ( ) override {
distributor_ - > SetWatchStatusCallback ( nullptr ) ;
}
grpc_core : : RefCountedPtr < grpc_tls_certificate_distributor > distributor ( )
const override {
return distributor_ ;
}
const char * type ( ) const override { return " fake " ; }
private :
int CompareImpl ( const grpc_tls_certificate_provider * other ) const override {
// TODO(yashykt): Maybe do something better here.
return grpc_core : : QsortCompare (
static_cast < const grpc_tls_certificate_provider * > ( this ) , other ) ;
}
grpc_core : : RefCountedPtr < grpc_tls_certificate_distributor > distributor_ ;
CertDataMap cert_data_map_ ;
} ;
class FakeCertificateProviderFactory
: public grpc_core : : CertificateProviderFactory {
public :
class Config : public grpc_core : : CertificateProviderFactory : : Config {
public :
explicit Config ( const char * name ) : name_ ( name ) { }
const char * name ( ) const override { return name_ ; }
std : : string ToString ( ) const override { return " {} " ; }
private :
const char * name_ ;
} ;
FakeCertificateProviderFactory (
const char * name , FakeCertificateProvider : : CertDataMap * * cert_data_map )
: name_ ( name ) , cert_data_map_ ( cert_data_map ) {
GPR_ASSERT ( cert_data_map ! = nullptr ) ;
}
const char * name ( ) const override { return name_ ; }
grpc_core : : RefCountedPtr < grpc_core : : CertificateProviderFactory : : Config >
CreateCertificateProviderConfig ( const grpc_core : : Json & /*config_json*/ ,
grpc_error_handle * /*error*/ ) override {
return grpc_core : : MakeRefCounted < Config > ( name_ ) ;
}
grpc_core : : RefCountedPtr < grpc_tls_certificate_provider >
CreateCertificateProvider (
grpc_core : : RefCountedPtr < grpc_core : : CertificateProviderFactory : : Config >
/*config*/ ) override {
if ( * cert_data_map_ = = nullptr ) return nullptr ;
return grpc_core : : MakeRefCounted < FakeCertificateProvider > ( * * cert_data_map_ ) ;
}
private :
const char * name_ ;
FakeCertificateProvider : : CertDataMap * * cert_data_map_ ;
} ;
// Global variables for each provider.
FakeCertificateProvider : : CertDataMap * g_fake1_cert_data_map = nullptr ;
FakeCertificateProvider : : CertDataMap * g_fake2_cert_data_map = nullptr ;
// A No-op HTTP filter used for verifying parsing logic.
class NoOpHttpFilter : public grpc_core : : XdsHttpFilterImpl {
public :
NoOpHttpFilter ( std : : string name , bool supported_on_clients ,
bool supported_on_servers , bool is_terminal_filter )
: name_ ( std : : move ( name ) ) ,
supported_on_clients_ ( supported_on_clients ) ,
supported_on_servers_ ( supported_on_servers ) ,
is_terminal_filter_ ( is_terminal_filter ) { }
void PopulateSymtab ( upb_DefPool * /* symtab */ ) const override { }
absl : : StatusOr < grpc_core : : XdsHttpFilterImpl : : FilterConfig >
GenerateFilterConfig ( upb_StringView /* serialized_filter_config */ ,
upb_Arena * /* arena */ ) const override {
return grpc_core : : XdsHttpFilterImpl : : FilterConfig { name_ , grpc_core : : Json ( ) } ;
}
absl : : StatusOr < grpc_core : : XdsHttpFilterImpl : : FilterConfig >
GenerateFilterConfigOverride ( upb_StringView /*serialized_filter_config*/ ,
upb_Arena * /*arena*/ ) const override {
return grpc_core : : XdsHttpFilterImpl : : FilterConfig { name_ , grpc_core : : Json ( ) } ;
}
const grpc_channel_filter * channel_filter ( ) const override { return nullptr ; }
absl : : StatusOr < grpc_core : : XdsHttpFilterImpl : : ServiceConfigJsonEntry >
GenerateServiceConfig (
const FilterConfig & /*hcm_filter_config*/ ,
const FilterConfig * /*filter_config_override*/ ) const override {
return grpc_core : : XdsHttpFilterImpl : : ServiceConfigJsonEntry { name_ , " " } ;
}
bool IsSupportedOnClients ( ) const override { return supported_on_clients_ ; }
bool IsSupportedOnServers ( ) const override { return supported_on_servers_ ; }
bool IsTerminalFilter ( ) const override { return is_terminal_filter_ ; }
private :
const std : : string name_ ;
const bool supported_on_clients_ ;
const bool supported_on_servers_ ;
const bool is_terminal_filter_ ;
} ;
using BasicTest = XdsEnd2endTest ;
// Tests that the balancer sends the correct response to the client, and the
// client sends RPCs to the backends using the default child policy.
TEST_P ( BasicTest , Vanilla ) {
CreateAndStartBackends ( 3 ) ;
const size_t kNumRpcsPerAddress = 100 ;
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Make sure that trying to connect works without a call.
channel_ - > GetState ( true /* try_to_connect */ ) ;
// We need to wait for all backends to come online.
WaitForAllBackends ( ) ;
// Send kNumRpcsPerAddress RPCs per server.
CheckRpcSendOk ( kNumRpcsPerAddress * backends_ . size ( ) ) ;
// Each backend should have gotten 100 requests.
for ( size_t i = 0 ; i < backends_ . size ( ) ; + + i ) {
EXPECT_EQ ( kNumRpcsPerAddress ,
backends_ [ i ] - > backend_service ( ) - > request_count ( ) ) ;
}
// Check LB policy name for the channel.
EXPECT_EQ ( " xds_cluster_manager_experimental " ,
channel_ - > GetLoadBalancingPolicyName ( ) ) ;
}
TEST_P ( BasicTest , IgnoresUnhealthyEndpoints ) {
CreateAndStartBackends ( 2 ) ;
const size_t kNumRpcsPerAddress = 100 ;
auto endpoints = CreateEndpointsForBackends ( ) ;
endpoints . push_back ( MakeNonExistantEndpoint ( ) ) ;
endpoints . back ( ) . health_status = HealthStatus : : DRAINING ;
EdsResourceArgs args ( {
{ " locality0 " , std : : move ( endpoints ) , kDefaultLocalityWeight ,
kDefaultLocalityPriority } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Make sure that trying to connect works without a call.
channel_ - > GetState ( true /* try_to_connect */ ) ;
// We need to wait for all backends to come online.
WaitForAllBackends ( ) ;
// Send kNumRpcsPerAddress RPCs per server.
CheckRpcSendOk ( kNumRpcsPerAddress * backends_ . size ( ) ) ;
// Each backend should have gotten 100 requests.
for ( size_t i = 0 ; i < backends_ . size ( ) ; + + i ) {
EXPECT_EQ ( kNumRpcsPerAddress ,
backends_ [ i ] - > backend_service ( ) - > request_count ( ) ) ;
}
}
// Tests that subchannel sharing works when the same backend is listed
// multiple times.
TEST_P ( BasicTest , SameBackendListedMultipleTimes ) {
CreateAndStartBackends ( 1 ) ;
// Same backend listed twice.
auto endpoints = CreateEndpointsForBackends ( ) ;
endpoints . push_back ( endpoints . front ( ) ) ;
EdsResourceArgs args ( { { " locality0 " , endpoints } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// We need to wait for the backend to come online.
WaitForAllBackends ( ) ;
// Send kNumRpcsPerAddress RPCs per server.
const size_t kNumRpcsPerAddress = 10 ;
CheckRpcSendOk ( kNumRpcsPerAddress * endpoints . size ( ) ) ;
// Backend should have gotten 20 requests.
EXPECT_EQ ( kNumRpcsPerAddress * endpoints . size ( ) ,
backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
}
// Tests that RPCs will be blocked until a non-empty serverlist is received.
TEST_P ( BasicTest , InitiallyEmptyServerlist ) {
CreateAndStartBackends ( 1 ) ;
// First response is an empty serverlist.
EdsResourceArgs : : Locality empty_locality ( " locality0 " , { } ) ;
EdsResourceArgs args ( { std : : move ( empty_locality ) } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// RPCs should fail.
CheckRpcSendFailure ( ) ;
// Send non-empty serverlist.
args = EdsResourceArgs ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// RPCs should eventually succeed.
WaitForAllBackends ( 0 , 1 , WaitForBackendOptions ( ) . set_allow_failures ( true ) ) ;
}
// Tests that RPCs will fail with UNAVAILABLE instead of DEADLINE_EXCEEDED if
// all the servers are unreachable.
TEST_P ( BasicTest , AllServersUnreachableFailFast ) {
// Set Rpc timeout to 5 seconds to ensure there is enough time
// for communication with the xDS server to take place upon test start up.
const uint32_t kRpcTimeoutMs = 5000 ;
const size_t kNumUnreachableServers = 5 ;
std : : vector < EdsResourceArgs : : Endpoint > endpoints ;
for ( size_t i = 0 ; i < kNumUnreachableServers ; + + i ) {
endpoints . emplace_back ( MakeNonExistantEndpoint ( ) ) ;
}
EdsResourceArgs args ( { { " locality0 " , std : : move ( endpoints ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
const Status status = SendRpc ( RpcOptions ( ) . set_timeout_ms ( kRpcTimeoutMs ) ) ;
// The error shouldn't be DEADLINE_EXCEEDED because timeout is set to 5
// seconds, and we should disocver in that time that the target backend is
// down.
EXPECT_EQ ( StatusCode : : UNAVAILABLE , status . error_code ( ) ) ;
}
// Tests that RPCs fail when the backends are down, and will succeed again
// after the backends are restarted.
TEST_P ( BasicTest , BackendsRestart ) {
CreateAndStartBackends ( 3 ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForAllBackends ( ) ;
// Stop backends. RPCs should fail.
ShutdownAllBackends ( ) ;
// Sending multiple failed requests instead of just one to ensure that the
// client notices that all backends are down before we restart them. If we
// didn't do this, then a single RPC could fail here due to the race
// condition between the LB pick and the GOAWAY from the chosen backend
// being shut down, which would not actually prove that the client noticed
// that all of the backends are down. Then, when we send another request
// below (which we expect to succeed), if the callbacks happen in the wrong
// order, the same race condition could happen again due to the client not
// yet having noticed that the backends were all down.
CheckRpcSendFailure ( CheckRpcSendFailureOptions ( ) . set_times ( backends_ . size ( ) ) ) ;
// Restart all backends. RPCs should start succeeding again.
StartAllBackends ( ) ;
CheckRpcSendOk ( 1 , RpcOptions ( ) . set_timeout_ms ( 2000 ) . set_wait_for_ready ( true ) ) ;
}
TEST_P ( BasicTest , IgnoresDuplicateUpdates ) {
CreateAndStartBackends ( 1 ) ;
const size_t kNumRpcsPerAddress = 100 ;
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Wait for all backends to come online.
WaitForAllBackends ( ) ;
// Send kNumRpcsPerAddress RPCs per server, but send an EDS update in
// between. If the update is not ignored, this will cause the
// round_robin policy to see an update, which will randomly reset its
// position in the address list.
for ( size_t i = 0 ; i < kNumRpcsPerAddress ; + + i ) {
CheckRpcSendOk ( 2 ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
CheckRpcSendOk ( 2 ) ;
}
// Each backend should have gotten the right number of requests.
for ( size_t i = 1 ; i < backends_ . size ( ) ; + + i ) {
EXPECT_EQ ( kNumRpcsPerAddress ,
backends_ [ i ] - > backend_service ( ) - > request_count ( ) ) ;
}
}
using XdsResolverOnlyTest = XdsEnd2endTest ;
// Tests switching over from one cluster to another.
TEST_P ( XdsResolverOnlyTest , ChangeClusters ) {
CreateAndStartBackends ( 2 ) ;
const char * kNewClusterName = " new_cluster_name " ;
const char * kNewEdsServiceName = " new_eds_service_name " ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// We need to wait for all backends to come online.
WaitForAllBackends ( 0 , 1 ) ;
// Populate new EDS resource.
args = EdsResourceArgs ( { { " locality0 " , CreateEndpointsForBackends ( 1 , 2 ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args , kNewEdsServiceName ) ) ;
// Populate new CDS resource.
Cluster new_cluster = default_cluster_ ;
new_cluster . set_name ( kNewClusterName ) ;
new_cluster . mutable_eds_cluster_config ( ) - > set_service_name (
kNewEdsServiceName ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( new_cluster ) ;
// Change RDS resource to point to new cluster.
RouteConfiguration new_route_config = default_route_config_ ;
new_route_config . mutable_virtual_hosts ( 0 )
- > mutable_routes ( 0 )
- > mutable_route ( )
- > set_cluster ( kNewClusterName ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
new_route_config ) ;
// Wait for all new backends to be used.
WaitForAllBackends ( 1 , 2 ) ;
}
// Tests that we go into TRANSIENT_FAILURE if the Cluster disappears.
TEST_P ( XdsResolverOnlyTest , ClusterRemoved ) {
CreateAndStartBackends ( 1 ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// We need to wait for all backends to come online.
WaitForAllBackends ( ) ;
// Unset CDS resource.
balancer_ - > ads_service ( ) - > UnsetResource ( kCdsTypeUrl , kDefaultClusterName ) ;
// Wait for RPCs to start failing.
do {
} while ( SendRpc ( RpcOptions ( ) , nullptr ) . ok ( ) ) ;
// Make sure RPCs are still failing.
CheckRpcSendFailure ( CheckRpcSendFailureOptions ( ) . set_times ( 1000 ) ) ;
// Make sure we ACK'ed the update.
auto response_state = balancer_ - > ads_service ( ) - > cds_response_state ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
TEST_P ( XdsResolverOnlyTest , DefaultRouteSpecifiesSlashPrefix ) {
CreateAndStartBackends ( 1 ) ;
RouteConfiguration route_config = default_route_config_ ;
route_config . mutable_virtual_hosts ( 0 )
- > mutable_routes ( 0 )
- > mutable_match ( )
- > set_prefix ( " / " ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
route_config ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// We need to wait for all backends to come online.
WaitForAllBackends ( ) ;
}
TEST_P ( XdsResolverOnlyTest , CircuitBreaking ) {
CreateAndStartBackends ( 1 ) ;
constexpr size_t kMaxConcurrentRequests = 10 ;
// Populate new EDS resources.
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Update CDS resource to set max concurrent request.
CircuitBreakers circuit_breaks ;
Cluster cluster = default_cluster_ ;
auto * threshold = cluster . mutable_circuit_breakers ( ) - > add_thresholds ( ) ;
threshold - > set_priority ( RoutingPriority : : DEFAULT ) ;
threshold - > mutable_max_requests ( ) - > set_value ( kMaxConcurrentRequests ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
// Send exactly max_concurrent_requests long RPCs.
LongRunningRpc rpcs [ kMaxConcurrentRequests ] ;
for ( size_t i = 0 ; i < kMaxConcurrentRequests ; + + i ) {
rpcs [ i ] . StartRpc ( stub_ . get ( ) ) ;
}
// Wait for all RPCs to be in flight.
while ( backends_ [ 0 ] - > backend_service ( ) - > RpcsWaitingForClientCancel ( ) <
kMaxConcurrentRequests ) {
gpr_sleep_until ( gpr_time_add ( gpr_now ( GPR_CLOCK_REALTIME ) ,
gpr_time_from_micros ( 1 * 1000 , GPR_TIMESPAN ) ) ) ;
}
// Sending a RPC now should fail, the error message should tell us
// we hit the max concurrent requests limit and got dropped.
Status status = SendRpc ( ) ;
EXPECT_FALSE ( status . ok ( ) ) ;
EXPECT_EQ ( status . error_message ( ) , " circuit breaker drop " ) ;
// Cancel one RPC to allow another one through
rpcs [ 0 ] . CancelRpc ( ) ;
status = SendRpc ( ) ;
EXPECT_TRUE ( status . ok ( ) ) ;
for ( size_t i = 1 ; i < kMaxConcurrentRequests ; + + i ) {
rpcs [ i ] . CancelRpc ( ) ;
}
}
TEST_P ( XdsResolverOnlyTest , CircuitBreakingMultipleChannelsShareCallCounter ) {
CreateAndStartBackends ( 1 ) ;
constexpr size_t kMaxConcurrentRequests = 10 ;
// Populate new EDS resources.
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Update CDS resource to set max concurrent request.
CircuitBreakers circuit_breaks ;
Cluster cluster = default_cluster_ ;
auto * threshold = cluster . mutable_circuit_breakers ( ) - > add_thresholds ( ) ;
threshold - > set_priority ( RoutingPriority : : DEFAULT ) ;
threshold - > mutable_max_requests ( ) - > set_value ( kMaxConcurrentRequests ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
auto channel2 = CreateChannel ( ) ;
auto stub2 = grpc : : testing : : EchoTestService : : NewStub ( channel2 ) ;
// Send exactly max_concurrent_requests long RPCs, alternating between
// the two channels.
LongRunningRpc rpcs [ kMaxConcurrentRequests ] ;
for ( size_t i = 0 ; i < kMaxConcurrentRequests ; + + i ) {
rpcs [ i ] . StartRpc ( i % 2 = = 0 ? stub_ . get ( ) : stub2 . get ( ) ) ;
}
// Wait for all RPCs to be in flight.
while ( backends_ [ 0 ] - > backend_service ( ) - > RpcsWaitingForClientCancel ( ) <
kMaxConcurrentRequests ) {
gpr_sleep_until ( gpr_time_add ( gpr_now ( GPR_CLOCK_REALTIME ) ,
gpr_time_from_micros ( 1 * 1000 , GPR_TIMESPAN ) ) ) ;
}
// Sending a RPC now should fail, the error message should tell us
// we hit the max concurrent requests limit and got dropped.
Status status = SendRpc ( ) ;
EXPECT_FALSE ( status . ok ( ) ) ;
EXPECT_EQ ( status . error_message ( ) , " circuit breaker drop " ) ;
// Cancel one RPC to allow another one through
rpcs [ 0 ] . CancelRpc ( ) ;
status = SendRpc ( ) ;
EXPECT_TRUE ( status . ok ( ) ) ;
for ( size_t i = 1 ; i < kMaxConcurrentRequests ; + + i ) {
rpcs [ i ] . CancelRpc ( ) ;
}
}
TEST_P ( XdsResolverOnlyTest , ClusterChangeAfterAdsCallFails ) {
CreateAndStartBackends ( 2 ) ;
const char * kNewEdsResourceName = " new_eds_resource_name " ;
// Populate EDS resources.
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Check that the channel is working.
CheckRpcSendOk ( ) ;
// Stop and restart the balancer.
balancer_ - > Shutdown ( ) ;
balancer_ - > Start ( ) ;
// Create new EDS resource.
args = EdsResourceArgs ( { { " locality0 " , CreateEndpointsForBackends ( 1 , 2 ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args , kNewEdsResourceName ) ) ;
// Change CDS resource to point to new EDS resource.
auto cluster = default_cluster_ ;
cluster . mutable_eds_cluster_config ( ) - > set_service_name ( kNewEdsResourceName ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
// Make sure client sees the change.
// TODO(roth): This should not be allowing errors. The errors are
// being caused by a bug that triggers in the following situation:
//
// 1. xDS call fails.
// 2. When xDS call is restarted, the server sends the updated CDS
// resource that points to the new EDS resource name.
// 3. When the client receives the CDS update, it does two things:
// - Sends the update to the CDS LB policy, which creates a new
// xds_cluster_resolver policy using the new EDS service name.
// - Notices that the CDS update no longer refers to the old EDS
// service name, so removes that resource, notifying the old
// xds_cluster_resolver policy that the resource no longer exists.
//
// Need to figure out a way to fix this bug, and then change this to
// not allow failures.
WaitForBackend ( 1 , WaitForBackendOptions ( ) . set_allow_failures ( true ) ) ;
}
// Tests that if the balancer is down, the RPCs will still be sent to the
// backends according to the last balancer response, until a new balancer is
// reachable.
TEST_P ( XdsResolverOnlyTest , KeepUsingLastDataIfBalancerGoesDown ) {
CreateAndStartBackends ( 2 ) ;
// Set up EDS resource pointing to backend 0.
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Start the client and make sure it sees the backend.
WaitForBackend ( 0 ) ;
// Stop the balancer, and verify that RPCs continue to flow to backend 0.
balancer_ - > Shutdown ( ) ;
auto deadline = grpc_timeout_seconds_to_deadline ( 5 ) ;
do {
CheckRpcSendOk ( ) ;
} while ( gpr_time_cmp ( gpr_now ( GPR_CLOCK_MONOTONIC ) , deadline ) < 0 ) ;
// Check the EDS resource to point to backend 1 and bring the balancer
// back up.
args = EdsResourceArgs ( { { " locality0 " , CreateEndpointsForBackends ( 1 , 2 ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
balancer_ - > Start ( ) ;
// Wait for client to see backend 1.
WaitForBackend ( 1 ) ;
}
using LdsTest = XdsEnd2endTest ;
// Tests that LDS client should send a NACK if there is no API listener in the
// Listener in the LDS response.
TEST_P ( LdsTest , NoApiListener ) {
auto listener = default_listener_ ;
listener . clear_api_listener ( ) ;
balancer_ - > ads_service ( ) - > SetLdsResource ( listener ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " Listener has neither address nor ApiListener " ) ) ;
}
// Tests that LDS client should send a NACK if the route_specifier in the
// http_connection_manager is neither inlined route_config nor RDS.
TEST_P ( LdsTest , WrongRouteSpecifier ) {
auto listener = default_listener_ ;
HttpConnectionManager http_connection_manager ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > UnpackTo (
& http_connection_manager ) ;
http_connection_manager . mutable_scoped_routes ( ) ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > PackFrom (
http_connection_manager ) ;
balancer_ - > ads_service ( ) - > SetLdsResource ( listener ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr (
" HttpConnectionManager neither has inlined route_config nor RDS. " ) ) ;
}
// Tests that LDS client should send a NACK if the rds message in the
// http_connection_manager is missing the config_source field.
TEST_P ( LdsTest , RdsMissingConfigSource ) {
auto listener = default_listener_ ;
HttpConnectionManager http_connection_manager ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > UnpackTo (
& http_connection_manager ) ;
http_connection_manager . mutable_rds ( ) - > set_route_config_name (
kDefaultRouteConfigurationName ) ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > PackFrom (
http_connection_manager ) ;
balancer_ - > ads_service ( ) - > SetLdsResource ( listener ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" HttpConnectionManager missing config_source for RDS. " ) ) ;
}
// Tests that LDS client should send a NACK if the rds message in the
// http_connection_manager has a config_source field that does not specify
// ADS or SELF.
TEST_P ( LdsTest , RdsConfigSourceDoesNotSpecifyAdsOrSelf ) {
auto listener = default_listener_ ;
HttpConnectionManager http_connection_manager ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > UnpackTo (
& http_connection_manager ) ;
auto * rds = http_connection_manager . mutable_rds ( ) ;
rds - > set_route_config_name ( kDefaultRouteConfigurationName ) ;
rds - > mutable_config_source ( ) - > set_path ( " /foo/bar " ) ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > PackFrom (
http_connection_manager ) ;
balancer_ - > ads_service ( ) - > SetLdsResource ( listener ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " HttpConnectionManager ConfigSource for "
" RDS does not specify ADS or SELF. " ) ) ;
}
// Tests that LDS client accepts the rds message in the
// http_connection_manager with a config_source field that specifies ADS.
TEST_P ( LdsTest , AcceptsRdsConfigSourceOfTypeAds ) {
CreateAndStartBackends ( 1 ) ;
auto listener = default_listener_ ;
HttpConnectionManager http_connection_manager ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > UnpackTo (
& http_connection_manager ) ;
auto * rds = http_connection_manager . mutable_rds ( ) ;
rds - > set_route_config_name ( kDefaultRouteConfigurationName ) ;
rds - > mutable_config_source ( ) - > mutable_ads ( ) ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > PackFrom (
http_connection_manager ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , listener ,
default_route_config_ ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForAllBackends ( ) ;
auto response_state = balancer_ - > ads_service ( ) - > lds_response_state ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
// Tests that we NACK non-terminal filters at the end of the list.
TEST_P ( LdsTest , NacksNonTerminalHttpFilterAtEndOfList ) {
auto listener = default_listener_ ;
HttpConnectionManager http_connection_manager ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > UnpackTo (
& http_connection_manager ) ;
auto * filter = http_connection_manager . mutable_http_filters ( 0 ) ;
filter - > set_name ( " unknown " ) ;
filter - > mutable_typed_config ( ) - > set_type_url (
" grpc.testing.client_only_http_filter " ) ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > PackFrom (
http_connection_manager ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , listener ,
default_route_config_ ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" non-terminal filter for config type grpc.testing "
" .client_only_http_filter is the last filter in the chain " ) ) ;
}
// Test that we NACK terminal filters that are not at the end of the list.
TEST_P ( LdsTest , NacksTerminalFilterBeforeEndOfList ) {
auto listener = default_listener_ ;
HttpConnectionManager http_connection_manager ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > UnpackTo (
& http_connection_manager ) ;
// The default_listener_ has a terminal router filter by default. Add an
// additional filter.
auto * filter = http_connection_manager . add_http_filters ( ) ;
filter - > set_name ( " grpc.testing.terminal_http_filter " ) ;
filter - > mutable_typed_config ( ) - > set_type_url (
" grpc.testing.terminal_http_filter " ) ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > PackFrom (
http_connection_manager ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , listener ,
default_route_config_ ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr (
" terminal filter for config type envoy.extensions.filters.http "
" .router.v3.Router must be the last filter in the chain " ) ) ;
}
// Test that we NACK empty filter names.
TEST_P ( LdsTest , RejectsEmptyHttpFilterName ) {
auto listener = default_listener_ ;
HttpConnectionManager http_connection_manager ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > UnpackTo (
& http_connection_manager ) ;
* http_connection_manager . add_http_filters ( ) =
http_connection_manager . http_filters ( 0 ) ;
auto * filter = http_connection_manager . mutable_http_filters ( 0 ) ;
filter - > Clear ( ) ;
filter - > mutable_typed_config ( ) - > PackFrom ( Listener ( ) ) ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > PackFrom (
http_connection_manager ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , listener ,
default_route_config_ ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " empty filter name at index 0 " ) ) ;
}
// Test that we NACK duplicate HTTP filter names.
TEST_P ( LdsTest , RejectsDuplicateHttpFilterName ) {
auto listener = default_listener_ ;
HttpConnectionManager http_connection_manager ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > UnpackTo (
& http_connection_manager ) ;
* http_connection_manager . add_http_filters ( ) =
http_connection_manager . http_filters ( 0 ) ;
http_connection_manager . mutable_http_filters ( 0 )
- > mutable_typed_config ( )
- > PackFrom ( HTTPFault ( ) ) ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > PackFrom (
http_connection_manager ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , listener ,
default_route_config_ ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " duplicate HTTP filter name: router " ) ) ;
}
// Test that we NACK unknown filter types.
TEST_P ( LdsTest , RejectsUnknownHttpFilterType ) {
auto listener = default_listener_ ;
HttpConnectionManager http_connection_manager ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > UnpackTo (
& http_connection_manager ) ;
* http_connection_manager . add_http_filters ( ) =
http_connection_manager . http_filters ( 0 ) ;
auto * filter = http_connection_manager . mutable_http_filters ( 0 ) ;
filter - > set_name ( " unknown " ) ;
filter - > mutable_typed_config ( ) - > PackFrom ( Listener ( ) ) ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > PackFrom (
http_connection_manager ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , listener ,
default_route_config_ ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " no filter registered for config type "
" envoy.config.listener.v3.Listener " ) ) ;
}
// Test that we ignore optional unknown filter types.
TEST_P ( LdsTest , IgnoresOptionalUnknownHttpFilterType ) {
CreateAndStartBackends ( 1 ) ;
auto listener = default_listener_ ;
HttpConnectionManager http_connection_manager ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > UnpackTo (
& http_connection_manager ) ;
* http_connection_manager . add_http_filters ( ) =
http_connection_manager . http_filters ( 0 ) ;
auto * filter = http_connection_manager . mutable_http_filters ( 0 ) ;
filter - > set_name ( " unknown " ) ;
filter - > mutable_typed_config ( ) - > PackFrom ( Listener ( ) ) ;
filter - > set_is_optional ( true ) ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > PackFrom (
http_connection_manager ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , listener ,
default_route_config_ ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForAllBackends ( ) ;
auto response_state = balancer_ - > ads_service ( ) - > lds_response_state ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
// Test that we NACK filters without configs.
TEST_P ( LdsTest , RejectsHttpFilterWithoutConfig ) {
auto listener = default_listener_ ;
HttpConnectionManager http_connection_manager ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > UnpackTo (
& http_connection_manager ) ;
* http_connection_manager . add_http_filters ( ) =
http_connection_manager . http_filters ( 0 ) ;
auto * filter = http_connection_manager . mutable_http_filters ( 0 ) ;
filter - > Clear ( ) ;
filter - > set_name ( " unknown " ) ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > PackFrom (
http_connection_manager ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , listener ,
default_route_config_ ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" no filter config specified for filter name unknown " ) ) ;
}
// Test that we ignore optional filters without configs.
TEST_P ( LdsTest , IgnoresOptionalHttpFilterWithoutConfig ) {
CreateAndStartBackends ( 1 ) ;
auto listener = default_listener_ ;
HttpConnectionManager http_connection_manager ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > UnpackTo (
& http_connection_manager ) ;
* http_connection_manager . add_http_filters ( ) =
http_connection_manager . http_filters ( 0 ) ;
auto * filter = http_connection_manager . mutable_http_filters ( 0 ) ;
filter - > Clear ( ) ;
filter - > set_name ( " unknown " ) ;
filter - > set_is_optional ( true ) ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > PackFrom (
http_connection_manager ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , listener ,
default_route_config_ ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForAllBackends ( ) ;
auto response_state = balancer_ - > ads_service ( ) - > lds_response_state ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
// Test that we NACK unparseable filter configs.
TEST_P ( LdsTest , RejectsUnparseableHttpFilterType ) {
auto listener = default_listener_ ;
HttpConnectionManager http_connection_manager ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > UnpackTo (
& http_connection_manager ) ;
* http_connection_manager . add_http_filters ( ) =
http_connection_manager . http_filters ( 0 ) ;
auto * filter = http_connection_manager . mutable_http_filters ( 0 ) ;
filter - > set_name ( " unknown " ) ;
filter - > mutable_typed_config ( ) - > PackFrom ( listener ) ;
filter - > mutable_typed_config ( ) - > set_type_url (
" type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault " ) ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > PackFrom (
http_connection_manager ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , listener ,
default_route_config_ ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr (
" filter config for type "
" envoy.extensions.filters.http.fault.v3.HTTPFault failed to parse " ) ) ;
}
// Test that we NACK HTTP filters unsupported on client-side.
TEST_P ( LdsTest , RejectsHttpFiltersNotSupportedOnClients ) {
auto listener = default_listener_ ;
HttpConnectionManager http_connection_manager ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > UnpackTo (
& http_connection_manager ) ;
* http_connection_manager . add_http_filters ( ) =
http_connection_manager . http_filters ( 0 ) ;
auto * filter = http_connection_manager . mutable_http_filters ( 0 ) ;
filter - > set_name ( " grpc.testing.server_only_http_filter " ) ;
filter - > mutable_typed_config ( ) - > set_type_url (
" grpc.testing.server_only_http_filter " ) ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > PackFrom (
http_connection_manager ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , listener ,
default_route_config_ ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " Filter grpc.testing.server_only_http_filter is not "
" supported on clients " ) ) ;
}
// Test that we ignore optional HTTP filters unsupported on client-side.
TEST_P ( LdsTest , IgnoresOptionalHttpFiltersNotSupportedOnClients ) {
CreateAndStartBackends ( 1 ) ;
auto listener = default_listener_ ;
HttpConnectionManager http_connection_manager ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > UnpackTo (
& http_connection_manager ) ;
* http_connection_manager . add_http_filters ( ) =
http_connection_manager . http_filters ( 0 ) ;
auto * filter = http_connection_manager . mutable_http_filters ( 0 ) ;
filter - > set_name ( " grpc.testing.server_only_http_filter " ) ;
filter - > mutable_typed_config ( ) - > set_type_url (
" grpc.testing.server_only_http_filter " ) ;
filter - > set_is_optional ( true ) ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > PackFrom (
http_connection_manager ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , listener ,
default_route_config_ ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForAllBackends ( ) ;
auto response_state = balancer_ - > ads_service ( ) - > lds_response_state ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
// Test that we NACK non-zero xff_num_trusted_hops
TEST_P ( LdsTest , RejectsNonZeroXffNumTrusterHops ) {
auto listener = default_listener_ ;
HttpConnectionManager http_connection_manager ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > UnpackTo (
& http_connection_manager ) ;
http_connection_manager . set_xff_num_trusted_hops ( 1 ) ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > PackFrom (
http_connection_manager ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , listener ,
default_route_config_ ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " 'xff_num_trusted_hops' must be zero " ) ) ;
}
// Test that we NACK non-empty original_ip_detection_extensions
TEST_P ( LdsTest , RejectsNonEmptyOriginalIpDetectionExtensions ) {
auto listener = default_listener_ ;
HttpConnectionManager http_connection_manager ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > UnpackTo (
& http_connection_manager ) ;
http_connection_manager . add_original_ip_detection_extensions ( ) ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > PackFrom (
http_connection_manager ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , listener ,
default_route_config_ ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " 'original_ip_detection_extensions' must be empty " ) ) ;
}
using LdsV2Test = XdsEnd2endTest ;
// Tests that we ignore the HTTP filter list in v2.
// TODO(roth): The test framework is not set up to allow us to test
// the server sending v2 resources when the client requests v3, so this
// just tests a pure v2 setup. When we have time, fix this.
TEST_P ( LdsV2Test , IgnoresHttpFilters ) {
CreateAndStartBackends ( 1 ) ;
auto listener = default_listener_ ;
HttpConnectionManager http_connection_manager ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > UnpackTo (
& http_connection_manager ) ;
auto * filter = http_connection_manager . add_http_filters ( ) ;
filter - > set_name ( " unknown " ) ;
filter - > mutable_typed_config ( ) - > PackFrom ( Listener ( ) ) ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > PackFrom (
http_connection_manager ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , listener ,
default_route_config_ ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
CheckRpcSendOk ( ) ;
}
using LdsRdsTest = XdsEnd2endTest ;
MATCHER_P2 ( AdjustedClockInRange , t1 , t2 , " equals time " ) {
gpr_cycle_counter cycle_now = gpr_get_cycle_counter ( ) ;
grpc_core : : Timestamp cycle_time =
grpc_core : : Timestamp : : FromCycleCounterRoundDown ( cycle_now ) ;
grpc_core : : Timestamp time_spec =
grpc_core : : Timestamp : : FromTimespecRoundDown ( gpr_now ( GPR_CLOCK_MONOTONIC ) ) ;
grpc_core : : Timestamp now = arg + ( time_spec - cycle_time ) ;
bool ok = true ;
ok & = : : testing : : ExplainMatchResult ( : : testing : : Ge ( t1 ) , now , result_listener ) ;
ok & = : : testing : : ExplainMatchResult ( : : testing : : Lt ( t2 ) , now , result_listener ) ;
return ok ;
}
// Tests that LDS client should send an ACK upon correct LDS response (with
// inlined RDS result).
TEST_P ( LdsRdsTest , Vanilla ) {
( void ) SendRpc ( ) ;
auto response_state = RouteConfigurationResponseState ( balancer_ . get ( ) ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
// Make sure we actually used the RPC service for the right version of xDS.
EXPECT_EQ ( balancer_ - > ads_service ( ) - > seen_v2_client ( ) , GetParam ( ) . use_v2 ( ) ) ;
EXPECT_NE ( balancer_ - > ads_service ( ) - > seen_v3_client ( ) , GetParam ( ) . use_v2 ( ) ) ;
}
// Tests that we go into TRANSIENT_FAILURE if the Listener is removed.
TEST_P ( LdsRdsTest , ListenerRemoved ) {
CreateAndStartBackends ( 1 ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// We need to wait for all backends to come online.
WaitForAllBackends ( ) ;
// Unset LDS resource.
balancer_ - > ads_service ( ) - > UnsetResource ( kLdsTypeUrl , kServerName ) ;
// Wait for RPCs to start failing.
do {
} while ( SendRpc ( RpcOptions ( ) , nullptr ) . ok ( ) ) ;
// Make sure RPCs are still failing.
CheckRpcSendFailure ( CheckRpcSendFailureOptions ( ) . set_times ( 1000 ) ) ;
// Make sure we ACK'ed the update.
auto response_state = balancer_ - > ads_service ( ) - > lds_response_state ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
// Tests that LDS client ACKs but fails if matching domain can't be found in
// the LDS response.
TEST_P ( LdsRdsTest , NoMatchedDomain ) {
RouteConfiguration route_config = default_route_config_ ;
route_config . mutable_virtual_hosts ( 0 ) - > clear_domains ( ) ;
route_config . mutable_virtual_hosts ( 0 ) - > add_domains ( " unmatched_domain " ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
CheckRpcSendFailure ( ) ;
// Do a bit of polling, to allow the ACK to get to the ADS server.
channel_ - > WaitForConnected ( grpc_timeout_milliseconds_to_deadline ( 100 ) ) ;
auto response_state = RouteConfigurationResponseState ( balancer_ . get ( ) ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
// Tests that LDS client should choose the virtual host with matching domain
// if multiple virtual hosts exist in the LDS response.
TEST_P ( LdsRdsTest , ChooseMatchedDomain ) {
RouteConfiguration route_config = default_route_config_ ;
* ( route_config . add_virtual_hosts ( ) ) = route_config . virtual_hosts ( 0 ) ;
route_config . mutable_virtual_hosts ( 0 ) - > clear_domains ( ) ;
route_config . mutable_virtual_hosts ( 0 ) - > add_domains ( " unmatched_domain " ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
( void ) SendRpc ( ) ;
auto response_state = RouteConfigurationResponseState ( balancer_ . get ( ) ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
// Tests that LDS client should choose the last route in the virtual host if
// multiple routes exist in the LDS response.
TEST_P ( LdsRdsTest , ChooseLastRoute ) {
RouteConfiguration route_config = default_route_config_ ;
* ( route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ) =
route_config . virtual_hosts ( 0 ) . routes ( 0 ) ;
route_config . mutable_virtual_hosts ( 0 )
- > mutable_routes ( 0 )
- > mutable_route ( )
- > mutable_cluster_header ( ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
( void ) SendRpc ( ) ;
auto response_state = RouteConfigurationResponseState ( balancer_ . get ( ) ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
// Tests that LDS client should ignore route which has query_parameters.
TEST_P ( LdsRdsTest , RouteMatchHasQueryParameters ) {
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_prefix ( " /grpc.testing.EchoTest1Service/ " ) ;
route1 - > mutable_match ( ) - > add_query_parameters ( ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " No valid routes specified. " ) ) ;
}
// Tests that LDS client should send a ACK if route match has a prefix
// that is either empty or a single slash
TEST_P ( LdsRdsTest , RouteMatchHasValidPrefixEmptyOrSingleSlash ) {
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_prefix ( " " ) ;
auto * default_route = route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
default_route - > mutable_match ( ) - > set_prefix ( " / " ) ;
default_route - > mutable_route ( ) - > set_cluster ( kDefaultClusterName ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
( void ) SendRpc ( ) ;
const auto response_state = RouteConfigurationResponseState ( balancer_ . get ( ) ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
// Tests that LDS client should ignore route which has a path
// prefix string does not start with "/".
TEST_P ( LdsRdsTest , RouteMatchHasInvalidPrefixNoLeadingSlash ) {
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_prefix ( " grpc.testing.EchoTest1Service/ " ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " No valid routes specified. " ) ) ;
}
// Tests that LDS client should ignore route which has a prefix
// string with more than 2 slashes.
TEST_P ( LdsRdsTest , RouteMatchHasInvalidPrefixExtraContent ) {
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_prefix ( " /grpc.testing.EchoTest1Service/Echo1/ " ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " No valid routes specified. " ) ) ;
}
// Tests that LDS client should ignore route which has a prefix
// string "//".
TEST_P ( LdsRdsTest , RouteMatchHasInvalidPrefixDoubleSlash ) {
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_prefix ( " // " ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " No valid routes specified. " ) ) ;
}
// Tests that LDS client should ignore route which has path
// but it's empty.
TEST_P ( LdsRdsTest , RouteMatchHasInvalidPathEmptyPath ) {
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_path ( " " ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " No valid routes specified. " ) ) ;
}
// Tests that LDS client should ignore route which has path
// string does not start with "/".
TEST_P ( LdsRdsTest , RouteMatchHasInvalidPathNoLeadingSlash ) {
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_path ( " grpc.testing.EchoTest1Service/Echo1 " ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " No valid routes specified. " ) ) ;
}
// Tests that LDS client should ignore route which has path
// string that has too many slashes; for example, ends with "/".
TEST_P ( LdsRdsTest , RouteMatchHasInvalidPathTooManySlashes ) {
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_path ( " /grpc.testing.EchoTest1Service/Echo1/ " ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " No valid routes specified. " ) ) ;
}
// Tests that LDS client should ignore route which has path
// string that has only 1 slash: missing "/" between service and method.
TEST_P ( LdsRdsTest , RouteMatchHasInvalidPathOnlyOneSlash ) {
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_path ( " /grpc.testing.EchoTest1Service.Echo1 " ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " No valid routes specified. " ) ) ;
}
// Tests that LDS client should ignore route which has path
// string that is missing service.
TEST_P ( LdsRdsTest , RouteMatchHasInvalidPathMissingService ) {
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_path ( " //Echo1 " ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " No valid routes specified. " ) ) ;
}
// Tests that LDS client should ignore route which has path
// string that is missing method.
TEST_P ( LdsRdsTest , RouteMatchHasInvalidPathMissingMethod ) {
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_path ( " /grpc.testing.EchoTest1Service/ " ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " No valid routes specified. " ) ) ;
}
// Test that LDS client should reject route which has invalid path regex.
TEST_P ( LdsRdsTest , RouteMatchHasInvalidPathRegex ) {
const char * kNewCluster1Name = " new_cluster_1 " ;
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > mutable_safe_regex ( ) - > set_regex ( " a[z-a] " ) ;
route1 - > mutable_route ( ) - > set_cluster ( kNewCluster1Name ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" path matcher: Invalid regex string specified in matcher. " ) ) ;
}
// Tests that LDS client should fail RPCs with UNAVAILABLE status code if the
// matching route has an action other than RouteAction.
TEST_P ( LdsRdsTest , MatchingRouteHasNoRouteAction ) {
RouteConfiguration route_config = default_route_config_ ;
// Set a route with an inappropriate route action
auto * vhost = route_config . mutable_virtual_hosts ( 0 ) ;
vhost - > mutable_routes ( 0 ) - > mutable_redirect ( ) ;
// Add another route to make sure that the resolver code actually tries to
// match to a route instead of using a shorthand logic to error out.
auto * route = vhost - > add_routes ( ) ;
route - > mutable_match ( ) - > set_prefix ( " " ) ;
route - > mutable_route ( ) - > set_cluster ( kDefaultClusterName ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
CheckRpcSendFailure ( CheckRpcSendFailureOptions ( ) . set_expected_error_code (
StatusCode : : UNAVAILABLE ) ) ;
}
TEST_P ( LdsRdsTest , RouteActionClusterHasEmptyClusterName ) {
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_prefix ( " /grpc.testing.EchoTest1Service/ " ) ;
route1 - > mutable_route ( ) - > set_cluster ( " " ) ;
auto * default_route = route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
default_route - > mutable_match ( ) - > set_prefix ( " " ) ;
default_route - > mutable_route ( ) - > set_cluster ( kDefaultClusterName ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " RouteAction cluster contains empty cluster name. " ) ) ;
}
TEST_P ( LdsRdsTest , RouteActionWeightedTargetHasIncorrectTotalWeightSet ) {
const size_t kWeight75 = 75 ;
const char * kNewCluster1Name = " new_cluster_1 " ;
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = 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 ( kWeight75 ) ;
route1 - > mutable_route ( )
- > mutable_weighted_clusters ( )
- > mutable_total_weight ( )
- > set_value ( kWeight75 + 1 ) ;
auto * default_route = route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
default_route - > mutable_match ( ) - > set_prefix ( " " ) ;
default_route - > mutable_route ( ) - > set_cluster ( kDefaultClusterName ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" RouteAction weighted_cluster has incorrect total weight " ) ) ;
}
TEST_P ( LdsRdsTest , RouteActionWeightedClusterHasZeroTotalWeight ) {
const char * kNewCluster1Name = " new_cluster_1 " ;
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = 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 ( 0 ) ;
route1 - > mutable_route ( )
- > mutable_weighted_clusters ( )
- > mutable_total_weight ( )
- > set_value ( 0 ) ;
auto * default_route = route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
default_route - > mutable_match ( ) - > set_prefix ( " " ) ;
default_route - > mutable_route ( ) - > set_cluster ( kDefaultClusterName ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr (
" RouteAction weighted_cluster has no valid clusters specified. " ) ) ;
}
TEST_P ( LdsRdsTest , RouteActionWeightedTargetClusterHasEmptyClusterName ) {
const size_t kWeight75 = 75 ;
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = 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 ( " " ) ;
weighted_cluster1 - > mutable_weight ( ) - > set_value ( kWeight75 ) ;
route1 - > mutable_route ( )
- > mutable_weighted_clusters ( )
- > mutable_total_weight ( )
- > set_value ( kWeight75 ) ;
auto * default_route = route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
default_route - > mutable_match ( ) - > set_prefix ( " " ) ;
default_route - > mutable_route ( ) - > set_cluster ( kDefaultClusterName ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " RouteAction weighted_cluster cluster "
" contains empty cluster name. " ) ) ;
}
TEST_P ( LdsRdsTest , RouteActionWeightedTargetClusterHasNoWeight ) {
const size_t kWeight75 = 75 ;
const char * kNewCluster1Name = " new_cluster_1 " ;
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = 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 ) ;
route1 - > mutable_route ( )
- > mutable_weighted_clusters ( )
- > mutable_total_weight ( )
- > set_value ( kWeight75 ) ;
auto * default_route = route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
default_route - > mutable_match ( ) - > set_prefix ( " " ) ;
default_route - > mutable_route ( ) - > set_cluster ( kDefaultClusterName ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" RouteAction weighted_cluster cluster missing weight " ) ) ;
}
TEST_P ( LdsRdsTest , RouteHeaderMatchInvalidRegex ) {
const char * kNewCluster1Name = " new_cluster_1 " ;
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_prefix ( " /grpc.testing.EchoTest1Service/ " ) ;
auto * header_matcher1 = route1 - > mutable_match ( ) - > add_headers ( ) ;
header_matcher1 - > set_name ( " header1 " ) ;
header_matcher1 - > mutable_safe_regex_match ( ) - > set_regex ( " a[z-a] " ) ;
route1 - > mutable_route ( ) - > set_cluster ( kNewCluster1Name ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr (
" header matcher: Invalid regex string specified in matcher. " ) ) ;
}
TEST_P ( LdsRdsTest , RouteHeaderMatchInvalidRange ) {
const char * kNewCluster1Name = " new_cluster_1 " ;
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_prefix ( " /grpc.testing.EchoTest1Service/ " ) ;
auto * header_matcher1 = route1 - > mutable_match ( ) - > add_headers ( ) ;
header_matcher1 - > set_name ( " header1 " ) ;
header_matcher1 - > mutable_range_match ( ) - > set_start ( 1001 ) ;
header_matcher1 - > mutable_range_match ( ) - > set_end ( 1000 ) ;
route1 - > mutable_route ( ) - > set_cluster ( kNewCluster1Name ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr (
" header matcher: Invalid range specifier specified: end cannot be "
" smaller than start. " ) ) ;
}
// Tests that LDS client should choose the default route (with no matching
// specified) after unable to find a match with previous routes.
TEST_P ( LdsRdsTest , XdsRoutingPathMatching ) {
CreateAndStartBackends ( 4 ) ;
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 kNumEcho1Rpcs = 10 ;
const size_t kNumEcho2Rpcs = 20 ;
const size_t kNumEchoRpcs = 30 ;
// Populate new EDS resources.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 2 ) } ,
} ) ;
EdsResourceArgs args1 ( {
{ " locality0 " , CreateEndpointsForBackends ( 2 , 3 ) } ,
} ) ;
EdsResourceArgs args2 ( {
{ " locality0 " , CreateEndpointsForBackends ( 3 , 4 ) } ,
} ) ;
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_path ( " /grpc.testing.EchoTest1Service/Echo1 " ) ;
route1 - > mutable_route ( ) - > set_cluster ( kNewCluster1Name ) ;
auto * route2 = new_route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
route2 - > mutable_match ( ) - > set_path ( " /grpc.testing.EchoTest2Service/Echo2 " ) ;
route2 - > mutable_route ( ) - > set_cluster ( kNewCluster2Name ) ;
auto * route3 = new_route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
route3 - > mutable_match ( ) - > set_path ( " /grpc.testing.EchoTest3Service/Echo3 " ) ;
route3 - > mutable_route ( ) - > set_cluster ( kDefaultClusterName ) ;
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 ( 0 , 2 ) ;
CheckRpcSendOk ( kNumEchoRpcs , RpcOptions ( ) . set_wait_for_ready ( true ) ) ;
CheckRpcSendOk ( kNumEcho1Rpcs , RpcOptions ( )
. set_rpc_service ( SERVICE_ECHO1 )
. set_rpc_method ( METHOD_ECHO1 )
. set_wait_for_ready ( true ) ) ;
CheckRpcSendOk ( kNumEcho2Rpcs , RpcOptions ( )
. set_rpc_service ( SERVICE_ECHO2 )
. set_rpc_method ( METHOD_ECHO2 )
. set_wait_for_ready ( true ) ) ;
// Make sure RPCs all go to the correct backend.
for ( size_t i = 0 ; i < 2 ; + + i ) {
EXPECT_EQ ( kNumEchoRpcs / 2 ,
backends_ [ i ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ i ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ i ] - > backend_service2 ( ) - > request_count ( ) ) ;
}
EXPECT_EQ ( 0 , backends_ [ 2 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( kNumEcho1Rpcs , backends_ [ 2 ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 2 ] - > backend_service2 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 3 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 3 ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( kNumEcho2Rpcs , backends_ [ 3 ] - > backend_service2 ( ) - > request_count ( ) ) ;
}
TEST_P ( LdsRdsTest , XdsRoutingPathMatchingCaseInsensitive ) {
CreateAndStartBackends ( 4 ) ;
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 kNumEcho1Rpcs = 10 ;
const size_t kNumEchoRpcs = 30 ;
// 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_ ;
// First route will not match, since it's case-sensitive.
// Second route will match with same path.
auto * route1 = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_path ( " /GrPc.TeStInG.EcHoTeSt1SErViCe/EcHo1 " ) ;
route1 - > mutable_route ( ) - > set_cluster ( kNewCluster1Name ) ;
auto * route2 = new_route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
route2 - > mutable_match ( ) - > set_path ( " /GrPc.TeStInG.EcHoTeSt1SErViCe/EcHo1 " ) ;
route2 - > mutable_match ( ) - > mutable_case_sensitive ( ) - > set_value ( false ) ;
route2 - > mutable_route ( ) - > set_cluster ( kNewCluster2Name ) ;
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 ) ;
CheckRpcSendOk ( kNumEchoRpcs , RpcOptions ( ) . set_wait_for_ready ( true ) ) ;
CheckRpcSendOk ( kNumEcho1Rpcs , RpcOptions ( )
. set_rpc_service ( SERVICE_ECHO1 )
. set_rpc_method ( METHOD_ECHO1 )
. set_wait_for_ready ( true ) ) ;
// 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 ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 1 ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 2 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( kNumEcho1Rpcs , backends_ [ 2 ] - > backend_service1 ( ) - > request_count ( ) ) ;
}
TEST_P ( LdsRdsTest , XdsRoutingPrefixMatching ) {
CreateAndStartBackends ( 4 ) ;
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 kNumEcho1Rpcs = 10 ;
const size_t kNumEcho2Rpcs = 20 ;
const size_t kNumEchoRpcs = 30 ;
// Populate new EDS resources.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 2 ) } ,
} ) ;
EdsResourceArgs args1 ( {
{ " locality0 " , CreateEndpointsForBackends ( 2 , 3 ) } ,
} ) ;
EdsResourceArgs args2 ( {
{ " locality0 " , CreateEndpointsForBackends ( 3 , 4 ) } ,
} ) ;
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/ " ) ;
route1 - > mutable_route ( ) - > set_cluster ( kNewCluster1Name ) ;
auto * route2 = new_route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
route2 - > mutable_match ( ) - > set_prefix ( " /grpc.testing.EchoTest2Service/ " ) ;
route2 - > mutable_route ( ) - > set_cluster ( kNewCluster2Name ) ;
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 ( 0 , 2 ) ;
CheckRpcSendOk ( kNumEchoRpcs , RpcOptions ( ) . set_wait_for_ready ( true ) ) ;
CheckRpcSendOk (
kNumEcho1Rpcs ,
RpcOptions ( ) . set_rpc_service ( SERVICE_ECHO1 ) . set_wait_for_ready ( true ) ) ;
CheckRpcSendOk (
kNumEcho2Rpcs ,
RpcOptions ( ) . set_rpc_service ( SERVICE_ECHO2 ) . set_wait_for_ready ( true ) ) ;
// Make sure RPCs all go to the correct backend.
for ( size_t i = 0 ; i < 2 ; + + i ) {
EXPECT_EQ ( kNumEchoRpcs / 2 ,
backends_ [ i ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ i ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ i ] - > backend_service2 ( ) - > request_count ( ) ) ;
}
EXPECT_EQ ( 0 , backends_ [ 2 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( kNumEcho1Rpcs , backends_ [ 2 ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 2 ] - > backend_service2 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 3 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 3 ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( kNumEcho2Rpcs , backends_ [ 3 ] - > backend_service2 ( ) - > request_count ( ) ) ;
}
TEST_P ( LdsRdsTest , XdsRoutingPrefixMatchingCaseInsensitive ) {
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 kNumEcho1Rpcs = 10 ;
const size_t kNumEchoRpcs = 30 ;
// 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_ ;
// First route will not match, since it's case-sensitive.
// Second route will match with same path.
auto * route1 = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_prefix ( " /GrPc.TeStInG.EcHoTeSt1SErViCe " ) ;
route1 - > mutable_route ( ) - > set_cluster ( kNewCluster1Name ) ;
auto * route2 = new_route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
route2 - > mutable_match ( ) - > set_prefix ( " /GrPc.TeStInG.EcHoTeSt1SErViCe " ) ;
route2 - > mutable_match ( ) - > mutable_case_sensitive ( ) - > set_value ( false ) ;
route2 - > mutable_route ( ) - > set_cluster ( kNewCluster2Name ) ;
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 ) ;
CheckRpcSendOk ( kNumEchoRpcs , RpcOptions ( ) . set_wait_for_ready ( true ) ) ;
CheckRpcSendOk ( kNumEcho1Rpcs , RpcOptions ( )
. set_rpc_service ( SERVICE_ECHO1 )
. set_rpc_method ( METHOD_ECHO1 )
. set_wait_for_ready ( true ) ) ;
// 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 ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 1 ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 2 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( kNumEcho1Rpcs , backends_ [ 2 ] - > backend_service1 ( ) - > request_count ( ) ) ;
}
TEST_P ( LdsRdsTest , XdsRoutingPathRegexMatching ) {
CreateAndStartBackends ( 4 ) ;
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 kNumEcho1Rpcs = 10 ;
const size_t kNumEcho2Rpcs = 20 ;
const size_t kNumEchoRpcs = 30 ;
// Populate new EDS resources.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 2 ) } ,
} ) ;
EdsResourceArgs args1 ( {
{ " locality0 " , CreateEndpointsForBackends ( 2 , 3 ) } ,
} ) ;
EdsResourceArgs args2 ( {
{ " locality0 " , CreateEndpointsForBackends ( 3 , 4 ) } ,
} ) ;
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 ) ;
// Will match "/grpc.testing.EchoTest1Service/"
route1 - > mutable_match ( ) - > mutable_safe_regex ( ) - > set_regex ( " .*1.* " ) ;
route1 - > mutable_route ( ) - > set_cluster ( kNewCluster1Name ) ;
auto * route2 = new_route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
// Will match "/grpc.testing.EchoTest2Service/"
route2 - > mutable_match ( ) - > mutable_safe_regex ( ) - > set_regex ( " .*2.* " ) ;
route2 - > mutable_route ( ) - > set_cluster ( kNewCluster2Name ) ;
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 ( 0 , 2 ) ;
CheckRpcSendOk ( kNumEchoRpcs , RpcOptions ( ) . set_wait_for_ready ( true ) ) ;
CheckRpcSendOk (
kNumEcho1Rpcs ,
RpcOptions ( ) . set_rpc_service ( SERVICE_ECHO1 ) . set_wait_for_ready ( true ) ) ;
CheckRpcSendOk (
kNumEcho2Rpcs ,
RpcOptions ( ) . set_rpc_service ( SERVICE_ECHO2 ) . set_wait_for_ready ( true ) ) ;
// Make sure RPCs all go to the correct backend.
for ( size_t i = 0 ; i < 2 ; + + i ) {
EXPECT_EQ ( kNumEchoRpcs / 2 ,
backends_ [ i ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ i ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ i ] - > backend_service2 ( ) - > request_count ( ) ) ;
}
EXPECT_EQ ( 0 , backends_ [ 2 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( kNumEcho1Rpcs , backends_ [ 2 ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 2 ] - > backend_service2 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 3 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 3 ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( kNumEcho2Rpcs , backends_ [ 3 ] - > backend_service2 ( ) - > request_count ( ) ) ;
}
TEST_P ( LdsRdsTest , XdsRoutingWeightedCluster ) {
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 char * kNotUsedClusterName = " not_used_cluster " ;
const size_t kNumEchoRpcs = 10 ; // RPCs that will go to a fixed backend.
const size_t kWeight75 = 75 ;
const size_t kWeight25 = 25 ;
const double kErrorTolerance = 0.05 ;
const double kWeight75Percent = static_cast < double > ( kWeight75 ) / 100 ;
const double kWeight25Percent = static_cast < double > ( kWeight25 ) / 100 ;
const size_t kNumEcho1Rpcs =
ComputeIdealNumRpcs ( kWeight75Percent , 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 ( kWeight75 ) ;
auto * weighted_cluster2 =
route1 - > mutable_route ( ) - > mutable_weighted_clusters ( ) - > add_clusters ( ) ;
weighted_cluster2 - > set_name ( kNewCluster2Name ) ;
weighted_cluster2 - > mutable_weight ( ) - > set_value ( kWeight25 ) ;
// Cluster with weight 0 will not be used.
auto * weighted_cluster3 =
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 ) ;
SetRouteConfiguration ( balancer_ . get ( ) , new_route_config ) ;
WaitForAllBackends ( 0 , 1 ) ;
WaitForAllBackends ( 1 , 3 , WaitForBackendOptions ( ) ,
RpcOptions ( ) . set_rpc_service ( SERVICE_ECHO1 ) ) ;
CheckRpcSendOk ( kNumEchoRpcs ) ;
CheckRpcSendOk ( 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 weight_75_request_count =
backends_ [ 1 ] - > backend_service1 ( ) - > request_count ( ) ;
EXPECT_EQ ( 0 , backends_ [ 2 ] - > backend_service ( ) - > request_count ( ) ) ;
const int weight_25_request_count =
backends_ [ 2 ] - > backend_service1 ( ) - > request_count ( ) ;
gpr_log ( GPR_INFO , " target_75 received %d rpcs and target_25 received %d rpcs " ,
weight_75_request_count , weight_25_request_count ) ;
EXPECT_THAT ( static_cast < double > ( weight_75_request_count ) / kNumEcho1Rpcs ,
: : testing : : DoubleNear ( kWeight75Percent , kErrorTolerance ) ) ;
EXPECT_THAT ( static_cast < double > ( weight_25_request_count ) / kNumEcho1Rpcs ,
: : testing : : DoubleNear ( kWeight25Percent , kErrorTolerance ) ) ;
}
TEST_P ( LdsRdsTest , RouteActionWeightedTargetDefaultRoute ) {
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 kWeight75 = 75 ;
const size_t kWeight25 = 25 ;
const double kErrorTolerance = 0.05 ;
const double kWeight75Percent = static_cast < double > ( kWeight75 ) / 100 ;
const double kWeight25Percent = static_cast < double > ( kWeight25 ) / 100 ;
const size_t kNumEchoRpcs =
ComputeIdealNumRpcs ( kWeight75Percent , 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 ( " " ) ;
auto * weighted_cluster1 =
route1 - > mutable_route ( ) - > mutable_weighted_clusters ( ) - > add_clusters ( ) ;
weighted_cluster1 - > set_name ( kNewCluster1Name ) ;
weighted_cluster1 - > mutable_weight ( ) - > set_value ( kWeight75 ) ;
auto * weighted_cluster2 =
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 ( 1 , 3 ) ;
CheckRpcSendOk ( kNumEchoRpcs ) ;
// Make sure RPCs all go to the correct backend.
EXPECT_EQ ( 0 , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
const int weight_75_request_count =
backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ;
const int weight_25_request_count =
backends_ [ 2 ] - > backend_service ( ) - > request_count ( ) ;
gpr_log ( GPR_INFO , " target_75 received %d rpcs and target_25 received %d rpcs " ,
weight_75_request_count , weight_25_request_count ) ;
EXPECT_THAT ( static_cast < double > ( weight_75_request_count ) / kNumEchoRpcs ,
: : testing : : DoubleNear ( kWeight75Percent , kErrorTolerance ) ) ;
EXPECT_THAT ( static_cast < double > ( weight_25_request_count ) / kNumEchoRpcs ,
: : testing : : DoubleNear ( kWeight25Percent , kErrorTolerance ) ) ;
}
TEST_P ( LdsRdsTest , XdsRoutingWeightedClusterUpdateWeights ) {
CreateAndStartBackends ( 4 ) ;
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 char * kNewCluster3Name = " new_cluster_3 " ;
const char * kNewEdsService3Name = " new_eds_service_name_3 " ;
const size_t kNumEchoRpcs = 10 ;
const size_t kWeight75 = 75 ;
const size_t kWeight25 = 25 ;
const size_t kWeight50 = 50 ;
const double kErrorTolerance = 0.05 ;
const double kWeight75Percent = static_cast < double > ( kWeight75 ) / 100 ;
const double kWeight25Percent = static_cast < double > ( kWeight25 ) / 100 ;
const double kWeight50Percent = static_cast < double > ( kWeight50 ) / 100 ;
const size_t kNumEcho1Rpcs7525 =
ComputeIdealNumRpcs ( kWeight75Percent , kErrorTolerance ) ;
const size_t kNumEcho1Rpcs5050 =
ComputeIdealNumRpcs ( kWeight50Percent , kErrorTolerance ) ;
// Populate new EDS resources.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } ,
} ) ;
EdsResourceArgs args1 ( {
{ " locality0 " , CreateEndpointsForBackends ( 1 , 2 ) } ,
} ) ;
EdsResourceArgs args2 ( {
{ " locality0 " , CreateEndpointsForBackends ( 2 , 3 ) } ,
} ) ;
EdsResourceArgs args3 ( {
{ " locality0 " , CreateEndpointsForBackends ( 3 , 4 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args1 , kNewEdsService1Name ) ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args2 , kNewEdsService2Name ) ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args3 , kNewEdsService3Name ) ) ;
// 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 ) ;
Cluster new_cluster3 = default_cluster_ ;
new_cluster3 . set_name ( kNewCluster3Name ) ;
new_cluster3 . mutable_eds_cluster_config ( ) - > set_service_name (
kNewEdsService3Name ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( new_cluster3 ) ;
// Populating Route Configurations.
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 ( kWeight75 ) ;
auto * weighted_cluster2 =
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 ) ;
SetRouteConfiguration ( balancer_ . get ( ) , new_route_config ) ;
WaitForAllBackends ( 0 , 1 ) ;
WaitForAllBackends ( 1 , 3 , WaitForBackendOptions ( ) ,
RpcOptions ( ) . set_rpc_service ( SERVICE_ECHO1 ) ) ;
CheckRpcSendOk ( kNumEchoRpcs ) ;
CheckRpcSendOk ( kNumEcho1Rpcs7525 ,
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 weight_75_request_count =
backends_ [ 1 ] - > backend_service1 ( ) - > request_count ( ) ;
EXPECT_EQ ( 0 , backends_ [ 1 ] - > backend_service2 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 2 ] - > backend_service ( ) - > request_count ( ) ) ;
const int weight_25_request_count =
backends_ [ 2 ] - > backend_service1 ( ) - > request_count ( ) ;
EXPECT_EQ ( 0 , backends_ [ 3 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 3 ] - > backend_service1 ( ) - > request_count ( ) ) ;
gpr_log ( GPR_INFO , " target_75 received %d rpcs and target_25 received %d rpcs " ,
weight_75_request_count , weight_25_request_count ) ;
EXPECT_THAT ( static_cast < double > ( weight_75_request_count ) / kNumEcho1Rpcs7525 ,
: : testing : : DoubleNear ( kWeight75Percent , kErrorTolerance ) ) ;
EXPECT_THAT ( static_cast < double > ( weight_25_request_count ) / kNumEcho1Rpcs7525 ,
: : testing : : DoubleNear ( kWeight25Percent , kErrorTolerance ) ) ;
// Change Route Configurations: same clusters different weights.
weighted_cluster1 - > mutable_weight ( ) - > set_value ( kWeight50 ) ;
weighted_cluster2 - > mutable_weight ( ) - > set_value ( kWeight50 ) ;
// Change default route to a new cluster to help to identify when new
// polices are seen by the client.
default_route - > mutable_route ( ) - > set_cluster ( kNewCluster3Name ) ;
SetRouteConfiguration ( balancer_ . get ( ) , new_route_config ) ;
ResetBackendCounters ( ) ;
WaitForAllBackends ( 3 , 4 ) ;
CheckRpcSendOk ( kNumEchoRpcs ) ;
CheckRpcSendOk ( kNumEcho1Rpcs5050 ,
RpcOptions ( ) . set_rpc_service ( SERVICE_ECHO1 ) ) ;
// Make sure RPCs all go to the correct backend.
EXPECT_EQ ( 0 , 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 weight_50_request_count_1 =
backends_ [ 1 ] - > backend_service1 ( ) - > request_count ( ) ;
EXPECT_EQ ( 0 , backends_ [ 2 ] - > backend_service ( ) - > request_count ( ) ) ;
const int weight_50_request_count_2 =
backends_ [ 2 ] - > backend_service1 ( ) - > request_count ( ) ;
EXPECT_EQ ( kNumEchoRpcs , backends_ [ 3 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 3 ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_THAT (
static_cast < double > ( weight_50_request_count_1 ) / kNumEcho1Rpcs5050 ,
: : testing : : DoubleNear ( kWeight50Percent , kErrorTolerance ) ) ;
EXPECT_THAT (
static_cast < double > ( weight_50_request_count_2 ) / kNumEcho1Rpcs5050 ,
: : testing : : DoubleNear ( kWeight50Percent , kErrorTolerance ) ) ;
}
TEST_P ( LdsRdsTest , XdsRoutingWeightedClusterUpdateClusters ) {
CreateAndStartBackends ( 4 ) ;
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 char * kNewCluster3Name = " new_cluster_3 " ;
const char * kNewEdsService3Name = " new_eds_service_name_3 " ;
const size_t kNumEchoRpcs = 10 ;
const size_t kWeight75 = 75 ;
const size_t kWeight25 = 25 ;
const size_t kWeight50 = 50 ;
const double kErrorTolerance = 0.05 ;
const double kWeight75Percent = static_cast < double > ( kWeight75 ) / 100 ;
const double kWeight25Percent = static_cast < double > ( kWeight25 ) / 100 ;
const double kWeight50Percent = static_cast < double > ( kWeight50 ) / 100 ;
const size_t kNumEcho1Rpcs7525 =
ComputeIdealNumRpcs ( kWeight75Percent , kErrorTolerance ) ;
const size_t kNumEcho1Rpcs5050 =
ComputeIdealNumRpcs ( kWeight50Percent , kErrorTolerance ) ;
// Populate new EDS resources.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } ,
} ) ;
EdsResourceArgs args1 ( {
{ " locality0 " , CreateEndpointsForBackends ( 1 , 2 ) } ,
} ) ;
EdsResourceArgs args2 ( {
{ " locality0 " , CreateEndpointsForBackends ( 2 , 3 ) } ,
} ) ;
EdsResourceArgs args3 ( {
{ " locality0 " , CreateEndpointsForBackends ( 3 , 4 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args1 , kNewEdsService1Name ) ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args2 , kNewEdsService2Name ) ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args3 , kNewEdsService3Name ) ) ;
// 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 ) ;
Cluster new_cluster3 = default_cluster_ ;
new_cluster3 . set_name ( kNewCluster3Name ) ;
new_cluster3 . mutable_eds_cluster_config ( ) - > set_service_name (
kNewEdsService3Name ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( new_cluster3 ) ;
// Populating Route Configurations.
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 ( kWeight75 ) ;
auto * weighted_cluster2 =
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 ) ;
SetRouteConfiguration ( balancer_ . get ( ) , new_route_config ) ;
WaitForBackend ( 0 ) ;
WaitForBackend ( 1 , WaitForBackendOptions ( ) ,
RpcOptions ( ) . set_rpc_service ( SERVICE_ECHO1 ) ) ;
CheckRpcSendOk ( kNumEchoRpcs ) ;
CheckRpcSendOk ( kNumEcho1Rpcs7525 ,
RpcOptions ( ) . set_rpc_service ( SERVICE_ECHO1 ) ) ;
// Make sure RPCs all go to the correct backend.
EXPECT_EQ ( kNumEchoRpcs , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
int weight_25_request_count =
backends_ [ 0 ] - > backend_service1 ( ) - > request_count ( ) ;
EXPECT_EQ ( 0 , backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ) ;
int weight_75_request_count =
backends_ [ 1 ] - > backend_service1 ( ) - > request_count ( ) ;
EXPECT_EQ ( 0 , backends_ [ 2 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 2 ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 3 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 3 ] - > backend_service1 ( ) - > request_count ( ) ) ;
gpr_log ( GPR_INFO , " target_75 received %d rpcs and target_25 received %d rpcs " ,
weight_75_request_count , weight_25_request_count ) ;
EXPECT_THAT ( static_cast < double > ( weight_75_request_count ) / kNumEcho1Rpcs7525 ,
: : testing : : DoubleNear ( kWeight75Percent , kErrorTolerance ) ) ;
EXPECT_THAT ( static_cast < double > ( weight_25_request_count ) / kNumEcho1Rpcs7525 ,
: : testing : : DoubleNear ( kWeight25Percent , kErrorTolerance ) ) ;
// Change Route Configurations: new set of clusters with different weights.
weighted_cluster1 - > mutable_weight ( ) - > set_value ( kWeight50 ) ;
weighted_cluster2 - > set_name ( kNewCluster2Name ) ;
weighted_cluster2 - > mutable_weight ( ) - > set_value ( kWeight50 ) ;
SetRouteConfiguration ( balancer_ . get ( ) , new_route_config ) ;
ResetBackendCounters ( ) ;
WaitForBackend ( 2 , WaitForBackendOptions ( ) ,
RpcOptions ( ) . set_rpc_service ( SERVICE_ECHO1 ) ) ;
CheckRpcSendOk ( kNumEchoRpcs ) ;
CheckRpcSendOk ( kNumEcho1Rpcs5050 ,
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 weight_50_request_count_1 =
backends_ [ 1 ] - > backend_service1 ( ) - > request_count ( ) ;
EXPECT_EQ ( 0 , backends_ [ 2 ] - > backend_service ( ) - > request_count ( ) ) ;
const int weight_50_request_count_2 =
backends_ [ 2 ] - > backend_service1 ( ) - > request_count ( ) ;
EXPECT_EQ ( 0 , backends_ [ 3 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 3 ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_THAT (
static_cast < double > ( weight_50_request_count_1 ) / kNumEcho1Rpcs5050 ,
: : testing : : DoubleNear ( kWeight50Percent , kErrorTolerance ) ) ;
EXPECT_THAT (
static_cast < double > ( weight_50_request_count_2 ) / kNumEcho1Rpcs5050 ,
: : testing : : DoubleNear ( kWeight50Percent , kErrorTolerance ) ) ;
// Change Route Configurations.
weighted_cluster1 - > mutable_weight ( ) - > set_value ( kWeight75 ) ;
weighted_cluster2 - > set_name ( kNewCluster3Name ) ;
weighted_cluster2 - > mutable_weight ( ) - > set_value ( kWeight25 ) ;
SetRouteConfiguration ( balancer_ . get ( ) , new_route_config ) ;
ResetBackendCounters ( ) ;
WaitForBackend ( 3 , WaitForBackendOptions ( ) ,
RpcOptions ( ) . set_rpc_service ( SERVICE_ECHO1 ) ) ;
CheckRpcSendOk ( kNumEchoRpcs ) ;
CheckRpcSendOk ( kNumEcho1Rpcs7525 ,
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 ( ) ) ;
weight_75_request_count = backends_ [ 1 ] - > backend_service1 ( ) - > request_count ( ) ;
EXPECT_EQ ( 0 , backends_ [ 2 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 2 ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 3 ] - > backend_service ( ) - > request_count ( ) ) ;
weight_25_request_count = backends_ [ 3 ] - > backend_service1 ( ) - > request_count ( ) ;
gpr_log ( GPR_INFO , " target_75 received %d rpcs and target_25 received %d rpcs " ,
weight_75_request_count , weight_25_request_count ) ;
EXPECT_THAT ( static_cast < double > ( weight_75_request_count ) / kNumEcho1Rpcs7525 ,
: : testing : : DoubleNear ( kWeight75Percent , kErrorTolerance ) ) ;
EXPECT_THAT ( static_cast < double > ( weight_25_request_count ) / kNumEcho1Rpcs7525 ,
: : testing : : DoubleNear ( kWeight25Percent , kErrorTolerance ) ) ;
}
TEST_P ( LdsRdsTest , XdsRoutingClusterUpdateClusters ) {
CreateAndStartBackends ( 2 ) ;
const char * kNewClusterName = " new_cluster " ;
const char * kNewEdsServiceName = " new_eds_service_name " ;
const size_t kNumEchoRpcs = 5 ;
// Populate new EDS resources.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } ,
} ) ;
EdsResourceArgs args1 ( {
{ " locality0 " , CreateEndpointsForBackends ( 1 , 2 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args1 , kNewEdsServiceName ) ) ;
// Populate new CDS resources.
Cluster new_cluster = default_cluster_ ;
new_cluster . set_name ( kNewClusterName ) ;
new_cluster . mutable_eds_cluster_config ( ) - > set_service_name (
kNewEdsServiceName ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( new_cluster ) ;
// Send Route Configuration.
RouteConfiguration new_route_config = default_route_config_ ;
SetRouteConfiguration ( balancer_ . get ( ) , new_route_config ) ;
WaitForAllBackends ( 0 , 1 ) ;
CheckRpcSendOk ( kNumEchoRpcs ) ;
// Make sure RPCs all go to the correct backend.
EXPECT_EQ ( kNumEchoRpcs , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
// Change Route Configurations: new default cluster.
auto * default_route =
new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
default_route - > mutable_route ( ) - > set_cluster ( kNewClusterName ) ;
SetRouteConfiguration ( balancer_ . get ( ) , new_route_config ) ;
WaitForAllBackends ( 1 , 2 ) ;
CheckRpcSendOk ( kNumEchoRpcs ) ;
// Make sure RPCs all go to the correct backend.
EXPECT_EQ ( kNumEchoRpcs , backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ) ;
}
TEST_P ( LdsRdsTest , XdsRoutingClusterUpdateClustersWithPickingDelays ) {
CreateAndStartBackends ( 2 ) ;
const char * kNewClusterName = " new_cluster " ;
const char * kNewEdsServiceName = " new_eds_service_name " ;
// Populate new EDS resources.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } ,
} ) ;
EdsResourceArgs args1 ( {
{ " locality0 " , CreateEndpointsForBackends ( 1 , 2 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args1 , kNewEdsServiceName ) ) ;
// Populate new CDS resources.
Cluster new_cluster = default_cluster_ ;
new_cluster . set_name ( kNewClusterName ) ;
new_cluster . mutable_eds_cluster_config ( ) - > set_service_name (
kNewEdsServiceName ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( new_cluster ) ;
// Bring down the current backend: 0, this will delay route picking time,
// resulting in un-committed RPCs.
ShutdownBackend ( 0 ) ;
// Send a RouteConfiguration with a default route that points to
// backend 0.
RouteConfiguration new_route_config = default_route_config_ ;
SetRouteConfiguration ( balancer_ . get ( ) , new_route_config ) ;
// Send exactly one RPC with no deadline and with wait_for_ready=true.
// This RPC will not complete until after backend 0 is started.
std : : thread sending_rpc ( [ this ] ( ) {
CheckRpcSendOk ( 1 , RpcOptions ( ) . set_wait_for_ready ( true ) . set_timeout_ms ( 0 ) ) ;
} ) ;
// Send a non-wait_for_ready RPC which should fail, this will tell us
// that the client has received the update and attempted to connect.
const Status status = SendRpc ( RpcOptions ( ) . set_timeout_ms ( 0 ) ) ;
EXPECT_FALSE ( status . ok ( ) ) ;
// Send a update RouteConfiguration to use backend 1.
auto * default_route =
new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
default_route - > mutable_route ( ) - > set_cluster ( kNewClusterName ) ;
SetRouteConfiguration ( balancer_ . get ( ) , new_route_config ) ;
// Wait for RPCs to go to the new backend: 1, this ensures that the client
// has processed the update.
WaitForBackend (
1 , WaitForBackendOptions ( ) . set_reset_counters ( false ) . set_allow_failures (
true ) ) ;
// Bring up the previous backend: 0, this will allow the delayed RPC to
// finally call on_call_committed upon completion.
StartBackend ( 0 ) ;
sending_rpc . join ( ) ;
// Make sure RPCs go to the correct backend:
EXPECT_EQ ( 1 , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 1 , backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ) ;
}
TEST_P ( LdsRdsTest , XdsRoutingApplyXdsTimeout ) {
const int64_t kTimeoutMillis = 500 ;
const int64_t kTimeoutNano = kTimeoutMillis * 1000000 ;
const int64_t kTimeoutGrpcTimeoutHeaderMaxSecond = 1 ;
const int64_t kTimeoutMaxStreamDurationSecond = 2 ;
const int64_t kTimeoutHttpMaxStreamDurationSecond = 3 ;
const int64_t kTimeoutApplicationSecond = 4 ;
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 char * kNewCluster3Name = " new_cluster_3 " ;
const char * kNewEdsService3Name = " new_eds_service_name_3 " ;
// Populate new EDS resources.
EdsResourceArgs args ( { { " locality0 " , { MakeNonExistantEndpoint ( ) } } } ) ;
EdsResourceArgs args1 ( { { " locality0 " , { MakeNonExistantEndpoint ( ) } } } ) ;
EdsResourceArgs args2 ( { { " locality0 " , { MakeNonExistantEndpoint ( ) } } } ) ;
EdsResourceArgs args3 ( { { " locality0 " , { MakeNonExistantEndpoint ( ) } } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args1 , kNewEdsService1Name ) ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args2 , kNewEdsService2Name ) ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args3 , kNewEdsService3Name ) ) ;
// 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 ) ;
Cluster new_cluster3 = default_cluster_ ;
new_cluster3 . set_name ( kNewCluster3Name ) ;
new_cluster3 . mutable_eds_cluster_config ( ) - > set_service_name (
kNewEdsService3Name ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( new_cluster3 ) ;
// Construct listener.
auto listener = default_listener_ ;
HttpConnectionManager http_connection_manager ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > UnpackTo (
& http_connection_manager ) ;
// Set up HTTP max_stream_duration of 3.5 seconds
auto * duration =
http_connection_manager . mutable_common_http_protocol_options ( )
- > mutable_max_stream_duration ( ) ;
duration - > set_seconds ( kTimeoutHttpMaxStreamDurationSecond ) ;
duration - > set_nanos ( kTimeoutNano ) ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > PackFrom (
http_connection_manager ) ;
// Construct route config.
RouteConfiguration new_route_config = default_route_config_ ;
// route 1: Set max_stream_duration of 2.5 seconds, Set
// grpc_timeout_header_max of 1.5
auto * route1 = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_path ( " /grpc.testing.EchoTest1Service/Echo1 " ) ;
route1 - > mutable_route ( ) - > set_cluster ( kNewCluster1Name ) ;
auto * max_stream_duration =
route1 - > mutable_route ( ) - > mutable_max_stream_duration ( ) ;
duration = max_stream_duration - > mutable_max_stream_duration ( ) ;
duration - > set_seconds ( kTimeoutMaxStreamDurationSecond ) ;
duration - > set_nanos ( kTimeoutNano ) ;
duration = max_stream_duration - > mutable_grpc_timeout_header_max ( ) ;
duration - > set_seconds ( kTimeoutGrpcTimeoutHeaderMaxSecond ) ;
duration - > set_nanos ( kTimeoutNano ) ;
// route 2: Set max_stream_duration of 2.5 seconds
auto * route2 = new_route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
route2 - > mutable_match ( ) - > set_path ( " /grpc.testing.EchoTest2Service/Echo2 " ) ;
route2 - > mutable_route ( ) - > set_cluster ( kNewCluster2Name ) ;
max_stream_duration = route2 - > mutable_route ( ) - > mutable_max_stream_duration ( ) ;
duration = max_stream_duration - > mutable_max_stream_duration ( ) ;
duration - > set_seconds ( kTimeoutMaxStreamDurationSecond ) ;
duration - > set_nanos ( kTimeoutNano ) ;
// route 3: No timeout values in route configuration
auto * route3 = new_route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
route3 - > mutable_match ( ) - > set_path ( " /grpc.testing.EchoTestService/Echo " ) ;
route3 - > mutable_route ( ) - > set_cluster ( kNewCluster3Name ) ;
// Set listener and route config.
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , std : : move ( listener ) ,
new_route_config ) ;
// Test grpc_timeout_header_max of 1.5 seconds applied
grpc_core : : Timestamp t0 = NowFromCycleCounter ( ) ;
grpc_core : : Timestamp t1 =
t0 + grpc_core : : Duration : : Seconds ( kTimeoutGrpcTimeoutHeaderMaxSecond ) +
grpc_core : : Duration : : Milliseconds ( kTimeoutMillis ) ;
grpc_core : : Timestamp t2 =
t0 + grpc_core : : Duration : : Seconds ( kTimeoutMaxStreamDurationSecond ) +
grpc_core : : Duration : : Milliseconds ( kTimeoutMillis ) ;
CheckRpcSendFailure (
CheckRpcSendFailureOptions ( )
. set_rpc_options ( RpcOptions ( )
. set_rpc_service ( SERVICE_ECHO1 )
. set_rpc_method ( METHOD_ECHO1 )
. set_wait_for_ready ( true )
. set_timeout_ms ( grpc_core : : Duration : : Seconds (
kTimeoutApplicationSecond )
. millis ( ) ) )
. set_expected_error_code ( StatusCode : : DEADLINE_EXCEEDED ) ) ;
EXPECT_THAT ( NowFromCycleCounter ( ) , AdjustedClockInRange ( t1 , t2 ) ) ;
// Test max_stream_duration of 2.5 seconds applied
t0 = NowFromCycleCounter ( ) ;
t1 = t0 + grpc_core : : Duration : : Seconds ( kTimeoutMaxStreamDurationSecond ) +
grpc_core : : Duration : : Milliseconds ( kTimeoutMillis ) ;
t2 = t0 + grpc_core : : Duration : : Seconds ( kTimeoutHttpMaxStreamDurationSecond ) +
grpc_core : : Duration : : Milliseconds ( kTimeoutMillis ) ;
CheckRpcSendFailure (
CheckRpcSendFailureOptions ( )
. set_rpc_options ( RpcOptions ( )
. set_rpc_service ( SERVICE_ECHO2 )
. set_rpc_method ( METHOD_ECHO2 )
. set_wait_for_ready ( true )
. set_timeout_ms ( grpc_core : : Duration : : Seconds (
kTimeoutApplicationSecond )
. millis ( ) ) )
. set_expected_error_code ( StatusCode : : DEADLINE_EXCEEDED ) ) ;
EXPECT_THAT ( NowFromCycleCounter ( ) , AdjustedClockInRange ( t1 , t2 ) ) ;
// Test http_stream_duration of 3.5 seconds applied
t0 = NowFromCycleCounter ( ) ;
t1 = t0 + grpc_core : : Duration : : Seconds ( kTimeoutHttpMaxStreamDurationSecond ) +
grpc_core : : Duration : : Milliseconds ( kTimeoutMillis ) ;
t2 = t0 + grpc_core : : Duration : : Seconds ( kTimeoutApplicationSecond ) +
grpc_core : : Duration : : Milliseconds ( kTimeoutMillis ) ;
CheckRpcSendFailure (
CheckRpcSendFailureOptions ( )
. set_rpc_options ( RpcOptions ( ) . set_wait_for_ready ( true ) . set_timeout_ms (
grpc_core : : Duration : : Seconds ( kTimeoutApplicationSecond ) . millis ( ) ) )
. set_expected_error_code ( StatusCode : : DEADLINE_EXCEEDED ) ) ;
EXPECT_THAT ( NowFromCycleCounter ( ) , AdjustedClockInRange ( t1 , t2 ) ) ;
}
TEST_P ( LdsRdsTest , XdsRoutingApplyApplicationTimeoutWhenXdsTimeoutExplicit0 ) {
const int64_t kTimeoutNano = 500000000 ;
const int64_t kTimeoutMaxStreamDurationSecond = 2 ;
const int64_t kTimeoutHttpMaxStreamDurationSecond = 3 ;
const int64_t kTimeoutApplicationSecond = 4 ;
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 " ;
// Populate new EDS resources.
EdsResourceArgs args ( { { " locality0 " , { MakeNonExistantEndpoint ( ) } } } ) ;
EdsResourceArgs args1 ( { { " locality0 " , { MakeNonExistantEndpoint ( ) } } } ) ;
EdsResourceArgs args2 ( { { " locality0 " , { MakeNonExistantEndpoint ( ) } } } ) ;
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 ) ;
// Construct listener.
auto listener = default_listener_ ;
HttpConnectionManager http_connection_manager ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > UnpackTo (
& http_connection_manager ) ;
// Set up HTTP max_stream_duration of 3.5 seconds
auto * duration =
http_connection_manager . mutable_common_http_protocol_options ( )
- > mutable_max_stream_duration ( ) ;
duration - > set_seconds ( kTimeoutHttpMaxStreamDurationSecond ) ;
duration - > set_nanos ( kTimeoutNano ) ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > PackFrom (
http_connection_manager ) ;
// Construct route config.
RouteConfiguration new_route_config = default_route_config_ ;
// route 1: Set max_stream_duration of 2.5 seconds, Set
// grpc_timeout_header_max of 0
auto * route1 = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_path ( " /grpc.testing.EchoTest1Service/Echo1 " ) ;
route1 - > mutable_route ( ) - > set_cluster ( kNewCluster1Name ) ;
auto * max_stream_duration =
route1 - > mutable_route ( ) - > mutable_max_stream_duration ( ) ;
duration = max_stream_duration - > mutable_max_stream_duration ( ) ;
duration - > set_seconds ( kTimeoutMaxStreamDurationSecond ) ;
duration - > set_nanos ( kTimeoutNano ) ;
duration = max_stream_duration - > mutable_grpc_timeout_header_max ( ) ;
duration - > set_seconds ( 0 ) ;
duration - > set_nanos ( 0 ) ;
// route 2: Set max_stream_duration to 0
auto * route2 = new_route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
route2 - > mutable_match ( ) - > set_path ( " /grpc.testing.EchoTest2Service/Echo2 " ) ;
route2 - > mutable_route ( ) - > set_cluster ( kNewCluster2Name ) ;
max_stream_duration = route2 - > mutable_route ( ) - > mutable_max_stream_duration ( ) ;
duration = max_stream_duration - > mutable_max_stream_duration ( ) ;
duration - > set_seconds ( 0 ) ;
duration - > set_nanos ( 0 ) ;
// Set listener and route config.
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , std : : move ( listener ) ,
new_route_config ) ;
// Test application timeout is applied for route 1
auto t0 = system_clock : : now ( ) ;
CheckRpcSendFailure (
CheckRpcSendFailureOptions ( )
. set_rpc_options (
RpcOptions ( )
. set_rpc_service ( SERVICE_ECHO1 )
. set_rpc_method ( METHOD_ECHO1 )
. set_wait_for_ready ( true )
. set_timeout_ms ( kTimeoutApplicationSecond * 1000 ) )
. set_expected_error_code ( StatusCode : : DEADLINE_EXCEEDED ) ) ;
auto ellapsed_nano_seconds =
std : : chrono : : duration_cast < std : : chrono : : nanoseconds > ( system_clock : : now ( ) -
t0 ) ;
EXPECT_GT ( ellapsed_nano_seconds . count ( ) ,
kTimeoutApplicationSecond * 1000000000 ) ;
// Test application timeout is applied for route 2
t0 = system_clock : : now ( ) ;
CheckRpcSendFailure (
CheckRpcSendFailureOptions ( )
. set_rpc_options (
RpcOptions ( )
. set_rpc_service ( SERVICE_ECHO2 )
. set_rpc_method ( METHOD_ECHO2 )
. set_wait_for_ready ( true )
. set_timeout_ms ( kTimeoutApplicationSecond * 1000 ) )
. set_expected_error_code ( StatusCode : : DEADLINE_EXCEEDED ) ) ;
ellapsed_nano_seconds = std : : chrono : : duration_cast < std : : chrono : : nanoseconds > (
system_clock : : now ( ) - t0 ) ;
EXPECT_GT ( ellapsed_nano_seconds . count ( ) ,
kTimeoutApplicationSecond * 1000000000 ) ;
}
TEST_P ( LdsRdsTest , XdsRoutingApplyApplicationTimeoutWhenHttpTimeoutExplicit0 ) {
const int64_t kTimeoutApplicationSecond = 4 ;
// Populate new EDS resources.
EdsResourceArgs args ( { { " locality0 " , { MakeNonExistantEndpoint ( ) } } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
auto listener = default_listener_ ;
HttpConnectionManager http_connection_manager ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > UnpackTo (
& http_connection_manager ) ;
// Set up HTTP max_stream_duration to be explicit 0
auto * duration =
http_connection_manager . mutable_common_http_protocol_options ( )
- > mutable_max_stream_duration ( ) ;
duration - > set_seconds ( 0 ) ;
duration - > set_nanos ( 0 ) ;
listener . mutable_api_listener ( ) - > mutable_api_listener ( ) - > PackFrom (
http_connection_manager ) ;
// Set listener and route config.
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , std : : move ( listener ) ,
default_route_config_ ) ;
// Test application timeout is applied for route 1
auto t0 = system_clock : : now ( ) ;
CheckRpcSendFailure (
CheckRpcSendFailureOptions ( )
. set_rpc_options ( RpcOptions ( ) . set_wait_for_ready ( true ) . set_timeout_ms (
grpc_core : : Duration : : Seconds ( kTimeoutApplicationSecond ) . millis ( ) ) )
. set_expected_error_code ( StatusCode : : DEADLINE_EXCEEDED ) ) ;
auto ellapsed_nano_seconds =
std : : chrono : : duration_cast < std : : chrono : : nanoseconds > ( system_clock : : now ( ) -
t0 ) ;
EXPECT_GT ( ellapsed_nano_seconds . count ( ) ,
kTimeoutApplicationSecond * 1000000000 ) ;
}
// Test to ensure application-specified deadline won't be affected when
// the xDS config does not specify a timeout.
TEST_P ( LdsRdsTest , XdsRoutingWithOnlyApplicationTimeout ) {
const int64_t kTimeoutApplicationSecond = 4 ;
// Populate new EDS resources.
EdsResourceArgs args ( { { " locality0 " , { MakeNonExistantEndpoint ( ) } } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
auto t0 = system_clock : : now ( ) ;
CheckRpcSendFailure (
CheckRpcSendFailureOptions ( )
. set_rpc_options ( RpcOptions ( ) . set_wait_for_ready ( true ) . set_timeout_ms (
grpc_core : : Duration : : Seconds ( kTimeoutApplicationSecond ) . millis ( ) ) )
. set_expected_error_code ( StatusCode : : DEADLINE_EXCEEDED ) ) ;
auto ellapsed_nano_seconds =
std : : chrono : : duration_cast < std : : chrono : : nanoseconds > ( system_clock : : now ( ) -
t0 ) ;
EXPECT_GT ( ellapsed_nano_seconds . count ( ) ,
kTimeoutApplicationSecond * 1000000000 ) ;
}
TEST_P ( LdsRdsTest , XdsRetryPolicyNumRetries ) {
CreateAndStartBackends ( 1 ) ;
const size_t kNumRetries = 3 ;
// Populate new EDS resources.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Construct route config to set retry policy.
RouteConfiguration new_route_config = default_route_config_ ;
auto * route1 = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * retry_policy = route1 - > mutable_route ( ) - > mutable_retry_policy ( ) ;
retry_policy - > set_retry_on (
" 5xx,cancelled,deadline-exceeded,internal,resource-exhausted, "
" unavailable " ) ;
retry_policy - > mutable_num_retries ( ) - > set_value ( kNumRetries ) ;
SetRouteConfiguration ( balancer_ . get ( ) , new_route_config ) ;
// Ensure we retried the correct number of times on all supported status.
CheckRpcSendFailure (
CheckRpcSendFailureOptions ( )
. set_rpc_options (
RpcOptions ( ) . set_server_expected_error ( StatusCode : : CANCELLED ) )
. set_expected_error_code ( StatusCode : : CANCELLED ) ) ;
EXPECT_EQ ( kNumRetries + 1 , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
ResetBackendCounters ( ) ;
CheckRpcSendFailure (
CheckRpcSendFailureOptions ( )
. set_rpc_options ( RpcOptions ( ) . set_server_expected_error (
StatusCode : : DEADLINE_EXCEEDED ) )
. set_expected_error_code ( StatusCode : : DEADLINE_EXCEEDED ) ) ;
EXPECT_EQ ( kNumRetries + 1 , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
ResetBackendCounters ( ) ;
CheckRpcSendFailure (
CheckRpcSendFailureOptions ( )
. set_rpc_options (
RpcOptions ( ) . set_server_expected_error ( StatusCode : : INTERNAL ) )
. set_expected_error_code ( StatusCode : : INTERNAL ) ) ;
EXPECT_EQ ( kNumRetries + 1 , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
ResetBackendCounters ( ) ;
CheckRpcSendFailure (
CheckRpcSendFailureOptions ( )
. set_rpc_options ( RpcOptions ( ) . set_server_expected_error (
StatusCode : : RESOURCE_EXHAUSTED ) )
. set_expected_error_code ( StatusCode : : RESOURCE_EXHAUSTED ) ) ;
EXPECT_EQ ( kNumRetries + 1 , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
ResetBackendCounters ( ) ;
CheckRpcSendFailure (
CheckRpcSendFailureOptions ( )
. set_rpc_options (
RpcOptions ( ) . set_server_expected_error ( StatusCode : : UNAVAILABLE ) )
. set_expected_error_code ( StatusCode : : UNAVAILABLE ) ) ;
EXPECT_EQ ( kNumRetries + 1 , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
ResetBackendCounters ( ) ;
// Ensure we don't retry on an unsupported status.
CheckRpcSendFailure (
CheckRpcSendFailureOptions ( )
. set_rpc_options ( RpcOptions ( ) . set_server_expected_error (
StatusCode : : UNAUTHENTICATED ) )
. set_expected_error_code ( StatusCode : : UNAUTHENTICATED ) ) ;
EXPECT_EQ ( 1 , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
}
TEST_P ( LdsRdsTest , XdsRetryPolicyAtVirtualHostLevel ) {
CreateAndStartBackends ( 1 ) ;
const size_t kNumRetries = 3 ;
// Populate new EDS resources.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Construct route config to set retry policy.
RouteConfiguration new_route_config = default_route_config_ ;
auto * retry_policy =
new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_retry_policy ( ) ;
retry_policy - > set_retry_on (
" cancelled,deadline-exceeded,internal,resource-exhausted,unavailable " ) ;
retry_policy - > mutable_num_retries ( ) - > set_value ( kNumRetries ) ;
SetRouteConfiguration ( balancer_ . get ( ) , new_route_config ) ;
// Ensure we retried the correct number of times on a supported status.
CheckRpcSendFailure (
CheckRpcSendFailureOptions ( )
. set_rpc_options ( RpcOptions ( ) . set_server_expected_error (
StatusCode : : DEADLINE_EXCEEDED ) )
. set_expected_error_code ( StatusCode : : DEADLINE_EXCEEDED ) ) ;
EXPECT_EQ ( kNumRetries + 1 , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
}
TEST_P ( LdsRdsTest , XdsRetryPolicyLongBackOff ) {
CreateAndStartBackends ( 1 ) ;
// Set num retries to 3, but due to longer back off, we expect only 1 retry
// will take place.
const size_t kNumRetries = 3 ;
// Populate new EDS resources.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Construct route config to set retry policy.
RouteConfiguration new_route_config = default_route_config_ ;
auto * route1 = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * retry_policy = route1 - > mutable_route ( ) - > mutable_retry_policy ( ) ;
retry_policy - > set_retry_on (
" 5xx,cancelled,deadline-exceeded,internal,resource-exhausted, "
" unavailable " ) ;
retry_policy - > mutable_num_retries ( ) - > set_value ( kNumRetries ) ;
auto base_interval =
retry_policy - > mutable_retry_back_off ( ) - > mutable_base_interval ( ) ;
// Set backoff to 1 second, 1/2 of rpc timeout of 2 second.
base_interval - > set_seconds ( 1 * grpc_test_slowdown_factor ( ) ) ;
base_interval - > set_nanos ( 0 ) ;
SetRouteConfiguration ( balancer_ . get ( ) , new_route_config ) ;
// No need to set max interval and just let it be the default of 10x of base.
// We expect 1 retry before the RPC times out with DEADLINE_EXCEEDED.
CheckRpcSendFailure (
CheckRpcSendFailureOptions ( )
. set_rpc_options (
RpcOptions ( ) . set_timeout_ms ( 2500 ) . set_server_expected_error (
StatusCode : : CANCELLED ) )
. set_expected_error_code ( StatusCode : : DEADLINE_EXCEEDED ) ) ;
EXPECT_EQ ( 1 + 1 , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
}
TEST_P ( LdsRdsTest , XdsRetryPolicyMaxBackOff ) {
CreateAndStartBackends ( 1 ) ;
// Set num retries to 3, but due to longer back off, we expect only 2 retry
// will take place, while the 2nd one will obey the max backoff.
const size_t kNumRetries = 3 ;
// Populate new EDS resources.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Construct route config to set retry policy.
RouteConfiguration new_route_config = default_route_config_ ;
auto * route1 = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * retry_policy = route1 - > mutable_route ( ) - > mutable_retry_policy ( ) ;
retry_policy - > set_retry_on (
" 5xx,cancelled,deadline-exceeded,internal,resource-exhausted, "
" unavailable " ) ;
retry_policy - > mutable_num_retries ( ) - > set_value ( kNumRetries ) ;
auto base_interval =
retry_policy - > mutable_retry_back_off ( ) - > mutable_base_interval ( ) ;
// Set backoff to 1 second.
base_interval - > set_seconds ( 1 * grpc_test_slowdown_factor ( ) ) ;
base_interval - > set_nanos ( 0 ) ;
auto max_interval =
retry_policy - > mutable_retry_back_off ( ) - > mutable_max_interval ( ) ;
// Set max interval to be the same as base, so 2 retries will take 2 seconds
// and both retries will take place before the 2.5 seconds rpc timeout.
// Tested to ensure if max is not set, this test will be the same as
// XdsRetryPolicyLongBackOff and we will only see 1 retry in that case.
max_interval - > set_seconds ( 1 * grpc_test_slowdown_factor ( ) ) ;
max_interval - > set_nanos ( 0 ) ;
SetRouteConfiguration ( balancer_ . get ( ) , new_route_config ) ;
// We expect 2 retry before the RPC times out with DEADLINE_EXCEEDED.
CheckRpcSendFailure (
CheckRpcSendFailureOptions ( )
. set_rpc_options (
RpcOptions ( ) . set_timeout_ms ( 2500 ) . set_server_expected_error (
StatusCode : : CANCELLED ) )
. set_expected_error_code ( StatusCode : : DEADLINE_EXCEEDED ) ) ;
EXPECT_EQ ( 2 + 1 , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
}
TEST_P ( LdsRdsTest , XdsRetryPolicyUnsupportedStatusCode ) {
CreateAndStartBackends ( 1 ) ;
const size_t kNumRetries = 3 ;
// Populate new EDS resources.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Construct route config to set retry policy.
RouteConfiguration new_route_config = default_route_config_ ;
auto * route1 = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * retry_policy = route1 - > mutable_route ( ) - > mutable_retry_policy ( ) ;
retry_policy - > set_retry_on ( " 5xx " ) ;
retry_policy - > mutable_num_retries ( ) - > set_value ( kNumRetries ) ;
SetRouteConfiguration ( balancer_ . get ( ) , new_route_config ) ;
// We expect no retry.
CheckRpcSendFailure (
CheckRpcSendFailureOptions ( )
. set_rpc_options ( RpcOptions ( ) . set_server_expected_error (
StatusCode : : DEADLINE_EXCEEDED ) )
. set_expected_error_code ( StatusCode : : DEADLINE_EXCEEDED ) ) ;
EXPECT_EQ ( 1 , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
}
TEST_P ( LdsRdsTest ,
XdsRetryPolicyUnsupportedStatusCodeWithVirtualHostLevelRetry ) {
CreateAndStartBackends ( 1 ) ;
const size_t kNumRetries = 3 ;
// Populate new EDS resources.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Construct route config to set retry policy with no supported retry_on
// statuses.
RouteConfiguration new_route_config = default_route_config_ ;
auto * route1 = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * retry_policy = route1 - > mutable_route ( ) - > mutable_retry_policy ( ) ;
retry_policy - > set_retry_on ( " 5xx " ) ;
retry_policy - > mutable_num_retries ( ) - > set_value ( kNumRetries ) ;
// Construct a virtual host level retry policy with supported statuses.
auto * virtual_host_retry_policy =
new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_retry_policy ( ) ;
virtual_host_retry_policy - > set_retry_on (
" cancelled,deadline-exceeded,internal,resource-exhausted,unavailable " ) ;
virtual_host_retry_policy - > mutable_num_retries ( ) - > set_value ( kNumRetries ) ;
SetRouteConfiguration ( balancer_ . get ( ) , new_route_config ) ;
// We expect no retry.
CheckRpcSendFailure (
CheckRpcSendFailureOptions ( )
. set_rpc_options ( RpcOptions ( ) . set_server_expected_error (
StatusCode : : DEADLINE_EXCEEDED ) )
. set_expected_error_code ( StatusCode : : DEADLINE_EXCEEDED ) ) ;
EXPECT_EQ ( 1 , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
}
TEST_P ( LdsRdsTest , XdsRetryPolicyInvalidNumRetriesZero ) {
CreateAndStartBackends ( 1 ) ;
// Populate new EDS resources.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Construct route config to set retry policy.
RouteConfiguration new_route_config = default_route_config_ ;
auto * route1 = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * retry_policy = route1 - > mutable_route ( ) - > mutable_retry_policy ( ) ;
retry_policy - > set_retry_on ( " deadline-exceeded " ) ;
// Setting num_retries to zero is not valid.
retry_policy - > mutable_num_retries ( ) - > set_value ( 0 ) ;
SetRouteConfiguration ( balancer_ . get ( ) , new_route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr (
" RouteAction RetryPolicy num_retries set to invalid value 0. " ) ) ;
}
TEST_P ( LdsRdsTest , XdsRetryPolicyRetryBackOffMissingBaseInterval ) {
CreateAndStartBackends ( 1 ) ;
// Populate new EDS resources.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Construct route config to set retry policy.
RouteConfiguration new_route_config = default_route_config_ ;
auto * route1 = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * retry_policy = route1 - > mutable_route ( ) - > mutable_retry_policy ( ) ;
retry_policy - > set_retry_on ( " deadline-exceeded " ) ;
retry_policy - > mutable_num_retries ( ) - > set_value ( 1 ) ;
// RetryBackoff is there but base interval is missing.
auto max_interval =
retry_policy - > mutable_retry_back_off ( ) - > mutable_max_interval ( ) ;
max_interval - > set_seconds ( 0 ) ;
max_interval - > set_nanos ( 250000000 ) ;
SetRouteConfiguration ( balancer_ . get ( ) , new_route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr (
" RouteAction RetryPolicy RetryBackoff missing base interval. " ) ) ;
}
TEST_P ( LdsRdsTest , XdsRoutingHeadersMatching ) {
CreateAndStartBackends ( 2 ) ;
const char * kNewClusterName = " new_cluster " ;
const char * kNewEdsServiceName = " new_eds_service_name " ;
const size_t kNumEcho1Rpcs = 100 ;
const size_t kNumEchoRpcs = 5 ;
// Populate new EDS resources.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } ,
} ) ;
EdsResourceArgs args1 ( {
{ " locality0 " , CreateEndpointsForBackends ( 1 , 2 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args1 , kNewEdsServiceName ) ) ;
// Populate new CDS resources.
Cluster new_cluster = default_cluster_ ;
new_cluster . set_name ( kNewClusterName ) ;
new_cluster . mutable_eds_cluster_config ( ) - > set_service_name (
kNewEdsServiceName ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( new_cluster ) ;
// Populating Route Configurations for LDS.
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_prefix ( " /grpc.testing.EchoTest1Service/ " ) ;
auto * header_matcher1 = route1 - > mutable_match ( ) - > add_headers ( ) ;
header_matcher1 - > set_name ( " header1 " ) ;
header_matcher1 - > set_exact_match ( " POST,PUT,GET " ) ;
auto * header_matcher2 = route1 - > mutable_match ( ) - > add_headers ( ) ;
header_matcher2 - > set_name ( " header2 " ) ;
header_matcher2 - > mutable_safe_regex_match ( ) - > set_regex ( " [a-z]* " ) ;
auto * header_matcher3 = route1 - > mutable_match ( ) - > add_headers ( ) ;
header_matcher3 - > set_name ( " header3 " ) ;
header_matcher3 - > mutable_range_match ( ) - > set_start ( 1 ) ;
header_matcher3 - > mutable_range_match ( ) - > set_end ( 1000 ) ;
auto * header_matcher4 = route1 - > mutable_match ( ) - > add_headers ( ) ;
header_matcher4 - > set_name ( " header4 " ) ;
header_matcher4 - > set_present_match ( false ) ;
auto * header_matcher5 = route1 - > mutable_match ( ) - > add_headers ( ) ;
header_matcher5 - > set_name ( " header5 " ) ;
header_matcher5 - > set_present_match ( true ) ;
auto * header_matcher6 = route1 - > mutable_match ( ) - > add_headers ( ) ;
header_matcher6 - > set_name ( " header6 " ) ;
header_matcher6 - > set_prefix_match ( " /grpc " ) ;
auto * header_matcher7 = route1 - > mutable_match ( ) - > add_headers ( ) ;
header_matcher7 - > set_name ( " header7 " ) ;
header_matcher7 - > set_suffix_match ( " .cc " ) ;
header_matcher7 - > set_invert_match ( true ) ;
route1 - > mutable_route ( ) - > set_cluster ( kNewClusterName ) ;
auto * default_route = route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
default_route - > mutable_match ( ) - > set_prefix ( " " ) ;
default_route - > mutable_route ( ) - > set_cluster ( kDefaultClusterName ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
std : : vector < std : : pair < std : : string , std : : string > > metadata = {
{ " header1 " , " POST " } ,
{ " header2 " , " blah " } ,
{ " header3 " , " 1 " } ,
{ " header5 " , " anything " } ,
{ " header6 " , " /grpc.testing.EchoTest1Service/ " } ,
{ " header1 " , " PUT " } ,
{ " header7 " , " grpc.java " } ,
{ " header1 " , " GET " } ,
} ;
const auto header_match_rpc_options = RpcOptions ( )
. set_rpc_service ( SERVICE_ECHO1 )
. set_rpc_method ( METHOD_ECHO1 )
. set_metadata ( std : : move ( metadata ) ) ;
// Make sure all backends are up.
WaitForBackend ( 0 ) ;
WaitForBackend ( 1 , WaitForBackendOptions ( ) , header_match_rpc_options ) ;
// Send RPCs.
CheckRpcSendOk ( kNumEchoRpcs ) ;
CheckRpcSendOk ( kNumEcho1Rpcs , header_match_rpc_options ) ;
EXPECT_EQ ( kNumEchoRpcs , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 0 ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 0 ] - > backend_service2 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( kNumEcho1Rpcs , backends_ [ 1 ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 1 ] - > backend_service2 ( ) - > request_count ( ) ) ;
auto response_state = RouteConfigurationResponseState ( balancer_ . get ( ) ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
TEST_P ( LdsRdsTest , XdsRoutingHeadersMatchingSpecialHeaderContentType ) {
CreateAndStartBackends ( 2 ) ;
const char * kNewClusterName = " new_cluster " ;
const char * kNewEdsServiceName = " new_eds_service_name " ;
const size_t kNumEchoRpcs = 100 ;
// Populate new EDS resources.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } ,
} ) ;
EdsResourceArgs args1 ( {
{ " locality0 " , CreateEndpointsForBackends ( 1 , 2 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args1 , kNewEdsServiceName ) ) ;
// Populate new CDS resources.
Cluster new_cluster = default_cluster_ ;
new_cluster . set_name ( kNewClusterName ) ;
new_cluster . mutable_eds_cluster_config ( ) - > set_service_name (
kNewEdsServiceName ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( new_cluster ) ;
// Populating Route Configurations for LDS.
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_prefix ( " " ) ;
auto * header_matcher1 = route1 - > mutable_match ( ) - > add_headers ( ) ;
header_matcher1 - > set_name ( " content-type " ) ;
header_matcher1 - > set_exact_match ( " notapplication/grpc " ) ;
route1 - > mutable_route ( ) - > set_cluster ( kNewClusterName ) ;
auto * default_route = route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
default_route - > mutable_match ( ) - > set_prefix ( " " ) ;
auto * header_matcher2 = default_route - > mutable_match ( ) - > add_headers ( ) ;
header_matcher2 - > set_name ( " content-type " ) ;
header_matcher2 - > set_exact_match ( " application/grpc " ) ;
default_route - > mutable_route ( ) - > set_cluster ( kDefaultClusterName ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
// Make sure the backend is up.
WaitForAllBackends ( 0 , 1 ) ;
// Send RPCs.
CheckRpcSendOk ( kNumEchoRpcs ) ;
EXPECT_EQ ( kNumEchoRpcs , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ) ;
auto response_state = RouteConfigurationResponseState ( balancer_ . get ( ) ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
TEST_P ( LdsRdsTest , XdsRoutingHeadersMatchingSpecialCasesToIgnore ) {
CreateAndStartBackends ( 2 ) ;
const char * kNewCluster1Name = " new_cluster_1 " ;
const char * kNewEdsService1Name = " new_eds_service_name_1 " ;
const size_t kNumEchoRpcs = 100 ;
// Populate new EDS resources.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } ,
} ) ;
EdsResourceArgs args1 ( {
{ " locality0 " , CreateEndpointsForBackends ( 1 , 2 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args1 , kNewEdsService1Name ) ) ;
// 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 ) ;
// Populating Route Configurations for LDS.
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_prefix ( " " ) ;
auto * header_matcher1 = route1 - > mutable_match ( ) - > add_headers ( ) ;
header_matcher1 - > set_name ( " grpc-foo-bin " ) ;
header_matcher1 - > set_present_match ( true ) ;
route1 - > mutable_route ( ) - > set_cluster ( kNewCluster1Name ) ;
auto * default_route = route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
default_route - > mutable_match ( ) - > set_prefix ( " " ) ;
default_route - > mutable_route ( ) - > set_cluster ( kDefaultClusterName ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
// Send headers which will mismatch each route
std : : vector < std : : pair < std : : string , std : : string > > metadata = {
{ " grpc-foo-bin " , " grpc-foo-bin " } ,
} ;
WaitForAllBackends ( 0 , 1 ) ;
CheckRpcSendOk ( kNumEchoRpcs , RpcOptions ( ) . set_metadata ( metadata ) ) ;
// Verify that only the default backend got RPCs since all previous routes
// were mismatched.
EXPECT_EQ ( kNumEchoRpcs , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ) ;
auto response_state = RouteConfigurationResponseState ( balancer_ . get ( ) ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
TEST_P ( LdsRdsTest , XdsRoutingRuntimeFractionMatching ) {
CreateAndStartBackends ( 2 ) ;
const char * kNewClusterName = " new_cluster " ;
const char * kNewEdsServiceName = " new_eds_service_name " ;
const double kErrorTolerance = 0.05 ;
const size_t kRouteMatchNumerator = 25 ;
const double kRouteMatchPercent =
static_cast < double > ( kRouteMatchNumerator ) / 100 ;
const size_t kNumRpcs =
ComputeIdealNumRpcs ( kRouteMatchPercent , kErrorTolerance ) ;
// Populate new EDS resources.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } ,
} ) ;
EdsResourceArgs args1 ( {
{ " locality0 " , CreateEndpointsForBackends ( 1 , 2 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args1 , kNewEdsServiceName ) ) ;
// Populate new CDS resources.
Cluster new_cluster = default_cluster_ ;
new_cluster . set_name ( kNewClusterName ) ;
new_cluster . mutable_eds_cluster_config ( ) - > set_service_name (
kNewEdsServiceName ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( new_cluster ) ;
// Populating Route Configurations for LDS.
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( )
- > mutable_runtime_fraction ( )
- > mutable_default_value ( )
- > set_numerator ( kRouteMatchNumerator ) ;
route1 - > mutable_route ( ) - > set_cluster ( kNewClusterName ) ;
auto * default_route = route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
default_route - > mutable_match ( ) - > set_prefix ( " " ) ;
default_route - > mutable_route ( ) - > set_cluster ( kDefaultClusterName ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
WaitForAllBackends ( 0 , 2 ) ;
CheckRpcSendOk ( kNumRpcs ) ;
const int default_backend_count =
backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ;
const int matched_backend_count =
backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ;
EXPECT_THAT ( static_cast < double > ( default_backend_count ) / kNumRpcs ,
: : testing : : DoubleNear ( 1 - kRouteMatchPercent , kErrorTolerance ) ) ;
EXPECT_THAT ( static_cast < double > ( matched_backend_count ) / kNumRpcs ,
: : testing : : DoubleNear ( kRouteMatchPercent , kErrorTolerance ) ) ;
auto response_state = RouteConfigurationResponseState ( balancer_ . get ( ) ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
TEST_P ( LdsRdsTest , XdsRoutingHeadersMatchingUnmatchCases ) {
CreateAndStartBackends ( 4 ) ;
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 char * kNewCluster3Name = " new_cluster_3 " ;
const char * kNewEdsService3Name = " new_eds_service_name_3 " ;
const size_t kNumEcho1Rpcs = 100 ;
const size_t kNumEchoRpcs = 5 ;
// Populate new EDS resources.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } ,
} ) ;
EdsResourceArgs args1 ( {
{ " locality0 " , CreateEndpointsForBackends ( 1 , 2 ) } ,
} ) ;
EdsResourceArgs args2 ( {
{ " locality0 " , CreateEndpointsForBackends ( 2 , 3 ) } ,
} ) ;
EdsResourceArgs args3 ( {
{ " locality0 " , CreateEndpointsForBackends ( 3 , 4 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args1 , kNewEdsService1Name ) ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args2 , kNewEdsService2Name ) ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args3 , kNewEdsService3Name ) ) ;
// 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 ) ;
Cluster new_cluster3 = default_cluster_ ;
new_cluster3 . set_name ( kNewCluster3Name ) ;
new_cluster3 . mutable_eds_cluster_config ( ) - > set_service_name (
kNewEdsService3Name ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( new_cluster3 ) ;
// Populating Route Configurations for LDS.
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_prefix ( " /grpc.testing.EchoTest1Service/ " ) ;
auto * header_matcher1 = route1 - > mutable_match ( ) - > add_headers ( ) ;
header_matcher1 - > set_name ( " header1 " ) ;
header_matcher1 - > set_exact_match ( " POST " ) ;
route1 - > mutable_route ( ) - > set_cluster ( kNewCluster1Name ) ;
auto route2 = route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
route2 - > mutable_match ( ) - > set_prefix ( " /grpc.testing.EchoTest1Service/ " ) ;
auto * header_matcher2 = route2 - > mutable_match ( ) - > add_headers ( ) ;
header_matcher2 - > set_name ( " header2 " ) ;
header_matcher2 - > mutable_range_match ( ) - > set_start ( 1 ) ;
header_matcher2 - > mutable_range_match ( ) - > set_end ( 1000 ) ;
route2 - > mutable_route ( ) - > set_cluster ( kNewCluster2Name ) ;
auto route3 = route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
route3 - > mutable_match ( ) - > set_prefix ( " /grpc.testing.EchoTest1Service/ " ) ;
auto * header_matcher3 = route3 - > mutable_match ( ) - > add_headers ( ) ;
header_matcher3 - > set_name ( " header3 " ) ;
header_matcher3 - > mutable_safe_regex_match ( ) - > set_regex ( " [a-z]* " ) ;
route3 - > mutable_route ( ) - > set_cluster ( kNewCluster3Name ) ;
auto * default_route = route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
default_route - > mutable_match ( ) - > set_prefix ( " " ) ;
default_route - > mutable_route ( ) - > set_cluster ( kDefaultClusterName ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
// Send headers which will mismatch each route
std : : vector < std : : pair < std : : string , std : : string > > metadata = {
{ " header1 " , " POST " } ,
{ " header2 " , " 1000 " } ,
{ " header3 " , " 123 " } ,
{ " header1 " , " GET " } ,
} ;
WaitForAllBackends ( 0 , 1 ) ;
CheckRpcSendOk ( kNumEchoRpcs , RpcOptions ( ) . set_metadata ( metadata ) ) ;
CheckRpcSendOk ( kNumEcho1Rpcs , RpcOptions ( )
. set_rpc_service ( SERVICE_ECHO1 )
. set_rpc_method ( METHOD_ECHO1 )
. set_metadata ( metadata ) ) ;
// Verify that only the default backend got RPCs since all previous routes
// were mismatched.
for ( size_t i = 1 ; i < 4 ; + + i ) {
EXPECT_EQ ( 0 , backends_ [ i ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ i ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ i ] - > backend_service2 ( ) - > request_count ( ) ) ;
}
EXPECT_EQ ( kNumEchoRpcs , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( kNumEcho1Rpcs , backends_ [ 0 ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 0 ] - > backend_service2 ( ) - > request_count ( ) ) ;
auto response_state = RouteConfigurationResponseState ( balancer_ . get ( ) ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
TEST_P ( LdsRdsTest , XdsRoutingChangeRoutesWithoutChangingClusters ) {
CreateAndStartBackends ( 2 ) ;
const char * kNewClusterName = " new_cluster " ;
const char * kNewEdsServiceName = " new_eds_service_name " ;
// Populate new EDS resources.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } ,
} ) ;
EdsResourceArgs args1 ( {
{ " locality0 " , CreateEndpointsForBackends ( 1 , 2 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args1 , kNewEdsServiceName ) ) ;
// Populate new CDS resources.
Cluster new_cluster = default_cluster_ ;
new_cluster . set_name ( kNewClusterName ) ;
new_cluster . mutable_eds_cluster_config ( ) - > set_service_name (
kNewEdsServiceName ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( new_cluster ) ;
// Populating Route Configurations for LDS.
RouteConfiguration route_config = default_route_config_ ;
auto * route1 = route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
route1 - > mutable_match ( ) - > set_prefix ( " /grpc.testing.EchoTest1Service/ " ) ;
route1 - > mutable_route ( ) - > set_cluster ( kNewClusterName ) ;
auto * default_route = route_config . mutable_virtual_hosts ( 0 ) - > add_routes ( ) ;
default_route - > mutable_match ( ) - > set_prefix ( " " ) ;
default_route - > mutable_route ( ) - > set_cluster ( kDefaultClusterName ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
// Make sure all backends are up and that requests for each RPC
// service go to the right backends.
WaitForBackend ( 0 , WaitForBackendOptions ( ) . set_reset_counters ( false ) ) ;
WaitForBackend ( 1 , WaitForBackendOptions ( ) . set_reset_counters ( false ) ,
RpcOptions ( ) . set_rpc_service ( SERVICE_ECHO1 ) ) ;
WaitForBackend ( 0 , WaitForBackendOptions ( ) . set_reset_counters ( false ) ,
RpcOptions ( ) . set_rpc_service ( SERVICE_ECHO2 ) ) ;
// Requests for services Echo and Echo2 should have gone to backend 0.
EXPECT_EQ ( 1 , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 0 ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 1 , backends_ [ 0 ] - > backend_service2 ( ) - > request_count ( ) ) ;
// Requests for service Echo1 should have gone to backend 1.
EXPECT_EQ ( 0 , backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 1 , backends_ [ 1 ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 1 ] - > backend_service2 ( ) - > request_count ( ) ) ;
// Now send an update that changes the first route to match a
// different RPC service, and wait for the client to make the change.
route1 - > mutable_match ( ) - > set_prefix ( " /grpc.testing.EchoTest2Service/ " ) ;
SetRouteConfiguration ( balancer_ . get ( ) , route_config ) ;
WaitForBackend ( 1 , WaitForBackendOptions ( ) ,
RpcOptions ( ) . set_rpc_service ( SERVICE_ECHO2 ) ) ;
// Now repeat the earlier test, making sure all traffic goes to the
// right place.
WaitForBackend ( 0 , WaitForBackendOptions ( ) . set_reset_counters ( false ) ) ;
WaitForBackend ( 0 , WaitForBackendOptions ( ) . set_reset_counters ( false ) ,
RpcOptions ( ) . set_rpc_service ( SERVICE_ECHO1 ) ) ;
WaitForBackend ( 1 , WaitForBackendOptions ( ) . set_reset_counters ( false ) ,
RpcOptions ( ) . set_rpc_service ( SERVICE_ECHO2 ) ) ;
// Requests for services Echo and Echo1 should have gone to backend 0.
EXPECT_EQ ( 1 , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 1 , backends_ [ 0 ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 0 ] - > backend_service2 ( ) - > request_count ( ) ) ;
// Requests for service Echo2 should have gone to backend 1.
EXPECT_EQ ( 0 , backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 1 ] - > backend_service1 ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 1 , backends_ [ 1 ] - > backend_service2 ( ) - > request_count ( ) ) ;
}
// Test that we NACK unknown filter types in VirtualHost.
TEST_P ( LdsRdsTest , RejectsUnknownHttpFilterTypeInVirtualHost ) {
if ( GetParam ( ) . use_v2 ( ) ) return ; // Filters supported in v3 only.
RouteConfiguration route_config = default_route_config_ ;
auto * per_filter_config =
route_config . mutable_virtual_hosts ( 0 ) - > mutable_typed_per_filter_config ( ) ;
( * per_filter_config ) [ " unknown " ] . PackFrom ( Listener ( ) ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " no filter registered for config type "
" envoy.config.listener.v3.Listener " ) ) ;
}
// Test that we ignore optional unknown filter types in VirtualHost.
TEST_P ( LdsRdsTest , IgnoresOptionalUnknownHttpFilterTypeInVirtualHost ) {
CreateAndStartBackends ( 1 ) ;
if ( GetParam ( ) . use_v2 ( ) ) return ; // Filters supported in v3 only.
RouteConfiguration route_config = default_route_config_ ;
auto * per_filter_config =
route_config . mutable_virtual_hosts ( 0 ) - > mutable_typed_per_filter_config ( ) ;
: : envoy : : config : : route : : v3 : : FilterConfig filter_config ;
filter_config . mutable_config ( ) - > PackFrom ( Listener ( ) ) ;
filter_config . set_is_optional ( true ) ;
( * per_filter_config ) [ " unknown " ] . PackFrom ( filter_config ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
route_config ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForAllBackends ( ) ;
auto response_state = RouteConfigurationResponseState ( balancer_ . get ( ) ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
// Test that we NACK filters without configs in VirtualHost.
TEST_P ( LdsRdsTest , RejectsHttpFilterWithoutConfigInVirtualHost ) {
if ( GetParam ( ) . use_v2 ( ) ) return ; // Filters supported in v3 only.
RouteConfiguration route_config = default_route_config_ ;
auto * per_filter_config =
route_config . mutable_virtual_hosts ( 0 ) - > mutable_typed_per_filter_config ( ) ;
( * per_filter_config ) [ " unknown " ] ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" no filter config specified for filter name unknown " ) ) ;
}
// Test that we NACK filters without configs in FilterConfig in VirtualHost.
TEST_P ( LdsRdsTest , RejectsHttpFilterWithoutConfigInFilterConfigInVirtualHost ) {
if ( GetParam ( ) . use_v2 ( ) ) return ; // Filters supported in v3 only.
RouteConfiguration route_config = default_route_config_ ;
auto * per_filter_config =
route_config . mutable_virtual_hosts ( 0 ) - > mutable_typed_per_filter_config ( ) ;
( * per_filter_config ) [ " unknown " ] . PackFrom (
: : envoy : : config : : route : : v3 : : FilterConfig ( ) ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" no filter config specified for filter name unknown " ) ) ;
}
// Test that we ignore optional filters without configs in VirtualHost.
TEST_P ( LdsRdsTest , IgnoresOptionalHttpFilterWithoutConfigInVirtualHost ) {
if ( GetParam ( ) . use_v2 ( ) ) return ; // Filters supported in v3 only.
CreateAndStartBackends ( 1 ) ;
RouteConfiguration route_config = default_route_config_ ;
auto * per_filter_config =
route_config . mutable_virtual_hosts ( 0 ) - > mutable_typed_per_filter_config ( ) ;
: : envoy : : config : : route : : v3 : : FilterConfig filter_config ;
filter_config . set_is_optional ( true ) ;
( * per_filter_config ) [ " unknown " ] . PackFrom ( filter_config ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
route_config ) ;
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForAllBackends ( ) ;
auto response_state = RouteConfigurationResponseState ( balancer_ . get ( ) ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
// Test that we NACK unparseable filter types in VirtualHost.
TEST_P ( LdsRdsTest , RejectsUnparseableHttpFilterTypeInVirtualHost ) {
if ( GetParam ( ) . use_v2 ( ) ) return ; // Filters supported in v3 only.
RouteConfiguration route_config = default_route_config_ ;
auto * per_filter_config =
route_config . mutable_virtual_hosts ( 0 ) - > mutable_typed_per_filter_config ( ) ;
( * per_filter_config ) [ " unknown " ] . PackFrom (
envoy : : extensions : : filters : : http : : router : : v3 : : Router ( ) ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " router filter does not support config override " ) ) ;
}
// Test that we NACK unknown filter types in Route.
TEST_P ( LdsRdsTest , RejectsUnknownHttpFilterTypeInRoute ) {
if ( GetParam ( ) . use_v2 ( ) ) return ; // Filters supported in v3 only.
RouteConfiguration route_config = default_route_config_ ;
auto * per_filter_config = route_config . mutable_virtual_hosts ( 0 )
- > mutable_routes ( 0 )
- > mutable_typed_per_filter_config ( ) ;
( * per_filter_config ) [ " unknown " ] . PackFrom ( Listener ( ) ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " no filter registered for config type "
" envoy.config.listener.v3.Listener " ) ) ;
}
// Test that we ignore optional unknown filter types in Route.
TEST_P ( LdsRdsTest , IgnoresOptionalUnknownHttpFilterTypeInRoute ) {
if ( GetParam ( ) . use_v2 ( ) ) return ; // Filters supported in v3 only.
CreateAndStartBackends ( 1 ) ;
RouteConfiguration route_config = default_route_config_ ;
auto * per_filter_config = route_config . mutable_virtual_hosts ( 0 )
- > mutable_routes ( 0 )
- > mutable_typed_per_filter_config ( ) ;
: : envoy : : config : : route : : v3 : : FilterConfig filter_config ;
filter_config . mutable_config ( ) - > PackFrom ( Listener ( ) ) ;
filter_config . set_is_optional ( true ) ;
( * per_filter_config ) [ " unknown " ] . PackFrom ( filter_config ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
route_config ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForAllBackends ( ) ;
auto response_state = RouteConfigurationResponseState ( balancer_ . get ( ) ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
// Test that we NACK filters without configs in Route.
TEST_P ( LdsRdsTest , RejectsHttpFilterWithoutConfigInRoute ) {
if ( GetParam ( ) . use_v2 ( ) ) return ; // Filters supported in v3 only.
RouteConfiguration route_config = default_route_config_ ;
auto * per_filter_config = route_config . mutable_virtual_hosts ( 0 )
- > mutable_routes ( 0 )
- > mutable_typed_per_filter_config ( ) ;
( * per_filter_config ) [ " unknown " ] ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" no filter config specified for filter name unknown " ) ) ;
}
// Test that we NACK filters without configs in FilterConfig in Route.
TEST_P ( LdsRdsTest , RejectsHttpFilterWithoutConfigInFilterConfigInRoute ) {
if ( GetParam ( ) . use_v2 ( ) ) return ; // Filters supported in v3 only.
RouteConfiguration route_config = default_route_config_ ;
auto * per_filter_config = route_config . mutable_virtual_hosts ( 0 )
- > mutable_routes ( 0 )
- > mutable_typed_per_filter_config ( ) ;
( * per_filter_config ) [ " unknown " ] . PackFrom (
: : envoy : : config : : route : : v3 : : FilterConfig ( ) ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" no filter config specified for filter name unknown " ) ) ;
}
// Test that we ignore optional filters without configs in Route.
TEST_P ( LdsRdsTest , IgnoresOptionalHttpFilterWithoutConfigInRoute ) {
if ( GetParam ( ) . use_v2 ( ) ) return ; // Filters supported in v3 only.
CreateAndStartBackends ( 1 ) ;
RouteConfiguration route_config = default_route_config_ ;
auto * per_filter_config = route_config . mutable_virtual_hosts ( 0 )
- > mutable_routes ( 0 )
- > mutable_typed_per_filter_config ( ) ;
: : envoy : : config : : route : : v3 : : FilterConfig filter_config ;
filter_config . set_is_optional ( true ) ;
( * per_filter_config ) [ " unknown " ] . PackFrom ( filter_config ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
route_config ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForAllBackends ( ) ;
auto response_state = RouteConfigurationResponseState ( balancer_ . get ( ) ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
// Test that we NACK unparseable filter types in Route.
TEST_P ( LdsRdsTest , RejectsUnparseableHttpFilterTypeInRoute ) {
if ( GetParam ( ) . use_v2 ( ) ) return ; // Filters supported in v3 only.
RouteConfiguration route_config = default_route_config_ ;
auto * per_filter_config = route_config . mutable_virtual_hosts ( 0 )
- > mutable_routes ( 0 )
- > mutable_typed_per_filter_config ( ) ;
( * per_filter_config ) [ " unknown " ] . PackFrom (
envoy : : extensions : : filters : : http : : router : : v3 : : Router ( ) ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " router filter does not support config override " ) ) ;
}
// Test that we NACK unknown filter types in ClusterWeight.
TEST_P ( LdsRdsTest , RejectsUnknownHttpFilterTypeInClusterWeight ) {
if ( GetParam ( ) . use_v2 ( ) ) return ; // Filters supported in v3 only.
RouteConfiguration route_config = default_route_config_ ;
auto * cluster_weight = route_config . mutable_virtual_hosts ( 0 )
- > mutable_routes ( 0 )
- > mutable_route ( )
- > mutable_weighted_clusters ( )
- > add_clusters ( ) ;
cluster_weight - > set_name ( kDefaultClusterName ) ;
cluster_weight - > mutable_weight ( ) - > set_value ( 100 ) ;
auto * per_filter_config = cluster_weight - > mutable_typed_per_filter_config ( ) ;
( * per_filter_config ) [ " unknown " ] . PackFrom ( Listener ( ) ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " no filter registered for config type "
" envoy.config.listener.v3.Listener " ) ) ;
}
// Test that we ignore optional unknown filter types in ClusterWeight.
TEST_P ( LdsRdsTest , IgnoresOptionalUnknownHttpFilterTypeInClusterWeight ) {
if ( GetParam ( ) . use_v2 ( ) ) return ; // Filters supported in v3 only.
CreateAndStartBackends ( 1 ) ;
RouteConfiguration route_config = default_route_config_ ;
auto * cluster_weight = route_config . mutable_virtual_hosts ( 0 )
- > mutable_routes ( 0 )
- > mutable_route ( )
- > mutable_weighted_clusters ( )
- > add_clusters ( ) ;
cluster_weight - > set_name ( kDefaultClusterName ) ;
cluster_weight - > mutable_weight ( ) - > set_value ( 100 ) ;
auto * per_filter_config = cluster_weight - > mutable_typed_per_filter_config ( ) ;
: : envoy : : config : : route : : v3 : : FilterConfig filter_config ;
filter_config . mutable_config ( ) - > PackFrom ( Listener ( ) ) ;
filter_config . set_is_optional ( true ) ;
( * per_filter_config ) [ " unknown " ] . PackFrom ( filter_config ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
route_config ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForAllBackends ( ) ;
auto response_state = RouteConfigurationResponseState ( balancer_ . get ( ) ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
// Test that we NACK filters without configs in ClusterWeight.
TEST_P ( LdsRdsTest , RejectsHttpFilterWithoutConfigInClusterWeight ) {
if ( GetParam ( ) . use_v2 ( ) ) return ; // Filters supported in v3 only.
RouteConfiguration route_config = default_route_config_ ;
auto * cluster_weight = route_config . mutable_virtual_hosts ( 0 )
- > mutable_routes ( 0 )
- > mutable_route ( )
- > mutable_weighted_clusters ( )
- > add_clusters ( ) ;
cluster_weight - > set_name ( kDefaultClusterName ) ;
cluster_weight - > mutable_weight ( ) - > set_value ( 100 ) ;
auto * per_filter_config = cluster_weight - > mutable_typed_per_filter_config ( ) ;
( * per_filter_config ) [ " unknown " ] ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" no filter config specified for filter name unknown " ) ) ;
}
// Test that we NACK filters without configs in FilterConfig in ClusterWeight.
TEST_P ( LdsRdsTest ,
RejectsHttpFilterWithoutConfigInFilterConfigInClusterWeight ) {
if ( GetParam ( ) . use_v2 ( ) ) return ; // Filters supported in v3 only.
RouteConfiguration route_config = default_route_config_ ;
auto * cluster_weight = route_config . mutable_virtual_hosts ( 0 )
- > mutable_routes ( 0 )
- > mutable_route ( )
- > mutable_weighted_clusters ( )
- > add_clusters ( ) ;
cluster_weight - > set_name ( kDefaultClusterName ) ;
cluster_weight - > mutable_weight ( ) - > set_value ( 100 ) ;
auto * per_filter_config = cluster_weight - > mutable_typed_per_filter_config ( ) ;
( * per_filter_config ) [ " unknown " ] . PackFrom (
: : envoy : : config : : route : : v3 : : FilterConfig ( ) ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" no filter config specified for filter name unknown " ) ) ;
}
// Test that we ignore optional filters without configs in ClusterWeight.
TEST_P ( LdsRdsTest , IgnoresOptionalHttpFilterWithoutConfigInClusterWeight ) {
if ( GetParam ( ) . use_v2 ( ) ) return ; // Filters supported in v3 only.
CreateAndStartBackends ( 1 ) ;
RouteConfiguration route_config = default_route_config_ ;
auto * cluster_weight = route_config . mutable_virtual_hosts ( 0 )
- > mutable_routes ( 0 )
- > mutable_route ( )
- > mutable_weighted_clusters ( )
- > add_clusters ( ) ;
cluster_weight - > set_name ( kDefaultClusterName ) ;
cluster_weight - > mutable_weight ( ) - > set_value ( 100 ) ;
auto * per_filter_config = cluster_weight - > mutable_typed_per_filter_config ( ) ;
: : envoy : : config : : route : : v3 : : FilterConfig filter_config ;
filter_config . set_is_optional ( true ) ;
( * per_filter_config ) [ " unknown " ] . PackFrom ( filter_config ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
route_config ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForAllBackends ( ) ;
auto response_state = RouteConfigurationResponseState ( balancer_ . get ( ) ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
// Test that we NACK unparseable filter types in ClusterWeight.
TEST_P ( LdsRdsTest , RejectsUnparseableHttpFilterTypeInClusterWeight ) {
if ( GetParam ( ) . use_v2 ( ) ) return ; // Filters supported in v3 only.
RouteConfiguration route_config = default_route_config_ ;
auto * cluster_weight = route_config . mutable_virtual_hosts ( 0 )
- > mutable_routes ( 0 )
- > mutable_route ( )
- > mutable_weighted_clusters ( )
- > add_clusters ( ) ;
cluster_weight - > set_name ( kDefaultClusterName ) ;
cluster_weight - > mutable_weight ( ) - > set_value ( 100 ) ;
auto * per_filter_config = cluster_weight - > mutable_typed_per_filter_config ( ) ;
( * per_filter_config ) [ " unknown " ] . PackFrom (
envoy : : extensions : : filters : : http : : router : : v3 : : Router ( ) ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
route_config ) ;
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " router filter does not support config override " ) ) ;
}
class CdsTest : public XdsEnd2endTest {
protected :
void SetUp ( ) override {
logical_dns_cluster_resolver_response_generator_ =
grpc_core : : MakeRefCounted < grpc_core : : FakeResolverResponseGenerator > ( ) ;
InitClient ( ) ;
ChannelArguments args ;
args . SetPointerWithVtable (
GRPC_ARG_XDS_LOGICAL_DNS_CLUSTER_FAKE_RESOLVER_RESPONSE_GENERATOR ,
logical_dns_cluster_resolver_response_generator_ . get ( ) ,
& grpc_core : : FakeResolverResponseGenerator : : kChannelArgPointerVtable ) ;
ResetStub ( /*failover_timeout_ms=*/ 0 , & args ) ;
}
grpc_core : : ServerAddressList CreateAddressListFromPortList (
const std : : vector < int > & ports ) {
grpc_core : : ServerAddressList addresses ;
for ( int port : ports ) {
absl : : StatusOr < grpc_core : : URI > lb_uri = grpc_core : : URI : : Parse (
absl : : StrCat ( ipv6_only_ ? " ipv6:[::1]: " : " ipv4:127.0.0.1: " , port ) ) ;
GPR_ASSERT ( lb_uri . ok ( ) ) ;
grpc_resolved_address address ;
GPR_ASSERT ( grpc_parse_uri ( * lb_uri , & address ) ) ;
addresses . emplace_back ( address . addr , address . len , nullptr ) ;
}
return addresses ;
}
std : : string CreateMetadataValueThatHashesToBackendPort ( int port ) {
return absl : : StrCat ( ipv6_only_ ? " [::1] " : " 127.0.0.1 " , " : " , port , " _0 " ) ;
}
std : : string CreateMetadataValueThatHashesToBackend ( int index ) {
return CreateMetadataValueThatHashesToBackendPort ( backends_ [ index ] - > port ( ) ) ;
}
grpc_core : : RefCountedPtr < grpc_core : : FakeResolverResponseGenerator >
logical_dns_cluster_resolver_response_generator_ ;
} ;
// Tests that CDS client should send an ACK upon correct CDS response.
TEST_P ( CdsTest , Vanilla ) {
( void ) SendRpc ( ) ;
auto response_state = balancer_ - > ads_service ( ) - > cds_response_state ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
TEST_P ( CdsTest , LogicalDNSClusterType ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
CreateAndStartBackends ( 1 ) ;
// Create Logical DNS Cluster
auto cluster = default_cluster_ ;
cluster . set_type ( Cluster : : LOGICAL_DNS ) ;
auto * address = cluster . mutable_load_assignment ( )
- > add_endpoints ( )
- > add_lb_endpoints ( )
- > mutable_endpoint ( )
- > mutable_address ( )
- > mutable_socket_address ( ) ;
address - > set_address ( kServerName ) ;
address - > set_port_value ( 443 ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
// Set Logical DNS result
{
grpc_core : : ExecCtx exec_ctx ;
grpc_core : : Resolver : : Result result ;
result . addresses = CreateAddressListFromPortList ( GetBackendPorts ( ) ) ;
logical_dns_cluster_resolver_response_generator_ - > SetResponse (
std : : move ( result ) ) ;
}
// RPCs should succeed.
CheckRpcSendOk ( ) ;
}
TEST_P ( CdsTest , LogicalDNSClusterTypeMissingLoadAssignment ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
// Create Logical DNS Cluster
auto cluster = default_cluster_ ;
cluster . set_type ( Cluster : : LOGICAL_DNS ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" load_assignment not present for LOGICAL_DNS cluster " ) ) ;
}
TEST_P ( CdsTest , LogicalDNSClusterTypeMissingLocalities ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
// Create Logical DNS Cluster
auto cluster = default_cluster_ ;
cluster . set_type ( Cluster : : LOGICAL_DNS ) ;
cluster . mutable_load_assignment ( ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " load_assignment for LOGICAL_DNS cluster must have "
" exactly one locality, found 0 " ) ) ;
}
TEST_P ( CdsTest , LogicalDNSClusterTypeMultipleLocalities ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
// Create Logical DNS Cluster
auto cluster = default_cluster_ ;
cluster . set_type ( Cluster : : LOGICAL_DNS ) ;
auto * load_assignment = cluster . mutable_load_assignment ( ) ;
load_assignment - > add_endpoints ( ) ;
load_assignment - > add_endpoints ( ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " load_assignment for LOGICAL_DNS cluster must have "
" exactly one locality, found 2 " ) ) ;
}
TEST_P ( CdsTest , LogicalDNSClusterTypeMissingEndpoints ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
// Create Logical DNS Cluster
auto cluster = default_cluster_ ;
cluster . set_type ( Cluster : : LOGICAL_DNS ) ;
cluster . mutable_load_assignment ( ) - > add_endpoints ( ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" locality for LOGICAL_DNS cluster must have exactly one "
" endpoint, found 0 " ) ) ;
}
TEST_P ( CdsTest , LogicalDNSClusterTypeMultipleEndpoints ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
// Create Logical DNS Cluster
auto cluster = default_cluster_ ;
cluster . set_type ( Cluster : : LOGICAL_DNS ) ;
auto * locality = cluster . mutable_load_assignment ( ) - > add_endpoints ( ) ;
locality - > add_lb_endpoints ( ) ;
locality - > add_lb_endpoints ( ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" locality for LOGICAL_DNS cluster must have exactly one "
" endpoint, found 2 " ) ) ;
}
TEST_P ( CdsTest , LogicalDNSClusterTypeEmptyEndpoint ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
// Create Logical DNS Cluster
auto cluster = default_cluster_ ;
cluster . set_type ( Cluster : : LOGICAL_DNS ) ;
cluster . mutable_load_assignment ( ) - > add_endpoints ( ) - > add_lb_endpoints ( ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " LbEndpoint endpoint field not set " ) ) ;
}
TEST_P ( CdsTest , LogicalDNSClusterTypeEndpointMissingAddress ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
// Create Logical DNS Cluster
auto cluster = default_cluster_ ;
cluster . set_type ( Cluster : : LOGICAL_DNS ) ;
cluster . mutable_load_assignment ( )
- > add_endpoints ( )
- > add_lb_endpoints ( )
- > mutable_endpoint ( ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " Endpoint address field not set " ) ) ;
}
TEST_P ( CdsTest , LogicalDNSClusterTypeAddressMissingSocketAddress ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
// Create Logical DNS Cluster
auto cluster = default_cluster_ ;
cluster . set_type ( Cluster : : LOGICAL_DNS ) ;
cluster . mutable_load_assignment ( )
- > add_endpoints ( )
- > add_lb_endpoints ( )
- > mutable_endpoint ( )
- > mutable_address ( ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " Address socket_address field not set " ) ) ;
}
TEST_P ( CdsTest , LogicalDNSClusterTypeSocketAddressHasResolverName ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
// Create Logical DNS Cluster
auto cluster = default_cluster_ ;
cluster . set_type ( Cluster : : LOGICAL_DNS ) ;
cluster . mutable_load_assignment ( )
- > add_endpoints ( )
- > add_lb_endpoints ( )
- > mutable_endpoint ( )
- > mutable_address ( )
- > mutable_socket_address ( )
- > set_resolver_name ( " foo " ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " LOGICAL_DNS clusters must NOT have a "
" custom resolver name set " ) ) ;
}
TEST_P ( CdsTest , LogicalDNSClusterTypeSocketAddressMissingAddress ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
// Create Logical DNS Cluster
auto cluster = default_cluster_ ;
cluster . set_type ( Cluster : : LOGICAL_DNS ) ;
cluster . mutable_load_assignment ( )
- > add_endpoints ( )
- > add_lb_endpoints ( )
- > mutable_endpoint ( )
- > mutable_address ( )
- > mutable_socket_address ( ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " SocketAddress address field not set " ) ) ;
}
TEST_P ( CdsTest , LogicalDNSClusterTypeSocketAddressMissingPort ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
// Create Logical DNS Cluster
auto cluster = default_cluster_ ;
cluster . set_type ( Cluster : : LOGICAL_DNS ) ;
cluster . mutable_load_assignment ( )
- > add_endpoints ( )
- > add_lb_endpoints ( )
- > mutable_endpoint ( )
- > mutable_address ( )
- > mutable_socket_address ( )
- > set_address ( kServerName ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " SocketAddress port_value field not set " ) ) ;
}
TEST_P ( CdsTest , AggregateClusterType ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
CreateAndStartBackends ( 2 ) ;
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 " ;
// Populate new EDS resources.
EdsResourceArgs args1 ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } ,
} ) ;
EdsResourceArgs args2 ( {
{ " locality0 " , CreateEndpointsForBackends ( 1 , 2 ) } ,
} ) ;
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 ) ;
// Create Aggregate 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 ( kNewCluster1Name ) ;
cluster_config . add_clusters ( kNewCluster2Name ) ;
custom_cluster - > mutable_typed_config ( ) - > PackFrom ( cluster_config ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
// Wait for traffic to go to backend 0.
WaitForBackend ( 0 ) ;
// Shutdown backend 0 and wait for all traffic to go to backend 1.
ShutdownBackend ( 0 ) ;
WaitForBackend ( 1 , WaitForBackendOptions ( ) . set_allow_failures ( true ) ) ;
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.
StartBackend ( 0 ) ;
WaitForBackend ( 0 ) ;
}
TEST_P ( CdsTest , AggregateClusterDiamondDependency ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
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 ( 0 ) ;
// Shutdown backend 0 and wait for all traffic to go to backend 1.
ShutdownBackend ( 0 ) ;
WaitForBackend ( 1 , WaitForBackendOptions ( ) . set_allow_failures ( true ) ) ;
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.
StartBackend ( 0 ) ;
WaitForBackend ( 0 ) ;
}
TEST_P ( CdsTest , AggregateClusterFallBackFromRingHashAtStartup ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
CreateAndStartBackends ( 2 ) ;
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 " ;
// Populate new EDS resources.
EdsResourceArgs args1 ( {
{ " locality0 " , { MakeNonExistantEndpoint ( ) , MakeNonExistantEndpoint ( ) } } ,
} ) ;
EdsResourceArgs args2 ( {
{ " locality0 " , CreateEndpointsForBackends ( ) } ,
} ) ;
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 ) ;
// Create Aggregate Cluster
auto cluster = default_cluster_ ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
CustomClusterType * custom_cluster = cluster . mutable_cluster_type ( ) ;
custom_cluster - > set_name ( " envoy.clusters.aggregate " ) ;
ClusterConfig cluster_config ;
cluster_config . add_clusters ( kNewCluster1Name ) ;
cluster_config . add_clusters ( kNewCluster2Name ) ;
custom_cluster - > mutable_typed_config ( ) - > PackFrom ( cluster_config ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
// Set up route with channel id hashing
auto new_route_config = default_route_config_ ;
auto * route = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * hash_policy = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy - > mutable_filter_state ( ) - > set_key ( " io.grpc.channel_id " ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
new_route_config ) ;
// Verifying that we are using ring hash as only 1 endpoint is receiving all
// the traffic.
CheckRpcSendOk ( 100 ) ;
bool found = false ;
for ( size_t i = 0 ; i < backends_ . size ( ) ; + + i ) {
if ( backends_ [ i ] - > backend_service ( ) - > request_count ( ) > 0 ) {
EXPECT_EQ ( backends_ [ i ] - > backend_service ( ) - > request_count ( ) , 100 )
< < " backend " < < i ;
EXPECT_FALSE ( found ) < < " backend " < < i ;
found = true ;
}
}
EXPECT_TRUE ( found ) ;
}
TEST_P ( CdsTest , AggregateClusterFallBackFromRingHashToLogicalDnsAtStartup ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
CreateAndStartBackends ( 1 ) ;
const char * kEdsClusterName = " eds_cluster " ;
const char * kLogicalDNSClusterName = " logical_dns_cluster " ;
// Populate EDS resource.
EdsResourceArgs args ( {
{ " locality0 " ,
{ MakeNonExistantEndpoint ( ) , MakeNonExistantEndpoint ( ) } ,
kDefaultLocalityWeight ,
0 } ,
{ " locality1 " ,
{ MakeNonExistantEndpoint ( ) , MakeNonExistantEndpoint ( ) } ,
kDefaultLocalityWeight ,
1 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Populate new CDS resources.
Cluster eds_cluster = default_cluster_ ;
eds_cluster . set_name ( kEdsClusterName ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( eds_cluster ) ;
// Populate LOGICAL_DNS cluster.
auto logical_dns_cluster = default_cluster_ ;
logical_dns_cluster . set_name ( kLogicalDNSClusterName ) ;
logical_dns_cluster . set_type ( Cluster : : LOGICAL_DNS ) ;
auto * address = logical_dns_cluster . mutable_load_assignment ( )
- > add_endpoints ( )
- > add_lb_endpoints ( )
- > mutable_endpoint ( )
- > mutable_address ( )
- > mutable_socket_address ( ) ;
address - > set_address ( kServerName ) ;
address - > set_port_value ( 443 ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( logical_dns_cluster ) ;
// Create Aggregate Cluster
auto cluster = default_cluster_ ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
CustomClusterType * custom_cluster = cluster . mutable_cluster_type ( ) ;
custom_cluster - > set_name ( " envoy.clusters.aggregate " ) ;
ClusterConfig cluster_config ;
cluster_config . add_clusters ( kEdsClusterName ) ;
cluster_config . add_clusters ( kLogicalDNSClusterName ) ;
custom_cluster - > mutable_typed_config ( ) - > PackFrom ( cluster_config ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
// Set up route with channel id hashing
auto new_route_config = default_route_config_ ;
auto * route = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * hash_policy = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy - > mutable_filter_state ( ) - > set_key ( " io.grpc.channel_id " ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
new_route_config ) ;
// Set Logical DNS result
{
grpc_core : : ExecCtx exec_ctx ;
grpc_core : : Resolver : : Result result ;
result . addresses = CreateAddressListFromPortList ( GetBackendPorts ( ) ) ;
logical_dns_cluster_resolver_response_generator_ - > SetResponse (
std : : move ( result ) ) ;
}
// Inject connection delay to make this act more realistically.
ConnectionDelayInjector delay_injector (
grpc_core : : Duration : : Milliseconds ( 500 ) * grpc_test_slowdown_factor ( ) ) ;
// Send RPC. Need the timeout to be long enough to account for the
// subchannel connection delays.
CheckRpcSendOk ( 1 , RpcOptions ( ) . set_timeout_ms ( 3500 ) ) ;
}
// 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
// switch back to P0, deactivating P1.
// 3. P0 then goes back to TRANSIENT_FAILURE, and we reactivate P1.
// The bug caused us to fail to choose P1 even though it is in state
// CONNECTING (because the failover timer was not running), so we
// incorrectly failed the RPCs.
TEST_P ( CdsTest , AggregateClusterFallBackWithConnectivityChurn ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
CreateAndStartBackends ( 2 ) ;
const char * kClusterName1 = " cluster1 " ;
const char * kClusterName2 = " cluster2 " ;
const char * kEdsServiceName2 = " eds_service_name2 " ;
// Populate EDS resources.
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
args = EdsResourceArgs ( { { " locality1 " , CreateEndpointsForBackends ( 1 , 2 ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args , kEdsServiceName2 ) ) ;
// Populate new CDS resources.
Cluster cluster1 = default_cluster_ ;
cluster1 . set_name ( kClusterName1 ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster1 ) ;
Cluster cluster2 = default_cluster_ ;
cluster2 . set_name ( kClusterName2 ) ;
cluster2 . mutable_eds_cluster_config ( ) - > set_service_name ( kEdsServiceName2 ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster2 ) ;
// Create Aggregate 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 ( kClusterName1 ) ;
cluster_config . add_clusters ( kClusterName2 ) ;
custom_cluster - > mutable_typed_config ( ) - > PackFrom ( cluster_config ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
// This class injects itself into all TCP connection attempts made
// against iomgr. It intercepts the attempts for the P0 and P1
// backends and allows them to proceed as desired to simulate the case
// being tested.
class ConnectionInjector : public ConnectionAttemptInjector {
public :
ConnectionInjector ( int p0_port , int p1_port )
: p0_port_ ( p0_port ) , p1_port_ ( p1_port ) { }
void HandleConnection ( grpc_closure * closure , grpc_endpoint * * ep ,
grpc_pollset_set * interested_parties ,
const grpc_channel_args * channel_args ,
const grpc_resolved_address * addr ,
grpc_core : : Timestamp deadline ) override {
{
grpc_core : : MutexLock lock ( & mu_ ) ;
const int port = grpc_sockaddr_get_port ( addr ) ;
gpr_log ( GPR_INFO , " ==> HandleConnection(): state_=%d, port=%d " , state_ ,
port ) ;
switch ( state_ ) {
case kInit :
// Make P0 report TF, which should trigger us to try to connect to
// P1.
if ( port = = p0_port_ ) {
gpr_log ( GPR_INFO , " *** INJECTING FAILURE FOR P0 ENDPOINT " ) ;
grpc_core : : ExecCtx : : Run ( DEBUG_LOCATION , closure ,
GRPC_ERROR_CREATE_FROM_STATIC_STRING (
" injected connection failure " ) ) ;
state_ = kP0Failed ;
return ;
}
break ;
case kP0Failed :
// Hold connection attempt to P1 so that it stays in CONNECTING.
if ( port = = p1_port_ ) {
gpr_log ( GPR_INFO ,
" *** DELAYING CONNECTION ATTEMPT FOR P1 ENDPOINT " ) ;
queued_p1_attempt_ = absl : : make_unique < QueuedAttempt > (
closure , ep , interested_parties , channel_args , addr ,
deadline ) ;
state_ = kDone ;
return ;
}
break ;
case kDone :
// P0 should attempt reconnection. Log it to make the test
// easier to debug, but allow it to complete, so that the
// priority policy deactivates P1.
if ( port = = p0_port_ ) {
gpr_log ( GPR_INFO ,
" *** INTERCEPTING CONNECTION ATTEMPT FOR P0 ENDPOINT " ) ;
}
break ;
}
}
AttemptConnection ( closure , ep , interested_parties , channel_args , addr ,
deadline ) ;
}
// Invoked by the test when the RPC to the P0 backend has succeeded
// and it's ready to allow the P1 connection attempt to proceed.
void CompletePriority1Connection ( ) {
grpc_core : : ExecCtx exec_ctx ;
std : : unique_ptr < QueuedAttempt > attempt ;
{
grpc_core : : MutexLock lock ( & mu_ ) ;
GPR_ASSERT ( state_ = = kDone ) ;
attempt = std : : move ( queued_p1_attempt_ ) ;
}
attempt - > Resume ( ) ;
}
private :
const int p0_port_ ;
const int p1_port_ ;
grpc_core : : Mutex mu_ ;
enum {
kInit ,
kP0Failed ,
kDone ,
} state_ ABSL_GUARDED_BY ( mu_ ) = kInit ;
std : : unique_ptr < QueuedAttempt > queued_p1_attempt_ ABSL_GUARDED_BY ( mu_ ) ;
} ;
ConnectionInjector connection_attempt_injector ( backends_ [ 0 ] - > port ( ) ,
backends_ [ 1 ] - > port ( ) ) ;
// Wait for P0 backend.
// Increase timeout to account for subchannel connection delays.
WaitForBackend ( 0 , WaitForBackendOptions ( ) , RpcOptions ( ) . set_timeout_ms ( 2000 ) ) ;
// Bring down the P0 backend.
ShutdownBackend ( 0 ) ;
// Allow the connection attempt to the P1 backend to resume.
connection_attempt_injector . CompletePriority1Connection ( ) ;
// Wait for P1 backend to start getting traffic.
WaitForBackend ( 1 ) ;
}
TEST_P ( CdsTest , AggregateClusterEdsToLogicalDns ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
CreateAndStartBackends ( 2 ) ;
const char * kNewCluster1Name = " new_cluster_1 " ;
const char * kNewEdsService1Name = " new_eds_service_name_1 " ;
const char * kLogicalDNSClusterName = " logical_dns_cluster " ;
// Populate new EDS resources.
EdsResourceArgs args1 ( { { " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args1 , kNewEdsService1Name ) ) ;
// 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 ) ;
// Create Logical DNS Cluster
auto logical_dns_cluster = default_cluster_ ;
logical_dns_cluster . set_name ( kLogicalDNSClusterName ) ;
logical_dns_cluster . set_type ( Cluster : : LOGICAL_DNS ) ;
auto * address = logical_dns_cluster . mutable_load_assignment ( )
- > add_endpoints ( )
- > add_lb_endpoints ( )
- > mutable_endpoint ( )
- > mutable_address ( )
- > mutable_socket_address ( ) ;
address - > set_address ( kServerName ) ;
address - > set_port_value ( 443 ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( logical_dns_cluster ) ;
// Create Aggregate 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 ( kNewCluster1Name ) ;
cluster_config . add_clusters ( kLogicalDNSClusterName ) ;
custom_cluster - > mutable_typed_config ( ) - > PackFrom ( cluster_config ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
// Set Logical DNS result
{
grpc_core : : ExecCtx exec_ctx ;
grpc_core : : Resolver : : Result result ;
result . addresses = CreateAddressListFromPortList ( GetBackendPorts ( 1 , 2 ) ) ;
logical_dns_cluster_resolver_response_generator_ - > SetResponse (
std : : move ( result ) ) ;
}
// Wait for traffic to go to backend 0.
WaitForBackend ( 0 ) ;
// Shutdown backend 0 and wait for all traffic to go to backend 1.
ShutdownBackend ( 0 ) ;
WaitForBackend ( 1 , WaitForBackendOptions ( ) . set_allow_failures ( true ) ) ;
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.
StartBackend ( 0 ) ;
WaitForBackend ( 0 ) ;
}
TEST_P ( CdsTest , AggregateClusterLogicalDnsToEds ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
CreateAndStartBackends ( 2 ) ;
const char * kNewCluster2Name = " new_cluster_2 " ;
const char * kNewEdsService2Name = " new_eds_service_name_2 " ;
const char * kLogicalDNSClusterName = " logical_dns_cluster " ;
// Populate new EDS resources.
EdsResourceArgs args2 ( {
{ " locality0 " , CreateEndpointsForBackends ( 1 , 2 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args2 , kNewEdsService2Name ) ) ;
// Populate new CDS resources.
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 ) ;
// Create Logical DNS Cluster
auto logical_dns_cluster = default_cluster_ ;
logical_dns_cluster . set_name ( kLogicalDNSClusterName ) ;
logical_dns_cluster . set_type ( Cluster : : LOGICAL_DNS ) ;
auto * address = logical_dns_cluster . mutable_load_assignment ( )
- > add_endpoints ( )
- > add_lb_endpoints ( )
- > mutable_endpoint ( )
- > mutable_address ( )
- > mutable_socket_address ( ) ;
address - > set_address ( kServerName ) ;
address - > set_port_value ( 443 ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( logical_dns_cluster ) ;
// Create Aggregate 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 ( kLogicalDNSClusterName ) ;
cluster_config . add_clusters ( kNewCluster2Name ) ;
custom_cluster - > mutable_typed_config ( ) - > PackFrom ( cluster_config ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
// Set Logical DNS result
{
grpc_core : : ExecCtx exec_ctx ;
grpc_core : : Resolver : : Result result ;
result . addresses = CreateAddressListFromPortList ( GetBackendPorts ( 0 , 1 ) ) ;
logical_dns_cluster_resolver_response_generator_ - > SetResponse (
std : : move ( result ) ) ;
}
// Wait for traffic to go to backend 0.
WaitForBackend ( 0 ) ;
// Shutdown backend 0 and wait for all traffic to go to backend 1.
ShutdownBackend ( 0 ) ;
WaitForBackend ( 1 , WaitForBackendOptions ( ) . set_allow_failures ( true ) ) ;
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.
StartBackend ( 0 ) ;
WaitForBackend ( 0 ) ;
}
// This test covers a bug seen in the wild where the
// xds_cluster_resolver policy's code to reuse child policy names did
// not correctly handle the case where the LOGICAL_DNS priority failed,
// thus returning a priority with no localities. This caused the child
// name to be reused incorrectly, which triggered an assertion failure
// in the xds_cluster_impl policy caused by changing its cluster name.
TEST_P ( CdsTest , AggregateClusterReconfigEdsWhileLogicalDnsChildFails ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
CreateAndStartBackends ( 2 ) ;
const char * kNewCluster1Name = " new_cluster_1 " ;
const char * kNewEdsService1Name = " new_eds_service_name_1 " ;
const char * kLogicalDNSClusterName = " logical_dns_cluster " ;
// Populate EDS resource with all unreachable endpoints.
// - Priority 0: locality0
// - Priority 1: locality1, locality2
EdsResourceArgs args1 ( {
{ " locality0 " , { MakeNonExistantEndpoint ( ) } , kDefaultLocalityWeight , 0 } ,
{ " locality1 " , { MakeNonExistantEndpoint ( ) } , kDefaultLocalityWeight , 1 } ,
{ " locality2 " , { MakeNonExistantEndpoint ( ) } , kDefaultLocalityWeight , 1 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args1 , kNewEdsService1Name ) ) ;
// 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 ) ;
// Create Logical DNS Cluster
auto logical_dns_cluster = default_cluster_ ;
logical_dns_cluster . set_name ( kLogicalDNSClusterName ) ;
logical_dns_cluster . set_type ( Cluster : : LOGICAL_DNS ) ;
auto * address = logical_dns_cluster . mutable_load_assignment ( )
- > add_endpoints ( )
- > add_lb_endpoints ( )
- > mutable_endpoint ( )
- > mutable_address ( )
- > mutable_socket_address ( ) ;
address - > set_address ( kServerName ) ;
address - > set_port_value ( 443 ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( logical_dns_cluster ) ;
// Create Aggregate 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 ( kNewCluster1Name ) ;
cluster_config . add_clusters ( kLogicalDNSClusterName ) ;
custom_cluster - > mutable_typed_config ( ) - > PackFrom ( cluster_config ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
// Set Logical DNS result
{
grpc_core : : ExecCtx exec_ctx ;
grpc_core : : Resolver : : Result result ;
result . addresses = absl : : UnavailableError ( " injected error " ) ;
logical_dns_cluster_resolver_response_generator_ - > SetResponse (
std : : move ( result ) ) ;
}
// When an RPC fails, we know the channel has seen the update.
CheckRpcSendFailure ( ) ;
// Send an EDS update that moves locality1 to priority 0.
args1 = EdsResourceArgs ( {
{ " locality1 " , CreateEndpointsForBackends ( 0 , 1 ) , kDefaultLocalityWeight ,
0 } ,
{ " locality2 " , CreateEndpointsForBackends ( 1 , 2 ) , kDefaultLocalityWeight ,
1 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args1 , kNewEdsService1Name ) ) ;
WaitForBackend ( 0 , WaitForBackendOptions ( ) . set_allow_failures ( true ) ) ;
}
TEST_P ( CdsTest , AggregateClusterMultipleClustersWithSameLocalities ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
CreateAndStartBackends ( 2 ) ;
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 " ;
// Populate EDS resource for cluster 1 with unreachable endpoint.
EdsResourceArgs args1 ( { { " locality0 " , { MakeNonExistantEndpoint ( ) } } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args1 , kNewEdsServiceName1 ) ) ;
// Populate CDS resource for cluster 1.
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 ) ;
// Populate EDS resource for cluster 2.
args1 = EdsResourceArgs ( { { " locality1 " , CreateEndpointsForBackends ( 0 , 1 ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args1 , kNewEdsServiceName2 ) ) ;
// Populate CDS resource for cluster 2.
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 ) ;
// Create Aggregate 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 ( kNewClusterName2 ) ;
custom_cluster - > mutable_typed_config ( ) - > PackFrom ( cluster_config ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
// Wait for channel to get the resources and get connected.
WaitForBackend ( 0 ) ;
// Send an EDS update for cluster 1 that reuses the locality name from
// cluster 1 and points traffic to backend 1.
args1 = EdsResourceArgs ( { { " locality1 " , CreateEndpointsForBackends ( 1 , 2 ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args1 , kNewEdsServiceName1 ) ) ;
WaitForBackend ( 1 ) ;
}
TEST_P ( CdsTest , AggregateClusterRecursionDepthJustBelowMax ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
// Populate EDS resource.
CreateAndStartBackends ( 1 ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Populate new CDS resource.
Cluster new_cluster = default_cluster_ ;
new_cluster . set_name ( absl : : StrCat ( kDefaultClusterName , 15 ) ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( new_cluster ) ;
// Populate aggregate cluster chain.
for ( int i = 14 ; i > = 0 ; - - i ) {
auto cluster = default_cluster_ ;
if ( i > 0 ) cluster . set_name ( absl : : StrCat ( kDefaultClusterName , i ) ) ;
CustomClusterType * custom_cluster = cluster . mutable_cluster_type ( ) ;
custom_cluster - > set_name ( " envoy.clusters.aggregate " ) ;
ClusterConfig cluster_config ;
cluster_config . add_clusters ( absl : : StrCat ( kDefaultClusterName , i + 1 ) ) ;
custom_cluster - > mutable_typed_config ( ) - > PackFrom ( cluster_config ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
}
// RPCs should fail with the right status.
CheckRpcSendOk ( ) ;
}
TEST_P ( CdsTest , AggregateClusterRecursionMaxDepth ) {
ScopedExperimentalEnvVar env_var (
" GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER " ) ;
// Populate EDS resource.
CreateAndStartBackends ( 1 ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Populate new CDS resource.
Cluster new_cluster = default_cluster_ ;
new_cluster . set_name ( absl : : StrCat ( kDefaultClusterName , 16 ) ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( new_cluster ) ;
// Populate aggregate cluster chain.
for ( int i = 15 ; i > = 0 ; - - i ) {
auto cluster = default_cluster_ ;
if ( i > 0 ) cluster . set_name ( absl : : StrCat ( kDefaultClusterName , i ) ) ;
CustomClusterType * custom_cluster = cluster . mutable_cluster_type ( ) ;
custom_cluster - > set_name ( " envoy.clusters.aggregate " ) ;
ClusterConfig cluster_config ;
cluster_config . add_clusters ( absl : : StrCat ( kDefaultClusterName , i + 1 ) ) ;
custom_cluster - > mutable_typed_config ( ) - > PackFrom ( cluster_config ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
}
// RPCs should fail with the right status.
const Status status = SendRpc ( ) ;
EXPECT_EQ ( StatusCode : : UNAVAILABLE , status . error_code ( ) ) ;
EXPECT_THAT (
status . error_message ( ) ,
: : testing : : HasSubstr ( " aggregate cluster graph exceeds max depth " ) ) ;
}
// Test that CDS client should send a NACK if cluster type is Logical DNS but
// the feature is not yet supported.
TEST_P ( CdsTest , LogicalDNSClusterTypeDisabled ) {
auto cluster = default_cluster_ ;
cluster . set_type ( Cluster : : LOGICAL_DNS ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " DiscoveryType is not valid. " ) ) ;
}
// Test that CDS client should send a NACK if cluster type is AGGREGATE but
// the feature is not yet supported.
TEST_P ( CdsTest , AggregateClusterTypeDisabled ) {
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 ( " cluster1 " ) ;
cluster_config . add_clusters ( " cluster2 " ) ;
custom_cluster - > mutable_typed_config ( ) - > PackFrom ( cluster_config ) ;
cluster . set_type ( Cluster : : LOGICAL_DNS ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " DiscoveryType is not valid. " ) ) ;
}
// Tests that CDS client should send a NACK if the cluster type in CDS
// response is unsupported.
TEST_P ( CdsTest , UnsupportedClusterType ) {
auto cluster = default_cluster_ ;
cluster . set_type ( Cluster : : STATIC ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " DiscoveryType is not valid. " ) ) ;
}
// Tests that we don't trigger does-not-exist callbacks for a resource
// that was previously valid but is updated to be invalid.
TEST_P ( CdsTest , InvalidClusterStillExistsIfPreviouslyCached ) {
CreateAndStartBackends ( 1 ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Check that everything works.
CheckRpcSendOk ( ) ;
// Now send an update changing the Cluster to be invalid.
auto cluster = default_cluster_ ;
cluster . set_type ( Cluster : : STATIC ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( StatusCode : : OK ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : ContainsRegex ( absl : : StrCat (
kDefaultClusterName ,
" : validation error.*DiscoveryType is not valid " ) ) ) ;
CheckRpcSendOk ( ) ;
}
// Tests that CDS client should send a NACK if the eds_config in CDS response
// is other than ADS or SELF.
TEST_P ( CdsTest , EdsConfigSourceDoesNotSpecifyAdsOrSelf ) {
auto cluster = default_cluster_ ;
cluster . mutable_eds_cluster_config ( ) - > mutable_eds_config ( ) - > set_path (
" /foo/bar " ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " EDS ConfigSource is not ADS or SELF. " ) ) ;
}
// Tests that CDS client accepts an eds_config of type ADS.
TEST_P ( CdsTest , AcceptsEdsConfigSourceOfTypeAds ) {
CreateAndStartBackends ( 1 ) ;
auto cluster = default_cluster_ ;
cluster . mutable_eds_cluster_config ( ) - > mutable_eds_config ( ) - > mutable_ads ( ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForAllBackends ( ) ;
auto response_state = balancer_ - > ads_service ( ) - > cds_response_state ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
// Tests that CDS client should send a NACK if the lb_policy in CDS response
// is other than ROUND_ROBIN.
TEST_P ( CdsTest , WrongLbPolicy ) {
auto cluster = default_cluster_ ;
cluster . set_lb_policy ( Cluster : : LEAST_REQUEST ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " LB policy is not supported. " ) ) ;
}
// Tests that CDS client should send a NACK if the lrs_server in CDS response
// is other than SELF.
TEST_P ( CdsTest , WrongLrsServer ) {
auto cluster = default_cluster_ ;
cluster . mutable_lrs_server ( ) - > mutable_ads ( ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " LRS ConfigSource is not self. " ) ) ;
}
// Tests that ring hash policy that hashes using channel id ensures all RPCs
// to go 1 particular backend.
TEST_P ( CdsTest , RingHashChannelIdHashing ) {
CreateAndStartBackends ( 4 ) ;
auto cluster = default_cluster_ ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
auto new_route_config = default_route_config_ ;
auto * route = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * hash_policy = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy - > mutable_filter_state ( ) - > set_key ( " io.grpc.channel_id " ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
new_route_config ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
CheckRpcSendOk ( 100 ) ;
bool found = false ;
for ( size_t i = 0 ; i < backends_ . size ( ) ; + + i ) {
if ( backends_ [ i ] - > backend_service ( ) - > request_count ( ) > 0 ) {
EXPECT_EQ ( backends_ [ i ] - > backend_service ( ) - > request_count ( ) , 100 )
< < " backend " < < i ;
EXPECT_FALSE ( found ) < < " backend " < < i ;
found = true ;
}
}
EXPECT_TRUE ( found ) ;
}
// Tests that ring hash policy that hashes using a header value can spread
// RPCs across all the backends.
TEST_P ( CdsTest , RingHashHeaderHashing ) {
CreateAndStartBackends ( 4 ) ;
auto cluster = default_cluster_ ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
auto new_route_config = default_route_config_ ;
auto * route = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * hash_policy = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy - > mutable_header ( ) - > set_header_name ( " address_hash " ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
new_route_config ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Note each type of RPC will contains a header value that will always be
// hashed to a specific backend as the header value matches the value used
// to create the entry in the ring.
std : : vector < std : : pair < std : : string , std : : string > > metadata = {
{ " address_hash " , CreateMetadataValueThatHashesToBackend ( 0 ) } } ;
std : : vector < std : : pair < std : : string , std : : string > > metadata1 = {
{ " address_hash " , CreateMetadataValueThatHashesToBackend ( 1 ) } } ;
std : : vector < std : : pair < std : : string , std : : string > > metadata2 = {
{ " address_hash " , CreateMetadataValueThatHashesToBackend ( 2 ) } } ;
std : : vector < std : : pair < std : : string , std : : string > > metadata3 = {
{ " address_hash " , CreateMetadataValueThatHashesToBackend ( 3 ) } } ;
const auto rpc_options = RpcOptions ( ) . set_metadata ( std : : move ( metadata ) ) ;
const auto rpc_options1 = RpcOptions ( ) . set_metadata ( std : : move ( metadata1 ) ) ;
const auto rpc_options2 = RpcOptions ( ) . set_metadata ( std : : move ( metadata2 ) ) ;
const auto rpc_options3 = RpcOptions ( ) . set_metadata ( std : : move ( metadata3 ) ) ;
WaitForBackend ( 0 , WaitForBackendOptions ( ) , rpc_options ) ;
WaitForBackend ( 1 , WaitForBackendOptions ( ) , rpc_options1 ) ;
WaitForBackend ( 2 , WaitForBackendOptions ( ) , rpc_options2 ) ;
WaitForBackend ( 3 , WaitForBackendOptions ( ) , rpc_options3 ) ;
CheckRpcSendOk ( 100 , rpc_options ) ;
CheckRpcSendOk ( 100 , rpc_options1 ) ;
CheckRpcSendOk ( 100 , rpc_options2 ) ;
CheckRpcSendOk ( 100 , rpc_options3 ) ;
for ( size_t i = 0 ; i < backends_ . size ( ) ; + + i ) {
EXPECT_EQ ( 100 , backends_ [ i ] - > backend_service ( ) - > request_count ( ) ) ;
}
}
// Tests that ring hash policy that hashes using a header value and regex
// rewrite to aggregate RPCs to 1 backend.
TEST_P ( CdsTest , RingHashHeaderHashingWithRegexRewrite ) {
CreateAndStartBackends ( 4 ) ;
auto cluster = default_cluster_ ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
auto new_route_config = default_route_config_ ;
auto * route = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * hash_policy = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy - > mutable_header ( ) - > set_header_name ( " address_hash " ) ;
hash_policy - > mutable_header ( )
- > mutable_regex_rewrite ( )
- > mutable_pattern ( )
- > set_regex ( " [0-9]+ " ) ;
hash_policy - > mutable_header ( ) - > mutable_regex_rewrite ( ) - > set_substitution (
" foo " ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
new_route_config ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
std : : vector < std : : pair < std : : string , std : : string > > metadata = {
{ " address_hash " , CreateMetadataValueThatHashesToBackend ( 0 ) } } ;
std : : vector < std : : pair < std : : string , std : : string > > metadata1 = {
{ " address_hash " , CreateMetadataValueThatHashesToBackend ( 1 ) } } ;
std : : vector < std : : pair < std : : string , std : : string > > metadata2 = {
{ " address_hash " , CreateMetadataValueThatHashesToBackend ( 2 ) } } ;
std : : vector < std : : pair < std : : string , std : : string > > metadata3 = {
{ " address_hash " , CreateMetadataValueThatHashesToBackend ( 3 ) } } ;
const auto rpc_options = RpcOptions ( ) . set_metadata ( std : : move ( metadata ) ) ;
const auto rpc_options1 = RpcOptions ( ) . set_metadata ( std : : move ( metadata1 ) ) ;
const auto rpc_options2 = RpcOptions ( ) . set_metadata ( std : : move ( metadata2 ) ) ;
const auto rpc_options3 = RpcOptions ( ) . set_metadata ( std : : move ( metadata3 ) ) ;
CheckRpcSendOk ( 100 , rpc_options ) ;
CheckRpcSendOk ( 100 , rpc_options1 ) ;
CheckRpcSendOk ( 100 , rpc_options2 ) ;
CheckRpcSendOk ( 100 , rpc_options3 ) ;
bool found = false ;
for ( size_t i = 0 ; i < backends_ . size ( ) ; + + i ) {
if ( backends_ [ i ] - > backend_service ( ) - > request_count ( ) > 0 ) {
EXPECT_EQ ( backends_ [ i ] - > backend_service ( ) - > request_count ( ) , 400 )
< < " backend " < < i ;
EXPECT_FALSE ( found ) < < " backend " < < i ;
found = true ;
}
}
EXPECT_TRUE ( found ) ;
}
// Tests that ring hash policy that hashes using a random value.
TEST_P ( CdsTest , RingHashNoHashPolicy ) {
CreateAndStartBackends ( 2 ) ;
const double kDistribution50Percent = 0.5 ;
const double kErrorTolerance = 0.05 ;
const uint32_t kRpcTimeoutMs = 10000 ;
const size_t kNumRpcs =
ComputeIdealNumRpcs ( kDistribution50Percent , kErrorTolerance ) ;
auto cluster = default_cluster_ ;
// Increasing min ring size for random distribution.
cluster . mutable_ring_hash_lb_config ( ) - > mutable_minimum_ring_size ( ) - > set_value (
100000 ) ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// TODO(donnadionne): remove extended timeout after ring creation
// optimization.
WaitForAllBackends ( 0 , 2 , WaitForBackendOptions ( ) ,
RpcOptions ( ) . set_timeout_ms ( kRpcTimeoutMs ) ) ;
CheckRpcSendOk ( kNumRpcs ) ;
const int request_count_1 = backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ;
const int request_count_2 = backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ;
EXPECT_THAT ( static_cast < double > ( request_count_1 ) / kNumRpcs ,
: : testing : : DoubleNear ( kDistribution50Percent , kErrorTolerance ) ) ;
EXPECT_THAT ( static_cast < double > ( request_count_2 ) / kNumRpcs ,
: : testing : : DoubleNear ( kDistribution50Percent , kErrorTolerance ) ) ;
}
// Test that ring hash policy evaluation will continue past the terminal
// policy if no results are produced yet.
TEST_P ( CdsTest , RingHashContinuesPastTerminalPolicyThatDoesNotProduceResult ) {
CreateAndStartBackends ( 2 ) ;
auto cluster = default_cluster_ ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
auto new_route_config = default_route_config_ ;
auto * route = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * hash_policy = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy - > mutable_header ( ) - > set_header_name ( " header_not_present " ) ;
hash_policy - > set_terminal ( true ) ;
auto * hash_policy2 = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy2 - > mutable_header ( ) - > set_header_name ( " address_hash " ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
new_route_config ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
std : : vector < std : : pair < std : : string , std : : string > > metadata = {
{ " address_hash " , CreateMetadataValueThatHashesToBackend ( 0 ) } } ;
const auto rpc_options = RpcOptions ( ) . set_metadata ( std : : move ( metadata ) ) ;
CheckRpcSendOk ( 100 , rpc_options ) ;
EXPECT_EQ ( backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) , 100 ) ;
EXPECT_EQ ( backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) , 0 ) ;
}
// Test random hash is used when header hashing specified a header field that
// the RPC did not have.
TEST_P ( CdsTest , RingHashOnHeaderThatIsNotPresent ) {
CreateAndStartBackends ( 2 ) ;
const double kDistribution50Percent = 0.5 ;
const double kErrorTolerance = 0.05 ;
const uint32_t kRpcTimeoutMs = 10000 ;
const size_t kNumRpcs =
ComputeIdealNumRpcs ( kDistribution50Percent , kErrorTolerance ) ;
auto cluster = default_cluster_ ;
// Increasing min ring size for random distribution.
cluster . mutable_ring_hash_lb_config ( ) - > mutable_minimum_ring_size ( ) - > set_value (
100000 ) ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
auto new_route_config = default_route_config_ ;
auto * route = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * hash_policy = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy - > mutable_header ( ) - > set_header_name ( " header_not_present " ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
new_route_config ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
std : : vector < std : : pair < std : : string , std : : string > > metadata = {
{ " unmatched_header " , absl : : StrFormat ( " % " PRIu32 , rand ( ) ) } ,
} ;
const auto rpc_options = RpcOptions ( ) . set_metadata ( std : : move ( metadata ) ) ;
// TODO(donnadionne): remove extended timeout after ring creation
// optimization.
WaitForAllBackends ( 0 , 2 , WaitForBackendOptions ( ) ,
RpcOptions ( ) . set_timeout_ms ( kRpcTimeoutMs ) ) ;
CheckRpcSendOk ( kNumRpcs , rpc_options ) ;
const int request_count_1 = backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ;
const int request_count_2 = backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ;
EXPECT_THAT ( static_cast < double > ( request_count_1 ) / kNumRpcs ,
: : testing : : DoubleNear ( kDistribution50Percent , kErrorTolerance ) ) ;
EXPECT_THAT ( static_cast < double > ( request_count_2 ) / kNumRpcs ,
: : testing : : DoubleNear ( kDistribution50Percent , kErrorTolerance ) ) ;
}
// Test random hash is used when only unsupported hash policies are
// configured.
TEST_P ( CdsTest , RingHashUnsupportedHashPolicyDefaultToRandomHashing ) {
CreateAndStartBackends ( 2 ) ;
const double kDistribution50Percent = 0.5 ;
const double kErrorTolerance = 0.05 ;
const uint32_t kRpcTimeoutMs = 10000 ;
const size_t kNumRpcs =
ComputeIdealNumRpcs ( kDistribution50Percent , kErrorTolerance ) ;
auto cluster = default_cluster_ ;
// Increasing min ring size for random distribution.
cluster . mutable_ring_hash_lb_config ( ) - > mutable_minimum_ring_size ( ) - > set_value (
100000 ) ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
auto new_route_config = default_route_config_ ;
auto * route = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * hash_policy_unsupported_1 = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy_unsupported_1 - > mutable_cookie ( ) - > set_name ( " cookie " ) ;
auto * hash_policy_unsupported_2 = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy_unsupported_2 - > mutable_connection_properties ( ) - > set_source_ip (
true ) ;
auto * hash_policy_unsupported_3 = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy_unsupported_3 - > mutable_query_parameter ( ) - > set_name (
" query_parameter " ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
new_route_config ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// TODO(donnadionne): remove extended timeout after ring creation
// optimization.
WaitForAllBackends ( 0 , 2 , WaitForBackendOptions ( ) ,
RpcOptions ( ) . set_timeout_ms ( kRpcTimeoutMs ) ) ;
CheckRpcSendOk ( kNumRpcs ) ;
const int request_count_1 = backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ;
const int request_count_2 = backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ;
EXPECT_THAT ( static_cast < double > ( request_count_1 ) / kNumRpcs ,
: : testing : : DoubleNear ( kDistribution50Percent , kErrorTolerance ) ) ;
EXPECT_THAT ( static_cast < double > ( request_count_2 ) / kNumRpcs ,
: : testing : : DoubleNear ( kDistribution50Percent , kErrorTolerance ) ) ;
}
// Tests that ring hash policy that hashes using a random value can spread
// RPCs across all the backends according to locality weight.
TEST_P ( CdsTest , RingHashRandomHashingDistributionAccordingToEndpointWeight ) {
CreateAndStartBackends ( 2 ) ;
const size_t kWeight1 = 1 ;
const size_t kWeight2 = 2 ;
const size_t kWeightTotal = kWeight1 + kWeight2 ;
const double kWeight33Percent = static_cast < double > ( kWeight1 ) / kWeightTotal ;
const double kWeight66Percent = static_cast < double > ( kWeight2 ) / kWeightTotal ;
const double kErrorTolerance = 0.05 ;
const uint32_t kRpcTimeoutMs = 10000 ;
const size_t kNumRpcs =
ComputeIdealNumRpcs ( kWeight33Percent , kErrorTolerance ) ;
auto cluster = default_cluster_ ;
// Increasing min ring size for random distribution.
cluster . mutable_ring_hash_lb_config ( ) - > mutable_minimum_ring_size ( ) - > set_value (
100000 ) ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
EdsResourceArgs args ( { { " locality0 " ,
{ CreateEndpoint ( 0 , HealthStatus : : UNKNOWN , 1 ) ,
CreateEndpoint ( 1 , HealthStatus : : UNKNOWN , 2 ) } } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// TODO(donnadionne): remove extended timeout after ring creation
// optimization.
WaitForAllBackends ( 0 , 2 , WaitForBackendOptions ( ) ,
RpcOptions ( ) . set_timeout_ms ( kRpcTimeoutMs ) ) ;
CheckRpcSendOk ( kNumRpcs ) ;
const int weight_33_request_count =
backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ;
const int weight_66_request_count =
backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ;
EXPECT_THAT ( static_cast < double > ( weight_33_request_count ) / kNumRpcs ,
: : testing : : DoubleNear ( kWeight33Percent , kErrorTolerance ) ) ;
EXPECT_THAT ( static_cast < double > ( weight_66_request_count ) / kNumRpcs ,
: : testing : : DoubleNear ( kWeight66Percent , kErrorTolerance ) ) ;
}
// Tests that ring hash policy that hashes using a random value can spread
// RPCs across all the backends according to locality weight.
TEST_P ( CdsTest ,
RingHashRandomHashingDistributionAccordingToLocalityAndEndpointWeight ) {
CreateAndStartBackends ( 2 ) ;
const size_t kWeight1 = 1 * 1 ;
const size_t kWeight2 = 2 * 2 ;
const size_t kWeightTotal = kWeight1 + kWeight2 ;
const double kWeight20Percent = static_cast < double > ( kWeight1 ) / kWeightTotal ;
const double kWeight80Percent = static_cast < double > ( kWeight2 ) / kWeightTotal ;
const double kErrorTolerance = 0.05 ;
const uint32_t kRpcTimeoutMs = 10000 ;
const size_t kNumRpcs =
ComputeIdealNumRpcs ( kWeight20Percent , kErrorTolerance ) ;
auto cluster = default_cluster_ ;
// Increasing min ring size for random distribution.
cluster . mutable_ring_hash_lb_config ( ) - > mutable_minimum_ring_size ( ) - > set_value (
100000 ) ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
EdsResourceArgs args (
{ { " locality0 " , { CreateEndpoint ( 0 , HealthStatus : : UNKNOWN , 1 ) } , 1 } ,
{ " locality1 " , { CreateEndpoint ( 1 , HealthStatus : : UNKNOWN , 2 ) } , 2 } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// TODO(donnadionne): remove extended timeout after ring creation
// optimization.
WaitForAllBackends ( 0 , 2 , WaitForBackendOptions ( ) ,
RpcOptions ( ) . set_timeout_ms ( kRpcTimeoutMs ) ) ;
CheckRpcSendOk ( kNumRpcs ) ;
const int weight_20_request_count =
backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ;
const int weight_80_request_count =
backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ;
EXPECT_THAT ( static_cast < double > ( weight_20_request_count ) / kNumRpcs ,
: : testing : : DoubleNear ( kWeight20Percent , kErrorTolerance ) ) ;
EXPECT_THAT ( static_cast < double > ( weight_80_request_count ) / kNumRpcs ,
: : testing : : DoubleNear ( kWeight80Percent , kErrorTolerance ) ) ;
}
// Tests round robin is not implacted by the endpoint weight, and that the
// localities in a locality map are picked according to their weights.
TEST_P ( CdsTest , RingHashEndpointWeightDoesNotImpactWeightedRoundRobin ) {
CreateAndStartBackends ( 2 ) ;
const int kLocalityWeight0 = 2 ;
const int kLocalityWeight1 = 8 ;
const int kTotalLocalityWeight = kLocalityWeight0 + kLocalityWeight1 ;
const double kLocalityWeightRate0 =
static_cast < double > ( kLocalityWeight0 ) / kTotalLocalityWeight ;
const double kLocalityWeightRate1 =
static_cast < double > ( kLocalityWeight1 ) / kTotalLocalityWeight ;
const double kErrorTolerance = 0.05 ;
const size_t kNumRpcs =
ComputeIdealNumRpcs ( kLocalityWeightRate0 , kErrorTolerance ) ;
// ADS response contains 2 localities, each of which contains 1 backend.
EdsResourceArgs args ( {
{ " locality0 " ,
{ CreateEndpoint ( 0 , HealthStatus : : UNKNOWN , 8 ) } ,
kLocalityWeight0 } ,
{ " locality1 " ,
{ CreateEndpoint ( 1 , HealthStatus : : UNKNOWN , 2 ) } ,
kLocalityWeight1 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Wait for both backends to be ready.
WaitForAllBackends ( 0 , 2 ) ;
// Send kNumRpcs RPCs.
CheckRpcSendOk ( kNumRpcs ) ;
// The locality picking rates should be roughly equal to the expectation.
const double locality_picked_rate_0 =
static_cast < double > ( backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) /
kNumRpcs ;
const double locality_picked_rate_1 =
static_cast < double > ( backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ) /
kNumRpcs ;
EXPECT_THAT ( locality_picked_rate_0 ,
: : testing : : DoubleNear ( kLocalityWeightRate0 , kErrorTolerance ) ) ;
EXPECT_THAT ( locality_picked_rate_1 ,
: : testing : : DoubleNear ( kLocalityWeightRate1 , kErrorTolerance ) ) ;
}
// Tests that ring hash policy that hashes using a fixed string ensures all
// RPCs to go 1 particular backend; and that subsequent hashing policies are
// ignored due to the setting of terminal.
TEST_P ( CdsTest , RingHashFixedHashingTerminalPolicy ) {
CreateAndStartBackends ( 2 ) ;
auto cluster = default_cluster_ ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
auto new_route_config = default_route_config_ ;
auto * route = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * hash_policy = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy - > mutable_header ( ) - > set_header_name ( " fixed_string " ) ;
hash_policy - > set_terminal ( true ) ;
auto * hash_policy_to_be_ignored = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy_to_be_ignored - > mutable_header ( ) - > set_header_name ( " random_string " ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
new_route_config ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
std : : vector < std : : pair < std : : string , std : : string > > metadata = {
{ " fixed_string " , " fixed_value " } ,
{ " random_string " , absl : : StrFormat ( " % " PRIu32 , rand ( ) ) } ,
} ;
const auto rpc_options = RpcOptions ( ) . set_metadata ( std : : move ( metadata ) ) ;
CheckRpcSendOk ( 100 , rpc_options ) ;
bool found = false ;
for ( size_t i = 0 ; i < backends_ . size ( ) ; + + i ) {
if ( backends_ [ i ] - > backend_service ( ) - > request_count ( ) > 0 ) {
EXPECT_EQ ( backends_ [ i ] - > backend_service ( ) - > request_count ( ) , 100 )
< < " backend " < < i ;
EXPECT_FALSE ( found ) < < " backend " < < i ;
found = true ;
}
}
EXPECT_TRUE ( found ) ;
}
// Test that the channel will go from idle to ready via connecting;
// (tho it is not possible to catch the connecting state before moving to
// ready)
TEST_P ( CdsTest , RingHashIdleToReady ) {
CreateAndStartBackends ( 1 ) ;
auto cluster = default_cluster_ ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
auto new_route_config = default_route_config_ ;
auto * route = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * hash_policy = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy - > mutable_filter_state ( ) - > set_key ( " io.grpc.channel_id " ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
new_route_config ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
EXPECT_EQ ( GRPC_CHANNEL_IDLE , channel_ - > GetState ( false ) ) ;
CheckRpcSendOk ( ) ;
EXPECT_EQ ( GRPC_CHANNEL_READY , channel_ - > GetState ( false ) ) ;
}
// Test that the channel will transition to READY once it starts
// connecting even if there are no RPCs being sent to the picker.
TEST_P ( CdsTest , RingHashContinuesConnectingWithoutPicks ) {
// Create EDS resource.
CreateAndStartBackends ( 1 ) ;
auto non_existant_endpoint = MakeNonExistantEndpoint ( ) ;
EdsResourceArgs args (
{ { " locality0 " , { non_existant_endpoint , CreateEndpoint ( 0 ) } } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Change CDS resource to use RING_HASH.
auto cluster = default_cluster_ ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
// Add hash policy to RDS resource.
auto new_route_config = default_route_config_ ;
auto * route = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * hash_policy = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy - > mutable_header ( ) - > set_header_name ( " address_hash " ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
new_route_config ) ;
// A connection injector that cancels the RPC after seeing the
// connection attempt for the non-existant endpoint.
class ConnectionInjector : public ConnectionAttemptInjector {
public :
explicit ConnectionInjector ( int port ) : port_ ( port ) { }
void HandleConnection ( grpc_closure * closure , grpc_endpoint * * ep ,
grpc_pollset_set * interested_parties ,
const grpc_channel_args * channel_args ,
const grpc_resolved_address * addr ,
grpc_core : : Timestamp deadline ) override {
{
grpc_core : : MutexLock lock ( & mu_ ) ;
const int port = grpc_sockaddr_get_port ( addr ) ;
gpr_log ( GPR_INFO , " ==> HandleConnection(): seen_port_=%d, port=%d " ,
seen_port_ , port ) ;
// Initial attempt should be for port0_, which should fail.
// Cancel the RPC at this point, so that it's no longer
// queued when the LB policy updates the picker.
if ( ! seen_port_ & & port = = port_ ) {
gpr_log ( GPR_INFO , " *** SEEN P0 CONNECTION ATTEMPT " ) ;
seen_port_ = true ;
cond_ . Signal ( ) ;
}
}
AttemptConnection ( closure , ep , interested_parties , channel_args , addr ,
deadline ) ;
}
void WaitForP0ConnectionAttempt ( ) {
grpc_core : : MutexLock lock ( & mu_ ) ;
while ( ! seen_port_ ) {
cond_ . Wait ( & mu_ ) ;
}
}
private :
const int port_ ;
grpc_core : : Mutex mu_ ;
grpc_core : : CondVar cond_ ;
bool seen_port_ ABSL_GUARDED_BY ( mu_ ) = false ;
} ;
ConnectionInjector connection_injector ( non_existant_endpoint . port ) ;
// A long-running RPC, just used to send the RPC in another thread.
LongRunningRpc rpc ;
std : : vector < std : : pair < std : : string , std : : string > > metadata = {
{ " address_hash " ,
CreateMetadataValueThatHashesToBackendPort ( non_existant_endpoint . port ) } } ;
rpc . StartRpc ( stub_ . get ( ) , RpcOptions ( ) . set_timeout_ms ( 0 ) . set_metadata (
std : : move ( metadata ) ) ) ;
// Wait for the RPC to trigger the P0 connection attempt, then cancel it.
connection_injector . WaitForP0ConnectionAttempt ( ) ;
rpc . CancelRpc ( ) ;
// Wait for channel to become connected without any pending RPC.
EXPECT_TRUE ( channel_ - > WaitForConnected ( grpc_timeout_seconds_to_deadline ( 5 ) ) ) ;
// RPC should have been cancelled.
EXPECT_EQ ( StatusCode : : CANCELLED , rpc . GetStatus ( ) . error_code ( ) ) ;
// Make sure the backend did not get any requests.
EXPECT_EQ ( 0UL , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
}
// Test that when the first pick is down leading to a transient failure, we
// will move on to the next ring hash entry.
TEST_P ( CdsTest , RingHashTransientFailureCheckNextOne ) {
CreateAndStartBackends ( 1 ) ;
auto cluster = default_cluster_ ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
auto new_route_config = default_route_config_ ;
auto * route = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * hash_policy = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy - > mutable_header ( ) - > set_header_name ( " address_hash " ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
new_route_config ) ;
std : : vector < EdsResourceArgs : : Endpoint > endpoints ;
const int unused_port = grpc_pick_unused_port_or_die ( ) ;
endpoints . emplace_back ( unused_port ) ;
endpoints . emplace_back ( backends_ [ 0 ] - > port ( ) ) ;
EdsResourceArgs args ( { { " locality0 " , std : : move ( endpoints ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
std : : vector < std : : pair < std : : string , std : : string > > metadata = {
{ " address_hash " ,
CreateMetadataValueThatHashesToBackendPort ( unused_port ) } } ;
const auto rpc_options = RpcOptions ( ) . set_metadata ( std : : move ( metadata ) ) ;
WaitForBackend ( 0 , WaitForBackendOptions ( ) , rpc_options ) ;
CheckRpcSendOk ( 100 , rpc_options ) ;
}
// Test that when a backend goes down, we will move on to the next subchannel
// (with a lower priority). When the backend comes back up, traffic will move
// back.
TEST_P ( CdsTest , RingHashSwitchToLowerPrioirtyAndThenBack ) {
CreateAndStartBackends ( 2 ) ;
auto cluster = default_cluster_ ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
auto new_route_config = default_route_config_ ;
auto * route = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * hash_policy = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy - > mutable_header ( ) - > set_header_name ( " address_hash " ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
new_route_config ) ;
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) , kDefaultLocalityWeight ,
0 } ,
{ " locality1 " , CreateEndpointsForBackends ( 1 , 2 ) , kDefaultLocalityWeight ,
1 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
std : : vector < std : : pair < std : : string , std : : string > > metadata = {
{ " address_hash " , CreateMetadataValueThatHashesToBackend ( 0 ) } } ;
const auto rpc_options = RpcOptions ( ) . set_metadata ( std : : move ( metadata ) ) ;
WaitForBackend ( 0 , WaitForBackendOptions ( ) , rpc_options ) ;
ShutdownBackend ( 0 ) ;
WaitForBackend ( 1 , WaitForBackendOptions ( ) . set_allow_failures ( true ) ,
rpc_options ) ;
StartBackend ( 0 ) ;
WaitForBackend ( 0 , WaitForBackendOptions ( ) , rpc_options ) ;
CheckRpcSendOk ( 100 , rpc_options ) ;
EXPECT_EQ ( 100 , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0 , backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ) ;
}
// Test that when all backends are down, we will keep reattempting.
TEST_P ( CdsTest , RingHashAllFailReattempt ) {
CreateAndStartBackends ( 1 ) ;
const uint32_t kConnectionTimeoutMilliseconds = 5000 ;
auto cluster = default_cluster_ ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
auto new_route_config = default_route_config_ ;
auto * route = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * hash_policy = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy - > mutable_header ( ) - > set_header_name ( " address_hash " ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
new_route_config ) ;
EdsResourceArgs args (
{ { " locality0 " , { MakeNonExistantEndpoint ( ) , CreateEndpoint ( 0 ) } } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
std : : vector < std : : pair < std : : string , std : : string > > metadata = {
{ " address_hash " , CreateMetadataValueThatHashesToBackend ( 0 ) } } ;
EXPECT_EQ ( GRPC_CHANNEL_IDLE , channel_ - > GetState ( false ) ) ;
ShutdownBackend ( 0 ) ;
CheckRpcSendFailure ( CheckRpcSendFailureOptions ( ) . set_rpc_options (
RpcOptions ( ) . set_metadata ( std : : move ( metadata ) ) ) ) ;
StartBackend ( 0 ) ;
// Ensure we are actively connecting without any traffic.
EXPECT_TRUE ( channel_ - > WaitForConnected (
grpc_timeout_milliseconds_to_deadline ( kConnectionTimeoutMilliseconds ) ) ) ;
}
// Test that when all backends are down and then up, we may pick a TF backend
// and we will then jump to ready backend.
TEST_P ( CdsTest , RingHashTransientFailureSkipToAvailableReady ) {
CreateAndStartBackends ( 2 ) ;
const uint32_t kConnectionTimeoutMilliseconds = 5000 ;
auto cluster = default_cluster_ ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
auto new_route_config = default_route_config_ ;
auto * route = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * hash_policy = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy - > mutable_header ( ) - > set_header_name ( " address_hash " ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
new_route_config ) ;
// Make sure we include some unused ports to fill the ring.
EdsResourceArgs args ( {
{ " locality0 " ,
{ CreateEndpoint ( 0 ) , CreateEndpoint ( 1 ) , MakeNonExistantEndpoint ( ) ,
MakeNonExistantEndpoint ( ) } } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
std : : vector < std : : pair < std : : string , std : : string > > metadata = {
{ " address_hash " , CreateMetadataValueThatHashesToBackend ( 0 ) } } ;
const auto rpc_options = RpcOptions ( ) . set_metadata ( std : : move ( metadata ) ) ;
EXPECT_EQ ( GRPC_CHANNEL_IDLE , channel_ - > GetState ( false ) ) ;
ShutdownBackend ( 0 ) ;
ShutdownBackend ( 1 ) ;
CheckRpcSendFailure (
CheckRpcSendFailureOptions ( ) . set_rpc_options ( rpc_options ) ) ;
EXPECT_EQ ( GRPC_CHANNEL_TRANSIENT_FAILURE , channel_ - > GetState ( false ) ) ;
// Bring up 0, should be picked as the RPC is hashed to it.
StartBackend ( 0 ) ;
EXPECT_TRUE ( channel_ - > WaitForConnected (
grpc_timeout_milliseconds_to_deadline ( kConnectionTimeoutMilliseconds ) ) ) ;
WaitForBackend ( 0 , WaitForBackendOptions ( ) , rpc_options ) ;
// Bring down 0 and bring up 1.
// Note the RPC contains a header value that will always be hashed to
// backend 0. So by purposely bring down backend 0 and bring up another
// backend, this will ensure Picker's first choice of backend 0 will fail
// and it will
// 1. reattempt backend 0 and
// 2. go through the remaining subchannels to find one in READY.
// Since the the entries in the ring is pretty distributed and we have
// unused ports to fill the ring, it is almost guaranteed that the Picker
// will go through some non-READY entries and skip them as per design.
ShutdownBackend ( 0 ) ;
CheckRpcSendFailure (
CheckRpcSendFailureOptions ( ) . set_rpc_options ( rpc_options ) ) ;
StartBackend ( 1 ) ;
EXPECT_TRUE ( channel_ - > WaitForConnected (
grpc_timeout_milliseconds_to_deadline ( kConnectionTimeoutMilliseconds ) ) ) ;
WaitForBackend ( 1 , WaitForBackendOptions ( ) , rpc_options ) ;
}
// Test unspported hash policy types are all ignored before a supported
// policy.
TEST_P ( CdsTest , RingHashUnsupportedHashPolicyUntilChannelIdHashing ) {
CreateAndStartBackends ( 2 ) ;
auto cluster = default_cluster_ ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
auto new_route_config = default_route_config_ ;
auto * route = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * hash_policy_unsupported_1 = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy_unsupported_1 - > mutable_cookie ( ) - > set_name ( " cookie " ) ;
auto * hash_policy_unsupported_2 = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy_unsupported_2 - > mutable_connection_properties ( ) - > set_source_ip (
true ) ;
auto * hash_policy_unsupported_3 = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy_unsupported_3 - > mutable_query_parameter ( ) - > set_name (
" query_parameter " ) ;
auto * hash_policy = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy - > mutable_filter_state ( ) - > set_key ( " io.grpc.channel_id " ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
new_route_config ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
CheckRpcSendOk ( 100 ) ;
bool found = false ;
for ( size_t i = 0 ; i < backends_ . size ( ) ; + + i ) {
if ( backends_ [ i ] - > backend_service ( ) - > request_count ( ) > 0 ) {
EXPECT_EQ ( backends_ [ i ] - > backend_service ( ) - > request_count ( ) , 100 )
< < " backend " < < i ;
EXPECT_FALSE ( found ) < < " backend " < < i ;
found = true ;
}
}
EXPECT_TRUE ( found ) ;
}
// Test we nack when ring hash policy has invalid hash function (something
// other than XX_HASH.
TEST_P ( CdsTest , RingHashPolicyHasInvalidHashFunction ) {
CreateAndStartBackends ( 1 ) ;
auto cluster = default_cluster_ ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
cluster . mutable_ring_hash_lb_config ( ) - > set_hash_function (
Cluster : : RingHashLbConfig : : MURMUR_HASH_2 ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
auto new_route_config = default_route_config_ ;
auto * route = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * hash_policy = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy - > mutable_filter_state ( ) - > set_key ( " io.grpc.channel_id " ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
new_route_config ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " ring hash lb config has invalid hash function. " ) ) ;
}
// Test we nack when ring hash policy has invalid ring size.
TEST_P ( CdsTest , RingHashPolicyHasInvalidMinimumRingSize ) {
CreateAndStartBackends ( 1 ) ;
auto cluster = default_cluster_ ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
cluster . mutable_ring_hash_lb_config ( ) - > mutable_minimum_ring_size ( ) - > set_value (
0 ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
auto new_route_config = default_route_config_ ;
auto * route = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * hash_policy = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy - > mutable_filter_state ( ) - > set_key ( " io.grpc.channel_id " ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
new_route_config ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" min_ring_size is not in the range of 1 to 8388608. " ) ) ;
}
// Test we nack when ring hash policy has invalid ring size.
TEST_P ( CdsTest , RingHashPolicyHasInvalidMaxmumRingSize ) {
CreateAndStartBackends ( 1 ) ;
auto cluster = default_cluster_ ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
cluster . mutable_ring_hash_lb_config ( ) - > mutable_maximum_ring_size ( ) - > set_value (
8388609 ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
auto new_route_config = default_route_config_ ;
auto * route = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * hash_policy = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy - > mutable_filter_state ( ) - > set_key ( " io.grpc.channel_id " ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
new_route_config ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" max_ring_size is not in the range of 1 to 8388608. " ) ) ;
}
// Test we nack when ring hash policy has invalid ring size.
TEST_P ( CdsTest , RingHashPolicyHasInvalidRingSizeMinGreaterThanMax ) {
CreateAndStartBackends ( 1 ) ;
auto cluster = default_cluster_ ;
cluster . set_lb_policy ( Cluster : : RING_HASH ) ;
cluster . mutable_ring_hash_lb_config ( ) - > mutable_maximum_ring_size ( ) - > set_value (
5000 ) ;
cluster . mutable_ring_hash_lb_config ( ) - > mutable_minimum_ring_size ( ) - > set_value (
5001 ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
auto new_route_config = default_route_config_ ;
auto * route = new_route_config . mutable_virtual_hosts ( 0 ) - > mutable_routes ( 0 ) ;
auto * hash_policy = route - > mutable_route ( ) - > add_hash_policy ( ) ;
hash_policy - > mutable_filter_state ( ) - > set_key ( " io.grpc.channel_id " ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
new_route_config ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" min_ring_size cannot be greater than max_ring_size. " ) ) ;
}
class XdsSecurityTest : public XdsEnd2endTest {
protected :
void SetUp ( ) override {
BootstrapBuilder builder = BootstrapBuilder ( ) ;
builder . AddCertificateProviderPlugin ( " fake_plugin1 " , " fake1 " ) ;
builder . AddCertificateProviderPlugin ( " fake_plugin2 " , " fake2 " ) ;
std : : vector < std : : string > fields ;
fields . push_back ( absl : : StrFormat ( " \" certificate_file \" : \" %s \" " ,
kClientCertPath ) ) ;
fields . push_back ( absl : : StrFormat ( " \" private_key_file \" : \" %s \" " ,
kClientKeyPath ) ) ;
fields . push_back ( absl : : StrFormat ( " \" ca_certificate_file \" : \" %s \" " ,
kCaCertPath ) ) ;
builder . AddCertificateProviderPlugin ( " file_plugin " , " file_watcher " ,
absl : : StrJoin ( fields , " , \n " ) ) ;
InitClient ( builder ) ;
CreateAndStartBackends ( 1 ) ;
root_cert_ = ReadFile ( kCaCertPath ) ;
bad_root_cert_ = ReadFile ( kBadClientCertPath ) ;
identity_pair_ = ReadTlsIdentityPair ( kClientKeyPath , kClientCertPath ) ;
// TODO(yashykt): Use different client certs here instead of reusing
// server certs after https://github.com/grpc/grpc/pull/24876 is merged
fallback_identity_pair_ =
ReadTlsIdentityPair ( kServerKeyPath , kServerCertPath ) ;
bad_identity_pair_ =
ReadTlsIdentityPair ( kBadClientKeyPath , kBadClientCertPath ) ;
server_san_exact_ . set_exact ( " *.test.google.fr " ) ;
server_san_prefix_ . set_prefix ( " waterzooi.test.google " ) ;
server_san_suffix_ . set_suffix ( " google.fr " ) ;
server_san_contains_ . set_contains ( " google " ) ;
server_san_regex_ . mutable_safe_regex ( ) - > mutable_google_re2 ( ) ;
server_san_regex_ . mutable_safe_regex ( ) - > set_regex (
" (foo|waterzooi).test.google.(fr|be) " ) ;
bad_san_1_ . set_exact ( " 192.168.1.4 " ) ;
bad_san_2_ . set_exact ( " foo.test.google.in " ) ;
authenticated_identity_ = { " testclient " } ;
fallback_authenticated_identity_ = { " *.test.google.fr " ,
" waterzooi.test.google.be " ,
" *.test.youtube.com " , " 192.168.1.3 " } ;
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
}
void TearDown ( ) override {
g_fake1_cert_data_map = nullptr ;
g_fake2_cert_data_map = nullptr ;
XdsEnd2endTest : : TearDown ( ) ;
}
// Sends CDS updates with the new security configuration and verifies that
// after propagation, this new configuration is used for connections. If \a
// identity_instance_name and \a root_instance_name are both empty,
// connections are expected to use fallback credentials.
void UpdateAndVerifyXdsSecurityConfiguration (
absl : : string_view root_instance_name ,
absl : : string_view root_certificate_name ,
absl : : string_view identity_instance_name ,
absl : : string_view identity_certificate_name ,
const std : : vector < StringMatcher > & san_matchers ,
const std : : vector < std : : string > & expected_authenticated_identity ,
bool test_expects_failure = false ) {
auto cluster = default_cluster_ ;
if ( ! identity_instance_name . empty ( ) | | ! root_instance_name . empty ( ) ) {
auto * transport_socket = cluster . mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
UpstreamTlsContext upstream_tls_context ;
if ( ! identity_instance_name . empty ( ) ) {
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_tls_certificate_provider_instance ( )
- > set_instance_name ( std : : string ( identity_instance_name ) ) ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_tls_certificate_provider_instance ( )
- > set_certificate_name ( std : : string ( identity_certificate_name ) ) ;
}
if ( ! root_instance_name . empty ( ) ) {
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > mutable_ca_certificate_provider_instance ( )
- > set_instance_name ( std : : string ( root_instance_name ) ) ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > mutable_ca_certificate_provider_instance ( )
- > set_certificate_name ( std : : string ( root_certificate_name ) ) ;
}
if ( ! san_matchers . empty ( ) ) {
auto * validation_context =
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( ) ;
for ( const auto & san_matcher : san_matchers ) {
* validation_context - > add_match_subject_alt_names ( ) = san_matcher ;
}
}
transport_socket - > mutable_typed_config ( ) - > PackFrom ( upstream_tls_context ) ;
}
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
// The updates might take time to have an effect, so use a retry loop.
constexpr int kRetryCount = 100 ;
int num_tries = 0 ;
for ( ; num_tries < kRetryCount ; num_tries + + ) {
// Restart the servers to force a reconnection so that previously
// connected subchannels are not used for the RPC.
ShutdownBackend ( 0 ) ;
StartBackend ( 0 ) ;
if ( test_expects_failure ) {
if ( SendRpc ( ) . ok ( ) ) {
gpr_log ( GPR_ERROR , " RPC succeeded. Failure expected. Trying again. " ) ;
continue ;
}
} else {
WaitForBackend ( 0 , WaitForBackendOptions ( ) . set_allow_failures ( true ) ) ;
Status status = SendRpc ( ) ;
if ( ! status . ok ( ) ) {
gpr_log ( GPR_ERROR , " RPC failed. code=%d message=%s Trying again. " ,
status . error_code ( ) , status . error_message ( ) . c_str ( ) ) ;
continue ;
}
if ( backends_ [ 0 ] - > backend_service ( ) - > last_peer_identity ( ) ! =
expected_authenticated_identity ) {
gpr_log (
GPR_ERROR ,
" Expected client identity does not match. (actual) %s vs "
" (expected) %s Trying again. " ,
absl : : StrJoin (
backends_ [ 0 ] - > backend_service ( ) - > last_peer_identity ( ) , " , " )
. c_str ( ) ,
absl : : StrJoin ( expected_authenticated_identity , " , " ) . c_str ( ) ) ;
continue ;
}
}
break ;
}
EXPECT_LT ( num_tries , kRetryCount ) ;
}
std : : string root_cert_ ;
std : : string bad_root_cert_ ;
grpc_core : : PemKeyCertPairList identity_pair_ ;
grpc_core : : PemKeyCertPairList fallback_identity_pair_ ;
grpc_core : : PemKeyCertPairList bad_identity_pair_ ;
StringMatcher server_san_exact_ ;
StringMatcher server_san_prefix_ ;
StringMatcher server_san_suffix_ ;
StringMatcher server_san_contains_ ;
StringMatcher server_san_regex_ ;
StringMatcher bad_san_1_ ;
StringMatcher bad_san_2_ ;
std : : vector < std : : string > authenticated_identity_ ;
std : : vector < std : : string > fallback_authenticated_identity_ ;
} ;
TEST_P ( XdsSecurityTest , UnknownTransportSocket ) {
auto cluster = default_cluster_ ;
auto * transport_socket = cluster . mutable_transport_socket ( ) ;
transport_socket - > set_name ( " unknown_transport_socket " ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" Unrecognized transport socket: unknown_transport_socket " ) ) ;
}
TEST_P ( XdsSecurityTest ,
TLSConfigurationWithoutValidationContextCertificateProviderInstance ) {
auto cluster = default_cluster_ ;
auto * transport_socket = cluster . mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " TLS configuration provided but no "
" ca_certificate_provider_instance found. " ) ) ;
}
TEST_P (
XdsSecurityTest ,
MatchSubjectAltNamesProvidedWithoutValidationContextCertificateProviderInstance ) {
auto cluster = default_cluster_ ;
auto * transport_socket = cluster . mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
UpstreamTlsContext upstream_tls_context ;
auto * validation_context = upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( ) ;
* validation_context - > add_match_subject_alt_names ( ) = server_san_exact_ ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( upstream_tls_context ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " TLS configuration provided but no "
" ca_certificate_provider_instance found. " ) ) ;
}
TEST_P (
XdsSecurityTest ,
TlsCertificateProviderInstanceWithoutValidationContextCertificateProviderInstance ) {
auto cluster = default_cluster_ ;
auto * transport_socket = cluster . mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
UpstreamTlsContext upstream_tls_context ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_tls_certificate_provider_instance ( )
- > set_instance_name ( std : : string ( " fake_plugin1 " ) ) ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( upstream_tls_context ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " TLS configuration provided but no "
" ca_certificate_provider_instance found. " ) ) ;
}
TEST_P ( XdsSecurityTest , RegexSanMatcherDoesNotAllowIgnoreCase ) {
auto cluster = default_cluster_ ;
auto * transport_socket = cluster . mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
UpstreamTlsContext upstream_tls_context ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > mutable_ca_certificate_provider_instance ( )
- > set_instance_name ( std : : string ( " fake_plugin1 " ) ) ;
auto * validation_context = upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( ) ;
StringMatcher matcher ;
matcher . mutable_safe_regex ( ) - > mutable_google_re2 ( ) ;
matcher . mutable_safe_regex ( ) - > set_regex (
" (foo|waterzooi).test.google.(fr|be) " ) ;
matcher . set_ignore_case ( true ) ;
* validation_context - > add_match_subject_alt_names ( ) = matcher ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( upstream_tls_context ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" StringMatcher: ignore_case has no effect for SAFE_REGEX. " ) ) ;
}
TEST_P ( XdsSecurityTest , UnknownRootCertificateProvider ) {
auto cluster = default_cluster_ ;
auto * transport_socket = cluster . mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
UpstreamTlsContext upstream_tls_context ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > mutable_ca_certificate_provider_instance ( )
- > set_instance_name ( " unknown " ) ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( upstream_tls_context ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" Unrecognized certificate provider instance name: unknown " ) ) ;
}
TEST_P ( XdsSecurityTest , UnknownIdentityCertificateProvider ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
auto cluster = default_cluster_ ;
auto * transport_socket = cluster . mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
UpstreamTlsContext upstream_tls_context ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_tls_certificate_provider_instance ( )
- > set_instance_name ( " unknown " ) ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > mutable_ca_certificate_provider_instance ( )
- > set_instance_name ( " fake_plugin1 " ) ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( upstream_tls_context ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" Unrecognized certificate provider instance name: unknown " ) ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest ,
NacksCertificateValidationContextWithVerifyCertificateSpki ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
auto cluster = default_cluster_ ;
auto * transport_socket = cluster . mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
UpstreamTlsContext upstream_tls_context ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > mutable_ca_certificate_provider_instance ( )
- > set_instance_name ( " fake_plugin1 " ) ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > add_verify_certificate_spki ( " spki " ) ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( upstream_tls_context ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr (
" CertificateValidationContext: verify_certificate_spki unsupported " ) ) ;
}
TEST_P ( XdsSecurityTest ,
NacksCertificateValidationContextWithVerifyCertificateHash ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
auto cluster = default_cluster_ ;
auto * transport_socket = cluster . mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
UpstreamTlsContext upstream_tls_context ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > mutable_ca_certificate_provider_instance ( )
- > set_instance_name ( " fake_plugin1 " ) ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > add_verify_certificate_hash ( " hash " ) ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( upstream_tls_context ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr (
" CertificateValidationContext: verify_certificate_hash unsupported " ) ) ;
}
TEST_P ( XdsSecurityTest ,
NacksCertificateValidationContextWithRequireSignedCertificateTimes ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
auto cluster = default_cluster_ ;
auto * transport_socket = cluster . mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
UpstreamTlsContext upstream_tls_context ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > mutable_ca_certificate_provider_instance ( )
- > set_instance_name ( " fake_plugin1 " ) ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > mutable_require_signed_certificate_timestamp ( )
- > set_value ( true ) ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( upstream_tls_context ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " CertificateValidationContext: "
" require_signed_certificate_timestamp unsupported " ) ) ;
}
TEST_P ( XdsSecurityTest , NacksCertificateValidationContextWithCrl ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
auto cluster = default_cluster_ ;
auto * transport_socket = cluster . mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
UpstreamTlsContext upstream_tls_context ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > mutable_ca_certificate_provider_instance ( )
- > set_instance_name ( " fake_plugin1 " ) ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > mutable_crl ( ) ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( upstream_tls_context ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " CertificateValidationContext: crl unsupported " ) ) ;
}
TEST_P ( XdsSecurityTest ,
NacksCertificateValidationContextWithCustomValidatorConfig ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
auto cluster = default_cluster_ ;
auto * transport_socket = cluster . mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
UpstreamTlsContext upstream_tls_context ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > mutable_ca_certificate_provider_instance ( )
- > set_instance_name ( " fake_plugin1 " ) ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > mutable_custom_validator_config ( ) ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( upstream_tls_context ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr (
" CertificateValidationContext: custom_validator_config unsupported " ) ) ;
}
TEST_P ( XdsSecurityTest , NacksValidationContextSdsSecretConfig ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
auto cluster = default_cluster_ ;
auto * transport_socket = cluster . mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
UpstreamTlsContext upstream_tls_context ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context_sds_secret_config ( ) ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( upstream_tls_context ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " validation_context_sds_secret_config unsupported " ) ) ;
}
TEST_P ( XdsSecurityTest , NacksTlsParams ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
auto cluster = default_cluster_ ;
auto * transport_socket = cluster . mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
UpstreamTlsContext upstream_tls_context ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > mutable_ca_certificate_provider_instance ( )
- > set_instance_name ( " fake_plugin1 " ) ;
upstream_tls_context . mutable_common_tls_context ( ) - > mutable_tls_params ( ) ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( upstream_tls_context ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " tls_params unsupported " ) ) ;
}
TEST_P ( XdsSecurityTest , NacksCustomHandshaker ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
auto cluster = default_cluster_ ;
auto * transport_socket = cluster . mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
UpstreamTlsContext upstream_tls_context ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > mutable_ca_certificate_provider_instance ( )
- > set_instance_name ( " fake_plugin1 " ) ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_custom_handshaker ( ) ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( upstream_tls_context ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " custom_handshaker unsupported " ) ) ;
}
TEST_P ( XdsSecurityTest , NacksTlsCertificates ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
auto cluster = default_cluster_ ;
auto * transport_socket = cluster . mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
UpstreamTlsContext upstream_tls_context ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > mutable_ca_certificate_provider_instance ( )
- > set_instance_name ( " fake_plugin1 " ) ;
upstream_tls_context . mutable_common_tls_context ( ) - > add_tls_certificates ( ) ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( upstream_tls_context ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " tls_certificates unsupported " ) ) ;
}
TEST_P ( XdsSecurityTest , NacksTlsCertificateSdsSecretConfigs ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
auto cluster = default_cluster_ ;
auto * transport_socket = cluster . mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
UpstreamTlsContext upstream_tls_context ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > mutable_ca_certificate_provider_instance ( )
- > set_instance_name ( " fake_plugin1 " ) ;
upstream_tls_context . mutable_common_tls_context ( )
- > add_tls_certificate_sds_secret_configs ( ) ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( upstream_tls_context ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
const auto response_state = WaitForCdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " tls_certificate_sds_secret_configs unsupported " ) ) ;
}
TEST_P ( XdsSecurityTest , TestTlsConfigurationInCombinedValidationContext ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
auto cluster = default_cluster_ ;
auto * transport_socket = cluster . mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
UpstreamTlsContext upstream_tls_context ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_combined_validation_context ( )
- > mutable_default_validation_context ( )
- > mutable_ca_certificate_provider_instance ( )
- > set_instance_name ( " fake_plugin1 " ) ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( upstream_tls_context ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
WaitForBackend ( 0 , WaitForBackendOptions ( ) . set_allow_failures ( true ) ) ;
Status status = SendRpc ( ) ;
EXPECT_TRUE ( status . ok ( ) ) < < " code= " < < status . error_code ( )
< < " message= " < < status . error_message ( ) ;
}
// TODO(yashykt): Remove this test once we stop supporting old fields
TEST_P ( XdsSecurityTest ,
TestTlsConfigurationInValidationContextCertificateProviderInstance ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
auto cluster = default_cluster_ ;
auto * transport_socket = cluster . mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
UpstreamTlsContext upstream_tls_context ;
upstream_tls_context . mutable_common_tls_context ( )
- > mutable_combined_validation_context ( )
- > mutable_validation_context_certificate_provider_instance ( )
- > set_instance_name ( " fake_plugin1 " ) ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( upstream_tls_context ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
WaitForBackend ( 0 , WaitForBackendOptions ( ) . set_allow_failures ( true ) ) ;
Status status = SendRpc ( ) ;
EXPECT_TRUE ( status . ok ( ) ) < < " code= " < < status . error_code ( )
< < " message= " < < status . error_message ( ) ;
}
TEST_P ( XdsSecurityTest , TestMtlsConfigurationWithNoSanMatchers ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " fake_plugin1 " ,
" " , { } , authenticated_identity_ ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestMtlsConfigurationWithExactSanMatcher ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " fake_plugin1 " ,
" " , { server_san_exact_ } ,
authenticated_identity_ ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestMtlsConfigurationWithPrefixSanMatcher ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " fake_plugin1 " ,
" " , { server_san_prefix_ } ,
authenticated_identity_ ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestMtlsConfigurationWithSuffixSanMatcher ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " fake_plugin1 " ,
" " , { server_san_suffix_ } ,
authenticated_identity_ ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestMtlsConfigurationWithContainsSanMatcher ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " fake_plugin1 " ,
" " , { server_san_contains_ } ,
authenticated_identity_ ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestMtlsConfigurationWithRegexSanMatcher ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " fake_plugin1 " ,
" " , { server_san_regex_ } ,
authenticated_identity_ ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestMtlsConfigurationWithSanMatchersUpdate ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration (
" fake_plugin1 " , " " , " fake_plugin1 " , " " ,
{ server_san_exact_ , server_san_prefix_ } , authenticated_identity_ ) ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " fake_plugin1 " ,
" " , { bad_san_1_ , bad_san_2_ } , { } ,
true /* failure */ ) ;
UpdateAndVerifyXdsSecurityConfiguration (
" fake_plugin1 " , " " , " fake_plugin1 " , " " ,
{ server_san_prefix_ , server_san_regex_ } , authenticated_identity_ ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestMtlsConfigurationWithRootPluginUpdate ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
FakeCertificateProvider : : CertDataMap fake2_cert_map = {
{ " " , { bad_root_cert_ , bad_identity_pair_ } } } ;
g_fake2_cert_data_map = & fake2_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " fake_plugin1 " ,
" " , { server_san_exact_ } ,
authenticated_identity_ ) ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin2 " /* bad root */ , " " ,
" fake_plugin1 " , " " , { } , { } ,
true /* failure */ ) ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " fake_plugin1 " ,
" " , { server_san_exact_ } ,
authenticated_identity_ ) ;
g_fake1_cert_data_map = nullptr ;
g_fake2_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestMtlsConfigurationWithIdentityPluginUpdate ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
FakeCertificateProvider : : CertDataMap fake2_cert_map = {
{ " " , { root_cert_ , fallback_identity_pair_ } } } ;
g_fake2_cert_data_map = & fake2_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " fake_plugin1 " ,
" " , { server_san_exact_ } ,
authenticated_identity_ ) ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " fake_plugin2 " ,
" " , { server_san_exact_ } ,
fallback_authenticated_identity_ ) ;
g_fake1_cert_data_map = nullptr ;
g_fake2_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestMtlsConfigurationWithBothPluginsUpdated ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
FakeCertificateProvider : : CertDataMap fake2_cert_map = {
{ " " , { bad_root_cert_ , bad_identity_pair_ } } ,
{ " good " , { root_cert_ , fallback_identity_pair_ } } } ;
g_fake2_cert_data_map = & fake2_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin2 " , " " , " fake_plugin2 " ,
" " , { } , { } , true /* failure */ ) ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " fake_plugin1 " ,
" " , { server_san_prefix_ } ,
authenticated_identity_ ) ;
UpdateAndVerifyXdsSecurityConfiguration (
" fake_plugin2 " , " good " , " fake_plugin2 " , " good " , { server_san_prefix_ } ,
fallback_authenticated_identity_ ) ;
g_fake1_cert_data_map = nullptr ;
g_fake2_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestMtlsConfigurationWithRootCertificateNameUpdate ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } ,
{ " bad " , { bad_root_cert_ , bad_identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " fake_plugin1 " ,
" " , { server_san_regex_ } ,
authenticated_identity_ ) ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " bad " , " fake_plugin1 " ,
" " , { server_san_regex_ } , { } ,
true /* failure */ ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest ,
TestMtlsConfigurationWithIdentityCertificateNameUpdate ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } ,
{ " bad " , { bad_root_cert_ , bad_identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " fake_plugin1 " ,
" " , { server_san_exact_ } ,
authenticated_identity_ ) ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " fake_plugin1 " ,
" bad " , { server_san_exact_ } , { } ,
true /* failure */ ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest ,
TestMtlsConfigurationWithIdentityCertificateNameUpdateGoodCerts ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } ,
{ " good " , { root_cert_ , fallback_identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " fake_plugin1 " ,
" " , { server_san_exact_ } ,
authenticated_identity_ ) ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " fake_plugin1 " ,
" good " , { server_san_exact_ } ,
fallback_authenticated_identity_ ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestMtlsConfigurationWithBothCertificateNamesUpdated ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } ,
{ " bad " , { bad_root_cert_ , bad_identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " bad " , " fake_plugin1 " ,
" bad " , { server_san_prefix_ } , { } ,
true /* failure */ ) ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " fake_plugin1 " ,
" " , { server_san_prefix_ } ,
authenticated_identity_ ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestTlsConfigurationWithNoSanMatchers ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " " , " " , { } ,
{ } /* unauthenticated */ ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestTlsConfigurationWithSanMatchers ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration (
" fake_plugin1 " , " " , " " , " " ,
{ server_san_exact_ , server_san_prefix_ , server_san_regex_ } ,
{ } /* unauthenticated */ ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestTlsConfigurationWithSanMatchersUpdate ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration (
" fake_plugin1 " , " " , " " , " " , { server_san_exact_ , server_san_prefix_ } ,
{ } /* unauthenticated */ ) ;
UpdateAndVerifyXdsSecurityConfiguration (
" fake_plugin1 " , " " , " " , " " , { bad_san_1_ , bad_san_2_ } ,
{ } /* unauthenticated */ , true /* failure */ ) ;
UpdateAndVerifyXdsSecurityConfiguration (
" fake_plugin1 " , " " , " " , " " , { server_san_prefix_ , server_san_regex_ } ,
{ } /* unauthenticated */ ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestTlsConfigurationWithRootCertificateNameUpdate ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } ,
{ " bad " , { bad_root_cert_ , bad_identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " " , " " ,
{ server_san_exact_ } ,
{ } /* unauthenticated */ ) ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " bad " , " " , " " ,
{ server_san_exact_ } , { } ,
true /* failure */ ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestTlsConfigurationWithRootPluginUpdate ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
FakeCertificateProvider : : CertDataMap fake2_cert_map = {
{ " " , { bad_root_cert_ , bad_identity_pair_ } } } ;
g_fake2_cert_data_map = & fake2_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " " , " " ,
{ server_san_exact_ } ,
{ } /* unauthenticated */ ) ;
UpdateAndVerifyXdsSecurityConfiguration (
" fake_plugin2 " , " " , " " , " " , { server_san_exact_ } , { } , true /* failure */ ) ;
g_fake1_cert_data_map = nullptr ;
g_fake2_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestFallbackConfiguration ) {
UpdateAndVerifyXdsSecurityConfiguration ( " " , " " , " " , " " , { } ,
fallback_authenticated_identity_ ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestMtlsToTls ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " fake_plugin1 " ,
" " , { server_san_exact_ } ,
authenticated_identity_ ) ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " " , " " ,
{ server_san_exact_ } ,
{ } /* unauthenticated */ ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestMtlsToFallback ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " fake_plugin1 " ,
" " , { server_san_exact_ } ,
authenticated_identity_ ) ;
UpdateAndVerifyXdsSecurityConfiguration ( " " , " " , " " , " " , { } ,
fallback_authenticated_identity_ ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestTlsToMtls ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " " , " " ,
{ server_san_exact_ } ,
{ } /* unauthenticated */ ) ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " fake_plugin1 " ,
" " , { server_san_exact_ } ,
authenticated_identity_ ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestTlsToFallback ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " " , " " ,
{ server_san_exact_ } ,
{ } /* unauthenticated */ ) ;
UpdateAndVerifyXdsSecurityConfiguration ( " " , " " , " " , " " , { } ,
fallback_authenticated_identity_ ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestFallbackToMtls ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration ( " " , " " , " " , " " , { } ,
fallback_authenticated_identity_ ) ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " fake_plugin1 " ,
" " , { server_san_exact_ } ,
authenticated_identity_ ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestFallbackToTls ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
UpdateAndVerifyXdsSecurityConfiguration ( " " , " " , " " , " " , { } ,
fallback_authenticated_identity_ ) ;
UpdateAndVerifyXdsSecurityConfiguration ( " fake_plugin1 " , " " , " " , " " ,
{ server_san_exact_ } ,
{ } /* unauthenticated */ ) ;
g_fake1_cert_data_map = nullptr ;
}
TEST_P ( XdsSecurityTest , TestFileWatcherCertificateProvider ) {
UpdateAndVerifyXdsSecurityConfiguration ( " file_plugin " , " " , " file_plugin " , " " ,
{ server_san_exact_ } ,
authenticated_identity_ ) ;
}
class XdsEnabledServerTest : public XdsEnd2endTest {
protected :
void SetUp ( ) override {
XdsEnd2endTest : : SetUp ( ) ;
CreateBackends ( 1 , /*xds_enabled=*/ true ) ;
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
}
} ;
TEST_P ( XdsEnabledServerTest , Basic ) {
backends_ [ 0 ] - > Start ( ) ;
WaitForBackend ( 0 ) ;
}
TEST_P ( XdsEnabledServerTest , BadLdsUpdateNoApiListenerNorAddress ) {
Listener listener = default_server_listener_ ;
listener . clear_address ( ) ;
listener . set_name (
absl : : StrCat ( " grpc/server?xds.resource.listening_address= " ,
ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ) ;
balancer_ - > ads_service ( ) - > SetLdsResource ( listener ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " Listener has neither address nor ApiListener " ) ) ;
}
TEST_P ( XdsEnabledServerTest , BadLdsUpdateBothApiListenerAndAddress ) {
Listener listener = default_server_listener_ ;
listener . mutable_api_listener ( ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " Listener has both address and ApiListener " ) ) ;
}
TEST_P ( XdsEnabledServerTest , NacksNonZeroXffNumTrusterHops ) {
Listener listener = default_server_listener_ ;
HttpConnectionManager http_connection_manager =
ServerHcmAccessor ( ) . Unpack ( listener ) ;
http_connection_manager . set_xff_num_trusted_hops ( 1 ) ;
ServerHcmAccessor ( ) . Pack ( http_connection_manager , & listener ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " 'xff_num_trusted_hops' must be zero " ) ) ;
}
TEST_P ( XdsEnabledServerTest , NacksNonEmptyOriginalIpDetectionExtensions ) {
Listener listener = default_server_listener_ ;
HttpConnectionManager http_connection_manager =
ServerHcmAccessor ( ) . Unpack ( listener ) ;
http_connection_manager . add_original_ip_detection_extensions ( ) ;
ServerHcmAccessor ( ) . Pack ( http_connection_manager , & listener ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " 'original_ip_detection_extensions' must be empty " ) ) ;
}
TEST_P ( XdsEnabledServerTest , UnsupportedL4Filter ) {
Listener listener = default_server_listener_ ;
listener . mutable_default_filter_chain ( ) - > clear_filters ( ) ;
listener . mutable_default_filter_chain ( ) - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom ( default_listener_ /* any proto object other than HttpConnectionManager */ ) ;
balancer_ - > ads_service ( ) - > SetLdsResource (
PopulateServerListenerNameAndPort ( listener , backends_ [ 0 ] - > port ( ) ) ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " Unsupported filter type " ) ) ;
}
TEST_P ( XdsEnabledServerTest , NacksEmptyHttpFilterList ) {
Listener listener = default_server_listener_ ;
HttpConnectionManager http_connection_manager =
ServerHcmAccessor ( ) . Unpack ( listener ) ;
http_connection_manager . clear_http_filters ( ) ;
ServerHcmAccessor ( ) . Pack ( http_connection_manager , & listener ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " Expected at least one HTTP filter " ) ) ;
}
TEST_P ( XdsEnabledServerTest , UnsupportedHttpFilter ) {
Listener listener = default_server_listener_ ;
HttpConnectionManager http_connection_manager =
ServerHcmAccessor ( ) . Unpack ( listener ) ;
http_connection_manager . clear_http_filters ( ) ;
auto * http_filter = http_connection_manager . add_http_filters ( ) ;
http_filter - > set_name ( " grpc.testing.unsupported_http_filter " ) ;
http_filter - > mutable_typed_config ( ) - > set_type_url (
" grpc.testing.unsupported_http_filter " ) ;
http_filter = http_connection_manager . add_http_filters ( ) ;
http_filter - > set_name ( " router " ) ;
http_filter - > mutable_typed_config ( ) - > PackFrom (
envoy : : extensions : : filters : : http : : router : : v3 : : Router ( ) ) ;
ServerHcmAccessor ( ) . Pack ( http_connection_manager , & listener ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " no filter registered for config type "
" grpc.testing.unsupported_http_filter " ) ) ;
}
TEST_P ( XdsEnabledServerTest , HttpFilterNotSupportedOnServer ) {
Listener listener = default_server_listener_ ;
HttpConnectionManager http_connection_manager =
ServerHcmAccessor ( ) . Unpack ( listener ) ;
http_connection_manager . clear_http_filters ( ) ;
auto * http_filter = http_connection_manager . add_http_filters ( ) ;
http_filter - > set_name ( " grpc.testing.client_only_http_filter " ) ;
http_filter - > mutable_typed_config ( ) - > set_type_url (
" grpc.testing.client_only_http_filter " ) ;
http_filter = http_connection_manager . add_http_filters ( ) ;
http_filter - > set_name ( " router " ) ;
http_filter - > mutable_typed_config ( ) - > PackFrom (
envoy : : extensions : : filters : : http : : router : : v3 : : Router ( ) ) ;
ServerHcmAccessor ( ) . Pack ( http_connection_manager , & listener ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " Filter grpc.testing.client_only_http_filter is not "
" supported on servers " ) ) ;
}
TEST_P ( XdsEnabledServerTest ,
HttpFilterNotSupportedOnServerIgnoredWhenOptional ) {
Listener listener = default_server_listener_ ;
HttpConnectionManager http_connection_manager =
ServerHcmAccessor ( ) . Unpack ( listener ) ;
http_connection_manager . clear_http_filters ( ) ;
auto * http_filter = http_connection_manager . add_http_filters ( ) ;
http_filter - > set_name ( " grpc.testing.client_only_http_filter " ) ;
http_filter - > mutable_typed_config ( ) - > set_type_url (
" grpc.testing.client_only_http_filter " ) ;
http_filter - > set_is_optional ( true ) ;
http_filter = http_connection_manager . add_http_filters ( ) ;
http_filter - > set_name ( " router " ) ;
http_filter - > mutable_typed_config ( ) - > PackFrom (
envoy : : extensions : : filters : : http : : router : : v3 : : Router ( ) ) ;
ServerHcmAccessor ( ) . Pack ( http_connection_manager , & listener ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
WaitForBackend ( 0 ) ;
auto response_state = balancer_ - > ads_service ( ) - > lds_response_state ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) ;
EXPECT_EQ ( response_state - > state , AdsServiceImpl : : ResponseState : : ACKED ) ;
}
// Verify that a mismatch of listening address results in "not serving"
// status.
TEST_P ( XdsEnabledServerTest , ListenerAddressMismatch ) {
Listener listener = default_server_listener_ ;
// Set a different listening address in the LDS update
listener . mutable_address ( ) - > mutable_socket_address ( ) - > set_address (
" 192.168.1.1 " ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : FAILED_PRECONDITION ) ;
}
TEST_P ( XdsEnabledServerTest , UseOriginalDstNotSupported ) {
Listener listener = default_server_listener_ ;
listener . mutable_use_original_dst ( ) - > set_value ( true ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " Field \' use_original_dst \' is not supported. " ) ) ;
}
class XdsServerSecurityTest : public XdsEnd2endTest {
protected :
void SetUp ( ) override {
BootstrapBuilder builder = BootstrapBuilder ( ) ;
builder . AddCertificateProviderPlugin ( " fake_plugin1 " , " fake1 " ) ;
builder . AddCertificateProviderPlugin ( " fake_plugin2 " , " fake2 " ) ;
std : : vector < std : : string > fields ;
fields . push_back ( absl : : StrFormat ( " \" certificate_file \" : \" %s \" " ,
kClientCertPath ) ) ;
fields . push_back ( absl : : StrFormat ( " \" private_key_file \" : \" %s \" " ,
kClientKeyPath ) ) ;
fields . push_back ( absl : : StrFormat ( " \" ca_certificate_file \" : \" %s \" " ,
kCaCertPath ) ) ;
builder . AddCertificateProviderPlugin ( " file_plugin " , " file_watcher " ,
absl : : StrJoin ( fields , " , \n " ) ) ;
InitClient ( builder ) ;
CreateBackends ( 1 , /*xds_enabled=*/ true ) ;
root_cert_ = ReadFile ( kCaCertPath ) ;
bad_root_cert_ = ReadFile ( kBadClientCertPath ) ;
identity_pair_ = ReadTlsIdentityPair ( kServerKeyPath , kServerCertPath ) ;
bad_identity_pair_ =
ReadTlsIdentityPair ( kBadClientKeyPath , kBadClientCertPath ) ;
identity_pair_2_ = ReadTlsIdentityPair ( kClientKeyPath , kClientCertPath ) ;
server_authenticated_identity_ = { " *.test.google.fr " ,
" waterzooi.test.google.be " ,
" *.test.youtube.com " , " 192.168.1.3 " } ;
server_authenticated_identity_2_ = { " testclient " } ;
client_authenticated_identity_ = { " *.test.google.fr " ,
" waterzooi.test.google.be " ,
" *.test.youtube.com " , " 192.168.1.3 " } ;
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
}
void TearDown ( ) override {
g_fake1_cert_data_map = nullptr ;
g_fake2_cert_data_map = nullptr ;
XdsEnd2endTest : : TearDown ( ) ;
}
void SetLdsUpdate ( absl : : string_view root_instance_name ,
absl : : string_view root_certificate_name ,
absl : : string_view identity_instance_name ,
absl : : string_view identity_certificate_name ,
bool require_client_certificates ) {
Listener listener = default_server_listener_ ;
auto * filter_chain = listener . mutable_default_filter_chain ( ) ;
if ( ! identity_instance_name . empty ( ) ) {
auto * transport_socket = filter_chain - > mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
DownstreamTlsContext downstream_tls_context ;
downstream_tls_context . mutable_common_tls_context ( )
- > mutable_tls_certificate_provider_instance ( )
- > set_instance_name ( std : : string ( identity_instance_name ) ) ;
downstream_tls_context . mutable_common_tls_context ( )
- > mutable_tls_certificate_provider_instance ( )
- > set_certificate_name ( std : : string ( identity_certificate_name ) ) ;
if ( ! root_instance_name . empty ( ) ) {
downstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > mutable_ca_certificate_provider_instance ( )
- > set_instance_name ( std : : string ( root_instance_name ) ) ;
downstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > mutable_ca_certificate_provider_instance ( )
- > set_certificate_name ( std : : string ( root_certificate_name ) ) ;
downstream_tls_context . mutable_require_client_certificate ( ) - > set_value (
require_client_certificates ) ;
}
transport_socket - > mutable_typed_config ( ) - > PackFrom (
downstream_tls_context ) ;
}
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
}
std : : shared_ptr < grpc : : Channel > CreateMtlsChannel ( ) {
ChannelArguments args ;
// Override target name for host name check
args . SetString ( GRPC_SSL_TARGET_NAME_OVERRIDE_ARG ,
ipv6_only_ ? " ::1 " : " 127.0.0.1 " ) ;
args . SetInt ( GRPC_ARG_USE_LOCAL_SUBCHANNEL_POOL , 1 ) ;
std : : string uri = absl : : StrCat (
ipv6_only_ ? " ipv6:[::1]: " : " ipv4:127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ;
IdentityKeyCertPair key_cert_pair ;
key_cert_pair . private_key = ReadFile ( kServerKeyPath ) ;
key_cert_pair . certificate_chain = ReadFile ( kServerCertPath ) ;
std : : vector < IdentityKeyCertPair > identity_key_cert_pairs ;
identity_key_cert_pairs . emplace_back ( key_cert_pair ) ;
auto certificate_provider = std : : make_shared < StaticDataCertificateProvider > (
ReadFile ( kCaCertPath ) , identity_key_cert_pairs ) ;
grpc : : experimental : : TlsChannelCredentialsOptions options ;
options . set_certificate_provider ( std : : move ( certificate_provider ) ) ;
options . watch_root_certs ( ) ;
options . watch_identity_key_cert_pairs ( ) ;
auto verifier =
ExternalCertificateVerifier : : Create < SyncCertificateVerifier > ( true ) ;
options . set_verify_server_certs ( true ) ;
options . set_certificate_verifier ( std : : move ( verifier ) ) ;
auto channel_creds = grpc : : experimental : : TlsCredentials ( options ) ;
GPR_ASSERT ( channel_creds . get ( ) ! = nullptr ) ;
return CreateCustomChannel ( uri , channel_creds , args ) ;
}
std : : shared_ptr < grpc : : Channel > CreateTlsChannel ( ) {
ChannelArguments args ;
// Override target name for host name check
args . SetString ( GRPC_SSL_TARGET_NAME_OVERRIDE_ARG ,
ipv6_only_ ? " ::1 " : " 127.0.0.1 " ) ;
args . SetInt ( GRPC_ARG_USE_LOCAL_SUBCHANNEL_POOL , 1 ) ;
std : : string uri = absl : : StrCat (
ipv6_only_ ? " ipv6:[::1]: " : " ipv4:127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ;
auto certificate_provider =
std : : make_shared < StaticDataCertificateProvider > ( ReadFile ( kCaCertPath ) ) ;
grpc : : experimental : : TlsChannelCredentialsOptions options ;
options . set_certificate_provider ( std : : move ( certificate_provider ) ) ;
options . watch_root_certs ( ) ;
auto verifier =
ExternalCertificateVerifier : : Create < SyncCertificateVerifier > ( true ) ;
options . set_verify_server_certs ( true ) ;
options . set_certificate_verifier ( std : : move ( verifier ) ) ;
auto channel_creds = grpc : : experimental : : TlsCredentials ( options ) ;
GPR_ASSERT ( channel_creds . get ( ) ! = nullptr ) ;
return CreateCustomChannel ( uri , channel_creds , args ) ;
}
std : : shared_ptr < grpc : : Channel > CreateInsecureChannel (
bool use_put_requests = false ) {
ChannelArguments args ;
// Override target name for host name check
args . SetString ( GRPC_SSL_TARGET_NAME_OVERRIDE_ARG ,
ipv6_only_ ? " ::1 " : " 127.0.0.1 " ) ;
args . SetInt ( GRPC_ARG_USE_LOCAL_SUBCHANNEL_POOL , 1 ) ;
if ( use_put_requests ) {
args . SetInt ( GRPC_ARG_TEST_ONLY_USE_PUT_REQUESTS , 1 ) ;
}
std : : string uri = absl : : StrCat (
ipv6_only_ ? " ipv6:[::1]: " : " ipv4:127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ;
return CreateCustomChannel ( uri , InsecureChannelCredentials ( ) , args ) ;
}
void SendRpc (
std : : function < std : : shared_ptr < grpc : : Channel > ( ) > channel_creator ,
std : : vector < std : : string > expected_server_identity ,
std : : vector < std : : string > expected_client_identity ,
bool test_expects_failure = false ,
absl : : optional < grpc : : StatusCode > expected_status = absl : : nullopt ) {
gpr_log ( GPR_INFO , " Sending RPC " ) ;
int num_tries = 0 ;
constexpr int kRetryCount = 100 ;
auto overall_deadline = absl : : Now ( ) + absl : : Seconds ( 5 ) ;
for ( ; num_tries < kRetryCount | | absl : : Now ( ) < overall_deadline ;
num_tries + + ) {
auto channel = channel_creator ( ) ;
auto stub = grpc : : testing : : EchoTestService : : NewStub ( channel ) ;
ClientContext context ;
context . set_wait_for_ready ( true ) ;
context . set_deadline ( grpc_timeout_milliseconds_to_deadline ( 2000 ) ) ;
EchoRequest request ;
// TODO(yashykt): Skipping the cancelled check on the server since the
// server's graceful shutdown isn't as per spec and the check isn't
// necessary for what we want to test here anyway.
// https://github.com/grpc/grpc/issues/24237
request . mutable_param ( ) - > set_skip_cancelled_check ( true ) ;
request . set_message ( kRequestMessage ) ;
EchoResponse response ;
Status status = stub - > Echo ( & context , request , & response ) ;
if ( test_expects_failure ) {
if ( status . ok ( ) ) {
gpr_log ( GPR_ERROR , " RPC succeeded. Failure expected. Trying again. " ) ;
continue ;
}
if ( expected_status . has_value ( ) & &
* expected_status ! = status . error_code ( ) ) {
gpr_log ( GPR_ERROR ,
" Expected status does not match Actual(%d) vs Expected(%d) " ,
status . error_code ( ) , * expected_status ) ;
continue ;
}
} else {
if ( ! status . ok ( ) ) {
gpr_log ( GPR_ERROR , " RPC failed. code=%d message=%s Trying again. " ,
status . error_code ( ) , status . error_message ( ) . c_str ( ) ) ;
continue ;
}
EXPECT_EQ ( response . message ( ) , kRequestMessage ) ;
std : : vector < std : : string > peer_identity ;
for ( const auto & entry : context . auth_context ( ) - > GetPeerIdentity ( ) ) {
peer_identity . emplace_back (
std : : string ( entry . data ( ) , entry . size ( ) ) . c_str ( ) ) ;
}
if ( peer_identity ! = expected_server_identity ) {
gpr_log ( GPR_ERROR ,
" Expected server identity does not match. (actual) %s vs "
" (expected) %s Trying again. " ,
absl : : StrJoin ( peer_identity , " , " ) . c_str ( ) ,
absl : : StrJoin ( expected_server_identity , " , " ) . c_str ( ) ) ;
continue ;
}
if ( backends_ [ 0 ] - > backend_service ( ) - > last_peer_identity ( ) ! =
expected_client_identity ) {
gpr_log (
GPR_ERROR ,
" Expected client identity does not match. (actual) %s vs "
" (expected) %s Trying again. " ,
absl : : StrJoin (
backends_ [ 0 ] - > backend_service ( ) - > last_peer_identity ( ) , " , " )
. c_str ( ) ,
absl : : StrJoin ( expected_client_identity , " , " ) . c_str ( ) ) ;
continue ;
}
}
break ;
}
EXPECT_LT ( num_tries , kRetryCount ) ;
}
std : : string root_cert_ ;
std : : string bad_root_cert_ ;
grpc_core : : PemKeyCertPairList identity_pair_ ;
grpc_core : : PemKeyCertPairList bad_identity_pair_ ;
grpc_core : : PemKeyCertPairList identity_pair_2_ ;
std : : vector < std : : string > server_authenticated_identity_ ;
std : : vector < std : : string > server_authenticated_identity_2_ ;
std : : vector < std : : string > client_authenticated_identity_ ;
} ;
TEST_P ( XdsServerSecurityTest , UnknownTransportSocket ) {
Listener listener = default_server_listener_ ;
auto * filter_chain = listener . mutable_default_filter_chain ( ) ;
auto * transport_socket = filter_chain - > mutable_transport_socket ( ) ;
transport_socket - > set_name ( " unknown_transport_socket " ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" Unrecognized transport socket: unknown_transport_socket " ) ) ;
}
TEST_P ( XdsServerSecurityTest , NacksRequireSNI ) {
Listener listener = default_server_listener_ ;
auto * filter_chain = listener . mutable_default_filter_chain ( ) ;
auto * transport_socket = filter_chain - > mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
DownstreamTlsContext downstream_tls_context ;
downstream_tls_context . mutable_common_tls_context ( )
- > mutable_tls_certificate_provider_instance ( )
- > set_instance_name ( " fake_plugin1 " ) ;
downstream_tls_context . mutable_require_sni ( ) - > set_value ( true ) ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( downstream_tls_context ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " require_sni: unsupported " ) ) ;
}
TEST_P ( XdsServerSecurityTest , NacksOcspStaplePolicyOtherThanLenientStapling ) {
Listener listener = default_server_listener_ ;
auto * filter_chain = listener . mutable_default_filter_chain ( ) ;
auto * transport_socket = filter_chain - > mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
DownstreamTlsContext downstream_tls_context ;
downstream_tls_context . mutable_common_tls_context ( )
- > mutable_tls_certificate_provider_instance ( )
- > set_instance_name ( " fake_plugin1 " ) ;
downstream_tls_context . set_ocsp_staple_policy (
envoy : : extensions : : transport_sockets : : tls : : v3 : :
DownstreamTlsContext_OcspStaplePolicy_STRICT_STAPLING ) ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( downstream_tls_context ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" ocsp_staple_policy: Only LENIENT_STAPLING supported " ) ) ;
}
TEST_P (
XdsServerSecurityTest ,
NacksRequiringClientCertificateWithoutValidationCertificateProviderInstance ) {
Listener listener = default_server_listener_ ;
auto * filter_chain = listener . mutable_default_filter_chain ( ) ;
auto * transport_socket = filter_chain - > mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
DownstreamTlsContext downstream_tls_context ;
downstream_tls_context . mutable_common_tls_context ( )
- > mutable_tls_certificate_provider_instance ( )
- > set_instance_name ( " fake_plugin1 " ) ;
downstream_tls_context . mutable_require_client_certificate ( ) - > set_value ( true ) ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( downstream_tls_context ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" TLS configuration requires client certificates but no "
" certificate provider instance specified for validation. " ) ) ;
}
TEST_P ( XdsServerSecurityTest ,
NacksTlsConfigurationWithoutIdentityProviderInstance ) {
Listener listener = default_server_listener_ ;
auto * filter_chain = listener . mutable_default_filter_chain ( ) ;
auto * transport_socket = filter_chain - > mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
DownstreamTlsContext downstream_tls_context ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( downstream_tls_context ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " TLS configuration provided but no "
" tls_certificate_provider_instance found. " ) ) ;
}
TEST_P ( XdsServerSecurityTest , NacksMatchSubjectAltNames ) {
Listener listener = default_server_listener_ ;
auto * filter_chain = listener . mutable_default_filter_chain ( ) ;
auto * transport_socket = filter_chain - > mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
DownstreamTlsContext downstream_tls_context ;
downstream_tls_context . mutable_common_tls_context ( )
- > mutable_tls_certificate_provider_instance ( )
- > set_instance_name ( " fake_plugin1 " ) ;
downstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > add_match_subject_alt_names ( )
- > set_exact ( " *.test.google.fr " ) ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( downstream_tls_context ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " match_subject_alt_names not supported on servers " ) ) ;
}
TEST_P ( XdsServerSecurityTest , UnknownIdentityCertificateProvider ) {
SetLdsUpdate ( " " , " " , " unknown " , " " , false ) ;
SendRpc ( [ this ] ( ) { return CreateTlsChannel ( ) ; } , { } , { } ,
true /* test_expects_failure */ ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" Unrecognized certificate provider instance name: unknown " ) ) ;
}
TEST_P ( XdsServerSecurityTest , UnknownRootCertificateProvider ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
SetLdsUpdate ( " unknown " , " " , " fake_plugin1 " , " " , false ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" Unrecognized certificate provider instance name: unknown " ) ) ;
}
TEST_P ( XdsServerSecurityTest ,
TestDeprecateTlsCertificateCertificateProviderInstanceField ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
Listener listener = default_server_listener_ ;
auto * filter_chain = listener . mutable_default_filter_chain ( ) ;
filter_chain - > mutable_filters ( ) - > at ( 0 ) . mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
auto * transport_socket = filter_chain - > mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
DownstreamTlsContext downstream_tls_context ;
downstream_tls_context . mutable_common_tls_context ( )
- > mutable_tls_certificate_certificate_provider_instance ( )
- > set_instance_name ( " fake_plugin1 " ) ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( downstream_tls_context ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
SendRpc ( [ this ] ( ) { return CreateTlsChannel ( ) ; } ,
server_authenticated_identity_ , { } ) ;
}
TEST_P ( XdsServerSecurityTest , CertificatesNotAvailable ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map ;
g_fake1_cert_data_map = & fake1_cert_map ;
SetLdsUpdate ( " fake_plugin1 " , " " , " fake_plugin1 " , " " , true ) ;
SendRpc ( [ this ] ( ) { return CreateMtlsChannel ( ) ; } , { } , { } ,
true /* test_expects_failure */ ) ;
}
TEST_P ( XdsServerSecurityTest , TestMtls ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
SetLdsUpdate ( " fake_plugin1 " , " " , " fake_plugin1 " , " " , true ) ;
backends_ [ 0 ] - > Start ( ) ;
SendRpc ( [ this ] ( ) { return CreateMtlsChannel ( ) ; } ,
server_authenticated_identity_ , client_authenticated_identity_ ) ;
}
TEST_P ( XdsServerSecurityTest , TestMtlsWithRootPluginUpdate ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
FakeCertificateProvider : : CertDataMap fake2_cert_map = {
{ " " , { bad_root_cert_ , bad_identity_pair_ } } } ;
g_fake2_cert_data_map = & fake2_cert_map ;
SetLdsUpdate ( " fake_plugin1 " , " " , " fake_plugin1 " , " " , true ) ;
backends_ [ 0 ] - > Start ( ) ;
SendRpc ( [ this ] ( ) { return CreateMtlsChannel ( ) ; } ,
server_authenticated_identity_ , client_authenticated_identity_ ) ;
SetLdsUpdate ( " fake_plugin2 " , " " , " fake_plugin1 " , " " , true ) ;
SendRpc ( [ this ] ( ) { return CreateMtlsChannel ( ) ; } , { } , { } ,
true /* test_expects_failure */ ) ;
}
TEST_P ( XdsServerSecurityTest , TestMtlsWithIdentityPluginUpdate ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
FakeCertificateProvider : : CertDataMap fake2_cert_map = {
{ " " , { root_cert_ , identity_pair_2_ } } } ;
g_fake2_cert_data_map = & fake2_cert_map ;
SetLdsUpdate ( " fake_plugin1 " , " " , " fake_plugin1 " , " " , true ) ;
backends_ [ 0 ] - > Start ( ) ;
SendRpc ( [ this ] ( ) { return CreateMtlsChannel ( ) ; } ,
server_authenticated_identity_ , client_authenticated_identity_ ) ;
SetLdsUpdate ( " fake_plugin1 " , " " , " fake_plugin2 " , " " , true ) ;
SendRpc ( [ this ] ( ) { return CreateMtlsChannel ( ) ; } ,
server_authenticated_identity_2_ , client_authenticated_identity_ ) ;
}
TEST_P ( XdsServerSecurityTest , TestMtlsWithBothPluginsUpdated ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
FakeCertificateProvider : : CertDataMap fake2_cert_map = {
{ " good " , { root_cert_ , identity_pair_2_ } } ,
{ " " , { bad_root_cert_ , bad_identity_pair_ } } } ;
g_fake2_cert_data_map = & fake2_cert_map ;
SetLdsUpdate ( " fake_plugin2 " , " " , " fake_plugin2 " , " " , true ) ;
backends_ [ 0 ] - > Start ( ) ;
SendRpc ( [ this ] ( ) { return CreateMtlsChannel ( ) ; } , { } , { } ,
true /* test_expects_failure */ ) ;
SetLdsUpdate ( " fake_plugin1 " , " " , " fake_plugin1 " , " " , true ) ;
SendRpc ( [ this ] ( ) { return CreateMtlsChannel ( ) ; } ,
server_authenticated_identity_ , client_authenticated_identity_ ) ;
SetLdsUpdate ( " fake_plugin2 " , " good " , " fake_plugin2 " , " good " , true ) ;
SendRpc ( [ this ] ( ) { return CreateMtlsChannel ( ) ; } ,
server_authenticated_identity_2_ , client_authenticated_identity_ ) ;
}
TEST_P ( XdsServerSecurityTest , TestMtlsWithRootCertificateNameUpdate ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } ,
{ " bad " , { bad_root_cert_ , bad_identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
SetLdsUpdate ( " fake_plugin1 " , " " , " fake_plugin1 " , " " , true ) ;
backends_ [ 0 ] - > Start ( ) ;
SendRpc ( [ this ] ( ) { return CreateMtlsChannel ( ) ; } ,
server_authenticated_identity_ , client_authenticated_identity_ ) ;
SetLdsUpdate ( " fake_plugin1 " , " bad " , " fake_plugin1 " , " " , true ) ;
SendRpc ( [ this ] ( ) { return CreateMtlsChannel ( ) ; } , { } , { } ,
true /* test_expects_failure */ ) ;
}
TEST_P ( XdsServerSecurityTest , TestMtlsWithIdentityCertificateNameUpdate ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } ,
{ " good " , { root_cert_ , identity_pair_2_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
SetLdsUpdate ( " fake_plugin1 " , " " , " fake_plugin1 " , " " , true ) ;
backends_ [ 0 ] - > Start ( ) ;
SendRpc ( [ this ] ( ) { return CreateMtlsChannel ( ) ; } ,
server_authenticated_identity_ , client_authenticated_identity_ ) ;
SetLdsUpdate ( " fake_plugin1 " , " " , " fake_plugin1 " , " good " , true ) ;
SendRpc ( [ this ] ( ) { return CreateMtlsChannel ( ) ; } ,
server_authenticated_identity_2_ , client_authenticated_identity_ ) ;
}
TEST_P ( XdsServerSecurityTest , TestMtlsWithBothCertificateNamesUpdated ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } ,
{ " good " , { root_cert_ , identity_pair_2_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
SetLdsUpdate ( " fake_plugin1 " , " " , " fake_plugin1 " , " " , true ) ;
backends_ [ 0 ] - > Start ( ) ;
SendRpc ( [ this ] ( ) { return CreateMtlsChannel ( ) ; } ,
server_authenticated_identity_ , client_authenticated_identity_ ) ;
SetLdsUpdate ( " fake_plugin1 " , " good " , " fake_plugin1 " , " good " , true ) ;
SendRpc ( [ this ] ( ) { return CreateMtlsChannel ( ) ; } ,
server_authenticated_identity_2_ , client_authenticated_identity_ ) ;
}
TEST_P ( XdsServerSecurityTest , TestMtlsNotRequiringButProvidingClientCerts ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
SetLdsUpdate ( " fake_plugin1 " , " " , " fake_plugin1 " , " " , false ) ;
backends_ [ 0 ] - > Start ( ) ;
SendRpc ( [ this ] ( ) { return CreateMtlsChannel ( ) ; } ,
server_authenticated_identity_ , client_authenticated_identity_ ) ;
}
TEST_P ( XdsServerSecurityTest , TestMtlsNotRequiringAndNotProvidingClientCerts ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
SetLdsUpdate ( " fake_plugin1 " , " " , " fake_plugin1 " , " " , false ) ;
backends_ [ 0 ] - > Start ( ) ;
SendRpc ( [ this ] ( ) { return CreateTlsChannel ( ) ; } ,
server_authenticated_identity_ , { } ) ;
}
TEST_P ( XdsServerSecurityTest , TestTls ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
SetLdsUpdate ( " " , " " , " fake_plugin1 " , " " , false ) ;
backends_ [ 0 ] - > Start ( ) ;
SendRpc ( [ this ] ( ) { return CreateTlsChannel ( ) ; } ,
server_authenticated_identity_ , { } ) ;
}
TEST_P ( XdsServerSecurityTest , TestTlsWithIdentityPluginUpdate ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
FakeCertificateProvider : : CertDataMap fake2_cert_map = {
{ " " , { root_cert_ , identity_pair_2_ } } } ;
g_fake2_cert_data_map = & fake2_cert_map ;
SetLdsUpdate ( " " , " " , " fake_plugin1 " , " " , false ) ;
backends_ [ 0 ] - > Start ( ) ;
SendRpc ( [ this ] ( ) { return CreateTlsChannel ( ) ; } ,
server_authenticated_identity_ , { } ) ;
SetLdsUpdate ( " " , " " , " fake_plugin2 " , " " , false ) ;
SendRpc ( [ this ] ( ) { return CreateTlsChannel ( ) ; } ,
server_authenticated_identity_2_ , { } ) ;
}
TEST_P ( XdsServerSecurityTest , TestTlsWithIdentityCertificateNameUpdate ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } ,
{ " good " , { root_cert_ , identity_pair_2_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
SetLdsUpdate ( " " , " " , " fake_plugin1 " , " " , false ) ;
backends_ [ 0 ] - > Start ( ) ;
SendRpc ( [ this ] ( ) { return CreateTlsChannel ( ) ; } ,
server_authenticated_identity_ , { } ) ;
SetLdsUpdate ( " " , " " , " fake_plugin1 " , " good " , false ) ;
SendRpc ( [ this ] ( ) { return CreateTlsChannel ( ) ; } ,
server_authenticated_identity_2_ , { } ) ;
}
TEST_P ( XdsServerSecurityTest , TestFallback ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
SetLdsUpdate ( " " , " " , " " , " " , false ) ;
backends_ [ 0 ] - > Start ( ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
}
TEST_P ( XdsServerSecurityTest , TestMtlsToTls ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
SetLdsUpdate ( " fake_plugin1 " , " " , " fake_plugin1 " , " " , true ) ;
backends_ [ 0 ] - > Start ( ) ;
SendRpc ( [ this ] ( ) { return CreateTlsChannel ( ) ; } , { } , { } ,
true /* test_expects_failure */ ) ;
SetLdsUpdate ( " " , " " , " fake_plugin1 " , " " , false ) ;
SendRpc ( [ this ] ( ) { return CreateTlsChannel ( ) ; } ,
server_authenticated_identity_ , { } ) ;
}
TEST_P ( XdsServerSecurityTest , TestTlsToMtls ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
SetLdsUpdate ( " " , " " , " fake_plugin1 " , " " , false ) ;
backends_ [ 0 ] - > Start ( ) ;
SendRpc ( [ this ] ( ) { return CreateTlsChannel ( ) ; } ,
server_authenticated_identity_ , { } ) ;
SetLdsUpdate ( " fake_plugin1 " , " " , " fake_plugin1 " , " " , true ) ;
SendRpc ( [ this ] ( ) { return CreateTlsChannel ( ) ; } , { } , { } ,
true /* test_expects_failure */ ) ;
}
TEST_P ( XdsServerSecurityTest , TestMtlsToFallback ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
SetLdsUpdate ( " fake_plugin1 " , " " , " fake_plugin1 " , " " , false ) ;
backends_ [ 0 ] - > Start ( ) ;
SendRpc ( [ this ] ( ) { return CreateMtlsChannel ( ) ; } ,
server_authenticated_identity_ , client_authenticated_identity_ ) ;
SetLdsUpdate ( " " , " " , " " , " " , false ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
}
TEST_P ( XdsServerSecurityTest , TestFallbackToMtls ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
SetLdsUpdate ( " " , " " , " " , " " , false ) ;
backends_ [ 0 ] - > Start ( ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
SetLdsUpdate ( " fake_plugin1 " , " " , " fake_plugin1 " , " " , true ) ;
SendRpc ( [ this ] ( ) { return CreateMtlsChannel ( ) ; } ,
server_authenticated_identity_ , client_authenticated_identity_ ) ;
}
TEST_P ( XdsServerSecurityTest , TestTlsToFallback ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
SetLdsUpdate ( " " , " " , " fake_plugin1 " , " " , false ) ;
backends_ [ 0 ] - > Start ( ) ;
SendRpc ( [ this ] ( ) { return CreateTlsChannel ( ) ; } ,
server_authenticated_identity_ , { } ) ;
SetLdsUpdate ( " " , " " , " " , " " , false ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
}
TEST_P ( XdsServerSecurityTest , TestFallbackToTls ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
SetLdsUpdate ( " " , " " , " " , " " , false ) ;
backends_ [ 0 ] - > Start ( ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
SetLdsUpdate ( " " , " " , " fake_plugin1 " , " " , false ) ;
SendRpc ( [ this ] ( ) { return CreateTlsChannel ( ) ; } ,
server_authenticated_identity_ , { } ) ;
}
class XdsEnabledServerStatusNotificationTest : public XdsServerSecurityTest {
protected :
void SetValidLdsUpdate ( ) { SetLdsUpdate ( " " , " " , " " , " " , false ) ; }
void SetInvalidLdsUpdate ( ) {
Listener listener = default_server_listener_ ;
listener . clear_address ( ) ;
listener . set_name ( absl : : StrCat (
" grpc/server?xds.resource.listening_address= " ,
ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ) ;
balancer_ - > ads_service ( ) - > SetLdsResource ( listener ) ;
}
void UnsetLdsUpdate ( ) {
balancer_ - > ads_service ( ) - > UnsetResource (
kLdsTypeUrl , absl : : StrCat ( " grpc/server?xds.resource.listening_address= " ,
ipv6_only_ ? " [::1]: " : " 127.0.0.1: " ,
backends_ [ 0 ] - > port ( ) ) ) ;
}
} ;
TEST_P ( XdsEnabledServerStatusNotificationTest , ServingStatus ) {
SetValidLdsUpdate ( ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
}
TEST_P ( XdsEnabledServerStatusNotificationTest , NotServingStatus ) {
SetInvalidLdsUpdate ( ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : UNAVAILABLE ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
true /* test_expects_failure */ ) ;
}
TEST_P ( XdsEnabledServerStatusNotificationTest , ErrorUpdateWhenAlreadyServing ) {
SetValidLdsUpdate ( ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
// Invalid update does not lead to a change in the serving status.
SetInvalidLdsUpdate ( ) ;
do {
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
} while ( ! balancer_ - > ads_service ( ) - > lds_response_state ( ) . has_value ( ) ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
}
TEST_P ( XdsEnabledServerStatusNotificationTest ,
NotServingStatusToServingStatusTransition ) {
SetInvalidLdsUpdate ( ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : UNAVAILABLE ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
true /* test_expects_failure */ ) ;
// Send a valid LDS update to change to serving status
SetValidLdsUpdate ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
}
// This test verifies that the resource getting deleted when already serving
// results in future connections being dropped.
TEST_P ( XdsEnabledServerStatusNotificationTest ,
ServingStatusToNonServingStatusTransition ) {
SetValidLdsUpdate ( ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
// Deleting the resource should result in a non-serving status.
UnsetLdsUpdate ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : NOT_FOUND ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
true /* test_expects_failure */ ) ;
}
TEST_P ( XdsEnabledServerStatusNotificationTest , RepeatedServingStatusChanges ) {
backends_ [ 0 ] - > Start ( ) ;
for ( int i = 0 ; i < 5 ; i + + ) {
// Send a valid LDS update to get the server to start listening
SetValidLdsUpdate ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " ,
backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
// Deleting the resource will make the server start rejecting connections
UnsetLdsUpdate ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " ,
backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : NOT_FOUND ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
true /* test_expects_failure */ ) ;
}
}
TEST_P ( XdsEnabledServerStatusNotificationTest , ExistingRpcsOnResourceDeletion ) {
// Send a valid LDS update to get the server to start listening
SetValidLdsUpdate ( ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
constexpr int kNumChannels = 10 ;
struct StreamingRpc {
std : : shared_ptr < Channel > channel ;
std : : unique_ptr < grpc : : testing : : EchoTestService : : Stub > stub ;
ClientContext context ;
std : : unique_ptr < ClientReaderWriter < EchoRequest , EchoResponse > > stream ;
} streaming_rpcs [ kNumChannels ] ;
EchoRequest request ;
EchoResponse response ;
request . set_message ( " Hello " ) ;
for ( int i = 0 ; i < kNumChannels ; i + + ) {
streaming_rpcs [ i ] . channel = CreateInsecureChannel ( ) ;
streaming_rpcs [ i ] . stub =
grpc : : testing : : EchoTestService : : NewStub ( streaming_rpcs [ i ] . channel ) ;
streaming_rpcs [ i ] . context . set_wait_for_ready ( true ) ;
streaming_rpcs [ i ] . stream =
streaming_rpcs [ i ] . stub - > BidiStream ( & streaming_rpcs [ i ] . context ) ;
EXPECT_TRUE ( streaming_rpcs [ i ] . stream - > Write ( request ) ) ;
streaming_rpcs [ i ] . stream - > Read ( & response ) ;
EXPECT_EQ ( request . message ( ) , response . message ( ) ) ;
}
// Deleting the resource will make the server start rejecting connections
UnsetLdsUpdate ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : NOT_FOUND ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
true /* test_expects_failure */ ) ;
for ( int i = 0 ; i < kNumChannels ; i + + ) {
EXPECT_TRUE ( streaming_rpcs [ i ] . stream - > Write ( request ) ) ;
streaming_rpcs [ i ] . stream - > Read ( & response ) ;
EXPECT_EQ ( request . message ( ) , response . message ( ) ) ;
EXPECT_TRUE ( streaming_rpcs [ i ] . stream - > WritesDone ( ) ) ;
auto status = streaming_rpcs [ i ] . stream - > Finish ( ) ;
EXPECT_TRUE ( status . ok ( ) )
< < status . error_message ( ) < < " , " < < status . error_details ( ) < < " , "
< < streaming_rpcs [ i ] . context . debug_error_string ( ) ;
// New RPCs on the existing channels should fail.
ClientContext new_context ;
new_context . set_deadline ( grpc_timeout_milliseconds_to_deadline ( 1000 ) ) ;
EXPECT_FALSE (
streaming_rpcs [ i ] . stub - > Echo ( & new_context , request , & response ) . ok ( ) ) ;
}
}
TEST_P ( XdsEnabledServerStatusNotificationTest ,
ExistingRpcsFailOnResourceUpdateAfterDrainGraceTimeExpires ) {
constexpr int kDrainGraceTimeMs = 100 ;
xds_drain_grace_time_ms_ = kDrainGraceTimeMs ;
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
// Send a valid LDS update to get the server to start listening
SetValidLdsUpdate ( ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
constexpr int kNumChannels = 10 ;
struct StreamingRpc {
std : : shared_ptr < Channel > channel ;
std : : unique_ptr < grpc : : testing : : EchoTestService : : Stub > stub ;
ClientContext context ;
std : : unique_ptr < ClientReaderWriter < EchoRequest , EchoResponse > > stream ;
} streaming_rpcs [ kNumChannels ] ;
EchoRequest request ;
EchoResponse response ;
request . set_message ( " Hello " ) ;
for ( int i = 0 ; i < kNumChannels ; i + + ) {
streaming_rpcs [ i ] . channel = CreateInsecureChannel ( ) ;
streaming_rpcs [ i ] . stub =
grpc : : testing : : EchoTestService : : NewStub ( streaming_rpcs [ i ] . channel ) ;
streaming_rpcs [ i ] . context . set_wait_for_ready ( true ) ;
streaming_rpcs [ i ] . stream =
streaming_rpcs [ i ] . stub - > BidiStream ( & streaming_rpcs [ i ] . context ) ;
EXPECT_TRUE ( streaming_rpcs [ i ] . stream - > Write ( request ) ) ;
streaming_rpcs [ i ] . stream - > Read ( & response ) ;
EXPECT_EQ ( request . message ( ) , response . message ( ) ) ;
}
grpc_core : : Timestamp update_time = NowFromCycleCounter ( ) ;
// Update the resource.
SetLdsUpdate ( " " , " " , " fake_plugin1 " , " " , false ) ;
// Wait for the updated resource to take effect.
SendRpc ( [ this ] ( ) { return CreateTlsChannel ( ) ; } ,
server_authenticated_identity_ , { } ) ;
// After the drain grace time expires, the existing RPCs should all fail.
for ( int i = 0 ; i < kNumChannels ; i + + ) {
// Wait for the drain grace time to expire
EXPECT_FALSE ( streaming_rpcs [ i ] . stream - > Read ( & response ) ) ;
// Make sure that the drain grace interval is honored.
EXPECT_GE ( NowFromCycleCounter ( ) - update_time ,
grpc_core : : Duration : : Milliseconds ( kDrainGraceTimeMs ) ) ;
auto status = streaming_rpcs [ i ] . stream - > Finish ( ) ;
EXPECT_EQ ( status . error_code ( ) , grpc : : StatusCode : : UNAVAILABLE )
< < status . error_code ( ) < < " , " < < status . error_message ( ) < < " , "
< < status . error_details ( ) < < " , "
< < streaming_rpcs [ i ] . context . debug_error_string ( ) ;
}
}
using XdsServerFilterChainMatchTest = XdsServerSecurityTest ;
TEST_P ( XdsServerFilterChainMatchTest ,
DefaultFilterChainUsedWhenNoFilterChainMentioned ) {
backends_ [ 0 ] - > Start ( ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
}
TEST_P ( XdsServerFilterChainMatchTest ,
DefaultFilterChainUsedWhenOtherFilterChainsDontMatch ) {
Listener listener = default_server_listener_ ;
// Add a filter chain that will never get matched
auto * filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
filter_chain - > mutable_filter_chain_match ( )
- > mutable_destination_port ( )
- > set_value ( 8080 ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
}
TEST_P ( XdsServerFilterChainMatchTest ,
FilterChainsWithDestinationPortDontMatch ) {
Listener listener = default_server_listener_ ;
// Add filter chain with destination port that should never get matched
auto * filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
filter_chain - > mutable_filter_chain_match ( )
- > mutable_destination_port ( )
- > set_value ( 8080 ) ;
listener . clear_default_filter_chain ( ) ;
balancer_ - > ads_service ( ) - > SetLdsResource (
PopulateServerListenerNameAndPort ( listener , backends_ [ 0 ] - > port ( ) ) ) ;
backends_ [ 0 ] - > Start ( ) ;
// RPC should fail since no matching filter chain was found and no default
// filter chain is configured.
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
true /* test_expects_failure */ ) ;
}
TEST_P ( XdsServerFilterChainMatchTest , FilterChainsWithServerNamesDontMatch ) {
Listener listener = default_server_listener_ ;
// Add filter chain with server name that should never get matched
auto * filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
filter_chain - > mutable_filter_chain_match ( ) - > add_server_names ( " server_name " ) ;
listener . clear_default_filter_chain ( ) ;
balancer_ - > ads_service ( ) - > SetLdsResource (
PopulateServerListenerNameAndPort ( listener , backends_ [ 0 ] - > port ( ) ) ) ;
backends_ [ 0 ] - > Start ( ) ;
// RPC should fail since no matching filter chain was found and no default
// filter chain is configured.
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
true /* test_expects_failure */ ) ;
}
TEST_P ( XdsServerFilterChainMatchTest ,
FilterChainsWithTransportProtocolsOtherThanRawBufferDontMatch ) {
Listener listener = default_server_listener_ ;
// Add filter chain with transport protocol "tls" that should never match
auto * filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
filter_chain - > mutable_filter_chain_match ( ) - > set_transport_protocol ( " tls " ) ;
listener . clear_default_filter_chain ( ) ;
balancer_ - > ads_service ( ) - > SetLdsResource (
PopulateServerListenerNameAndPort ( listener , backends_ [ 0 ] - > port ( ) ) ) ;
backends_ [ 0 ] - > Start ( ) ;
// RPC should fail since no matching filter chain was found and no default
// filter chain is configured.
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
true /* test_expects_failure */ ) ;
}
TEST_P ( XdsServerFilterChainMatchTest ,
FilterChainsWithApplicationProtocolsDontMatch ) {
Listener listener = default_server_listener_ ;
// Add filter chain with application protocol that should never get matched
auto * filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
filter_chain - > mutable_filter_chain_match ( ) - > add_application_protocols ( " h2 " ) ;
listener . clear_default_filter_chain ( ) ;
balancer_ - > ads_service ( ) - > SetLdsResource (
PopulateServerListenerNameAndPort ( listener , backends_ [ 0 ] - > port ( ) ) ) ;
backends_ [ 0 ] - > Start ( ) ;
// RPC should fail since no matching filter chain was found and no default
// filter chain is configured.
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
true /* test_expects_failure */ ) ;
}
TEST_P ( XdsServerFilterChainMatchTest ,
FilterChainsWithTransportProtocolRawBufferIsPreferred ) {
Listener listener = default_server_listener_ ;
// Add filter chain with "raw_buffer" transport protocol
auto * filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
filter_chain - > mutable_filter_chain_match ( ) - > set_transport_protocol (
" raw_buffer " ) ;
// Add another filter chain with no transport protocol set but application
// protocol set (fails match)
filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
filter_chain - > mutable_filter_chain_match ( ) - > add_application_protocols ( " h2 " ) ;
listener . clear_default_filter_chain ( ) ;
balancer_ - > ads_service ( ) - > SetLdsResource (
PopulateServerListenerNameAndPort ( listener , backends_ [ 0 ] - > port ( ) ) ) ;
backends_ [ 0 ] - > Start ( ) ;
// A successful RPC proves that filter chains that mention "raw_buffer" as
// the transport protocol are chosen as the best match in the round.
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
}
TEST_P ( XdsServerFilterChainMatchTest ,
FilterChainsWithMoreSpecificDestinationPrefixRangesArePreferred ) {
Listener listener = default_server_listener_ ;
// Add filter chain with prefix range (length 4 and 16) but with server name
// mentioned. (Prefix range is matched first.)
auto * filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
auto * prefix_range =
filter_chain - > mutable_filter_chain_match ( ) - > add_prefix_ranges ( ) ;
prefix_range - > set_address_prefix ( ipv6_only_ ? " ::1 " : " 127.0.0.1 " ) ;
prefix_range - > mutable_prefix_len ( ) - > set_value ( 4 ) ;
prefix_range =
filter_chain - > mutable_filter_chain_match ( ) - > add_prefix_ranges ( ) ;
prefix_range - > set_address_prefix ( ipv6_only_ ? " ::1 " : " 127.0.0.1 " ) ;
prefix_range - > mutable_prefix_len ( ) - > set_value ( 16 ) ;
filter_chain - > mutable_filter_chain_match ( ) - > add_server_names ( " server_name " ) ;
// Add filter chain with two prefix ranges (length 8 and 24). Since 24 is
// the highest match, it should be chosen.
filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
prefix_range =
filter_chain - > mutable_filter_chain_match ( ) - > add_prefix_ranges ( ) ;
prefix_range - > set_address_prefix ( ipv6_only_ ? " ::1 " : " 127.0.0.1 " ) ;
prefix_range - > mutable_prefix_len ( ) - > set_value ( 8 ) ;
prefix_range =
filter_chain - > mutable_filter_chain_match ( ) - > add_prefix_ranges ( ) ;
prefix_range - > set_address_prefix ( ipv6_only_ ? " ::1 " : " 127.0.0.1 " ) ;
prefix_range - > mutable_prefix_len ( ) - > set_value ( 24 ) ;
// Add another filter chain with a non-matching prefix range (with length
// 30)
filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
prefix_range =
filter_chain - > mutable_filter_chain_match ( ) - > add_prefix_ranges ( ) ;
prefix_range - > set_address_prefix ( " 192.168.1.1 " ) ;
prefix_range - > mutable_prefix_len ( ) - > set_value ( 30 ) ;
filter_chain - > mutable_filter_chain_match ( ) - > add_server_names ( " server_name " ) ;
// Add another filter chain with no prefix range mentioned
filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
filter_chain - > mutable_filter_chain_match ( ) - > add_server_names ( " server_name " ) ;
listener . clear_default_filter_chain ( ) ;
balancer_ - > ads_service ( ) - > SetLdsResource (
PopulateServerListenerNameAndPort ( listener , backends_ [ 0 ] - > port ( ) ) ) ;
backends_ [ 0 ] - > Start ( ) ;
// A successful RPC proves that the filter chain with the longest matching
// prefix range was the best match.
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
}
TEST_P ( XdsServerFilterChainMatchTest ,
FilterChainsThatMentionSourceTypeArePreferred ) {
Listener listener = default_server_listener_ ;
// Add filter chain with the local source type (best match)
auto * filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
filter_chain - > mutable_filter_chain_match ( ) - > set_source_type (
FilterChainMatch : : SAME_IP_OR_LOOPBACK ) ;
// Add filter chain with the external source type but bad source port.
// Note that backends_[0]->port() will never be a match for the source port
// because it is already being used by a backend.
filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
filter_chain - > mutable_filter_chain_match ( ) - > set_source_type (
FilterChainMatch : : EXTERNAL ) ;
filter_chain - > mutable_filter_chain_match ( ) - > add_source_ports (
backends_ [ 0 ] - > port ( ) ) ;
// Add filter chain with the default source type (ANY) but bad source port.
filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
filter_chain - > mutable_filter_chain_match ( ) - > add_source_ports (
backends_ [ 0 ] - > port ( ) ) ;
listener . clear_default_filter_chain ( ) ;
balancer_ - > ads_service ( ) - > SetLdsResource (
PopulateServerListenerNameAndPort ( listener , backends_ [ 0 ] - > port ( ) ) ) ;
backends_ [ 0 ] - > Start ( ) ;
// A successful RPC proves that the filter chain with the longest matching
// prefix range was the best match.
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
}
TEST_P ( XdsServerFilterChainMatchTest ,
FilterChainsWithMoreSpecificSourcePrefixRangesArePreferred ) {
Listener listener = default_server_listener_ ;
// Add filter chain with source prefix range (length 16) but with a bad
// source port mentioned. (Prefix range is matched first.) Note that
// backends_[0]->port() will never be a match for the source port because it
// is already being used by a backend.
auto * filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
auto * source_prefix_range =
filter_chain - > mutable_filter_chain_match ( ) - > add_source_prefix_ranges ( ) ;
source_prefix_range - > set_address_prefix ( ipv6_only_ ? " ::1 " : " 127.0.0.1 " ) ;
source_prefix_range - > mutable_prefix_len ( ) - > set_value ( 4 ) ;
source_prefix_range =
filter_chain - > mutable_filter_chain_match ( ) - > add_source_prefix_ranges ( ) ;
source_prefix_range - > set_address_prefix ( ipv6_only_ ? " ::1 " : " 127.0.0.1 " ) ;
source_prefix_range - > mutable_prefix_len ( ) - > set_value ( 16 ) ;
filter_chain - > mutable_filter_chain_match ( ) - > add_source_ports (
backends_ [ 0 ] - > port ( ) ) ;
// Add filter chain with two source prefix ranges (length 8 and 24). Since
// 24 is the highest match, it should be chosen.
filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
source_prefix_range =
filter_chain - > mutable_filter_chain_match ( ) - > add_source_prefix_ranges ( ) ;
source_prefix_range - > set_address_prefix ( ipv6_only_ ? " ::1 " : " 127.0.0.1 " ) ;
source_prefix_range - > mutable_prefix_len ( ) - > set_value ( 8 ) ;
source_prefix_range =
filter_chain - > mutable_filter_chain_match ( ) - > add_source_prefix_ranges ( ) ;
source_prefix_range - > set_address_prefix ( ipv6_only_ ? " ::1 " : " 127.0.0.1 " ) ;
source_prefix_range - > mutable_prefix_len ( ) - > set_value ( 24 ) ;
// Add another filter chain with a non-matching source prefix range (with
// length 30) and bad source port
filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
source_prefix_range =
filter_chain - > mutable_filter_chain_match ( ) - > add_source_prefix_ranges ( ) ;
source_prefix_range - > set_address_prefix ( " 192.168.1.1 " ) ;
source_prefix_range - > mutable_prefix_len ( ) - > set_value ( 30 ) ;
filter_chain - > mutable_filter_chain_match ( ) - > add_source_ports (
backends_ [ 0 ] - > port ( ) ) ;
// Add another filter chain with no source prefix range mentioned and bad
// source port
filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
filter_chain - > mutable_filter_chain_match ( ) - > add_source_ports (
backends_ [ 0 ] - > port ( ) ) ;
listener . clear_default_filter_chain ( ) ;
balancer_ - > ads_service ( ) - > SetLdsResource (
PopulateServerListenerNameAndPort ( listener , backends_ [ 0 ] - > port ( ) ) ) ;
backends_ [ 0 ] - > Start ( ) ;
// A successful RPC proves that the filter chain with the longest matching
// source prefix range was the best match.
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
}
TEST_P ( XdsServerFilterChainMatchTest ,
FilterChainsWithMoreSpecificSourcePortArePreferred ) {
Listener listener = default_server_listener_ ;
auto * filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
// Since we don't know which port will be used by the channel, just add all
// ports except for 0.
for ( int i = 1 ; i < 65536 ; i + + ) {
filter_chain - > mutable_filter_chain_match ( ) - > add_source_ports ( i ) ;
}
// Add another filter chain with no source port mentioned with a bad
// DownstreamTlsContext configuration.
filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
auto * transport_socket = filter_chain - > mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
DownstreamTlsContext downstream_tls_context ;
downstream_tls_context . mutable_common_tls_context ( )
- > mutable_tls_certificate_provider_instance ( )
- > set_instance_name ( " fake_plugin1 " ) ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( downstream_tls_context ) ;
listener . clear_default_filter_chain ( ) ;
balancer_ - > ads_service ( ) - > SetLdsResource (
PopulateServerListenerNameAndPort ( listener , backends_ [ 0 ] - > port ( ) ) ) ;
backends_ [ 0 ] - > Start ( ) ;
// A successful RPC proves that the filter chain with matching source port
// was chosen.
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
}
TEST_P ( XdsServerFilterChainMatchTest , DuplicateMatchNacked ) {
Listener listener = default_server_listener_ ;
// Add filter chain
auto * filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
// Add a duplicate filter chain
filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr (
" Duplicate matching rules detected when adding filter chain: {} " ) ) ;
}
TEST_P ( XdsServerFilterChainMatchTest , DuplicateMatchOnPrefixRangesNacked ) {
Listener listener = default_server_listener_ ;
// Add filter chain with prefix range
auto * filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
auto * prefix_range =
filter_chain - > mutable_filter_chain_match ( ) - > add_prefix_ranges ( ) ;
prefix_range - > set_address_prefix ( ipv6_only_ ? " ::1 " : " 127.0.0.1 " ) ;
prefix_range - > mutable_prefix_len ( ) - > set_value ( 16 ) ;
prefix_range =
filter_chain - > mutable_filter_chain_match ( ) - > add_prefix_ranges ( ) ;
prefix_range - > set_address_prefix ( ipv6_only_ ? " ::1 " : " 127.0.0.1 " ) ;
prefix_range - > mutable_prefix_len ( ) - > set_value ( 24 ) ;
// Add a filter chain with a duplicate prefix range entry
filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
prefix_range =
filter_chain - > mutable_filter_chain_match ( ) - > add_prefix_ranges ( ) ;
prefix_range - > set_address_prefix ( ipv6_only_ ? " ::1 " : " 127.0.0.1 " ) ;
prefix_range - > mutable_prefix_len ( ) - > set_value ( 16 ) ;
prefix_range =
filter_chain - > mutable_filter_chain_match ( ) - > add_prefix_ranges ( ) ;
prefix_range - > set_address_prefix ( ipv6_only_ ? " ::1 " : " 127.0.0.1 " ) ;
prefix_range - > mutable_prefix_len ( ) - > set_value ( 32 ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
if ( ipv6_only_ ) {
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr (
" Duplicate matching rules detected when adding filter chain: "
" {prefix_ranges={{address_prefix=[::]:0, prefix_len=16}, "
" {address_prefix=[::]:0, prefix_len=32}}} " ) ) ;
} else {
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr (
" Duplicate matching rules detected when adding filter chain: "
" {prefix_ranges={{address_prefix=127.0.0.0:0, prefix_len=16}, "
" {address_prefix=127.0.0.1:0, prefix_len=32}}} " ) ) ;
}
}
TEST_P ( XdsServerFilterChainMatchTest , DuplicateMatchOnTransportProtocolNacked ) {
Listener listener = default_server_listener_ ;
// Add filter chain with "raw_buffer" transport protocol
auto * filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
filter_chain - > mutable_filter_chain_match ( ) - > set_transport_protocol (
" raw_buffer " ) ;
// Add a duplicate filter chain with the same "raw_buffer" transport
// protocol entry
filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
filter_chain - > mutable_filter_chain_match ( ) - > set_transport_protocol (
" raw_buffer " ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " Duplicate matching rules detected when adding "
" filter chain: {transport_protocol=raw_buffer} " ) ) ;
}
TEST_P ( XdsServerFilterChainMatchTest , DuplicateMatchOnLocalSourceTypeNacked ) {
Listener listener = default_server_listener_ ;
// Add filter chain with the local source type
auto * filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
filter_chain - > mutable_filter_chain_match ( ) - > set_source_type (
FilterChainMatch : : SAME_IP_OR_LOOPBACK ) ;
// Add a duplicate filter chain with the same local source type entry
filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
filter_chain - > mutable_filter_chain_match ( ) - > set_source_type (
FilterChainMatch : : SAME_IP_OR_LOOPBACK ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " Duplicate matching rules detected when adding "
" filter chain: {source_type=SAME_IP_OR_LOOPBACK} " ) ) ;
}
TEST_P ( XdsServerFilterChainMatchTest ,
DuplicateMatchOnExternalSourceTypeNacked ) {
Listener listener = default_server_listener_ ;
// Add filter chain with the external source type
auto * filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
filter_chain - > mutable_filter_chain_match ( ) - > set_source_type (
FilterChainMatch : : EXTERNAL ) ;
// Add a duplicate filter chain with the same external source type entry
filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
filter_chain - > mutable_filter_chain_match ( ) - > set_source_type (
FilterChainMatch : : EXTERNAL ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " Duplicate matching rules detected when adding "
" filter chain: {source_type=EXTERNAL} " ) ) ;
}
TEST_P ( XdsServerFilterChainMatchTest ,
DuplicateMatchOnSourcePrefixRangesNacked ) {
Listener listener = default_server_listener_ ;
// Add filter chain with source prefix range
auto * filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
auto * prefix_range =
filter_chain - > mutable_filter_chain_match ( ) - > add_source_prefix_ranges ( ) ;
prefix_range - > set_address_prefix ( ipv6_only_ ? " ::1 " : " 127.0.0.1 " ) ;
prefix_range - > mutable_prefix_len ( ) - > set_value ( 16 ) ;
prefix_range =
filter_chain - > mutable_filter_chain_match ( ) - > add_source_prefix_ranges ( ) ;
prefix_range - > set_address_prefix ( ipv6_only_ ? " ::1 " : " 127.0.0.1 " ) ;
prefix_range - > mutable_prefix_len ( ) - > set_value ( 24 ) ;
// Add a filter chain with a duplicate source prefix range entry
filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
prefix_range =
filter_chain - > mutable_filter_chain_match ( ) - > add_source_prefix_ranges ( ) ;
prefix_range - > set_address_prefix ( ipv6_only_ ? " ::1 " : " 127.0.0.1 " ) ;
prefix_range - > mutable_prefix_len ( ) - > set_value ( 16 ) ;
prefix_range =
filter_chain - > mutable_filter_chain_match ( ) - > add_source_prefix_ranges ( ) ;
prefix_range - > set_address_prefix ( ipv6_only_ ? " ::1 " : " 127.0.0.1 " ) ;
prefix_range - > mutable_prefix_len ( ) - > set_value ( 32 ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
if ( ipv6_only_ ) {
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr (
" Duplicate matching rules detected when adding filter chain: "
" {source_prefix_ranges={{address_prefix=[::]:0, prefix_len=16}, "
" {address_prefix=[::]:0, prefix_len=32}}} " ) ) ;
} else {
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr (
" Duplicate matching rules detected when adding filter chain: "
" {source_prefix_ranges={{address_prefix=127.0.0.0:0, "
" prefix_len=16}, "
" {address_prefix=127.0.0.1:0, prefix_len=32}}} " ) ) ;
}
}
TEST_P ( XdsServerFilterChainMatchTest , DuplicateMatchOnSourcePortNacked ) {
Listener listener = default_server_listener_ ;
// Add filter chain with the external source type
auto * filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
filter_chain - > mutable_filter_chain_match ( ) - > add_source_ports ( 8080 ) ;
// Add a duplicate filter chain with the same source port entry
filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
ServerHcmAccessor ( ) . Unpack ( listener ) ) ;
filter_chain - > mutable_filter_chain_match ( ) - > add_source_ports ( 8080 ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT (
response_state - > error_message ,
: : testing : : HasSubstr ( " Duplicate matching rules detected when adding "
" filter chain: {source_ports={8080}} " ) ) ;
}
class XdsServerRdsTest : public XdsEnabledServerStatusNotificationTest {
protected :
XdsServerRdsTest ( ) : env_var_ ( " GRPC_XDS_EXPERIMENTAL_RBAC " ) { }
ScopedExperimentalEnvVar env_var_ ;
} ;
TEST_P ( XdsServerRdsTest , Basic ) {
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
}
TEST_P ( XdsServerRdsTest , NacksInvalidDomainPattern ) {
RouteConfiguration route_config = default_server_route_config_ ;
route_config . mutable_virtual_hosts ( ) - > at ( 0 ) . add_domains ( " " ) ;
SetServerListenerNameAndRouteConfiguration (
balancer_ . get ( ) , default_server_listener_ , backends_ [ 0 ] - > port ( ) ,
route_config ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForRouteConfigNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " Invalid domain pattern \" \" " ) ) ;
}
TEST_P ( XdsServerRdsTest , NacksEmptyDomainsList ) {
RouteConfiguration route_config = default_server_route_config_ ;
route_config . mutable_virtual_hosts ( ) - > at ( 0 ) . clear_domains ( ) ;
SetServerListenerNameAndRouteConfiguration (
balancer_ . get ( ) , default_server_listener_ , backends_ [ 0 ] - > port ( ) ,
route_config ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForRouteConfigNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " VirtualHost has no domains " ) ) ;
}
TEST_P ( XdsServerRdsTest , NacksEmptyRoutesList ) {
RouteConfiguration route_config = default_server_route_config_ ;
route_config . mutable_virtual_hosts ( ) - > at ( 0 ) . clear_routes ( ) ;
SetServerListenerNameAndRouteConfiguration (
balancer_ . get ( ) , default_server_listener_ , backends_ [ 0 ] - > port ( ) ,
route_config ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForRouteConfigNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " No route found in the virtual host " ) ) ;
}
TEST_P ( XdsServerRdsTest , NacksEmptyMatch ) {
RouteConfiguration route_config = default_server_route_config_ ;
route_config . mutable_virtual_hosts ( )
- > at ( 0 )
. mutable_routes ( )
- > at ( 0 )
. clear_match ( ) ;
SetServerListenerNameAndRouteConfiguration (
balancer_ . get ( ) , default_server_listener_ , backends_ [ 0 ] - > port ( ) ,
route_config ) ;
backends_ [ 0 ] - > Start ( ) ;
const auto response_state = WaitForRouteConfigNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " Match can't be null " ) ) ;
}
TEST_P ( XdsServerRdsTest , FailsRouteMatchesOtherThanNonForwardingAction ) {
SetServerListenerNameAndRouteConfiguration (
balancer_ . get ( ) , default_server_listener_ , backends_ [ 0 ] - > port ( ) ,
default_route_config_ /* inappropriate route config for servers */ ) ;
backends_ [ 0 ] - > Start ( ) ;
// The server should be ready to serve but RPCs should fail.
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
true /* test_expects_failure */ ) ;
}
// Test that non-inline route configuration also works for non-default filter
// chains
TEST_P ( XdsServerRdsTest , NonInlineRouteConfigurationNonDefaultFilterChain ) {
if ( ! GetParam ( ) . enable_rds_testing ( ) ) {
return ;
}
Listener listener = default_server_listener_ ;
auto * filter_chain = listener . add_filter_chains ( ) ;
HttpConnectionManager http_connection_manager =
ServerHcmAccessor ( ) . Unpack ( listener ) ;
auto * rds = http_connection_manager . mutable_rds ( ) ;
rds - > set_route_config_name ( kDefaultServerRouteConfigurationName ) ;
rds - > mutable_config_source ( ) - > mutable_self ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
http_connection_manager ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
}
TEST_P ( XdsServerRdsTest , NonInlineRouteConfigurationNotAvailable ) {
if ( ! GetParam ( ) . enable_rds_testing ( ) ) {
return ;
}
Listener listener = default_server_listener_ ;
PopulateServerListenerNameAndPort ( listener , backends_ [ 0 ] - > port ( ) ) ;
HttpConnectionManager http_connection_manager =
ServerHcmAccessor ( ) . Unpack ( listener ) ;
auto * rds = http_connection_manager . mutable_rds ( ) ;
rds - > set_route_config_name ( " unknown_server_route_config " ) ;
rds - > mutable_config_source ( ) - > mutable_self ( ) ;
listener . add_filter_chains ( ) - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
http_connection_manager ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
true /* test_expects_failure */ ) ;
}
// TODO(yashykt): Once https://github.com/grpc/grpc/issues/24035 is fixed, we
// should add tests that make sure that different route configs are used for
// incoming connections with a different match.
TEST_P ( XdsServerRdsTest , MultipleRouteConfigurations ) {
Listener listener = default_server_listener_ ;
// Set a filter chain with a new route config name
auto new_route_config = default_server_route_config_ ;
new_route_config . set_name ( " new_server_route_config " ) ;
HttpConnectionManager http_connection_manager =
ServerHcmAccessor ( ) . Unpack ( listener ) ;
auto * rds = http_connection_manager . mutable_rds ( ) ;
rds - > set_route_config_name ( new_route_config . name ( ) ) ;
rds - > mutable_config_source ( ) - > mutable_self ( ) ;
listener . add_filter_chains ( ) - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
http_connection_manager ) ;
// Set another filter chain with another route config name
auto another_route_config = default_server_route_config_ ;
another_route_config . set_name ( " another_server_route_config " ) ;
http_connection_manager . mutable_rds ( ) - > set_route_config_name (
another_route_config . name ( ) ) ;
auto * filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
http_connection_manager ) ;
filter_chain - > mutable_filter_chain_match ( ) - > set_source_type (
FilterChainMatch : : SAME_IP_OR_LOOPBACK ) ;
// Add another filter chain with the same route config name
filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
http_connection_manager ) ;
filter_chain - > mutable_filter_chain_match ( ) - > set_source_type (
FilterChainMatch : : EXTERNAL ) ;
// Add another filter chain with an inline route config
filter_chain = listener . add_filter_chains ( ) ;
filter_chain - > mutable_filter_chain_match ( ) - > add_source_ports ( 1234 ) ;
http_connection_manager = ServerHcmAccessor ( ) . Unpack ( listener ) ;
* http_connection_manager . mutable_route_config ( ) =
default_server_route_config_ ;
filter_chain - > add_filters ( ) - > mutable_typed_config ( ) - > PackFrom (
http_connection_manager ) ;
// Set resources on the ADS service
balancer_ - > ads_service ( ) - > SetRdsResource ( new_route_config ) ;
balancer_ - > ads_service ( ) - > SetRdsResource ( another_route_config ) ;
SetServerListenerNameAndRouteConfiguration ( balancer_ . get ( ) , listener ,
backends_ [ 0 ] - > port ( ) ,
default_server_route_config_ ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
}
// Tests RBAC configurations on the server with RDS testing and route config
// override permutations.
class XdsRbacTest : public XdsServerRdsTest {
protected :
void SetServerRbacPolicies ( Listener listener ,
const std : : vector < RBAC > & rbac_policies ) {
HttpConnectionManager http_connection_manager =
ServerHcmAccessor ( ) . Unpack ( listener ) ;
http_connection_manager . clear_http_filters ( ) ;
RouteConfiguration route_config = default_server_route_config_ ;
int count = 0 ;
for ( auto & rbac : rbac_policies ) {
auto * filter = http_connection_manager . add_http_filters ( ) ;
std : : string filter_name = absl : : StrFormat ( " rbac%d " , + + count ) ;
filter - > set_name ( filter_name ) ;
switch ( GetParam ( ) . filter_config_setup ( ) ) {
case XdsTestType : : HttpFilterConfigLocation : : kHttpFilterConfigInListener :
filter - > mutable_typed_config ( ) - > PackFrom ( rbac ) ;
break ;
case XdsTestType : : HttpFilterConfigLocation : : kHttpFilterConfigInRoute :
filter - > mutable_typed_config ( ) - > PackFrom ( RBAC ( ) ) ;
google : : protobuf : : Any filter_config ;
RBACPerRoute rbac_per_route ;
* rbac_per_route . mutable_rbac ( ) = rbac ;
filter_config . PackFrom ( rbac_per_route ) ;
auto * config_map = route_config . mutable_virtual_hosts ( 0 )
- > mutable_routes ( 0 )
- > mutable_typed_per_filter_config ( ) ;
( * config_map ) [ filter_name ] = std : : move ( filter_config ) ;
}
}
auto * filter = http_connection_manager . add_http_filters ( ) ;
filter - > set_name ( " router " ) ;
filter - > mutable_typed_config ( ) - > PackFrom (
envoy : : extensions : : filters : : http : : router : : v3 : : Router ( ) ) ;
ServerHcmAccessor ( ) . Pack ( http_connection_manager , & listener ) ;
SetServerListenerNameAndRouteConfiguration (
balancer_ . get ( ) , listener , backends_ [ 0 ] - > port ( ) , route_config ) ;
}
void SetServerRbacPolicy ( Listener listener , const RBAC & rbac ) {
SetServerRbacPolicies ( std : : move ( listener ) , { rbac } ) ;
}
void SetServerRbacPolicy ( const RBAC & rbac ) {
SetServerRbacPolicy ( default_server_listener_ , rbac ) ;
}
} ;
TEST_P ( XdsRbacTest , AbsentRbacPolicy ) {
SetServerRbacPolicy ( RBAC ( ) ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
// An absent RBAC policy leads to all RPCs being accepted.
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
}
TEST_P ( XdsRbacTest , LogAction ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( envoy : : config : : rbac : : v3 : : RBAC_Action_LOG ) ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
// A Log action is identical to no rbac policy being configured.
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
}
using XdsRbacNackTest = XdsRbacTest ;
TEST_P ( XdsRbacNackTest , NacksSchemePrincipalHeader ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( envoy : : config : : rbac : : v3 : : RBAC_Action_ALLOW ) ;
Policy policy ;
auto * header = policy . add_principals ( ) - > mutable_header ( ) ;
header - > set_name ( " :scheme " ) ;
header - > set_exact_match ( " http " ) ;
policy . add_permissions ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
if ( GetParam ( ) . enable_rds_testing ( ) & &
GetParam ( ) . filter_config_setup ( ) = =
XdsTestType : : HttpFilterConfigLocation : : kHttpFilterConfigInRoute ) {
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " ':scheme' not allowed in header " ) ) ;
} else {
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " ':scheme' not allowed in header " ) ) ;
}
}
TEST_P ( XdsRbacNackTest , NacksGrpcPrefixedPrincipalHeaders ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( envoy : : config : : rbac : : v3 : : RBAC_Action_ALLOW ) ;
Policy policy ;
auto * header = policy . add_principals ( ) - > mutable_header ( ) ;
header - > set_name ( " grpc-status " ) ;
header - > set_exact_match ( " 0 " ) ;
policy . add_permissions ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
if ( GetParam ( ) . enable_rds_testing ( ) & &
GetParam ( ) . filter_config_setup ( ) = =
XdsTestType : : HttpFilterConfigLocation : : kHttpFilterConfigInRoute ) {
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " 'grpc-' prefixes not allowed in header " ) ) ;
} else {
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " 'grpc-' prefixes not allowed in header " ) ) ;
}
}
TEST_P ( XdsRbacNackTest , NacksSchemePermissionHeader ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( envoy : : config : : rbac : : v3 : : RBAC_Action_ALLOW ) ;
Policy policy ;
auto * header = policy . add_permissions ( ) - > mutable_header ( ) ;
header - > set_name ( " :scheme " ) ;
header - > set_exact_match ( " http " ) ;
policy . add_principals ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
if ( GetParam ( ) . enable_rds_testing ( ) & &
GetParam ( ) . filter_config_setup ( ) = =
XdsTestType : : HttpFilterConfigLocation : : kHttpFilterConfigInRoute ) {
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " ':scheme' not allowed in header " ) ) ;
} else {
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " ':scheme' not allowed in header " ) ) ;
}
}
TEST_P ( XdsRbacNackTest , NacksGrpcPrefixedPermissionHeaders ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( envoy : : config : : rbac : : v3 : : RBAC_Action_ALLOW ) ;
Policy policy ;
auto * header = policy . add_permissions ( ) - > mutable_header ( ) ;
header - > set_name ( " grpc-status " ) ;
header - > set_exact_match ( " 0 " ) ;
policy . add_principals ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
if ( GetParam ( ) . enable_rds_testing ( ) & &
GetParam ( ) . filter_config_setup ( ) = =
XdsTestType : : HttpFilterConfigLocation : : kHttpFilterConfigInRoute ) {
const auto response_state = WaitForRdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " 'grpc-' prefixes not allowed in header " ) ) ;
} else {
const auto response_state = WaitForLdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " 'grpc-' prefixes not allowed in header " ) ) ;
}
}
// Tests RBAC policies where a route override is always present. Action
// permutations are not added.
using XdsRbacTestWithRouteOverrideAlwaysPresent = XdsRbacTest ;
TEST_P ( XdsRbacTestWithRouteOverrideAlwaysPresent , EmptyRBACPerRouteOverride ) {
HttpConnectionManager http_connection_manager ;
Listener listener = default_server_listener_ ;
RouteConfiguration route_config = default_server_route_config_ ;
auto * filter = http_connection_manager . add_http_filters ( ) ;
filter - > set_name ( " rbac " ) ;
// Create a top-level RBAC policy with a DENY action for all RPCs
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( RBAC_Action_DENY ) ;
Policy policy ;
policy . add_permissions ( ) - > set_any ( true ) ;
policy . add_principals ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
filter - > mutable_typed_config ( ) - > PackFrom ( rbac ) ;
// Override with an Empty RBACPerRoute policy which should result in RBAC
// being disabled and RPCs being allowed.
google : : protobuf : : Any filter_config ;
filter_config . PackFrom ( RBACPerRoute ( ) ) ;
auto * config_map = route_config . mutable_virtual_hosts ( 0 )
- > mutable_routes ( 0 )
- > mutable_typed_per_filter_config ( ) ;
( * config_map ) [ " rbac " ] = std : : move ( filter_config ) ;
filter = http_connection_manager . add_http_filters ( ) ;
filter - > set_name ( " router " ) ;
filter - > mutable_typed_config ( ) - > PackFrom (
envoy : : extensions : : filters : : http : : router : : v3 : : Router ( ) ) ;
ServerHcmAccessor ( ) . Pack ( http_connection_manager , & listener ) ;
SetServerListenerNameAndRouteConfiguration (
balancer_ . get ( ) , listener , backends_ [ 0 ] - > port ( ) , route_config ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
}
// Test a non-empty top level RBAC with a non-empty RBACPerRouteOverride
TEST_P ( XdsRbacTestWithRouteOverrideAlwaysPresent ,
NonEmptyTopLevelRBACNonEmptyPerRouteOverride ) {
HttpConnectionManager http_connection_manager ;
Listener listener = default_server_listener_ ;
RouteConfiguration route_config = default_server_route_config_ ;
auto * filter = http_connection_manager . add_http_filters ( ) ;
filter - > set_name ( " rbac " ) ;
// Create a top-level RBAC policy with a DENY action for all RPCs
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( RBAC_Action_DENY ) ;
Policy policy ;
policy . add_permissions ( ) - > set_any ( true ) ;
policy . add_principals ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
filter - > mutable_typed_config ( ) - > PackFrom ( rbac ) ;
// Override with a non-empty RBACPerRoute policy which allows all RPCs.
google : : protobuf : : Any filter_config ;
RBACPerRoute rbac_per_route ;
rules = rbac_per_route . mutable_rbac ( ) - > mutable_rules ( ) ;
rules - > set_action ( RBAC_Action_ALLOW ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
filter_config . PackFrom ( RBACPerRoute ( ) ) ;
auto * config_map = route_config . mutable_virtual_hosts ( 0 )
- > mutable_routes ( 0 )
- > mutable_typed_per_filter_config ( ) ;
( * config_map ) [ " rbac " ] = std : : move ( filter_config ) ;
filter = http_connection_manager . add_http_filters ( ) ;
filter - > set_name ( " router " ) ;
filter - > mutable_typed_config ( ) - > PackFrom (
envoy : : extensions : : filters : : http : : router : : v3 : : Router ( ) ) ;
ServerHcmAccessor ( ) . Pack ( http_connection_manager , & listener ) ;
SetServerListenerNameAndRouteConfiguration (
balancer_ . get ( ) , listener , backends_ [ 0 ] - > port ( ) , route_config ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ) ;
}
// Adds Action Permutations to XdsRbacTest
using XdsRbacTestWithActionPermutations = XdsRbacTest ;
TEST_P ( XdsRbacTestWithActionPermutations , EmptyRbacPolicy ) {
RBAC rbac ;
rbac . mutable_rules ( ) - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
// An empty RBAC policy leads to all RPCs being rejected.
SendRpc (
[ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_ALLOW ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
}
TEST_P ( XdsRbacTestWithActionPermutations , AnyPermissionAnyPrincipal ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
policy . add_permissions ( ) - > set_any ( true ) ;
policy . add_principals ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_DENY ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
}
TEST_P ( XdsRbacTestWithActionPermutations , MultipleRbacPolicies ) {
RBAC always_allow ;
auto * rules = always_allow . mutable_rules ( ) ;
rules - > set_action ( RBAC_Action_ALLOW ) ;
Policy policy ;
policy . add_permissions ( ) - > set_any ( true ) ;
policy . add_principals ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
RBAC rbac ;
rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicies ( default_server_listener_ ,
{ always_allow , rbac , always_allow } ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_DENY ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
}
TEST_P ( XdsRbacTestWithActionPermutations , MethodPostPermissionAnyPrincipal ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
auto * header = policy . add_permissions ( ) - > mutable_header ( ) ;
header - > set_name ( " :method " ) ;
header - > set_exact_match ( " POST " ) ;
policy . add_principals ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
// All RPCs use POST method by default
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_DENY ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
// Test that an RPC with PUT method is handled properly.
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( /*use_put_requests=*/ true ) ; } ,
{ } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) ! = RBAC_Action_DENY ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
}
TEST_P ( XdsRbacTestWithActionPermutations , MethodGetPermissionAnyPrincipal ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
auto * header = policy . add_permissions ( ) - > mutable_header ( ) ;
header - > set_name ( " :method " ) ;
header - > set_exact_match ( " GET " ) ;
policy . add_principals ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
// Test that an RPC with a POST method gets rejected
SendRpc (
[ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_ALLOW ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
// TODO(yashykt): When we start supporting GET requests in the future, this
// should be modified to test that they are accepted with this rule.
}
TEST_P ( XdsRbacTestWithActionPermutations , MethodPutPermissionAnyPrincipal ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
auto * header = policy . add_permissions ( ) - > mutable_header ( ) ;
header - > set_name ( " :method " ) ;
header - > set_exact_match ( " PUT " ) ;
policy . add_principals ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
// Test that an RPC with a POST method gets rejected
SendRpc (
[ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_ALLOW ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
// Test that an RPC with a PUT method gets accepted
SendRpc (
[ this ] ( ) { return CreateInsecureChannel ( /*use_put_requests=*/ true ) ; } , { } ,
{ } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) ! = RBAC_Action_ALLOW ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
}
TEST_P ( XdsRbacTestWithActionPermutations , UrlPathPermissionAnyPrincipal ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
policy . add_permissions ( ) - > mutable_url_path ( ) - > mutable_path ( ) - > set_exact (
" /grpc.testing.EchoTestService/Echo " ) ;
policy . add_principals ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_DENY ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
// Test an RPC with a different URL path
auto stub = grpc : : testing : : EchoTestService : : NewStub ( CreateInsecureChannel ( ) ) ;
ClientContext context ;
context . set_wait_for_ready ( true ) ;
context . set_deadline ( grpc_timeout_milliseconds_to_deadline ( 2000 ) ) ;
EchoRequest request ;
request . set_message ( kRequestMessage ) ;
EchoResponse response ;
Status status = stub - > Echo1 ( & context , request , & response ) ;
EXPECT_TRUE ( GetParam ( ) . rbac_action ( ) = = RBAC_Action_DENY ? status . ok ( )
: ! status . ok ( ) )
< < status . error_code ( ) < < " , " < < status . error_message ( ) < < " , "
< < status . error_details ( ) < < " , " < < context . debug_error_string ( ) ;
}
TEST_P ( XdsRbacTestWithActionPermutations , DestinationIpPermissionAnyPrincipal ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
auto * range = policy . add_permissions ( ) - > mutable_destination_ip ( ) ;
range - > set_address_prefix ( ipv6_only_ ? " ::1 " : " 127.0.0.1 " ) ;
range - > mutable_prefix_len ( ) - > set_value ( ipv6_only_ ? 128 : 32 ) ;
policy . add_principals ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_DENY ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
// Change the policy itself for a negative test where there is no match.
policy . clear_permissions ( ) ;
range = policy . add_permissions ( ) - > mutable_destination_ip ( ) ;
range - > set_address_prefix ( ipv6_only_ ? " ::2 " : " 127.0.0.2 " ) ;
range - > mutable_prefix_len ( ) - > set_value ( ipv6_only_ ? 128 : 32 ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
SendRpc (
[ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_ALLOW ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
}
TEST_P ( XdsRbacTestWithActionPermutations ,
DestinationPortPermissionAnyPrincipal ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
policy . add_permissions ( ) - > set_destination_port ( backends_ [ 0 ] - > port ( ) ) ;
policy . add_principals ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_DENY ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
// Change the policy itself for a negative test where there is no match.
policy . clear_permissions ( ) ;
policy . add_permissions ( ) - > set_destination_port ( 1 ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
SendRpc (
[ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_ALLOW ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
}
TEST_P ( XdsRbacTestWithActionPermutations , MetadataPermissionAnyPrincipal ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
policy . add_permissions ( ) - > mutable_metadata ( ) ;
policy . add_principals ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc (
[ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_ALLOW ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
// Test metadata with inverted match
policy . clear_permissions ( ) ;
policy . add_permissions ( ) - > mutable_metadata ( ) - > set_invert ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_DENY ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
}
TEST_P ( XdsRbacTestWithActionPermutations , ReqServerNamePermissionAnyPrincipal ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
policy . add_principals ( ) - > set_any ( true ) ;
policy . add_permissions ( ) - > mutable_requested_server_name ( ) - > set_exact (
" server_name " ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc (
[ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_ALLOW ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
policy . clear_permissions ( ) ;
policy . add_permissions ( ) - > mutable_requested_server_name ( ) - > set_exact ( " " ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_DENY ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
}
TEST_P ( XdsRbacTestWithActionPermutations , NotRulePermissionAnyPrincipal ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
policy . add_permissions ( )
- > mutable_not_rule ( )
- > mutable_requested_server_name ( )
- > set_exact ( " server_name " ) ;
policy . add_principals ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_DENY ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
// Change the policy itself for a negative test where there is no match.
policy . clear_permissions ( ) ;
policy . add_permissions ( ) - > mutable_not_rule ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
SendRpc (
[ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_ALLOW ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
}
TEST_P ( XdsRbacTestWithActionPermutations , AndRulePermissionAnyPrincipal ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
auto * and_rules = policy . add_permissions ( ) - > mutable_and_rules ( ) ;
and_rules - > add_rules ( ) - > set_any ( true ) ;
and_rules - > add_rules ( ) - > set_destination_port ( backends_ [ 0 ] - > port ( ) ) ;
policy . add_principals ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_DENY ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
// Change the policy itself for a negative test where there is no match.
and_rules = ( * policy . mutable_permissions ( ) ) [ 0 ] . mutable_and_rules ( ) ;
( * and_rules - > mutable_rules ( ) ) [ 1 ] . set_destination_port ( 1 ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
SendRpc (
[ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_ALLOW ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
}
TEST_P ( XdsRbacTestWithActionPermutations , OrRulePermissionAnyPrincipal ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
auto * or_rules = policy . add_permissions ( ) - > mutable_or_rules ( ) ;
or_rules - > add_rules ( ) - > mutable_not_rule ( ) - > set_any ( true ) ;
or_rules - > add_rules ( ) - > set_destination_port ( backends_ [ 0 ] - > port ( ) ) ;
policy . add_principals ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_DENY ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
// Change the policy itself for a negative test where there is no match.
or_rules = ( * policy . mutable_permissions ( ) ) [ 0 ] . mutable_or_rules ( ) ;
( * or_rules - > mutable_rules ( ) ) [ 1 ] . set_destination_port ( 1 ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
SendRpc (
[ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_ALLOW ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
}
TEST_P ( XdsRbacTestWithActionPermutations , AnyPermissionMethodPostPrincipal ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
auto * header = policy . add_principals ( ) - > mutable_header ( ) ;
header - > set_name ( " :method " ) ;
header - > set_exact_match ( " POST " ) ;
policy . add_permissions ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
// All RPCs use POST method by default
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_DENY ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
// Test that an RPC with PUT method is handled properly.
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( /*use_put_requests=*/ true ) ; } ,
{ } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) ! = RBAC_Action_DENY ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
}
TEST_P ( XdsRbacTestWithActionPermutations , AnyPermissionMethodGetPrincipal ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
auto * header = policy . add_principals ( ) - > mutable_header ( ) ;
header - > set_name ( " :method " ) ;
header - > set_exact_match ( " GET " ) ;
policy . add_permissions ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
// Test that an RPC with a POST method gets rejected
SendRpc (
[ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_ALLOW ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
// TODO(yashykt): When we start supporting GET requests in the future, this
// should be modified to test that they are accepted with this rule.
}
TEST_P ( XdsRbacTestWithActionPermutations , AnyPermissionMethodPutPrincipal ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
auto * header = policy . add_principals ( ) - > mutable_header ( ) ;
header - > set_name ( " :method " ) ;
header - > set_exact_match ( " PUT " ) ;
policy . add_permissions ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
// Test that an RPC with a PUT method gets accepted
SendRpc (
[ this ] ( ) { return CreateInsecureChannel ( /*use_put_requests=*/ true ) ; } , { } ,
{ } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) ! = RBAC_Action_ALLOW ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
// Test that an RPC with a POST method gets rejected
SendRpc (
[ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_ALLOW ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
}
TEST_P ( XdsRbacTestWithActionPermutations , AnyPermissionUrlPathPrincipal ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
policy . add_principals ( ) - > mutable_url_path ( ) - > mutable_path ( ) - > set_exact (
" /grpc.testing.EchoTestService/Echo " ) ;
policy . add_permissions ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_DENY ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
// Test an RPC with a different URL path
auto stub = grpc : : testing : : EchoTestService : : NewStub ( CreateInsecureChannel ( ) ) ;
ClientContext context ;
context . set_wait_for_ready ( true ) ;
context . set_deadline ( grpc_timeout_milliseconds_to_deadline ( 2000 ) ) ;
EchoRequest request ;
request . set_message ( kRequestMessage ) ;
EchoResponse response ;
Status status = stub - > Echo1 ( & context , request , & response ) ;
EXPECT_TRUE ( GetParam ( ) . rbac_action ( ) = = RBAC_Action_DENY ? status . ok ( )
: ! status . ok ( ) )
< < status . error_code ( ) < < " , " < < status . error_message ( ) < < " , "
< < status . error_details ( ) < < " , " < < context . debug_error_string ( ) ;
}
TEST_P ( XdsRbacTestWithActionPermutations ,
AnyPermissionDirectRemoteIpPrincipal ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
auto * range = policy . add_principals ( ) - > mutable_direct_remote_ip ( ) ;
range - > set_address_prefix ( ipv6_only_ ? " ::1 " : " 127.0.0.1 " ) ;
range - > mutable_prefix_len ( ) - > set_value ( ipv6_only_ ? 128 : 32 ) ;
policy . add_permissions ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_DENY ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
// Change the policy itself for a negative test where there is no match.
policy . clear_principals ( ) ;
range = policy . add_principals ( ) - > mutable_direct_remote_ip ( ) ;
range - > set_address_prefix ( ipv6_only_ ? " ::2 " : " 127.0.0.2 " ) ;
range - > mutable_prefix_len ( ) - > set_value ( ipv6_only_ ? 128 : 32 ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
SendRpc (
[ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_ALLOW ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
}
TEST_P ( XdsRbacTestWithActionPermutations , AnyPermissionRemoteIpPrincipal ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
auto * range = policy . add_principals ( ) - > mutable_remote_ip ( ) ;
range - > set_address_prefix ( ipv6_only_ ? " ::1 " : " 127.0.0.1 " ) ;
range - > mutable_prefix_len ( ) - > set_value ( ipv6_only_ ? 128 : 32 ) ;
policy . add_permissions ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_DENY ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
// Change the policy itself for a negative test where there is no match.
policy . clear_principals ( ) ;
range = policy . add_principals ( ) - > mutable_remote_ip ( ) ;
range - > set_address_prefix ( ipv6_only_ ? " ::2 " : " 127.0.0.2 " ) ;
range - > mutable_prefix_len ( ) - > set_value ( ipv6_only_ ? 128 : 32 ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
SendRpc (
[ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_ALLOW ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
}
TEST_P ( XdsRbacTestWithActionPermutations , AnyPermissionAuthenticatedPrincipal ) {
FakeCertificateProvider : : CertDataMap fake1_cert_map = {
{ " " , { root_cert_ , identity_pair_ } } } ;
g_fake1_cert_data_map = & fake1_cert_map ;
Listener listener = default_server_listener_ ;
auto * filter_chain = listener . mutable_default_filter_chain ( ) ;
auto * transport_socket = filter_chain - > mutable_transport_socket ( ) ;
transport_socket - > set_name ( " envoy.transport_sockets.tls " ) ;
DownstreamTlsContext downstream_tls_context ;
downstream_tls_context . mutable_common_tls_context ( )
- > mutable_tls_certificate_provider_instance ( )
- > set_instance_name ( " fake_plugin1 " ) ;
downstream_tls_context . mutable_common_tls_context ( )
- > mutable_validation_context ( )
- > mutable_ca_certificate_provider_instance ( )
- > set_instance_name ( " fake_plugin1 " ) ;
downstream_tls_context . mutable_require_client_certificate ( ) - > set_value ( true ) ;
transport_socket - > mutable_typed_config ( ) - > PackFrom ( downstream_tls_context ) ;
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
policy . add_principals ( )
- > mutable_authenticated ( )
- > mutable_principal_name ( )
- > set_exact ( " *.test.google.fr " ) ;
policy . add_permissions ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( listener , rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateMtlsChannel ( ) ; } ,
server_authenticated_identity_ , client_authenticated_identity_ ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_DENY ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
}
TEST_P ( XdsRbacTestWithActionPermutations , AnyPermissionMetadataPrincipal ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
policy . add_principals ( ) - > mutable_metadata ( ) ;
policy . add_permissions ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc (
[ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_ALLOW ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
// Test metadata with inverted match
policy . clear_principals ( ) ;
policy . add_principals ( ) - > mutable_metadata ( ) - > set_invert ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_DENY ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
}
TEST_P ( XdsRbacTestWithActionPermutations , AnyPermissionNotIdPrincipal ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
policy . add_principals ( )
- > mutable_not_id ( )
- > mutable_url_path ( )
- > mutable_path ( )
- > set_exact ( " /grpc.testing.EchoTestService/Echo1 " ) ;
policy . add_permissions ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_DENY ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
// Change the policy itself for a negative test where there is no match.
policy . clear_principals ( ) ;
policy . add_principals ( ) - > mutable_not_id ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
SendRpc (
[ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_ALLOW ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
}
TEST_P ( XdsRbacTestWithActionPermutations , AnyPermissionAndIdPrincipal ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
auto * and_ids = policy . add_principals ( ) - > mutable_and_ids ( ) ;
and_ids - > add_ids ( ) - > set_any ( true ) ;
and_ids - > add_ids ( ) - > mutable_url_path ( ) - > mutable_path ( ) - > set_exact (
" /grpc.testing.EchoTestService/Echo " ) ;
policy . add_permissions ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_DENY ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
// Change the policy itself for a negative test where there is no match.
and_ids = ( * policy . mutable_principals ( ) ) [ 0 ] . mutable_and_ids ( ) ;
( * and_ids - > mutable_ids ( ) ) [ 1 ] . mutable_url_path ( ) - > mutable_path ( ) - > set_exact (
" /grpc.testing.EchoTestService/Echo1 " ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
SendRpc (
[ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_ALLOW ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
}
TEST_P ( XdsRbacTestWithActionPermutations , AnyPermissionOrIdPrincipal ) {
RBAC rbac ;
auto * rules = rbac . mutable_rules ( ) ;
rules - > set_action ( GetParam ( ) . rbac_action ( ) ) ;
Policy policy ;
auto * or_ids = policy . add_principals ( ) - > mutable_or_ids ( ) ;
or_ids - > add_ids ( ) - > mutable_not_id ( ) - > set_any ( true ) ;
or_ids - > add_ids ( ) - > mutable_url_path ( ) - > mutable_path ( ) - > set_exact (
" /grpc.testing.EchoTestService/Echo " ) ;
policy . add_permissions ( ) - > set_any ( true ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
backends_ [ 0 ] - > Start ( ) ;
backends_ [ 0 ] - > notifier ( ) - > WaitOnServingStatusChange (
absl : : StrCat ( ipv6_only_ ? " [::1]: " : " 127.0.0.1: " , backends_ [ 0 ] - > port ( ) ) ,
grpc : : StatusCode : : OK ) ;
SendRpc ( [ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_DENY ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
// Change the policy itself for a negative test where there is no match.
or_ids = ( * policy . mutable_principals ( ) ) [ 0 ] . mutable_or_ids ( ) ;
( * or_ids - > mutable_ids ( ) ) [ 1 ] . mutable_url_path ( ) - > mutable_path ( ) - > set_exact (
" /grpc.testing.EchoTestService/Echo1 " ) ;
( * rules - > mutable_policies ( ) ) [ " policy " ] = policy ;
SetServerRbacPolicy ( rbac ) ;
SendRpc (
[ this ] ( ) { return CreateInsecureChannel ( ) ; } , { } , { } ,
/*test_expects_failure=*/ GetParam ( ) . rbac_action ( ) = = RBAC_Action_ALLOW ,
grpc : : StatusCode : : PERMISSION_DENIED ) ;
}
using EdsTest = XdsEnd2endTest ;
// Tests that EDS client should send a NACK if the EDS update contains
// sparse priorities.
TEST_P ( EdsTest , NacksSparsePriorityList ) {
EdsResourceArgs args ( {
{ " locality0 " , { MakeNonExistantEndpoint ( ) } , kDefaultLocalityWeight , 1 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
const auto response_state = WaitForEdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr ( " sparse priority list " ) ) ;
}
// Tests that EDS client should send a NACK if the EDS update contains
// multiple instances of the same locality in the same priority.
TEST_P ( EdsTest , NacksDuplicateLocalityInSamePriority ) {
EdsResourceArgs args ( {
{ " locality0 " , { MakeNonExistantEndpoint ( ) } , kDefaultLocalityWeight , 0 } ,
{ " locality0 " , { MakeNonExistantEndpoint ( ) } , kDefaultLocalityWeight , 0 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
const auto response_state = WaitForEdsNack ( ) ;
ASSERT_TRUE ( response_state . has_value ( ) ) < < " timed out waiting for NACK " ;
EXPECT_THAT ( response_state - > error_message ,
: : testing : : HasSubstr (
" duplicate locality {region= \" xds_default_locality_region \" , "
" zone= \" xds_default_locality_zone \" , sub_zone= \" locality0 \" } "
" found in priority 0 " ) ) ;
}
// In most of our tests, we use different names for different resource
// types, to make sure that there are no cut-and-paste errors in the code
// that cause us to look at data for the wrong resource type. So we add
// this test to make sure that the EDS resource name defaults to the
// cluster name if not specified in the CDS resource.
TEST_P ( EdsTest , EdsServiceNameDefaultsToClusterName ) {
CreateAndStartBackends ( 1 ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args , kDefaultClusterName ) ) ;
Cluster cluster = default_cluster_ ;
cluster . mutable_eds_cluster_config ( ) - > clear_service_name ( ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( cluster ) ;
CheckRpcSendOk ( ) ;
}
using LocalityMapTest = XdsEnd2endTest ;
// Tests that the localities in a locality map are picked according to their
// weights.
TEST_P ( LocalityMapTest , WeightedRoundRobin ) {
CreateAndStartBackends ( 2 ) ;
const int kLocalityWeight0 = 2 ;
const int kLocalityWeight1 = 8 ;
const int kTotalLocalityWeight = kLocalityWeight0 + kLocalityWeight1 ;
const double kLocalityWeightRate0 =
static_cast < double > ( kLocalityWeight0 ) / kTotalLocalityWeight ;
const double kLocalityWeightRate1 =
static_cast < double > ( kLocalityWeight1 ) / kTotalLocalityWeight ;
const double kErrorTolerance = 0.05 ;
const size_t kNumRpcs =
ComputeIdealNumRpcs ( kLocalityWeightRate0 , kErrorTolerance ) ;
// ADS response contains 2 localities, each of which contains 1 backend.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) , kLocalityWeight0 } ,
{ " locality1 " , CreateEndpointsForBackends ( 1 , 2 ) , kLocalityWeight1 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Wait for both backends to be ready.
WaitForAllBackends ( 0 , 2 ) ;
// Send kNumRpcs RPCs.
CheckRpcSendOk ( kNumRpcs ) ;
// The locality picking rates should be roughly equal to the expectation.
const double locality_picked_rate_0 =
static_cast < double > ( backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) /
kNumRpcs ;
const double locality_picked_rate_1 =
static_cast < double > ( backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ) /
kNumRpcs ;
EXPECT_THAT ( locality_picked_rate_0 ,
: : testing : : DoubleNear ( kLocalityWeightRate0 , kErrorTolerance ) ) ;
EXPECT_THAT ( locality_picked_rate_1 ,
: : testing : : DoubleNear ( kLocalityWeightRate1 , kErrorTolerance ) ) ;
}
// Tests that we correctly handle a locality containing no endpoints.
TEST_P ( LocalityMapTest , LocalityContainingNoEndpoints ) {
CreateAndStartBackends ( 2 ) ;
const size_t kNumRpcs = 5000 ;
// EDS response contains 2 localities, one with no endpoints.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( ) } ,
{ " locality1 " , { } } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Wait for both backends to be ready.
WaitForAllBackends ( ) ;
// Send kNumRpcs RPCs.
CheckRpcSendOk ( kNumRpcs ) ;
// All traffic should go to the reachable locality.
EXPECT_EQ ( backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ,
kNumRpcs / backends_ . size ( ) ) ;
EXPECT_EQ ( backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ,
kNumRpcs / backends_ . size ( ) ) ;
}
// EDS update with no localities.
TEST_P ( LocalityMapTest , NoLocalities ) {
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( { } ) ) ;
Status status = SendRpc ( ) ;
EXPECT_FALSE ( status . ok ( ) ) ;
EXPECT_EQ ( status . error_code ( ) , StatusCode : : UNAVAILABLE ) ;
}
// Tests that the locality map can work properly even when it contains a large
// number of localities.
TEST_P ( LocalityMapTest , StressTest ) {
CreateAndStartBackends ( 2 ) ;
const size_t kNumLocalities = 100 ;
const uint32_t kRpcTimeoutMs = 5000 ;
// The first ADS response contains kNumLocalities localities, each of which
// contains backend 0.
EdsResourceArgs args ;
for ( size_t i = 0 ; i < kNumLocalities ; + + i ) {
std : : string name = absl : : StrCat ( " locality " , i ) ;
EdsResourceArgs : : Locality locality ( name , CreateEndpointsForBackends ( 0 , 1 ) ) ;
args . locality_list . emplace_back ( std : : move ( locality ) ) ;
}
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Wait until backend 0 is ready.
WaitForBackend ( 0 , WaitForBackendOptions ( ) . set_reset_counters ( false ) ,
RpcOptions ( ) . set_timeout_ms ( kRpcTimeoutMs ) ) ;
EXPECT_EQ ( 0U , backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ) ;
// The second ADS response contains 1 locality, which contains backend 1.
args = EdsResourceArgs ( { { " locality0 " , CreateEndpointsForBackends ( 1 , 2 ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Wait until backend 1 is ready.
WaitForBackend ( 1 ) ;
}
// Tests that the localities in a locality map are picked correctly after
// update (addition, modification, deletion).
TEST_P ( LocalityMapTest , UpdateMap ) {
CreateAndStartBackends ( 4 ) ;
const size_t kNumRpcs = 3000 ;
// The locality weight for the first 3 localities.
const std : : vector < int > kLocalityWeights0 = { 2 , 3 , 4 } ;
const double kTotalLocalityWeight0 =
std : : accumulate ( kLocalityWeights0 . begin ( ) , kLocalityWeights0 . end ( ) , 0 ) ;
std : : vector < double > locality_weight_rate_0 ;
locality_weight_rate_0 . reserve ( kLocalityWeights0 . size ( ) ) ;
for ( int weight : kLocalityWeights0 ) {
locality_weight_rate_0 . push_back ( weight / kTotalLocalityWeight0 ) ;
}
// Delete the first locality, keep the second locality, change the third
// locality's weight from 4 to 2, and add a new locality with weight 6.
const std : : vector < int > kLocalityWeights1 = { 3 , 2 , 6 } ;
const double kTotalLocalityWeight1 =
std : : accumulate ( kLocalityWeights1 . begin ( ) , kLocalityWeights1 . end ( ) , 0 ) ;
std : : vector < double > locality_weight_rate_1 = {
0 /* placeholder for locality 0 */ } ;
for ( int weight : kLocalityWeights1 ) {
locality_weight_rate_1 . push_back ( weight / kTotalLocalityWeight1 ) ;
}
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) , 2 } ,
{ " locality1 " , CreateEndpointsForBackends ( 1 , 2 ) , 3 } ,
{ " locality2 " , CreateEndpointsForBackends ( 2 , 3 ) , 4 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Wait for the first 3 backends to be ready.
WaitForAllBackends ( 0 , 3 ) ;
gpr_log ( GPR_INFO , " ========= BEFORE FIRST BATCH ========== " ) ;
// Send kNumRpcs RPCs.
CheckRpcSendOk ( kNumRpcs ) ;
gpr_log ( GPR_INFO , " ========= DONE WITH FIRST BATCH ========== " ) ;
// The picking rates of the first 3 backends should be roughly equal to the
// expectation.
std : : vector < double > locality_picked_rates ;
for ( size_t i = 0 ; i < 3 ; + + i ) {
locality_picked_rates . push_back (
static_cast < double > ( backends_ [ i ] - > backend_service ( ) - > request_count ( ) ) /
kNumRpcs ) ;
}
const double kErrorTolerance = 0.2 ;
for ( size_t i = 0 ; i < 3 ; + + i ) {
gpr_log ( GPR_INFO , " Locality % " PRIuPTR " rate %f " , i ,
locality_picked_rates [ i ] ) ;
EXPECT_THAT (
locality_picked_rates [ i ] ,
: : testing : : AllOf (
: : testing : : Ge ( locality_weight_rate_0 [ i ] * ( 1 - kErrorTolerance ) ) ,
: : testing : : Le ( locality_weight_rate_0 [ i ] * ( 1 + kErrorTolerance ) ) ) ) ;
}
args = EdsResourceArgs ( {
{ " locality1 " , CreateEndpointsForBackends ( 1 , 2 ) , 3 } ,
{ " locality2 " , CreateEndpointsForBackends ( 2 , 3 ) , 2 } ,
{ " locality3 " , CreateEndpointsForBackends ( 3 , 4 ) , 6 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Backend 3 hasn't received any request.
EXPECT_EQ ( 0U , backends_ [ 3 ] - > backend_service ( ) - > request_count ( ) ) ;
// Wait until the locality update has been processed, as signaled by backend
// 3 receiving a request.
WaitForAllBackends ( 3 , 4 ) ;
gpr_log ( GPR_INFO , " ========= BEFORE SECOND BATCH ========== " ) ;
// Send kNumRpcs RPCs.
CheckRpcSendOk ( kNumRpcs ) ;
gpr_log ( GPR_INFO , " ========= DONE WITH SECOND BATCH ========== " ) ;
// Backend 0 no longer receives any request.
EXPECT_EQ ( 0U , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
// The picking rates of the last 3 backends should be roughly equal to the
// expectation.
locality_picked_rates = { 0 /* placeholder for backend 0 */ } ;
for ( size_t i = 1 ; i < 4 ; + + i ) {
locality_picked_rates . push_back (
static_cast < double > ( backends_ [ i ] - > backend_service ( ) - > request_count ( ) ) /
kNumRpcs ) ;
}
for ( size_t i = 1 ; i < 4 ; + + i ) {
gpr_log ( GPR_INFO , " Locality % " PRIuPTR " rate %f " , i ,
locality_picked_rates [ i ] ) ;
EXPECT_THAT (
locality_picked_rates [ i ] ,
: : testing : : AllOf (
: : testing : : Ge ( locality_weight_rate_1 [ i ] * ( 1 - kErrorTolerance ) ) ,
: : testing : : Le ( locality_weight_rate_1 [ i ] * ( 1 + kErrorTolerance ) ) ) ) ;
}
}
// Tests that we don't fail RPCs when replacing all of the localities in
// a given priority.
TEST_P ( LocalityMapTest , ReplaceAllLocalitiesInPriority ) {
CreateAndStartBackends ( 2 ) ;
// Initial EDS update has backend 0.
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Wait for the first backend to be ready.
WaitForBackend ( 0 ) ;
// Send EDS update that replaces the locality and switches to backend 1.
args = EdsResourceArgs ( { { " locality1 " , CreateEndpointsForBackends ( 1 , 2 ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// When the client sees the update, RPCs should start going to backend 1.
// No RPCs should fail during this change.
WaitForBackend ( 1 ) ;
}
TEST_P ( LocalityMapTest , ConsistentWeightedTargetUpdates ) {
CreateAndStartBackends ( 4 ) ;
// Initial update has two localities.
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 1 , 2 ) } ,
{ " locality1 " , CreateEndpointsForBackends ( 2 , 3 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForAllBackends ( 1 , 3 ) ;
// Next update removes locality1.
// Also add backend 0 to locality0, so that we can tell when the
// update has been seen.
args = EdsResourceArgs ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 2 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForBackend ( 0 ) ;
// Next update re-adds locality1.
// Also add backend 3 to locality1, so that we can tell when the
// update has been seen.
args = EdsResourceArgs ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 2 ) } ,
{ " locality1 " , CreateEndpointsForBackends ( 2 , 4 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForBackend ( 3 ) ;
}
class FailoverTest : public XdsEnd2endTest {
public :
void SetUp ( ) override {
XdsEnd2endTest : : SetUp ( ) ;
ResetStub ( /*failover_timeout_ms=*/ 500 ) ;
}
} ;
// Localities with the highest priority are used when multiple priority exist.
TEST_P ( FailoverTest , ChooseHighestPriority ) {
CreateAndStartBackends ( 4 ) ;
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) , kDefaultLocalityWeight ,
1 } ,
{ " locality1 " , CreateEndpointsForBackends ( 1 , 2 ) , kDefaultLocalityWeight ,
2 } ,
{ " locality2 " , CreateEndpointsForBackends ( 2 , 3 ) , kDefaultLocalityWeight ,
3 } ,
{ " locality3 " , CreateEndpointsForBackends ( 3 , 4 ) , kDefaultLocalityWeight ,
0 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForBackend ( 3 , WaitForBackendOptions ( ) . set_reset_counters ( false ) ) ;
for ( size_t i = 0 ; i < 3 ; + + i ) {
EXPECT_EQ ( 0U , backends_ [ i ] - > backend_service ( ) - > request_count ( ) ) ;
}
}
// Does not choose priority with no endpoints.
TEST_P ( FailoverTest , DoesNotUsePriorityWithNoEndpoints ) {
CreateAndStartBackends ( 3 ) ;
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) , kDefaultLocalityWeight ,
1 } ,
{ " locality1 " , CreateEndpointsForBackends ( 1 , 2 ) , kDefaultLocalityWeight ,
2 } ,
{ " locality2 " , CreateEndpointsForBackends ( 2 , 3 ) , kDefaultLocalityWeight ,
3 } ,
{ " locality3 " , { } , kDefaultLocalityWeight , 0 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForBackend ( 0 , WaitForBackendOptions ( ) . set_reset_counters ( false ) ) ;
for ( size_t i = 1 ; i < 3 ; + + i ) {
EXPECT_EQ ( 0U , backends_ [ i ] - > backend_service ( ) - > request_count ( ) ) ;
}
}
// Does not choose locality with no endpoints.
TEST_P ( FailoverTest , DoesNotUseLocalityWithNoEndpoints ) {
CreateAndStartBackends ( 1 ) ;
EdsResourceArgs args ( {
{ " locality0 " , { } , kDefaultLocalityWeight , 0 } ,
{ " locality1 " , CreateEndpointsForBackends ( ) , kDefaultLocalityWeight , 0 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Wait for all backends to be used.
WaitForAllBackends ( ) ;
}
// If the higher priority localities are not reachable, failover to the
// highest priority among the rest.
TEST_P ( FailoverTest , Failover ) {
CreateAndStartBackends ( 2 ) ;
EdsResourceArgs args ( {
{ " locality0 " , { MakeNonExistantEndpoint ( ) } , kDefaultLocalityWeight , 1 } ,
{ " locality1 " , CreateEndpointsForBackends ( 0 , 1 ) , kDefaultLocalityWeight ,
2 } ,
{ " locality2 " , CreateEndpointsForBackends ( 1 , 2 ) , kDefaultLocalityWeight ,
3 } ,
{ " locality3 " , { MakeNonExistantEndpoint ( ) } , kDefaultLocalityWeight , 0 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForBackend ( 0 , WaitForBackendOptions ( ) . set_reset_counters ( false ) ) ;
EXPECT_EQ ( 0U , backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ) ;
}
// If a locality with higher priority than the current one becomes ready,
// switch to it.
TEST_P ( FailoverTest , SwitchBackToHigherPriority ) {
CreateAndStartBackends ( 4 ) ;
const size_t kNumRpcs = 100 ;
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) , kDefaultLocalityWeight ,
1 } ,
{ " locality1 " , CreateEndpointsForBackends ( 1 , 2 ) , kDefaultLocalityWeight ,
2 } ,
{ " locality2 " , CreateEndpointsForBackends ( 2 , 3 ) , kDefaultLocalityWeight ,
3 } ,
{ " locality3 " , CreateEndpointsForBackends ( 3 , 4 ) , kDefaultLocalityWeight ,
0 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForBackend ( 3 ) ;
ShutdownBackend ( 3 ) ;
ShutdownBackend ( 0 ) ;
WaitForBackend (
1 , WaitForBackendOptions ( ) . set_reset_counters ( false ) . set_allow_failures (
true ) ) ;
for ( size_t i = 0 ; i < backends_ . size ( ) ; + + i ) {
if ( i = = 1 ) continue ;
EXPECT_EQ ( 0U , backends_ [ i ] - > backend_service ( ) - > request_count ( ) ) ;
}
StartBackend ( 0 ) ;
WaitForBackend ( 0 ) ;
CheckRpcSendOk ( kNumRpcs ) ;
EXPECT_EQ ( kNumRpcs , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
}
// The first update only contains unavailable priorities. The second update
// contains available priorities.
TEST_P ( FailoverTest , UpdateInitialUnavailable ) {
CreateAndStartBackends ( 2 ) ;
EdsResourceArgs args ( {
{ " locality0 " , { MakeNonExistantEndpoint ( ) } , kDefaultLocalityWeight , 0 } ,
{ " locality1 " , { MakeNonExistantEndpoint ( ) } , kDefaultLocalityWeight , 1 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
CheckRpcSendFailure ( ) ;
args = EdsResourceArgs ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) , kDefaultLocalityWeight ,
0 } ,
{ " locality1 " , CreateEndpointsForBackends ( 1 , 2 ) , kDefaultLocalityWeight ,
1 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForBackend ( 0 , WaitForBackendOptions ( ) . set_allow_failures ( true ) ) ;
}
// Tests that after the localities' priorities are updated, we still choose
// the highest READY priority with the updated localities.
TEST_P ( FailoverTest , UpdatePriority ) {
CreateAndStartBackends ( 4 ) ;
const size_t kNumRpcs = 100 ;
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) , kDefaultLocalityWeight ,
1 } ,
{ " locality1 " , CreateEndpointsForBackends ( 1 , 2 ) , kDefaultLocalityWeight ,
2 } ,
{ " locality2 " , CreateEndpointsForBackends ( 2 , 3 ) , kDefaultLocalityWeight ,
3 } ,
{ " locality3 " , CreateEndpointsForBackends ( 3 , 4 ) , kDefaultLocalityWeight ,
0 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForBackend ( 3 , WaitForBackendOptions ( ) . set_reset_counters ( false ) ) ;
EXPECT_EQ ( 0U , backends_ [ 0 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0U , backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 0U , backends_ [ 2 ] - > backend_service ( ) - > request_count ( ) ) ;
args = EdsResourceArgs ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 1 ) , kDefaultLocalityWeight ,
2 } ,
{ " locality1 " , CreateEndpointsForBackends ( 1 , 2 ) , kDefaultLocalityWeight ,
0 } ,
{ " locality2 " , CreateEndpointsForBackends ( 2 , 3 ) , kDefaultLocalityWeight ,
1 } ,
{ " locality3 " , CreateEndpointsForBackends ( 3 , 4 ) , kDefaultLocalityWeight ,
3 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForBackend ( 1 ) ;
CheckRpcSendOk ( kNumRpcs ) ;
EXPECT_EQ ( kNumRpcs , backends_ [ 1 ] - > backend_service ( ) - > request_count ( ) ) ;
}
// Moves all localities in the current priority to a higher priority.
TEST_P ( FailoverTest , MoveAllLocalitiesInCurrentPriorityToHigherPriority ) {
CreateAndStartBackends ( 3 ) ;
auto non_existant_endpoint = MakeNonExistantEndpoint ( ) ;
// First update:
// - Priority 0 is locality 0, containing an unreachable backend.
// - Priority 1 is locality 1, containing backends 0 and 1.
EdsResourceArgs args ( {
{ " locality0 " , { non_existant_endpoint } , kDefaultLocalityWeight , 0 } ,
{ " locality1 " , CreateEndpointsForBackends ( 0 , 2 ) , kDefaultLocalityWeight ,
1 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// When we get the first update, all backends in priority 0 are down,
// so we will create priority 1. Backends 0 and 1 should have traffic,
// but backend 2 should not.
WaitForAllBackends ( 0 , 2 , WaitForBackendOptions ( ) . set_reset_counters ( false ) ) ;
EXPECT_EQ ( 0UL , backends_ [ 2 ] - > backend_service ( ) - > request_count ( ) ) ;
// Second update:
// - Priority 0 contains both localities 0 and 1.
// - Priority 1 is not present.
// - We add backend 2 to locality 1, just so we have a way to know
// when the update has been seen by the client.
args = EdsResourceArgs ( {
{ " locality0 " , { non_existant_endpoint } , kDefaultLocalityWeight , 0 } ,
{ " locality1 " , CreateEndpointsForBackends ( 0 , 3 ) , kDefaultLocalityWeight ,
0 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// When backend 2 gets traffic, we know the second update has been seen.
WaitForBackend ( 2 ) ;
// The xDS server got at least 1 response.
EXPECT_TRUE ( balancer_ - > ads_service ( ) - > eds_response_state ( ) . has_value ( ) ) ;
}
// This tests a bug triggered by the xds_cluster_resolver policy reusing
// a child name for the priority policy when that child name was still
// present but deactivated.
TEST_P ( FailoverTest , PriorityChildNameChurn ) {
CreateAndStartBackends ( 4 ) ;
auto non_existant_endpoint = MakeNonExistantEndpoint ( ) ;
// Initial update:
// - P0:locality0, child number 0 (unreachable)
// - P1:locality1, child number 1
// - P2:locality2, child number 2
EdsResourceArgs args ( {
{ " locality0 " , { non_existant_endpoint } , kDefaultLocalityWeight , 0 } ,
{ " locality1 " , CreateEndpointsForBackends ( 0 , 1 ) , kDefaultLocalityWeight ,
1 } ,
{ " locality2 " , CreateEndpointsForBackends ( 1 , 2 ) , kDefaultLocalityWeight ,
2 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForBackend ( 0 ) ;
// Next update:
// - P0:locality0, child number 0 (still unreachable)
// - P1:locality2, child number 2 (moved from P2 to P1)
// - P2:locality3, child number 3 (new child)
// Child number 1 will be deactivated.
args = EdsResourceArgs ( {
{ " locality0 " , { non_existant_endpoint } , kDefaultLocalityWeight , 0 } ,
{ " locality2 " , CreateEndpointsForBackends ( 1 , 2 ) , kDefaultLocalityWeight ,
1 } ,
{ " locality3 " , CreateEndpointsForBackends ( 2 , 3 ) , kDefaultLocalityWeight ,
2 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForBackend ( 1 ) ;
// Next update:
// - P0:locality0, child number 0 (still unreachable)
// - P1:locality4, child number 4 (new child number -- should not reuse #1)
// - P2:locality3, child number 3
// Child number 1 will be deactivated.
args = EdsResourceArgs ( {
{ " locality0 " , { non_existant_endpoint } , kDefaultLocalityWeight , 0 } ,
{ " locality4 " , CreateEndpointsForBackends ( 3 , 4 ) , kDefaultLocalityWeight ,
1 } ,
{ " locality3 " , CreateEndpointsForBackends ( 2 , 3 ) , kDefaultLocalityWeight ,
2 } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
WaitForBackend ( 3 , WaitForBackendOptions ( ) . set_reset_counters ( false ) ) ;
// P2 should not have gotten any traffic in this change.
EXPECT_EQ ( 0UL , backends_ [ 2 ] - > backend_service ( ) - > request_count ( ) ) ;
}
using DropTest = XdsEnd2endTest ;
// Tests that RPCs are dropped according to the drop config.
TEST_P ( DropTest , Vanilla ) {
CreateAndStartBackends ( 1 ) ;
const uint32_t kDropPerMillionForLb = 100000 ;
const uint32_t kDropPerMillionForThrottle = 200000 ;
const double kDropRateForLb = kDropPerMillionForLb / 1000000.0 ;
const double kDropRateForThrottle = kDropPerMillionForThrottle / 1000000.0 ;
const double kDropRateForLbAndThrottle =
kDropRateForLb + ( 1 - kDropRateForLb ) * kDropRateForThrottle ;
const double kErrorTolerance = 0.05 ;
const size_t kNumRpcs =
ComputeIdealNumRpcs ( kDropRateForLbAndThrottle , kErrorTolerance ) ;
// The ADS response contains two drop categories.
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
args . drop_categories = { { kLbDropType , kDropPerMillionForLb } ,
{ kThrottleDropType , kDropPerMillionForThrottle } } ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Send kNumRpcs RPCs and count the drops.
size_t num_drops =
SendRpcsAndCountFailuresWithMessage ( kNumRpcs , " EDS-configured drop: " ) ;
// The drop rate should be roughly equal to the expectation.
const double seen_drop_rate = static_cast < double > ( num_drops ) / kNumRpcs ;
EXPECT_THAT ( seen_drop_rate , : : testing : : DoubleNear ( kDropRateForLbAndThrottle ,
kErrorTolerance ) ) ;
}
// Tests that drop config is converted correctly from per hundred.
TEST_P ( DropTest , DropPerHundred ) {
CreateAndStartBackends ( 1 ) ;
const uint32_t kDropPerHundredForLb = 10 ;
const double kDropRateForLb = kDropPerHundredForLb / 100.0 ;
const double kErrorTolerance = 0.05 ;
const size_t kNumRpcs = ComputeIdealNumRpcs ( kDropRateForLb , kErrorTolerance ) ;
// The ADS response contains one drop category.
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
args . drop_categories = { { kLbDropType , kDropPerHundredForLb } } ;
args . drop_denominator = FractionalPercent : : HUNDRED ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Send kNumRpcs RPCs and count the drops.
size_t num_drops =
SendRpcsAndCountFailuresWithMessage ( kNumRpcs , " EDS-configured drop: " ) ;
// The drop rate should be roughly equal to the expectation.
const double seen_drop_rate = static_cast < double > ( num_drops ) / kNumRpcs ;
EXPECT_THAT ( seen_drop_rate ,
: : testing : : DoubleNear ( kDropRateForLb , kErrorTolerance ) ) ;
}
// Tests that drop config is converted correctly from per ten thousand.
TEST_P ( DropTest , DropPerTenThousand ) {
CreateAndStartBackends ( 1 ) ;
const uint32_t kDropPerTenThousandForLb = 1000 ;
const double kDropRateForLb = kDropPerTenThousandForLb / 10000.0 ;
const double kErrorTolerance = 0.05 ;
const size_t kNumRpcs = ComputeIdealNumRpcs ( kDropRateForLb , kErrorTolerance ) ;
// The ADS response contains one drop category.
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
args . drop_categories = { { kLbDropType , kDropPerTenThousandForLb } } ;
args . drop_denominator = FractionalPercent : : TEN_THOUSAND ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Send kNumRpcs RPCs and count the drops.
size_t num_drops =
SendRpcsAndCountFailuresWithMessage ( kNumRpcs , " EDS-configured drop: " ) ;
// The drop rate should be roughly equal to the expectation.
const double seen_drop_rate = static_cast < double > ( num_drops ) / kNumRpcs ;
EXPECT_THAT ( seen_drop_rate ,
: : testing : : DoubleNear ( kDropRateForLb , kErrorTolerance ) ) ;
}
// Tests that drop is working correctly after update.
TEST_P ( DropTest , Update ) {
CreateAndStartBackends ( 1 ) ;
const uint32_t kDropPerMillionForLb = 100000 ;
const uint32_t kDropPerMillionForThrottle = 200000 ;
const double kErrorTolerance = 0.05 ;
const double kDropRateForLb = kDropPerMillionForLb / 1000000.0 ;
const double kDropRateForThrottle = kDropPerMillionForThrottle / 1000000.0 ;
const double kDropRateForLbAndThrottle =
kDropRateForLb + ( 1 - kDropRateForLb ) * kDropRateForThrottle ;
const size_t kNumRpcsLbOnly =
ComputeIdealNumRpcs ( kDropRateForLb , kErrorTolerance ) ;
const size_t kNumRpcsBoth =
ComputeIdealNumRpcs ( kDropRateForLbAndThrottle , kErrorTolerance ) ;
// The first ADS response contains one drop category.
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
args . drop_categories = { { kLbDropType , kDropPerMillionForLb } } ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Send kNumRpcsLbOnly RPCs and count the drops.
gpr_log ( GPR_INFO , " ========= BEFORE FIRST BATCH ========== " ) ;
size_t num_drops = SendRpcsAndCountFailuresWithMessage (
kNumRpcsLbOnly , " EDS-configured drop: " ) ;
gpr_log ( GPR_INFO , " ========= DONE WITH FIRST BATCH ========== " ) ;
// The drop rate should be roughly equal to the expectation.
double seen_drop_rate = static_cast < double > ( num_drops ) / kNumRpcsLbOnly ;
gpr_log ( GPR_INFO , " First batch drop rate %f " , seen_drop_rate ) ;
EXPECT_THAT ( seen_drop_rate ,
: : testing : : DoubleNear ( kDropRateForLb , kErrorTolerance ) ) ;
// The second ADS response contains two drop categories, send an update EDS
// response.
args . drop_categories = { { kLbDropType , kDropPerMillionForLb } ,
{ kThrottleDropType , kDropPerMillionForThrottle } } ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Wait until the drop rate increases to the middle of the two configs,
// which implies that the update has been in effect.
const double kDropRateThreshold =
( kDropRateForLb + kDropRateForLbAndThrottle ) / 2 ;
size_t num_rpcs = kNumRpcsBoth ;
while ( seen_drop_rate < kDropRateThreshold ) {
EchoResponse response ;
const Status status = SendRpc ( RpcOptions ( ) , & response ) ;
+ + num_rpcs ;
if ( ! status . ok ( ) & &
absl : : StartsWith ( status . error_message ( ) , " EDS-configured drop: " ) ) {
+ + num_drops ;
} else {
EXPECT_TRUE ( status . ok ( ) ) < < " code= " < < status . error_code ( )
< < " message= " < < status . error_message ( ) ;
EXPECT_EQ ( response . message ( ) , kRequestMessage ) ;
}
seen_drop_rate = static_cast < double > ( num_drops ) / num_rpcs ;
}
// Send kNumRpcsBoth RPCs and count the drops.
gpr_log ( GPR_INFO , " ========= BEFORE SECOND BATCH ========== " ) ;
num_drops = SendRpcsAndCountFailuresWithMessage ( kNumRpcsBoth ,
" EDS-configured drop: " ) ;
gpr_log ( GPR_INFO , " ========= DONE WITH SECOND BATCH ========== " ) ;
// The new drop rate should be roughly equal to the expectation.
seen_drop_rate = static_cast < double > ( num_drops ) / kNumRpcsBoth ;
gpr_log ( GPR_INFO , " Second batch drop rate %f " , seen_drop_rate ) ;
EXPECT_THAT ( seen_drop_rate , : : testing : : DoubleNear ( kDropRateForLbAndThrottle ,
kErrorTolerance ) ) ;
}
// Tests that all the RPCs are dropped if any drop category drops 100%.
TEST_P ( DropTest , DropAll ) {
const size_t kNumRpcs = 1000 ;
const uint32_t kDropPerMillionForLb = 100000 ;
const uint32_t kDropPerMillionForThrottle = 1000000 ;
// The ADS response contains two drop categories.
EdsResourceArgs args ;
args . drop_categories = { { kLbDropType , kDropPerMillionForLb } ,
{ kThrottleDropType , kDropPerMillionForThrottle } } ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Send kNumRpcs RPCs and all of them are dropped.
size_t num_drops =
SendRpcsAndCountFailuresWithMessage ( kNumRpcs , " EDS-configured drop: " ) ;
EXPECT_EQ ( num_drops , kNumRpcs ) ;
}
using ClientLoadReportingTest = XdsEnd2endTest ;
// Tests that the load report received at the balancer is correct.
TEST_P ( ClientLoadReportingTest , Vanilla ) {
CreateAndStartBackends ( 4 ) ;
const size_t kNumRpcsPerAddress = 10 ;
const size_t kNumFailuresPerAddress = 3 ;
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 2 ) } ,
{ " locality1 " , CreateEndpointsForBackends ( 2 , 4 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Wait until all backends are ready.
size_t num_warmup_rpcs = WaitForAllBackends (
0 , 4 , WaitForBackendOptions ( ) . set_reset_counters ( false ) ) ;
// Send kNumRpcsPerAddress RPCs per server.
CheckRpcSendOk ( kNumRpcsPerAddress * backends_ . size ( ) ) ;
CheckRpcSendFailure ( CheckRpcSendFailureOptions ( )
. set_times ( kNumFailuresPerAddress * backends_ . size ( ) )
. set_rpc_options ( RpcOptions ( ) . set_server_fail ( true ) ) ) ;
const size_t total_successful_rpcs_sent =
( kNumRpcsPerAddress * backends_ . size ( ) ) + num_warmup_rpcs ;
const size_t total_failed_rpcs_sent =
kNumFailuresPerAddress * backends_ . size ( ) ;
// Check that the backends got the right number of requests.
size_t total_rpcs_sent = 0 ;
for ( const auto & backend : backends_ ) {
total_rpcs_sent + = backend - > backend_service ( ) - > request_count ( ) ;
}
EXPECT_EQ ( total_rpcs_sent ,
total_successful_rpcs_sent + total_failed_rpcs_sent ) ;
// The load report received at the balancer should be correct.
std : : vector < ClientStats > load_report =
balancer_ - > lrs_service ( ) - > WaitForLoadReport ( ) ;
ASSERT_EQ ( load_report . size ( ) , 1UL ) ;
ClientStats & client_stats = load_report . front ( ) ;
EXPECT_EQ ( client_stats . cluster_name ( ) , kDefaultClusterName ) ;
EXPECT_EQ ( client_stats . eds_service_name ( ) , kDefaultEdsServiceName ) ;
EXPECT_EQ ( total_successful_rpcs_sent ,
client_stats . total_successful_requests ( ) ) ;
EXPECT_EQ ( 0U , client_stats . total_requests_in_progress ( ) ) ;
EXPECT_EQ ( total_rpcs_sent , client_stats . total_issued_requests ( ) ) ;
EXPECT_EQ ( total_failed_rpcs_sent , client_stats . total_error_requests ( ) ) ;
EXPECT_EQ ( 0U , client_stats . total_dropped_requests ( ) ) ;
ASSERT_THAT (
client_stats . locality_stats ( ) ,
: : testing : : ElementsAre ( : : testing : : Pair ( " locality0 " , : : testing : : _ ) ,
: : testing : : Pair ( " locality1 " , : : testing : : _ ) ) ) ;
size_t num_successful_rpcs = 0 ;
size_t num_failed_rpcs = 0 ;
for ( const auto & p : client_stats . locality_stats ( ) ) {
EXPECT_EQ ( p . second . total_requests_in_progress , 0U ) ;
EXPECT_EQ (
p . second . total_issued_requests ,
p . second . total_successful_requests + p . second . total_error_requests ) ;
num_successful_rpcs + = p . second . total_successful_requests ;
num_failed_rpcs + = p . second . total_error_requests ;
}
EXPECT_EQ ( num_successful_rpcs , total_successful_rpcs_sent ) ;
EXPECT_EQ ( num_failed_rpcs , total_failed_rpcs_sent ) ;
EXPECT_EQ ( num_successful_rpcs + num_failed_rpcs , total_rpcs_sent ) ;
// The LRS service got a single request, and sent a single response.
EXPECT_EQ ( 1U , balancer_ - > lrs_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 1U , balancer_ - > lrs_service ( ) - > response_count ( ) ) ;
}
// Tests send_all_clusters.
TEST_P ( ClientLoadReportingTest , SendAllClusters ) {
CreateAndStartBackends ( 2 ) ;
balancer_ - > lrs_service ( ) - > set_send_all_clusters ( true ) ;
const size_t kNumRpcsPerAddress = 10 ;
const size_t kNumFailuresPerAddress = 3 ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Wait until all backends are ready.
size_t num_warmup_rpcs = WaitForAllBackends ( ) ;
// Send kNumRpcsPerAddress RPCs per server.
CheckRpcSendOk ( kNumRpcsPerAddress * backends_ . size ( ) ) ;
CheckRpcSendFailure ( CheckRpcSendFailureOptions ( )
. set_times ( kNumFailuresPerAddress * backends_ . size ( ) )
. set_rpc_options ( RpcOptions ( ) . set_server_fail ( true ) ) ) ;
// Check that each backend got the right number of requests.
for ( size_t i = 0 ; i < backends_ . size ( ) ; + + i ) {
EXPECT_EQ ( kNumRpcsPerAddress + kNumFailuresPerAddress ,
backends_ [ i ] - > backend_service ( ) - > request_count ( ) ) ;
}
// The load report received at the balancer should be correct.
std : : vector < ClientStats > load_report =
balancer_ - > lrs_service ( ) - > WaitForLoadReport ( ) ;
ASSERT_EQ ( load_report . size ( ) , 1UL ) ;
ClientStats & client_stats = load_report . front ( ) ;
EXPECT_EQ ( kNumRpcsPerAddress * backends_ . size ( ) + num_warmup_rpcs ,
client_stats . total_successful_requests ( ) ) ;
EXPECT_EQ ( 0U , client_stats . total_requests_in_progress ( ) ) ;
EXPECT_EQ ( ( kNumRpcsPerAddress + kNumFailuresPerAddress ) * backends_ . size ( ) +
num_warmup_rpcs ,
client_stats . total_issued_requests ( ) ) ;
EXPECT_EQ ( kNumFailuresPerAddress * backends_ . size ( ) ,
client_stats . total_error_requests ( ) ) ;
EXPECT_EQ ( 0U , client_stats . total_dropped_requests ( ) ) ;
// The LRS service got a single request, and sent a single response.
EXPECT_EQ ( 1U , balancer_ - > lrs_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 1U , balancer_ - > lrs_service ( ) - > response_count ( ) ) ;
}
// Tests that we don't include stats for clusters that are not requested
// by the LRS server.
TEST_P ( ClientLoadReportingTest , HonorsClustersRequestedByLrsServer ) {
CreateAndStartBackends ( 1 ) ;
balancer_ - > lrs_service ( ) - > set_cluster_names ( { " bogus " } ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Wait until all backends are ready.
WaitForAllBackends ( ) ;
// The load report received at the balancer should be correct.
std : : vector < ClientStats > load_report =
balancer_ - > lrs_service ( ) - > WaitForLoadReport ( ) ;
ASSERT_EQ ( load_report . size ( ) , 0UL ) ;
// The LRS service got a single request, and sent a single response.
EXPECT_EQ ( 1U , balancer_ - > lrs_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 1U , balancer_ - > lrs_service ( ) - > response_count ( ) ) ;
}
// Tests that if the balancer restarts, the client load report contains the
// stats before and after the restart correctly.
TEST_P ( ClientLoadReportingTest , BalancerRestart ) {
CreateAndStartBackends ( 4 ) ;
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( 0 , 2 ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Wait until all backends returned by the balancer are ready.
size_t num_rpcs = WaitForAllBackends ( 0 , 2 ) ;
std : : vector < ClientStats > load_report =
balancer_ - > lrs_service ( ) - > WaitForLoadReport ( ) ;
ASSERT_EQ ( load_report . size ( ) , 1UL ) ;
ClientStats client_stats = std : : move ( load_report . front ( ) ) ;
EXPECT_EQ ( num_rpcs , client_stats . total_successful_requests ( ) ) ;
EXPECT_EQ ( 0U , client_stats . total_requests_in_progress ( ) ) ;
EXPECT_EQ ( 0U , client_stats . total_error_requests ( ) ) ;
EXPECT_EQ ( 0U , client_stats . total_dropped_requests ( ) ) ;
// Shut down the balancer.
balancer_ - > Shutdown ( ) ;
// We should continue using the last EDS response we received from the
// balancer before it was shut down.
// Note: We need to use WaitForAllBackends() here instead of just
// CheckRpcSendOk(kNumBackendsFirstPass), because when the balancer
// shuts down, the XdsClient will generate an error to the
// ListenerWatcher, which will cause the xds resolver to send a
// no-op update to the LB policy. When this update gets down to the
// round_robin child policy for the locality, it will generate a new
// subchannel list, which resets the start index randomly. So we need
// to be a little more permissive here to avoid spurious failures.
ResetBackendCounters ( ) ;
num_rpcs = WaitForAllBackends ( 0 , 2 ) ;
// Now restart the balancer, this time pointing to the new backends.
balancer_ - > Start ( ) ;
args = EdsResourceArgs ( { { " locality0 " , CreateEndpointsForBackends ( 2 , 4 ) } } ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Wait for queries to start going to one of the new backends.
// This tells us that we're now using the new serverlist.
num_rpcs + = WaitForAllBackends ( 2 , 4 ) ;
// Send one RPC per backend.
CheckRpcSendOk ( 2 ) ;
num_rpcs + = 2 ;
// Check client stats.
load_report = balancer_ - > lrs_service ( ) - > WaitForLoadReport ( ) ;
ASSERT_EQ ( load_report . size ( ) , 1UL ) ;
client_stats = std : : move ( load_report . front ( ) ) ;
EXPECT_EQ ( num_rpcs , client_stats . total_successful_requests ( ) ) ;
EXPECT_EQ ( 0U , client_stats . total_requests_in_progress ( ) ) ;
EXPECT_EQ ( 0U , client_stats . total_error_requests ( ) ) ;
EXPECT_EQ ( 0U , client_stats . total_dropped_requests ( ) ) ;
}
// Tests load reporting when switching over from one cluster to another.
TEST_P ( ClientLoadReportingTest , ChangeClusters ) {
CreateAndStartBackends ( 4 ) ;
const char * kNewClusterName = " new_cluster_name " ;
const char * kNewEdsServiceName = " new_eds_service_name " ;
balancer_ - > lrs_service ( ) - > set_cluster_names (
{ kDefaultClusterName , kNewClusterName } ) ;
// cluster kDefaultClusterName -> locality0 -> backends 0 and 1
EdsResourceArgs args ( {
{ " locality0 " , CreateEndpointsForBackends ( 0 , 2 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// cluster kNewClusterName -> locality1 -> backends 2 and 3
EdsResourceArgs args2 ( {
{ " locality1 " , CreateEndpointsForBackends ( 2 , 4 ) } ,
} ) ;
balancer_ - > ads_service ( ) - > SetEdsResource (
BuildEdsResource ( args2 , kNewEdsServiceName ) ) ;
// CDS resource for kNewClusterName.
Cluster new_cluster = default_cluster_ ;
new_cluster . set_name ( kNewClusterName ) ;
new_cluster . mutable_eds_cluster_config ( ) - > set_service_name (
kNewEdsServiceName ) ;
balancer_ - > ads_service ( ) - > SetCdsResource ( new_cluster ) ;
// Wait for all backends to come online.
size_t num_rpcs = WaitForAllBackends ( 0 , 2 ) ;
// The load report received at the balancer should be correct.
std : : vector < ClientStats > load_report =
balancer_ - > lrs_service ( ) - > WaitForLoadReport ( ) ;
EXPECT_THAT (
load_report ,
: : testing : : ElementsAre ( : : testing : : AllOf (
: : testing : : Property ( & ClientStats : : cluster_name , kDefaultClusterName ) ,
: : testing : : Property ( & ClientStats : : eds_service_name ,
kDefaultEdsServiceName ) ,
: : testing : : Property (
& ClientStats : : locality_stats ,
: : testing : : ElementsAre ( : : testing : : Pair (
" locality0 " ,
: : testing : : AllOf (
: : testing : : Field ( & ClientStats : : LocalityStats : :
total_successful_requests ,
num_rpcs ) ,
: : testing : : Field ( & ClientStats : : LocalityStats : :
total_requests_in_progress ,
0UL ) ,
: : testing : : Field (
& ClientStats : : LocalityStats : : total_error_requests ,
0UL ) ,
: : testing : : Field (
& ClientStats : : LocalityStats : : total_issued_requests ,
num_rpcs ) ) ) ) ) ,
: : testing : : Property ( & ClientStats : : total_dropped_requests , 0UL ) ) ) ) ;
// Change RDS resource to point to new cluster.
RouteConfiguration new_route_config = default_route_config_ ;
new_route_config . mutable_virtual_hosts ( 0 )
- > mutable_routes ( 0 )
- > mutable_route ( )
- > set_cluster ( kNewClusterName ) ;
SetListenerAndRouteConfiguration ( balancer_ . get ( ) , default_listener_ ,
new_route_config ) ;
// Wait for all new backends to be used.
num_rpcs = WaitForAllBackends ( 2 , 4 ) ;
// The load report received at the balancer should be correct.
load_report = balancer_ - > lrs_service ( ) - > WaitForLoadReport ( ) ;
EXPECT_THAT (
load_report ,
: : testing : : ElementsAre (
: : testing : : AllOf (
: : testing : : Property ( & ClientStats : : cluster_name ,
kDefaultClusterName ) ,
: : testing : : Property ( & ClientStats : : eds_service_name ,
kDefaultEdsServiceName ) ,
: : testing : : Property (
& ClientStats : : locality_stats ,
: : testing : : ElementsAre ( : : testing : : Pair (
" locality0 " ,
: : testing : : AllOf (
: : testing : : Field ( & ClientStats : : LocalityStats : :
total_successful_requests ,
: : testing : : Lt ( num_rpcs ) ) ,
: : testing : : Field ( & ClientStats : : LocalityStats : :
total_requests_in_progress ,
0UL ) ,
: : testing : : Field (
& ClientStats : : LocalityStats : : total_error_requests ,
0UL ) ,
: : testing : : Field ( & ClientStats : : LocalityStats : :
total_issued_requests ,
: : testing : : Le ( num_rpcs ) ) ) ) ) ) ,
: : testing : : Property ( & ClientStats : : total_dropped_requests , 0UL ) ) ,
: : testing : : AllOf (
: : testing : : Property ( & ClientStats : : cluster_name , kNewClusterName ) ,
: : testing : : Property ( & ClientStats : : eds_service_name ,
kNewEdsServiceName ) ,
: : testing : : Property (
& ClientStats : : locality_stats ,
: : testing : : ElementsAre ( : : testing : : Pair (
" locality1 " ,
: : testing : : AllOf (
: : testing : : Field ( & ClientStats : : LocalityStats : :
total_successful_requests ,
: : testing : : Le ( num_rpcs ) ) ,
: : testing : : Field ( & ClientStats : : LocalityStats : :
total_requests_in_progress ,
0UL ) ,
: : testing : : Field (
& ClientStats : : LocalityStats : : total_error_requests ,
0UL ) ,
: : testing : : Field ( & ClientStats : : LocalityStats : :
total_issued_requests ,
: : testing : : Le ( num_rpcs ) ) ) ) ) ) ,
: : testing : : Property ( & ClientStats : : total_dropped_requests , 0UL ) ) ) ) ;
size_t total_ok = 0 ;
for ( const ClientStats & client_stats : load_report ) {
total_ok + = client_stats . total_successful_requests ( ) ;
}
EXPECT_EQ ( total_ok , num_rpcs ) ;
// The LRS service got a single request, and sent a single response.
EXPECT_EQ ( 1U , balancer_ - > lrs_service ( ) - > request_count ( ) ) ;
EXPECT_EQ ( 1U , balancer_ - > lrs_service ( ) - > response_count ( ) ) ;
}
// Tests that the drop stats are correctly reported by client load reporting.
TEST_P ( ClientLoadReportingTest , DropStats ) {
CreateAndStartBackends ( 1 ) ;
const uint32_t kDropPerMillionForLb = 100000 ;
const uint32_t kDropPerMillionForThrottle = 200000 ;
const double kErrorTolerance = 0.05 ;
const double kDropRateForLb = kDropPerMillionForLb / 1000000.0 ;
const double kDropRateForThrottle = kDropPerMillionForThrottle / 1000000.0 ;
const double kDropRateForLbAndThrottle =
kDropRateForLb + ( 1 - kDropRateForLb ) * kDropRateForThrottle ;
const size_t kNumRpcs =
ComputeIdealNumRpcs ( kDropRateForLbAndThrottle , kErrorTolerance ) ;
const char kStatusMessageDropPrefix [ ] = " EDS-configured drop: " ;
// The ADS response contains two drop categories.
EdsResourceArgs args ( { { " locality0 " , CreateEndpointsForBackends ( ) } } ) ;
args . drop_categories = { { kLbDropType , kDropPerMillionForLb } ,
{ kThrottleDropType , kDropPerMillionForThrottle } } ;
balancer_ - > ads_service ( ) - > SetEdsResource ( BuildEdsResource ( args ) ) ;
// Send kNumRpcs RPCs and count the drops.
size_t num_drops =
SendRpcsAndCountFailuresWithMessage ( kNumRpcs , kStatusMessageDropPrefix ) ;
// The drop rate should be roughly equal to the expectation.
const double seen_drop_rate = static_cast < double > ( num_drops ) / kNumRpcs ;
EXPECT_THAT ( seen_drop_rate , : : testing : : DoubleNear ( kDropRateForLbAndThrottle ,
kErrorTolerance ) ) ;
// Check client stats.
ClientStats client_stats ;
do {
std : : vector < ClientStats > load_reports =
balancer_ - > lrs_service ( ) - > WaitForLoadReport ( ) ;
for ( const auto & load_report : load_reports ) {
client_stats + = load_report ;
}
} while ( client_stats . total_issued_requests ( ) +
client_stats . total_dropped_requests ( ) <
kNumRpcs ) ;
EXPECT_EQ ( num_drops , client_stats . total_dropped_requests ( ) ) ;
EXPECT_THAT ( static_cast < double > ( client_stats . dropped_requests ( kLbDropType ) ) /
kNumRpcs ,
: : testing : : DoubleNear ( kDropRateForLb , kErrorTolerance ) ) ;
EXPECT_THAT (
static_cast < double > ( client_stats . dropped_requests ( kThrottleDropType ) ) /
( kNumRpcs * ( 1 - kDropRateForLb ) ) ,
: : testing : : DoubleNear ( kDropRateForThrottle , kErrorTolerance ) ) ;
}
// Run both with and without load reporting.
INSTANTIATE_TEST_SUITE_P (
XdsTest , BasicTest ,
: : testing : : Values ( XdsTestType ( ) , XdsTestType ( ) . set_enable_load_reporting ( ) ) ,
& XdsTestType : : Name ) ;
// LDS depends on XdsResolver.
INSTANTIATE_TEST_SUITE_P ( XdsTest , LdsTest , : : testing : : Values ( XdsTestType ( ) ) ,
& XdsTestType : : Name ) ;
INSTANTIATE_TEST_SUITE_P ( XdsTest , LdsV2Test ,
: : testing : : Values ( XdsTestType ( ) . set_use_v2 ( ) ) ,
& XdsTestType : : Name ) ;
// LDS/RDS commmon tests depend on XdsResolver.
INSTANTIATE_TEST_SUITE_P (
XdsTest , LdsRdsTest ,
: : testing : : Values ( XdsTestType ( ) , XdsTestType ( ) . set_enable_rds_testing ( ) ,
// Also test with xDS v2.
XdsTestType ( ) . set_enable_rds_testing ( ) . set_use_v2 ( ) ) ,
& XdsTestType : : Name ) ;
// CDS depends on XdsResolver.
INSTANTIATE_TEST_SUITE_P (
XdsTest , CdsTest ,
: : testing : : Values ( XdsTestType ( ) , XdsTestType ( ) . set_enable_load_reporting ( ) ) ,
& XdsTestType : : Name ) ;
// CDS depends on XdsResolver.
// Security depends on v3.
// Not enabling load reporting or RDS, since those are irrelevant to these
// tests.
INSTANTIATE_TEST_SUITE_P (
XdsTest , XdsSecurityTest ,
: : testing : : Values ( XdsTestType ( ) . set_use_xds_credentials ( ) ) ,
& XdsTestType : : Name ) ;
// We are only testing the server here.
// Run with bootstrap from env var, so that we use a global XdsClient
// instance. Otherwise, we would need to use a separate fake resolver
// result generator on the client and server sides.
INSTANTIATE_TEST_SUITE_P ( XdsTest , XdsEnabledServerTest ,
: : testing : : Values ( XdsTestType ( ) . set_bootstrap_source (
XdsTestType : : kBootstrapFromEnvVar ) ) ,
& XdsTestType : : Name ) ;
// We are only testing the server here.
// Run with bootstrap from env var so that we use one XdsClient.
INSTANTIATE_TEST_SUITE_P (
XdsTest , XdsServerSecurityTest ,
: : testing : : Values (
XdsTestType ( )
. set_bootstrap_source ( XdsTestType : : kBootstrapFromEnvVar )
. set_use_xds_credentials ( ) ) ,
& XdsTestType : : Name ) ;
INSTANTIATE_TEST_SUITE_P (
XdsTest , XdsEnabledServerStatusNotificationTest ,
: : testing : : Values ( XdsTestType ( ) . set_use_xds_credentials ( ) ) ,
& XdsTestType : : Name ) ;
// Run with bootstrap from env var so that we use one XdsClient.
INSTANTIATE_TEST_SUITE_P (
XdsTest , XdsServerFilterChainMatchTest ,
: : testing : : Values (
XdsTestType ( )
. set_bootstrap_source ( XdsTestType : : kBootstrapFromEnvVar )
. set_use_xds_credentials ( ) ) ,
& XdsTestType : : Name ) ;
// Test xDS-enabled server with and without RDS.
// Run with bootstrap from env var so that we use one XdsClient.
INSTANTIATE_TEST_SUITE_P (
XdsTest , XdsServerRdsTest ,
: : testing : : Values (
XdsTestType ( )
. set_bootstrap_source ( XdsTestType : : kBootstrapFromEnvVar )
. set_use_xds_credentials ( ) ,
XdsTestType ( )
. set_bootstrap_source ( XdsTestType : : kBootstrapFromEnvVar )
. set_use_xds_credentials ( )
. set_enable_rds_testing ( ) ) ,
& XdsTestType : : Name ) ;
// We are only testing the server here.
// Run with bootstrap from env var, so that we use a global XdsClient
// instance. Otherwise, we would need to use a separate fake resolver
// result generator on the client and server sides.
INSTANTIATE_TEST_SUITE_P (
XdsTest , XdsRbacTest ,
: : testing : : Values (
XdsTestType ( ) . set_use_xds_credentials ( ) . set_bootstrap_source (
XdsTestType : : kBootstrapFromEnvVar ) ,
XdsTestType ( )
. set_use_xds_credentials ( )
. set_enable_rds_testing ( )
. set_bootstrap_source ( XdsTestType : : kBootstrapFromEnvVar ) ,
XdsTestType ( )
. set_use_xds_credentials ( )
. set_filter_config_setup (
XdsTestType : : HttpFilterConfigLocation : : kHttpFilterConfigInRoute )
. set_bootstrap_source ( XdsTestType : : kBootstrapFromEnvVar ) ,
XdsTestType ( )
. set_use_xds_credentials ( )
. set_enable_rds_testing ( )
. set_filter_config_setup (
XdsTestType : : HttpFilterConfigLocation : : kHttpFilterConfigInRoute )
. set_bootstrap_source ( XdsTestType : : kBootstrapFromEnvVar ) ) ,
& XdsTestType : : Name ) ;
// We are only testing the server here.
// Run with bootstrap from env var, so that we use a global XdsClient
// instance. Otherwise, we would need to use a separate fake resolver
// result generator on the client and server sides.
// Note that we are simply using the default fake credentials instead of xds
// credentials for NACK tests to avoid a mismatch between the client and the
// server's security settings when using the WaitForNack() infrastructure.
INSTANTIATE_TEST_SUITE_P (
XdsTest , XdsRbacNackTest ,
: : testing : : Values (
XdsTestType ( ) . set_bootstrap_source ( XdsTestType : : kBootstrapFromEnvVar ) ,
XdsTestType ( ) . set_enable_rds_testing ( ) . set_bootstrap_source (
XdsTestType : : kBootstrapFromEnvVar ) ,
XdsTestType ( )
. set_filter_config_setup (
XdsTestType : : HttpFilterConfigLocation : : kHttpFilterConfigInRoute )
. set_bootstrap_source ( XdsTestType : : kBootstrapFromEnvVar ) ,
XdsTestType ( )
. set_enable_rds_testing ( )
. set_filter_config_setup (
XdsTestType : : HttpFilterConfigLocation : : kHttpFilterConfigInRoute )
. set_bootstrap_source ( XdsTestType : : kBootstrapFromEnvVar ) ) ,
& XdsTestType : : Name ) ;
// We are only testing the server here.
// Run with bootstrap from env var, so that we use a global XdsClient
// instance. Otherwise, we would need to use a separate fake resolver
// result generator on the client and server sides.
INSTANTIATE_TEST_SUITE_P (
XdsTest , XdsRbacTestWithRouteOverrideAlwaysPresent ,
: : testing : : Values (
XdsTestType ( )
. set_use_xds_credentials ( )
. set_filter_config_setup (
XdsTestType : : HttpFilterConfigLocation : : kHttpFilterConfigInRoute )
. set_bootstrap_source ( XdsTestType : : kBootstrapFromEnvVar ) ,
XdsTestType ( )
. set_use_xds_credentials ( )
. set_enable_rds_testing ( )
. set_filter_config_setup (
XdsTestType : : HttpFilterConfigLocation : : kHttpFilterConfigInRoute )
. set_bootstrap_source ( XdsTestType : : kBootstrapFromEnvVar ) ) ,
& XdsTestType : : Name ) ;
// We are only testing the server here.
// Run with bootstrap from env var, so that we use a global XdsClient
// instance. Otherwise, we would need to use a separate fake resolver
// result generator on the client and server sides.
INSTANTIATE_TEST_SUITE_P (
XdsTest , XdsRbacTestWithActionPermutations ,
: : testing : : Values (
XdsTestType ( )
. set_use_xds_credentials ( )
. set_rbac_action ( RBAC_Action_ALLOW )
. set_bootstrap_source ( XdsTestType : : kBootstrapFromEnvVar ) ,
XdsTestType ( )
. set_use_xds_credentials ( )
. set_rbac_action ( RBAC_Action_DENY )
. set_bootstrap_source ( XdsTestType : : kBootstrapFromEnvVar ) ,
XdsTestType ( )
. set_use_xds_credentials ( )
. set_enable_rds_testing ( )
. set_rbac_action ( RBAC_Action_ALLOW )
. set_bootstrap_source ( XdsTestType : : kBootstrapFromEnvVar ) ,
XdsTestType ( )
. set_use_xds_credentials ( )
. set_enable_rds_testing ( )
. set_rbac_action ( RBAC_Action_DENY )
. set_bootstrap_source ( XdsTestType : : kBootstrapFromEnvVar ) ,
XdsTestType ( )
. set_use_xds_credentials ( )
. set_filter_config_setup (
XdsTestType : : HttpFilterConfigLocation : : kHttpFilterConfigInRoute )
. set_rbac_action ( RBAC_Action_ALLOW )
. set_bootstrap_source ( XdsTestType : : kBootstrapFromEnvVar ) ,
XdsTestType ( )
. set_use_xds_credentials ( )
. set_filter_config_setup (
XdsTestType : : HttpFilterConfigLocation : : kHttpFilterConfigInRoute )
. set_rbac_action ( RBAC_Action_DENY )
. set_bootstrap_source ( XdsTestType : : kBootstrapFromEnvVar ) ,
XdsTestType ( )
. set_use_xds_credentials ( )
. set_enable_rds_testing ( )
. set_filter_config_setup (
XdsTestType : : HttpFilterConfigLocation : : kHttpFilterConfigInRoute )
. set_rbac_action ( RBAC_Action_ALLOW )
. set_bootstrap_source ( XdsTestType : : kBootstrapFromEnvVar ) ,
XdsTestType ( )
. set_use_xds_credentials ( )
. set_enable_rds_testing ( )
. set_filter_config_setup (
XdsTestType : : HttpFilterConfigLocation : : kHttpFilterConfigInRoute )
. set_rbac_action ( RBAC_Action_DENY )
. set_bootstrap_source ( XdsTestType : : kBootstrapFromEnvVar ) ) ,
& XdsTestType : : Name ) ;
// EDS could be tested with or without XdsResolver, but the tests would
// be the same either way, so we test it only with XdsResolver.
INSTANTIATE_TEST_SUITE_P (
XdsTest , EdsTest ,
: : testing : : Values ( XdsTestType ( ) , XdsTestType ( ) . set_enable_load_reporting ( ) ) ,
& XdsTestType : : Name ) ;
// XdsResolverOnlyTest depends on XdsResolver.
INSTANTIATE_TEST_SUITE_P (
XdsTest , XdsResolverOnlyTest ,
: : testing : : Values ( XdsTestType ( ) , XdsTestType ( ) . set_enable_load_reporting ( ) ) ,
& XdsTestType : : Name ) ;
INSTANTIATE_TEST_SUITE_P (
XdsTest , LocalityMapTest ,
: : testing : : Values ( XdsTestType ( ) , XdsTestType ( ) . set_enable_load_reporting ( ) ) ,
& XdsTestType : : Name ) ;
INSTANTIATE_TEST_SUITE_P (
XdsTest , FailoverTest ,
: : testing : : Values ( XdsTestType ( ) , XdsTestType ( ) . set_enable_load_reporting ( ) ) ,
& XdsTestType : : Name ) ;
INSTANTIATE_TEST_SUITE_P (
XdsTest , DropTest ,
: : testing : : Values ( XdsTestType ( ) , XdsTestType ( ) . set_enable_load_reporting ( ) ) ,
& XdsTestType : : Name ) ;
// Load reporting tests are not run with load reporting disabled.
INSTANTIATE_TEST_SUITE_P (
XdsTest , ClientLoadReportingTest ,
: : testing : : Values ( XdsTestType ( ) . set_enable_load_reporting ( ) ) ,
& XdsTestType : : Name ) ;
} // namespace
} // namespace testing
} // namespace grpc
int main ( int argc , char * * argv ) {
grpc : : testing : : TestEnvironment env ( & argc , argv ) ;
: : testing : : InitGoogleTest ( & argc , argv ) ;
// Make the backup poller poll very frequently in order to pick up
// updates from all the subchannels's FDs.
GPR_GLOBAL_CONFIG_SET ( grpc_client_channel_backup_poll_interval_ms , 1 ) ;
// Allow testing PUT requests.
grpc_core : :
InternalOnlyDoNotUseUnlessYouHavePermissionFromGrpcTeamAllowBrokenPutRequests ( ) ;
# if TARGET_OS_IPHONE
// Workaround Apple CFStream bug
gpr_setenv ( " grpc_cfstream " , " 0 " ) ;
# endif
grpc_core : : CertificateProviderRegistry : : RegisterCertificateProviderFactory (
absl : : make_unique < grpc : : testing : : FakeCertificateProviderFactory > (
" fake1 " , & grpc : : testing : : g_fake1_cert_data_map ) ) ;
grpc_core : : CertificateProviderRegistry : : RegisterCertificateProviderFactory (
absl : : make_unique < grpc : : testing : : FakeCertificateProviderFactory > (
" fake2 " , & grpc : : testing : : g_fake2_cert_data_map ) ) ;
grpc_init ( ) ;
grpc : : testing : : ConnectionAttemptInjector : : Init ( ) ;
grpc_core : : XdsHttpFilterRegistry : : RegisterFilter (
absl : : make_unique < grpc : : testing : : NoOpHttpFilter > (
" grpc.testing.client_only_http_filter " ,
/* supported_on_clients = */ true , /* supported_on_servers = */ false ,
/* is_terminal_filter */ false ) ,
{ " grpc.testing.client_only_http_filter " } ) ;
grpc_core : : XdsHttpFilterRegistry : : RegisterFilter (
absl : : make_unique < grpc : : testing : : NoOpHttpFilter > (
" grpc.testing.server_only_http_filter " ,
/* supported_on_clients = */ false , /* supported_on_servers = */ true ,
/* is_terminal_filter */ false ) ,
{ " grpc.testing.server_only_http_filter " } ) ;
grpc_core : : XdsHttpFilterRegistry : : RegisterFilter (
absl : : make_unique < grpc : : testing : : NoOpHttpFilter > (
" grpc.testing.terminal_http_filter " ,
/* supported_on_clients = */ true , /* supported_on_servers = */ true ,
/* is_terminal_filter */ true ) ,
{ " grpc.testing.terminal_http_filter " } ) ;
const auto result = RUN_ALL_TESTS ( ) ;
grpc_shutdown ( ) ;
return result ;
}