From b853ccc6db7262e304f90256034dc05ec71d486b Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Thu, 27 Oct 2022 11:51:05 -0700 Subject: [PATCH] service config: use new JSON API (#30467) * Declarative JSON parser * Automated change: Fix sanity tests * fix * shrinking stuff a little * static vtables * separate fns * simpler? * make maps work * windows fixes * Automated change: Fix sanity tests * simplify code * Automated change: Fix sanity tests * vtable-test * dont always create vec/map impls for every type * comments * make error consistent * move method private * progress * durations! * Automated change: Fix sanity tests * fix * fix * fix * Automated change: Fix sanity tests * post-load * Automated change: Fix sanity tests * document JsonPostLoad() and add static_assert * don't copy field names, to avoid length limitations * use absl::Status * accept either string or number for numeric values * add test for direct data member of another struct type * remove unused method * add support for retaining part of the JSON wirthout processing * update test for changes in Json::Parse() API * add absl::optional support * Automated change: Fix sanity tests * fix tests, improve error messages, and add overload to parse to existing object * remove overload of LoadFromJson() * change special case for Json to instead use Json::Object * rename resolver_result_parsing to client_channel_service_config * split up service_config_test into a separate test for each component * convert client channel service config parser to new API * fix build * converted retry global params * convert retry method-level parsing, but still need to find a way to control parsing via a channel arg * improve error structure, add missing types, and improve tests * clang-format * Automated change: Fix sanity tests * fix build * add LoadJsonObjectField(), add LoadFromJson() overload that takes an ErrorList parameter, and add tests for parsing bare top-level types * fix msan * Automated change: Fix sanity tests * fix error message * Automated change: Fix sanity tests * fix test * add ability to disable fields * plumb in channel args to disable parsing * Automated change: Fix sanity tests * use const char* instead of absl::string_view for enable_key * fix resolver_component_test * Automated change: Fix sanity tests * work around mac build problem * Automated change: Fix sanity tests * work around gcc6 problem * Automated change: Fix sanity tests * fix build * fix build * don't use alternative builder in tests * convert message size service config parser * convert fault injection service config parser * rename files * add specialization for unique_ptr * avoid moves in client channel service config parser * avoid moves in retry service config parser, and do some cleanup * avoid moves in message_size service config parser * avoid moves for fault injection service config parser, and use internal channel arg * convert rbac service config parser * clang-format * WIP * convert top-level service config parser * clang-format * fix build * fix rbac service config parser test * fix signed-ness problem and reversed-conditional bug * fix unused param * fix json_string method * fix max message length defaults * add copy ctors to appease windows compiler * fix RLS LB config parser test * fix name resolution test * fix build * work around gmock portability bug * fix sanity * add missing build dep * make RBAC principal and permissions movable, not copyable * Revert "make RBAC principal and permissions movable, not copyable" This reverts commit 53315bccc9531346a0eb59cec30bd72f57298146. * attempt to simplify HeaderMatcher and StringMatcher parsing * more bloat reduction in RBAC service config parser * fix sanity * add missing build dep * attempt work-around for MSVC bug * Revert "attempt work-around for MSVC bug" This reverts commit e54c89e1e4f678ed2068a9ba97d9adea685a52ab. * attempt work-around for Windows build problem * try another work-around * fix sanity * appease clang-tidy * generate_projects * attempt to fix Windows build * more windows fixes * more windows fix * yet more windows fixes * try without noexcept * remove unnecessary boilerplate * code review changes * fix breakage Co-authored-by: Craig Tiller Co-authored-by: ctiller Co-authored-by: Craig Tiller Co-authored-by: markdroth --- BUILD | 14 +- CMakeLists.txt | 119 +- Makefile | 10 +- build_autogenerated.yaml | 49 +- config.m4 | 4 +- config.w32 | 4 +- gRPC-C++.podspec | 10 +- gRPC-Core.podspec | 14 +- grpc.gemspec | 9 +- grpc.gyp | 9 +- package.xml | 9 +- src/core/BUILD | 25 +- .../filters/client_channel/client_channel.cc | 2 +- .../client_channel/client_channel_plugin.cc | 2 +- .../client_channel_service_config.cc | 153 ++ ...sing.h => client_channel_service_config.h} | 49 +- .../client_channel/lb_policy/rls/rls.cc | 9 +- .../client_channel/resolver_result_parsing.cc | 186 -- .../client_channel/retry_service_config.cc | 423 ++--- .../client_channel/retry_service_config.h | 43 +- .../filters/client_channel/retry_throttle.cc | 16 +- .../filters/client_channel/retry_throttle.h | 15 +- .../fault_injection/fault_injection_filter.cc | 2 +- .../fault_injection_service_config_parser.cc | 118 ++ ...> fault_injection_service_config_parser.h} | 28 +- .../fault_injection/service_config_parser.cc | 185 -- .../message_decompress_filter.cc | 22 +- .../message_size/message_size_filter.cc | 168 +- .../message_size/message_size_filter.h | 37 +- .../rbac/rbac_service_config_parser.cc | 1248 +++++++------ .../filters/rbac/rbac_service_config_parser.h | 7 +- src/core/ext/xds/xds_http_fault_filter.cc | 2 +- src/core/ext/xds/xds_http_rbac_filter.cc | 6 +- src/core/lib/json/json_object_loader.h | 21 + src/core/lib/service_config/service_config.h | 2 +- .../lib/service_config/service_config_impl.cc | 266 ++- .../lib/service_config/service_config_impl.h | 31 +- .../service_config/service_config_parser.cc | 45 +- .../service_config/service_config_parser.h | 24 +- src/python/grpcio/grpc_core_dependencies.py | 4 +- test/core/client_channel/BUILD | 18 +- .../client_channel_service_config_test.cc | 315 ++++ .../retry_service_config_test.cc | 712 ++++++++ .../rls_lb_config_parser_test.cc | 38 +- .../client_channel/service_config_test.cc | 1557 ----------------- .../rbac/rbac_service_config_parser_test.cc | 240 ++- test/core/json/json_object_loader_test.cc | 124 +- test/core/message_size/BUILD | 33 + .../message_size_service_config_test.cc | 125 ++ test/core/service_config/BUILD | 33 + .../service_config/service_config_test.cc | 468 +++++ test/core/xds/xds_http_filters_test.cc | 4 +- .../naming/resolver_component_tests_runner.py | 4 +- .../naming/resolver_test_record_groups.yaml | 4 +- tools/doxygen/Doxyfile.c++.internal | 9 +- tools/doxygen/Doxyfile.core.internal | 9 +- tools/run_tests/generated/tests.json | 72 + 57 files changed, 3792 insertions(+), 3363 deletions(-) create mode 100644 src/core/ext/filters/client_channel/client_channel_service_config.cc rename src/core/ext/filters/client_channel/{resolver_result_parsing.h => client_channel_service_config.h} (64%) delete mode 100644 src/core/ext/filters/client_channel/resolver_result_parsing.cc create mode 100644 src/core/ext/filters/fault_injection/fault_injection_service_config_parser.cc rename src/core/ext/filters/fault_injection/{service_config_parser.h => fault_injection_service_config_parser.h} (79%) delete mode 100644 src/core/ext/filters/fault_injection/service_config_parser.cc create mode 100644 test/core/client_channel/client_channel_service_config_test.cc create mode 100644 test/core/client_channel/retry_service_config_test.cc delete mode 100644 test/core/client_channel/service_config_test.cc create mode 100644 test/core/message_size/BUILD create mode 100644 test/core/message_size/message_size_service_config_test.cc create mode 100644 test/core/service_config/BUILD create mode 100644 test/core/service_config/service_config_test.cc diff --git a/BUILD b/BUILD index be97a52ae94..b052be2350a 100644 --- a/BUILD +++ b/BUILD @@ -2503,9 +2503,9 @@ grpc_cc_library( "//src/core:lib/service_config/service_config_impl.h", ], external_deps = [ - "absl/status", "absl/status:statusor", "absl/strings", + "absl/types:optional", ], language = "c++", visibility = ["@grpc:client_channel"], @@ -2516,9 +2516,12 @@ grpc_cc_library( "//src/core:channel_args", "//src/core:grpc_service_config", "//src/core:json", + "//src/core:json_args", + "//src/core:json_object_loader", "//src/core:service_config_parser", "//src/core:slice", "//src/core:slice_refcount", + "//src/core:validation_errors", ], ) @@ -2589,6 +2592,7 @@ grpc_cc_library( "//src/core:ext/filters/client_channel/client_channel_channelz.cc", "//src/core:ext/filters/client_channel/client_channel_factory.cc", "//src/core:ext/filters/client_channel/client_channel_plugin.cc", + "//src/core:ext/filters/client_channel/client_channel_service_config.cc", "//src/core:ext/filters/client_channel/config_selector.cc", "//src/core:ext/filters/client_channel/dynamic_filters.cc", "//src/core:ext/filters/client_channel/global_subchannel_pool.cc", @@ -2597,7 +2601,6 @@ grpc_cc_library( "//src/core:ext/filters/client_channel/lb_policy/child_policy_handler.cc", "//src/core:ext/filters/client_channel/lb_policy/oob_backend_metric.cc", "//src/core:ext/filters/client_channel/local_subchannel_pool.cc", - "//src/core:ext/filters/client_channel/resolver_result_parsing.cc", "//src/core:ext/filters/client_channel/retry_filter.cc", "//src/core:ext/filters/client_channel/retry_service_config.cc", "//src/core:ext/filters/client_channel/retry_throttle.cc", @@ -2612,6 +2615,7 @@ grpc_cc_library( "//src/core:ext/filters/client_channel/client_channel.h", "//src/core:ext/filters/client_channel/client_channel_channelz.h", "//src/core:ext/filters/client_channel/client_channel_factory.h", + "//src/core:ext/filters/client_channel/client_channel_service_config.h", "//src/core:ext/filters/client_channel/config_selector.h", "//src/core:ext/filters/client_channel/connector.h", "//src/core:ext/filters/client_channel/dynamic_filters.h", @@ -2621,7 +2625,6 @@ grpc_cc_library( "//src/core:ext/filters/client_channel/lb_policy/child_policy_handler.h", "//src/core:ext/filters/client_channel/lb_policy/oob_backend_metric.h", "//src/core:ext/filters/client_channel/local_subchannel_pool.h", - "//src/core:ext/filters/client_channel/resolver_result_parsing.h", "//src/core:ext/filters/client_channel/retry_filter.h", "//src/core:ext/filters/client_channel/retry_service_config.h", "//src/core:ext/filters/client_channel/retry_throttle.h", @@ -2680,7 +2683,9 @@ grpc_cc_library( "//src/core:init_internally", "//src/core:iomgr_fwd", "//src/core:json", - "//src/core:json_util", + "//src/core:json_args", + "//src/core:json_channel_args", + "//src/core:json_object_loader", "//src/core:lb_policy", "//src/core:lb_policy_registry", "//src/core:memory_quota", @@ -2701,6 +2706,7 @@ grpc_cc_library( "//src/core:transport_fwd", "//src/core:unique_type_name", "//src/core:useful", + "//src/core:validation_errors", ], ) diff --git a/CMakeLists.txt b/CMakeLists.txt index 65c67339e57..abf6aa74a24 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -870,6 +870,7 @@ if(gRPC_BUILD_TESTS) add_dependencies(buildtests_cxx client_auth_filter_test) add_dependencies(buildtests_cxx client_authority_filter_test) add_dependencies(buildtests_cxx client_callback_end2end_test) + add_dependencies(buildtests_cxx client_channel_service_config_test) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_cxx client_channel_stress_test) endif() @@ -1033,6 +1034,7 @@ if(gRPC_BUILD_TESTS) add_dependencies(buildtests_cxx memory_quota_test) add_dependencies(buildtests_cxx message_allocator_end2end_test) add_dependencies(buildtests_cxx message_compress_test) + add_dependencies(buildtests_cxx message_size_service_config_test) add_dependencies(buildtests_cxx metadata_map_test) add_dependencies(buildtests_cxx miscompile_with_no_unique_address_test) add_dependencies(buildtests_cxx mock_stream_test) @@ -1098,6 +1100,7 @@ if(gRPC_BUILD_TESTS) endif() add_dependencies(buildtests_cxx resolve_address_using_native_resolver_test) add_dependencies(buildtests_cxx resource_quota_test) + add_dependencies(buildtests_cxx retry_service_config_test) add_dependencies(buildtests_cxx retry_throttle_test) add_dependencies(buildtests_cxx rls_end2end_test) add_dependencies(buildtests_cxx rls_lb_config_parser_test) @@ -1655,6 +1658,7 @@ add_library(grpc src/core/ext/filters/client_channel/client_channel_channelz.cc src/core/ext/filters/client_channel/client_channel_factory.cc src/core/ext/filters/client_channel/client_channel_plugin.cc + src/core/ext/filters/client_channel/client_channel_service_config.cc src/core/ext/filters/client_channel/config_selector.cc src/core/ext/filters/client_channel/dynamic_filters.cc src/core/ext/filters/client_channel/global_subchannel_pool.cc @@ -1696,7 +1700,6 @@ add_library(grpc src/core/ext/filters/client_channel/resolver/polling_resolver.cc src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc - src/core/ext/filters/client_channel/resolver_result_parsing.cc src/core/ext/filters/client_channel/retry_filter.cc src/core/ext/filters/client_channel/retry_service_config.cc src/core/ext/filters/client_channel/retry_throttle.cc @@ -1706,7 +1709,7 @@ add_library(grpc src/core/ext/filters/client_channel/subchannel_stream_client.cc src/core/ext/filters/deadline/deadline_filter.cc src/core/ext/filters/fault_injection/fault_injection_filter.cc - src/core/ext/filters/fault_injection/service_config_parser.cc + src/core/ext/filters/fault_injection/fault_injection_service_config_parser.cc src/core/ext/filters/http/client/http_client_filter.cc src/core/ext/filters/http/client_authority_filter.cc src/core/ext/filters/http/http_filters_plugin.cc @@ -2608,6 +2611,7 @@ add_library(grpc_unsecure src/core/ext/filters/client_channel/client_channel_channelz.cc src/core/ext/filters/client_channel/client_channel_factory.cc src/core/ext/filters/client_channel/client_channel_plugin.cc + src/core/ext/filters/client_channel/client_channel_service_config.cc src/core/ext/filters/client_channel/config_selector.cc src/core/ext/filters/client_channel/dynamic_filters.cc src/core/ext/filters/client_channel/global_subchannel_pool.cc @@ -2641,7 +2645,6 @@ add_library(grpc_unsecure src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc src/core/ext/filters/client_channel/resolver/polling_resolver.cc src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc - src/core/ext/filters/client_channel/resolver_result_parsing.cc src/core/ext/filters/client_channel/retry_filter.cc src/core/ext/filters/client_channel/retry_service_config.cc src/core/ext/filters/client_channel/retry_throttle.cc @@ -2651,7 +2654,7 @@ add_library(grpc_unsecure src/core/ext/filters/client_channel/subchannel_stream_client.cc src/core/ext/filters/deadline/deadline_filter.cc src/core/ext/filters/fault_injection/fault_injection_filter.cc - src/core/ext/filters/fault_injection/service_config_parser.cc + src/core/ext/filters/fault_injection/fault_injection_service_config_parser.cc src/core/ext/filters/http/client/http_client_filter.cc src/core/ext/filters/http/client_authority_filter.cc src/core/ext/filters/http/http_filters_plugin.cc @@ -2840,7 +2843,6 @@ add_library(grpc_unsecure src/core/lib/iomgr/wakeup_fd_posix.cc src/core/lib/json/json_object_loader.cc src/core/lib/json/json_reader.cc - src/core/lib/json/json_util.cc src/core/lib/json/json_writer.cc src/core/lib/load_balancing/lb_policy.cc src/core/lib/load_balancing/lb_policy_registry.cc @@ -7839,6 +7841,41 @@ target_link_libraries(client_callback_end2end_test ) +endif() +if(gRPC_BUILD_TESTS) + +add_executable(client_channel_service_config_test + test/core/client_channel/client_channel_service_config_test.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc +) + +target_include_directories(client_channel_service_config_test + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR} + ${_gRPC_RE2_INCLUDE_DIR} + ${_gRPC_SSL_INCLUDE_DIR} + ${_gRPC_UPB_GENERATED_DIR} + ${_gRPC_UPB_GRPC_GENERATED_DIR} + ${_gRPC_UPB_INCLUDE_DIR} + ${_gRPC_XXHASH_INCLUDE_DIR} + ${_gRPC_ZLIB_INCLUDE_DIR} + third_party/googletest/googletest/include + third_party/googletest/googletest + third_party/googletest/googlemock/include + third_party/googletest/googlemock + ${_gRPC_PROTO_GENS_DIR} +) + +target_link_libraries(client_channel_service_config_test + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util +) + + endif() if(gRPC_BUILD_TESTS) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) @@ -13621,6 +13658,41 @@ target_link_libraries(message_compress_test ) +endif() +if(gRPC_BUILD_TESTS) + +add_executable(message_size_service_config_test + test/core/message_size/message_size_service_config_test.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc +) + +target_include_directories(message_size_service_config_test + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR} + ${_gRPC_RE2_INCLUDE_DIR} + ${_gRPC_SSL_INCLUDE_DIR} + ${_gRPC_UPB_GENERATED_DIR} + ${_gRPC_UPB_GRPC_GENERATED_DIR} + ${_gRPC_UPB_INCLUDE_DIR} + ${_gRPC_XXHASH_INCLUDE_DIR} + ${_gRPC_ZLIB_INCLUDE_DIR} + third_party/googletest/googletest/include + third_party/googletest/googletest + third_party/googletest/googlemock/include + third_party/googletest/googlemock + ${_gRPC_PROTO_GENS_DIR} +) + +target_link_libraries(message_size_service_config_test + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util +) + + endif() if(gRPC_BUILD_TESTS) @@ -15744,6 +15816,41 @@ target_link_libraries(resource_quota_test ) +endif() +if(gRPC_BUILD_TESTS) + +add_executable(retry_service_config_test + test/core/client_channel/retry_service_config_test.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc +) + +target_include_directories(retry_service_config_test + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR} + ${_gRPC_RE2_INCLUDE_DIR} + ${_gRPC_SSL_INCLUDE_DIR} + ${_gRPC_UPB_GENERATED_DIR} + ${_gRPC_UPB_GRPC_GENERATED_DIR} + ${_gRPC_UPB_INCLUDE_DIR} + ${_gRPC_XXHASH_INCLUDE_DIR} + ${_gRPC_ZLIB_INCLUDE_DIR} + third_party/googletest/googletest/include + third_party/googletest/googletest + third_party/googletest/googlemock/include + third_party/googletest/googlemock + ${_gRPC_PROTO_GENS_DIR} +) + +target_link_libraries(retry_service_config_test + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util +) + + endif() if(gRPC_BUILD_TESTS) @@ -16748,7 +16855,7 @@ endif() if(gRPC_BUILD_TESTS) add_executable(service_config_test - test/core/client_channel/service_config_test.cc + test/core/service_config/service_config_test.cc third_party/googletest/googletest/src/gtest-all.cc third_party/googletest/googlemock/src/gmock-all.cc ) diff --git a/Makefile b/Makefile index 31eedc9e018..86ed1146edc 100644 --- a/Makefile +++ b/Makefile @@ -968,6 +968,7 @@ LIBGRPC_SRC = \ src/core/ext/filters/client_channel/client_channel_channelz.cc \ src/core/ext/filters/client_channel/client_channel_factory.cc \ src/core/ext/filters/client_channel/client_channel_plugin.cc \ + src/core/ext/filters/client_channel/client_channel_service_config.cc \ src/core/ext/filters/client_channel/config_selector.cc \ src/core/ext/filters/client_channel/dynamic_filters.cc \ src/core/ext/filters/client_channel/global_subchannel_pool.cc \ @@ -1009,7 +1010,6 @@ LIBGRPC_SRC = \ src/core/ext/filters/client_channel/resolver/polling_resolver.cc \ src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc \ src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc \ - src/core/ext/filters/client_channel/resolver_result_parsing.cc \ src/core/ext/filters/client_channel/retry_filter.cc \ src/core/ext/filters/client_channel/retry_service_config.cc \ src/core/ext/filters/client_channel/retry_throttle.cc \ @@ -1019,7 +1019,7 @@ LIBGRPC_SRC = \ src/core/ext/filters/client_channel/subchannel_stream_client.cc \ src/core/ext/filters/deadline/deadline_filter.cc \ src/core/ext/filters/fault_injection/fault_injection_filter.cc \ - src/core/ext/filters/fault_injection/service_config_parser.cc \ + src/core/ext/filters/fault_injection/fault_injection_service_config_parser.cc \ src/core/ext/filters/http/client/http_client_filter.cc \ src/core/ext/filters/http/client_authority_filter.cc \ src/core/ext/filters/http/http_filters_plugin.cc \ @@ -1784,6 +1784,7 @@ LIBGRPC_UNSECURE_SRC = \ src/core/ext/filters/client_channel/client_channel_channelz.cc \ src/core/ext/filters/client_channel/client_channel_factory.cc \ src/core/ext/filters/client_channel/client_channel_plugin.cc \ + src/core/ext/filters/client_channel/client_channel_service_config.cc \ src/core/ext/filters/client_channel/config_selector.cc \ src/core/ext/filters/client_channel/dynamic_filters.cc \ src/core/ext/filters/client_channel/global_subchannel_pool.cc \ @@ -1817,7 +1818,6 @@ LIBGRPC_UNSECURE_SRC = \ src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc \ src/core/ext/filters/client_channel/resolver/polling_resolver.cc \ src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc \ - src/core/ext/filters/client_channel/resolver_result_parsing.cc \ src/core/ext/filters/client_channel/retry_filter.cc \ src/core/ext/filters/client_channel/retry_service_config.cc \ src/core/ext/filters/client_channel/retry_throttle.cc \ @@ -1827,7 +1827,7 @@ LIBGRPC_UNSECURE_SRC = \ src/core/ext/filters/client_channel/subchannel_stream_client.cc \ src/core/ext/filters/deadline/deadline_filter.cc \ src/core/ext/filters/fault_injection/fault_injection_filter.cc \ - src/core/ext/filters/fault_injection/service_config_parser.cc \ + src/core/ext/filters/fault_injection/fault_injection_service_config_parser.cc \ src/core/ext/filters/http/client/http_client_filter.cc \ src/core/ext/filters/http/client_authority_filter.cc \ src/core/ext/filters/http/http_filters_plugin.cc \ @@ -2016,7 +2016,6 @@ LIBGRPC_UNSECURE_SRC = \ src/core/lib/iomgr/wakeup_fd_posix.cc \ src/core/lib/json/json_object_loader.cc \ src/core/lib/json/json_reader.cc \ - src/core/lib/json/json_util.cc \ src/core/lib/json/json_writer.cc \ src/core/lib/load_balancing/lb_policy.cc \ src/core/lib/load_balancing/lb_policy_registry.cc \ @@ -3185,6 +3184,7 @@ src/core/ext/xds/xds_routing.cc: $(OPENSSL_DEP) src/core/ext/xds/xds_server_config_fetcher.cc: $(OPENSSL_DEP) src/core/ext/xds/xds_transport_grpc.cc: $(OPENSSL_DEP) src/core/lib/http/httpcli_security_connector.cc: $(OPENSSL_DEP) +src/core/lib/json/json_util.cc: $(OPENSSL_DEP) src/core/lib/matchers/matchers.cc: $(OPENSSL_DEP) src/core/lib/security/authorization/grpc_authorization_engine.cc: $(OPENSSL_DEP) src/core/lib/security/authorization/matchers.cc: $(OPENSSL_DEP) diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index e73711d2908..e5243269f2d 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -329,6 +329,7 @@ libs: - src/core/ext/filters/client_channel/client_channel.h - src/core/ext/filters/client_channel/client_channel_channelz.h - src/core/ext/filters/client_channel/client_channel_factory.h + - src/core/ext/filters/client_channel/client_channel_service_config.h - src/core/ext/filters/client_channel/config_selector.h - src/core/ext/filters/client_channel/connector.h - src/core/ext/filters/client_channel/dynamic_filters.h @@ -356,7 +357,6 @@ libs: - src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h - src/core/ext/filters/client_channel/resolver/polling_resolver.h - src/core/ext/filters/client_channel/resolver/xds/xds_resolver.h - - src/core/ext/filters/client_channel/resolver_result_parsing.h - src/core/ext/filters/client_channel/retry_filter.h - src/core/ext/filters/client_channel/retry_service_config.h - src/core/ext/filters/client_channel/retry_throttle.h @@ -366,7 +366,7 @@ libs: - src/core/ext/filters/client_channel/subchannel_stream_client.h - src/core/ext/filters/deadline/deadline_filter.h - src/core/ext/filters/fault_injection/fault_injection_filter.h - - src/core/ext/filters/fault_injection/service_config_parser.h + - src/core/ext/filters/fault_injection/fault_injection_service_config_parser.h - src/core/ext/filters/http/client/http_client_filter.h - src/core/ext/filters/http/client_authority_filter.h - src/core/ext/filters/http/message_compress/message_compress_filter.h @@ -869,6 +869,7 @@ libs: - src/core/lib/iomgr/wakeup_fd_posix.h - src/core/lib/json/json.h - src/core/lib/json/json_args.h + - src/core/lib/json/json_channel_args.h - src/core/lib/json/json_object_loader.h - src/core/lib/json/json_util.h - src/core/lib/load_balancing/lb_policy.h @@ -1048,6 +1049,7 @@ libs: - src/core/ext/filters/client_channel/client_channel_channelz.cc - src/core/ext/filters/client_channel/client_channel_factory.cc - src/core/ext/filters/client_channel/client_channel_plugin.cc + - src/core/ext/filters/client_channel/client_channel_service_config.cc - src/core/ext/filters/client_channel/config_selector.cc - src/core/ext/filters/client_channel/dynamic_filters.cc - src/core/ext/filters/client_channel/global_subchannel_pool.cc @@ -1089,7 +1091,6 @@ libs: - src/core/ext/filters/client_channel/resolver/polling_resolver.cc - src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc - src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc - - src/core/ext/filters/client_channel/resolver_result_parsing.cc - src/core/ext/filters/client_channel/retry_filter.cc - src/core/ext/filters/client_channel/retry_service_config.cc - src/core/ext/filters/client_channel/retry_throttle.cc @@ -1099,7 +1100,7 @@ libs: - src/core/ext/filters/client_channel/subchannel_stream_client.cc - src/core/ext/filters/deadline/deadline_filter.cc - src/core/ext/filters/fault_injection/fault_injection_filter.cc - - src/core/ext/filters/fault_injection/service_config_parser.cc + - src/core/ext/filters/fault_injection/fault_injection_service_config_parser.cc - src/core/ext/filters/http/client/http_client_filter.cc - src/core/ext/filters/http/client_authority_filter.cc - src/core/ext/filters/http/http_filters_plugin.cc @@ -1886,6 +1887,7 @@ libs: - src/core/ext/filters/client_channel/client_channel.h - src/core/ext/filters/client_channel/client_channel_channelz.h - src/core/ext/filters/client_channel/client_channel_factory.h + - src/core/ext/filters/client_channel/client_channel_service_config.h - src/core/ext/filters/client_channel/config_selector.h - src/core/ext/filters/client_channel/connector.h - src/core/ext/filters/client_channel/dynamic_filters.h @@ -1910,7 +1912,6 @@ libs: - src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h - src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h - src/core/ext/filters/client_channel/resolver/polling_resolver.h - - src/core/ext/filters/client_channel/resolver_result_parsing.h - src/core/ext/filters/client_channel/retry_filter.h - src/core/ext/filters/client_channel/retry_service_config.h - src/core/ext/filters/client_channel/retry_throttle.h @@ -1920,7 +1921,7 @@ libs: - src/core/ext/filters/client_channel/subchannel_stream_client.h - src/core/ext/filters/deadline/deadline_filter.h - src/core/ext/filters/fault_injection/fault_injection_filter.h - - src/core/ext/filters/fault_injection/service_config_parser.h + - src/core/ext/filters/fault_injection/fault_injection_service_config_parser.h - src/core/ext/filters/http/client/http_client_filter.h - src/core/ext/filters/http/client_authority_filter.h - src/core/ext/filters/http/message_compress/message_compress_filter.h @@ -2125,8 +2126,8 @@ libs: - src/core/lib/iomgr/wakeup_fd_posix.h - src/core/lib/json/json.h - src/core/lib/json/json_args.h + - src/core/lib/json/json_channel_args.h - src/core/lib/json/json_object_loader.h - - src/core/lib/json/json_util.h - src/core/lib/load_balancing/lb_policy.h - src/core/lib/load_balancing/lb_policy_factory.h - src/core/lib/load_balancing/lb_policy_registry.h @@ -2252,6 +2253,7 @@ libs: - src/core/ext/filters/client_channel/client_channel_channelz.cc - src/core/ext/filters/client_channel/client_channel_factory.cc - src/core/ext/filters/client_channel/client_channel_plugin.cc + - src/core/ext/filters/client_channel/client_channel_service_config.cc - src/core/ext/filters/client_channel/config_selector.cc - src/core/ext/filters/client_channel/dynamic_filters.cc - src/core/ext/filters/client_channel/global_subchannel_pool.cc @@ -2285,7 +2287,6 @@ libs: - src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc - src/core/ext/filters/client_channel/resolver/polling_resolver.cc - src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc - - src/core/ext/filters/client_channel/resolver_result_parsing.cc - src/core/ext/filters/client_channel/retry_filter.cc - src/core/ext/filters/client_channel/retry_service_config.cc - src/core/ext/filters/client_channel/retry_throttle.cc @@ -2295,7 +2296,7 @@ libs: - src/core/ext/filters/client_channel/subchannel_stream_client.cc - src/core/ext/filters/deadline/deadline_filter.cc - src/core/ext/filters/fault_injection/fault_injection_filter.cc - - src/core/ext/filters/fault_injection/service_config_parser.cc + - src/core/ext/filters/fault_injection/fault_injection_service_config_parser.cc - src/core/ext/filters/http/client/http_client_filter.cc - src/core/ext/filters/http/client_authority_filter.cc - src/core/ext/filters/http/http_filters_plugin.cc @@ -2484,7 +2485,6 @@ libs: - src/core/lib/iomgr/wakeup_fd_posix.cc - src/core/lib/json/json_object_loader.cc - src/core/lib/json/json_reader.cc - - src/core/lib/json/json_util.cc - src/core/lib/json/json_writer.cc - src/core/lib/load_balancing/lb_policy.cc - src/core/lib/load_balancing/lb_policy_registry.cc @@ -5187,6 +5187,15 @@ targets: - test/cpp/end2end/test_service_impl.cc deps: - grpc++_test_util +- name: client_channel_service_config_test + gtest: true + build: test + language: c++ + headers: [] + src: + - test/core/client_channel/client_channel_service_config_test.cc + deps: + - grpc_test_util - name: client_channel_stress_test gtest: true build: test @@ -8042,6 +8051,15 @@ targets: deps: - grpc_test_util uses_polling: false +- name: message_size_service_config_test + gtest: true + build: test + language: c++ + headers: [] + src: + - test/core/message_size/message_size_service_config_test.cc + deps: + - grpc_test_util - name: metadata_map_test gtest: true build: test @@ -9065,6 +9083,15 @@ targets: deps: - grpc_test_util_unsecure uses_polling: false +- name: retry_service_config_test + gtest: true + build: test + language: c++ + headers: [] + src: + - test/core/client_channel/retry_service_config_test.cc + deps: + - grpc_test_util - name: retry_throttle_test gtest: true build: test @@ -9487,7 +9514,7 @@ targets: language: c++ headers: [] src: - - test/core/client_channel/service_config_test.cc + - test/core/service_config/service_config_test.cc deps: - grpc_test_util - name: settings_timeout_test diff --git a/config.m4 b/config.m4 index d95d861b2fc..5618e9a060b 100644 --- a/config.m4 +++ b/config.m4 @@ -50,6 +50,7 @@ if test "$PHP_GRPC" != "no"; then src/core/ext/filters/client_channel/client_channel_channelz.cc \ src/core/ext/filters/client_channel/client_channel_factory.cc \ src/core/ext/filters/client_channel/client_channel_plugin.cc \ + src/core/ext/filters/client_channel/client_channel_service_config.cc \ src/core/ext/filters/client_channel/config_selector.cc \ src/core/ext/filters/client_channel/dynamic_filters.cc \ src/core/ext/filters/client_channel/global_subchannel_pool.cc \ @@ -91,7 +92,6 @@ if test "$PHP_GRPC" != "no"; then src/core/ext/filters/client_channel/resolver/polling_resolver.cc \ src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc \ src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc \ - src/core/ext/filters/client_channel/resolver_result_parsing.cc \ src/core/ext/filters/client_channel/retry_filter.cc \ src/core/ext/filters/client_channel/retry_service_config.cc \ src/core/ext/filters/client_channel/retry_throttle.cc \ @@ -101,7 +101,7 @@ if test "$PHP_GRPC" != "no"; then src/core/ext/filters/client_channel/subchannel_stream_client.cc \ src/core/ext/filters/deadline/deadline_filter.cc \ src/core/ext/filters/fault_injection/fault_injection_filter.cc \ - src/core/ext/filters/fault_injection/service_config_parser.cc \ + src/core/ext/filters/fault_injection/fault_injection_service_config_parser.cc \ src/core/ext/filters/http/client/http_client_filter.cc \ src/core/ext/filters/http/client_authority_filter.cc \ src/core/ext/filters/http/http_filters_plugin.cc \ diff --git a/config.w32 b/config.w32 index a9e5bfc8790..e2bff4c3171 100644 --- a/config.w32 +++ b/config.w32 @@ -16,6 +16,7 @@ if (PHP_GRPC != "no") { "src\\core\\ext\\filters\\client_channel\\client_channel_channelz.cc " + "src\\core\\ext\\filters\\client_channel\\client_channel_factory.cc " + "src\\core\\ext\\filters\\client_channel\\client_channel_plugin.cc " + + "src\\core\\ext\\filters\\client_channel\\client_channel_service_config.cc " + "src\\core\\ext\\filters\\client_channel\\config_selector.cc " + "src\\core\\ext\\filters\\client_channel\\dynamic_filters.cc " + "src\\core\\ext\\filters\\client_channel\\global_subchannel_pool.cc " + @@ -57,7 +58,6 @@ if (PHP_GRPC != "no") { "src\\core\\ext\\filters\\client_channel\\resolver\\polling_resolver.cc " + "src\\core\\ext\\filters\\client_channel\\resolver\\sockaddr\\sockaddr_resolver.cc " + "src\\core\\ext\\filters\\client_channel\\resolver\\xds\\xds_resolver.cc " + - "src\\core\\ext\\filters\\client_channel\\resolver_result_parsing.cc " + "src\\core\\ext\\filters\\client_channel\\retry_filter.cc " + "src\\core\\ext\\filters\\client_channel\\retry_service_config.cc " + "src\\core\\ext\\filters\\client_channel\\retry_throttle.cc " + @@ -67,7 +67,7 @@ if (PHP_GRPC != "no") { "src\\core\\ext\\filters\\client_channel\\subchannel_stream_client.cc " + "src\\core\\ext\\filters\\deadline\\deadline_filter.cc " + "src\\core\\ext\\filters\\fault_injection\\fault_injection_filter.cc " + - "src\\core\\ext\\filters\\fault_injection\\service_config_parser.cc " + + "src\\core\\ext\\filters\\fault_injection\\fault_injection_service_config_parser.cc " + "src\\core\\ext\\filters\\http\\client\\http_client_filter.cc " + "src\\core\\ext\\filters\\http\\client_authority_filter.cc " + "src\\core\\ext\\filters\\http\\http_filters_plugin.cc " + diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec index 4096819a511..fb8609e99fc 100644 --- a/gRPC-C++.podspec +++ b/gRPC-C++.podspec @@ -231,6 +231,7 @@ Pod::Spec.new do |s| 'src/core/ext/filters/client_channel/client_channel.h', 'src/core/ext/filters/client_channel/client_channel_channelz.h', 'src/core/ext/filters/client_channel/client_channel_factory.h', + 'src/core/ext/filters/client_channel/client_channel_service_config.h', 'src/core/ext/filters/client_channel/config_selector.h', 'src/core/ext/filters/client_channel/connector.h', 'src/core/ext/filters/client_channel/dynamic_filters.h', @@ -258,7 +259,6 @@ Pod::Spec.new do |s| 'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h', 'src/core/ext/filters/client_channel/resolver/polling_resolver.h', 'src/core/ext/filters/client_channel/resolver/xds/xds_resolver.h', - 'src/core/ext/filters/client_channel/resolver_result_parsing.h', 'src/core/ext/filters/client_channel/retry_filter.h', 'src/core/ext/filters/client_channel/retry_service_config.h', 'src/core/ext/filters/client_channel/retry_throttle.h', @@ -268,7 +268,7 @@ Pod::Spec.new do |s| 'src/core/ext/filters/client_channel/subchannel_stream_client.h', 'src/core/ext/filters/deadline/deadline_filter.h', 'src/core/ext/filters/fault_injection/fault_injection_filter.h', - 'src/core/ext/filters/fault_injection/service_config_parser.h', + 'src/core/ext/filters/fault_injection/fault_injection_service_config_parser.h', 'src/core/ext/filters/http/client/http_client_filter.h', 'src/core/ext/filters/http/client_authority_filter.h', 'src/core/ext/filters/http/message_compress/message_compress_filter.h', @@ -832,6 +832,7 @@ Pod::Spec.new do |s| 'src/core/lib/iomgr/wakeup_fd_posix.h', 'src/core/lib/json/json.h', 'src/core/lib/json/json_args.h', + 'src/core/lib/json/json_channel_args.h', 'src/core/lib/json/json_object_loader.h', 'src/core/lib/json/json_util.h', 'src/core/lib/load_balancing/lb_policy.h', @@ -1125,6 +1126,7 @@ Pod::Spec.new do |s| 'src/core/ext/filters/client_channel/client_channel.h', 'src/core/ext/filters/client_channel/client_channel_channelz.h', 'src/core/ext/filters/client_channel/client_channel_factory.h', + 'src/core/ext/filters/client_channel/client_channel_service_config.h', 'src/core/ext/filters/client_channel/config_selector.h', 'src/core/ext/filters/client_channel/connector.h', 'src/core/ext/filters/client_channel/dynamic_filters.h', @@ -1152,7 +1154,6 @@ Pod::Spec.new do |s| 'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h', 'src/core/ext/filters/client_channel/resolver/polling_resolver.h', 'src/core/ext/filters/client_channel/resolver/xds/xds_resolver.h', - 'src/core/ext/filters/client_channel/resolver_result_parsing.h', 'src/core/ext/filters/client_channel/retry_filter.h', 'src/core/ext/filters/client_channel/retry_service_config.h', 'src/core/ext/filters/client_channel/retry_throttle.h', @@ -1162,7 +1163,7 @@ Pod::Spec.new do |s| 'src/core/ext/filters/client_channel/subchannel_stream_client.h', 'src/core/ext/filters/deadline/deadline_filter.h', 'src/core/ext/filters/fault_injection/fault_injection_filter.h', - 'src/core/ext/filters/fault_injection/service_config_parser.h', + 'src/core/ext/filters/fault_injection/fault_injection_service_config_parser.h', 'src/core/ext/filters/http/client/http_client_filter.h', 'src/core/ext/filters/http/client_authority_filter.h', 'src/core/ext/filters/http/message_compress/message_compress_filter.h', @@ -1708,6 +1709,7 @@ Pod::Spec.new do |s| 'src/core/lib/iomgr/wakeup_fd_posix.h', 'src/core/lib/json/json.h', 'src/core/lib/json/json_args.h', + 'src/core/lib/json/json_channel_args.h', 'src/core/lib/json/json_object_loader.h', 'src/core/lib/json/json_util.h', 'src/core/lib/load_balancing/lb_policy.h', diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index 62b53d25d7e..48088f3b1b8 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -216,6 +216,8 @@ Pod::Spec.new do |s| 'src/core/ext/filters/client_channel/client_channel_factory.cc', 'src/core/ext/filters/client_channel/client_channel_factory.h', 'src/core/ext/filters/client_channel/client_channel_plugin.cc', + 'src/core/ext/filters/client_channel/client_channel_service_config.cc', + 'src/core/ext/filters/client_channel/client_channel_service_config.h', 'src/core/ext/filters/client_channel/config_selector.cc', 'src/core/ext/filters/client_channel/config_selector.h', 'src/core/ext/filters/client_channel/connector.h', @@ -284,8 +286,6 @@ Pod::Spec.new do |s| 'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc', 'src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc', 'src/core/ext/filters/client_channel/resolver/xds/xds_resolver.h', - 'src/core/ext/filters/client_channel/resolver_result_parsing.cc', - 'src/core/ext/filters/client_channel/resolver_result_parsing.h', 'src/core/ext/filters/client_channel/retry_filter.cc', 'src/core/ext/filters/client_channel/retry_filter.h', 'src/core/ext/filters/client_channel/retry_service_config.cc', @@ -304,8 +304,8 @@ Pod::Spec.new do |s| 'src/core/ext/filters/deadline/deadline_filter.h', 'src/core/ext/filters/fault_injection/fault_injection_filter.cc', 'src/core/ext/filters/fault_injection/fault_injection_filter.h', - 'src/core/ext/filters/fault_injection/service_config_parser.cc', - 'src/core/ext/filters/fault_injection/service_config_parser.h', + 'src/core/ext/filters/fault_injection/fault_injection_service_config_parser.cc', + 'src/core/ext/filters/fault_injection/fault_injection_service_config_parser.h', 'src/core/ext/filters/http/client/http_client_filter.cc', 'src/core/ext/filters/http/client/http_client_filter.h', 'src/core/ext/filters/http/client_authority_filter.cc', @@ -1353,6 +1353,7 @@ Pod::Spec.new do |s| 'src/core/lib/iomgr/wakeup_fd_posix.h', 'src/core/lib/json/json.h', 'src/core/lib/json/json_args.h', + 'src/core/lib/json/json_channel_args.h', 'src/core/lib/json/json_object_loader.cc', 'src/core/lib/json/json_object_loader.h', 'src/core/lib/json/json_reader.cc', @@ -1783,6 +1784,7 @@ Pod::Spec.new do |s| 'src/core/ext/filters/client_channel/client_channel.h', 'src/core/ext/filters/client_channel/client_channel_channelz.h', 'src/core/ext/filters/client_channel/client_channel_factory.h', + 'src/core/ext/filters/client_channel/client_channel_service_config.h', 'src/core/ext/filters/client_channel/config_selector.h', 'src/core/ext/filters/client_channel/connector.h', 'src/core/ext/filters/client_channel/dynamic_filters.h', @@ -1810,7 +1812,6 @@ Pod::Spec.new do |s| 'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h', 'src/core/ext/filters/client_channel/resolver/polling_resolver.h', 'src/core/ext/filters/client_channel/resolver/xds/xds_resolver.h', - 'src/core/ext/filters/client_channel/resolver_result_parsing.h', 'src/core/ext/filters/client_channel/retry_filter.h', 'src/core/ext/filters/client_channel/retry_service_config.h', 'src/core/ext/filters/client_channel/retry_throttle.h', @@ -1820,7 +1821,7 @@ Pod::Spec.new do |s| 'src/core/ext/filters/client_channel/subchannel_stream_client.h', 'src/core/ext/filters/deadline/deadline_filter.h', 'src/core/ext/filters/fault_injection/fault_injection_filter.h', - 'src/core/ext/filters/fault_injection/service_config_parser.h', + 'src/core/ext/filters/fault_injection/fault_injection_service_config_parser.h', 'src/core/ext/filters/http/client/http_client_filter.h', 'src/core/ext/filters/http/client_authority_filter.h', 'src/core/ext/filters/http/message_compress/message_compress_filter.h', @@ -2346,6 +2347,7 @@ Pod::Spec.new do |s| 'src/core/lib/iomgr/wakeup_fd_posix.h', 'src/core/lib/json/json.h', 'src/core/lib/json/json_args.h', + 'src/core/lib/json/json_channel_args.h', 'src/core/lib/json/json_object_loader.h', 'src/core/lib/json/json_util.h', 'src/core/lib/load_balancing/lb_policy.h', diff --git a/grpc.gemspec b/grpc.gemspec index 8a3f357dea7..d142689dfb0 100644 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -127,6 +127,8 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/filters/client_channel/client_channel_factory.cc ) s.files += %w( src/core/ext/filters/client_channel/client_channel_factory.h ) s.files += %w( src/core/ext/filters/client_channel/client_channel_plugin.cc ) + s.files += %w( src/core/ext/filters/client_channel/client_channel_service_config.cc ) + s.files += %w( src/core/ext/filters/client_channel/client_channel_service_config.h ) s.files += %w( src/core/ext/filters/client_channel/config_selector.cc ) s.files += %w( src/core/ext/filters/client_channel/config_selector.h ) s.files += %w( src/core/ext/filters/client_channel/connector.h ) @@ -195,8 +197,6 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc ) s.files += %w( src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc ) s.files += %w( src/core/ext/filters/client_channel/resolver/xds/xds_resolver.h ) - s.files += %w( src/core/ext/filters/client_channel/resolver_result_parsing.cc ) - s.files += %w( src/core/ext/filters/client_channel/resolver_result_parsing.h ) s.files += %w( src/core/ext/filters/client_channel/retry_filter.cc ) s.files += %w( src/core/ext/filters/client_channel/retry_filter.h ) s.files += %w( src/core/ext/filters/client_channel/retry_service_config.cc ) @@ -215,8 +215,8 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/filters/deadline/deadline_filter.h ) s.files += %w( src/core/ext/filters/fault_injection/fault_injection_filter.cc ) s.files += %w( src/core/ext/filters/fault_injection/fault_injection_filter.h ) - s.files += %w( src/core/ext/filters/fault_injection/service_config_parser.cc ) - s.files += %w( src/core/ext/filters/fault_injection/service_config_parser.h ) + s.files += %w( src/core/ext/filters/fault_injection/fault_injection_service_config_parser.cc ) + s.files += %w( src/core/ext/filters/fault_injection/fault_injection_service_config_parser.h ) s.files += %w( src/core/ext/filters/http/client/http_client_filter.cc ) s.files += %w( src/core/ext/filters/http/client/http_client_filter.h ) s.files += %w( src/core/ext/filters/http/client_authority_filter.cc ) @@ -1264,6 +1264,7 @@ Gem::Specification.new do |s| s.files += %w( src/core/lib/iomgr/wakeup_fd_posix.h ) s.files += %w( src/core/lib/json/json.h ) s.files += %w( src/core/lib/json/json_args.h ) + s.files += %w( src/core/lib/json/json_channel_args.h ) s.files += %w( src/core/lib/json/json_object_loader.cc ) s.files += %w( src/core/lib/json/json_object_loader.h ) s.files += %w( src/core/lib/json/json_reader.cc ) diff --git a/grpc.gyp b/grpc.gyp index beb5bf85d39..ddf64da5784 100644 --- a/grpc.gyp +++ b/grpc.gyp @@ -382,6 +382,7 @@ 'src/core/ext/filters/client_channel/client_channel_channelz.cc', 'src/core/ext/filters/client_channel/client_channel_factory.cc', 'src/core/ext/filters/client_channel/client_channel_plugin.cc', + 'src/core/ext/filters/client_channel/client_channel_service_config.cc', 'src/core/ext/filters/client_channel/config_selector.cc', 'src/core/ext/filters/client_channel/dynamic_filters.cc', 'src/core/ext/filters/client_channel/global_subchannel_pool.cc', @@ -423,7 +424,6 @@ 'src/core/ext/filters/client_channel/resolver/polling_resolver.cc', 'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc', 'src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc', - 'src/core/ext/filters/client_channel/resolver_result_parsing.cc', 'src/core/ext/filters/client_channel/retry_filter.cc', 'src/core/ext/filters/client_channel/retry_service_config.cc', 'src/core/ext/filters/client_channel/retry_throttle.cc', @@ -433,7 +433,7 @@ 'src/core/ext/filters/client_channel/subchannel_stream_client.cc', 'src/core/ext/filters/deadline/deadline_filter.cc', 'src/core/ext/filters/fault_injection/fault_injection_filter.cc', - 'src/core/ext/filters/fault_injection/service_config_parser.cc', + 'src/core/ext/filters/fault_injection/fault_injection_service_config_parser.cc', 'src/core/ext/filters/http/client/http_client_filter.cc', 'src/core/ext/filters/http/client_authority_filter.cc', 'src/core/ext/filters/http/http_filters_plugin.cc', @@ -1145,6 +1145,7 @@ 'src/core/ext/filters/client_channel/client_channel_channelz.cc', 'src/core/ext/filters/client_channel/client_channel_factory.cc', 'src/core/ext/filters/client_channel/client_channel_plugin.cc', + 'src/core/ext/filters/client_channel/client_channel_service_config.cc', 'src/core/ext/filters/client_channel/config_selector.cc', 'src/core/ext/filters/client_channel/dynamic_filters.cc', 'src/core/ext/filters/client_channel/global_subchannel_pool.cc', @@ -1178,7 +1179,6 @@ 'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc', 'src/core/ext/filters/client_channel/resolver/polling_resolver.cc', 'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc', - 'src/core/ext/filters/client_channel/resolver_result_parsing.cc', 'src/core/ext/filters/client_channel/retry_filter.cc', 'src/core/ext/filters/client_channel/retry_service_config.cc', 'src/core/ext/filters/client_channel/retry_throttle.cc', @@ -1188,7 +1188,7 @@ 'src/core/ext/filters/client_channel/subchannel_stream_client.cc', 'src/core/ext/filters/deadline/deadline_filter.cc', 'src/core/ext/filters/fault_injection/fault_injection_filter.cc', - 'src/core/ext/filters/fault_injection/service_config_parser.cc', + 'src/core/ext/filters/fault_injection/fault_injection_service_config_parser.cc', 'src/core/ext/filters/http/client/http_client_filter.cc', 'src/core/ext/filters/http/client_authority_filter.cc', 'src/core/ext/filters/http/http_filters_plugin.cc', @@ -1377,7 +1377,6 @@ 'src/core/lib/iomgr/wakeup_fd_posix.cc', 'src/core/lib/json/json_object_loader.cc', 'src/core/lib/json/json_reader.cc', - 'src/core/lib/json/json_util.cc', 'src/core/lib/json/json_writer.cc', 'src/core/lib/load_balancing/lb_policy.cc', 'src/core/lib/load_balancing/lb_policy_registry.cc', diff --git a/package.xml b/package.xml index 5c1b2e85df0..99acefc790b 100644 --- a/package.xml +++ b/package.xml @@ -109,6 +109,8 @@ + + @@ -177,8 +179,6 @@ - - @@ -197,8 +197,8 @@ - - + + @@ -1246,6 +1246,7 @@ + diff --git a/src/core/BUILD b/src/core/BUILD index d7f76fbd352..99b4c3831be 100644 --- a/src/core/BUILD +++ b/src/core/BUILD @@ -2058,15 +2058,12 @@ grpc_cc_library( hdrs = [ "lib/service_config/service_config_parser.h", ], - external_deps = [ - "absl/status", - "absl/status:statusor", - "absl/strings", - ], + external_deps = ["absl/strings"], language = "c++", deps = [ "channel_args", "json", + "validation_errors", "//:gpr", ], ) @@ -3056,7 +3053,6 @@ grpc_cc_library( ], external_deps = [ "absl/status", - "absl/status:statusor", "absl/strings", "absl/strings:str_format", "absl/types:optional", @@ -3070,9 +3066,12 @@ grpc_cc_library( "closure", "grpc_service_config", "json", + "json_args", + "json_object_loader", "service_config_parser", "slice_buffer", "status_helper", + "validation_errors", "//:channel_stack_builder", "//:config", "//:debug_location", @@ -3086,11 +3085,11 @@ grpc_cc_library( name = "grpc_fault_injection_filter", srcs = [ "ext/filters/fault_injection/fault_injection_filter.cc", - "ext/filters/fault_injection/service_config_parser.cc", + "ext/filters/fault_injection/fault_injection_service_config_parser.cc", ], hdrs = [ "ext/filters/fault_injection/fault_injection_filter.h", - "ext/filters/fault_injection/service_config_parser.h", + "ext/filters/fault_injection/fault_injection_service_config_parser.h", ], external_deps = [ "absl/base:core_headers", @@ -3107,12 +3106,13 @@ grpc_cc_library( "context", "grpc_service_config", "json", - "json_util", + "json_args", + "json_object_loader", "service_config_parser", "sleep", - "status_helper", "time", "try_seq", + "validation_errors", "//:config", "//:gpr", "//:grpc_base", @@ -3135,7 +3135,6 @@ grpc_cc_library( "absl/status", "absl/status:statusor", "absl/strings", - "absl/strings:str_format", "absl/types:optional", ], language = "c++", @@ -3148,10 +3147,12 @@ grpc_cc_library( "grpc_rbac_engine", "grpc_service_config", "json", - "json_util", + "json_args", + "json_object_loader", "service_config_parser", "status_helper", "transport_fwd", + "validation_errors", "//:config", "//:debug_location", "//:gpr", diff --git a/src/core/ext/filters/client_channel/client_channel.cc b/src/core/ext/filters/client_channel/client_channel.cc index a3c99194c44..6235de48dc2 100644 --- a/src/core/ext/filters/client_channel/client_channel.cc +++ b/src/core/ext/filters/client_channel/client_channel.cc @@ -47,12 +47,12 @@ #include "src/core/ext/filters/client_channel/backend_metric.h" #include "src/core/ext/filters/client_channel/backup_poller.h" #include "src/core/ext/filters/client_channel/client_channel_channelz.h" +#include "src/core/ext/filters/client_channel/client_channel_service_config.h" #include "src/core/ext/filters/client_channel/config_selector.h" #include "src/core/ext/filters/client_channel/dynamic_filters.h" #include "src/core/ext/filters/client_channel/global_subchannel_pool.h" #include "src/core/ext/filters/client_channel/lb_policy/child_policy_handler.h" #include "src/core/ext/filters/client_channel/local_subchannel_pool.h" -#include "src/core/ext/filters/client_channel/resolver_result_parsing.h" #include "src/core/ext/filters/client_channel/retry_filter.h" #include "src/core/ext/filters/client_channel/subchannel.h" #include "src/core/ext/filters/client_channel/subchannel_interface_internal.h" diff --git a/src/core/ext/filters/client_channel/client_channel_plugin.cc b/src/core/ext/filters/client_channel/client_channel_plugin.cc index bdb535ad5c0..f19714af184 100644 --- a/src/core/ext/filters/client_channel/client_channel_plugin.cc +++ b/src/core/ext/filters/client_channel/client_channel_plugin.cc @@ -19,7 +19,7 @@ #include #include "src/core/ext/filters/client_channel/client_channel.h" -#include "src/core/ext/filters/client_channel/resolver_result_parsing.h" +#include "src/core/ext/filters/client_channel/client_channel_service_config.h" #include "src/core/ext/filters/client_channel/retry_service_config.h" #include "src/core/lib/channel/channel_stack_builder.h" #include "src/core/lib/config/core_configuration.h" diff --git a/src/core/ext/filters/client_channel/client_channel_service_config.cc b/src/core/ext/filters/client_channel/client_channel_service_config.cc new file mode 100644 index 00000000000..d37376afd9e --- /dev/null +++ b/src/core/ext/filters/client_channel/client_channel_service_config.cc @@ -0,0 +1,153 @@ +// +// Copyright 2018 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include + +#include "src/core/ext/filters/client_channel/client_channel_service_config.h" + +#include +#include + +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/ascii.h" +#include "absl/strings/str_cat.h" +#include "absl/types/optional.h" + +#include "src/core/lib/load_balancing/lb_policy_registry.h" + +// As per the retry design, we do not allow more than 5 retry attempts. +#define MAX_MAX_RETRY_ATTEMPTS 5 + +namespace grpc_core { +namespace internal { + +// +// ClientChannelGlobalParsedConfig::HealthCheckConfig +// + +const JsonLoaderInterface* +ClientChannelGlobalParsedConfig::HealthCheckConfig::JsonLoader( + const JsonArgs&) { + static const auto* loader = + JsonObjectLoader() + .OptionalField("serviceName", &HealthCheckConfig::service_name) + .Finish(); + return loader; +} + +// +// ClientChannelGlobalParsedConfig +// + +const JsonLoaderInterface* ClientChannelGlobalParsedConfig::JsonLoader( + const JsonArgs&) { + static const auto* loader = + JsonObjectLoader() + // Note: "loadBalancingConfig" requires special handling, so + // that field will be parsed in JsonPostLoad() instead. + .OptionalField( + "loadBalancingPolicy", + &ClientChannelGlobalParsedConfig::parsed_deprecated_lb_policy_) + .OptionalField("healthCheckConfig", + &ClientChannelGlobalParsedConfig::health_check_config_) + .Finish(); + return loader; +} + +void ClientChannelGlobalParsedConfig::JsonPostLoad(const Json& json, + const JsonArgs&, + ValidationErrors* errors) { + const auto& lb_policy_registry = + CoreConfiguration::Get().lb_policy_registry(); + // Parse LB config. + { + ValidationErrors::ScopedField field(errors, ".loadBalancingConfig"); + auto it = json.object_value().find("loadBalancingConfig"); + if (it != json.object_value().end()) { + auto config = lb_policy_registry.ParseLoadBalancingConfig(it->second); + if (!config.ok()) { + errors->AddError(config.status().message()); + } else { + parsed_lb_config_ = std::move(*config); + } + } + } + // Sanity-check deprecated "loadBalancingPolicy" field. + if (!parsed_deprecated_lb_policy_.empty()) { + ValidationErrors::ScopedField field(errors, ".loadBalancingPolicy"); + // Convert to lower-case. + absl::AsciiStrToLower(&parsed_deprecated_lb_policy_); + bool requires_config = false; + if (!lb_policy_registry.LoadBalancingPolicyExists( + parsed_deprecated_lb_policy_, &requires_config)) { + errors->AddError(absl::StrCat("unknown LB policy \"", + parsed_deprecated_lb_policy_, "\"")); + } else if (requires_config) { + errors->AddError(absl::StrCat( + "LB policy \"", parsed_deprecated_lb_policy_, + "\" requires a config. Please use loadBalancingConfig instead.")); + } + } +} + +// +// ClientChannelMethodParsedConfig +// + +const JsonLoaderInterface* ClientChannelMethodParsedConfig::JsonLoader( + const JsonArgs&) { + static const auto* loader = + JsonObjectLoader() + .OptionalField("timeout", &ClientChannelMethodParsedConfig::timeout_) + .OptionalField("waitForReady", + &ClientChannelMethodParsedConfig::wait_for_ready_) + .Finish(); + return loader; +} + +// +// ClientChannelServiceConfigParser +// + +size_t ClientChannelServiceConfigParser::ParserIndex() { + return CoreConfiguration::Get().service_config_parser().GetParserIndex( + parser_name()); +} + +void ClientChannelServiceConfigParser::Register( + CoreConfiguration::Builder* builder) { + builder->service_config_parser()->RegisterParser( + std::make_unique()); +} + +std::unique_ptr +ClientChannelServiceConfigParser::ParseGlobalParams(const ChannelArgs& /*args*/, + const Json& json, + ValidationErrors* errors) { + return LoadFromJson>( + json, JsonArgs(), errors); +} + +std::unique_ptr +ClientChannelServiceConfigParser::ParsePerMethodParams( + const ChannelArgs& /*args*/, const Json& json, ValidationErrors* errors) { + return LoadFromJson>( + json, JsonArgs(), errors); +} + +} // namespace internal +} // namespace grpc_core diff --git a/src/core/ext/filters/client_channel/resolver_result_parsing.h b/src/core/ext/filters/client_channel/client_channel_service_config.h similarity index 64% rename from src/core/ext/filters/client_channel/resolver_result_parsing.h rename to src/core/ext/filters/client_channel/client_channel_service_config.h index c87b8af615c..7f9fa7e7fb6 100644 --- a/src/core/ext/filters/client_channel/resolver_result_parsing.h +++ b/src/core/ext/filters/client_channel/client_channel_service_config.h @@ -14,8 +14,8 @@ // limitations under the License. // -#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_RESULT_PARSING_H -#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_RESULT_PARSING_H +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_SERVICE_CONFIG_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_SERVICE_CONFIG_H #include @@ -23,9 +23,7 @@ #include #include -#include -#include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" @@ -33,7 +31,10 @@ #include "src/core/lib/config/core_configuration.h" #include "src/core/lib/gprpp/ref_counted_ptr.h" #include "src/core/lib/gprpp/time.h" +#include "src/core/lib/gprpp/validation_errors.h" #include "src/core/lib/json/json.h" +#include "src/core/lib/json/json_args.h" +#include "src/core/lib/json/json_object_loader.h" #include "src/core/lib/load_balancing/lb_policy.h" #include "src/core/lib/service_config/service_config_parser.h" @@ -43,14 +44,6 @@ namespace internal { class ClientChannelGlobalParsedConfig : public ServiceConfigParser::ParsedConfig { public: - ClientChannelGlobalParsedConfig( - RefCountedPtr parsed_lb_config, - std::string parsed_deprecated_lb_policy, - absl::optional health_check_service_name) - : parsed_lb_config_(std::move(parsed_lb_config)), - parsed_deprecated_lb_policy_(std::move(parsed_deprecated_lb_policy)), - health_check_service_name_(std::move(health_check_service_name)) {} - RefCountedPtr parsed_lb_config() const { return parsed_lb_config_; } @@ -60,26 +53,34 @@ class ClientChannelGlobalParsedConfig } const absl::optional& health_check_service_name() const { - return health_check_service_name_; + return health_check_config_.service_name; } + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + void JsonPostLoad(const Json& json, const JsonArgs&, + ValidationErrors* errors); + private: + struct HealthCheckConfig { + absl::optional service_name; + + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + }; + RefCountedPtr parsed_lb_config_; std::string parsed_deprecated_lb_policy_; - absl::optional health_check_service_name_; + HealthCheckConfig health_check_config_; }; class ClientChannelMethodParsedConfig : public ServiceConfigParser::ParsedConfig { public: - ClientChannelMethodParsedConfig(Duration timeout, - const absl::optional& wait_for_ready) - : timeout_(timeout), wait_for_ready_(wait_for_ready) {} - Duration timeout() const { return timeout_; } absl::optional wait_for_ready() const { return wait_for_ready_; } + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + private: Duration timeout_; absl::optional wait_for_ready_; @@ -89,11 +90,13 @@ class ClientChannelServiceConfigParser : public ServiceConfigParser::Parser { public: absl::string_view name() const override { return parser_name(); } - absl::StatusOr> - ParseGlobalParams(const ChannelArgs& /*args*/, const Json& json) override; + std::unique_ptr ParseGlobalParams( + const ChannelArgs& /*args*/, const Json& json, + ValidationErrors* errors) override; - absl::StatusOr> - ParsePerMethodParams(const ChannelArgs& /*args*/, const Json& json) override; + std::unique_ptr ParsePerMethodParams( + const ChannelArgs& /*args*/, const Json& json, + ValidationErrors* errors) override; static size_t ParserIndex(); static void Register(CoreConfiguration::Builder* builder); @@ -105,4 +108,4 @@ class ClientChannelServiceConfigParser : public ServiceConfigParser::Parser { } // namespace internal } // namespace grpc_core -#endif // GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_RESULT_PARSING_H +#endif // GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_SERVICE_CONFIG_H diff --git a/src/core/ext/filters/client_channel/lb_policy/rls/rls.cc b/src/core/ext/filters/client_channel/lb_policy/rls/rls.cc index 28e7e59cddd..98976c9dcf5 100644 --- a/src/core/ext/filters/client_channel/lb_policy/rls/rls.cc +++ b/src/core/ext/filters/client_channel/lb_policy/rls/rls.cc @@ -2426,13 +2426,8 @@ void RlsLbConfig::JsonPostLoad(const Json& json, const JsonArgs&, if (it != json.object_value().end()) { ValidationErrors::ScopedField field(errors, ".routeLookupChannelServiceConfig"); - grpc_error_handle child_error; - rls_channel_service_config_ = it->second.Dump(); - auto service_config = MakeRefCounted( - ChannelArgs(), rls_channel_service_config_, it->second, &child_error); - if (!child_error.ok()) { - errors->AddError(StatusToString(child_error)); - } + // Don't need to save the result here, just need the errors (if any). + ServiceConfigImpl::Create(ChannelArgs(), it->second, errors); } // Validate childPolicyConfigTargetFieldName. { diff --git a/src/core/ext/filters/client_channel/resolver_result_parsing.cc b/src/core/ext/filters/client_channel/resolver_result_parsing.cc deleted file mode 100644 index a0a9f939972..00000000000 --- a/src/core/ext/filters/client_channel/resolver_result_parsing.cc +++ /dev/null @@ -1,186 +0,0 @@ -// -// Copyright 2018 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#include - -#include "src/core/ext/filters/client_channel/resolver_result_parsing.h" - -#include - -#include -#include -#include - -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/str_cat.h" -#include "absl/types/optional.h" - -#include - -#include "src/core/lib/gprpp/status_helper.h" -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/json/json_util.h" -#include "src/core/lib/load_balancing/lb_policy_registry.h" - -// As per the retry design, we do not allow more than 5 retry attempts. -#define MAX_MAX_RETRY_ATTEMPTS 5 - -namespace grpc_core { -namespace internal { - -size_t ClientChannelServiceConfigParser::ParserIndex() { - return CoreConfiguration::Get().service_config_parser().GetParserIndex( - parser_name()); -} - -void ClientChannelServiceConfigParser::Register( - CoreConfiguration::Builder* builder) { - builder->service_config_parser()->RegisterParser( - std::make_unique()); -} - -namespace { - -absl::optional ParseHealthCheckConfig(const Json& field, - grpc_error_handle* error) { - GPR_DEBUG_ASSERT(error != nullptr && error->ok()); - if (field.type() != Json::Type::OBJECT) { - *error = GRPC_ERROR_CREATE( - "field:healthCheckConfig error:should be of type object"); - return absl::nullopt; - } - std::vector error_list; - absl::optional service_name; - auto it = field.object_value().find("serviceName"); - if (it != field.object_value().end()) { - if (it->second.type() != Json::Type::STRING) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:serviceName error:should be of type string")); - } else { - service_name = it->second.string_value(); - } - } - *error = - GRPC_ERROR_CREATE_FROM_VECTOR("field:healthCheckConfig", &error_list); - return service_name; -} - -} // namespace - -absl::StatusOr> -ClientChannelServiceConfigParser::ParseGlobalParams(const ChannelArgs& /*args*/, - const Json& json) { - std::vector error_list; - const auto& lb_policy_registry = - CoreConfiguration::Get().lb_policy_registry(); - // Parse LB config. - RefCountedPtr parsed_lb_config; - auto it = json.object_value().find("loadBalancingConfig"); - if (it != json.object_value().end()) { - auto config = lb_policy_registry.ParseLoadBalancingConfig(it->second); - if (!config.ok()) { - error_list.push_back(GRPC_ERROR_CREATE(absl::StrCat( - "field:loadBalancingConfig error:", config.status().message()))); - } else { - parsed_lb_config = std::move(*config); - } - } - // Parse deprecated LB policy. - std::string lb_policy_name; - it = json.object_value().find("loadBalancingPolicy"); - if (it != json.object_value().end()) { - if (it->second.type() != Json::Type::STRING) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:loadBalancingPolicy error:type should be string")); - } else { - lb_policy_name = it->second.string_value(); - for (size_t i = 0; i < lb_policy_name.size(); ++i) { - lb_policy_name[i] = tolower(lb_policy_name[i]); - } - bool requires_config = false; - if (!lb_policy_registry.LoadBalancingPolicyExists(lb_policy_name.c_str(), - &requires_config)) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:loadBalancingPolicy error:Unknown lb policy")); - } else if (requires_config) { - error_list.push_back(GRPC_ERROR_CREATE( - absl::StrCat("field:loadBalancingPolicy error:", lb_policy_name, - " requires a config. Please use loadBalancingConfig " - "instead."))); - } - } - } - // Parse health check config. - absl::optional health_check_service_name; - it = json.object_value().find("healthCheckConfig"); - if (it != json.object_value().end()) { - grpc_error_handle parsing_error; - health_check_service_name = - ParseHealthCheckConfig(it->second, &parsing_error); - if (!parsing_error.ok()) { - error_list.push_back(parsing_error); - } - } - if (!error_list.empty()) { - grpc_error_handle error = GRPC_ERROR_CREATE_FROM_VECTOR( - "Client channel global parser", &error_list); - absl::Status status = absl::InvalidArgumentError( - absl::StrCat("error parsing client channel global parameters: ", - StatusToString(error))); - return status; - } - return std::make_unique( - std::move(parsed_lb_config), std::move(lb_policy_name), - std::move(health_check_service_name)); -} - -absl::StatusOr> -ClientChannelServiceConfigParser::ParsePerMethodParams( - const ChannelArgs& /*args*/, const Json& json) { - std::vector error_list; - // Parse waitForReady. - absl::optional wait_for_ready; - auto it = json.object_value().find("waitForReady"); - if (it != json.object_value().end()) { - if (it->second.type() == Json::Type::JSON_TRUE) { - wait_for_ready.emplace(true); - } else if (it->second.type() == Json::Type::JSON_FALSE) { - wait_for_ready.emplace(false); - } else { - error_list.push_back(GRPC_ERROR_CREATE( - "field:waitForReady error:Type should be true/false")); - } - } - // Parse timeout. - Duration timeout; - ParseJsonObjectFieldAsDuration(json.object_value(), "timeout", &timeout, - &error_list, false); - // Return result. - if (!error_list.empty()) { - grpc_error_handle error = - GRPC_ERROR_CREATE_FROM_VECTOR("Client channel parser", &error_list); - absl::Status status = absl::InvalidArgumentError( - absl::StrCat("error parsing client channel method parameters: ", - StatusToString(error))); - return status; - } - return std::make_unique(timeout, - wait_for_ready); -} - -} // namespace internal -} // namespace grpc_core diff --git a/src/core/ext/filters/client_channel/retry_service_config.cc b/src/core/ext/filters/client_channel/retry_service_config.cc index 44f877a6c3b..6df853a4c67 100644 --- a/src/core/ext/filters/client_channel/retry_service_config.cc +++ b/src/core/ext/filters/client_channel/retry_service_config.cc @@ -18,16 +18,13 @@ #include "src/core/ext/filters/client_channel/retry_service_config.h" -#include -#include - -#include +#include #include #include #include #include -#include "absl/status/status.h" +#include "absl/strings/numbers.h" #include "absl/strings/str_cat.h" #include "absl/types/optional.h" @@ -38,10 +35,7 @@ #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/status_util.h" #include "src/core/lib/config/core_configuration.h" -#include "src/core/lib/gpr/string.h" -#include "src/core/lib/gprpp/status_helper.h" -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/json/json_util.h" +#include "src/core/lib/json/json_channel_args.h" // As per the retry design, we do not allow more than 5 retry attempts. #define MAX_MAX_RETRY_ATTEMPTS 5 @@ -49,271 +43,240 @@ namespace grpc_core { namespace internal { -size_t RetryServiceConfigParser::ParserIndex() { - return CoreConfiguration::Get().service_config_parser().GetParserIndex( - parser_name()); -} +// +// RetryGlobalConfig +// -void RetryServiceConfigParser::Register(CoreConfiguration::Builder* builder) { - builder->service_config_parser()->RegisterParser( - std::make_unique()); +const JsonLoaderInterface* RetryGlobalConfig::JsonLoader(const JsonArgs&) { + // Note: Both fields require custom processing, so they're handled in + // JsonPostLoad() instead. + static const auto* loader = JsonObjectLoader().Finish(); + return loader; } -namespace { - -grpc_error_handle ParseRetryThrottling(const Json& json, - intptr_t* max_milli_tokens, - intptr_t* milli_token_ratio) { - if (json.type() != Json::Type::OBJECT) { - return GRPC_ERROR_CREATE( - "field:retryThrottling error:Type should be object"); - } - std::vector error_list; +void RetryGlobalConfig::JsonPostLoad(const Json& json, const JsonArgs& args, + ValidationErrors* errors) { // Parse maxTokens. - auto it = json.object_value().find("maxTokens"); - if (it == json.object_value().end()) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:retryThrottling field:maxTokens error:Not found")); - } else if (it->second.type() != Json::Type::NUMBER) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:retryThrottling field:maxTokens error:Type should be " - "number")); - } else { - *max_milli_tokens = - gpr_parse_nonnegative_int(it->second.string_value().c_str()) * 1000; - if (*max_milli_tokens <= 0) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:retryThrottling field:maxTokens error:should be " - "greater than zero")); + auto max_tokens = LoadJsonObjectField(json.object_value(), args, + "maxTokens", errors); + if (max_tokens.has_value()) { + ValidationErrors::ScopedField field(errors, ".maxTokens"); + if (*max_tokens == 0) { + errors->AddError("must be greater than 0"); + } else { + // Multiply by 1000 to represent as milli-tokens. + max_milli_tokens_ = static_cast(*max_tokens) * 1000; } } // Parse tokenRatio. - it = json.object_value().find("tokenRatio"); + ValidationErrors::ScopedField field(errors, ".tokenRatio"); + auto it = json.object_value().find("tokenRatio"); if (it == json.object_value().end()) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:retryThrottling field:tokenRatio error:Not found")); - } else if (it->second.type() != Json::Type::NUMBER) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:retryThrottling field:tokenRatio error:type should be " - "number")); - } else { + errors->AddError("field not present"); + return; + } + if (it->second.type() != Json::Type::NUMBER && + it->second.type() != Json::Type::STRING) { + errors->AddError("is not a number"); + return; + } + absl::string_view buf = it->second.string_value(); + uint32_t multiplier = 1; + uint32_t decimal_value = 0; + auto decimal_point = buf.find('.'); + if (decimal_point != absl::string_view::npos) { + absl::string_view after_decimal = buf.substr(decimal_point + 1); + buf = buf.substr(0, decimal_point); // We support up to 3 decimal digits. - size_t whole_len = it->second.string_value().size(); - const char* value = it->second.string_value().c_str(); - uint32_t multiplier = 1; - uint32_t decimal_value = 0; - const char* decimal_point = strchr(value, '.'); - if (decimal_point != nullptr) { - whole_len = static_cast(decimal_point - value); - multiplier = 1000; - size_t decimal_len = strlen(decimal_point + 1); - if (decimal_len > 3) decimal_len = 3; - if (!gpr_parse_bytes_to_uint32(decimal_point + 1, decimal_len, - &decimal_value)) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:retryThrottling field:tokenRatio error:Failed " - "parsing")); - return GRPC_ERROR_CREATE_FROM_VECTOR("retryThrottling", &error_list); - } - uint32_t decimal_multiplier = 1; - for (size_t i = 0; i < (3 - decimal_len); ++i) { - decimal_multiplier *= 10; - } - decimal_value *= decimal_multiplier; - } - uint32_t whole_value; - if (!gpr_parse_bytes_to_uint32(value, whole_len, &whole_value)) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:retryThrottling field:tokenRatio error:Failed " - "parsing")); - return GRPC_ERROR_CREATE_FROM_VECTOR("retryThrottling", &error_list); + multiplier = 1000; + if (after_decimal.length() > 3) after_decimal = after_decimal.substr(0, 3); + // Parse decimal value. + if (!absl::SimpleAtoi(after_decimal, &decimal_value)) { + errors->AddError("could not parse as a number"); + return; } - *milli_token_ratio = - static_cast((whole_value * multiplier) + decimal_value); - if (*milli_token_ratio <= 0) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:retryThrottling field:tokenRatio error:value should " - "be greater than 0")); + uint32_t decimal_multiplier = 1; + for (size_t i = 0; i < (3 - after_decimal.length()); ++i) { + decimal_multiplier *= 10; } + decimal_value *= decimal_multiplier; + } + uint32_t whole_value; + if (!absl::SimpleAtoi(buf, &whole_value)) { + errors->AddError("could not parse as a number"); + return; + } + milli_token_ratio_ = + static_cast((whole_value * multiplier) + decimal_value); + if (milli_token_ratio_ <= 0) { + errors->AddError("must be greater than 0"); } - return GRPC_ERROR_CREATE_FROM_VECTOR("retryThrottling", &error_list); } -} // namespace +// +// RetryMethodConfig +// -absl::StatusOr> -RetryServiceConfigParser::ParseGlobalParams(const ChannelArgs& /*args*/, - const Json& json) { - auto it = json.object_value().find("retryThrottling"); - if (it == json.object_value().end()) return nullptr; - intptr_t max_milli_tokens = 0; - intptr_t milli_token_ratio = 0; - grpc_error_handle error = - ParseRetryThrottling(it->second, &max_milli_tokens, &milli_token_ratio); - if (!error.ok()) { - absl::Status status = absl::InvalidArgumentError(absl::StrCat( - "error parsing retry global parameters: ", StatusToString(error))); - return status; - } - return std::make_unique(max_milli_tokens, - milli_token_ratio); +const JsonLoaderInterface* RetryMethodConfig::JsonLoader(const JsonArgs&) { + static const auto* loader = + JsonObjectLoader() + // Note: The "retryableStatusCodes" field requires custom parsing, + // so it's handled in JsonPostLoad() instead. + .Field("maxAttempts", &RetryMethodConfig::max_attempts_) + .Field("initialBackoff", &RetryMethodConfig::initial_backoff_) + .Field("maxBackoff", &RetryMethodConfig::max_backoff_) + .Field("backoffMultiplier", &RetryMethodConfig::backoff_multiplier_) + .OptionalField("perAttemptRecvTimeout", + &RetryMethodConfig::per_attempt_recv_timeout_, + GRPC_ARG_EXPERIMENTAL_ENABLE_HEDGING) + .Finish(); + return loader; } -namespace { - -grpc_error_handle ParseRetryPolicy( - const ChannelArgs& args, const Json& json, int* max_attempts, - Duration* initial_backoff, Duration* max_backoff, float* backoff_multiplier, - StatusCodeSet* retryable_status_codes, - absl::optional* per_attempt_recv_timeout) { - if (json.type() != Json::Type::OBJECT) { - return GRPC_ERROR_CREATE( - "field:retryPolicy error:should be of type object"); - } - std::vector error_list; - // Parse maxAttempts. - auto it = json.object_value().find("maxAttempts"); - if (it == json.object_value().end()) { - error_list.push_back( - GRPC_ERROR_CREATE("field:maxAttempts error:required field missing")); - } else { - if (it->second.type() != Json::Type::NUMBER) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:maxAttempts error:should be of type number")); - } else { - *max_attempts = - gpr_parse_nonnegative_int(it->second.string_value().c_str()); - if (*max_attempts <= 1) { - error_list.push_back( - GRPC_ERROR_CREATE("field:maxAttempts error:should be at least 2")); - } else if (*max_attempts > MAX_MAX_RETRY_ATTEMPTS) { +void RetryMethodConfig::JsonPostLoad(const Json& json, const JsonArgs& args, + ValidationErrors* errors) { + // Validate maxAttempts. + { + ValidationErrors::ScopedField field(errors, ".maxAttempts"); + if (!errors->FieldHasErrors()) { + if (max_attempts_ <= 1) { + errors->AddError("must be at least 2"); + } else if (max_attempts_ > MAX_MAX_RETRY_ATTEMPTS) { gpr_log(GPR_ERROR, "service config: clamped retryPolicy.maxAttempts at %d", MAX_MAX_RETRY_ATTEMPTS); - *max_attempts = MAX_MAX_RETRY_ATTEMPTS; + max_attempts_ = MAX_MAX_RETRY_ATTEMPTS; } } } - // Parse initialBackoff. - if (ParseJsonObjectFieldAsDuration(json.object_value(), "initialBackoff", - initial_backoff, &error_list) && - *initial_backoff == Duration::Zero()) { - error_list.push_back( - GRPC_ERROR_CREATE("field:initialBackoff error:must be greater than 0")); + // Validate initialBackoff. + { + ValidationErrors::ScopedField field(errors, ".initialBackoff"); + if (!errors->FieldHasErrors() && initial_backoff_ == Duration::Zero()) { + errors->AddError("must be greater than 0"); + } } - // Parse maxBackoff. - if (ParseJsonObjectFieldAsDuration(json.object_value(), "maxBackoff", - max_backoff, &error_list) && - *max_backoff == Duration::Zero()) { - error_list.push_back( - GRPC_ERROR_CREATE("field:maxBackoff error:must be greater than 0")); + // Validate maxBackoff. + { + ValidationErrors::ScopedField field(errors, ".maxBackoff"); + if (!errors->FieldHasErrors() && max_backoff_ == Duration::Zero()) { + errors->AddError("must be greater than 0"); + } } - // Parse backoffMultiplier. - it = json.object_value().find("backoffMultiplier"); - if (it == json.object_value().end()) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:backoffMultiplier error:required field missing")); - } else { - if (it->second.type() != Json::Type::NUMBER) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:backoffMultiplier error:should be of type number")); - } else { - if (sscanf(it->second.string_value().c_str(), "%f", backoff_multiplier) != - 1) { - error_list.push_back( - GRPC_ERROR_CREATE("field:backoffMultiplier error:failed to parse")); - } else if (*backoff_multiplier <= 0) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:backoffMultiplier error:must be greater than 0")); - } + // Validate backoffMultiplier. + { + ValidationErrors::ScopedField field(errors, ".backoffMultiplier"); + if (!errors->FieldHasErrors() && backoff_multiplier_ <= 0) { + errors->AddError("must be greater than 0"); } } // Parse retryableStatusCodes. - it = json.object_value().find("retryableStatusCodes"); - if (it != json.object_value().end()) { - if (it->second.type() != Json::Type::ARRAY) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:retryableStatusCodes error:must be of type array")); - } else { - for (const Json& element : it->second.array_value()) { - if (element.type() != Json::Type::STRING) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:retryableStatusCodes error:status codes should be of type " - "string")); - continue; - } - grpc_status_code status; - if (!grpc_status_code_from_string(element.string_value().c_str(), - &status)) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:retryableStatusCodes error:failed to parse status code")); - continue; - } - retryable_status_codes->Add(status); + auto status_code_list = LoadJsonObjectField>( + json.object_value(), args, "retryableStatusCodes", errors, + /*required=*/false); + if (status_code_list.has_value()) { + for (size_t i = 0; i < status_code_list->size(); ++i) { + ValidationErrors::ScopedField field( + errors, absl::StrCat(".retryableStatusCodes[", i, "]")); + grpc_status_code status; + if (!grpc_status_code_from_string((*status_code_list)[i].c_str(), + &status)) { + errors->AddError("failed to parse status code"); + } else { + retryable_status_codes_.Add(status); } } } - // Parse perAttemptRecvTimeout. - if (args.GetBool(GRPC_ARG_EXPERIMENTAL_ENABLE_HEDGING).value_or(false)) { - it = json.object_value().find("perAttemptRecvTimeout"); - if (it != json.object_value().end()) { - Duration per_attempt_recv_timeout_value; - if (!ParseDurationFromJson(it->second, &per_attempt_recv_timeout_value)) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:perAttemptRecvTimeout error:type must be STRING of the " - "form given by google.proto.Duration.")); - } else { - *per_attempt_recv_timeout = per_attempt_recv_timeout_value; - // TODO(roth): As part of implementing hedging, relax this check such - // that we allow a value of 0 if a hedging policy is specified. - if (per_attempt_recv_timeout_value == Duration::Zero()) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:perAttemptRecvTimeout error:must be greater than 0")); - } + // Validate perAttemptRecvTimeout. + if (args.IsEnabled(GRPC_ARG_EXPERIMENTAL_ENABLE_HEDGING)) { + if (per_attempt_recv_timeout_.has_value()) { + ValidationErrors::ScopedField field(errors, ".perAttemptRecvTimeout"); + // TODO(roth): As part of implementing hedging, relax this check such + // that we allow a value of 0 if a hedging policy is specified. + if (!errors->FieldHasErrors() && + *per_attempt_recv_timeout_ == Duration::Zero()) { + errors->AddError("must be greater than 0"); } - } else if (retryable_status_codes->Empty()) { + } else if (retryable_status_codes_.Empty()) { // If perAttemptRecvTimeout not present, retryableStatusCodes must be // non-empty. - error_list.push_back(GRPC_ERROR_CREATE( - "field:retryableStatusCodes error:must be non-empty if " - "perAttemptRecvTimeout not present")); + ValidationErrors::ScopedField field(errors, ".retryableStatusCodes"); + if (!errors->FieldHasErrors()) { + errors->AddError( + "must be non-empty if perAttemptRecvTimeout not present"); + } } - } else { + } else if (retryable_status_codes_.Empty()) { // Hedging not enabled, so the error message for // retryableStatusCodes unset should be different. - if (retryable_status_codes->Empty()) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:retryableStatusCodes error:must be non-empty")); + ValidationErrors::ScopedField field(errors, ".retryableStatusCodes"); + if (!errors->FieldHasErrors()) { + errors->AddError("must be non-empty"); } } - return GRPC_ERROR_CREATE_FROM_VECTOR("retryPolicy", &error_list); } +// +// RetryServiceConfigParser +// + +size_t RetryServiceConfigParser::ParserIndex() { + return CoreConfiguration::Get().service_config_parser().GetParserIndex( + parser_name()); +} + +void RetryServiceConfigParser::Register(CoreConfiguration::Builder* builder) { + builder->service_config_parser()->RegisterParser( + std::make_unique()); +} + +namespace { + +struct GlobalConfig { + std::unique_ptr retry_throttling; + + static const JsonLoaderInterface* JsonLoader(const JsonArgs&) { + static const auto* loader = + JsonObjectLoader() + .OptionalField("retryThrottling", &GlobalConfig::retry_throttling) + .Finish(); + return loader; + } +}; + } // namespace -absl::StatusOr> -RetryServiceConfigParser::ParsePerMethodParams(const ChannelArgs& args, - const Json& json) { - // Parse retry policy. - auto it = json.object_value().find("retryPolicy"); - if (it == json.object_value().end()) return nullptr; - int max_attempts = 0; - Duration initial_backoff; - Duration max_backoff; - float backoff_multiplier = 0; - StatusCodeSet retryable_status_codes; - absl::optional per_attempt_recv_timeout; - grpc_error_handle error = ParseRetryPolicy( - args, it->second, &max_attempts, &initial_backoff, &max_backoff, - &backoff_multiplier, &retryable_status_codes, &per_attempt_recv_timeout); - if (!error.ok()) { - absl::Status status = absl::InvalidArgumentError(absl::StrCat( - "error parsing retry method parameters: ", StatusToString(error))); - return status; +std::unique_ptr +RetryServiceConfigParser::ParseGlobalParams(const ChannelArgs& /*args*/, + const Json& json, + ValidationErrors* errors) { + auto global_params = LoadFromJson(json, JsonArgs(), errors); + return std::move(global_params.retry_throttling); +} + +namespace { + +struct MethodConfig { + std::unique_ptr retry_policy; + + static const JsonLoaderInterface* JsonLoader(const JsonArgs&) { + static const auto* loader = + JsonObjectLoader() + .OptionalField("retryPolicy", &MethodConfig::retry_policy) + .Finish(); + return loader; } - return std::make_unique( - max_attempts, initial_backoff, max_backoff, backoff_multiplier, - retryable_status_codes, per_attempt_recv_timeout); +}; + +} // namespace + +std::unique_ptr +RetryServiceConfigParser::ParsePerMethodParams(const ChannelArgs& args, + const Json& json, + ValidationErrors* errors) { + auto method_params = + LoadFromJson(json, JsonChannelArgs(args), errors); + return std::move(method_params.retry_policy); } } // namespace internal diff --git a/src/core/ext/filters/client_channel/retry_service_config.h b/src/core/ext/filters/client_channel/retry_service_config.h index 6fdcdb2d055..8eb87847795 100644 --- a/src/core/ext/filters/client_channel/retry_service_config.h +++ b/src/core/ext/filters/client_channel/retry_service_config.h @@ -24,7 +24,6 @@ #include -#include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" @@ -32,7 +31,10 @@ #include "src/core/lib/channel/status_util.h" #include "src/core/lib/config/core_configuration.h" #include "src/core/lib/gprpp/time.h" +#include "src/core/lib/gprpp/validation_errors.h" #include "src/core/lib/json/json.h" +#include "src/core/lib/json/json_args.h" +#include "src/core/lib/json/json_object_loader.h" #include "src/core/lib/service_config/service_config_parser.h" namespace grpc_core { @@ -40,31 +42,20 @@ namespace internal { class RetryGlobalConfig : public ServiceConfigParser::ParsedConfig { public: - RetryGlobalConfig(intptr_t max_milli_tokens, intptr_t milli_token_ratio) - : max_milli_tokens_(max_milli_tokens), - milli_token_ratio_(milli_token_ratio) {} + uintptr_t max_milli_tokens() const { return max_milli_tokens_; } + uintptr_t milli_token_ratio() const { return milli_token_ratio_; } - intptr_t max_milli_tokens() const { return max_milli_tokens_; } - intptr_t milli_token_ratio() const { return milli_token_ratio_; } + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + void JsonPostLoad(const Json& json, const JsonArgs& args, + ValidationErrors* errors); private: - intptr_t max_milli_tokens_ = 0; - intptr_t milli_token_ratio_ = 0; + uintptr_t max_milli_tokens_ = 0; + uintptr_t milli_token_ratio_ = 0; }; class RetryMethodConfig : public ServiceConfigParser::ParsedConfig { public: - RetryMethodConfig(int max_attempts, Duration initial_backoff, - Duration max_backoff, float backoff_multiplier, - StatusCodeSet retryable_status_codes, - absl::optional per_attempt_recv_timeout) - : max_attempts_(max_attempts), - initial_backoff_(initial_backoff), - max_backoff_(max_backoff), - backoff_multiplier_(backoff_multiplier), - retryable_status_codes_(retryable_status_codes), - per_attempt_recv_timeout_(per_attempt_recv_timeout) {} - int max_attempts() const { return max_attempts_; } Duration initial_backoff() const { return initial_backoff_; } Duration max_backoff() const { return max_backoff_; } @@ -76,6 +67,10 @@ class RetryMethodConfig : public ServiceConfigParser::ParsedConfig { return per_attempt_recv_timeout_; } + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + void JsonPostLoad(const Json& json, const JsonArgs& args, + ValidationErrors* errors); + private: int max_attempts_ = 0; Duration initial_backoff_; @@ -89,11 +84,13 @@ class RetryServiceConfigParser : public ServiceConfigParser::Parser { public: absl::string_view name() const override { return parser_name(); } - absl::StatusOr> - ParseGlobalParams(const ChannelArgs& /*args*/, const Json& json) override; + std::unique_ptr ParseGlobalParams( + const ChannelArgs& /*args*/, const Json& json, + ValidationErrors* errors) override; - absl::StatusOr> - ParsePerMethodParams(const ChannelArgs& args, const Json& json) override; + std::unique_ptr ParsePerMethodParams( + const ChannelArgs& args, const Json& json, + ValidationErrors* errors) override; static size_t ParserIndex(); static void Register(CoreConfiguration::Builder* builder); diff --git a/src/core/ext/filters/client_channel/retry_throttle.cc b/src/core/ext/filters/client_channel/retry_throttle.cc index 671ea3f634d..7108c193881 100644 --- a/src/core/ext/filters/client_channel/retry_throttle.cc +++ b/src/core/ext/filters/client_channel/retry_throttle.cc @@ -34,22 +34,22 @@ namespace internal { // ServerRetryThrottleData::ServerRetryThrottleData( - intptr_t max_milli_tokens, intptr_t milli_token_ratio, + uintptr_t max_milli_tokens, uintptr_t milli_token_ratio, ServerRetryThrottleData* old_throttle_data) : max_milli_tokens_(max_milli_tokens), milli_token_ratio_(milli_token_ratio) { - intptr_t initial_milli_tokens = max_milli_tokens; + uintptr_t initial_milli_tokens = max_milli_tokens; // If there was a pre-existing entry for this server name, initialize // the token count by scaling proportionately to the old data. This // ensures that if we're already throttling retries on the old scale, // we will start out doing the same thing on the new one. if (old_throttle_data != nullptr) { double token_fraction = - static_cast( + static_cast( gpr_atm_acq_load(&old_throttle_data->milli_tokens_)) / static_cast(old_throttle_data->max_milli_tokens_); initial_milli_tokens = - static_cast(token_fraction * max_milli_tokens); + static_cast(token_fraction * max_milli_tokens); } gpr_atm_rel_store(&milli_tokens_, static_cast(initial_milli_tokens)); // If there was a pre-existing entry, mark it as stale and give it a @@ -86,8 +86,8 @@ bool ServerRetryThrottleData::RecordFailure() { ServerRetryThrottleData* throttle_data = this; GetReplacementThrottleDataIfNeeded(&throttle_data); // We decrement milli_tokens by 1000 (1 token) for each failure. - const intptr_t new_value = - static_cast(gpr_atm_no_barrier_clamped_add( + const uintptr_t new_value = + static_cast(gpr_atm_no_barrier_clamped_add( &throttle_data->milli_tokens_, static_cast(-1000), static_cast(0), static_cast(throttle_data->max_milli_tokens_))); @@ -118,8 +118,8 @@ ServerRetryThrottleMap* ServerRetryThrottleMap::Get() { } RefCountedPtr ServerRetryThrottleMap::GetDataForServer( - const std::string& server_name, intptr_t max_milli_tokens, - intptr_t milli_token_ratio) { + const std::string& server_name, uintptr_t max_milli_tokens, + uintptr_t milli_token_ratio) { MutexLock lock(&mu_); auto it = map_.find(server_name); ServerRetryThrottleData* throttle_data = diff --git a/src/core/ext/filters/client_channel/retry_throttle.h b/src/core/ext/filters/client_channel/retry_throttle.h index 195277de07b..62464c09894 100644 --- a/src/core/ext/filters/client_channel/retry_throttle.h +++ b/src/core/ext/filters/client_channel/retry_throttle.h @@ -40,7 +40,8 @@ namespace internal { /// Tracks retry throttling data for an individual server name. class ServerRetryThrottleData : public RefCounted { public: - ServerRetryThrottleData(intptr_t max_milli_tokens, intptr_t milli_token_ratio, + ServerRetryThrottleData(uintptr_t max_milli_tokens, + uintptr_t milli_token_ratio, ServerRetryThrottleData* old_throttle_data); ~ServerRetryThrottleData() override; @@ -50,15 +51,15 @@ class ServerRetryThrottleData : public RefCounted { /// Records a success. void RecordSuccess(); - intptr_t max_milli_tokens() const { return max_milli_tokens_; } - intptr_t milli_token_ratio() const { return milli_token_ratio_; } + uintptr_t max_milli_tokens() const { return max_milli_tokens_; } + uintptr_t milli_token_ratio() const { return milli_token_ratio_; } private: void GetReplacementThrottleDataIfNeeded( ServerRetryThrottleData** throttle_data); - const intptr_t max_milli_tokens_; - const intptr_t milli_token_ratio_; + const uintptr_t max_milli_tokens_; + const uintptr_t milli_token_ratio_; gpr_atm milli_tokens_; // A pointer to the replacement for this ServerRetryThrottleData entry. // If non-nullptr, then this entry is stale and must not be used. @@ -74,8 +75,8 @@ class ServerRetryThrottleMap { /// Returns the failure data for \a server_name, creating a new entry if /// needed. RefCountedPtr GetDataForServer( - const std::string& server_name, intptr_t max_milli_tokens, - intptr_t milli_token_ratio); + const std::string& server_name, uintptr_t max_milli_tokens, + uintptr_t milli_token_ratio); private: using StringToDataMap = diff --git a/src/core/ext/filters/fault_injection/fault_injection_filter.cc b/src/core/ext/filters/fault_injection/fault_injection_filter.cc index 567b5ab1a6b..95ea8c7e50b 100644 --- a/src/core/ext/filters/fault_injection/fault_injection_filter.cc +++ b/src/core/ext/filters/fault_injection/fault_injection_filter.cc @@ -35,7 +35,7 @@ #include #include -#include "src/core/ext/filters/fault_injection/service_config_parser.h" +#include "src/core/ext/filters/fault_injection/fault_injection_service_config_parser.h" #include "src/core/lib/channel/channel_stack.h" #include "src/core/lib/channel/context.h" #include "src/core/lib/channel/status_util.h" diff --git a/src/core/ext/filters/fault_injection/fault_injection_service_config_parser.cc b/src/core/ext/filters/fault_injection/fault_injection_service_config_parser.cc new file mode 100644 index 00000000000..cf7352957b0 --- /dev/null +++ b/src/core/ext/filters/fault_injection/fault_injection_service_config_parser.cc @@ -0,0 +1,118 @@ +// +// Copyright 2021 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include + +#include "src/core/ext/filters/fault_injection/fault_injection_service_config_parser.h" + +#include + +#include "absl/types/optional.h" + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/status_util.h" + +namespace grpc_core { + +const JsonLoaderInterface* +FaultInjectionMethodParsedConfig::FaultInjectionPolicy::JsonLoader( + const JsonArgs&) { + static const auto* loader = + JsonObjectLoader() + .OptionalField("abortMessage", &FaultInjectionPolicy::abort_message) + .OptionalField("abortCodeHeader", + &FaultInjectionPolicy::abort_code_header) + .OptionalField("abortPercentageHeader", + &FaultInjectionPolicy::abort_percentage_header) + .OptionalField("abortPercentageNumerator", + &FaultInjectionPolicy::abort_percentage_numerator) + .OptionalField("abortPercentageDenominator", + &FaultInjectionPolicy::abort_percentage_denominator) + .OptionalField("delay", &FaultInjectionPolicy::delay) + .OptionalField("delayHeader", &FaultInjectionPolicy::delay_header) + .OptionalField("delayPercentageHeader", + &FaultInjectionPolicy::delay_percentage_header) + .OptionalField("delayPercentageNumerator", + &FaultInjectionPolicy::delay_percentage_numerator) + .OptionalField("delayPercentageDenominator", + &FaultInjectionPolicy::delay_percentage_denominator) + .OptionalField("maxFaults", &FaultInjectionPolicy::max_faults) + .Finish(); + return loader; +} + +void FaultInjectionMethodParsedConfig::FaultInjectionPolicy::JsonPostLoad( + const Json& json, const JsonArgs& args, ValidationErrors* errors) { + // Parse abort_code. + auto abort_code_string = LoadJsonObjectField( + json.object_value(), args, "abortCode", errors, /*required=*/false); + if (abort_code_string.has_value() && + !grpc_status_code_from_string(abort_code_string->c_str(), &abort_code)) { + ValidationErrors::ScopedField field(errors, ".abortCode"); + errors->AddError("failed to parse status code"); + } + // Validate abort_percentage_denominator. + if (abort_percentage_denominator != 100 && + abort_percentage_denominator != 10000 && + abort_percentage_denominator != 1000000) { + ValidationErrors::ScopedField field(errors, ".abortPercentageDenominator"); + errors->AddError("must be one of 100, 10000, or 1000000"); + } + // Validate delay_percentage_denominator. + if (delay_percentage_denominator != 100 && + delay_percentage_denominator != 10000 && + delay_percentage_denominator != 1000000) { + ValidationErrors::ScopedField field(errors, ".delayPercentageDenominator"); + errors->AddError("must be one of 100, 10000, or 1000000"); + } +} + +const JsonLoaderInterface* FaultInjectionMethodParsedConfig::JsonLoader( + const JsonArgs&) { + static const auto* loader = + JsonObjectLoader() + .OptionalField( + "faultInjectionPolicy", + &FaultInjectionMethodParsedConfig::fault_injection_policies_) + .Finish(); + return loader; +} + +std::unique_ptr +FaultInjectionServiceConfigParser::ParsePerMethodParams( + const ChannelArgs& args, const Json& json, ValidationErrors* errors) { + // Only parse fault injection policy if the following channel arg is present. + if (!args.GetBool(GRPC_ARG_PARSE_FAULT_INJECTION_METHOD_CONFIG) + .value_or(false)) { + return nullptr; + } + // Parse fault injection policy from given Json + return LoadFromJson>( + json, JsonArgs(), errors); +} + +void FaultInjectionServiceConfigParser::Register( + CoreConfiguration::Builder* builder) { + builder->service_config_parser()->RegisterParser( + std::make_unique()); +} + +size_t FaultInjectionServiceConfigParser::ParserIndex() { + return CoreConfiguration::Get().service_config_parser().GetParserIndex( + parser_name()); +} + +} // namespace grpc_core diff --git a/src/core/ext/filters/fault_injection/service_config_parser.h b/src/core/ext/filters/fault_injection/fault_injection_service_config_parser.h similarity index 79% rename from src/core/ext/filters/fault_injection/service_config_parser.h rename to src/core/ext/filters/fault_injection/fault_injection_service_config_parser.h index 8961fa30b74..a6317b7adbe 100644 --- a/src/core/ext/filters/fault_injection/service_config_parser.h +++ b/src/core/ext/filters/fault_injection/fault_injection_service_config_parser.h @@ -14,8 +14,8 @@ // limitations under the License. // -#ifndef GRPC_CORE_EXT_FILTERS_FAULT_INJECTION_SERVICE_CONFIG_PARSER_H -#define GRPC_CORE_EXT_FILTERS_FAULT_INJECTION_SERVICE_CONFIG_PARSER_H +#ifndef GRPC_CORE_EXT_FILTERS_FAULT_INJECTION_FAULT_INJECTION_SERVICE_CONFIG_PARSER_H +#define GRPC_CORE_EXT_FILTERS_FAULT_INJECTION_FAULT_INJECTION_SERVICE_CONFIG_PARSER_H #include @@ -25,10 +25,8 @@ #include #include #include -#include #include -#include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include @@ -36,7 +34,10 @@ #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/config/core_configuration.h" #include "src/core/lib/gprpp/time.h" +#include "src/core/lib/gprpp/validation_errors.h" #include "src/core/lib/json/json.h" +#include "src/core/lib/json/json_args.h" +#include "src/core/lib/json/json_object_loader.h" #include "src/core/lib/service_config/service_config_parser.h" // Channel arg key for enabling parsing fault injection via method config. @@ -50,7 +51,7 @@ class FaultInjectionMethodParsedConfig public: struct FaultInjectionPolicy { grpc_status_code abort_code = GRPC_STATUS_OK; - std::string abort_message; + std::string abort_message = "Fault injected"; std::string abort_code_header; std::string abort_percentage_header; uint32_t abort_percentage_numerator = 0; @@ -64,11 +65,11 @@ class FaultInjectionMethodParsedConfig // By default, the max allowed active faults are unlimited. uint32_t max_faults = std::numeric_limits::max(); - }; - explicit FaultInjectionMethodParsedConfig( - std::vector fault_injection_policies) - : fault_injection_policies_(std::move(fault_injection_policies)) {} + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + void JsonPostLoad(const Json& json, const JsonArgs&, + ValidationErrors* errors); + }; // Returns the fault injection policy at certain index. // There might be multiple fault injection policies functioning at the same @@ -83,6 +84,8 @@ class FaultInjectionMethodParsedConfig return &fault_injection_policies_[index]; } + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + private: std::vector fault_injection_policies_; }; @@ -92,8 +95,9 @@ class FaultInjectionServiceConfigParser final public: absl::string_view name() const override { return parser_name(); } // Parses the per-method service config for fault injection filter. - absl::StatusOr> - ParsePerMethodParams(const ChannelArgs& args, const Json& json) override; + std::unique_ptr ParsePerMethodParams( + const ChannelArgs& args, const Json& json, + ValidationErrors* errors) override; // Returns the parser index for FaultInjectionServiceConfigParser. static size_t ParserIndex(); // Registers FaultInjectionServiceConfigParser to ServiceConfigParser. @@ -105,4 +109,4 @@ class FaultInjectionServiceConfigParser final } // namespace grpc_core -#endif // GRPC_CORE_EXT_FILTERS_FAULT_INJECTION_SERVICE_CONFIG_PARSER_H +#endif // GRPC_CORE_EXT_FILTERS_FAULT_INJECTION_FAULT_INJECTION_SERVICE_CONFIG_PARSER_H diff --git a/src/core/ext/filters/fault_injection/service_config_parser.cc b/src/core/ext/filters/fault_injection/service_config_parser.cc deleted file mode 100644 index 5670d549856..00000000000 --- a/src/core/ext/filters/fault_injection/service_config_parser.cc +++ /dev/null @@ -1,185 +0,0 @@ -// -// Copyright 2021 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#include - -#include "src/core/ext/filters/fault_injection/service_config_parser.h" - -#include -#include - -#include "absl/status/status.h" -#include "absl/strings/str_cat.h" -#include "absl/types/optional.h" - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/channel/status_util.h" -#include "src/core/lib/gprpp/status_helper.h" -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/json/json_util.h" - -namespace grpc_core { - -namespace { - -std::vector -ParseFaultInjectionPolicy(const Json::Array& policies_json_array, - std::vector* error_list) { - std::vector policies; - for (size_t i = 0; i < policies_json_array.size(); i++) { - FaultInjectionMethodParsedConfig::FaultInjectionPolicy - fault_injection_policy; - std::vector sub_error_list; - if (policies_json_array[i].type() != Json::Type::OBJECT) { - error_list->push_back(GRPC_ERROR_CREATE(absl::StrCat( - "faultInjectionPolicy index ", i, " is not a JSON object"))); - continue; - } - const Json::Object& json_object = policies_json_array[i].object_value(); - // Parse abort_code - std::string abort_code_string; - if (ParseJsonObjectField(json_object, "abortCode", &abort_code_string, - &sub_error_list, false)) { - if (!grpc_status_code_from_string(abort_code_string.c_str(), - &(fault_injection_policy.abort_code))) { - sub_error_list.push_back(GRPC_ERROR_CREATE( - "field:abortCode error:failed to parse status code")); - } - } - // Parse abort_message - if (!ParseJsonObjectField(json_object, "abortMessage", - &fault_injection_policy.abort_message, - &sub_error_list, false)) { - fault_injection_policy.abort_message = "Fault injected"; - } - // Parse abort_code_header - ParseJsonObjectField(json_object, "abortCodeHeader", - &fault_injection_policy.abort_code_header, - &sub_error_list, false); - // Parse abort_percentage_header - ParseJsonObjectField(json_object, "abortPercentageHeader", - &fault_injection_policy.abort_percentage_header, - &sub_error_list, false); - // Parse abort_percentage_numerator - ParseJsonObjectField(json_object, "abortPercentageNumerator", - &fault_injection_policy.abort_percentage_numerator, - &sub_error_list, false); - // Parse abort_percentage_denominator - if (ParseJsonObjectField( - json_object, "abortPercentageDenominator", - &fault_injection_policy.abort_percentage_denominator, - &sub_error_list, false)) { - if (fault_injection_policy.abort_percentage_denominator != 100 && - fault_injection_policy.abort_percentage_denominator != 10000 && - fault_injection_policy.abort_percentage_denominator != 1000000) { - sub_error_list.push_back(GRPC_ERROR_CREATE( - "field:abortPercentageDenominator error:Denominator can only be " - "one of " - "100, 10000, 1000000")); - } - } - // Parse delay - ParseJsonObjectFieldAsDuration(json_object, "delay", - &fault_injection_policy.delay, - &sub_error_list, false); - // Parse delay_header - ParseJsonObjectField(json_object, "delayHeader", - &fault_injection_policy.delay_header, &sub_error_list, - false); - // Parse delay_percentage_header - ParseJsonObjectField(json_object, "delayPercentageHeader", - &fault_injection_policy.delay_percentage_header, - &sub_error_list, false); - // Parse delay_percentage_numerator - ParseJsonObjectField(json_object, "delayPercentageNumerator", - &fault_injection_policy.delay_percentage_numerator, - &sub_error_list, false); - // Parse delay_percentage_denominator - if (ParseJsonObjectField( - json_object, "delayPercentageDenominator", - &fault_injection_policy.delay_percentage_denominator, - &sub_error_list, false)) { - if (fault_injection_policy.delay_percentage_denominator != 100 && - fault_injection_policy.delay_percentage_denominator != 10000 && - fault_injection_policy.delay_percentage_denominator != 1000000) { - sub_error_list.push_back(GRPC_ERROR_CREATE( - "field:delayPercentageDenominator error:Denominator can only be " - "one of " - "100, 10000, 1000000")); - } - } - // Parse max_faults - static_assert( - std::is_unsigned::value, - "maxFaults should be unsigned"); - ParseJsonObjectField(json_object, "maxFaults", - &fault_injection_policy.max_faults, &sub_error_list, - false); - if (!sub_error_list.empty()) { - error_list->push_back(GRPC_ERROR_CREATE_FROM_VECTOR( - absl::StrCat("failed to parse faultInjectionPolicy index ", i), - &sub_error_list)); - } - policies.push_back(std::move(fault_injection_policy)); - } - return policies; -} - -} // namespace - -absl::StatusOr> -FaultInjectionServiceConfigParser::ParsePerMethodParams(const ChannelArgs& args, - const Json& json) { - // Only parse fault injection policy if the following channel arg is present. - if (!args.GetBool(GRPC_ARG_PARSE_FAULT_INJECTION_METHOD_CONFIG) - .value_or(false)) { - return nullptr; - } - // Parse fault injection policy from given Json - std::vector - fault_injection_policies; - std::vector error_list; - const Json::Array* policies_json_array; - if (ParseJsonObjectField(json.object_value(), "faultInjectionPolicy", - &policies_json_array, &error_list)) { - fault_injection_policies = - ParseFaultInjectionPolicy(*policies_json_array, &error_list); - } - if (!error_list.empty()) { - grpc_error_handle error = - GRPC_ERROR_CREATE_FROM_VECTOR("Fault injection parser", &error_list); - absl::Status status = absl::InvalidArgumentError( - absl::StrCat("error parsing fault injection method parameters: ", - StatusToString(error))); - return status; - } - if (fault_injection_policies.empty()) return nullptr; - return std::make_unique( - std::move(fault_injection_policies)); -} - -void FaultInjectionServiceConfigParser::Register( - CoreConfiguration::Builder* builder) { - builder->service_config_parser()->RegisterParser( - std::make_unique()); -} - -size_t FaultInjectionServiceConfigParser::ParserIndex() { - return CoreConfiguration::Get().service_config_parser().GetParserIndex( - parser_name()); -} - -} // namespace grpc_core diff --git a/src/core/ext/filters/http/message_compress/message_decompress_filter.cc b/src/core/ext/filters/http/message_compress/message_decompress_filter.cc index ba90c0f7599..73a37b4d41f 100644 --- a/src/core/ext/filters/http/message_compress/message_decompress_filter.cc +++ b/src/core/ext/filters/http/message_compress/message_decompress_filter.cc @@ -20,9 +20,9 @@ #include "src/core/ext/filters/http/message_compress/message_decompress_filter.h" -#include #include +#include #include #include "absl/status/status.h" @@ -57,13 +57,13 @@ class ChannelData { message_size_service_config_parser_index_( MessageSizeParser::ParserIndex()) {} - int max_recv_size() const { return max_recv_size_; } + absl::optional max_recv_size() const { return max_recv_size_; } size_t message_size_service_config_parser_index() const { return message_size_service_config_parser_index_; } private: - int max_recv_size_; + absl::optional max_recv_size_; const size_t message_size_service_config_parser_index_; }; @@ -86,10 +86,10 @@ class CallData { const MessageSizeParsedConfig* limits = MessageSizeParsedConfig::GetFromCallContext( args.context, chand->message_size_service_config_parser_index()); - if (limits != nullptr && limits->limits().max_recv_size >= 0 && - (limits->limits().max_recv_size < max_recv_message_length_ || - max_recv_message_length_ < 0)) { - max_recv_message_length_ = limits->limits().max_recv_size; + if (limits != nullptr && limits->max_recv_size().has_value() && + (!max_recv_message_length_.has_value() || + *limits->max_recv_size() < *max_recv_message_length_)) { + max_recv_message_length_ = *limits->max_recv_size(); } } @@ -117,7 +117,7 @@ class CallData { grpc_metadata_batch* recv_initial_metadata_ = nullptr; // Fields for handling recv_message_ready callback bool seen_recv_message_ready_ = false; - int max_recv_message_length_; + absl::optional max_recv_message_length_; grpc_compression_algorithm algorithm_ = GRPC_COMPRESS_NONE; absl::optional* recv_message_ = nullptr; uint32_t* recv_message_flags_ = nullptr; @@ -171,15 +171,15 @@ void CallData::OnRecvMessageReady(void* arg, grpc_error_handle error) { ((*calld->recv_message_flags_ & GRPC_WRITE_INTERNAL_COMPRESS) == 0)) { return calld->ContinueRecvMessageReadyCallback(absl::OkStatus()); } - if (calld->max_recv_message_length_ >= 0 && + if (calld->max_recv_message_length_.has_value() && (*calld->recv_message_)->Length() > - static_cast(calld->max_recv_message_length_)) { + static_cast(*calld->max_recv_message_length_)) { GPR_DEBUG_ASSERT(calld->error_.ok()); calld->error_ = grpc_error_set_int( GRPC_ERROR_CREATE( absl::StrFormat("Received message larger than max (%u vs. %d)", (*calld->recv_message_)->Length(), - calld->max_recv_message_length_)), + *calld->max_recv_message_length_)), StatusIntProperty::kRpcStatus, GRPC_STATUS_RESOURCE_EXHAUSTED); return calld->ContinueRecvMessageReadyCallback(calld->error_); } diff --git a/src/core/ext/filters/message_size/message_size_filter.cc b/src/core/ext/filters/message_size/message_size_filter.cc index 8103a04efa0..33114cd9b04 100644 --- a/src/core/ext/filters/message_size/message_size_filter.cc +++ b/src/core/ext/filters/message_size/message_size_filter.cc @@ -18,17 +18,11 @@ #include "src/core/ext/filters/message_size/message_size_filter.h" -#include -#include +#include #include -#include -#include -#include #include "absl/status/status.h" -#include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" -#include "absl/types/optional.h" #include #include @@ -38,7 +32,6 @@ #include "src/core/lib/channel/channel_stack.h" #include "src/core/lib/channel/channel_stack_builder.h" #include "src/core/lib/config/core_configuration.h" -#include "src/core/lib/gpr/string.h" #include "src/core/lib/gprpp/debug_location.h" #include "src/core/lib/gprpp/status_helper.h" #include "src/core/lib/iomgr/call_combiner.h" @@ -71,58 +64,54 @@ const MessageSizeParsedConfig* MessageSizeParsedConfig::GetFromCallContext( svc_cfg_call_data->GetMethodParsedConfig(service_config_parser_index)); } +MessageSizeParsedConfig MessageSizeParsedConfig::GetFromChannelArgs( + const ChannelArgs& channel_args) { + MessageSizeParsedConfig limits; + limits.max_send_size_ = GetMaxSendSizeFromChannelArgs(channel_args); + limits.max_recv_size_ = GetMaxRecvSizeFromChannelArgs(channel_args); + return limits; +} + +absl::optional GetMaxRecvSizeFromChannelArgs( + const ChannelArgs& args) { + if (args.WantMinimalStack()) return absl::nullopt; + int size = args.GetInt(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH) + .value_or(GRPC_DEFAULT_MAX_RECV_MESSAGE_LENGTH); + if (size < 0) return absl::nullopt; + return static_cast(size); +} + +absl::optional GetMaxSendSizeFromChannelArgs( + const ChannelArgs& args) { + if (args.WantMinimalStack()) return absl::nullopt; + int size = args.GetInt(GRPC_ARG_MAX_SEND_MESSAGE_LENGTH) + .value_or(GRPC_DEFAULT_MAX_SEND_MESSAGE_LENGTH); + if (size < 0) return absl::nullopt; + return static_cast(size); +} + +const JsonLoaderInterface* MessageSizeParsedConfig::JsonLoader( + const JsonArgs&) { + static const auto* loader = + JsonObjectLoader() + .OptionalField("maxRequestMessageBytes", + &MessageSizeParsedConfig::max_send_size_) + .OptionalField("maxResponseMessageBytes", + &MessageSizeParsedConfig::max_recv_size_) + .Finish(); + return loader; +} + // // MessageSizeParser // -absl::StatusOr> +std::unique_ptr MessageSizeParser::ParsePerMethodParams(const ChannelArgs& /*args*/, - const Json& json) { - std::vector error_list; - // Max request size. - int max_request_message_bytes = -1; - auto it = json.object_value().find("maxRequestMessageBytes"); - if (it != json.object_value().end()) { - if (it->second.type() != Json::Type::STRING && - it->second.type() != Json::Type::NUMBER) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:maxRequestMessageBytes error:should be of type number")); - } else { - max_request_message_bytes = - gpr_parse_nonnegative_int(it->second.string_value().c_str()); - if (max_request_message_bytes == -1) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:maxRequestMessageBytes error:should be non-negative")); - } - } - } - // Max response size. - int max_response_message_bytes = -1; - it = json.object_value().find("maxResponseMessageBytes"); - if (it != json.object_value().end()) { - if (it->second.type() != Json::Type::STRING && - it->second.type() != Json::Type::NUMBER) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:maxResponseMessageBytes error:should be of type number")); - } else { - max_response_message_bytes = - gpr_parse_nonnegative_int(it->second.string_value().c_str()); - if (max_response_message_bytes == -1) { - error_list.push_back(GRPC_ERROR_CREATE( - "field:maxResponseMessageBytes error:should be non-negative")); - } - } - } - if (!error_list.empty()) { - grpc_error_handle error = - GRPC_ERROR_CREATE_FROM_VECTOR("Message size parser", &error_list); - absl::Status status = absl::InvalidArgumentError( - absl::StrCat("error parsing message size method parameters: ", - StatusToString(error))); - return status; - } - return std::make_unique(max_request_message_bytes, - max_response_message_bytes); + const Json& json, + ValidationErrors* errors) { + return LoadFromJson>( + json, JsonArgs(), errors); } void MessageSizeParser::Register(CoreConfiguration::Builder* builder) { @@ -135,23 +124,11 @@ size_t MessageSizeParser::ParserIndex() { parser_name()); } -int GetMaxRecvSizeFromChannelArgs(const ChannelArgs& args) { - if (args.WantMinimalStack()) return -1; - return std::max(-1, args.GetInt(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH) - .value_or(GRPC_DEFAULT_MAX_RECV_MESSAGE_LENGTH)); -} - -int GetMaxSendSizeFromChannelArgs(const ChannelArgs& args) { - if (args.WantMinimalStack()) return -1; - return std::max(-1, args.GetInt(GRPC_ARG_MAX_SEND_MESSAGE_LENGTH) - .value_or(GRPC_DEFAULT_MAX_SEND_MESSAGE_LENGTH)); -} - } // namespace grpc_core namespace { struct channel_data { - grpc_core::MessageSizeParsedConfig::message_size_limits limits; + grpc_core::MessageSizeParsedConfig limits; const size_t service_config_parser_index{ grpc_core::MessageSizeParser::ParserIndex()}; }; @@ -169,27 +146,30 @@ struct call_data { // Note: Per-method config is only available on the client, so we // apply the max request size to the send limit and the max response // size to the receive limit. - const grpc_core::MessageSizeParsedConfig* limits = + const grpc_core::MessageSizeParsedConfig* config_from_call_context = grpc_core::MessageSizeParsedConfig::GetFromCallContext( args.context, chand.service_config_parser_index); - if (limits != nullptr) { - if (limits->limits().max_send_size >= 0 && - (limits->limits().max_send_size < this->limits.max_send_size || - this->limits.max_send_size < 0)) { - this->limits.max_send_size = limits->limits().max_send_size; + if (config_from_call_context != nullptr) { + absl::optional max_send_size = limits.max_send_size(); + absl::optional max_recv_size = limits.max_recv_size(); + if (config_from_call_context->max_send_size().has_value() && + (!max_send_size.has_value() || + *config_from_call_context->max_send_size() < *max_send_size)) { + max_send_size = *config_from_call_context->max_send_size(); } - if (limits->limits().max_recv_size >= 0 && - (limits->limits().max_recv_size < this->limits.max_recv_size || - this->limits.max_recv_size < 0)) { - this->limits.max_recv_size = limits->limits().max_recv_size; + if (config_from_call_context->max_recv_size().has_value() && + (!max_recv_size.has_value() || + *config_from_call_context->max_recv_size() < *max_recv_size)) { + max_recv_size = *config_from_call_context->max_recv_size(); } + limits = grpc_core::MessageSizeParsedConfig(max_send_size, max_recv_size); } } ~call_data() {} grpc_core::CallCombiner* call_combiner; - grpc_core::MessageSizeParsedConfig::message_size_limits limits; + grpc_core::MessageSizeParsedConfig limits; // Receive closures are chained: we inject this closure as the // recv_message_ready up-call on transport_stream_op, and remember to // call our next_recv_message_ready member after handling it. @@ -214,13 +194,14 @@ struct call_data { static void recv_message_ready(void* user_data, grpc_error_handle error) { grpc_call_element* elem = static_cast(user_data); call_data* calld = static_cast(elem->call_data); - if (calld->recv_message->has_value() && calld->limits.max_recv_size >= 0 && + if (calld->recv_message->has_value() && + calld->limits.max_recv_size().has_value() && (*calld->recv_message)->Length() > - static_cast(calld->limits.max_recv_size)) { + static_cast(*calld->limits.max_recv_size())) { grpc_error_handle new_error = grpc_error_set_int( GRPC_ERROR_CREATE(absl::StrFormat( "Received message larger than max (%u vs. %d)", - (*calld->recv_message)->Length(), calld->limits.max_recv_size)), + (*calld->recv_message)->Length(), *calld->limits.max_recv_size())), grpc_core::StatusIntProperty::kRpcStatus, GRPC_STATUS_RESOURCE_EXHAUSTED); error = grpc_error_add_child(error, new_error); @@ -269,15 +250,15 @@ static void message_size_start_transport_stream_op_batch( grpc_call_element* elem, grpc_transport_stream_op_batch* op) { call_data* calld = static_cast(elem->call_data); // Check max send message size. - if (op->send_message && calld->limits.max_send_size >= 0 && + if (op->send_message && calld->limits.max_send_size().has_value() && op->payload->send_message.send_message->Length() > - static_cast(calld->limits.max_send_size)) { + static_cast(*calld->limits.max_send_size())) { grpc_transport_stream_op_batch_finish_with_failure( op, grpc_error_set_int(GRPC_ERROR_CREATE(absl::StrFormat( "Sent message larger than max (%u vs. %d)", op->payload->send_message.send_message->Length(), - calld->limits.max_send_size)), + *calld->limits.max_send_size())), grpc_core::StatusIntProperty::kRpcStatus, GRPC_STATUS_RESOURCE_EXHAUSTED), calld->call_combiner); @@ -317,21 +298,13 @@ static void message_size_destroy_call_elem( calld->~call_data(); } -grpc_core::MessageSizeParsedConfig::message_size_limits get_message_size_limits( - const grpc_core::ChannelArgs& channel_args) { - grpc_core::MessageSizeParsedConfig::message_size_limits lim; - lim.max_send_size = grpc_core::GetMaxSendSizeFromChannelArgs(channel_args); - lim.max_recv_size = grpc_core::GetMaxRecvSizeFromChannelArgs(channel_args); - return lim; -} - // Constructor for channel_data. static grpc_error_handle message_size_init_channel_elem( grpc_channel_element* elem, grpc_channel_element_args* args) { GPR_ASSERT(!args->is_last); channel_data* chand = static_cast(elem->channel_data); new (chand) channel_data(); - chand->limits = get_message_size_limits( + chand->limits = grpc_core::MessageSizeParsedConfig::GetFromChannelArgs( grpc_core::ChannelArgs::FromC(args->channel_args)); return absl::OkStatus(); } @@ -375,10 +348,11 @@ static bool maybe_add_message_size_filter( if (channel_args.WantMinimalStack()) { return true; } - grpc_core::MessageSizeParsedConfig::message_size_limits lim = - get_message_size_limits(channel_args); + grpc_core::MessageSizeParsedConfig limits = + grpc_core::MessageSizeParsedConfig::GetFromChannelArgs(channel_args); const bool enable = - lim.max_send_size != -1 || lim.max_recv_size != -1 || + limits.max_send_size().has_value() || + limits.max_recv_size().has_value() || channel_args.GetString(GRPC_ARG_SERVICE_CONFIG).has_value(); if (enable) builder->PrependFilter(&grpc_message_size_filter); return true; diff --git a/src/core/ext/filters/message_size/message_size_filter.h b/src/core/ext/filters/message_size/message_size_filter.h index 4493b2acc2c..e6b90ba9f9d 100644 --- a/src/core/ext/filters/message_size/message_size_filter.h +++ b/src/core/ext/filters/message_size/message_size_filter.h @@ -20,18 +20,22 @@ #include #include +#include #include -#include "absl/status/statusor.h" #include "absl/strings/string_view.h" +#include "absl/types/optional.h" #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/channel_fwd.h" #include "src/core/lib/channel/channel_stack.h" #include "src/core/lib/channel/context.h" #include "src/core/lib/config/core_configuration.h" +#include "src/core/lib/gprpp/validation_errors.h" #include "src/core/lib/json/json.h" +#include "src/core/lib/json/json_args.h" +#include "src/core/lib/json/json_object_loader.h" #include "src/core/lib/service_config/service_config_parser.h" extern const grpc_channel_filter grpc_message_size_filter; @@ -40,32 +44,35 @@ namespace grpc_core { class MessageSizeParsedConfig : public ServiceConfigParser::ParsedConfig { public: - struct message_size_limits { - int max_send_size; - int max_recv_size; - }; + absl::optional max_send_size() const { return max_send_size_; } + absl::optional max_recv_size() const { return max_recv_size_; } - MessageSizeParsedConfig(int max_send_size, int max_recv_size) { - limits_.max_send_size = max_send_size; - limits_.max_recv_size = max_recv_size; - } + MessageSizeParsedConfig() = default; - const message_size_limits& limits() const { return limits_; } + MessageSizeParsedConfig(absl::optional max_send_size, + absl::optional max_recv_size) + : max_send_size_(max_send_size), max_recv_size_(max_recv_size) {} static const MessageSizeParsedConfig* GetFromCallContext( const grpc_call_context_element* context, size_t service_config_parser_index); + static MessageSizeParsedConfig GetFromChannelArgs(const ChannelArgs& args); + + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + private: - message_size_limits limits_; + absl::optional max_send_size_; + absl::optional max_recv_size_; }; class MessageSizeParser : public ServiceConfigParser::Parser { public: absl::string_view name() const override { return parser_name(); } - absl::StatusOr> - ParsePerMethodParams(const ChannelArgs& /*args*/, const Json& json) override; + std::unique_ptr ParsePerMethodParams( + const ChannelArgs& /*args*/, const Json& json, + ValidationErrors* errors) override; static void Register(CoreConfiguration::Builder* builder); @@ -75,8 +82,8 @@ class MessageSizeParser : public ServiceConfigParser::Parser { static absl::string_view parser_name() { return "message_size"; } }; -int GetMaxRecvSizeFromChannelArgs(const ChannelArgs& args); -int GetMaxSendSizeFromChannelArgs(const ChannelArgs& args); +absl::optional GetMaxRecvSizeFromChannelArgs(const ChannelArgs& args); +absl::optional GetMaxSendSizeFromChannelArgs(const ChannelArgs& args); } // namespace grpc_core diff --git a/src/core/ext/filters/rbac/rbac_service_config_parser.cc b/src/core/ext/filters/rbac/rbac_service_config_parser.cc index cac1063d32c..a1dab43048b 100644 --- a/src/core/ext/filters/rbac/rbac_service_config_parser.cc +++ b/src/core/ext/filters/rbac/rbac_service_config_parser.cc @@ -18,587 +18,789 @@ #include "src/core/ext/filters/rbac/rbac_service_config_parser.h" -#include - +#include #include #include #include "absl/status/status.h" #include "absl/status/statusor.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_format.h" #include "absl/types/optional.h" #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/gprpp/status_helper.h" -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/json/json_util.h" +#include "src/core/lib/json/json_args.h" +#include "src/core/lib/json/json_object_loader.h" #include "src/core/lib/matchers/matchers.h" -#include "src/core/lib/transport/error_utils.h" namespace grpc_core { namespace { -std::string ParseRegexMatcher(const Json::Object& regex_matcher_json, - std::vector* error_list) { - std::string regex; - ParseJsonObjectField(regex_matcher_json, "regex", ®ex, error_list); - return regex; +// RbacConfig: one or more RbacPolicy structs +struct RbacConfig { + // RbacPolicy: optional Rules + struct RbacPolicy { + // Rules: an action, plus a map of policy names to Policy structs + struct Rules { + // Policy: a list of Permissions and a list of Principals + struct Policy { + // CidrRange: represents an IP range + struct CidrRange { + Rbac::CidrRange cidr_range; + + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + void JsonPostLoad(const Json& json, const JsonArgs& args, + ValidationErrors* errors); + }; + + // SafeRegexMatch: a regex matcher + struct SafeRegexMatch { + std::string regex; + + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + }; + + // HeaderMatch: a matcher for HTTP headers + struct HeaderMatch { + // RangeMatch: matches a range of numerical values + struct RangeMatch { + int64_t start; + int64_t end; + + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + }; + + HeaderMatcher matcher; + + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + void JsonPostLoad(const Json& json, const JsonArgs& args, + ValidationErrors* errors); + }; + + // StringMatch: a matcher for strings + struct StringMatch { + StringMatcher matcher; + + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + void JsonPostLoad(const Json& json, const JsonArgs& args, + ValidationErrors* errors); + }; + + // PathMatch: a matcher for paths + struct PathMatch { + StringMatch path; + + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + }; + + // Metadata: a matcher for Envoy metadata (not really applicable + // to gRPC; we use only the invert field for proper match semantics) + struct Metadata { + bool invert = false; + + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + }; + + // Permission: a matcher for request attributes + struct Permission { + // PermissionList: a list used for "and" and "or" matchers + struct PermissionList { + std::vector rules; + + PermissionList() = default; + PermissionList(const PermissionList&) = delete; + PermissionList& operator=(const PermissionList&) = delete; + PermissionList(PermissionList&&) = default; + PermissionList& operator=(PermissionList&&) = default; + + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + }; + + std::unique_ptr permission; + + Permission() = default; + Permission(const Permission&) = delete; + Permission& operator=(const Permission&) = delete; + Permission(Permission&&) = default; + Permission& operator=(Permission&&) = default; + + static std::vector> + MakeRbacPermissionList(std::vector permission_list); + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + void JsonPostLoad(const Json& json, const JsonArgs& args, + ValidationErrors* errors); + }; + + // Principal: a matcher for client identity + struct Principal { + // PrincipalList: a list used for "and" and "or" matchers + struct PrincipalList { + std::vector ids; + + PrincipalList() = default; + PrincipalList(const PrincipalList&) = delete; + PrincipalList& operator=(const PrincipalList&) = delete; + PrincipalList(PrincipalList&&) = default; + PrincipalList& operator=(PrincipalList&&) = default; + + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + }; + + struct Authenticated { + absl::optional principal_name; + + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + }; + + std::unique_ptr principal; + + Principal() = default; + Principal(const Principal&) = delete; + Principal& operator=(const Principal&) = delete; + Principal(Principal&&) = default; + Principal& operator=(Principal&&) = default; + + static std::vector> + MakeRbacPrincipalList(std::vector principal_list); + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + void JsonPostLoad(const Json& json, const JsonArgs& args, + ValidationErrors* errors); + }; + + std::vector permissions; + std::vector principals; + + Policy() = default; + Policy(const Policy&) = delete; + Policy& operator=(const Policy&) = delete; + Policy(Policy&&) = default; + Policy& operator=(Policy&&) = default; + + Rbac::Policy TakeAsRbacPolicy(); + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + }; + + int action; + std::map policies; + + Rules() = default; + Rules(const Rules&) = delete; + Rules& operator=(const Rules&) = delete; + Rules(Rules&&) = default; + Rules& operator=(Rules&&) = default; + + Rbac TakeAsRbac(); + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + void JsonPostLoad(const Json&, const JsonArgs&, ValidationErrors* errors); + }; + + absl::optional rules; + + Rbac TakeAsRbac(); + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); + }; + + std::vector rbac_policies; + + std::vector TakeAsRbacList(); + static const JsonLoaderInterface* JsonLoader(const JsonArgs&); +}; + +// +// RbacConfig::RbacPolicy::Rules::Policy::CidrRange +// + +const JsonLoaderInterface* +RbacConfig::RbacPolicy::Rules::Policy::CidrRange::JsonLoader(const JsonArgs&) { + // All fields handled in JsonPostLoad(). + static const auto* loader = JsonObjectLoader().Finish(); + return loader; } -absl::StatusOr ParseHeaderMatcher( - const Json::Object& header_matcher_json, - std::vector* error_list) { - std::string name; - ParseJsonObjectField(header_matcher_json, "name", &name, error_list); - std::string match; - HeaderMatcher::Type type = HeaderMatcher::Type(); - const Json::Object* inner_json; - int64_t start = 0; - int64_t end = 0; - bool present_match = false; - bool invert_match = false; - ParseJsonObjectField(header_matcher_json, "invertMatch", &invert_match, - error_list, /*required=*/false); - if (ParseJsonObjectField(header_matcher_json, "exactMatch", &match, - error_list, /*required=*/false)) { - type = HeaderMatcher::Type::kExact; - } else if (ParseJsonObjectField(header_matcher_json, "safeRegexMatch", - &inner_json, error_list, - /*required=*/false)) { - type = HeaderMatcher::Type::kSafeRegex; - std::vector safe_regex_matcher_error_list; - match = ParseRegexMatcher(*inner_json, &safe_regex_matcher_error_list); - if (!safe_regex_matcher_error_list.empty()) { - error_list->push_back(GRPC_ERROR_CREATE_FROM_VECTOR( - "safeRegexMatch", &safe_regex_matcher_error_list)); - } - } else if (ParseJsonObjectField(header_matcher_json, "rangeMatch", - &inner_json, error_list, - /*required=*/false)) { - type = HeaderMatcher::Type::kRange; - std::vector range_error_list; - ParseJsonObjectField(*inner_json, "start", &start, &range_error_list); - ParseJsonObjectField(*inner_json, "end", &end, &range_error_list); - if (!range_error_list.empty()) { - error_list->push_back( - GRPC_ERROR_CREATE_FROM_VECTOR("rangeMatch", &range_error_list)); - } - } else if (ParseJsonObjectField(header_matcher_json, "presentMatch", - &present_match, error_list, - /*required=*/false)) { - type = HeaderMatcher::Type::kPresent; - } else if (ParseJsonObjectField(header_matcher_json, "prefixMatch", &match, - error_list, /*required=*/false)) { - type = HeaderMatcher::Type::kPrefix; - } else if (ParseJsonObjectField(header_matcher_json, "suffixMatch", &match, - error_list, /*required=*/false)) { - type = HeaderMatcher::Type::kSuffix; - } else if (ParseJsonObjectField(header_matcher_json, "containsMatch", &match, - error_list, /*required=*/false)) { - type = HeaderMatcher::Type::kContains; - } else { - return absl::InvalidArgumentError("No valid matcher found"); - } - return HeaderMatcher::Create(name, type, match, start, end, present_match, - invert_match); +void RbacConfig::RbacPolicy::Rules::Policy::CidrRange::JsonPostLoad( + const Json& json, const JsonArgs& args, ValidationErrors* errors) { + auto address_prefix = LoadJsonObjectField( + json.object_value(), args, "addressPrefix", errors); + auto prefix_len = LoadJsonObjectField(json.object_value(), args, + "prefixLen", errors, + /*required=*/false); + cidr_range = + Rbac::CidrRange(address_prefix.value_or(""), prefix_len.value_or(0)); } -absl::StatusOr ParseStringMatcher( - const Json::Object& string_matcher_json, - std::vector* error_list) { - std::string match; - StringMatcher::Type type = StringMatcher::Type(); - const Json::Object* inner_json; - bool ignore_case = false; - ParseJsonObjectField(string_matcher_json, "ignoreCase", &ignore_case, - error_list, /*required=*/false); - if (ParseJsonObjectField(string_matcher_json, "exact", &match, error_list, - /*required=*/false)) { - type = StringMatcher::Type::kExact; - } else if (ParseJsonObjectField(string_matcher_json, "prefix", &match, - error_list, /*required=*/false)) { - type = StringMatcher::Type::kPrefix; - } else if (ParseJsonObjectField(string_matcher_json, "suffix", &match, - error_list, /*required=*/false)) { - type = StringMatcher::Type::kSuffix; - } else if (ParseJsonObjectField(string_matcher_json, "safeRegex", &inner_json, - error_list, /*required=*/false)) { - type = StringMatcher::Type::kSafeRegex; - std::vector safe_regex_matcher_error_list; - match = ParseRegexMatcher(*inner_json, &safe_regex_matcher_error_list); - if (!safe_regex_matcher_error_list.empty()) { - error_list->push_back(GRPC_ERROR_CREATE_FROM_VECTOR( - "safeRegex", &safe_regex_matcher_error_list)); - } - } else if (ParseJsonObjectField(string_matcher_json, "contains", &match, - error_list, /*required=*/false)) { - type = StringMatcher::Type::kContains; - } else { - return absl::InvalidArgumentError("No valid matcher found"); - } - return StringMatcher::Create(type, match, ignore_case); +// +// RbacConfig::RbacPolicy::Rules::Policy::SafeRegexMatch +// + +const JsonLoaderInterface* +RbacConfig::RbacPolicy::Rules::Policy::SafeRegexMatch::JsonLoader( + const JsonArgs&) { + static const auto* loader = JsonObjectLoader() + .Field("regex", &SafeRegexMatch::regex) + .Finish(); + return loader; } -absl::StatusOr ParsePathMatcher( - const Json::Object& path_matcher_json, - std::vector* error_list) { - const Json::Object* string_matcher_json; - if (ParseJsonObjectField(path_matcher_json, "path", &string_matcher_json, - error_list)) { - std::vector sub_error_list; - auto matcher = ParseStringMatcher(*string_matcher_json, &sub_error_list); - if (!sub_error_list.empty()) { - error_list->push_back( - GRPC_ERROR_CREATE_FROM_VECTOR("path", &sub_error_list)); - } - return matcher; - } - return absl::InvalidArgumentError("No path found"); +// +// RbacConfig::RbacPolicy::Rules::Policy::HeaderMatch::RangeMatch +// + +const JsonLoaderInterface* +RbacConfig::RbacPolicy::Rules::Policy::HeaderMatch::RangeMatch::JsonLoader( + const JsonArgs&) { + static const auto* loader = JsonObjectLoader() + .Field("start", &RangeMatch::start) + .Field("end", &RangeMatch::end) + .Finish(); + return loader; } -Rbac::CidrRange ParseCidrRange(const Json::Object& cidr_range_json, - std::vector* error_list) { - std::string address_prefix; - ParseJsonObjectField(cidr_range_json, "addressPrefix", &address_prefix, - error_list); - const Json::Object* uint32_json; - uint32_t prefix_len = 0; // default value - if (ParseJsonObjectField(cidr_range_json, "prefixLen", &uint32_json, - error_list, /*required=*/false)) { - std::vector sub_error_list; - ParseJsonObjectField(*uint32_json, "value", &prefix_len, &sub_error_list); - if (!sub_error_list.empty()) { - error_list->push_back( - GRPC_ERROR_CREATE_FROM_VECTOR("prefixLen", &sub_error_list)); - } - } - return Rbac::CidrRange(std::move(address_prefix), prefix_len); +// +// RbacConfig::RbacPolicy::Rules::Policy::HeaderMatch +// + +const JsonLoaderInterface* +RbacConfig::RbacPolicy::Rules::Policy::HeaderMatch::JsonLoader( + const JsonArgs&) { + // All fields handled in JsonPostLoad(). + static const auto* loader = JsonObjectLoader().Finish(); + return loader; } -Rbac::Permission ParsePermission(const Json::Object& permission_json, - std::vector* error_list) { - auto parse_permission_set = [](const Json::Object& permission_set_json, - std::vector* error_list) { - const Json::Array* rules_json; - std::vector> permissions; - if (ParseJsonObjectField(permission_set_json, "rules", &rules_json, - error_list)) { - for (size_t i = 0; i < rules_json->size(); ++i) { - const Json::Object* permission_json; - if (!ExtractJsonType((*rules_json)[i], - absl::StrFormat("rules[%d]", i).c_str(), - &permission_json, error_list)) { - continue; - } - std::vector permission_error_list; - permissions.emplace_back(std::make_unique( - ParsePermission(*permission_json, &permission_error_list))); - if (!permission_error_list.empty()) { - error_list->push_back(GRPC_ERROR_CREATE_FROM_VECTOR( - absl::StrFormat("rules[%d]", i), &permission_error_list)); - } - } - } - return permissions; - }; - Rbac::Permission permission; - const Json::Object* inner_json; - bool any; - int port; - if (ParseJsonObjectField(permission_json, "andRules", &inner_json, error_list, - /*required=*/false)) { - std::vector and_rules_error_list; - permission = Rbac::Permission::MakeAndPermission( - parse_permission_set(*inner_json, &and_rules_error_list)); - if (!and_rules_error_list.empty()) { - error_list->push_back( - GRPC_ERROR_CREATE_FROM_VECTOR("andRules", &and_rules_error_list)); - } - } else if (ParseJsonObjectField(permission_json, "orRules", &inner_json, - error_list, /*required=*/false)) { - std::vector or_rules_error_list; - permission = Rbac::Permission::MakeOrPermission( - parse_permission_set(*inner_json, &or_rules_error_list)); - if (!or_rules_error_list.empty()) { - error_list->push_back( - GRPC_ERROR_CREATE_FROM_VECTOR("orRules", &or_rules_error_list)); - } - } else if (ParseJsonObjectField(permission_json, "any", &any, error_list, - /*required=*/false) && - any) { - permission = Rbac::Permission::MakeAnyPermission(); - } else if (ParseJsonObjectField(permission_json, "header", &inner_json, - error_list, - /*required=*/false)) { - std::vector header_error_list; - auto matcher = ParseHeaderMatcher(*inner_json, &header_error_list); - if (matcher.ok()) { - permission = Rbac::Permission::MakeHeaderPermission(*matcher); +void RbacConfig::RbacPolicy::Rules::Policy::HeaderMatch::JsonPostLoad( + const Json& json, const JsonArgs& args, ValidationErrors* errors) { + const size_t original_error_size = errors->size(); + std::string name = LoadJsonObjectField(json.object_value(), args, + "name", errors) + .value_or(""); + bool invert_match = LoadJsonObjectField(json.object_value(), args, + "invertMatch", errors, + /*required=*/false) + .value_or(false); + auto set_header_matcher = [&](absl::StatusOr header_matcher) { + if (header_matcher.ok()) { + matcher = *header_matcher; } else { - header_error_list.push_back(absl_status_to_grpc_error(matcher.status())); - } - if (!header_error_list.empty()) { - error_list->push_back( - GRPC_ERROR_CREATE_FROM_VECTOR("header", &header_error_list)); - } - } else if (ParseJsonObjectField(permission_json, "urlPath", &inner_json, - error_list, - /*required=*/false)) { - std::vector url_path_error_list; - auto matcher = ParsePathMatcher(*inner_json, &url_path_error_list); - if (matcher.ok()) { - permission = Rbac::Permission::MakePathPermission(*matcher); - } else { - url_path_error_list.push_back( - absl_status_to_grpc_error(matcher.status())); - } - if (!url_path_error_list.empty()) { - error_list->push_back( - GRPC_ERROR_CREATE_FROM_VECTOR("urlPath", &url_path_error_list)); + errors->AddError(header_matcher.status().message()); } - } else if (ParseJsonObjectField(permission_json, "destinationIp", &inner_json, - error_list, /*required=*/false)) { - std::vector destination_ip_error_list; - permission = Rbac::Permission::MakeDestIpPermission( - ParseCidrRange(*inner_json, &destination_ip_error_list)); - if (!destination_ip_error_list.empty()) { - error_list->push_back(GRPC_ERROR_CREATE_FROM_VECTOR( - "destinationIp", &destination_ip_error_list)); - } - } else if (ParseJsonObjectField(permission_json, "destinationPort", &port, - error_list, /*required=*/false)) { - permission = Rbac::Permission::MakeDestPortPermission(port); - } else if (ParseJsonObjectField(permission_json, "metadata", &inner_json, - error_list, /*required=*/false)) { - std::vector metadata_error_list; - bool invert = false; - ParseJsonObjectField(*inner_json, "invert", &invert, &metadata_error_list, - /*required=*/false); - if (metadata_error_list.empty()) { - permission = Rbac::Permission::MakeMetadataPermission(invert); - } else { - error_list->push_back( - GRPC_ERROR_CREATE_FROM_VECTOR("metadata", &metadata_error_list)); - } - } else if (ParseJsonObjectField(permission_json, "notRule", &inner_json, - error_list, /*required=*/false)) { - std::vector not_rule_error_list; - permission = Rbac::Permission::MakeNotPermission( - ParsePermission(*inner_json, ¬_rule_error_list)); - if (!not_rule_error_list.empty()) { - error_list->push_back( - GRPC_ERROR_CREATE_FROM_VECTOR("notRule", ¬_rule_error_list)); - } - } else if (ParseJsonObjectField(permission_json, "requestedServerName", - &inner_json, error_list, - /*required=*/false)) { - std::vector req_server_name_error_list; - auto matcher = ParseStringMatcher(*inner_json, &req_server_name_error_list); - if (matcher.ok()) { - permission = Rbac::Permission::MakeReqServerNamePermission(*matcher); - } else { - req_server_name_error_list.push_back( - absl_status_to_grpc_error(matcher.status())); - } - if (!req_server_name_error_list.empty()) { - error_list->push_back(GRPC_ERROR_CREATE_FROM_VECTOR( - "requestedServerName", &req_server_name_error_list)); - } - } else { - error_list->push_back(GRPC_ERROR_CREATE("No valid rule found")); + }; + auto check_match = [&](absl::string_view field_name, + HeaderMatcher::Type type) { + auto match = LoadJsonObjectField(json.object_value(), args, + field_name, errors, + /*required=*/false); + if (match.has_value()) { + set_header_matcher( + HeaderMatcher::Create(name, type, *match, 0, 0, false, invert_match)); + return true; + } + return false; + }; + if (check_match("exactMatch", HeaderMatcher::Type::kExact) || + check_match("prefixMatch", HeaderMatcher::Type::kPrefix) || + check_match("suffixMatch", HeaderMatcher::Type::kSuffix) || + check_match("containsMatch", HeaderMatcher::Type::kContains)) { + return; + } + auto present_match = LoadJsonObjectField(json.object_value(), args, + "presentMatch", errors, + /*required=*/false); + if (present_match.has_value()) { + set_header_matcher( + HeaderMatcher::Create(name, HeaderMatcher::Type::kPresent, "", 0, 0, + *present_match, invert_match)); + return; + } + auto regex_match = LoadJsonObjectField( + json.object_value(), args, "safeRegexMatch", errors, + /*required=*/false); + if (regex_match.has_value()) { + set_header_matcher( + HeaderMatcher::Create(name, HeaderMatcher::Type::kSafeRegex, + regex_match->regex, 0, 0, false, invert_match)); + return; + } + auto range_match = LoadJsonObjectField(json.object_value(), args, + "rangeMatch", errors, + /*required=*/false); + if (range_match.has_value()) { + set_header_matcher(HeaderMatcher::Create(name, HeaderMatcher::Type::kRange, + "", range_match->start, + range_match->end, invert_match)); + return; + } + if (errors->size() == original_error_size) { + errors->AddError("no valid matcher found"); } - return permission; } -Rbac::Principal ParsePrincipal(const Json::Object& principal_json, - std::vector* error_list) { - auto parse_principal_set = [](const Json::Object& principal_set_json, - std::vector* error_list) { - const Json::Array* rules_json; - std::vector> principals; - if (ParseJsonObjectField(principal_set_json, "ids", &rules_json, - error_list)) { - for (size_t i = 0; i < rules_json->size(); ++i) { - const Json::Object* principal_json; - if (!ExtractJsonType((*rules_json)[i], - absl::StrFormat("ids[%d]", i).c_str(), - &principal_json, error_list)) { - continue; - } - std::vector principal_error_list; - principals.emplace_back(std::make_unique( - ParsePrincipal(*principal_json, &principal_error_list))); - if (!principal_error_list.empty()) { - error_list->push_back(GRPC_ERROR_CREATE_FROM_VECTOR( - absl::StrFormat("ids[%d]", i), &principal_error_list)); - } - } - } - return principals; - }; - Rbac::Principal principal; - const Json::Object* inner_json; - bool any; - if (ParseJsonObjectField(principal_json, "andIds", &inner_json, error_list, - /*required=*/false)) { - std::vector and_rules_error_list; - principal = Rbac::Principal::MakeAndPrincipal( - parse_principal_set(*inner_json, &and_rules_error_list)); - if (!and_rules_error_list.empty()) { - error_list->push_back( - GRPC_ERROR_CREATE_FROM_VECTOR("andIds", &and_rules_error_list)); - } - } else if (ParseJsonObjectField(principal_json, "orIds", &inner_json, - error_list, /*required=*/false)) { - std::vector or_rules_error_list; - principal = Rbac::Principal::MakeOrPrincipal( - parse_principal_set(*inner_json, &or_rules_error_list)); - if (!or_rules_error_list.empty()) { - error_list->push_back( - GRPC_ERROR_CREATE_FROM_VECTOR("orIds", &or_rules_error_list)); - } - } else if (ParseJsonObjectField(principal_json, "any", &any, error_list, - /*required=*/false) && - any) { - principal = Rbac::Principal::MakeAnyPrincipal(); - } else if (ParseJsonObjectField(principal_json, "authenticated", &inner_json, - error_list, /*required=*/false)) { - std::vector authenticated_error_list; - const Json::Object* principal_name_json; - if (ParseJsonObjectField(*inner_json, "principalName", &principal_name_json, - &authenticated_error_list, /*required=*/false)) { - std::vector principal_name_error_list; - auto matcher = - ParseStringMatcher(*principal_name_json, &principal_name_error_list); - if (matcher.ok()) { - principal = Rbac::Principal::MakeAuthenticatedPrincipal(*matcher); - } else { - principal_name_error_list.push_back( - absl_status_to_grpc_error(matcher.status())); - } - if (!principal_name_error_list.empty()) { - authenticated_error_list.push_back(GRPC_ERROR_CREATE_FROM_VECTOR( - "principalName", &principal_name_error_list)); - } - } else if (authenticated_error_list.empty()) { - // No principalName found. Match for all users. - principal = Rbac::Principal::MakeAnyPrincipal(); - } else { - error_list->push_back(GRPC_ERROR_CREATE_FROM_VECTOR( - "authenticated", &authenticated_error_list)); - } - } else if (ParseJsonObjectField(principal_json, "sourceIp", &inner_json, - error_list, /*required=*/false)) { - std::vector source_ip_error_list; - principal = Rbac::Principal::MakeSourceIpPrincipal( - ParseCidrRange(*inner_json, &source_ip_error_list)); - if (!source_ip_error_list.empty()) { - error_list->push_back( - GRPC_ERROR_CREATE_FROM_VECTOR("sourceIp", &source_ip_error_list)); - } - } else if (ParseJsonObjectField(principal_json, "directRemoteIp", &inner_json, - error_list, /*required=*/false)) { - std::vector direct_remote_ip_error_list; - principal = Rbac::Principal::MakeDirectRemoteIpPrincipal( - ParseCidrRange(*inner_json, &direct_remote_ip_error_list)); - if (!direct_remote_ip_error_list.empty()) { - error_list->push_back(GRPC_ERROR_CREATE_FROM_VECTOR( - "directRemoteIp", &direct_remote_ip_error_list)); - } - } else if (ParseJsonObjectField(principal_json, "remoteIp", &inner_json, - error_list, /*required=*/false)) { - std::vector remote_ip_error_list; - principal = Rbac::Principal::MakeRemoteIpPrincipal( - ParseCidrRange(*inner_json, &remote_ip_error_list)); - if (!remote_ip_error_list.empty()) { - error_list->push_back( - GRPC_ERROR_CREATE_FROM_VECTOR("remoteIp", &remote_ip_error_list)); - } - } else if (ParseJsonObjectField(principal_json, "header", &inner_json, - error_list, - /*required=*/false)) { - std::vector header_error_list; - auto matcher = ParseHeaderMatcher(*inner_json, &header_error_list); - if (matcher.ok()) { - principal = Rbac::Principal::MakeHeaderPrincipal(*matcher); - } else { - header_error_list.push_back(absl_status_to_grpc_error(matcher.status())); - } - if (!header_error_list.empty()) { - error_list->push_back( - GRPC_ERROR_CREATE_FROM_VECTOR("header", &header_error_list)); - } - } else if (ParseJsonObjectField(principal_json, "urlPath", &inner_json, - error_list, - /*required=*/false)) { - std::vector url_path_error_list; - auto matcher = ParsePathMatcher(*inner_json, &url_path_error_list); - if (matcher.ok()) { - principal = Rbac::Principal::MakePathPrincipal(*matcher); - } else { - url_path_error_list.push_back( - absl_status_to_grpc_error(matcher.status())); - } - if (!url_path_error_list.empty()) { - error_list->push_back( - GRPC_ERROR_CREATE_FROM_VECTOR("urlPath", &url_path_error_list)); - } - } else if (ParseJsonObjectField(principal_json, "metadata", &inner_json, - error_list, /*required=*/false)) { - std::vector metadata_error_list; - bool invert = false; - ParseJsonObjectField(*inner_json, "invert", &invert, &metadata_error_list, - /*required=*/false); - if (metadata_error_list.empty()) { - principal = Rbac::Principal::MakeMetadataPrincipal(invert); +// +// RbacConfig::RbacPolicy::Rules::Policy::StringMatch +// + +const JsonLoaderInterface* +RbacConfig::RbacPolicy::Rules::Policy::StringMatch::JsonLoader( + const JsonArgs&) { + // All fields handled in JsonPostLoad(). + static const auto* loader = JsonObjectLoader().Finish(); + return loader; +} + +void RbacConfig::RbacPolicy::Rules::Policy::StringMatch::JsonPostLoad( + const Json& json, const JsonArgs& args, ValidationErrors* errors) { + const size_t original_error_size = errors->size(); + bool ignore_case = + LoadJsonObjectField(json.object_value(), args, "ignoreCase", errors, + /*required=*/false) + .value_or(false); + auto set_string_matcher = [&](absl::StatusOr string_matcher) { + if (string_matcher.ok()) { + matcher = *string_matcher; } else { - error_list->push_back( - GRPC_ERROR_CREATE_FROM_VECTOR("metadata", &metadata_error_list)); - } - } else if (ParseJsonObjectField(principal_json, "notId", &inner_json, - error_list, /*required=*/false)) { - std::vector not_rule_error_list; - principal = Rbac::Principal::MakeNotPrincipal( - ParsePrincipal(*inner_json, ¬_rule_error_list)); - if (!not_rule_error_list.empty()) { - error_list->push_back( - GRPC_ERROR_CREATE_FROM_VECTOR("notId", ¬_rule_error_list)); + errors->AddError(string_matcher.status().message()); } - } else { - error_list->push_back(GRPC_ERROR_CREATE("No valid id found")); + }; + auto check_match = [&](absl::string_view field_name, + StringMatcher::Type type) { + auto match = LoadJsonObjectField(json.object_value(), args, + field_name, errors, + /*required=*/false); + if (match.has_value()) { + set_string_matcher(StringMatcher::Create(type, *match, ignore_case)); + return true; + } + return false; + }; + if (check_match("exact", StringMatcher::Type::kExact) || + check_match("prefix", StringMatcher::Type::kPrefix) || + check_match("suffix", StringMatcher::Type::kSuffix) || + check_match("contains", StringMatcher::Type::kContains)) { + return; + } + auto regex_match = LoadJsonObjectField( + json.object_value(), args, "safeRegex", errors, + /*required=*/false); + if (regex_match.has_value()) { + set_string_matcher(StringMatcher::Create(StringMatcher::Type::kSafeRegex, + regex_match->regex, ignore_case)); + return; + } + if (errors->size() == original_error_size) { + errors->AddError("no valid matcher found"); } - return principal; } -Rbac::Policy ParsePolicy(const Json::Object& policy_json, - std::vector* error_list) { - Rbac::Policy policy; - const Json::Array* permissions_json_array; +// +// RbacConfig::RbacPolicy::Rules::Policy::PathMatch +// + +const JsonLoaderInterface* +RbacConfig::RbacPolicy::Rules::Policy::PathMatch::JsonLoader(const JsonArgs&) { + static const auto* loader = + JsonObjectLoader().Field("path", &PathMatch::path).Finish(); + return loader; +} + +// +// RbacConfig::RbacPolicy::Rules::Policy::Metadata +// + +const JsonLoaderInterface* +RbacConfig::RbacPolicy::Rules::Policy::Metadata::JsonLoader(const JsonArgs&) { + static const auto* loader = JsonObjectLoader() + .OptionalField("invert", &Metadata::invert) + .Finish(); + return loader; +} + +// +// RbacConfig::RbacPolicy::Rules::Policy::Permission::PermissionList +// + +const JsonLoaderInterface* +RbacConfig::RbacPolicy::Rules::Policy::Permission::PermissionList::JsonLoader( + const JsonArgs&) { + static const auto* loader = JsonObjectLoader() + .Field("rules", &PermissionList::rules) + .Finish(); + return loader; +} + +// +// RbacConfig::RbacPolicy::Rules::Policy::Permission +// + +std::vector> +RbacConfig::RbacPolicy::Rules::Policy::Permission::MakeRbacPermissionList( + std::vector permission_list) { std::vector> permissions; - if (ParseJsonObjectField(policy_json, "permissions", &permissions_json_array, - error_list)) { - for (size_t i = 0; i < permissions_json_array->size(); ++i) { - const Json::Object* permission_json; - if (!ExtractJsonType((*permissions_json_array)[i], - absl::StrFormat("permissions[%d]", i), - &permission_json, error_list)) { - continue; - } - std::vector permission_error_list; - permissions.emplace_back(std::make_unique( - ParsePermission(*permission_json, &permission_error_list))); - if (!permission_error_list.empty()) { - error_list->push_back(GRPC_ERROR_CREATE_FROM_VECTOR( - absl::StrFormat("permissions[%d]", i), &permission_error_list)); - } - } + permissions.reserve(permission_list.size()); + for (auto& rule : permission_list) { + permissions.emplace_back(std::move(rule.permission)); + } + return permissions; +} + +const JsonLoaderInterface* +RbacConfig::RbacPolicy::Rules::Policy::Permission::JsonLoader(const JsonArgs&) { + // All fields handled in JsonPostLoad(). + static const auto* loader = JsonObjectLoader().Finish(); + return loader; +} + +void RbacConfig::RbacPolicy::Rules::Policy::Permission::JsonPostLoad( + const Json& json, const JsonArgs& args, ValidationErrors* errors) { + const size_t original_error_size = errors->size(); + auto any = LoadJsonObjectField(json.object_value(), args, "any", errors, + /*required=*/false); + if (any.has_value()) { + permission = std::make_unique( + Rbac::Permission::MakeAnyPermission()); + return; + } + auto header = LoadJsonObjectField(json.object_value(), args, + "header", errors, + /*required=*/false); + if (header.has_value()) { + permission = std::make_unique( + Rbac::Permission::MakeHeaderPermission(std::move(header->matcher))); + return; + } + auto url_path = LoadJsonObjectField(json.object_value(), args, + "urlPath", errors, + /*required=*/false); + if (url_path.has_value()) { + permission = std::make_unique( + Rbac::Permission::MakePathPermission(url_path->path.matcher)); + return; } - const Json::Array* principals_json_array; + auto destination_ip = LoadJsonObjectField( + json.object_value(), args, "destinationIp", errors, + /*required=*/false); + if (destination_ip.has_value()) { + permission = std::make_unique( + Rbac::Permission::MakeDestIpPermission( + std::move(destination_ip->cidr_range))); + return; + } + auto destination_port = LoadJsonObjectField( + json.object_value(), args, "destinationPort", errors, + /*required=*/false); + if (destination_port.has_value()) { + permission = std::make_unique( + Rbac::Permission::MakeDestPortPermission(*destination_port)); + return; + } + auto metadata = LoadJsonObjectField(json.object_value(), args, + "metadata", errors, + /*required=*/false); + if (metadata.has_value()) { + permission = std::make_unique( + Rbac::Permission::MakeMetadataPermission(metadata->invert)); + return; + } + auto requested_server_name = LoadJsonObjectField( + json.object_value(), args, "requestedServerName", errors, + /*required=*/false); + if (requested_server_name.has_value()) { + permission = std::make_unique( + Rbac::Permission::MakeReqServerNamePermission( + std::move(requested_server_name->matcher))); + return; + } + auto rules = LoadJsonObjectField(json.object_value(), args, + "andRules", errors, + /*required=*/false); + if (rules.has_value()) { + permission = + std::make_unique(Rbac::Permission::MakeAndPermission( + MakeRbacPermissionList(std::move(rules->rules)))); + return; + } + rules = LoadJsonObjectField(json.object_value(), args, + "orRules", errors, + /*required=*/false); + if (rules.has_value()) { + permission = + std::make_unique(Rbac::Permission::MakeOrPermission( + MakeRbacPermissionList(std::move(rules->rules)))); + return; + } + auto not_rule = LoadJsonObjectField(json.object_value(), args, + "notRule", errors, + /*required=*/false); + if (not_rule.has_value()) { + permission = std::make_unique( + Rbac::Permission::MakeNotPermission(std::move(*not_rule->permission))); + return; + } + if (errors->size() == original_error_size) { + errors->AddError("no valid rule found"); + } +} + +// +// RbacConfig::RbacPolicy::Rules::Policy::Principal::PrincipalList +// + +const JsonLoaderInterface* +RbacConfig::RbacPolicy::Rules::Policy::Principal::PrincipalList::JsonLoader( + const JsonArgs&) { + static const auto* loader = JsonObjectLoader() + .Field("ids", &PrincipalList::ids) + .Finish(); + return loader; +} + +// +// RbacConfig::RbacPolicy::Rules::Policy::Principal::Authenticated +// + +const JsonLoaderInterface* +RbacConfig::RbacPolicy::Rules::Policy::Principal::Authenticated::JsonLoader( + const JsonArgs&) { + static const auto* loader = + JsonObjectLoader() + .OptionalField("principalName", &Authenticated::principal_name) + .Finish(); + return loader; +} + +// +// RbacConfig::RbacPolicy::Rules::Policy::Principal +// + +std::vector> +RbacConfig::RbacPolicy::Rules::Policy::Principal::MakeRbacPrincipalList( + std::vector principal_list) { std::vector> principals; - if (ParseJsonObjectField(policy_json, "principals", &principals_json_array, - error_list)) { - for (size_t i = 0; i < principals_json_array->size(); ++i) { - const Json::Object* principal_json; - if (!ExtractJsonType((*principals_json_array)[i], - absl::StrFormat("principals[%d]", i), - &principal_json, error_list)) { - continue; - } - std::vector principal_error_list; - principals.emplace_back(std::make_unique( - ParsePrincipal(*principal_json, &principal_error_list))); - if (!principal_error_list.empty()) { - error_list->push_back(GRPC_ERROR_CREATE_FROM_VECTOR( - absl::StrFormat("principals[%d]", i), &principal_error_list)); - } - } + principals.reserve(principal_list.size()); + for (auto& id : principal_list) { + principals.emplace_back(std::move(id.principal)); } - policy.permissions = - Rbac::Permission::MakeOrPermission(std::move(permissions)); - policy.principals = Rbac::Principal::MakeOrPrincipal(std::move(principals)); - return policy; + return principals; } -Rbac ParseRbac(const Json::Object& rbac_json, - std::vector* error_list) { - Rbac rbac; - const Json::Object* rules_json; - if (!ParseJsonObjectField(rbac_json, "rules", &rules_json, error_list, - /*required=*/false)) { - // No enforcing to be applied. An empty deny policy with an empty map is - // equivalent to no enforcing. - return Rbac(Rbac::Action::kDeny, {}); +const JsonLoaderInterface* +RbacConfig::RbacPolicy::Rules::Policy::Principal::JsonLoader(const JsonArgs&) { + // All fields handled in JsonPostLoad(). + static const auto* loader = JsonObjectLoader().Finish(); + return loader; +} + +void RbacConfig::RbacPolicy::Rules::Policy::Principal::JsonPostLoad( + const Json& json, const JsonArgs& args, ValidationErrors* errors) { + const size_t original_error_size = errors->size(); + auto any = LoadJsonObjectField(json.object_value(), args, "any", errors, + /*required=*/false); + if (any.has_value()) { + principal = + std::make_unique(Rbac::Principal::MakeAnyPrincipal()); + return; } - int action; - if (ParseJsonObjectField(*rules_json, "action", &action, error_list)) { - if (action > 1) { - error_list->push_back(GRPC_ERROR_CREATE("Unknown action")); + auto authenticated = LoadJsonObjectField( + json.object_value(), args, "authenticated", errors, + /*required=*/false); + if (authenticated.has_value()) { + if (authenticated->principal_name.has_value()) { + principal = std::make_unique( + Rbac::Principal::MakeAuthenticatedPrincipal( + std::move(authenticated->principal_name->matcher))); + } else { + // No principalName found. Match for all users. + principal = std::make_unique( + Rbac::Principal::MakeAnyPrincipal()); } + return; + } + auto cidr_range = LoadJsonObjectField(json.object_value(), args, + "sourceIp", errors, + /*required=*/false); + if (cidr_range.has_value()) { + principal = std::make_unique( + Rbac::Principal::MakeSourceIpPrincipal( + std::move(cidr_range->cidr_range))); + return; + } + cidr_range = LoadJsonObjectField(json.object_value(), args, + "directRemoteIp", errors, + /*required=*/false); + if (cidr_range.has_value()) { + principal = std::make_unique( + Rbac::Principal::MakeDirectRemoteIpPrincipal( + std::move(cidr_range->cidr_range))); + return; + } + cidr_range = LoadJsonObjectField(json.object_value(), args, + "remoteIp", errors, + /*required=*/false); + if (cidr_range.has_value()) { + principal = std::make_unique( + Rbac::Principal::MakeRemoteIpPrincipal( + std::move(cidr_range->cidr_range))); + return; + } + auto header = LoadJsonObjectField(json.object_value(), args, + "header", errors, + /*required=*/false); + if (header.has_value()) { + principal = std::make_unique( + Rbac::Principal::MakeHeaderPrincipal(std::move(header->matcher))); + return; + } + auto url_path = LoadJsonObjectField(json.object_value(), args, + "urlPath", errors, + /*required=*/false); + if (url_path.has_value()) { + principal = std::make_unique( + Rbac::Principal::MakePathPrincipal(std::move(url_path->path.matcher))); + return; + } + auto metadata = LoadJsonObjectField(json.object_value(), args, + "metadata", errors, + /*required=*/false); + if (metadata.has_value()) { + principal = std::make_unique( + Rbac::Principal::MakeMetadataPrincipal(metadata->invert)); + return; + } + auto ids = LoadJsonObjectField(json.object_value(), args, + "andIds", errors, + /*required=*/false); + if (ids.has_value()) { + principal = + std::make_unique(Rbac::Principal::MakeAndPrincipal( + MakeRbacPrincipalList(std::move(ids->ids)))); + return; + } + ids = LoadJsonObjectField(json.object_value(), args, "orIds", + errors, + /*required=*/false); + if (ids.has_value()) { + principal = + std::make_unique(Rbac::Principal::MakeOrPrincipal( + MakeRbacPrincipalList(std::move(ids->ids)))); + return; + } + auto not_rule = + LoadJsonObjectField(json.object_value(), args, "notId", errors, + /*required=*/false); + if (not_rule.has_value()) { + principal = std::make_unique( + Rbac::Principal::MakeNotPrincipal(std::move(*not_rule->principal))); + return; + } + if (errors->size() == original_error_size) { + errors->AddError("no valid id found"); } +} + +// +// RbacConfig::RbacPolicy::Rules::Policy +// + +Rbac::Policy RbacConfig::RbacPolicy::Rules::Policy::TakeAsRbacPolicy() { + Rbac::Policy policy; + policy.permissions = Rbac::Permission::MakeOrPermission( + Permission::MakeRbacPermissionList(std::move(permissions))); + policy.principals = Rbac::Principal::MakeOrPrincipal( + Principal::MakeRbacPrincipalList(std::move(principals))); + return policy; +} + +const JsonLoaderInterface* RbacConfig::RbacPolicy::Rules::Policy::JsonLoader( + const JsonArgs&) { + static const auto* loader = JsonObjectLoader() + .Field("permissions", &Policy::permissions) + .Field("principals", &Policy::principals) + .Finish(); + return loader; +} + +// +// RbacConfig::RbacPolicy::Rules +// + +Rbac RbacConfig::RbacPolicy::Rules::TakeAsRbac() { + Rbac rbac; rbac.action = static_cast(action); - const Json::Object* policies_json; - if (ParseJsonObjectField(*rules_json, "policies", &policies_json, error_list, - /*required=*/false)) { - for (const auto& entry : *policies_json) { - std::vector policy_error_list; - rbac.policies.emplace( - entry.first, - ParsePolicy(entry.second.object_value(), &policy_error_list)); - if (!policy_error_list.empty()) { - error_list->push_back(GRPC_ERROR_CREATE_FROM_VECTOR( - absl::StrFormat("policies key:'%s'", entry.first.c_str()), - &policy_error_list)); - } - } + for (auto& p : policies) { + rbac.policies.emplace(p.first, p.second.TakeAsRbacPolicy()); } return rbac; } -std::vector ParseRbacArray(const Json::Array& policies_json_array, - std::vector* error_list) { - std::vector policies; - for (size_t i = 0; i < policies_json_array.size(); ++i) { - const Json::Object* rbac_json; - if (!ExtractJsonType(policies_json_array[i], - absl::StrFormat("rbacPolicy[%d]", i), &rbac_json, - error_list)) { - continue; - } - std::vector rbac_policy_error_list; - policies.emplace_back(ParseRbac(*rbac_json, &rbac_policy_error_list)); - if (!rbac_policy_error_list.empty()) { - error_list->push_back(GRPC_ERROR_CREATE_FROM_VECTOR( - absl::StrFormat("rbacPolicy[%d]", i), &rbac_policy_error_list)); - } +const JsonLoaderInterface* RbacConfig::RbacPolicy::Rules::JsonLoader( + const JsonArgs&) { + static const auto* loader = JsonObjectLoader() + .Field("action", &Rules::action) + .OptionalField("policies", &Rules::policies) + .Finish(); + return loader; +} + +void RbacConfig::RbacPolicy::Rules::JsonPostLoad(const Json&, const JsonArgs&, + ValidationErrors* errors) { + // Validate action field. + auto rbac_action = static_cast(action); + if (rbac_action != Rbac::Action::kAllow && + rbac_action != Rbac::Action::kDeny) { + ValidationErrors::ScopedField field(errors, ".action"); + errors->AddError("unknown action"); + } +} + +// +// RbacConfig::RbacPolicy +// + +Rbac RbacConfig::RbacPolicy::TakeAsRbac() { + if (!rules.has_value()) { + // No enforcing to be applied. An empty deny policy with an empty map + // is equivalent to no enforcing. + return Rbac(Rbac::Action::kDeny, {}); } - return policies; + return rules->TakeAsRbac(); +} + +const JsonLoaderInterface* RbacConfig::RbacPolicy::JsonLoader(const JsonArgs&) { + static const auto* loader = JsonObjectLoader() + .OptionalField("rules", &RbacPolicy::rules) + .Finish(); + return loader; +} + +// +// RbacConfig +// + +std::vector RbacConfig::TakeAsRbacList() { + std::vector rbac_list; + for (auto& rbac_policy : rbac_policies) { + rbac_list.emplace_back(rbac_policy.TakeAsRbac()); + } + return rbac_list; +} + +const JsonLoaderInterface* RbacConfig::JsonLoader(const JsonArgs&) { + static const auto* loader = + JsonObjectLoader() + .Field("rbacPolicy", &RbacConfig::rbac_policies) + .Finish(); + return loader; } } // namespace -absl::StatusOr> +std::unique_ptr RbacServiceConfigParser::ParsePerMethodParams(const ChannelArgs& args, - const Json& json) { + const Json& json, + ValidationErrors* errors) { // Only parse rbac policy if the channel arg is present if (!args.GetBool(GRPC_ARG_PARSE_RBAC_METHOD_CONFIG).value_or(false)) { return nullptr; } - std::vector rbac_policies; - std::vector error_list; - const Json::Array* policies_json_array; - if (ParseJsonObjectField(json.object_value(), "rbacPolicy", - &policies_json_array, &error_list)) { - rbac_policies = ParseRbacArray(*policies_json_array, &error_list); - } - grpc_error_handle error = - GRPC_ERROR_CREATE_FROM_VECTOR("Rbac parser", &error_list); - if (!error.ok()) { - absl::Status status = absl::InvalidArgumentError(absl::StrCat( - "error parsing RBAC method parameters: ", StatusToString(error))); - return status; - } + auto rbac_config = LoadFromJson(json, JsonArgs(), errors); + std::vector rbac_policies = rbac_config.TakeAsRbacList(); if (rbac_policies.empty()) return nullptr; return std::make_unique(std::move(rbac_policies)); } diff --git a/src/core/ext/filters/rbac/rbac_service_config_parser.h b/src/core/ext/filters/rbac/rbac_service_config_parser.h index 06f83827b7b..3419e4b4c92 100644 --- a/src/core/ext/filters/rbac/rbac_service_config_parser.h +++ b/src/core/ext/filters/rbac/rbac_service_config_parser.h @@ -26,11 +26,11 @@ #include #include -#include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/config/core_configuration.h" +#include "src/core/lib/gprpp/validation_errors.h" #include "src/core/lib/json/json.h" #include "src/core/lib/security/authorization/grpc_authorization_engine.h" #include "src/core/lib/security/authorization/rbac_policy.h" @@ -69,8 +69,9 @@ class RbacServiceConfigParser : public ServiceConfigParser::Parser { public: absl::string_view name() const override { return parser_name(); } // Parses the per-method service config for rbac filter. - absl::StatusOr> - ParsePerMethodParams(const ChannelArgs& args, const Json& json) override; + std::unique_ptr ParsePerMethodParams( + const ChannelArgs& args, const Json& json, + ValidationErrors* errors) override; // Returns the parser index for RbacServiceConfigParser. static size_t ParserIndex(); // Registers RbacServiceConfigParser to ServiceConfigParser. diff --git a/src/core/ext/xds/xds_http_fault_filter.cc b/src/core/ext/xds/xds_http_fault_filter.cc index 11e7b1b38de..bf6207ff45b 100644 --- a/src/core/ext/xds/xds_http_fault_filter.cc +++ b/src/core/ext/xds/xds_http_fault_filter.cc @@ -38,7 +38,7 @@ #include #include "src/core/ext/filters/fault_injection/fault_injection_filter.h" -#include "src/core/ext/filters/fault_injection/service_config_parser.h" +#include "src/core/ext/filters/fault_injection/fault_injection_service_config_parser.h" #include "src/core/ext/xds/xds_common_types.h" #include "src/core/ext/xds/xds_http_filters.h" #include "src/core/lib/channel/channel_args.h" diff --git a/src/core/ext/xds/xds_http_rbac_filter.cc b/src/core/ext/xds/xds_http_rbac_filter.cc index bf4cd463045..e8cd4c79c00 100644 --- a/src/core/ext/xds/xds_http_rbac_filter.cc +++ b/src/core/ext/xds/xds_http_rbac_filter.cc @@ -165,10 +165,6 @@ Json ParsePathMatcherToJson(const envoy_type_matcher_v3_PathMatcher* matcher, return Json::Object{{"path", std::move(path_json)}}; } -Json ParseUInt32ValueToJson(const google_protobuf_UInt32Value* value) { - return Json::Object{{"value", google_protobuf_UInt32Value_value(value)}}; -} - Json ParseCidrRangeToJson(const envoy_config_core_v3_CidrRange* range) { Json::Object json; json.emplace("addressPrefix", @@ -176,7 +172,7 @@ Json ParseCidrRangeToJson(const envoy_config_core_v3_CidrRange* range) { envoy_config_core_v3_CidrRange_address_prefix(range))); const auto* prefix_len = envoy_config_core_v3_CidrRange_prefix_len(range); if (prefix_len != nullptr) { - json.emplace("prefixLen", ParseUInt32ValueToJson(prefix_len)); + json.emplace("prefixLen", google_protobuf_UInt32Value_value(prefix_len)); } return json; } diff --git a/src/core/lib/json/json_object_loader.h b/src/core/lib/json/json_object_loader.h index 095e8860982..9a768161ae7 100644 --- a/src/core/lib/json/json_object_loader.h +++ b/src/core/lib/json/json_object_loader.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -392,6 +393,26 @@ class AutoLoader> final : public LoadOptional { ~AutoLoader() = default; }; +// Specializations of AutoLoader for std::unique_ptr<>. +template +class AutoLoader> final : public LoadOptional { + public: + void* Emplace(void* dst) const final { + auto& p = *static_cast*>(dst); + p = std::make_unique(); + return p.get(); + } + void Reset(void* dst) const final { + static_cast*>(dst)->reset(); + } + const LoaderInterface* ElementLoader() const final { + return LoaderForType(); + } + + private: + ~AutoLoader() = default; +}; + // Implementation of aforementioned LoaderForType. // Simply keeps a static AutoLoader and returns a pointer to that. template diff --git a/src/core/lib/service_config/service_config.h b/src/core/lib/service_config/service_config.h index 064c58b80ff..fa73d55e337 100644 --- a/src/core/lib/service_config/service_config.h +++ b/src/core/lib/service_config/service_config.h @@ -58,7 +58,7 @@ namespace grpc_core { // TODO(roth): Consider stripping this down further to the completely minimal -// interface requied to be exposed as part of the resolver API. +// interface required to be exposed as part of the resolver API. class ServiceConfig : public RefCounted { public: static absl::string_view ChannelArgName() { diff --git a/src/core/lib/service_config/service_config_impl.cc b/src/core/lib/service_config/service_config_impl.cc index 62de724587c..da3fa5d5a69 100644 --- a/src/core/lib/service_config/service_config_impl.cc +++ b/src/core/lib/service_config/service_config_impl.cc @@ -20,197 +20,151 @@ #include -#include #include #include -#include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" -#include "absl/strings/str_join.h" - -#include +#include "absl/types/optional.h" #include "src/core/lib/config/core_configuration.h" #include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/gprpp/validation_errors.h" #include "src/core/lib/json/json.h" +#include "src/core/lib/json/json_args.h" +#include "src/core/lib/json/json_object_loader.h" #include "src/core/lib/service_config/service_config_parser.h" #include "src/core/lib/slice/slice.h" #include "src/core/lib/slice/slice_internal.h" namespace grpc_core { +namespace { + +struct MethodConfig { + struct Name { + absl::optional service; + absl::optional method; + + static const JsonLoaderInterface* JsonLoader(const JsonArgs&) { + static const auto* loader = JsonObjectLoader() + .OptionalField("service", &Name::service) + .OptionalField("method", &Name::method) + .Finish(); + return loader; + } + + void JsonPostLoad(const Json&, const JsonArgs&, ValidationErrors* errors) { + if (!service.has_value() && method.has_value()) { + errors->AddError("method name populated without service name"); + } + } + + std::string Path() const { + if (!service.has_value() || service->empty()) return ""; + return absl::StrCat("/", *service, "/", + method.has_value() ? *method : ""); + } + }; + + std::vector names; + + static const JsonLoaderInterface* JsonLoader(const JsonArgs&) { + static const auto* loader = JsonObjectLoader() + .OptionalField("name", &MethodConfig::names) + .Finish(); + return loader; + } +}; + +} // namespace + absl::StatusOr> ServiceConfigImpl::Create( const ChannelArgs& args, absl::string_view json_string) { auto json = Json::Parse(json_string); if (!json.ok()) return json.status(); - absl::Status status; - auto service_config = MakeRefCounted( - args, std::string(json_string), std::move(*json), &status); - if (!status.ok()) return status; + ValidationErrors errors; + auto service_config = Create(args, *json, json_string, &errors); + if (!errors.ok()) return errors.status("errors validating service config"); return service_config; } -ServiceConfigImpl::ServiceConfigImpl(const ChannelArgs& args, - std::string json_string, Json json, - absl::Status* status) - : json_string_(std::move(json_string)), json_(std::move(json)) { - GPR_DEBUG_ASSERT(status != nullptr); - if (json_.type() != Json::Type::OBJECT) { - *status = absl::InvalidArgumentError("JSON value is not an object"); - return; - } - std::vector errors; - auto parsed_global_configs = - CoreConfiguration::Get().service_config_parser().ParseGlobalParameters( - args, json_); - if (!parsed_global_configs.ok()) { - errors.emplace_back(parsed_global_configs.status().message()); - } else { - parsed_global_configs_ = std::move(*parsed_global_configs); - } - absl::Status local_status = ParsePerMethodParams(args); - if (!local_status.ok()) errors.emplace_back(local_status.message()); - if (!errors.empty()) { - *status = absl::InvalidArgumentError(absl::StrCat( - "Service config parsing errors: [", absl::StrJoin(errors, "; "), "]")); - } -} - -ServiceConfigImpl::~ServiceConfigImpl() { - for (auto& p : parsed_method_configs_map_) { - CSliceUnref(p.first); - } +RefCountedPtr ServiceConfigImpl::Create( + const ChannelArgs& args, const Json& json, ValidationErrors* errors) { + return Create(args, json, json.Dump(), errors); } -absl::Status ServiceConfigImpl::ParseJsonMethodConfig(const ChannelArgs& args, - const Json& json, - size_t index) { - std::vector errors; - const ServiceConfigParser::ParsedConfigVector* vector_ptr = nullptr; - // Parse method config with each registered parser. - auto parsed_configs_or = - CoreConfiguration::Get().service_config_parser().ParsePerMethodParameters( - args, json); - if (!parsed_configs_or.ok()) { - errors.emplace_back(parsed_configs_or.status().message()); - } else { - auto parsed_configs = - std::make_unique( - std::move(*parsed_configs_or)); - parsed_method_config_vectors_storage_.push_back(std::move(parsed_configs)); - vector_ptr = parsed_method_config_vectors_storage_.back().get(); +RefCountedPtr ServiceConfigImpl::Create( + const ChannelArgs& args, const Json& json, absl::string_view json_string, + ValidationErrors* errors) { + if (json.type() != Json::Type::OBJECT) { + errors->AddError("is not an object"); + return nullptr; } - // Add an entry for each path. - auto it = json.object_value().find("name"); - if (it != json.object_value().end()) { - if (it->second.type() != Json::Type::ARRAY) { - errors.emplace_back("field:name error:not of type Array"); - } else { - const Json::Array& name_array = it->second.array_value(); - for (const Json& name : name_array) { - absl::StatusOr path = ParseJsonMethodName(name); - if (!path.ok()) { - errors.emplace_back(path.status().message()); + auto service_config = MakeRefCounted(); + service_config->json_string_ = std::string(json_string); + // Parse global parameters. + service_config->parsed_global_configs_ = + CoreConfiguration::Get().service_config_parser().ParseGlobalParameters( + args, json, errors); + // Parse per-method parameters. + auto method_configs = LoadJsonObjectField>( + json.object_value(), JsonArgs(), "methodConfig", errors, + /*required=*/false); + if (method_configs.has_value()) { + service_config->parsed_method_config_vectors_storage_.reserve( + method_configs->size()); + for (size_t i = 0; i < method_configs->size(); ++i) { + const Json::Object& method_config_json = (*method_configs)[i]; + ValidationErrors::ScopedField field( + errors, absl::StrCat(".methodConfig[", i, "]")); + // Have each parser read this method config. + auto parsed_configs = + CoreConfiguration::Get() + .service_config_parser() + .ParsePerMethodParameters(args, method_config_json, errors); + // Store the parsed configs. + service_config->parsed_method_config_vectors_storage_.push_back( + std::move(parsed_configs)); + const ServiceConfigParser::ParsedConfigVector* vector_ptr = + &service_config->parsed_method_config_vectors_storage_.back(); + // Parse the names. + auto method_config = + LoadFromJson(method_config_json, JsonArgs(), errors); + for (size_t j = 0; j < method_config.names.size(); ++j) { + ValidationErrors::ScopedField field(errors, + absl::StrCat(".name[", j, "]")); + std::string path = method_config.names[j].Path(); + if (path.empty()) { + if (service_config->default_method_config_vector_ != nullptr) { + errors->AddError("duplicate default method config"); + } + service_config->default_method_config_vector_ = vector_ptr; } else { - if (path->empty()) { - if (default_method_config_vector_ != nullptr) { - errors.emplace_back( - "field:name error:multiple default method configs"); - } - default_method_config_vector_ = vector_ptr; + grpc_slice key = grpc_slice_from_cpp_string(std::move(path)); + // If the key is not already present in the map, this will + // store a ref to the key in the map. + auto& value = service_config->parsed_method_configs_map_[key]; + if (value != nullptr) { + errors->AddError(absl::StrCat("multiple method configs for path ", + StringViewFromSlice(key))); + // The map entry already existed, so we need to unref the + // key we just created. + CSliceUnref(key); } else { - grpc_slice key = grpc_slice_from_cpp_string(std::move(*path)); - // If the key is not already present in the map, this will - // store a ref to the key in the map. - auto& value = parsed_method_configs_map_[key]; - if (value != nullptr) { - errors.emplace_back( - "field:name error:multiple method configs with same name"); - // The map entry already existed, so we need to unref the - // key we just created. - CSliceUnref(key); - } else { - value = vector_ptr; - } + value = vector_ptr; } } } } } - if (!errors.empty()) { - return absl::InvalidArgumentError( - absl::StrCat("index ", index, ": [", absl::StrJoin(errors, "; "), "]")); - } - return absl::OkStatus(); -} - -absl::Status ServiceConfigImpl::ParsePerMethodParams(const ChannelArgs& args) { - auto it = json_.object_value().find("methodConfig"); - if (it == json_.object_value().end()) return absl::OkStatus(); - if (it->second.type() != Json::Type::ARRAY) { - return absl::InvalidArgumentError("field must be of type array"); - } - std::vector errors; - for (size_t i = 0; i < it->second.array_value().size(); ++i) { - const Json& method_config = it->second.array_value()[i]; - if (method_config.type() != Json::Type::OBJECT) { - errors.emplace_back(absl::StrCat("index ", i, ": not of type Object")); - } else { - absl::Status status = ParseJsonMethodConfig(args, method_config, i); - if (!status.ok()) errors.emplace_back(status.message()); - } - } - if (!errors.empty()) { - return absl::InvalidArgumentError(absl::StrCat( - "errors parsing methodConfig: [", absl::StrJoin(errors, "; "), "]")); - } - return absl::OkStatus(); + return service_config; } -absl::StatusOr ServiceConfigImpl::ParseJsonMethodName( - const Json& json) { - if (json.type() != Json::Type::OBJECT) { - return absl::InvalidArgumentError("field:name error:type is not object"); - } - // Find service name. - const std::string* service_name = nullptr; - auto it = json.object_value().find("service"); - if (it != json.object_value().end() && - it->second.type() != Json::Type::JSON_NULL) { - if (it->second.type() != Json::Type::STRING) { - return absl::InvalidArgumentError( - "field:name error: field:service error:not of type string"); - } - if (!it->second.string_value().empty()) { - service_name = &it->second.string_value(); - } - } - const std::string* method_name = nullptr; - // Find method name. - it = json.object_value().find("method"); - if (it != json.object_value().end() && - it->second.type() != Json::Type::JSON_NULL) { - if (it->second.type() != Json::Type::STRING) { - return absl::InvalidArgumentError( - "field:name error: field:method error:not of type string"); - } - if (!it->second.string_value().empty()) { - method_name = &it->second.string_value(); - } - } - // If neither service nor method are specified, it's the default. - // Method name may not be specified without service name. - if (service_name == nullptr) { - if (method_name != nullptr) { - return absl::InvalidArgumentError( - "field:name error:method name populated without service name"); - } - return ""; +ServiceConfigImpl::~ServiceConfigImpl() { + for (auto& p : parsed_method_configs_map_) { + CSliceUnref(p.first); } - // Construct path. - return absl::StrCat("/", *service_name, "/", - method_name == nullptr ? "" : *method_name); } const ServiceConfigParser::ParsedConfigVector* diff --git a/src/core/lib/service_config/service_config_impl.h b/src/core/lib/service_config/service_config_impl.h index 6aa6f8bbc95..016be254cb0 100644 --- a/src/core/lib/service_config/service_config_impl.h +++ b/src/core/lib/service_config/service_config_impl.h @@ -26,7 +26,6 @@ #include #include -#include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" @@ -35,6 +34,7 @@ #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/gprpp/validation_errors.h" #include "src/core/lib/json/json.h" #include "src/core/lib/service_config/service_config.h" #include "src/core/lib/service_config/service_config_parser.h" @@ -72,8 +72,15 @@ class ServiceConfigImpl final : public ServiceConfig { static absl::StatusOr> Create( const ChannelArgs& args, absl::string_view json_string); - ServiceConfigImpl(const ChannelArgs& args, std::string json_string, Json json, - absl::Status* status); + // Alternate forms that are useful in edge cases. + static RefCountedPtr Create(const ChannelArgs& args, + const Json& json, + absl::string_view json_string, + ValidationErrors* errors); + static RefCountedPtr Create(const ChannelArgs& args, + const Json& json, + ValidationErrors* errors); + ~ServiceConfigImpl() override; absl::string_view json_string() const override { return json_string_; } @@ -94,20 +101,10 @@ class ServiceConfigImpl final : public ServiceConfig { const grpc_slice& path) const override; private: - // Helper functions for parsing the method configs. - absl::Status ParsePerMethodParams(const ChannelArgs& args); - absl::Status ParseJsonMethodConfig(const ChannelArgs& args, const Json& json, - size_t index); - - // Returns a path string for the JSON name object specified by json. - // Sets *error on error. - static absl::StatusOr ParseJsonMethodName(const Json& json); - std::string json_string_; Json json_; - std::vector> - parsed_global_configs_; + ServiceConfigParser::ParsedConfigVector parsed_global_configs_; // A map from the method name to the parsed config vector. Note that we are // using a raw pointer and not a unique pointer so that we can use the same // vector for multiple names. @@ -118,11 +115,11 @@ class ServiceConfigImpl final : public ServiceConfig { const ServiceConfigParser::ParsedConfigVector* default_method_config_vector_ = nullptr; // Storage for all the vectors that are being used in - // parsed_method_configs_table_. - std::vector> + // parsed_method_configs_map_ and default_method_config_vector_. + std::vector parsed_method_config_vectors_storage_; }; } // namespace grpc_core -#endif /* GRPC_CORE_LIB_SERVICE_CONFIG_SERVICE_CONFIG_IMPL_H */ +#endif // GRPC_CORE_LIB_SERVICE_CONFIG_SERVICE_CONFIG_IMPL_H diff --git a/src/core/lib/service_config/service_config_parser.cc b/src/core/lib/service_config/service_config_parser.cc index 0ec4be9fd79..c97f7daae55 100644 --- a/src/core/lib/service_config/service_config_parser.cc +++ b/src/core/lib/service_config/service_config_parser.cc @@ -22,9 +22,7 @@ #include -#include "absl/status/status.h" #include "absl/strings/str_cat.h" -#include "absl/strings/str_join.h" #include @@ -49,43 +47,28 @@ void ServiceConfigParser::Builder::RegisterParser( registered_parsers_.emplace_back(std::move(parser)); } -absl::StatusOr +ServiceConfigParser::ParsedConfigVector ServiceConfigParser::ParseGlobalParameters(const ChannelArgs& args, - const Json& json) const { + const Json& json, + ValidationErrors* errors) const { ParsedConfigVector parsed_global_configs; - std::vector errors; - for (size_t i = 0; i < registered_parsers_.size(); i++) { - auto parsed_config = registered_parsers_[i]->ParseGlobalParams(args, json); - if (!parsed_config.ok()) { - errors.emplace_back(parsed_config.status().message()); - } else { - parsed_global_configs.push_back(std::move(*parsed_config)); - } - } - if (!errors.empty()) { - return absl::InvalidArgumentError(absl::StrJoin(errors, "; ")); + for (auto& parser : registered_parsers_) { + parsed_global_configs.push_back( + parser->ParseGlobalParams(args, json, errors)); } - return std::move(parsed_global_configs); + return parsed_global_configs; } -absl::StatusOr +ServiceConfigParser::ParsedConfigVector ServiceConfigParser::ParsePerMethodParameters(const ChannelArgs& args, - const Json& json) const { + const Json& json, + ValidationErrors* errors) const { ParsedConfigVector parsed_method_configs; - std::vector errors; - for (size_t i = 0; i < registered_parsers_.size(); ++i) { - auto parsed_config = - registered_parsers_[i]->ParsePerMethodParams(args, json); - if (!parsed_config.ok()) { - errors.emplace_back(parsed_config.status().message()); - } else { - parsed_method_configs.push_back(std::move(*parsed_config)); - } - } - if (!errors.empty()) { - return absl::InvalidArgumentError(absl::StrJoin(errors, "; ")); + for (auto& parser : registered_parsers_) { + parsed_method_configs.push_back( + parser->ParsePerMethodParams(args, json, errors)); } - return std::move(parsed_method_configs); + return parsed_method_configs; } size_t ServiceConfigParser::GetParserIndex(absl::string_view name) const { diff --git a/src/core/lib/service_config/service_config_parser.h b/src/core/lib/service_config/service_config_parser.h index 600b1624028..ac0e718448c 100644 --- a/src/core/lib/service_config/service_config_parser.h +++ b/src/core/lib/service_config/service_config_parser.h @@ -26,10 +26,10 @@ #include #include -#include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gprpp/validation_errors.h" #include "src/core/lib/json/json.h" namespace grpc_core { @@ -52,13 +52,15 @@ class ServiceConfigParser { virtual absl::string_view name() const = 0; - virtual absl::StatusOr> ParseGlobalParams( - const ChannelArgs& /*args*/, const Json& /*json*/) { + virtual std::unique_ptr ParseGlobalParams( + const ChannelArgs& /*args*/, const Json& /*json*/, + ValidationErrors* /*errors*/) { return nullptr; } - virtual absl::StatusOr> ParsePerMethodParams( - const ChannelArgs& /*args*/, const Json& /*json*/) { + virtual std::unique_ptr ParsePerMethodParams( + const ChannelArgs& /*args*/, const Json& /*json*/, + ValidationErrors* /*errors*/) { return nullptr; } }; @@ -80,11 +82,13 @@ class ServiceConfigParser { ServiceConfigParserList registered_parsers_; }; - absl::StatusOr ParseGlobalParameters( - const ChannelArgs& args, const Json& json) const; + ParsedConfigVector ParseGlobalParameters(const ChannelArgs& args, + const Json& json, + ValidationErrors* errors) const; - absl::StatusOr ParsePerMethodParameters( - const ChannelArgs& args, const Json& json) const; + ParsedConfigVector ParsePerMethodParameters(const ChannelArgs& args, + const Json& json, + ValidationErrors* errors) const; // Return the index for a given registered parser. // If there is an error, return -1. @@ -98,4 +102,4 @@ class ServiceConfigParser { } // namespace grpc_core -#endif /* GRPC_CORE_LIB_SERVICE_CONFIG_SERVICE_CONFIG_PARSER_H */ +#endif // GRPC_CORE_LIB_SERVICE_CONFIG_SERVICE_CONFIG_PARSER_H diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index ab797e7460f..7294e274f6b 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -25,6 +25,7 @@ CORE_SOURCE_FILES = [ 'src/core/ext/filters/client_channel/client_channel_channelz.cc', 'src/core/ext/filters/client_channel/client_channel_factory.cc', 'src/core/ext/filters/client_channel/client_channel_plugin.cc', + 'src/core/ext/filters/client_channel/client_channel_service_config.cc', 'src/core/ext/filters/client_channel/config_selector.cc', 'src/core/ext/filters/client_channel/dynamic_filters.cc', 'src/core/ext/filters/client_channel/global_subchannel_pool.cc', @@ -66,7 +67,6 @@ CORE_SOURCE_FILES = [ 'src/core/ext/filters/client_channel/resolver/polling_resolver.cc', 'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc', 'src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc', - 'src/core/ext/filters/client_channel/resolver_result_parsing.cc', 'src/core/ext/filters/client_channel/retry_filter.cc', 'src/core/ext/filters/client_channel/retry_service_config.cc', 'src/core/ext/filters/client_channel/retry_throttle.cc', @@ -76,7 +76,7 @@ CORE_SOURCE_FILES = [ 'src/core/ext/filters/client_channel/subchannel_stream_client.cc', 'src/core/ext/filters/deadline/deadline_filter.cc', 'src/core/ext/filters/fault_injection/fault_injection_filter.cc', - 'src/core/ext/filters/fault_injection/service_config_parser.cc', + 'src/core/ext/filters/fault_injection/fault_injection_service_config_parser.cc', 'src/core/ext/filters/http/client/http_client_filter.cc', 'src/core/ext/filters/http/client_authority_filter.cc', 'src/core/ext/filters/http/http_filters_plugin.cc', diff --git a/test/core/client_channel/BUILD b/test/core/client_channel/BUILD index 896b84b0907..55dba872fc3 100644 --- a/test/core/client_channel/BUILD +++ b/test/core/client_channel/BUILD @@ -60,8 +60,22 @@ grpc_cc_test( ) grpc_cc_test( - name = "service_config_test", - srcs = ["service_config_test.cc"], + name = "client_channel_service_config_test", + srcs = ["client_channel_service_config_test.cc"], + external_deps = [ + "gtest", + ], + language = "C++", + deps = [ + "//:gpr", + "//:grpc", + "//test/core/util:grpc_test_util", + ], +) + +grpc_cc_test( + name = "retry_service_config_test", + srcs = ["retry_service_config_test.cc"], external_deps = [ "gtest", ], diff --git a/test/core/client_channel/client_channel_service_config_test.cc b/test/core/client_channel/client_channel_service_config_test.cc new file mode 100644 index 00000000000..5fbf65b95e5 --- /dev/null +++ b/test/core/client_channel/client_channel_service_config_test.cc @@ -0,0 +1,315 @@ +// +// 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. +// + +#include "src/core/ext/filters/client_channel/client_channel_service_config.h" + +#include + +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "gtest/gtest.h" + +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/config/core_configuration.h" +#include "src/core/lib/gprpp/time.h" +#include "src/core/lib/service_config/service_config.h" +#include "src/core/lib/service_config/service_config_impl.h" +#include "src/core/lib/service_config/service_config_parser.h" +#include "test/core/util/test_config.h" + +namespace grpc_core { +namespace testing { + +// A regular expression to enter referenced or child errors. +#define CHILD_ERROR_TAG ".*children.*" + +class ClientChannelParserTest : public ::testing::Test { + protected: + void SetUp() override { + parser_index_ = + CoreConfiguration::Get().service_config_parser().GetParserIndex( + "client_channel"); + } + + size_t parser_index_; +}; + +TEST_F(ClientChannelParserTest, ValidLoadBalancingConfigPickFirst) { + const char* test_json = "{\"loadBalancingConfig\": [{\"pick_first\":{}}]}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + ASSERT_TRUE(service_config.ok()) << service_config.status(); + const auto* parsed_config = + static_cast( + (*service_config)->GetGlobalParsedConfig(parser_index_)); + auto lb_config = parsed_config->parsed_lb_config(); + EXPECT_EQ(lb_config->name(), "pick_first"); +} + +TEST_F(ClientChannelParserTest, ValidLoadBalancingConfigRoundRobin) { + const char* test_json = + "{\"loadBalancingConfig\": [{\"round_robin\":{}}, {}]}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + ASSERT_TRUE(service_config.ok()) << service_config.status(); + auto parsed_config = static_cast( + (*service_config)->GetGlobalParsedConfig(parser_index_)); + auto lb_config = parsed_config->parsed_lb_config(); + EXPECT_EQ(lb_config->name(), "round_robin"); +} + +TEST_F(ClientChannelParserTest, ValidLoadBalancingConfigGrpclb) { + const char* test_json = + "{\"loadBalancingConfig\": " + "[{\"grpclb\":{\"childPolicy\":[{\"pick_first\":{}}]}}]}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + ASSERT_TRUE(service_config.ok()) << service_config.status(); + const auto* parsed_config = + static_cast( + (*service_config)->GetGlobalParsedConfig(parser_index_)); + auto lb_config = parsed_config->parsed_lb_config(); + EXPECT_EQ(lb_config->name(), "grpclb"); +} + +TEST_F(ClientChannelParserTest, ValidLoadBalancingConfigXds) { + const char* test_json = + "{\n" + " \"loadBalancingConfig\":[\n" + " { \"does_not_exist\":{} },\n" + " { \"xds_cluster_resolver_experimental\":{\n" + " \"discoveryMechanisms\": [\n" + " { \"clusterName\": \"foo\",\n" + " \"type\": \"EDS\"\n" + " } ],\n" + " \"xdsLbPolicy\": [{\"round_robin\":{}}]\n" + " } }\n" + " ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + ASSERT_TRUE(service_config.ok()) << service_config.status(); + const auto* parsed_config = + static_cast( + (*service_config)->GetGlobalParsedConfig(parser_index_)); + auto lb_config = parsed_config->parsed_lb_config(); + EXPECT_EQ(lb_config->name(), "xds_cluster_resolver_experimental"); +} + +TEST_F(ClientChannelParserTest, UnknownLoadBalancingConfig) { + const char* test_json = "{\"loadBalancingConfig\": [{\"unknown\":{}}]}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:loadBalancingConfig error:" + "No known policies in list: unknown]") + << service_config.status(); +} + +TEST_F(ClientChannelParserTest, InvalidGrpclbLoadBalancingConfig) { + const char* test_json = + "{\"loadBalancingConfig\": [" + " {\"grpclb\":{\"childPolicy\":1}}," + " {\"round_robin\":{}}" + "]}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:loadBalancingConfig error:" + "errors validating grpclb LB policy config: [" + "field:childPolicy error:type should be array]]") + << service_config.status(); +} + +TEST_F(ClientChannelParserTest, ValidLoadBalancingPolicy) { + const char* test_json = "{\"loadBalancingPolicy\":\"pick_first\"}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + ASSERT_TRUE(service_config.ok()) << service_config.status(); + const auto* parsed_config = + static_cast( + (*service_config)->GetGlobalParsedConfig(parser_index_)); + EXPECT_EQ(parsed_config->parsed_deprecated_lb_policy(), "pick_first"); +} + +TEST_F(ClientChannelParserTest, ValidLoadBalancingPolicyAllCaps) { + const char* test_json = "{\"loadBalancingPolicy\":\"PICK_FIRST\"}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + ASSERT_TRUE(service_config.ok()) << service_config.status(); + const auto* parsed_config = + static_cast( + (*service_config)->GetGlobalParsedConfig(parser_index_)); + EXPECT_EQ(parsed_config->parsed_deprecated_lb_policy(), "pick_first"); +} + +TEST_F(ClientChannelParserTest, UnknownLoadBalancingPolicy) { + const char* test_json = "{\"loadBalancingPolicy\":\"unknown\"}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:loadBalancingPolicy error:unknown LB policy \"unknown\"]") + << service_config.status(); +} + +TEST_F(ClientChannelParserTest, LoadBalancingPolicyXdsNotAllowed) { + const char* test_json = + "{\"loadBalancingPolicy\":\"xds_cluster_resolver_experimental\"}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:loadBalancingPolicy error:LB policy " + "\"xds_cluster_resolver_experimental\" requires a config. Please " + "use loadBalancingConfig instead.]") + << service_config.status(); +} + +TEST_F(ClientChannelParserTest, ValidTimeout) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"timeout\": \"5s\"\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + ASSERT_TRUE(service_config.ok()) << service_config.status(); + const auto* vector_ptr = + (*service_config) + ->GetMethodParsedConfigVector( + grpc_slice_from_static_string("/TestServ/TestMethod")); + ASSERT_NE(vector_ptr, nullptr); + auto parsed_config = ((*vector_ptr)[parser_index_]).get(); + EXPECT_EQ( + (static_cast(parsed_config)) + ->timeout(), + Duration::Seconds(5)); +} + +TEST_F(ClientChannelParserTest, InvalidTimeout) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"timeout\": \"5sec\"\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].timeout " + "error:Not a duration (no s suffix)]") + << service_config.status(); +} + +TEST_F(ClientChannelParserTest, ValidWaitForReady) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"waitForReady\": true\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + ASSERT_TRUE(service_config.ok()) << service_config.status(); + const auto* vector_ptr = + (*service_config) + ->GetMethodParsedConfigVector( + grpc_slice_from_static_string("/TestServ/TestMethod")); + ASSERT_NE(vector_ptr, nullptr); + auto parsed_config = ((*vector_ptr)[parser_index_]).get(); + ASSERT_TRUE( + (static_cast(parsed_config)) + ->wait_for_ready() + .has_value()); + EXPECT_TRUE( + (static_cast(parsed_config)) + ->wait_for_ready() + .value()); +} + +TEST_F(ClientChannelParserTest, InvalidWaitForReady) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"waitForReady\": \"true\"\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].waitForReady error:is not a boolean]") + << service_config.status(); +} + +TEST_F(ClientChannelParserTest, ValidHealthCheck) { + const char* test_json = + "{\n" + " \"healthCheckConfig\": {\n" + " \"serviceName\": \"health_check_service_name\"\n" + " }\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + ASSERT_TRUE(service_config.ok()) << service_config.status(); + const auto* parsed_config = + static_cast( + (*service_config)->GetGlobalParsedConfig(parser_index_)); + ASSERT_NE(parsed_config, nullptr); + EXPECT_EQ(parsed_config->health_check_service_name(), + "health_check_service_name"); +} + +TEST_F(ClientChannelParserTest, InvalidHealthCheckMultipleEntries) { + const char* test_json = + "{\n" + " \"healthCheckConfig\": {\n" + " \"serviceName\": \"health_check_service_name\"\n" + " },\n" + " \"healthCheckConfig\": {\n" + " \"serviceName\": \"health_check_service_name1\"\n" + " }\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "JSON parsing failed: [" + "duplicate key \"healthCheckConfig\" at index 104]") + << service_config.status(); +} + +} // namespace testing +} // namespace grpc_core + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + grpc::testing::TestEnvironment env(&argc, argv); + grpc_init(); + int ret = RUN_ALL_TESTS(); + grpc_shutdown(); + return ret; +} diff --git a/test/core/client_channel/retry_service_config_test.cc b/test/core/client_channel/retry_service_config_test.cc new file mode 100644 index 00000000000..b64941e5222 --- /dev/null +++ b/test/core/client_channel/retry_service_config_test.cc @@ -0,0 +1,712 @@ +// +// 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. +// + +#include "src/core/ext/filters/client_channel/retry_service_config.h" + +#include + +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "gtest/gtest.h" + +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/config/core_configuration.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/gprpp/time.h" +#include "src/core/lib/service_config/service_config.h" +#include "src/core/lib/service_config/service_config_impl.h" +#include "src/core/lib/service_config/service_config_parser.h" +#include "test/core/util/test_config.h" + +namespace grpc_core { +namespace testing { + +class RetryParserTest : public ::testing::Test { + protected: + void SetUp() override { + parser_index_ = + CoreConfiguration::Get().service_config_parser().GetParserIndex( + "retry"); + } + + size_t parser_index_; +}; + +TEST_F(RetryParserTest, ValidRetryThrottling) { + const char* test_json = + "{\n" + " \"retryThrottling\": {\n" + " \"maxTokens\": 2,\n" + " \"tokenRatio\": 1.0\n" + " }\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + ASSERT_TRUE(service_config.ok()) << service_config.status(); + const auto* parsed_config = static_cast( + (*service_config)->GetGlobalParsedConfig(parser_index_)); + ASSERT_NE(parsed_config, nullptr); + EXPECT_EQ(parsed_config->max_milli_tokens(), 2000); + EXPECT_EQ(parsed_config->milli_token_ratio(), 1000); +} + +TEST_F(RetryParserTest, RetryThrottlingMissingFields) { + const char* test_json = + "{\n" + " \"retryThrottling\": {\n" + " }\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:retryThrottling.maxTokens error:field not present; " + "field:retryThrottling.tokenRatio error:field not present]") + << service_config.status(); +} + +TEST_F(RetryParserTest, InvalidRetryThrottlingNegativeMaxTokens) { + const char* test_json = + "{\n" + " \"retryThrottling\": {\n" + " \"maxTokens\": -2,\n" + " \"tokenRatio\": 1.0\n" + " }\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:retryThrottling.maxTokens error:" + "failed to parse non-negative number]") + << service_config.status(); +} + +TEST_F(RetryParserTest, InvalidRetryThrottlingInvalidTokenRatio) { + const char* test_json = + "{\n" + " \"retryThrottling\": {\n" + " \"maxTokens\": 2,\n" + " \"tokenRatio\": -1\n" + " }\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:retryThrottling.tokenRatio error:" + "could not parse as a number]"); +} + +TEST_F(RetryParserTest, ValidRetryPolicy) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 3,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + ASSERT_TRUE(service_config.ok()) << service_config.status(); + const auto* vector_ptr = + (*service_config) + ->GetMethodParsedConfigVector( + grpc_slice_from_static_string("/TestServ/TestMethod")); + ASSERT_NE(vector_ptr, nullptr); + const auto* parsed_config = static_cast( + ((*vector_ptr)[parser_index_]).get()); + ASSERT_NE(parsed_config, nullptr); + EXPECT_EQ(parsed_config->max_attempts(), 3); + EXPECT_EQ(parsed_config->initial_backoff(), Duration::Seconds(1)); + EXPECT_EQ(parsed_config->max_backoff(), Duration::Minutes(2)); + EXPECT_EQ(parsed_config->backoff_multiplier(), 1.6f); + EXPECT_EQ(parsed_config->per_attempt_recv_timeout(), absl::nullopt); + EXPECT_TRUE( + parsed_config->retryable_status_codes().Contains(GRPC_STATUS_ABORTED)); +} + +TEST_F(RetryParserTest, InvalidRetryPolicyWrongType) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"retryPolicy\": 5\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].retryPolicy error:is not an object]") + << service_config.status(); +} + +TEST_F(RetryParserTest, InvalidRetryPolicyRequiredFieldsMissing) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].retryPolicy.backoffMultiplier " + "error:field not present; " + "field:methodConfig[0].retryPolicy.initialBackoff " + "error:field not present; " + "field:methodConfig[0].retryPolicy.maxAttempts " + "error:field not present; " + "field:methodConfig[0].retryPolicy.maxBackoff " + "error:field not present]") + << service_config.status(); +} + +TEST_F(RetryParserTest, InvalidRetryPolicyMaxAttemptsWrongType) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": \"FOO\",\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].retryPolicy.maxAttempts " + "error:failed to parse number]") + << service_config.status(); +} + +TEST_F(RetryParserTest, InvalidRetryPolicyMaxAttemptsBadValue) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 1,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].retryPolicy.maxAttempts " + "error:must be at least 2]") + << service_config.status(); +} + +TEST_F(RetryParserTest, InvalidRetryPolicyInitialBackoffWrongType) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1sec\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].retryPolicy.initialBackoff " + "error:Not a duration (no s suffix)]") + << service_config.status(); +} + +TEST_F(RetryParserTest, InvalidRetryPolicyInitialBackoffBadValue) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"0s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].retryPolicy.initialBackoff " + "error:must be greater than 0]") + << service_config.status(); +} + +TEST_F(RetryParserTest, InvalidRetryPolicyMaxBackoffWrongType) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120sec\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].retryPolicy.maxBackoff " + "error:Not a duration (no s suffix)]") + << service_config.status(); +} + +TEST_F(RetryParserTest, InvalidRetryPolicyMaxBackoffBadValue) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"0s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].retryPolicy.maxBackoff " + "error:must be greater than 0]") + << service_config.status(); +} + +TEST_F(RetryParserTest, InvalidRetryPolicyBackoffMultiplierWrongType) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": [],\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].retryPolicy.backoffMultiplier " + "error:is not a number]") + << service_config.status(); +} + +TEST_F(RetryParserTest, InvalidRetryPolicyBackoffMultiplierBadValue) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 0,\n" + " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" + " }\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].retryPolicy.backoffMultiplier " + "error:must be greater than 0]") + << service_config.status(); +} + +TEST_F(RetryParserTest, InvalidRetryPolicyEmptyRetryableStatusCodes) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": \"1.6\",\n" + " \"retryableStatusCodes\": []\n" + " }\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].retryPolicy.retryableStatusCodes " + "error:must be non-empty]") + << service_config.status(); +} + +TEST_F(RetryParserTest, InvalidRetryPolicyRetryableStatusCodesWrongType) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": \"1.6\",\n" + " \"retryableStatusCodes\": 0\n" + " }\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].retryPolicy.retryableStatusCodes " + "error:is not an array]") + << service_config.status(); +} + +TEST_F(RetryParserTest, + InvalidRetryPolicyRetryableStatusCodesElementsWrongType) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": \"1.6\",\n" + " \"retryableStatusCodes\": [true, 2]\n" + " }\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].retryPolicy.retryableStatusCodes " + "error:must be non-empty; " + "field:methodConfig[0].retryPolicy.retryableStatusCodes[0] " + "error:is not a string; " + "field:methodConfig[0].retryPolicy.retryableStatusCodes[1] " + "error:is not a string]") + << service_config.status(); +} + +TEST_F(RetryParserTest, InvalidRetryPolicyUnparseableRetryableStatusCodes) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": \"1.6\",\n" + " \"retryableStatusCodes\": [\"FOO\", \"BAR\"]\n" + " }\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].retryPolicy.retryableStatusCodes " + "error:must be non-empty; " + "field:methodConfig[0].retryPolicy.retryableStatusCodes[0] " + "error:failed to parse status code; " + "field:methodConfig[0].retryPolicy.retryableStatusCodes[1] " + "error:failed to parse status code]") + << service_config.status(); +} + +TEST_F(RetryParserTest, ValidRetryPolicyWithPerAttemptRecvTimeout) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"perAttemptRecvTimeout\": \"1s\",\n" + " \"retryableStatusCodes\": [\"ABORTED\"]\n" + " }\n" + " } ]\n" + "}"; + const ChannelArgs args = + ChannelArgs().Set(GRPC_ARG_EXPERIMENTAL_ENABLE_HEDGING, 1); + auto service_config = ServiceConfigImpl::Create(args, test_json); + ASSERT_TRUE(service_config.ok()) << service_config.status(); + const auto* vector_ptr = + (*service_config) + ->GetMethodParsedConfigVector( + grpc_slice_from_static_string("/TestServ/TestMethod")); + ASSERT_NE(vector_ptr, nullptr); + const auto* parsed_config = static_cast( + ((*vector_ptr)[parser_index_]).get()); + ASSERT_NE(parsed_config, nullptr); + EXPECT_EQ(parsed_config->max_attempts(), 2); + EXPECT_EQ(parsed_config->initial_backoff(), Duration::Seconds(1)); + EXPECT_EQ(parsed_config->max_backoff(), Duration::Minutes(2)); + EXPECT_EQ(parsed_config->backoff_multiplier(), 1.6f); + EXPECT_EQ(parsed_config->per_attempt_recv_timeout(), Duration::Seconds(1)); + EXPECT_TRUE( + parsed_config->retryable_status_codes().Contains(GRPC_STATUS_ABORTED)); +} + +TEST_F(RetryParserTest, + ValidRetryPolicyWithPerAttemptRecvTimeoutIgnoredWhenHedgingDisabled) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"perAttemptRecvTimeout\": \"1s\",\n" + " \"retryableStatusCodes\": [\"ABORTED\"]\n" + " }\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + ASSERT_TRUE(service_config.ok()) << service_config.status(); + const auto* vector_ptr = + (*service_config) + ->GetMethodParsedConfigVector( + grpc_slice_from_static_string("/TestServ/TestMethod")); + ASSERT_NE(vector_ptr, nullptr); + const auto* parsed_config = static_cast( + ((*vector_ptr)[parser_index_]).get()); + ASSERT_NE(parsed_config, nullptr); + EXPECT_EQ(parsed_config->max_attempts(), 2); + EXPECT_EQ(parsed_config->initial_backoff(), Duration::Seconds(1)); + EXPECT_EQ(parsed_config->max_backoff(), Duration::Minutes(2)); + EXPECT_EQ(parsed_config->backoff_multiplier(), 1.6f); + EXPECT_EQ(parsed_config->per_attempt_recv_timeout(), absl::nullopt); + EXPECT_TRUE( + parsed_config->retryable_status_codes().Contains(GRPC_STATUS_ABORTED)); +} + +TEST_F(RetryParserTest, + ValidRetryPolicyWithPerAttemptRecvTimeoutAndUnsetRetryableStatusCodes) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": 1.6,\n" + " \"perAttemptRecvTimeout\": \"1s\"\n" + " }\n" + " } ]\n" + "}"; + const ChannelArgs args = + ChannelArgs().Set(GRPC_ARG_EXPERIMENTAL_ENABLE_HEDGING, 1); + auto service_config = ServiceConfigImpl::Create(args, test_json); + ASSERT_TRUE(service_config.ok()) << service_config.status(); + const auto* vector_ptr = + (*service_config) + ->GetMethodParsedConfigVector( + grpc_slice_from_static_string("/TestServ/TestMethod")); + ASSERT_NE(vector_ptr, nullptr); + const auto* parsed_config = static_cast( + ((*vector_ptr)[parser_index_]).get()); + ASSERT_NE(parsed_config, nullptr); + EXPECT_EQ(parsed_config->max_attempts(), 2); + EXPECT_EQ(parsed_config->initial_backoff(), Duration::Seconds(1)); + EXPECT_EQ(parsed_config->max_backoff(), Duration::Minutes(2)); + EXPECT_EQ(parsed_config->backoff_multiplier(), 1.6f); + EXPECT_EQ(parsed_config->per_attempt_recv_timeout(), Duration::Seconds(1)); + EXPECT_TRUE(parsed_config->retryable_status_codes().Empty()); +} + +TEST_F(RetryParserTest, InvalidRetryPolicyPerAttemptRecvTimeoutUnparseable) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": \"1.6\",\n" + " \"perAttemptRecvTimeout\": \"1sec\",\n" + " \"retryableStatusCodes\": [\"ABORTED\"]\n" + " }\n" + " } ]\n" + "}"; + const ChannelArgs args = + ChannelArgs().Set(GRPC_ARG_EXPERIMENTAL_ENABLE_HEDGING, 1); + auto service_config = ServiceConfigImpl::Create(args, test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].retryPolicy.perAttemptRecvTimeout " + "error:Not a duration (no s suffix)]") + << service_config.status(); +} + +TEST_F(RetryParserTest, InvalidRetryPolicyPerAttemptRecvTimeoutWrongType) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": \"1.6\",\n" + " \"perAttemptRecvTimeout\": 1,\n" + " \"retryableStatusCodes\": [\"ABORTED\"]\n" + " }\n" + " } ]\n" + "}"; + const ChannelArgs args = + ChannelArgs().Set(GRPC_ARG_EXPERIMENTAL_ENABLE_HEDGING, 1); + auto service_config = ServiceConfigImpl::Create(args, test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].retryPolicy.perAttemptRecvTimeout " + "error:is not a string]") + << service_config.status(); +} + +TEST_F(RetryParserTest, InvalidRetryPolicyPerAttemptRecvTimeoutBadValue) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\": 2,\n" + " \"initialBackoff\": \"1s\",\n" + " \"maxBackoff\": \"120s\",\n" + " \"backoffMultiplier\": \"1.6\",\n" + " \"perAttemptRecvTimeout\": \"0s\",\n" + " \"retryableStatusCodes\": [\"ABORTED\"]\n" + " }\n" + " } ]\n" + "}"; + const ChannelArgs args = + ChannelArgs().Set(GRPC_ARG_EXPERIMENTAL_ENABLE_HEDGING, 1); + auto service_config = ServiceConfigImpl::Create(args, test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].retryPolicy.perAttemptRecvTimeout " + "error:must be greater than 0]") + << service_config.status(); +} + +} // namespace testing +} // namespace grpc_core + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + grpc::testing::TestEnvironment env(&argc, argv); + grpc_init(); + int ret = RUN_ALL_TESTS(); + grpc_shutdown(); + return ret; +} diff --git a/test/core/client_channel/rls_lb_config_parser_test.cc b/test/core/client_channel/rls_lb_config_parser_test.cc index 07ffa68b56a..42cb7b25b34 100644 --- a/test/core/client_channel/rls_lb_config_parser_test.cc +++ b/test/core/client_channel/rls_lb_config_parser_test.cc @@ -14,11 +14,8 @@ // limitations under the License. // -#include - #include "absl/status/status.h" #include "absl/status/statusor.h" -#include "absl/strings/string_view.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -111,15 +108,14 @@ TEST_F(RlsConfigParsingTest, TopLevelFieldsWrongTypes) { auto service_config = ServiceConfigImpl::Create(ChannelArgs(), service_config_json); EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT( - service_config.status().message(), - ::testing::HasSubstr( - "errors validing RLS LB policy config: [" - "field:childPolicy error:is not an array; " - "field:childPolicyConfigTargetFieldName error:is not a string; " - "field:routeLookupChannelServiceConfig error:" - "INVALID_ARGUMENT:JSON value is not an object; " - "field:routeLookupConfig error:is not an object]")) + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:loadBalancingConfig " + "error:errors validing RLS LB policy config: [" + "field:childPolicy error:is not an array; " + "field:childPolicyConfigTargetFieldName error:is not a string; " + "field:routeLookupChannelServiceConfig error:is not an object; " + "field:routeLookupConfig error:is not an object]]") << service_config.status(); } @@ -191,17 +187,13 @@ TEST_F(RlsConfigParsingTest, InvalidRlsChannelServiceConfig) { auto service_config = ServiceConfigImpl::Create(ChannelArgs(), service_config_json); EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT( - std::string(service_config.status().message()), - ::testing::ContainsRegex( - "errors validing RLS LB policy config: \\[" - "field:routeLookupChannelServiceConfig error:" - "INVALID_ARGUMENT:Service config parsing errors: \\[" - "error parsing client channel global parameters: " - "UNKNOWN:Client channel global parser" - ".*children:.*" - "\\[UNKNOWN:field:loadBalancingPolicy error:Unknown lb policy.*" - "field:routeLookupConfig error:field not present\\]")) + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:loadBalancingConfig " + "error:errors validing RLS LB policy config: [" + "field:routeLookupChannelServiceConfig.loadBalancingPolicy " + "error:unknown LB policy \"unknown\"; " + "field:routeLookupConfig error:field not present]]") << service_config.status(); } diff --git a/test/core/client_channel/service_config_test.cc b/test/core/client_channel/service_config_test.cc deleted file mode 100644 index f6c24d700a0..00000000000 --- a/test/core/client_channel/service_config_test.cc +++ /dev/null @@ -1,1557 +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. - * - */ - -#include "src/core/lib/service_config/service_config.h" - -#include -#include -#include -#include -#include - -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/str_cat.h" -#include "absl/types/optional.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include -#include - -#include "src/core/ext/filters/client_channel/resolver_result_parsing.h" -#include "src/core/ext/filters/client_channel/retry_service_config.h" -#include "src/core/ext/filters/message_size/message_size_filter.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/channel/status_util.h" -#include "src/core/lib/config/core_configuration.h" -#include "src/core/lib/gpr/string.h" -#include "src/core/lib/gprpp/ref_counted_ptr.h" -#include "src/core/lib/gprpp/time.h" -#include "src/core/lib/json/json.h" -#include "src/core/lib/load_balancing/lb_policy.h" -#include "src/core/lib/service_config/service_config_impl.h" -#include "src/core/lib/service_config/service_config_parser.h" -#include "test/core/util/test_config.h" - -namespace grpc_core { -namespace testing { - -// -// ServiceConfig tests -// - -// Set this channel arg to true to disable parsing. -#define GRPC_ARG_DISABLE_PARSING "disable_parsing" - -// A regular expression to enter referenced or child errors. -#define CHILD_ERROR_TAG ".*children.*" - -class TestParsedConfig1 : public ServiceConfigParser::ParsedConfig { - public: - explicit TestParsedConfig1(int value) : value_(value) {} - - int value() const { return value_; } - - private: - int value_; -}; - -class TestParser1 : public ServiceConfigParser::Parser { - public: - absl::string_view name() const override { return "test_parser_1"; } - - absl::StatusOr> - ParseGlobalParams(const ChannelArgs& args, const Json& json) override { - if (args.GetBool(GRPC_ARG_DISABLE_PARSING).value_or(false)) { - return nullptr; - } - auto it = json.object_value().find("global_param"); - if (it != json.object_value().end()) { - if (it->second.type() != Json::Type::NUMBER) { - return absl::InvalidArgumentError(InvalidTypeErrorMessage()); - } - int value = gpr_parse_nonnegative_int(it->second.string_value().c_str()); - if (value == -1) { - return absl::InvalidArgumentError(InvalidValueErrorMessage()); - } - return std::make_unique(value); - } - return nullptr; - } - - static const char* InvalidTypeErrorMessage() { - return "global_param value type should be a number"; - } - - static const char* InvalidValueErrorMessage() { - return "global_param value type should be non-negative"; - } -}; - -class TestParser2 : public ServiceConfigParser::Parser { - public: - absl::string_view name() const override { return "test_parser_2"; } - - absl::StatusOr> - ParsePerMethodParams(const ChannelArgs& args, const Json& json) override { - if (args.GetBool(GRPC_ARG_DISABLE_PARSING).value_or(false)) { - return nullptr; - } - auto it = json.object_value().find("method_param"); - if (it != json.object_value().end()) { - if (it->second.type() != Json::Type::NUMBER) { - return absl::InvalidArgumentError(InvalidTypeErrorMessage()); - } - int value = gpr_parse_nonnegative_int(it->second.string_value().c_str()); - if (value == -1) { - return absl::InvalidArgumentError(InvalidValueErrorMessage()); - } - return std::make_unique(value); - } - return nullptr; - } - - static const char* InvalidTypeErrorMessage() { - return "method_param value type should be a number"; - } - - static const char* InvalidValueErrorMessage() { - return "method_param value type should be non-negative"; - } -}; - -// This parser always adds errors -class ErrorParser : public ServiceConfigParser::Parser { - public: - explicit ErrorParser(absl::string_view name) : name_(name) {} - - absl::string_view name() const override { return name_; } - - absl::StatusOr> - ParsePerMethodParams(const ChannelArgs& /*arg*/, - const Json& /*json*/) override { - return absl::InvalidArgumentError(MethodError()); - } - - absl::StatusOr> - ParseGlobalParams(const ChannelArgs& /*arg*/, const Json& /*json*/) override { - return absl::InvalidArgumentError(GlobalError()); - } - - static const char* MethodError() { return "ErrorParser : methodError"; } - - static const char* GlobalError() { return "ErrorParser : globalError"; } - - private: - absl::string_view name_; -}; - -class ServiceConfigTest : public ::testing::Test { - protected: - void SetUp() override { - builder_ = std::make_unique( - [](CoreConfiguration::Builder* builder) { - builder->service_config_parser()->RegisterParser( - std::make_unique()); - builder->service_config_parser()->RegisterParser( - std::make_unique()); - }); - EXPECT_EQ(CoreConfiguration::Get().service_config_parser().GetParserIndex( - "test_parser_1"), - 0); - EXPECT_EQ(CoreConfiguration::Get().service_config_parser().GetParserIndex( - "test_parser_2"), - 1); - } - - private: - std::unique_ptr builder_; -}; - -TEST_F(ServiceConfigTest, ErrorCheck1) { - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), ""); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT(std::string(service_config.status().message()), - ::testing::ContainsRegex("JSON parse error")); -} - -TEST_F(ServiceConfigTest, BasicTest1) { - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), "{}"); - ASSERT_TRUE(service_config.ok()) << service_config.status(); -} - -TEST_F(ServiceConfigTest, SkipMethodConfigWithNoNameOrEmptyName) { - const char* test_json = - "{\"methodConfig\": [" - " {\"method_param\":1}," - " {\"name\":[], \"method_param\":1}," - " {\"name\":[{\"service\":\"TestServ\"}], \"method_param\":2}" - "]}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - auto vector_ptr = - (*service_config) - ->GetMethodParsedConfigVector( - grpc_slice_from_static_string("/TestServ/TestMethod")); - ASSERT_EQ(vector_ptr->size(), 2UL); - auto parsed_config = ((*vector_ptr)[1]).get(); - EXPECT_EQ(static_cast(parsed_config)->value(), 2); -} - -TEST_F(ServiceConfigTest, ErrorDuplicateMethodConfigNames) { - const char* test_json = - "{\"methodConfig\": [" - " {\"name\":[{\"service\":\"TestServ\"}]}," - " {\"name\":[{\"service\":\"TestServ\"}]}" - "]}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ(service_config.status().message(), - "Service config parsing errors: [" - "errors parsing methodConfig: [" - "index 1: [" - "field:name error:multiple method configs with same name]]]"); -} - -TEST_F(ServiceConfigTest, ErrorDuplicateMethodConfigNamesWithNullMethod) { - const char* test_json = - "{\"methodConfig\": [" - " {\"name\":[{\"service\":\"TestServ\",\"method\":null}]}," - " {\"name\":[{\"service\":\"TestServ\"}]}" - "]}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ(service_config.status().message(), - "Service config parsing errors: [" - "errors parsing methodConfig: [" - "index 1: [" - "field:name error:multiple method configs with same name]]]"); -} - -TEST_F(ServiceConfigTest, ErrorDuplicateMethodConfigNamesWithEmptyMethod) { - const char* test_json = - "{\"methodConfig\": [" - " {\"name\":[{\"service\":\"TestServ\",\"method\":\"\"}]}," - " {\"name\":[{\"service\":\"TestServ\"}]}" - "]}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ(service_config.status().message(), - "Service config parsing errors: [" - "errors parsing methodConfig: [" - "index 1: [" - "field:name error:multiple method configs with same name]]]"); -} - -TEST_F(ServiceConfigTest, ErrorDuplicateDefaultMethodConfigs) { - const char* test_json = - "{\"methodConfig\": [" - " {\"name\":[{}]}," - " {\"name\":[{}]}" - "]}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ(service_config.status().message(), - "Service config parsing errors: [" - "errors parsing methodConfig: [" - "index 1: [" - "field:name error:multiple default method configs]]]"); -} - -TEST_F(ServiceConfigTest, ErrorDuplicateDefaultMethodConfigsWithNullService) { - const char* test_json = - "{\"methodConfig\": [" - " {\"name\":[{\"service\":null}]}," - " {\"name\":[{}]}" - "]}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ(service_config.status().message(), - "Service config parsing errors: [" - "errors parsing methodConfig: [" - "index 1: [" - "field:name error:multiple default method configs]]]"); -} - -TEST_F(ServiceConfigTest, ErrorDuplicateDefaultMethodConfigsWithEmptyService) { - const char* test_json = - "{\"methodConfig\": [" - " {\"name\":[{\"service\":\"\"}]}," - " {\"name\":[{}]}" - "]}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ(service_config.status().message(), - "Service config parsing errors: [" - "errors parsing methodConfig: [" - "index 1: [" - "field:name error:multiple default method configs]]]"); -} - -TEST_F(ServiceConfigTest, ValidMethodConfig) { - const char* test_json = - "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}]}]}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - ASSERT_TRUE(service_config.ok()) << service_config.status(); -} - -TEST_F(ServiceConfigTest, Parser1BasicTest1) { - const char* test_json = "{\"global_param\":5}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - EXPECT_EQ((static_cast( - (*service_config)->GetGlobalParsedConfig(0))) - ->value(), - 5); - EXPECT_EQ((*service_config) - ->GetMethodParsedConfigVector( - grpc_slice_from_static_string("/TestServ/TestMethod")), - nullptr); -} - -TEST_F(ServiceConfigTest, Parser1BasicTest2) { - const char* test_json = "{\"global_param\":1000}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - EXPECT_EQ((static_cast( - (*service_config)->GetGlobalParsedConfig(0))) - ->value(), - 1000); -} - -TEST_F(ServiceConfigTest, Parser1DisabledViaChannelArg) { - const ChannelArgs args = ChannelArgs().Set(GRPC_ARG_DISABLE_PARSING, 1); - const char* test_json = "{\"global_param\":5}"; - auto service_config = ServiceConfigImpl::Create(args, test_json); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - EXPECT_EQ((*service_config)->GetGlobalParsedConfig(0), nullptr); -} - -TEST_F(ServiceConfigTest, Parser1ErrorInvalidType) { - const char* test_json = "{\"global_param\":\"5\"}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ(service_config.status().message(), - absl::StrCat("Service config parsing errors: [", - TestParser1::InvalidTypeErrorMessage(), "]")); -} - -TEST_F(ServiceConfigTest, Parser1ErrorInvalidValue) { - const char* test_json = "{\"global_param\":-5}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ(service_config.status().message(), - absl::StrCat("Service config parsing errors: [", - TestParser1::InvalidValueErrorMessage(), "]")); -} - -TEST_F(ServiceConfigTest, Parser2BasicTest) { - const char* test_json = - "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}], " - "\"method_param\":5}]}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - const auto* vector_ptr = - (*service_config) - ->GetMethodParsedConfigVector( - grpc_slice_from_static_string("/TestServ/TestMethod")); - ASSERT_NE(vector_ptr, nullptr); - auto parsed_config = ((*vector_ptr)[1]).get(); - EXPECT_EQ(static_cast(parsed_config)->value(), 5); -} - -TEST_F(ServiceConfigTest, Parser2DisabledViaChannelArg) { - const ChannelArgs args = ChannelArgs().Set(GRPC_ARG_DISABLE_PARSING, 1); - const char* test_json = - "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}], " - "\"method_param\":5}]}"; - auto service_config = ServiceConfigImpl::Create(args, test_json); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - const auto* vector_ptr = - (*service_config) - ->GetMethodParsedConfigVector( - grpc_slice_from_static_string("/TestServ/TestMethod")); - ASSERT_NE(vector_ptr, nullptr); - auto parsed_config = ((*vector_ptr)[1]).get(); - EXPECT_EQ(parsed_config, nullptr); -} - -TEST_F(ServiceConfigTest, Parser2ErrorInvalidType) { - const char* test_json = - "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}], " - "\"method_param\":\"5\"}]}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ(service_config.status().message(), - absl::StrCat("Service config parsing errors: [" - "errors parsing methodConfig: [" - "index 0: [", - TestParser2::InvalidTypeErrorMessage(), "]]]")); -} - -TEST_F(ServiceConfigTest, Parser2ErrorInvalidValue) { - const char* test_json = - "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}], " - "\"method_param\":-5}]}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ(service_config.status().message(), - absl::StrCat("Service config parsing errors: [" - "errors parsing methodConfig: [" - "index 0: [", - TestParser2::InvalidValueErrorMessage(), "]]]")); -} - -TEST(ServiceConfigParserTest, DoubleRegistration) { - CoreConfiguration::Reset(); - ASSERT_DEATH_IF_SUPPORTED( - CoreConfiguration::WithSubstituteBuilder builder( - [](CoreConfiguration::Builder* builder) { - builder->service_config_parser()->RegisterParser( - std::make_unique("xyzabc")); - builder->service_config_parser()->RegisterParser( - std::make_unique("xyzabc")); - }), - "xyzabc.*already registered"); -} - -// Test parsing with ErrorParsers which always add errors -class ErroredParsersScopingTest : public ::testing::Test { - protected: - void SetUp() override { - builder_ = std::make_unique( - [](CoreConfiguration::Builder* builder) { - builder->service_config_parser()->RegisterParser( - std::make_unique("ep1")); - builder->service_config_parser()->RegisterParser( - std::make_unique("ep2")); - }); - EXPECT_EQ( - CoreConfiguration::Get().service_config_parser().GetParserIndex("ep1"), - 0); - EXPECT_EQ( - CoreConfiguration::Get().service_config_parser().GetParserIndex("ep2"), - 1); - } - - private: - std::unique_ptr builder_; -}; - -TEST_F(ErroredParsersScopingTest, GlobalParams) { - const char* test_json = "{}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ(service_config.status().message(), - absl::StrCat("Service config parsing errors: [", - ErrorParser::GlobalError(), "; ", - ErrorParser::GlobalError(), "]")); -} - -TEST_F(ErroredParsersScopingTest, MethodParams) { - const char* test_json = "{\"methodConfig\": [{}]}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ( - service_config.status().message(), - absl::StrCat("Service config parsing errors: [", - ErrorParser::GlobalError(), "; ", ErrorParser::GlobalError(), - "; " - "errors parsing methodConfig: [" - "index 0: [", - ErrorParser::MethodError(), "; ", ErrorParser::MethodError(), - "]]]")); -} - -// -// client_channel parser tests -// - -class ClientChannelParserTest : public ::testing::Test { - protected: - void SetUp() override { - EXPECT_EQ(CoreConfiguration::Get().service_config_parser().GetParserIndex( - "client_channel"), - 0); - } -}; - -TEST_F(ClientChannelParserTest, ValidLoadBalancingConfigPickFirst) { - const char* test_json = "{\"loadBalancingConfig\": [{\"pick_first\":{}}]}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - const auto* parsed_config = - static_cast( - (*service_config)->GetGlobalParsedConfig(0)); - auto lb_config = parsed_config->parsed_lb_config(); - EXPECT_EQ(lb_config->name(), "pick_first"); -} - -TEST_F(ClientChannelParserTest, ValidLoadBalancingConfigRoundRobin) { - const char* test_json = - "{\"loadBalancingConfig\": [{\"round_robin\":{}}, {}]}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - auto parsed_config = static_cast( - (*service_config)->GetGlobalParsedConfig(0)); - auto lb_config = parsed_config->parsed_lb_config(); - EXPECT_EQ(lb_config->name(), "round_robin"); -} - -TEST_F(ClientChannelParserTest, ValidLoadBalancingConfigGrpclb) { - const char* test_json = - "{\"loadBalancingConfig\": " - "[{\"grpclb\":{\"childPolicy\":[{\"pick_first\":{}}]}}]}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - const auto* parsed_config = - static_cast( - (*service_config)->GetGlobalParsedConfig(0)); - auto lb_config = parsed_config->parsed_lb_config(); - EXPECT_EQ(lb_config->name(), "grpclb"); -} - -TEST_F(ClientChannelParserTest, ValidLoadBalancingConfigXds) { - const char* test_json = - "{\n" - " \"loadBalancingConfig\":[\n" - " { \"does_not_exist\":{} },\n" - " { \"xds_cluster_resolver_experimental\":{\n" - " \"discoveryMechanisms\": [\n" - " { \"clusterName\": \"foo\",\n" - " \"type\": \"EDS\"\n" - " } ],\n" - " \"xdsLbPolicy\": [{\"round_robin\":{}}]\n" - " } }\n" - " ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - const auto* parsed_config = - static_cast( - (*service_config)->GetGlobalParsedConfig(0)); - auto lb_config = parsed_config->parsed_lb_config(); - EXPECT_EQ(lb_config->name(), "xds_cluster_resolver_experimental"); -} - -TEST_F(ClientChannelParserTest, UnknownLoadBalancingConfig) { - const char* test_json = "{\"loadBalancingConfig\": [{\"unknown\":{}}]}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT( - std::string(service_config.status().message()), - ::testing::MatchesRegex( - "Service config parsing errors: \\[" - "error parsing client channel global parameters:" CHILD_ERROR_TAG - "field:loadBalancingConfig " - "error:No known policies in list: unknown.*")); -} - -TEST_F(ClientChannelParserTest, InvalidGrpclbLoadBalancingConfig) { - const char* test_json = - "{\"loadBalancingConfig\": [" - " {\"grpclb\":{\"childPolicy\":1}}," - " {\"round_robin\":{}}" - "]}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT( - std::string(service_config.status().message()), - ::testing::MatchesRegex( - "Service config parsing errors: \\[" - "error parsing client channel global parameters:" CHILD_ERROR_TAG - "field:loadBalancingConfig error:" - "errors validating grpclb LB policy config: \\[" - "field:childPolicy error:type should be array\\].*")); -} - -TEST_F(ClientChannelParserTest, ValidLoadBalancingPolicy) { - const char* test_json = "{\"loadBalancingPolicy\":\"pick_first\"}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - const auto* parsed_config = - static_cast( - (*service_config)->GetGlobalParsedConfig(0)); - EXPECT_EQ(parsed_config->parsed_deprecated_lb_policy(), "pick_first"); -} - -TEST_F(ClientChannelParserTest, ValidLoadBalancingPolicyAllCaps) { - const char* test_json = "{\"loadBalancingPolicy\":\"PICK_FIRST\"}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - const auto* parsed_config = - static_cast( - (*service_config)->GetGlobalParsedConfig(0)); - EXPECT_EQ(parsed_config->parsed_deprecated_lb_policy(), "pick_first"); -} - -TEST_F(ClientChannelParserTest, UnknownLoadBalancingPolicy) { - const char* test_json = "{\"loadBalancingPolicy\":\"unknown\"}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT( - std::string(service_config.status().message()), - ::testing::MatchesRegex( - "Service config parsing errors: \\[" - "error parsing client channel global parameters:" CHILD_ERROR_TAG - "field:loadBalancingPolicy error:Unknown lb policy.*")); -} - -TEST_F(ClientChannelParserTest, LoadBalancingPolicyXdsNotAllowed) { - const char* test_json = - "{\"loadBalancingPolicy\":\"xds_cluster_resolver_experimental\"}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT( - std::string(service_config.status().message()), - ::testing::MatchesRegex( - "Service config parsing errors: \\[" - "error parsing client channel global parameters:" CHILD_ERROR_TAG - "field:loadBalancingPolicy " - "error:xds_cluster_resolver_experimental requires " - "a config. Please use loadBalancingConfig instead.*")); -} - -TEST_F(ClientChannelParserTest, ValidTimeout) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"timeout\": \"5s\"\n" - " } ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - const auto* vector_ptr = - (*service_config) - ->GetMethodParsedConfigVector( - grpc_slice_from_static_string("/TestServ/TestMethod")); - ASSERT_NE(vector_ptr, nullptr); - auto parsed_config = ((*vector_ptr)[0]).get(); - EXPECT_EQ( - (static_cast(parsed_config)) - ->timeout(), - Duration::Seconds(5)); -} - -TEST_F(ClientChannelParserTest, InvalidTimeout) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"timeout\": \"5sec\"\n" - " } ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT( - std::string(service_config.status().message()), - ::testing::MatchesRegex( - "Service config parsing errors: \\[" - "errors parsing methodConfig: \\[" - "index 0: \\[" - "error parsing client channel method parameters: " CHILD_ERROR_TAG - "field:timeout error:type should be STRING of the form given " - "by google.proto.Duration.*")); -} - -TEST_F(ClientChannelParserTest, ValidWaitForReady) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"waitForReady\": true\n" - " } ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - const auto* vector_ptr = - (*service_config) - ->GetMethodParsedConfigVector( - grpc_slice_from_static_string("/TestServ/TestMethod")); - ASSERT_NE(vector_ptr, nullptr); - auto parsed_config = ((*vector_ptr)[0]).get(); - ASSERT_TRUE( - (static_cast(parsed_config)) - ->wait_for_ready() - .has_value()); - EXPECT_TRUE( - (static_cast(parsed_config)) - ->wait_for_ready() - .value()); -} - -TEST_F(ClientChannelParserTest, InvalidWaitForReady) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"service\", \"method\": \"method\" }\n" - " ],\n" - " \"waitForReady\": \"true\"\n" - " } ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT( - std::string(service_config.status().message()), - ::testing::MatchesRegex( - "Service config parsing errors: \\[" - "errors parsing methodConfig: \\[" - "index 0: \\[" - "error parsing client channel method parameters: " CHILD_ERROR_TAG - "field:waitForReady error:Type should be true/false.*")); -} - -TEST_F(ClientChannelParserTest, ValidHealthCheck) { - const char* test_json = - "{\n" - " \"healthCheckConfig\": {\n" - " \"serviceName\": \"health_check_service_name\"\n" - " }\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - const auto* parsed_config = - static_cast( - (*service_config)->GetGlobalParsedConfig(0)); - ASSERT_NE(parsed_config, nullptr); - EXPECT_EQ(parsed_config->health_check_service_name(), - "health_check_service_name"); -} - -TEST_F(ClientChannelParserTest, InvalidHealthCheckMultipleEntries) { - const char* test_json = - "{\n" - " \"healthCheckConfig\": {\n" - " \"serviceName\": \"health_check_service_name\"\n" - " },\n" - " \"healthCheckConfig\": {\n" - " \"serviceName\": \"health_check_service_name1\"\n" - " }\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ(service_config.status().message(), - "JSON parsing failed: [" - "duplicate key \"healthCheckConfig\" at index 104]"); -} - -// -// retry parser tests -// - -class RetryParserTest : public ::testing::Test { - protected: - void SetUp() override { - builder_ = std::make_unique( - [](CoreConfiguration::Builder* builder) { - builder->service_config_parser()->RegisterParser( - std::make_unique()); - }); - EXPECT_EQ(CoreConfiguration::Get().service_config_parser().GetParserIndex( - "retry"), - 0); - } - - private: - std::unique_ptr builder_; -}; - -TEST_F(RetryParserTest, ValidRetryThrottling) { - const char* test_json = - "{\n" - " \"retryThrottling\": {\n" - " \"maxTokens\": 2,\n" - " \"tokenRatio\": 1.0\n" - " }\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - const auto* parsed_config = static_cast( - (*service_config)->GetGlobalParsedConfig(0)); - ASSERT_NE(parsed_config, nullptr); - EXPECT_EQ(parsed_config->max_milli_tokens(), 2000); - EXPECT_EQ(parsed_config->milli_token_ratio(), 1000); -} - -TEST_F(RetryParserTest, RetryThrottlingMissingFields) { - const char* test_json = - "{\n" - " \"retryThrottling\": {\n" - " }\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT(std::string(service_config.status().message()), - ::testing::ContainsRegex( - "Service config parsing errors: \\[" - "error parsing retry global parameters:" - ".*retryThrottling" CHILD_ERROR_TAG - "field:retryThrottling field:maxTokens error:Not found" - ".*field:retryThrottling field:tokenRatio error:Not found")); -} - -TEST_F(RetryParserTest, InvalidRetryThrottlingNegativeMaxTokens) { - const char* test_json = - "{\n" - " \"retryThrottling\": {\n" - " \"maxTokens\": -2,\n" - " \"tokenRatio\": 1.0\n" - " }\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT(std::string(service_config.status().message()), - ::testing::ContainsRegex( - "Service config parsing errors: \\[" - "error parsing retry global parameters:" - ".*retryThrottling" CHILD_ERROR_TAG - "field:retryThrottling field:maxTokens error:should " - "be greater than zero")); -} - -TEST_F(RetryParserTest, InvalidRetryThrottlingInvalidTokenRatio) { - const char* test_json = - "{\n" - " \"retryThrottling\": {\n" - " \"maxTokens\": 2,\n" - " \"tokenRatio\": -1\n" - " }\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT(std::string(service_config.status().message()), - ::testing::ContainsRegex("Service config parsing errors: \\[" - "error parsing retry global parameters:" - ".*retryThrottling" CHILD_ERROR_TAG - "field:retryThrottling field:tokenRatio " - "error:Failed parsing")); -} - -TEST_F(RetryParserTest, ValidRetryPolicy) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 3,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - const auto* vector_ptr = - (*service_config) - ->GetMethodParsedConfigVector( - grpc_slice_from_static_string("/TestServ/TestMethod")); - ASSERT_NE(vector_ptr, nullptr); - const auto* parsed_config = - static_cast(((*vector_ptr)[0]).get()); - ASSERT_NE(parsed_config, nullptr); - EXPECT_EQ(parsed_config->max_attempts(), 3); - EXPECT_EQ(parsed_config->initial_backoff(), Duration::Seconds(1)); - EXPECT_EQ(parsed_config->max_backoff(), Duration::Minutes(2)); - EXPECT_EQ(parsed_config->backoff_multiplier(), 1.6f); - EXPECT_EQ(parsed_config->per_attempt_recv_timeout(), absl::nullopt); - EXPECT_TRUE( - parsed_config->retryable_status_codes().Contains(GRPC_STATUS_ABORTED)); -} - -TEST_F(RetryParserTest, InvalidRetryPolicyWrongType) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"retryPolicy\": 5\n" - " } ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT(std::string(service_config.status().message()), - ::testing::ContainsRegex( - "Service config parsing errors: \\[" - "errors parsing methodConfig: \\[" - "index 0: \\[" - "error parsing retry method parameters:.*" - "field:retryPolicy error:should be of type object")); -} - -TEST_F(RetryParserTest, InvalidRetryPolicyRequiredFieldsMissing) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT(std::string(service_config.status().message()), - ::testing::ContainsRegex( - "Service config parsing errors: \\[" - "errors parsing methodConfig: \\[" - "index 0: \\[" - "error parsing retry method parameters:.*" - "retryPolicy" CHILD_ERROR_TAG - ".*field:maxAttempts error:required field missing" - ".*field:initialBackoff error:does not exist" - ".*field:maxBackoff error:does not exist" - ".*field:backoffMultiplier error:required field missing")); -} - -TEST_F(RetryParserTest, InvalidRetryPolicyMaxAttemptsWrongType) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": \"FOO\",\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT(std::string(service_config.status().message()), - ::testing::ContainsRegex( - "Service config parsing errors: \\[" - "errors parsing methodConfig: \\[" - "index 0: \\[" - "error parsing retry method parameters:.*" - "retryPolicy" CHILD_ERROR_TAG - "field:maxAttempts error:should be of type number")); -} - -TEST_F(RetryParserTest, InvalidRetryPolicyMaxAttemptsBadValue) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 1,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT( - std::string(service_config.status().message()), - ::testing::ContainsRegex("Service config parsing errors: \\[" - "errors parsing methodConfig: \\[" - "index 0: \\[" - "error parsing retry method parameters:.*" - "retryPolicy" CHILD_ERROR_TAG - "field:maxAttempts error:should be at least 2")); -} - -TEST_F(RetryParserTest, InvalidRetryPolicyInitialBackoffWrongType) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1sec\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT(std::string(service_config.status().message()), - ::testing::ContainsRegex( - "Service config parsing errors: \\[" - "errors parsing methodConfig: \\[" - "index 0: \\[" - "error parsing retry method parameters:.*" - "retryPolicy" CHILD_ERROR_TAG - "field:initialBackoff error:type should be STRING of the " - "form given by google.proto.Duration")); -} - -TEST_F(RetryParserTest, InvalidRetryPolicyInitialBackoffBadValue) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"0s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT(std::string(service_config.status().message()), - ::testing::ContainsRegex( - "Service config parsing errors: \\[" - "errors parsing methodConfig: \\[" - "index 0: \\[" - "error parsing retry method parameters:.*" - "retryPolicy" CHILD_ERROR_TAG - "field:initialBackoff error:must be greater than 0")); -} - -TEST_F(RetryParserTest, InvalidRetryPolicyMaxBackoffWrongType) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120sec\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT(std::string(service_config.status().message()), - ::testing::ContainsRegex( - "Service config parsing errors: \\[" - "errors parsing methodConfig: \\[" - "index 0: \\[" - "error parsing retry method parameters:.*" - "retryPolicy" CHILD_ERROR_TAG - "field:maxBackoff error:type should be STRING of the form " - "given by google.proto.Duration")); -} - -TEST_F(RetryParserTest, InvalidRetryPolicyMaxBackoffBadValue) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"0s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT(std::string(service_config.status().message()), - ::testing::ContainsRegex( - "Service config parsing errors: \\[" - "errors parsing methodConfig: \\[" - "index 0: \\[" - "error parsing retry method parameters:.*" - "retryPolicy" CHILD_ERROR_TAG - "field:maxBackoff error:must be greater than 0")); -} - -TEST_F(RetryParserTest, InvalidRetryPolicyBackoffMultiplierWrongType) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": \"1.6\",\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT(std::string(service_config.status().message()), - ::testing::ContainsRegex( - "Service config parsing errors: \\[" - "errors parsing methodConfig: \\[" - "index 0: \\[" - "error parsing retry method parameters:.*" - "retryPolicy" CHILD_ERROR_TAG - "field:backoffMultiplier error:should be of type number")); -} - -TEST_F(RetryParserTest, InvalidRetryPolicyBackoffMultiplierBadValue) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 0,\n" - " \"retryableStatusCodes\": [ \"ABORTED\" ]\n" - " }\n" - " } ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT(std::string(service_config.status().message()), - ::testing::ContainsRegex( - "Service config parsing errors: \\[" - "errors parsing methodConfig: \\[" - "index 0: \\[" - "error parsing retry method parameters:.*" - "retryPolicy" CHILD_ERROR_TAG - "field:backoffMultiplier error:must be greater than 0")); -} - -TEST_F(RetryParserTest, InvalidRetryPolicyEmptyRetryableStatusCodes) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": \"1.6\",\n" - " \"retryableStatusCodes\": []\n" - " }\n" - " } ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT(std::string(service_config.status().message()), - ::testing::ContainsRegex( - "Service config parsing errors: \\[" - "errors parsing methodConfig: \\[" - "index 0: \\[" - "error parsing retry method parameters:.*" - "retryPolicy" CHILD_ERROR_TAG - "field:retryableStatusCodes error:must be non-empty")); -} - -TEST_F(RetryParserTest, InvalidRetryPolicyRetryableStatusCodesWrongType) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": \"1.6\",\n" - " \"retryableStatusCodes\": 0\n" - " }\n" - " } ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT(std::string(service_config.status().message()), - ::testing::ContainsRegex( - "Service config parsing errors: \\[" - "errors parsing methodConfig: \\[" - "index 0: \\[" - "error parsing retry method parameters:.*" - "retryPolicy" CHILD_ERROR_TAG - "field:retryableStatusCodes error:must be of type array")); -} - -TEST_F(RetryParserTest, InvalidRetryPolicyUnparseableRetryableStatusCodes) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": \"1.6\",\n" - " \"retryableStatusCodes\": [\"FOO\", 2]\n" - " }\n" - " } ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT(std::string(service_config.status().message()), - ::testing::ContainsRegex( - "Service config parsing errors: \\[" - "errors parsing methodConfig: \\[" - "index 0: \\[" - "error parsing retry method parameters:.*" - "retryPolicy" CHILD_ERROR_TAG "field:retryableStatusCodes " - "error:failed to parse status code" - ".*field:retryableStatusCodes " - "error:status codes should be of type string")); -} - -TEST_F(RetryParserTest, ValidRetryPolicyWithPerAttemptRecvTimeout) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"perAttemptRecvTimeout\": \"1s\",\n" - " \"retryableStatusCodes\": [\"ABORTED\"]\n" - " }\n" - " } ]\n" - "}"; - const ChannelArgs args = - ChannelArgs().Set(GRPC_ARG_EXPERIMENTAL_ENABLE_HEDGING, 1); - auto service_config = ServiceConfigImpl::Create(args, test_json); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - const auto* vector_ptr = - (*service_config) - ->GetMethodParsedConfigVector( - grpc_slice_from_static_string("/TestServ/TestMethod")); - ASSERT_NE(vector_ptr, nullptr); - const auto* parsed_config = - static_cast(((*vector_ptr)[0]).get()); - ASSERT_NE(parsed_config, nullptr); - EXPECT_EQ(parsed_config->max_attempts(), 2); - EXPECT_EQ(parsed_config->initial_backoff(), Duration::Seconds(1)); - EXPECT_EQ(parsed_config->max_backoff(), Duration::Minutes(2)); - EXPECT_EQ(parsed_config->backoff_multiplier(), 1.6f); - EXPECT_EQ(parsed_config->per_attempt_recv_timeout(), Duration::Seconds(1)); - EXPECT_TRUE( - parsed_config->retryable_status_codes().Contains(GRPC_STATUS_ABORTED)); -} - -TEST_F(RetryParserTest, - ValidRetryPolicyWithPerAttemptRecvTimeoutIgnoredWhenHedgingDisabled) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"perAttemptRecvTimeout\": \"1s\",\n" - " \"retryableStatusCodes\": [\"ABORTED\"]\n" - " }\n" - " } ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - const auto* vector_ptr = - (*service_config) - ->GetMethodParsedConfigVector( - grpc_slice_from_static_string("/TestServ/TestMethod")); - ASSERT_NE(vector_ptr, nullptr); - const auto* parsed_config = - static_cast(((*vector_ptr)[0]).get()); - ASSERT_NE(parsed_config, nullptr); - EXPECT_EQ(parsed_config->max_attempts(), 2); - EXPECT_EQ(parsed_config->initial_backoff(), Duration::Seconds(1)); - EXPECT_EQ(parsed_config->max_backoff(), Duration::Minutes(2)); - EXPECT_EQ(parsed_config->backoff_multiplier(), 1.6f); - EXPECT_EQ(parsed_config->per_attempt_recv_timeout(), absl::nullopt); - EXPECT_TRUE( - parsed_config->retryable_status_codes().Contains(GRPC_STATUS_ABORTED)); -} - -TEST_F(RetryParserTest, - ValidRetryPolicyWithPerAttemptRecvTimeoutAndUnsetRetryableStatusCodes) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": 1.6,\n" - " \"perAttemptRecvTimeout\": \"1s\"\n" - " }\n" - " } ]\n" - "}"; - const ChannelArgs args = - ChannelArgs().Set(GRPC_ARG_EXPERIMENTAL_ENABLE_HEDGING, 1); - auto service_config = ServiceConfigImpl::Create(args, test_json); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - const auto* vector_ptr = - (*service_config) - ->GetMethodParsedConfigVector( - grpc_slice_from_static_string("/TestServ/TestMethod")); - ASSERT_NE(vector_ptr, nullptr); - const auto* parsed_config = - static_cast(((*vector_ptr)[0]).get()); - ASSERT_NE(parsed_config, nullptr); - EXPECT_EQ(parsed_config->max_attempts(), 2); - EXPECT_EQ(parsed_config->initial_backoff(), Duration::Seconds(1)); - EXPECT_EQ(parsed_config->max_backoff(), Duration::Minutes(2)); - EXPECT_EQ(parsed_config->backoff_multiplier(), 1.6f); - EXPECT_EQ(parsed_config->per_attempt_recv_timeout(), Duration::Seconds(1)); - EXPECT_TRUE(parsed_config->retryable_status_codes().Empty()); -} - -TEST_F(RetryParserTest, InvalidRetryPolicyPerAttemptRecvTimeoutUnparseable) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": \"1.6\",\n" - " \"perAttemptRecvTimeout\": \"1sec\",\n" - " \"retryableStatusCodes\": [\"ABORTED\"]\n" - " }\n" - " } ]\n" - "}"; - const ChannelArgs args = - ChannelArgs().Set(GRPC_ARG_EXPERIMENTAL_ENABLE_HEDGING, 1); - auto service_config = ServiceConfigImpl::Create(args, test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT(std::string(service_config.status().message()), - ::testing::ContainsRegex( - "Service config parsing errors: \\[" - "errors parsing methodConfig: \\[" - "index 0: \\[" - "error parsing retry method parameters:.*" - "retryPolicy" CHILD_ERROR_TAG - "field:perAttemptRecvTimeout error:type must be STRING " - "of the form given by google.proto.Duration.")); -} - -TEST_F(RetryParserTest, InvalidRetryPolicyPerAttemptRecvTimeoutWrongType) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": \"1.6\",\n" - " \"perAttemptRecvTimeout\": 1,\n" - " \"retryableStatusCodes\": [\"ABORTED\"]\n" - " }\n" - " } ]\n" - "}"; - const ChannelArgs args = - ChannelArgs().Set(GRPC_ARG_EXPERIMENTAL_ENABLE_HEDGING, 1); - auto service_config = ServiceConfigImpl::Create(args, test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT(std::string(service_config.status().message()), - ::testing::ContainsRegex( - "Service config parsing errors: \\[" - "errors parsing methodConfig: \\[" - "index 0: \\[" - "error parsing retry method parameters:.*" - "retryPolicy" CHILD_ERROR_TAG - "field:perAttemptRecvTimeout error:type must be STRING " - "of the form given by google.proto.Duration.")); -} - -TEST_F(RetryParserTest, InvalidRetryPolicyPerAttemptRecvTimeoutBadValue) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"retryPolicy\": {\n" - " \"maxAttempts\": 2,\n" - " \"initialBackoff\": \"1s\",\n" - " \"maxBackoff\": \"120s\",\n" - " \"backoffMultiplier\": \"1.6\",\n" - " \"perAttemptRecvTimeout\": \"0s\",\n" - " \"retryableStatusCodes\": [\"ABORTED\"]\n" - " }\n" - " } ]\n" - "}"; - const ChannelArgs args = - ChannelArgs().Set(GRPC_ARG_EXPERIMENTAL_ENABLE_HEDGING, 1); - auto service_config = ServiceConfigImpl::Create(args, test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT(std::string(service_config.status().message()), - ::testing::ContainsRegex( - "Service config parsing errors: \\[" - "errors parsing methodConfig: \\[" - "index 0: \\[" - "error parsing retry method parameters:.*" - "retryPolicy" CHILD_ERROR_TAG - "field:perAttemptRecvTimeout error:must be greater than 0")); -} - -// -// message_size parser tests -// - -class MessageSizeParserTest : public ::testing::Test { - protected: - void SetUp() override { - builder_ = std::make_unique( - [](CoreConfiguration::Builder* builder) { - builder->service_config_parser()->RegisterParser( - std::make_unique()); - }); - EXPECT_EQ(CoreConfiguration::Get().service_config_parser().GetParserIndex( - "message_size"), - 0); - } - - private: - std::unique_ptr builder_; -}; - -TEST_F(MessageSizeParserTest, Valid) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"maxRequestMessageBytes\": 1024,\n" - " \"maxResponseMessageBytes\": 1024\n" - " } ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - const auto* vector_ptr = - (*service_config) - ->GetMethodParsedConfigVector( - grpc_slice_from_static_string("/TestServ/TestMethod")); - ASSERT_NE(vector_ptr, nullptr); - auto parsed_config = - static_cast(((*vector_ptr)[0]).get()); - ASSERT_NE(parsed_config, nullptr); - EXPECT_EQ(parsed_config->limits().max_send_size, 1024); - EXPECT_EQ(parsed_config->limits().max_recv_size, 1024); -} - -TEST_F(MessageSizeParserTest, InvalidMaxRequestMessageBytes) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"maxRequestMessageBytes\": -1024\n" - " } ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT(std::string(service_config.status().message()), - ::testing::ContainsRegex( - "Service config parsing errors: \\[" - "errors parsing methodConfig: \\[" - "index 0: \\[" - "error parsing message size method parameters:.*" - "Message size parser" CHILD_ERROR_TAG - "field:maxRequestMessageBytes error:should be non-negative")); -} - -TEST_F(MessageSizeParserTest, InvalidMaxResponseMessageBytes) { - const char* test_json = - "{\n" - " \"methodConfig\": [ {\n" - " \"name\": [\n" - " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" - " ],\n" - " \"maxResponseMessageBytes\": {}\n" - " } ]\n" - "}"; - auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); - EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT(std::string(service_config.status().message()), - ::testing::ContainsRegex( - "Service config parsing errors: \\[" - "errors parsing methodConfig: \\[" - "index 0: \\[" - "error parsing message size method parameters:.*" - "Message size parser" CHILD_ERROR_TAG - "field:maxResponseMessageBytes error:should be of type " - "number")); -} - -} // namespace testing -} // namespace grpc_core - -int main(int argc, char** argv) { - ::testing::InitGoogleTest(&argc, argv); - grpc::testing::TestEnvironment env(&argc, argv); - grpc_init(); - int ret = RUN_ALL_TESTS(); - grpc_shutdown(); - return ret; -} diff --git a/test/core/ext/filters/rbac/rbac_service_config_parser_test.cc b/test/core/ext/filters/rbac/rbac_service_config_parser_test.cc index 1b1a0b335f4..e4aa1f9a920 100644 --- a/test/core/ext/filters/rbac/rbac_service_config_parser_test.cc +++ b/test/core/ext/filters/rbac/rbac_service_config_parser_test.cc @@ -14,10 +14,8 @@ #include "src/core/ext/filters/rbac/rbac_service_config_parser.h" -#include - #include "absl/status/status.h" -#include "gmock/gmock.h" +#include "absl/status/statusor.h" #include "gtest/gtest.h" #include @@ -28,9 +26,6 @@ #include "src/core/lib/service_config/service_config_impl.h" #include "test/core/util/test_config.h" -// A regular expression to enter referenced or child errors. -#define CHILD_ERROR_TAG ".*children.*" - namespace grpc_core { namespace testing { namespace { @@ -148,10 +143,9 @@ TEST(RbacServiceConfigParsingTest, BadRbacPolicyType) { ChannelArgs args = ChannelArgs().Set(GRPC_ARG_PARSE_RBAC_METHOD_CONFIG, 1); auto service_config = ServiceConfigImpl::Create(args, test_json); EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT( - std::string(service_config.status().message()), - ::testing::ContainsRegex("Rbac parser" CHILD_ERROR_TAG - "field:rbacPolicy error:type should be ARRAY")) + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].rbacPolicy error:is not an array]") << service_config.status(); } @@ -168,11 +162,9 @@ TEST(RbacServiceConfigParsingTest, BadRulesType) { ChannelArgs args = ChannelArgs().Set(GRPC_ARG_PARSE_RBAC_METHOD_CONFIG, 1); auto service_config = ServiceConfigImpl::Create(args, test_json); EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT( - std::string(service_config.status().message()), - ::testing::ContainsRegex("Rbac parser" CHILD_ERROR_TAG - "rbacPolicy\\[0\\]" CHILD_ERROR_TAG - "field:rules error:type should be OBJECT")) + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].rbacPolicy[0].rules error:is not an object]") << service_config.status(); } @@ -194,12 +186,12 @@ TEST(RbacServiceConfigParsingTest, BadActionAndPolicyType) { ChannelArgs args = ChannelArgs().Set(GRPC_ARG_PARSE_RBAC_METHOD_CONFIG, 1); auto service_config = ServiceConfigImpl::Create(args, test_json); EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT( - std::string(service_config.status().message()), - ::testing::ContainsRegex("Rbac parser" CHILD_ERROR_TAG - "rbacPolicy\\[0\\]" CHILD_ERROR_TAG - "field:action error:type should be NUMBER.*" - "field:policies error:type should be OBJECT")) + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].rbacPolicy[0].rules.action " + "error:is not a number; " + "field:methodConfig[0].rbacPolicy[0].rules.policies " + "error:is not an object]") << service_config.status(); } @@ -224,13 +216,12 @@ TEST(RbacServiceConfigParsingTest, MissingPermissionAndPrincipals) { ChannelArgs args = ChannelArgs().Set(GRPC_ARG_PARSE_RBAC_METHOD_CONFIG, 1); auto service_config = ServiceConfigImpl::Create(args, test_json); EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT( - std::string(service_config.status().message()), - ::testing::ContainsRegex("Rbac parser" CHILD_ERROR_TAG - "rbacPolicy\\[0\\]" CHILD_ERROR_TAG - "policies key:'policy'" CHILD_ERROR_TAG - "field:permissions error:does not exist.*" - "field:principals error:does not exist")) + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions error:field not present; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".principals error:field not present]") << service_config.status(); } @@ -257,13 +248,12 @@ TEST(RbacServiceConfigParsingTest, EmptyPrincipalAndPermission) { ChannelArgs args = ChannelArgs().Set(GRPC_ARG_PARSE_RBAC_METHOD_CONFIG, 1); auto service_config = ServiceConfigImpl::Create(args, test_json); EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT( - std::string(service_config.status().message()), - ::testing::ContainsRegex( - "Rbac parser" CHILD_ERROR_TAG "rbacPolicy\\[0\\]" CHILD_ERROR_TAG - "policies key:'policy'" CHILD_ERROR_TAG - "permissions\\[0\\]" CHILD_ERROR_TAG "No valid rule found.*" - "principals\\[0\\]" CHILD_ERROR_TAG "No valid id found")) + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[0] error:no valid rule found; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".principals[0] error:no valid id found]") << service_config.status(); } @@ -370,53 +360,51 @@ TEST(RbacServiceConfigParsingTest, VariousPermissionsAndPrincipalsBadTypes) { ChannelArgs args = ChannelArgs().Set(GRPC_ARG_PARSE_RBAC_METHOD_CONFIG, 1); auto service_config = ServiceConfigImpl::Create(args, test_json); EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT( - std::string(service_config.status().message()), - ::testing::ContainsRegex( - "Rbac parser" CHILD_ERROR_TAG "rbacPolicy\\[0\\]" CHILD_ERROR_TAG - "policies key:'policy'" CHILD_ERROR_TAG - "permissions\\[0\\]" CHILD_ERROR_TAG - "field:andRules error:type should be OBJECT.*" - "permissions\\[1\\]" CHILD_ERROR_TAG - "field:orRules error:type should be OBJECT.*" - "permissions\\[2\\]" CHILD_ERROR_TAG - "field:any error:type should be BOOLEAN.*" - "permissions\\[3\\]" CHILD_ERROR_TAG - "field:header error:type should be OBJECT.*" - "permissions\\[4\\]" CHILD_ERROR_TAG - "field:urlPath error:type should be OBJECT.*" - "permissions\\[5\\]" CHILD_ERROR_TAG - "field:destinationIp error:type should be OBJECT.*" - "permissions\\[6\\]" CHILD_ERROR_TAG - "field:destinationPort error:failed to parse.*" - "permissions\\[7\\]" CHILD_ERROR_TAG - "field:metadata error:type should be OBJECT.*" - "permissions\\[8\\]" CHILD_ERROR_TAG - "field:notRule error:type should be OBJECT.*" - "permissions\\[9\\]" CHILD_ERROR_TAG - "field:requestedServerName error:type should be OBJECT.*" - "principals\\[0\\]" CHILD_ERROR_TAG - "field:andIds error:type should be OBJECT.*" - "principals\\[1\\]" CHILD_ERROR_TAG - "field:orIds error:type should be OBJECT.*" - "principals\\[2\\]" CHILD_ERROR_TAG - "field:any error:type should be BOOLEAN.*" - "principals\\[3\\]" CHILD_ERROR_TAG - "field:authenticated error:type should be OBJECT.*" - "principals\\[4\\]" CHILD_ERROR_TAG - "field:sourceIp error:type should be OBJECT.*" - "principals\\[5\\]" CHILD_ERROR_TAG - "field:directRemoteIp error:type should be OBJECT.*" - "principals\\[6\\]" CHILD_ERROR_TAG - "field:remoteIp error:type should be OBJECT.*" - "principals\\[7\\]" CHILD_ERROR_TAG - "field:header error:type should be OBJECT.*" - "principals\\[8\\]" CHILD_ERROR_TAG - "field:urlPath error:type should be OBJECT.*" - "principals\\[9\\]" CHILD_ERROR_TAG - "field:metadata error:type should be OBJECT.*" - "principals\\[10\\]" CHILD_ERROR_TAG - "field:notId error:type should be OBJECT.*")) + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[0].andRules error:is not an object; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[1].orRules error:is not an object; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[2].any error:is not a boolean; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[3].header error:is not an object; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[4].urlPath error:is not an object; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[5].destinationIp error:is not an object; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[6].destinationPort " + "error:failed to parse non-negative number; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[7].metadata error:is not an object; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[8].notRule error:is not an object; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[9].requestedServerName error:is not an object; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".principals[0].andIds error:is not an object; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".principals[10].notId error:is not an object; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".principals[1].orIds error:is not an object; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".principals[2].any error:is not a boolean; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".principals[3].authenticated error:is not an object; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".principals[4].sourceIp error:is not an object; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".principals[5].directRemoteIp error:is not an object; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".principals[6].remoteIp error:is not an object; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".principals[7].header error:is not an object; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".principals[8].urlPath error:is not an object; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".principals[9].metadata error:is not an object]") << service_config.status(); } @@ -484,7 +472,8 @@ TEST(RbacServiceConfigParsingTest, HeaderMatcherBadTypes) { " {\"header\":{\"name\":\"name\", \"presentMatch\":1}},\n" " {\"header\":{\"name\":\"name\", \"prefixMatch\":1}},\n" " {\"header\":{\"name\":\"name\", \"suffixMatch\":1}},\n" - " {\"header\":{\"name\":\"name\", \"containsMatch\":1}}\n" + " {\"header\":{\"name\":\"name\", \"containsMatch\":1}},\n" + " {\"header\":{\"name\":\"name\"}}\n" " ],\n" " \"principals\":[]\n" " }\n" @@ -496,26 +485,26 @@ TEST(RbacServiceConfigParsingTest, HeaderMatcherBadTypes) { ChannelArgs args = ChannelArgs().Set(GRPC_ARG_PARSE_RBAC_METHOD_CONFIG, 1); auto service_config = ServiceConfigImpl::Create(args, test_json); EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT( - std::string(service_config.status().message()), - ::testing::ContainsRegex( - "Rbac parser" CHILD_ERROR_TAG "rbacPolicy\\[0\\]" CHILD_ERROR_TAG - "policies key:'policy'" CHILD_ERROR_TAG - "permissions\\[0\\]" CHILD_ERROR_TAG "header" CHILD_ERROR_TAG - "field:invertMatch error:type should be BOOLEAN.*" - "field:exactMatch error:type should be STRING.*" - "permissions\\[1\\]" CHILD_ERROR_TAG "header" CHILD_ERROR_TAG - "field:safeRegexMatch error:type should be OBJECT.*" - "permissions\\[2\\]" CHILD_ERROR_TAG "header" CHILD_ERROR_TAG - "field:rangeMatch error:type should be OBJECT.*" - "permissions\\[3\\]" CHILD_ERROR_TAG "header" CHILD_ERROR_TAG - "field:presentMatch error:type should be BOOLEAN.*" - "permissions\\[4\\]" CHILD_ERROR_TAG "header" CHILD_ERROR_TAG - "field:prefixMatch error:type should be STRING.*" - "permissions\\[5\\]" CHILD_ERROR_TAG "header" CHILD_ERROR_TAG - "field:suffixMatch error:type should be STRING.*" - "permissions\\[6\\]" CHILD_ERROR_TAG "header" CHILD_ERROR_TAG - "field:containsMatch error:type should be STRING.*")) + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[0].header.exactMatch error:is not a string; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[0].header.invertMatch error:is not a boolean; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[1].header.safeRegexMatch error:is not an object; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[2].header.rangeMatch error:is not an object; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[3].header.presentMatch error:is not a boolean; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[4].header.prefixMatch error:is not a string; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[5].header.suffixMatch error:is not a string; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[6].header.containsMatch error:is not a string; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[7].header error:no valid matcher found]") << service_config.status(); } @@ -578,7 +567,8 @@ TEST(RbacServiceConfigParsingTest, StringMatcherBadTypes) { " {\"requestedServerName\":{\"prefix\":1}},\n" " {\"requestedServerName\":{\"suffix\":1}},\n" " {\"requestedServerName\":{\"safeRegex\":1}},\n" - " {\"requestedServerName\":{\"contains\":1}}\n" + " {\"requestedServerName\":{\"contains\":1}},\n" + " {\"requestedServerName\":{}}\n" " ],\n" " \"principals\":[]\n" " }\n" @@ -590,27 +580,27 @@ TEST(RbacServiceConfigParsingTest, StringMatcherBadTypes) { ChannelArgs args = ChannelArgs().Set(GRPC_ARG_PARSE_RBAC_METHOD_CONFIG, 1); auto service_config = ServiceConfigImpl::Create(args, test_json); EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_THAT( - std::string(service_config.status().message()), - ::testing::ContainsRegex("Rbac parser" CHILD_ERROR_TAG - "rbacPolicy\\[0\\]" CHILD_ERROR_TAG - "policies key:'policy'" CHILD_ERROR_TAG - "permissions\\[0\\]" CHILD_ERROR_TAG - "requestedServerName" CHILD_ERROR_TAG - "field:ignoreCase error:type should be BOOLEAN.*" - "field:exact error:type should be STRING.*" - "permissions\\[1\\]" CHILD_ERROR_TAG - "requestedServerName" CHILD_ERROR_TAG - "field:prefix error:type should be STRING.*" - "permissions\\[2\\]" CHILD_ERROR_TAG - "requestedServerName" CHILD_ERROR_TAG - "field:suffix error:type should be STRING.*" - "permissions\\[3\\]" CHILD_ERROR_TAG - "requestedServerName" CHILD_ERROR_TAG - "field:safeRegex error:type should be OBJECT.*" - "permissions\\[4\\]" CHILD_ERROR_TAG - "requestedServerName" CHILD_ERROR_TAG - "field:contains error:type should be STRING.*")) + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[0].requestedServerName.exact error:is not a string; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[0].requestedServerName.ignoreCase " + "error:is not a boolean; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[1].requestedServerName.prefix " + "error:is not a string; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[2].requestedServerName.suffix " + "error:is not a string; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[3].requestedServerName.safeRegex " + "error:is not an object; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[4].requestedServerName.contains " + "error:is not a string; " + "field:methodConfig[0].rbacPolicy[0].rules.policies[\"policy\"]" + ".permissions[5].requestedServerName error:no valid matcher found]") << service_config.status(); } diff --git a/test/core/json/json_object_loader_test.cc b/test/core/json/json_object_loader_test.cc index e65cc902228..73088675047 100644 --- a/test/core/json/json_object_loader_test.cc +++ b/test/core/json/json_object_loader_test.cc @@ -49,6 +49,7 @@ TYPED_TEST_P(SignedIntegerTest, IntegerFields) { TypeParam value = 0; TypeParam optional_value = 0; absl::optional absl_optional_value; + std::unique_ptr unique_ptr_value; static const JsonLoaderInterface* JsonLoader(const JsonArgs&) { static const auto* loader = @@ -57,6 +58,7 @@ TYPED_TEST_P(SignedIntegerTest, IntegerFields) { .OptionalField("optional_value", &TestStruct::optional_value) .OptionalField("absl_optional_value", &TestStruct::absl_optional_value) + .OptionalField("unique_ptr_value", &TestStruct::unique_ptr_value) .Finish(); return loader; } @@ -79,6 +81,13 @@ TYPED_TEST_P(SignedIntegerTest, IntegerFields) { EXPECT_EQ(test_struct->value, 5); EXPECT_EQ(test_struct->optional_value, 0); EXPECT_FALSE(test_struct->absl_optional_value.has_value()); + // Fails to parse number from JSON string. + test_struct = Parse("{\"value\": \"foo\"}"); + EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ( + test_struct.status().message(), + "errors validating JSON: [field:value error:failed to parse number]") + << test_struct.status(); // Fails if required field is not present. test_struct = Parse("{}"); EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument); @@ -88,20 +97,23 @@ TYPED_TEST_P(SignedIntegerTest, IntegerFields) { // Optional fields present. test_struct = Parse( "{\"value\": 5, \"optional_value\": 7, " - "\"absl_optional_value\": 9}"); + "\"absl_optional_value\": 9, \"unique_ptr_value\": 11}"); ASSERT_TRUE(test_struct.ok()) << test_struct.status(); EXPECT_EQ(test_struct->value, 5); EXPECT_EQ(test_struct->optional_value, 7); EXPECT_EQ(test_struct->absl_optional_value, 9); + ASSERT_NE(test_struct->unique_ptr_value, nullptr); + EXPECT_EQ(*test_struct->unique_ptr_value, 11); // Wrong JSON type. test_struct = Parse( "{\"value\": [], \"optional_value\": {}, " - "\"absl_optional_value\": true}"); + "\"absl_optional_value\": true, \"unique_ptr_value\": false}"); EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument); EXPECT_EQ(test_struct.status().message(), "errors validating JSON: [" "field:absl_optional_value error:is not a number; " "field:optional_value error:is not a number; " + "field:unique_ptr_value error:is not a number; " "field:value error:is not a number]") << test_struct.status(); } @@ -125,6 +137,7 @@ TYPED_TEST_P(UnsignedIntegerTest, IntegerFields) { TypeParam value = 0; TypeParam optional_value = 0; absl::optional absl_optional_value; + std::unique_ptr unique_ptr_value; static const JsonLoaderInterface* JsonLoader(const JsonArgs&) { static const auto* loader = @@ -133,6 +146,7 @@ TYPED_TEST_P(UnsignedIntegerTest, IntegerFields) { .OptionalField("optional_value", &TestStruct::optional_value) .OptionalField("absl_optional_value", &TestStruct::absl_optional_value) + .OptionalField("unique_ptr_value", &TestStruct::unique_ptr_value) .Finish(); return loader; } @@ -156,6 +170,13 @@ TYPED_TEST_P(UnsignedIntegerTest, IntegerFields) { EXPECT_EQ(test_struct->value, 5); EXPECT_EQ(test_struct->optional_value, 0); EXPECT_FALSE(test_struct->absl_optional_value.has_value()); + // Fails to parse number from JSON string. + test_struct = Parse("{\"value\": \"foo\"}"); + EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(test_struct.status().message(), + "errors validating JSON: [" + "field:value error:failed to parse non-negative number]") + << test_struct.status(); // Fails if required field is not present. test_struct = Parse("{}"); EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument); @@ -165,21 +186,24 @@ TYPED_TEST_P(UnsignedIntegerTest, IntegerFields) { // Optional fields present. test_struct = Parse( "{\"value\": 5, \"optional_value\": 7, " - "\"absl_optional_value\": 9}"); + "\"absl_optional_value\": 9, \"unique_ptr_value\": 11}"); ASSERT_TRUE(test_struct.ok()) << test_struct.status(); EXPECT_EQ(test_struct->value, 5); EXPECT_EQ(test_struct->optional_value, 7); ASSERT_TRUE(test_struct->absl_optional_value.has_value()); EXPECT_EQ(*test_struct->absl_optional_value, 9); + ASSERT_NE(test_struct->unique_ptr_value, nullptr); + EXPECT_EQ(*test_struct->unique_ptr_value, 11); // Wrong JSON type. test_struct = Parse( "{\"value\": [], \"optional_value\": {}, " - "\"absl_optional_value\": true}"); + "\"absl_optional_value\": true, \"unique_ptr_value\": false}"); EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument); EXPECT_EQ(test_struct.status().message(), "errors validating JSON: [" "field:absl_optional_value error:is not a number; " "field:optional_value error:is not a number; " + "field:unique_ptr_value error:is not a number; " "field:value error:is not a number]") << test_struct.status(); } @@ -203,6 +227,7 @@ TYPED_TEST_P(FloatingPointTest, FloatFields) { TypeParam value = 0; TypeParam optional_value = 0; absl::optional absl_optional_value; + std::unique_ptr unique_ptr_value; static const JsonLoaderInterface* JsonLoader(const JsonArgs&) { static const auto* loader = @@ -211,6 +236,7 @@ TYPED_TEST_P(FloatingPointTest, FloatFields) { .OptionalField("optional_value", &TestStruct::optional_value) .OptionalField("absl_optional_value", &TestStruct::absl_optional_value) + .OptionalField("unique_ptr_value", &TestStruct::unique_ptr_value) .Finish(); return loader; } @@ -233,6 +259,13 @@ TYPED_TEST_P(FloatingPointTest, FloatFields) { EXPECT_NEAR(test_struct->value, 5.2, 0.0001); EXPECT_EQ(test_struct->optional_value, 0); EXPECT_FALSE(test_struct->absl_optional_value.has_value()); + // Fails to parse number from JSON string. + test_struct = Parse("{\"value\": \"foo\"}"); + EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(test_struct.status().message(), + "errors validating JSON: [" + "field:value error:failed to parse floating-point number]") + << test_struct.status(); // Fails if required field is not present. test_struct = Parse("{}"); EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument); @@ -242,21 +275,24 @@ TYPED_TEST_P(FloatingPointTest, FloatFields) { // Optional fields present. test_struct = Parse( "{\"value\": 5.2, \"optional_value\": 7.5, " - "\"absl_optional_value\": 9.8}"); + "\"absl_optional_value\": 9.8, \"unique_ptr_value\": 11.5}"); ASSERT_TRUE(test_struct.ok()) << test_struct.status(); EXPECT_NEAR(test_struct->value, 5.2, 0.0001); EXPECT_NEAR(test_struct->optional_value, 7.5, 0.0001); ASSERT_TRUE(test_struct->absl_optional_value.has_value()); EXPECT_NEAR(*test_struct->absl_optional_value, 9.8, 0.0001); + ASSERT_NE(test_struct->unique_ptr_value, nullptr); + EXPECT_NEAR(*test_struct->unique_ptr_value, 11.5, 0.0001); // Wrong JSON type. test_struct = Parse( "{\"value\": [], \"optional_value\": {}, " - "\"absl_optional_value\": true}"); + "\"absl_optional_value\": true, \"unique_ptr_value\": false}"); EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument); EXPECT_EQ(test_struct.status().message(), "errors validating JSON: [" "field:absl_optional_value error:is not a number; " "field:optional_value error:is not a number; " + "field:unique_ptr_value error:is not a number; " "field:value error:is not a number]") << test_struct.status(); } @@ -275,6 +311,7 @@ TEST(JsonObjectLoader, BooleanFields) { bool value = false; bool optional_value = true; absl::optional absl_optional_value; + std::unique_ptr unique_ptr_value; static const JsonLoaderInterface* JsonLoader(const JsonArgs&) { static const auto* loader = @@ -283,6 +320,7 @@ TEST(JsonObjectLoader, BooleanFields) { .OptionalField("optional_value", &TestStruct::optional_value) .OptionalField("absl_optional_value", &TestStruct::absl_optional_value) + .OptionalField("unique_ptr_value", &TestStruct::unique_ptr_value) .Finish(); return loader; } @@ -308,20 +346,23 @@ TEST(JsonObjectLoader, BooleanFields) { // Optional fields present. test_struct = Parse( "{\"value\": true, \"optional_value\": false," - "\"absl_optional_value\": true}"); + "\"absl_optional_value\": true, \"unique_ptr_value\": false}"); ASSERT_TRUE(test_struct.ok()) << test_struct.status(); EXPECT_EQ(test_struct->value, true); EXPECT_EQ(test_struct->optional_value, false); EXPECT_EQ(test_struct->absl_optional_value, true); + ASSERT_NE(test_struct->unique_ptr_value, nullptr); + EXPECT_EQ(*test_struct->unique_ptr_value, false); // Wrong JSON type. test_struct = Parse( "{\"value\": [], \"optional_value\": {}, " - "\"absl_optional_value\": 1}"); + "\"absl_optional_value\": 1, \"unique_ptr_value\": \"foo\"}"); EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument); EXPECT_EQ(test_struct.status().message(), "errors validating JSON: [" "field:absl_optional_value error:is not a boolean; " "field:optional_value error:is not a boolean; " + "field:unique_ptr_value error:is not a boolean; " "field:value error:is not a boolean]") << test_struct.status(); } @@ -335,6 +376,7 @@ TEST(JsonObjectLoader, StringFields) { std::string value; std::string optional_value; absl::optional absl_optional_value; + std::unique_ptr unique_ptr_value; static const JsonLoaderInterface* JsonLoader(const JsonArgs&) { static const auto* loader = @@ -343,6 +385,7 @@ TEST(JsonObjectLoader, StringFields) { .OptionalField("optional_value", &TestStruct::optional_value) .OptionalField("absl_optional_value", &TestStruct::absl_optional_value) + .OptionalField("unique_ptr_value", &TestStruct::unique_ptr_value) .Finish(); return loader; } @@ -362,20 +405,23 @@ TEST(JsonObjectLoader, StringFields) { // Optional fields present. test_struct = Parse( "{\"value\": \"foo\", \"optional_value\": \"bar\"," - "\"absl_optional_value\": \"baz\"}"); + "\"absl_optional_value\": \"baz\", \"unique_ptr_value\": \"quux\"}"); ASSERT_TRUE(test_struct.ok()) << test_struct.status(); EXPECT_EQ(test_struct->value, "foo"); EXPECT_EQ(test_struct->optional_value, "bar"); EXPECT_EQ(test_struct->absl_optional_value, "baz"); + ASSERT_NE(test_struct->unique_ptr_value, nullptr); + EXPECT_EQ(*test_struct->unique_ptr_value, "quux"); // Wrong JSON type. test_struct = Parse( "{\"value\": [], \"optional_value\": {}, " - "\"absl_optional_value\": 1}"); + "\"absl_optional_value\": 1, \"unique_ptr_value\": true}"); EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument); EXPECT_EQ(test_struct.status().message(), "errors validating JSON: [" "field:absl_optional_value error:is not a string; " "field:optional_value error:is not a string; " + "field:unique_ptr_value error:is not a string; " "field:value error:is not a string]") << test_struct.status(); } @@ -389,6 +435,7 @@ TEST(JsonObjectLoader, DurationFields) { Duration value = Duration::Zero(); Duration optional_value = Duration::Zero(); absl::optional absl_optional_value; + std::unique_ptr unique_ptr_value; static const JsonLoaderInterface* JsonLoader(const JsonArgs&) { static const auto* loader = @@ -397,6 +444,7 @@ TEST(JsonObjectLoader, DurationFields) { .OptionalField("optional_value", &TestStruct::optional_value) .OptionalField("absl_optional_value", &TestStruct::absl_optional_value) + .OptionalField("unique_ptr_value", &TestStruct::unique_ptr_value) .Finish(); return loader; } @@ -441,20 +489,23 @@ TEST(JsonObjectLoader, DurationFields) { // Optional fields present. test_struct = Parse( "{\"value\": \"3s\", \"optional_value\": \"3.2s\", " - "\"absl_optional_value\": \"10s\"}"); + "\"absl_optional_value\": \"10s\", \"unique_ptr_value\": \"11s\"}"); ASSERT_TRUE(test_struct.ok()) << test_struct.status(); EXPECT_EQ(test_struct->value, Duration::Seconds(3)); EXPECT_EQ(test_struct->optional_value, Duration::Milliseconds(3200)); EXPECT_EQ(test_struct->absl_optional_value, Duration::Seconds(10)); + ASSERT_NE(test_struct->unique_ptr_value, nullptr); + EXPECT_EQ(*test_struct->unique_ptr_value, Duration::Seconds(11)); // Wrong JSON type. test_struct = Parse( "{\"value\": [], \"optional_value\": {}, " - "\"absl_optional_value\": 1}"); + "\"absl_optional_value\": 1, \"unique_ptr_value\": true}"); EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument); EXPECT_EQ(test_struct.status().message(), "errors validating JSON: [" "field:absl_optional_value error:is not a string; " "field:optional_value error:is not a string; " + "field:unique_ptr_value error:is not a string; " "field:value error:is not a string]") << test_struct.status(); } @@ -468,6 +519,7 @@ TEST(JsonObjectLoader, JsonObjectFields) { Json::Object value; Json::Object optional_value; absl::optional absl_optional_value; + std::unique_ptr unique_ptr_value; static const JsonLoaderInterface* JsonLoader(const JsonArgs&) { static const auto* loader = @@ -476,6 +528,7 @@ TEST(JsonObjectLoader, JsonObjectFields) { .OptionalField("optional_value", &TestStruct::optional_value) .OptionalField("absl_optional_value", &TestStruct::absl_optional_value) + .OptionalField("unique_ptr_value", &TestStruct::unique_ptr_value) .Finish(); return loader; } @@ -495,21 +548,24 @@ TEST(JsonObjectLoader, JsonObjectFields) { // Optional fields present. test_struct = Parse( "{\"value\": {\"a\":1}, \"optional_value\": {\"b\":2}, " - "\"absl_optional_value\": {\"c\":3}}"); + "\"absl_optional_value\": {\"c\":3}, \"unique_ptr_value\": {\"d\":4}}"); ASSERT_TRUE(test_struct.ok()) << test_struct.status(); EXPECT_EQ(Json{test_struct->value}.Dump(), "{\"a\":1}"); EXPECT_EQ(Json{test_struct->optional_value}.Dump(), "{\"b\":2}"); ASSERT_TRUE(test_struct->absl_optional_value.has_value()); EXPECT_EQ(Json{*test_struct->absl_optional_value}.Dump(), "{\"c\":3}"); + ASSERT_NE(test_struct->unique_ptr_value, nullptr); + EXPECT_EQ(Json{*test_struct->unique_ptr_value}.Dump(), "{\"d\":4}"); // Wrong JSON type. test_struct = Parse( "{\"value\": [], \"optional_value\": true, " - "\"absl_optional_value\": 1}"); + "\"absl_optional_value\": 1, \"unique_ptr_value\": \"foo\"}"); EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument); EXPECT_EQ(test_struct.status().message(), "errors validating JSON: [" "field:absl_optional_value error:is not an object; " "field:optional_value error:is not an object; " + "field:unique_ptr_value error:is not an object; " "field:value error:is not an object]") << test_struct.status(); } @@ -523,6 +579,7 @@ TEST(JsonObjectLoader, MapFields) { std::map value; std::map optional_value; absl::optional> absl_optional_value; + std::unique_ptr> unique_ptr_value; static const JsonLoaderInterface* JsonLoader(const JsonArgs&) { static const auto* loader = @@ -531,6 +588,7 @@ TEST(JsonObjectLoader, MapFields) { .OptionalField("optional_value", &TestStruct::optional_value) .OptionalField("absl_optional_value", &TestStruct::absl_optional_value) + .OptionalField("unique_ptr_value", &TestStruct::unique_ptr_value) .Finish(); return loader; } @@ -551,7 +609,8 @@ TEST(JsonObjectLoader, MapFields) { // Optional fields present. test_struct = Parse( "{\"value\": {\"a\":1}, \"optional_value\": {\"b\":\"foo\"}, " - "\"absl_optional_value\": {\"c\":true}}"); + "\"absl_optional_value\": {\"c\":true}, " + "\"unique_ptr_value\": {\"d\":4}}"); ASSERT_TRUE(test_struct.ok()) << test_struct.status(); EXPECT_THAT(test_struct->value, ::testing::ElementsAre(::testing::Pair("a", 1))); @@ -560,15 +619,19 @@ TEST(JsonObjectLoader, MapFields) { ASSERT_TRUE(test_struct->absl_optional_value.has_value()); EXPECT_THAT(*test_struct->absl_optional_value, ::testing::ElementsAre(::testing::Pair("c", true))); + ASSERT_NE(test_struct->unique_ptr_value, nullptr); + EXPECT_THAT(*test_struct->unique_ptr_value, + ::testing::ElementsAre(::testing::Pair("d", 4))); // Wrong JSON type. test_struct = Parse( "{\"value\": [], \"optional_value\": true, " - "\"absl_optional_value\": 1}"); + "\"absl_optional_value\": 1, \"unique_ptr_value\": \"foo\"}"); EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument); EXPECT_EQ(test_struct.status().message(), "errors validating JSON: [" "field:absl_optional_value error:is not an object; " "field:optional_value error:is not an object; " + "field:unique_ptr_value error:is not an object; " "field:value error:is not an object]") << test_struct.status(); // Wrong JSON type for map value. @@ -593,6 +656,7 @@ TEST(JsonObjectLoader, VectorFields) { std::vector value; std::vector optional_value; absl::optional> absl_optional_value; + std::unique_ptr> unique_ptr_value; static const JsonLoaderInterface* JsonLoader(const JsonArgs&) { static const auto* loader = @@ -601,6 +665,7 @@ TEST(JsonObjectLoader, VectorFields) { .OptionalField("optional_value", &TestStruct::optional_value) .OptionalField("absl_optional_value", &TestStruct::absl_optional_value) + .OptionalField("unique_ptr_value", &TestStruct::unique_ptr_value) .Finish(); return loader; } @@ -620,7 +685,8 @@ TEST(JsonObjectLoader, VectorFields) { // Optional fields present. test_struct = Parse( "{\"value\": [4, 5, 6], \"optional_value\": [\"foo\", \"bar\"], " - "\"absl_optional_value\": [true, false, true]}"); + "\"absl_optional_value\": [true, false, true], " + "\"unique_ptr_value\": [1, 2, 3]}"); ASSERT_TRUE(test_struct.ok()) << test_struct.status(); EXPECT_THAT(test_struct->value, ::testing::ElementsAre(4, 5, 6)); EXPECT_THAT(test_struct->optional_value, @@ -628,15 +694,18 @@ TEST(JsonObjectLoader, VectorFields) { ASSERT_TRUE(test_struct->absl_optional_value.has_value()); EXPECT_THAT(*test_struct->absl_optional_value, ::testing::ElementsAre(true, false, true)); + ASSERT_NE(test_struct->unique_ptr_value, nullptr); + EXPECT_THAT(*test_struct->unique_ptr_value, ::testing::ElementsAre(1, 2, 3)); // Wrong JSON type. test_struct = Parse( "{\"value\": {}, \"optional_value\": true, " - "\"absl_optional_value\": 1}"); + "\"absl_optional_value\": 1, \"unique_ptr_value\": \"foo\"}"); EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument); EXPECT_EQ(test_struct.status().message(), "errors validating JSON: [" "field:absl_optional_value error:is not an array; " "field:optional_value error:is not an array; " + "field:unique_ptr_value error:is not an array; " "field:value error:is not an array]") << test_struct.status(); // Wrong JSON type for map value. @@ -674,6 +743,7 @@ TEST(JsonObjectLoader, NestedStructFields) { NestedStruct outer; NestedStruct optional_outer; absl::optional absl_optional_outer; + std::unique_ptr unique_ptr_outer; static const JsonLoaderInterface* JsonLoader(const JsonArgs&) { static const auto* loader = @@ -682,6 +752,7 @@ TEST(JsonObjectLoader, NestedStructFields) { .OptionalField("optional_outer", &TestStruct::optional_outer) .OptionalField("absl_optional_outer", &TestStruct::absl_optional_outer) + .OptionalField("unique_ptr_outer", &TestStruct::unique_ptr_outer) .Finish(); return loader; } @@ -708,22 +779,26 @@ TEST(JsonObjectLoader, NestedStructFields) { // Optional fields present. test_struct = Parse( "{\"outer\": {\"inner\":1}, \"optional_outer\": {\"inner\":2}, " - "\"absl_optional_outer\": {\"inner\":3}}"); + "\"absl_optional_outer\": {\"inner\":3}, " + "\"unique_ptr_outer\": {\"inner\":4}}"); ASSERT_TRUE(test_struct.ok()) << test_struct.status(); EXPECT_EQ(test_struct->outer.inner, 1); EXPECT_EQ(test_struct->optional_outer.inner, 2); ASSERT_TRUE(test_struct->absl_optional_outer.has_value()); EXPECT_EQ(test_struct->absl_optional_outer->inner, 3); + ASSERT_NE(test_struct->unique_ptr_outer, nullptr); + EXPECT_EQ(test_struct->unique_ptr_outer->inner, 4); // Wrong JSON type. test_struct = Parse( "{\"outer\": \"foo\", \"optional_outer\": true, " - "\"absl_optional_outer\": 1}"); + "\"absl_optional_outer\": 1, \"unique_ptr_outer\": \"foo\"}"); EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument); EXPECT_EQ(test_struct.status().message(), "errors validating JSON: [" "field:absl_optional_outer error:is not an object; " "field:optional_outer error:is not an object; " - "field:outer error:is not an object]") + "field:outer error:is not an object; " + "field:unique_ptr_outer error:is not an object]") << test_struct.status(); // Wrong JSON type for inner value. test_struct = Parse( @@ -774,6 +849,13 @@ TEST(JsonObjectLoader, BareBool) { EXPECT_TRUE(*parsed); } +TEST(JsonObjectLoader, BareUniquePtr) { + auto parsed = Parse>("3"); + ASSERT_TRUE(parsed.ok()) << parsed.status(); + ASSERT_NE(*parsed, nullptr); + EXPECT_EQ(**parsed, 3); +} + TEST(JsonObjectLoader, BareVector) { auto parsed = Parse>("[1, 2, 3]"); ASSERT_TRUE(parsed.ok()) << parsed.status(); diff --git a/test/core/message_size/BUILD b/test/core/message_size/BUILD new file mode 100644 index 00000000000..d188cc37898 --- /dev/null +++ b/test/core/message_size/BUILD @@ -0,0 +1,33 @@ +# Copyright 2017 gRPC authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//bazel:grpc_build_system.bzl", "grpc_cc_test", "grpc_package") + +grpc_package(name = "test/core/message_size") + +licenses(["notice"]) + +grpc_cc_test( + name = "message_size_service_config_test", + srcs = ["message_size_service_config_test.cc"], + external_deps = [ + "gtest", + ], + language = "C++", + deps = [ + "//:gpr", + "//:grpc", + "//test/core/util:grpc_test_util", + ], +) diff --git a/test/core/message_size/message_size_service_config_test.cc b/test/core/message_size/message_size_service_config_test.cc new file mode 100644 index 00000000000..4b669dfe5b8 --- /dev/null +++ b/test/core/message_size/message_size_service_config_test.cc @@ -0,0 +1,125 @@ +// +// 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. +// + +#include + +#include +#include + +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "gtest/gtest.h" + +#include +#include + +#include "src/core/ext/filters/message_size/message_size_filter.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/config/core_configuration.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/service_config/service_config.h" +#include "src/core/lib/service_config/service_config_impl.h" +#include "src/core/lib/service_config/service_config_parser.h" +#include "test/core/util/test_config.h" + +namespace grpc_core { +namespace testing { + +class MessageSizeParserTest : public ::testing::Test { + protected: + void SetUp() override { + parser_index_ = + CoreConfiguration::Get().service_config_parser().GetParserIndex( + "message_size"); + } + + size_t parser_index_; +}; + +TEST_F(MessageSizeParserTest, Valid) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"maxRequestMessageBytes\": 1024,\n" + " \"maxResponseMessageBytes\": 1024\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + ASSERT_TRUE(service_config.ok()) << service_config.status(); + const auto* vector_ptr = + (*service_config) + ->GetMethodParsedConfigVector( + grpc_slice_from_static_string("/TestServ/TestMethod")); + ASSERT_NE(vector_ptr, nullptr); + auto parsed_config = static_cast( + ((*vector_ptr)[parser_index_]).get()); + ASSERT_NE(parsed_config, nullptr); + EXPECT_EQ(parsed_config->max_send_size(), 1024U); + EXPECT_EQ(parsed_config->max_recv_size(), 1024U); +} + +TEST_F(MessageSizeParserTest, InvalidMaxRequestMessageBytes) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"maxRequestMessageBytes\": -1024\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].maxRequestMessageBytes " + "error:failed to parse non-negative number]") + << service_config.status(); +} + +TEST_F(MessageSizeParserTest, InvalidMaxResponseMessageBytes) { + const char* test_json = + "{\n" + " \"methodConfig\": [ {\n" + " \"name\": [\n" + " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n" + " ],\n" + " \"maxResponseMessageBytes\": {}\n" + " } ]\n" + "}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].maxResponseMessageBytes " + "error:is not a number]") + << service_config.status(); +} + +} // namespace testing +} // namespace grpc_core + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + grpc::testing::TestEnvironment env(&argc, argv); + grpc_init(); + int ret = RUN_ALL_TESTS(); + grpc_shutdown(); + return ret; +} diff --git a/test/core/service_config/BUILD b/test/core/service_config/BUILD new file mode 100644 index 00000000000..34070410740 --- /dev/null +++ b/test/core/service_config/BUILD @@ -0,0 +1,33 @@ +# Copyright 2017 gRPC authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//bazel:grpc_build_system.bzl", "grpc_cc_test", "grpc_package") + +grpc_package(name = "test/core/service_config") + +licenses(["notice"]) + +grpc_cc_test( + name = "service_config_test", + srcs = ["service_config_test.cc"], + external_deps = [ + "gtest", + ], + language = "C++", + deps = [ + "//:gpr", + "//:grpc", + "//test/core/util:grpc_test_util", + ], +) diff --git a/test/core/service_config/service_config_test.cc b/test/core/service_config/service_config_test.cc new file mode 100644 index 00000000000..9baf42f6ab8 --- /dev/null +++ b/test/core/service_config/service_config_test.cc @@ -0,0 +1,468 @@ +// +// 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. +// + +#include "src/core/lib/service_config/service_config.h" + +#include + +#include +#include +#include + +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" +#include "absl/types/optional.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/config/core_configuration.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/gprpp/validation_errors.h" +#include "src/core/lib/json/json.h" +#include "src/core/lib/json/json_args.h" +#include "src/core/lib/json/json_object_loader.h" +#include "src/core/lib/service_config/service_config_impl.h" +#include "src/core/lib/service_config/service_config_parser.h" +#include "test/core/util/test_config.h" + +namespace grpc_core { +namespace testing { + +// Set this channel arg to true to disable parsing. +#define GRPC_ARG_DISABLE_PARSING "disable_parsing" + +class TestParsedConfig1 : public ServiceConfigParser::ParsedConfig { + public: + uint32_t value() const { return value_; } + + static const JsonLoaderInterface* JsonLoader(const JsonArgs&) { + static const auto* loader = + JsonObjectLoader() + .OptionalField("global_param", &TestParsedConfig1::value_) + .Finish(); + return loader; + } + + private: + uint32_t value_; +}; + +class TestParser1 : public ServiceConfigParser::Parser { + public: + absl::string_view name() const override { return "test_parser_1"; } + + std::unique_ptr ParseGlobalParams( + const ChannelArgs& args, const Json& json, + ValidationErrors* errors) override { + if (args.GetBool(GRPC_ARG_DISABLE_PARSING).value_or(false)) { + return nullptr; + } + return LoadFromJson>(json, JsonArgs(), + errors); + } +}; + +class TestParsedConfig2 : public ServiceConfigParser::ParsedConfig { + public: + uint32_t value() const { return value_; } + + static const JsonLoaderInterface* JsonLoader(const JsonArgs&) { + static const auto* loader = + JsonObjectLoader() + .OptionalField("method_param", &TestParsedConfig2::value_) + .Finish(); + return loader; + } + + private: + uint32_t value_; +}; + +class TestParser2 : public ServiceConfigParser::Parser { + public: + absl::string_view name() const override { return "test_parser_2"; } + + std::unique_ptr ParsePerMethodParams( + const ChannelArgs& args, const Json& json, + ValidationErrors* errors) override { + if (args.GetBool(GRPC_ARG_DISABLE_PARSING).value_or(false)) { + return nullptr; + } + return LoadFromJson>(json, JsonArgs(), + errors); + } +}; + +class ServiceConfigTest : public ::testing::Test { + protected: + void SetUp() override { + builder_ = std::make_unique( + [](CoreConfiguration::Builder* builder) { + builder->service_config_parser()->RegisterParser( + std::make_unique()); + builder->service_config_parser()->RegisterParser( + std::make_unique()); + }); + EXPECT_EQ(CoreConfiguration::Get().service_config_parser().GetParserIndex( + "test_parser_1"), + 0); + EXPECT_EQ(CoreConfiguration::Get().service_config_parser().GetParserIndex( + "test_parser_2"), + 1); + } + + private: + std::unique_ptr builder_; +}; + +TEST_F(ServiceConfigTest, JsonParseError) { + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), ""); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_THAT(std::string(service_config.status().message()), + ::testing::StartsWith("JSON parsing failed")) + << service_config.status(); +} + +TEST_F(ServiceConfigTest, EmptyConfig) { + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), "{}"); + ASSERT_TRUE(service_config.ok()) << service_config.status(); + EXPECT_EQ((*service_config)->json_string(), "{}"); +} + +TEST_F(ServiceConfigTest, SkipMethodConfigWithNoNameOrEmptyName) { + const char* test_json = + "{\"methodConfig\": [" + " {\"method_param\":1}," + " {\"name\":[], \"method_param\":1}," + " {\"name\":[{\"service\":\"TestServ\"}], \"method_param\":2}" + "]}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + ASSERT_TRUE(service_config.ok()) << service_config.status(); + auto vector_ptr = + (*service_config) + ->GetMethodParsedConfigVector( + grpc_slice_from_static_string("/TestServ/TestMethod")); + ASSERT_EQ(vector_ptr->size(), 2UL); + auto parsed_config = ((*vector_ptr)[1]).get(); + EXPECT_EQ(static_cast(parsed_config)->value(), 2); +} + +TEST_F(ServiceConfigTest, ErrorDuplicateMethodConfigNames) { + const char* test_json = + "{\"methodConfig\": [" + " {\"name\":[{\"service\":\"TestServ\"}]}," + " {\"name\":[{\"service\":\"TestServ\"}]}" + "]}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[1].name[0] " + "error:multiple method configs for path /TestServ/]") + << service_config.status(); +} + +TEST_F(ServiceConfigTest, ErrorDuplicateMethodConfigNamesWithNullMethod) { + const char* test_json = + "{\"methodConfig\": [" + " {\"name\":[{\"service\":\"TestServ\",\"method\":null}]}," + " {\"name\":[{\"service\":\"TestServ\"}]}" + "]}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[1].name[0] " + "error:multiple method configs for path /TestServ/]") + << service_config.status(); +} + +TEST_F(ServiceConfigTest, ErrorDuplicateMethodConfigNamesWithEmptyMethod) { + const char* test_json = + "{\"methodConfig\": [" + " {\"name\":[{\"service\":\"TestServ\",\"method\":\"\"}]}," + " {\"name\":[{\"service\":\"TestServ\"}]}" + "]}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[1].name[0] " + "error:multiple method configs for path /TestServ/]") + << service_config.status(); +} + +TEST_F(ServiceConfigTest, ErrorDuplicateDefaultMethodConfigs) { + const char* test_json = + "{\"methodConfig\": [" + " {\"name\":[{}]}," + " {\"name\":[{}]}" + "]}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[1].name[0] " + "error:duplicate default method config]") + << service_config.status(); +} + +TEST_F(ServiceConfigTest, ErrorDuplicateDefaultMethodConfigsWithNullService) { + const char* test_json = + "{\"methodConfig\": [" + " {\"name\":[{\"service\":null}]}," + " {\"name\":[{}]}" + "]}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[1].name[0] " + "error:duplicate default method config]") + << service_config.status(); +} + +TEST_F(ServiceConfigTest, ErrorDuplicateDefaultMethodConfigsWithEmptyService) { + const char* test_json = + "{\"methodConfig\": [" + " {\"name\":[{\"service\":\"\"}]}," + " {\"name\":[{}]}" + "]}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[1].name[0] " + "error:duplicate default method config]") + << service_config.status(); +} + +TEST_F(ServiceConfigTest, ValidMethodConfig) { + const char* test_json = + "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}]}]}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + ASSERT_TRUE(service_config.ok()) << service_config.status(); +} + +TEST_F(ServiceConfigTest, Parser1BasicTest1) { + const char* test_json = "{\"global_param\":5}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + ASSERT_TRUE(service_config.ok()) << service_config.status(); + EXPECT_EQ((static_cast( + (*service_config)->GetGlobalParsedConfig(0))) + ->value(), + 5); + EXPECT_EQ((*service_config) + ->GetMethodParsedConfigVector( + grpc_slice_from_static_string("/TestServ/TestMethod")), + nullptr); +} + +TEST_F(ServiceConfigTest, Parser1BasicTest2) { + const char* test_json = "{\"global_param\":1000}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + ASSERT_TRUE(service_config.ok()) << service_config.status(); + EXPECT_EQ((static_cast( + (*service_config)->GetGlobalParsedConfig(0))) + ->value(), + 1000); +} + +TEST_F(ServiceConfigTest, Parser1DisabledViaChannelArg) { + const ChannelArgs args = ChannelArgs().Set(GRPC_ARG_DISABLE_PARSING, 1); + const char* test_json = "{\"global_param\":5}"; + auto service_config = ServiceConfigImpl::Create(args, test_json); + ASSERT_TRUE(service_config.ok()) << service_config.status(); + EXPECT_EQ((*service_config)->GetGlobalParsedConfig(0), nullptr); +} + +TEST_F(ServiceConfigTest, Parser1ErrorInvalidType) { + const char* test_json = "{\"global_param\":[]}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:global_param error:is not a number]") + << service_config.status(); +} + +TEST_F(ServiceConfigTest, Parser1ErrorInvalidValue) { + const char* test_json = "{\"global_param\":-5}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:global_param error:failed to parse non-negative number]") + << service_config.status(); +} + +TEST_F(ServiceConfigTest, Parser2BasicTest) { + const char* test_json = + "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}], " + "\"method_param\":5}]}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + ASSERT_TRUE(service_config.ok()) << service_config.status(); + const auto* vector_ptr = + (*service_config) + ->GetMethodParsedConfigVector( + grpc_slice_from_static_string("/TestServ/TestMethod")); + ASSERT_NE(vector_ptr, nullptr); + auto parsed_config = ((*vector_ptr)[1]).get(); + EXPECT_EQ(static_cast(parsed_config)->value(), 5); +} + +TEST_F(ServiceConfigTest, Parser2DisabledViaChannelArg) { + const ChannelArgs args = ChannelArgs().Set(GRPC_ARG_DISABLE_PARSING, 1); + const char* test_json = + "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}], " + "\"method_param\":5}]}"; + auto service_config = ServiceConfigImpl::Create(args, test_json); + ASSERT_TRUE(service_config.ok()) << service_config.status(); + const auto* vector_ptr = + (*service_config) + ->GetMethodParsedConfigVector( + grpc_slice_from_static_string("/TestServ/TestMethod")); + ASSERT_NE(vector_ptr, nullptr); + auto parsed_config = ((*vector_ptr)[1]).get(); + EXPECT_EQ(parsed_config, nullptr); +} + +TEST_F(ServiceConfigTest, Parser2ErrorInvalidType) { + const char* test_json = + "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}], " + "\"method_param\":[]}]}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].method_param error:is not a number]") + << service_config.status(); +} + +TEST_F(ServiceConfigTest, Parser2ErrorInvalidValue) { + const char* test_json = + "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}], " + "\"method_param\":-5}]}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:methodConfig[0].method_param " + "error:failed to parse non-negative number]") + << service_config.status(); +} + +TEST(ServiceConfigParserTest, DoubleRegistration) { + CoreConfiguration::Reset(); + ASSERT_DEATH_IF_SUPPORTED( + CoreConfiguration::WithSubstituteBuilder builder( + [](CoreConfiguration::Builder* builder) { + builder->service_config_parser()->RegisterParser( + std::make_unique()); + builder->service_config_parser()->RegisterParser( + std::make_unique()); + }), + "test_parser_1.*already registered"); +} + +// This parser always adds errors +class ErrorParser : public ServiceConfigParser::Parser { + public: + explicit ErrorParser(absl::string_view name) : name_(name) {} + + absl::string_view name() const override { return name_; } + + std::unique_ptr ParsePerMethodParams( + const ChannelArgs& /*arg*/, const Json& /*json*/, + ValidationErrors* errors) override { + ValidationErrors::ScopedField field(errors, absl::StrCat(".", name_)); + errors->AddError("method error"); + return nullptr; + } + + std::unique_ptr ParseGlobalParams( + const ChannelArgs& /*arg*/, const Json& /*json*/, + ValidationErrors* errors) override { + ValidationErrors::ScopedField field(errors, absl::StrCat(".", name_)); + errors->AddError("global error"); + return nullptr; + } + + private: + absl::string_view name_; +}; + +// Test parsing with ErrorParsers which always add errors +class ErroredParsersScopingTest : public ::testing::Test { + protected: + void SetUp() override { + builder_ = std::make_unique( + [](CoreConfiguration::Builder* builder) { + builder->service_config_parser()->RegisterParser( + std::make_unique("ep1")); + builder->service_config_parser()->RegisterParser( + std::make_unique("ep2")); + }); + EXPECT_EQ( + CoreConfiguration::Get().service_config_parser().GetParserIndex("ep1"), + 0); + EXPECT_EQ( + CoreConfiguration::Get().service_config_parser().GetParserIndex("ep2"), + 1); + } + + private: + std::unique_ptr builder_; +}; + +TEST_F(ErroredParsersScopingTest, GlobalParams) { + const char* test_json = "{}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:ep1 error:global error; field:ep2 error:global error]") + << service_config.status(); +} + +TEST_F(ErroredParsersScopingTest, MethodParams) { + const char* test_json = "{\"methodConfig\": [{}]}"; + auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json); + EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(service_config.status().message(), + "errors validating service config: [" + "field:ep1 error:global error; " + "field:ep2 error:global error; " + "field:methodConfig[0].ep1 error:method error; " + "field:methodConfig[0].ep2 error:method error]") + << service_config.status(); +} + +} // namespace testing +} // namespace grpc_core + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + grpc::testing::TestEnvironment env(&argc, argv); + grpc_init(); + int ret = RUN_ALL_TESTS(); + grpc_shutdown(); + return ret; +} diff --git a/test/core/xds/xds_http_filters_test.cc b/test/core/xds/xds_http_filters_test.cc index bc3addce010..5dd14fad35f 100644 --- a/test/core/xds/xds_http_filters_test.cc +++ b/test/core/xds/xds_http_filters_test.cc @@ -37,7 +37,7 @@ #include #include "src/core/ext/filters/fault_injection/fault_injection_filter.h" -#include "src/core/ext/filters/fault_injection/service_config_parser.h" +#include "src/core/ext/filters/fault_injection/fault_injection_service_config_parser.h" #include "src/core/ext/filters/rbac/rbac_filter.h" #include "src/core/ext/filters/rbac/rbac_service_config_parser.h" #include "src/proto/grpc/testing/xds/v3/address.pb.h" @@ -693,7 +693,7 @@ TEST_P(XdsRbacFilterConfigTest, AllPermissionTypes) { "\"ignoreCase\":false,\"safeRegex\":{\"regex\":\"regex_match\"}}}}," // destination IP match with prefix len "{\"destinationIp\":{" - "\"addressPrefix\":\"127.0.0\",\"prefixLen\":{\"value\":24}}}," + "\"addressPrefix\":\"127.0.0\",\"prefixLen\":24}}," // destination IP match "{\"destinationIp\":{\"addressPrefix\":\"10.0.0\"}}," // destination port match diff --git a/test/cpp/naming/resolver_component_tests_runner.py b/test/cpp/naming/resolver_component_tests_runner.py index 47cca33839c..e9f6768155a 100755 --- a/test/cpp/naming/resolver_component_tests_runner.py +++ b/test/cpp/naming/resolver_component_tests_runner.py @@ -577,7 +577,7 @@ current_test_subprocess = subprocess.Popen([ '--do_ordered_address_comparison', 'False', '--expected_addrs', '1.2.3.4:443,False', '--expected_chosen_service_config', '', - '--expected_service_config_error', 'field:waitForReady error:Type should be true/false', + '--expected_service_config_error', 'field:methodConfig[0].waitForReady error:is not a boolean', '--expected_lb_policy', '', '--enable_srv_queries', 'True', '--enable_txt_queries', 'True', @@ -613,7 +613,7 @@ current_test_subprocess = subprocess.Popen([ '--do_ordered_address_comparison', 'False', '--expected_addrs', '1.2.3.4:443,False', '--expected_chosen_service_config', '', - '--expected_service_config_error', 'field:loadBalancingPolicy error:type should be string', + '--expected_service_config_error', 'field:loadBalancingPolicy error:is not a string', '--expected_lb_policy', '', '--enable_srv_queries', 'True', '--enable_txt_queries', 'True', diff --git a/test/cpp/naming/resolver_test_record_groups.yaml b/test/cpp/naming/resolver_test_record_groups.yaml index 8121b67faaa..7ff3418e4a7 100644 --- a/test/cpp/naming/resolver_test_record_groups.yaml +++ b/test/cpp/naming/resolver_test_record_groups.yaml @@ -435,7 +435,7 @@ resolver_component_tests: - {address: '1.2.3.4:443', is_balancer: false} do_ordered_address_comparison: false expected_chosen_service_config: null - expected_service_config_error: 'field:waitForReady error:Type should be true/false' + expected_service_config_error: 'field:methodConfig[0].waitForReady error:is not a boolean' expected_lb_policy: null enable_srv_queries: true enable_txt_queries: true @@ -465,7 +465,7 @@ resolver_component_tests: - {address: '1.2.3.4:443', is_balancer: false} do_ordered_address_comparison: false expected_chosen_service_config: null - expected_service_config_error: 'field:loadBalancingPolicy error:type should be string' + expected_service_config_error: 'field:loadBalancingPolicy error:is not a string' expected_lb_policy: null enable_srv_queries: true enable_txt_queries: true diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal index 11117bf5316..dc22e0de296 100644 --- a/tools/doxygen/Doxyfile.c++.internal +++ b/tools/doxygen/Doxyfile.c++.internal @@ -1074,6 +1074,8 @@ src/core/ext/filters/client_channel/client_channel_channelz.h \ src/core/ext/filters/client_channel/client_channel_factory.cc \ src/core/ext/filters/client_channel/client_channel_factory.h \ src/core/ext/filters/client_channel/client_channel_plugin.cc \ +src/core/ext/filters/client_channel/client_channel_service_config.cc \ +src/core/ext/filters/client_channel/client_channel_service_config.h \ src/core/ext/filters/client_channel/config_selector.cc \ src/core/ext/filters/client_channel/config_selector.h \ src/core/ext/filters/client_channel/connector.h \ @@ -1142,8 +1144,6 @@ src/core/ext/filters/client_channel/resolver/polling_resolver.h \ src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc \ src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc \ src/core/ext/filters/client_channel/resolver/xds/xds_resolver.h \ -src/core/ext/filters/client_channel/resolver_result_parsing.cc \ -src/core/ext/filters/client_channel/resolver_result_parsing.h \ src/core/ext/filters/client_channel/retry_filter.cc \ src/core/ext/filters/client_channel/retry_filter.h \ src/core/ext/filters/client_channel/retry_service_config.cc \ @@ -1162,8 +1162,8 @@ src/core/ext/filters/deadline/deadline_filter.cc \ src/core/ext/filters/deadline/deadline_filter.h \ src/core/ext/filters/fault_injection/fault_injection_filter.cc \ src/core/ext/filters/fault_injection/fault_injection_filter.h \ -src/core/ext/filters/fault_injection/service_config_parser.cc \ -src/core/ext/filters/fault_injection/service_config_parser.h \ +src/core/ext/filters/fault_injection/fault_injection_service_config_parser.cc \ +src/core/ext/filters/fault_injection/fault_injection_service_config_parser.h \ src/core/ext/filters/http/client/http_client_filter.cc \ src/core/ext/filters/http/client/http_client_filter.h \ src/core/ext/filters/http/client_authority_filter.cc \ @@ -2249,6 +2249,7 @@ src/core/lib/iomgr/wakeup_fd_posix.cc \ src/core/lib/iomgr/wakeup_fd_posix.h \ src/core/lib/json/json.h \ src/core/lib/json/json_args.h \ +src/core/lib/json/json_channel_args.h \ src/core/lib/json/json_object_loader.cc \ src/core/lib/json/json_object_loader.h \ src/core/lib/json/json_reader.cc \ diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index bc0ebd3b1f0..29ec195e306 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -890,6 +890,8 @@ src/core/ext/filters/client_channel/client_channel_channelz.h \ src/core/ext/filters/client_channel/client_channel_factory.cc \ src/core/ext/filters/client_channel/client_channel_factory.h \ src/core/ext/filters/client_channel/client_channel_plugin.cc \ +src/core/ext/filters/client_channel/client_channel_service_config.cc \ +src/core/ext/filters/client_channel/client_channel_service_config.h \ src/core/ext/filters/client_channel/config_selector.cc \ src/core/ext/filters/client_channel/config_selector.h \ src/core/ext/filters/client_channel/connector.h \ @@ -962,8 +964,6 @@ src/core/ext/filters/client_channel/resolver/sockaddr/README.md \ src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc \ src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc \ src/core/ext/filters/client_channel/resolver/xds/xds_resolver.h \ -src/core/ext/filters/client_channel/resolver_result_parsing.cc \ -src/core/ext/filters/client_channel/resolver_result_parsing.h \ src/core/ext/filters/client_channel/retry_filter.cc \ src/core/ext/filters/client_channel/retry_filter.h \ src/core/ext/filters/client_channel/retry_service_config.cc \ @@ -982,8 +982,8 @@ src/core/ext/filters/deadline/deadline_filter.cc \ src/core/ext/filters/deadline/deadline_filter.h \ src/core/ext/filters/fault_injection/fault_injection_filter.cc \ src/core/ext/filters/fault_injection/fault_injection_filter.h \ -src/core/ext/filters/fault_injection/service_config_parser.cc \ -src/core/ext/filters/fault_injection/service_config_parser.h \ +src/core/ext/filters/fault_injection/fault_injection_service_config_parser.cc \ +src/core/ext/filters/fault_injection/fault_injection_service_config_parser.h \ src/core/ext/filters/http/client/http_client_filter.cc \ src/core/ext/filters/http/client/http_client_filter.h \ src/core/ext/filters/http/client_authority_filter.cc \ @@ -2040,6 +2040,7 @@ src/core/lib/iomgr/wakeup_fd_posix.cc \ src/core/lib/iomgr/wakeup_fd_posix.h \ src/core/lib/json/json.h \ src/core/lib/json/json_args.h \ +src/core/lib/json/json_channel_args.h \ src/core/lib/json/json_object_loader.cc \ src/core/lib/json/json_object_loader.h \ src/core/lib/json/json_reader.cc \ diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index 60193ff3cf4..b3bff15b8ae 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -1943,6 +1943,30 @@ ], "uses_polling": true }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": true, + "language": "c++", + "name": "client_channel_service_config_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": true + }, { "args": [], "benchmark": false, @@ -4645,6 +4669,30 @@ ], "uses_polling": false }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": true, + "language": "c++", + "name": "message_size_service_config_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": true + }, { "args": [], "benchmark": false, @@ -5737,6 +5785,30 @@ ], "uses_polling": false }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": true, + "language": "c++", + "name": "retry_service_config_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": true + }, { "args": [], "benchmark": false,