diff --git a/BUILD b/BUILD index 277f21c512a..788e5bb34c2 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,12 +2516,9 @@ 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", ], ) @@ -2592,7 +2589,6 @@ 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", @@ -2601,6 +2597,7 @@ 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", @@ -2615,7 +2612,6 @@ 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", @@ -2625,6 +2621,7 @@ 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", @@ -2683,9 +2680,7 @@ grpc_cc_library( "//src/core:init_internally", "//src/core:iomgr_fwd", "//src/core:json", - "//src/core:json_args", - "//src/core:json_channel_args", - "//src/core:json_object_loader", + "//src/core:json_util", "//src/core:lb_policy", "//src/core:lb_policy_registry", "//src/core:memory_quota", @@ -2706,7 +2701,6 @@ 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 73a34423991..b058bb9a3b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -869,7 +869,6 @@ 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,7 +1032,6 @@ 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) @@ -1099,7 +1097,6 @@ 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) @@ -1658,7 +1655,6 @@ 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 @@ -1700,6 +1696,7 @@ 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 @@ -1709,7 +1706,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/fault_injection_service_config_parser.cc + src/core/ext/filters/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 @@ -2611,7 +2608,6 @@ 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 @@ -2645,6 +2641,7 @@ 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 @@ -2654,7 +2651,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/fault_injection_service_config_parser.cc + src/core/ext/filters/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 @@ -2843,6 +2840,7 @@ 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 @@ -7802,41 +7800,6 @@ 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) @@ -13619,41 +13582,6 @@ 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) @@ -15777,41 +15705,6 @@ 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) @@ -16816,7 +16709,7 @@ endif() if(gRPC_BUILD_TESTS) add_executable(service_config_test - test/core/service_config/service_config_test.cc + test/core/client_channel/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 86ed1146edc..31eedc9e018 100644 --- a/Makefile +++ b/Makefile @@ -968,7 +968,6 @@ 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 \ @@ -1010,6 +1009,7 @@ 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/fault_injection_service_config_parser.cc \ + src/core/ext/filters/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,7 +1784,6 @@ 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 \ @@ -1818,6 +1817,7 @@ 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/fault_injection_service_config_parser.cc \ + src/core/ext/filters/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,6 +2016,7 @@ 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 \ @@ -3184,7 +3185,6 @@ 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 29da9210ebf..54e2fb63239 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -329,7 +329,6 @@ 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 @@ -357,6 +356,7 @@ 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/fault_injection_service_config_parser.h + - src/core/ext/filters/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,7 +869,6 @@ 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 @@ -1051,7 +1050,6 @@ 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 @@ -1093,6 +1091,7 @@ 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 @@ -1102,7 +1101,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/fault_injection_service_config_parser.cc + - src/core/ext/filters/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 @@ -1889,7 +1888,6 @@ 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 @@ -1914,6 +1912,7 @@ 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 @@ -1923,7 +1922,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/fault_injection_service_config_parser.h + - src/core/ext/filters/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 @@ -2128,8 +2127,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 @@ -2257,7 +2256,6 @@ 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 @@ -2291,6 +2289,7 @@ 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 @@ -2300,7 +2299,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/fault_injection_service_config_parser.cc + - src/core/ext/filters/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 @@ -2489,6 +2488,7 @@ 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 @@ -5170,15 +5170,6 @@ 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 @@ -8034,15 +8025,6 @@ 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 @@ -9066,15 +9048,6 @@ 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 @@ -9497,7 +9470,7 @@ targets: language: c++ headers: [] src: - - test/core/service_config/service_config_test.cc + - test/core/client_channel/service_config_test.cc deps: - grpc_test_util - name: settings_timeout_test diff --git a/config.m4 b/config.m4 index 5618e9a060b..d95d861b2fc 100644 --- a/config.m4 +++ b/config.m4 @@ -50,7 +50,6 @@ 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 \ @@ -92,6 +91,7 @@ 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/fault_injection_service_config_parser.cc \ + src/core/ext/filters/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 e2bff4c3171..a9e5bfc8790 100644 --- a/config.w32 +++ b/config.w32 @@ -16,7 +16,6 @@ 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 " + @@ -58,6 +57,7 @@ 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\\fault_injection_service_config_parser.cc " + + "src\\core\\ext\\filters\\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 66f2dc13a7a..25086448f0e 100644 --- a/gRPC-C++.podspec +++ b/gRPC-C++.podspec @@ -231,7 +231,6 @@ 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', @@ -259,6 +258,7 @@ 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/fault_injection_service_config_parser.h', + 'src/core/ext/filters/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,7 +832,6 @@ 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', @@ -1128,7 +1127,6 @@ 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', @@ -1156,6 +1154,7 @@ 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', @@ -1165,7 +1164,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/fault_injection_service_config_parser.h', + 'src/core/ext/filters/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', @@ -1711,7 +1710,6 @@ 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 598a3fcf6c6..bac53ed85a8 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -216,8 +216,6 @@ 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', @@ -286,6 +284,8 @@ 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/fault_injection_service_config_parser.cc', - 'src/core/ext/filters/fault_injection/fault_injection_service_config_parser.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/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,7 +1353,6 @@ 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', @@ -1786,7 +1785,6 @@ 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', @@ -1814,6 +1812,7 @@ 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', @@ -1823,7 +1822,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/fault_injection_service_config_parser.h', + 'src/core/ext/filters/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', @@ -2349,7 +2348,6 @@ 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 5f1d8c0b7d9..b32d69a424c 100644 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -127,8 +127,6 @@ 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 ) @@ -197,6 +195,8 @@ 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/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/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/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,7 +1264,6 @@ 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 ddf64da5784..beb5bf85d39 100644 --- a/grpc.gyp +++ b/grpc.gyp @@ -382,7 +382,6 @@ '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', @@ -424,6 +423,7 @@ '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/fault_injection_service_config_parser.cc', + 'src/core/ext/filters/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,7 +1145,6 @@ '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', @@ -1179,6 +1178,7 @@ '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/fault_injection_service_config_parser.cc', + 'src/core/ext/filters/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,6 +1377,7 @@ '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 1dc8ab46392..557758a6da6 100644 --- a/package.xml +++ b/package.xml @@ -109,8 +109,6 @@ - - @@ -179,6 +177,8 @@ + + @@ -197,8 +197,8 @@ - - + + @@ -1246,7 +1246,6 @@ - diff --git a/src/core/BUILD b/src/core/BUILD index 737d5b143fd..473978ea678 100644 --- a/src/core/BUILD +++ b/src/core/BUILD @@ -2070,12 +2070,15 @@ grpc_cc_library( hdrs = [ "lib/service_config/service_config_parser.h", ], - external_deps = ["absl/strings"], + external_deps = [ + "absl/status", + "absl/status:statusor", + "absl/strings", + ], language = "c++", deps = [ "channel_args", "json", - "validation_errors", "//:gpr", ], ) @@ -3065,6 +3068,7 @@ grpc_cc_library( ], external_deps = [ "absl/status", + "absl/status:statusor", "absl/strings", "absl/strings:str_format", "absl/types:optional", @@ -3078,12 +3082,9 @@ 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", @@ -3097,11 +3098,11 @@ grpc_cc_library( name = "grpc_fault_injection_filter", srcs = [ "ext/filters/fault_injection/fault_injection_filter.cc", - "ext/filters/fault_injection/fault_injection_service_config_parser.cc", + "ext/filters/fault_injection/service_config_parser.cc", ], hdrs = [ "ext/filters/fault_injection/fault_injection_filter.h", - "ext/filters/fault_injection/fault_injection_service_config_parser.h", + "ext/filters/fault_injection/service_config_parser.h", ], external_deps = [ "absl/base:core_headers", @@ -3118,13 +3119,12 @@ grpc_cc_library( "context", "grpc_service_config", "json", - "json_args", - "json_object_loader", + "json_util", "service_config_parser", "sleep", + "status_helper", "time", "try_seq", - "validation_errors", "//:config", "//:gpr", "//:grpc_base", @@ -3147,6 +3147,7 @@ grpc_cc_library( "absl/status", "absl/status:statusor", "absl/strings", + "absl/strings:str_format", "absl/types:optional", ], language = "c++", @@ -3159,12 +3160,10 @@ grpc_cc_library( "grpc_rbac_engine", "grpc_service_config", "json", - "json_args", - "json_object_loader", + "json_util", "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 6235de48dc2..a3c99194c44 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 f19714af184..bdb535ad5c0 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/client_channel_service_config.h" +#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/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 deleted file mode 100644 index d37376afd9e..00000000000 --- a/src/core/ext/filters/client_channel/client_channel_service_config.cc +++ /dev/null @@ -1,153 +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/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/lb_policy/rls/rls.cc b/src/core/ext/filters/client_channel/lb_policy/rls/rls.cc index 98976c9dcf5..28e7e59cddd 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,8 +2426,13 @@ void RlsLbConfig::JsonPostLoad(const Json& json, const JsonArgs&, if (it != json.object_value().end()) { ValidationErrors::ScopedField field(errors, ".routeLookupChannelServiceConfig"); - // Don't need to save the result here, just need the errors (if any). - ServiceConfigImpl::Create(ChannelArgs(), it->second, errors); + 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)); + } } // 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 new file mode 100644 index 00000000000..a0a9f939972 --- /dev/null +++ b/src/core/ext/filters/client_channel/resolver_result_parsing.cc @@ -0,0 +1,186 @@ +// +// 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/client_channel_service_config.h b/src/core/ext/filters/client_channel/resolver_result_parsing.h similarity index 64% rename from src/core/ext/filters/client_channel/client_channel_service_config.h rename to src/core/ext/filters/client_channel/resolver_result_parsing.h index 7f9fa7e7fb6..c87b8af615c 100644 --- a/src/core/ext/filters/client_channel/client_channel_service_config.h +++ b/src/core/ext/filters/client_channel/resolver_result_parsing.h @@ -14,8 +14,8 @@ // limitations under the License. // -#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_SERVICE_CONFIG_H -#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_SERVICE_CONFIG_H +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_RESULT_PARSING_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_RESULT_PARSING_H #include @@ -23,7 +23,9 @@ #include #include +#include +#include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" @@ -31,10 +33,7 @@ #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" @@ -44,6 +43,14 @@ 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_; } @@ -53,34 +60,26 @@ class ClientChannelGlobalParsedConfig } const absl::optional& health_check_service_name() const { - return health_check_config_.service_name; + return health_check_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_; - HealthCheckConfig health_check_config_; + absl::optional health_check_service_name_; }; 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_; @@ -90,13 +89,11 @@ class ClientChannelServiceConfigParser : public ServiceConfigParser::Parser { public: absl::string_view name() const override { return parser_name(); } - std::unique_ptr ParseGlobalParams( - const ChannelArgs& /*args*/, const Json& json, - ValidationErrors* errors) override; + absl::StatusOr> + ParseGlobalParams(const ChannelArgs& /*args*/, const Json& json) override; - std::unique_ptr ParsePerMethodParams( - const ChannelArgs& /*args*/, const Json& json, - ValidationErrors* errors) override; + absl::StatusOr> + ParsePerMethodParams(const ChannelArgs& /*args*/, const Json& json) override; static size_t ParserIndex(); static void Register(CoreConfiguration::Builder* builder); @@ -108,4 +105,4 @@ class ClientChannelServiceConfigParser : public ServiceConfigParser::Parser { } // namespace internal } // namespace grpc_core -#endif // GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_SERVICE_CONFIG_H +#endif // GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_RESULT_PARSING_H 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 6df853a4c67..44f877a6c3b 100644 --- a/src/core/ext/filters/client_channel/retry_service_config.cc +++ b/src/core/ext/filters/client_channel/retry_service_config.cc @@ -18,13 +18,16 @@ #include "src/core/ext/filters/client_channel/retry_service_config.h" -#include +#include +#include + +#include #include #include #include #include -#include "absl/strings/numbers.h" +#include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "absl/types/optional.h" @@ -35,7 +38,10 @@ #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/json/json_channel_args.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" // As per the retry design, we do not allow more than 5 retry attempts. #define MAX_MAX_RETRY_ATTEMPTS 5 @@ -43,240 +49,271 @@ namespace grpc_core { namespace internal { -// -// RetryGlobalConfig -// +size_t RetryServiceConfigParser::ParserIndex() { + return CoreConfiguration::Get().service_config_parser().GetParserIndex( + parser_name()); +} -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; +void RetryServiceConfigParser::Register(CoreConfiguration::Builder* builder) { + builder->service_config_parser()->RegisterParser( + std::make_unique()); } -void RetryGlobalConfig::JsonPostLoad(const Json& json, const JsonArgs& args, - ValidationErrors* errors) { +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; // Parse maxTokens. - 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; + 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")); } } // Parse tokenRatio. - ValidationErrors::ScopedField field(errors, ".tokenRatio"); - auto it = json.object_value().find("tokenRatio"); + it = json.object_value().find("tokenRatio"); if (it == json.object_value().end()) { - 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); + 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 { // We support up to 3 decimal digits. - 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; + 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 decimal_multiplier = 1; - for (size_t i = 0; i < (3 - after_decimal.length()); ++i) { - decimal_multiplier *= 10; + 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); + } + *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")); } - 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); } -// -// RetryMethodConfig -// +} // namespace -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; +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); } -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) { +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) { 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; } } } - // Validate initialBackoff. - { - ValidationErrors::ScopedField field(errors, ".initialBackoff"); - if (!errors->FieldHasErrors() && initial_backoff_ == Duration::Zero()) { - errors->AddError("must be greater than 0"); - } + // 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 maxBackoff. - { - ValidationErrors::ScopedField field(errors, ".maxBackoff"); - if (!errors->FieldHasErrors() && max_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 backoffMultiplier. - { - ValidationErrors::ScopedField field(errors, ".backoffMultiplier"); - if (!errors->FieldHasErrors() && backoff_multiplier_ <= 0) { - 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")); + } } } // Parse retryableStatusCodes. - 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); + 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); } } } - // 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"); + // 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")); + } } - } else if (retryable_status_codes_.Empty()) { + } else if (retryable_status_codes->Empty()) { // If perAttemptRecvTimeout not present, retryableStatusCodes must be // non-empty. - ValidationErrors::ScopedField field(errors, ".retryableStatusCodes"); - if (!errors->FieldHasErrors()) { - errors->AddError( - "must be non-empty if perAttemptRecvTimeout not present"); - } + error_list.push_back(GRPC_ERROR_CREATE( + "field:retryableStatusCodes error:must be non-empty if " + "perAttemptRecvTimeout not present")); } - } else if (retryable_status_codes_.Empty()) { + } else { // Hedging not enabled, so the error message for // retryableStatusCodes unset should be different. - ValidationErrors::ScopedField field(errors, ".retryableStatusCodes"); - if (!errors->FieldHasErrors()) { - errors->AddError("must be non-empty"); + if (retryable_status_codes->Empty()) { + error_list.push_back(GRPC_ERROR_CREATE( + "field:retryableStatusCodes error: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 -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; - } -}; - -} // namespace - -std::unique_ptr +absl::StatusOr> 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); + 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; + } + return std::make_unique( + max_attempts, initial_backoff, max_backoff, backoff_multiplier, + retryable_status_codes, per_attempt_recv_timeout); } } // 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 8eb87847795..6fdcdb2d055 100644 --- a/src/core/ext/filters/client_channel/retry_service_config.h +++ b/src/core/ext/filters/client_channel/retry_service_config.h @@ -24,6 +24,7 @@ #include +#include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" @@ -31,10 +32,7 @@ #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 { @@ -42,20 +40,31 @@ namespace internal { class RetryGlobalConfig : public ServiceConfigParser::ParsedConfig { public: - uintptr_t max_milli_tokens() const { return max_milli_tokens_; } - uintptr_t milli_token_ratio() const { return milli_token_ratio_; } + RetryGlobalConfig(intptr_t max_milli_tokens, intptr_t milli_token_ratio) + : max_milli_tokens_(max_milli_tokens), + milli_token_ratio_(milli_token_ratio) {} - static const JsonLoaderInterface* JsonLoader(const JsonArgs&); - void JsonPostLoad(const Json& json, const JsonArgs& args, - ValidationErrors* errors); + intptr_t max_milli_tokens() const { return max_milli_tokens_; } + intptr_t milli_token_ratio() const { return milli_token_ratio_; } private: - uintptr_t max_milli_tokens_ = 0; - uintptr_t milli_token_ratio_ = 0; + intptr_t max_milli_tokens_ = 0; + intptr_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_; } @@ -67,10 +76,6 @@ 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_; @@ -84,13 +89,11 @@ class RetryServiceConfigParser : public ServiceConfigParser::Parser { public: absl::string_view name() const override { return parser_name(); } - std::unique_ptr ParseGlobalParams( - const ChannelArgs& /*args*/, const Json& json, - ValidationErrors* errors) override; + absl::StatusOr> + ParseGlobalParams(const ChannelArgs& /*args*/, const Json& json) override; - std::unique_ptr ParsePerMethodParams( - const ChannelArgs& args, const Json& json, - ValidationErrors* errors) override; + absl::StatusOr> + ParsePerMethodParams(const ChannelArgs& args, const Json& json) 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 7108c193881..671ea3f634d 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( - uintptr_t max_milli_tokens, uintptr_t milli_token_ratio, + intptr_t max_milli_tokens, intptr_t milli_token_ratio, ServerRetryThrottleData* old_throttle_data) : max_milli_tokens_(max_milli_tokens), milli_token_ratio_(milli_token_ratio) { - uintptr_t initial_milli_tokens = max_milli_tokens; + intptr_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 uintptr_t new_value = - static_cast(gpr_atm_no_barrier_clamped_add( + const intptr_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, uintptr_t max_milli_tokens, - uintptr_t milli_token_ratio) { + const std::string& server_name, intptr_t max_milli_tokens, + intptr_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 62464c09894..195277de07b 100644 --- a/src/core/ext/filters/client_channel/retry_throttle.h +++ b/src/core/ext/filters/client_channel/retry_throttle.h @@ -40,8 +40,7 @@ namespace internal { /// Tracks retry throttling data for an individual server name. class ServerRetryThrottleData : public RefCounted { public: - ServerRetryThrottleData(uintptr_t max_milli_tokens, - uintptr_t milli_token_ratio, + ServerRetryThrottleData(intptr_t max_milli_tokens, intptr_t milli_token_ratio, ServerRetryThrottleData* old_throttle_data); ~ServerRetryThrottleData() override; @@ -51,15 +50,15 @@ class ServerRetryThrottleData : public RefCounted { /// Records a success. void RecordSuccess(); - 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_; } private: void GetReplacementThrottleDataIfNeeded( ServerRetryThrottleData** throttle_data); - const uintptr_t max_milli_tokens_; - const uintptr_t milli_token_ratio_; + const intptr_t max_milli_tokens_; + const intptr_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. @@ -75,8 +74,8 @@ class ServerRetryThrottleMap { /// Returns the failure data for \a server_name, creating a new entry if /// needed. RefCountedPtr GetDataForServer( - const std::string& server_name, uintptr_t max_milli_tokens, - uintptr_t milli_token_ratio); + const std::string& server_name, intptr_t max_milli_tokens, + intptr_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 95ea8c7e50b..567b5ab1a6b 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/fault_injection_service_config_parser.h" +#include "src/core/ext/filters/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 deleted file mode 100644 index cf7352957b0..00000000000 --- a/src/core/ext/filters/fault_injection/fault_injection_service_config_parser.cc +++ /dev/null @@ -1,118 +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/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.cc b/src/core/ext/filters/fault_injection/service_config_parser.cc new file mode 100644 index 00000000000..5670d549856 --- /dev/null +++ b/src/core/ext/filters/fault_injection/service_config_parser.cc @@ -0,0 +1,185 @@ +// +// 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/fault_injection/fault_injection_service_config_parser.h b/src/core/ext/filters/fault_injection/service_config_parser.h similarity index 79% rename from src/core/ext/filters/fault_injection/fault_injection_service_config_parser.h rename to src/core/ext/filters/fault_injection/service_config_parser.h index a6317b7adbe..8961fa30b74 100644 --- a/src/core/ext/filters/fault_injection/fault_injection_service_config_parser.h +++ b/src/core/ext/filters/fault_injection/service_config_parser.h @@ -14,8 +14,8 @@ // limitations under the License. // -#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 +#ifndef GRPC_CORE_EXT_FILTERS_FAULT_INJECTION_SERVICE_CONFIG_PARSER_H +#define GRPC_CORE_EXT_FILTERS_FAULT_INJECTION_SERVICE_CONFIG_PARSER_H #include @@ -25,8 +25,10 @@ #include #include #include +#include #include +#include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include @@ -34,10 +36,7 @@ #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. @@ -51,7 +50,7 @@ class FaultInjectionMethodParsedConfig public: struct FaultInjectionPolicy { grpc_status_code abort_code = GRPC_STATUS_OK; - std::string abort_message = "Fault injected"; + std::string abort_message; std::string abort_code_header; std::string abort_percentage_header; uint32_t abort_percentage_numerator = 0; @@ -65,12 +64,12 @@ class FaultInjectionMethodParsedConfig // By default, the max allowed active faults are unlimited. uint32_t max_faults = std::numeric_limits::max(); - - static const JsonLoaderInterface* JsonLoader(const JsonArgs&); - void JsonPostLoad(const Json& json, const JsonArgs&, - ValidationErrors* errors); }; + explicit FaultInjectionMethodParsedConfig( + std::vector fault_injection_policies) + : fault_injection_policies_(std::move(fault_injection_policies)) {} + // Returns the fault injection policy at certain index. // There might be multiple fault injection policies functioning at the same // time. The order between the policies are stable, and an index is used to @@ -84,8 +83,6 @@ class FaultInjectionMethodParsedConfig return &fault_injection_policies_[index]; } - static const JsonLoaderInterface* JsonLoader(const JsonArgs&); - private: std::vector fault_injection_policies_; }; @@ -95,9 +92,8 @@ class FaultInjectionServiceConfigParser final public: absl::string_view name() const override { return parser_name(); } // Parses the per-method service config for fault injection filter. - std::unique_ptr ParsePerMethodParams( - const ChannelArgs& args, const Json& json, - ValidationErrors* errors) override; + absl::StatusOr> + ParsePerMethodParams(const ChannelArgs& args, const Json& json) override; // Returns the parser index for FaultInjectionServiceConfigParser. static size_t ParserIndex(); // Registers FaultInjectionServiceConfigParser to ServiceConfigParser. @@ -109,4 +105,4 @@ class FaultInjectionServiceConfigParser final } // namespace grpc_core -#endif // GRPC_CORE_EXT_FILTERS_FAULT_INJECTION_FAULT_INJECTION_SERVICE_CONFIG_PARSER_H +#endif // GRPC_CORE_EXT_FILTERS_FAULT_INJECTION_SERVICE_CONFIG_PARSER_H 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 73a37b4d41f..ba90c0f7599 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()) {} - absl::optional max_recv_size() const { return max_recv_size_; } + int 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: - absl::optional max_recv_size_; + int 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->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(); + 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; } } @@ -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; - absl::optional max_recv_message_length_; + int 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_.has_value() && + if (calld->max_recv_message_length_ >= 0 && (*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 33114cd9b04..8103a04efa0 100644 --- a/src/core/ext/filters/message_size/message_size_filter.cc +++ b/src/core/ext/filters/message_size/message_size_filter.cc @@ -18,11 +18,17 @@ #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 @@ -32,6 +38,7 @@ #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" @@ -64,54 +71,58 @@ 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 // -std::unique_ptr +absl::StatusOr> MessageSizeParser::ParsePerMethodParams(const ChannelArgs& /*args*/, - const Json& json, - ValidationErrors* errors) { - return LoadFromJson>( - json, JsonArgs(), errors); + 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); } void MessageSizeParser::Register(CoreConfiguration::Builder* builder) { @@ -124,11 +135,23 @@ 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 limits; + grpc_core::MessageSizeParsedConfig::message_size_limits limits; const size_t service_config_parser_index{ grpc_core::MessageSizeParser::ParserIndex()}; }; @@ -146,30 +169,27 @@ 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* config_from_call_context = + const grpc_core::MessageSizeParsedConfig* limits = grpc_core::MessageSizeParsedConfig::GetFromCallContext( args.context, chand.service_config_parser_index); - 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 != 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->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(); + 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; } - limits = grpc_core::MessageSizeParsedConfig(max_send_size, max_recv_size); } } ~call_data() {} grpc_core::CallCombiner* call_combiner; - grpc_core::MessageSizeParsedConfig limits; + grpc_core::MessageSizeParsedConfig::message_size_limits 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. @@ -194,14 +214,13 @@ 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().has_value() && + if (calld->recv_message->has_value() && calld->limits.max_recv_size >= 0 && (*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); @@ -250,15 +269,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().has_value() && + if (op->send_message && calld->limits.max_send_size >= 0 && 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); @@ -298,13 +317,21 @@ 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 = grpc_core::MessageSizeParsedConfig::GetFromChannelArgs( + chand->limits = get_message_size_limits( grpc_core::ChannelArgs::FromC(args->channel_args)); return absl::OkStatus(); } @@ -348,11 +375,10 @@ static bool maybe_add_message_size_filter( if (channel_args.WantMinimalStack()) { return true; } - grpc_core::MessageSizeParsedConfig limits = - grpc_core::MessageSizeParsedConfig::GetFromChannelArgs(channel_args); + grpc_core::MessageSizeParsedConfig::message_size_limits lim = + get_message_size_limits(channel_args); const bool enable = - limits.max_send_size().has_value() || - limits.max_recv_size().has_value() || + lim.max_send_size != -1 || lim.max_recv_size != -1 || 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 e6b90ba9f9d..4493b2acc2c 100644 --- a/src/core/ext/filters/message_size/message_size_filter.h +++ b/src/core/ext/filters/message_size/message_size_filter.h @@ -20,22 +20,18 @@ #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; @@ -44,35 +40,32 @@ namespace grpc_core { class MessageSizeParsedConfig : public ServiceConfigParser::ParsedConfig { public: - absl::optional max_send_size() const { return max_send_size_; } - absl::optional max_recv_size() const { return max_recv_size_; } + struct message_size_limits { + int max_send_size; + int max_recv_size; + }; - MessageSizeParsedConfig() = default; + MessageSizeParsedConfig(int max_send_size, int max_recv_size) { + limits_.max_send_size = max_send_size; + limits_.max_recv_size = max_recv_size; + } - MessageSizeParsedConfig(absl::optional max_send_size, - absl::optional max_recv_size) - : max_send_size_(max_send_size), max_recv_size_(max_recv_size) {} + const message_size_limits& limits() const { return limits_; } 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: - absl::optional max_send_size_; - absl::optional max_recv_size_; + message_size_limits limits_; }; class MessageSizeParser : public ServiceConfigParser::Parser { public: absl::string_view name() const override { return parser_name(); } - std::unique_ptr ParsePerMethodParams( - const ChannelArgs& /*args*/, const Json& json, - ValidationErrors* errors) override; + absl::StatusOr> + ParsePerMethodParams(const ChannelArgs& /*args*/, const Json& json) override; static void Register(CoreConfiguration::Builder* builder); @@ -82,8 +75,8 @@ class MessageSizeParser : public ServiceConfigParser::Parser { static absl::string_view parser_name() { return "message_size"; } }; -absl::optional GetMaxRecvSizeFromChannelArgs(const ChannelArgs& args); -absl::optional GetMaxSendSizeFromChannelArgs(const ChannelArgs& args); +int GetMaxRecvSizeFromChannelArgs(const ChannelArgs& args); +int 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 a1dab43048b..cac1063d32c 100644 --- a/src/core/ext/filters/rbac/rbac_service_config_parser.cc +++ b/src/core/ext/filters/rbac/rbac_service_config_parser.cc @@ -18,789 +18,587 @@ #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/json/json_args.h" -#include "src/core/lib/json/json_object_loader.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/matchers/matchers.h" +#include "src/core/lib/transport/error_utils.h" namespace grpc_core { namespace { -// 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; +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; } -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 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); } -// -// 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 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::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; +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 -// - -const JsonLoaderInterface* -RbacConfig::RbacPolicy::Rules::Policy::HeaderMatch::JsonLoader( - const JsonArgs&) { - // All fields handled in JsonPostLoad(). - static const auto* loader = JsonObjectLoader().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); } -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 { - errors->AddError(header_matcher.status().message()); +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; }; - 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"); + 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); + } 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)); + } + } 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")); } + return permission; } -// -// 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 { - errors->AddError(string_matcher.status().message()); +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; }; - 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"); + 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); + } 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)); + } + } else { + error_list->push_back(GRPC_ERROR_CREATE("No valid id found")); } + return principal; } -// -// 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) { +Rbac::Policy ParsePolicy(const Json::Object& policy_json, + std::vector* error_list) { + Rbac::Policy policy; + const Json::Array* permissions_json_array; std::vector> permissions; - 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; - } - 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"); + 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)); + } + } } -} - -// -// 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) { + const Json::Array* principals_json_array; std::vector> principals; - principals.reserve(principal_list.size()); - for (auto& id : principal_list) { - principals.emplace_back(std::move(id.principal)); - } - return principals; -} - -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; - } - 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()); + 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)); + } } - 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))); + policy.permissions = + Rbac::Permission::MakeOrPermission(std::move(permissions)); + policy.principals = Rbac::Principal::MakeOrPrincipal(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 ParseRbac(const Json::Object& rbac_json, + std::vector* error_list) { Rbac rbac; - rbac.action = static_cast(action); - for (auto& p : policies) { - rbac.policies.emplace(p.first, p.second.TakeAsRbacPolicy()); + 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, {}); } - return rbac; -} - -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"); + int action; + if (ParseJsonObjectField(*rules_json, "action", &action, error_list)) { + if (action > 1) { + error_list->push_back(GRPC_ERROR_CREATE("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, {}); + 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)); + } + } } - return rules->TakeAsRbac(); -} - -const JsonLoaderInterface* RbacConfig::RbacPolicy::JsonLoader(const JsonArgs&) { - static const auto* loader = JsonObjectLoader() - .OptionalField("rules", &RbacPolicy::rules) - .Finish(); - return loader; + return rbac; } -// -// RbacConfig -// - -std::vector RbacConfig::TakeAsRbacList() { - std::vector rbac_list; - for (auto& rbac_policy : rbac_policies) { - rbac_list.emplace_back(rbac_policy.TakeAsRbac()); +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)); + } } - return rbac_list; -} - -const JsonLoaderInterface* RbacConfig::JsonLoader(const JsonArgs&) { - static const auto* loader = - JsonObjectLoader() - .Field("rbacPolicy", &RbacConfig::rbac_policies) - .Finish(); - return loader; + return policies; } } // namespace -std::unique_ptr +absl::StatusOr> RbacServiceConfigParser::ParsePerMethodParams(const ChannelArgs& args, - const Json& json, - ValidationErrors* errors) { + const Json& json) { // Only parse rbac policy if the channel arg is present if (!args.GetBool(GRPC_ARG_PARSE_RBAC_METHOD_CONFIG).value_or(false)) { return nullptr; } - auto rbac_config = LoadFromJson(json, JsonArgs(), errors); - std::vector rbac_policies = rbac_config.TakeAsRbacList(); + 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; + } 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 3419e4b4c92..06f83827b7b 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,9 +69,8 @@ class RbacServiceConfigParser : public ServiceConfigParser::Parser { public: absl::string_view name() const override { return parser_name(); } // Parses the per-method service config for rbac filter. - std::unique_ptr ParsePerMethodParams( - const ChannelArgs& args, const Json& json, - ValidationErrors* errors) override; + absl::StatusOr> + ParsePerMethodParams(const ChannelArgs& args, const Json& json) 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 bf6207ff45b..11e7b1b38de 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/fault_injection_service_config_parser.h" +#include "src/core/ext/filters/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 e8cd4c79c00..bf4cd463045 100644 --- a/src/core/ext/xds/xds_http_rbac_filter.cc +++ b/src/core/ext/xds/xds_http_rbac_filter.cc @@ -165,6 +165,10 @@ 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", @@ -172,7 +176,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", google_protobuf_UInt32Value_value(prefix_len)); + json.emplace("prefixLen", ParseUInt32ValueToJson(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 9a768161ae7..095e8860982 100644 --- a/src/core/lib/json/json_object_loader.h +++ b/src/core/lib/json/json_object_loader.h @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -393,26 +392,6 @@ 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 fa73d55e337..064c58b80ff 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 required to be exposed as part of the resolver API. +// interface requied 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 da3fa5d5a69..62de724587c 100644 --- a/src/core/lib/service_config/service_config_impl.cc +++ b/src/core/lib/service_config/service_config_impl.cc @@ -20,151 +20,197 @@ #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 "absl/strings/str_join.h" + +#include #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(); - ValidationErrors errors; - auto service_config = Create(args, *json, json_string, &errors); - if (!errors.ok()) return errors.status("errors validating service config"); + absl::Status status; + auto service_config = MakeRefCounted( + args, std::string(json_string), std::move(*json), &status); + if (!status.ok()) return status; return service_config; } -RefCountedPtr ServiceConfigImpl::Create( - const ChannelArgs& args, const Json& json, ValidationErrors* errors) { - return Create(args, json, json.Dump(), errors); +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, "; "), "]")); + } } -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; +ServiceConfigImpl::~ServiceConfigImpl() { + for (auto& p : parsed_method_configs_map_) { + CSliceUnref(p.first); } - 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; +} + +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(); + } + // 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()); } 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 = 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); + 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; } else { - value = 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 = 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; + } } } } } } - return service_config; + if (!errors.empty()) { + return absl::InvalidArgumentError( + absl::StrCat("index ", index, ": [", absl::StrJoin(errors, "; "), "]")); + } + return absl::OkStatus(); } -ServiceConfigImpl::~ServiceConfigImpl() { - for (auto& p : parsed_method_configs_map_) { - CSliceUnref(p.first); +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(); +} + +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 ""; } + // 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 016be254cb0..6aa6f8bbc95 100644 --- a/src/core/lib/service_config/service_config_impl.h +++ b/src/core/lib/service_config/service_config_impl.h @@ -26,6 +26,7 @@ #include #include +#include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" @@ -34,7 +35,6 @@ #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,15 +72,8 @@ class ServiceConfigImpl final : public ServiceConfig { static absl::StatusOr> Create( const ChannelArgs& args, absl::string_view json_string); - // 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(const ChannelArgs& args, std::string json_string, Json json, + absl::Status* status); ~ServiceConfigImpl() override; absl::string_view json_string() const override { return json_string_; } @@ -101,10 +94,20 @@ 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_; - ServiceConfigParser::ParsedConfigVector parsed_global_configs_; + std::vector> + 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. @@ -115,11 +118,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_map_ and default_method_config_vector_. - std::vector + // parsed_method_configs_table_. + 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 c97f7daae55..0ec4be9fd79 100644 --- a/src/core/lib/service_config/service_config_parser.cc +++ b/src/core/lib/service_config/service_config_parser.cc @@ -22,7 +22,9 @@ #include +#include "absl/status/status.h" #include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" #include @@ -47,28 +49,43 @@ void ServiceConfigParser::Builder::RegisterParser( registered_parsers_.emplace_back(std::move(parser)); } -ServiceConfigParser::ParsedConfigVector +absl::StatusOr ServiceConfigParser::ParseGlobalParameters(const ChannelArgs& args, - const Json& json, - ValidationErrors* errors) const { + const Json& json) const { ParsedConfigVector parsed_global_configs; - for (auto& parser : registered_parsers_) { - parsed_global_configs.push_back( - parser->ParseGlobalParams(args, json, errors)); + 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, "; ")); } - return parsed_global_configs; + return std::move(parsed_global_configs); } -ServiceConfigParser::ParsedConfigVector +absl::StatusOr ServiceConfigParser::ParsePerMethodParameters(const ChannelArgs& args, - const Json& json, - ValidationErrors* errors) const { + const Json& json) const { ParsedConfigVector parsed_method_configs; - for (auto& parser : registered_parsers_) { - parsed_method_configs.push_back( - parser->ParsePerMethodParams(args, json, errors)); + 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, "; ")); } - return parsed_method_configs; + return std::move(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 ac0e718448c..600b1624028 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,15 +52,13 @@ class ServiceConfigParser { virtual absl::string_view name() const = 0; - virtual std::unique_ptr ParseGlobalParams( - const ChannelArgs& /*args*/, const Json& /*json*/, - ValidationErrors* /*errors*/) { + virtual absl::StatusOr> ParseGlobalParams( + const ChannelArgs& /*args*/, const Json& /*json*/) { return nullptr; } - virtual std::unique_ptr ParsePerMethodParams( - const ChannelArgs& /*args*/, const Json& /*json*/, - ValidationErrors* /*errors*/) { + virtual absl::StatusOr> ParsePerMethodParams( + const ChannelArgs& /*args*/, const Json& /*json*/) { return nullptr; } }; @@ -82,13 +80,11 @@ class ServiceConfigParser { ServiceConfigParserList registered_parsers_; }; - ParsedConfigVector ParseGlobalParameters(const ChannelArgs& args, - const Json& json, - ValidationErrors* errors) const; + absl::StatusOr ParseGlobalParameters( + const ChannelArgs& args, const Json& json) const; - ParsedConfigVector ParsePerMethodParameters(const ChannelArgs& args, - const Json& json, - ValidationErrors* errors) const; + absl::StatusOr ParsePerMethodParameters( + const ChannelArgs& args, const Json& json) const; // Return the index for a given registered parser. // If there is an error, return -1. @@ -102,4 +98,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 7294e274f6b..ab797e7460f 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -25,7 +25,6 @@ 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', @@ -67,6 +66,7 @@ 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/fault_injection_service_config_parser.cc', + 'src/core/ext/filters/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 55dba872fc3..896b84b0907 100644 --- a/test/core/client_channel/BUILD +++ b/test/core/client_channel/BUILD @@ -60,22 +60,8 @@ grpc_cc_test( ) grpc_cc_test( - 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"], + name = "service_config_test", + srcs = ["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 deleted file mode 100644 index 5fbf65b95e5..00000000000 --- a/test/core/client_channel/client_channel_service_config_test.cc +++ /dev/null @@ -1,315 +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/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 deleted file mode 100644 index b64941e5222..00000000000 --- a/test/core/client_channel/retry_service_config_test.cc +++ /dev/null @@ -1,712 +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/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 42cb7b25b34..07ffa68b56a 100644 --- a/test/core/client_channel/rls_lb_config_parser_test.cc +++ b/test/core/client_channel/rls_lb_config_parser_test.cc @@ -14,8 +14,11 @@ // 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" @@ -108,14 +111,15 @@ TEST_F(RlsConfigParsingTest, TopLevelFieldsWrongTypes) { auto service_config = ServiceConfigImpl::Create(ChannelArgs(), service_config_json); EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - 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]]") + 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]")) << service_config.status(); } @@ -187,13 +191,17 @@ TEST_F(RlsConfigParsingTest, InvalidRlsChannelServiceConfig) { auto service_config = ServiceConfigImpl::Create(ChannelArgs(), service_config_json); EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument); - 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]]") + 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\\]")) << service_config.status(); } diff --git a/test/core/client_channel/service_config_test.cc b/test/core/client_channel/service_config_test.cc new file mode 100644 index 00000000000..f6c24d700a0 --- /dev/null +++ b/test/core/client_channel/service_config_test.cc @@ -0,0 +1,1557 @@ +/* + * + * 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 e4aa1f9a920..1b1a0b335f4 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,8 +14,10 @@ #include "src/core/ext/filters/rbac/rbac_service_config_parser.h" +#include + #include "absl/status/status.h" -#include "absl/status/statusor.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" #include @@ -26,6 +28,9 @@ #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 { @@ -143,9 +148,10 @@ 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_EQ(service_config.status().message(), - "errors validating service config: [" - "field:methodConfig[0].rbacPolicy error:is not an array]") + EXPECT_THAT( + std::string(service_config.status().message()), + ::testing::ContainsRegex("Rbac parser" CHILD_ERROR_TAG + "field:rbacPolicy error:type should be ARRAY")) << service_config.status(); } @@ -162,9 +168,11 @@ 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_EQ(service_config.status().message(), - "errors validating service config: [" - "field:methodConfig[0].rbacPolicy[0].rules error:is not an object]") + 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")) << service_config.status(); } @@ -186,12 +194,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_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]") + 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")) << service_config.status(); } @@ -216,12 +224,13 @@ 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_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]") + 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")) << service_config.status(); } @@ -248,12 +257,13 @@ 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_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]") + 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")) << service_config.status(); } @@ -360,51 +370,53 @@ 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_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]") + 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.*")) << service_config.status(); } @@ -472,8 +484,7 @@ 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\"}}\n" + " {\"header\":{\"name\":\"name\", \"containsMatch\":1}}\n" " ],\n" " \"principals\":[]\n" " }\n" @@ -485,26 +496,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_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]") + 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.*")) << service_config.status(); } @@ -567,8 +578,7 @@ TEST(RbacServiceConfigParsingTest, StringMatcherBadTypes) { " {\"requestedServerName\":{\"prefix\":1}},\n" " {\"requestedServerName\":{\"suffix\":1}},\n" " {\"requestedServerName\":{\"safeRegex\":1}},\n" - " {\"requestedServerName\":{\"contains\":1}},\n" - " {\"requestedServerName\":{}}\n" + " {\"requestedServerName\":{\"contains\":1}}\n" " ],\n" " \"principals\":[]\n" " }\n" @@ -580,27 +590,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_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]") + 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.*")) << service_config.status(); } diff --git a/test/core/json/json_object_loader_test.cc b/test/core/json/json_object_loader_test.cc index 73088675047..e65cc902228 100644 --- a/test/core/json/json_object_loader_test.cc +++ b/test/core/json/json_object_loader_test.cc @@ -49,7 +49,6 @@ 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 = @@ -58,7 +57,6 @@ 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; } @@ -81,13 +79,6 @@ 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); @@ -97,23 +88,20 @@ TYPED_TEST_P(SignedIntegerTest, IntegerFields) { // Optional fields present. test_struct = Parse( "{\"value\": 5, \"optional_value\": 7, " - "\"absl_optional_value\": 9, \"unique_ptr_value\": 11}"); + "\"absl_optional_value\": 9}"); 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, \"unique_ptr_value\": false}"); + "\"absl_optional_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 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(); } @@ -137,7 +125,6 @@ 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 = @@ -146,7 +133,6 @@ 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; } @@ -170,13 +156,6 @@ 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); @@ -186,24 +165,21 @@ TYPED_TEST_P(UnsignedIntegerTest, IntegerFields) { // Optional fields present. test_struct = Parse( "{\"value\": 5, \"optional_value\": 7, " - "\"absl_optional_value\": 9, \"unique_ptr_value\": 11}"); + "\"absl_optional_value\": 9}"); 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, \"unique_ptr_value\": false}"); + "\"absl_optional_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 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(); } @@ -227,7 +203,6 @@ 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 = @@ -236,7 +211,6 @@ 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; } @@ -259,13 +233,6 @@ 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); @@ -275,24 +242,21 @@ TYPED_TEST_P(FloatingPointTest, FloatFields) { // Optional fields present. test_struct = Parse( "{\"value\": 5.2, \"optional_value\": 7.5, " - "\"absl_optional_value\": 9.8, \"unique_ptr_value\": 11.5}"); + "\"absl_optional_value\": 9.8}"); 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, \"unique_ptr_value\": false}"); + "\"absl_optional_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 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(); } @@ -311,7 +275,6 @@ 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 = @@ -320,7 +283,6 @@ 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; } @@ -346,23 +308,20 @@ TEST(JsonObjectLoader, BooleanFields) { // Optional fields present. test_struct = Parse( "{\"value\": true, \"optional_value\": false," - "\"absl_optional_value\": true, \"unique_ptr_value\": false}"); + "\"absl_optional_value\": true}"); 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, \"unique_ptr_value\": \"foo\"}"); + "\"absl_optional_value\": 1}"); 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(); } @@ -376,7 +335,6 @@ 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 = @@ -385,7 +343,6 @@ 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; } @@ -405,23 +362,20 @@ TEST(JsonObjectLoader, StringFields) { // Optional fields present. test_struct = Parse( "{\"value\": \"foo\", \"optional_value\": \"bar\"," - "\"absl_optional_value\": \"baz\", \"unique_ptr_value\": \"quux\"}"); + "\"absl_optional_value\": \"baz\"}"); 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, \"unique_ptr_value\": true}"); + "\"absl_optional_value\": 1}"); 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(); } @@ -435,7 +389,6 @@ 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 = @@ -444,7 +397,6 @@ 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; } @@ -489,23 +441,20 @@ TEST(JsonObjectLoader, DurationFields) { // Optional fields present. test_struct = Parse( "{\"value\": \"3s\", \"optional_value\": \"3.2s\", " - "\"absl_optional_value\": \"10s\", \"unique_ptr_value\": \"11s\"}"); + "\"absl_optional_value\": \"10s\"}"); 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, \"unique_ptr_value\": true}"); + "\"absl_optional_value\": 1}"); 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(); } @@ -519,7 +468,6 @@ 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 = @@ -528,7 +476,6 @@ 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; } @@ -548,24 +495,21 @@ TEST(JsonObjectLoader, JsonObjectFields) { // Optional fields present. test_struct = Parse( "{\"value\": {\"a\":1}, \"optional_value\": {\"b\":2}, " - "\"absl_optional_value\": {\"c\":3}, \"unique_ptr_value\": {\"d\":4}}"); + "\"absl_optional_value\": {\"c\":3}}"); 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, \"unique_ptr_value\": \"foo\"}"); + "\"absl_optional_value\": 1}"); 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(); } @@ -579,7 +523,6 @@ 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 = @@ -588,7 +531,6 @@ 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; } @@ -609,8 +551,7 @@ TEST(JsonObjectLoader, MapFields) { // Optional fields present. test_struct = Parse( "{\"value\": {\"a\":1}, \"optional_value\": {\"b\":\"foo\"}, " - "\"absl_optional_value\": {\"c\":true}, " - "\"unique_ptr_value\": {\"d\":4}}"); + "\"absl_optional_value\": {\"c\":true}}"); ASSERT_TRUE(test_struct.ok()) << test_struct.status(); EXPECT_THAT(test_struct->value, ::testing::ElementsAre(::testing::Pair("a", 1))); @@ -619,19 +560,15 @@ 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, \"unique_ptr_value\": \"foo\"}"); + "\"absl_optional_value\": 1}"); 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. @@ -656,7 +593,6 @@ 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 = @@ -665,7 +601,6 @@ 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; } @@ -685,8 +620,7 @@ TEST(JsonObjectLoader, VectorFields) { // Optional fields present. test_struct = Parse( "{\"value\": [4, 5, 6], \"optional_value\": [\"foo\", \"bar\"], " - "\"absl_optional_value\": [true, false, true], " - "\"unique_ptr_value\": [1, 2, 3]}"); + "\"absl_optional_value\": [true, false, true]}"); ASSERT_TRUE(test_struct.ok()) << test_struct.status(); EXPECT_THAT(test_struct->value, ::testing::ElementsAre(4, 5, 6)); EXPECT_THAT(test_struct->optional_value, @@ -694,18 +628,15 @@ 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, \"unique_ptr_value\": \"foo\"}"); + "\"absl_optional_value\": 1}"); 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. @@ -743,7 +674,6 @@ 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 = @@ -752,7 +682,6 @@ 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; } @@ -779,26 +708,22 @@ TEST(JsonObjectLoader, NestedStructFields) { // Optional fields present. test_struct = Parse( "{\"outer\": {\"inner\":1}, \"optional_outer\": {\"inner\":2}, " - "\"absl_optional_outer\": {\"inner\":3}, " - "\"unique_ptr_outer\": {\"inner\":4}}"); + "\"absl_optional_outer\": {\"inner\":3}}"); 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, \"unique_ptr_outer\": \"foo\"}"); + "\"absl_optional_outer\": 1}"); 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:unique_ptr_outer error:is not an object]") + "field:outer error:is not an object]") << test_struct.status(); // Wrong JSON type for inner value. test_struct = Parse( @@ -849,13 +774,6 @@ 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 deleted file mode 100644 index d188cc37898..00000000000 --- a/test/core/message_size/BUILD +++ /dev/null @@ -1,33 +0,0 @@ -# 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 deleted file mode 100644 index 4b669dfe5b8..00000000000 --- a/test/core/message_size/message_size_service_config_test.cc +++ /dev/null @@ -1,125 +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 - -#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 deleted file mode 100644 index 34070410740..00000000000 --- a/test/core/service_config/BUILD +++ /dev/null @@ -1,33 +0,0 @@ -# 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 deleted file mode 100644 index 9baf42f6ab8..00000000000 --- a/test/core/service_config/service_config_test.cc +++ /dev/null @@ -1,468 +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 "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 5dd14fad35f..bc3addce010 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/fault_injection_service_config_parser.h" +#include "src/core/ext/filters/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\":24}}," + "\"addressPrefix\":\"127.0.0\",\"prefixLen\":{\"value\":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 e9f6768155a..47cca33839c 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:methodConfig[0].waitForReady error:is not a boolean', + '--expected_service_config_error', 'field:waitForReady error:Type should be true/false', '--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:is not a string', + '--expected_service_config_error', 'field:loadBalancingPolicy error:type should be 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 7ff3418e4a7..8121b67faaa 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:methodConfig[0].waitForReady error:is not a boolean' + expected_service_config_error: 'field:waitForReady error:Type should be true/false' 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:is not a string' + expected_service_config_error: 'field:loadBalancingPolicy error:type should be 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 c180ea013f7..da4bb0834b5 100644 --- a/tools/doxygen/Doxyfile.c++.internal +++ b/tools/doxygen/Doxyfile.c++.internal @@ -1074,8 +1074,6 @@ 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 \ @@ -1144,6 +1142,8 @@ 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/fault_injection_service_config_parser.cc \ -src/core/ext/filters/fault_injection/fault_injection_service_config_parser.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/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,7 +2249,6 @@ 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 a236f685f30..225aacedc84 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -890,8 +890,6 @@ 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 \ @@ -964,6 +962,8 @@ 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/fault_injection_service_config_parser.cc \ -src/core/ext/filters/fault_injection/fault_injection_service_config_parser.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/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,7 +2040,6 @@ 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 6473637cac6..f94d77b9ac4 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -1919,30 +1919,6 @@ ], "uses_polling": true }, - { - "args": [], - "benchmark": false, - "ci_platforms": [ - "linux", - "mac", - "posix", - "windows" - ], - "cpu_cost": 1.0, - "exclude_configs": [], - "exclude_iomgrs": [], - "flaky": false, - "gtest": true, - "language": "c++", - "name": "client_channel_service_config_test", - "platforms": [ - "linux", - "mac", - "posix", - "windows" - ], - "uses_polling": true - }, { "args": [], "benchmark": false, @@ -4645,30 +4621,6 @@ ], "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, @@ -5761,30 +5713,6 @@ ], "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,