Merge remote-tracking branch 'upstream/master' into route_response

reviewable/pr22280/r2
Donna Dionne 5 years ago
commit 50a04f99e7
  1. 5
      .github/ISSUE_TEMPLATE/bug_report.md
  2. 2
      .github/ISSUE_TEMPLATE/cleanup_request.md
  3. 2
      .github/ISSUE_TEMPLATE/feature_request.md
  4. 2
      .github/pull_request_template.md
  5. 1
      BUILD
  6. 119
      CMakeLists.txt
  7. 122
      Makefile
  8. 6
      bazel/grpc_deps.bzl
  9. 41
      build_autogenerated.yaml
  10. 5
      doc/environment_variables.md
  11. 2
      gRPC-C++.podspec
  12. 2
      gRPC-Core.podspec
  13. 34
      include/grpcpp/opencensus.h
  14. 47
      include/grpcpp/opencensus_impl.h
  15. 104
      src/compiler/python_generator.cc
  16. 3
      src/compiler/python_private_generator.h
  17. 47
      src/core/ext/filters/client_channel/lb_policy/xds/cds.cc
  18. 81
      src/core/ext/filters/client_channel/lb_policy/xds/xds.cc
  19. 32
      src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc
  20. 711
      src/core/ext/filters/client_channel/xds/xds_api.cc
  21. 14
      src/core/ext/filters/client_channel/xds/xds_api.h
  22. 87
      src/core/ext/filters/client_channel/xds/xds_bootstrap.cc
  23. 6
      src/core/ext/filters/client_channel/xds/xds_bootstrap.h
  24. 241
      src/core/ext/filters/client_channel/xds/xds_client.cc
  25. 21
      src/core/ext/filters/client_channel/xds/xds_client.h
  26. 1
      src/core/tsi/transport_security_interface.h
  27. 65
      src/cpp/ext/filters/census/grpc_plugin.cc
  28. 32
      src/cpp/ext/filters/census/views.cc
  29. 1
      src/objective-c/tests/UnitTests/APIv2Tests.m
  30. 8
      src/python/grpcio/grpc/_cython/_cygrpc/aio/callback_common.pxd.pxi
  31. 21
      src/python/grpcio/grpc/_cython/_cygrpc/aio/callback_common.pyx.pxi
  32. 2
      src/python/grpcio/grpc/_cython/_cygrpc/aio/channel.pxd.pxi
  33. 2
      src/python/grpcio/grpc/_cython/_cygrpc/aio/channel.pyx.pxi
  34. 30
      src/python/grpcio/grpc/_cython/_cygrpc/aio/completion_queue.pxd.pxi
  35. 96
      src/python/grpcio/grpc/_cython/_cygrpc/aio/completion_queue.pyx.pxi
  36. 98
      src/python/grpcio/grpc/_cython/_cygrpc/aio/grpc_aio.pyx.pxi
  37. 11
      src/python/grpcio/grpc/_cython/_cygrpc/aio/iomgr/iomgr.pyx.pxi
  38. 27
      src/python/grpcio/grpc/_cython/_cygrpc/aio/iomgr/resolver.pyx.pxi
  39. 4
      src/python/grpcio/grpc/_cython/_cygrpc/aio/iomgr/socket.pxd.pxi
  40. 68
      src/python/grpcio/grpc/_cython/_cygrpc/aio/iomgr/socket.pyx.pxi
  41. 7
      src/python/grpcio/grpc/_cython/_cygrpc/aio/iomgr/timer.pxd.pxi
  42. 24
      src/python/grpcio/grpc/_cython/_cygrpc/aio/iomgr/timer.pyx.pxi
  43. 2
      src/python/grpcio/grpc/_cython/_cygrpc/aio/server.pxd.pxi
  44. 4
      src/python/grpcio/grpc/_cython/_cygrpc/aio/server.pyx.pxi
  45. 2
      src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi
  46. 5
      src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
  47. 1
      src/python/grpcio/grpc/_cython/cygrpc.pxd
  48. 2
      src/python/grpcio/grpc/_cython/cygrpc.pyx
  49. 35
      src/python/grpcio/grpc/_simple_stubs.py
  50. 26
      src/python/grpcio/grpc/experimental/aio/_channel.py
  51. 8
      src/python/grpcio/grpc/experimental/aio/_interceptor.py
  52. 5
      src/python/grpcio/grpc/experimental/aio/_server.py
  53. 1
      src/python/grpcio_tests/commands.py
  54. 114
      src/python/grpcio_tests/tests/protoc_plugin/_python_plugin_test.py
  55. 1
      src/python/grpcio_tests/tests/tests.json
  56. 8
      src/python/grpcio_tests/tests_aio/unit/call_test.py
  57. 14
      src/python/grpcio_tests/tests_aio/unit/server_test.py
  58. 7
      src/python/grpcio_tests/tests_py3_only/unit/_simple_stubs_test.py
  59. 13
      templates/CMakeLists.txt.template
  60. 2
      templates/gRPC-C++.podspec.template
  61. 2
      templates/gRPC-Core.podspec.template
  62. 456
      test/cpp/end2end/xds_end2end_test.cc
  63. 5
      test/cpp/interop/xds_interop_client.cc
  64. 1
      test/cpp/microbenchmarks/bm_metadata.cc
  65. 2
      third_party/abseil-cpp
  66. 2
      tools/bazel.rc
  67. 26
      tools/buildgen/extract_metadata_from_bazel_xml.py
  68. 1
      tools/internal_ci/linux/grpc_python_bazel_test_in_docker.sh
  69. 2
      tools/internal_ci/linux/grpc_xds.cfg
  70. 5
      tools/internal_ci/linux/grpc_xds_bazel_test_in_docker.sh
  71. 1
      tools/interop_matrix/client_matrix.py
  72. 108
      tools/run_tests/generated/tests.json
  73. 986
      tools/run_tests/run_xds_tests.py
  74. 2
      tools/run_tests/sanity/check_submodules.sh

@ -2,12 +2,11 @@
name: Report a bug
about: Create a report to help us improve
labels: kind/bug, priority/P2
assignees: veblush
assignees: karthikravis
---
<!--
This form is for bug reports and feature requests ONLY!
For general questions and troubleshooting, please ask/look for answers here:
- grpc.io mailing list: https://groups.google.com/forum/#!forum/grpc-io
@ -26,7 +25,7 @@ Issues specific to *grpc-java*, *grpc-go*, *grpc-node*, *grpc-dart*, *grpc-web*
### What did you do?
If possible, provide a recipe for reproducing the error. Try being specific and include code snippets if helpful.
Please provide either 1) A unit test for reproducing the bug or 2) Specific steps for us to follow to reproduce the bug. If there’s not enough information to debug the problem, gRPC team may close the issue at their discretion. You’re welcome to re-open the issue once you have a reproduction.
### What did you expect to see?

@ -2,7 +2,7 @@
name: Request a cleanup
about: Suggest a cleanup in our repository
labels: kind/internal cleanup, priority/P2
assignees: veblush
assignees: karthikravis
---

@ -2,7 +2,7 @@
name: Request a feature
about: Suggest an idea for this project
labels: kind/enhancement, priority/P2
assignees: veblush
assignees: karthikravis
---

@ -8,4 +8,4 @@ If you know who should review your pull request, please remove the mentioning be
-->
@veblush
@karthikravis

@ -2335,7 +2335,6 @@ grpc_cc_library(
],
hdrs = [
"include/grpcpp/opencensus.h",
"include/grpcpp/opencensus_impl.h",
"src/cpp/ext/filters/census/channel_filter.h",
"src/cpp/ext/filters/census/client_filter.h",
"src/cpp/ext/filters/census/context.h",

@ -152,6 +152,14 @@ if(WIN32)
set(_gRPC_PLATFORM_WINDOWS ON)
endif()
# Use C99 standard
set(CMAKE_C_STANDARD 99)
# Add c++11 flags
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
@ -201,11 +209,6 @@ include(cmake/ssl.cmake)
include(cmake/upb.cmake)
include(cmake/zlib.cmake)
if(NOT MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif()
if(_gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_IOS)
set(_gRPC_ALLTARGETS_LIBRARIES ${CMAKE_DL_LIBS} m pthread)
elseif(_gRPC_PLATFORM_ANDROID)
@ -822,6 +825,8 @@ if(gRPC_BUILD_TESTS)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_dependencies(buildtests_cxx xds_end2end_test)
endif()
add_dependencies(buildtests_cxx xds_interop_client)
add_dependencies(buildtests_cxx xds_interop_server)
add_dependencies(buildtests_cxx alts_credentials_fuzzer_one_entry)
add_dependencies(buildtests_cxx client_fuzzer_one_entry)
add_dependencies(buildtests_cxx hpack_parser_fuzzer_test_one_entry)
@ -14509,6 +14514,110 @@ endif()
endif()
if(gRPC_BUILD_TESTS)
add_executable(xds_interop_client
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/empty.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/empty.grpc.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/empty.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/empty.grpc.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/messages.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/messages.grpc.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/messages.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/messages.grpc.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/test.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/test.grpc.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/test.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/test.grpc.pb.h
test/cpp/interop/xds_interop_client.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(xds_interop_client
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
${_gRPC_SSL_INCLUDE_DIR}
${_gRPC_UPB_GENERATED_DIR}
${_gRPC_UPB_GRPC_GENERATED_DIR}
${_gRPC_UPB_INCLUDE_DIR}
${_gRPC_ZLIB_INCLUDE_DIR}
third_party/googletest/googletest/include
third_party/googletest/googletest
third_party/googletest/googlemock/include
third_party/googletest/googlemock
${_gRPC_PROTO_GENS_DIR}
)
target_link_libraries(xds_interop_client
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
grpc++
grpc++_test_config
grpc
gpr
address_sorting
upb
${_gRPC_GFLAGS_LIBRARIES}
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(xds_interop_server
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/empty.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/empty.grpc.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/empty.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/empty.grpc.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/messages.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/messages.grpc.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/messages.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/messages.grpc.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/test.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/test.grpc.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/test.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/test.grpc.pb.h
test/cpp/interop/xds_interop_server.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(xds_interop_server
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
${_gRPC_SSL_INCLUDE_DIR}
${_gRPC_UPB_GENERATED_DIR}
${_gRPC_UPB_GRPC_GENERATED_DIR}
${_gRPC_UPB_INCLUDE_DIR}
${_gRPC_ZLIB_INCLUDE_DIR}
third_party/googletest/googletest/include
third_party/googletest/googletest
third_party/googletest/googlemock/include
third_party/googletest/googlemock
${_gRPC_PROTO_GENS_DIR}
)
target_link_libraries(xds_interop_server
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
grpc++
grpc++_test_config
grpc
gpr
address_sorting
upb
${_gRPC_GFLAGS_LIBRARIES}
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(alts_credentials_fuzzer_one_entry
test/core/security/alts_credentials_fuzzer.cc
test/core/util/one_corpus_entry_fuzzer.cc

@ -1307,6 +1307,8 @@ work_serializer_test: $(BINDIR)/$(CONFIG)/work_serializer_test
writes_per_rpc_test: $(BINDIR)/$(CONFIG)/writes_per_rpc_test
xds_bootstrap_test: $(BINDIR)/$(CONFIG)/xds_bootstrap_test
xds_end2end_test: $(BINDIR)/$(CONFIG)/xds_end2end_test
xds_interop_client: $(BINDIR)/$(CONFIG)/xds_interop_client
xds_interop_server: $(BINDIR)/$(CONFIG)/xds_interop_server
boringssl_ssl_test: $(BINDIR)/$(CONFIG)/boringssl_ssl_test
boringssl_crypto_test: $(BINDIR)/$(CONFIG)/boringssl_crypto_test
alts_credentials_fuzzer_one_entry: $(BINDIR)/$(CONFIG)/alts_credentials_fuzzer_one_entry
@ -1666,6 +1668,8 @@ buildtests_cxx: privatelibs_cxx \
$(BINDIR)/$(CONFIG)/writes_per_rpc_test \
$(BINDIR)/$(CONFIG)/xds_bootstrap_test \
$(BINDIR)/$(CONFIG)/xds_end2end_test \
$(BINDIR)/$(CONFIG)/xds_interop_client \
$(BINDIR)/$(CONFIG)/xds_interop_server \
$(BINDIR)/$(CONFIG)/boringssl_ssl_test \
$(BINDIR)/$(CONFIG)/boringssl_crypto_test \
$(BINDIR)/$(CONFIG)/alts_credentials_fuzzer_one_entry \
@ -1822,6 +1826,8 @@ buildtests_cxx: privatelibs_cxx \
$(BINDIR)/$(CONFIG)/writes_per_rpc_test \
$(BINDIR)/$(CONFIG)/xds_bootstrap_test \
$(BINDIR)/$(CONFIG)/xds_end2end_test \
$(BINDIR)/$(CONFIG)/xds_interop_client \
$(BINDIR)/$(CONFIG)/xds_interop_server \
$(BINDIR)/$(CONFIG)/alts_credentials_fuzzer_one_entry \
$(BINDIR)/$(CONFIG)/client_fuzzer_one_entry \
$(BINDIR)/$(CONFIG)/hpack_parser_fuzzer_test_one_entry \
@ -2178,12 +2184,8 @@ test_cxx: buildtests_cxx
$(Q) $(BINDIR)/$(CONFIG)/bm_error || ( echo test bm_error failed ; exit 1 )
$(E) "[RUN] Testing bm_fullstack_streaming_ping_pong"
$(Q) $(BINDIR)/$(CONFIG)/bm_fullstack_streaming_ping_pong || ( echo test bm_fullstack_streaming_ping_pong failed ; exit 1 )
$(E) "[RUN] Testing bm_fullstack_streaming_pump"
$(Q) $(BINDIR)/$(CONFIG)/bm_fullstack_streaming_pump || ( echo test bm_fullstack_streaming_pump failed ; exit 1 )
$(E) "[RUN] Testing bm_fullstack_unary_ping_pong"
$(Q) $(BINDIR)/$(CONFIG)/bm_fullstack_unary_ping_pong || ( echo test bm_fullstack_unary_ping_pong failed ; exit 1 )
$(E) "[RUN] Testing bm_metadata"
$(Q) $(BINDIR)/$(CONFIG)/bm_metadata || ( echo test bm_metadata failed ; exit 1 )
$(E) "[RUN] Testing bm_pollset"
$(Q) $(BINDIR)/$(CONFIG)/bm_pollset || ( echo test bm_pollset failed ; exit 1 )
$(E) "[RUN] Testing bm_timer"
@ -2254,8 +2256,6 @@ test_cxx: buildtests_cxx
$(Q) $(BINDIR)/$(CONFIG)/grpc_tool_test || ( echo test grpc_tool_test failed ; exit 1 )
$(E) "[RUN] Testing grpclb_api_test"
$(Q) $(BINDIR)/$(CONFIG)/grpclb_api_test || ( echo test grpclb_api_test failed ; exit 1 )
$(E) "[RUN] Testing grpclb_end2end_test"
$(Q) $(BINDIR)/$(CONFIG)/grpclb_end2end_test || ( echo test grpclb_end2end_test failed ; exit 1 )
$(E) "[RUN] Testing h2_ssl_session_reuse_test"
$(Q) $(BINDIR)/$(CONFIG)/h2_ssl_session_reuse_test || ( echo test h2_ssl_session_reuse_test failed ; exit 1 )
$(E) "[RUN] Testing head_of_line_blocking_bad_client_test"
@ -2332,8 +2332,6 @@ test_cxx: buildtests_cxx
$(Q) $(BINDIR)/$(CONFIG)/service_config_end2end_test || ( echo test service_config_end2end_test failed ; exit 1 )
$(E) "[RUN] Testing service_config_test"
$(Q) $(BINDIR)/$(CONFIG)/service_config_test || ( echo test service_config_test failed ; exit 1 )
$(E) "[RUN] Testing settings_timeout_test"
$(Q) $(BINDIR)/$(CONFIG)/settings_timeout_test || ( echo test settings_timeout_test failed ; exit 1 )
$(E) "[RUN] Testing shutdown_test"
$(Q) $(BINDIR)/$(CONFIG)/shutdown_test || ( echo test shutdown_test failed ; exit 1 )
$(E) "[RUN] Testing simple_request_bad_client_test"
@ -2380,8 +2378,6 @@ test_cxx: buildtests_cxx
$(Q) $(BINDIR)/$(CONFIG)/writes_per_rpc_test || ( echo test writes_per_rpc_test failed ; exit 1 )
$(E) "[RUN] Testing xds_bootstrap_test"
$(Q) $(BINDIR)/$(CONFIG)/xds_bootstrap_test || ( echo test xds_bootstrap_test failed ; exit 1 )
$(E) "[RUN] Testing xds_end2end_test"
$(Q) $(BINDIR)/$(CONFIG)/xds_end2end_test || ( echo test xds_end2end_test failed ; exit 1 )
flaky_test_cxx: buildtests_cxx
@ -19152,6 +19148,112 @@ $(OBJDIR)/$(CONFIG)/test/cpp/end2end/test_service_impl.o: $(GENDIR)/src/proto/gr
$(OBJDIR)/$(CONFIG)/test/cpp/end2end/xds_end2end_test.o: $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/ads_for_test.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/ads_for_test.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/cds_for_test.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/cds_for_test.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/eds_for_test.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/eds_for_test.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/lds_rds_for_test.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/lds_rds_for_test.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/lrs_for_test.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/lrs_for_test.grpc.pb.cc
XDS_INTEROP_CLIENT_SRC = \
$(GENDIR)/src/proto/grpc/testing/empty.pb.cc $(GENDIR)/src/proto/grpc/testing/empty.grpc.pb.cc \
$(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc \
$(GENDIR)/src/proto/grpc/testing/test.pb.cc $(GENDIR)/src/proto/grpc/testing/test.grpc.pb.cc \
test/cpp/interop/xds_interop_client.cc \
XDS_INTEROP_CLIENT_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(XDS_INTEROP_CLIENT_SRC))))
ifeq ($(NO_SECURE),true)
# You can't build secure targets if you don't have OpenSSL.
$(BINDIR)/$(CONFIG)/xds_interop_client: openssl_dep_error
else
ifeq ($(NO_PROTOBUF),true)
# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
$(BINDIR)/$(CONFIG)/xds_interop_client: protobuf_dep_error
else
$(BINDIR)/$(CONFIG)/xds_interop_client: $(PROTOBUF_DEP) $(XDS_INTEROP_CLIENT_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libaddress_sorting.a $(LIBDIR)/$(CONFIG)/libupb.a
$(E) "[LD] Linking $@"
$(Q) mkdir -p `dirname $@`
$(Q) $(LDXX) $(LDFLAGS) $(XDS_INTEROP_CLIENT_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libaddress_sorting.a $(LIBDIR)/$(CONFIG)/libupb.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/xds_interop_client
endif
endif
$(OBJDIR)/$(CONFIG)/src/proto/grpc/testing/empty.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libaddress_sorting.a $(LIBDIR)/$(CONFIG)/libupb.a
$(OBJDIR)/$(CONFIG)/src/proto/grpc/testing/messages.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libaddress_sorting.a $(LIBDIR)/$(CONFIG)/libupb.a
$(OBJDIR)/$(CONFIG)/src/proto/grpc/testing/test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libaddress_sorting.a $(LIBDIR)/$(CONFIG)/libupb.a
$(OBJDIR)/$(CONFIG)/test/cpp/interop/xds_interop_client.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libaddress_sorting.a $(LIBDIR)/$(CONFIG)/libupb.a
deps_xds_interop_client: $(XDS_INTEROP_CLIENT_OBJS:.o=.dep)
ifneq ($(NO_SECURE),true)
ifneq ($(NO_DEPS),true)
-include $(XDS_INTEROP_CLIENT_OBJS:.o=.dep)
endif
endif
$(OBJDIR)/$(CONFIG)/test/cpp/interop/xds_interop_client.o: $(GENDIR)/src/proto/grpc/testing/empty.pb.cc $(GENDIR)/src/proto/grpc/testing/empty.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/test.pb.cc $(GENDIR)/src/proto/grpc/testing/test.grpc.pb.cc
XDS_INTEROP_SERVER_SRC = \
$(GENDIR)/src/proto/grpc/testing/empty.pb.cc $(GENDIR)/src/proto/grpc/testing/empty.grpc.pb.cc \
$(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc \
$(GENDIR)/src/proto/grpc/testing/test.pb.cc $(GENDIR)/src/proto/grpc/testing/test.grpc.pb.cc \
test/cpp/interop/xds_interop_server.cc \
XDS_INTEROP_SERVER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(XDS_INTEROP_SERVER_SRC))))
ifeq ($(NO_SECURE),true)
# You can't build secure targets if you don't have OpenSSL.
$(BINDIR)/$(CONFIG)/xds_interop_server: openssl_dep_error
else
ifeq ($(NO_PROTOBUF),true)
# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
$(BINDIR)/$(CONFIG)/xds_interop_server: protobuf_dep_error
else
$(BINDIR)/$(CONFIG)/xds_interop_server: $(PROTOBUF_DEP) $(XDS_INTEROP_SERVER_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libaddress_sorting.a $(LIBDIR)/$(CONFIG)/libupb.a
$(E) "[LD] Linking $@"
$(Q) mkdir -p `dirname $@`
$(Q) $(LDXX) $(LDFLAGS) $(XDS_INTEROP_SERVER_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libaddress_sorting.a $(LIBDIR)/$(CONFIG)/libupb.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/xds_interop_server
endif
endif
$(OBJDIR)/$(CONFIG)/src/proto/grpc/testing/empty.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libaddress_sorting.a $(LIBDIR)/$(CONFIG)/libupb.a
$(OBJDIR)/$(CONFIG)/src/proto/grpc/testing/messages.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libaddress_sorting.a $(LIBDIR)/$(CONFIG)/libupb.a
$(OBJDIR)/$(CONFIG)/src/proto/grpc/testing/test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libaddress_sorting.a $(LIBDIR)/$(CONFIG)/libupb.a
$(OBJDIR)/$(CONFIG)/test/cpp/interop/xds_interop_server.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libaddress_sorting.a $(LIBDIR)/$(CONFIG)/libupb.a
deps_xds_interop_server: $(XDS_INTEROP_SERVER_OBJS:.o=.dep)
ifneq ($(NO_SECURE),true)
ifneq ($(NO_DEPS),true)
-include $(XDS_INTEROP_SERVER_OBJS:.o=.dep)
endif
endif
$(OBJDIR)/$(CONFIG)/test/cpp/interop/xds_interop_server.o: $(GENDIR)/src/proto/grpc/testing/empty.pb.cc $(GENDIR)/src/proto/grpc/testing/empty.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/test.pb.cc $(GENDIR)/src/proto/grpc/testing/test.grpc.pb.cc
BORINGSSL_SSL_TEST_SRC = \
third_party/boringssl-with-bazel/src/crypto/test/abi_test.cc \
third_party/boringssl-with-bazel/src/crypto/test/gtest_main.cc \

@ -197,9 +197,9 @@ def grpc_deps():
if "com_google_absl" not in native.existing_rules():
http_archive(
name = "com_google_absl",
sha256 = "c14b840dc57926b8b671805426a82249e5ea0d7fddf709fd4619eb38cbb36fb5",
strip_prefix = "abseil-cpp-b832dce8489ef7b6231384909fd9b68d5a5ff2b7",
url = "https://github.com/abseil/abseil-cpp/archive/b832dce8489ef7b6231384909fd9b68d5a5ff2b7.tar.gz",
sha256 = "f368a8476f4e2e0eccf8a7318b98dafbe30b2600f4e3cf52636e5eb145aba06a",
strip_prefix = "abseil-cpp-df3ea785d8c30a9503321a3d35ee7d35808f190d",
url = "https://github.com/abseil/abseil-cpp/archive/df3ea785d8c30a9503321a3d35ee7d35808f190d.tar.gz",
)
if "bazel_toolchains" not in native.existing_rules():

@ -5128,6 +5128,7 @@ targets:
- posix
- name: bm_fullstack_streaming_pump
build: test
run: false
language: c++
headers:
- test/cpp/microbenchmarks/fullstack_streaming_pump.h
@ -5195,6 +5196,7 @@ targets:
- posix
- name: bm_metadata
build: test
run: false
language: c++
headers: []
src:
@ -6054,6 +6056,7 @@ targets:
- name: grpclb_end2end_test
gtest: true
build: test
run: false
language: c++
headers:
- test/cpp/end2end/test_service_impl.h
@ -7058,6 +7061,7 @@ targets:
- name: settings_timeout_test
gtest: true
build: test
run: false
language: c++
headers: []
src:
@ -7538,6 +7542,7 @@ targets:
- name: xds_end2end_test
gtest: true
build: test
run: false
language: c++
headers:
- test/cpp/end2end/test_service_impl.h
@ -7565,4 +7570,40 @@ targets:
- linux
- posix
- mac
- name: xds_interop_client
build: test
run: false
language: c++
headers: []
src:
- src/proto/grpc/testing/empty.proto
- src/proto/grpc/testing/messages.proto
- src/proto/grpc/testing/test.proto
- test/cpp/interop/xds_interop_client.cc
deps:
- grpc_test_util
- grpc++
- grpc++_test_config
- grpc
- gpr
- address_sorting
- upb
- name: xds_interop_server
build: test
run: false
language: c++
headers: []
src:
- src/proto/grpc/testing/empty.proto
- src/proto/grpc/testing/messages.proto
- src/proto/grpc/testing/test.proto
- test/cpp/interop/xds_interop_server.cc
deps:
- grpc_test_util
- grpc++
- grpc++_test_config
- grpc
- gpr
- address_sorting
- upb
tests: []

@ -49,6 +49,7 @@ some configuration as environment variables that can be set.
- cares_resolver - traces operations of the c-ares based DNS resolver
- cares_address_sorting - traces operations of the c-ares based DNS
resolver's resolved address sorter
- cds_lb - traces cds LB policy
- channel - traces operations on the C core channel stack
- client_channel_call - traces client channel call batch activity
- client_channel_routing - traces client channel call routing, including
@ -77,11 +78,15 @@ some configuration as environment variables that can be set.
- server_channel - lightweight trace of significant server channel events
- secure_endpoint - traces bytes flowing through encrypted channels
- subchannel - traces the connectivity state of subchannel
- subchannel_pool - traces subchannel pool
- timer - timers (alarms) in the grpc internals
- timer_check - more detailed trace of timer logic in grpc internals
- transport_security - traces metadata about secure channel establishment
- tcp - traces bytes in and out of a channel
- tsi - traces tsi transport security
- xds_client - traces xds client
- xds_lb - traces xds LB policy
- xds_resolver - traces xds resolver
The following tracers will only run in binaries built in DEBUG mode. This is
accomplished by invoking `CONFIG=dbg make <target>`

@ -214,7 +214,7 @@ Pod::Spec.new do |s|
ss.header_mappings_dir = '.'
ss.dependency "#{s.name}/Interface", version
ss.dependency 'gRPC-Core', version
abseil_version = '0.20200225.0'
abseil_version = '1.20200225.0'
ss.dependency 'abseil/container/inlined_vector', abseil_version
ss.dependency 'abseil/memory/memory', abseil_version
ss.dependency 'abseil/strings/str_format', abseil_version

@ -173,7 +173,7 @@ Pod::Spec.new do |s|
ss.libraries = 'z'
ss.dependency "#{s.name}/Interface", version
ss.dependency 'BoringSSL-GRPC', '0.0.7'
abseil_version = '0.20200225.0'
abseil_version = '1.20200225.0'
ss.dependency 'abseil/container/inlined_vector', abseil_version
ss.dependency 'abseil/memory/memory', abseil_version
ss.dependency 'abseil/strings/str_format', abseil_version

@ -19,20 +19,32 @@
#ifndef GRPCPP_OPENCENSUS_H
#define GRPCPP_OPENCENSUS_H
#include "grpcpp/opencensus_impl.h"
#include "opencensus/trace/span.h"
namespace grpc_impl {
class ServerContext;
}
namespace grpc {
// These symbols in this file will not be included in the binary unless
// grpc_opencensus_plugin build target was added as a dependency. At the moment
// it is only setup to be built with Bazel.
static inline void RegisterOpenCensusPlugin() {
::grpc_impl::RegisterOpenCensusPlugin();
}
static inline void RegisterOpenCensusViewsForExport() {
::grpc_impl::RegisterOpenCensusViewsForExport();
}
static inline ::opencensus::trace::Span GetSpanFromServerContext(
::grpc_impl::ServerContext* context) {
return ::grpc_impl::GetSpanFromServerContext(context);
}
// Registers the OpenCensus plugin with gRPC, so that it will be used for future
// RPCs. This must be called before any views are created.
void RegisterOpenCensusPlugin();
// RPC stats definitions, defined by
// https://github.com/census-instrumentation/opencensus-specs/blob/master/stats/gRPC.md
// Registers the cumulative gRPC views so that they will be exported by any
// registered stats exporter. For on-task stats, construct a View using the
// ViewDescriptors below.
void RegisterOpenCensusViewsForExport();
// Returns the tracing Span for the current RPC.
::opencensus::trace::Span GetSpanFromServerContext(
::grpc_impl::ServerContext* context);
} // namespace grpc

@ -1,47 +0,0 @@
/*
*
* Copyright 2019 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.
*
*/
#ifndef GRPCPP_OPENCENSUS_IMPL_H
#define GRPCPP_OPENCENSUS_IMPL_H
#include "opencensus/trace/span.h"
namespace grpc_impl {
class ServerContext;
// These symbols in this file will not be included in the binary unless
// grpc_opencensus_plugin build target was added as a dependency. At the moment
// it is only setup to be built with Bazel.
// Registers the OpenCensus plugin with gRPC, so that it will be used for future
// RPCs. This must be called before any views are created.
void RegisterOpenCensusPlugin();
// RPC stats definitions, defined by
// https://github.com/census-instrumentation/opencensus-specs/blob/master/stats/gRPC.md
// Registers the cumulative gRPC views so that they will be exported by any
// registered stats exporter. For on-task stats, construct a View using the
// ViewDescriptors below.
void RegisterOpenCensusViewsForExport();
// Returns the tracing Span for the current RPC.
::opencensus::trace::Span GetSpanFromServerContext(ServerContext* context);
} // namespace grpc_impl
#endif // GRPCPP_OPENCENSUS_IMPL_H

@ -70,10 +70,16 @@ typedef set<StringPair> StringPairSet;
class IndentScope {
public:
explicit IndentScope(grpc_generator::Printer* printer) : printer_(printer) {
// NOTE(rbellevi): Two-space tabs are hard-coded in the protocol compiler.
// Doubling our indents and outdents guarantees compliance with PEP8.
printer_->Indent();
printer_->Indent();
}
~IndentScope() { printer_->Outdent(); }
~IndentScope() {
printer_->Outdent();
printer_->Outdent();
}
private:
grpc_generator::Printer* printer_;
@ -92,8 +98,9 @@ void PrivateGenerator::PrintAllComments(StringVector comments,
// smarter and more sophisticated, but at the moment, if there is
// no docstring to print, we simply emit "pass" to ensure validity
// of the generated code.
out->Print("# missing associated documentation comment in .proto file\n");
out->Print("pass\n");
out->Print(
"\"\"\"Missing associated documentation comment in .proto "
"file\"\"\"\n");
return;
}
out->Print("\"\"\"");
@ -570,6 +577,93 @@ bool PrivateGenerator::PrintAddServicerToServer(
return true;
}
/* Prints out a service class used as a container for static methods pertaining
* to a class. This class has the exact name of service written in the ".proto"
* file, with no suffixes. Since this class merely acts as a namespace, it
* should never be instantiated.
*/
bool PrivateGenerator::PrintServiceClass(
const grpc::string& package_qualified_service_name,
const grpc_generator::Service* service, grpc_generator::Printer* out) {
StringMap dict;
dict["Service"] = service->name();
out->Print("\n\n");
out->Print(" # This class is part of an EXPERIMENTAL API.\n");
out->Print(dict, "class $Service$(object):\n");
{
IndentScope class_indent(out);
StringVector service_comments = service->GetAllComments();
PrintAllComments(service_comments, out);
for (int i = 0; i < service->method_count(); ++i) {
const auto& method = service->method(i);
grpc::string request_module_and_class;
if (!method->get_module_and_message_path_input(
&request_module_and_class, generator_file_name,
generate_in_pb2_grpc, config.import_prefix,
config.prefixes_to_filter)) {
return false;
}
grpc::string response_module_and_class;
if (!method->get_module_and_message_path_output(
&response_module_and_class, generator_file_name,
generate_in_pb2_grpc, config.import_prefix,
config.prefixes_to_filter)) {
return false;
}
out->Print("\n");
StringMap method_dict;
method_dict["Method"] = method->name();
out->Print("@staticmethod\n");
out->Print(method_dict, "def $Method$(");
grpc::string request_parameter(
method->ClientStreaming() ? "request_iterator" : "request");
StringMap args_dict;
args_dict["RequestParameter"] = request_parameter;
{
IndentScope args_indent(out);
IndentScope args_double_indent(out);
out->Print(args_dict, "$RequestParameter$,\n");
out->Print("target,\n");
out->Print("options=(),\n");
out->Print("channel_credentials=None,\n");
out->Print("call_credentials=None,\n");
out->Print("compression=None,\n");
out->Print("wait_for_ready=None,\n");
out->Print("timeout=None,\n");
out->Print("metadata=None):\n");
}
{
IndentScope method_indent(out);
grpc::string arity_method_name =
grpc::string(method->ClientStreaming() ? "stream" : "unary") + "_" +
grpc::string(method->ServerStreaming() ? "stream" : "unary");
args_dict["ArityMethodName"] = arity_method_name;
args_dict["PackageQualifiedService"] = package_qualified_service_name;
args_dict["Method"] = method->name();
out->Print(args_dict,
"return "
"grpc.experimental.$ArityMethodName$($RequestParameter$, "
"target, '/$PackageQualifiedService$/$Method$',\n");
{
IndentScope continuation_indent(out);
StringMap serializer_dict;
serializer_dict["RequestModuleAndClass"] = request_module_and_class;
serializer_dict["ResponseModuleAndClass"] = response_module_and_class;
out->Print(serializer_dict,
"$RequestModuleAndClass$.SerializeToString,\n");
out->Print(serializer_dict, "$ResponseModuleAndClass$.FromString,\n");
out->Print("options, channel_credentials,\n");
out->Print(
"call_credentials, compression, wait_for_ready, timeout, "
"metadata)\n");
}
}
}
}
// TODO(rbellevi): Add methods pertinent to the server side as well.
return true;
}
bool PrivateGenerator::PrintBetaPreamble(grpc_generator::Printer* out) {
StringMap var;
var["Package"] = config.beta_package_root;
@ -646,7 +740,9 @@ bool PrivateGenerator::PrintGAServices(grpc_generator::Printer* out) {
if (!(PrintStub(package_qualified_service_name, service.get(), out) &&
PrintServicer(service.get(), out) &&
PrintAddServicerToServer(package_qualified_service_name,
service.get(), out))) {
service.get(), out) &&
PrintServiceClass(package_qualified_service_name, service.get(),
out))) {
return false;
}
}

@ -59,6 +59,9 @@ struct PrivateGenerator {
const grpc_generator::Service* service,
grpc_generator::Printer* out);
bool PrintServiceClass(const grpc::string& package_qualified_service_name,
const grpc_generator::Service* service,
grpc_generator::Printer* out);
bool PrintBetaServicer(const grpc_generator::Service* service,
grpc_generator::Printer* out);
bool PrintBetaServerFactory(

@ -113,8 +113,14 @@ class CdsLb : public LoadBalancingPolicy {
void CdsLb::ClusterWatcher::OnClusterChanged(XdsApi::CdsUpdate cluster_data) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_cds_lb_trace)) {
gpr_log(GPR_INFO, "[cdslb %p] received CDS update from xds client",
parent_.get());
gpr_log(GPR_INFO,
"[cdslb %p] received CDS update from xds client %p: "
"eds_service_name=%s lrs_load_reporting_server_name=%s",
parent_.get(), parent_->xds_client_.get(),
cluster_data.eds_service_name.c_str(),
cluster_data.lrs_load_reporting_server_name.has_value()
? cluster_data.lrs_load_reporting_server_name.value().c_str()
: "(unset)");
}
// Construct config for child policy.
Json::Object child_config = {
@ -152,9 +158,18 @@ void CdsLb::ClusterWatcher::OnClusterChanged(XdsApi::CdsUpdate cluster_data) {
parent_->child_policy_ =
LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(
"xds_experimental", std::move(args));
if (parent_->child_policy_ == nullptr) {
OnError(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"failed to create xds_experimental child policy"));
return;
}
grpc_pollset_set_add_pollset_set(
parent_->child_policy_->interested_parties(),
parent_->interested_parties());
if (GRPC_TRACE_FLAG_ENABLED(grpc_cds_lb_trace)) {
gpr_log(GPR_INFO, "[cdslb %p] created child policy xds_experimental (%p)",
parent_.get(), parent_->child_policy_.get());
}
}
// Update child policy.
UpdateArgs args;
@ -220,9 +235,9 @@ void CdsLb::Helper::AddTraceEvent(TraceSeverity severity, StringView message) {
CdsLb::CdsLb(Args args)
: LoadBalancingPolicy(std::move(args)),
xds_client_(XdsClient::GetFromChannelArgs(*args.args)) {
if (xds_client_ != nullptr && GRPC_TRACE_FLAG_ENABLED(grpc_cds_lb_trace)) {
gpr_log(GPR_INFO, "[cdslb %p] Using xds client %p from channel", this,
xds_client_.get());
if (GRPC_TRACE_FLAG_ENABLED(grpc_cds_lb_trace)) {
gpr_log(GPR_INFO, "[cdslb %p] created -- using xds client %p from channel",
this, xds_client_.get());
}
}
@ -245,6 +260,10 @@ void CdsLb::ShutdownLocked() {
}
if (xds_client_ != nullptr) {
if (cluster_watcher_ != nullptr) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_cds_lb_trace)) {
gpr_log(GPR_INFO, "[cdslb %p] cancelling watch for cluster %s", this,
config_->cluster().c_str());
}
xds_client_->CancelClusterDataWatch(
StringView(config_->cluster().c_str()), cluster_watcher_);
}
@ -257,12 +276,13 @@ void CdsLb::ResetBackoffLocked() {
}
void CdsLb::UpdateLocked(UpdateArgs args) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_cds_lb_trace)) {
gpr_log(GPR_INFO, "[cdslb %p] received update", this);
}
// Update config.
auto old_config = std::move(config_);
config_ = std::move(args.config);
if (GRPC_TRACE_FLAG_ENABLED(grpc_cds_lb_trace)) {
gpr_log(GPR_INFO, "[cdslb %p] received update: cluster=%s", this,
config_->cluster().c_str());
}
// Update args.
grpc_channel_args_destroy(args_);
args_ = args.args;
@ -270,8 +290,17 @@ void CdsLb::UpdateLocked(UpdateArgs args) {
// If cluster name changed, cancel watcher and restart.
if (old_config == nullptr || old_config->cluster() != config_->cluster()) {
if (old_config != nullptr) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_cds_lb_trace)) {
gpr_log(GPR_INFO, "[cdslb %p] cancelling watch for cluster %s", this,
old_config->cluster().c_str());
}
xds_client_->CancelClusterDataWatch(
StringView(old_config->cluster().c_str()), cluster_watcher_);
StringView(old_config->cluster().c_str()), cluster_watcher_,
/*delay_unsubscription=*/true);
}
if (GRPC_TRACE_FLAG_ENABLED(grpc_cds_lb_trace)) {
gpr_log(GPR_INFO, "[cdslb %p] starting watch for cluster %s", this,
config_->cluster().c_str());
}
auto watcher = absl::make_unique<ClusterWatcher>(Ref());
cluster_watcher_ = watcher.get();

@ -69,7 +69,7 @@
namespace grpc_core {
TraceFlag grpc_lb_xds_trace(false, "xds");
TraceFlag grpc_lb_xds_trace(false, "xds_lb");
namespace {
@ -298,7 +298,7 @@ class XdsLb : public LoadBalancingPolicy {
~LocalityMap() { xds_policy_.reset(DEBUG_LOCATION, "LocalityMap"); }
void UpdateLocked(
const XdsApi::PriorityListUpdate::LocalityMap& locality_map_update,
const XdsApi::PriorityListUpdate::LocalityMap& priority_update,
bool update_locality_stats);
void ResetBackoffLocked();
void UpdateXdsPickerLocked();
@ -619,6 +619,9 @@ class XdsLb::EndpointWatcher : public XdsClient::EndpointWatcherInterface {
if (strstr(grpc_error_string(error), "xds call failed")) {
xds_policy_->channel_control_helper()->RequestReresolution();
}
} else if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
gpr_log(GPR_INFO, "[xdslb %p] xds watcher reported error (ignoring): %s",
xds_policy_.get(), grpc_error_string(error));
}
GRPC_ERROR_UNREF(error);
}
@ -643,9 +646,8 @@ XdsLb::XdsLb(Args args)
locality_map_failover_timeout_ms_(grpc_channel_args_find_integer(
args.args, GRPC_ARG_XDS_FAILOVER_TIMEOUT_MS,
{GRPC_XDS_DEFAULT_FAILOVER_TIMEOUT_MS, 0, INT_MAX})) {
if (xds_client_from_channel_ != nullptr &&
GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
gpr_log(GPR_INFO, "[xdslb %p] Using xds client %p from channel", this,
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
gpr_log(GPR_INFO, "[xdslb %p] created -- xds client from channel: %p", this,
xds_client_from_channel_.get());
}
// Record server name.
@ -687,6 +689,10 @@ void XdsLb::ShutdownLocked() {
// destroying the Xds client leading to a situation where the Xds lb policy is
// never destroyed.
if (xds_client_from_channel_ != nullptr) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
gpr_log(GPR_INFO, "[xdslb %p] cancelling watch for %s", this,
eds_service_name());
}
xds_client()->CancelEndpointDataWatch(StringView(eds_service_name()),
endpoint_watcher_);
xds_client_from_channel_.reset();
@ -781,8 +787,17 @@ void XdsLb::UpdateLocked(UpdateArgs args) {
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_);
endpoint_watcher_,
/*delay_unsubscription=*/true);
}
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
gpr_log(GPR_INFO, "[xdslb %p] starting watch for %s", this,
eds_service_name());
}
auto watcher = absl::make_unique<EndpointWatcher>(
Ref(DEBUG_LOCATION, "EndpointWatcher"));
@ -1018,7 +1033,7 @@ XdsLb::LocalityMap::LocalityMap(RefCountedPtr<XdsLb> xds_policy,
}
void XdsLb::LocalityMap::UpdateLocked(
const XdsApi::PriorityListUpdate::LocalityMap& locality_map_update,
const XdsApi::PriorityListUpdate::LocalityMap& priority_update,
bool update_locality_stats) {
if (xds_policy_->shutting_down_) return;
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
@ -1028,11 +1043,11 @@ void XdsLb::LocalityMap::UpdateLocked(
// Maybe reactivate the locality map in case all the active locality maps have
// failed.
MaybeReactivateLocked();
// Remove (later) the localities not in locality_map_update.
// Remove (later) the localities not in priority_update.
for (auto iter = localities_.begin(); iter != localities_.end();) {
const auto& name = iter->first;
Locality* locality = iter->second.get();
if (locality_map_update.Contains(name)) {
if (priority_update.Contains(name)) {
++iter;
continue;
}
@ -1043,8 +1058,8 @@ void XdsLb::LocalityMap::UpdateLocked(
++iter;
}
}
// Add or update the localities in locality_map_update.
for (const auto& p : locality_map_update.localities) {
// Add or update the localities in priority_update.
for (const auto& p : priority_update.localities) {
const auto& name = p.first;
const auto& locality_update = p.second;
OrphanablePtr<Locality>& locality = localities_[name];
@ -1064,6 +1079,32 @@ void XdsLb::LocalityMap::UpdateLocked(
locality->UpdateLocked(locality_update.lb_weight,
locality_update.serverlist, update_locality_stats);
}
// If this is the current priority and we removed all of the READY
// localities, go into state CONNECTING.
// TODO(roth): Ideally, we should model this as a graceful policy
// switch: we should keep using the old localities for a short period
// of time, long enough to give the new localities a chance to get
// connected. As part of refactoring this policy, we should try to
// fix that.
if (priority_ == xds_policy()->current_priority_) {
bool found_ready = false;
for (auto& p : localities_) {
const auto& locality_name = p.first;
Locality* locality = p.second.get();
if (!locality_map_update()->Contains(locality_name)) continue;
if (locality->connectivity_state() == GRPC_CHANNEL_READY) {
found_ready = true;
break;
}
}
if (!found_ready) {
xds_policy_->channel_control_helper()->UpdateState(
GRPC_CHANNEL_CONNECTING,
absl::make_unique<QueuePicker>(
xds_policy_->Ref(DEBUG_LOCATION, "QueuePicker")));
xds_policy_->current_priority_ = UINT32_MAX;
}
}
}
void XdsLb::LocalityMap::ResetBackoffLocked() {
@ -1071,6 +1112,9 @@ void XdsLb::LocalityMap::ResetBackoffLocked() {
}
void XdsLb::LocalityMap::UpdateXdsPickerLocked() {
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
gpr_log(GPR_INFO, "[xdslb %p] constructing new picker", xds_policy());
}
// Construct a new xds picker which maintains a map of all locality pickers
// that are ready. Each locality is represented by a portion of the range
// proportional to its weight, such that the total range is the sum of the
@ -1081,11 +1125,18 @@ void XdsLb::LocalityMap::UpdateXdsPickerLocked() {
const auto& locality_name = p.first;
Locality* locality = p.second.get();
// Skip the localities that are not in the latest locality map update.
if (!locality_map_update()->Contains(locality_name)) continue;
const auto* locality_update = locality_map_update();
if (locality_update == nullptr) continue;
if (!locality_update->Contains(locality_name)) continue;
if (locality->connectivity_state() != GRPC_CHANNEL_READY) continue;
end += locality->weight();
picker_list.push_back(
std::make_pair(end, locality->GetLoadReportingPicker()));
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
gpr_log(GPR_INFO, "[xdslb %p] locality=%s weight=%d picker=%p",
xds_policy(), locality_name->AsHumanReadableString(),
locality->weight(), picker_list.back().second.get());
}
}
xds_policy()->channel_control_helper()->UpdateState(
GRPC_CHANNEL_READY,
@ -1492,6 +1543,12 @@ XdsLb::LocalityMap::Locality::Helper::CreateSubchannel(
void XdsLb::LocalityMap::Locality::Helper::UpdateState(
grpc_connectivity_state state, std::unique_ptr<SubchannelPicker> picker) {
if (locality_->xds_policy()->shutting_down_) return;
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
gpr_log(GPR_INFO,
"[xdslb %p helper %p] child policy handler %p reports state=%s",
locality_->xds_policy(), this, locality_->child_policy_.get(),
ConnectivityStateName(state));
}
// Cache the state and picker in the locality.
locality_->connectivity_state_ = state;
locality_->picker_wrapper_ =

@ -24,6 +24,8 @@
namespace grpc_core {
TraceFlag grpc_xds_resolver_trace(false, "xds_resolver");
namespace {
//
@ -38,14 +40,28 @@ class XdsResolver : public Resolver {
interested_parties_(args.pollset_set) {
char* path = args.uri->path;
if (path[0] == '/') ++path;
server_name_.reset(gpr_strdup(path));
server_name_ = path;
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
gpr_log(GPR_INFO, "[xds_resolver %p] created for server name %s", this,
server_name_.c_str());
}
}
~XdsResolver() override { grpc_channel_args_destroy(args_); }
~XdsResolver() override {
grpc_channel_args_destroy(args_);
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
gpr_log(GPR_INFO, "[xds_resolver %p] destroyed", this);
}
}
void StartLocked() override;
void ShutdownLocked() override { xds_client_.reset(); }
void ShutdownLocked() override {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
gpr_log(GPR_INFO, "[xds_resolver %p] shutting down", this);
}
xds_client_.reset();
}
private:
class ServiceConfigWatcher : public XdsClient::ServiceConfigWatcherInterface {
@ -60,7 +76,7 @@ class XdsResolver : public Resolver {
RefCountedPtr<XdsResolver> resolver_;
};
grpc_core::UniquePtr<char> server_name_;
std::string server_name_;
const grpc_channel_args* args_;
grpc_pollset_set* interested_parties_;
OrphanablePtr<XdsClient> xds_client_;
@ -69,6 +85,10 @@ class XdsResolver : public Resolver {
void XdsResolver::ServiceConfigWatcher::OnServiceConfigChanged(
RefCountedPtr<ServiceConfig> service_config) {
if (resolver_->xds_client_ == nullptr) return;
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
gpr_log(GPR_INFO, "[xds_resolver %p] received updated service config: %s",
resolver_.get(), service_config->json_string().c_str());
}
grpc_arg xds_client_arg = resolver_->xds_client_->MakeChannelArg();
Result result;
result.args =
@ -79,6 +99,8 @@ void XdsResolver::ServiceConfigWatcher::OnServiceConfigChanged(
void XdsResolver::ServiceConfigWatcher::OnError(grpc_error* error) {
if (resolver_->xds_client_ == nullptr) return;
gpr_log(GPR_ERROR, "[xds_resolver %p] received error: %s", resolver_.get(),
grpc_error_string(error));
grpc_arg xds_client_arg = resolver_->xds_client_->MakeChannelArg();
Result result;
result.args =
@ -90,7 +112,7 @@ void XdsResolver::ServiceConfigWatcher::OnError(grpc_error* error) {
void XdsResolver::StartLocked() {
grpc_error* error = GRPC_ERROR_NONE;
xds_client_ = MakeOrphanable<XdsClient>(
combiner(), interested_parties_, StringView(server_name_.get()),
combiner(), interested_parties_, server_name_,
absl::make_unique<ServiceConfigWatcher>(Ref()), *args_, &error);
if (error != GRPC_ERROR_NONE) {
gpr_log(GPR_ERROR,

@ -23,6 +23,7 @@
#include <cstdlib>
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
#include <grpc/impl/codegen/log.h>
#include <grpc/support/alloc.h>
@ -125,8 +126,11 @@ const char* XdsApi::kCdsTypeUrl = "type.googleapis.com/envoy.api.v2.Cluster";
const char* XdsApi::kEdsTypeUrl =
"type.googleapis.com/envoy.api.v2.ClusterLoadAssignment";
XdsApi::XdsApi(const XdsBootstrap::Node* node)
: node_(node),
XdsApi::XdsApi(XdsClient* client, TraceFlag* tracer,
const XdsBootstrap::Node* node)
: client_(client),
tracer_(tracer),
node_(node),
build_version_(absl::StrCat("gRPC C-core ", GPR_PLATFORM_STRING, " ",
grpc_version_string())),
user_agent_name_(absl::StrCat("gRPC C-core ", GPR_PLATFORM_STRING)) {}
@ -289,6 +293,162 @@ envoy_api_v2_DiscoveryRequest* CreateDiscoveryRequest(
return request;
}
inline absl::string_view UpbStringToAbsl(const upb_strview& str) {
return absl::string_view(str.data, str.size);
}
inline void AddStringField(const char* name, const upb_strview& value,
std::vector<std::string>* fields,
bool add_if_empty = false) {
if (value.size > 0 || add_if_empty) {
fields->emplace_back(
absl::StrCat(name, ": \"", UpbStringToAbsl(value), "\""));
}
}
inline void AddLocalityField(int indent_level,
const envoy_api_v2_core_Locality* locality,
std::vector<std::string>* fields) {
std::string indent =
absl::StrJoin(std::vector<std::string>(indent_level, " "), "");
// region
std::string field = absl::StrCat(indent, "region");
AddStringField(field.c_str(), envoy_api_v2_core_Locality_region(locality),
fields);
// zone
field = absl::StrCat(indent, "zone");
AddStringField(field.c_str(), envoy_api_v2_core_Locality_zone(locality),
fields);
// sub_zone
field = absl::StrCat(indent, "sub_zone");
AddStringField(field.c_str(), envoy_api_v2_core_Locality_sub_zone(locality),
fields);
}
void AddNodeLogFields(const envoy_api_v2_core_Node* node,
std::vector<std::string>* fields) {
fields->emplace_back("node {");
// id
AddStringField(" id", envoy_api_v2_core_Node_id(node), fields);
// metadata
const google_protobuf_Struct* metadata =
envoy_api_v2_core_Node_metadata(node);
if (metadata != nullptr) {
fields->emplace_back(" metadata {");
size_t num_entries;
const google_protobuf_Struct_FieldsEntry* const* entries =
google_protobuf_Struct_fields(metadata, &num_entries);
for (size_t i = 0; i < num_entries; ++i) {
fields->emplace_back(" field {");
// key
AddStringField(" key",
google_protobuf_Struct_FieldsEntry_key(entries[i]),
fields);
// value
const google_protobuf_Value* value =
google_protobuf_Struct_FieldsEntry_value(entries[i]);
if (value != nullptr) {
std::string value_str;
if (google_protobuf_Value_has_string_value(value)) {
value_str = absl::StrCat(
"string_value: \"",
UpbStringToAbsl(google_protobuf_Value_string_value(value)), "\"");
} else if (google_protobuf_Value_has_null_value(value)) {
value_str = "null_value: NULL_VALUE";
} else if (google_protobuf_Value_has_number_value(value)) {
value_str = absl::StrCat("double_value: ",
google_protobuf_Value_number_value(value));
} else if (google_protobuf_Value_has_bool_value(value)) {
value_str = absl::StrCat("bool_value: ",
google_protobuf_Value_bool_value(value));
} else if (google_protobuf_Value_has_struct_value(value)) {
value_str = "struct_value: <not printed>";
} else if (google_protobuf_Value_has_list_value(value)) {
value_str = "list_value: <not printed>";
} else {
value_str = "<unknown>";
}
fields->emplace_back(absl::StrCat(" value { ", value_str, " }"));
}
fields->emplace_back(" }");
}
fields->emplace_back(" }");
}
// locality
const envoy_api_v2_core_Locality* locality =
envoy_api_v2_core_Node_locality(node);
if (locality != nullptr) {
fields->emplace_back(" locality {");
AddLocalityField(2, locality, fields);
fields->emplace_back(" }");
}
// build_version
AddStringField(" build_version", envoy_api_v2_core_Node_build_version(node),
fields);
// user_agent_name
AddStringField(" user_agent_name",
envoy_api_v2_core_Node_user_agent_name(node), fields);
// user_agent_version
AddStringField(" user_agent_version",
envoy_api_v2_core_Node_user_agent_version(node), fields);
// client_features
size_t num_client_features;
const upb_strview* client_features =
envoy_api_v2_core_Node_client_features(node, &num_client_features);
for (size_t i = 0; i < num_client_features; ++i) {
AddStringField(" client_features", client_features[i], fields);
}
fields->emplace_back("}");
}
void MaybeLogDiscoveryRequest(XdsClient* client, TraceFlag* tracer,
const envoy_api_v2_DiscoveryRequest* request) {
if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
// TODO(roth): When we can upgrade upb, use upb textformat code to dump
// the raw proto instead of doing this manually.
std::vector<std::string> fields;
// version_info
AddStringField("version_info",
envoy_api_v2_DiscoveryRequest_version_info(request),
&fields);
// node
const envoy_api_v2_core_Node* node =
envoy_api_v2_DiscoveryRequest_node(request);
if (node != nullptr) AddNodeLogFields(node, &fields);
// resource_names
size_t num_resource_names;
const upb_strview* resource_names =
envoy_api_v2_DiscoveryRequest_resource_names(request,
&num_resource_names);
for (size_t i = 0; i < num_resource_names; ++i) {
AddStringField("resource_names", resource_names[i], &fields);
}
// type_url
AddStringField("type_url", envoy_api_v2_DiscoveryRequest_type_url(request),
&fields);
// response_nonce
AddStringField("response_nonce",
envoy_api_v2_DiscoveryRequest_response_nonce(request),
&fields);
// error_detail
const struct google_rpc_Status* error_detail =
envoy_api_v2_DiscoveryRequest_error_detail(request);
if (error_detail != nullptr) {
fields.emplace_back("error_detail {");
// code
int32_t code = google_rpc_Status_code(error_detail);
if (code != 0) fields.emplace_back(absl::StrCat(" code: ", code));
// message
AddStringField(" message", google_rpc_Status_message(error_detail),
&fields);
fields.emplace_back("}");
}
gpr_log(GPR_DEBUG, "[xds_client %p] constructed ADS request: %s", client,
absl::StrJoin(fields, "\n").c_str());
}
}
grpc_slice SerializeDiscoveryRequest(upb_arena* arena,
envoy_api_v2_DiscoveryRequest* request) {
size_t output_length;
@ -305,6 +465,7 @@ grpc_slice XdsApi::CreateUnsupportedTypeNackRequest(const std::string& type_url,
upb::Arena arena;
envoy_api_v2_DiscoveryRequest* request = CreateDiscoveryRequest(
arena.ptr(), type_url.c_str(), /*version=*/"", nonce, error);
MaybeLogDiscoveryRequest(client_, tracer_, request);
return SerializeDiscoveryRequest(arena.ptr(), request);
}
@ -326,6 +487,7 @@ grpc_slice XdsApi::CreateLdsRequest(const std::string& server_name,
envoy_api_v2_DiscoveryRequest_add_resource_names(
request, upb_strview_make(server_name.data(), server_name.size()),
arena.ptr());
MaybeLogDiscoveryRequest(client_, tracer_, request);
return SerializeDiscoveryRequest(arena.ptr(), request);
}
@ -348,6 +510,7 @@ grpc_slice XdsApi::CreateRdsRequest(const std::string& route_config_name,
request,
upb_strview_make(route_config_name.data(), route_config_name.size()),
arena.ptr());
MaybeLogDiscoveryRequest(client_, tracer_, request);
return SerializeDiscoveryRequest(arena.ptr(), request);
}
@ -371,6 +534,7 @@ grpc_slice XdsApi::CreateCdsRequest(const std::set<StringView>& cluster_names,
request, upb_strview_make(cluster_name.data(), cluster_name.size()),
arena.ptr());
}
MaybeLogDiscoveryRequest(client_, tracer_, request);
return SerializeDiscoveryRequest(arena.ptr(), request);
}
@ -394,11 +558,347 @@ grpc_slice XdsApi::CreateEdsRequest(
upb_strview_make(eds_service_name.data(), eds_service_name.size()),
arena.ptr());
}
MaybeLogDiscoveryRequest(client_, tracer_, request);
return SerializeDiscoveryRequest(arena.ptr(), request);
}
namespace {
void MaybeLogDiscoveryResponse(XdsClient* client, TraceFlag* tracer,
const envoy_api_v2_DiscoveryResponse* response) {
if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
// TODO(roth): When we can upgrade upb, use upb textformat code to dump
// the raw proto instead of doing this manually.
std::vector<std::string> fields;
// version_info
AddStringField("version_info",
envoy_api_v2_DiscoveryResponse_version_info(response),
&fields);
// resources
size_t num_resources;
envoy_api_v2_DiscoveryResponse_resources(response, &num_resources);
fields.emplace_back(
absl::StrCat("resources: <", num_resources, " element(s)>"));
// type_url
AddStringField("type_url",
envoy_api_v2_DiscoveryResponse_type_url(response), &fields);
// nonce
AddStringField("nonce", envoy_api_v2_DiscoveryResponse_nonce(response),
&fields);
gpr_log(GPR_DEBUG, "[xds_client %p] received response: %s", client,
absl::StrJoin(fields, "\n").c_str());
}
}
void MaybeLogRouteConfiguration(
XdsClient* client, TraceFlag* tracer,
const envoy_api_v2_RouteConfiguration* route_config) {
if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
// TODO(roth): When we can upgrade upb, use upb textformat code to dump
// the raw proto instead of doing this manually.
std::vector<std::string> fields;
// name
AddStringField("name", envoy_api_v2_RouteConfiguration_name(route_config),
&fields);
// virtual_hosts
size_t num_virtual_hosts;
const envoy_api_v2_route_VirtualHost* const* virtual_hosts =
envoy_api_v2_RouteConfiguration_virtual_hosts(route_config,
&num_virtual_hosts);
for (size_t i = 0; i < num_virtual_hosts; ++i) {
const auto* virtual_host = virtual_hosts[i];
fields.push_back("virtual_hosts {");
// name
AddStringField(
" name", envoy_api_v2_route_VirtualHost_name(virtual_host), &fields);
// domains
size_t num_domains;
const upb_strview* const domains =
envoy_api_v2_route_VirtualHost_domains(virtual_host, &num_domains);
for (size_t j = 0; j < num_domains; ++j) {
AddStringField(" domains", domains[j], &fields);
}
// routes
size_t num_routes;
const envoy_api_v2_route_Route* const* routes =
envoy_api_v2_route_VirtualHost_routes(virtual_host, &num_routes);
for (size_t j = 0; j < num_routes; ++j) {
const auto* route = routes[j];
fields.push_back(" route {");
// name
AddStringField(" name", envoy_api_v2_route_Route_name(route),
&fields);
// match
const envoy_api_v2_route_RouteMatch* match =
envoy_api_v2_route_Route_match(route);
if (match != nullptr) {
fields.emplace_back(" match {");
// path matching
if (envoy_api_v2_route_RouteMatch_has_prefix(match)) {
AddStringField(" prefix",
envoy_api_v2_route_RouteMatch_prefix(match), &fields,
/*add_if_empty=*/true);
} else if (envoy_api_v2_route_RouteMatch_has_path(match)) {
AddStringField(" path",
envoy_api_v2_route_RouteMatch_path(match), &fields,
/*add_if_empty=*/true);
} else if (envoy_api_v2_route_RouteMatch_has_regex(match)) {
AddStringField(" regex",
envoy_api_v2_route_RouteMatch_regex(match), &fields,
/*add_if_empty=*/true);
} else if (envoy_api_v2_route_RouteMatch_has_safe_regex(match)) {
fields.emplace_back(" safe_regex: <not printed>");
} else {
fields.emplace_back(" <unknown path matching type>");
}
// header matching
size_t num_headers;
envoy_api_v2_route_RouteMatch_headers(match, &num_headers);
if (num_headers > 0) {
fields.emplace_back(
absl::StrCat(" headers: <", num_headers, " element(s)>"));
}
fields.emplace_back(" }");
}
// action
if (envoy_api_v2_route_Route_has_route(route)) {
const envoy_api_v2_route_RouteAction* action =
envoy_api_v2_route_Route_route(route);
fields.emplace_back(" route {");
if (envoy_api_v2_route_RouteAction_has_cluster(action)) {
AddStringField(" cluster",
envoy_api_v2_route_RouteAction_cluster(action),
&fields);
} else if (envoy_api_v2_route_RouteAction_has_cluster_header(
action)) {
AddStringField(
" cluster_header",
envoy_api_v2_route_RouteAction_cluster_header(action), &fields);
} else if (envoy_api_v2_route_RouteAction_has_weighted_clusters(
action)) {
fields.emplace_back(" weighted_clusters: <not printed>");
}
fields.emplace_back(" }");
} else if (envoy_api_v2_route_Route_has_redirect(route)) {
fields.emplace_back(" redirect: <not printed>");
} else if (envoy_api_v2_route_Route_has_direct_response(route)) {
fields.emplace_back(" direct_response: <not printed>");
} else if (envoy_api_v2_route_Route_has_filter_action(route)) {
fields.emplace_back(" filter_action: <not printed>");
}
fields.push_back(" }");
}
fields.push_back("}");
}
gpr_log(GPR_DEBUG, "[xds_client %p] RouteConfiguration: %s", client,
absl::StrJoin(fields, "\n").c_str());
}
}
void MaybeLogCluster(XdsClient* client, TraceFlag* tracer,
const envoy_api_v2_Cluster* cluster) {
if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
// TODO(roth): When we can upgrade upb, use upb textformat code to dump
// the raw proto instead of doing this manually.
std::vector<std::string> fields;
// name
AddStringField("name", envoy_api_v2_Cluster_name(cluster), &fields);
// type
if (envoy_api_v2_Cluster_has_type(cluster)) {
fields.emplace_back(
absl::StrCat("type: ", envoy_api_v2_Cluster_type(cluster)));
} else if (envoy_api_v2_Cluster_has_cluster_type(cluster)) {
fields.emplace_back("cluster_type: <not printed>");
} else {
fields.emplace_back("<unknown type>");
}
// eds_cluster_config
const envoy_api_v2_Cluster_EdsClusterConfig* eds_cluster_config =
envoy_api_v2_Cluster_eds_cluster_config(cluster);
if (eds_cluster_config != nullptr) {
fields.emplace_back("eds_cluster_config {");
// eds_config
const struct envoy_api_v2_core_ConfigSource* eds_config =
envoy_api_v2_Cluster_EdsClusterConfig_eds_config(eds_cluster_config);
if (eds_config != nullptr) {
if (envoy_api_v2_core_ConfigSource_has_ads(eds_config)) {
fields.emplace_back(" eds_config { ads {} }");
} else {
fields.emplace_back(" eds_config: <non-ADS type>");
}
}
// service_name
AddStringField(" service_name",
envoy_api_v2_Cluster_EdsClusterConfig_service_name(
eds_cluster_config),
&fields);
fields.emplace_back("}");
}
// lb_policy
fields.emplace_back(
absl::StrCat("lb_policy: ", envoy_api_v2_Cluster_lb_policy(cluster)));
// lrs_server
const envoy_api_v2_core_ConfigSource* lrs_server =
envoy_api_v2_Cluster_lrs_server(cluster);
if (lrs_server != nullptr) {
if (envoy_api_v2_core_ConfigSource_has_self(lrs_server)) {
fields.emplace_back("lrs_server { self {} }");
} else {
fields.emplace_back("lrs_server: <non-self type>");
}
}
gpr_log(GPR_DEBUG, "[xds_client %p] Cluster: %s", client,
absl::StrJoin(fields, "\n").c_str());
}
}
void MaybeLogClusterLoadAssignment(
XdsClient* client, TraceFlag* tracer,
const envoy_api_v2_ClusterLoadAssignment* cla) {
if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
// TODO(roth): When we can upgrade upb, use upb textformat code to dump
// the raw proto instead of doing this manually.
std::vector<std::string> fields;
// cluster_name
AddStringField("cluster_name",
envoy_api_v2_ClusterLoadAssignment_cluster_name(cla),
&fields);
// endpoints
size_t num_localities;
const struct envoy_api_v2_endpoint_LocalityLbEndpoints* const*
locality_endpoints =
envoy_api_v2_ClusterLoadAssignment_endpoints(cla, &num_localities);
for (size_t i = 0; i < num_localities; ++i) {
const auto* locality_endpoint = locality_endpoints[i];
fields.emplace_back("endpoints {");
// locality
const auto* locality =
envoy_api_v2_endpoint_LocalityLbEndpoints_locality(locality_endpoint);
if (locality != nullptr) {
fields.emplace_back(" locality {");
AddLocalityField(2, locality, &fields);
fields.emplace_back(" }");
}
// lb_endpoints
size_t num_lb_endpoints;
const envoy_api_v2_endpoint_LbEndpoint* const* lb_endpoints =
envoy_api_v2_endpoint_LocalityLbEndpoints_lb_endpoints(
locality_endpoint, &num_lb_endpoints);
for (size_t j = 0; j < num_lb_endpoints; ++j) {
const auto* lb_endpoint = lb_endpoints[j];
fields.emplace_back(" lb_endpoints {");
// health_status
uint32_t health_status =
envoy_api_v2_endpoint_LbEndpoint_health_status(lb_endpoint);
if (health_status > 0) {
fields.emplace_back(
absl::StrCat(" health_status: ", health_status));
}
// endpoint
const envoy_api_v2_endpoint_Endpoint* endpoint =
envoy_api_v2_endpoint_LbEndpoint_endpoint(lb_endpoint);
if (endpoint != nullptr) {
fields.emplace_back(" endpoint {");
// address
const auto* address =
envoy_api_v2_endpoint_Endpoint_address(endpoint);
if (address != nullptr) {
fields.emplace_back(" address {");
// socket_address
const auto* socket_address =
envoy_api_v2_core_Address_socket_address(address);
if (socket_address != nullptr) {
fields.emplace_back(" socket_address {");
// address
AddStringField(
" address",
envoy_api_v2_core_SocketAddress_address(socket_address),
&fields);
// port_value
if (envoy_api_v2_core_SocketAddress_has_port_value(
socket_address)) {
fields.emplace_back(
absl::StrCat(" port_value: ",
envoy_api_v2_core_SocketAddress_port_value(
socket_address)));
} else {
fields.emplace_back(" <non-numeric port>");
}
fields.emplace_back(" }");
} else {
fields.emplace_back(" <non-socket address>");
}
fields.emplace_back(" }");
}
fields.emplace_back(" }");
}
fields.emplace_back(" }");
}
// load_balancing_weight
const google_protobuf_UInt32Value* lb_weight =
envoy_api_v2_endpoint_LocalityLbEndpoints_load_balancing_weight(
locality_endpoint);
if (lb_weight != nullptr) {
fields.emplace_back(
absl::StrCat(" load_balancing_weight { value: ",
google_protobuf_UInt32Value_value(lb_weight), " }"));
}
// priority
uint32_t priority =
envoy_api_v2_endpoint_LocalityLbEndpoints_priority(locality_endpoint);
if (priority > 0) {
fields.emplace_back(absl::StrCat(" priority: ", priority));
}
fields.emplace_back("}");
}
// policy
const envoy_api_v2_ClusterLoadAssignment_Policy* policy =
envoy_api_v2_ClusterLoadAssignment_policy(cla);
if (policy != nullptr) {
fields.emplace_back("policy {");
// drop_overloads
size_t num_drop_overloads;
const envoy_api_v2_ClusterLoadAssignment_Policy_DropOverload* const*
drop_overloads =
envoy_api_v2_ClusterLoadAssignment_Policy_drop_overloads(
policy, &num_drop_overloads);
for (size_t i = 0; i < num_drop_overloads; ++i) {
auto* drop_overload = drop_overloads[i];
fields.emplace_back(" drop_overloads {");
// category
AddStringField(
" category",
envoy_api_v2_ClusterLoadAssignment_Policy_DropOverload_category(
drop_overload),
&fields);
// drop_percentage
const auto* drop_percentage =
envoy_api_v2_ClusterLoadAssignment_Policy_DropOverload_drop_percentage(
drop_overload);
if (drop_percentage != nullptr) {
fields.emplace_back(" drop_percentage {");
fields.emplace_back(absl::StrCat(
" numerator: ",
envoy_type_FractionalPercent_numerator(drop_percentage)));
fields.emplace_back(absl::StrCat(
" denominator: ",
envoy_type_FractionalPercent_denominator(drop_percentage)));
fields.emplace_back(" }");
}
fields.emplace_back(" }");
}
// overprovisioning_factor
fields.emplace_back("}");
}
gpr_log(GPR_DEBUG, "[xds_client %p] ClusterLoadAssignment: %s", client,
absl::StrJoin(fields, "\n").c_str());
}
}
// Better match type has smaller value.
enum MatchType {
EXACT_MATCH,
@ -449,8 +949,10 @@ MatchType DomainPatternMatchType(const std::string& domain_pattern) {
}
grpc_error* RouteConfigParse(
XdsClient* client, TraceFlag* tracer,
const envoy_api_v2_RouteConfiguration* route_config,
const std::string& expected_server_name, XdsApi::RdsUpdate* rds_update) {
MaybeLogRouteConfiguration(client, tracer, route_config);
// Get the virtual hosts.
size_t size;
const envoy_api_v2_route_VirtualHost* const* virtual_hosts =
@ -540,17 +1042,15 @@ grpc_error* RouteConfigParse(
return GRPC_ERROR_NONE;
}
grpc_error* LdsResponseParse(const envoy_api_v2_DiscoveryResponse* response,
grpc_error* LdsResponseParse(XdsClient* client, TraceFlag* tracer,
const envoy_api_v2_DiscoveryResponse* response,
const std::string& expected_server_name,
XdsApi::LdsUpdate* lds_update, upb_arena* arena) {
absl::optional<XdsApi::LdsUpdate>* lds_update,
upb_arena* arena) {
// Get the resources from the response.
size_t size;
const google_protobuf_Any* const* resources =
envoy_api_v2_DiscoveryResponse_resources(response, &size);
if (size < 1) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"LDS response contains 0 resource.");
}
for (size_t i = 0; i < size; ++i) {
// Check the type_url of the resource.
const upb_strview type_url = google_protobuf_Any_type_url(resources[i]);
@ -590,14 +1090,11 @@ grpc_error* LdsResponseParse(const envoy_api_v2_DiscoveryResponse* response,
envoy_config_filter_network_http_connection_manager_v2_HttpConnectionManager_route_config(
http_connection_manager);
XdsApi::RdsUpdate rds_update;
grpc_error* error =
RouteConfigParse(route_config, expected_server_name, &rds_update);
grpc_error* error = RouteConfigParse(client, tracer, route_config,
expected_server_name, &rds_update);
if (error != GRPC_ERROR_NONE) return error;
lds_update->rds_update.emplace(std::move(rds_update));
const upb_strview route_config_name =
envoy_api_v2_RouteConfiguration_name(route_config);
lds_update->route_config_name =
std::string(route_config_name.data, route_config_name.size);
lds_update->emplace();
(*lds_update)->rds_update.emplace(std::move(rds_update));
return GRPC_ERROR_NONE;
}
// Validate that RDS must be used to get the route_config dynamically.
@ -613,26 +1110,24 @@ grpc_error* LdsResponseParse(const envoy_api_v2_DiscoveryResponse* response,
const upb_strview route_config_name =
envoy_config_filter_network_http_connection_manager_v2_Rds_route_config_name(
rds);
lds_update->route_config_name =
lds_update->emplace();
(*lds_update)->route_config_name =
std::string(route_config_name.data, route_config_name.size);
return GRPC_ERROR_NONE;
}
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"No listener found for expected server name.");
return GRPC_ERROR_NONE;
}
grpc_error* RdsResponseParse(const envoy_api_v2_DiscoveryResponse* response,
grpc_error* RdsResponseParse(XdsClient* client, TraceFlag* tracer,
const envoy_api_v2_DiscoveryResponse* response,
const std::string& expected_server_name,
const std::string& expected_route_config_name,
XdsApi::RdsUpdate* rds_update, upb_arena* arena) {
absl::optional<XdsApi::RdsUpdate>* rds_update,
upb_arena* arena) {
// Get the resources from the response.
size_t size;
const google_protobuf_Any* const* resources =
envoy_api_v2_DiscoveryResponse_resources(response, &size);
if (size < 1) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"RDS response contains 0 resource.");
}
for (size_t i = 0; i < size; ++i) {
// Check the type_url of the resource.
const upb_strview type_url = google_protobuf_Any_type_url(resources[i]);
@ -655,27 +1150,24 @@ grpc_error* RdsResponseParse(const envoy_api_v2_DiscoveryResponse* response,
if (!upb_strview_eql(name, expected_name)) continue;
// Parse the route_config.
XdsApi::RdsUpdate local_rds_update;
grpc_error* error =
RouteConfigParse(route_config, expected_server_name, &local_rds_update);
grpc_error* error = RouteConfigParse(
client, tracer, route_config, expected_server_name, &local_rds_update);
if (error != GRPC_ERROR_NONE) return error;
*rds_update = std::move(local_rds_update);
rds_update->emplace(std::move(local_rds_update));
return GRPC_ERROR_NONE;
}
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"No route config found for expected name.");
return GRPC_ERROR_NONE;
}
grpc_error* CdsResponseParse(const envoy_api_v2_DiscoveryResponse* response,
grpc_error* CdsResponseParse(XdsClient* client, TraceFlag* tracer,
const envoy_api_v2_DiscoveryResponse* response,
const std::set<StringView>& expected_cluster_names,
XdsApi::CdsUpdateMap* cds_update_map,
upb_arena* arena) {
// Get the resources from the response.
size_t size;
const google_protobuf_Any* const* resources =
envoy_api_v2_DiscoveryResponse_resources(response, &size);
if (size < 1) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"CDS response contains 0 resource.");
}
// Parse all the resources in the CDS response.
for (size_t i = 0; i < size; ++i) {
XdsApi::CdsUpdate cds_update;
@ -691,6 +1183,14 @@ grpc_error* CdsResponseParse(const envoy_api_v2_DiscoveryResponse* response,
if (cluster == nullptr) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Can't decode cluster.");
}
MaybeLogCluster(client, tracer, cluster);
// Ignore unexpected cluster names.
upb_strview cluster_name = envoy_api_v2_Cluster_name(cluster);
StringView cluster_name_strview(cluster_name.data, cluster_name.size);
if (expected_cluster_names.find(cluster_name_strview) ==
expected_cluster_names.end()) {
continue;
}
// Check the cluster_discovery_type.
if (!envoy_api_v2_Cluster_has_type(cluster)) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING("DiscoveryType not found.");
@ -729,7 +1229,6 @@ grpc_error* CdsResponseParse(const envoy_api_v2_DiscoveryResponse* response,
}
cds_update.lrs_load_reporting_server_name.emplace("");
}
upb_strview cluster_name = envoy_api_v2_Cluster_name(cluster);
cds_update_map->emplace(std::string(cluster_name.data, cluster_name.size),
std::move(cds_update));
}
@ -849,6 +1348,7 @@ grpc_error* DropParseAndAppend(
}
grpc_error* EdsResponsedParse(
XdsClient* client, TraceFlag* tracer,
const envoy_api_v2_DiscoveryResponse* response,
const std::set<StringView>& expected_eds_service_names,
XdsApi::EdsUpdateMap* eds_update_map, upb_arena* arena) {
@ -856,10 +1356,6 @@ grpc_error* EdsResponsedParse(
size_t size;
const google_protobuf_Any* const* resources =
envoy_api_v2_DiscoveryResponse_resources(response, &size);
if (size < 1) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"EDS response contains 0 resource.");
}
for (size_t i = 0; i < size; ++i) {
XdsApi::EdsUpdate eds_update;
// Check the type_url of the resource.
@ -878,6 +1374,7 @@ grpc_error* EdsResponsedParse(
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Can't parse cluster_load_assignment.");
}
MaybeLogClusterLoadAssignment(client, tracer, cluster_load_assignment);
// Check the cluster name (which actually means eds_service_name). Ignore
// unexpected names.
upb_strview cluster_name = envoy_api_v2_ClusterLoadAssignment_cluster_name(
@ -934,8 +1431,10 @@ grpc_error* EdsResponsedParse(
grpc_error* XdsApi::ParseAdsResponse(
const grpc_slice& encoded_response, const std::string& expected_server_name,
const std::string& expected_route_config_name,
const std::set<StringView>& expected_cluster_names,
const std::set<StringView>& expected_eds_service_names,
LdsUpdate* lds_update, RdsUpdate* rds_update, CdsUpdateMap* cds_update_map,
absl::optional<LdsUpdate>* lds_update,
absl::optional<RdsUpdate>* rds_update, CdsUpdateMap* cds_update_map,
EdsUpdateMap* eds_update_map, std::string* version, std::string* nonce,
std::string* type_url) {
upb::Arena arena;
@ -950,6 +1449,7 @@ grpc_error* XdsApi::ParseAdsResponse(
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Can't decode the whole response.");
}
MaybeLogDiscoveryResponse(client_, tracer_, response);
// Record the type_url, the version_info, and the nonce of the response.
upb_strview type_url_strview =
envoy_api_v2_DiscoveryResponse_type_url(response);
@ -961,17 +1461,19 @@ grpc_error* XdsApi::ParseAdsResponse(
*nonce = std::string(nonce_strview.data, nonce_strview.size);
// Parse the response according to the resource type.
if (*type_url == kLdsTypeUrl) {
return LdsResponseParse(response, expected_server_name, lds_update,
arena.ptr());
return LdsResponseParse(client_, tracer_, response, expected_server_name,
lds_update, arena.ptr());
} else if (*type_url == kRdsTypeUrl) {
return RdsResponseParse(response, expected_server_name,
return RdsResponseParse(client_, tracer_, response, expected_server_name,
expected_route_config_name, rds_update,
arena.ptr());
} else if (*type_url == kCdsTypeUrl) {
return CdsResponseParse(response, cds_update_map, arena.ptr());
return CdsResponseParse(client_, tracer_, response, expected_cluster_names,
cds_update_map, arena.ptr());
} else if (*type_url == kEdsTypeUrl) {
return EdsResponsedParse(response, expected_eds_service_names,
eds_update_map, arena.ptr());
return EdsResponsedParse(client_, tracer_, response,
expected_eds_service_names, eds_update_map,
arena.ptr());
} else {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Unsupported ADS resource type.");
@ -980,6 +1482,121 @@ grpc_error* XdsApi::ParseAdsResponse(
namespace {
void MaybeLogLrsRequest(
XdsClient* client, TraceFlag* tracer,
const envoy_service_load_stats_v2_LoadStatsRequest* request) {
if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
// TODO(roth): When we can upgrade upb, use upb textformat code to dump
// the raw proto instead of doing this manually.
std::vector<std::string> fields;
// node
const auto* node =
envoy_service_load_stats_v2_LoadStatsRequest_node(request);
if (node != nullptr) {
AddNodeLogFields(node, &fields);
}
// cluster_stats
size_t num_cluster_stats;
const struct envoy_api_v2_endpoint_ClusterStats* const* cluster_stats =
envoy_service_load_stats_v2_LoadStatsRequest_cluster_stats(
request, &num_cluster_stats);
for (size_t i = 0; i < num_cluster_stats; ++i) {
const auto* cluster_stat = cluster_stats[i];
fields.emplace_back("cluster_stats {");
// cluster_name
AddStringField(
" cluster_name",
envoy_api_v2_endpoint_ClusterStats_cluster_name(cluster_stat),
&fields);
// cluster_service_name
AddStringField(
" cluster_service_name",
envoy_api_v2_endpoint_ClusterStats_cluster_service_name(cluster_stat),
&fields);
// upstream_locality_stats
size_t num_stats;
const envoy_api_v2_endpoint_UpstreamLocalityStats* const* stats =
envoy_api_v2_endpoint_ClusterStats_upstream_locality_stats(
cluster_stat, &num_stats);
for (size_t j = 0; j < num_stats; ++j) {
const auto* stat = stats[j];
fields.emplace_back(" upstream_locality_stats {");
// locality
const auto* locality =
envoy_api_v2_endpoint_UpstreamLocalityStats_locality(stat);
if (locality != nullptr) {
fields.emplace_back(" locality {");
AddLocalityField(3, locality, &fields);
fields.emplace_back(" }");
}
// total_successful_requests
fields.emplace_back(absl::StrCat(
" total_successful_requests: ",
envoy_api_v2_endpoint_UpstreamLocalityStats_total_successful_requests(
stat)));
// total_requests_in_progress
fields.emplace_back(absl::StrCat(
" total_requests_in_progress: ",
envoy_api_v2_endpoint_UpstreamLocalityStats_total_requests_in_progress(
stat)));
// total_error_requests
fields.emplace_back(absl::StrCat(
" total_error_requests: ",
envoy_api_v2_endpoint_UpstreamLocalityStats_total_error_requests(
stat)));
// total_issued_requests
fields.emplace_back(absl::StrCat(
" total_issued_requests: ",
envoy_api_v2_endpoint_UpstreamLocalityStats_total_issued_requests(
stat)));
fields.emplace_back(" }");
}
// total_dropped_requests
fields.emplace_back(absl::StrCat(
" total_dropped_requests: ",
envoy_api_v2_endpoint_ClusterStats_total_dropped_requests(
cluster_stat)));
// dropped_requests
size_t num_drops;
const envoy_api_v2_endpoint_ClusterStats_DroppedRequests* const* drops =
envoy_api_v2_endpoint_ClusterStats_dropped_requests(cluster_stat,
&num_drops);
for (size_t j = 0; j < num_drops; ++j) {
const auto* drop = drops[j];
fields.emplace_back(" dropped_requests {");
// category
AddStringField(
" category",
envoy_api_v2_endpoint_ClusterStats_DroppedRequests_category(drop),
&fields);
// dropped_count
fields.emplace_back(absl::StrCat(
" dropped_count: ",
envoy_api_v2_endpoint_ClusterStats_DroppedRequests_dropped_count(
drop)));
fields.emplace_back(" }");
}
// load_report_interval
const auto* load_report_interval =
envoy_api_v2_endpoint_ClusterStats_load_report_interval(cluster_stat);
if (load_report_interval != nullptr) {
fields.emplace_back(" load_report_interval {");
fields.emplace_back(absl::StrCat(
" seconds: ",
google_protobuf_Duration_seconds(load_report_interval)));
fields.emplace_back(
absl::StrCat(" nanos: ",
google_protobuf_Duration_nanos(load_report_interval)));
fields.emplace_back(" }");
}
fields.emplace_back("}");
}
gpr_log(GPR_DEBUG, "[xds_client %p] constructed LRS request: %s", client,
absl::StrJoin(fields, "\n").c_str());
}
}
grpc_slice SerializeLrsRequest(
const envoy_service_load_stats_v2_LoadStatsRequest* request,
upb_arena* arena) {
@ -1002,6 +1619,7 @@ grpc_slice XdsApi::CreateLrsInitialRequest(const std::string& server_name) {
arena.ptr());
PopulateNode(arena.ptr(), node_, build_version_, user_agent_name_,
server_name, node_msg);
MaybeLogLrsRequest(client_, tracer_, request);
return SerializeLrsRequest(request, arena.ptr());
}
@ -1114,6 +1732,7 @@ grpc_slice XdsApi::CreateLrsRequest(
google_protobuf_Duration_set_seconds(load_report_interval, timespec.tv_sec);
google_protobuf_Duration_set_nanos(load_report_interval, timespec.tv_nsec);
}
MaybeLogLrsRequest(client_, tracer_, request);
return SerializeLrsRequest(request, arena.ptr());
}

@ -34,6 +34,8 @@
namespace grpc_core {
class XdsClient;
class XdsApi {
public:
static const char* kLdsTypeUrl;
@ -187,7 +189,7 @@ class XdsApi {
std::pair<std::string /*cluster_name*/, std::string /*eds_service_name*/>,
ClusterLoadReport>;
explicit XdsApi(const XdsBootstrap::Node* node);
XdsApi(XdsClient* client, TraceFlag* tracer, const XdsBootstrap::Node* node);
// Creates a request to nack an unsupported resource type.
// Takes ownership of \a error.
@ -230,10 +232,12 @@ class XdsApi {
const grpc_slice& encoded_response,
const std::string& expected_server_name,
const std::string& expected_route_config_name,
const std::set<StringView>& expected_cluster_names,
const std::set<StringView>& expected_eds_service_names,
LdsUpdate* lds_update, RdsUpdate* rds_update,
CdsUpdateMap* cds_update_map, EdsUpdateMap* eds_update_map,
std::string* version, std::string* nonce, std::string* type_url);
absl::optional<LdsUpdate>* lds_update,
absl::optional<RdsUpdate>* rds_update, CdsUpdateMap* cds_update_map,
EdsUpdateMap* eds_update_map, std::string* version, std::string* nonce,
std::string* type_url);
// Creates an LRS request querying \a server_name.
grpc_slice CreateLrsInitialRequest(const std::string& server_name);
@ -249,6 +253,8 @@ class XdsApi {
grpc_millis* load_reporting_interval);
private:
XdsClient* client_;
TraceFlag* tracer_;
const XdsBootstrap::Node* node_;
const std::string build_version_;
const std::string user_agent_name_;

@ -29,20 +29,97 @@
namespace grpc_core {
std::unique_ptr<XdsBootstrap> XdsBootstrap::ReadFromFile(grpc_error** error) {
namespace {
UniquePtr<char> BootstrapString(const XdsBootstrap& bootstrap) {
gpr_strvec v;
gpr_strvec_init(&v);
char* tmp;
if (bootstrap.node() != nullptr) {
gpr_asprintf(&tmp,
"node={\n"
" id=\"%s\",\n"
" cluster=\"%s\",\n"
" locality={\n"
" region=\"%s\",\n"
" zone=\"%s\",\n"
" subzone=\"%s\"\n"
" },\n"
" metadata=%s,\n"
"},\n",
bootstrap.node()->id.c_str(),
bootstrap.node()->cluster.c_str(),
bootstrap.node()->locality_region.c_str(),
bootstrap.node()->locality_zone.c_str(),
bootstrap.node()->locality_subzone.c_str(),
bootstrap.node()->metadata.Dump().c_str());
gpr_strvec_add(&v, tmp);
}
gpr_asprintf(&tmp,
"servers=[\n"
" {\n"
" uri=\"%s\",\n"
" creds=[\n",
bootstrap.server().server_uri.c_str());
gpr_strvec_add(&v, tmp);
for (size_t i = 0; i < bootstrap.server().channel_creds.size(); ++i) {
const auto& creds = bootstrap.server().channel_creds[i];
gpr_asprintf(&tmp, " {type=\"%s\", config=%s},\n", creds.type.c_str(),
creds.config.Dump().c_str());
gpr_strvec_add(&v, tmp);
}
gpr_strvec_add(&v, gpr_strdup(" ]\n }\n]"));
UniquePtr<char> result(gpr_strvec_flatten(&v, nullptr));
gpr_strvec_destroy(&v);
return result;
}
} // namespace
std::unique_ptr<XdsBootstrap> XdsBootstrap::ReadFromFile(XdsClient* client,
TraceFlag* tracer,
grpc_error** error) {
grpc_core::UniquePtr<char> path(gpr_getenv("GRPC_XDS_BOOTSTRAP"));
if (path == nullptr) {
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"GRPC_XDS_BOOTSTRAP env var not set");
"Environment variable GRPC_XDS_BOOTSTRAP not defined");
return nullptr;
}
if (GRPC_TRACE_FLAG_ENABLED(*tracer)) {
gpr_log(GPR_INFO,
"[xds_client %p] Got bootstrap file location from "
"GRPC_XDS_BOOTSTRAP environment variable: %s",
client, path.get());
}
grpc_slice contents;
*error = grpc_load_file(path.get(), /*add_null_terminator=*/true, &contents);
if (*error != GRPC_ERROR_NONE) return nullptr;
Json json = Json::Parse(StringViewFromSlice(contents), error);
StringView contents_str_view = StringViewFromSlice(contents);
if (GRPC_TRACE_FLAG_ENABLED(*tracer)) {
UniquePtr<char> str = StringViewToCString(contents_str_view);
gpr_log(GPR_DEBUG, "[xds_client %p] Bootstrap file contents: %s", client,
str.get());
}
Json json = Json::Parse(contents_str_view, error);
grpc_slice_unref_internal(contents);
if (*error != GRPC_ERROR_NONE) return nullptr;
return absl::make_unique<XdsBootstrap>(std::move(json), error);
if (*error != GRPC_ERROR_NONE) {
char* msg;
gpr_asprintf(&msg, "Failed to parse bootstrap file %s", path.get());
grpc_error* error_out =
GRPC_ERROR_CREATE_REFERENCING_FROM_COPIED_STRING(msg, error, 1);
gpr_free(msg);
GRPC_ERROR_UNREF(*error);
*error = error_out;
return nullptr;
}
std::unique_ptr<XdsBootstrap> result =
absl::make_unique<XdsBootstrap>(std::move(json), error);
if (*error == GRPC_ERROR_NONE && GRPC_TRACE_FLAG_ENABLED(*tracer)) {
gpr_log(GPR_INFO,
"[xds_client %p] Bootstrap config for creating xds client:\n%s",
client, BootstrapString(*result).get());
}
return result;
}
XdsBootstrap::XdsBootstrap(Json json, grpc_error** error) {

@ -33,6 +33,8 @@
namespace grpc_core {
class XdsClient;
class XdsBootstrap {
public:
struct Node {
@ -56,7 +58,9 @@ class XdsBootstrap {
// If *error is not GRPC_ERROR_NONE after returning, then there was an
// error reading the file.
static std::unique_ptr<XdsBootstrap> ReadFromFile(grpc_error** error);
static std::unique_ptr<XdsBootstrap> ReadFromFile(XdsClient* client,
TraceFlag* tracer,
grpc_error** error);
// Do not instantiate directly -- use ReadFromFile() above instead.
XdsBootstrap(Json json, grpc_error** error);

@ -22,6 +22,8 @@
#include <limits.h>
#include <string.h>
#include "absl/strings/str_join.h"
#include <grpc/byte_buffer_reader.h>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
@ -126,7 +128,8 @@ class XdsClient::ChannelState::AdsCallState
bool seen_response() const { return seen_response_; }
void Subscribe(const std::string& type_url, const std::string& name);
void Unsubscribe(const std::string& type_url, const std::string& name);
void Unsubscribe(const std::string& type_url, const std::string& name,
bool delay_unsubscription);
bool HasSubscribedResources() const;
@ -238,8 +241,8 @@ class XdsClient::ChannelState::AdsCallState
void SendMessageLocked(const std::string& type_url);
void AcceptLdsUpdate(XdsApi::LdsUpdate lds_update);
void AcceptRdsUpdate(XdsApi::RdsUpdate rds_update);
void AcceptLdsUpdate(absl::optional<XdsApi::LdsUpdate> lds_update);
void AcceptRdsUpdate(absl::optional<XdsApi::RdsUpdate> rds_update);
void AcceptCdsUpdate(XdsApi::CdsUpdateMap cds_update_map);
void AcceptEdsUpdate(XdsApi::EdsUpdateMap eds_update_map);
@ -555,9 +558,10 @@ void XdsClient::ChannelState::Subscribe(const std::string& type_url,
}
void XdsClient::ChannelState::Unsubscribe(const std::string& type_url,
const std::string& name) {
const std::string& name,
bool delay_unsubscription) {
if (ads_calld_ != nullptr) {
ads_calld_->calld()->Unsubscribe(type_url, name);
ads_calld_->calld()->Unsubscribe(type_url, name, delay_unsubscription);
if (!ads_calld_->calld()->HasSubscribedResources()) ads_calld_.reset();
}
}
@ -788,33 +792,46 @@ void XdsClient::ChannelState::AdsCallState::SendMessageLocked(
return;
}
auto& state = state_map_[type_url];
grpc_error* error = state.error;
state.error = GRPC_ERROR_NONE;
grpc_slice request_payload_slice;
std::set<StringView> resource_names;
if (type_url == XdsApi::kLdsTypeUrl) {
resource_names.insert(xds_client()->server_name_);
request_payload_slice = xds_client()->api_.CreateLdsRequest(
xds_client()->server_name_, state.version, state.nonce, error,
!sent_initial_message_);
xds_client()->server_name_, state.version, state.nonce,
GRPC_ERROR_REF(state.error), !sent_initial_message_);
state.subscribed_resources[xds_client()->server_name_]->Start(Ref());
} else if (type_url == XdsApi::kRdsTypeUrl) {
resource_names.insert(xds_client()->route_config_name_);
request_payload_slice = xds_client()->api_.CreateRdsRequest(
xds_client()->route_config_name_, state.version, state.nonce, error,
!sent_initial_message_);
xds_client()->route_config_name_, state.version, state.nonce,
GRPC_ERROR_REF(state.error), !sent_initial_message_);
state.subscribed_resources[xds_client()->route_config_name_]->Start(Ref());
} else if (type_url == XdsApi::kCdsTypeUrl) {
resource_names = ClusterNamesForRequest();
request_payload_slice = xds_client()->api_.CreateCdsRequest(
ClusterNamesForRequest(), state.version, state.nonce, error,
resource_names, state.version, state.nonce, GRPC_ERROR_REF(state.error),
!sent_initial_message_);
} else if (type_url == XdsApi::kEdsTypeUrl) {
resource_names = EdsServiceNamesForRequest();
request_payload_slice = xds_client()->api_.CreateEdsRequest(
EdsServiceNamesForRequest(), state.version, state.nonce, error,
resource_names, state.version, state.nonce, GRPC_ERROR_REF(state.error),
!sent_initial_message_);
} else {
request_payload_slice = xds_client()->api_.CreateUnsupportedTypeNackRequest(
type_url, state.nonce, state.error);
type_url, state.nonce, GRPC_ERROR_REF(state.error));
state_map_.erase(type_url);
}
sent_initial_message_ = true;
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO,
"[xds_client %p] sending ADS request: type=%s version=%s nonce=%s "
"error=%s resources=%s",
xds_client(), type_url.c_str(), state.version.c_str(),
state.nonce.c_str(), grpc_error_string(state.error),
absl::StrJoin(resource_names, " ").c_str());
}
GRPC_ERROR_UNREF(state.error);
state.error = GRPC_ERROR_NONE;
// Create message payload.
send_message_payload_ =
grpc_raw_byte_buffer_create(&request_payload_slice, 1);
@ -847,9 +864,10 @@ void XdsClient::ChannelState::AdsCallState::Subscribe(
}
void XdsClient::ChannelState::AdsCallState::Unsubscribe(
const std::string& type_url, const std::string& name) {
const std::string& type_url, const std::string& name,
bool delay_unsubscription) {
state_map_[type_url].subscribed_resources.erase(name);
SendMessageLocked(type_url);
if (!delay_unsubscription) SendMessageLocked(type_url);
}
bool XdsClient::ChannelState::AdsCallState::HasSubscribedResources() const {
@ -860,24 +878,32 @@ bool XdsClient::ChannelState::AdsCallState::HasSubscribedResources() const {
}
void XdsClient::ChannelState::AdsCallState::AcceptLdsUpdate(
XdsApi::LdsUpdate lds_update) {
absl::optional<XdsApi::LdsUpdate> lds_update) {
if (!lds_update.has_value()) {
gpr_log(GPR_INFO,
"[xds_client %p] LDS update does not include requested resource",
xds_client());
xds_client()->service_config_watcher_->OnError(
GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"LDS update does not include requested resource"));
return;
}
const std::string& cluster_name =
lds_update.rds_update.has_value()
? lds_update.rds_update.value().cluster_name
lds_update->rds_update.has_value()
? lds_update->rds_update.value().cluster_name
: "";
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
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)",
xds_client(), lds_update.route_config_name.c_str(),
xds_client(), lds_update->route_config_name.c_str(),
cluster_name.c_str());
}
auto& lds_state = state_map_[XdsApi::kLdsTypeUrl];
auto& state = lds_state.subscribed_resources[xds_client()->server_name_];
if (state != nullptr) state->Finish();
// Ignore identical update.
if (xds_client()->route_config_name_ == lds_update.route_config_name &&
if (xds_client()->route_config_name_ == lds_update->route_config_name &&
xds_client()->cluster_name_ == cluster_name) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO,
@ -886,12 +912,17 @@ void XdsClient::ChannelState::AdsCallState::AcceptLdsUpdate(
}
return;
}
xds_client()->route_config_name_ = std::move(lds_update.route_config_name);
if (lds_update.rds_update.has_value()) {
if (!xds_client()->route_config_name_.empty()) {
Unsubscribe(
XdsApi::kRdsTypeUrl, xds_client()->route_config_name_,
/*delay_unsubscription=*/!lds_update->route_config_name.empty());
}
xds_client()->route_config_name_ = std::move(lds_update->route_config_name);
if (lds_update->rds_update.has_value()) {
// If cluster_name was found inlined in LDS response, notify the watcher
// immediately.
xds_client()->cluster_name_ =
std::move(lds_update.rds_update.value().cluster_name);
std::move(lds_update->rds_update.value().cluster_name);
RefCountedPtr<ServiceConfig> service_config;
grpc_error* error = xds_client()->CreateServiceConfig(
xds_client()->cluster_name_, &service_config);
@ -908,19 +939,26 @@ void XdsClient::ChannelState::AdsCallState::AcceptLdsUpdate(
}
void XdsClient::ChannelState::AdsCallState::AcceptRdsUpdate(
XdsApi::RdsUpdate rds_update) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
absl::optional<XdsApi::RdsUpdate> rds_update) {
if (!rds_update.has_value()) {
gpr_log(GPR_INFO,
"[xds_client %p] RDS update received: "
"cluster_name=%s",
xds_client(), rds_update.cluster_name.c_str());
"[xds_client %p] RDS update does not include requested resource",
xds_client());
xds_client()->service_config_watcher_->OnError(
GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"RDS update does not include requested resource"));
return;
}
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO, "[xds_client %p] RDS update received: cluster_name=%s",
xds_client(), rds_update->cluster_name.c_str());
}
auto& rds_state = state_map_[XdsApi::kRdsTypeUrl];
auto& state =
rds_state.subscribed_resources[xds_client()->route_config_name_];
if (state != nullptr) state->Finish();
// Ignore identical update.
if (xds_client()->cluster_name_ == rds_update.cluster_name) {
if (xds_client()->cluster_name_ == rds_update->cluster_name) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO,
"[xds_client %p] RDS update identical to current, ignoring.",
@ -928,7 +966,7 @@ void XdsClient::ChannelState::AdsCallState::AcceptRdsUpdate(
}
return;
}
xds_client()->cluster_name_ = std::move(rds_update.cluster_name);
xds_client()->cluster_name_ = std::move(rds_update->cluster_name);
// Notify the watcher.
RefCountedPtr<ServiceConfig> service_config;
grpc_error* error = xds_client()->CreateServiceConfig(
@ -944,6 +982,7 @@ void XdsClient::ChannelState::AdsCallState::AcceptRdsUpdate(
void XdsClient::ChannelState::AdsCallState::AcceptCdsUpdate(
XdsApi::CdsUpdateMap cds_update_map) {
auto& cds_state = state_map_[XdsApi::kCdsTypeUrl];
std::set<std::string> eds_resource_names_seen;
for (auto& p : cds_update_map) {
const char* cluster_name = p.first.c_str();
XdsApi::CdsUpdate& cds_update = p.second;
@ -952,21 +991,22 @@ void XdsClient::ChannelState::AdsCallState::AcceptCdsUpdate(
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO,
"[xds_client %p] CDS update (cluster=%s) received: "
"eds_service_name=%s, "
"lrs_load_reporting_server_name=%s",
"eds_service_name=%s, lrs_load_reporting_server_name=%s",
xds_client(), cluster_name, cds_update.eds_service_name.c_str(),
cds_update.lrs_load_reporting_server_name.has_value()
? cds_update.lrs_load_reporting_server_name.value().c_str()
: "(N/A)");
}
ClusterState& cluster_state = xds_client()->cluster_map_[cluster_name];
// Record the EDS resource names seen.
eds_resource_names_seen.insert(cds_update.eds_service_name.empty()
? cluster_name
: cds_update.eds_service_name);
// Ignore identical update.
ClusterState& cluster_state = xds_client()->cluster_map_[cluster_name];
if (cluster_state.update.has_value() &&
cds_update.eds_service_name ==
cluster_state.update.value().eds_service_name &&
cds_update.lrs_load_reporting_server_name.value() ==
cluster_state.update.value()
.lrs_load_reporting_server_name.value()) {
cds_update.eds_service_name == cluster_state.update->eds_service_name &&
cds_update.lrs_load_reporting_server_name ==
cluster_state.update->lrs_load_reporting_server_name) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO,
"[xds_client %p] CDS update identical to current, ignoring.",
@ -975,12 +1015,41 @@ void XdsClient::ChannelState::AdsCallState::AcceptCdsUpdate(
continue;
}
// Update the cluster state.
cluster_state.update.emplace(std::move(cds_update));
cluster_state.update = std::move(cds_update);
// Notify all watchers.
for (const auto& p : cluster_state.watchers) {
p.first->OnClusterChanged(cluster_state.update.value());
}
}
// For any subscribed resource that is not present in the update,
// remove it from the cache and notify watchers of the error.
for (const auto& p : cds_state.subscribed_resources) {
const std::string& cluster_name = p.first;
if (cds_update_map.find(cluster_name) == cds_update_map.end()) {
ClusterState& cluster_state = xds_client()->cluster_map_[cluster_name];
cluster_state.update.reset();
for (const auto& p : cluster_state.watchers) {
p.first->OnError(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Cluster not present in CDS update"));
}
}
}
// Also remove any EDS resources that are no longer referred to by any CDS
// resources.
auto& eds_state = state_map_[XdsApi::kEdsTypeUrl];
for (const auto& p : eds_state.subscribed_resources) {
const std::string& eds_resource_name = p.first;
if (eds_resource_names_seen.find(eds_resource_name) ==
eds_resource_names_seen.end()) {
EndpointState& endpoint_state =
xds_client()->endpoint_map_[eds_resource_name];
endpoint_state.update.reset();
for (const auto& p : endpoint_state.watchers) {
p.first->OnError(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"ClusterLoadAssignment resource removed due to CDS update"));
}
}
}
}
void XdsClient::ChannelState::AdsCallState::AcceptEdsUpdate(
@ -1043,25 +1112,27 @@ void XdsClient::ChannelState::AdsCallState::AcceptEdsUpdate(
EndpointState& endpoint_state =
xds_client()->endpoint_map_[eds_service_name];
// Ignore identical update.
const XdsApi::EdsUpdate& prev_update = endpoint_state.update;
const bool priority_list_changed =
prev_update.priority_list_update != eds_update.priority_list_update;
const bool drop_config_changed =
prev_update.drop_config == nullptr ||
*prev_update.drop_config != *eds_update.drop_config;
if (!priority_list_changed && !drop_config_changed) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO,
"[xds_client %p] EDS update identical to current, ignoring.",
xds_client());
if (endpoint_state.update.has_value()) {
const XdsApi::EdsUpdate& prev_update = endpoint_state.update.value();
const bool priority_list_changed =
prev_update.priority_list_update != eds_update.priority_list_update;
const bool drop_config_changed =
prev_update.drop_config == nullptr ||
*prev_update.drop_config != *eds_update.drop_config;
if (!priority_list_changed && !drop_config_changed) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO,
"[xds_client %p] EDS update identical to current, ignoring.",
xds_client());
}
continue;
}
continue;
}
// Update the cluster state.
endpoint_state.update = std::move(eds_update);
// Notify all watchers.
for (const auto& p : endpoint_state.watchers) {
p.first->OnEndpointChanged(endpoint_state.update);
p.first->OnEndpointChanged(endpoint_state.update.value());
}
}
}
@ -1135,8 +1206,8 @@ void XdsClient::ChannelState::AdsCallState::OnResponseReceivedLocked(
// mode. We will also need to cancel the timer when we receive a serverlist
// from the balancer.
// Parse the response.
XdsApi::LdsUpdate lds_update;
XdsApi::RdsUpdate rds_update;
absl::optional<XdsApi::LdsUpdate> lds_update;
absl::optional<XdsApi::RdsUpdate> rds_update;
XdsApi::CdsUpdateMap cds_update_map;
XdsApi::EdsUpdateMap eds_update_map;
std::string version;
@ -1145,12 +1216,14 @@ void XdsClient::ChannelState::AdsCallState::OnResponseReceivedLocked(
// Note that ParseAdsResponse() also validates the response.
grpc_error* parse_error = xds_client->api_.ParseAdsResponse(
response_slice, xds_client->server_name_, xds_client->route_config_name_,
ads_calld->ClusterNamesForRequest(),
ads_calld->EdsServiceNamesForRequest(), &lds_update, &rds_update,
&cds_update_map, &eds_update_map, &version, &nonce, &type_url);
grpc_slice_unref_internal(response_slice);
if (type_url.empty()) {
// Ignore unparsable response.
gpr_log(GPR_ERROR, "[xds_client %p] No type_url found. error=%s",
gpr_log(GPR_ERROR,
"[xds_client %p] Error parsing ADS response (%s) -- ignoring",
xds_client, grpc_error_string(parse_error));
GRPC_ERROR_UNREF(parse_error);
} else {
@ -1162,10 +1235,11 @@ void XdsClient::ChannelState::AdsCallState::OnResponseReceivedLocked(
GRPC_ERROR_UNREF(state.error);
state.error = parse_error;
// NACK unacceptable update.
gpr_log(
GPR_ERROR,
"[xds_client %p] ADS response can't be accepted, NACKing. error=%s",
xds_client, grpc_error_string(parse_error));
gpr_log(GPR_ERROR,
"[xds_client %p] ADS response invalid for resource type %s "
"version %s, will NACK: nonce=%s error=%s",
xds_client, type_url.c_str(), version.c_str(),
state.nonce.c_str(), grpc_error_string(parse_error));
ads_calld->SendMessageLocked(type_url);
} else {
ads_calld->seen_response_ = true;
@ -1727,10 +1801,15 @@ XdsClient::XdsClient(Combiner* combiner, grpc_pollset_set* interested_parties,
request_timeout_(GetRequestTimeout(channel_args)),
combiner_(GRPC_COMBINER_REF(combiner, "xds_client")),
interested_parties_(interested_parties),
bootstrap_(XdsBootstrap::ReadFromFile(error)),
api_(bootstrap_ == nullptr ? nullptr : bootstrap_->node()),
bootstrap_(
XdsBootstrap::ReadFromFile(this, &grpc_xds_client_trace, error)),
api_(this, &grpc_xds_client_trace,
bootstrap_ == nullptr ? nullptr : bootstrap_->node()),
server_name_(server_name),
service_config_watcher_(std::move(watcher)) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO, "[xds_client %p] creating xds client", this);
}
if (*error != GRPC_ERROR_NONE) {
gpr_log(GPR_ERROR, "[xds_client %p] failed to read bootstrap file: %s",
this, grpc_error_string(*error));
@ -1755,9 +1834,17 @@ XdsClient::XdsClient(Combiner* combiner, grpc_pollset_set* interested_parties,
}
}
XdsClient::~XdsClient() { GRPC_COMBINER_UNREF(combiner_, "xds_client"); }
XdsClient::~XdsClient() {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO, "[xds_client %p] destroying xds client", this);
}
GRPC_COMBINER_UNREF(combiner_, "xds_client");
}
void XdsClient::Orphan() {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO, "[xds_client %p] shutting down xds client", this);
}
shutting_down_ = true;
chand_.reset();
// We do not clear cluster_map_ and endpoint_map_ if the xds client was
@ -1782,13 +1869,18 @@ void XdsClient::WatchClusterData(
// If we've already received an CDS update, notify the new watcher
// immediately.
if (cluster_state.update.has_value()) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO, "[xds_client %p] returning cached cluster data for %s",
this, StringViewToCString(cluster_name).get());
}
w->OnClusterChanged(cluster_state.update.value());
}
chand_->Subscribe(XdsApi::kCdsTypeUrl, cluster_name_str);
}
void XdsClient::CancelClusterDataWatch(StringView cluster_name,
ClusterWatcherInterface* watcher) {
ClusterWatcherInterface* watcher,
bool delay_unsubscription) {
if (shutting_down_) return;
std::string cluster_name_str = std::string(cluster_name);
ClusterState& cluster_state = cluster_map_[cluster_name_str];
@ -1797,7 +1889,8 @@ void XdsClient::CancelClusterDataWatch(StringView cluster_name,
cluster_state.watchers.erase(it);
if (cluster_state.watchers.empty()) {
cluster_map_.erase(cluster_name_str);
chand_->Unsubscribe(XdsApi::kCdsTypeUrl, cluster_name_str);
chand_->Unsubscribe(XdsApi::kCdsTypeUrl, cluster_name_str,
delay_unsubscription);
}
}
}
@ -1811,14 +1904,19 @@ void XdsClient::WatchEndpointData(
endpoint_state.watchers[w] = std::move(watcher);
// If we've already received an EDS update, notify the new watcher
// immediately.
if (!endpoint_state.update.priority_list_update.empty()) {
w->OnEndpointChanged(endpoint_state.update);
if (endpoint_state.update.has_value()) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO, "[xds_client %p] returning cached endpoint data for %s",
this, StringViewToCString(eds_service_name).get());
}
w->OnEndpointChanged(endpoint_state.update.value());
}
chand_->Subscribe(XdsApi::kEdsTypeUrl, eds_service_name_str);
}
void XdsClient::CancelEndpointDataWatch(StringView eds_service_name,
EndpointWatcherInterface* watcher) {
EndpointWatcherInterface* watcher,
bool delay_unsubscription) {
if (shutting_down_) return;
std::string eds_service_name_str = std::string(eds_service_name);
EndpointState& endpoint_state = endpoint_map_[eds_service_name_str];
@ -1827,7 +1925,8 @@ void XdsClient::CancelEndpointDataWatch(StringView eds_service_name,
endpoint_state.watchers.erase(it);
if (endpoint_state.watchers.empty()) {
endpoint_map_.erase(eds_service_name_str);
chand_->Unsubscribe(XdsApi::kEdsTypeUrl, eds_service_name_str);
chand_->Unsubscribe(XdsApi::kEdsTypeUrl, eds_service_name_str,
delay_unsubscription);
}
}
}

@ -86,20 +86,26 @@ class XdsClient : public InternallyRefCounted<XdsClient> {
// keep a raw pointer to the watcher, which may be used only for
// cancellation. (Because the caller does not own the watcher, the
// pointer must not be used for any other purpose.)
// If the caller is going to start a new watch after cancelling the
// old one, it should set delay_unsubscription to true.
void WatchClusterData(StringView cluster_name,
std::unique_ptr<ClusterWatcherInterface> watcher);
void CancelClusterDataWatch(StringView cluster_name,
ClusterWatcherInterface* watcher);
ClusterWatcherInterface* watcher,
bool delay_unsubscription = false);
// Start and cancel endpoint data watch for a cluster.
// The XdsClient takes ownership of the watcher, but the caller may
// keep a raw pointer to the watcher, which may be used only for
// cancellation. (Because the caller does not own the watcher, the
// pointer must not be used for any other purpose.)
// If the caller is going to start a new watch after cancelling the
// old one, it should set delay_unsubscription to true.
void WatchEndpointData(StringView eds_service_name,
std::unique_ptr<EndpointWatcherInterface> watcher);
void CancelEndpointDataWatch(StringView eds_service_name,
EndpointWatcherInterface* watcher);
EndpointWatcherInterface* watcher,
bool delay_unsubscription = false);
// Adds and removes drop stats for cluster_name and eds_service_name.
RefCountedPtr<XdsClusterDropStats> AddClusterDropStats(
@ -167,7 +173,8 @@ class XdsClient : public InternallyRefCounted<XdsClient> {
void CancelConnectivityWatchLocked();
void Subscribe(const std::string& type_url, const std::string& name);
void Unsubscribe(const std::string& type_url, const std::string& name);
void Unsubscribe(const std::string& type_url, const std::string& name,
bool delay_unsubscription);
private:
class StateWatcher;
@ -189,7 +196,7 @@ class XdsClient : public InternallyRefCounted<XdsClient> {
std::map<ClusterWatcherInterface*, std::unique_ptr<ClusterWatcherInterface>>
watchers;
// The latest data seen from CDS.
Optional<XdsApi::CdsUpdate> update;
absl::optional<XdsApi::CdsUpdate> update;
};
struct EndpointState {
@ -197,7 +204,7 @@ class XdsClient : public InternallyRefCounted<XdsClient> {
std::unique_ptr<EndpointWatcherInterface>>
watchers;
// The latest data seen from EDS.
XdsApi::EdsUpdate update;
absl::optional<XdsApi::EdsUpdate> update;
};
struct LoadReportState {
@ -241,9 +248,9 @@ class XdsClient : public InternallyRefCounted<XdsClient> {
std::string route_config_name_;
std::string cluster_name_;
// All the received clusters are cached, no matter they are watched or not.
// One entry for each watched CDS resource.
std::map<std::string /*cluster_name*/, ClusterState> cluster_map_;
// Only the watched EDS service names are stored.
// One entry for each watched EDS resource.
std::map<std::string /*eds_service_name*/, EndpointState> endpoint_map_;
std::map<
std::pair<std::string /*cluster_name*/, std::string /*eds_service_name*/>,

@ -44,6 +44,7 @@ typedef enum {
TSI_OUT_OF_RESOURCES = 12,
TSI_ASYNC = 13,
TSI_HANDSHAKE_SHUTDOWN = 14,
TSI_CLOSE_NOTIFY = 15, // Indicates that the connection should be closed.
} tsi_result;
typedef enum {

@ -31,6 +31,35 @@
namespace grpc {
void RegisterOpenCensusPlugin() {
RegisterChannelFilter<CensusChannelData, CensusClientCallData>(
"opencensus_client", GRPC_CLIENT_CHANNEL, INT_MAX /* priority */,
nullptr /* condition function */);
RegisterChannelFilter<CensusChannelData, CensusServerCallData>(
"opencensus_server", GRPC_SERVER_CHANNEL, INT_MAX /* priority */,
nullptr /* condition function */);
// Access measures to ensure they are initialized. Otherwise, creating a view
// before the first RPC would cause an error.
RpcClientSentBytesPerRpc();
RpcClientReceivedBytesPerRpc();
RpcClientRoundtripLatency();
RpcClientServerLatency();
RpcClientSentMessagesPerRpc();
RpcClientReceivedMessagesPerRpc();
RpcServerSentBytesPerRpc();
RpcServerReceivedBytesPerRpc();
RpcServerServerLatency();
RpcServerSentMessagesPerRpc();
RpcServerReceivedMessagesPerRpc();
}
::opencensus::trace::Span GetSpanFromServerContext(ServerContext* context) {
return reinterpret_cast<const CensusContext*>(context->census_context())
->Span();
}
// These measure definitions should be kept in sync across opencensus
// implementations--see
// https://github.com/census-instrumentation/opencensus-java/blob/master/contrib/grpc_metrics/src/main/java/io/opencensus/contrib/grpc/metrics/RpcMeasureConstants.java.
@ -98,39 +127,5 @@ ABSL_CONST_INIT const absl::string_view
ABSL_CONST_INIT const absl::string_view kRpcServerServerLatencyMeasureName =
"grpc.io/server/server_latency";
} // namespace grpc
namespace grpc_impl {
void RegisterOpenCensusPlugin() {
grpc::RegisterChannelFilter<grpc::CensusChannelData,
grpc::CensusClientCallData>(
"opencensus_client", GRPC_CLIENT_CHANNEL, INT_MAX /* priority */,
nullptr /* condition function */);
grpc::RegisterChannelFilter<grpc::CensusChannelData,
grpc::CensusServerCallData>(
"opencensus_server", GRPC_SERVER_CHANNEL, INT_MAX /* priority */,
nullptr /* condition function */);
// Access measures to ensure they are initialized. Otherwise, creating a view
// before the first RPC would cause an error.
grpc::RpcClientSentBytesPerRpc();
grpc::RpcClientReceivedBytesPerRpc();
grpc::RpcClientRoundtripLatency();
grpc::RpcClientServerLatency();
grpc::RpcClientSentMessagesPerRpc();
grpc::RpcClientReceivedMessagesPerRpc();
grpc::RpcServerSentBytesPerRpc();
grpc::RpcServerReceivedBytesPerRpc();
grpc::RpcServerServerLatency();
grpc::RpcServerSentMessagesPerRpc();
grpc::RpcServerReceivedMessagesPerRpc();
}
::opencensus::trace::Span GetSpanFromServerContext(
grpc::ServerContext* context) {
return reinterpret_cast<const grpc::CensusContext*>(context->census_context())
->Span();
}
} // namespace grpc_impl
} // namespace grpc

@ -25,23 +25,6 @@
#include "opencensus/stats/internal/set_aggregation_window.h"
#include "opencensus/stats/stats.h"
namespace grpc_impl {
void RegisterOpenCensusViewsForExport() {
grpc::ClientSentMessagesPerRpcCumulative().RegisterForExport();
grpc::ClientSentBytesPerRpcCumulative().RegisterForExport();
grpc::ClientReceivedMessagesPerRpcCumulative().RegisterForExport();
grpc::ClientReceivedBytesPerRpcCumulative().RegisterForExport();
grpc::ClientRoundtripLatencyCumulative().RegisterForExport();
grpc::ClientServerLatencyCumulative().RegisterForExport();
grpc::ServerSentMessagesPerRpcCumulative().RegisterForExport();
grpc::ServerSentBytesPerRpcCumulative().RegisterForExport();
grpc::ServerReceivedMessagesPerRpcCumulative().RegisterForExport();
grpc::ServerReceivedBytesPerRpcCumulative().RegisterForExport();
grpc::ServerServerLatencyCumulative().RegisterForExport();
}
} // namespace grpc_impl
namespace grpc {
using ::opencensus::stats::Aggregation;
@ -88,6 +71,21 @@ ViewDescriptor HourDescriptor() {
} // namespace
void RegisterOpenCensusViewsForExport() {
ClientSentMessagesPerRpcCumulative().RegisterForExport();
ClientSentBytesPerRpcCumulative().RegisterForExport();
ClientReceivedMessagesPerRpcCumulative().RegisterForExport();
ClientReceivedBytesPerRpcCumulative().RegisterForExport();
ClientRoundtripLatencyCumulative().RegisterForExport();
ClientServerLatencyCumulative().RegisterForExport();
ServerSentMessagesPerRpcCumulative().RegisterForExport();
ServerSentBytesPerRpcCumulative().RegisterForExport();
ServerReceivedMessagesPerRpcCumulative().RegisterForExport();
ServerReceivedBytesPerRpcCumulative().RegisterForExport();
ServerServerLatencyCumulative().RegisterForExport();
}
// client cumulative
const ViewDescriptor& ClientSentBytesPerRpcCumulative() {
const static ViewDescriptor descriptor =

@ -401,6 +401,7 @@ static const NSTimeInterval kInvertedTimeout = 2;
GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
options.timeout = 0.001;
options.transportType = GRPCTransportTypeInsecure;
GRPCRequestOptions *requestOptions =
[[GRPCRequestOptions alloc] initWithHost:kHostAddress
path:kFullDuplexCallMethod.HTTPPath

@ -52,13 +52,5 @@ cdef class CallbackWrapper:
cdef grpc_experimental_completion_queue_functor *c_functor(self)
cdef class CallbackCompletionQueue:
cdef grpc_completion_queue *_cq
cdef object _shutdown_completed # asyncio.Future
cdef CallbackWrapper _wrapper
cdef grpc_completion_queue* c_ptr(self)
cdef class GrpcCallWrapper:
cdef grpc_call* call

@ -69,27 +69,6 @@ cdef CallbackFailureHandler CQ_SHUTDOWN_FAILURE_HANDLER = CallbackFailureHandler
InternalError)
cdef class CallbackCompletionQueue:
def __cinit__(self):
self._shutdown_completed = asyncio.get_event_loop().create_future()
self._wrapper = CallbackWrapper(
self._shutdown_completed,
CQ_SHUTDOWN_FAILURE_HANDLER)
self._cq = grpc_completion_queue_create_for_callback(
self._wrapper.c_functor(),
NULL
)
cdef grpc_completion_queue* c_ptr(self):
return self._cq
async def shutdown(self):
grpc_completion_queue_shutdown(self._cq)
await self._shutdown_completed
grpc_completion_queue_destroy(self._cq)
class ExecuteBatchError(Exception): pass

@ -21,7 +21,7 @@ cdef enum AioChannelStatus:
cdef class AioChannel:
cdef:
grpc_channel * channel
CallbackCompletionQueue cq
BaseCompletionQueue cq
object loop
bytes _target
AioChannelStatus _status

@ -31,7 +31,7 @@ cdef class AioChannel:
options = ()
cdef _ChannelArgs channel_args = _ChannelArgs(options)
self._target = target
self.cq = CallbackCompletionQueue()
self.cq = create_completion_queue()
self.loop = loop
self._status = AIO_CHANNEL_STATUS_READY

@ -0,0 +1,30 @@
# 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.
cdef class BaseCompletionQueue:
cdef grpc_completion_queue *_cq
cdef grpc_completion_queue* c_ptr(self)
cdef class PollerCompletionQueue(BaseCompletionQueue):
cdef bint _shutdown
cdef object _shutdown_completed
cdef object _poller_thread
cdef void _poll(self) except *
cdef class CallbackCompletionQueue(BaseCompletionQueue):
cdef object _shutdown_completed # asyncio.Future
cdef CallbackWrapper _wrapper

@ -0,0 +1,96 @@
# 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.
cdef gpr_timespec _GPR_INF_FUTURE = gpr_inf_future(GPR_CLOCK_REALTIME)
def _handle_callback_wrapper(CallbackWrapper callback_wrapper, int success):
CallbackWrapper.functor_run(callback_wrapper.c_functor(), success)
cdef class BaseCompletionQueue:
async def shutdown(self):
raise NotImplementedError()
cdef grpc_completion_queue* c_ptr(self):
return self._cq
cdef class PollerCompletionQueue(BaseCompletionQueue):
def __cinit__(self):
self._cq = grpc_completion_queue_create_for_next(NULL)
self._shutdown = False
self._shutdown_completed = asyncio.get_event_loop().create_future()
self._poller_thread = threading.Thread(target=self._poll_wrapper, daemon=True)
self._poller_thread.start()
cdef void _poll(self) except *:
cdef grpc_event event
cdef CallbackContext *context
while not self._shutdown:
with nogil:
event = grpc_completion_queue_next(self._cq,
_GPR_INF_FUTURE,
NULL)
if event.type == GRPC_QUEUE_TIMEOUT:
raise AssertionError("Core should not return timeout error!")
elif event.type == GRPC_QUEUE_SHUTDOWN:
self._shutdown = True
aio_loop_call_soon_threadsafe(self._shutdown_completed.set_result, None)
else:
context = <CallbackContext *>event.tag
aio_loop_call_soon_threadsafe(
_handle_callback_wrapper,
<CallbackWrapper>context.callback_wrapper,
event.success)
def _poll_wrapper(self):
self._poll()
async def shutdown(self):
grpc_completion_queue_shutdown(self._cq)
await self._shutdown_completed
grpc_completion_queue_destroy(self._cq)
self._poller_thread.join()
cdef class CallbackCompletionQueue(BaseCompletionQueue):
def __cinit__(self):
self._shutdown_completed = grpc_aio_loop().create_future()
self._wrapper = CallbackWrapper(
self._shutdown_completed,
CQ_SHUTDOWN_FAILURE_HANDLER)
self._cq = grpc_completion_queue_create_for_callback(
self._wrapper.c_functor(),
NULL
)
async def shutdown(self):
grpc_completion_queue_shutdown(self._cq)
await self._shutdown_completed
grpc_completion_queue_destroy(self._cq)
cdef BaseCompletionQueue create_completion_queue():
if grpc_aio_engine is AsyncIOEngine.CUSTOM_IO_MANAGER:
return CallbackCompletionQueue()
elif grpc_aio_engine is AsyncIOEngine.POLLER:
return PollerCompletionQueue()
else:
raise ValueError('Unsupported engine type [%s]' % grpc_aio_engine)

@ -13,25 +13,97 @@
# limitations under the License.
cdef bint _grpc_aio_initialized = 0
cdef bint _grpc_aio_initialized = False
# NOTE(lidiz) Theoretically, applications can run in multiple event loops as
# long as they are in the same thread with same magic. This is not a supported
# use case. So, the gRPC Python Async Stack should use a single event loop
# picked by "init_grpc_aio".
cdef object _grpc_aio_loop # asyncio.AbstractEventLoop
cdef int64_t _event_loop_thread_ident
cdef str _GRPC_ASYNCIO_ENGINE = os.environ.get('GRPC_ASYNCIO_ENGINE', 'default').lower()
grpc_aio_engine = None
cdef object _grpc_initialization_lock = threading.Lock()
class AsyncIOEngine(enum.Enum):
DEFAULT = 'default'
CUSTOM_IO_MANAGER = 'custom'
POLLER = 'poller'
def init_grpc_aio():
global _grpc_aio_initialized
global _grpc_aio_loop
global _event_loop_thread_ident
global grpc_aio_engine
with _grpc_initialization_lock:
# Marks this function as called
if _grpc_aio_initialized:
return
else:
_grpc_aio_initialized = True
# Picks the engine for gRPC AsyncIO Stack
for engine_type in AsyncIOEngine:
if engine_type.value == _GRPC_ASYNCIO_ENGINE:
grpc_aio_engine = engine_type
break
if grpc_aio_engine is None or grpc_aio_engine is AsyncIOEngine.DEFAULT:
grpc_aio_engine = AsyncIOEngine.CUSTOM_IO_MANAGER
# Anchors the event loop that the gRPC library going to use.
_grpc_aio_loop = asyncio.get_event_loop()
_event_loop_thread_ident = threading.current_thread().ident
if grpc_aio_engine is AsyncIOEngine.CUSTOM_IO_MANAGER:
# Activates asyncio IO manager.
# NOTE(lidiz) Custom IO manager must be activated before the first
# `grpc_init()`. Otherwise, some special configurations in Core won't
# pick up the change, and resulted in SEGFAULT or ABORT.
install_asyncio_iomgr()
# TODO(https://github.com/grpc/grpc/issues/22244) we need a the
# grpc_shutdown_blocking() counterpart for this call. Otherwise, the gRPC
# library won't shutdown cleanly.
grpc_init()
# Timers are triggered by the Asyncio loop. We disable
# the background thread that is being used by the native
# gRPC iomgr.
grpc_timer_manager_set_threading(False)
# gRPC callbaks are executed within the same thread used by the Asyncio
# event loop, as it is being done by the other Asyncio callbacks.
Executor.SetThreadingAll(False)
else:
# TODO(https://github.com/grpc/grpc/issues/22244) we need a the
# grpc_shutdown_blocking() counterpart for this call. Otherwise, the gRPC
# library won't shutdown cleanly.
grpc_init()
def grpc_aio_loop():
"""Returns the one-and-only gRPC Aio event loop."""
return _grpc_aio_loop
if _grpc_aio_initialized:
return
install_asyncio_iomgr()
grpc_init()
def aio_loop_schedule_coroutine(object coro):
"""Thread-safely schedules coroutine to gRPC Aio event loop.
# Timers are triggered by the Asyncio loop. We disable
# the background thread that is being used by the native
# gRPC iomgr.
grpc_timer_manager_set_threading(False)
If invoked within the same thread as the event loop, return an
Asyncio.Task. Otherwise, return a concurrent.futures.Future (the sync
Future). For non-asyncio threads, sync Future objects are probably easier
to handle (without worrying other thread-safety stuff).
"""
if _event_loop_thread_ident != threading.current_thread().ident:
return asyncio.run_coroutine_threadsafe(coro, _grpc_aio_loop)
else:
return _grpc_aio_loop.create_task(coro)
# gRPC callbaks are executed within the same thread used by the Asyncio
# event loop, as it is being done by the other Asyncio callbacks.
Executor.SetThreadingAll(False)
_grpc_aio_initialized = 1
def aio_loop_call_soon_threadsafe(object func, *args):
# TODO(lidiz) After we are confident, we can drop this assert. Otherwsie,
# we should limit this function to non-grpc-event-loop thread.
assert _event_loop_thread_ident != threading.current_thread().ident
return _grpc_aio_loop.call_soon_threadsafe(func, *args)

@ -49,7 +49,6 @@ cdef void asyncio_socket_connect(
const grpc_sockaddr* addr,
size_t addr_len,
grpc_custom_connect_callback connect_cb) with gil:
host, port = sockaddr_to_tuple(addr, addr_len)
socket = <_AsyncioSocket>grpc_socket.impl
socket.connect(host, port, connect_cb)
@ -185,14 +184,16 @@ cdef void asyncio_resolve_async(
cdef void asyncio_timer_start(grpc_custom_timer* grpc_timer) with gil:
timer = _AsyncioTimer.create(grpc_timer, grpc_timer.timeout_ms / 1000.0)
Py_INCREF(timer)
grpc_timer.timer = <void*>timer
cdef void asyncio_timer_stop(grpc_custom_timer* grpc_timer) with gil:
timer = <_AsyncioTimer>grpc_timer.timer
timer.stop()
Py_DECREF(timer)
# TODO(https://github.com/grpc/grpc/issues/22278) remove this if condition
if grpc_timer.timer == NULL:
return
else:
timer = <_AsyncioTimer>grpc_timer.timer
timer.stop()
cdef void asyncio_init_loop() with gil:

@ -29,34 +29,27 @@ cdef class _AsyncioResolver:
id_ = id(self)
return f"<{class_name} {id_}>"
def _resolve_cb(self, future):
error = False
async def _async_resolve(self, bytes host, bytes port):
self._task_resolve = None
try:
res = future.result()
resolved = await grpc_aio_loop().getaddrinfo(host, port)
except Exception as e:
error = True
error_msg = str(e)
finally:
self._task_resolve = None
if not error:
grpc_custom_resolve_callback(
<grpc_custom_resolver*>self._grpc_resolver,
tuples_to_resolvaddr(res),
<grpc_error*>0
NULL,
grpc_socket_error("Resolve address [{}:{}] failed: {}: {}".format(
host, port, type(e), str(e)).encode())
)
else:
grpc_custom_resolve_callback(
<grpc_custom_resolver*>self._grpc_resolver,
NULL,
grpc_socket_error("getaddrinfo {}".format(error_msg).encode())
tuples_to_resolvaddr(resolved),
<grpc_error*>0
)
cdef void resolve(self, char* host, char* port):
assert not self._task_resolve
loop = asyncio.get_event_loop()
self._task_resolve = asyncio.ensure_future(
loop.getaddrinfo(host, port)
self._task_resolve = grpc_aio_loop().create_task(
self._async_resolve(host, port)
)
self._task_resolve.add_done_callback(self._resolve_cb)

@ -24,10 +24,14 @@ cdef class _AsyncioSocket:
object _task_read
object _task_write
object _task_connect
object _task_listen
char * _read_buffer
# Caches the picked event loop, so we can avoid the 30ns overhead each
# time we need access to the event loop.
object _loop
# TODO(lidiz) Drop after 3.6 deprecation. Python 3.7 introduces methods
# like `is_closing()` to help graceful shutdown.
bint _closed
# Client-side attributes
grpc_custom_connect_callback _grpc_connect_cb

@ -31,11 +31,12 @@ cdef class _AsyncioSocket:
self._task_connect = None
self._task_read = None
self._task_write = None
self._task_listen = None
self._read_buffer = NULL
self._server = None
self._py_socket = None
self._peername = None
self._loop = asyncio.get_event_loop()
self._closed = False
@staticmethod
cdef _AsyncioSocket create(grpc_custom_socket * grpc_socket,
@ -62,27 +63,37 @@ cdef class _AsyncioSocket:
connected = self.is_connected()
return f"<{class_name} {id_} connected={connected}>"
def _connect_cb(self, future):
async def _async_connect(self, object host, object port,):
self._task_connect = None
try:
self._reader, self._writer = future.result()
self._reader, self._writer = await asyncio.open_connection(host, port)
except Exception as e:
self._grpc_connect_cb(
<grpc_custom_socket*>self._grpc_socket,
grpc_socket_error("Socket connect failed: {}".format(e).encode())
grpc_socket_error("Socket connect failed: {}: {}".format(type(e), str(e)).encode())
)
return
finally:
self._task_connect = None
else:
# gRPC default posix implementation disables nagle
# algorithm.
sock = self._writer.transport.get_extra_info('socket')
sock.setsockopt(native_socket.IPPROTO_TCP, native_socket.TCP_NODELAY, True)
# gRPC default posix implementation disables nagle
# algorithm.
sock = self._writer.transport.get_extra_info('socket')
sock.setsockopt(native_socket.IPPROTO_TCP, native_socket.TCP_NODELAY, True)
self._grpc_connect_cb(
<grpc_custom_socket*>self._grpc_socket,
<grpc_error*>0
)
self._grpc_connect_cb(
<grpc_custom_socket*>self._grpc_socket,
<grpc_error*>0
cdef void connect(self,
object host,
object port,
grpc_custom_connect_callback grpc_connect_cb):
assert not self._reader
assert not self._task_connect
self._task_connect = grpc_aio_loop().create_task(
self._async_connect(host, port)
)
self._grpc_connect_cb = grpc_connect_cb
async def _async_read(self, size_t length):
self._task_read = None
@ -106,25 +117,12 @@ cdef class _AsyncioSocket:
<grpc_error*>0
)
cdef void connect(self,
object host,
object port,
grpc_custom_connect_callback grpc_connect_cb):
assert not self._reader
assert not self._task_connect
self._task_connect = asyncio.ensure_future(
asyncio.open_connection(host, port)
)
self._grpc_connect_cb = grpc_connect_cb
self._task_connect.add_done_callback(self._connect_cb)
cdef void read(self, char * buffer_, size_t length, grpc_custom_read_callback grpc_read_cb):
assert not self._task_read
self._grpc_read_cb = grpc_read_cb
self._read_buffer = buffer_
self._task_read = self._loop.create_task(self._async_read(length))
self._task_read = grpc_aio_loop().create_task(self._async_read(length))
async def _async_write(self, bytearray outbound_buffer):
self._writer.write(outbound_buffer)
@ -157,14 +155,20 @@ cdef class _AsyncioSocket:
outbound_buffer.extend(<bytes>start[:length])
self._grpc_write_cb = grpc_write_cb
self._task_write = self._loop.create_task(self._async_write(outbound_buffer))
self._task_write = grpc_aio_loop().create_task(self._async_write(outbound_buffer))
cdef bint is_connected(self):
return self._reader and not self._reader._transport.is_closing()
cdef void close(self):
if self._closed:
return
else:
self._closed = True
if self.is_connected():
self._writer.close()
if self._task_listen and not self._task_listen.done():
self._task_listen.close()
if self._server:
self._server.close()
# NOTE(lidiz) If the asyncio.Server is created from a Python socket,
@ -174,6 +178,10 @@ cdef class _AsyncioSocket:
self._py_socket.close()
def _new_connection_callback(self, object reader, object writer):
# If the socket is closed, stop.
if self._closed:
return
# Close the connection if server is not started yet.
if self._grpc_accept_cb == NULL:
writer.close()
@ -201,7 +209,7 @@ cdef class _AsyncioSocket:
sock=self._py_socket,
)
self._loop.create_task(create_asyncio_server())
self._task_listen = grpc_aio_loop().create_task(create_asyncio_server())
cdef accept(self,
grpc_custom_socket* grpc_socket_client,

@ -15,11 +15,10 @@
cdef class _AsyncioTimer:
cdef:
grpc_custom_timer * _grpc_timer
object _deadline
object _timer_handler
int _active
object _timer_future
bint _active
@staticmethod
cdef _AsyncioTimer create(grpc_custom_timer * grpc_timer, deadline)
cdef _AsyncioTimer create(grpc_custom_timer * grpc_timer, float timeout)
cdef stop(self)

@ -16,21 +16,22 @@
cdef class _AsyncioTimer:
def __cinit__(self):
self._grpc_timer = NULL
self._timer_handler = None
self._active = 0
self._timer_future = None
self._active = False
cpython.Py_INCREF(self)
@staticmethod
cdef _AsyncioTimer create(grpc_custom_timer * grpc_timer, deadline):
cdef _AsyncioTimer create(grpc_custom_timer * grpc_timer, float timeout):
timer = _AsyncioTimer()
timer._grpc_timer = grpc_timer
timer._deadline = deadline
timer._timer_handler = asyncio.get_event_loop().call_later(deadline, timer._on_deadline)
timer._active = 1
timer._timer_future = grpc_aio_loop().call_later(timeout, timer.on_time_up)
timer._active = True
return timer
def _on_deadline(self):
self._active = 0
def on_time_up(self):
self._active = False
grpc_custom_timer_callback(self._grpc_timer, <grpc_error*>0)
cpython.Py_DECREF(self)
def __repr__(self):
class_name = self.__class__.__name__
@ -38,8 +39,9 @@ cdef class _AsyncioTimer:
return f"<{class_name} {id_} deadline={self._deadline} active={self._active}>"
cdef stop(self):
if self._active == 0:
if not self._active:
return
self._timer_handler.cancel()
self._active = 0
self._timer_future.cancel()
self._active = False
cpython.Py_DECREF(self)

@ -51,7 +51,7 @@ cdef enum AioServerStatus:
cdef class AioServer:
cdef Server _server
cdef CallbackCompletionQueue _cq
cdef BaseCompletionQueue _cq
cdef list _generic_handlers
cdef AioServerStatus _status
cdef object _loop # asyncio.EventLoop

@ -613,7 +613,7 @@ cdef class AioServer:
# NOTE(lidiz) Core objects won't be deallocated automatically.
# If AioServer.shutdown is not called, those objects will leak.
self._server = Server(options)
self._cq = CallbackCompletionQueue()
self._cq = create_completion_queue()
grpc_server_register_completion_queue(
self._server.c_server,
self._cq.c_ptr(),
@ -736,7 +736,7 @@ cdef class AioServer:
# The shutdown callback won't be called until there is no live RPC.
grpc_server_shutdown_and_notify(
self._server.c_server,
self._cq._cq,
self._cq.c_ptr(),
self._shutdown_callback_wrapper.c_functor())
# Ensures the serving task (coroutine) exits.

@ -256,6 +256,8 @@ cdef void _call(
on_success(started_tags)
else:
raise ValueError('Cannot invoke RPC: %s' % channel_state.closed_reason)
cdef void _process_integrated_call_tag(
_ChannelState state, _BatchOperationTag tag) except *:
cdef _CallState call_state = state.integrated_call_states.pop(tag)

@ -148,8 +148,9 @@ cdef class Server:
# much but repeatedly release the GIL and wait
while not self.is_shutdown:
time.sleep(0)
grpc_server_destroy(self.c_server)
self.c_server = NULL
with nogil:
grpc_server_destroy(self.c_server)
self.c_server = NULL
def __dealloc__(self):
if self.c_server == NULL:

@ -45,6 +45,7 @@ IF UNAME_SYSNAME != "Windows":
include "_cygrpc/aio/iomgr/socket.pxd.pxi"
include "_cygrpc/aio/iomgr/timer.pxd.pxi"
include "_cygrpc/aio/iomgr/resolver.pxd.pxi"
include "_cygrpc/aio/completion_queue.pxd.pxi"
include "_cygrpc/aio/rpc_status.pxd.pxi"
include "_cygrpc/aio/grpc_aio.pxd.pxi"
include "_cygrpc/aio/callback_common.pxd.pxi"

@ -20,6 +20,7 @@ import os
import sys
import threading
import time
import enum
import grpc
@ -71,6 +72,7 @@ include "_cygrpc/aio/iomgr/timer.pyx.pxi"
include "_cygrpc/aio/iomgr/resolver.pyx.pxi"
include "_cygrpc/aio/common.pyx.pxi"
include "_cygrpc/aio/rpc_status.pyx.pxi"
include "_cygrpc/aio/completion_queue.pyx.pxi"
include "_cygrpc/aio/callback_common.pyx.pxi"
include "_cygrpc/aio/grpc_aio.pyx.pxi"
include "_cygrpc/aio/call.pyx.pxi"

@ -53,8 +53,10 @@ else:
def _create_channel(target: str, options: Sequence[Tuple[str, str]],
channel_credentials: Optional[grpc.ChannelCredentials],
compression: Optional[grpc.Compression]) -> grpc.Channel:
channel_credentials = channel_credentials or grpc.local_channel_credentials(
)
# TODO(rbellevi): Revisit the default value for this.
if channel_credentials is None:
raise NotImplementedError(
"channel_credentials must be supplied explicitly.")
if channel_credentials._credentials is grpc.experimental._insecure_channel_credentials:
_LOGGER.debug(f"Creating insecure channel with options '{options}' " +
f"and compression '{compression}'")
@ -156,26 +158,13 @@ class ChannelCache:
return len(self._mapping)
# TODO(rbellevi): Consider a credential type that has the
# following functionality matrix:
#
# +----------+-------+--------+
# | | local | remote |
# |----------+-------+--------+
# | secure | o | o |
# | insecure | o | x |
# +----------+-------+--------+
#
# Make this the default option.
@experimental_api
def unary_unary(
request: RequestType,
target: str,
method: str,
request_serializer: Optional[Callable[[Any], bytes]] = None,
request_deserializer: Optional[Callable[[bytes], Any]] = None,
response_deserializer: Optional[Callable[[bytes], Any]] = None,
options: Sequence[Tuple[AnyStr, AnyStr]] = (),
channel_credentials: Optional[grpc.ChannelCredentials] = None,
call_credentials: Optional[grpc.CallCredentials] = None,
@ -232,7 +221,7 @@ def unary_unary(
channel = ChannelCache.get().get_channel(target, options,
channel_credentials, compression)
multicallable = channel.unary_unary(method, request_serializer,
request_deserializer)
response_deserializer)
return multicallable(request,
metadata=metadata,
wait_for_ready=wait_for_ready,
@ -246,7 +235,7 @@ def unary_stream(
target: str,
method: str,
request_serializer: Optional[Callable[[Any], bytes]] = None,
request_deserializer: Optional[Callable[[bytes], Any]] = None,
response_deserializer: Optional[Callable[[bytes], Any]] = None,
options: Sequence[Tuple[AnyStr, AnyStr]] = (),
channel_credentials: Optional[grpc.ChannelCredentials] = None,
call_credentials: Optional[grpc.CallCredentials] = None,
@ -302,7 +291,7 @@ def unary_stream(
channel = ChannelCache.get().get_channel(target, options,
channel_credentials, compression)
multicallable = channel.unary_stream(method, request_serializer,
request_deserializer)
response_deserializer)
return multicallable(request,
metadata=metadata,
wait_for_ready=wait_for_ready,
@ -316,7 +305,7 @@ def stream_unary(
target: str,
method: str,
request_serializer: Optional[Callable[[Any], bytes]] = None,
request_deserializer: Optional[Callable[[bytes], Any]] = None,
response_deserializer: Optional[Callable[[bytes], Any]] = None,
options: Sequence[Tuple[AnyStr, AnyStr]] = (),
channel_credentials: Optional[grpc.ChannelCredentials] = None,
call_credentials: Optional[grpc.CallCredentials] = None,
@ -372,7 +361,7 @@ def stream_unary(
channel = ChannelCache.get().get_channel(target, options,
channel_credentials, compression)
multicallable = channel.stream_unary(method, request_serializer,
request_deserializer)
response_deserializer)
return multicallable(request_iterator,
metadata=metadata,
wait_for_ready=wait_for_ready,
@ -386,7 +375,7 @@ def stream_stream(
target: str,
method: str,
request_serializer: Optional[Callable[[Any], bytes]] = None,
request_deserializer: Optional[Callable[[bytes], Any]] = None,
response_deserializer: Optional[Callable[[bytes], Any]] = None,
options: Sequence[Tuple[AnyStr, AnyStr]] = (),
channel_credentials: Optional[grpc.ChannelCredentials] = None,
call_credentials: Optional[grpc.CallCredentials] = None,
@ -442,7 +431,7 @@ def stream_stream(
channel = ChannelCache.get().get_channel(target, options,
channel_credentials, compression)
multicallable = channel.stream_stream(method, request_serializer,
request_deserializer)
response_deserializer)
return multicallable(request_iterator,
metadata=metadata,
wait_for_ready=wait_for_ready,

@ -228,7 +228,7 @@ class Channel(_base_channel.Channel):
"UnaryUnaryClientInterceptors, the following are invalid: {}"\
.format(invalid_interceptors))
self._loop = asyncio.get_event_loop()
self._loop = cygrpc.grpc_aio_loop()
self._channel = cygrpc.AioChannel(
_common.encode(target),
_augment_channel_arguments(options, compression), credentials,
@ -240,7 +240,7 @@ class Channel(_base_channel.Channel):
async def __aexit__(self, exc_type, exc_val, exc_tb):
await self._close(None)
async def _close(self, grace):
async def _close(self, grace): # pylint: disable=too-many-branches
if self._channel.closed():
return
@ -252,7 +252,27 @@ class Channel(_base_channel.Channel):
calls = []
call_tasks = []
for task in tasks:
stack = task.get_stack(limit=1)
try:
stack = task.get_stack(limit=1)
except AttributeError as attribute_error:
# NOTE(lidiz) tl;dr: If the Task is created with a CPython
# object, it will trigger AttributeError.
#
# In the global finalizer, the event loop schedules
# a CPython PyAsyncGenAThrow object.
# https://github.com/python/cpython/blob/00e45877e33d32bb61aa13a2033e3bba370bda4d/Lib/asyncio/base_events.py#L484
#
# However, the PyAsyncGenAThrow object is written in C and
# failed to include the normal Python frame objects. Hence,
# this exception is a false negative, and it is safe to ignore
# the failure. It is fixed by https://github.com/python/cpython/pull/18669,
# but not available until 3.9 or 3.8.3. So, we have to keep it
# for a while.
# TODO(lidiz) drop this hack after 3.8 deprecation
if 'frame' in str(attribute_error):
continue
else:
raise
# If the Task is created by a C-extension, the stack will be empty.
if not stack:

@ -160,10 +160,10 @@ class InterceptedUnaryUnaryCall(_base_call.UnaryUnaryCall):
loop: asyncio.AbstractEventLoop) -> None:
self._channel = channel
self._loop = loop
self._interceptors_task = asyncio.ensure_future(self._invoke(
interceptors, method, timeout, metadata, credentials,
wait_for_ready, request, request_serializer, response_deserializer),
loop=loop)
self._interceptors_task = loop.create_task(
self._invoke(interceptors, method, timeout, metadata, credentials,
wait_for_ready, request, request_serializer,
response_deserializer))
self._pending_add_done_callbacks = []
self._interceptors_task.add_done_callback(
self._fire_pending_add_done_callbacks)

@ -13,7 +13,6 @@
# limitations under the License.
"""Server-side implementation of gRPC Asyncio Python."""
import asyncio
from concurrent.futures import Executor
from typing import Any, Optional, Sequence
@ -41,7 +40,7 @@ class Server(_base_server.Server):
options: ChannelArgumentType,
maximum_concurrent_rpcs: Optional[int],
compression: Optional[grpc.Compression]):
self._loop = asyncio.get_event_loop()
self._loop = cygrpc.grpc_aio_loop()
if interceptors:
invalid_interceptors = [
interceptor for interceptor in interceptors
@ -163,7 +162,7 @@ class Server(_base_server.Server):
be safe to slightly extend the underlying Cython object's life span.
"""
if hasattr(self, '_server'):
self._loop.create_task(self._server.shutdown(None))
cygrpc.aio_loop_schedule_coroutine(self._server.shutdown(None))
def server(migration_thread_pool: Optional[Executor] = None,

@ -193,6 +193,7 @@ class TestGevent(setuptools.Command):
'unit._server_ssl_cert_config_test',
# TODO(https://github.com/grpc/grpc/issues/14901) enable this test
'protoc_plugin._python_plugin_test.PythonPluginTest',
'protoc_plugin._python_plugin_test.SimpleStubsPluginTest',
# Beta API is unsupported for gevent
'protoc_plugin.beta_python_plugin_test',
'unit.beta._beta_features_test',

@ -27,6 +27,7 @@ import unittest
from six import moves
import grpc
import grpc.experimental
from tests.unit import test_common
from tests.unit.framework.common import test_constants
@ -503,5 +504,118 @@ class PythonPluginTest(unittest.TestCase):
service.server.stop(None)
@unittest.skipIf(sys.version_info[0] < 3, "Unsupported on Python 2.")
class SimpleStubsPluginTest(unittest.TestCase):
servicer_methods = _ServicerMethods()
class Servicer(service_pb2_grpc.TestServiceServicer):
def UnaryCall(self, request, context):
return SimpleStubsPluginTest.servicer_methods.UnaryCall(
request, context)
def StreamingOutputCall(self, request, context):
return SimpleStubsPluginTest.servicer_methods.StreamingOutputCall(
request, context)
def StreamingInputCall(self, request_iterator, context):
return SimpleStubsPluginTest.servicer_methods.StreamingInputCall(
request_iterator, context)
def FullDuplexCall(self, request_iterator, context):
return SimpleStubsPluginTest.servicer_methods.FullDuplexCall(
request_iterator, context)
def HalfDuplexCall(self, request_iterator, context):
return SimpleStubsPluginTest.servicer_methods.HalfDuplexCall(
request_iterator, context)
def setUp(self):
super(SimpleStubsPluginTest, self).setUp()
self._server = test_common.test_server()
service_pb2_grpc.add_TestServiceServicer_to_server(
self.Servicer(), self._server)
self._port = self._server.add_insecure_port('[::]:0')
self._server.start()
self._target = 'localhost:{}'.format(self._port)
def tearDown(self):
self._server.stop(None)
super(SimpleStubsPluginTest, self).tearDown()
def testUnaryCall(self):
request = request_pb2.SimpleRequest(response_size=13)
response = service_pb2_grpc.TestService.UnaryCall(
request,
self._target,
channel_credentials=grpc.experimental.insecure_channel_credentials(
),
wait_for_ready=True)
expected_response = self.servicer_methods.UnaryCall(
request, 'not a real context!')
self.assertEqual(expected_response, response)
def testStreamingOutputCall(self):
request = _streaming_output_request()
expected_responses = self.servicer_methods.StreamingOutputCall(
request, 'not a real RpcContext!')
responses = service_pb2_grpc.TestService.StreamingOutputCall(
request,
self._target,
channel_credentials=grpc.experimental.insecure_channel_credentials(
),
wait_for_ready=True)
for expected_response, response in moves.zip_longest(
expected_responses, responses):
self.assertEqual(expected_response, response)
def testStreamingInputCall(self):
response = service_pb2_grpc.TestService.StreamingInputCall(
_streaming_input_request_iterator(),
self._target,
channel_credentials=grpc.experimental.insecure_channel_credentials(
),
wait_for_ready=True)
expected_response = self.servicer_methods.StreamingInputCall(
_streaming_input_request_iterator(), 'not a real RpcContext!')
self.assertEqual(expected_response, response)
def testFullDuplexCall(self):
responses = service_pb2_grpc.TestService.FullDuplexCall(
_full_duplex_request_iterator(),
self._target,
channel_credentials=grpc.experimental.insecure_channel_credentials(
),
wait_for_ready=True)
expected_responses = self.servicer_methods.FullDuplexCall(
_full_duplex_request_iterator(), 'not a real RpcContext!')
for expected_response, response in moves.zip_longest(
expected_responses, responses):
self.assertEqual(expected_response, response)
def testHalfDuplexCall(self):
def half_duplex_request_iterator():
request = request_pb2.StreamingOutputCallRequest()
request.response_parameters.add(size=1, interval_us=0)
yield request
request = request_pb2.StreamingOutputCallRequest()
request.response_parameters.add(size=2, interval_us=0)
request.response_parameters.add(size=3, interval_us=0)
yield request
responses = service_pb2_grpc.TestService.HalfDuplexCall(
half_duplex_request_iterator(),
self._target,
channel_credentials=grpc.experimental.insecure_channel_credentials(
),
wait_for_ready=True)
expected_responses = self.servicer_methods.HalfDuplexCall(
half_duplex_request_iterator(), 'not a real RpcContext!')
for expected_response, response in moves.zip_longest(
expected_responses, responses):
self.assertEqual(expected_response, response)
if __name__ == '__main__':
unittest.main(verbosity=2)

@ -7,6 +7,7 @@
"interop._insecure_intraop_test.InsecureIntraopTest",
"interop._secure_intraop_test.SecureIntraopTest",
"protoc_plugin._python_plugin_test.PythonPluginTest",
"protoc_plugin._python_plugin_test.SimpleStubsPluginTest",
"protoc_plugin._split_definitions_test.SameProtoGrpcBeforeProtoProtocStyleTest",
"protoc_plugin._split_definitions_test.SameProtoMid2016ProtocStyleTest",
"protoc_plugin._split_definitions_test.SameProtoProtoBeforeGrpcProtocStyleTest",

@ -457,16 +457,16 @@ class TestUnaryStreamCall(_MulticallableTestMixin, AioTestBase):
# Should be around the same as the timeout
remained_time = call.time_remaining()
self.assertGreater(remained_time, test_constants.SHORT_TIMEOUT * 3 // 2)
self.assertLess(remained_time, test_constants.SHORT_TIMEOUT * 2)
self.assertGreater(remained_time, test_constants.SHORT_TIMEOUT * 3 / 2)
self.assertLess(remained_time, test_constants.SHORT_TIMEOUT * 5 / 2)
response = await call.read()
self.assertEqual(_RESPONSE_PAYLOAD_SIZE, len(response.payload.body))
# Should be around the timeout minus a unit of wait time
remained_time = call.time_remaining()
self.assertGreater(remained_time, test_constants.SHORT_TIMEOUT // 2)
self.assertLess(remained_time, test_constants.SHORT_TIMEOUT * 3 // 2)
self.assertGreater(remained_time, test_constants.SHORT_TIMEOUT / 2)
self.assertLess(remained_time, test_constants.SHORT_TIMEOUT * 3 / 2)
self.assertEqual(grpc.StatusCode.OK, await call.code())

@ -348,11 +348,10 @@ class TestServer(AioTestBase):
await self._server.stop(test_constants.SHORT_TIMEOUT)
with self.assertRaises(grpc.RpcError) as exception_context:
with self.assertRaises(aio.AioRpcError) as exception_context:
await call
self.assertEqual(grpc.StatusCode.UNAVAILABLE,
exception_context.exception.code())
self.assertIn('GOAWAY', exception_context.exception.details())
async def test_concurrent_graceful_shutdown(self):
call = self._channel.unary_unary(_BLOCK_BRIEFLY)(_REQUEST)
@ -384,21 +383,18 @@ class TestServer(AioTestBase):
self._server.stop(test_constants.LONG_TIMEOUT),
)
with self.assertRaises(grpc.RpcError) as exception_context:
with self.assertRaises(aio.AioRpcError) as exception_context:
await call
self.assertEqual(grpc.StatusCode.UNAVAILABLE,
exception_context.exception.code())
self.assertIn('GOAWAY', exception_context.exception.details())
@unittest.skip('https://github.com/grpc/grpc/issues/20818')
async def test_shutdown_before_call(self):
server_target, server, _ = _start_test_server()
await server.stop(None)
await self._server.stop(None)
# Ensures the server is cleaned up at this point.
# Some proper exception should be raised.
async with aio.insecure_channel('localhost:%d' % port) as channel:
await channel.unary_unary(_SIMPLE_UNARY_UNARY)(_REQUEST)
with self.assertRaises(aio.AioRpcError):
await self._channel.unary_unary(_SIMPLE_UNARY_UNARY)(_REQUEST)
async def test_unimplemented(self):
call = self._channel.unary_unary(_UNIMPLEMENTED_METHOD)

@ -174,13 +174,6 @@ class SimpleStubsTest(unittest.TestCase):
channel_credentials=grpc.local_channel_credentials())
self.assertEqual(_REQUEST, response)
def test_channel_credentials_default(self):
with _server(grpc.local_server_credentials()) as port:
target = f'localhost:{port}'
response = grpc.experimental.unary_unary(_REQUEST, target,
_UNARY_UNARY)
self.assertEqual(_REQUEST, response)
def test_channels_cached(self):
with _server(grpc.local_server_credentials()) as port:
target = f'localhost:{port}'

@ -242,6 +242,14 @@
set(_gRPC_PLATFORM_WINDOWS ON)
endif()
# Use C99 standard
set(CMAKE_C_STANDARD 99)
# Add c++11 flags
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
## Some libraries are shared even with BUILD_SHARED_LIBRARIES=OFF
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
set(CMAKE_MODULE_PATH "<%text>${CMAKE_CURRENT_SOURCE_DIR}</%text>/cmake/modules")
@ -292,11 +300,6 @@
include(cmake/upb.cmake)
include(cmake/zlib.cmake)
if(NOT MSVC)
set(CMAKE_C_FLAGS "<%text>${CMAKE_C_FLAGS}</%text> -std=c99")
set(CMAKE_CXX_FLAGS "<%text>${CMAKE_CXX_FLAGS}</%text> -std=c++11")
endif()
if(_gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_IOS)
set(_gRPC_ALLTARGETS_LIBRARIES <%text>${CMAKE_DL_LIBS}</%text> m pthread)
elseif(_gRPC_PLATFORM_ANDROID)

@ -164,7 +164,7 @@
ss.header_mappings_dir = '.'
ss.dependency "#{s.name}/Interface", version
ss.dependency 'gRPC-Core', version
abseil_version = '0.20200225.0'
abseil_version = '1.20200225.0'
% for abseil_spec in grpcpp_abseil_specs:
ss.dependency '${abseil_spec}', abseil_version
% endfor

@ -193,7 +193,7 @@
ss.libraries = 'z'
ss.dependency "#{s.name}/Interface", version
ss.dependency 'BoringSSL-GRPC', '0.0.7'
abseil_version = '0.20200225.0'
abseil_version = '1.20200225.0'
% for abseil_spec in grpc_abseil_specs:
ss.dependency '${abseil_spec}', abseil_version
% endfor

@ -351,7 +351,6 @@ class ClientStats {
std::map<grpc::string, uint64_t> dropped_requests_;
};
// TODO(roth): Change this service to a real fake.
class AdsServiceImpl : public AggregatedDiscoveryService::Service,
public std::enable_shared_from_this<AdsServiceImpl> {
public:
@ -400,7 +399,7 @@ class AdsServiceImpl : public AggregatedDiscoveryService::Service,
std::pair<std::string /* type url */, std::string /* resource name */>>;
// A struct representing a client's subscription to a particular resource.
struct SubscriberState {
struct SubscriptionState {
// Version that the client currently knows about.
int current_version = 0;
// The queue upon which to place updates when the resource is updated.
@ -408,23 +407,25 @@ class AdsServiceImpl : public AggregatedDiscoveryService::Service,
};
// A struct representing the a client's subscription to all the resources.
using SubscriptionNameMap =
std::map<std::string /* resource_name */, SubscriptionState>;
using SubscriptionMap =
std::map<std::string /* type_url */,
std::map<std::string /* resource_name */, SubscriberState>>;
std::map<std::string /* type_url */, SubscriptionNameMap>;
// A struct representing the current state for a resource:
// - the version of the resource that is set by the SetResource() methods.
// - a list of subscribers interested in this resource.
// - a list of subscriptions interested in this resource.
struct ResourceState {
int version = 0;
absl::optional<google::protobuf::Any> resource;
std::set<SubscriberState*> subscribers;
std::set<SubscriptionState*> subscriptions;
};
// A struct representing the current state for all resources:
// LDS, CDS, EDS, and RDS for the class as a whole.
using ResourcesMap =
std::map<std::string, std::map<std::string, ResourceState>>;
using ResourceNameMap =
std::map<std::string /* resource_name */, ResourceState>;
using ResourceMap = std::map<std::string /* type_url */, ResourceNameMap>;
AdsServiceImpl(bool enable_load_reporting) {
// Construct RDS response data.
@ -475,101 +476,62 @@ class AdsServiceImpl : public AggregatedDiscoveryService::Service,
}
// Checks whether the client needs to receive a newer version of
// the resource.
bool ClientNeedsResourceUpdate(const string& resource_type,
const string& name,
SubscriptionMap* subscription_map) {
auto subscriber_it = (*subscription_map)[resource_type].find(name);
if (subscriber_it == (*subscription_map)[resource_type].end()) {
gpr_log(GPR_INFO,
"ADS[%p]: Skipping an unsubscribed update for resource %s and "
"name %s",
this, resource_type.c_str(), name.c_str());
return false;
}
const auto& resource_state = resources_map_[resource_type][name];
if (subscriber_it->second.current_version < resource_state.version) {
subscriber_it->second.current_version = resource_state.version;
gpr_log(GPR_INFO,
"ADS[%p]: Need to process new %s update %s, bring current to %d",
this, resource_type.c_str(), name.c_str(),
subscriber_it->second.current_version);
// the resource. If so, updates subscription_state->current_version and
// returns true.
bool ClientNeedsResourceUpdate(const ResourceState& resource_state,
SubscriptionState* subscription_state) {
if (subscription_state->current_version < resource_state.version) {
subscription_state->current_version = resource_state.version;
return true;
} else {
gpr_log(GPR_INFO,
"ADS[%p]: Skipping an old %s update %s, current is at %d", this,
resource_type.c_str(), name.c_str(),
subscriber_it->second.current_version);
return false;
}
return false;
}
// Resource subscription:
// 1. inserting an entry into the subscription map indexed by resource
// type/name pair.
// 2. inserting or updating an entry into the resources map indexed
// by resource type/name pair about this subscription.
void ResourceSubscribe(const std::string& resource_type,
const std::string& name, UpdateQueue* update_queue,
SubscriptionMap* subscription_map) {
SubscriberState& subscriber_state =
(*subscription_map)[resource_type][name];
subscriber_state.update_queue = update_queue;
ResourceState& resource_state = resources_map_[resource_type][name];
resource_state.subscribers.emplace(&subscriber_state);
gpr_log(
GPR_INFO,
"ADS[%p]: subscribe to resource type %s name %s version %d state %p",
this, resource_type.c_str(), name.c_str(), resource_state.version,
&subscriber_state);
}
// Resource unsubscription:
// 1. update the entry in the resources map indexed
// by resource type/name pair to remove this subscription
// 2. remove this entry from the subscription map.
// 3. remove this resource type from the subscription map if there are no more
// resources subscribed for the resource type.
void ResourceUnsubscribe(const std::string& resource_type,
const std::string& name,
SubscriptionMap* subscription_map) {
auto subscription_by_type_it = subscription_map->find(resource_type);
if (subscription_by_type_it == subscription_map->end()) {
gpr_log(GPR_INFO, "ADS[%p]: resource type %s not subscribed", this,
resource_type.c_str());
return;
}
auto& subscription_by_type_map = subscription_by_type_it->second;
auto subscription_it = subscription_by_type_map.find(name);
if (subscription_it == subscription_by_type_map.end()) {
gpr_log(GPR_INFO, "ADS[%p]: resource name %s of type %s not subscribed",
this, name.c_str(), resource_type.c_str());
return;
}
gpr_log(GPR_INFO,
"ADS[%p]: Unsubscribe to resource type %s name %s state %p", this,
resource_type.c_str(), name.c_str(), &subscription_it->second);
auto resource_by_type_it = resources_map_.find(resource_type);
GPR_ASSERT(resource_by_type_it != resources_map_.end());
auto& resource_by_type_map = resource_by_type_it->second;
auto resource_it = resource_by_type_map.find(name);
GPR_ASSERT(resource_it != resource_by_type_map.end());
resource_it->second.subscribers.erase(&subscription_it->second);
if (resource_it->second.subscribers.empty() &&
!resource_it->second.resource.has_value()) {
gpr_log(GPR_INFO,
"ADS[%p]: Erasing resource type %s name %s from resource map "
"since there are no more subscribers for this unset resource",
this, resource_type.c_str(), name.c_str());
resource_by_type_map.erase(resource_it);
}
subscription_by_type_map.erase(subscription_it);
if (subscription_by_type_map.empty()) {
gpr_log(GPR_INFO,
"ADS[%p]: Erasing resource type %s from subscription_map", this,
resource_type.c_str());
subscription_map->erase(subscription_by_type_it);
// Subscribes to a resource if not already subscribed:
// 1. Sets the update_queue field in subscription_state.
// 2. Adds subscription_state to resource_state->subscriptions.
void MaybeSubscribe(const std::string& resource_type,
const std::string& resource_name,
SubscriptionState* subscription_state,
ResourceState* resource_state,
UpdateQueue* update_queue) {
// The update_queue will be null if we were not previously subscribed.
if (subscription_state->update_queue != nullptr) return;
subscription_state->update_queue = update_queue;
resource_state->subscriptions.emplace(subscription_state);
gpr_log(GPR_INFO, "ADS[%p]: subscribe to resource type %s name %s state %p",
this, resource_type.c_str(), resource_name.c_str(),
&subscription_state);
}
// Removes subscriptions for resources no longer present in the
// current request.
void ProcessUnsubscriptions(
const std::string& resource_type,
const std::set<std::string>& resources_in_current_request,
SubscriptionNameMap* subscription_name_map,
ResourceNameMap* resource_name_map) {
for (auto it = subscription_name_map->begin();
it != subscription_name_map->end();) {
const std::string& resource_name = it->first;
SubscriptionState& subscription_state = it->second;
if (resources_in_current_request.find(resource_name) !=
resources_in_current_request.end()) {
++it;
continue;
}
gpr_log(GPR_INFO, "ADS[%p]: Unsubscribe to type=%s name=%s state=%p",
this, resource_type.c_str(), resource_name.c_str(),
&subscription_state);
auto resource_it = resource_name_map->find(resource_name);
GPR_ASSERT(resource_it != resource_name_map->end());
auto& resource_state = resource_it->second;
resource_state.subscriptions.erase(&subscription_state);
if (resource_state.subscriptions.empty() &&
!resource_state.resource.has_value()) {
resource_name_map->erase(resource_it);
}
it = subscription_name_map->erase(it);
}
}
@ -577,7 +539,7 @@ class AdsServiceImpl : public AggregatedDiscoveryService::Service,
// for all resources and by adding all subscribed resources for LDS and CDS.
void CompleteBuildingDiscoveryResponse(
const std::string& resource_type, const int version,
const SubscriptionMap& subscription_map,
const SubscriptionNameMap& subscription_name_map,
const std::set<std::string>& resources_added_to_response,
DiscoveryResponse* response) {
resource_type_response_state_[resource_type] = SENT;
@ -587,18 +549,15 @@ class AdsServiceImpl : public AggregatedDiscoveryService::Service,
if (resource_type == kLdsTypeUrl || resource_type == kCdsTypeUrl) {
// For LDS and CDS we must send back all subscribed resources
// (even the unchanged ones)
auto subscription_map_by_type_it = subscription_map.find(resource_type);
GPR_ASSERT(subscription_map_by_type_it != subscription_map.end());
for (const auto& subscription : subscription_map_by_type_it->second) {
if (resources_added_to_response.find(subscription.first) ==
for (const auto& p : subscription_name_map) {
const std::string& resource_name = p.first;
if (resources_added_to_response.find(resource_name) ==
resources_added_to_response.end()) {
absl::optional<google::protobuf::Any>& resource =
resources_map_[resource_type][subscription.first].resource;
if (resource.has_value()) {
response->add_resources()->CopyFrom(resource.value());
} else {
gpr_log(GPR_INFO, "ADS[%p]: Unknown resource type %s and name %s",
this, resource_type.c_str(), subscription.first.c_str());
const ResourceState& resource_state =
resource_map_[resource_type][resource_name];
if (resource_state.resource.has_value()) {
response->add_resources()->CopyFrom(
resource_state.resource.value());
}
}
}
@ -622,7 +581,6 @@ class AdsServiceImpl : public AggregatedDiscoveryService::Service,
// Resources that the client will be subscribed to keyed by resource type
// url.
SubscriptionMap subscription_map;
std::map<std::string, SubscriberState> subscriber_map;
// Current Version map keyed by resource type url.
std::map<std::string, int> resource_type_version;
// Creating blocking thread to read from stream.
@ -636,7 +594,7 @@ class AdsServiceImpl : public AggregatedDiscoveryService::Service,
// Main loop to look for requests and updates.
while (true) {
// Look for new requests and and decide what to handle.
DiscoveryResponse response;
absl::optional<DiscoveryResponse> response;
// Boolean to keep track if the loop received any work to do: a request
// or an update; regardless whether a response was actually sent out.
bool did_work = false;
@ -647,7 +605,8 @@ class AdsServiceImpl : public AggregatedDiscoveryService::Service,
DiscoveryRequest request = std::move(requests.front());
requests.pop_front();
did_work = true;
gpr_log(GPR_INFO, "ADS[%p]: Handling request %s with content %s",
gpr_log(GPR_INFO,
"ADS[%p]: Received request for type %s with content %s",
this, request.type_url().c_str(),
request.DebugString().c_str());
// Identify ACK and NACK by looking for version information and
@ -667,94 +626,97 @@ class AdsServiceImpl : public AggregatedDiscoveryService::Service,
// 3. unsubscribe if necessary
if (resource_types_to_ignore_.find(request.type_url()) ==
resource_types_to_ignore_.end()) {
auto& subscription_name_map =
subscription_map[request.type_url()];
auto& resource_name_map = resource_map_[request.type_url()];
std::set<std::string> resources_in_current_request;
std::set<std::string> resources_added_to_response;
for (const std::string& resource_name :
request.resource_names()) {
resources_in_current_request.emplace(resource_name);
auto subscriber_it =
subscription_map[request.type_url()].find(resource_name);
if (subscriber_it ==
subscription_map[request.type_url()].end()) {
ResourceSubscribe(request.type_url(), resource_name,
&update_queue, &subscription_map);
}
if (ClientNeedsResourceUpdate(request.type_url(), resource_name,
&subscription_map)) {
auto& subscription_state = subscription_name_map[resource_name];
auto& resource_state = resource_name_map[resource_name];
MaybeSubscribe(request.type_url(), resource_name,
&subscription_state, &resource_state,
&update_queue);
if (ClientNeedsResourceUpdate(resource_state,
&subscription_state)) {
gpr_log(
GPR_INFO,
"ADS[%p]: Sending update for type=%s name=%s version=%d",
this, request.type_url().c_str(), resource_name.c_str(),
resource_state.version);
resources_added_to_response.emplace(resource_name);
gpr_log(GPR_INFO,
"ADS[%p]: Handling resource type %s and name %s",
this, request.type_url().c_str(),
resource_name.c_str());
auto resource =
resources_map_[request.type_url()][resource_name];
GPR_ASSERT(resource.resource.has_value());
response.add_resources()->CopyFrom(resource.resource.value());
}
}
// Remove subscriptions no longer requested: build a list of
// unsubscriber names first while iterating the subscription_map
// and then erase from the subscription_map in
// ResourceUnsubscribe.
std::set<std::string> unsubscriber_list;
for (const auto& subscription :
subscription_map[request.type_url()]) {
if (resources_in_current_request.find(subscription.first) ==
resources_in_current_request.end()) {
unsubscriber_list.emplace(subscription.first);
if (!response.has_value()) response.emplace();
if (resource_state.resource.has_value()) {
response->add_resources()->CopyFrom(
resource_state.resource.value());
}
}
}
for (const auto& name : unsubscriber_list) {
ResourceUnsubscribe(request.type_url(), name,
&subscription_map);
}
if (!response.resources().empty()) {
// Process unsubscriptions for any resource no longer
// present in the request's resource list.
ProcessUnsubscriptions(
request.type_url(), resources_in_current_request,
&subscription_name_map, &resource_name_map);
// Send response if needed.
if (!resources_added_to_response.empty()) {
CompleteBuildingDiscoveryResponse(
request.type_url(),
++resource_type_version[request.type_url()],
subscription_map, resources_added_to_response, &response);
subscription_name_map, resources_added_to_response,
&response.value());
}
}
}
}
if (!response.resources().empty()) {
gpr_log(GPR_INFO, "ADS[%p]: sending request response '%s'", this,
response.DebugString().c_str());
stream->Write(response);
if (response.has_value()) {
gpr_log(GPR_INFO, "ADS[%p]: Sending response: %s", this,
response->DebugString().c_str());
stream->Write(response.value());
}
response.Clear();
response.reset();
// Look for updates and decide what to handle.
{
grpc_core::MutexLock lock(&ads_mu_);
if (!update_queue.empty()) {
std::pair<std::string, std::string> update =
std::move(update_queue.front());
const std::string resource_type =
std::move(update_queue.front().first);
const std::string resource_name =
std::move(update_queue.front().second);
update_queue.pop_front();
did_work = true;
gpr_log(GPR_INFO, "ADS[%p]: Handling update type %s name %s", this,
update.first.c_str(), update.second.c_str());
auto subscriber_it =
subscription_map[update.first].find(update.second);
if (subscriber_it != subscription_map[update.first].end()) {
if (ClientNeedsResourceUpdate(update.first, update.second,
&subscription_map)) {
gpr_log(GPR_INFO,
"ADS[%p]: Updating resource type %s and name %s", this,
update.first.c_str(), update.second.c_str());
auto resource = resources_map_[update.first][update.second];
GPR_ASSERT(resource.resource.has_value());
response.add_resources()->CopyFrom(resource.resource.value());
gpr_log(GPR_INFO, "ADS[%p]: Received update for type=%s name=%s",
this, resource_type.c_str(), resource_name.c_str());
auto& subscription_name_map = subscription_map[resource_type];
auto& resource_name_map = resource_map_[resource_type];
auto it = subscription_name_map.find(resource_name);
if (it != subscription_name_map.end()) {
SubscriptionState& subscription_state = it->second;
ResourceState& resource_state = resource_name_map[resource_name];
if (ClientNeedsResourceUpdate(resource_state,
&subscription_state)) {
gpr_log(
GPR_INFO,
"ADS[%p]: Sending update for type=%s name=%s version=%d",
this, resource_type.c_str(), resource_name.c_str(),
resource_state.version);
response.emplace();
if (resource_state.resource.has_value()) {
response->add_resources()->CopyFrom(
resource_state.resource.value());
}
CompleteBuildingDiscoveryResponse(
update.first, ++resource_type_version[update.first],
subscription_map, {update.second}, &response);
resource_type, ++resource_type_version[resource_type],
subscription_name_map, {resource_name}, &response.value());
}
}
}
}
if (!response.resources().empty()) {
gpr_log(GPR_INFO, "ADS[%p]: sending update response '%s'", this,
response.DebugString().c_str());
stream->Write(response);
if (response.has_value()) {
gpr_log(GPR_INFO, "ADS[%p]: Sending update response: %s", this,
response->DebugString().c_str());
stream->Write(response.value());
}
// If we didn't find anything to do, delay before the next loop
// iteration; otherwise, check whether we should exit and then
@ -805,16 +767,28 @@ class AdsServiceImpl : public AggregatedDiscoveryService::Service,
resource_types_to_ignore_.emplace(type_url);
}
void UnsetResource(const std::string& type_url, const std::string& name) {
grpc_core::MutexLock lock(&ads_mu_);
ResourceState& state = resource_map_[type_url][name];
++state.version;
state.resource.reset();
gpr_log(GPR_INFO, "ADS[%p]: Unsetting %s resource %s to version %u", this,
type_url.c_str(), name.c_str(), state.version);
for (SubscriptionState* subscription : state.subscriptions) {
subscription->update_queue->emplace_back(type_url, name);
}
}
void SetResource(google::protobuf::Any resource, const std::string& type_url,
const std::string& name) {
grpc_core::MutexLock lock(&ads_mu_);
ResourceState& state = resources_map_[type_url][name];
ResourceState& state = resource_map_[type_url][name];
++state.version;
state.resource = std::move(resource);
gpr_log(GPR_INFO, "ADS[%p]: Updating %s resource %s to version %u", this,
type_url.c_str(), name.c_str(), state.version);
for (SubscriberState* subscriber : state.subscribers) {
subscriber->update_queue->emplace_back(type_url, name);
for (SubscriptionState* subscription : state.subscriptions) {
subscription->update_queue->emplace_back(type_url, name);
}
}
@ -873,15 +847,17 @@ class AdsServiceImpl : public AggregatedDiscoveryService::Service,
{
grpc_core::MutexLock lock(&ads_mu_);
NotifyDoneWithAdsCallLocked();
resources_map_.clear();
resource_map_.clear();
resource_type_response_state_.clear();
}
gpr_log(GPR_INFO, "ADS[%p]: shut down", this);
}
static ClusterLoadAssignment BuildEdsResource(const EdsResourceArgs& args) {
static ClusterLoadAssignment BuildEdsResource(
const EdsResourceArgs& args,
const char* cluster_name = kDefaultResourceName) {
ClusterLoadAssignment assignment;
assignment.set_cluster_name(kDefaultResourceName);
assignment.set_cluster_name(cluster_name);
for (const auto& locality : args.locality_list) {
auto* endpoints = assignment.add_endpoints();
endpoints->mutable_load_balancing_weight()->set_value(locality.lb_weight);
@ -946,8 +922,8 @@ class AdsServiceImpl : public AggregatedDiscoveryService::Service,
// Note that an entry will exist whenever either of the following is true:
// - The resource exists (i.e., has been created by SetResource() and has not
// yet been destroyed by UnsetResource()).
// - There is at least one subscriber for the resource.
ResourcesMap resources_map_;
// - There is at least one subscription for the resource.
ResourceMap resource_map_;
};
class LrsServiceImpl : public LrsService,
@ -1233,11 +1209,16 @@ class XdsEnd2endTest : public ::testing::TestWithParam<TestType> {
return std::make_tuple(num_ok, num_failure, num_drops);
}
void WaitForBackend(size_t backend_idx, bool reset_counters = true) {
void WaitForBackend(size_t backend_idx, bool reset_counters = true,
bool require_success = false) {
gpr_log(GPR_INFO, "========= WAITING FOR BACKEND %lu ==========",
static_cast<unsigned long>(backend_idx));
do {
(void)SendRpc();
Status status = SendRpc();
if (require_success) {
EXPECT_TRUE(status.ok()) << "code=" << status.error_code()
<< " message=" << status.error_message();
}
} while (backends_[backend_idx]->backend_service()->request_count() == 0);
if (reset_counters) ResetBackendCounters();
gpr_log(GPR_INFO, "========= BACKEND %lu READY ==========",
@ -1677,6 +1658,68 @@ TEST_P(BasicTest, BackendsRestart) {
true /* wait_for_ready */);
}
using XdsResolverOnlyTest = BasicTest;
// Tests switching over from one cluster to another.
TEST_P(XdsResolverOnlyTest, ChangeClusters) {
const char* kNewClusterName = "new_cluster_name";
SetNextResolution({});
SetNextResolutionForLbChannelAllBalancers();
AdsServiceImpl::EdsResourceArgs args({
{"locality0", GetBackendPorts(0, 2)},
});
balancers_[0]->ads_service()->SetEdsResource(
AdsServiceImpl::BuildEdsResource(args), kDefaultResourceName);
// We need to wait for all backends to come online.
WaitForAllBackends(0, 2);
// Populate new EDS resource.
AdsServiceImpl::EdsResourceArgs args2({
{"locality0", GetBackendPorts(2, 4)},
});
balancers_[0]->ads_service()->SetEdsResource(
AdsServiceImpl::BuildEdsResource(args2, kNewClusterName),
kNewClusterName);
// Populate new CDS resource.
Cluster new_cluster = balancers_[0]->ads_service()->default_cluster();
new_cluster.set_name(kNewClusterName);
balancers_[0]->ads_service()->SetCdsResource(new_cluster, kNewClusterName);
// 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::tuple<int, int, int> counts = WaitForAllBackends(2, 4);
// Make sure no RPCs failed in the transition.
EXPECT_EQ(0, std::get<1>(counts));
}
// Tests that things keep workng if the cluster resource disappears.
TEST_P(XdsResolverOnlyTest, ClusterRemoved) {
SetNextResolution({});
SetNextResolutionForLbChannelAllBalancers();
AdsServiceImpl::EdsResourceArgs args({
{"locality0", GetBackendPorts()},
});
balancers_[0]->ads_service()->SetEdsResource(
AdsServiceImpl::BuildEdsResource(args), kDefaultResourceName);
// We need to wait for all backends to come online.
WaitForAllBackends();
// Unset CDS resource.
balancers_[0]->ads_service()->UnsetResource(kCdsTypeUrl,
kDefaultResourceName);
// Make sure RPCs are still succeeding.
CheckRpcSendOk(100 * num_backends_);
// Make sure we ACK'ed the update.
EXPECT_EQ(balancers_[0]->ads_service()->cds_response_state(),
AdsServiceImpl::ACKED);
}
using SecureNamingTest = BasicTest;
// Tests that secure naming check passes if target name is expected.
@ -2207,14 +2250,6 @@ TEST_P(LocalityMapTest, UpdateMap) {
});
balancers_[0]->ads_service()->SetEdsResource(
AdsServiceImpl::BuildEdsResource(args), kDefaultResourceName);
args = AdsServiceImpl::EdsResourceArgs({
{"locality1", GetBackendPorts(1, 2), 3},
{"locality2", GetBackendPorts(2, 3), 2},
{"locality3", GetBackendPorts(3, 4), 6},
});
std::thread delayed_resource_setter(std::bind(
&BasicTest::SetEdsResourceWithDelay, this, 0,
AdsServiceImpl::BuildEdsResource(args), 5000, kDefaultResourceName));
// Wait for the first 3 backends to be ready.
WaitForAllBackends(0, 3);
gpr_log(GPR_INFO, "========= BEFORE FIRST BATCH ==========");
@ -2237,6 +2272,13 @@ TEST_P(LocalityMapTest, UpdateMap) {
::testing::Ge(locality_weight_rate_0[i] * (1 - kErrorTolerance)),
::testing::Le(locality_weight_rate_0[i] * (1 + kErrorTolerance))));
}
args = AdsServiceImpl::EdsResourceArgs({
{"locality1", GetBackendPorts(1, 2), 3},
{"locality2", GetBackendPorts(2, 3), 2},
{"locality3", GetBackendPorts(3, 4), 6},
});
balancers_[0]->ads_service()->SetEdsResource(
AdsServiceImpl::BuildEdsResource(args), kDefaultResourceName);
// Backend 3 hasn't received any request.
EXPECT_EQ(0U, backends_[3]->backend_service()->request_count());
// Wait until the locality update has been processed, as signaled by backend 3
@ -2263,6 +2305,30 @@ TEST_P(LocalityMapTest, UpdateMap) {
::testing::Ge(locality_weight_rate_1[i] * (1 - kErrorTolerance)),
::testing::Le(locality_weight_rate_1[i] * (1 + kErrorTolerance))));
}
}
// Tests that we don't fail RPCs when replacing all of the localities in
// a given priority.
TEST_P(LocalityMapTest, ReplaceAllLocalitiesInPriority) {
SetNextResolution({});
SetNextResolutionForLbChannelAllBalancers();
AdsServiceImpl::EdsResourceArgs args({
{"locality0", GetBackendPorts(0, 1)},
});
balancers_[0]->ads_service()->SetEdsResource(
AdsServiceImpl::BuildEdsResource(args), kDefaultResourceName);
args = AdsServiceImpl::EdsResourceArgs({
{"locality1", GetBackendPorts(1, 2)},
});
std::thread delayed_resource_setter(std::bind(
&BasicTest::SetEdsResourceWithDelay, this, 0,
AdsServiceImpl::BuildEdsResource(args), 5000, kDefaultResourceName));
// Wait for the first backend to be ready.
WaitForBackend(0);
// Keep sending RPCs until we switch over to backend 1, which tells us
// that we received the update. No RPCs should fail during this
// transition.
WaitForBackend(1, /*reset_counters=*/true, /*require_success=*/true);
delayed_resource_setter.join();
}
@ -3345,6 +3411,12 @@ INSTANTIATE_TEST_SUITE_P(XdsTest, EdsTest,
TestType(true, true)),
&TestTypeName);
// XdsResolverOnlyTest depends on XdsResolver.
INSTANTIATE_TEST_SUITE_P(XdsTest, XdsResolverOnlyTest,
::testing::Values(TestType(true, false),
TestType(true, true)),
&TestTypeName);
INSTANTIATE_TEST_SUITE_P(XdsTest, LocalityMapTest,
::testing::Values(TestType(false, true),
TestType(false, false),

@ -124,8 +124,6 @@ class TestClient {
void AsyncUnaryCall() {
SimpleResponse response;
ClientContext context;
int saved_request_id;
{
std::lock_guard<std::mutex> lk(mu);
@ -134,9 +132,8 @@ class TestClient {
std::chrono::system_clock::time_point deadline =
std::chrono::system_clock::now() +
std::chrono::seconds(FLAGS_rpc_timeout_sec);
context.set_deadline(deadline);
AsyncClientCall* call = new AsyncClientCall;
call->context.set_deadline(deadline);
call->saved_request_id = saved_request_id;
call->response_reader = stub_->PrepareAsyncUnaryCall(
&call->context, SimpleRequest::default_instance(), &cq_);

@ -63,6 +63,7 @@ static void BM_SliceReIntern(benchmark::State& state) {
for (auto _ : state) {
grpc_slice_unref(grpc_core::ManagedMemorySlice(&slice));
}
grpc_slice_unref(slice);
track_counters.Finish(state);
}
BENCHMARK(BM_SliceReIntern);

@ -1 +1 @@
Subproject commit b832dce8489ef7b6231384909fd9b68d5a5ff2b7
Subproject commit df3ea785d8c30a9503321a3d35ee7d35808f190d

@ -89,3 +89,5 @@ build:basicprof --copt=-DGRPC_BASIC_PROFILER
build:basicprof --copt=-DGRPC_TIMERS_RDTSC
build:python_single_threaded_unary_stream --test_env="GRPC_SINGLE_THREADED_UNARY_STREAM=true"
build:python_poller_engine --test_env="GRPC_ASYNCIO_ENGINE=poller"

@ -43,6 +43,7 @@ def _rule_dict_from_xml_node(rule_xml_node):
'args': [],
'generator_function': None,
'size': None,
'flaky': False,
}
for child in rule_xml_node:
# all the metadata we want is stored under "list" tags
@ -54,6 +55,10 @@ def _rule_dict_from_xml_node(rule_xml_node):
string_name = child.attrib['name']
if string_name in ['generator_function', 'size']:
result[string_name] = child.attrib['value']
if child.tag == 'boolean':
bool_name = child.attrib['name']
if bool_name in ['flaky']:
result[bool_name] = child.attrib['value'] == 'true'
return result
@ -478,6 +483,13 @@ def _generate_build_extra_metadata_for_tests(tests, bazel_rules):
# don't run the tests marked as "manual"
test_dict['run'] = False
if bazel_rule['flaky']:
# don't run tests that are marked as "flaky" under bazel
# because that would only add noise for the run_tests.py tests
# and seeing more failures for tests that we already know are flaky
# doesn't really help anything
test_dict['run'] = False
if 'no_uses_polling' in bazel_tags:
test_dict['uses_polling'] = False
@ -753,6 +765,20 @@ _BUILD_EXTRA_METADATA = {
'_TYPE': 'target',
'_RENAME': 'interop_server'
},
'test/cpp/interop:xds_interop_client': {
'language': 'c++',
'build': 'test',
'run': False,
'_TYPE': 'target',
'_RENAME': 'xds_interop_client'
},
'test/cpp/interop:xds_interop_server': {
'language': 'c++',
'build': 'test',
'run': False,
'_TYPE': 'target',
'_RENAME': 'xds_interop_server'
},
'test/cpp/interop:http2_client': {
'language': 'c++',
'build': 'test',

@ -28,6 +28,7 @@ TEST_TARGETS="//src/python/... //examples/python/..."
BAZEL_FLAGS="--spawn_strategy=standalone --genrule_strategy=standalone --test_output=errors"
bazel test ${BAZEL_FLAGS} ${TEST_TARGETS}
bazel test --config=python_single_threaded_unary_stream ${BAZEL_FLAGS} ${TEST_TARGETS}
bazel test --config=python_poller_engine ${BAZEL_FLAGS} ${TEST_TARGETS}
# TODO(https://github.com/grpc/grpc/issues/19854): Move this to a new Kokoro

@ -16,7 +16,7 @@
# Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/linux/grpc_bazel.sh"
timeout_mins: 60
timeout_mins: 90
env_vars {
key: "BAZEL_SCRIPT"
value: "tools/internal_ci/linux/grpc_xds_bazel_test_in_docker.sh"

@ -47,9 +47,10 @@ touch "$TOOLS_DIR"/src/proto/grpc/testing/__init__.py
bazel build test/cpp/interop:xds_interop_client
"$PYTHON" tools/run_tests/run_xds_tests.py \
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-bin/test/cpp/interop/xds_interop_client --server=xds-experimental:///{service_host}:{service_port} --stats_port={stats_port} --qps={qps}'
--client_cmd='bazel-bin/test/cpp/interop/xds_interop_client --server=xds-experimental:///{server_uri} --stats_port={stats_port} --qps={qps}'

@ -153,6 +153,7 @@ LANG_RELEASE_MATRIX = {
('v1.25.0', ReleaseInfo(runtimes=['go1.11'])),
('v1.26.0', ReleaseInfo(runtimes=['go1.11'])),
('v1.27.1', ReleaseInfo(runtimes=['go1.11'])),
('v1.28.0', ReleaseInfo(runtimes=['go1.11'])),
]),
'java':
OrderedDict([

@ -3829,26 +3829,6 @@
],
"uses_polling": true
},
{
"args": [],
"benchmark": true,
"ci_platforms": [
"linux",
"posix"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": false,
"language": "c++",
"name": "bm_fullstack_streaming_pump",
"platforms": [
"linux",
"posix"
],
"uses_polling": true
},
{
"args": [],
"benchmark": true,
@ -3869,26 +3849,6 @@
],
"uses_polling": true
},
{
"args": [],
"benchmark": true,
"ci_platforms": [
"linux",
"posix"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": false,
"language": "c++",
"name": "bm_metadata",
"platforms": [
"linux",
"posix"
],
"uses_polling": false
},
{
"args": [],
"benchmark": true,
@ -4713,28 +4673,6 @@
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": true,
"language": "c++",
"name": "grpclb_end2end_test",
"platforms": [
"linux",
"mac",
"posix"
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,
@ -5639,30 +5577,6 @@
],
"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": "settings_timeout_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,
@ -6207,28 +6121,6 @@
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": true,
"language": "c++",
"name": "xds_end2end_test",
"platforms": [
"linux",
"mac",
"posix"
],
"uses_polling": true
},
{
"args": [],
"boringssl": true,

File diff suppressed because it is too large Load Diff

@ -26,7 +26,7 @@ want_submodules=$(mktemp /tmp/submXXXXXX)
git submodule | awk '{ print $1 }' | sort > "$submodules"
cat << EOF | awk '{ print $1 }' | sort > "$want_submodules"
b832dce8489ef7b6231384909fd9b68d5a5ff2b7 third_party/abseil-cpp (heads/master)
df3ea785d8c30a9503321a3d35ee7d35808f190d third_party/abseil-cpp (heads/master)
090faecb454fbd6e6e17a75ef8146acb037118d4 third_party/benchmark (v1.5.0)
73594cde8c9a52a102c4341c244c833aa61b9c06 third_party/bloaty (remotes/origin/wide-14-g73594cd)
1c2769383f027befac5b75b6cedd25daf3bf4dcf third_party/boringssl-with-bazel (remotes/origin/master-with-bazel)

Loading…
Cancel
Save