diff --git a/CMakeLists.txt b/CMakeLists.txt index dc008e02078..c966ec332c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1267,11 +1267,9 @@ if(gRPC_BUILD_TESTS) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_cxx xds_fault_injection_end2end_test) endif() - add_dependencies(buildtests_cxx xds_http_filters_test) add_dependencies(buildtests_cxx xds_interop_client) add_dependencies(buildtests_cxx xds_interop_server) add_dependencies(buildtests_cxx xds_lb_policy_registry_test) - add_dependencies(buildtests_cxx xds_listener_resource_type_test) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_cxx xds_outlier_detection_end2end_test) endif() @@ -20540,10 +20538,6 @@ add_executable(xds_cluster_resource_type_test ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/tls.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/tls.pb.h ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/tls.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/typed_struct.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/typed_struct.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/typed_struct.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/typed_struct.grpc.pb.h test/core/xds/xds_cluster_resource_type_test.cc third_party/googletest/googletest/src/gtest-all.cc third_party/googletest/googlemock/src/gmock-all.cc @@ -21650,116 +21644,6 @@ endif() endif() if(gRPC_BUILD_TESTS) -add_executable(xds_http_filters_test - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/reflection/v1alpha/reflection.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/reflection/v1alpha/reflection.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/reflection/v1alpha/reflection.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/reflection/v1alpha/reflection.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/address.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/address.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/address.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/address.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/base.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/base.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/base.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/base.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/expr.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/expr.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/expr.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/expr.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/extension.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/extension.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/extension.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/extension.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault_common.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault_common.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault_common.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault_common.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/http_filter_rbac.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/http_filter_rbac.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/http_filter_rbac.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/http_filter_rbac.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/metadata.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/metadata.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/metadata.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/metadata.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/path.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/path.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/path.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/path.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/percent.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/percent.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/percent.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/percent.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/range.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/range.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/range.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/range.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/rbac.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/rbac.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/rbac.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/rbac.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/regex.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/regex.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/regex.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/regex.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/route.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/route.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/route.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/route.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/router.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/router.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/router.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/router.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/string.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/string.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/string.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/string.grpc.pb.h - test/core/xds/xds_http_filters_test.cc - test/cpp/util/cli_call.cc - test/cpp/util/cli_credentials.cc - test/cpp/util/proto_file_parser.cc - test/cpp/util/proto_reflection_descriptor_database.cc - test/cpp/util/service_describer.cc - third_party/googletest/googletest/src/gtest-all.cc - third_party/googletest/googlemock/src/gmock-all.cc -) - -target_include_directories(xds_http_filters_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(xds_http_filters_test - ${_gRPC_PROTOBUF_LIBRARIES} - ${_gRPC_ALLTARGETS_LIBRARIES} - absl::flags - grpc++ - grpc_test_util -) - - -endif() -if(gRPC_BUILD_TESTS) - add_executable(xds_interop_client ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/empty.pb.cc ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/empty.grpc.pb.cc @@ -21998,140 +21882,6 @@ target_link_libraries(xds_lb_policy_registry_test ) -endif() -if(gRPC_BUILD_TESTS) - -add_executable(xds_listener_resource_type_test - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/reflection/v1alpha/reflection.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/reflection/v1alpha/reflection.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/reflection/v1alpha/reflection.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/reflection/v1alpha/reflection.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/address.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/address.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/address.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/address.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/base.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/base.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/base.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/base.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/config_source.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/config_source.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/config_source.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/config_source.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/expr.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/expr.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/expr.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/expr.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/extension.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/extension.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/extension.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/extension.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault_common.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault_common.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault_common.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault_common.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/http_connection_manager.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/http_connection_manager.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/http_connection_manager.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/http_connection_manager.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/http_filter_rbac.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/http_filter_rbac.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/http_filter_rbac.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/http_filter_rbac.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/listener.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/listener.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/listener.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/listener.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/metadata.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/metadata.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/metadata.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/metadata.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/path.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/path.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/path.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/path.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/percent.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/percent.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/percent.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/percent.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/protocol.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/protocol.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/protocol.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/protocol.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/range.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/range.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/range.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/range.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/rbac.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/rbac.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/rbac.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/rbac.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/regex.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/regex.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/regex.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/regex.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/route.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/route.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/route.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/route.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/router.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/router.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/router.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/router.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/string.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/string.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/string.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/string.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/tls.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/tls.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/tls.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/tls.grpc.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/typed_struct.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/typed_struct.grpc.pb.cc - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/typed_struct.pb.h - ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/typed_struct.grpc.pb.h - test/core/xds/xds_listener_resource_type_test.cc - test/cpp/util/cli_call.cc - test/cpp/util/cli_credentials.cc - test/cpp/util/proto_file_parser.cc - test/cpp/util/proto_reflection_descriptor_database.cc - test/cpp/util/service_describer.cc - third_party/googletest/googletest/src/gtest-all.cc - third_party/googletest/googlemock/src/gmock-all.cc -) - -target_include_directories(xds_listener_resource_type_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(xds_listener_resource_type_test - ${_gRPC_PROTOBUF_LIBRARIES} - ${_gRPC_ALLTARGETS_LIBRARIES} - absl::flags - grpc++ - grpc_test_util -) - - endif() if(gRPC_BUILD_TESTS) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) @@ -22694,6 +22444,14 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/extension.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/extension.pb.h ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/extension.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault_common.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault_common.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault_common.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/fault_common.grpc.pb.h ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/http_connection_manager.pb.cc ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/http_connection_manager.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/http_connection_manager.pb.h diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index b5e66c3f3ba..a97eb85ddae 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -11221,7 +11221,6 @@ targets: - src/proto/grpc/testing/xds/v3/regex.proto - src/proto/grpc/testing/xds/v3/string.proto - src/proto/grpc/testing/xds/v3/tls.proto - - src/proto/grpc/testing/xds/v3/typed_struct.proto - test/core/xds/xds_cluster_resource_type_test.cc deps: - grpc_test_util @@ -11481,6 +11480,7 @@ targets: headers: - test/cpp/end2end/counted_service.h - test/cpp/end2end/test_service_impl.h + - test/cpp/end2end/xds/no_op_http_filter.h - test/cpp/end2end/xds/xds_end2end_test_lib.h - test/cpp/end2end/xds/xds_server.h - test/cpp/util/tls_test_utils.h @@ -11600,46 +11600,6 @@ targets: - linux - posix - mac -- name: xds_http_filters_test - gtest: true - build: test - language: c++ - headers: - - test/cpp/util/cli_call.h - - test/cpp/util/cli_credentials.h - - test/cpp/util/config_grpc_cli.h - - test/cpp/util/proto_file_parser.h - - test/cpp/util/proto_reflection_descriptor_database.h - - test/cpp/util/service_describer.h - src: - - src/proto/grpc/reflection/v1alpha/reflection.proto - - src/proto/grpc/testing/xds/v3/address.proto - - src/proto/grpc/testing/xds/v3/base.proto - - src/proto/grpc/testing/xds/v3/expr.proto - - src/proto/grpc/testing/xds/v3/extension.proto - - src/proto/grpc/testing/xds/v3/fault.proto - - src/proto/grpc/testing/xds/v3/fault_common.proto - - src/proto/grpc/testing/xds/v3/http_filter_rbac.proto - - src/proto/grpc/testing/xds/v3/metadata.proto - - src/proto/grpc/testing/xds/v3/path.proto - - src/proto/grpc/testing/xds/v3/percent.proto - - src/proto/grpc/testing/xds/v3/range.proto - - src/proto/grpc/testing/xds/v3/rbac.proto - - src/proto/grpc/testing/xds/v3/regex.proto - - src/proto/grpc/testing/xds/v3/route.proto - - src/proto/grpc/testing/xds/v3/router.proto - - src/proto/grpc/testing/xds/v3/string.proto - - test/core/xds/xds_http_filters_test.cc - - test/cpp/util/cli_call.cc - - test/cpp/util/cli_credentials.cc - - test/cpp/util/proto_file_parser.cc - - test/cpp/util/proto_reflection_descriptor_database.cc - - test/cpp/util/service_describer.cc - deps: - - absl/flags:flag - - grpc++ - - grpc_test_util - uses_polling: false - name: xds_interop_client build: test run: false @@ -11726,52 +11686,6 @@ targets: - grpc++ - grpc_test_util uses_polling: false -- name: xds_listener_resource_type_test - gtest: true - build: test - language: c++ - headers: - - test/cpp/util/cli_call.h - - test/cpp/util/cli_credentials.h - - test/cpp/util/config_grpc_cli.h - - test/cpp/util/proto_file_parser.h - - test/cpp/util/proto_reflection_descriptor_database.h - - test/cpp/util/service_describer.h - src: - - src/proto/grpc/reflection/v1alpha/reflection.proto - - src/proto/grpc/testing/xds/v3/address.proto - - src/proto/grpc/testing/xds/v3/base.proto - - src/proto/grpc/testing/xds/v3/config_source.proto - - src/proto/grpc/testing/xds/v3/expr.proto - - src/proto/grpc/testing/xds/v3/extension.proto - - src/proto/grpc/testing/xds/v3/fault.proto - - src/proto/grpc/testing/xds/v3/fault_common.proto - - src/proto/grpc/testing/xds/v3/http_connection_manager.proto - - src/proto/grpc/testing/xds/v3/http_filter_rbac.proto - - src/proto/grpc/testing/xds/v3/listener.proto - - src/proto/grpc/testing/xds/v3/metadata.proto - - src/proto/grpc/testing/xds/v3/path.proto - - src/proto/grpc/testing/xds/v3/percent.proto - - src/proto/grpc/testing/xds/v3/protocol.proto - - src/proto/grpc/testing/xds/v3/range.proto - - src/proto/grpc/testing/xds/v3/rbac.proto - - src/proto/grpc/testing/xds/v3/regex.proto - - src/proto/grpc/testing/xds/v3/route.proto - - src/proto/grpc/testing/xds/v3/router.proto - - src/proto/grpc/testing/xds/v3/string.proto - - src/proto/grpc/testing/xds/v3/tls.proto - - src/proto/grpc/testing/xds/v3/typed_struct.proto - - test/core/xds/xds_listener_resource_type_test.cc - - test/cpp/util/cli_call.cc - - test/cpp/util/cli_credentials.cc - - test/cpp/util/proto_file_parser.cc - - test/cpp/util/proto_reflection_descriptor_database.cc - - test/cpp/util/service_describer.cc - deps: - - absl/flags:flag - - grpc++ - - grpc_test_util - uses_polling: false - name: xds_outlier_detection_end2end_test gtest: true build: test @@ -11780,6 +11694,7 @@ targets: headers: - test/cpp/end2end/counted_service.h - test/cpp/end2end/test_service_impl.h + - test/cpp/end2end/xds/no_op_http_filter.h - test/cpp/end2end/xds/xds_end2end_test_lib.h - test/cpp/end2end/xds/xds_server.h - test/cpp/util/tls_test_utils.h @@ -11948,6 +11863,7 @@ targets: headers: - test/cpp/end2end/counted_service.h - test/cpp/end2end/test_service_impl.h + - test/cpp/end2end/xds/no_op_http_filter.h - test/cpp/end2end/xds/xds_end2end_test_lib.h - test/cpp/end2end/xds/xds_server.h - test/cpp/util/tls_test_utils.h @@ -11965,6 +11881,8 @@ targets: - src/proto/grpc/testing/xds/v3/endpoint.proto - src/proto/grpc/testing/xds/v3/expr.proto - src/proto/grpc/testing/xds/v3/extension.proto + - src/proto/grpc/testing/xds/v3/fault.proto + - src/proto/grpc/testing/xds/v3/fault_common.proto - src/proto/grpc/testing/xds/v3/http_connection_manager.proto - src/proto/grpc/testing/xds/v3/http_filter_rbac.proto - src/proto/grpc/testing/xds/v3/listener.proto diff --git a/src/core/BUILD b/src/core/BUILD index 7673f2b55aa..c7f59b0c728 100644 --- a/src/core/BUILD +++ b/src/core/BUILD @@ -3501,7 +3501,6 @@ grpc_cc_library( "grpc_xds_channel_stack_modifier", "grpc_xds_client", "iomgr_fwd", - "match", "resolved_address", "slice_refcount", "status_helper", diff --git a/src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc b/src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc index 870e67bc416..564fa6f3c0c 100644 --- a/src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc +++ b/src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc @@ -377,7 +377,7 @@ class XdsResolver : public Resolver { // This will not contain the RouteConfiguration, even if it comes with the // LDS response; instead, the relevant VirtualHost from the // RouteConfiguration will be saved in current_virtual_host_. - XdsListenerResource::HttpConnectionManager current_listener_; + XdsListenerResource current_listener_; std::string route_config_name_; RouteConfigWatcher* route_config_watcher_ = nullptr; @@ -468,7 +468,8 @@ XdsResolver::XdsConfigSelector::XdsConfigSelector( // one. if (!route_action->max_stream_duration.has_value()) { route_action->max_stream_duration = - resolver_->current_listener_.http_max_stream_duration; + resolver_->current_listener_.http_connection_manager + .http_max_stream_duration; } Match( route_action->action, @@ -523,7 +524,8 @@ XdsResolver::XdsConfigSelector::XdsConfigSelector( } } // Populate filter list. - for (const auto& http_filter : resolver_->current_listener_.http_filters) { + for (const auto& http_filter : + resolver_->current_listener_.http_connection_manager.http_filters) { // Find filter. This is guaranteed to succeed, because it's checked // at config validation time in the XdsApi code. const XdsHttpFilterImpl* filter_impl = @@ -600,7 +602,7 @@ XdsResolver::XdsConfigSelector::CreateMethodConfig( } // Handle xDS HTTP filters. auto result = XdsRouting::GeneratePerHTTPFilterConfigs( - resolver_->current_listener_.http_filters, + resolver_->current_listener_.http_connection_manager.http_filters, resolver_->current_virtual_host_, route, cluster_weight, resolver_->args_); if (!result.ok()) return result.status(); @@ -887,35 +889,39 @@ void XdsResolver::OnListenerUpdate(XdsListenerResource listener) { if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) { gpr_log(GPR_INFO, "[xds_resolver %p] received updated listener data", this); } - if (xds_client_ == nullptr) return; - current_listener_ = std::move( - absl::get(listener.listener)); - MatchMutable( - ¤t_listener_.route_config, - // RDS resource name - [&](std::string* rds_name) { - if (route_config_watcher_ != nullptr) { - XdsRouteConfigResourceType::CancelWatch( - xds_client_.get(), route_config_name_, route_config_watcher_, - /*delay_unsubscription=*/!rds_name->empty()); - route_config_watcher_ = nullptr; - } - route_config_name_ = std::move(*rds_name); - if (!route_config_name_.empty()) { - current_virtual_host_.routes.clear(); - auto watcher = MakeRefCounted(Ref()); - route_config_watcher_ = watcher.get(); - XdsRouteConfigResourceType::StartWatch( - xds_client_.get(), route_config_name_, std::move(watcher)); - } - // HCM may contain newer filter config. We need to propagate the - // update as config selector to the channel. - GenerateResult(); - }, - // inlined RouteConfig - [&](XdsRouteConfigResource* route_config) { - OnRouteConfigUpdate(std::move(*route_config)); - }); + if (xds_client_ == nullptr) { + return; + } + if (listener.http_connection_manager.route_config_name != + route_config_name_) { + if (route_config_watcher_ != nullptr) { + XdsRouteConfigResourceType::CancelWatch( + xds_client_.get(), route_config_name_, route_config_watcher_, + /*delay_unsubscription=*/ + !listener.http_connection_manager.route_config_name.empty()); + route_config_watcher_ = nullptr; + } + route_config_name_ = + std::move(listener.http_connection_manager.route_config_name); + if (!route_config_name_.empty()) { + current_virtual_host_.routes.clear(); + auto watcher = MakeRefCounted(Ref()); + route_config_watcher_ = watcher.get(); + XdsRouteConfigResourceType::StartWatch( + xds_client_.get(), route_config_name_, std::move(watcher)); + } + } + current_listener_ = std::move(listener); + if (route_config_name_.empty()) { + GPR_ASSERT( + current_listener_.http_connection_manager.rds_update.has_value()); + OnRouteConfigUpdate( + std::move(*current_listener_.http_connection_manager.rds_update)); + } else { + // HCM may contain newer filter config. We need to propagate the update as + // config selector to the channel + GenerateResult(); + } } namespace { diff --git a/src/core/ext/filters/fault_injection/fault_injection_filter.h b/src/core/ext/filters/fault_injection/fault_injection_filter.h index d9c08cc7011..611c3d7f45b 100644 --- a/src/core/ext/filters/fault_injection/fault_injection_filter.h +++ b/src/core/ext/filters/fault_injection/fault_injection_filter.h @@ -34,6 +34,10 @@ #include "src/core/lib/promise/arena_promise.h" #include "src/core/lib/transport/transport.h" +// Channel arg key for enabling parsing fault injection via method config. +#define GRPC_ARG_PARSE_FAULT_INJECTION_METHOD_CONFIG \ + "grpc.parse_fault_injection_method_config" + namespace grpc_core { // This channel filter is intended to be used by the dynamic filters, instead diff --git a/src/core/ext/filters/fault_injection/service_config_parser.cc b/src/core/ext/filters/fault_injection/service_config_parser.cc index 5670d549856..d9e9d229398 100644 --- a/src/core/ext/filters/fault_injection/service_config_parser.cc +++ b/src/core/ext/filters/fault_injection/service_config_parser.cc @@ -25,6 +25,7 @@ #include "absl/strings/str_cat.h" #include "absl/types/optional.h" +#include "src/core/ext/filters/fault_injection/fault_injection_filter.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" diff --git a/src/core/ext/filters/fault_injection/service_config_parser.h b/src/core/ext/filters/fault_injection/service_config_parser.h index 8961fa30b74..af266e0f1fd 100644 --- a/src/core/ext/filters/fault_injection/service_config_parser.h +++ b/src/core/ext/filters/fault_injection/service_config_parser.h @@ -39,10 +39,6 @@ #include "src/core/lib/json/json.h" #include "src/core/lib/service_config/service_config_parser.h" -// Channel arg key for enabling parsing fault injection via method config. -#define GRPC_ARG_PARSE_FAULT_INJECTION_METHOD_CONFIG \ - "grpc.internal.parse_fault_injection_method_config" - namespace grpc_core { class FaultInjectionMethodParsedConfig diff --git a/src/core/ext/xds/xds_cluster.cc b/src/core/ext/xds/xds_cluster.cc index 86ea34c1fb2..0287b967617 100644 --- a/src/core/ext/xds/xds_cluster.cc +++ b/src/core/ext/xds/xds_cluster.cc @@ -27,7 +27,6 @@ #include "absl/strings/str_cat.h" #include "absl/strings/str_join.h" #include "absl/strings/strip.h" -#include "absl/types/variant.h" #include "envoy/config/cluster/v3/circuit_breaker.upb.h" #include "envoy/config/cluster/v3/cluster.upb.h" #include "envoy/config/cluster/v3/cluster.upbdefs.h" @@ -54,7 +53,6 @@ #include "src/core/lib/gprpp/host_port.h" #include "src/core/lib/gprpp/time.h" #include "src/core/lib/gprpp/validation_errors.h" -#include "src/core/lib/matchers/matchers.h" namespace grpc_core { @@ -102,34 +100,39 @@ std::string XdsClusterResource::ToString() const { namespace { -CommonTlsContext UpstreamTlsContextParse( +absl::optional UpstreamTlsContextParse( const XdsResourceType::DecodeContext& context, const envoy_config_core_v3_TransportSocket* transport_socket, ValidationErrors* errors) { ValidationErrors::ScopedField field(errors, ".typed_config"); const auto* typed_config = envoy_config_core_v3_TransportSocket_typed_config(transport_socket); - auto extension = ExtractXdsExtension(context, typed_config, errors); - if (!extension.has_value()) return {}; - if (extension->type != + if (typed_config == nullptr) { + errors->AddError("field not present"); + return absl::nullopt; + } + absl::string_view type_url = absl::StripPrefix( + UpbStringToAbsl(google_protobuf_Any_type_url(typed_config)), + "type.googleapis.com/"); + if (type_url != "envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext") { ValidationErrors::ScopedField field(errors, ".type_url"); - errors->AddError("unsupported transport socket type"); - return {}; - } - absl::string_view* serialized_upstream_tls_context = - absl::get_if(&extension->value); - if (serialized_upstream_tls_context == nullptr) { - errors->AddError("can't decode UpstreamTlsContext"); - return {}; + errors->AddError( + absl::StrCat("unrecognized transport socket type: ", type_url)); + return absl::nullopt; } + ValidationErrors::ScopedField field2( + errors, + ".value[envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext]"); + absl::string_view serialized_upstream_tls_context = + UpbStringToAbsl(google_protobuf_Any_value(typed_config)); const auto* upstream_tls_context = envoy_extensions_transport_sockets_tls_v3_UpstreamTlsContext_parse( - serialized_upstream_tls_context->data(), - serialized_upstream_tls_context->size(), context.arena); + serialized_upstream_tls_context.data(), + serialized_upstream_tls_context.size(), context.arena); if (upstream_tls_context == nullptr) { errors->AddError("can't decode UpstreamTlsContext"); - return {}; + return absl::nullopt; } ValidationErrors::ScopedField field3(errors, ".common_tls_context"); const auto* common_tls_context_proto = @@ -391,8 +394,11 @@ absl::StatusOr CdsResourceParse( envoy_config_cluster_v3_Cluster_transport_socket(cluster); if (transport_socket != nullptr) { ValidationErrors::ScopedField field(&errors, ".transport_socket"); - cds_update.common_tls_context = + auto common_tls_context = UpstreamTlsContextParse(context, transport_socket, &errors); + if (common_tls_context.has_value()) { + cds_update.common_tls_context = std::move(*common_tls_context); + } } // Record LRS server name (if any). const envoy_config_core_v3_ConfigSource* lrs_server = diff --git a/src/core/ext/xds/xds_http_fault_filter.cc b/src/core/ext/xds/xds_http_fault_filter.cc index 11e7b1b38de..37ea234b2ce 100644 --- a/src/core/ext/xds/xds_http_fault_filter.cc +++ b/src/core/ext/xds/xds_http_fault_filter.cc @@ -24,6 +24,7 @@ #include #include +#include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" @@ -38,7 +39,6 @@ #include #include "src/core/ext/filters/fault_injection/fault_injection_filter.h" -#include "src/core/ext/filters/fault_injection/service_config_parser.h" #include "src/core/ext/xds/xds_common_types.h" #include "src/core/ext/xds/xds_http_filters.h" #include "src/core/lib/channel/channel_args.h" @@ -50,6 +50,9 @@ namespace grpc_core { +const char* kXdsHttpFaultFilterConfigName = + "envoy.extensions.filters.http.fault.v3.HTTPFault"; + namespace { uint32_t GetDenominator(const envoy_type_v3_FractionalPercent* fraction) { @@ -71,36 +74,13 @@ uint32_t GetDenominator(const envoy_type_v3_FractionalPercent* fraction) { return 100; } -} // namespace - -absl::string_view XdsHttpFaultFilter::ConfigProtoName() const { - return "envoy.extensions.filters.http.fault.v3.HTTPFault"; -} - -absl::string_view XdsHttpFaultFilter::OverrideConfigProtoName() const { - return ""; -} - -void XdsHttpFaultFilter::PopulateSymtab(upb_DefPool* symtab) const { - envoy_extensions_filters_http_fault_v3_HTTPFault_getmsgdef(symtab); -} - -absl::optional -XdsHttpFaultFilter::GenerateFilterConfig(XdsExtension extension, - upb_Arena* arena, - ValidationErrors* errors) const { - absl::string_view* serialized_filter_config = - absl::get_if(&extension.value); - if (serialized_filter_config == nullptr) { - errors->AddError("could not parse fault injection filter config"); - return absl::nullopt; - } +absl::StatusOr ParseHttpFaultIntoJson( + absl::string_view serialized_http_fault, upb_Arena* arena) { auto* http_fault = envoy_extensions_filters_http_fault_v3_HTTPFault_parse( - serialized_filter_config->data(), serialized_filter_config->size(), - arena); + serialized_http_fault.data(), serialized_http_fault.size(), arena); if (http_fault == nullptr) { - errors->AddError("could not parse fault injection filter config"); - return absl::nullopt; + return absl::InvalidArgumentError( + "could not parse fault injection filter config"); } // NOTE(lidiz): Here, we are manually translating the upb messages into the // JSON form of the filter config as part of method config, which will be @@ -115,7 +95,6 @@ XdsHttpFaultFilter::GenerateFilterConfig(XdsExtension extension, const auto* fault_abort = envoy_extensions_filters_http_fault_v3_HTTPFault_abort(http_fault); if (fault_abort != nullptr) { - ValidationErrors::ScopedField field(errors, ".abort"); grpc_status_code abort_grpc_status_code = GRPC_STATUS_OK; // Try if gRPC status code is set first int abort_grpc_status_code_raw = @@ -124,9 +103,8 @@ XdsHttpFaultFilter::GenerateFilterConfig(XdsExtension extension, if (abort_grpc_status_code_raw != 0) { if (!grpc_status_code_from_int(abort_grpc_status_code_raw, &abort_grpc_status_code)) { - ValidationErrors::ScopedField field(errors, ".grpc_status"); - errors->AddError(absl::StrCat("invalid gRPC status code: ", - abort_grpc_status_code_raw)); + return absl::InvalidArgumentError(absl::StrCat( + "invalid gRPC status code: ", abort_grpc_status_code_raw)); } } else { // if gRPC status code is empty, check http status @@ -153,25 +131,23 @@ XdsHttpFaultFilter::GenerateFilterConfig(XdsExtension extension, auto* percent = envoy_extensions_filters_http_fault_v3_FaultAbort_percentage( fault_abort); - if (percent != nullptr) { - fault_injection_policy_json["abortPercentageNumerator"] = - envoy_type_v3_FractionalPercent_numerator(percent); - fault_injection_policy_json["abortPercentageDenominator"] = - GetDenominator(percent); - } + fault_injection_policy_json["abortPercentageNumerator"] = + Json(envoy_type_v3_FractionalPercent_numerator(percent)); + fault_injection_policy_json["abortPercentageDenominator"] = + Json(GetDenominator(percent)); } // Section 2: Parse the delay injection config const auto* fault_delay = envoy_extensions_filters_http_fault_v3_HTTPFault_delay(http_fault); if (fault_delay != nullptr) { - ValidationErrors::ScopedField field(errors, ".delay"); // Parse the delay duration const auto* delay_duration = envoy_extensions_filters_common_fault_v3_FaultDelay_fixed_delay( fault_delay); if (delay_duration != nullptr) { - ValidationErrors::ScopedField field(errors, ".fixed_delay"); - Duration duration = ParseDuration(delay_duration, errors); + ValidationErrors errors; + Duration duration = ParseDuration(delay_duration, &errors); + if (!errors.ok()) return errors.status("fixed_delay"); fault_injection_policy_json["delay"] = duration.ToJsonString(); } // Set the headers if we enabled header delay injection control @@ -186,12 +162,10 @@ XdsHttpFaultFilter::GenerateFilterConfig(XdsExtension extension, auto* percent = envoy_extensions_filters_common_fault_v3_FaultDelay_percentage( fault_delay); - if (percent != nullptr) { - fault_injection_policy_json["delayPercentageNumerator"] = - envoy_type_v3_FractionalPercent_numerator(percent); - fault_injection_policy_json["delayPercentageDenominator"] = - GetDenominator(percent); - } + fault_injection_policy_json["delayPercentageNumerator"] = + Json(envoy_type_v3_FractionalPercent_numerator(percent)); + fault_injection_policy_json["delayPercentageDenominator"] = + Json(GetDenominator(percent)); } // Section 3: Parse the maximum active faults const auto* max_fault_wrapper = @@ -201,16 +175,38 @@ XdsHttpFaultFilter::GenerateFilterConfig(XdsExtension extension, fault_injection_policy_json["maxFaults"] = google_protobuf_UInt32Value_value(max_fault_wrapper); } - return FilterConfig{ConfigProtoName(), - std::move(fault_injection_policy_json)}; + return fault_injection_policy_json; +} + +} // namespace + +void XdsHttpFaultFilter::PopulateSymtab(upb_DefPool* symtab) const { + envoy_extensions_filters_http_fault_v3_HTTPFault_getmsgdef(symtab); +} + +absl::StatusOr +XdsHttpFaultFilter::GenerateFilterConfig(XdsExtension extension, + upb_Arena* arena) const { + absl::string_view* serialized_filter_config = + absl::get_if(&extension.value); + if (serialized_filter_config == nullptr) { + return absl::InvalidArgumentError( + "could not parse fault injection filter config"); + } + absl::StatusOr parse_result = + ParseHttpFaultIntoJson(*serialized_filter_config, arena); + if (!parse_result.ok()) { + return parse_result.status(); + } + return FilterConfig{kXdsHttpFaultFilterConfigName, std::move(*parse_result)}; } -absl::optional -XdsHttpFaultFilter::GenerateFilterConfigOverride( - XdsExtension extension, upb_Arena* arena, ValidationErrors* errors) const { +absl::StatusOr +XdsHttpFaultFilter::GenerateFilterConfigOverride(XdsExtension extension, + upb_Arena* arena) const { // HTTPFault filter has the same message type in HTTP connection manager's // filter config and in overriding filter config field. - return GenerateFilterConfig(std::move(extension), arena, errors); + return GenerateFilterConfig(std::move(extension), arena); } const grpc_channel_filter* XdsHttpFaultFilter::channel_filter() const { diff --git a/src/core/ext/xds/xds_http_fault_filter.h b/src/core/ext/xds/xds_http_fault_filter.h index a72a6c5cab5..f075f6ad8c3 100644 --- a/src/core/ext/xds/xds_http_fault_filter.h +++ b/src/core/ext/xds/xds_http_fault_filter.h @@ -20,8 +20,6 @@ #include #include "absl/status/statusor.h" -#include "absl/strings/string_view.h" -#include "absl/types/optional.h" #include "upb/arena.h" #include "upb/def.h" @@ -29,30 +27,40 @@ #include "src/core/ext/xds/xds_http_filters.h" #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/channel_fwd.h" -#include "src/core/lib/gprpp/validation_errors.h" namespace grpc_core { +extern const char* kXdsHttpFaultFilterConfigName; + class XdsHttpFaultFilter : public XdsHttpFilterImpl { public: - absl::string_view ConfigProtoName() const override; - absl::string_view OverrideConfigProtoName() const override; + // Overrides the PopulateSymtab method void PopulateSymtab(upb_DefPool* symtab) const override; - absl::optional GenerateFilterConfig( - XdsExtension extension, upb_Arena* arena, - ValidationErrors* errors) const override; - absl::optional GenerateFilterConfigOverride( - XdsExtension extension, upb_Arena* arena, - ValidationErrors* errors) const override; + + // Overrides the GenerateFilterConfig method + absl::StatusOr GenerateFilterConfig( + XdsExtension extension, upb_Arena* arena) const override; + + // Overrides the GenerateFilterConfigOverride method + absl::StatusOr GenerateFilterConfigOverride( + XdsExtension extension, upb_Arena* arena) const override; + + // Overrides the channel_filter method const grpc_channel_filter* channel_filter() const override; + + // Overrides the ModifyChannelArgs method ChannelArgs ModifyChannelArgs(const ChannelArgs& args) const override; + + // Overrides the GenerateServiceConfig method absl::StatusOr GenerateServiceConfig( const FilterConfig& hcm_filter_config, const FilterConfig* filter_config_override) const override; + bool IsSupportedOnClients() const override { return true; } + bool IsSupportedOnServers() const override { return false; } }; } // namespace grpc_core -#endif // GRPC_CORE_EXT_XDS_XDS_HTTP_FAULT_FILTER_H +#endif /* GRPC_CORE_EXT_XDS_XDS_HTTP_FAULT_FILTER_H */ diff --git a/src/core/ext/xds/xds_http_filters.cc b/src/core/ext/xds/xds_http_filters.cc index ac2089c4fde..a700dd597b6 100644 --- a/src/core/ext/xds/xds_http_filters.cc +++ b/src/core/ext/xds/xds_http_filters.cc @@ -23,65 +23,63 @@ #include #include +#include "absl/status/status.h" #include "absl/types/variant.h" #include "envoy/extensions/filters/http/router/v3/router.upb.h" #include "envoy/extensions/filters/http/router/v3/router.upbdefs.h" -#include - #include "src/core/ext/xds/xds_http_fault_filter.h" #include "src/core/ext/xds/xds_http_rbac_filter.h" namespace grpc_core { -// -// XdsHttpRouterFilter -// +const char* kXdsHttpRouterFilterConfigName = + "envoy.extensions.filters.http.router.v3.Router"; -absl::string_view XdsHttpRouterFilter::ConfigProtoName() const { - return "envoy.extensions.filters.http.router.v3.Router"; -} +namespace { -absl::string_view XdsHttpRouterFilter::OverrideConfigProtoName() const { - return ""; -} +class XdsHttpRouterFilter : public XdsHttpFilterImpl { + public: + void PopulateSymtab(upb_DefPool* symtab) const override { + envoy_extensions_filters_http_router_v3_Router_getmsgdef(symtab); + } -void XdsHttpRouterFilter::PopulateSymtab(upb_DefPool* symtab) const { - envoy_extensions_filters_http_router_v3_Router_getmsgdef(symtab); -} + absl::StatusOr GenerateFilterConfig( + XdsExtension extension, upb_Arena* arena) const override { + absl::string_view* serialized_filter_config = + absl::get_if(&extension.value); + if (serialized_filter_config == nullptr) { + return absl::InvalidArgumentError("could not parse router filter config"); + } + if (envoy_extensions_filters_http_router_v3_Router_parse( + serialized_filter_config->data(), serialized_filter_config->size(), + arena) == nullptr) { + return absl::InvalidArgumentError("could not parse router filter config"); + } + return FilterConfig{kXdsHttpRouterFilterConfigName, Json()}; + } -absl::optional -XdsHttpRouterFilter::GenerateFilterConfig(XdsExtension extension, - upb_Arena* arena, - ValidationErrors* errors) const { - absl::string_view* serialized_filter_config = - absl::get_if(&extension.value); - if (serialized_filter_config == nullptr) { - errors->AddError("could not parse router filter config"); - return absl::nullopt; + absl::StatusOr GenerateFilterConfigOverride( + XdsExtension /*extension*/, upb_Arena* /*arena*/) const override { + return absl::InvalidArgumentError( + "router filter does not support config override"); } - if (envoy_extensions_filters_http_router_v3_Router_parse( - serialized_filter_config->data(), serialized_filter_config->size(), - arena) == nullptr) { - errors->AddError("could not parse router filter config"); - return absl::nullopt; + + const grpc_channel_filter* channel_filter() const override { return nullptr; } + + // No-op. This will never be called, since channel_filter() returns null. + absl::StatusOr GenerateServiceConfig( + const FilterConfig& /*hcm_filter_config*/, + const FilterConfig* /*filter_config_override*/) const override { + return absl::UnimplementedError("router filter should never be called"); } - return FilterConfig{ConfigProtoName(), Json()}; -} -absl::optional -XdsHttpRouterFilter::GenerateFilterConfigOverride( - XdsExtension /*extension*/, upb_Arena* /*arena*/, - ValidationErrors* errors) const { - errors->AddError("router filter does not support config override"); - return absl::nullopt; -} + bool IsSupportedOnClients() const override { return true; } -// -// XdsHttpFilterRegistry -// + bool IsSupportedOnServers() const override { return true; } -namespace { + bool IsTerminalFilter() const override { return true; } +}; using FilterOwnerList = std::vector>; using FilterRegistryMap = std::map; @@ -92,13 +90,10 @@ FilterRegistryMap* g_filter_registry = nullptr; } // namespace void XdsHttpFilterRegistry::RegisterFilter( - std::unique_ptr filter) { - GPR_ASSERT(g_filter_registry->emplace(filter->ConfigProtoName(), filter.get()) - .second); - auto override_proto_name = filter->OverrideConfigProtoName(); - if (!override_proto_name.empty()) { - GPR_ASSERT( - g_filter_registry->emplace(override_proto_name, filter.get()).second); + std::unique_ptr filter, + const std::set& config_proto_type_names) { + for (auto config_proto_type_name : config_proto_type_names) { + (*g_filter_registry)[config_proto_type_name] = filter.get(); } g_filters->push_back(std::move(filter)); } @@ -116,14 +111,17 @@ void XdsHttpFilterRegistry::PopulateSymtab(upb_DefPool* symtab) { } } -void XdsHttpFilterRegistry::Init(bool register_builtins) { +void XdsHttpFilterRegistry::Init() { g_filters = new FilterOwnerList; g_filter_registry = new FilterRegistryMap; - if (register_builtins) { - RegisterFilter(std::make_unique()); - RegisterFilter(std::make_unique()); - RegisterFilter(std::make_unique()); - } + RegisterFilter(std::make_unique(), + {kXdsHttpRouterFilterConfigName}); + RegisterFilter(std::make_unique(), + {kXdsHttpFaultFilterConfigName}); + RegisterFilter(std::make_unique(), + {kXdsHttpRbacFilterConfigName}); + RegisterFilter(std::make_unique(), + {kXdsHttpRbacFilterConfigOverrideName}); } void XdsHttpFilterRegistry::Shutdown() { diff --git a/src/core/ext/xds/xds_http_filters.h b/src/core/ext/xds/xds_http_filters.h index 88d659905a0..3f5dd4724af 100644 --- a/src/core/ext/xds/xds_http_filters.h +++ b/src/core/ext/xds/xds_http_filters.h @@ -20,24 +20,24 @@ #include #include +#include #include -#include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" -#include "absl/types/optional.h" #include "upb/arena.h" #include "upb/def.h" #include "src/core/ext/xds/xds_common_types.h" #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/channel_fwd.h" -#include "src/core/lib/gprpp/validation_errors.h" #include "src/core/lib/json/json.h" namespace grpc_core { +extern const char* kXdsHttpRouterFilterConfigName; + class XdsHttpFilterImpl { public: struct FilterConfig { @@ -69,33 +69,25 @@ class XdsHttpFilterImpl { virtual ~XdsHttpFilterImpl() = default; - // Returns the top-level filter config proto message name. - virtual absl::string_view ConfigProtoName() const = 0; - - // Returns the override filter config proto message name. - // If empty, no override type is supported. - virtual absl::string_view OverrideConfigProtoName() const = 0; - // Loads the proto message into the upb symtab. virtual void PopulateSymtab(upb_DefPool* symtab) const = 0; // Generates a Config from the xDS filter config proto. // Used for the top-level config in the HCM HTTP filter list. - virtual absl::optional GenerateFilterConfig( - XdsExtension extension, upb_Arena* arena, - ValidationErrors* errors) const = 0; + virtual absl::StatusOr GenerateFilterConfig( + XdsExtension extension, upb_Arena* arena) const = 0; // Generates a Config from the xDS filter config proto. // Used for the typed_per_filter_config override in VirtualHost and Route. - virtual absl::optional GenerateFilterConfigOverride( - XdsExtension extension, upb_Arena* arena, - ValidationErrors* errors) const = 0; + virtual absl::StatusOr GenerateFilterConfigOverride( + XdsExtension extension, upb_Arena* arena) const = 0; // C-core channel filter implementation. virtual const grpc_channel_filter* channel_filter() const = 0; // Modifies channel args that may affect service config parsing (not // visible to the channel as a whole). + // Takes ownership of args. Caller takes ownership of return value. virtual ChannelArgs ModifyChannelArgs(const ChannelArgs& args) const { return args; } @@ -120,32 +112,11 @@ class XdsHttpFilterImpl { virtual bool IsTerminalFilter() const { return false; } }; -class XdsHttpRouterFilter : public XdsHttpFilterImpl { - public: - absl::string_view ConfigProtoName() const override; - absl::string_view OverrideConfigProtoName() const override; - void PopulateSymtab(upb_DefPool* symtab) const override; - absl::optional GenerateFilterConfig( - XdsExtension extension, upb_Arena* arena, - ValidationErrors* errors) const override; - absl::optional GenerateFilterConfigOverride( - XdsExtension extension, upb_Arena* arena, - ValidationErrors* errors) const override; - const grpc_channel_filter* channel_filter() const override { return nullptr; } - absl::StatusOr GenerateServiceConfig( - const FilterConfig& /*hcm_filter_config*/, - const FilterConfig* /*filter_config_override*/) const override { - // This will never be called, since channel_filter() returns null. - return absl::UnimplementedError("router filter should never be called"); - } - bool IsSupportedOnClients() const override { return true; } - bool IsSupportedOnServers() const override { return true; } - bool IsTerminalFilter() const override { return true; } -}; - class XdsHttpFilterRegistry { public: - static void RegisterFilter(std::unique_ptr filter); + static void RegisterFilter( + std::unique_ptr filter, + const std::set& config_proto_type_names); static const XdsHttpFilterImpl* GetFilterForType( absl::string_view proto_type_name); @@ -153,10 +124,10 @@ class XdsHttpFilterRegistry { static void PopulateSymtab(upb_DefPool* symtab); // Global init and shutdown. - static void Init(bool register_builtins = true); + static void Init(); static void Shutdown(); }; } // namespace grpc_core -#endif // GRPC_CORE_EXT_XDS_XDS_HTTP_FILTERS_H +#endif /* GRPC_CORE_EXT_XDS_XDS_HTTP_FILTERS_H */ diff --git a/src/core/ext/xds/xds_http_rbac_filter.cc b/src/core/ext/xds/xds_http_rbac_filter.cc index bf4cd463045..6f2d722357c 100644 --- a/src/core/ext/xds/xds_http_rbac_filter.cc +++ b/src/core/ext/xds/xds_http_rbac_filter.cc @@ -25,9 +25,12 @@ #include #include #include +#include +#include "absl/status/status.h" #include "absl/strings/match.h" #include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" #include "absl/strings/string_view.h" #include "absl/types/variant.h" #include "envoy/config/core/v3/address.upb.h" @@ -51,6 +54,12 @@ namespace grpc_core { +const char* kXdsHttpRbacFilterConfigName = + "envoy.extensions.filters.http.rbac.v3.RBAC"; + +const char* kXdsHttpRbacFilterConfigOverrideName = + "envoy.extensions.filters.http.rbac.v3.RBACPerRoute"; + namespace { Json ParseRegexMatcherToJson( @@ -65,20 +74,18 @@ Json ParseInt64RangeToJson(const envoy_type_v3_Int64Range* range) { {"end", envoy_type_v3_Int64Range_end(range)}}; } -Json ParseHeaderMatcherToJson(const envoy_config_route_v3_HeaderMatcher* header, - ValidationErrors* errors) { +absl::StatusOr ParseHeaderMatcherToJson( + const envoy_config_route_v3_HeaderMatcher* header) { Json::Object header_json; - { - ValidationErrors::ScopedField field(errors, ".name"); - std::string name = - UpbStringToStdString(envoy_config_route_v3_HeaderMatcher_name(header)); - if (name == ":scheme") { - errors->AddError("':scheme' not allowed in header"); - } else if (absl::StartsWith(name, "grpc-")) { - errors->AddError("'grpc-' prefixes not allowed in header"); - } - header_json.emplace("name", std::move(name)); + std::vector errors; + std::string name = + UpbStringToStdString(envoy_config_route_v3_HeaderMatcher_name(header)); + if (name == ":scheme") { + errors.emplace_back("':scheme' not allowed in header"); + } else if (absl::StartsWith(name, "grpc-")) { + errors.emplace_back("'grpc-' prefixes not allowed in header"); } + header_json.emplace("name", std::move(name)); if (envoy_config_route_v3_HeaderMatcher_has_exact_match(header)) { header_json.emplace( "exactMatch", @@ -114,16 +121,19 @@ Json ParseHeaderMatcherToJson(const envoy_config_route_v3_HeaderMatcher* header, UpbStringToStdString( envoy_config_route_v3_HeaderMatcher_contains_match(header))); } else { - errors->AddError("invalid route header matcher specified"); + errors.emplace_back("Invalid route header matcher specified."); + } + if (!errors.empty()) { + return absl::InvalidArgumentError(absl::StrCat( + "errors parsing HeaderMatcher: [", absl::StrJoin(errors, "; "), "]")); } header_json.emplace("invertMatch", envoy_config_route_v3_HeaderMatcher_invert_match(header)); return header_json; } -Json ParseStringMatcherToJson( - const envoy_type_matcher_v3_StringMatcher* matcher, - ValidationErrors* errors) { +absl::StatusOr ParseStringMatcherToJson( + const envoy_type_matcher_v3_StringMatcher* matcher) { Json::Object json; if (envoy_type_matcher_v3_StringMatcher_has_exact(matcher)) { json.emplace("exact", @@ -146,23 +156,26 @@ Json ParseStringMatcherToJson( UpbStringToStdString( envoy_type_matcher_v3_StringMatcher_contains(matcher))); } else { - errors->AddError("invalid match pattern"); + return absl::InvalidArgumentError("StringMatcher: Invalid match pattern"); } json.emplace("ignoreCase", envoy_type_matcher_v3_StringMatcher_ignore_case(matcher)); return json; } -Json ParsePathMatcherToJson(const envoy_type_matcher_v3_PathMatcher* matcher, - ValidationErrors* errors) { - ValidationErrors::ScopedField field(errors, ".path"); +absl::StatusOr ParsePathMatcherToJson( + const envoy_type_matcher_v3_PathMatcher* matcher) { const auto* path = envoy_type_matcher_v3_PathMatcher_path(matcher); if (path == nullptr) { - errors->AddError("field not present"); - return Json(); + return absl::InvalidArgumentError("PathMatcher has empty path"); } - Json path_json = ParseStringMatcherToJson(path, errors); - return Json::Object{{"path", std::move(path_json)}}; + Json::Object json; + auto path_json = ParseStringMatcherToJson(path); + if (!path_json.ok()) { + return path_json; + } + json.emplace("path", std::move(*path_json)); + return json; } Json ParseUInt32ValueToJson(const google_protobuf_UInt32Value* value) { @@ -192,49 +205,65 @@ Json ParseMetadataMatcherToJson( return json; } -Json ParsePermissionToJson(const envoy_config_rbac_v3_Permission* permission, - ValidationErrors* errors) { +absl::StatusOr ParsePermissionToJson( + const envoy_config_rbac_v3_Permission* permission) { Json::Object permission_json; // Helper function to parse Permission::Set to JSON. Used by `and_rules` and // `or_rules`. auto parse_permission_set_to_json = - [errors](const envoy_config_rbac_v3_Permission_Set* set) -> Json { + [](const envoy_config_rbac_v3_Permission_Set* set) + -> absl::StatusOr { + std::vector errors; Json::Array rules_json; size_t size; const envoy_config_rbac_v3_Permission* const* rules = envoy_config_rbac_v3_Permission_Set_rules(set, &size); for (size_t i = 0; i < size; ++i) { - ValidationErrors::ScopedField field(errors, - absl::StrCat(".rules[", i, "]")); - Json permission_json = ParsePermissionToJson(rules[i], errors); - rules_json.emplace_back(std::move(permission_json)); + auto permission_json = ParsePermissionToJson(rules[i]); + if (!permission_json.ok()) { + errors.emplace_back(permission_json.status().message()); + } else { + rules_json.emplace_back(std::move(*permission_json)); + } + } + if (!errors.empty()) { + return absl::InvalidArgumentError(absl::StrCat( + "errors parsing Set: [", absl::StrJoin(errors, "; "), "]")); } return Json::Object({{"rules", std::move(rules_json)}}); }; if (envoy_config_rbac_v3_Permission_has_and_rules(permission)) { - ValidationErrors::ScopedField field(errors, ".and_permission"); const auto* and_rules = envoy_config_rbac_v3_Permission_and_rules(permission); - Json permission_set_json = parse_permission_set_to_json(and_rules); - permission_json.emplace("andRules", std::move(permission_set_json)); + auto permission_set_json = parse_permission_set_to_json(and_rules); + if (!permission_set_json.ok()) { + return permission_set_json; + } + permission_json.emplace("andRules", std::move(*permission_set_json)); } else if (envoy_config_rbac_v3_Permission_has_or_rules(permission)) { - ValidationErrors::ScopedField field(errors, ".or_permission"); const auto* or_rules = envoy_config_rbac_v3_Permission_or_rules(permission); - Json permission_set_json = parse_permission_set_to_json(or_rules); - permission_json.emplace("orRules", std::move(permission_set_json)); + auto permission_set_json = parse_permission_set_to_json(or_rules); + if (!permission_set_json.ok()) { + return permission_set_json; + } + permission_json.emplace("orRules", std::move(*permission_set_json)); } else if (envoy_config_rbac_v3_Permission_has_any(permission)) { permission_json.emplace("any", envoy_config_rbac_v3_Permission_any(permission)); } else if (envoy_config_rbac_v3_Permission_has_header(permission)) { - ValidationErrors::ScopedField field(errors, ".header"); - Json header_json = ParseHeaderMatcherToJson( - envoy_config_rbac_v3_Permission_header(permission), errors); - permission_json.emplace("header", std::move(header_json)); + auto header_json = ParseHeaderMatcherToJson( + envoy_config_rbac_v3_Permission_header(permission)); + if (!header_json.ok()) { + return header_json; + } + permission_json.emplace("header", std::move(*header_json)); } else if (envoy_config_rbac_v3_Permission_has_url_path(permission)) { - ValidationErrors::ScopedField field(errors, ".url_path"); - Json url_path_json = ParsePathMatcherToJson( - envoy_config_rbac_v3_Permission_url_path(permission), errors); - permission_json.emplace("urlPath", std::move(url_path_json)); + auto url_path_json = ParsePathMatcherToJson( + envoy_config_rbac_v3_Permission_url_path(permission)); + if (!url_path_json.ok()) { + return url_path_json; + } + permission_json.emplace("urlPath", std::move(*url_path_json)); } else if (envoy_config_rbac_v3_Permission_has_destination_ip(permission)) { permission_json.emplace( "destinationIp", @@ -249,53 +278,69 @@ Json ParsePermissionToJson(const envoy_config_rbac_v3_Permission* permission, "metadata", ParseMetadataMatcherToJson( envoy_config_rbac_v3_Permission_metadata(permission))); } else if (envoy_config_rbac_v3_Permission_has_not_rule(permission)) { - ValidationErrors::ScopedField field(errors, ".not_rule"); - Json not_rule_json = ParsePermissionToJson( - envoy_config_rbac_v3_Permission_not_rule(permission), errors); - permission_json.emplace("notRule", std::move(not_rule_json)); + auto not_rule_json = ParsePermissionToJson( + envoy_config_rbac_v3_Permission_not_rule(permission)); + if (!not_rule_json.ok()) { + return not_rule_json; + } + permission_json.emplace("notRule", std::move(*not_rule_json)); } else if (envoy_config_rbac_v3_Permission_has_requested_server_name( permission)) { - ValidationErrors::ScopedField field(errors, ".requested_server_name"); - Json requested_server_name_json = ParseStringMatcherToJson( - envoy_config_rbac_v3_Permission_requested_server_name(permission), - errors); + auto requested_server_name_json = ParseStringMatcherToJson( + envoy_config_rbac_v3_Permission_requested_server_name(permission)); + if (!requested_server_name_json.ok()) { + return requested_server_name_json; + } permission_json.emplace("requestedServerName", - std::move(requested_server_name_json)); + std::move(*requested_server_name_json)); } else { - errors->AddError("invalid rule"); + return absl::InvalidArgumentError("Permission: Invalid rule"); } return permission_json; } -Json ParsePrincipalToJson(const envoy_config_rbac_v3_Principal* principal, - ValidationErrors* errors) { +absl::StatusOr ParsePrincipalToJson( + const envoy_config_rbac_v3_Principal* principal) { Json::Object principal_json; // Helper function to parse Principal::Set to JSON. Used by `and_ids` and // `or_ids`. auto parse_principal_set_to_json = - [errors](const envoy_config_rbac_v3_Principal_Set* set) -> Json { + [](const envoy_config_rbac_v3_Principal_Set* set) + -> absl::StatusOr { + Json::Object json; + std::vector errors; Json::Array ids_json; size_t size; const envoy_config_rbac_v3_Principal* const* ids = envoy_config_rbac_v3_Principal_Set_ids(set, &size); for (size_t i = 0; i < size; ++i) { - ValidationErrors::ScopedField field(errors, - absl::StrCat(".ids[", i, "]")); - Json principal_json = ParsePrincipalToJson(ids[i], errors); - ids_json.emplace_back(std::move(principal_json)); + auto principal_json = ParsePrincipalToJson(ids[i]); + if (!principal_json.ok()) { + errors.emplace_back(principal_json.status().message()); + } else { + ids_json.emplace_back(std::move(*principal_json)); + } + } + if (!errors.empty()) { + return absl::InvalidArgumentError(absl::StrCat( + "errors parsing Set: [", absl::StrJoin(errors, "; "), "]")); } return Json::Object({{"ids", std::move(ids_json)}}); }; if (envoy_config_rbac_v3_Principal_has_and_ids(principal)) { - ValidationErrors::ScopedField field(errors, ".and_ids"); const auto* and_rules = envoy_config_rbac_v3_Principal_and_ids(principal); - Json principal_set_json = parse_principal_set_to_json(and_rules); - principal_json.emplace("andIds", std::move(principal_set_json)); + auto principal_set_json = parse_principal_set_to_json(and_rules); + if (!principal_set_json.ok()) { + return principal_set_json; + } + principal_json.emplace("andIds", std::move(*principal_set_json)); } else if (envoy_config_rbac_v3_Principal_has_or_ids(principal)) { - ValidationErrors::ScopedField field(errors, ".or_ids"); const auto* or_rules = envoy_config_rbac_v3_Principal_or_ids(principal); - Json principal_set_json = parse_principal_set_to_json(or_rules); - principal_json.emplace("orIds", std::move(principal_set_json)); + auto principal_set_json = parse_principal_set_to_json(or_rules); + if (!principal_set_json.ok()) { + return principal_set_json; + } + principal_json.emplace("orIds", std::move(*principal_set_json)); } else if (envoy_config_rbac_v3_Principal_has_any(principal)) { principal_json.emplace("any", envoy_config_rbac_v3_Principal_any(principal)); @@ -307,12 +352,12 @@ Json ParsePrincipalToJson(const envoy_config_rbac_v3_Principal* principal, envoy_config_rbac_v3_Principal_Authenticated_principal_name( envoy_config_rbac_v3_Principal_authenticated(principal)); if (principal_name != nullptr) { - ValidationErrors::ScopedField field(errors, - ".authenticated.principal_name"); - Json principal_name_json = - ParseStringMatcherToJson(principal_name, errors); + auto principal_name_json = ParseStringMatcherToJson(principal_name); + if (!principal_name_json.ok()) { + return principal_name_json; + } authenticated_json->emplace("principalName", - std::move(principal_name_json)); + std::move(*principal_name_json)); } } else if (envoy_config_rbac_v3_Principal_has_source_ip(principal)) { principal_json.emplace( @@ -328,71 +373,84 @@ Json ParsePrincipalToJson(const envoy_config_rbac_v3_Principal* principal, "remoteIp", ParseCidrRangeToJson( envoy_config_rbac_v3_Principal_remote_ip(principal))); } else if (envoy_config_rbac_v3_Principal_has_header(principal)) { - ValidationErrors::ScopedField field(errors, ".header"); - Json header_json = ParseHeaderMatcherToJson( - envoy_config_rbac_v3_Principal_header(principal), errors); - principal_json.emplace("header", std::move(header_json)); + auto header_json = ParseHeaderMatcherToJson( + envoy_config_rbac_v3_Principal_header(principal)); + if (!header_json.ok()) { + return header_json; + } + principal_json.emplace("header", std::move(*header_json)); } else if (envoy_config_rbac_v3_Principal_has_url_path(principal)) { - ValidationErrors::ScopedField field(errors, ".url_path"); - Json url_path_json = ParsePathMatcherToJson( - envoy_config_rbac_v3_Principal_url_path(principal), errors); - principal_json.emplace("urlPath", std::move(url_path_json)); + auto url_path_json = ParsePathMatcherToJson( + envoy_config_rbac_v3_Principal_url_path(principal)); + if (!url_path_json.ok()) { + return url_path_json; + } + principal_json.emplace("urlPath", std::move(*url_path_json)); } else if (envoy_config_rbac_v3_Principal_has_metadata(principal)) { principal_json.emplace( "metadata", ParseMetadataMatcherToJson( envoy_config_rbac_v3_Principal_metadata(principal))); } else if (envoy_config_rbac_v3_Principal_has_not_id(principal)) { - ValidationErrors::ScopedField field(errors, ".not_id"); - Json not_id_json = ParsePrincipalToJson( - envoy_config_rbac_v3_Principal_not_id(principal), errors); - principal_json.emplace("notId", std::move(not_id_json)); + auto not_id_json = + ParsePrincipalToJson(envoy_config_rbac_v3_Principal_not_id(principal)); + if (!not_id_json.ok()) { + return not_id_json; + } + principal_json.emplace("notId", std::move(*not_id_json)); } else { - errors->AddError("invalid rule"); + return absl::InvalidArgumentError("Principal: Invalid rule"); } return principal_json; } -Json ParsePolicyToJson(const envoy_config_rbac_v3_Policy* policy, - ValidationErrors* errors) { +absl::StatusOr ParsePolicyToJson( + const envoy_config_rbac_v3_Policy* policy) { Json::Object policy_json; + std::vector errors; size_t size; Json::Array permissions_json; const envoy_config_rbac_v3_Permission* const* permissions = envoy_config_rbac_v3_Policy_permissions(policy, &size); for (size_t i = 0; i < size; ++i) { - ValidationErrors::ScopedField field(errors, - absl::StrCat(".permissions[", i, "]")); - Json permission_json = ParsePermissionToJson(permissions[i], errors); - permissions_json.emplace_back(std::move(permission_json)); + auto permission_json = ParsePermissionToJson(permissions[i]); + if (!permission_json.ok()) { + errors.emplace_back(permission_json.status().message()); + } else { + permissions_json.emplace_back(std::move(*permission_json)); + } } policy_json.emplace("permissions", std::move(permissions_json)); Json::Array principals_json; const envoy_config_rbac_v3_Principal* const* principals = envoy_config_rbac_v3_Policy_principals(policy, &size); for (size_t i = 0; i < size; ++i) { - ValidationErrors::ScopedField field(errors, - absl::StrCat(".principals[", i, "]")); - Json principal_json = ParsePrincipalToJson(principals[i], errors); - principals_json.emplace_back(std::move(principal_json)); + auto principal_json = ParsePrincipalToJson(principals[i]); + if (!principal_json.ok()) { + errors.emplace_back(principal_json.status().message()); + } else { + principals_json.emplace_back(std::move(*principal_json)); + } } policy_json.emplace("principals", std::move(principals_json)); if (envoy_config_rbac_v3_Policy_has_condition(policy)) { - ValidationErrors::ScopedField field(errors, ".condition"); - errors->AddError("condition not supported"); + errors.emplace_back("Policy: condition not supported"); } if (envoy_config_rbac_v3_Policy_has_checked_condition(policy)) { - ValidationErrors::ScopedField field(errors, ".checked_condition"); - errors->AddError("checked condition not supported"); + errors.emplace_back("Policy: checked condition not supported"); + } + if (!errors.empty()) { + return absl::InvalidArgumentError(absl::StrCat( + "errors parsing Policy: [", absl::StrJoin(errors, "; "), "]")); } return policy_json; } -Json ParseHttpRbacToJson(const envoy_extensions_filters_http_rbac_v3_RBAC* rbac, - ValidationErrors* errors) { +absl::StatusOr ParseHttpRbacToJson( + const envoy_extensions_filters_http_rbac_v3_RBAC* rbac) { Json::Object rbac_json; + std::vector errors; const auto* rules = envoy_extensions_filters_http_rbac_v3_RBAC_rules(rbac); if (rules != nullptr) { - ValidationErrors::ScopedField field(errors, ".rules"); int action = envoy_config_rbac_v3_RBAC_action(rules); // Treat Log action as RBAC being absent if (action == envoy_config_rbac_v3_RBAC_LOG) { @@ -408,82 +466,89 @@ Json ParseHttpRbacToJson(const envoy_extensions_filters_http_rbac_v3_RBAC* rbac, if (entry == nullptr) { break; } - absl::string_view key = - UpbStringToAbsl(envoy_config_rbac_v3_RBAC_PoliciesEntry_key(entry)); - ValidationErrors::ScopedField field( - errors, absl::StrCat(".policies[", key, "]")); - Json policy = ParsePolicyToJson( - envoy_config_rbac_v3_RBAC_PoliciesEntry_value(entry), errors); - policies_object.emplace(std::string(key), std::move(policy)); + auto policy = ParsePolicyToJson( + envoy_config_rbac_v3_RBAC_PoliciesEntry_value(entry)); + if (!policy.ok()) { + errors.emplace_back(absl::StrCat( + "RBAC PoliciesEntry key:", + UpbStringToStdString( + envoy_config_rbac_v3_RBAC_PoliciesEntry_key(entry)), + " error:", policy.status().message())); + } else { + policies_object.emplace( + UpbStringToStdString( + envoy_config_rbac_v3_RBAC_PoliciesEntry_key(entry)), + std::move(*policy)); + } } inner_rbac_json.emplace("policies", std::move(policies_object)); } rbac_json.emplace("rules", std::move(inner_rbac_json)); } + if (!errors.empty()) { + return absl::InvalidArgumentError(absl::StrCat( + "errors parsing RBAC: [", absl::StrJoin(errors, "; "), "]")); + } return rbac_json; } } // namespace -absl::string_view XdsHttpRbacFilter::ConfigProtoName() const { - return "envoy.extensions.filters.http.rbac.v3.RBAC"; -} - -absl::string_view XdsHttpRbacFilter::OverrideConfigProtoName() const { - return "envoy.extensions.filters.http.rbac.v3.RBACPerRoute"; -} - void XdsHttpRbacFilter::PopulateSymtab(upb_DefPool* symtab) const { envoy_extensions_filters_http_rbac_v3_RBAC_getmsgdef(symtab); } -absl::optional +absl::StatusOr XdsHttpRbacFilter::GenerateFilterConfig(XdsExtension extension, - upb_Arena* arena, - ValidationErrors* errors) const { + upb_Arena* arena) const { absl::string_view* serialized_filter_config = absl::get_if(&extension.value); if (serialized_filter_config == nullptr) { - errors->AddError("could not parse HTTP RBAC filter config"); - return absl::nullopt; + return absl::InvalidArgumentError( + "could not parse HTTP RBAC filter config"); } auto* rbac = envoy_extensions_filters_http_rbac_v3_RBAC_parse( serialized_filter_config->data(), serialized_filter_config->size(), arena); if (rbac == nullptr) { - errors->AddError("could not parse HTTP RBAC filter config"); - return absl::nullopt; + return absl::InvalidArgumentError( + "could not parse HTTP RBAC filter config"); } - return FilterConfig{ConfigProtoName(), ParseHttpRbacToJson(rbac, errors)}; + absl::StatusOr rbac_json = ParseHttpRbacToJson(rbac); + if (!rbac_json.ok()) { + return rbac_json.status(); + } + return FilterConfig{kXdsHttpRbacFilterConfigName, std::move(*rbac_json)}; } -absl::optional -XdsHttpRbacFilter::GenerateFilterConfigOverride( - XdsExtension extension, upb_Arena* arena, ValidationErrors* errors) const { +absl::StatusOr +XdsHttpRbacFilter::GenerateFilterConfigOverride(XdsExtension extension, + upb_Arena* arena) const { absl::string_view* serialized_filter_config = absl::get_if(&extension.value); if (serialized_filter_config == nullptr) { - errors->AddError("could not parse RBACPerRoute"); - return absl::nullopt; + return absl::InvalidArgumentError("could not parse RBACPerRoute"); } auto* rbac_per_route = envoy_extensions_filters_http_rbac_v3_RBACPerRoute_parse( serialized_filter_config->data(), serialized_filter_config->size(), arena); if (rbac_per_route == nullptr) { - errors->AddError("could not parse RBACPerRoute"); - return absl::nullopt; + return absl::InvalidArgumentError("could not parse RBACPerRoute"); } - Json rbac_json; + absl::StatusOr rbac_json; const auto* rbac = envoy_extensions_filters_http_rbac_v3_RBACPerRoute_rbac(rbac_per_route); if (rbac == nullptr) { rbac_json = Json::Object(); } else { - ValidationErrors::ScopedField field(errors, ".rbac"); - rbac_json = ParseHttpRbacToJson(rbac, errors); + rbac_json = ParseHttpRbacToJson(rbac); + if (!rbac_json.ok()) { + return rbac_json.status(); + } } - return FilterConfig{OverrideConfigProtoName(), std::move(rbac_json)}; + return FilterConfig{kXdsHttpRbacFilterConfigOverrideName, + std::move(*rbac_json)}; } const grpc_channel_filter* XdsHttpRbacFilter::channel_filter() const { diff --git a/src/core/ext/xds/xds_http_rbac_filter.h b/src/core/ext/xds/xds_http_rbac_filter.h index a582f06f625..89602a3c61b 100644 --- a/src/core/ext/xds/xds_http_rbac_filter.h +++ b/src/core/ext/xds/xds_http_rbac_filter.h @@ -20,8 +20,6 @@ #include #include "absl/status/statusor.h" -#include "absl/strings/string_view.h" -#include "absl/types/optional.h" #include "upb/arena.h" #include "upb/def.h" @@ -29,27 +27,32 @@ #include "src/core/ext/xds/xds_http_filters.h" #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/channel_fwd.h" -#include "src/core/lib/gprpp/validation_errors.h" namespace grpc_core { +extern const char* kXdsHttpRbacFilterConfigName; +extern const char* kXdsHttpRbacFilterConfigOverrideName; + class XdsHttpRbacFilter : public XdsHttpFilterImpl { public: - absl::string_view ConfigProtoName() const override; - absl::string_view OverrideConfigProtoName() const override; void PopulateSymtab(upb_DefPool* symtab) const override; - absl::optional GenerateFilterConfig( - XdsExtension extension, upb_Arena* arena, - ValidationErrors* errors) const override; - absl::optional GenerateFilterConfigOverride( - XdsExtension extension, upb_Arena* arena, - ValidationErrors* errors) const override; + + absl::StatusOr GenerateFilterConfig( + XdsExtension extension, upb_Arena* arena) const override; + + absl::StatusOr GenerateFilterConfigOverride( + XdsExtension extension, upb_Arena* arena) const override; + const grpc_channel_filter* channel_filter() const override; + ChannelArgs ModifyChannelArgs(const ChannelArgs& args) const override; + absl::StatusOr GenerateServiceConfig( const FilterConfig& hcm_filter_config, const FilterConfig* filter_config_override) const override; + bool IsSupportedOnClients() const override { return false; } + bool IsSupportedOnServers() const override { return true; } }; diff --git a/src/core/ext/xds/xds_listener.cc b/src/core/ext/xds/xds_listener.cc index 6fbe1eebf28..1270d80f455 100644 --- a/src/core/ext/xds/xds_listener.cc +++ b/src/core/ext/xds/xds_listener.cc @@ -28,6 +28,7 @@ #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/str_join.h" +#include "absl/strings/strip.h" #include "envoy/config/core/v3/address.upb.h" #include "envoy/config/core/v3/base.upb.h" #include "envoy/config/core/v3/config_source.upb.h" @@ -55,29 +56,42 @@ #include "src/core/lib/address_utils/sockaddr_utils.h" #include "src/core/lib/debug/trace.h" #include "src/core/lib/gprpp/host_port.h" -#include "src/core/lib/gprpp/match.h" +#include "src/core/lib/gprpp/status_helper.h" #include "src/core/lib/gprpp/validation_errors.h" +#include "src/core/lib/iomgr/error.h" #include "src/core/lib/iomgr/sockaddr.h" -#include "src/core/lib/matchers/matchers.h" namespace grpc_core { +// +// XdsListenerResource::DownstreamTlsContext +// + +std::string XdsListenerResource::DownstreamTlsContext::ToString() const { + return absl::StrFormat("common_tls_context=%s, require_client_certificate=%s", + common_tls_context.ToString(), + require_client_certificate ? "true" : "false"); +} + +bool XdsListenerResource::DownstreamTlsContext::Empty() const { + return common_tls_context.Empty(); +} + // // XdsListenerResource::HttpConnectionManager // std::string XdsListenerResource::HttpConnectionManager::ToString() const { std::vector contents; - contents.push_back(Match( - route_config, - [](const std::string& rds_name) { - return absl::StrCat("rds_name=", rds_name); - }, - [](const XdsRouteConfigResource& route_config) { - return absl::StrCat("route_config=", route_config.ToString()); - })); - contents.push_back(absl::StrCat("http_max_stream_duration=", - http_max_stream_duration.ToString())); + contents.push_back(absl::StrFormat( + "route_config_name=%s", + !route_config_name.empty() ? route_config_name.c_str() : "")); + contents.push_back(absl::StrFormat("http_max_stream_duration=%s", + http_max_stream_duration.ToString())); + if (rds_update.has_value()) { + contents.push_back( + absl::StrFormat("rds_update=%s", rds_update->ToString())); + } if (!http_filters.empty()) { std::vector filter_strings; for (const auto& http_filter : http_filters) { @@ -90,7 +104,7 @@ std::string XdsListenerResource::HttpConnectionManager::ToString() const { } // -// XdsListenerResource::HttpConnectionManager::HttpFilter +// XdsListenerResource::HttpFilter // std::string XdsListenerResource::HttpConnectionManager::HttpFilter::ToString() @@ -98,20 +112,6 @@ std::string XdsListenerResource::HttpConnectionManager::HttpFilter::ToString() return absl::StrCat("{name=", name, ", config=", config.ToString(), "}"); } -// -// XdsListenerResource::DownstreamTlsContext -// - -std::string XdsListenerResource::DownstreamTlsContext::ToString() const { - return absl::StrFormat("common_tls_context=%s, require_client_certificate=%s", - common_tls_context.ToString(), - require_client_certificate ? "true" : "false"); -} - -bool XdsListenerResource::DownstreamTlsContext::Empty() const { - return common_tls_context.Empty(); -} - // // XdsListenerResource::FilterChainData // @@ -243,36 +243,26 @@ std::string XdsListenerResource::FilterChainMap::ToString() const { } // -// XdsListenerResource::TcpListener +// XdsListenerResource // -std::string XdsListenerResource::TcpListener::ToString() const { +std::string XdsListenerResource::ToString() const { std::vector contents; - contents.push_back(absl::StrCat("address=", address)); - contents.push_back( - absl::StrCat("filter_chain_map=", filter_chain_map.ToString())); - if (default_filter_chain.has_value()) { - contents.push_back(absl::StrCat("default_filter_chain=", - default_filter_chain->ToString())); + if (type == ListenerType::kTcpListener) { + contents.push_back(absl::StrCat("address=", address)); + contents.push_back( + absl::StrCat("filter_chain_map=", filter_chain_map.ToString())); + if (default_filter_chain.has_value()) { + contents.push_back(absl::StrCat("default_filter_chain=", + default_filter_chain->ToString())); + } + } else if (type == ListenerType::kHttpApiListener) { + contents.push_back(absl::StrFormat("http_connection_manager=%s", + http_connection_manager.ToString())); } return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); } -// -// XdsListenerResource -// - -std::string XdsListenerResource::ToString() const { - return Match( - listener, - [](const HttpConnectionManager& hcm) { - return absl::StrCat("{http_connection_manager=", hcm.ToString(), "}"); - }, - [](const TcpListener& tcp) { - return absl::StrCat("{tcp_listener=", tcp.ToString(), "}"); - }); -} - // // XdsListenerResourceType // @@ -296,159 +286,143 @@ void MaybeLogHttpConnectionManager( } } -XdsListenerResource::HttpConnectionManager HttpConnectionManagerParse( +absl::StatusOr +HttpConnectionManagerParse( bool is_client, const XdsResourceType::DecodeContext& context, - XdsExtension extension, ValidationErrors* errors) { - if (extension.type != - "envoy.extensions.filters.network.http_connection_manager.v3" - ".HttpConnectionManager") { - errors->AddError("unsupported filter type"); - return {}; - } - auto* serialized_hcm_config = - absl::get_if(&extension.value); - if (serialized_hcm_config == nullptr) { - errors->AddError("could not parse HttpConnectionManager config"); - return {}; - } - const auto* http_connection_manager_proto = - envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_parse( - serialized_hcm_config->data(), serialized_hcm_config->size(), - context.arena); - if (http_connection_manager_proto == nullptr) { - errors->AddError("could not parse HttpConnectionManager config"); - return {}; - } + const envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager* + http_connection_manager_proto) { MaybeLogHttpConnectionManager(context, http_connection_manager_proto); + std::vector errors; XdsListenerResource::HttpConnectionManager http_connection_manager; - // xff_num_trusted_hops -- must be zero as per + // NACK a non-zero `xff_num_trusted_hops` and a `non-empty + // original_ip_detection_extensions` as mentioned in // https://github.com/grpc/proposal/blob/master/A41-xds-rbac.md if (envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_xff_num_trusted_hops( http_connection_manager_proto) != 0) { - ValidationErrors::ScopedField field(errors, ".xff_num_trusted_hops"); - errors->AddError("must be zero"); + errors.emplace_back("'xff_num_trusted_hops' must be zero"); } - // original_ip_detection_extensions -- must be empty as per - // https://github.com/grpc/proposal/blob/master/A41-xds-rbac.md if (envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_has_original_ip_detection_extensions( http_connection_manager_proto)) { - ValidationErrors::ScopedField field(errors, - ".original_ip_detection_extensions"); - errors->AddError("must be empty"); + errors.emplace_back("'original_ip_detection_extensions' must be empty"); } - // common_http_protocol_options + // Obtain max_stream_duration from Http Protocol Options. const envoy_config_core_v3_HttpProtocolOptions* options = envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_common_http_protocol_options( http_connection_manager_proto); if (options != nullptr) { - // max_stream_duration const google_protobuf_Duration* duration = envoy_config_core_v3_HttpProtocolOptions_max_stream_duration(options); if (duration != nullptr) { - ValidationErrors::ScopedField field( - errors, ".common_http_protocol_options.max_stream_duration"); + ValidationErrors validation_errors; http_connection_manager.http_max_stream_duration = - ParseDuration(duration, errors); + ParseDuration(duration, &validation_errors); + if (!validation_errors.ok()) { + errors.emplace_back( + validation_errors.status("max_stream_duration").message()); + } } } - // http_filters - { - ValidationErrors::ScopedField field(errors, ".http_filters"); - size_t num_filters = 0; - const auto* http_filters = - envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_http_filters( - http_connection_manager_proto, &num_filters); - std::set names_seen; - const size_t original_error_size = errors->size(); - for (size_t i = 0; i < num_filters; ++i) { - ValidationErrors::ScopedField field(errors, absl::StrCat("[", i, "]")); - const auto* http_filter = http_filters[i]; - // name - absl::string_view name = UpbStringToAbsl( - envoy_extensions_filters_network_http_connection_manager_v3_HttpFilter_name( - http_filter)); - { - ValidationErrors::ScopedField field(errors, ".name"); - if (name.empty()) { - errors->AddError("empty filter name"); - continue; - } - if (names_seen.find(name) != names_seen.end()) { - errors->AddError(absl::StrCat("duplicate HTTP filter name: ", name)); - continue; - } + // Parse filters. + size_t num_filters = 0; + const auto* http_filters = + envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_http_filters( + http_connection_manager_proto, &num_filters); + std::set names_seen; + for (size_t i = 0; i < num_filters; ++i) { + const auto* http_filter = http_filters[i]; + absl::string_view name = UpbStringToAbsl( + envoy_extensions_filters_network_http_connection_manager_v3_HttpFilter_name( + http_filter)); + if (name.empty()) { + errors.emplace_back(absl::StrCat("empty filter name at index ", i)); + continue; + } + if (names_seen.find(name) != names_seen.end()) { + errors.emplace_back(absl::StrCat("duplicate HTTP filter name: ", name)); + continue; + } + names_seen.insert(name); + const bool is_optional = + envoy_extensions_filters_network_http_connection_manager_v3_HttpFilter_is_optional( + http_filter); + const google_protobuf_Any* any = + envoy_extensions_filters_network_http_connection_manager_v3_HttpFilter_typed_config( + http_filter); + if (any == nullptr) { + if (!is_optional) { + errors.emplace_back( + absl::StrCat("no filter config specified for filter name ", name)); } - names_seen.insert(name); - // is_optional - const bool is_optional = - envoy_extensions_filters_network_http_connection_manager_v3_HttpFilter_is_optional( - http_filter); - // typed_config - { - ValidationErrors::ScopedField field(errors, ".typed_config"); - const google_protobuf_Any* typed_config = - envoy_extensions_filters_network_http_connection_manager_v3_HttpFilter_typed_config( - http_filter); - if (typed_config == nullptr) { - if (!is_optional) errors->AddError("field not present"); - continue; - } - auto extension = ExtractXdsExtension(context, typed_config, errors); - const XdsHttpFilterImpl* filter_impl = nullptr; - if (extension.has_value()) { - filter_impl = - XdsHttpFilterRegistry::GetFilterForType(extension->type); - } - if (filter_impl == nullptr) { - if (!is_optional) errors->AddError("unsupported filter type"); - continue; - } - if ((is_client && !filter_impl->IsSupportedOnClients()) || - (!is_client && !filter_impl->IsSupportedOnServers())) { - if (!is_optional) { - errors->AddError(absl::StrCat("filter is not supported on ", - is_client ? "clients" : "servers")); - } - continue; - } - absl::optional filter_config = - filter_impl->GenerateFilterConfig(std::move(*extension), - context.arena, errors); - if (filter_config.has_value()) { - http_connection_manager.http_filters.emplace_back( - XdsListenerResource::HttpConnectionManager::HttpFilter{ - std::string(name), std::move(*filter_config)}); - } + continue; + } + ValidationErrors validation_errors; + ValidationErrors::ScopedField field( + &validation_errors, + absl::StrCat(".http_filters[", i, "].typed_config")); + auto extension = ExtractXdsExtension(context, any, &validation_errors); + if (!validation_errors.ok()) { + errors.emplace_back( + validation_errors.status(absl::StrCat("filter name ", name)) + .message()); + continue; + } + GPR_ASSERT(extension.has_value()); + const XdsHttpFilterImpl* filter_impl = + XdsHttpFilterRegistry::GetFilterForType(extension->type); + if (filter_impl == nullptr) { + if (!is_optional) { + errors.emplace_back(absl::StrCat( + "no filter registered for config type ", extension->type)); } + continue; } - if (errors->size() == original_error_size && - http_connection_manager.http_filters.empty()) { - errors->AddError("expected at least one HTTP filter"); + if ((is_client && !filter_impl->IsSupportedOnClients()) || + (!is_client && !filter_impl->IsSupportedOnServers())) { + if (!is_optional) { + errors.emplace_back(absl::StrFormat("Filter %s is not supported on %s", + extension->type, + is_client ? "clients" : "servers")); + } + continue; } - // Make sure that the last filter is terminal and non-last filters are - // non-terminal. Note that this check is being performed in a separate loop - // to take care of the case where there are two terminal filters in the list - // out of which only one gets added in the final list. - for (const auto& http_filter : http_connection_manager.http_filters) { - const XdsHttpFilterImpl* filter_impl = - XdsHttpFilterRegistry::GetFilterForType( - http_filter.config.config_proto_type_name); - if (&http_filter != &http_connection_manager.http_filters.back()) { - // Filters before the last filter must not be terminal. - if (filter_impl->IsTerminalFilter()) { - errors->AddError( - absl::StrCat("terminal filter for config type ", - http_filter.config.config_proto_type_name, - " must be the last filter in the chain")); - } - } else { - // The last filter must be terminal. - if (!filter_impl->IsTerminalFilter()) { - errors->AddError( - absl::StrCat("non-terminal filter for config type ", - http_filter.config.config_proto_type_name, - " is the last filter in the chain")); - } + absl::StatusOr filter_config = + filter_impl->GenerateFilterConfig(std::move(*extension), context.arena); + if (!filter_config.ok()) { + errors.emplace_back(absl::StrCat( + "filter config for type ", extension->type, + " failed to parse: ", StatusToString(filter_config.status()))); + continue; + } + http_connection_manager.http_filters.emplace_back( + XdsListenerResource::HttpConnectionManager::HttpFilter{ + std::string(name), std::move(*filter_config)}); + } + if (http_connection_manager.http_filters.empty()) { + errors.emplace_back("Expected at least one HTTP filter"); + } + // Make sure that the last filter is terminal and non-last filters are + // non-terminal. Note that this check is being performed in a separate loop + // to take care of the case where there are two terminal filters in the list + // out of which only one gets added in the final list. + for (const auto& http_filter : http_connection_manager.http_filters) { + const XdsHttpFilterImpl* filter_impl = + XdsHttpFilterRegistry::GetFilterForType( + http_filter.config.config_proto_type_name); + if (&http_filter != &http_connection_manager.http_filters.back()) { + // Filters before the last filter must not be terminal. + if (filter_impl->IsTerminalFilter()) { + errors.emplace_back( + absl::StrCat("terminal filter for config type ", + http_filter.config.config_proto_type_name, + " must be the last filter in the chain")); + } + } else { + // The last filter must be terminal. + if (!filter_impl->IsTerminalFilter()) { + errors.emplace_back( + absl::StrCat("non-terminal filter for config type ", + http_filter.config.config_proto_type_name, + " is the last filter in the chain")); } } } @@ -460,10 +434,9 @@ XdsListenerResource::HttpConnectionManager HttpConnectionManagerParse( http_connection_manager_proto); auto rds_update = XdsRouteConfigResource::Parse(context, route_config); if (!rds_update.ok()) { - ValidationErrors::ScopedField field(errors, ".route_config"); - errors->AddError(rds_update.status().message()); + errors.emplace_back(rds_update.status().message()); } else { - http_connection_manager.route_config = std::move(*rds_update); + http_connection_manager.rds_update = std::move(*rds_update); } } else { // Validate that RDS must be used to get the route_config dynamically. @@ -471,149 +444,149 @@ XdsListenerResource::HttpConnectionManager HttpConnectionManagerParse( envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_rds( http_connection_manager_proto); if (rds == nullptr) { - errors->AddError("neither route_config nor rds fields are present"); + return GRPC_ERROR_CREATE( + "HttpConnectionManager neither has inlined route_config nor RDS."); + } + // Check that the ConfigSource specifies ADS. + const envoy_config_core_v3_ConfigSource* config_source = + envoy_extensions_filters_network_http_connection_manager_v3_Rds_config_source( + rds); + if (config_source == nullptr) { + errors.emplace_back( + "HttpConnectionManager missing config_source for RDS."); + } else if (!envoy_config_core_v3_ConfigSource_has_ads(config_source) && + !envoy_config_core_v3_ConfigSource_has_self(config_source)) { + errors.emplace_back( + "HttpConnectionManager ConfigSource for RDS does not specify ADS " + "or SELF."); } else { // Get the route_config_name. - http_connection_manager.route_config = UpbStringToStdString( + http_connection_manager.route_config_name = UpbStringToStdString( envoy_extensions_filters_network_http_connection_manager_v3_Rds_route_config_name( rds)); - // Check that the ConfigSource specifies ADS. - const envoy_config_core_v3_ConfigSource* config_source = - envoy_extensions_filters_network_http_connection_manager_v3_Rds_config_source( - rds); - ValidationErrors::ScopedField field(errors, ".rds.config_source"); - if (config_source == nullptr) { - errors->AddError("field not present"); - } else if (!envoy_config_core_v3_ConfigSource_has_ads(config_source) && - !envoy_config_core_v3_ConfigSource_has_self(config_source)) { - errors->AddError("ConfigSource does not specify ADS or SELF"); - } } } + // Return result. + if (!errors.empty()) { + return absl::InvalidArgumentError( + absl::StrCat("Errors parsing HttpConnectionManager config: [", + absl::StrJoin(errors, "; "), "]")); + } return http_connection_manager; } absl::StatusOr LdsResourceParseClient( const XdsResourceType::DecodeContext& context, const envoy_config_listener_v3_ApiListener* api_listener) { - XdsListenerResource lds_update; - ValidationErrors errors; - ValidationErrors::ScopedField field(&errors, "api_listener.api_listener"); - auto* api_listener_field = - envoy_config_listener_v3_ApiListener_api_listener(api_listener); - if (api_listener_field == nullptr) { - errors.AddError("field not present"); - } else { - auto extension = ExtractXdsExtension(context, api_listener_field, &errors); - if (extension.has_value()) { - lds_update.listener = HttpConnectionManagerParse( - /*is_client=*/true, context, std::move(*extension), &errors); - } + const upb_StringView encoded_api_listener = google_protobuf_Any_value( + envoy_config_listener_v3_ApiListener_api_listener(api_listener)); + const auto* http_connection_manager = + envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_parse( + encoded_api_listener.data, encoded_api_listener.size, context.arena); + if (http_connection_manager == nullptr) { + return absl::InvalidArgumentError( + "Could not parse HttpConnectionManager config from ApiListener"); } - if (!errors.ok()) return errors.status("errors validating ApiListener"); - return std::move(lds_update); + auto hcm = HttpConnectionManagerParse(true /* is_client */, context, + http_connection_manager); + if (!hcm.ok()) return hcm.status(); + XdsListenerResource lds_update; + lds_update.type = XdsListenerResource::ListenerType::kHttpApiListener; + lds_update.http_connection_manager = std::move(*hcm); + return lds_update; } -XdsListenerResource::DownstreamTlsContext DownstreamTlsContextParse( +absl::StatusOr +DownstreamTlsContextParse( const XdsResourceType::DecodeContext& context, - const envoy_config_core_v3_TransportSocket* transport_socket, - ValidationErrors* errors) { - ValidationErrors::ScopedField field(errors, ".typed_config"); + const envoy_config_core_v3_TransportSocket* transport_socket) { const auto* typed_config = envoy_config_core_v3_TransportSocket_typed_config(transport_socket); - auto extension = ExtractXdsExtension(context, typed_config, errors); - if (!extension.has_value()) return {}; - if (extension->type != - "envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext") { - ValidationErrors::ScopedField field(errors, ".type_url"); - errors->AddError("unsupported transport socket type"); - return {}; + if (typed_config == nullptr) { + return absl::InvalidArgumentError("transport socket typed config unset"); } - absl::string_view* serialized_downstream_tls_context = - absl::get_if(&extension->value); - if (serialized_downstream_tls_context == nullptr) { - errors->AddError("can't decode DownstreamTlsContext"); - return {}; + absl::string_view type_url = absl::StripPrefix( + UpbStringToAbsl(google_protobuf_Any_type_url(typed_config)), + "type.googleapis.com/"); + if (type_url != + "envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext") { + return absl::InvalidArgumentError( + absl::StrCat("Unrecognized transport socket type: ", type_url)); } + const upb_StringView encoded_downstream_tls_context = + google_protobuf_Any_value(typed_config); const auto* downstream_tls_context_proto = envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_parse( - serialized_downstream_tls_context->data(), - serialized_downstream_tls_context->size(), context.arena); + encoded_downstream_tls_context.data, + encoded_downstream_tls_context.size, context.arena); if (downstream_tls_context_proto == nullptr) { - errors->AddError("can't decode DownstreamTlsContext"); - return {}; + return absl::InvalidArgumentError("Can't decode downstream tls context."); } + std::vector errors; XdsListenerResource::DownstreamTlsContext downstream_tls_context; auto* common_tls_context = envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_common_tls_context( downstream_tls_context_proto); if (common_tls_context != nullptr) { - ValidationErrors::ScopedField field(errors, ".common_tls_context"); - downstream_tls_context.common_tls_context = - CommonTlsContext::Parse(context, common_tls_context, errors); - // Note: We can't be more specific about the field name for this - // error, because we don't know which fields they were found in - // inside of CommonTlsContext, so we make the error message a bit - // more verbose to compensate. - if (!downstream_tls_context.common_tls_context - .certificate_validation_context.match_subject_alt_names.empty()) { - errors->AddError("match_subject_alt_names not supported on servers"); + ValidationErrors validation_errors; + downstream_tls_context.common_tls_context = CommonTlsContext::Parse( + context, common_tls_context, &validation_errors); + if (!validation_errors.ok()) { + errors.emplace_back( + validation_errors.status("errors in common_tls_context").message()); } } - // Note: We can't be more specific about the field name for this - // error, because we don't know which fields they were found in - // inside of CommonTlsContext, so we make the error message a bit - // more verbose to compensate. - if (downstream_tls_context.common_tls_context - .tls_certificate_provider_instance.instance_name.empty()) { - errors->AddError( - "TLS configuration provided but no " - "tls_certificate_provider_instance found"); - } auto* require_client_certificate = envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_require_client_certificate( downstream_tls_context_proto); if (require_client_certificate != nullptr) { downstream_tls_context.require_client_certificate = google_protobuf_BoolValue_value(require_client_certificate); - if (downstream_tls_context.require_client_certificate && - downstream_tls_context.common_tls_context.certificate_validation_context - .ca_certificate_provider_instance.instance_name.empty()) { - ValidationErrors::ScopedField field(errors, - ".require_client_certificate"); - errors->AddError( - "client certificate required but no certificate " - "provider instance specified for validation"); - } } auto* require_sni = envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_require_sni( downstream_tls_context_proto); if (require_sni != nullptr && google_protobuf_BoolValue_value(require_sni)) { - ValidationErrors::ScopedField field(errors, ".require_sni"); - errors->AddError("field unsupported"); + errors.emplace_back("require_sni: unsupported"); } if (envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_ocsp_staple_policy( downstream_tls_context_proto) != envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_LENIENT_STAPLING) { - ValidationErrors::ScopedField field(errors, ".ocsp_staple_policy"); - errors->AddError("value must be LENIENT_STAPLING"); + errors.emplace_back("ocsp_staple_policy: Only LENIENT_STAPLING supported"); + } + if (downstream_tls_context.common_tls_context + .tls_certificate_provider_instance.instance_name.empty()) { + errors.emplace_back( + "TLS configuration provided but no " + "tls_certificate_provider_instance found."); + } + if (downstream_tls_context.require_client_certificate && + downstream_tls_context.common_tls_context.certificate_validation_context + .ca_certificate_provider_instance.instance_name.empty()) { + errors.emplace_back( + "TLS configuration requires client certificates but no certificate " + "provider instance specified for validation."); + } + if (!downstream_tls_context.common_tls_context.certificate_validation_context + .match_subject_alt_names.empty()) { + errors.emplace_back("match_subject_alt_names not supported on servers"); + } + // Return result. + if (!errors.empty()) { + return absl::InvalidArgumentError( + absl::StrCat("Errors parsing DownstreamTlsContext: [", + absl::StrJoin(errors, "; "), "]")); } return downstream_tls_context; } -absl::optional CidrRangeParse( - const envoy_config_core_v3_CidrRange* cidr_range_proto, - ValidationErrors* errors) { - ValidationErrors::ScopedField field(errors, ".address_prefix"); +absl::StatusOr CidrRangeParse( + const envoy_config_core_v3_CidrRange* cidr_range_proto) { XdsListenerResource::FilterChainMap::CidrRange cidr_range; std::string address_prefix = UpbStringToStdString( envoy_config_core_v3_CidrRange_address_prefix(cidr_range_proto)); auto address = StringToSockaddr(address_prefix, /*port=*/0); - if (!address.ok()) { - errors->AddError(address.status().message()); - return absl::nullopt; - } + if (!address.ok()) return address.status(); cidr_range.address = *address; cidr_range.prefix_len = 0; auto* prefix_len_proto = @@ -631,12 +604,10 @@ absl::optional CidrRangeParse( return cidr_range; } -absl::optional FilterChainMatchParse( - const envoy_config_listener_v3_FilterChainMatch* filter_chain_match_proto, - ValidationErrors* errors) { +absl::StatusOr FilterChainMatchParse( + const envoy_config_listener_v3_FilterChainMatch* filter_chain_match_proto) { + std::vector errors; FilterChain::FilterChainMatch filter_chain_match; - const size_t original_error_size = errors->size(); - // destination_port auto* destination_port = envoy_config_listener_v3_FilterChainMatch_destination_port( filter_chain_match_proto); @@ -644,56 +615,51 @@ absl::optional FilterChainMatchParse( filter_chain_match.destination_port = google_protobuf_UInt32Value_value(destination_port); } - // prefix_ranges size_t size = 0; auto* prefix_ranges = envoy_config_listener_v3_FilterChainMatch_prefix_ranges( filter_chain_match_proto, &size); filter_chain_match.prefix_ranges.reserve(size); for (size_t i = 0; i < size; i++) { - ValidationErrors::ScopedField field( - errors, absl::StrCat(".prefix_ranges[", i, "]")); - auto cidr_range = CidrRangeParse(prefix_ranges[i], errors); - if (cidr_range.has_value()) { - filter_chain_match.prefix_ranges.push_back(*cidr_range); + auto cidr_range = CidrRangeParse(prefix_ranges[i]); + if (!cidr_range.ok()) { + errors.emplace_back(absl::StrCat("prefix range ", i, ": ", + cidr_range.status().message())); + continue; } + filter_chain_match.prefix_ranges.push_back(*cidr_range); } - // source_type filter_chain_match.source_type = static_cast( envoy_config_listener_v3_FilterChainMatch_source_type( filter_chain_match_proto)); - // source_prefix_ranges auto* source_prefix_ranges = envoy_config_listener_v3_FilterChainMatch_source_prefix_ranges( filter_chain_match_proto, &size); filter_chain_match.source_prefix_ranges.reserve(size); for (size_t i = 0; i < size; i++) { - ValidationErrors::ScopedField field( - errors, absl::StrCat(".source_prefix_ranges[", i, "]")); - auto cidr_range = CidrRangeParse(source_prefix_ranges[i], errors); - if (cidr_range.has_value()) { - filter_chain_match.source_prefix_ranges.push_back(*cidr_range); + auto cidr_range = CidrRangeParse(source_prefix_ranges[i]); + if (!cidr_range.ok()) { + errors.emplace_back(absl::StrCat("source prefix range ", i, ": ", + cidr_range.status().message())); + continue; } + filter_chain_match.source_prefix_ranges.push_back(*cidr_range); } - // source_ports auto* source_ports = envoy_config_listener_v3_FilterChainMatch_source_ports( filter_chain_match_proto, &size); filter_chain_match.source_ports.reserve(size); for (size_t i = 0; i < size; i++) { filter_chain_match.source_ports.push_back(source_ports[i]); } - // server_names auto* server_names = envoy_config_listener_v3_FilterChainMatch_server_names( filter_chain_match_proto, &size); for (size_t i = 0; i < size; i++) { filter_chain_match.server_names.push_back( UpbStringToStdString(server_names[i])); } - // transport_protocol filter_chain_match.transport_protocol = UpbStringToStdString( envoy_config_listener_v3_FilterChainMatch_transport_protocol( filter_chain_match_proto)); - // application_protocols auto* application_protocols = envoy_config_listener_v3_FilterChainMatch_application_protocols( filter_chain_match_proto, &size); @@ -702,93 +668,111 @@ absl::optional FilterChainMatchParse( UpbStringToStdString(application_protocols[i])); } // Return result. - if (errors->size() != original_error_size) return absl::nullopt; + if (!errors.empty()) { + return absl::InvalidArgumentError( + absl::StrCat("errors parsing filter chain match: [", + absl::StrJoin(errors, "; "), "]")); + } return filter_chain_match; } -absl::optional FilterChainParse( +absl::StatusOr FilterChainParse( const XdsResourceType::DecodeContext& context, - const envoy_config_listener_v3_FilterChain* filter_chain_proto, - ValidationErrors* errors) { + const envoy_config_listener_v3_FilterChain* filter_chain_proto) { FilterChain filter_chain; - const size_t original_error_size = errors->size(); - // filter_chain_match + std::vector errors; auto* filter_chain_match = envoy_config_listener_v3_FilterChain_filter_chain_match( filter_chain_proto); if (filter_chain_match != nullptr) { - ValidationErrors::ScopedField field(errors, ".filter_chain_match"); - auto match = FilterChainMatchParse(filter_chain_match, errors); - if (match.has_value()) { + auto match = FilterChainMatchParse(filter_chain_match); + if (!match.ok()) { + errors.emplace_back(match.status().message()); + } else { filter_chain.filter_chain_match = std::move(*match); } } - // filters - { - ValidationErrors::ScopedField field(errors, ".filters"); - filter_chain.filter_chain_data = - std::make_shared(); - size_t size = 0; - auto* filters = - envoy_config_listener_v3_FilterChain_filters(filter_chain_proto, &size); - if (size != 1) { - errors->AddError( - "must have exactly one filter (HttpConnectionManager -- " - "no other filter is supported at the moment)"); - } - // entries in filters list - for (size_t i = 0; i < size; ++i) { - ValidationErrors::ScopedField field( - errors, absl::StrCat("[", i, "].typed_config")); - auto* typed_config = - envoy_config_listener_v3_Filter_typed_config(filters[i]); - auto extension = ExtractXdsExtension(context, typed_config, errors); - if (extension.has_value()) { - filter_chain.filter_chain_data->http_connection_manager = - HttpConnectionManagerParse(/*is_client=*/false, context, - std::move(*extension), errors); + filter_chain.filter_chain_data = + std::make_shared(); + // Parse the filters list. Currently we only support HttpConnectionManager. + size_t size = 0; + auto* filters = + envoy_config_listener_v3_FilterChain_filters(filter_chain_proto, &size); + if (size != 1) { + errors.push_back( + "FilterChain should have exactly one filter: HttpConnectionManager; no " + "other filter is supported at the moment"); + } else { + auto* typed_config = + envoy_config_listener_v3_Filter_typed_config(filters[0]); + if (typed_config == nullptr) { + errors.emplace_back("No typed_config found in filter."); + } else { + absl::string_view type_url = absl::StripPrefix( + UpbStringToAbsl(google_protobuf_Any_type_url(typed_config)), + "type.googleapis.com/"); + if (type_url != + "envoy.extensions.filters.network.http_connection_manager.v3." + "HttpConnectionManager") { + errors.emplace_back(absl::StrCat("Unsupported filter type ", type_url)); + } else { + const upb_StringView encoded_http_connection_manager = + google_protobuf_Any_value(typed_config); + const auto* http_connection_manager = + envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_parse( + encoded_http_connection_manager.data, + encoded_http_connection_manager.size, context.arena); + if (http_connection_manager == nullptr) { + errors.emplace_back( + "Could not parse HttpConnectionManager config from filter " + "typed_config"); + } else { + auto hcm = HttpConnectionManagerParse( + /*is_client=*/false, context, http_connection_manager); + if (!hcm.ok()) { + errors.emplace_back(hcm.status().message()); + } else { + filter_chain.filter_chain_data->http_connection_manager = + std::move(*hcm); + } + } } } } - // transport_socket auto* transport_socket = envoy_config_listener_v3_FilterChain_transport_socket(filter_chain_proto); if (transport_socket != nullptr) { - ValidationErrors::ScopedField field(errors, ".transport_socket"); - filter_chain.filter_chain_data->downstream_tls_context = - DownstreamTlsContextParse(context, transport_socket, errors); + auto downstream_context = + DownstreamTlsContextParse(context, transport_socket); + if (!downstream_context.ok()) { + errors.emplace_back(downstream_context.status().message()); + } else { + filter_chain.filter_chain_data->downstream_tls_context = + std::move(*downstream_context); + } } // Return result. - if (errors->size() != original_error_size) return absl::nullopt; + if (!errors.empty()) { + return absl::InvalidArgumentError(absl::StrCat( + "Errors parsing FilterChain: [", absl::StrJoin(errors, "; "), "]")); + } return filter_chain; } -absl::optional AddressParse( - const envoy_config_core_v3_Address* address_proto, - ValidationErrors* errors) { - if (address_proto == nullptr) { - errors->AddError("field not present"); - return absl::nullopt; - } - ValidationErrors::ScopedField field(errors, ".socket_address"); +absl::StatusOr AddressParse( + const envoy_config_core_v3_Address* address_proto) { const auto* socket_address = envoy_config_core_v3_Address_socket_address(address_proto); if (socket_address == nullptr) { - errors->AddError("field not present"); - return absl::nullopt; - } - { - ValidationErrors::ScopedField field(errors, ".protocol"); - if (envoy_config_core_v3_SocketAddress_protocol(socket_address) != - envoy_config_core_v3_SocketAddress_TCP) { - errors->AddError("value must be TCP"); - } + return absl::InvalidArgumentError("Address does not have socket_address"); + } + if (envoy_config_core_v3_SocketAddress_protocol(socket_address) != + envoy_config_core_v3_SocketAddress_TCP) { + return absl::InvalidArgumentError("SocketAddress protocol is not TCP"); } - ValidationErrors::ScopedField field2(errors, ".port_value"); uint32_t port = envoy_config_core_v3_SocketAddress_port_value(socket_address); if (port > 65535) { - errors->AddError("invalid port"); - return absl::nullopt; + return absl::InvalidArgumentError("Invalid port"); } return JoinHostPort( UpbStringToAbsl( @@ -812,103 +796,97 @@ struct InternalFilterChainMap { DestinationIpMap destination_ip_map; }; -void AddFilterChainDataForSourcePort( +absl::Status AddFilterChainDataForSourcePort( const FilterChain& filter_chain, uint32_t port, - XdsListenerResource::FilterChainMap::SourcePortsMap* ports_map, - ValidationErrors* errors) { + XdsListenerResource::FilterChainMap::SourcePortsMap* ports_map) { auto insert_result = ports_map->emplace( port, XdsListenerResource::FilterChainMap::FilterChainDataSharedPtr{ filter_chain.filter_chain_data}); if (!insert_result.second) { - errors->AddError(absl::StrCat( - "duplicate matching rules detected when adding filter chain: ", + return absl::InvalidArgumentError(absl::StrCat( + "Duplicate matching rules detected when adding filter chain: ", filter_chain.filter_chain_match.ToString())); } + return absl::OkStatus(); } -void AddFilterChainDataForSourcePorts( +absl::Status AddFilterChainDataForSourcePorts( const FilterChain& filter_chain, - XdsListenerResource::FilterChainMap::SourcePortsMap* ports_map, - ValidationErrors* errors) { + XdsListenerResource::FilterChainMap::SourcePortsMap* ports_map) { if (filter_chain.filter_chain_match.source_ports.empty()) { - AddFilterChainDataForSourcePort(filter_chain, 0, ports_map, errors); + return AddFilterChainDataForSourcePort(filter_chain, 0, ports_map); } else { for (uint32_t port : filter_chain.filter_chain_match.source_ports) { - AddFilterChainDataForSourcePort(filter_chain, port, ports_map, errors); + absl::Status status = + AddFilterChainDataForSourcePort(filter_chain, port, ports_map); + if (!status.ok()) return status; } } + return absl::OkStatus(); } -void AddFilterChainDataForSourceIpRange( +absl::Status AddFilterChainDataForSourceIpRange( const FilterChain& filter_chain, - InternalFilterChainMap::SourceIpMap* source_ip_map, - ValidationErrors* errors) { + InternalFilterChainMap::SourceIpMap* source_ip_map) { if (filter_chain.filter_chain_match.source_prefix_ranges.empty()) { auto insert_result = source_ip_map->emplace( "", XdsListenerResource::FilterChainMap::SourceIp()); - AddFilterChainDataForSourcePorts( - filter_chain, &insert_result.first->second.ports_map, errors); + return AddFilterChainDataForSourcePorts( + filter_chain, &insert_result.first->second.ports_map); } else { for (const auto& prefix_range : filter_chain.filter_chain_match.source_prefix_ranges) { auto addr_str = grpc_sockaddr_to_string(&prefix_range.address, false); - if (!addr_str.ok()) { - errors->AddError(absl::StrCat( - "error parsing source IP sockaddr (should not happen): ", - addr_str.status().message())); - continue; - } + if (!addr_str.ok()) return addr_str.status(); auto insert_result = source_ip_map->emplace( absl::StrCat(*addr_str, "/", prefix_range.prefix_len), XdsListenerResource::FilterChainMap::SourceIp()); if (insert_result.second) { insert_result.first->second.prefix_range.emplace(prefix_range); } - AddFilterChainDataForSourcePorts( - filter_chain, &insert_result.first->second.ports_map, errors); + absl::Status status = AddFilterChainDataForSourcePorts( + filter_chain, &insert_result.first->second.ports_map); + if (!status.ok()) return status; } } + return absl::OkStatus(); } -void AddFilterChainDataForSourceType( +absl::Status AddFilterChainDataForSourceType( const FilterChain& filter_chain, - InternalFilterChainMap::DestinationIp* destination_ip, - ValidationErrors* errors) { + InternalFilterChainMap::DestinationIp* destination_ip) { GPR_ASSERT(static_cast( filter_chain.filter_chain_match.source_type) < 3); - AddFilterChainDataForSourceIpRange( - filter_chain, - &destination_ip->source_types_array[static_cast( - filter_chain.filter_chain_match.source_type)], - errors); + return AddFilterChainDataForSourceIpRange( + filter_chain, &destination_ip->source_types_array[static_cast( + filter_chain.filter_chain_match.source_type)]); } -void AddFilterChainDataForApplicationProtocols( +absl::Status AddFilterChainDataForApplicationProtocols( const FilterChain& filter_chain, - InternalFilterChainMap::DestinationIp* destination_ip, - ValidationErrors* errors) { + InternalFilterChainMap::DestinationIp* destination_ip) { // Only allow filter chains that do not mention application protocols - if (filter_chain.filter_chain_match.application_protocols.empty()) { - AddFilterChainDataForSourceType(filter_chain, destination_ip, errors); + if (!filter_chain.filter_chain_match.application_protocols.empty()) { + return absl::OkStatus(); } + return AddFilterChainDataForSourceType(filter_chain, destination_ip); } -void AddFilterChainDataForTransportProtocol( +absl::Status AddFilterChainDataForTransportProtocol( const FilterChain& filter_chain, - InternalFilterChainMap::DestinationIp* destination_ip, - ValidationErrors* errors) { + InternalFilterChainMap::DestinationIp* destination_ip) { const std::string& transport_protocol = filter_chain.filter_chain_match.transport_protocol; // Only allow filter chains with no transport protocol or "raw_buffer" if (!transport_protocol.empty() && transport_protocol != "raw_buffer") { - return; + return absl::OkStatus(); } // If for this configuration, we've already seen filter chains that mention // the transport protocol as "raw_buffer", we will never match filter chains // that do not mention it. if (destination_ip->transport_protocol_raw_buffer_provided && transport_protocol.empty()) { - return; + return absl::OkStatus(); } if (!transport_protocol.empty() && !destination_ip->transport_protocol_raw_buffer_provided) { @@ -918,50 +896,45 @@ void AddFilterChainDataForTransportProtocol( destination_ip->source_types_array = InternalFilterChainMap::ConnectionSourceTypesArray(); } - AddFilterChainDataForApplicationProtocols(filter_chain, destination_ip, - errors); + return AddFilterChainDataForApplicationProtocols(filter_chain, + destination_ip); } -void AddFilterChainDataForServerNames( +absl::Status AddFilterChainDataForServerNames( const FilterChain& filter_chain, - InternalFilterChainMap::DestinationIp* destination_ip, - ValidationErrors* errors) { + InternalFilterChainMap::DestinationIp* destination_ip) { // Don't continue adding filter chains with server names mentioned - if (filter_chain.filter_chain_match.server_names.empty()) { - AddFilterChainDataForTransportProtocol(filter_chain, destination_ip, - errors); + if (!filter_chain.filter_chain_match.server_names.empty()) { + return absl::OkStatus(); } + return AddFilterChainDataForTransportProtocol(filter_chain, destination_ip); } -void AddFilterChainDataForDestinationIpRange( +absl::Status AddFilterChainDataForDestinationIpRange( const FilterChain& filter_chain, - InternalFilterChainMap::DestinationIpMap* destination_ip_map, - ValidationErrors* errors) { + InternalFilterChainMap::DestinationIpMap* destination_ip_map) { if (filter_chain.filter_chain_match.prefix_ranges.empty()) { auto insert_result = destination_ip_map->emplace( "", InternalFilterChainMap::DestinationIp()); - AddFilterChainDataForServerNames(filter_chain, &insert_result.first->second, - errors); + return AddFilterChainDataForServerNames(filter_chain, + &insert_result.first->second); } else { for (const auto& prefix_range : filter_chain.filter_chain_match.prefix_ranges) { auto addr_str = grpc_sockaddr_to_string(&prefix_range.address, false); - if (!addr_str.ok()) { - errors->AddError(absl::StrCat( - "error parsing destination IP sockaddr (should not happen): ", - addr_str.status().message())); - continue; - } + if (!addr_str.ok()) return addr_str.status(); auto insert_result = destination_ip_map->emplace( absl::StrCat(*addr_str, "/", prefix_range.prefix_len), InternalFilterChainMap::DestinationIp()); if (insert_result.second) { insert_result.first->second.prefix_range.emplace(prefix_range); } - AddFilterChainDataForServerNames(filter_chain, - &insert_result.first->second, errors); + absl::Status status = AddFilterChainDataForServerNames( + filter_chain, &insert_result.first->second); + if (!status.ok()) return status; } } + return absl::OkStatus(); } XdsListenerResource::FilterChainMap BuildFromInternalFilterChainMap( @@ -983,14 +956,15 @@ XdsListenerResource::FilterChainMap BuildFromInternalFilterChainMap( return filter_chain_map; } -XdsListenerResource::FilterChainMap BuildFilterChainMap( - const std::vector& filter_chains, ValidationErrors* errors) { +absl::StatusOr BuildFilterChainMap( + const std::vector& filter_chains) { InternalFilterChainMap internal_filter_chain_map; for (const auto& filter_chain : filter_chains) { // Discard filter chain entries that specify destination port if (filter_chain.filter_chain_match.destination_port != 0) continue; - AddFilterChainDataForDestinationIpRange( - filter_chain, &internal_filter_chain_map.destination_ip_map, errors); + absl::Status status = AddFilterChainDataForDestinationIpRange( + filter_chain, &internal_filter_chain_map.destination_ip_map); + if (!status.ok()) return status; } return BuildFromInternalFilterChainMap(&internal_filter_chain_map); } @@ -998,65 +972,46 @@ XdsListenerResource::FilterChainMap BuildFilterChainMap( absl::StatusOr LdsResourceParseServer( const XdsResourceType::DecodeContext& context, const envoy_config_listener_v3_Listener* listener) { - ValidationErrors errors; - XdsListenerResource::TcpListener tcp_listener; - // address - { - ValidationErrors::ScopedField field(&errors, "address"); - auto address = AddressParse( - envoy_config_listener_v3_Listener_address(listener), &errors); - if (address.has_value()) tcp_listener.address = std::move(*address); - } - // use_original_dst - { - ValidationErrors::ScopedField field(&errors, "use_original_dst"); - const auto* use_original_dst = - envoy_config_listener_v3_Listener_use_original_dst(listener); - if (use_original_dst != nullptr && - google_protobuf_BoolValue_value(use_original_dst)) { - errors.AddError("field not supported"); + XdsListenerResource lds_update; + lds_update.type = XdsListenerResource::ListenerType::kTcpListener; + auto address = + AddressParse(envoy_config_listener_v3_Listener_address(listener)); + if (!address.ok()) return address.status(); + lds_update.address = std::move(*address); + const auto* use_original_dst = + envoy_config_listener_v3_Listener_use_original_dst(listener); + if (use_original_dst != nullptr) { + if (google_protobuf_BoolValue_value(use_original_dst)) { + return absl::InvalidArgumentError( + "Field \'use_original_dst\' is not supported."); } } - // filter_chains - size_t num_filter_chains = 0; - { - ValidationErrors::ScopedField field(&errors, "filter_chains"); - auto* filter_chains = envoy_config_listener_v3_Listener_filter_chains( - listener, &num_filter_chains); - std::vector parsed_filter_chains; - parsed_filter_chains.reserve(num_filter_chains); - for (size_t i = 0; i < num_filter_chains; i++) { - ValidationErrors::ScopedField field(&errors, absl::StrCat("[", i, "]")); - auto filter_chain = FilterChainParse(context, filter_chains[i], &errors); - if (filter_chain.has_value()) { - parsed_filter_chains.push_back(std::move(*filter_chain)); - } - } - tcp_listener.filter_chain_map = - BuildFilterChainMap(parsed_filter_chains, &errors); - } - // default_filter_chain - { - ValidationErrors::ScopedField field(&errors, "default_filter_chain"); - auto* default_filter_chain = - envoy_config_listener_v3_Listener_default_filter_chain(listener); - if (default_filter_chain != nullptr) { - auto filter_chain = - FilterChainParse(context, default_filter_chain, &errors); - if (filter_chain.has_value() && - filter_chain->filter_chain_data != nullptr) { - tcp_listener.default_filter_chain = - std::move(*filter_chain->filter_chain_data); - } - } else if (num_filter_chains == 0) { - // Make sure that there is at least one filter chain to use. - errors.AddError("must be set if filter_chains is unset"); + size_t size = 0; + auto* filter_chains = + envoy_config_listener_v3_Listener_filter_chains(listener, &size); + std::vector parsed_filter_chains; + parsed_filter_chains.reserve(size); + for (size_t i = 0; i < size; i++) { + auto filter_chain = FilterChainParse(context, filter_chains[i]); + if (!filter_chain.ok()) return filter_chain.status(); + parsed_filter_chains.push_back(std::move(*filter_chain)); + } + auto filter_chain_map = BuildFilterChainMap(parsed_filter_chains); + if (!filter_chain_map.ok()) return filter_chain_map.status(); + lds_update.filter_chain_map = std::move(*filter_chain_map); + auto* default_filter_chain = + envoy_config_listener_v3_Listener_default_filter_chain(listener); + if (default_filter_chain != nullptr) { + auto filter_chain = FilterChainParse(context, default_filter_chain); + if (!filter_chain.ok()) return filter_chain.status(); + if (filter_chain->filter_chain_data != nullptr) { + lds_update.default_filter_chain = + std::move(*filter_chain->filter_chain_data); } } - // Return result. - if (!errors.ok()) return errors.status("errors validating server Listener"); - XdsListenerResource lds_update; - lds_update.listener = std::move(tcp_listener); + if (size == 0 && default_filter_chain == nullptr) { + return absl::InvalidArgumentError("No filter chain provided."); + } return lds_update; } diff --git a/src/core/ext/xds/xds_listener.h b/src/core/ext/xds/xds_listener.h index 67d41c02ba9..19c5f145011 100644 --- a/src/core/ext/xds/xds_listener.h +++ b/src/core/ext/xds/xds_listener.h @@ -32,7 +32,6 @@ #include "absl/strings/string_view.h" #include "absl/types/optional.h" -#include "absl/types/variant.h" #include "envoy/config/listener/v3/listener.upbdefs.h" #include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upbdefs.h" #include "upb/def.h" @@ -47,14 +46,36 @@ namespace grpc_core { +// TODO(roth): When we can use absl::variant<>, consider using that +// here, to enforce the fact that only one of the two fields can be set. struct XdsListenerResource : public XdsResourceType::ResourceData { - struct HttpConnectionManager { - // The RDS resource name or inline RouteConfiguration. - absl::variant route_config; + struct DownstreamTlsContext { + CommonTlsContext common_tls_context; + bool require_client_certificate = false; + + bool operator==(const DownstreamTlsContext& other) const { + return common_tls_context == other.common_tls_context && + require_client_certificate == other.require_client_certificate; + } + std::string ToString() const; + bool Empty() const; + }; + + enum class ListenerType { + kTcpListener = 0, + kHttpApiListener, + } type; + + struct HttpConnectionManager { + // The name to use in the RDS request. + std::string route_config_name; // Storing the Http Connection Manager Common Http Protocol Option // max_stream_duration Duration http_max_stream_duration; + // The RouteConfiguration to use for this listener. + // Present only if it is inlined in the LDS response. + absl::optional rds_update; struct HttpFilter { std::string name; @@ -69,26 +90,21 @@ struct XdsListenerResource : public XdsResourceType::ResourceData { std::vector http_filters; bool operator==(const HttpConnectionManager& other) const { - return route_config == other.route_config && + return route_config_name == other.route_config_name && http_max_stream_duration == other.http_max_stream_duration && + rds_update == other.rds_update && http_filters == other.http_filters; } std::string ToString() const; }; - struct DownstreamTlsContext { - CommonTlsContext common_tls_context; - bool require_client_certificate = false; - - bool operator==(const DownstreamTlsContext& other) const { - return common_tls_context == other.common_tls_context && - require_client_certificate == other.require_client_certificate; - } + // Populated for type=kHttpApiListener. + HttpConnectionManager http_connection_manager; - std::string ToString() const; - bool Empty() const; - }; + // Populated for type=kTcpListener. + // host:port listening_address set when type is kTcpListener + std::string address; struct FilterChainData { DownstreamTlsContext downstream_tls_context; @@ -169,26 +185,15 @@ struct XdsListenerResource : public XdsResourceType::ResourceData { } std::string ToString() const; - }; - - struct TcpListener { - std::string address; // host:port listening address - FilterChainMap filter_chain_map; - absl::optional default_filter_chain; - - bool operator==(const TcpListener& other) const { - return address == other.address && - filter_chain_map == other.filter_chain_map && - default_filter_chain == other.default_filter_chain; - } - - std::string ToString() const; - }; + } filter_chain_map; - absl::variant listener; + absl::optional default_filter_chain; bool operator==(const XdsListenerResource& other) const { - return listener == other.listener; + return http_connection_manager == other.http_connection_manager && + address == other.address && + filter_chain_map == other.filter_chain_map && + default_filter_chain == other.default_filter_chain; } std::string ToString() const; diff --git a/src/core/ext/xds/xds_route_config.cc b/src/core/ext/xds/xds_route_config.cc index 7a63e1ceec5..bfce809b070 100644 --- a/src/core/ext/xds/xds_route_config.cc +++ b/src/core/ext/xds/xds_route_config.cc @@ -638,13 +638,14 @@ ParseTypedPerFilterConfig( return absl::InvalidArgumentError(absl::StrCat( "no filter registered for config type ", extension->type)); } - absl::optional filter_config = + absl::StatusOr filter_config = filter_impl->GenerateFilterConfigOverride(std::move(*extension), - context.arena, &errors); - if (!errors.ok()) { - return errors.status("errors validation extension"); + context.arena); + if (!filter_config.ok()) { + return absl::InvalidArgumentError( + absl::StrCat("filter config for type ", extension->type, + " failed to parse: ", filter_config.status().message())); } - GPR_ASSERT(filter_config.has_value()); typed_per_filter_config[std::string(key)] = std::move(*filter_config); } return typed_per_filter_config; diff --git a/src/core/ext/xds/xds_server_config_fetcher.cc b/src/core/ext/xds/xds_server_config_fetcher.cc index 0e1f3da3042..5ba7882fa14 100644 --- a/src/core/ext/xds/xds_server_config_fetcher.cc +++ b/src/core/ext/xds/xds_server_config_fetcher.cc @@ -70,7 +70,6 @@ #include "src/core/lib/debug/trace.h" #include "src/core/lib/gprpp/debug_location.h" #include "src/core/lib/gprpp/host_port.h" -#include "src/core/lib/gprpp/match.h" #include "src/core/lib/gprpp/ref_counted_ptr.h" #include "src/core/lib/gprpp/status_helper.h" #include "src/core/lib/gprpp/sync.h" @@ -551,9 +550,7 @@ void XdsServerConfigFetcher::ListenerWatcher::OnResourceChanged( "[ListenerWatcher %p] Received LDS update from xds client %p: %s", this, xds_client_.get(), listener.ToString().c_str()); } - auto& tcp_listener = - absl::get(listener.listener); - if (tcp_listener.address != listening_address_) { + if (listener.address != listening_address_) { MutexLock lock(&mu_); OnFatalError(absl::FailedPreconditionError( "Address in LDS update does not match listening address")); @@ -561,8 +558,8 @@ void XdsServerConfigFetcher::ListenerWatcher::OnResourceChanged( } auto new_filter_chain_match_manager = MakeRefCounted( xds_client_->Ref(DEBUG_LOCATION, "FilterChainMatchManager"), - std::move(tcp_listener.filter_chain_map), - std::move(tcp_listener.default_filter_chain)); + std::move(listener.filter_chain_map), + std::move(listener.default_filter_chain)); MutexLock lock(&mu_); if (filter_chain_match_manager_ == nullptr || !(new_filter_chain_match_manager->filter_chain_map() == @@ -678,25 +675,32 @@ void XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager:: for (const auto& source_type : destination_ip.source_types_array) { for (const auto& source_ip : source_type) { for (const auto& source_port_pair : source_ip.ports_map) { - auto* filter_chain_data = source_port_pair.second.data.get(); - const auto* rds_name = absl::get_if( - &filter_chain_data->http_connection_manager.route_config); - if (rds_name != nullptr) resource_names.insert(*rds_name); - filter_chain_data_set.insert(filter_chain_data); + if (!source_port_pair.second.data->http_connection_manager + .route_config_name.empty()) { + resource_names.insert( + source_port_pair.second.data->http_connection_manager + .route_config_name); + } + filter_chain_data_set.insert(source_port_pair.second.data.get()); } } } } if (default_filter_chain_.has_value()) { - auto& hcm = default_filter_chain_->http_connection_manager; - const auto* rds_name = absl::get_if(&hcm.route_config); - if (rds_name != nullptr) resource_names.insert(*rds_name); - std::reverse(hcm.http_filters.begin(), hcm.http_filters.end()); + if (!default_filter_chain_->http_connection_manager.route_config_name + .empty()) { + resource_names.insert( + default_filter_chain_->http_connection_manager.route_config_name); + } + std::reverse( + default_filter_chain_->http_connection_manager.http_filters.begin(), + default_filter_chain_->http_connection_manager.http_filters.end()); } // Reverse the lists of HTTP filters in all the filter chains for (auto* filter_chain_data : filter_chain_data_set) { - auto& hcm = filter_chain_data->http_connection_manager; - std::reverse(hcm.http_filters.begin(), hcm.http_filters.end()); + std::reverse( + filter_chain_data->http_connection_manager.http_filters.begin(), + filter_chain_data->http_connection_manager.http_filters.end()); } // Start watching on referenced RDS resources struct WatcherToStart { @@ -1072,29 +1076,27 @@ absl::StatusOr XdsServerConfigFetcher::ListenerWatcher:: filters.push_back(&kServerConfigSelectorFilter); channel_stack_modifier = MakeRefCounted(std::move(filters)); - Match( - filter_chain->http_connection_manager.route_config, - // RDS resource name - [&](const std::string& rds_name) { - absl::StatusOr initial_resource; - { - MutexLock lock(&mu_); - initial_resource = rds_map_[rds_name].rds_update.value(); - } - server_config_selector_provider = - MakeRefCounted( - xds_client_->Ref(DEBUG_LOCATION, - "DynamicXdsServerConfigSelectorProvider"), - rds_name, std::move(initial_resource), - filter_chain->http_connection_manager.http_filters); - }, - // inline RouteConfig - [&](const XdsRouteConfigResource& route_config) { - server_config_selector_provider = - MakeRefCounted( - route_config, - filter_chain->http_connection_manager.http_filters); - }); + if (filter_chain->http_connection_manager.rds_update.has_value()) { + server_config_selector_provider = + MakeRefCounted( + filter_chain->http_connection_manager.rds_update.value(), + filter_chain->http_connection_manager.http_filters); + } else { + absl::StatusOr initial_resource; + { + MutexLock lock(&mu_); + initial_resource = + rds_map_[filter_chain->http_connection_manager.route_config_name] + .rds_update.value(); + } + server_config_selector_provider = + MakeRefCounted( + xds_client_->Ref(DEBUG_LOCATION, + "DynamicXdsServerConfigSelectorProvider"), + filter_chain->http_connection_manager.route_config_name, + std::move(initial_resource), + filter_chain->http_connection_manager.http_filters); + } args = args.SetObject(server_config_selector_provider) .SetObject(channel_stack_modifier); // Add XdsCertificateProvider if credentials are xDS. diff --git a/src/proto/grpc/testing/xds/v3/address.proto b/src/proto/grpc/testing/xds/v3/address.proto index df0c90acd4c..47efbed8e06 100644 --- a/src/proto/grpc/testing/xds/v3/address.proto +++ b/src/proto/grpc/testing/xds/v3/address.proto @@ -24,12 +24,6 @@ import "google/protobuf/wrappers.proto"; // [#next-free-field: 7] message SocketAddress { - enum Protocol { - TCP = 0; - UDP = 1; - } - Protocol protocol = 1; - // The address for this socket. :ref:`Listeners ` will bind // to the address. An empty address is not allowed. Specify ``0.0.0.0`` or ``::`` // to bind to any address. [#comment:TODO(zuercher) reinstate when implemented: diff --git a/src/proto/grpc/testing/xds/v3/route.proto b/src/proto/grpc/testing/xds/v3/route.proto index 8a8a2cec681..b06fbc71946 100644 --- a/src/proto/grpc/testing/xds/v3/route.proto +++ b/src/proto/grpc/testing/xds/v3/route.proto @@ -426,8 +426,6 @@ message HeaderMatcher { // // * The suffix *abcd* matches the value *xyzabcd*, but not for *xyzbcd*. string suffix_match = 10; - - string contains_match = 12; } // If specified, the match result will be inverted before checking. Defaults to false. diff --git a/test/core/xds/BUILD b/test/core/xds/BUILD index a69a0e3a439..1a086416e47 100644 --- a/test/core/xds/BUILD +++ b/test/core/xds/BUILD @@ -107,7 +107,6 @@ grpc_cc_test( srcs = ["xds_lb_policy_registry_test.cc"], external_deps = ["gtest"], language = "C++", - uses_event_engine = False, uses_polling = False, deps = [ "//:gpr", @@ -175,47 +174,6 @@ grpc_cc_test( ], ) -grpc_cc_test( - name = "xds_http_filters_test", - srcs = ["xds_http_filters_test.cc"], - external_deps = ["gtest"], - language = "C++", - uses_event_engine = False, - uses_polling = False, - deps = [ - "//:gpr", - "//:grpc", - "//src/proto/grpc/testing/xds/v3:fault_proto", - "//src/proto/grpc/testing/xds/v3:http_filter_rbac_proto", - "//src/proto/grpc/testing/xds/v3:router_proto", - "//test/core/util:grpc_test_util", - "//test/cpp/util:grpc_cli_utils", - ], -) - -grpc_cc_test( - name = "xds_listener_resource_type_test", - srcs = ["xds_listener_resource_type_test.cc"], - external_deps = ["gtest"], - language = "C++", - uses_event_engine = False, - uses_polling = False, - deps = [ - "//:gpr", - "//:grpc", - "//src/core:grpc_xds_client", - "//src/proto/grpc/testing/xds/v3:fault_proto", - "//src/proto/grpc/testing/xds/v3:http_connection_manager_proto", - "//src/proto/grpc/testing/xds/v3:http_filter_rbac_proto", - "//src/proto/grpc/testing/xds/v3:listener_proto", - "//src/proto/grpc/testing/xds/v3:router_proto", - "//src/proto/grpc/testing/xds/v3:tls_proto", - "//src/proto/grpc/testing/xds/v3:typed_struct_proto", - "//test/core/util:grpc_test_util", - "//test/cpp/util:grpc_cli_utils", - ], -) - grpc_cc_test( name = "xds_cluster_resource_type_test", srcs = ["xds_cluster_resource_type_test.cc"], @@ -230,7 +188,6 @@ grpc_cc_test( "//src/proto/grpc/testing/xds/v3:aggregate_cluster_proto", "//src/proto/grpc/testing/xds/v3:cluster_proto", "//src/proto/grpc/testing/xds/v3:tls_proto", - "//src/proto/grpc/testing/xds/v3:typed_struct_proto", "//test/core/util:grpc_test_util", ], ) diff --git a/test/core/xds/xds_cluster_resource_type_test.cc b/test/core/xds/xds_cluster_resource_type_test.cc index 1924c6acdac..8622ac7adfc 100644 --- a/test/core/xds/xds_cluster_resource_type_test.cc +++ b/test/core/xds/xds_cluster_resource_type_test.cc @@ -53,7 +53,6 @@ #include "src/proto/grpc/testing/xds/v3/endpoint.pb.h" #include "src/proto/grpc/testing/xds/v3/outlier_detection.pb.h" #include "src/proto/grpc/testing/xds/v3/tls.pb.h" -#include "src/proto/grpc/testing/xds/v3/typed_struct.pb.h" #include "test/core/util/test_config.h" using envoy::config::cluster::v3::Cluster; @@ -810,7 +809,7 @@ TEST_F(TlsConfigTest, UnknownCertificateProviderInstance) { << decode_result.resource.status(); } -TEST_F(TlsConfigTest, UnknownTransportSocketType) { +TEST_F(TlsConfigTest, TransportSocketTypedConfigUnset) { Cluster cluster; cluster.set_name("foo"); cluster.set_type(cluster.EDS); @@ -828,13 +827,13 @@ TEST_F(TlsConfigTest, UnknownTransportSocketType) { absl::StatusCode::kInvalidArgument); EXPECT_EQ(decode_result.resource.status().message(), "errors validating Cluster resource: [" - "field:transport_socket.typed_config.value[" - "envoy.config.cluster.v3.Cluster].type_url " - "error:unsupported transport socket type]") + "field:transport_socket.typed_config.type_url " + "error:unrecognized transport socket type: " + "envoy.config.cluster.v3.Cluster]") << decode_result.resource.status(); } -TEST_F(TlsConfigTest, UnparseableUpstreamTlsContext) { +TEST_F(TlsConfigTest, UnknownTransportSocketType) { Cluster cluster; cluster.set_name("foo"); cluster.set_type(cluster.EDS); @@ -860,35 +859,6 @@ TEST_F(TlsConfigTest, UnparseableUpstreamTlsContext) { << decode_result.resource.status(); } -TEST_F(TlsConfigTest, UpstreamTlsContextInTypedStruct) { - Cluster cluster; - cluster.set_name("foo"); - cluster.set_type(cluster.EDS); - cluster.mutable_eds_cluster_config()->mutable_eds_config()->mutable_self(); - auto* transport_socket = cluster.mutable_transport_socket(); - xds::type::v3::TypedStruct typed_struct; - typed_struct.set_type_url( - "types.googleapis.com/" - "envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext"); - transport_socket->mutable_typed_config()->PackFrom(typed_struct); - std::string serialized_resource; - ASSERT_TRUE(cluster.SerializeToString(&serialized_resource)); - auto* resource_type = XdsClusterResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - ASSERT_TRUE(decode_result.name.has_value()); - EXPECT_EQ(*decode_result.name, "foo"); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating Cluster resource: [" - "field:transport_socket.typed_config.value[" - "xds.type.v3.TypedStruct].value[" - "envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext] " - "error:can't decode UpstreamTlsContext]") - << decode_result.resource.status(); -} - TEST_F(TlsConfigTest, CaCertProviderUnset) { Cluster cluster; cluster.set_name("foo"); diff --git a/test/core/xds/xds_http_filters_test.cc b/test/core/xds/xds_http_filters_test.cc deleted file mode 100644 index 9ea97797f4b..00000000000 --- a/test/core/xds/xds_http_filters_test.cc +++ /dev/null @@ -1,924 +0,0 @@ -// -// Copyright 2022 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/xds/xds_http_filters.h" - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "absl/status/status.h" -#include "absl/strings/strip.h" -#include "absl/types/variant.h" -#include "gtest/gtest.h" -#include "upb/upb.hpp" - -#include -#include -#include - -#include "src/core/ext/filters/fault_injection/fault_injection_filter.h" -#include "src/core/ext/filters/fault_injection/service_config_parser.h" -#include "src/core/ext/filters/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" -#include "src/proto/grpc/testing/xds/v3/fault.pb.h" -#include "src/proto/grpc/testing/xds/v3/fault_common.pb.h" -#include "src/proto/grpc/testing/xds/v3/http_filter_rbac.pb.h" -#include "src/proto/grpc/testing/xds/v3/metadata.pb.h" -#include "src/proto/grpc/testing/xds/v3/path.pb.h" -#include "src/proto/grpc/testing/xds/v3/percent.pb.h" -#include "src/proto/grpc/testing/xds/v3/range.pb.h" -#include "src/proto/grpc/testing/xds/v3/rbac.pb.h" -#include "src/proto/grpc/testing/xds/v3/regex.pb.h" -#include "src/proto/grpc/testing/xds/v3/route.pb.h" -#include "src/proto/grpc/testing/xds/v3/router.pb.h" -#include "src/proto/grpc/testing/xds/v3/string.pb.h" -#include "test/core/util/test_config.h" - -namespace grpc_core { -namespace testing { -namespace { - -using ::envoy::extensions::filters::http::fault::v3::HTTPFault; -using ::envoy::extensions::filters::http::rbac::v3::RBAC; -using ::envoy::extensions::filters::http::rbac::v3::RBACPerRoute; -using ::envoy::extensions::filters::http::router::v3::Router; - -// -// base class for filter tests -// - -class XdsHttpFilterTest : public ::testing::Test { - protected: - XdsHttpFilterTest() { - // Start with a clean registry for each test. - XdsHttpFilterRegistry::Shutdown(); - XdsHttpFilterRegistry::Init(); - } - - XdsExtension MakeXdsExtension(const google::protobuf::Message& message) { - any_storage_.PackFrom(message); - absl::string_view type = - absl::StripPrefix(any_storage_.type_url(), "type.googleapis.com/"); - ValidationErrors::ScopedField field( - &errors_, absl::StrCat("http_filter.value[", type, "]")); - XdsExtension extension; - extension.type = type; - extension.value = absl::string_view(any_storage_.value()); - extension.validation_fields.push_back(std::move(field)); - return extension; - } - - static const XdsHttpFilterImpl* GetFilter(absl::string_view type) { - return XdsHttpFilterRegistry::GetFilterForType( - absl::StripPrefix(type, "type.googleapis.com/")); - } - - ValidationErrors errors_; - upb::Arena arena_; - google::protobuf::Any any_storage_; -}; - -// -// XdsHttpFilterRegistry tests -// - -using XdsHttpFilterRegistryTest = XdsHttpFilterTest; - -TEST_F(XdsHttpFilterRegistryTest, Basic) { - // Start with an empty registry. - XdsHttpFilterRegistry::Shutdown(); - XdsHttpFilterRegistry::Init(/*register_builtins=*/false); - // Returns null when a filter has not yet been registered. - XdsExtension extension = MakeXdsExtension(Router()); - EXPECT_EQ(GetFilter(extension.type), nullptr); - // Now register the filter. - auto filter = std::make_unique(); - auto* filter_ptr = filter.get(); - XdsHttpFilterRegistry::RegisterFilter(std::move(filter)); - // And check that it is now present. - EXPECT_EQ(GetFilter(extension.type), filter_ptr); -} - -using XdsHttpFilterRegistryDeathTest = XdsHttpFilterTest; - -TEST_F(XdsHttpFilterRegistryDeathTest, DuplicateRegistryFails) { - GTEST_FLAG_SET(death_test_style, "threadsafe"); - ASSERT_DEATH( - // The router filter is already in the registry. - XdsHttpFilterRegistry::RegisterFilter( - std::make_unique()), - ""); -} - -// -// Router filter tests -// - -class XdsRouterFilterTest : public XdsHttpFilterTest { - protected: - XdsRouterFilterTest() { - XdsExtension extension = MakeXdsExtension(Router()); - filter_ = GetFilter(extension.type); - GPR_ASSERT(filter_ != nullptr); - } - - const XdsHttpFilterImpl* filter_; -}; - -TEST_F(XdsRouterFilterTest, Accessors) { - EXPECT_EQ(filter_->ConfigProtoName(), - "envoy.extensions.filters.http.router.v3.Router"); - EXPECT_EQ(filter_->OverrideConfigProtoName(), ""); - EXPECT_EQ(filter_->channel_filter(), nullptr); - EXPECT_TRUE(filter_->IsSupportedOnClients()); - EXPECT_TRUE(filter_->IsSupportedOnServers()); - EXPECT_TRUE(filter_->IsTerminalFilter()); -} - -TEST_F(XdsRouterFilterTest, GenerateFilterConfig) { - XdsExtension extension = MakeXdsExtension(Router()); - auto config = filter_->GenerateFilterConfig(std::move(extension), - arena_.ptr(), &errors_); - ASSERT_TRUE(errors_.ok()) << errors_.status("unexpected errors"); - ASSERT_TRUE(config.has_value()); - EXPECT_EQ(config->config_proto_type_name, filter_->ConfigProtoName()); - EXPECT_EQ(config->config, Json()) << config->config.Dump(); -} - -TEST_F(XdsRouterFilterTest, GenerateFilterConfigTypedStruct) { - XdsExtension extension = MakeXdsExtension(Router()); - extension.value = Json(); - auto config = filter_->GenerateFilterConfig(std::move(extension), - arena_.ptr(), &errors_); - absl::Status status = errors_.status("errors validating filter config"); - EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ( - status.message(), - "errors validating filter config: [" - "field:http_filter.value[envoy.extensions.filters.http.router.v3.Router] " - "error:could not parse router filter config]") - << status; -} - -TEST_F(XdsRouterFilterTest, GenerateFilterConfigUnparseable) { - XdsExtension extension = MakeXdsExtension(Router()); - std::string serialized_resource("\0", 1); - extension.value = absl::string_view(serialized_resource); - auto config = filter_->GenerateFilterConfig(std::move(extension), - arena_.ptr(), &errors_); - absl::Status status = errors_.status("errors validating filter config"); - EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ( - status.message(), - "errors validating filter config: [" - "field:http_filter.value[envoy.extensions.filters.http.router.v3.Router] " - "error:could not parse router filter config]") - << status; -} - -TEST_F(XdsRouterFilterTest, GenerateFilterConfigOverride) { - XdsExtension extension = MakeXdsExtension(Router()); - auto config = filter_->GenerateFilterConfigOverride(std::move(extension), - arena_.ptr(), &errors_); - absl::Status status = errors_.status("errors validating filter config"); - EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ( - status.message(), - "errors validating filter config: [" - "field:http_filter.value[envoy.extensions.filters.http.router.v3.Router] " - "error:router filter does not support config override]") - << status; -} - -// -// Fault injection filter tests -// - -class XdsFaultInjectionFilterTest : public XdsHttpFilterTest { - protected: - XdsFaultInjectionFilterTest() { - XdsExtension extension = MakeXdsExtension(HTTPFault()); - filter_ = GetFilter(extension.type); - GPR_ASSERT(filter_ != nullptr); - } - - const XdsHttpFilterImpl* filter_; -}; - -TEST_F(XdsFaultInjectionFilterTest, Accessors) { - EXPECT_EQ(filter_->ConfigProtoName(), - "envoy.extensions.filters.http.fault.v3.HTTPFault"); - EXPECT_EQ(filter_->OverrideConfigProtoName(), ""); - EXPECT_EQ(filter_->channel_filter(), &FaultInjectionFilter::kFilter); - EXPECT_TRUE(filter_->IsSupportedOnClients()); - EXPECT_FALSE(filter_->IsSupportedOnServers()); - EXPECT_FALSE(filter_->IsTerminalFilter()); -} - -TEST_F(XdsFaultInjectionFilterTest, ModifyChannelArgs) { - ChannelArgs args = filter_->ModifyChannelArgs(ChannelArgs()); - auto value = args.GetInt(GRPC_ARG_PARSE_FAULT_INJECTION_METHOD_CONFIG); - ASSERT_TRUE(value.has_value()); - EXPECT_EQ(*value, 1); -} - -TEST_F(XdsFaultInjectionFilterTest, GenerateServiceConfigTopLevelConfig) { - XdsHttpFilterImpl::FilterConfig config; - config.config = Json::Object{{"foo", "bar"}}; - auto service_config = filter_->GenerateServiceConfig(config, nullptr); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - EXPECT_EQ(service_config->service_config_field_name, "faultInjectionPolicy"); - EXPECT_EQ(service_config->element, "{\"foo\":\"bar\"}"); -} - -TEST_F(XdsFaultInjectionFilterTest, GenerateServiceConfigOverrideConfig) { - XdsHttpFilterImpl::FilterConfig top_config; - top_config.config = Json::Object{{"foo", "bar"}}; - XdsHttpFilterImpl::FilterConfig override_config; - override_config.config = Json::Object{{"baz", "quux"}}; - auto service_config = - filter_->GenerateServiceConfig(top_config, &override_config); - ASSERT_TRUE(service_config.ok()) << service_config.status(); - EXPECT_EQ(service_config->service_config_field_name, "faultInjectionPolicy"); - EXPECT_EQ(service_config->element, "{\"baz\":\"quux\"}"); -} - -// For the fault injection filter, GenerateFilterConfig() and -// GenerateFilterConfigOverride() accept the same input, so we want to -// run all tests for both. -class XdsFaultInjectionFilterConfigTest - : public XdsFaultInjectionFilterTest, - public ::testing::WithParamInterface { - protected: - absl::optional GenerateConfig( - XdsExtension extension) { - if (GetParam()) { - return filter_->GenerateFilterConfigOverride(std::move(extension), - arena_.ptr(), &errors_); - } - return filter_->GenerateFilterConfig(std::move(extension), arena_.ptr(), - &errors_); - } -}; - -INSTANTIATE_TEST_SUITE_P(XdsFaultFilter, XdsFaultInjectionFilterConfigTest, - ::testing::Bool()); - -TEST_P(XdsFaultInjectionFilterConfigTest, EmptyConfig) { - XdsExtension extension = MakeXdsExtension(HTTPFault()); - auto config = GenerateConfig(std::move(extension)); - ASSERT_TRUE(errors_.ok()) << errors_.status("unexpected errors"); - ASSERT_TRUE(config.has_value()); - EXPECT_EQ(config->config_proto_type_name, filter_->ConfigProtoName()); - EXPECT_EQ(config->config, Json(Json::Object())) << config->config.Dump(); -} - -TEST_P(XdsFaultInjectionFilterConfigTest, BasicConfig) { - HTTPFault fault; - auto* abort = fault.mutable_abort(); - abort->set_grpc_status(GRPC_STATUS_UNAVAILABLE); - abort->mutable_percentage()->set_numerator(75); - auto* delay = fault.mutable_delay(); - auto* fixed_delay = delay->mutable_fixed_delay(); - fixed_delay->set_seconds(1); - fixed_delay->set_nanos(500000000); - delay->mutable_percentage()->set_numerator(25); - fault.mutable_max_active_faults()->set_value(10); - XdsExtension extension = MakeXdsExtension(fault); - auto config = GenerateConfig(std::move(extension)); - ASSERT_TRUE(errors_.ok()) << errors_.status("unexpected errors"); - ASSERT_TRUE(config.has_value()); - EXPECT_EQ(config->config_proto_type_name, filter_->ConfigProtoName()); - EXPECT_EQ(config->config.Dump(), - "{\"abortCode\":\"UNAVAILABLE\"," - "\"abortPercentageDenominator\":100," - "\"abortPercentageNumerator\":75," - "\"delay\":\"1.500000000s\"," - "\"delayPercentageDenominator\":100," - "\"delayPercentageNumerator\":25," - "\"maxFaults\":10}"); -} - -TEST_P(XdsFaultInjectionFilterConfigTest, HttpAbortCode) { - HTTPFault fault; - auto* abort = fault.mutable_abort(); - abort->set_http_status(404); - XdsExtension extension = MakeXdsExtension(fault); - auto config = GenerateConfig(std::move(extension)); - ASSERT_TRUE(errors_.ok()) << errors_.status("unexpected errors"); - ASSERT_TRUE(config.has_value()); - EXPECT_EQ(config->config_proto_type_name, filter_->ConfigProtoName()); - EXPECT_EQ(config->config.Dump(), "{\"abortCode\":\"UNIMPLEMENTED\"}"); -} - -TEST_P(XdsFaultInjectionFilterConfigTest, HeaderAbortAndDelay) { - HTTPFault fault; - fault.mutable_abort()->mutable_header_abort(); - fault.mutable_delay()->mutable_header_delay(); - XdsExtension extension = MakeXdsExtension(fault); - auto config = GenerateConfig(std::move(extension)); - ASSERT_TRUE(errors_.ok()) << errors_.status("unexpected errors"); - ASSERT_TRUE(config.has_value()); - EXPECT_EQ(config->config_proto_type_name, filter_->ConfigProtoName()); - EXPECT_EQ( - config->config.Dump(), - "{\"abortCode\":\"OK\"," - "\"abortCodeHeader\":\"x-envoy-fault-abort-grpc-request\"," - "\"abortPercentageHeader\":\"x-envoy-fault-abort-percentage\"," - "\"delayHeader\":\"x-envoy-fault-delay-request\"," - "\"delayPercentageHeader\":\"x-envoy-fault-delay-request-percentage\"}"); -} - -TEST_P(XdsFaultInjectionFilterConfigTest, InvalidGrpcStatusCode) { - HTTPFault fault; - fault.mutable_abort()->set_grpc_status(17); - XdsExtension extension = MakeXdsExtension(fault); - auto config = GenerateConfig(std::move(extension)); - absl::Status status = errors_.status("errors validating filter config"); - EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ(status.message(), - "errors validating filter config: [" - "field:http_filter.value[envoy.extensions.filters.http.fault.v3" - ".HTTPFault].abort.grpc_status " - "error:invalid gRPC status code: 17]") - << status; -} - -TEST_P(XdsFaultInjectionFilterConfigTest, InvalidDuration) { - HTTPFault fault; - fault.mutable_delay()->mutable_fixed_delay()->set_seconds(315576000001); - XdsExtension extension = MakeXdsExtension(fault); - auto config = GenerateConfig(std::move(extension)); - absl::Status status = errors_.status("errors validating filter config"); - EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ(status.message(), - "errors validating filter config: [" - "field:http_filter.value[envoy.extensions.filters.http.fault.v3" - ".HTTPFault].delay.fixed_delay.seconds " - "error:value must be in the range [0, 315576000000]]") - << status; -} - -TEST_P(XdsFaultInjectionFilterConfigTest, TypedStruct) { - XdsExtension extension = MakeXdsExtension(HTTPFault()); - extension.value = Json(); - auto config = GenerateConfig(std::move(extension)); - absl::Status status = errors_.status("errors validating filter config"); - EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ(status.message(), - "errors validating filter config: [" - "field:http_filter.value[envoy.extensions.filters.http.fault.v3" - ".HTTPFault] error:could not parse fault injection filter config]") - << status; -} - -TEST_P(XdsFaultInjectionFilterConfigTest, Unparseable) { - XdsExtension extension = MakeXdsExtension(HTTPFault()); - std::string serialized_resource("\0", 1); - extension.value = absl::string_view(serialized_resource); - auto config = GenerateConfig(std::move(extension)); - absl::Status status = errors_.status("errors validating filter config"); - EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ(status.message(), - "errors validating filter config: [" - "field:http_filter.value[envoy.extensions.filters.http.fault.v3" - ".HTTPFault] error:could not parse fault injection filter config]") - << status; -} - -// -// RBAC filter tests -// - -class XdsRbacFilterTest : public XdsHttpFilterTest { - protected: - XdsRbacFilterTest() { - XdsExtension extension = MakeXdsExtension(RBAC()); - filter_ = GetFilter(extension.type); - GPR_ASSERT(filter_ != nullptr); - } - - const XdsHttpFilterImpl* filter_; -}; - -TEST_F(XdsRbacFilterTest, Accessors) { - EXPECT_EQ(filter_->ConfigProtoName(), - "envoy.extensions.filters.http.rbac.v3.RBAC"); - EXPECT_EQ(filter_->OverrideConfigProtoName(), - "envoy.extensions.filters.http.rbac.v3.RBACPerRoute"); - EXPECT_EQ(filter_->channel_filter(), &RbacFilter::kFilterVtable); - EXPECT_FALSE(filter_->IsSupportedOnClients()); - EXPECT_TRUE(filter_->IsSupportedOnServers()); - EXPECT_FALSE(filter_->IsTerminalFilter()); -} - -TEST_F(XdsRbacFilterTest, ModifyChannelArgs) { - ChannelArgs args = filter_->ModifyChannelArgs(ChannelArgs()); - auto value = args.GetInt(GRPC_ARG_PARSE_RBAC_METHOD_CONFIG); - ASSERT_TRUE(value.has_value()); - EXPECT_EQ(*value, 1); -} - -TEST_F(XdsRbacFilterTest, GenerateFilterConfig) { - XdsExtension extension = MakeXdsExtension(RBAC()); - auto config = filter_->GenerateFilterConfig(std::move(extension), - arena_.ptr(), &errors_); - ASSERT_TRUE(errors_.ok()) << errors_.status("unexpected errors"); - ASSERT_TRUE(config.has_value()); - EXPECT_EQ(config->config_proto_type_name, filter_->ConfigProtoName()); - EXPECT_EQ(config->config, Json(Json::Object())) << config->config.Dump(); -} - -TEST_F(XdsRbacFilterTest, GenerateFilterConfigTypedStruct) { - XdsExtension extension = MakeXdsExtension(RBAC()); - extension.value = Json(); - auto config = filter_->GenerateFilterConfig(std::move(extension), - arena_.ptr(), &errors_); - absl::Status status = errors_.status("errors validating filter config"); - EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ( - status.message(), - "errors validating filter config: [" - "field:http_filter.value[envoy.extensions.filters.http.rbac.v3.RBAC] " - "error:could not parse HTTP RBAC filter config]") - << status; -} - -TEST_F(XdsRbacFilterTest, GenerateFilterConfigUnparseable) { - XdsExtension extension = MakeXdsExtension(RBAC()); - std::string serialized_resource("\0", 1); - extension.value = absl::string_view(serialized_resource); - auto config = filter_->GenerateFilterConfig(std::move(extension), - arena_.ptr(), &errors_); - absl::Status status = errors_.status("errors validating filter config"); - EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ( - status.message(), - "errors validating filter config: [" - "field:http_filter.value[envoy.extensions.filters.http.rbac.v3.RBAC] " - "error:could not parse HTTP RBAC filter config]") - << status; -} - -TEST_F(XdsRbacFilterTest, GenerateFilterConfigOverride) { - XdsExtension extension = MakeXdsExtension(RBACPerRoute()); - auto config = filter_->GenerateFilterConfigOverride(std::move(extension), - arena_.ptr(), &errors_); - ASSERT_TRUE(errors_.ok()) << errors_.status("unexpected errors"); - ASSERT_TRUE(config.has_value()); - EXPECT_EQ(config->config_proto_type_name, filter_->OverrideConfigProtoName()); - EXPECT_EQ(config->config, Json(Json::Object())) << config->config.Dump(); -} - -TEST_F(XdsRbacFilterTest, GenerateFilterConfigOverrideTypedStruct) { - XdsExtension extension = MakeXdsExtension(RBACPerRoute()); - extension.value = Json(); - auto config = filter_->GenerateFilterConfigOverride(std::move(extension), - arena_.ptr(), &errors_); - absl::Status status = errors_.status("errors validating filter config"); - EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ(status.message(), - "errors validating filter config: [" - "field:http_filter.value[envoy.extensions.filters.http.rbac.v3" - ".RBACPerRoute] error:could not parse RBACPerRoute]") - << status; -} - -TEST_F(XdsRbacFilterTest, GenerateFilterConfigOverrideUnparseable) { - XdsExtension extension = MakeXdsExtension(RBACPerRoute()); - std::string serialized_resource("\0", 1); - extension.value = absl::string_view(serialized_resource); - auto config = filter_->GenerateFilterConfigOverride(std::move(extension), - arena_.ptr(), &errors_); - absl::Status status = errors_.status("errors validating filter config"); - EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ(status.message(), - "errors validating filter config: [" - "field:http_filter.value[envoy.extensions.filters.http.rbac.v3" - ".RBACPerRoute] error:could not parse RBACPerRoute]") - << status; -} - -// For the RBAC filter, the override config is a superset of the -// top-level config, so we test all of the common fields as input for -// both GenerateFilterConfig() and GenerateFilterConfigOverride(). -class XdsRbacFilterConfigTest : public XdsRbacFilterTest, - public ::testing::WithParamInterface { - protected: - absl::optional GenerateConfig(RBAC rbac) { - if (GetParam()) { - RBACPerRoute rbac_per_route; - *rbac_per_route.mutable_rbac() = rbac; - XdsExtension extension = MakeXdsExtension(rbac_per_route); - return filter_->GenerateFilterConfigOverride(std::move(extension), - arena_.ptr(), &errors_); - } - XdsExtension extension = MakeXdsExtension(rbac); - return filter_->GenerateFilterConfig(std::move(extension), arena_.ptr(), - &errors_); - } - - std::string FieldPrefix() { - return absl::StrCat("http_filter.value[", - (GetParam() ? filter_->OverrideConfigProtoName() - : filter_->ConfigProtoName()), - "]", (GetParam() ? ".rbac" : "")); - } -}; - -INSTANTIATE_TEST_SUITE_P(XdsRbacFilter, XdsRbacFilterConfigTest, - ::testing::Bool()); - -TEST_P(XdsRbacFilterConfigTest, EmptyConfig) { - auto config = GenerateConfig(RBAC()); - ASSERT_TRUE(errors_.ok()) << errors_.status("unexpected errors"); - ASSERT_TRUE(config.has_value()); - EXPECT_EQ(config->config_proto_type_name, - GetParam() ? filter_->OverrideConfigProtoName() - : filter_->ConfigProtoName()); - EXPECT_EQ(config->config, Json(Json::Object())) << config->config.Dump(); -} - -TEST_P(XdsRbacFilterConfigTest, AllPermissionTypes) { - RBAC rbac; - auto* rules = rbac.mutable_rules(); - rules->set_action(rules->ALLOW); - auto& policy = (*rules->mutable_policies())["policy_name"]; - // any - policy.add_permissions()->set_any(true); - // header exact match with invert - auto* header = policy.add_permissions()->mutable_header(); - header->set_name("header_name1"); - header->set_exact_match("exact_match"); - header->set_invert_match(true); - // header regex match - header = policy.add_permissions()->mutable_header(); - header->set_name("header_name2"); - header->mutable_safe_regex_match()->set_regex("regex_match"); - // header range match - header = policy.add_permissions()->mutable_header(); - header->set_name("header_name3"); - auto* range = header->mutable_range_match(); - range->set_start(1); - range->set_end(3); - // header present match - header = policy.add_permissions()->mutable_header(); - header->set_name("header_name4"); - header->set_present_match(true); - // header prefix match - header = policy.add_permissions()->mutable_header(); - header->set_name("header_name5"); - header->set_prefix_match("prefix_match"); - // header suffix match - header = policy.add_permissions()->mutable_header(); - header->set_name("header_name6"); - header->set_suffix_match("suffix_match"); - // header contains match - header = policy.add_permissions()->mutable_header(); - header->set_name("header_name7"); - header->set_contains_match("contains_match"); - // path exact match with ignore_case - auto* string_matcher = - policy.add_permissions()->mutable_url_path()->mutable_path(); - string_matcher->set_exact("exact_match"); - string_matcher->set_ignore_case(true); - // path prefix match - string_matcher = policy.add_permissions()->mutable_url_path()->mutable_path(); - string_matcher->set_prefix("prefix_match"); - // path suffix match - string_matcher = policy.add_permissions()->mutable_url_path()->mutable_path(); - string_matcher->set_suffix("suffix_match"); - // path contains match - string_matcher = policy.add_permissions()->mutable_url_path()->mutable_path(); - string_matcher->set_contains("contains_match"); - // path regex match - string_matcher = policy.add_permissions()->mutable_url_path()->mutable_path(); - string_matcher->mutable_safe_regex()->set_regex("regex_match"); - // destination IP match with prefix len - auto* cidr_range = policy.add_permissions()->mutable_destination_ip(); - cidr_range->set_address_prefix("127.0.0"); - cidr_range->mutable_prefix_len()->set_value(24); - // destination IP match - cidr_range = policy.add_permissions()->mutable_destination_ip(); - cidr_range->set_address_prefix("10.0.0"); - // destination port match - policy.add_permissions()->set_destination_port(1234); - // metadata match - policy.add_permissions()->mutable_metadata(); - // metadata match with invert - policy.add_permissions()->mutable_metadata()->set_invert(true); - // requested server name - string_matcher = policy.add_permissions()->mutable_requested_server_name(); - string_matcher->set_exact("exact_match"); - // not - policy.add_permissions()->mutable_not_rule()->set_any(true); - // and - policy.add_permissions()->mutable_and_rules()->add_rules()->set_any(true); - // or - policy.add_permissions()->mutable_or_rules()->add_rules()->set_any(true); - auto config = GenerateConfig(rbac); - ASSERT_TRUE(errors_.ok()) << errors_.status("unexpected errors"); - ASSERT_TRUE(config.has_value()); - EXPECT_EQ(config->config_proto_type_name, - GetParam() ? filter_->OverrideConfigProtoName() - : filter_->ConfigProtoName()); - EXPECT_EQ(config->config.Dump(), - "{\"rules\":{" - "\"action\":0," - "\"policies\":{" - "\"policy_name\":{" - "\"permissions\":[" - // any - "{\"any\":true}," - // header exact match with invert - "{\"header\":" - "{\"exactMatch\":\"exact_match\",\"invertMatch\":true," - "\"name\":\"header_name1\"}}," - // header regex match - "{\"header\":" - "{\"invertMatch\":false,\"name\":\"header_name2\"," - "\"safeRegexMatch\":{\"regex\":\"regex_match\"}}}," - // header range match - "{\"header\":" - "{\"invertMatch\":false,\"name\":\"header_name3\"," - "\"rangeMatch\":{\"end\":3,\"start\":1}}}," - // header present match - "{\"header\":" - "{\"invertMatch\":false,\"name\":\"header_name4\"," - "\"presentMatch\":true}}," - // header prefix match - "{\"header\":" - "{\"invertMatch\":false,\"name\":\"header_name5\"," - "\"prefixMatch\":\"prefix_match\"}}," - // header suffix match - "{\"header\":" - "{\"invertMatch\":false,\"name\":\"header_name6\"," - "\"suffixMatch\":\"suffix_match\"}}," - // header contains match - "{\"header\":" - "{\"containsMatch\":\"contains_match\",\"invertMatch\":false," - "\"name\":\"header_name7\"}}," - // path exact match with ignore_case - "{\"urlPath\":{\"path\":{" - "\"exact\":\"exact_match\",\"ignoreCase\":true}}}," - // path prefix match - "{\"urlPath\":{\"path\":{" - "\"ignoreCase\":false,\"prefix\":\"prefix_match\"}}}," - // path suffix match - "{\"urlPath\":{\"path\":{" - "\"ignoreCase\":false,\"suffix\":\"suffix_match\"}}}," - // path contains match - "{\"urlPath\":{\"path\":{" - "\"contains\":\"contains_match\",\"ignoreCase\":false}}}," - // path regex match - "{\"urlPath\":{\"path\":{" - "\"ignoreCase\":false,\"safeRegex\":{\"regex\":\"regex_match\"}}}}," - // destination IP match with prefix len - "{\"destinationIp\":{" - "\"addressPrefix\":\"127.0.0\",\"prefixLen\":{\"value\":24}}}," - // destination IP match - "{\"destinationIp\":{\"addressPrefix\":\"10.0.0\"}}," - // destination port match - "{\"destinationPort\":1234}," - // metadata match - "{\"metadata\":{\"invert\":false}}," - // metadata match with invert - "{\"metadata\":{\"invert\":true}}," - // requested server name - "{\"requestedServerName\":{" - "\"exact\":\"exact_match\",\"ignoreCase\":false}}," - // not - "{\"notRule\":{\"any\":true}}," - // and - "{\"andRules\":{\"rules\":[{\"any\":true}]}}," - // or - "{\"orRules\":{\"rules\":[{\"any\":true}]}}" - "]," - "\"principals\":[]" - "}}}}"); -} - -TEST_P(XdsRbacFilterConfigTest, AllPrincipalTypes) { - RBAC rbac; - auto* rules = rbac.mutable_rules(); - rules->set_action(rules->ALLOW); - auto& policy = (*rules->mutable_policies())["policy_name"]; - // any - policy.add_principals()->set_any(true); - // authenticated principal name - // (not testing all possible string matchers here, since they're - // covered in the AllPermissionTypes test above) - auto* string_matcher = policy.add_principals() - ->mutable_authenticated() - ->mutable_principal_name(); - string_matcher->set_exact("exact_match"); - // source IP - auto* cidr_range = policy.add_principals()->mutable_source_ip(); - cidr_range->set_address_prefix("127.0.0"); - // direct remote IP - cidr_range = policy.add_principals()->mutable_direct_remote_ip(); - cidr_range->set_address_prefix("127.0.1"); - // remote IP - cidr_range = policy.add_principals()->mutable_remote_ip(); - cidr_range->set_address_prefix("127.0.2"); - // header match - // (not testing all possible header matchers here, since they're - // covered in the AllPermissionTypes test above) - auto* header = policy.add_principals()->mutable_header(); - header->set_name("header_name1"); - header->set_exact_match("exact_match"); - // path match - // (not testing all possible string matchers here, since they're - // covered in the AllPermissionTypes test above) - string_matcher = policy.add_principals()->mutable_url_path()->mutable_path(); - string_matcher->set_exact("exact_match"); - // metadata match - // (not testing invert here, since it's covered in the AllPermissionTypes - // test above) - policy.add_principals()->mutable_metadata(); - // not - policy.add_principals()->mutable_not_id()->set_any(true); - // and - policy.add_principals()->mutable_and_ids()->add_ids()->set_any(true); - // or - policy.add_principals()->mutable_or_ids()->add_ids()->set_any(true); - auto config = GenerateConfig(rbac); - ASSERT_TRUE(errors_.ok()) << errors_.status("unexpected errors"); - ASSERT_TRUE(config.has_value()); - EXPECT_EQ(config->config_proto_type_name, - GetParam() ? filter_->OverrideConfigProtoName() - : filter_->ConfigProtoName()); - EXPECT_EQ(config->config.Dump(), - "{\"rules\":{" - "\"action\":0," - "\"policies\":{" - "\"policy_name\":{" - "\"permissions\":[]," - "\"principals\":[" - // any - "{\"any\":true}," - // authenticated principal name - "{\"authenticated\":{\"principalName\":{" - "\"exact\":\"exact_match\",\"ignoreCase\":false}}}," - // source IP - "{\"sourceIp\":{\"addressPrefix\":\"127.0.0\"}}," - // direct remote IP - "{\"directRemoteIp\":{\"addressPrefix\":\"127.0.1\"}}," - // remote IP - "{\"remoteIp\":{\"addressPrefix\":\"127.0.2\"}}," - // header exact match with invert - "{\"header\":" - "{\"exactMatch\":\"exact_match\",\"invertMatch\":false," - "\"name\":\"header_name1\"}}," - // path exact match - "{\"urlPath\":{\"path\":{" - "\"exact\":\"exact_match\",\"ignoreCase\":false}}}," - // metadata match - "{\"metadata\":{\"invert\":false}}," - // not - "{\"notId\":{\"any\":true}}," - // and - "{\"andIds\":{\"ids\":[{\"any\":true}]}}," - // or - "{\"orIds\":{\"ids\":[{\"any\":true}]}}" - "]" - "}}}}"); -} - -TEST_P(XdsRbacFilterConfigTest, InvalidFieldsInPolicy) { - RBAC rbac; - auto* rules = rbac.mutable_rules(); - rules->set_action(rules->ALLOW); - auto& policy = (*rules->mutable_policies())["policy_name"]; - policy.mutable_condition(); - policy.mutable_checked_condition(); - auto config = GenerateConfig(rbac); - absl::Status status = errors_.status("errors validating filter config"); - EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ(status.message(), - absl::StrCat("errors validating filter config: [" - "field:", - FieldPrefix(), - ".rules.policies[policy_name].checked_condition " - "error:checked condition not supported; " - "field:", - FieldPrefix(), - ".rules.policies[policy_name].condition " - "error:condition not supported]")) - << status; -} - -TEST_P(XdsRbacFilterConfigTest, InvalidHeaderMatchers) { - RBAC rbac; - auto* rules = rbac.mutable_rules(); - rules->set_action(rules->ALLOW); - auto& policy = (*rules->mutable_policies())["policy_name"]; - auto* header = policy.add_permissions()->mutable_header(); - header->set_name(":scheme"); - header->set_exact_match("exact_match"); - header = policy.add_principals()->mutable_header(); - header->set_name("grpc-foo"); - header->set_exact_match("exact_match"); - header = policy.add_principals()->mutable_header(); - header->set_name("header_name"); - auto config = GenerateConfig(rbac); - absl::Status status = errors_.status("errors validating filter config"); - EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ( - status.message(), - absl::StrCat("errors validating filter config: [" - "field:", - FieldPrefix(), - ".rules.policies[policy_name].permissions[0].header.name " - "error:':scheme' not allowed in header; " - "field:", - FieldPrefix(), - ".rules.policies[policy_name].principals[0].header.name " - "error:'grpc-' prefixes not allowed in header; " - "field:", - FieldPrefix(), - ".rules.policies[policy_name].principals[1].header " - "error:invalid route header matcher specified]")) - << status; -} - -TEST_P(XdsRbacFilterConfigTest, InvalidStringMatchers) { - RBAC rbac; - auto* rules = rbac.mutable_rules(); - rules->set_action(rules->ALLOW); - auto& policy = (*rules->mutable_policies())["policy_name"]; - policy.add_permissions()->mutable_url_path()->mutable_path(); - policy.add_principals()->mutable_url_path(); - auto config = GenerateConfig(rbac); - absl::Status status = errors_.status("errors validating filter config"); - EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ( - status.message(), - absl::StrCat("errors validating filter config: [" - "field:", - FieldPrefix(), - ".rules.policies[policy_name].permissions[0].url_path.path " - "error:invalid match pattern; " - "field:", - FieldPrefix(), - ".rules.policies[policy_name].principals[0].url_path.path " - "error:field not present]")) - << status; -} - -TEST_P(XdsRbacFilterConfigTest, InvalidPermissionAndPrincipal) { - RBAC rbac; - auto* rules = rbac.mutable_rules(); - rules->set_action(rules->ALLOW); - auto& policy = (*rules->mutable_policies())["policy_name"]; - policy.add_permissions(); - policy.add_principals(); - auto config = GenerateConfig(rbac); - absl::Status status = errors_.status("errors validating filter config"); - EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ(status.message(), - absl::StrCat("errors validating filter config: [" - "field:", - FieldPrefix(), - ".rules.policies[policy_name].permissions[0] " - "error:invalid rule; " - "field:", - FieldPrefix(), - ".rules.policies[policy_name].principals[0] " - "error:invalid rule]")) - << status; -} - -} // namespace -} // namespace testing -} // namespace grpc_core - -int main(int argc, char** argv) { - ::testing::InitGoogleTest(&argc, argv); - grpc::testing::TestEnvironment env(&argc, argv); - grpc_init(); - auto result = RUN_ALL_TESTS(); - grpc_shutdown(); - return result; -} diff --git a/test/core/xds/xds_listener_resource_type_test.cc b/test/core/xds/xds_listener_resource_type_test.cc deleted file mode 100644 index ce8206f1fb0..00000000000 --- a/test/core/xds/xds_listener_resource_type_test.cc +++ /dev/null @@ -1,1980 +0,0 @@ -// -// Copyright 2022 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 -#include - -#include -#include -#include - -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/string_view.h" -#include "absl/types/optional.h" -#include "absl/types/variant.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "upb/def.hpp" -#include "upb/upb.hpp" - -#include -#include - -#include "src/core/ext/xds/xds_bootstrap.h" -#include "src/core/ext/xds/xds_bootstrap_grpc.h" -#include "src/core/ext/xds/xds_client.h" -#include "src/core/ext/xds/xds_common_types.h" -#include "src/core/ext/xds/xds_http_filters.h" -#include "src/core/ext/xds/xds_listener.h" -#include "src/core/ext/xds/xds_resource_type.h" -#include "src/core/lib/address_utils/sockaddr_utils.h" -#include "src/core/lib/debug/trace.h" -#include "src/core/lib/gprpp/ref_counted_ptr.h" -#include "src/core/lib/gprpp/time.h" -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/json/json.h" -#include "src/proto/grpc/testing/xds/v3/address.pb.h" -#include "src/proto/grpc/testing/xds/v3/base.pb.h" -#include "src/proto/grpc/testing/xds/v3/config_source.pb.h" -#include "src/proto/grpc/testing/xds/v3/fault.pb.h" -#include "src/proto/grpc/testing/xds/v3/http_connection_manager.pb.h" -#include "src/proto/grpc/testing/xds/v3/http_filter_rbac.pb.h" -#include "src/proto/grpc/testing/xds/v3/listener.pb.h" -#include "src/proto/grpc/testing/xds/v3/protocol.pb.h" -#include "src/proto/grpc/testing/xds/v3/router.pb.h" -#include "src/proto/grpc/testing/xds/v3/string.pb.h" -#include "src/proto/grpc/testing/xds/v3/tls.pb.h" -#include "src/proto/grpc/testing/xds/v3/typed_struct.pb.h" -#include "test/core/util/test_config.h" - -using envoy::config::listener::v3::Listener; -using envoy::extensions::filters::http::fault::v3::HTTPFault; -using envoy::extensions::filters::http::rbac::v3::RBAC; -using envoy::extensions::filters::http::router::v3::Router; -using envoy::extensions::filters::network::http_connection_manager::v3:: - HttpConnectionManager; -using envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext; - -namespace grpc_core { -namespace testing { -namespace { - -TraceFlag xds_listener_resource_type_test_trace( - true, "xds_listener_resource_type_test"); - -class XdsListenerTest : public ::testing::Test { - protected: - XdsListenerTest() - : xds_client_(MakeXdsClient()), - decode_context_{xds_client_.get(), xds_client_->bootstrap().server(), - &xds_listener_resource_type_test_trace, - upb_def_pool_.ptr(), upb_arena_.ptr()} {} - - static RefCountedPtr MakeXdsClient() { - grpc_error_handle error; - auto bootstrap = GrpcXdsBootstrap::Create( - "{\n" - " \"xds_servers\": [\n" - " {\n" - " \"server_uri\": \"xds.example.com\",\n" - " \"channel_creds\": [\n" - " {\"type\": \"google_default\"}\n" - " ]\n" - " }\n" - " ],\n" - " \"certificate_providers\": {\n" - " \"provider1\": {\n" - " \"plugin_name\": \"file_watcher\",\n" - " \"config\": {\n" - " \"certificate_file\": \"/path/to/cert\",\n" - " \"private_key_file\": \"/path/to/key\"\n" - " }\n" - " }\n" - " }\n" - "}"); - if (!bootstrap.ok()) { - gpr_log(GPR_ERROR, "Error parsing bootstrap: %s", - bootstrap.status().ToString().c_str()); - GPR_ASSERT(false); - } - return MakeRefCounted(std::move(*bootstrap), - /*transport_factory=*/nullptr); - } - - RefCountedPtr xds_client_; - upb::DefPool upb_def_pool_; - upb::Arena upb_arena_; - XdsResourceType::DecodeContext decode_context_; -}; - -TEST_F(XdsListenerTest, Definition) { - auto* resource_type = XdsListenerResourceType::Get(); - ASSERT_NE(resource_type, nullptr); - EXPECT_EQ(resource_type->type_url(), "envoy.config.listener.v3.Listener"); - EXPECT_TRUE(resource_type->AllResourcesRequiredInSotW()); -} - -TEST_F(XdsListenerTest, UnparseableProto) { - std::string serialized_resource("\0", 1); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "Can't parse Listener resource.") - << decode_result.resource.status(); -} - -TEST_F(XdsListenerTest, NeitherAddressNotApiListener) { - Listener listener; - listener.set_name("foo"); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "Listener has neither address nor ApiListener") - << decode_result.resource.status(); -} - -// TODO(roth): Re-enable the following test once -// github.com/istio/istio/issues/38914 is resolved. -TEST_F(XdsListenerTest, DISABLED_BothAddressAndApiListener) { - Listener listener; - listener.set_name("foo"); - listener.mutable_api_listener(); - listener.mutable_address(); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "Listener has both address and ApiListener") - << decode_result.resource.status(); -} - -// -// HttpConnectionManager tests -// - -struct HttpConnectionManagerLocation { - bool in_api_listener = false; - - explicit HttpConnectionManagerLocation(bool in_api_listener) - : in_api_listener(in_api_listener) {} - - // For use as the final parameter in INSTANTIATE_TEST_SUITE_P(). - static std::string Name( - const ::testing::TestParamInfo& info) { - return info.param.in_api_listener ? "ApiListener" : "TcpListener"; - } -}; - -// These tests cover common behavior for both API listeners and TCP -// listeners, so we run them in both contexts. -class HttpConnectionManagerTest - : public XdsListenerTest, - public ::testing::WithParamInterface { - protected: - static Listener MakeListener(HttpConnectionManager hcm) { - Listener listener; - listener.set_name("foo"); - if (GetParam().in_api_listener) { - // Client. - listener.mutable_api_listener()->mutable_api_listener()->PackFrom(hcm); - } else { - // Server. - listener.mutable_default_filter_chain() - ->add_filters() - ->mutable_typed_config() - ->PackFrom(hcm); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - } - return listener; - } - - static absl::optional - GetHCMConfig(const XdsListenerResource& resource) { - if (GetParam().in_api_listener) { - // Client. - auto* hcm = absl::get_if( - &resource.listener); - if (hcm == nullptr) return absl::nullopt; - return *hcm; - } - // Server. - auto* tcp_listener = - absl::get_if(&resource.listener); - if (tcp_listener == nullptr) return absl::nullopt; - if (!tcp_listener->default_filter_chain.has_value()) return absl::nullopt; - return tcp_listener->default_filter_chain->http_connection_manager; - } - - static absl::string_view ErrorPrefix() { - // Client. - if (GetParam().in_api_listener) return "errors validating ApiListener: "; - // Server. - return "errors validating server Listener: "; - } - - static absl::string_view FieldPrefix() { - // Client. - if (GetParam().in_api_listener) return "api_listener.api_listener"; - // Server. - return "default_filter_chain.filters[0].typed_config"; - } -}; - -INSTANTIATE_TEST_SUITE_P( - XdsHcm, HttpConnectionManagerTest, - ::testing::Values(HttpConnectionManagerLocation(true), - HttpConnectionManagerLocation(false)), - &HttpConnectionManagerLocation::Name); - -TEST_P(HttpConnectionManagerTest, MinimumValidConfig) { - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - Listener listener = MakeListener(hcm); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - ASSERT_TRUE(decode_result.resource.ok()) << decode_result.resource.status(); - ASSERT_TRUE(decode_result.name.has_value()); - EXPECT_EQ(*decode_result.name, "foo"); - auto& resource = static_cast(**decode_result.resource); - auto http_connection_manager = GetHCMConfig(resource); - ASSERT_TRUE(http_connection_manager.has_value()); - auto* rds_name = - absl::get_if(&http_connection_manager->route_config); - ASSERT_NE(rds_name, nullptr); - EXPECT_EQ(*rds_name, "rds_name"); - ASSERT_EQ(http_connection_manager->http_filters.size(), 1UL); - auto& router = http_connection_manager->http_filters[0]; - EXPECT_EQ(router.name, "router"); - EXPECT_EQ(router.config.config_proto_type_name, - "envoy.extensions.filters.http.router.v3.Router"); - EXPECT_EQ(router.config.config, Json()) << router.config.config.Dump(); - EXPECT_EQ(http_connection_manager->http_max_stream_duration, - Duration::Zero()); -} - -TEST_P(HttpConnectionManagerTest, RdsConfigSourceUsesAds) { - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_ads(); - Listener listener = MakeListener(hcm); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - ASSERT_TRUE(decode_result.resource.ok()) << decode_result.resource.status(); - ASSERT_TRUE(decode_result.name.has_value()); - EXPECT_EQ(*decode_result.name, "foo"); - auto& resource = static_cast(**decode_result.resource); - auto http_connection_manager = GetHCMConfig(resource); - ASSERT_TRUE(http_connection_manager.has_value()); - auto* rds_name = - absl::get_if(&http_connection_manager->route_config); - ASSERT_NE(rds_name, nullptr); - EXPECT_EQ(*rds_name, "rds_name"); - ASSERT_EQ(http_connection_manager->http_filters.size(), 1UL); - auto& router = http_connection_manager->http_filters[0]; - EXPECT_EQ(router.name, "router"); - EXPECT_EQ(router.config.config_proto_type_name, - "envoy.extensions.filters.http.router.v3.Router"); - EXPECT_EQ(router.config.config, Json()) << router.config.config.Dump(); - EXPECT_EQ(http_connection_manager->http_max_stream_duration, - Duration::Zero()); -} - -TEST_P(HttpConnectionManagerTest, NeitherRouteConfigNorRdsName) { - HttpConnectionManager hcm; - hcm.mutable_scoped_routes(); - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - Listener listener = MakeListener(hcm); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ( - decode_result.resource.status().message(), - absl::StrCat(ErrorPrefix(), "[field:", FieldPrefix(), - ".value[" - "envoy.extensions.filters.network.http_connection_manager.v3" - ".HttpConnectionManager] " - "error:neither route_config nor rds fields are present]")) - << decode_result.resource.status(); -} - -TEST_P(HttpConnectionManagerTest, RdsConfigSourceNotAdsOrSelf) { - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->set_path("/foo/bar"); - Listener listener = MakeListener(hcm); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ( - decode_result.resource.status().message(), - absl::StrCat(ErrorPrefix(), "[field:", FieldPrefix(), - ".value[" - "envoy.extensions.filters.network.http_connection_manager.v3" - ".HttpConnectionManager].rds.config_source " - "error:ConfigSource does not specify ADS or SELF]")) - << decode_result.resource.status(); -} - -TEST_P(HttpConnectionManagerTest, RdsConfigSourceNotSet) { - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - Listener listener = MakeListener(hcm); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ( - decode_result.resource.status().message(), - absl::StrCat(ErrorPrefix(), "[field:", FieldPrefix(), - ".value[" - "envoy.extensions.filters.network.http_connection_manager.v3" - ".HttpConnectionManager].rds.config_source " - "error:field not present]")) - << decode_result.resource.status(); -} - -TEST_P(HttpConnectionManagerTest, SetsMaxStreamDuration) { - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - auto* duration = - hcm.mutable_common_http_protocol_options()->mutable_max_stream_duration(); - duration->set_seconds(5); - duration->set_nanos(5000000); - Listener listener = MakeListener(hcm); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - ASSERT_TRUE(decode_result.resource.ok()) << decode_result.resource.status(); - ASSERT_TRUE(decode_result.name.has_value()); - EXPECT_EQ(*decode_result.name, "foo"); - auto& resource = static_cast(**decode_result.resource); - auto http_connection_manager = GetHCMConfig(resource); - ASSERT_TRUE(http_connection_manager.has_value()); - auto* rds_name = - absl::get_if(&http_connection_manager->route_config); - ASSERT_NE(rds_name, nullptr); - EXPECT_EQ(*rds_name, "rds_name"); - ASSERT_EQ(http_connection_manager->http_filters.size(), 1UL); - auto& router = http_connection_manager->http_filters[0]; - EXPECT_EQ(router.name, "router"); - EXPECT_EQ(router.config.config_proto_type_name, - "envoy.extensions.filters.http.router.v3.Router"); - EXPECT_EQ(router.config.config, Json()) << router.config.config.Dump(); - EXPECT_EQ(http_connection_manager->http_max_stream_duration, - Duration::Milliseconds(5005)); -} - -TEST_P(HttpConnectionManagerTest, InvalidMaxStreamDuration) { - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - hcm.mutable_common_http_protocol_options() - ->mutable_max_stream_duration() - ->set_seconds(-1); - Listener listener = MakeListener(hcm); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ( - decode_result.resource.status().message(), - absl::StrCat(ErrorPrefix(), "[field:", FieldPrefix(), - ".value[" - "envoy.extensions.filters.network.http_connection_manager.v3" - ".HttpConnectionManager].common_http_protocol_options" - ".max_stream_duration.seconds " - "error:value must be in the range [0, 315576000000]]")) - << decode_result.resource.status(); -} - -TEST_P(HttpConnectionManagerTest, UnsupportedFieldsSet) { - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - hcm.set_xff_num_trusted_hops(1); - hcm.add_original_ip_detection_extensions(); - Listener listener = MakeListener(hcm); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ( - decode_result.resource.status().message(), - absl::StrCat(ErrorPrefix(), "[field:", FieldPrefix(), - ".value[" - "envoy.extensions.filters.network.http_connection_manager.v3" - ".HttpConnectionManager].original_ip_detection_extensions " - "error:must be empty; field:", - FieldPrefix(), - ".value[" - "envoy.extensions.filters.network.http_connection_manager.v3" - ".HttpConnectionManager].xff_num_trusted_hops " - "error:must be zero]")) - << decode_result.resource.status(); -} - -TEST_P(HttpConnectionManagerTest, EmptyHttpFilterName) { - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - Listener listener = MakeListener(hcm); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ( - decode_result.resource.status().message(), - absl::StrCat(ErrorPrefix(), "[field:", FieldPrefix(), - ".value[" - "envoy.extensions.filters.network.http_connection_manager.v3" - ".HttpConnectionManager].http_filters[0].name " - "error:empty filter name]")) - << decode_result.resource.status(); -} - -TEST_P(HttpConnectionManagerTest, DuplicateHttpFilterName) { - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - *hcm.add_http_filters() = hcm.http_filters(0); // Copy filter. - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - Listener listener = MakeListener(hcm); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ( - decode_result.resource.status().message(), - absl::StrCat(ErrorPrefix(), "[field:", FieldPrefix(), - ".value[" - "envoy.extensions.filters.network.http_connection_manager.v3" - ".HttpConnectionManager].http_filters[1].name " - "error:duplicate HTTP filter name: router]")) - << decode_result.resource.status(); -} - -TEST_P(HttpConnectionManagerTest, HttpFilterMissingConfig) { - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - Listener listener = MakeListener(hcm); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ( - decode_result.resource.status().message(), - absl::StrCat(ErrorPrefix(), "[field:", FieldPrefix(), - ".value[" - "envoy.extensions.filters.network.http_connection_manager.v3" - ".HttpConnectionManager].http_filters[0].typed_config " - "error:field not present]")) - << decode_result.resource.status(); -} - -TEST_P(HttpConnectionManagerTest, HttpFilterMissingConfigButOptional) { - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("foo"); - filter->set_is_optional(true); - filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - Listener listener = MakeListener(hcm); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - ASSERT_TRUE(decode_result.resource.ok()) << decode_result.resource.status(); - ASSERT_TRUE(decode_result.name.has_value()); - EXPECT_EQ(*decode_result.name, "foo"); - auto& resource = static_cast(**decode_result.resource); - auto http_connection_manager = GetHCMConfig(resource); - ASSERT_TRUE(http_connection_manager.has_value()); - ASSERT_EQ(http_connection_manager->http_filters.size(), 1UL); - auto& router = http_connection_manager->http_filters[0]; - EXPECT_EQ(router.name, "router"); - EXPECT_EQ(router.config.config_proto_type_name, - "envoy.extensions.filters.http.router.v3.Router"); - EXPECT_EQ(router.config.config, Json()) << router.config.config.Dump(); -} - -TEST_P(HttpConnectionManagerTest, HttpFilterTypeNotSupported) { - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Listener()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - Listener listener = MakeListener(hcm); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ( - decode_result.resource.status().message(), - absl::StrCat(ErrorPrefix(), "[field:", FieldPrefix(), - ".value[" - "envoy.extensions.filters.network.http_connection_manager.v3" - ".HttpConnectionManager].http_filters[0].typed_config.value[" - "envoy.config.listener.v3.Listener] " - "error:unsupported filter type]")) - << decode_result.resource.status(); -} - -TEST_P(HttpConnectionManagerTest, HttpFilterTypeNotSupportedButOptional) { - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("unsupported"); - filter->mutable_typed_config()->PackFrom(Listener()); - filter->set_is_optional(true); - filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - Listener listener = MakeListener(hcm); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - ASSERT_TRUE(decode_result.resource.ok()) << decode_result.resource.status(); - ASSERT_TRUE(decode_result.name.has_value()); - EXPECT_EQ(*decode_result.name, "foo"); - auto& resource = static_cast(**decode_result.resource); - auto http_connection_manager = GetHCMConfig(resource); - ASSERT_TRUE(http_connection_manager.has_value()); - ASSERT_EQ(http_connection_manager->http_filters.size(), 1UL); - auto& router = http_connection_manager->http_filters[0]; - EXPECT_EQ(router.name, "router"); - EXPECT_EQ(router.config.config_proto_type_name, - "envoy.extensions.filters.http.router.v3.Router"); - EXPECT_EQ(router.config.config, Json()) << router.config.config.Dump(); -} - -TEST_P(HttpConnectionManagerTest, NoHttpFilters) { - HttpConnectionManager hcm; - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - Listener listener = MakeListener(hcm); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ( - decode_result.resource.status().message(), - absl::StrCat(ErrorPrefix(), "[field:", FieldPrefix(), - ".value[" - "envoy.extensions.filters.network.http_connection_manager.v3" - ".HttpConnectionManager].http_filters " - "error:expected at least one HTTP filter]")) - << decode_result.resource.status(); -} - -TEST_P(HttpConnectionManagerTest, TerminalFilterNotLast) { - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - filter = hcm.add_http_filters(); - if (GetParam().in_api_listener) { - // Client. - filter->set_name("fault"); - filter->mutable_typed_config()->PackFrom(HTTPFault()); - } else { - // Server. - filter->set_name("rbac"); - filter->mutable_typed_config()->PackFrom(RBAC()); - } - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - Listener listener = MakeListener(hcm); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ( - decode_result.resource.status().message(), - absl::StrCat(ErrorPrefix(), "[field:", FieldPrefix(), - ".value[" - "envoy.extensions.filters.network.http_connection_manager.v3" - ".HttpConnectionManager].http_filters errors:[" - "terminal filter for config type " - "envoy.extensions.filters.http.router.v3.Router must be the " - "last filter in the chain; " - "non-terminal filter for config type ", - (GetParam().in_api_listener - ? "envoy.extensions.filters.http.fault.v3.HTTPFault" - : "envoy.extensions.filters.http.rbac.v3.RBAC"), - " is the last filter in the chain]]")) - << decode_result.resource.status(); -} - -using HttpConnectionManagerClientOrServerOnlyTest = XdsListenerTest; - -TEST_F(HttpConnectionManagerClientOrServerOnlyTest, - HttpFilterNotSupportedOnClient) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("rbac"); - filter->mutable_typed_config()->PackFrom( - envoy::extensions::filters::http::rbac::v3::RBAC()); - filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - listener.mutable_api_listener()->mutable_api_listener()->PackFrom(hcm); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating ApiListener: [" - "field:api_listener.api_listener.value[" - "envoy.extensions.filters.network.http_connection_manager.v3" - ".HttpConnectionManager].http_filters[0].typed_config.value[" - "envoy.extensions.filters.http.rbac.v3.RBAC] " - "error:filter is not supported on clients]") - << decode_result.resource.status(); -} - -TEST_F(HttpConnectionManagerClientOrServerOnlyTest, - HttpFilterNotSupportedOnClientButOptional) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("rbac"); - filter->mutable_typed_config()->PackFrom( - envoy::extensions::filters::http::rbac::v3::RBAC()); - filter->set_is_optional(true); - filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - listener.mutable_api_listener()->mutable_api_listener()->PackFrom(hcm); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - ASSERT_TRUE(decode_result.resource.ok()) << decode_result.resource.status(); - ASSERT_TRUE(decode_result.name.has_value()); - EXPECT_EQ(*decode_result.name, "foo"); - auto& resource = static_cast(**decode_result.resource); - auto* api_listener = absl::get_if( - &resource.listener); - ASSERT_NE(api_listener, nullptr); - ASSERT_EQ(api_listener->http_filters.size(), 1UL); - auto& router = api_listener->http_filters[0]; - EXPECT_EQ(router.name, "router"); - EXPECT_EQ(router.config.config_proto_type_name, - "envoy.extensions.filters.http.router.v3.Router"); - EXPECT_EQ(router.config.config, Json()) << router.config.config.Dump(); -} - -TEST_F(HttpConnectionManagerClientOrServerOnlyTest, - HttpFilterNotSupportedOnServer) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("fault"); - filter->mutable_typed_config()->PackFrom( - envoy::extensions::filters::http::fault::v3::HTTPFault()); - filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - listener.mutable_default_filter_chain() - ->add_filters() - ->mutable_typed_config() - ->PackFrom(hcm); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating server Listener: [" - "field:default_filter_chain.filters[0].typed_config.value[" - "envoy.extensions.filters.network.http_connection_manager.v3" - ".HttpConnectionManager].http_filters[0].typed_config.value[" - "envoy.extensions.filters.http.fault.v3.HTTPFault] " - "error:filter is not supported on servers]") - << decode_result.resource.status(); -} - -TEST_F(HttpConnectionManagerClientOrServerOnlyTest, - HttpFilterNotSupportedOnServerButOptional) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("fault"); - filter->mutable_typed_config()->PackFrom( - envoy::extensions::filters::http::fault::v3::HTTPFault()); - filter->set_is_optional(true); - filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - listener.mutable_default_filter_chain() - ->add_filters() - ->mutable_typed_config() - ->PackFrom(hcm); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - ASSERT_TRUE(decode_result.resource.ok()) << decode_result.resource.status(); - ASSERT_TRUE(decode_result.name.has_value()); - EXPECT_EQ(*decode_result.name, "foo"); - auto& resource = static_cast(**decode_result.resource); - auto* tcp_listener = - absl::get_if(&resource.listener); - ASSERT_NE(tcp_listener, nullptr); - ASSERT_TRUE(tcp_listener->default_filter_chain.has_value()); - const auto& http_connection_manager = - tcp_listener->default_filter_chain->http_connection_manager; - ASSERT_EQ(http_connection_manager.http_filters.size(), 1UL); - auto& router = http_connection_manager.http_filters[0]; - EXPECT_EQ(router.name, "router"); - EXPECT_EQ(router.config.config_proto_type_name, - "envoy.extensions.filters.http.router.v3.Router"); - EXPECT_EQ(router.config.config, Json()) << router.config.config.Dump(); -} - -// -// API listener tests -// - -using ApiListenerTest = XdsListenerTest; - -TEST_F(ApiListenerTest, InnerApiListenerNotSet) { - Listener listener; - listener.set_name("foo"); - listener.mutable_api_listener(); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating ApiListener: [" - "field:api_listener.api_listener error:field not present]") - << decode_result.resource.status(); -} - -TEST_F(ApiListenerTest, DoesNotContainHttpConnectionManager) { - Listener listener; - listener.set_name("foo"); - listener.mutable_api_listener()->mutable_api_listener()->PackFrom(Listener()); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating ApiListener: [" - "field:api_listener.api_listener.value[" - "envoy.config.listener.v3.Listener] " - "error:unsupported filter type]") - << decode_result.resource.status(); -} - -TEST_F(ApiListenerTest, UnparseableHttpConnectionManagerConfig) { - Listener listener; - listener.set_name("foo"); - auto* any = listener.mutable_api_listener()->mutable_api_listener(); - any->PackFrom(HttpConnectionManager()); - any->set_value(std::string("\0", 1)); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating ApiListener: [" - "field:api_listener.api_listener.value[" - "envoy.extensions.filters.network.http_connection_manager.v3" - ".HttpConnectionManager] " - "error:could not parse HttpConnectionManager config]") - << decode_result.resource.status(); -} - -// -// TCP listener tests -// - -using TcpListenerTest = XdsListenerTest; - -TEST_F(TcpListenerTest, MinimumValidConfig) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - listener.mutable_default_filter_chain() - ->add_filters() - ->mutable_typed_config() - ->PackFrom(hcm); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - ASSERT_TRUE(decode_result.resource.ok()) << decode_result.resource.status(); - ASSERT_TRUE(decode_result.name.has_value()); - EXPECT_EQ(*decode_result.name, "foo"); - auto& resource = static_cast(**decode_result.resource); - auto* tcp_listener = - absl::get_if(&resource.listener); - ASSERT_NE(tcp_listener, nullptr); - EXPECT_EQ(tcp_listener->address, "127.0.0.1:443"); - EXPECT_THAT(tcp_listener->filter_chain_map.destination_ip_vector, - ::testing::ElementsAre()); - ASSERT_TRUE(tcp_listener->default_filter_chain.has_value()); - EXPECT_TRUE( - tcp_listener->default_filter_chain->downstream_tls_context.Empty()); - const auto& http_connection_manager = - tcp_listener->default_filter_chain->http_connection_manager; - auto* rds_name = - absl::get_if(&http_connection_manager.route_config); - ASSERT_NE(rds_name, nullptr); - EXPECT_EQ(*rds_name, "rds_name"); - ASSERT_EQ(http_connection_manager.http_filters.size(), 1UL); - auto& router = http_connection_manager.http_filters[0]; - EXPECT_EQ(router.name, "router"); - EXPECT_EQ(router.config.config_proto_type_name, - "envoy.extensions.filters.http.router.v3.Router"); - EXPECT_EQ(router.config.config, Json()) << router.config.config.Dump(); - EXPECT_EQ(http_connection_manager.http_max_stream_duration, Duration::Zero()); -} - -// TODO(yashkt): Add tests for all interesting combinations of filter -// chain match criteria. -TEST_F(TcpListenerTest, FilterChainMatchCriteria) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - auto* filter_chain = listener.add_filter_chains(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - auto* match = filter_chain->mutable_filter_chain_match(); - auto* cidr_range = match->add_prefix_ranges(); - cidr_range->set_address_prefix("1.2.3.4"); - cidr_range->mutable_prefix_len()->set_value(24); - cidr_range = match->add_source_prefix_ranges(); - cidr_range->set_address_prefix("5.6.7.8"); - cidr_range->mutable_prefix_len()->set_value(16); - match->add_source_ports(1025); - match->set_transport_protocol("raw_buffer"); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - ASSERT_TRUE(decode_result.resource.ok()) << decode_result.resource.status(); - ASSERT_TRUE(decode_result.name.has_value()); - EXPECT_EQ(*decode_result.name, "foo"); - auto& resource = static_cast(**decode_result.resource); - auto* tcp_listener = - absl::get_if(&resource.listener); - ASSERT_NE(tcp_listener, nullptr); - EXPECT_EQ(tcp_listener->address, "127.0.0.1:443"); - EXPECT_FALSE(tcp_listener->default_filter_chain.has_value()); - ASSERT_EQ(tcp_listener->filter_chain_map.destination_ip_vector.size(), 1UL); - auto& dest_ip = tcp_listener->filter_chain_map.destination_ip_vector.front(); - ASSERT_TRUE(dest_ip.prefix_range.has_value()); - auto addr = grpc_sockaddr_to_string(&dest_ip.prefix_range->address, false); - ASSERT_TRUE(addr.ok()) << addr.status(); - EXPECT_EQ(*addr, "1.2.3.0:0"); - EXPECT_EQ(dest_ip.prefix_range->prefix_len, 24); - ASSERT_EQ(dest_ip.source_types_array.size(), 3UL); - EXPECT_THAT(dest_ip.source_types_array[1], ::testing::ElementsAre()); - EXPECT_THAT(dest_ip.source_types_array[2], ::testing::ElementsAre()); - ASSERT_EQ(dest_ip.source_types_array[0].size(), 1UL); - auto& source_ip = dest_ip.source_types_array[0].front(); - ASSERT_TRUE(source_ip.prefix_range.has_value()); - addr = grpc_sockaddr_to_string(&source_ip.prefix_range->address, false); - ASSERT_TRUE(addr.ok()) << addr.status(); - EXPECT_EQ(*addr, "5.6.0.0:0"); - EXPECT_EQ(source_ip.prefix_range->prefix_len, 16); - ASSERT_EQ(source_ip.ports_map.size(), 1UL); - auto it = source_ip.ports_map.begin(); - EXPECT_EQ(it->first, 1025); - ASSERT_NE(it->second.data, nullptr); - auto& filter_data = *it->second.data; - EXPECT_TRUE(filter_data.downstream_tls_context.Empty()); - const auto& http_connection_manager = filter_data.http_connection_manager; - auto* rds_name = - absl::get_if(&http_connection_manager.route_config); - ASSERT_NE(rds_name, nullptr); - EXPECT_EQ(*rds_name, "rds_name"); - ASSERT_EQ(http_connection_manager.http_filters.size(), 1UL); - auto& router = http_connection_manager.http_filters[0]; - EXPECT_EQ(router.name, "router"); - EXPECT_EQ(router.config.config_proto_type_name, - "envoy.extensions.filters.http.router.v3.Router"); - EXPECT_EQ(router.config.config, Json()) << router.config.config.Dump(); - EXPECT_EQ(http_connection_manager.http_max_stream_duration, Duration::Zero()); -} - -TEST_F(TcpListenerTest, SocketAddressNotPresent) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - listener.mutable_default_filter_chain() - ->add_filters() - ->mutable_typed_config() - ->PackFrom(hcm); - listener.mutable_address(); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating server Listener: [" - "field:address.socket_address error:field not present]") - << decode_result.resource.status(); -} - -TEST_F(TcpListenerTest, SocketAddressBadValues) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - listener.mutable_default_filter_chain() - ->add_filters() - ->mutable_typed_config() - ->PackFrom(hcm); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(65536); - address->set_protocol(address->UDP); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating server Listener: [" - "field:address.socket_address.port_value error:invalid port; " - "field:address.socket_address.protocol error:value must be TCP]") - << decode_result.resource.status(); -} - -TEST_F(TcpListenerTest, UseOriginalDstNotSupported) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - listener.mutable_default_filter_chain() - ->add_filters() - ->mutable_typed_config() - ->PackFrom(hcm); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - listener.mutable_use_original_dst()->set_value(true); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating server Listener: [" - "field:use_original_dst error:field not supported]") - << decode_result.resource.status(); -} - -TEST_F(TcpListenerTest, NoFilterChains) { - Listener listener; - listener.set_name("foo"); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating server Listener: [" - "field:default_filter_chain " - "error:must be set if filter_chains is unset]") - << decode_result.resource.status(); -} - -TEST_F(TcpListenerTest, UnsupportedFilter) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - listener.mutable_default_filter_chain() - ->add_filters() - ->mutable_typed_config() - ->PackFrom(Listener()); - listener.mutable_default_filter_chain() - ->add_filters() - ->mutable_typed_config() - ->PackFrom(hcm); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating server Listener: [" - "field:default_filter_chain.filters " - "error:must have exactly one filter (HttpConnectionManager -- " - "no other filter is supported at the moment); " - "field:default_filter_chain.filters[0].typed_config.value[" - "envoy.config.listener.v3.Listener] " - "error:unsupported filter type]") - << decode_result.resource.status(); -} - -TEST_F(TcpListenerTest, BadCidrRanges) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - auto* filter_chain = listener.add_filter_chains(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - auto* match = filter_chain->mutable_filter_chain_match(); - auto* cidr_range = match->add_prefix_ranges(); - cidr_range->set_address_prefix("foobar"); - cidr_range->mutable_prefix_len()->set_value(24); - cidr_range = match->add_source_prefix_ranges(); - cidr_range->set_address_prefix("invalid"); - cidr_range->mutable_prefix_len()->set_value(16); - match->add_source_ports(1025); - match->set_transport_protocol("raw_buffer"); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating server Listener: [" - "field:filter_chains[0].filter_chain_match.prefix_ranges[0]" - ".address_prefix error:Failed to parse address:foobar:0; " - "field:filter_chains[0].filter_chain_match.source_prefix_ranges[0]" - ".address_prefix error:Failed to parse address:invalid:0]") - << decode_result.resource.status(); -} - -TEST_F(TcpListenerTest, DuplicateMatchOnDestinationPrefixRanges) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - auto* filter_chain = listener.add_filter_chains(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - auto* match = filter_chain->mutable_filter_chain_match(); - auto* cidr_range = match->add_prefix_ranges(); - cidr_range->set_address_prefix("1.2.3.4"); - cidr_range->mutable_prefix_len()->set_value(24); - cidr_range = match->add_prefix_ranges(); - cidr_range->set_address_prefix("1.2.3.4"); - cidr_range->mutable_prefix_len()->set_value(16); - filter_chain = listener.add_filter_chains(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - match = filter_chain->mutable_filter_chain_match(); - cidr_range = match->add_prefix_ranges(); - cidr_range->set_address_prefix("1.2.3.4"); - cidr_range->mutable_prefix_len()->set_value(24); - cidr_range = match->add_prefix_ranges(); - cidr_range->set_address_prefix("1.2.3.4"); - cidr_range->mutable_prefix_len()->set_value(32); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating server Listener: [field:filter_chains " - "error:duplicate matching rules detected when adding filter chain: " - "{prefix_ranges={{address_prefix=1.2.3.0:0, prefix_len=24}, " - "{address_prefix=1.2.3.4:0, prefix_len=32}}}]") - << decode_result.resource.status(); -} - -TEST_F(TcpListenerTest, DuplicateMatchOnTransportProtocol) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - auto* filter_chain = listener.add_filter_chains(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - filter_chain->mutable_filter_chain_match()->set_transport_protocol( - "raw_buffer"); - filter_chain = listener.add_filter_chains(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - filter_chain->mutable_filter_chain_match()->set_transport_protocol( - "raw_buffer"); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating server Listener: [field:filter_chains " - "error:duplicate matching rules detected when adding filter chain: " - "{transport_protocol=raw_buffer}]") - << decode_result.resource.status(); -} - -TEST_F(TcpListenerTest, DuplicateMatchOnSourceType) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - auto* filter_chain = listener.add_filter_chains(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - auto* match = filter_chain->mutable_filter_chain_match(); - match->set_source_type(match->SAME_IP_OR_LOOPBACK); - filter_chain = listener.add_filter_chains(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - match = filter_chain->mutable_filter_chain_match(); - match->set_source_type(match->SAME_IP_OR_LOOPBACK); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating server Listener: [field:filter_chains " - "error:duplicate matching rules detected when adding filter chain: " - "{source_type=SAME_IP_OR_LOOPBACK}]") - << decode_result.resource.status(); -} - -TEST_F(TcpListenerTest, DuplicateMatchOnSourcePrefixRanges) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - auto* filter_chain = listener.add_filter_chains(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - auto* match = filter_chain->mutable_filter_chain_match(); - auto* cidr_range = match->add_source_prefix_ranges(); - cidr_range->set_address_prefix("1.2.3.4"); - cidr_range->mutable_prefix_len()->set_value(24); - cidr_range = match->add_source_prefix_ranges(); - cidr_range->set_address_prefix("1.2.3.4"); - cidr_range->mutable_prefix_len()->set_value(16); - filter_chain = listener.add_filter_chains(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - match = filter_chain->mutable_filter_chain_match(); - cidr_range = match->add_source_prefix_ranges(); - cidr_range->set_address_prefix("1.2.3.4"); - cidr_range->mutable_prefix_len()->set_value(24); - cidr_range = match->add_source_prefix_ranges(); - cidr_range->set_address_prefix("1.2.3.4"); - cidr_range->mutable_prefix_len()->set_value(32); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating server Listener: [field:filter_chains " - "error:duplicate matching rules detected when adding filter chain: " - "{source_prefix_ranges={{address_prefix=1.2.3.0:0, prefix_len=24}, " - "{address_prefix=1.2.3.4:0, prefix_len=32}}}]") - << decode_result.resource.status(); -} - -TEST_F(TcpListenerTest, DuplicateMatchOnSourcePort) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - auto* filter_chain = listener.add_filter_chains(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - filter_chain->mutable_filter_chain_match()->add_source_ports(8080); - filter_chain = listener.add_filter_chains(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - filter_chain->mutable_filter_chain_match()->add_source_ports(8080); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating server Listener: [field:filter_chains " - "error:duplicate matching rules detected when adding filter chain: " - "{source_ports={8080}}]") - << decode_result.resource.status(); -} - -TEST_F(TcpListenerTest, DownstreamTlsContext) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - auto* filter_chain = listener.mutable_default_filter_chain(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - auto* transport_socket = filter_chain->mutable_transport_socket(); - transport_socket->set_name("envoy.transport_sockets.tls"); - DownstreamTlsContext downstream_tls_context; - auto* common_tls_context = - downstream_tls_context.mutable_common_tls_context(); - auto* cert_provider = - common_tls_context->mutable_tls_certificate_provider_instance(); - cert_provider->set_instance_name("provider1"); - cert_provider->set_certificate_name("cert_name"); - transport_socket->mutable_typed_config()->PackFrom(downstream_tls_context); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - ASSERT_TRUE(decode_result.resource.ok()) << decode_result.resource.status(); - ASSERT_TRUE(decode_result.name.has_value()); - EXPECT_EQ(*decode_result.name, "foo"); - auto& resource = static_cast(**decode_result.resource); - auto* tcp_listener = - absl::get_if(&resource.listener); - ASSERT_NE(tcp_listener, nullptr); - EXPECT_EQ(tcp_listener->address, "127.0.0.1:443"); - EXPECT_THAT(tcp_listener->filter_chain_map.destination_ip_vector, - ::testing::ElementsAre()); - ASSERT_TRUE(tcp_listener->default_filter_chain.has_value()); - auto& tls_context = - tcp_listener->default_filter_chain->downstream_tls_context; - EXPECT_FALSE(tls_context.require_client_certificate); - auto& cert_provider_instance = - tls_context.common_tls_context.tls_certificate_provider_instance; - EXPECT_EQ(cert_provider_instance.instance_name, "provider1"); - EXPECT_EQ(cert_provider_instance.certificate_name, "cert_name"); - EXPECT_TRUE( - tls_context.common_tls_context.certificate_validation_context.Empty()); -} - -TEST_F(TcpListenerTest, DownstreamTlsContextWithCaCertProviderInstance) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - auto* filter_chain = listener.mutable_default_filter_chain(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - auto* transport_socket = filter_chain->mutable_transport_socket(); - transport_socket->set_name("envoy.transport_sockets.tls"); - DownstreamTlsContext downstream_tls_context; - auto* common_tls_context = - downstream_tls_context.mutable_common_tls_context(); - auto* cert_provider = - common_tls_context->mutable_tls_certificate_provider_instance(); - cert_provider->set_instance_name("provider1"); - cert_provider->set_certificate_name("cert_name"); - cert_provider = common_tls_context->mutable_validation_context() - ->mutable_ca_certificate_provider_instance(); - cert_provider->set_instance_name("provider1"); - cert_provider->set_certificate_name("ca_cert_name"); - transport_socket->mutable_typed_config()->PackFrom(downstream_tls_context); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - ASSERT_TRUE(decode_result.resource.ok()) << decode_result.resource.status(); - ASSERT_TRUE(decode_result.name.has_value()); - EXPECT_EQ(*decode_result.name, "foo"); - auto& resource = static_cast(**decode_result.resource); - auto* tcp_listener = - absl::get_if(&resource.listener); - ASSERT_NE(tcp_listener, nullptr); - EXPECT_EQ(tcp_listener->address, "127.0.0.1:443"); - EXPECT_THAT(tcp_listener->filter_chain_map.destination_ip_vector, - ::testing::ElementsAre()); - ASSERT_TRUE(tcp_listener->default_filter_chain.has_value()); - auto& tls_context = - tcp_listener->default_filter_chain->downstream_tls_context; - EXPECT_FALSE(tls_context.require_client_certificate); - auto& cert_provider_instance = - tls_context.common_tls_context.tls_certificate_provider_instance; - EXPECT_EQ(cert_provider_instance.instance_name, "provider1"); - EXPECT_EQ(cert_provider_instance.certificate_name, "cert_name"); - auto& ca_cert_provider_instance = - tls_context.common_tls_context.certificate_validation_context - .ca_certificate_provider_instance; - EXPECT_EQ(ca_cert_provider_instance.instance_name, "provider1"); - EXPECT_EQ(ca_cert_provider_instance.certificate_name, "ca_cert_name"); -} - -TEST_F(TcpListenerTest, ClientCertificateRequired) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - auto* filter_chain = listener.mutable_default_filter_chain(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - auto* transport_socket = filter_chain->mutable_transport_socket(); - transport_socket->set_name("envoy.transport_sockets.tls"); - DownstreamTlsContext downstream_tls_context; - downstream_tls_context.mutable_require_client_certificate()->set_value(true); - auto* common_tls_context = - downstream_tls_context.mutable_common_tls_context(); - auto* cert_provider = - common_tls_context->mutable_tls_certificate_provider_instance(); - cert_provider->set_instance_name("provider1"); - cert_provider->set_certificate_name("cert_name"); - cert_provider = common_tls_context->mutable_validation_context() - ->mutable_ca_certificate_provider_instance(); - cert_provider->set_instance_name("provider1"); - cert_provider->set_certificate_name("ca_cert_name"); - transport_socket->mutable_typed_config()->PackFrom(downstream_tls_context); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - ASSERT_TRUE(decode_result.resource.ok()) << decode_result.resource.status(); - ASSERT_TRUE(decode_result.name.has_value()); - EXPECT_EQ(*decode_result.name, "foo"); - auto& resource = static_cast(**decode_result.resource); - auto* tcp_listener = - absl::get_if(&resource.listener); - ASSERT_NE(tcp_listener, nullptr); - EXPECT_EQ(tcp_listener->address, "127.0.0.1:443"); - EXPECT_THAT(tcp_listener->filter_chain_map.destination_ip_vector, - ::testing::ElementsAre()); - ASSERT_TRUE(tcp_listener->default_filter_chain.has_value()); - auto& tls_context = - tcp_listener->default_filter_chain->downstream_tls_context; - EXPECT_TRUE(tls_context.require_client_certificate); - auto& cert_provider_instance = - tls_context.common_tls_context.tls_certificate_provider_instance; - EXPECT_EQ(cert_provider_instance.instance_name, "provider1"); - EXPECT_EQ(cert_provider_instance.certificate_name, "cert_name"); - auto& ca_cert_provider_instance = - tls_context.common_tls_context.certificate_validation_context - .ca_certificate_provider_instance; - EXPECT_EQ(ca_cert_provider_instance.instance_name, "provider1"); - EXPECT_EQ(ca_cert_provider_instance.certificate_name, "ca_cert_name"); -} - -// This is just one example of where CommonTlsContext::Parse() will -// generate an error, to show that we're propagating any such errors -// correctly. An exhaustive set of tests for CommonTlsContext::Parse() -// is in xds_common_types_test.cc. -TEST_F(TcpListenerTest, UnknownCertificateProviderInstance) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - auto* filter_chain = listener.mutable_default_filter_chain(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - auto* transport_socket = filter_chain->mutable_transport_socket(); - transport_socket->set_name("envoy.transport_sockets.tls"); - DownstreamTlsContext downstream_tls_context; - auto* common_tls_context = - downstream_tls_context.mutable_common_tls_context(); - auto* cert_provider = - common_tls_context->mutable_tls_certificate_provider_instance(); - cert_provider->set_instance_name("fake"); - cert_provider->set_certificate_name("cert_name"); - transport_socket->mutable_typed_config()->PackFrom(downstream_tls_context); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating server Listener: [" - "field:default_filter_chain.transport_socket.typed_config.value[" - "envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext]" - ".common_tls_context.tls_certificate_provider_instance" - ".instance_name " - "error:unrecognized certificate provider instance name: fake]") - << decode_result.resource.status(); -} - -TEST_F(TcpListenerTest, UnknownTransportSocketType) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - auto* filter_chain = listener.mutable_default_filter_chain(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - auto* transport_socket = filter_chain->mutable_transport_socket(); - transport_socket->set_name("envoy.transport_sockets.tls"); - transport_socket->mutable_typed_config()->PackFrom(Listener()); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating server Listener: [" - "field:default_filter_chain.transport_socket.typed_config.value[" - "envoy.config.listener.v3.Listener].type_url " - "error:unsupported transport socket type]") - << decode_result.resource.status(); -} - -TEST_F(TcpListenerTest, UnparseableDownstreamTlsContext) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - auto* filter_chain = listener.mutable_default_filter_chain(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - auto* transport_socket = filter_chain->mutable_transport_socket(); - transport_socket->set_name("envoy.transport_sockets.tls"); - auto* typed_config = transport_socket->mutable_typed_config(); - typed_config->PackFrom(DownstreamTlsContext()); - typed_config->set_value(std::string("\0", 1)); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating server Listener: [" - "field:default_filter_chain.transport_socket.typed_config.value[" - "envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext] " - "error:can't decode DownstreamTlsContext]") - << decode_result.resource.status(); -} - -TEST_F(TcpListenerTest, DownstreamTlsContextInTypedStruct) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - auto* filter_chain = listener.mutable_default_filter_chain(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - auto* transport_socket = filter_chain->mutable_transport_socket(); - transport_socket->set_name("envoy.transport_sockets.tls"); - xds::type::v3::TypedStruct typed_struct; - typed_struct.set_type_url( - "types.googleapis.com/" - "envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext"); - transport_socket->mutable_typed_config()->PackFrom(typed_struct); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating server Listener: [" - "field:default_filter_chain.transport_socket.typed_config.value[" - "xds.type.v3.TypedStruct].value[" - "envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext] " - "error:can't decode DownstreamTlsContext]") - << decode_result.resource.status(); -} - -TEST_F(TcpListenerTest, MatchSubjectAltNames) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - auto* filter_chain = listener.mutable_default_filter_chain(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - auto* transport_socket = filter_chain->mutable_transport_socket(); - transport_socket->set_name("envoy.transport_sockets.tls"); - DownstreamTlsContext downstream_tls_context; - auto* common_tls_context = - downstream_tls_context.mutable_common_tls_context(); - auto* cert_provider = - common_tls_context->mutable_tls_certificate_provider_instance(); - cert_provider->set_instance_name("provider1"); - cert_provider->set_certificate_name("cert_name"); - common_tls_context->mutable_validation_context() - ->add_match_subject_alt_names() - ->set_exact("exact"); - transport_socket->mutable_typed_config()->PackFrom(downstream_tls_context); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating server Listener: [" - "field:default_filter_chain.transport_socket.typed_config.value[" - "envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext]" - ".common_tls_context " - "error:match_subject_alt_names not supported on servers]") - << decode_result.resource.status(); -} - -TEST_F(TcpListenerTest, NoTlsCertificateProvider) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - auto* filter_chain = listener.mutable_default_filter_chain(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - auto* transport_socket = filter_chain->mutable_transport_socket(); - transport_socket->set_name("envoy.transport_sockets.tls"); - transport_socket->mutable_typed_config()->PackFrom(DownstreamTlsContext()); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating server Listener: [" - "field:default_filter_chain.transport_socket.typed_config.value[" - "envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext] " - "error:TLS configuration provided but no " - "tls_certificate_provider_instance found]") - << decode_result.resource.status(); -} - -TEST_F(TcpListenerTest, RequireClientCertWithoutCaCertProvider) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - auto* filter_chain = listener.mutable_default_filter_chain(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - auto* transport_socket = filter_chain->mutable_transport_socket(); - transport_socket->set_name("envoy.transport_sockets.tls"); - DownstreamTlsContext downstream_tls_context; - downstream_tls_context.mutable_require_client_certificate()->set_value(true); - auto* common_tls_context = - downstream_tls_context.mutable_common_tls_context(); - auto* cert_provider = - common_tls_context->mutable_tls_certificate_provider_instance(); - cert_provider->set_instance_name("provider1"); - cert_provider->set_certificate_name("cert_name"); - transport_socket->mutable_typed_config()->PackFrom(downstream_tls_context); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating server Listener: [" - "field:default_filter_chain.transport_socket.typed_config.value[" - "envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext]" - ".require_client_certificate " - "error:client certificate required but no certificate " - "provider instance specified for validation]") - << decode_result.resource.status(); -} - -TEST_F(TcpListenerTest, UnsupportedFields) { - Listener listener; - listener.set_name("foo"); - HttpConnectionManager hcm; - auto* filter = hcm.add_http_filters(); - filter->set_name("router"); - filter->mutable_typed_config()->PackFrom(Router()); - auto* rds = hcm.mutable_rds(); - rds->set_route_config_name("rds_name"); - rds->mutable_config_source()->mutable_self(); - auto* filter_chain = listener.mutable_default_filter_chain(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom(hcm); - auto* transport_socket = filter_chain->mutable_transport_socket(); - transport_socket->set_name("envoy.transport_sockets.tls"); - DownstreamTlsContext downstream_tls_context; - downstream_tls_context.mutable_require_sni()->set_value(true); - downstream_tls_context.set_ocsp_staple_policy( - downstream_tls_context.STRICT_STAPLING); - auto* common_tls_context = - downstream_tls_context.mutable_common_tls_context(); - auto* cert_provider = - common_tls_context->mutable_tls_certificate_provider_instance(); - cert_provider->set_instance_name("provider1"); - cert_provider->set_certificate_name("cert_name"); - transport_socket->mutable_typed_config()->PackFrom(downstream_tls_context); - auto* address = listener.mutable_address()->mutable_socket_address(); - address->set_address("127.0.0.1"); - address->set_port_value(443); - std::string serialized_resource; - ASSERT_TRUE(listener.SerializeToString(&serialized_resource)); - auto* resource_type = XdsListenerResourceType::Get(); - auto decode_result = - resource_type->Decode(decode_context_, serialized_resource); - EXPECT_EQ(decode_result.resource.status().code(), - absl::StatusCode::kInvalidArgument); - EXPECT_EQ(decode_result.resource.status().message(), - "errors validating server Listener: [" - "field:default_filter_chain.transport_socket.typed_config.value[" - "envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext]" - ".ocsp_staple_policy " - "error:value must be LENIENT_STAPLING; " - "field:default_filter_chain.transport_socket.typed_config.value[" - "envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext]" - ".require_sni " - "error:field unsupported]") - << decode_result.resource.status(); -} - -} // namespace -} // 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/cpp/end2end/xds/BUILD b/test/cpp/end2end/xds/BUILD index ecd312f1c3f..7d7d2d4d48e 100644 --- a/test/cpp/end2end/xds/BUILD +++ b/test/cpp/end2end/xds/BUILD @@ -45,6 +45,15 @@ grpc_cc_library( ], ) +grpc_cc_library( + name = "no_op_http_filter", + testonly = True, + hdrs = ["no_op_http_filter.h"], + deps = [ + "//:grpc", + ], +) + grpc_cc_library( name = "xds_end2end_test_lib", testonly = True, @@ -103,6 +112,7 @@ grpc_cc_test( "no_windows", ], # TODO(jtattermusch): fix test on windows deps = [ + ":no_op_http_filter", ":xds_end2end_test_lib", "//:gpr", "//:grpc", @@ -269,6 +279,7 @@ grpc_cc_test( "no_windows", ], # TODO(jtattermusch): fix test on windows deps = [ + ":no_op_http_filter", ":xds_end2end_test_lib", "//:gpr", "//:grpc", @@ -349,10 +360,12 @@ grpc_cc_test( "no_windows", ], # TODO(jtattermusch): fix test on windows deps = [ + ":no_op_http_filter", ":xds_end2end_test_lib", "//:gpr", "//:grpc", "//:grpc++", + "//src/proto/grpc/testing/xds/v3:fault_proto", "//src/proto/grpc/testing/xds/v3:router_proto", "//test/core/util:grpc_test_util", ], diff --git a/test/cpp/end2end/xds/no_op_http_filter.h b/test/cpp/end2end/xds/no_op_http_filter.h new file mode 100644 index 00000000000..35b3a96919f --- /dev/null +++ b/test/cpp/end2end/xds/no_op_http_filter.h @@ -0,0 +1,70 @@ +// 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. + +#include + +#include "src/core/ext/xds/xds_http_filters.h" + +namespace grpc { +namespace testing { + +// A No-op HTTP filter used for verifying parsing logic. +class NoOpHttpFilter : public grpc_core::XdsHttpFilterImpl { + public: + NoOpHttpFilter(std::string name, bool supported_on_clients, + bool supported_on_servers, bool is_terminal_filter) + : name_(std::move(name)), + supported_on_clients_(supported_on_clients), + supported_on_servers_(supported_on_servers), + is_terminal_filter_(is_terminal_filter) {} + + void PopulateSymtab(upb_DefPool* /*symtab*/) const override {} + + absl::StatusOr + GenerateFilterConfig(grpc_core::XdsExtension /*extension*/, + upb_Arena* /*arena*/) const override { + return grpc_core::XdsHttpFilterImpl::FilterConfig{name_, grpc_core::Json()}; + } + + absl::StatusOr + GenerateFilterConfigOverride( + grpc_core::XdsExtension /*serialized_filter_config*/, + upb_Arena* /*arena*/) const override { + return grpc_core::XdsHttpFilterImpl::FilterConfig{name_, grpc_core::Json()}; + } + + const grpc_channel_filter* channel_filter() const override { return nullptr; } + + absl::StatusOr + GenerateServiceConfig( + const FilterConfig& /*hcm_filter_config*/, + const FilterConfig* /*filter_config_override*/) const override { + return grpc_core::XdsHttpFilterImpl::ServiceConfigJsonEntry{name_, ""}; + } + + bool IsSupportedOnClients() const override { return supported_on_clients_; } + + bool IsSupportedOnServers() const override { return supported_on_servers_; } + + bool IsTerminalFilter() const override { return is_terminal_filter_; } + + private: + const std::string name_; + const bool supported_on_clients_; + const bool supported_on_servers_; + const bool is_terminal_filter_; +}; + +} // namespace testing +} // namespace grpc diff --git a/test/cpp/end2end/xds/xds_end2end_test.cc b/test/cpp/end2end/xds/xds_end2end_test.cc index 6a396db8cd7..314b5bcf727 100644 --- a/test/cpp/end2end/xds/xds_end2end_test.cc +++ b/test/cpp/end2end/xds/xds_end2end_test.cc @@ -101,6 +101,7 @@ #include "src/proto/grpc/testing/xds/v3/tls.grpc.pb.h" #include "test/core/util/port.h" #include "test/core/util/test_config.h" +#include "test/cpp/end2end/xds/no_op_http_filter.h" #include "test/cpp/end2end/xds/xds_end2end_test_lib.h" #include "test/cpp/util/test_config.h" #include "test/cpp/util/tls_test_utils.h" @@ -766,24 +767,183 @@ TEST_P(XdsEnabledServerTest, ListenerDeletionIgnored) { CheckRpcSendOk(DEBUG_LOCATION); } -// Testing just one example of an invalid resource here. -// Unit tests for XdsListenerResourceType have exhaustive tests for all -// of the invalid cases. TEST_P(XdsEnabledServerTest, BadLdsUpdateNoApiListenerNorAddress) { DoSetUp(); Listener listener = default_server_listener_; listener.clear_address(); - listener.set_name(GetServerListenerName(backends_[0]->port())); + listener.set_name( + absl::StrCat("grpc/server?xds.resource.listening_address=", + ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port())); balancer_->ads_service()->SetLdsResource(listener); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; - EXPECT_EQ( + EXPECT_THAT( response_state->error_message, - absl::StrCat( - "xDS response validation errors: [resource index 0: ", - GetServerListenerName(backends_[0]->port()), - ": INVALID_ARGUMENT: Listener has neither address nor ApiListener]")); + ::testing::HasSubstr("Listener has neither address nor ApiListener")); +} + +// TODO(roth): Re-enable the following test once +// github.com/istio/istio/issues/38914 is resolved. +TEST_P(XdsEnabledServerTest, DISABLED_BadLdsUpdateBothApiListenerAndAddress) { + DoSetUp(); + Listener listener = default_server_listener_; + listener.mutable_api_listener(); + SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, + backends_[0]->port(), + default_server_route_config_); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT( + response_state->error_message, + ::testing::HasSubstr("Listener has both address and ApiListener")); +} + +TEST_P(XdsEnabledServerTest, NacksNonZeroXffNumTrusterHops) { + DoSetUp(); + Listener listener = default_server_listener_; + HttpConnectionManager http_connection_manager = + ServerHcmAccessor().Unpack(listener); + http_connection_manager.set_xff_num_trusted_hops(1); + ServerHcmAccessor().Pack(http_connection_manager, &listener); + SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, + backends_[0]->port(), + default_server_route_config_); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr("'xff_num_trusted_hops' must be zero")); +} + +TEST_P(XdsEnabledServerTest, NacksNonEmptyOriginalIpDetectionExtensions) { + DoSetUp(); + Listener listener = default_server_listener_; + HttpConnectionManager http_connection_manager = + ServerHcmAccessor().Unpack(listener); + http_connection_manager.add_original_ip_detection_extensions(); + ServerHcmAccessor().Pack(http_connection_manager, &listener); + SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, + backends_[0]->port(), + default_server_route_config_); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT( + response_state->error_message, + ::testing::HasSubstr("'original_ip_detection_extensions' must be empty")); +} + +TEST_P(XdsEnabledServerTest, UnsupportedL4Filter) { + DoSetUp(); + Listener listener = default_server_listener_; + listener.mutable_default_filter_chain()->clear_filters(); + listener.mutable_default_filter_chain()->add_filters()->mutable_typed_config()->PackFrom(default_listener_ /* any proto object other than HttpConnectionManager */); + balancer_->ads_service()->SetLdsResource( + PopulateServerListenerNameAndPort(listener, backends_[0]->port())); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr("Unsupported filter type")); +} + +TEST_P(XdsEnabledServerTest, NacksEmptyHttpFilterList) { + DoSetUp(); + Listener listener = default_server_listener_; + HttpConnectionManager http_connection_manager = + ServerHcmAccessor().Unpack(listener); + http_connection_manager.clear_http_filters(); + ServerHcmAccessor().Pack(http_connection_manager, &listener); + SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, + backends_[0]->port(), + default_server_route_config_); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr("Expected at least one HTTP filter")); +} + +TEST_P(XdsEnabledServerTest, UnsupportedHttpFilter) { + DoSetUp(); + Listener listener = default_server_listener_; + HttpConnectionManager http_connection_manager = + ServerHcmAccessor().Unpack(listener); + http_connection_manager.clear_http_filters(); + auto* http_filter = http_connection_manager.add_http_filters(); + http_filter->set_name("grpc.testing.unsupported_http_filter"); + http_filter->mutable_typed_config()->set_type_url( + "custom/grpc.testing.unsupported_http_filter"); + http_filter = http_connection_manager.add_http_filters(); + http_filter->set_name("router"); + http_filter->mutable_typed_config()->PackFrom( + envoy::extensions::filters::http::router::v3::Router()); + ServerHcmAccessor().Pack(http_connection_manager, &listener); + SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, + backends_[0]->port(), + default_server_route_config_); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr("no filter registered for config type " + "grpc.testing.unsupported_http_filter")); +} + +TEST_P(XdsEnabledServerTest, HttpFilterNotSupportedOnServer) { + DoSetUp(); + Listener listener = default_server_listener_; + HttpConnectionManager http_connection_manager = + ServerHcmAccessor().Unpack(listener); + http_connection_manager.clear_http_filters(); + auto* http_filter = http_connection_manager.add_http_filters(); + http_filter->set_name("grpc.testing.client_only_http_filter"); + http_filter->mutable_typed_config()->set_type_url( + "custom/grpc.testing.client_only_http_filter"); + http_filter = http_connection_manager.add_http_filters(); + http_filter->set_name("router"); + http_filter->mutable_typed_config()->PackFrom( + envoy::extensions::filters::http::router::v3::Router()); + ServerHcmAccessor().Pack(http_connection_manager, &listener); + SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, + backends_[0]->port(), + default_server_route_config_); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT( + response_state->error_message, + ::testing::HasSubstr("Filter grpc.testing.client_only_http_filter is not " + "supported on servers")); +} + +TEST_P(XdsEnabledServerTest, + HttpFilterNotSupportedOnServerIgnoredWhenOptional) { + DoSetUp(); + Listener listener = default_server_listener_; + HttpConnectionManager http_connection_manager = + ServerHcmAccessor().Unpack(listener); + http_connection_manager.clear_http_filters(); + auto* http_filter = http_connection_manager.add_http_filters(); + http_filter->set_name("grpc.testing.client_only_http_filter"); + http_filter->mutable_typed_config()->set_type_url( + "custom/grpc.testing.client_only_http_filter"); + http_filter->set_is_optional(true); + http_filter = http_connection_manager.add_http_filters(); + http_filter->set_name("router"); + http_filter->mutable_typed_config()->PackFrom( + envoy::extensions::filters::http::router::v3::Router()); + ServerHcmAccessor().Pack(http_connection_manager, &listener); + SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, + backends_[0]->port(), + default_server_route_config_); + backends_[0]->Start(); + WaitForBackend(DEBUG_LOCATION, 0); + auto response_state = balancer_->ads_service()->lds_response_state(); + ASSERT_TRUE(response_state.has_value()); + EXPECT_EQ(response_state->state, AdsServiceImpl::ResponseState::ACKED); } // Verify that a mismatch of listening address results in "not serving" @@ -803,6 +963,21 @@ TEST_P(XdsEnabledServerTest, ListenerAddressMismatch) { grpc::StatusCode::FAILED_PRECONDITION); } +TEST_P(XdsEnabledServerTest, UseOriginalDstNotSupported) { + DoSetUp(); + Listener listener = default_server_listener_; + listener.mutable_use_original_dst()->set_value(true); + SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, + backends_[0]->port(), + default_server_route_config_); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT( + response_state->error_message, + ::testing::HasSubstr("Field \'use_original_dst\' is not supported.")); +} + class XdsServerSecurityTest : public XdsEnd2endTest { protected: void SetUp() override { @@ -1027,6 +1202,173 @@ class XdsServerSecurityTest : public XdsEnd2endTest { std::vector client_authenticated_identity_; }; +TEST_P(XdsServerSecurityTest, TransportSocketTypedConfigUnset) { + Listener listener = default_server_listener_; + auto* filter_chain = listener.mutable_default_filter_chain(); + filter_chain->mutable_transport_socket(); + SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, + backends_[0]->port(), + default_server_route_config_); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr("transport socket typed config unset")); +} + +TEST_P(XdsServerSecurityTest, UnknownTransportSocket) { + Listener listener = default_server_listener_; + auto* filter_chain = listener.mutable_default_filter_chain(); + auto* transport_socket = filter_chain->mutable_transport_socket(); + transport_socket->mutable_typed_config()->PackFrom(Listener()); + SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, + backends_[0]->port(), + default_server_route_config_); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr("Unrecognized transport socket type: " + "envoy.config.listener.v3.Listener")); +} + +TEST_P(XdsServerSecurityTest, NacksRequireSNI) { + Listener listener = default_server_listener_; + auto* filter_chain = listener.mutable_default_filter_chain(); + auto* transport_socket = filter_chain->mutable_transport_socket(); + transport_socket->set_name("envoy.transport_sockets.tls"); + DownstreamTlsContext downstream_tls_context; + downstream_tls_context.mutable_common_tls_context() + ->mutable_tls_certificate_provider_instance() + ->set_instance_name("fake_plugin1"); + downstream_tls_context.mutable_require_sni()->set_value(true); + transport_socket->mutable_typed_config()->PackFrom(downstream_tls_context); + SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, + backends_[0]->port(), + default_server_route_config_); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr("require_sni: unsupported")); +} + +TEST_P(XdsServerSecurityTest, NacksOcspStaplePolicyOtherThanLenientStapling) { + Listener listener = default_server_listener_; + auto* filter_chain = listener.mutable_default_filter_chain(); + auto* transport_socket = filter_chain->mutable_transport_socket(); + transport_socket->set_name("envoy.transport_sockets.tls"); + DownstreamTlsContext downstream_tls_context; + downstream_tls_context.mutable_common_tls_context() + ->mutable_tls_certificate_provider_instance() + ->set_instance_name("fake_plugin1"); + downstream_tls_context.set_ocsp_staple_policy( + envoy::extensions::transport_sockets::tls::v3:: + DownstreamTlsContext_OcspStaplePolicy_STRICT_STAPLING); + transport_socket->mutable_typed_config()->PackFrom(downstream_tls_context); + SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, + backends_[0]->port(), + default_server_route_config_); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr( + "ocsp_staple_policy: Only LENIENT_STAPLING supported")); +} + +TEST_P( + XdsServerSecurityTest, + NacksRequiringClientCertificateWithoutValidationCertificateProviderInstance) { + Listener listener = default_server_listener_; + auto* filter_chain = listener.mutable_default_filter_chain(); + auto* transport_socket = filter_chain->mutable_transport_socket(); + transport_socket->set_name("envoy.transport_sockets.tls"); + DownstreamTlsContext downstream_tls_context; + downstream_tls_context.mutable_common_tls_context() + ->mutable_tls_certificate_provider_instance() + ->set_instance_name("fake_plugin1"); + downstream_tls_context.mutable_require_client_certificate()->set_value(true); + transport_socket->mutable_typed_config()->PackFrom(downstream_tls_context); + SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, + backends_[0]->port(), + default_server_route_config_); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr( + "TLS configuration requires client certificates but no " + "certificate provider instance specified for validation.")); +} + +TEST_P(XdsServerSecurityTest, + NacksTlsConfigurationWithoutIdentityProviderInstance) { + Listener listener = default_server_listener_; + auto* filter_chain = listener.mutable_default_filter_chain(); + auto* transport_socket = filter_chain->mutable_transport_socket(); + transport_socket->set_name("envoy.transport_sockets.tls"); + DownstreamTlsContext downstream_tls_context; + transport_socket->mutable_typed_config()->PackFrom(downstream_tls_context); + SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, + backends_[0]->port(), + default_server_route_config_); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr("TLS configuration provided but no " + "tls_certificate_provider_instance found.")); +} + +TEST_P(XdsServerSecurityTest, NacksMatchSubjectAltNames) { + Listener listener = default_server_listener_; + auto* filter_chain = listener.mutable_default_filter_chain(); + auto* transport_socket = filter_chain->mutable_transport_socket(); + transport_socket->set_name("envoy.transport_sockets.tls"); + DownstreamTlsContext downstream_tls_context; + downstream_tls_context.mutable_common_tls_context() + ->mutable_tls_certificate_provider_instance() + ->set_instance_name("fake_plugin1"); + downstream_tls_context.mutable_common_tls_context() + ->mutable_validation_context() + ->add_match_subject_alt_names() + ->set_exact("*.test.google.fr"); + transport_socket->mutable_typed_config()->PackFrom(downstream_tls_context); + SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, + backends_[0]->port(), + default_server_route_config_); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT( + response_state->error_message, + ::testing::HasSubstr("match_subject_alt_names not supported on servers")); +} + +TEST_P(XdsServerSecurityTest, UnknownIdentityCertificateProvider) { + SetLdsUpdate("", "", "unknown", "", false); + SendRpc([this]() { return CreateTlsChannel(); }, {}, {}, + true /* test_expects_failure */); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr( + "unrecognized certificate provider instance name: unknown")); +} + +TEST_P(XdsServerSecurityTest, UnknownRootCertificateProvider) { + g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); + SetLdsUpdate("unknown", "", "fake_plugin1", "", false); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr( + "unrecognized certificate provider instance name: unknown")); +} + TEST_P(XdsServerSecurityTest, TestDeprecateTlsCertificateCertificateProviderInstanceField) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); @@ -1788,6 +2130,232 @@ TEST_P(XdsServerFilterChainMatchTest, SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } +TEST_P(XdsServerFilterChainMatchTest, DuplicateMatchNacked) { + Listener listener = default_server_listener_; + // Add filter chain + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + GetHttpConnectionManager(listener)); + // Add a duplicate filter chain + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + GetHttpConnectionManager(listener)); + SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, + backends_[0]->port(), + default_server_route_config_); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT( + response_state->error_message, + ::testing::HasSubstr( + "Duplicate matching rules detected when adding filter chain: {}")); +} + +TEST_P(XdsServerFilterChainMatchTest, DuplicateMatchOnPrefixRangesNacked) { + Listener listener = default_server_listener_; + // Add filter chain with prefix range + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + GetHttpConnectionManager(listener)); + auto* prefix_range = + filter_chain->mutable_filter_chain_match()->add_prefix_ranges(); + prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); + prefix_range->mutable_prefix_len()->set_value(16); + prefix_range = + filter_chain->mutable_filter_chain_match()->add_prefix_ranges(); + prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); + prefix_range->mutable_prefix_len()->set_value(24); + // Add a filter chain with a duplicate prefix range entry + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + GetHttpConnectionManager(listener)); + prefix_range = + filter_chain->mutable_filter_chain_match()->add_prefix_ranges(); + prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); + prefix_range->mutable_prefix_len()->set_value(16); + prefix_range = + filter_chain->mutable_filter_chain_match()->add_prefix_ranges(); + prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); + prefix_range->mutable_prefix_len()->set_value(32); + SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, + backends_[0]->port(), + default_server_route_config_); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + if (ipv6_only_) { + EXPECT_THAT( + response_state->error_message, + ::testing::HasSubstr( + "Duplicate matching rules detected when adding filter chain: " + "{prefix_ranges={{address_prefix=[::]:0, prefix_len=16}, " + "{address_prefix=[::]:0, prefix_len=32}}}")); + } else { + EXPECT_THAT( + response_state->error_message, + ::testing::HasSubstr( + "Duplicate matching rules detected when adding filter chain: " + "{prefix_ranges={{address_prefix=127.0.0.0:0, prefix_len=16}, " + "{address_prefix=127.0.0.1:0, prefix_len=32}}}")); + } +} + +TEST_P(XdsServerFilterChainMatchTest, DuplicateMatchOnTransportProtocolNacked) { + Listener listener = default_server_listener_; + // Add filter chain with "raw_buffer" transport protocol + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + GetHttpConnectionManager(listener)); + filter_chain->mutable_filter_chain_match()->set_transport_protocol( + "raw_buffer"); + // Add a duplicate filter chain with the same "raw_buffer" transport + // protocol entry + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + GetHttpConnectionManager(listener)); + filter_chain->mutable_filter_chain_match()->set_transport_protocol( + "raw_buffer"); + SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, + backends_[0]->port(), + default_server_route_config_); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT( + response_state->error_message, + ::testing::HasSubstr("Duplicate matching rules detected when adding " + "filter chain: {transport_protocol=raw_buffer}")); +} + +TEST_P(XdsServerFilterChainMatchTest, DuplicateMatchOnLocalSourceTypeNacked) { + Listener listener = default_server_listener_; + // Add filter chain with the local source type + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + GetHttpConnectionManager(listener)); + filter_chain->mutable_filter_chain_match()->set_source_type( + FilterChainMatch::SAME_IP_OR_LOOPBACK); + // Add a duplicate filter chain with the same local source type entry + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + GetHttpConnectionManager(listener)); + filter_chain->mutable_filter_chain_match()->set_source_type( + FilterChainMatch::SAME_IP_OR_LOOPBACK); + SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, + backends_[0]->port(), + default_server_route_config_); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT( + response_state->error_message, + ::testing::HasSubstr("Duplicate matching rules detected when adding " + "filter chain: {source_type=SAME_IP_OR_LOOPBACK}")); +} + +TEST_P(XdsServerFilterChainMatchTest, + DuplicateMatchOnExternalSourceTypeNacked) { + Listener listener = default_server_listener_; + // Add filter chain with the external source type + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + GetHttpConnectionManager(listener)); + filter_chain->mutable_filter_chain_match()->set_source_type( + FilterChainMatch::EXTERNAL); + // Add a duplicate filter chain with the same external source type entry + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + GetHttpConnectionManager(listener)); + filter_chain->mutable_filter_chain_match()->set_source_type( + FilterChainMatch::EXTERNAL); + SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, + backends_[0]->port(), + default_server_route_config_); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT( + response_state->error_message, + ::testing::HasSubstr("Duplicate matching rules detected when adding " + "filter chain: {source_type=EXTERNAL}")); +} + +TEST_P(XdsServerFilterChainMatchTest, + DuplicateMatchOnSourcePrefixRangesNacked) { + Listener listener = default_server_listener_; + // Add filter chain with source prefix range + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + GetHttpConnectionManager(listener)); + auto* prefix_range = + filter_chain->mutable_filter_chain_match()->add_source_prefix_ranges(); + prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); + prefix_range->mutable_prefix_len()->set_value(16); + prefix_range = + filter_chain->mutable_filter_chain_match()->add_source_prefix_ranges(); + prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); + prefix_range->mutable_prefix_len()->set_value(24); + // Add a filter chain with a duplicate source prefix range entry + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + GetHttpConnectionManager(listener)); + prefix_range = + filter_chain->mutable_filter_chain_match()->add_source_prefix_ranges(); + prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); + prefix_range->mutable_prefix_len()->set_value(16); + prefix_range = + filter_chain->mutable_filter_chain_match()->add_source_prefix_ranges(); + prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); + prefix_range->mutable_prefix_len()->set_value(32); + SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, + backends_[0]->port(), + default_server_route_config_); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + if (ipv6_only_) { + EXPECT_THAT( + response_state->error_message, + ::testing::HasSubstr( + "Duplicate matching rules detected when adding filter chain: " + "{source_prefix_ranges={{address_prefix=[::]:0, prefix_len=16}, " + "{address_prefix=[::]:0, prefix_len=32}}}")); + } else { + EXPECT_THAT( + response_state->error_message, + ::testing::HasSubstr( + "Duplicate matching rules detected when adding filter chain: " + "{source_prefix_ranges={{address_prefix=127.0.0.0:0, " + "prefix_len=16}, " + "{address_prefix=127.0.0.1:0, prefix_len=32}}}")); + } +} + +TEST_P(XdsServerFilterChainMatchTest, DuplicateMatchOnSourcePortNacked) { + Listener listener = default_server_listener_; + // Add filter chain with the external source type + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + GetHttpConnectionManager(listener)); + filter_chain->mutable_filter_chain_match()->add_source_ports(8080); + // Add a duplicate filter chain with the same source port entry + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + GetHttpConnectionManager(listener)); + filter_chain->mutable_filter_chain_match()->add_source_ports(8080); + SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, + backends_[0]->port(), + default_server_route_config_); + backends_[0]->Start(); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT( + response_state->error_message, + ::testing::HasSubstr("Duplicate matching rules detected when adding " + "filter chain: {source_ports={8080}}")); +} + using XdsServerRdsTest = XdsEnabledServerStatusNotificationTest; TEST_P(XdsServerRdsTest, Basic) { @@ -2040,6 +2608,116 @@ TEST_P(XdsRbacTest, LogAction) { SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } +using XdsRbacNackTest = XdsRbacTest; + +TEST_P(XdsRbacNackTest, NacksSchemePrincipalHeader) { + RBAC rbac; + auto* rules = rbac.mutable_rules(); + rules->set_action(envoy::config::rbac::v3::RBAC_Action_ALLOW); + Policy policy; + auto* header = policy.add_principals()->mutable_header(); + header->set_name(":scheme"); + header->set_exact_match("http"); + policy.add_permissions()->set_any(true); + (*rules->mutable_policies())["policy"] = policy; + SetServerRbacPolicy(rbac); + backends_[0]->Start(); + if (GetParam().enable_rds_testing() && + GetParam().filter_config_setup() == + XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute) { + const auto response_state = WaitForRdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr("':scheme' not allowed in header")); + } else { + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr("':scheme' not allowed in header")); + } +} + +TEST_P(XdsRbacNackTest, NacksGrpcPrefixedPrincipalHeaders) { + RBAC rbac; + auto* rules = rbac.mutable_rules(); + rules->set_action(envoy::config::rbac::v3::RBAC_Action_ALLOW); + Policy policy; + auto* header = policy.add_principals()->mutable_header(); + header->set_name("grpc-status"); + header->set_exact_match("0"); + policy.add_permissions()->set_any(true); + (*rules->mutable_policies())["policy"] = policy; + SetServerRbacPolicy(rbac); + backends_[0]->Start(); + if (GetParam().enable_rds_testing() && + GetParam().filter_config_setup() == + XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute) { + const auto response_state = WaitForRdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr("'grpc-' prefixes not allowed in header")); + } else { + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr("'grpc-' prefixes not allowed in header")); + } +} + +TEST_P(XdsRbacNackTest, NacksSchemePermissionHeader) { + RBAC rbac; + auto* rules = rbac.mutable_rules(); + rules->set_action(envoy::config::rbac::v3::RBAC_Action_ALLOW); + Policy policy; + auto* header = policy.add_permissions()->mutable_header(); + header->set_name(":scheme"); + header->set_exact_match("http"); + policy.add_principals()->set_any(true); + (*rules->mutable_policies())["policy"] = policy; + SetServerRbacPolicy(rbac); + backends_[0]->Start(); + if (GetParam().enable_rds_testing() && + GetParam().filter_config_setup() == + XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute) { + const auto response_state = WaitForRdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr("':scheme' not allowed in header")); + } else { + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr("':scheme' not allowed in header")); + } +} + +TEST_P(XdsRbacNackTest, NacksGrpcPrefixedPermissionHeaders) { + RBAC rbac; + auto* rules = rbac.mutable_rules(); + rules->set_action(envoy::config::rbac::v3::RBAC_Action_ALLOW); + Policy policy; + auto* header = policy.add_permissions()->mutable_header(); + header->set_name("grpc-status"); + header->set_exact_match("0"); + policy.add_principals()->set_any(true); + (*rules->mutable_policies())["policy"] = policy; + SetServerRbacPolicy(rbac); + backends_[0]->Start(); + if (GetParam().enable_rds_testing() && + GetParam().filter_config_setup() == + XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute) { + const auto response_state = WaitForRdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr("'grpc-' prefixes not allowed in header")); + } else { + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr("'grpc-' prefixes not allowed in header")); + } +} + // Tests RBAC policies where a route override is always present. Action // permutations are not added. using XdsRbacTestWithRouteOverrideAlwaysPresent = XdsRbacTest; @@ -2909,6 +3587,30 @@ INSTANTIATE_TEST_SUITE_P( .set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar)), &XdsTestType::Name); +// We are only testing the server here. +// Run with bootstrap from env var, so that we use a global XdsClient +// instance. Otherwise, we would need to use a separate fake resolver +// result generator on the client and server sides. +// Note that we are simply using the default fake credentials instead of xds +// credentials for NACK tests to avoid a mismatch between the client and the +// server's security settings when using the WaitForNack() infrastructure. +INSTANTIATE_TEST_SUITE_P( + XdsTest, XdsRbacNackTest, + ::testing::Values( + XdsTestType().set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar), + XdsTestType().set_enable_rds_testing().set_bootstrap_source( + XdsTestType::kBootstrapFromEnvVar), + XdsTestType() + .set_filter_config_setup( + XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute) + .set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar), + XdsTestType() + .set_enable_rds_testing() + .set_filter_config_setup( + XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute) + .set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar)), + &XdsTestType::Name); + // We are only testing the server here. // Run with bootstrap from env var, so that we use a global XdsClient // instance. Otherwise, we would need to use a separate fake resolver @@ -3012,6 +3714,24 @@ int main(int argc, char** argv) { "fake2", grpc::testing::g_fake2_cert_data_map)); }); grpc_init(); + grpc_core::XdsHttpFilterRegistry::RegisterFilter( + std::make_unique( + "grpc.testing.client_only_http_filter", + /* supported_on_clients = */ true, /* supported_on_servers = */ false, + /* is_terminal_filter */ false), + {"grpc.testing.client_only_http_filter"}); + grpc_core::XdsHttpFilterRegistry::RegisterFilter( + std::make_unique( + "grpc.testing.server_only_http_filter", + /* supported_on_clients = */ false, /* supported_on_servers = */ true, + /* is_terminal_filter */ false), + {"grpc.testing.server_only_http_filter"}); + grpc_core::XdsHttpFilterRegistry::RegisterFilter( + std::make_unique( + "grpc.testing.terminal_http_filter", + /* supported_on_clients = */ true, /* supported_on_servers = */ true, + /* is_terminal_filter */ true), + {"grpc.testing.terminal_http_filter"}); const auto result = RUN_ALL_TESTS(); grpc_shutdown(); return result; diff --git a/test/cpp/end2end/xds/xds_outlier_detection_end2end_test.cc b/test/cpp/end2end/xds/xds_outlier_detection_end2end_test.cc index 153eaf7bd9e..477fd61817c 100644 --- a/test/cpp/end2end/xds/xds_outlier_detection_end2end_test.cc +++ b/test/cpp/end2end/xds/xds_outlier_detection_end2end_test.cc @@ -25,6 +25,7 @@ #include "src/proto/grpc/testing/xds/v3/fault.grpc.pb.h" #include "src/proto/grpc/testing/xds/v3/outlier_detection.grpc.pb.h" #include "src/proto/grpc/testing/xds/v3/router.grpc.pb.h" +#include "test/cpp/end2end/xds/no_op_http_filter.h" #include "test/cpp/end2end/xds/xds_end2end_test_lib.h" namespace grpc { diff --git a/test/cpp/end2end/xds/xds_routing_end2end_test.cc b/test/cpp/end2end/xds/xds_routing_end2end_test.cc index 25a8a3861f6..d56ac655e6c 100644 --- a/test/cpp/end2end/xds/xds_routing_end2end_test.cc +++ b/test/cpp/end2end/xds/xds_routing_end2end_test.cc @@ -21,13 +21,16 @@ #include #include "src/core/ext/filters/client_channel/backup_poller.h" +#include "src/proto/grpc/testing/xds/v3/fault.grpc.pb.h" #include "src/proto/grpc/testing/xds/v3/router.grpc.pb.h" +#include "test/cpp/end2end/xds/no_op_http_filter.h" #include "test/cpp/end2end/xds/xds_end2end_test_lib.h" namespace grpc { namespace testing { namespace { +using ::envoy::extensions::filters::http::fault::v3::HTTPFault; using std::chrono::system_clock; using LdsTest = XdsEnd2endTest; @@ -35,10 +38,9 @@ using LdsTest = XdsEnd2endTest; INSTANTIATE_TEST_SUITE_P(XdsTest, LdsTest, ::testing::Values(XdsTestType()), &XdsTestType::Name); -// Testing just one example of an invalid resource here. -// Unit tests for XdsListenerResourceType have exhaustive tests for all -// of the invalid cases. -TEST_P(LdsTest, NacksInvalidListener) { +// Tests that LDS client should send a NACK if there is no API listener in the +// Listener in the LDS response. +TEST_P(LdsTest, NoApiListener) { auto listener = default_listener_; listener.clear_api_listener(); balancer_->ads_service()->SetLdsResource(listener); @@ -49,6 +51,382 @@ TEST_P(LdsTest, NacksInvalidListener) { ::testing::HasSubstr("Listener has neither address nor ApiListener")); } +// Tests that LDS client should send a NACK if the route_specifier in the +// http_connection_manager is neither inlined route_config nor RDS. +TEST_P(LdsTest, WrongRouteSpecifier) { + auto listener = default_listener_; + HttpConnectionManager http_connection_manager; + listener.mutable_api_listener()->mutable_api_listener()->UnpackTo( + &http_connection_manager); + http_connection_manager.mutable_scoped_routes(); + listener.mutable_api_listener()->mutable_api_listener()->PackFrom( + http_connection_manager); + balancer_->ads_service()->SetLdsResource(listener); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT( + response_state->error_message, + ::testing::HasSubstr( + "HttpConnectionManager neither has inlined route_config nor RDS.")); +} + +// Tests that LDS client should send a NACK if the rds message in the +// http_connection_manager is missing the config_source field. +TEST_P(LdsTest, RdsMissingConfigSource) { + auto listener = default_listener_; + HttpConnectionManager http_connection_manager; + listener.mutable_api_listener()->mutable_api_listener()->UnpackTo( + &http_connection_manager); + http_connection_manager.mutable_rds()->set_route_config_name( + kDefaultRouteConfigurationName); + listener.mutable_api_listener()->mutable_api_listener()->PackFrom( + http_connection_manager); + balancer_->ads_service()->SetLdsResource(listener); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr( + "HttpConnectionManager missing config_source for RDS.")); +} + +// Tests that LDS client should send a NACK if the rds message in the +// http_connection_manager has a config_source field that does not specify +// ADS or SELF. +TEST_P(LdsTest, RdsConfigSourceDoesNotSpecifyAdsOrSelf) { + auto listener = default_listener_; + HttpConnectionManager http_connection_manager; + listener.mutable_api_listener()->mutable_api_listener()->UnpackTo( + &http_connection_manager); + auto* rds = http_connection_manager.mutable_rds(); + rds->set_route_config_name(kDefaultRouteConfigurationName); + rds->mutable_config_source()->set_path("/foo/bar"); + listener.mutable_api_listener()->mutable_api_listener()->PackFrom( + http_connection_manager); + balancer_->ads_service()->SetLdsResource(listener); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr("HttpConnectionManager ConfigSource for " + "RDS does not specify ADS or SELF.")); +} + +// Tests that LDS client accepts the rds message in the +// http_connection_manager with a config_source field that specifies ADS. +TEST_P(LdsTest, AcceptsRdsConfigSourceOfTypeAds) { + CreateAndStartBackends(1); + auto listener = default_listener_; + HttpConnectionManager http_connection_manager; + listener.mutable_api_listener()->mutable_api_listener()->UnpackTo( + &http_connection_manager); + auto* rds = http_connection_manager.mutable_rds(); + rds->set_route_config_name(kDefaultRouteConfigurationName); + rds->mutable_config_source()->mutable_ads(); + listener.mutable_api_listener()->mutable_api_listener()->PackFrom( + http_connection_manager); + SetListenerAndRouteConfiguration(balancer_.get(), listener, + default_route_config_); + EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}}); + balancer_->ads_service()->SetEdsResource(BuildEdsResource(args)); + WaitForAllBackends(DEBUG_LOCATION); + auto response_state = balancer_->ads_service()->lds_response_state(); + ASSERT_TRUE(response_state.has_value()); + EXPECT_EQ(response_state->state, AdsServiceImpl::ResponseState::ACKED); +} + +// Tests that we NACK non-terminal filters at the end of the list. +TEST_P(LdsTest, NacksNonTerminalHttpFilterAtEndOfList) { + auto listener = default_listener_; + HttpConnectionManager http_connection_manager; + listener.mutable_api_listener()->mutable_api_listener()->UnpackTo( + &http_connection_manager); + auto* filter = http_connection_manager.mutable_http_filters(0); + filter->set_name("unknown"); + filter->mutable_typed_config()->set_type_url( + "custom/grpc.testing.client_only_http_filter"); + listener.mutable_api_listener()->mutable_api_listener()->PackFrom( + http_connection_manager); + SetListenerAndRouteConfiguration(balancer_.get(), listener, + default_route_config_); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr( + "non-terminal filter for config type grpc.testing" + ".client_only_http_filter is the last filter in the chain")); +} + +// Test that we NACK terminal filters that are not at the end of the list. +TEST_P(LdsTest, NacksTerminalFilterBeforeEndOfList) { + auto listener = default_listener_; + HttpConnectionManager http_connection_manager; + listener.mutable_api_listener()->mutable_api_listener()->UnpackTo( + &http_connection_manager); + // The default_listener_ has a terminal router filter by default. Add an + // additional filter. + auto* filter = http_connection_manager.add_http_filters(); + filter->set_name("grpc.testing.terminal_http_filter"); + filter->mutable_typed_config()->set_type_url( + "custom/grpc.testing.terminal_http_filter"); + listener.mutable_api_listener()->mutable_api_listener()->PackFrom( + http_connection_manager); + SetListenerAndRouteConfiguration(balancer_.get(), listener, + default_route_config_); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT( + response_state->error_message, + ::testing::HasSubstr( + "terminal filter for config type envoy.extensions.filters.http" + ".router.v3.Router must be the last filter in the chain")); +} + +// Test that we NACK empty filter names. +TEST_P(LdsTest, RejectsEmptyHttpFilterName) { + auto listener = default_listener_; + HttpConnectionManager http_connection_manager; + listener.mutable_api_listener()->mutable_api_listener()->UnpackTo( + &http_connection_manager); + *http_connection_manager.add_http_filters() = + http_connection_manager.http_filters(0); + auto* filter = http_connection_manager.mutable_http_filters(0); + filter->Clear(); + filter->mutable_typed_config()->PackFrom(Listener()); + listener.mutable_api_listener()->mutable_api_listener()->PackFrom( + http_connection_manager); + SetListenerAndRouteConfiguration(balancer_.get(), listener, + default_route_config_); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr("empty filter name at index 0")); +} + +// Test that we NACK duplicate HTTP filter names. +TEST_P(LdsTest, RejectsDuplicateHttpFilterName) { + auto listener = default_listener_; + HttpConnectionManager http_connection_manager; + listener.mutable_api_listener()->mutable_api_listener()->UnpackTo( + &http_connection_manager); + *http_connection_manager.add_http_filters() = + http_connection_manager.http_filters(0); + http_connection_manager.mutable_http_filters(0) + ->mutable_typed_config() + ->PackFrom(HTTPFault()); + listener.mutable_api_listener()->mutable_api_listener()->PackFrom( + http_connection_manager); + SetListenerAndRouteConfiguration(balancer_.get(), listener, + default_route_config_); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr("duplicate HTTP filter name: router")); +} + +// Test that we NACK unknown filter types. +TEST_P(LdsTest, RejectsUnknownHttpFilterType) { + auto listener = default_listener_; + HttpConnectionManager http_connection_manager; + listener.mutable_api_listener()->mutable_api_listener()->UnpackTo( + &http_connection_manager); + *http_connection_manager.add_http_filters() = + http_connection_manager.http_filters(0); + auto* filter = http_connection_manager.mutable_http_filters(0); + filter->set_name("unknown"); + filter->mutable_typed_config()->PackFrom(Listener()); + listener.mutable_api_listener()->mutable_api_listener()->PackFrom( + http_connection_manager); + SetListenerAndRouteConfiguration(balancer_.get(), listener, + default_route_config_); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr("no filter registered for config type " + "envoy.config.listener.v3.Listener")); +} + +// Test that we ignore optional unknown filter types. +TEST_P(LdsTest, IgnoresOptionalUnknownHttpFilterType) { + CreateAndStartBackends(1); + auto listener = default_listener_; + HttpConnectionManager http_connection_manager; + listener.mutable_api_listener()->mutable_api_listener()->UnpackTo( + &http_connection_manager); + *http_connection_manager.add_http_filters() = + http_connection_manager.http_filters(0); + auto* filter = http_connection_manager.mutable_http_filters(0); + filter->set_name("unknown"); + filter->mutable_typed_config()->PackFrom(Listener()); + filter->set_is_optional(true); + listener.mutable_api_listener()->mutable_api_listener()->PackFrom( + http_connection_manager); + SetListenerAndRouteConfiguration(balancer_.get(), listener, + default_route_config_); + EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}}); + balancer_->ads_service()->SetEdsResource(BuildEdsResource(args)); + WaitForAllBackends(DEBUG_LOCATION); + auto response_state = balancer_->ads_service()->lds_response_state(); + ASSERT_TRUE(response_state.has_value()); + EXPECT_EQ(response_state->state, AdsServiceImpl::ResponseState::ACKED); +} + +// Test that we NACK filters without configs. +TEST_P(LdsTest, RejectsHttpFilterWithoutConfig) { + auto listener = default_listener_; + HttpConnectionManager http_connection_manager; + listener.mutable_api_listener()->mutable_api_listener()->UnpackTo( + &http_connection_manager); + *http_connection_manager.add_http_filters() = + http_connection_manager.http_filters(0); + auto* filter = http_connection_manager.mutable_http_filters(0); + filter->Clear(); + filter->set_name("unknown"); + listener.mutable_api_listener()->mutable_api_listener()->PackFrom( + http_connection_manager); + SetListenerAndRouteConfiguration(balancer_.get(), listener, + default_route_config_); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr( + "no filter config specified for filter name unknown")); +} + +// Test that we ignore optional filters without configs. +TEST_P(LdsTest, IgnoresOptionalHttpFilterWithoutConfig) { + CreateAndStartBackends(1); + auto listener = default_listener_; + HttpConnectionManager http_connection_manager; + listener.mutable_api_listener()->mutable_api_listener()->UnpackTo( + &http_connection_manager); + *http_connection_manager.add_http_filters() = + http_connection_manager.http_filters(0); + auto* filter = http_connection_manager.mutable_http_filters(0); + filter->Clear(); + filter->set_name("unknown"); + filter->set_is_optional(true); + listener.mutable_api_listener()->mutable_api_listener()->PackFrom( + http_connection_manager); + SetListenerAndRouteConfiguration(balancer_.get(), listener, + default_route_config_); + EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}}); + balancer_->ads_service()->SetEdsResource(BuildEdsResource(args)); + WaitForAllBackends(DEBUG_LOCATION); + auto response_state = balancer_->ads_service()->lds_response_state(); + ASSERT_TRUE(response_state.has_value()); + EXPECT_EQ(response_state->state, AdsServiceImpl::ResponseState::ACKED); +} + +// Test that we NACK unparseable filter configs. +TEST_P(LdsTest, RejectsUnparseableHttpFilterType) { + auto listener = default_listener_; + HttpConnectionManager http_connection_manager; + listener.mutable_api_listener()->mutable_api_listener()->UnpackTo( + &http_connection_manager); + *http_connection_manager.add_http_filters() = + http_connection_manager.http_filters(0); + auto* filter = http_connection_manager.mutable_http_filters(0); + filter->set_name("unknown"); + filter->mutable_typed_config()->PackFrom(listener); + filter->mutable_typed_config()->set_type_url( + "type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault"); + listener.mutable_api_listener()->mutable_api_listener()->PackFrom( + http_connection_manager); + SetListenerAndRouteConfiguration(balancer_.get(), listener, + default_route_config_); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT( + response_state->error_message, + ::testing::HasSubstr( + "filter config for type " + "envoy.extensions.filters.http.fault.v3.HTTPFault failed to parse")); +} + +// Test that we NACK HTTP filters unsupported on client-side. +TEST_P(LdsTest, RejectsHttpFiltersNotSupportedOnClients) { + auto listener = default_listener_; + HttpConnectionManager http_connection_manager; + listener.mutable_api_listener()->mutable_api_listener()->UnpackTo( + &http_connection_manager); + *http_connection_manager.add_http_filters() = + http_connection_manager.http_filters(0); + auto* filter = http_connection_manager.mutable_http_filters(0); + filter->set_name("grpc.testing.server_only_http_filter"); + filter->mutable_typed_config()->set_type_url( + "custom/grpc.testing.server_only_http_filter"); + listener.mutable_api_listener()->mutable_api_listener()->PackFrom( + http_connection_manager); + SetListenerAndRouteConfiguration(balancer_.get(), listener, + default_route_config_); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT( + response_state->error_message, + ::testing::HasSubstr("Filter grpc.testing.server_only_http_filter is not " + "supported on clients")); +} + +// Test that we ignore optional HTTP filters unsupported on client-side. +TEST_P(LdsTest, IgnoresOptionalHttpFiltersNotSupportedOnClients) { + CreateAndStartBackends(1); + auto listener = default_listener_; + HttpConnectionManager http_connection_manager; + listener.mutable_api_listener()->mutable_api_listener()->UnpackTo( + &http_connection_manager); + *http_connection_manager.add_http_filters() = + http_connection_manager.http_filters(0); + auto* filter = http_connection_manager.mutable_http_filters(0); + filter->set_name("grpc.testing.server_only_http_filter"); + filter->mutable_typed_config()->set_type_url( + "custom/grpc.testing.server_only_http_filter"); + filter->set_is_optional(true); + listener.mutable_api_listener()->mutable_api_listener()->PackFrom( + http_connection_manager); + SetListenerAndRouteConfiguration(balancer_.get(), listener, + default_route_config_); + EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}}); + balancer_->ads_service()->SetEdsResource(BuildEdsResource(args)); + WaitForAllBackends(DEBUG_LOCATION); + auto response_state = balancer_->ads_service()->lds_response_state(); + ASSERT_TRUE(response_state.has_value()); + EXPECT_EQ(response_state->state, AdsServiceImpl::ResponseState::ACKED); +} + +// Test that we NACK non-zero xff_num_trusted_hops +TEST_P(LdsTest, RejectsNonZeroXffNumTrusterHops) { + auto listener = default_listener_; + HttpConnectionManager http_connection_manager; + listener.mutable_api_listener()->mutable_api_listener()->UnpackTo( + &http_connection_manager); + http_connection_manager.set_xff_num_trusted_hops(1); + listener.mutable_api_listener()->mutable_api_listener()->PackFrom( + http_connection_manager); + SetListenerAndRouteConfiguration(balancer_.get(), listener, + default_route_config_); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT(response_state->error_message, + ::testing::HasSubstr("'xff_num_trusted_hops' must be zero")); +} + +// Test that we NACK non-empty original_ip_detection_extensions +TEST_P(LdsTest, RejectsNonEmptyOriginalIpDetectionExtensions) { + auto listener = default_listener_; + HttpConnectionManager http_connection_manager; + listener.mutable_api_listener()->mutable_api_listener()->UnpackTo( + &http_connection_manager); + http_connection_manager.add_original_ip_detection_extensions(); + listener.mutable_api_listener()->mutable_api_listener()->PackFrom( + http_connection_manager); + SetListenerAndRouteConfiguration(balancer_.get(), listener, + default_route_config_); + const auto response_state = WaitForLdsNack(DEBUG_LOCATION); + ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; + EXPECT_THAT( + response_state->error_message, + ::testing::HasSubstr("'original_ip_detection_extensions' must be empty")); +} + class LdsDeletionTest : public XdsEnd2endTest { protected: void SetUp() override {} // Individual tests call InitClient(). @@ -2748,6 +3126,24 @@ int main(int argc, char** argv) { grpc_core::SetEnv("grpc_cfstream", "0"); #endif grpc_init(); + grpc_core::XdsHttpFilterRegistry::RegisterFilter( + std::make_unique( + "grpc.testing.client_only_http_filter", + /* supported_on_clients = */ true, /* supported_on_servers = */ false, + /* is_terminal_filter */ false), + {"grpc.testing.client_only_http_filter"}); + grpc_core::XdsHttpFilterRegistry::RegisterFilter( + std::make_unique( + "grpc.testing.server_only_http_filter", + /* supported_on_clients = */ false, /* supported_on_servers = */ true, + /* is_terminal_filter */ false), + {"grpc.testing.server_only_http_filter"}); + grpc_core::XdsHttpFilterRegistry::RegisterFilter( + std::make_unique( + "grpc.testing.terminal_http_filter", + /* supported_on_clients = */ true, /* supported_on_servers = */ true, + /* is_terminal_filter */ true), + {"grpc.testing.terminal_http_filter"}); const auto result = RUN_ALL_TESTS(); grpc_shutdown(); return result; diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index 0ee3cdbb5a2..fc286d3d5c7 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -8293,30 +8293,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": "xds_http_filters_test", - "platforms": [ - "linux", - "mac", - "posix", - "windows" - ], - "uses_polling": false - }, { "args": [], "benchmark": false, @@ -8341,30 +8317,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": "xds_listener_resource_type_test", - "platforms": [ - "linux", - "mac", - "posix", - "windows" - ], - "uses_polling": false - }, { "args": [], "benchmark": false,