Merge remote-tracking branch 'upstream/master' into c++

pull/22292/head
Ashitha Santhosh 5 years ago
commit b1652b4443
  1. 2
      Makefile
  2. 5
      bazel/cython_library.bzl
  3. 1
      build_autogenerated.yaml
  4. 8
      examples/cpp/helloworld/CMakeLists.txt
  5. 8
      include/grpc/grpc_security.h
  6. 18
      include/grpcpp/impl/codegen/client_callback_impl.h
  7. 51
      src/core/ext/filters/client_channel/lb_policy/child_policy_handler.cc
  8. 17
      src/core/ext/filters/client_channel/lb_policy/child_policy_handler.h
  9. 122
      src/core/ext/filters/client_channel/lb_policy/xds/xds.cc
  10. 20
      src/core/ext/filters/client_channel/xds/xds_api.h
  11. 8
      src/core/ext/filters/client_channel/xds/xds_channel.cc
  12. 183
      src/core/ext/filters/client_channel/xds/xds_client.cc
  13. 19
      src/core/ext/filters/client_channel/xds/xds_client.h
  14. 25
      src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h
  15. 14
      src/core/lib/security/security_connector/tls/tls_security_connector.cc
  16. 6
      src/cpp/README.md
  17. 10
      src/cpp/common/tls_credentials_options.cc
  18. 2
      src/php/ext/grpc/config.m4
  19. 8
      src/python/grpcio/grpc/__init__.py
  20. 8
      src/python/grpcio/grpc/_cython/BUILD.bazel
  21. 18
      src/python/grpcio/grpc/_cython/_cygrpc/aio/call.pyx.pxi
  22. 1
      src/python/grpcio/grpc/_cython/_cygrpc/aio/channel.pxd.pxi
  23. 5
      src/python/grpcio/grpc/_cython/_cygrpc/aio/channel.pyx.pxi
  24. 4
      src/python/grpcio/grpc/_cython/_cygrpc/aio/grpc_aio.pxd.pxi
  25. 8
      src/python/grpcio/grpc/_cython/_cygrpc/aio/grpc_aio.pyx.pxi
  26. 11
      src/python/grpcio/grpc/_cython/_cygrpc/aio/iomgr/iomgr.pyx.pxi
  27. 14
      src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
  28. 5
      src/python/grpcio/grpc/experimental/aio/__init__.py
  29. 2
      src/python/grpcio_tests/tests_aio/interop/BUILD.bazel
  30. 6
      src/python/grpcio_tests/tests_aio/tests.json
  31. 2
      src/python/grpcio_tests/tests_aio/unit/BUILD.bazel
  32. 3
      src/python/grpcio_tests/tests_aio/unit/_test_base.py
  33. 6
      src/python/grpcio_tests/tests_aio/unit/_test_server.py
  34. 43
      src/python/grpcio_tests/tests_aio/unit/call_test.py
  35. 31
      src/python/grpcio_tests/tests_aio/unit/init_test.py
  36. 130
      src/python/grpcio_tests/tests_aio/unit/secure_call_test.py
  37. 12
      src/python/grpcio_tests/tests_py3_only/interop/BUILD.bazel
  38. 254
      src/python/grpcio_tests/tests_py3_only/interop/xds_interop_client.py
  39. 1
      test/core/channel/BUILD
  40. 2
      test/core/end2end/tests/disappearing_server.cc
  41. 7
      test/core/security/grpc_tls_credentials_options_test.cc
  42. 37
      test/core/tsi/ssl_transport_security_test.cc
  43. 57
      test/cpp/client/credentials_test.cc
  44. 360
      test/cpp/end2end/xds_end2end_test.cc
  45. 56
      tools/internal_ci/linux/grpc_xds_bazel_python_test_in_docker.sh
  46. 23
      tools/internal_ci/linux/grpc_xds_python.cfg
  47. 5
      tools/interop_matrix/client_matrix.py
  48. 13
      tools/interop_matrix/create_matrix_images.py
  49. 3
      tools/remote_build/mac.bazelrc
  50. 24
      tools/run_tests/generated/tests.json
  51. 35
      tools/run_tests/run_tests_matrix.py
  52. 7
      tools/run_tests/run_xds_tests.py

@ -2206,8 +2206,6 @@ test_cxx: buildtests_cxx
$(Q) $(BINDIR)/$(CONFIG)/channelz_registry_test || ( echo test channelz_registry_test failed ; exit 1 ) $(Q) $(BINDIR)/$(CONFIG)/channelz_registry_test || ( echo test channelz_registry_test failed ; exit 1 )
$(E) "[RUN] Testing channelz_service_test" $(E) "[RUN] Testing channelz_service_test"
$(Q) $(BINDIR)/$(CONFIG)/channelz_service_test || ( echo test channelz_service_test failed ; exit 1 ) $(Q) $(BINDIR)/$(CONFIG)/channelz_service_test || ( echo test channelz_service_test failed ; exit 1 )
$(E) "[RUN] Testing channelz_test"
$(Q) $(BINDIR)/$(CONFIG)/channelz_test || ( echo test channelz_test failed ; exit 1 )
$(E) "[RUN] Testing cli_call_test" $(E) "[RUN] Testing cli_call_test"
$(Q) $(BINDIR)/$(CONFIG)/cli_call_test || ( echo test cli_call_test failed ; exit 1 ) $(Q) $(BINDIR)/$(CONFIG)/cli_call_test || ( echo test cli_call_test failed ; exit 1 )
$(E) "[RUN] Testing client_callback_end2end_test" $(E) "[RUN] Testing client_callback_end2end_test"

@ -63,12 +63,15 @@ def pyx_library(name, deps = [], py_deps = [], srcs = [], **kwargs):
) )
shared_objects.append(shared_object_name) shared_objects.append(shared_object_name)
data = shared_objects[:]
data += kwargs.pop("data", [])
# Now create a py_library with these shared objects as data. # Now create a py_library with these shared objects as data.
native.py_library( native.py_library(
name = name, name = name,
srcs = py_srcs, srcs = py_srcs,
deps = py_deps, deps = py_deps,
srcs_version = "PY2AND3", srcs_version = "PY2AND3",
data = shared_objects, data = data,
**kwargs **kwargs
) )

@ -5441,6 +5441,7 @@ targets:
- name: channelz_test - name: channelz_test
gtest: true gtest: true
build: test build: test
run: false
language: c++ language: c++
headers: headers:
- test/cpp/util/channel_trace_proto_helper.h - test/cpp/util/channel_trace_proto_helper.h

@ -60,7 +60,7 @@ if(GRPC_AS_SUBMODULE)
else() else()
set(_PROTOBUF_PROTOC $<TARGET_FILE:protobuf::protoc>) set(_PROTOBUF_PROTOC $<TARGET_FILE:protobuf::protoc>)
endif() endif()
set(_GRPC_GRPCPP_UNSECURE grpc++_unsecure) set(_GRPC_GRPCPP grpc++)
if(CMAKE_CROSSCOMPILING) if(CMAKE_CROSSCOMPILING)
find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin) find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin)
else() else()
@ -87,7 +87,7 @@ elseif(GRPC_FETCHCONTENT)
set(_PROTOBUF_LIBPROTOBUF libprotobuf) set(_PROTOBUF_LIBPROTOBUF libprotobuf)
set(_REFLECTION grpc++_reflection) set(_REFLECTION grpc++_reflection)
set(_PROTOBUF_PROTOC $<TARGET_FILE:protoc>) set(_PROTOBUF_PROTOC $<TARGET_FILE:protoc>)
set(_GRPC_GRPCPP_UNSECURE grpc++_unsecure) set(_GRPC_GRPCPP grpc++)
if(CMAKE_CROSSCOMPILING) if(CMAKE_CROSSCOMPILING)
find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin) find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin)
else() else()
@ -116,7 +116,7 @@ else()
find_package(gRPC CONFIG REQUIRED) find_package(gRPC CONFIG REQUIRED)
message(STATUS "Using gRPC ${gRPC_VERSION}") message(STATUS "Using gRPC ${gRPC_VERSION}")
set(_GRPC_GRPCPP_UNSECURE gRPC::grpc++_unsecure) set(_GRPC_GRPCPP gRPC::grpc++)
if(CMAKE_CROSSCOMPILING) if(CMAKE_CROSSCOMPILING)
find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin) find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin)
else() else()
@ -155,6 +155,6 @@ foreach(_target
${hw_grpc_srcs}) ${hw_grpc_srcs})
target_link_libraries(${_target} target_link_libraries(${_target}
${_REFLECTION} ${_REFLECTION}
${_GRPC_GRPCPP_UNSECURE} ${_GRPC_GRPCPP}
${_PROTOBUF_LIBPROTOBUF}) ${_PROTOBUF_LIBPROTOBUF})
endforeach() endforeach()

@ -714,6 +714,10 @@ GRPCAPI grpc_server_credentials* grpc_local_server_credentials_create(
/** --- TLS channel/server credentials --- /** --- TLS channel/server credentials ---
* It is used for experimental purpose for now and subject to change. */ * It is used for experimental purpose for now and subject to change. */
/** Struct for indicating errors. It is used for
* experimental purpose for now and subject to change. */
typedef struct grpc_tls_error_details grpc_tls_error_details;
/** Config for TLS key materials. It is used for /** Config for TLS key materials. It is used for
* experimental purpose for now and subject to change. */ * experimental purpose for now and subject to change. */
typedef struct grpc_tls_key_materials_config grpc_tls_key_materials_config; typedef struct grpc_tls_key_materials_config grpc_tls_key_materials_config;
@ -857,7 +861,7 @@ struct grpc_tls_credential_reload_arg {
void* cb_user_data; void* cb_user_data;
grpc_tls_key_materials_config* key_materials_config; grpc_tls_key_materials_config* key_materials_config;
grpc_ssl_certificate_config_reload_status status; grpc_ssl_certificate_config_reload_status status;
const char* error_details; grpc_tls_error_details* error_details;
grpc_tls_credential_reload_config* config; grpc_tls_credential_reload_config* config;
void* context; void* context;
void (*destroy_context)(void* ctx); void (*destroy_context)(void* ctx);
@ -935,7 +939,7 @@ struct grpc_tls_server_authorization_check_arg {
const char* peer_cert; const char* peer_cert;
const char* peer_cert_full_chain; const char* peer_cert_full_chain;
grpc_status_code status; grpc_status_code status;
const char* error_details; grpc_tls_error_details* error_details;
grpc_tls_server_authorization_check_config* config; grpc_tls_server_authorization_check_config* config;
void* context; void* context;
void (*destroy_context)(void* ctx); void (*destroy_context)(void* ctx);

@ -272,7 +272,10 @@ class ClientBidiReactor {
void RemoveHold() { stream_->RemoveHold(); } void RemoveHold() { stream_->RemoveHold(); }
/// Notifies the application that all operations associated with this RPC /// Notifies the application that all operations associated with this RPC
/// have completed and provides the RPC status outcome. /// have completed and all Holds have been removed. OnDone provides the RPC
/// status outcome for both successful and failed RPCs and will be called in
/// all cases. If it is not called, it indicates an application-level problem
/// (like failure to remove a hold).
/// ///
/// \param[in] s The status outcome of this RPC /// \param[in] s The status outcome of this RPC
virtual void OnDone(const ::grpc::Status& /*s*/) {} virtual void OnDone(const ::grpc::Status& /*s*/) {}
@ -283,19 +286,21 @@ class ClientBidiReactor {
/// call of OnReadDone or OnDone. /// call of OnReadDone or OnDone.
/// ///
/// \param[in] ok Was the initial metadata read successfully? If false, no /// \param[in] ok Was the initial metadata read successfully? If false, no
/// new read/write operation will succeed. /// new read/write operation will succeed, and any further
/// Start* operations should not be called.
virtual void OnReadInitialMetadataDone(bool /*ok*/) {} virtual void OnReadInitialMetadataDone(bool /*ok*/) {}
/// Notifies the application that a StartRead operation completed. /// Notifies the application that a StartRead operation completed.
/// ///
/// \param[in] ok Was it successful? If false, no new read/write operation /// \param[in] ok Was it successful? If false, no new read/write operation
/// will succeed. /// will succeed, and any further Start* should not be called.
virtual void OnReadDone(bool /*ok*/) {} virtual void OnReadDone(bool /*ok*/) {}
/// Notifies the application that a StartWrite operation completed. /// Notifies the application that a StartWrite or StartWriteLast operation
/// completed.
/// ///
/// \param[in] ok Was it successful? If false, no new read/write operation /// \param[in] ok Was it successful? If false, no new read/write operation
/// will succeed. /// will succeed, and any further Start* should not be called.
virtual void OnWriteDone(bool /*ok*/) {} virtual void OnWriteDone(bool /*ok*/) {}
/// Notifies the application that a StartWritesDone operation completed. Note /// Notifies the application that a StartWritesDone operation completed. Note
@ -303,7 +308,8 @@ class ClientBidiReactor {
/// those that are implicitly invoked as part of a StartWriteLast. /// those that are implicitly invoked as part of a StartWriteLast.
/// ///
/// \param[in] ok Was it successful? If false, the application will later see /// \param[in] ok Was it successful? If false, the application will later see
/// the failure reflected as a bad status in OnDone. /// the failure reflected as a bad status in OnDone and no
/// further Start* should be called.
virtual void OnWritesDoneDone(bool /*ok*/) {} virtual void OnWritesDoneDone(bool /*ok*/) {}
private: private:

@ -16,6 +16,8 @@
#include <grpc/support/port_platform.h> #include <grpc/support/port_platform.h>
#include <cstring>
#include "src/core/ext/filters/client_channel/lb_policy/child_policy_handler.h" #include "src/core/ext/filters/client_channel/lb_policy/child_policy_handler.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
@ -138,8 +140,6 @@ void ChildPolicyHandler::ShutdownLocked() {
} }
void ChildPolicyHandler::UpdateLocked(UpdateArgs args) { void ChildPolicyHandler::UpdateLocked(UpdateArgs args) {
// The name of the policy that this update wants us to use.
const char* child_policy_name = args.config->name();
// If the child policy name changes, we need to create a new child // If the child policy name changes, we need to create a new child
// policy. When this happens, we leave child_policy_ as-is and store // policy. When this happens, we leave child_policy_ as-is and store
// the new child policy in pending_child_policy_. Once the new child // the new child policy in pending_child_policy_. Once the new child
@ -166,10 +166,10 @@ void ChildPolicyHandler::UpdateLocked(UpdateArgs args) {
// previous update that changed the policy name, or we have already // previous update that changed the policy name, or we have already
// finished swapping in the new policy; in this case, child_policy_ // finished swapping in the new policy; in this case, child_policy_
// is non-null but pending_child_policy_ is null). In this case: // is non-null but pending_child_policy_ is null). In this case:
// a. If child_policy_->name() equals child_policy_name, then we // a. If going from the current config to the new config does not
// update the existing child policy. // require a new policy, then we update the existing child policy.
// b. If child_policy_->name() does not equal child_policy_name, // b. If going from the current config to the new config does require a
// we create a new policy. The policy will be stored in // new policy, we create a new policy. The policy will be stored in
// pending_child_policy_ and will later be swapped into // pending_child_policy_ and will later be swapped into
// child_policy_ by the helper when the new child transitions // child_policy_ by the helper when the new child transitions
// into state READY. // into state READY.
@ -180,10 +180,11 @@ void ChildPolicyHandler::UpdateLocked(UpdateArgs args) {
// not yet transitioned into state READY and been swapped into // not yet transitioned into state READY and been swapped into
// child_policy_; in this case, both child_policy_ and // child_policy_; in this case, both child_policy_ and
// pending_child_policy_ are non-null). In this case: // pending_child_policy_ are non-null). In this case:
// a. If pending_child_policy_->name() equals child_policy_name, // a. If going from the current config to the new config does not
// then we update the existing pending child policy. // require a new policy, then we update the existing pending
// b. If pending_child_policy->name() does not equal // child policy.
// child_policy_name, then we create a new policy. The new // b. If going from the current config to the new config does require a
// new child policy, then we create a new policy. The new
// policy is stored in pending_child_policy_ (replacing the one // policy is stored in pending_child_policy_ (replacing the one
// that was there before, which will be immediately shut down) // that was there before, which will be immediately shut down)
// and will later be swapped into child_policy_ by the helper // and will later be swapped into child_policy_ by the helper
@ -191,12 +192,10 @@ void ChildPolicyHandler::UpdateLocked(UpdateArgs args) {
const bool create_policy = const bool create_policy =
// case 1 // case 1
child_policy_ == nullptr || child_policy_ == nullptr ||
// case 2b // cases 2b and 3b
(pending_child_policy_ == nullptr && ConfigChangeRequiresNewPolicyInstance(current_config_.get(),
strcmp(child_policy_->name(), child_policy_name) != 0) || args.config.get());
// case 3b current_config_ = args.config;
(pending_child_policy_ != nullptr &&
strcmp(pending_child_policy_->name(), child_policy_name) != 0);
LoadBalancingPolicy* policy_to_update = nullptr; LoadBalancingPolicy* policy_to_update = nullptr;
if (create_policy) { if (create_policy) {
// Cases 1, 2b, and 3b: create a new child policy. // Cases 1, 2b, and 3b: create a new child policy.
@ -205,11 +204,11 @@ void ChildPolicyHandler::UpdateLocked(UpdateArgs args) {
if (GRPC_TRACE_FLAG_ENABLED(*tracer_)) { if (GRPC_TRACE_FLAG_ENABLED(*tracer_)) {
gpr_log(GPR_INFO, gpr_log(GPR_INFO,
"[child_policy_handler %p] creating new %schild policy %s", this, "[child_policy_handler %p] creating new %schild policy %s", this,
child_policy_ == nullptr ? "" : "pending ", child_policy_name); child_policy_ == nullptr ? "" : "pending ", args.config->name());
} }
auto& lb_policy = auto& lb_policy =
child_policy_ == nullptr ? child_policy_ : pending_child_policy_; child_policy_ == nullptr ? child_policy_ : pending_child_policy_;
lb_policy = CreateChildPolicy(child_policy_name, *args.args); lb_policy = CreateChildPolicy(args.config->name(), *args.args);
policy_to_update = lb_policy.get(); policy_to_update = lb_policy.get();
} else { } else {
// Cases 2a and 3a: update an existing policy. // Cases 2a and 3a: update an existing policy.
@ -257,8 +256,7 @@ OrphanablePtr<LoadBalancingPolicy> ChildPolicyHandler::CreateChildPolicy(
std::unique_ptr<ChannelControlHelper>(helper); std::unique_ptr<ChannelControlHelper>(helper);
lb_policy_args.args = &args; lb_policy_args.args = &args;
OrphanablePtr<LoadBalancingPolicy> lb_policy = OrphanablePtr<LoadBalancingPolicy> lb_policy =
LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy( CreateLoadBalancingPolicy(child_policy_name, std::move(lb_policy_args));
child_policy_name, std::move(lb_policy_args));
if (GPR_UNLIKELY(lb_policy == nullptr)) { if (GPR_UNLIKELY(lb_policy == nullptr)) {
gpr_log(GPR_ERROR, "could not create LB policy \"%s\"", child_policy_name); gpr_log(GPR_ERROR, "could not create LB policy \"%s\"", child_policy_name);
return nullptr; return nullptr;
@ -277,4 +275,17 @@ OrphanablePtr<LoadBalancingPolicy> ChildPolicyHandler::CreateChildPolicy(
return lb_policy; return lb_policy;
} }
bool ChildPolicyHandler::ConfigChangeRequiresNewPolicyInstance(
LoadBalancingPolicy::Config* old_config,
LoadBalancingPolicy::Config* new_config) const {
return strcmp(old_config->name(), new_config->name()) != 0;
}
OrphanablePtr<LoadBalancingPolicy>
ChildPolicyHandler::CreateLoadBalancingPolicy(
const char* name, LoadBalancingPolicy::Args args) const {
return LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(
name, std::move(args));
}
} // namespace grpc_core } // namespace grpc_core

@ -42,6 +42,18 @@ class ChildPolicyHandler : public LoadBalancingPolicy {
void ExitIdleLocked() override; void ExitIdleLocked() override;
void ResetBackoffLocked() override; void ResetBackoffLocked() override;
// Returns true if transitioning from the old config to the new config
// requires instantiating a new policy object.
virtual bool ConfigChangeRequiresNewPolicyInstance(
LoadBalancingPolicy::Config* old_config,
LoadBalancingPolicy::Config* new_config) const;
// Instantiates a new policy of the specified name.
// May be overridden by subclasses to avoid recursion when an LB
// policy factory returns a ChildPolicyHandler.
virtual OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy(
const char* name, LoadBalancingPolicy::Args args) const;
private: private:
class Helper; class Helper;
@ -55,6 +67,11 @@ class ChildPolicyHandler : public LoadBalancingPolicy {
bool shutting_down_ = false; bool shutting_down_ = false;
// The most recent config passed to UpdateLocked().
// If pending_child_policy_ is non-null, this is the config passed to
// pending_child_policy_; otherwise, it's the config passed to child_policy_.
RefCountedPtr<LoadBalancingPolicy::Config> current_config_;
// Child LB policy. // Child LB policy.
OrphanablePtr<LoadBalancingPolicy> child_policy_; OrphanablePtr<LoadBalancingPolicy> child_policy_;
OrphanablePtr<LoadBalancingPolicy> pending_child_policy_; OrphanablePtr<LoadBalancingPolicy> pending_child_policy_;

@ -25,6 +25,8 @@
#include <limits.h> #include <limits.h>
#include <string.h> #include <string.h>
#include "absl/types/optional.h"
#include <grpc/grpc.h> #include <grpc/grpc.h>
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/string_util.h> #include <grpc/support/string_util.h>
@ -80,7 +82,7 @@ class XdsConfig : public LoadBalancingPolicy::Config {
XdsConfig(RefCountedPtr<LoadBalancingPolicy::Config> child_policy, XdsConfig(RefCountedPtr<LoadBalancingPolicy::Config> child_policy,
RefCountedPtr<LoadBalancingPolicy::Config> fallback_policy, RefCountedPtr<LoadBalancingPolicy::Config> fallback_policy,
std::string eds_service_name, std::string eds_service_name,
Optional<std::string> lrs_load_reporting_server_name) absl::optional<std::string> lrs_load_reporting_server_name)
: child_policy_(std::move(child_policy)), : child_policy_(std::move(child_policy)),
fallback_policy_(std::move(fallback_policy)), fallback_policy_(std::move(fallback_policy)),
eds_service_name_(std::move(eds_service_name)), eds_service_name_(std::move(eds_service_name)),
@ -101,7 +103,7 @@ class XdsConfig : public LoadBalancingPolicy::Config {
return eds_service_name_.empty() ? nullptr : eds_service_name_.c_str(); return eds_service_name_.empty() ? nullptr : eds_service_name_.c_str();
}; };
const Optional<std::string>& lrs_load_reporting_server_name() const { const absl::optional<std::string>& lrs_load_reporting_server_name() const {
return lrs_load_reporting_server_name_; return lrs_load_reporting_server_name_;
}; };
@ -109,7 +111,7 @@ class XdsConfig : public LoadBalancingPolicy::Config {
RefCountedPtr<LoadBalancingPolicy::Config> child_policy_; RefCountedPtr<LoadBalancingPolicy::Config> child_policy_;
RefCountedPtr<LoadBalancingPolicy::Config> fallback_policy_; RefCountedPtr<LoadBalancingPolicy::Config> fallback_policy_;
std::string eds_service_name_; std::string eds_service_name_;
Optional<std::string> lrs_load_reporting_server_name_; absl::optional<std::string> lrs_load_reporting_server_name_;
}; };
class XdsLb : public LoadBalancingPolicy { class XdsLb : public LoadBalancingPolicy {
@ -723,7 +725,6 @@ void XdsLb::UpdateLocked(UpdateArgs args) {
} }
const bool is_initial_update = args_ == nullptr; const bool is_initial_update = args_ == nullptr;
// Update config. // Update config.
const char* old_eds_service_name = eds_service_name();
auto old_config = std::move(config_); auto old_config = std::move(config_);
config_ = std::move(args.config); config_ = std::move(args.config);
// Update fallback address list. // Update fallback address list.
@ -771,30 +772,8 @@ void XdsLb::UpdateLocked(UpdateArgs args) {
eds_service_name(), eds_service_name()); eds_service_name(), eds_service_name());
} }
} }
// Update priority list. // On the initial update, create the endpoint watcher.
// Note that this comes after updating drop_stats_, since we want that if (is_initial_update) {
// to be used by any new picker we create here.
// No need to do this on the initial update, since there won't be any
// priorities to update yet.
if (!is_initial_update) {
const bool update_locality_stats =
config_->lrs_load_reporting_server_name() !=
old_config->lrs_load_reporting_server_name() ||
strcmp(old_eds_service_name, eds_service_name()) != 0;
UpdatePrioritiesLocked(update_locality_stats);
}
// Update endpoint watcher if needed.
if (is_initial_update ||
strcmp(old_eds_service_name, eds_service_name()) != 0) {
if (!is_initial_update) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
gpr_log(GPR_INFO, "[xdslb %p] cancelling watch for %s", this,
old_eds_service_name);
}
xds_client()->CancelEndpointDataWatch(StringView(old_eds_service_name),
endpoint_watcher_,
/*delay_unsubscription=*/true);
}
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) { if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
gpr_log(GPR_INFO, "[xdslb %p] starting watch for %s", this, gpr_log(GPR_INFO, "[xdslb %p] starting watch for %s", this,
eds_service_name()); eds_service_name());
@ -804,6 +783,16 @@ void XdsLb::UpdateLocked(UpdateArgs args) {
endpoint_watcher_ = watcher.get(); endpoint_watcher_ = watcher.get();
xds_client()->WatchEndpointData(StringView(eds_service_name()), xds_client()->WatchEndpointData(StringView(eds_service_name()),
std::move(watcher)); std::move(watcher));
} else {
// Update priority list.
// Note that this comes after updating drop_stats_, since we want that
// to be used by any new picker we create here.
// No need to do this on the initial update, since there won't be any
// priorities to update yet.
const bool update_locality_stats =
config_->lrs_load_reporting_server_name() !=
old_config->lrs_load_reporting_server_name();
UpdatePrioritiesLocked(update_locality_stats);
} }
} }
@ -931,12 +920,14 @@ void XdsLb::UpdateXdsPickerLocked() {
// If we are in fallback mode, don't generate an xds picker from localities. // If we are in fallback mode, don't generate an xds picker from localities.
if (fallback_policy_ != nullptr) return; if (fallback_policy_ != nullptr) return;
if (current_priority_ == UINT32_MAX) { if (current_priority_ == UINT32_MAX) {
grpc_error* error = grpc_error_set_int( if (fallback_policy_ == nullptr) {
GRPC_ERROR_CREATE_FROM_STATIC_STRING("no ready locality map"), grpc_error* error = grpc_error_set_int(
GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE); GRPC_ERROR_CREATE_FROM_STATIC_STRING("no ready locality map"),
channel_control_helper()->UpdateState( GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE);
GRPC_CHANNEL_TRANSIENT_FAILURE, channel_control_helper()->UpdateState(
absl::make_unique<TransientFailurePicker>(error)); GRPC_CHANNEL_TRANSIENT_FAILURE,
absl::make_unique<TransientFailurePicker>(error));
}
return; return;
} }
priorities_[current_priority_]->UpdateXdsPickerLocked(); priorities_[current_priority_]->UpdateXdsPickerLocked();
@ -998,7 +989,16 @@ OrphanablePtr<XdsLb::LocalityMap::Locality> XdsLb::ExtractLocalityLocked(
if (priority == exclude_priority) continue; if (priority == exclude_priority) continue;
LocalityMap* locality_map = priorities_[priority].get(); LocalityMap* locality_map = priorities_[priority].get();
auto locality = locality_map->ExtractLocalityLocked(name); auto locality = locality_map->ExtractLocalityLocked(name);
if (locality != nullptr) return locality; if (locality != nullptr) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
gpr_log(GPR_INFO,
"[xdslb %p] moving locality %p %s to new priority (%" PRIu32
" -> %" PRIu32 ")",
this, locality.get(), name->AsHumanReadableString(),
exclude_priority, priority);
}
return locality;
}
} }
return nullptr; return nullptr;
} }
@ -1024,7 +1024,7 @@ XdsLb::LocalityMap::LocalityMap(RefCountedPtr<XdsLb> xds_policy,
&on_failover_timer_); &on_failover_timer_);
failover_timer_callback_pending_ = true; failover_timer_callback_pending_ = true;
// This is the first locality map ever created, report CONNECTING. // This is the first locality map ever created, report CONNECTING.
if (priority_ == 0) { if (priority_ == 0 && xds_policy_->fallback_policy_ == nullptr) {
xds_policy_->channel_control_helper()->UpdateState( xds_policy_->channel_control_helper()->UpdateState(
GRPC_CHANNEL_CONNECTING, GRPC_CHANNEL_CONNECTING,
absl::make_unique<QueuePicker>( absl::make_unique<QueuePicker>(
@ -1158,6 +1158,10 @@ XdsLb::LocalityMap::ExtractLocalityLocked(
} }
void XdsLb::LocalityMap::DeactivateLocked() { void XdsLb::LocalityMap::DeactivateLocked() {
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
gpr_log(GPR_INFO, "[xdslb %p] deactivating priority %" PRIu32, xds_policy(),
priority_);
}
// If already deactivated, don't do it again. // If already deactivated, don't do it again.
if (delayed_removal_timer_callback_pending_) return; if (delayed_removal_timer_callback_pending_) return;
MaybeCancelFailoverTimerLocked(); MaybeCancelFailoverTimerLocked();
@ -1182,6 +1186,10 @@ bool XdsLb::LocalityMap::MaybeReactivateLocked() {
// Don't reactivate a priority that is not higher than the current one. // Don't reactivate a priority that is not higher than the current one.
if (priority_ >= xds_policy_->current_priority_) return false; if (priority_ >= xds_policy_->current_priority_) return false;
// Reactivate this priority by cancelling deletion timer. // Reactivate this priority by cancelling deletion timer.
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
gpr_log(GPR_INFO, "[xdslb %p] reactivating priority %" PRIu32, xds_policy(),
priority_);
}
if (delayed_removal_timer_callback_pending_) { if (delayed_removal_timer_callback_pending_) {
grpc_timer_cancel(&delayed_removal_timer_); grpc_timer_cancel(&delayed_removal_timer_);
} }
@ -1438,6 +1446,10 @@ void XdsLb::LocalityMap::Locality::UpdateLocked(uint32_t locality_weight,
// Update locality weight. // Update locality weight.
weight_ = locality_weight; weight_ = locality_weight;
if (delayed_removal_timer_callback_pending_) { if (delayed_removal_timer_callback_pending_) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
gpr_log(GPR_INFO, "[xdslb %p] Locality %p %s: reactivating", xds_policy(),
this, name_->AsHumanReadableString());
}
grpc_timer_cancel(&delayed_removal_timer_); grpc_timer_cancel(&delayed_removal_timer_);
} }
// Update locality stats. // Update locality stats.
@ -1495,6 +1507,10 @@ void XdsLb::LocalityMap::Locality::Orphan() {
void XdsLb::LocalityMap::Locality::DeactivateLocked() { void XdsLb::LocalityMap::Locality::DeactivateLocked() {
// If already deactivated, don't do that again. // If already deactivated, don't do that again.
if (weight_ == 0) return; if (weight_ == 0) return;
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
gpr_log(GPR_INFO, "[xdslb %p] Locality %p %s: deactivating", xds_policy(),
this, name_->AsHumanReadableString());
}
// Set the locality weight to 0 so that future xds picker won't contain this // Set the locality weight to 0 so that future xds picker won't contain this
// locality. // locality.
weight_ = 0; weight_ = 0;
@ -1572,7 +1588,7 @@ class XdsFactory : public LoadBalancingPolicyFactory {
public: public:
OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy( OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy(
LoadBalancingPolicy::Args args) const override { LoadBalancingPolicy::Args args) const override {
return MakeOrphanable<XdsLb>(std::move(args)); return MakeOrphanable<XdsChildHandler>(std::move(args), &grpc_lb_xds_trace);
} }
const char* name() const override { return kXds; } const char* name() const override { return kXds; }
@ -1656,7 +1672,7 @@ class XdsFactory : public LoadBalancingPolicyFactory {
} }
} }
if (error_list.empty()) { if (error_list.empty()) {
Optional<std::string> optional_lrs_load_reporting_server_name; absl::optional<std::string> optional_lrs_load_reporting_server_name;
if (lrs_load_reporting_server_name != nullptr) { if (lrs_load_reporting_server_name != nullptr) {
optional_lrs_load_reporting_server_name.emplace( optional_lrs_load_reporting_server_name.emplace(
std::string(lrs_load_reporting_server_name)); std::string(lrs_load_reporting_server_name));
@ -1670,6 +1686,36 @@ class XdsFactory : public LoadBalancingPolicyFactory {
return nullptr; return nullptr;
} }
} }
private:
class XdsChildHandler : public ChildPolicyHandler {
public:
XdsChildHandler(Args args, TraceFlag* tracer)
: ChildPolicyHandler(std::move(args), tracer) {}
bool ConfigChangeRequiresNewPolicyInstance(
LoadBalancingPolicy::Config* old_config,
LoadBalancingPolicy::Config* new_config) const override {
GPR_ASSERT(old_config->name() == kXds);
GPR_ASSERT(new_config->name() == kXds);
XdsConfig* old_xds_config = static_cast<XdsConfig*>(old_config);
XdsConfig* new_xds_config = static_cast<XdsConfig*>(new_config);
const char* old_eds_service_name =
old_xds_config->eds_service_name() == nullptr
? ""
: old_xds_config->eds_service_name();
const char* new_eds_service_name =
new_xds_config->eds_service_name() == nullptr
? ""
: new_xds_config->eds_service_name();
return strcmp(old_eds_service_name, new_eds_service_name) != 0;
}
OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy(
const char* name, LoadBalancingPolicy::Args args) const override {
return MakeOrphanable<XdsLb>(std::move(args));
}
};
}; };
} // namespace } // namespace

@ -25,12 +25,13 @@
#include <set> #include <set>
#include "absl/types/optional.h"
#include <grpc/slice_buffer.h> #include <grpc/slice_buffer.h>
#include "src/core/ext/filters/client_channel/server_address.h" #include "src/core/ext/filters/client_channel/server_address.h"
#include "src/core/ext/filters/client_channel/xds/xds_bootstrap.h" #include "src/core/ext/filters/client_channel/xds/xds_bootstrap.h"
#include "src/core/ext/filters/client_channel/xds/xds_client_stats.h" #include "src/core/ext/filters/client_channel/xds/xds_client_stats.h"
#include "src/core/lib/gprpp/optional.h"
namespace grpc_core { namespace grpc_core {
@ -46,14 +47,25 @@ class XdsApi {
struct RdsUpdate { struct RdsUpdate {
// The name to use in the CDS request. // The name to use in the CDS request.
std::string cluster_name; std::string cluster_name;
bool operator==(const RdsUpdate& other) const {
return cluster_name == other.cluster_name;
}
}; };
// TODO(roth): When we can use absl::variant<>, consider using that
// here, to enforce the fact that only one of the two fields can be set.
struct LdsUpdate { struct LdsUpdate {
// The name to use in the RDS request. // The name to use in the RDS request.
std::string route_config_name; std::string route_config_name;
// The name to use in the CDS request. Present if the LDS response has it // The name to use in the CDS request. Present if the LDS response has it
// inlined. // inlined.
Optional<RdsUpdate> rds_update; absl::optional<RdsUpdate> rds_update;
bool operator==(const LdsUpdate& other) const {
return route_config_name == other.route_config_name &&
rds_update == other.rds_update;
}
}; };
using LdsUpdateMap = std::map<std::string /*server_name*/, LdsUpdate>; using LdsUpdateMap = std::map<std::string /*server_name*/, LdsUpdate>;
@ -68,7 +80,7 @@ class XdsApi {
// If not set, load reporting will be disabled. // If not set, load reporting will be disabled.
// If set to the empty string, will use the same server we obtained the CDS // If set to the empty string, will use the same server we obtained the CDS
// data from. // data from.
Optional<std::string> lrs_load_reporting_server_name; absl::optional<std::string> lrs_load_reporting_server_name;
}; };
using CdsUpdateMap = std::map<std::string /*cluster_name*/, CdsUpdate>; using CdsUpdateMap = std::map<std::string /*cluster_name*/, CdsUpdate>;
@ -180,7 +192,7 @@ class XdsApi {
struct ClusterLoadReport { struct ClusterLoadReport {
XdsClusterDropStats::DroppedRequestsMap dropped_requests; XdsClusterDropStats::DroppedRequestsMap dropped_requests;
std::map<XdsLocalityName*, XdsClusterLocalityStats::Snapshot, std::map<RefCountedPtr<XdsLocalityName>, XdsClusterLocalityStats::Snapshot,
XdsLocalityName::Less> XdsLocalityName::Less>
locality_stats; locality_stats;
grpc_millis load_report_interval; grpc_millis load_report_interval;

@ -30,8 +30,12 @@ grpc_channel_args* ModifyXdsChannelArgs(grpc_channel_args* args) {
grpc_channel* CreateXdsChannel(const XdsBootstrap& bootstrap, grpc_channel* CreateXdsChannel(const XdsBootstrap& bootstrap,
const grpc_channel_args& args, const grpc_channel_args& args,
grpc_error** /*error*/) { grpc_error** error) {
if (!bootstrap.server().channel_creds.empty()) return nullptr; if (!bootstrap.server().channel_creds.empty()) {
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"credential specified but gRPC not built with security");
return nullptr;
}
return grpc_insecure_channel_create(bootstrap.server().server_uri.c_str(), return grpc_insecure_channel_create(bootstrap.server().server_uri.c_str(),
&args, nullptr); &args, nullptr);
} }

@ -302,7 +302,6 @@ class XdsClient::ChannelState::LrsCallState
void Orphan() override; void Orphan() override;
void MaybeStartReportingLocked(); void MaybeStartReportingLocked();
bool ShouldSendLoadReports(const StringView& cluster_name) const;
RetryableCall<LrsCallState>* parent() { return parent_.get(); } RetryableCall<LrsCallState>* parent() { return parent_.get(); }
ChannelState* chand() const { return parent_->chand(); } ChannelState* chand() const { return parent_->chand(); }
@ -705,7 +704,8 @@ XdsClient::ChannelState::AdsCallState::AdsCallState(
grpc_op* op = ops; grpc_op* op = ops;
op->op = GRPC_OP_SEND_INITIAL_METADATA; op->op = GRPC_OP_SEND_INITIAL_METADATA;
op->data.send_initial_metadata.count = 0; op->data.send_initial_metadata.count = 0;
op->flags = 0; op->flags = GRPC_INITIAL_METADATA_WAIT_FOR_READY |
GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET;
op->reserved = nullptr; op->reserved = nullptr;
op++; op++;
call_error = grpc_call_start_batch_and_execute(call_, ops, (size_t)(op - ops), call_error = grpc_call_start_batch_and_execute(call_, ops, (size_t)(op - ops),
@ -801,11 +801,12 @@ void XdsClient::ChannelState::AdsCallState::SendMessageLocked(
GRPC_ERROR_REF(state.error), !sent_initial_message_); GRPC_ERROR_REF(state.error), !sent_initial_message_);
state.subscribed_resources[xds_client()->server_name_]->Start(Ref()); state.subscribed_resources[xds_client()->server_name_]->Start(Ref());
} else if (type_url == XdsApi::kRdsTypeUrl) { } else if (type_url == XdsApi::kRdsTypeUrl) {
resource_names.insert(xds_client()->route_config_name_); resource_names.insert(xds_client()->lds_result_->route_config_name);
request_payload_slice = xds_client()->api_.CreateRdsRequest( request_payload_slice = xds_client()->api_.CreateRdsRequest(
xds_client()->route_config_name_, state.version, state.nonce, xds_client()->lds_result_->route_config_name, state.version,
GRPC_ERROR_REF(state.error), !sent_initial_message_); state.nonce, GRPC_ERROR_REF(state.error), !sent_initial_message_);
state.subscribed_resources[xds_client()->route_config_name_]->Start(Ref()); state.subscribed_resources[xds_client()->lds_result_->route_config_name]
->Start(Ref());
} else if (type_url == XdsApi::kCdsTypeUrl) { } else if (type_url == XdsApi::kCdsTypeUrl) {
resource_names = ClusterNamesForRequest(); resource_names = ClusterNamesForRequest();
request_payload_slice = xds_client()->api_.CreateCdsRequest( request_payload_slice = xds_client()->api_.CreateCdsRequest(
@ -888,23 +889,23 @@ void XdsClient::ChannelState::AdsCallState::AcceptLdsUpdate(
"LDS update does not include requested resource")); "LDS update does not include requested resource"));
return; return;
} }
const std::string& cluster_name =
lds_update->rds_update.has_value()
? lds_update->rds_update.value().cluster_name
: "";
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) { if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO, gpr_log(GPR_INFO,
"[xds_client %p] LDS update received: route_config_name=%s, " "[xds_client %p] LDS update received: route_config_name=%s, "
"cluster_name=%s (empty if RDS is needed to obtain it)", "cluster_name=%s",
xds_client(), lds_update->route_config_name.c_str(), xds_client(),
cluster_name.c_str()); (!lds_update->route_config_name.empty()
? lds_update->route_config_name.c_str()
: "<inlined>"),
(lds_update->rds_update.has_value()
? lds_update->rds_update->cluster_name.c_str()
: "<to be obtained via RDS>"));
} }
auto& lds_state = state_map_[XdsApi::kLdsTypeUrl]; auto& lds_state = state_map_[XdsApi::kLdsTypeUrl];
auto& state = lds_state.subscribed_resources[xds_client()->server_name_]; auto& state = lds_state.subscribed_resources[xds_client()->server_name_];
if (state != nullptr) state->Finish(); if (state != nullptr) state->Finish();
// Ignore identical update. // Ignore identical update.
if (xds_client()->route_config_name_ == lds_update->route_config_name && if (xds_client()->lds_result_ == lds_update) {
xds_client()->cluster_name_ == cluster_name) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) { if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO, gpr_log(GPR_INFO,
"[xds_client %p] LDS update identical to current, ignoring.", "[xds_client %p] LDS update identical to current, ignoring.",
@ -912,20 +913,19 @@ void XdsClient::ChannelState::AdsCallState::AcceptLdsUpdate(
} }
return; return;
} }
if (!xds_client()->route_config_name_.empty()) { if (xds_client()->lds_result_.has_value() &&
!xds_client()->lds_result_->route_config_name.empty()) {
Unsubscribe( Unsubscribe(
XdsApi::kRdsTypeUrl, xds_client()->route_config_name_, XdsApi::kRdsTypeUrl, xds_client()->lds_result_->route_config_name,
/*delay_unsubscription=*/!lds_update->route_config_name.empty()); /*delay_unsubscription=*/!lds_update->route_config_name.empty());
} }
xds_client()->route_config_name_ = std::move(lds_update->route_config_name); xds_client()->lds_result_ = std::move(lds_update);
if (lds_update->rds_update.has_value()) { if (xds_client()->lds_result_->rds_update.has_value()) {
// If cluster_name was found inlined in LDS response, notify the watcher // If the RouteConfiguration was found inlined in LDS response, notify
// immediately. // the watcher immediately.
xds_client()->cluster_name_ =
std::move(lds_update->rds_update.value().cluster_name);
RefCountedPtr<ServiceConfig> service_config; RefCountedPtr<ServiceConfig> service_config;
grpc_error* error = xds_client()->CreateServiceConfig( grpc_error* error = xds_client()->CreateServiceConfig(
xds_client()->cluster_name_, &service_config); xds_client()->lds_result_->rds_update->cluster_name, &service_config);
if (error == GRPC_ERROR_NONE) { if (error == GRPC_ERROR_NONE) {
xds_client()->service_config_watcher_->OnServiceConfigChanged( xds_client()->service_config_watcher_->OnServiceConfigChanged(
std::move(service_config)); std::move(service_config));
@ -934,7 +934,8 @@ void XdsClient::ChannelState::AdsCallState::AcceptLdsUpdate(
} }
} else { } else {
// Send RDS request for dynamic resolution. // Send RDS request for dynamic resolution.
Subscribe(XdsApi::kRdsTypeUrl, xds_client()->route_config_name_); Subscribe(XdsApi::kRdsTypeUrl,
xds_client()->lds_result_->route_config_name);
} }
} }
@ -955,10 +956,11 @@ void XdsClient::ChannelState::AdsCallState::AcceptRdsUpdate(
} }
auto& rds_state = state_map_[XdsApi::kRdsTypeUrl]; auto& rds_state = state_map_[XdsApi::kRdsTypeUrl];
auto& state = auto& state =
rds_state.subscribed_resources[xds_client()->route_config_name_]; rds_state
.subscribed_resources[xds_client()->lds_result_->route_config_name];
if (state != nullptr) state->Finish(); if (state != nullptr) state->Finish();
// Ignore identical update. // Ignore identical update.
if (xds_client()->cluster_name_ == rds_update->cluster_name) { if (xds_client()->rds_result_ == rds_update) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) { if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO, gpr_log(GPR_INFO,
"[xds_client %p] RDS update identical to current, ignoring.", "[xds_client %p] RDS update identical to current, ignoring.",
@ -966,11 +968,11 @@ void XdsClient::ChannelState::AdsCallState::AcceptRdsUpdate(
} }
return; return;
} }
xds_client()->cluster_name_ = std::move(rds_update->cluster_name); xds_client()->rds_result_ = std::move(rds_update);
// Notify the watcher. // Notify the watcher.
RefCountedPtr<ServiceConfig> service_config; RefCountedPtr<ServiceConfig> service_config;
grpc_error* error = xds_client()->CreateServiceConfig( grpc_error* error = xds_client()->CreateServiceConfig(
xds_client()->cluster_name_, &service_config); xds_client()->rds_result_->cluster_name, &service_config);
if (error == GRPC_ERROR_NONE) { if (error == GRPC_ERROR_NONE) {
xds_client()->service_config_watcher_->OnServiceConfigChanged( xds_client()->service_config_watcher_->OnServiceConfigChanged(
std::move(service_config)); std::move(service_config));
@ -1215,7 +1217,10 @@ void XdsClient::ChannelState::AdsCallState::OnResponseReceivedLocked(
std::string type_url; std::string type_url;
// Note that ParseAdsResponse() also validates the response. // Note that ParseAdsResponse() also validates the response.
grpc_error* parse_error = xds_client->api_.ParseAdsResponse( grpc_error* parse_error = xds_client->api_.ParseAdsResponse(
response_slice, xds_client->server_name_, xds_client->route_config_name_, response_slice, xds_client->server_name_,
(xds_client->lds_result_.has_value()
? xds_client->lds_result_->route_config_name
: ""),
ads_calld->ClusterNamesForRequest(), ads_calld->ClusterNamesForRequest(),
ads_calld->EdsServiceNamesForRequest(), &lds_update, &rds_update, ads_calld->EdsServiceNamesForRequest(), &lds_update, &rds_update,
&cds_update_map, &eds_update_map, &version, &nonce, &type_url); &cds_update_map, &eds_update_map, &version, &nonce, &type_url);
@ -1409,7 +1414,7 @@ bool LoadReportCountersAreZero(const XdsApi::ClusterLoadReportMap& snapshot) {
void XdsClient::ChannelState::LrsCallState::Reporter::SendReportLocked() { void XdsClient::ChannelState::LrsCallState::Reporter::SendReportLocked() {
// Construct snapshot from all reported stats. // Construct snapshot from all reported stats.
XdsApi::ClusterLoadReportMap snapshot = XdsApi::ClusterLoadReportMap snapshot =
xds_client()->BuildLoadReportSnapshot(); xds_client()->BuildLoadReportSnapshot(parent_->cluster_names_);
// Skip client load report if the counters were all zero in the last // Skip client load report if the counters were all zero in the last
// report and they are still zero in this one. // report and they are still zero in this one.
const bool old_val = last_report_counters_were_zero_; const bool old_val = last_report_counters_were_zero_;
@ -1455,6 +1460,12 @@ void XdsClient::ChannelState::LrsCallState::Reporter::OnReportDoneLocked(
Reporter* self = static_cast<Reporter*>(arg); Reporter* self = static_cast<Reporter*>(arg);
grpc_byte_buffer_destroy(self->parent_->send_message_payload_); grpc_byte_buffer_destroy(self->parent_->send_message_payload_);
self->parent_->send_message_payload_ = nullptr; self->parent_->send_message_payload_ = nullptr;
// If there are no more registered stats to report, cancel the call.
if (self->xds_client()->load_report_map_.empty()) {
self->parent_->chand()->StopLrsCall();
self->Unref(DEBUG_LOCATION, "Reporter+report_done+no_more_reporters");
return;
}
if (error != GRPC_ERROR_NONE || !self->IsCurrentReporterOnCall()) { if (error != GRPC_ERROR_NONE || !self->IsCurrentReporterOnCall()) {
// If this reporter is no longer the current one on the call, the reason // If this reporter is no longer the current one on the call, the reason
// might be that it was orphaned for a new one due to config update. // might be that it was orphaned for a new one due to config update.
@ -1510,7 +1521,8 @@ XdsClient::ChannelState::LrsCallState::LrsCallState(
grpc_op* op = ops; grpc_op* op = ops;
op->op = GRPC_OP_SEND_INITIAL_METADATA; op->op = GRPC_OP_SEND_INITIAL_METADATA;
op->data.send_initial_metadata.count = 0; op->data.send_initial_metadata.count = 0;
op->flags = 0; op->flags = GRPC_INITIAL_METADATA_WAIT_FOR_READY |
GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET;
op->reserved = nullptr; op->reserved = nullptr;
op++; op++;
// Op: send request message. // Op: send request message.
@ -1608,13 +1620,6 @@ void XdsClient::ChannelState::LrsCallState::MaybeStartReportingLocked() {
Ref(DEBUG_LOCATION, "LRS+load_report+start"), load_reporting_interval_); Ref(DEBUG_LOCATION, "LRS+load_report+start"), load_reporting_interval_);
} }
bool XdsClient::ChannelState::LrsCallState::ShouldSendLoadReports(
const StringView& cluster_name) const {
// Only send load reports for the clusters that are asked for by the LRS
// server.
return cluster_names_.find(std::string(cluster_name)) != cluster_names_.end();
}
void XdsClient::ChannelState::LrsCallState::OnInitialRequestSent( void XdsClient::ChannelState::LrsCallState::OnInitialRequestSent(
void* arg, grpc_error* error) { void* arg, grpc_error* error) {
LrsCallState* lrs_calld = static_cast<LrsCallState*>(arg); LrsCallState* lrs_calld = static_cast<LrsCallState*>(arg);
@ -1961,19 +1966,14 @@ void XdsClient::RemoveClusterDropStats(
LoadReportState& load_report_state = load_report_it->second; LoadReportState& load_report_state = load_report_it->second;
// TODO(roth): When we add support for direct federation, use the // TODO(roth): When we add support for direct federation, use the
// server name specified in lrs_server. // server name specified in lrs_server.
// TODO(roth): In principle, we should try to send a final load report
// containing whatever final stats have been accumulated since the
// last load report.
auto it = load_report_state.drop_stats.find(cluster_drop_stats); auto it = load_report_state.drop_stats.find(cluster_drop_stats);
if (it != load_report_state.drop_stats.end()) { if (it != load_report_state.drop_stats.end()) {
load_report_state.drop_stats.erase(it); // Record final drop stats in deleted_drop_stats, which will be
if (load_report_state.drop_stats.empty() && // added to the next load report.
load_report_state.locality_stats.empty()) { for (const auto& p : cluster_drop_stats->GetSnapshotAndReset()) {
load_report_map_.erase(load_report_it); load_report_state.deleted_drop_stats[p.first] += p.second;
if (chand_ != nullptr && load_report_map_.empty()) {
chand_->StopLrsCall();
}
} }
load_report_state.drop_stats.erase(it);
} }
} }
@ -1994,7 +1994,7 @@ RefCountedPtr<XdsClusterLocalityStats> XdsClient::AddClusterLocalityStats(
Ref(DEBUG_LOCATION, "LocalityStats"), lrs_server, Ref(DEBUG_LOCATION, "LocalityStats"), lrs_server,
it->first.first /*cluster_name*/, it->first.second /*eds_service_name*/, it->first.first /*cluster_name*/, it->first.second /*eds_service_name*/,
locality); locality);
it->second.locality_stats[std::move(locality)].insert( it->second.locality_stats[std::move(locality)].locality_stats.insert(
cluster_locality_stats.get()); cluster_locality_stats.get());
chand_->MaybeStartLrsCall(); chand_->MaybeStartLrsCall();
return cluster_locality_stats; return cluster_locality_stats;
@ -2010,25 +2010,16 @@ void XdsClient::RemoveClusterLocalityStats(
LoadReportState& load_report_state = load_report_it->second; LoadReportState& load_report_state = load_report_it->second;
// TODO(roth): When we add support for direct federation, use the // TODO(roth): When we add support for direct federation, use the
// server name specified in lrs_server. // server name specified in lrs_server.
// TODO(roth): In principle, we should try to send a final load report
// containing whatever final stats have been accumulated since the
// last load report.
auto locality_it = load_report_state.locality_stats.find(locality); auto locality_it = load_report_state.locality_stats.find(locality);
if (locality_it == load_report_state.locality_stats.end()) return; if (locality_it == load_report_state.locality_stats.end()) return;
auto& locality_set = locality_it->second; auto& locality_set = locality_it->second.locality_stats;
auto it = locality_set.find(cluster_locality_stats); auto it = locality_set.find(cluster_locality_stats);
if (it != locality_set.end()) { if (it != locality_set.end()) {
// Record final snapshot in deleted_locality_stats, which will be
// added to the next load report.
locality_it->second.deleted_locality_stats.emplace_back(
cluster_locality_stats->GetSnapshotAndReset());
locality_set.erase(it); locality_set.erase(it);
if (locality_set.empty()) {
load_report_state.locality_stats.erase(locality_it);
if (load_report_state.locality_stats.empty() &&
load_report_state.drop_stats.empty()) {
load_report_map_.erase(load_report_it);
if (chand_ != nullptr && load_report_map_.empty()) {
chand_->StopLrsCall();
}
}
}
} }
} }
@ -2057,32 +2048,70 @@ grpc_error* XdsClient::CreateServiceConfig(
return error; return error;
} }
XdsApi::ClusterLoadReportMap XdsClient::BuildLoadReportSnapshot() { XdsApi::ClusterLoadReportMap XdsClient::BuildLoadReportSnapshot(
const std::set<std::string>& clusters) {
XdsApi::ClusterLoadReportMap snapshot_map; XdsApi::ClusterLoadReportMap snapshot_map;
for (auto& p : load_report_map_) { for (auto load_report_it = load_report_map_.begin();
const auto& cluster_key = p.first; // cluster and EDS service name load_report_it != load_report_map_.end();) {
LoadReportState& load_report = p.second; // Cluster key is cluster and EDS service name.
XdsApi::ClusterLoadReport& snapshot = snapshot_map[cluster_key]; const auto& cluster_key = load_report_it->first;
LoadReportState& load_report = load_report_it->second;
// If the CDS response for a cluster indicates to use LRS but the
// LRS server does not say that it wants reports for this cluster,
// then we'll have stats objects here whose data we're not going to
// include in the load report. However, we still need to clear out
// the data from the stats objects, so that if the LRS server starts
// asking for the data in the future, we don't incorrectly include
// data from previous reporting intervals in that future report.
const bool record_stats =
clusters.find(cluster_key.first) != clusters.end();
XdsApi::ClusterLoadReport snapshot;
// Aggregate drop stats. // Aggregate drop stats.
snapshot.dropped_requests = std::move(load_report.deleted_drop_stats);
for (auto& drop_stats : load_report.drop_stats) { for (auto& drop_stats : load_report.drop_stats) {
for (const auto& p : drop_stats->GetSnapshotAndReset()) { for (const auto& p : drop_stats->GetSnapshotAndReset()) {
snapshot.dropped_requests[p.first] += p.second; snapshot.dropped_requests[p.first] += p.second;
} }
} }
// Aggregate locality stats. // Aggregate locality stats.
for (auto& p : load_report.locality_stats) { for (auto it = load_report.locality_stats.begin();
XdsLocalityName* locality_name = p.first.get(); it != load_report.locality_stats.end();) {
auto& locality_stats_set = p.second; const RefCountedPtr<XdsLocalityName>& locality_name = it->first;
auto& locality_state = it->second;
XdsClusterLocalityStats::Snapshot& locality_snapshot = XdsClusterLocalityStats::Snapshot& locality_snapshot =
snapshot.locality_stats[locality_name]; snapshot.locality_stats[locality_name];
for (auto& locality_stats : locality_stats_set) { for (auto& locality_stats : locality_state.locality_stats) {
locality_snapshot += locality_stats->GetSnapshotAndReset(); locality_snapshot += locality_stats->GetSnapshotAndReset();
} }
// Add final snapshots from recently deleted locality stats objects.
for (auto& deleted_locality_stats :
locality_state.deleted_locality_stats) {
locality_snapshot += deleted_locality_stats;
}
locality_state.deleted_locality_stats.clear();
// If the only thing left in this entry was final snapshots from
// deleted locality stats objects, remove the entry.
if (locality_state.locality_stats.empty()) {
it = load_report.locality_stats.erase(it);
} else {
++it;
}
}
if (record_stats) {
// Compute load report interval.
const grpc_millis now = ExecCtx::Get()->Now();
snapshot.load_report_interval = now - load_report.last_report_time;
load_report.last_report_time = now;
// Record snapshot.
snapshot_map[cluster_key] = std::move(snapshot);
}
// If the only thing left in this entry was final snapshots from
// deleted stats objects, remove the entry.
if (load_report.locality_stats.empty() && load_report.drop_stats.empty()) {
load_report_it = load_report_map_.erase(load_report_it);
} else {
++load_report_it;
} }
// Compute load report interval.
const grpc_millis now = ExecCtx::Get()->Now();
snapshot.load_report_interval = now - load_report.last_report_time;
load_report.last_report_time = now;
} }
return snapshot_map; return snapshot_map;
} }

@ -21,13 +21,14 @@
#include <set> #include <set>
#include "absl/types/optional.h"
#include "src/core/ext/filters/client_channel/service_config.h" #include "src/core/ext/filters/client_channel/service_config.h"
#include "src/core/ext/filters/client_channel/xds/xds_api.h" #include "src/core/ext/filters/client_channel/xds/xds_api.h"
#include "src/core/ext/filters/client_channel/xds/xds_bootstrap.h" #include "src/core/ext/filters/client_channel/xds/xds_bootstrap.h"
#include "src/core/ext/filters/client_channel/xds/xds_client_stats.h" #include "src/core/ext/filters/client_channel/xds/xds_client_stats.h"
#include "src/core/lib/gprpp/map.h" #include "src/core/lib/gprpp/map.h"
#include "src/core/lib/gprpp/memory.h" #include "src/core/lib/gprpp/memory.h"
#include "src/core/lib/gprpp/optional.h"
#include "src/core/lib/gprpp/orphanable.h" #include "src/core/lib/gprpp/orphanable.h"
#include "src/core/lib/gprpp/ref_counted.h" #include "src/core/lib/gprpp/ref_counted.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h" #include "src/core/lib/gprpp/ref_counted_ptr.h"
@ -208,8 +209,14 @@ class XdsClient : public InternallyRefCounted<XdsClient> {
}; };
struct LoadReportState { struct LoadReportState {
struct LocalityState {
std::set<XdsClusterLocalityStats*> locality_stats;
std::vector<XdsClusterLocalityStats::Snapshot> deleted_locality_stats;
};
std::set<XdsClusterDropStats*> drop_stats; std::set<XdsClusterDropStats*> drop_stats;
std::map<RefCountedPtr<XdsLocalityName>, std::set<XdsClusterLocalityStats*>, XdsClusterDropStats::DroppedRequestsMap deleted_drop_stats;
std::map<RefCountedPtr<XdsLocalityName>, LocalityState,
XdsLocalityName::Less> XdsLocalityName::Less>
locality_stats; locality_stats;
grpc_millis last_report_time = ExecCtx::Get()->Now(); grpc_millis last_report_time = ExecCtx::Get()->Now();
@ -222,7 +229,8 @@ class XdsClient : public InternallyRefCounted<XdsClient> {
const std::string& cluster_name, const std::string& cluster_name,
RefCountedPtr<ServiceConfig>* service_config) const; RefCountedPtr<ServiceConfig>* service_config) const;
XdsApi::ClusterLoadReportMap BuildLoadReportSnapshot(); XdsApi::ClusterLoadReportMap BuildLoadReportSnapshot(
const std::set<std::string>& clusters);
// Channel arg vtable functions. // Channel arg vtable functions.
static void* ChannelArgCopy(void* p); static void* ChannelArgCopy(void* p);
@ -246,8 +254,9 @@ class XdsClient : public InternallyRefCounted<XdsClient> {
// The channel for communicating with the xds server. // The channel for communicating with the xds server.
OrphanablePtr<ChannelState> chand_; OrphanablePtr<ChannelState> chand_;
std::string route_config_name_; absl::optional<XdsApi::LdsUpdate> lds_result_;
std::string cluster_name_; absl::optional<XdsApi::RdsUpdate> rds_result_;
// One entry for each watched CDS resource. // One entry for each watched CDS resource.
std::map<std::string /*cluster_name*/, ClusterState> cluster_map_; std::map<std::string /*cluster_name*/, ClusterState> cluster_map_;
// One entry for each watched EDS resource. // One entry for each watched EDS resource.

@ -27,6 +27,19 @@
#include "src/core/lib/gprpp/ref_counted.h" #include "src/core/lib/gprpp/ref_counted.h"
#include "src/core/lib/security/security_connector/ssl_utils.h" #include "src/core/lib/security/security_connector/ssl_utils.h"
struct grpc_tls_error_details
: public grpc_core::RefCounted<grpc_tls_error_details> {
public:
grpc_tls_error_details() : error_details_("") {}
void set_error_details(const char* err_details) {
error_details_ = err_details;
}
const std::string& error_details() { return error_details_; }
private:
std::string error_details_;
};
/** TLS key materials config. **/ /** TLS key materials config. **/
struct grpc_tls_key_materials_config struct grpc_tls_key_materials_config
: public grpc_core::RefCounted<grpc_tls_key_materials_config> { : public grpc_core::RefCounted<grpc_tls_key_materials_config> {
@ -93,8 +106,8 @@ struct grpc_tls_credential_reload_config
gpr_log(GPR_ERROR, "schedule API is nullptr"); gpr_log(GPR_ERROR, "schedule API is nullptr");
if (arg != nullptr) { if (arg != nullptr) {
arg->status = GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL; arg->status = GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL;
arg->error_details = arg->error_details->set_error_details(
gpr_strdup("schedule API in credential reload config is nullptr"); "schedule API in credential reload config is nullptr");
} }
return 1; return 1;
} }
@ -108,8 +121,8 @@ struct grpc_tls_credential_reload_config
gpr_log(GPR_ERROR, "cancel API is nullptr."); gpr_log(GPR_ERROR, "cancel API is nullptr.");
if (arg != nullptr) { if (arg != nullptr) {
arg->status = GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL; arg->status = GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL;
arg->error_details = arg->error_details->set_error_details(
gpr_strdup("cancel API in credential reload config is nullptr"); "cancel API in credential reload config is nullptr");
} }
return; return;
} }
@ -169,7 +182,7 @@ struct grpc_tls_server_authorization_check_config
gpr_log(GPR_ERROR, "schedule API is nullptr"); gpr_log(GPR_ERROR, "schedule API is nullptr");
if (arg != nullptr) { if (arg != nullptr) {
arg->status = GRPC_STATUS_NOT_FOUND; arg->status = GRPC_STATUS_NOT_FOUND;
arg->error_details = gpr_strdup( arg->error_details->set_error_details(
"schedule API in server authorization check config is nullptr"); "schedule API in server authorization check config is nullptr");
} }
return 1; return 1;
@ -185,7 +198,7 @@ struct grpc_tls_server_authorization_check_config
gpr_log(GPR_ERROR, "cancel API is nullptr."); gpr_log(GPR_ERROR, "cancel API is nullptr.");
if (arg != nullptr) { if (arg != nullptr) {
arg->status = GRPC_STATUS_NOT_FOUND; arg->status = GRPC_STATUS_NOT_FOUND;
arg->error_details = gpr_strdup( arg->error_details->set_error_details(
"schedule API in server authorization check config is nullptr"); "schedule API in server authorization check config is nullptr");
} }
return; return;

@ -88,6 +88,7 @@ grpc_status_code TlsFetchKeyMaterials(
if (credential_reload_config != nullptr) { if (credential_reload_config != nullptr) {
grpc_tls_credential_reload_arg* arg = new grpc_tls_credential_reload_arg(); grpc_tls_credential_reload_arg* arg = new grpc_tls_credential_reload_arg();
arg->key_materials_config = key_materials_config.get(); arg->key_materials_config = key_materials_config.get();
arg->error_details = new grpc_tls_error_details();
int result = credential_reload_config->Schedule(arg); int result = credential_reload_config->Schedule(arg);
if (result) { if (result) {
/** Credential reloading is performed async. This is not yet supported. /** Credential reloading is performed async. This is not yet supported.
@ -105,13 +106,13 @@ grpc_status_code TlsFetchKeyMaterials(
} else if (arg->status == GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL) { } else if (arg->status == GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL) {
gpr_log(GPR_ERROR, "Credential reload failed with an error:"); gpr_log(GPR_ERROR, "Credential reload failed with an error:");
if (arg->error_details != nullptr) { if (arg->error_details != nullptr) {
gpr_log(GPR_ERROR, "%s", arg->error_details); gpr_log(GPR_ERROR, "%s", arg->error_details->error_details().c_str());
} }
reload_status = reload_status =
is_key_materials_empty ? GRPC_STATUS_INTERNAL : GRPC_STATUS_OK; is_key_materials_empty ? GRPC_STATUS_INTERNAL : GRPC_STATUS_OK;
} }
} }
gpr_free((void*)arg->error_details); delete arg->error_details;
/** If the credential reload config was constructed via a wrapped language, /** If the credential reload config was constructed via a wrapped language,
* then |arg->context| and |arg->destroy_context| will not be nullptr. In * then |arg->context| and |arg->destroy_context| will not be nullptr. In
* this case, we must destroy |arg->context|, which stores the wrapped * this case, we must destroy |arg->context|, which stores the wrapped
@ -406,14 +407,14 @@ grpc_error* TlsChannelSecurityConnector::ProcessServerAuthorizationCheckResult(
gpr_asprintf(&msg, gpr_asprintf(&msg,
"Server authorization check is cancelled by the caller with " "Server authorization check is cancelled by the caller with "
"error: %s", "error: %s",
arg->error_details); arg->error_details->error_details().c_str());
error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
} else if (arg->status == GRPC_STATUS_OK) { } else if (arg->status == GRPC_STATUS_OK) {
/* Server authorization check completed successfully but returned check /* Server authorization check completed successfully but returned check
* failure. */ * failure. */
if (!arg->success) { if (!arg->success) {
gpr_asprintf(&msg, "Server authorization check failed with error: %s", gpr_asprintf(&msg, "Server authorization check failed with error: %s",
arg->error_details); arg->error_details->error_details().c_str());
error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
} }
/* Server authorization check did not complete correctly. */ /* Server authorization check did not complete correctly. */
@ -421,7 +422,7 @@ grpc_error* TlsChannelSecurityConnector::ProcessServerAuthorizationCheckResult(
gpr_asprintf( gpr_asprintf(
&msg, &msg,
"Server authorization check did not finish correctly with error: %s", "Server authorization check did not finish correctly with error: %s",
arg->error_details); arg->error_details->error_details().c_str());
error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
} }
gpr_free(msg); gpr_free(msg);
@ -433,6 +434,7 @@ TlsChannelSecurityConnector::ServerAuthorizationCheckArgCreate(
void* user_data) { void* user_data) {
grpc_tls_server_authorization_check_arg* arg = grpc_tls_server_authorization_check_arg* arg =
new grpc_tls_server_authorization_check_arg(); new grpc_tls_server_authorization_check_arg();
arg->error_details = new grpc_tls_error_details();
arg->cb = ServerAuthorizationCheckDone; arg->cb = ServerAuthorizationCheckDone;
arg->cb_user_data = user_data; arg->cb_user_data = user_data;
arg->status = GRPC_STATUS_OK; arg->status = GRPC_STATUS_OK;
@ -447,7 +449,7 @@ void TlsChannelSecurityConnector::ServerAuthorizationCheckArgDestroy(
gpr_free((void*)arg->target_name); gpr_free((void*)arg->target_name);
gpr_free((void*)arg->peer_cert); gpr_free((void*)arg->peer_cert);
if (arg->peer_cert_full_chain) gpr_free((void*)arg->peer_cert_full_chain); if (arg->peer_cert_full_chain) gpr_free((void*)arg->peer_cert_full_chain);
gpr_free((void*)arg->error_details); delete arg->error_details;
if (arg->destroy_context != nullptr) { if (arg->destroy_context != nullptr) {
arg->destroy_context(arg->context); arg->destroy_context(arg->context);
} }

@ -157,12 +157,12 @@ You can find out how to build and run our simplest gRPC C++ example in our
For more detailed documentation on using gRPC in C++ , see our main For more detailed documentation on using gRPC in C++ , see our main
documentation site at [grpc.io](https://grpc.io), specifically: documentation site at [grpc.io](https://grpc.io), specifically:
* [Overview](https://grpc.io/docs/): An introduction to gRPC with a simple * [Overview](https://grpc.io/docs): An introduction to gRPC with a simple
Hello World example in all our supported languages, including C++. Hello World example in all our supported languages, including C++.
* [gRPC Basics - C++](https://grpc.io/docs/tutorials/basic/c.html): * [gRPC Basics - C++](https://grpc.io/docs/tutorials/basic/cpp):
A tutorial that steps you through creating a simple gRPC C++ example A tutorial that steps you through creating a simple gRPC C++ example
application. application.
* [Asynchronous Basics - C++](https://grpc.io/docs/tutorials/async/helloasync-cpp.html): * [Asynchronous Basics - C++](https://grpc.io/docs/tutorials/async/helloasync-cpp):
A tutorial that shows you how to use gRPC C++'s asynchronous/non-blocking A tutorial that shows you how to use gRPC C++'s asynchronous/non-blocking
APIs. APIs.

@ -68,8 +68,7 @@ grpc_ssl_certificate_config_reload_status TlsCredentialReloadArg::status()
} }
grpc::string TlsCredentialReloadArg::error_details() const { grpc::string TlsCredentialReloadArg::error_details() const {
grpc::string cpp_error_details(c_arg_->error_details); return c_arg_->error_details->error_details();
return cpp_error_details;
} }
void TlsCredentialReloadArg::set_cb_user_data(void* cb_user_data) { void TlsCredentialReloadArg::set_cb_user_data(void* cb_user_data) {
@ -159,7 +158,7 @@ void TlsCredentialReloadArg::set_status(
void TlsCredentialReloadArg::set_error_details( void TlsCredentialReloadArg::set_error_details(
const grpc::string& error_details) { const grpc::string& error_details) {
c_arg_->error_details = gpr_strdup(error_details.c_str()); c_arg_->error_details->set_error_details(error_details.c_str());
} }
void TlsCredentialReloadArg::OnCredentialReloadDoneCallback() { void TlsCredentialReloadArg::OnCredentialReloadDoneCallback() {
@ -221,8 +220,7 @@ grpc_status_code TlsServerAuthorizationCheckArg::status() const {
} }
grpc::string TlsServerAuthorizationCheckArg::error_details() const { grpc::string TlsServerAuthorizationCheckArg::error_details() const {
grpc::string cpp_error_details(c_arg_->error_details); return c_arg_->error_details->error_details();
return cpp_error_details;
} }
void TlsServerAuthorizationCheckArg::set_cb_user_data(void* cb_user_data) { void TlsServerAuthorizationCheckArg::set_cb_user_data(void* cb_user_data) {
@ -254,7 +252,7 @@ void TlsServerAuthorizationCheckArg::set_status(grpc_status_code status) {
void TlsServerAuthorizationCheckArg::set_error_details( void TlsServerAuthorizationCheckArg::set_error_details(
const grpc::string& error_details) { const grpc::string& error_details) {
c_arg_->error_details = gpr_strdup(error_details.c_str()); c_arg_->error_details->set_error_details(error_details.c_str());
} }
void TlsServerAuthorizationCheckArg::OnServerAuthorizationCheckDoneCallback() { void TlsServerAuthorizationCheckArg::OnServerAuthorizationCheckDoneCallback() {

@ -86,7 +86,7 @@ if test "$PHP_GRPC" != "no"; then
PHP_NEW_EXTENSION(grpc, byte_buffer.c call.c call_credentials.c channel.c \ PHP_NEW_EXTENSION(grpc, byte_buffer.c call.c call_credentials.c channel.c \
channel_credentials.c completion_queue.c timeval.c server.c \ channel_credentials.c completion_queue.c timeval.c server.c \
server_credentials.c php_grpc.c, $ext_shared, , -Wall -Werror -std=c11) server_credentials.c php_grpc.c, $ext_shared, , -Wall -Werror -std=c11 -DGRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK=1)
fi fi
if test "$PHP_COVERAGE" = "yes"; then if test "$PHP_COVERAGE" = "yes"; then

@ -1076,6 +1076,14 @@ class Channel(six.with_metaclass(abc.ABCMeta)):
""" """
raise NotImplementedError() raise NotImplementedError()
def __enter__(self):
"""Enters the runtime context related to the channel object."""
raise NotImplementedError()
def __exit__(self, exc_type, exc_val, exc_tb):
"""Exits the runtime context related to the channel object."""
raise NotImplementedError()
########################## Service-Side Context ############################## ########################## Service-Side Context ##############################

@ -2,6 +2,13 @@ package(default_visibility = ["//visibility:public"])
load("//bazel:cython_library.bzl", "pyx_library") load("//bazel:cython_library.bzl", "pyx_library")
genrule(
name = "copy_roots_pem",
srcs = ["//:etc/roots.pem"],
outs = ["_credentials/roots.pem"],
cmd = "cp $(SRCS) $(@)",
)
pyx_library( pyx_library(
name = "cygrpc", name = "cygrpc",
srcs = glob([ srcs = glob([
@ -9,6 +16,7 @@ pyx_library(
"cygrpc.pxd", "cygrpc.pxd",
"cygrpc.pyx", "cygrpc.pyx",
]), ]),
data = [":copy_roots_pem"],
deps = [ deps = [
"//:grpc", "//:grpc",
], ],

@ -125,7 +125,7 @@ cdef class _AioCall(GrpcCallWrapper):
if credentials is not None: if credentials is not None:
set_credentials_error = grpc_call_set_credentials(self.call, credentials.c()) set_credentials_error = grpc_call_set_credentials(self.call, credentials.c())
if set_credentials_error != GRPC_CALL_OK: if set_credentials_error != GRPC_CALL_OK:
raise Exception("Credentials couldn't have been set") raise InternalError("Credentials couldn't have been set: {0}".format(set_credentials_error))
grpc_slice_unref(method_slice) grpc_slice_unref(method_slice)
@ -178,7 +178,7 @@ cdef class _AioCall(GrpcCallWrapper):
def cancel(self, str details): def cancel(self, str details):
"""Cancels the RPC in Core with given RPC status. """Cancels the RPC in Core with given RPC status.
Above abstractions must invoke this method to set Core objects into Above abstractions must invoke this method to set Core objects into
proper state. proper state.
""" """
@ -209,7 +209,7 @@ cdef class _AioCall(GrpcCallWrapper):
def done(self): def done(self):
"""Returns if the RPC call has finished. """Returns if the RPC call has finished.
Checks if the status has been provided, either Checks if the status has been provided, either
because the RPC finished or because was cancelled.. because the RPC finished or because was cancelled..
@ -220,7 +220,7 @@ cdef class _AioCall(GrpcCallWrapper):
def cancelled(self): def cancelled(self):
"""Returns if the RPC was cancelled. """Returns if the RPC was cancelled.
Returns: Returns:
True if the RPC was cancelled. True if the RPC was cancelled.
""" """
@ -231,7 +231,7 @@ cdef class _AioCall(GrpcCallWrapper):
async def status(self): async def status(self):
"""Returns the status of the RPC call. """Returns the status of the RPC call.
It returns the finshed status of the RPC. If the RPC It returns the finshed status of the RPC. If the RPC
has not finished yet this function will wait until the RPC has not finished yet this function will wait until the RPC
gets finished. gets finished.
@ -254,7 +254,7 @@ cdef class _AioCall(GrpcCallWrapper):
async def initial_metadata(self): async def initial_metadata(self):
"""Returns the initial metadata of the RPC call. """Returns the initial metadata of the RPC call.
If the initial metadata has not been received yet this function will If the initial metadata has not been received yet this function will
wait until the RPC gets finished. wait until the RPC gets finished.
@ -286,7 +286,7 @@ cdef class _AioCall(GrpcCallWrapper):
bytes request, bytes request,
tuple outbound_initial_metadata): tuple outbound_initial_metadata):
"""Performs a unary unary RPC. """Performs a unary unary RPC.
Args: Args:
request: the serialized requests in bytes. request: the serialized requests in bytes.
outbound_initial_metadata: optional outbound metadata. outbound_initial_metadata: optional outbound metadata.
@ -420,7 +420,7 @@ cdef class _AioCall(GrpcCallWrapper):
tuple outbound_initial_metadata, tuple outbound_initial_metadata,
object metadata_sent_observer): object metadata_sent_observer):
"""Actual implementation of the complete unary-stream call. """Actual implementation of the complete unary-stream call.
Needs to pay extra attention to the raise mechanism. If we want to Needs to pay extra attention to the raise mechanism. If we want to
propagate the final status exception, then we have to raise it. propagate the final status exception, then we have to raise it.
Othersize, it would end normally and raise `StopAsyncIteration()`. Othersize, it would end normally and raise `StopAsyncIteration()`.
@ -490,7 +490,7 @@ cdef class _AioCall(GrpcCallWrapper):
outbound_initial_metadata, outbound_initial_metadata,
self._send_initial_metadata_flags, self._send_initial_metadata_flags,
self._loop) self._loop)
# Notify upper level that sending messages are allowed now. # Notify upper level that sending messages are allowed now.
metadata_sent_observer() metadata_sent_observer()
# Receives initial metadata. # Receives initial metadata.

@ -24,3 +24,4 @@ cdef class AioChannel:
object loop object loop
bytes _target bytes _target
AioChannelStatus _status AioChannelStatus _status
bint _is_secure

@ -36,11 +36,13 @@ cdef class AioChannel:
self._status = AIO_CHANNEL_STATUS_READY self._status = AIO_CHANNEL_STATUS_READY
if credentials is None: if credentials is None:
self._is_secure = False
self.channel = grpc_insecure_channel_create( self.channel = grpc_insecure_channel_create(
<char *>target, <char *>target,
channel_args.c_args(), channel_args.c_args(),
NULL) NULL)
else: else:
self._is_secure = True
self.channel = grpc_secure_channel_create( self.channel = grpc_secure_channel_create(
<grpc_channel_credentials *> credentials.c(), <grpc_channel_credentials *> credentials.c(),
<char *>target, <char *>target,
@ -122,6 +124,9 @@ cdef class AioChannel:
cdef CallCredentials cython_call_credentials cdef CallCredentials cython_call_credentials
if python_call_credentials is not None: if python_call_credentials is not None:
if not self._is_secure:
raise UsageError("Call credentials are only valid on secure channels")
cython_call_credentials = python_call_credentials._credentials cython_call_credentials = python_call_credentials._credentials
else: else:
cython_call_credentials = None cython_call_credentials = None

@ -23,10 +23,10 @@ cdef class _AioState:
cdef grpc_completion_queue *global_completion_queue() cdef grpc_completion_queue *global_completion_queue()
cdef init_grpc_aio() cpdef init_grpc_aio()
cdef shutdown_grpc_aio() cpdef shutdown_grpc_aio()
cdef extern from "src/core/lib/iomgr/timer_manager.h": cdef extern from "src/core/lib/iomgr/timer_manager.h":

@ -114,9 +114,9 @@ cdef _actual_aio_shutdown():
raise ValueError('Unsupported engine type [%s]' % _global_aio_state.engine) raise ValueError('Unsupported engine type [%s]' % _global_aio_state.engine)
cdef init_grpc_aio(): cpdef init_grpc_aio():
"""Initialis the gRPC AsyncIO module. """Initializes the gRPC AsyncIO module.
Expected to be invoked on critical class constructors. Expected to be invoked on critical class constructors.
E.g., AioChannel, AioServer. E.g., AioChannel, AioServer.
""" """
@ -126,7 +126,7 @@ cdef init_grpc_aio():
_actual_aio_initialization() _actual_aio_initialization()
cdef shutdown_grpc_aio(): cpdef shutdown_grpc_aio():
"""Shuts down the gRPC AsyncIO module. """Shuts down the gRPC AsyncIO module.
Expected to be invoked on critical class destructors. Expected to be invoked on critical class destructors.

@ -212,7 +212,18 @@ cdef void asyncio_run_loop(size_t timeout_ms) with gil:
pass pass
def _auth_plugin_callback_wrapper(object cb,
str service_url,
str method_name,
object callback):
asyncio.get_event_loop().call_soon(cb, service_url, method_name, callback)
def install_asyncio_iomgr(): def install_asyncio_iomgr():
# Auth plugins invoke user provided logic in another thread by default. We
# need to override that behavior by registering the call to the event loop.
set_async_callback_func(_auth_plugin_callback_wrapper)
asyncio_resolver_vtable.resolve = asyncio_resolve asyncio_resolver_vtable.resolve = asyncio_resolve
asyncio_resolver_vtable.resolve_async = asyncio_resolve_async asyncio_resolver_vtable.resolve_async = asyncio_resolve_async

@ -34,12 +34,14 @@ cdef class CallCredentials:
raise NotImplementedError() raise NotImplementedError()
cdef int _get_metadata( cdef int _get_metadata(void *state,
void *state, grpc_auth_metadata_context context, grpc_auth_metadata_context context,
grpc_credentials_plugin_metadata_cb cb, void *user_data, grpc_credentials_plugin_metadata_cb cb,
grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX], void *user_data,
size_t *num_creds_md, grpc_status_code *status, grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
const char **error_details) except * with gil: size_t *num_creds_md,
grpc_status_code *status,
const char **error_details) except * with gil:
cdef size_t metadata_count cdef size_t metadata_count
cdef grpc_metadata *c_metadata cdef grpc_metadata *c_metadata
def callback(metadata, grpc_status_code status, bytes error_details): def callback(metadata, grpc_status_code status, bytes error_details):

@ -20,7 +20,8 @@ created. AsyncIO doesn't provide thread safety for most of its APIs.
from typing import Any, Optional, Sequence, Tuple from typing import Any, Optional, Sequence, Tuple
import grpc import grpc
from grpc._cython.cygrpc import (EOF, AbortError, BaseError, InternalError, from grpc._cython.cygrpc import (init_grpc_aio, shutdown_grpc_aio, EOF,
AbortError, BaseError, InternalError,
UsageError) UsageError)
from ._base_call import (Call, RpcContext, StreamStreamCall, StreamUnaryCall, from ._base_call import (Call, RpcContext, StreamStreamCall, StreamUnaryCall,
@ -39,6 +40,8 @@ from ._channel import insecure_channel, secure_channel
################################### __all__ ################################# ################################### __all__ #################################
__all__ = ( __all__ = (
'init_grpc_aio',
'shutdown_grpc_aio',
'AioRpcError', 'AioRpcError',
'RpcContext', 'RpcContext',
'Call', 'Call',

@ -56,6 +56,7 @@ py_binary(
python_version = "PY3", python_version = "PY3",
deps = [ deps = [
"//src/python/grpcio/grpc:grpcio", "//src/python/grpcio/grpc:grpcio",
"//src/python/grpcio_tests/tests/interop:resources",
"//src/python/grpcio_tests/tests/interop:server", "//src/python/grpcio_tests/tests/interop:server",
"//src/python/grpcio_tests/tests_aio/unit:_test_server", "//src/python/grpcio_tests/tests_aio/unit:_test_server",
], ],
@ -70,5 +71,6 @@ py_binary(
":methods", ":methods",
"//src/python/grpcio/grpc:grpcio", "//src/python/grpcio/grpc:grpcio",
"//src/python/grpcio_tests/tests/interop:client", "//src/python/grpcio_tests/tests/interop:client",
"//src/python/grpcio_tests/tests/interop:resources",
], ],
) )

@ -19,9 +19,11 @@
"unit.compression_test.TestCompression", "unit.compression_test.TestCompression",
"unit.connectivity_test.TestConnectivityState", "unit.connectivity_test.TestConnectivityState",
"unit.done_callback_test.TestDoneCallback", "unit.done_callback_test.TestDoneCallback",
"unit.init_test.TestInsecureChannel", "unit.init_test.TestChannel",
"unit.init_test.TestSecureChannel",
"unit.metadata_test.TestMetadata", "unit.metadata_test.TestMetadata",
"unit.secure_call_test.TestStreamStreamSecureCall",
"unit.secure_call_test.TestUnaryStreamSecureCall",
"unit.secure_call_test.TestUnaryUnarySecureCall",
"unit.server_interceptor_test.TestServerInterceptor", "unit.server_interceptor_test.TestServerInterceptor",
"unit.server_test.TestServer", "unit.server_test.TestServer",
"unit.timeout_test.TestTimeout", "unit.timeout_test.TestTimeout",

@ -41,6 +41,7 @@ py_library(
"//src/proto/grpc/testing:py_messages_proto", "//src/proto/grpc/testing:py_messages_proto",
"//src/proto/grpc/testing:test_py_pb2_grpc", "//src/proto/grpc/testing:test_py_pb2_grpc",
"//src/python/grpcio/grpc:grpcio", "//src/python/grpcio/grpc:grpcio",
"//src/python/grpcio_tests/tests/unit:resources",
], ],
) )
@ -76,6 +77,7 @@ _FLAKY_TESTS = [
"//src/proto/grpc/testing:benchmark_service_py_pb2_grpc", "//src/proto/grpc/testing:benchmark_service_py_pb2_grpc",
"//src/proto/grpc/testing:py_messages_proto", "//src/proto/grpc/testing:py_messages_proto",
"//src/python/grpcio/grpc:grpcio", "//src/python/grpcio/grpc:grpcio",
"//src/python/grpcio_tests/tests/unit:resources",
"//src/python/grpcio_tests/tests/unit/framework/common", "//src/python/grpcio_tests/tests/unit/framework/common",
"@six", "@six",
], ],

@ -64,3 +64,6 @@ class AioTestBase(unittest.TestCase):
return _async_to_sync_decorator(attr, self._TEST_LOOP) return _async_to_sync_decorator(attr, self._TEST_LOOP)
# For other attributes, let them pass. # For other attributes, let them pass.
return attr return attr
aio.init_grpc_aio()

@ -17,6 +17,7 @@ import datetime
import grpc import grpc
from grpc.experimental import aio from grpc.experimental import aio
from tests.unit import resources
from src.proto.grpc.testing import empty_pb2, messages_pb2, test_pb2_grpc from src.proto.grpc.testing import empty_pb2, messages_pb2, test_pb2_grpc
from tests_aio.unit import _constants from tests_aio.unit import _constants
@ -129,8 +130,9 @@ async def start_test_server(port=0,
if secure: if secure:
if server_credentials is None: if server_credentials is None:
server_credentials = grpc.local_server_credentials( server_credentials = grpc.ssl_server_credentials([
grpc.LocalConnectionType.LOCAL_TCP) (resources.private_key(), resources.certificate_chain())
])
port = server.add_secure_port('[::]:%d' % port, server_credentials) port = server.add_secure_port('[::]:%d' % port, server_credentials)
else: else:
port = server.add_insecure_port('[::]:%d' % port) port = server.add_insecure_port('[::]:%d' % port)

@ -14,7 +14,6 @@
"""Tests behavior of the Call classes.""" """Tests behavior of the Call classes."""
import asyncio import asyncio
import datetime
import logging import logging
import unittest import unittest
@ -24,6 +23,8 @@ from grpc.experimental import aio
from src.proto.grpc.testing import messages_pb2, test_pb2_grpc from src.proto.grpc.testing import messages_pb2, test_pb2_grpc
from tests.unit.framework.common import test_constants from tests.unit.framework.common import test_constants
from tests_aio.unit._test_base import AioTestBase from tests_aio.unit._test_base import AioTestBase
from tests.unit import resources
from tests_aio.unit._test_server import start_test_server from tests_aio.unit._test_server import start_test_server
_NUM_STREAM_RESPONSES = 5 _NUM_STREAM_RESPONSES = 5
@ -55,7 +56,7 @@ class TestUnaryUnaryCall(_MulticallableTestMixin, AioTestBase):
self.assertTrue(str(call) is not None) self.assertTrue(str(call) is not None)
self.assertTrue(repr(call) is not None) self.assertTrue(repr(call) is not None)
response = await call await call
self.assertTrue(str(call) is not None) self.assertTrue(str(call) is not None)
self.assertTrue(repr(call) is not None) self.assertTrue(repr(call) is not None)
@ -202,6 +203,17 @@ class TestUnaryUnaryCall(_MulticallableTestMixin, AioTestBase):
with self.assertRaises(asyncio.CancelledError): with self.assertRaises(asyncio.CancelledError):
await task await task
async def test_passing_credentials_fails_over_insecure_channel(self):
call_credentials = grpc.composite_call_credentials(
grpc.access_token_call_credentials("abc"),
grpc.access_token_call_credentials("def"),
)
with self.assertRaisesRegex(
grpc._cygrpc.UsageError,
"Call credentials are only valid on secure channels"):
self._stub.UnaryCall(messages_pb2.SimpleRequest(),
credentials=call_credentials)
class TestUnaryStreamCall(_MulticallableTestMixin, AioTestBase): class TestUnaryStreamCall(_MulticallableTestMixin, AioTestBase):
@ -410,33 +422,6 @@ class TestUnaryStreamCall(_MulticallableTestMixin, AioTestBase):
with self.assertRaises(asyncio.CancelledError): with self.assertRaises(asyncio.CancelledError):
await task await task
def test_call_credentials(self):
class DummyAuth(grpc.AuthMetadataPlugin):
def __call__(self, context, callback):
signature = context.method_name[::-1]
callback((("test", signature),), None)
async def coro():
server_target, _ = await start_test_server(secure=False) # pylint: disable=unused-variable
async with aio.insecure_channel(server_target) as channel:
hi = channel.unary_unary('/grpc.testing.TestService/UnaryCall',
request_serializer=messages_pb2.
SimpleRequest.SerializeToString,
response_deserializer=messages_pb2.
SimpleResponse.FromString)
call_credentials = grpc.metadata_call_credentials(DummyAuth())
call = hi(messages_pb2.SimpleRequest(),
credentials=call_credentials)
response = await call
self.assertIsInstance(response, messages_pb2.SimpleResponse)
self.assertEqual(await call.code(), grpc.StatusCode.OK)
self.loop.run_until_complete(coro())
async def test_time_remaining(self): async def test_time_remaining(self):
request = messages_pb2.StreamingOutputCallRequest() request = messages_pb2.StreamingOutputCallRequest()
# First message comes back immediately # First message comes back immediately

@ -20,8 +20,14 @@ from grpc.experimental import aio
from tests_aio.unit._test_server import start_test_server from tests_aio.unit._test_server import start_test_server
from tests_aio.unit._test_base import AioTestBase from tests_aio.unit._test_base import AioTestBase
from tests.unit import resources
class TestInsecureChannel(AioTestBase): _PRIVATE_KEY = resources.private_key()
_CERTIFICATE_CHAIN = resources.certificate_chain()
_TEST_ROOT_CERTIFICATES = resources.test_root_certificates()
class TestChannel(AioTestBase):
async def test_insecure_channel(self): async def test_insecure_channel(self):
server_target, _ = await start_test_server() # pylint: disable=unused-variable server_target, _ = await start_test_server() # pylint: disable=unused-variable
@ -29,21 +35,16 @@ class TestInsecureChannel(AioTestBase):
channel = aio.insecure_channel(server_target) channel = aio.insecure_channel(server_target)
self.assertIsInstance(channel, aio.Channel) self.assertIsInstance(channel, aio.Channel)
async def tests_secure_channel(self):
server_target, _ = await start_test_server(secure=True) # pylint: disable=unused-variable
credentials = grpc.ssl_channel_credentials(
root_certificates=_TEST_ROOT_CERTIFICATES,
private_key=_PRIVATE_KEY,
certificate_chain=_CERTIFICATE_CHAIN,
)
secure_channel = aio.secure_channel(server_target, credentials)
class TestSecureChannel(AioTestBase): self.assertIsInstance(secure_channel, aio.Channel)
"""Test a secure channel connected to a secure server"""
def test_secure_channel(self):
async def coro():
server_target, _ = await start_test_server(secure=True) # pylint: disable=unused-variable
credentials = grpc.local_channel_credentials(
grpc.LocalConnectionType.LOCAL_TCP)
secure_channel = aio.secure_channel(server_target, credentials)
self.assertIsInstance(secure_channel, aio.Channel)
self.loop.run_until_complete(coro())
if __name__ == '__main__': if __name__ == '__main__':

@ -0,0 +1,130 @@
# Copyright 2020 The 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.
"""Tests the behaviour of the Call classes under a secure channel."""
import unittest
import logging
import grpc
from grpc.experimental import aio
from src.proto.grpc.testing import messages_pb2, test_pb2_grpc
from tests_aio.unit._test_base import AioTestBase
from tests_aio.unit._test_server import start_test_server
from tests.unit import resources
_SERVER_HOST_OVERRIDE = 'foo.test.google.fr'
_NUM_STREAM_RESPONSES = 5
_RESPONSE_PAYLOAD_SIZE = 42
class _SecureCallMixin:
"""A Mixin to run the call tests over a secure channel."""
async def setUp(self):
server_credentials = grpc.ssl_server_credentials([
(resources.private_key(), resources.certificate_chain())
])
channel_credentials = grpc.ssl_channel_credentials(
resources.test_root_certificates())
self._server_address, self._server = await start_test_server(
secure=True, server_credentials=server_credentials)
channel_options = ((
'grpc.ssl_target_name_override',
_SERVER_HOST_OVERRIDE,
),)
self._channel = aio.secure_channel(self._server_address,
channel_credentials, channel_options)
self._stub = test_pb2_grpc.TestServiceStub(self._channel)
async def tearDown(self):
await self._channel.close()
await self._server.stop(None)
class TestUnaryUnarySecureCall(_SecureCallMixin, AioTestBase):
"""unary_unary Calls made over a secure channel."""
async def test_call_ok_over_secure_channel(self):
call = self._stub.UnaryCall(messages_pb2.SimpleRequest())
response = await call
self.assertIsInstance(response, messages_pb2.SimpleResponse)
self.assertEqual(await call.code(), grpc.StatusCode.OK)
async def test_call_with_credentials(self):
call_credentials = grpc.composite_call_credentials(
grpc.access_token_call_credentials("abc"),
grpc.access_token_call_credentials("def"),
)
call = self._stub.UnaryCall(messages_pb2.SimpleRequest(),
credentials=call_credentials)
response = await call
self.assertIsInstance(response, messages_pb2.SimpleResponse)
class TestUnaryStreamSecureCall(_SecureCallMixin, AioTestBase):
"""unary_stream calls over a secure channel"""
async def test_unary_stream_async_generator_secure(self):
request = messages_pb2.StreamingOutputCallRequest()
request.response_parameters.extend(
messages_pb2.ResponseParameters(size=_RESPONSE_PAYLOAD_SIZE,)
for _ in range(_NUM_STREAM_RESPONSES))
call_credentials = grpc.composite_call_credentials(
grpc.access_token_call_credentials("abc"),
grpc.access_token_call_credentials("def"),
)
call = self._stub.StreamingOutputCall(request,
credentials=call_credentials)
async for response in call:
self.assertIsInstance(response,
messages_pb2.StreamingOutputCallResponse)
self.assertEqual(len(response.payload.body), _RESPONSE_PAYLOAD_SIZE)
self.assertEqual(await call.code(), grpc.StatusCode.OK)
# Prepares the request that stream in a ping-pong manner.
_STREAM_OUTPUT_REQUEST_ONE_RESPONSE = messages_pb2.StreamingOutputCallRequest()
_STREAM_OUTPUT_REQUEST_ONE_RESPONSE.response_parameters.append(
messages_pb2.ResponseParameters(size=_RESPONSE_PAYLOAD_SIZE))
class TestStreamStreamSecureCall(_SecureCallMixin, AioTestBase):
_STREAM_ITERATIONS = 2
async def test_async_generator_secure_channel(self):
async def request_generator():
for _ in range(self._STREAM_ITERATIONS):
yield _STREAM_OUTPUT_REQUEST_ONE_RESPONSE
call_credentials = grpc.composite_call_credentials(
grpc.access_token_call_credentials("abc"),
grpc.access_token_call_credentials("def"),
)
call = self._stub.FullDuplexCall(request_generator(),
credentials=call_credentials)
async for response in call:
self.assertEqual(_RESPONSE_PAYLOAD_SIZE, len(response.payload.body))
self.assertEqual(await call.code(), grpc.StatusCode.OK)
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
unittest.main(verbosity=2)

@ -0,0 +1,12 @@
py_binary(
name = "xds_interop_client",
srcs = ["xds_interop_client.py"],
python_version = "PY3",
deps = [
"//src/proto/grpc/testing:empty_py_pb2",
"//src/proto/grpc/testing:py_messages_proto",
"//src/proto/grpc/testing:py_test_proto",
"//src/proto/grpc/testing:test_py_pb2_grpc",
"//src/python/grpcio/grpc:grpcio",
],
)

@ -0,0 +1,254 @@
# Copyright 2020 The 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.
import argparse
import logging
import signal
import threading
import time
import sys
from typing import DefaultDict, Dict, List, Mapping, Set
import collections
from concurrent import futures
import grpc
from src.proto.grpc.testing import test_pb2
from src.proto.grpc.testing import test_pb2_grpc
from src.proto.grpc.testing import messages_pb2
from src.proto.grpc.testing import empty_pb2
logger = logging.getLogger()
console_handler = logging.StreamHandler()
formatter = logging.Formatter(fmt='%(asctime)s: %(levelname)-8s %(message)s')
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
class _StatsWatcher:
_start: int
_end: int
_rpcs_needed: int
_rpcs_by_peer: DefaultDict[str, int]
_no_remote_peer: int
_lock: threading.Lock
_condition: threading.Condition
def __init__(self, start: int, end: int):
self._start = start
self._end = end
self._rpcs_needed = end - start
self._rpcs_by_peer = collections.defaultdict(int)
self._condition = threading.Condition()
self._no_remote_peer = 0
def on_rpc_complete(self, request_id: int, peer: str) -> None:
"""Records statistics for a single RPC."""
if self._start <= request_id < self._end:
with self._condition:
if not peer:
self._no_remote_peer += 1
else:
self._rpcs_by_peer[peer] += 1
self._rpcs_needed -= 1
self._condition.notify()
def await_rpc_stats_response(self, timeout_sec: int
) -> messages_pb2.LoadBalancerStatsResponse:
"""Blocks until a full response has been collected."""
with self._condition:
self._condition.wait_for(lambda: not self._rpcs_needed,
timeout=float(timeout_sec))
response = messages_pb2.LoadBalancerStatsResponse()
for peer, count in self._rpcs_by_peer.items():
response.rpcs_by_peer[peer] = count
response.num_failures = self._no_remote_peer + self._rpcs_needed
return response
_global_lock = threading.Lock()
_stop_event = threading.Event()
_global_rpc_id: int = 0
_watchers: Set[_StatsWatcher] = set()
_global_server = None
def _handle_sigint(sig, frame):
_stop_event.set()
_global_server.stop(None)
class _LoadBalancerStatsServicer(test_pb2_grpc.LoadBalancerStatsServiceServicer
):
def __init__(self):
super(_LoadBalancerStatsServicer).__init__()
def GetClientStats(self, request: messages_pb2.LoadBalancerStatsRequest,
context: grpc.ServicerContext
) -> messages_pb2.LoadBalancerStatsResponse:
logger.info("Received stats request.")
start = None
end = None
watcher = None
with _global_lock:
start = _global_rpc_id + 1
end = start + request.num_rpcs
watcher = _StatsWatcher(start, end)
_watchers.add(watcher)
response = watcher.await_rpc_stats_response(request.timeout_sec)
with _global_lock:
_watchers.remove(watcher)
logger.info("Returning stats response: {}".format(response))
return response
def _start_rpc(request_id: int, stub: test_pb2_grpc.TestServiceStub,
timeout: float, futures: Mapping[int, grpc.Future]) -> None:
logger.info(f"Sending request to backend: {request_id}")
future = stub.UnaryCall.future(messages_pb2.SimpleRequest(),
timeout=timeout)
futures[request_id] = future
def _on_rpc_done(rpc_id: int, future: grpc.Future,
print_response: bool) -> None:
exception = future.exception()
hostname = ""
if exception is not None:
if exception.code() == grpc.StatusCode.DEADLINE_EXCEEDED:
logger.error(f"RPC {rpc_id} timed out")
else:
logger.error(exception)
else:
response = future.result()
logger.info(f"Got result {rpc_id}")
hostname = response.hostname
if print_response:
if future.code() == grpc.StatusCode.OK:
logger.info("Successful response.")
else:
logger.info(f"RPC failed: {call}")
with _global_lock:
for watcher in _watchers:
watcher.on_rpc_complete(rpc_id, hostname)
def _remove_completed_rpcs(futures: Mapping[int, grpc.Future],
print_response: bool) -> None:
logger.debug("Removing completed RPCs")
done = []
for future_id, future in futures.items():
if future.done():
_on_rpc_done(future_id, future, args.print_response)
done.append(future_id)
for rpc_id in done:
del futures[rpc_id]
def _cancel_all_rpcs(futures: Mapping[int, grpc.Future]) -> None:
logger.info("Cancelling all remaining RPCs")
for future in futures.values():
future.cancel()
def _run_single_channel(args: argparse.Namespace):
global _global_rpc_id # pylint: disable=global-statement
duration_per_query = 1.0 / float(args.qps)
with grpc.insecure_channel(args.server) as channel:
stub = test_pb2_grpc.TestServiceStub(channel)
futures: Dict[int, grpc.Future] = {}
while not _stop_event.is_set():
request_id = None
with _global_lock:
request_id = _global_rpc_id
_global_rpc_id += 1
start = time.time()
end = start + duration_per_query
_start_rpc(request_id, stub, float(args.rpc_timeout_sec), futures)
_remove_completed_rpcs(futures, args.print_response)
logger.debug(f"Currently {len(futures)} in-flight RPCs")
now = time.time()
while now < end:
time.sleep(end - now)
now = time.time()
_cancel_all_rpcs(futures)
def _run(args: argparse.Namespace) -> None:
logger.info("Starting python xDS Interop Client.")
global _global_server # pylint: disable=global-statement
channel_threads: List[threading.Thread] = []
for i in range(args.num_channels):
thread = threading.Thread(target=_run_single_channel, args=(args,))
thread.start()
channel_threads.append(thread)
_global_server = grpc.server(futures.ThreadPoolExecutor())
_global_server.add_insecure_port(f"0.0.0.0:{args.stats_port}")
test_pb2_grpc.add_LoadBalancerStatsServiceServicer_to_server(
_LoadBalancerStatsServicer(), _global_server)
_global_server.start()
_global_server.wait_for_termination()
for i in range(args.num_channels):
thread.join()
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description='Run Python XDS interop client.')
parser.add_argument(
"--num_channels",
default=1,
type=int,
help="The number of channels from which to send requests.")
parser.add_argument("--print_response",
default=False,
action="store_true",
help="Write RPC response to STDOUT.")
parser.add_argument(
"--qps",
default=1,
type=int,
help="The number of queries to send from each channel per second.")
parser.add_argument("--rpc_timeout_sec",
default=30,
type=int,
help="The per-RPC timeout in seconds.")
parser.add_argument("--server",
default="localhost:50051",
help="The address of the server.")
parser.add_argument(
"--stats_port",
default=50052,
type=int,
help="The port on which to expose the peer distribution stats service.")
parser.add_argument('--verbose',
help='verbose log output',
default=False,
action='store_true')
parser.add_argument("--log_file",
default=None,
type=str,
help="A file to log to.")
args = parser.parse_args()
signal.signal(signal.SIGINT, _handle_sigint)
if args.verbose:
logger.setLevel(logging.DEBUG)
if args.log_file:
file_handler = logging.FileHandler(args.log_file, mode='a')
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
_run(args)

@ -87,6 +87,7 @@ grpc_cc_test(
external_deps = [ external_deps = [
"gtest", "gtest",
], ],
flaky = True, # TODO(b/151792070)
language = "C++", language = "C++",
deps = [ deps = [
"//:gpr", "//:gpr",

@ -210,7 +210,9 @@ static void disappearing_server_test(grpc_end2end_test_config config) {
void disappearing_server(grpc_end2end_test_config config) { void disappearing_server(grpc_end2end_test_config config) {
GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION); GPR_ASSERT(config.feature_mask & FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION);
#ifndef GPR_WINDOWS /* b/148110727 for more details */
disappearing_server_test(config); disappearing_server_test(config);
#endif /* GPR_WINDOWS */
} }
void disappearing_server_pre_init(void) {} void disappearing_server_pre_init(void) {}

@ -50,6 +50,13 @@ TEST(GrpcTlsCredentialsOptionsTest, SetKeyMaterials) {
delete config; delete config;
} }
TEST(GrpcTlsCredentialsOptionsTest, ErrorDetails) {
grpc_tls_error_details error_details;
EXPECT_STREQ(error_details.error_details().c_str(), "");
error_details.set_error_details("test error details");
EXPECT_STREQ(error_details.error_details().c_str(), "test error details");
}
} // namespace testing } // namespace testing
int main(int argc, char** argv) { int main(int argc, char** argv) {

@ -16,23 +16,23 @@
* *
*/ */
#include "src/core/tsi/ssl_transport_security.h"
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "src/core/lib/iomgr/load_file.h" #include "src/core/lib/iomgr/load_file.h"
#include "src/core/lib/security/security_connector/security_connector.h" #include "src/core/lib/security/security_connector/security_connector.h"
#include "src/core/tsi/ssl_transport_security.h"
#include "src/core/tsi/transport_security.h" #include "src/core/tsi/transport_security.h"
#include "src/core/tsi/transport_security_interface.h" #include "src/core/tsi/transport_security_interface.h"
#include "test/core/tsi/transport_security_test_lib.h" #include "test/core/tsi/transport_security_test_lib.h"
#include "test/core/util/test_config.h" #include "test/core/util/test_config.h"
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
extern "C" { extern "C" {
#include <openssl/crypto.h> #include <openssl/crypto.h>
#include <openssl/pem.h> #include <openssl/pem.h>
@ -45,6 +45,7 @@ extern "C" {
#define SSL_TSI_TEST_SERVER_KEY_CERT_PAIRS_NUM 2 #define SSL_TSI_TEST_SERVER_KEY_CERT_PAIRS_NUM 2
#define SSL_TSI_TEST_BAD_SERVER_KEY_CERT_PAIRS_NUM 1 #define SSL_TSI_TEST_BAD_SERVER_KEY_CERT_PAIRS_NUM 1
#define SSL_TSI_TEST_CREDENTIALS_DIR "src/core/tsi/test_creds/" #define SSL_TSI_TEST_CREDENTIALS_DIR "src/core/tsi/test_creds/"
#define SSL_TSI_TEST_WRONG_SNI "test.google.cn"
// OpenSSL 1.1 uses AES256 for encryption session ticket by default so specify // OpenSSL 1.1 uses AES256 for encryption session ticket by default so specify
// different STEK size. // different STEK size.
@ -308,10 +309,14 @@ static void ssl_test_check_handshaker_peers(tsi_test_fixture* fixture) {
check_session_reusage(ssl_fixture, &peer); check_session_reusage(ssl_fixture, &peer);
check_alpn(ssl_fixture, &peer); check_alpn(ssl_fixture, &peer);
check_security_level(&peer); check_security_level(&peer);
if (ssl_fixture->server_name_indication != nullptr) { if (ssl_fixture->server_name_indication == nullptr ||
check_server1_peer(&peer); strcmp(ssl_fixture->server_name_indication, SSL_TSI_TEST_WRONG_SNI) ==
} else { 0) {
// Expect server to use default server0.pem.
check_server0_peer(&peer); check_server0_peer(&peer);
} else {
// Expect server to use server1.pem.
check_server1_peer(&peer);
} }
} else { } else {
GPR_ASSERT(ssl_fixture->base.client_result == nullptr); GPR_ASSERT(ssl_fixture->base.client_result == nullptr);
@ -551,6 +556,19 @@ void ssl_tsi_test_do_handshake_with_server_name_indication_wild_star_domain() {
tsi_test_fixture_destroy(fixture); tsi_test_fixture_destroy(fixture);
} }
void ssl_tsi_test_do_handshake_with_wrong_server_name_indication() {
gpr_log(GPR_INFO,
"ssl_tsi_test_do_handshake_with_wrong_server_name_indication");
/* server certs do not contain "test.google.cn". */
tsi_test_fixture* fixture = ssl_tsi_test_fixture_create();
ssl_tsi_test_fixture* ssl_fixture =
reinterpret_cast<ssl_tsi_test_fixture*>(fixture);
ssl_fixture->server_name_indication =
const_cast<char*>(SSL_TSI_TEST_WRONG_SNI);
tsi_test_do_handshake(fixture);
tsi_test_fixture_destroy(fixture);
}
void ssl_tsi_test_do_handshake_with_bad_server_cert() { void ssl_tsi_test_do_handshake_with_bad_server_cert() {
gpr_log(GPR_INFO, "ssl_tsi_test_do_handshake_with_bad_server_cert"); gpr_log(GPR_INFO, "ssl_tsi_test_do_handshake_with_bad_server_cert");
tsi_test_fixture* fixture = ssl_tsi_test_fixture_create(); tsi_test_fixture* fixture = ssl_tsi_test_fixture_create();
@ -915,6 +933,7 @@ int main(int argc, char** argv) {
ssl_tsi_test_do_handshake_with_client_authentication_and_root_store(); ssl_tsi_test_do_handshake_with_client_authentication_and_root_store();
ssl_tsi_test_do_handshake_with_server_name_indication_exact_domain(); ssl_tsi_test_do_handshake_with_server_name_indication_exact_domain();
ssl_tsi_test_do_handshake_with_server_name_indication_wild_star_domain(); ssl_tsi_test_do_handshake_with_server_name_indication_wild_star_domain();
ssl_tsi_test_do_handshake_with_wrong_server_name_indication();
ssl_tsi_test_do_handshake_with_bad_server_cert(); ssl_tsi_test_do_handshake_with_bad_server_cert();
ssl_tsi_test_do_handshake_with_bad_client_cert(); ssl_tsi_test_do_handshake_with_bad_client_cert();
#ifdef OPENSSL_IS_BORINGSSL #ifdef OPENSSL_IS_BORINGSSL

@ -78,7 +78,7 @@ static void tls_server_authorization_check_callback(
arg->target_name = gpr_strdup("callback_target_name"); arg->target_name = gpr_strdup("callback_target_name");
arg->peer_cert = gpr_strdup("callback_peer_cert"); arg->peer_cert = gpr_strdup("callback_peer_cert");
arg->status = GRPC_STATUS_OK; arg->status = GRPC_STATUS_OK;
arg->error_details = gpr_strdup("callback_error_details"); arg->error_details->set_error_details("callback_error_details");
} }
class TestTlsServerAuthorizationCheck class TestTlsServerAuthorizationCheck
@ -342,6 +342,7 @@ TEST_F(CredentialsTest, TlsCredentialReloadConfigSchedule) {
std::shared_ptr<TlsCredentialReloadConfig> config( std::shared_ptr<TlsCredentialReloadConfig> config(
new TlsCredentialReloadConfig(test_credential_reload)); new TlsCredentialReloadConfig(test_credential_reload));
grpc_tls_credential_reload_arg* c_arg = new grpc_tls_credential_reload_arg(); grpc_tls_credential_reload_arg* c_arg = new grpc_tls_credential_reload_arg();
c_arg->error_details = new grpc_tls_error_details();
c_arg->context = nullptr; c_arg->context = nullptr;
TlsCredentialReloadArg* arg = new TlsCredentialReloadArg(c_arg); TlsCredentialReloadArg* arg = new TlsCredentialReloadArg(c_arg);
struct TlsKeyMaterialsConfig::PemKeyCertPair pair1 = {"private_key1", struct TlsKeyMaterialsConfig::PemKeyCertPair pair1 = {"private_key1",
@ -352,7 +353,6 @@ TEST_F(CredentialsTest, TlsCredentialReloadConfigSchedule) {
arg->set_key_materials("pem_root_certs", pair_list); arg->set_key_materials("pem_root_certs", pair_list);
arg->set_status(GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_NEW); arg->set_status(GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_NEW);
arg->set_error_details("error_details"); arg->set_error_details("error_details");
const char* error_details_before_schedule = c_arg->error_details;
int schedule_output = config->Schedule(arg); int schedule_output = config->Schedule(arg);
EXPECT_EQ(schedule_output, 0); EXPECT_EQ(schedule_output, 0);
@ -372,11 +372,11 @@ TEST_F(CredentialsTest, TlsCredentialReloadConfigSchedule) {
EXPECT_STREQ(arg->error_details().c_str(), "error_details"); EXPECT_STREQ(arg->error_details().c_str(), "error_details");
// Cleanup. // Cleanup.
gpr_free(const_cast<char*>(error_details_before_schedule));
delete c_arg->key_materials_config; delete c_arg->key_materials_config;
if (c_arg->destroy_context != nullptr) { if (c_arg->destroy_context != nullptr) {
c_arg->destroy_context(c_arg->context); c_arg->destroy_context(c_arg->context);
} }
delete c_arg->error_details;
delete c_arg; delete c_arg;
delete config->c_config(); delete config->c_config();
} }
@ -386,6 +386,7 @@ TEST_F(CredentialsTest, TlsCredentialReloadConfigCppToC) {
new TestTlsCredentialReload()); new TestTlsCredentialReload());
TlsCredentialReloadConfig config(test_credential_reload); TlsCredentialReloadConfig config(test_credential_reload);
grpc_tls_credential_reload_arg c_arg; grpc_tls_credential_reload_arg c_arg;
c_arg.error_details = new grpc_tls_error_details();
c_arg.context = nullptr; c_arg.context = nullptr;
c_arg.cb_user_data = static_cast<void*>(nullptr); c_arg.cb_user_data = static_cast<void*>(nullptr);
grpc_tls_key_materials_config c_key_materials; grpc_tls_key_materials_config c_key_materials;
@ -407,7 +408,7 @@ TEST_F(CredentialsTest, TlsCredentialReloadConfigCppToC) {
c_arg.key_materials_config = &c_key_materials; c_arg.key_materials_config = &c_key_materials;
c_arg.status = GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED; c_arg.status = GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED;
grpc::string test_error_details = "error_details"; grpc::string test_error_details = "error_details";
c_arg.error_details = test_error_details.c_str(); c_arg.error_details->set_error_details(test_error_details.c_str());
grpc_tls_credential_reload_config* c_config = config.c_config(); grpc_tls_credential_reload_config* c_config = config.c_config();
c_arg.config = c_config; c_arg.config = c_config;
@ -424,10 +425,12 @@ TEST_F(CredentialsTest, TlsCredentialReloadConfigCppToC) {
EXPECT_STREQ(pair_list[1].private_key(), "private_key3"); EXPECT_STREQ(pair_list[1].private_key(), "private_key3");
EXPECT_STREQ(pair_list[1].cert_chain(), "cert_chain3"); EXPECT_STREQ(pair_list[1].cert_chain(), "cert_chain3");
EXPECT_EQ(c_arg.status, GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_NEW); EXPECT_EQ(c_arg.status, GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_NEW);
EXPECT_STREQ(c_arg.error_details, test_error_details.c_str()); EXPECT_STREQ(c_arg.error_details->error_details().c_str(),
test_error_details.c_str());
// Cleanup. // Cleanup.
c_arg.destroy_context(c_arg.context); c_arg.destroy_context(c_arg.context);
delete c_arg.error_details;
delete config.c_config(); delete config.c_config();
} }
@ -441,6 +444,7 @@ TEST_F(CredentialsTest, TlsServerAuthorizationCheckArgCallback) {
new grpc_tls_server_authorization_check_arg; new grpc_tls_server_authorization_check_arg;
c_arg->cb = tls_server_authorization_check_callback; c_arg->cb = tls_server_authorization_check_callback;
c_arg->context = nullptr; c_arg->context = nullptr;
c_arg->error_details = new grpc_tls_error_details();
TlsServerAuthorizationCheckArg* arg = TlsServerAuthorizationCheckArg* arg =
new TlsServerAuthorizationCheckArg(c_arg); new TlsServerAuthorizationCheckArg(c_arg);
arg->set_cb_user_data(nullptr); arg->set_cb_user_data(nullptr);
@ -451,7 +455,6 @@ TEST_F(CredentialsTest, TlsServerAuthorizationCheckArgCallback) {
arg->set_error_details("error_details"); arg->set_error_details("error_details");
const char* target_name_before_callback = c_arg->target_name; const char* target_name_before_callback = c_arg->target_name;
const char* peer_cert_before_callback = c_arg->peer_cert; const char* peer_cert_before_callback = c_arg->peer_cert;
const char* error_details_before_callback = c_arg->error_details;
arg->OnServerAuthorizationCheckDoneCallback(); arg->OnServerAuthorizationCheckDoneCallback();
EXPECT_STREQ(static_cast<char*>(arg->cb_user_data()), "cb_user_data"); EXPECT_STREQ(static_cast<char*>(arg->cb_user_data()), "cb_user_data");
@ -465,10 +468,9 @@ TEST_F(CredentialsTest, TlsServerAuthorizationCheckArgCallback) {
// Cleanup. // Cleanup.
gpr_free(const_cast<char*>(target_name_before_callback)); gpr_free(const_cast<char*>(target_name_before_callback));
gpr_free(const_cast<char*>(peer_cert_before_callback)); gpr_free(const_cast<char*>(peer_cert_before_callback));
gpr_free(const_cast<char*>(error_details_before_callback));
gpr_free(const_cast<char*>(c_arg->target_name)); gpr_free(const_cast<char*>(c_arg->target_name));
gpr_free(const_cast<char*>(c_arg->peer_cert)); gpr_free(const_cast<char*>(c_arg->peer_cert));
gpr_free(const_cast<char*>(c_arg->error_details)); delete c_arg->error_details;
delete arg; delete arg;
delete c_arg; delete c_arg;
} }
@ -479,6 +481,7 @@ TEST_F(CredentialsTest, TlsServerAuthorizationCheckConfigSchedule) {
TlsServerAuthorizationCheckConfig config(test_server_authorization_check); TlsServerAuthorizationCheckConfig config(test_server_authorization_check);
grpc_tls_server_authorization_check_arg* c_arg = grpc_tls_server_authorization_check_arg* c_arg =
new grpc_tls_server_authorization_check_arg(); new grpc_tls_server_authorization_check_arg();
c_arg->error_details = new grpc_tls_error_details();
c_arg->context = nullptr; c_arg->context = nullptr;
TlsServerAuthorizationCheckArg* arg = TlsServerAuthorizationCheckArg* arg =
new TlsServerAuthorizationCheckArg(c_arg); new TlsServerAuthorizationCheckArg(c_arg);
@ -490,7 +493,6 @@ TEST_F(CredentialsTest, TlsServerAuthorizationCheckConfigSchedule) {
arg->set_error_details("error_details"); arg->set_error_details("error_details");
const char* target_name_before_schedule = c_arg->target_name; const char* target_name_before_schedule = c_arg->target_name;
const char* peer_cert_before_schedule = c_arg->peer_cert; const char* peer_cert_before_schedule = c_arg->peer_cert;
const char* error_details_before_schedule = c_arg->error_details;
int schedule_output = config.Schedule(arg); int schedule_output = config.Schedule(arg);
EXPECT_EQ(schedule_output, 1); EXPECT_EQ(schedule_output, 1);
@ -505,10 +507,9 @@ TEST_F(CredentialsTest, TlsServerAuthorizationCheckConfigSchedule) {
gpr_free(arg->cb_user_data()); gpr_free(arg->cb_user_data());
gpr_free(const_cast<char*>(target_name_before_schedule)); gpr_free(const_cast<char*>(target_name_before_schedule));
gpr_free(const_cast<char*>(peer_cert_before_schedule)); gpr_free(const_cast<char*>(peer_cert_before_schedule));
gpr_free(const_cast<char*>(error_details_before_schedule));
gpr_free(const_cast<char*>(c_arg->target_name)); gpr_free(const_cast<char*>(c_arg->target_name));
gpr_free(const_cast<char*>(c_arg->peer_cert)); gpr_free(const_cast<char*>(c_arg->peer_cert));
gpr_free(const_cast<char*>(c_arg->error_details)); delete c_arg->error_details;
if (c_arg->destroy_context != nullptr) { if (c_arg->destroy_context != nullptr) {
c_arg->destroy_context(c_arg->context); c_arg->destroy_context(c_arg->context);
} }
@ -527,7 +528,8 @@ TEST_F(CredentialsTest, TlsServerAuthorizationCheckConfigCppToC) {
c_arg.target_name = "target_name"; c_arg.target_name = "target_name";
c_arg.peer_cert = "peer_cert"; c_arg.peer_cert = "peer_cert";
c_arg.status = GRPC_STATUS_UNAUTHENTICATED; c_arg.status = GRPC_STATUS_UNAUTHENTICATED;
c_arg.error_details = "error_details"; c_arg.error_details = new grpc_tls_error_details();
c_arg.error_details->set_error_details("error_details");
c_arg.config = config.c_config(); c_arg.config = config.c_config();
c_arg.context = nullptr; c_arg.context = nullptr;
int c_schedule_output = (c_arg.config)->Schedule(&c_arg); int c_schedule_output = (c_arg.config)->Schedule(&c_arg);
@ -537,12 +539,13 @@ TEST_F(CredentialsTest, TlsServerAuthorizationCheckConfigCppToC) {
EXPECT_STREQ(c_arg.target_name, "sync_target_name"); EXPECT_STREQ(c_arg.target_name, "sync_target_name");
EXPECT_STREQ(c_arg.peer_cert, "sync_peer_cert"); EXPECT_STREQ(c_arg.peer_cert, "sync_peer_cert");
EXPECT_EQ(c_arg.status, GRPC_STATUS_OK); EXPECT_EQ(c_arg.status, GRPC_STATUS_OK);
EXPECT_STREQ(c_arg.error_details, "sync_error_details"); EXPECT_STREQ(c_arg.error_details->error_details().c_str(),
"sync_error_details");
// Cleanup. // Cleanup.
gpr_free(c_arg.cb_user_data); gpr_free(c_arg.cb_user_data);
c_arg.destroy_context(c_arg.context); c_arg.destroy_context(c_arg.context);
gpr_free(const_cast<char*>(c_arg.error_details)); delete c_arg.error_details;
gpr_free(const_cast<char*>(c_arg.target_name)); gpr_free(const_cast<char*>(c_arg.target_name));
gpr_free(const_cast<char*>(c_arg.peer_cert)); gpr_free(const_cast<char*>(c_arg.peer_cert));
delete config.c_config(); delete config.c_config();
@ -587,7 +590,9 @@ TEST_F(CredentialsTest, TlsCredentialsOptionsCppToC) {
c_options->key_materials_config(); c_options->key_materials_config();
c_credential_reload_arg.status = GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED; c_credential_reload_arg.status = GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED;
grpc::string test_error_details = "error_details"; grpc::string test_error_details = "error_details";
c_credential_reload_arg.error_details = test_error_details.c_str(); c_credential_reload_arg.error_details = new grpc_tls_error_details();
c_credential_reload_arg.error_details->set_error_details(
test_error_details.c_str());
c_credential_reload_arg.context = nullptr; c_credential_reload_arg.context = nullptr;
grpc_tls_server_authorization_check_config* grpc_tls_server_authorization_check_config*
c_server_authorization_check_config = c_server_authorization_check_config =
@ -599,7 +604,9 @@ TEST_F(CredentialsTest, TlsCredentialsOptionsCppToC) {
c_server_authorization_check_arg.target_name = "target_name"; c_server_authorization_check_arg.target_name = "target_name";
c_server_authorization_check_arg.peer_cert = "peer_cert"; c_server_authorization_check_arg.peer_cert = "peer_cert";
c_server_authorization_check_arg.status = GRPC_STATUS_UNAUTHENTICATED; c_server_authorization_check_arg.status = GRPC_STATUS_UNAUTHENTICATED;
c_server_authorization_check_arg.error_details = "error_details"; c_server_authorization_check_arg.error_details = new grpc_tls_error_details();
c_server_authorization_check_arg.error_details->set_error_details(
"error_details");
c_server_authorization_check_arg.context = nullptr; c_server_authorization_check_arg.context = nullptr;
EXPECT_STREQ(c_key_materials_config->pem_root_certs(), "pem_root_certs"); EXPECT_STREQ(c_key_materials_config->pem_root_certs(), "pem_root_certs");
EXPECT_EQ( EXPECT_EQ(
@ -627,7 +634,7 @@ TEST_F(CredentialsTest, TlsCredentialsOptionsCppToC) {
EXPECT_STREQ(c_pair_list[1].cert_chain(), "cert_chain3"); EXPECT_STREQ(c_pair_list[1].cert_chain(), "cert_chain3");
EXPECT_EQ(c_credential_reload_arg.status, EXPECT_EQ(c_credential_reload_arg.status,
GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_NEW); GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_NEW);
EXPECT_STREQ(c_credential_reload_arg.error_details, EXPECT_STREQ(c_credential_reload_arg.error_details->error_details().c_str(),
test_error_details.c_str()); test_error_details.c_str());
int c_server_authorization_check_schedule_output = int c_server_authorization_check_schedule_output =
@ -642,17 +649,19 @@ TEST_F(CredentialsTest, TlsCredentialsOptionsCppToC) {
"sync_target_name"); "sync_target_name");
EXPECT_STREQ(c_server_authorization_check_arg.peer_cert, "sync_peer_cert"); EXPECT_STREQ(c_server_authorization_check_arg.peer_cert, "sync_peer_cert");
EXPECT_EQ(c_server_authorization_check_arg.status, GRPC_STATUS_OK); EXPECT_EQ(c_server_authorization_check_arg.status, GRPC_STATUS_OK);
EXPECT_STREQ(c_server_authorization_check_arg.error_details, EXPECT_STREQ(
"sync_error_details"); c_server_authorization_check_arg.error_details->error_details().c_str(),
"sync_error_details");
// Cleanup. // Cleanup.
c_credential_reload_arg.destroy_context(c_credential_reload_arg.context); c_credential_reload_arg.destroy_context(c_credential_reload_arg.context);
delete c_credential_reload_arg.error_details;
c_server_authorization_check_arg.destroy_context( c_server_authorization_check_arg.destroy_context(
c_server_authorization_check_arg.context); c_server_authorization_check_arg.context);
gpr_free(c_server_authorization_check_arg.cb_user_data); gpr_free(c_server_authorization_check_arg.cb_user_data);
gpr_free(const_cast<char*>(c_server_authorization_check_arg.target_name)); gpr_free(const_cast<char*>(c_server_authorization_check_arg.target_name));
gpr_free(const_cast<char*>(c_server_authorization_check_arg.peer_cert)); gpr_free(const_cast<char*>(c_server_authorization_check_arg.peer_cert));
gpr_free(const_cast<char*>(c_server_authorization_check_arg.error_details)); delete c_server_authorization_check_arg.error_details;
delete c_options; delete c_options;
} }
@ -698,6 +707,7 @@ TEST_F(CredentialsTest, TlsCredentialReloadConfigErrorMessages) {
std::shared_ptr<TlsCredentialReloadConfig> config( std::shared_ptr<TlsCredentialReloadConfig> config(
new TlsCredentialReloadConfig(nullptr)); new TlsCredentialReloadConfig(nullptr));
grpc_tls_credential_reload_arg* c_arg = new grpc_tls_credential_reload_arg; grpc_tls_credential_reload_arg* c_arg = new grpc_tls_credential_reload_arg;
c_arg->error_details = new grpc_tls_error_details();
c_arg->context = nullptr; c_arg->context = nullptr;
TlsCredentialReloadArg* arg = new TlsCredentialReloadArg(c_arg); TlsCredentialReloadArg* arg = new TlsCredentialReloadArg(c_arg);
int schedule_output = config->Schedule(arg); int schedule_output = config->Schedule(arg);
@ -706,7 +716,6 @@ TEST_F(CredentialsTest, TlsCredentialReloadConfigErrorMessages) {
EXPECT_EQ(arg->status(), GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL); EXPECT_EQ(arg->status(), GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL);
EXPECT_STREQ(arg->error_details().c_str(), EXPECT_STREQ(arg->error_details().c_str(),
"the interface of the credential reload config is nullptr"); "the interface of the credential reload config is nullptr");
gpr_free(const_cast<char*>(c_arg->error_details));
arg->set_status(GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED); arg->set_status(GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED);
config->Cancel(arg); config->Cancel(arg);
@ -715,10 +724,10 @@ TEST_F(CredentialsTest, TlsCredentialReloadConfigErrorMessages) {
"the interface of the credential reload config is nullptr"); "the interface of the credential reload config is nullptr");
// Cleanup. // Cleanup.
gpr_free(const_cast<char*>(c_arg->error_details));
if (c_arg->destroy_context != nullptr) { if (c_arg->destroy_context != nullptr) {
c_arg->destroy_context(c_arg->context); c_arg->destroy_context(c_arg->context);
} }
delete c_arg->error_details;
delete c_arg; delete c_arg;
delete config->c_config(); delete config->c_config();
} }
@ -728,6 +737,7 @@ TEST_F(CredentialsTest, TlsServerAuthorizationCheckConfigErrorMessages) {
new TlsServerAuthorizationCheckConfig(nullptr)); new TlsServerAuthorizationCheckConfig(nullptr));
grpc_tls_server_authorization_check_arg* c_arg = grpc_tls_server_authorization_check_arg* c_arg =
new grpc_tls_server_authorization_check_arg; new grpc_tls_server_authorization_check_arg;
c_arg->error_details = new grpc_tls_error_details();
c_arg->context = nullptr; c_arg->context = nullptr;
TlsServerAuthorizationCheckArg* arg = TlsServerAuthorizationCheckArg* arg =
new TlsServerAuthorizationCheckArg(c_arg); new TlsServerAuthorizationCheckArg(c_arg);
@ -738,7 +748,6 @@ TEST_F(CredentialsTest, TlsServerAuthorizationCheckConfigErrorMessages) {
EXPECT_STREQ( EXPECT_STREQ(
arg->error_details().c_str(), arg->error_details().c_str(),
"the interface of the server authorization check config is nullptr"); "the interface of the server authorization check config is nullptr");
gpr_free(const_cast<char*>(c_arg->error_details));
arg->set_status(GRPC_STATUS_OK); arg->set_status(GRPC_STATUS_OK);
config->Cancel(arg); config->Cancel(arg);
@ -748,7 +757,7 @@ TEST_F(CredentialsTest, TlsServerAuthorizationCheckConfigErrorMessages) {
"the interface of the server authorization check config is nullptr"); "the interface of the server authorization check config is nullptr");
// Cleanup. // Cleanup.
gpr_free(const_cast<char*>(c_arg->error_details)); delete c_arg->error_details;
if (c_arg->destroy_context != nullptr) { if (c_arg->destroy_context != nullptr) {
c_arg->destroy_context(c_arg->context); c_arg->destroy_context(c_arg->context);
} }

@ -22,7 +22,9 @@
#include <numeric> #include <numeric>
#include <set> #include <set>
#include <sstream> #include <sstream>
#include <string>
#include <thread> #include <thread>
#include <vector>
#include <grpc/grpc.h> #include <grpc/grpc.h>
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
@ -296,8 +298,9 @@ class ClientStats {
}; };
// Converts from proto message class. // Converts from proto message class.
ClientStats(const ClusterStats& cluster_stats) explicit ClientStats(const ClusterStats& cluster_stats)
: total_dropped_requests_(cluster_stats.total_dropped_requests()) { : cluster_name_(cluster_stats.cluster_name()),
total_dropped_requests_(cluster_stats.total_dropped_requests()) {
for (const auto& input_locality_stats : for (const auto& input_locality_stats :
cluster_stats.upstream_locality_stats()) { cluster_stats.upstream_locality_stats()) {
locality_stats_.emplace(input_locality_stats.locality().sub_zone(), locality_stats_.emplace(input_locality_stats.locality().sub_zone(),
@ -310,6 +313,11 @@ class ClientStats {
} }
} }
const std::string& cluster_name() const { return cluster_name_; }
const std::map<grpc::string, LocalityStats>& locality_stats() const {
return locality_stats_;
}
uint64_t total_successful_requests() const { uint64_t total_successful_requests() const {
uint64_t sum = 0; uint64_t sum = 0;
for (auto& p : locality_stats_) { for (auto& p : locality_stats_) {
@ -338,7 +346,9 @@ class ClientStats {
} }
return sum; return sum;
} }
uint64_t total_dropped_requests() const { return total_dropped_requests_; } uint64_t total_dropped_requests() const { return total_dropped_requests_; }
uint64_t dropped_requests(const grpc::string& category) const { uint64_t dropped_requests(const grpc::string& category) const {
auto iter = dropped_requests_.find(category); auto iter = dropped_requests_.find(category);
GPR_ASSERT(iter != dropped_requests_.end()); GPR_ASSERT(iter != dropped_requests_.end());
@ -346,6 +356,7 @@ class ClientStats {
} }
private: private:
std::string cluster_name_;
std::map<grpc::string, LocalityStats> locality_stats_; std::map<grpc::string, LocalityStats> locality_stats_;
uint64_t total_dropped_requests_; uint64_t total_dropped_requests_;
std::map<grpc::string, uint64_t> dropped_requests_; std::map<grpc::string, uint64_t> dropped_requests_;
@ -391,7 +402,6 @@ class AdsServiceImpl : public AggregatedDiscoveryService::Service,
}; };
using Stream = ServerReaderWriter<DiscoveryResponse, DiscoveryRequest>; using Stream = ServerReaderWriter<DiscoveryResponse, DiscoveryRequest>;
using ResponseDelayPair = std::pair<DiscoveryResponse, int>;
// A queue of resource type/name pairs that have changed since the client // A queue of resource type/name pairs that have changed since the client
// subscribed to them. // subscribed to them.
@ -933,60 +943,62 @@ class LrsServiceImpl : public LrsService,
explicit LrsServiceImpl(int client_load_reporting_interval_seconds) explicit LrsServiceImpl(int client_load_reporting_interval_seconds)
: client_load_reporting_interval_seconds_( : client_load_reporting_interval_seconds_(
client_load_reporting_interval_seconds) {} client_load_reporting_interval_seconds),
cluster_names_({kDefaultResourceName}) {}
Status StreamLoadStats(ServerContext* /*context*/, Stream* stream) override { Status StreamLoadStats(ServerContext* /*context*/, Stream* stream) override {
gpr_log(GPR_INFO, "LRS[%p]: StreamLoadStats starts", this); gpr_log(GPR_INFO, "LRS[%p]: StreamLoadStats starts", this);
GPR_ASSERT(client_load_reporting_interval_seconds_ > 0);
// Take a reference of the LrsServiceImpl object, reference will go // Take a reference of the LrsServiceImpl object, reference will go
// out of scope after this method exits. // out of scope after this method exits.
std::shared_ptr<LrsServiceImpl> lrs_service_impl = shared_from_this(); std::shared_ptr<LrsServiceImpl> lrs_service_impl = shared_from_this();
// Read request. // Read initial request.
LoadStatsRequest request; LoadStatsRequest request;
if (stream->Read(&request)) { if (stream->Read(&request)) {
if (client_load_reporting_interval_seconds_ > 0) { IncreaseRequestCount(); // Only for initial request.
IncreaseRequestCount(); // Verify server name set in metadata.
// Send response. auto it =
LoadStatsResponse response; request.node().metadata().fields().find("PROXYLESS_CLIENT_HOSTNAME");
std::string server_name; GPR_ASSERT(it != request.node().metadata().fields().end());
auto it = request.node().metadata().fields().find( EXPECT_EQ(it->second.string_value(), kDefaultResourceName);
"PROXYLESS_CLIENT_HOSTNAME"); // Send initial response.
if (it != request.node().metadata().fields().end()) { LoadStatsResponse response;
server_name = it->second.string_value(); for (const std::string& cluster_name : cluster_names_) {
} response.add_clusters(cluster_name);
GPR_ASSERT(server_name != ""); }
response.add_clusters(server_name); response.mutable_load_reporting_interval()->set_seconds(
response.mutable_load_reporting_interval()->set_seconds( client_load_reporting_interval_seconds_);
client_load_reporting_interval_seconds_); stream->Write(response);
stream->Write(response); IncreaseResponseCount();
IncreaseResponseCount(); // Wait for report.
// Wait for report. request.Clear();
request.Clear(); while (stream->Read(&request)) {
if (stream->Read(&request)) { gpr_log(GPR_INFO, "LRS[%p]: received client load report message: %s",
gpr_log(GPR_INFO, "LRS[%p]: received client load report message '%s'", this, request.DebugString().c_str());
this, request.DebugString().c_str()); std::vector<ClientStats> stats;
GPR_ASSERT(request.cluster_stats().size() == 1); for (const auto& cluster_stats : request.cluster_stats()) {
const ClusterStats& cluster_stats = request.cluster_stats()[0]; stats.emplace_back(cluster_stats);
// We need to acquire the lock here in order to prevent the notify_one
// below from firing before its corresponding wait is executed.
grpc_core::MutexLock lock(&load_report_mu_);
GPR_ASSERT(client_stats_ == nullptr);
client_stats_.reset(new ClientStats(cluster_stats));
load_report_ready_ = true;
load_report_cond_.Signal();
} }
grpc_core::MutexLock lock(&load_report_mu_);
result_queue_.emplace_back(std::move(stats));
if (load_report_cond_ != nullptr) load_report_cond_->Signal();
} }
// Wait until notified done. // Wait until notified done.
grpc_core::MutexLock lock(&lrs_mu_); grpc_core::MutexLock lock(&lrs_mu_);
lrs_cv_.WaitUntil(&lrs_mu_, [this] { return lrs_done; }); lrs_cv_.WaitUntil(&lrs_mu_, [this] { return lrs_done_; });
} }
gpr_log(GPR_INFO, "LRS[%p]: StreamLoadStats done", this); gpr_log(GPR_INFO, "LRS[%p]: StreamLoadStats done", this);
return Status::OK; return Status::OK;
} }
// Must be called before the LRS call is started.
void set_cluster_names(const std::set<std::string>& cluster_names) {
cluster_names_ = cluster_names;
}
void Start() { void Start() {
lrs_done = false; lrs_done_ = false;
load_report_ready_ = false; result_queue_.clear();
client_stats_.reset();
} }
void Shutdown() { void Shutdown() {
@ -997,12 +1009,18 @@ class LrsServiceImpl : public LrsService,
gpr_log(GPR_INFO, "LRS[%p]: shut down", this); gpr_log(GPR_INFO, "LRS[%p]: shut down", this);
} }
ClientStats* WaitForLoadReport() { std::vector<ClientStats> WaitForLoadReport() {
grpc_core::MutexLock lock(&load_report_mu_); grpc_core::MutexLock lock(&load_report_mu_);
load_report_cond_.WaitUntil(&load_report_mu_, grpc_core::CondVar cv;
[this] { return load_report_ready_; }); if (result_queue_.empty()) {
load_report_ready_ = false; load_report_cond_ = &cv;
return client_stats_.get(); load_report_cond_->WaitUntil(&load_report_mu_,
[this] { return !result_queue_.empty(); });
load_report_cond_ = nullptr;
}
std::vector<ClientStats> result = std::move(result_queue_.front());
result_queue_.pop_front();
return result;
} }
void NotifyDoneWithLrsCall() { void NotifyDoneWithLrsCall() {
@ -1010,26 +1028,24 @@ class LrsServiceImpl : public LrsService,
NotifyDoneWithLrsCallLocked(); NotifyDoneWithLrsCallLocked();
} }
private:
void NotifyDoneWithLrsCallLocked() { void NotifyDoneWithLrsCallLocked() {
if (!lrs_done) { if (!lrs_done_) {
lrs_done = true; lrs_done_ = true;
lrs_cv_.Broadcast(); lrs_cv_.Broadcast();
} }
} }
private:
const int client_load_reporting_interval_seconds_; const int client_load_reporting_interval_seconds_;
std::set<std::string> cluster_names_;
grpc_core::CondVar lrs_cv_; grpc_core::CondVar lrs_cv_;
// Protect lrs_done. grpc_core::Mutex lrs_mu_; // Protects lrs_done_.
grpc_core::Mutex lrs_mu_; bool lrs_done_ = false;
bool lrs_done = false;
grpc_core::CondVar load_report_cond_; grpc_core::Mutex load_report_mu_; // Protects the members below.
// Protect the members below. grpc_core::CondVar* load_report_cond_ = nullptr;
grpc_core::Mutex load_report_mu_; std::deque<std::vector<ClientStats>> result_queue_;
std::unique_ptr<ClientStats> client_stats_;
bool load_report_ready_ = false;
}; };
class TestType { class TestType {
@ -1720,6 +1736,141 @@ TEST_P(XdsResolverOnlyTest, ClusterRemoved) {
AdsServiceImpl::ACKED); AdsServiceImpl::ACKED);
} }
class XdsResolverLoadReportingOnlyTest : public XdsEnd2endTest {
public:
XdsResolverLoadReportingOnlyTest() : XdsEnd2endTest(4, 1, 3) {}
};
// Tests load reporting when switching over from one cluster to another.
TEST_P(XdsResolverLoadReportingOnlyTest, ChangeClusters) {
const char* kNewClusterName = "new_cluster_name";
balancers_[0]->lrs_service()->set_cluster_names(
{kDefaultResourceName, kNewClusterName});
SetNextResolution({});
SetNextResolutionForLbChannelAllBalancers();
// cluster kDefaultResourceName -> locality0 -> backends 0 and 1
AdsServiceImpl::EdsResourceArgs args({
{"locality0", GetBackendPorts(0, 2)},
});
balancers_[0]->ads_service()->SetEdsResource(
AdsServiceImpl::BuildEdsResource(args), kDefaultResourceName);
// cluster kNewClusterName -> locality1 -> backends 2 and 3
AdsServiceImpl::EdsResourceArgs args2({
{"locality1", GetBackendPorts(2, 4)},
});
balancers_[0]->ads_service()->SetEdsResource(
AdsServiceImpl::BuildEdsResource(args2, kNewClusterName),
kNewClusterName);
// CDS resource for kNewClusterName.
Cluster new_cluster = balancers_[0]->ads_service()->default_cluster();
new_cluster.set_name(kNewClusterName);
balancers_[0]->ads_service()->SetCdsResource(new_cluster, kNewClusterName);
// Wait for all backends to come online.
int num_ok = 0;
int num_failure = 0;
int num_drops = 0;
std::tie(num_ok, num_failure, num_drops) = WaitForAllBackends(0, 2);
// The load report received at the balancer should be correct.
std::vector<ClientStats> load_report =
balancers_[0]->lrs_service()->WaitForLoadReport();
EXPECT_THAT(
load_report,
::testing::ElementsAre(::testing::AllOf(
::testing::Property(&ClientStats::cluster_name, kDefaultResourceName),
::testing::Property(
&ClientStats::locality_stats,
::testing::ElementsAre(::testing::Pair(
"locality0",
::testing::AllOf(
::testing::Field(&ClientStats::LocalityStats::
total_successful_requests,
num_ok),
::testing::Field(&ClientStats::LocalityStats::
total_requests_in_progress,
0UL),
::testing::Field(
&ClientStats::LocalityStats::total_error_requests,
num_failure),
::testing::Field(
&ClientStats::LocalityStats::total_issued_requests,
num_failure + num_ok))))),
::testing::Property(&ClientStats::total_dropped_requests,
num_drops))));
// Change RDS resource to point to new cluster.
RouteConfiguration new_route_config =
balancers_[0]->ads_service()->default_route_config();
new_route_config.mutable_virtual_hosts(0)
->mutable_routes(0)
->mutable_route()
->set_cluster(kNewClusterName);
Listener listener =
balancers_[0]->ads_service()->BuildListener(new_route_config);
balancers_[0]->ads_service()->SetLdsResource(listener, kDefaultResourceName);
// Wait for all new backends to be used.
std::tie(num_ok, num_failure, num_drops) = WaitForAllBackends(2, 4);
// The load report received at the balancer should be correct.
load_report = balancers_[0]->lrs_service()->WaitForLoadReport();
EXPECT_THAT(
load_report,
::testing::ElementsAre(
::testing::AllOf(
::testing::Property(&ClientStats::cluster_name,
kDefaultResourceName),
::testing::Property(
&ClientStats::locality_stats,
::testing::ElementsAre(::testing::Pair(
"locality0",
::testing::AllOf(
::testing::Field(&ClientStats::LocalityStats::
total_successful_requests,
::testing::Lt(num_ok)),
::testing::Field(&ClientStats::LocalityStats::
total_requests_in_progress,
0UL),
::testing::Field(
&ClientStats::LocalityStats::total_error_requests,
::testing::Le(num_failure)),
::testing::Field(
&ClientStats::LocalityStats::
total_issued_requests,
::testing::Le(num_failure + num_ok)))))),
::testing::Property(&ClientStats::total_dropped_requests,
num_drops)),
::testing::AllOf(
::testing::Property(&ClientStats::cluster_name, kNewClusterName),
::testing::Property(
&ClientStats::locality_stats,
::testing::ElementsAre(::testing::Pair(
"locality1",
::testing::AllOf(
::testing::Field(&ClientStats::LocalityStats::
total_successful_requests,
::testing::Le(num_ok)),
::testing::Field(&ClientStats::LocalityStats::
total_requests_in_progress,
0UL),
::testing::Field(
&ClientStats::LocalityStats::total_error_requests,
::testing::Le(num_failure)),
::testing::Field(
&ClientStats::LocalityStats::
total_issued_requests,
::testing::Le(num_failure + num_ok)))))),
::testing::Property(&ClientStats::total_dropped_requests,
num_drops))));
int total_ok = 0;
int total_failure = 0;
for (const ClientStats& client_stats : load_report) {
total_ok += client_stats.total_successful_requests();
total_failure += client_stats.total_error_requests();
}
EXPECT_EQ(total_ok, num_ok);
EXPECT_EQ(total_failure, num_failure);
// The LRS service got a single request, and sent a single response.
EXPECT_EQ(1U, balancers_[0]->lrs_service()->request_count());
EXPECT_EQ(1U, balancers_[0]->lrs_service()->response_count());
}
using SecureNamingTest = BasicTest; using SecureNamingTest = BasicTest;
// Tests that secure naming check passes if target name is expected. // Tests that secure naming check passes if target name is expected.
@ -2224,7 +2375,7 @@ TEST_P(LocalityMapTest, StressTest) {
TEST_P(LocalityMapTest, UpdateMap) { TEST_P(LocalityMapTest, UpdateMap) {
SetNextResolution({}); SetNextResolution({});
SetNextResolutionForLbChannelAllBalancers(); SetNextResolutionForLbChannelAllBalancers();
const size_t kNumRpcs = 1000; const size_t kNumRpcs = 3000;
// The locality weight for the first 3 localities. // The locality weight for the first 3 localities.
const std::vector<int> kLocalityWeights0 = {2, 3, 4}; const std::vector<int> kLocalityWeights0 = {2, 3, 4};
const double kTotalLocalityWeight0 = const double kTotalLocalityWeight0 =
@ -2266,6 +2417,8 @@ TEST_P(LocalityMapTest, UpdateMap) {
} }
const double kErrorTolerance = 0.2; const double kErrorTolerance = 0.2;
for (size_t i = 0; i < 3; ++i) { for (size_t i = 0; i < 3; ++i) {
gpr_log(GPR_INFO, "Locality %" PRIuPTR " rate %f", i,
locality_picked_rates[i]);
EXPECT_THAT( EXPECT_THAT(
locality_picked_rates[i], locality_picked_rates[i],
::testing::AllOf( ::testing::AllOf(
@ -2283,7 +2436,7 @@ TEST_P(LocalityMapTest, UpdateMap) {
EXPECT_EQ(0U, backends_[3]->backend_service()->request_count()); EXPECT_EQ(0U, backends_[3]->backend_service()->request_count());
// Wait until the locality update has been processed, as signaled by backend 3 // Wait until the locality update has been processed, as signaled by backend 3
// receiving a request. // receiving a request.
WaitForBackend(3); WaitForAllBackends(3, 4);
gpr_log(GPR_INFO, "========= BEFORE SECOND BATCH =========="); gpr_log(GPR_INFO, "========= BEFORE SECOND BATCH ==========");
// Send kNumRpcs RPCs. // Send kNumRpcs RPCs.
CheckRpcSendOk(kNumRpcs); CheckRpcSendOk(kNumRpcs);
@ -2299,6 +2452,8 @@ TEST_P(LocalityMapTest, UpdateMap) {
kNumRpcs); kNumRpcs);
} }
for (size_t i = 1; i < 4; ++i) { for (size_t i = 1; i < 4; ++i) {
gpr_log(GPR_INFO, "Locality %" PRIuPTR " rate %f", i,
locality_picked_rates[i]);
EXPECT_THAT( EXPECT_THAT(
locality_picked_rates[i], locality_picked_rates[i],
::testing::AllOf( ::testing::AllOf(
@ -2957,7 +3112,7 @@ TEST_P(FallbackTest, FallbackModeIsExitedWhenBalancerSaysToDropAllCalls) {
} }
// Tests that fallback mode is exited if the child policy becomes ready. // Tests that fallback mode is exited if the child policy becomes ready.
TEST_P(FallbackTest, FallbackModeIsExitedAfterChildRready) { TEST_P(FallbackTest, FallbackModeIsExitedAfterChildReady) {
// Return an unreachable balancer and one fallback backend. // Return an unreachable balancer and one fallback backend.
SetNextResolution({backends_[0]->port()}); SetNextResolution({backends_[0]->port()});
SetNextResolutionForLbChannel({g_port_saver->GetPort()}); SetNextResolutionForLbChannel({g_port_saver->GetPort()});
@ -3227,14 +3382,50 @@ TEST_P(ClientLoadReportingTest, Vanilla) {
EXPECT_EQ(1U, balancers_[0]->lrs_service()->request_count()); EXPECT_EQ(1U, balancers_[0]->lrs_service()->request_count());
EXPECT_EQ(1U, balancers_[0]->lrs_service()->response_count()); EXPECT_EQ(1U, balancers_[0]->lrs_service()->response_count());
// The load report received at the balancer should be correct. // The load report received at the balancer should be correct.
ClientStats* client_stats = balancers_[0]->lrs_service()->WaitForLoadReport(); std::vector<ClientStats> load_report =
balancers_[0]->lrs_service()->WaitForLoadReport();
ASSERT_EQ(load_report.size(), 1UL);
ClientStats& client_stats = load_report.front();
EXPECT_EQ(kNumRpcsPerAddress * num_backends_ + num_ok, EXPECT_EQ(kNumRpcsPerAddress * num_backends_ + num_ok,
client_stats->total_successful_requests()); client_stats.total_successful_requests());
EXPECT_EQ(0U, client_stats->total_requests_in_progress()); EXPECT_EQ(0U, client_stats.total_requests_in_progress());
EXPECT_EQ(kNumRpcsPerAddress * num_backends_ + num_ok, EXPECT_EQ(kNumRpcsPerAddress * num_backends_ + num_ok,
client_stats->total_issued_requests()); client_stats.total_issued_requests());
EXPECT_EQ(0U, client_stats->total_error_requests()); EXPECT_EQ(0U, client_stats.total_error_requests());
EXPECT_EQ(0U, client_stats->total_dropped_requests()); EXPECT_EQ(0U, client_stats.total_dropped_requests());
}
// Tests that we don't include stats for clusters that are not requested
// by the LRS server.
TEST_P(ClientLoadReportingTest, HonorsClustersRequestedByLrsServer) {
balancers_[0]->lrs_service()->set_cluster_names({"bogus"});
SetNextResolution({});
SetNextResolutionForLbChannel({balancers_[0]->port()});
const size_t kNumRpcsPerAddress = 100;
AdsServiceImpl::EdsResourceArgs args({
{"locality0", GetBackendPorts()},
});
balancers_[0]->ads_service()->SetEdsResource(
AdsServiceImpl::BuildEdsResource(args), kDefaultResourceName);
// Wait until all backends are ready.
int num_ok = 0;
int num_failure = 0;
int num_drops = 0;
std::tie(num_ok, num_failure, num_drops) = WaitForAllBackends();
// Send kNumRpcsPerAddress RPCs per server.
CheckRpcSendOk(kNumRpcsPerAddress * num_backends_);
// 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());
}
// The LRS service got a single request, and sent a single response.
EXPECT_EQ(1U, balancers_[0]->lrs_service()->request_count());
EXPECT_EQ(1U, balancers_[0]->lrs_service()->response_count());
// The load report received at the balancer should be correct.
std::vector<ClientStats> load_report =
balancers_[0]->lrs_service()->WaitForLoadReport();
ASSERT_EQ(load_report.size(), 0UL);
} }
// Tests that if the balancer restarts, the client load report contains the // Tests that if the balancer restarts, the client load report contains the
@ -3257,12 +3448,15 @@ TEST_P(ClientLoadReportingTest, BalancerRestart) {
std::tie(num_ok, num_failure, num_drops) = std::tie(num_ok, num_failure, num_drops) =
WaitForAllBackends(/* start_index */ 0, WaitForAllBackends(/* start_index */ 0,
/* stop_index */ kNumBackendsFirstPass); /* stop_index */ kNumBackendsFirstPass);
ClientStats* client_stats = balancers_[0]->lrs_service()->WaitForLoadReport(); std::vector<ClientStats> load_report =
balancers_[0]->lrs_service()->WaitForLoadReport();
ASSERT_EQ(load_report.size(), 1UL);
ClientStats client_stats = std::move(load_report.front());
EXPECT_EQ(static_cast<size_t>(num_ok), EXPECT_EQ(static_cast<size_t>(num_ok),
client_stats->total_successful_requests()); client_stats.total_successful_requests());
EXPECT_EQ(0U, client_stats->total_requests_in_progress()); EXPECT_EQ(0U, client_stats.total_requests_in_progress());
EXPECT_EQ(0U, client_stats->total_error_requests()); EXPECT_EQ(0U, client_stats.total_error_requests());
EXPECT_EQ(0U, client_stats->total_dropped_requests()); EXPECT_EQ(0U, client_stats.total_dropped_requests());
// Shut down the balancer. // Shut down the balancer.
balancers_[0]->Shutdown(); balancers_[0]->Shutdown();
// We should continue using the last EDS response we received from the // We should continue using the last EDS response we received from the
@ -3294,11 +3488,13 @@ TEST_P(ClientLoadReportingTest, BalancerRestart) {
CheckRpcSendOk(kNumBackendsSecondPass); CheckRpcSendOk(kNumBackendsSecondPass);
num_started += kNumBackendsSecondPass; num_started += kNumBackendsSecondPass;
// Check client stats. // Check client stats.
client_stats = balancers_[0]->lrs_service()->WaitForLoadReport(); load_report = balancers_[0]->lrs_service()->WaitForLoadReport();
EXPECT_EQ(num_started, client_stats->total_successful_requests()); ASSERT_EQ(load_report.size(), 1UL);
EXPECT_EQ(0U, client_stats->total_requests_in_progress()); client_stats = std::move(load_report.front());
EXPECT_EQ(0U, client_stats->total_error_requests()); EXPECT_EQ(num_started, client_stats.total_successful_requests());
EXPECT_EQ(0U, client_stats->total_dropped_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());
} }
class ClientLoadReportingWithDropTest : public XdsEnd2endTest { class ClientLoadReportingWithDropTest : public XdsEnd2endTest {
@ -3352,15 +3548,18 @@ TEST_P(ClientLoadReportingWithDropTest, Vanilla) {
::testing::Ge(KDropRateForLbAndThrottle * (1 - kErrorTolerance)), ::testing::Ge(KDropRateForLbAndThrottle * (1 - kErrorTolerance)),
::testing::Le(KDropRateForLbAndThrottle * (1 + kErrorTolerance)))); ::testing::Le(KDropRateForLbAndThrottle * (1 + kErrorTolerance))));
// Check client stats. // Check client stats.
ClientStats* client_stats = balancers_[0]->lrs_service()->WaitForLoadReport(); std::vector<ClientStats> load_report =
EXPECT_EQ(num_drops, client_stats->total_dropped_requests()); balancers_[0]->lrs_service()->WaitForLoadReport();
ASSERT_EQ(load_report.size(), 1UL);
ClientStats& client_stats = load_report.front();
EXPECT_EQ(num_drops, client_stats.total_dropped_requests());
const size_t total_rpc = num_warmup + kNumRpcs; const size_t total_rpc = num_warmup + kNumRpcs;
EXPECT_THAT( EXPECT_THAT(
client_stats->dropped_requests(kLbDropType), client_stats.dropped_requests(kLbDropType),
::testing::AllOf( ::testing::AllOf(
::testing::Ge(total_rpc * kDropRateForLb * (1 - kErrorTolerance)), ::testing::Ge(total_rpc * kDropRateForLb * (1 - kErrorTolerance)),
::testing::Le(total_rpc * kDropRateForLb * (1 + kErrorTolerance)))); ::testing::Le(total_rpc * kDropRateForLb * (1 + kErrorTolerance))));
EXPECT_THAT(client_stats->dropped_requests(kThrottleDropType), EXPECT_THAT(client_stats.dropped_requests(kThrottleDropType),
::testing::AllOf( ::testing::AllOf(
::testing::Ge(total_rpc * (1 - kDropRateForLb) * ::testing::Ge(total_rpc * (1 - kDropRateForLb) *
kDropRateForThrottle * (1 - kErrorTolerance)), kDropRateForThrottle * (1 - kErrorTolerance)),
@ -3417,6 +3616,11 @@ INSTANTIATE_TEST_SUITE_P(XdsTest, XdsResolverOnlyTest,
TestType(true, true)), TestType(true, true)),
&TestTypeName); &TestTypeName);
// XdsResolverLoadReprtingOnlyTest depends on XdsResolver and load reporting.
INSTANTIATE_TEST_SUITE_P(XdsTest, XdsResolverLoadReportingOnlyTest,
::testing::Values(TestType(true, true)),
&TestTypeName);
INSTANTIATE_TEST_SUITE_P(XdsTest, LocalityMapTest, INSTANTIATE_TEST_SUITE_P(XdsTest, LocalityMapTest,
::testing::Values(TestType(false, true), ::testing::Values(TestType(false, true),
TestType(false, false), TestType(false, false),

@ -0,0 +1,56 @@
#!/usr/bin/env bash
# Copyright 2020 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.
set -ex -o igncr || set -ex
mkdir -p /var/local/git
git clone /var/local/jenkins/grpc /var/local/git/grpc
(cd /var/local/jenkins/grpc/ && git submodule foreach 'cd /var/local/git/grpc \
&& git submodule update --init --reference /var/local/jenkins/grpc/${name} \
${name}')
cd /var/local/git/grpc
VIRTUAL_ENV=$(mktemp -d)
virtualenv "$VIRTUAL_ENV"
PYTHON="$VIRTUAL_ENV"/bin/python
"$PYTHON" -m pip install --upgrade grpcio-tools google-api-python-client google-auth-httplib2 oauth2client
# Prepare generated Python code.
TOOLS_DIR=tools/run_tests
PROTO_SOURCE_DIR=src/proto/grpc/testing
PROTO_DEST_DIR="$TOOLS_DIR"/"$PROTO_SOURCE_DIR"
mkdir -p "$PROTO_DEST_DIR"
touch "$TOOLS_DIR"/src/__init__.py
touch "$TOOLS_DIR"/src/proto/__init__.py
touch "$TOOLS_DIR"/src/proto/grpc/__init__.py
touch "$TOOLS_DIR"/src/proto/grpc/testing/__init__.py
"$PYTHON" -m grpc_tools.protoc \
--proto_path=. \
--python_out="$TOOLS_DIR" \
--grpc_python_out="$TOOLS_DIR" \
"$PROTO_SOURCE_DIR"/test.proto \
"$PROTO_SOURCE_DIR"/messages.proto \
"$PROTO_SOURCE_DIR"/empty.proto
bazel build //src/python/grpcio_tests/tests_py3_only/interop:xds_interop_client
GRPC_VERBOSITY=debug GRPC_TRACE=xds_client,xds_resolver,cds_lb,xds_lb "$PYTHON" \
tools/run_tests/run_xds_tests.py \
--test_case=all \
--project_id=grpc-testing \
--gcp_suffix=$(date '+%s') \
--verbose \
--client_cmd='bazel run //src/python/grpcio_tests/tests_py3_only/interop:xds_interop_client -- --server=xds-experimental:///{server_uri} --stats_port={stats_port} --qps={qps} --verbose'

@ -0,0 +1,23 @@
# Copyright 2020 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.
# Config file for the internal CI (in protobuf text format)
# Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/linux/grpc_bazel.sh"
timeout_mins: 90
env_vars {
key: "BAZEL_SCRIPT"
value: "tools/internal_ci/linux/grpc_xds_bazel_python_test_in_docker.sh"
}

@ -104,6 +104,7 @@ LANG_RELEASE_MATRIX = {
('v1.24.0', ReleaseInfo()), ('v1.24.0', ReleaseInfo()),
('v1.25.0', ReleaseInfo()), ('v1.25.0', ReleaseInfo()),
('v1.26.0', ReleaseInfo()), ('v1.26.0', ReleaseInfo()),
('v1.27.3', ReleaseInfo()),
]), ]),
'go': 'go':
OrderedDict([ OrderedDict([
@ -268,6 +269,7 @@ LANG_RELEASE_MATRIX = {
('v1.24.0', ReleaseInfo(runtimes=['python'])), ('v1.24.0', ReleaseInfo(runtimes=['python'])),
('v1.25.0', ReleaseInfo(runtimes=['python'])), ('v1.25.0', ReleaseInfo(runtimes=['python'])),
('v1.26.0', ReleaseInfo(runtimes=['python'])), ('v1.26.0', ReleaseInfo(runtimes=['python'])),
('v1.27.3', ReleaseInfo(runtimes=['python'])),
]), ]),
'node': 'node':
OrderedDict([ OrderedDict([
@ -325,6 +327,7 @@ LANG_RELEASE_MATRIX = {
# If you are not encountering the error in above issue # If you are not encountering the error in above issue
# go ahead and upload the docker image for new releases. # go ahead and upload the docker image for new releases.
('v1.26.0', ReleaseInfo()), ('v1.26.0', ReleaseInfo()),
('v1.27.3', ReleaseInfo()),
]), ]),
'php': 'php':
OrderedDict([ OrderedDict([
@ -355,6 +358,7 @@ LANG_RELEASE_MATRIX = {
('v1.24.0', ReleaseInfo()), ('v1.24.0', ReleaseInfo()),
('v1.25.0', ReleaseInfo()), ('v1.25.0', ReleaseInfo()),
('v1.26.0', ReleaseInfo()), ('v1.26.0', ReleaseInfo()),
('v1.27.3', ReleaseInfo()),
]), ]),
'csharp': 'csharp':
OrderedDict([ OrderedDict([
@ -390,5 +394,6 @@ LANG_RELEASE_MATRIX = {
('v1.24.0', ReleaseInfo()), ('v1.24.0', ReleaseInfo()),
('v1.25.0', ReleaseInfo()), ('v1.25.0', ReleaseInfo()),
('v1.26.0', ReleaseInfo()), ('v1.26.0', ReleaseInfo()),
('v1.27.3', ReleaseInfo()),
]), ]),
} }

@ -325,6 +325,19 @@ def checkout_grpc_stack(lang, release):
'%s: %s' % (str(output), commit_log), '%s: %s' % (str(output), commit_log),
do_newline=True) do_newline=True)
# git submodule update
jobset.message('START',
'git submodule update --init at %s from %s' %
(release, stack_base),
do_newline=True)
subprocess.check_call(['git', 'submodule', 'update', '--init'],
cwd=stack_base,
stderr=subprocess.STDOUT)
jobset.message('SUCCESS',
'git submodule update --init',
'%s: %s' % (str(output), commit_log),
do_newline=True)
# Write git log to commit_log so it can be packaged with the docker image. # Write git log to commit_log so it can be packaged with the docker image.
with open(os.path.join(stack_base, 'commit_log'), 'w') as f: with open(os.path.join(stack_base, 'commit_log'), 'w') as f:
f.write(commit_log) f.write(commit_log)

@ -4,9 +4,6 @@
# but try to use RBE build cache and upload results # but try to use RBE build cache and upload results
# to ResultStore # to ResultStore
# don't use port server
build --define GRPC_PORT_ISOLATED_RUNTIME=1
startup --host_jvm_args=-Dbazel.DigestFunction=SHA256 startup --host_jvm_args=-Dbazel.DigestFunction=SHA256
# remote cache is needed not only for build speedup, # remote cache is needed not only for build speedup,

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

@ -30,13 +30,9 @@ os.chdir(_ROOT)
_DEFAULT_RUNTESTS_TIMEOUT = 1 * 60 * 60 _DEFAULT_RUNTESTS_TIMEOUT = 1 * 60 * 60
# Set the timeout high to allow enough time for sanitizers and pre-building # C/C++ tests can take long time
# clang docker.
_CPP_RUNTESTS_TIMEOUT = 4 * 60 * 60 _CPP_RUNTESTS_TIMEOUT = 4 * 60 * 60
# C++ TSAN takes longer than other sanitizers
_CPP_TSAN_RUNTESTS_TIMEOUT = 8 * 60 * 60
# Set timeout high for ObjC for Cocoapods to install pods # Set timeout high for ObjC for Cocoapods to install pods
_OBJC_RUNTESTS_TIMEOUT = 90 * 60 _OBJC_RUNTESTS_TIMEOUT = 90 * 60
@ -261,35 +257,6 @@ def _create_test_jobs(extra_args=[], inner_jobs=_DEFAULT_INNER_JOBS):
inner_jobs=inner_jobs, inner_jobs=inner_jobs,
timeout_seconds=_OBJC_RUNTESTS_TIMEOUT) timeout_seconds=_OBJC_RUNTESTS_TIMEOUT)
# sanitizers
test_jobs += _generate_jobs(languages=['c'],
configs=['msan', 'asan', 'tsan', 'ubsan'],
platforms=['linux'],
arch='x64',
compiler='clang7.0',
labels=['sanitizers', 'corelang'],
extra_args=extra_args,
inner_jobs=inner_jobs,
timeout_seconds=_CPP_RUNTESTS_TIMEOUT)
test_jobs += _generate_jobs(languages=['c++'],
configs=['asan'],
platforms=['linux'],
arch='x64',
compiler='clang7.0',
labels=['sanitizers', 'corelang'],
extra_args=extra_args,
inner_jobs=inner_jobs,
timeout_seconds=_CPP_RUNTESTS_TIMEOUT)
test_jobs += _generate_jobs(languages=['c++'],
configs=['tsan'],
platforms=['linux'],
arch='x64',
compiler='clang7.0',
labels=['sanitizers', 'corelang'],
extra_args=extra_args,
inner_jobs=inner_jobs,
timeout_seconds=_CPP_TSAN_RUNTESTS_TIMEOUT)
return test_jobs return test_jobs

@ -82,7 +82,7 @@ argp.add_argument('--secondary_zone',
argp.add_argument('--qps', default=10, type=int, help='Client QPS') argp.add_argument('--qps', default=10, type=int, help='Client QPS')
argp.add_argument( argp.add_argument(
'--wait_for_backend_sec', '--wait_for_backend_sec',
default=600, default=1200,
type=int, type=int,
help='Time limit for waiting for created backend services to report ' help='Time limit for waiting for created backend services to report '
'healthy when launching or updated GCP resources') 'healthy when launching or updated GCP resources')
@ -158,6 +158,9 @@ _BOOTSTRAP_TEMPLATE = """
"id": "{node_id}", "id": "{node_id}",
"metadata": {{ "metadata": {{
"TRAFFICDIRECTOR_NETWORK_NAME": "%s" "TRAFFICDIRECTOR_NETWORK_NAME": "%s"
}},
"locality": {{
"zone": "%s"
}} }}
}}, }},
"xds_servers": [{{ "xds_servers": [{{
@ -169,7 +172,7 @@ _BOOTSTRAP_TEMPLATE = """
}} }}
] ]
}}] }}]
}}""" % (args.network.split('/')[-1], args.xds_server) }}""" % (args.network.split('/')[-1], args.zone, args.xds_server)
_PATH_MATCHER_NAME = 'path-matcher' _PATH_MATCHER_NAME = 'path-matcher'
_BASE_TEMPLATE_NAME = 'test-template' _BASE_TEMPLATE_NAME = 'test-template'
_BASE_INSTANCE_GROUP_NAME = 'test-ig' _BASE_INSTANCE_GROUP_NAME = 'test-ig'

Loading…
Cancel
Save