Second attempt: xDS Listener: use ValidationErrors and add unit test (#31457)

* Revert "Revert "xDS Listener: use ValidationErrors and add unit test (#31351)" (#31456)"

This reverts commit ab3d62ae8f.

* Revert "Revert "xds_http_filters_test: fix includes for import (#31454)" (#31455)"

This reverts commit 32590d110a.

* fix import

* work around internal differences in Any API (string vs. cord)

* iwyu
pull/31293/head^2
Mark D. Roth 2 years ago committed by GitHub
parent 23c7e48779
commit b9aca8db8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 258
      CMakeLists.txt
  2. 92
      build_autogenerated.yaml
  3. 1
      src/core/BUILD
  4. 72
      src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc
  5. 4
      src/core/ext/filters/fault_injection/fault_injection_filter.h
  6. 1
      src/core/ext/filters/fault_injection/service_config_parser.cc
  7. 4
      src/core/ext/filters/fault_injection/service_config_parser.h
  8. 42
      src/core/ext/xds/xds_cluster.cc
  9. 104
      src/core/ext/xds/xds_http_fault_filter.cc
  10. 32
      src/core/ext/xds/xds_http_fault_filter.h
  11. 106
      src/core/ext/xds/xds_http_filters.cc
  12. 55
      src/core/ext/xds/xds_http_filters.h
  13. 339
      src/core/ext/xds/xds_http_rbac_filter.cc
  14. 25
      src/core/ext/xds/xds_http_rbac_filter.h
  15. 899
      src/core/ext/xds/xds_listener.cc
  16. 71
      src/core/ext/xds/xds_listener.h
  17. 11
      src/core/ext/xds/xds_route_config.cc
  18. 82
      src/core/ext/xds/xds_server_config_fetcher.cc
  19. 6
      src/proto/grpc/testing/xds/v3/address.proto
  20. 2
      src/proto/grpc/testing/xds/v3/route.proto
  21. 43
      test/core/xds/BUILD
  22. 40
      test/core/xds/xds_cluster_resource_type_test.cc
  23. 929
      test/core/xds/xds_http_filters_test.cc
  24. 1980
      test/core/xds/xds_listener_resource_type_test.cc
  25. 13
      test/cpp/end2end/xds/BUILD
  26. 70
      test/cpp/end2end/xds/no_op_http_filter.h
  27. 738
      test/cpp/end2end/xds/xds_end2end_test.cc
  28. 1
      test/cpp/end2end/xds/xds_outlier_detection_end2end_test.cc
  29. 404
      test/cpp/end2end/xds/xds_routing_end2end_test.cc
  30. 48
      tools/run_tests/generated/tests.json

258
CMakeLists.txt generated

@ -1268,9 +1268,11 @@ 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()
@ -20610,6 +20612,10 @@ 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
@ -21716,6 +21722,116 @@ 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
@ -21954,6 +22070,140 @@ 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)
@ -22516,14 +22766,6 @@ 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

@ -11322,6 +11322,7 @@ 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
@ -11581,7 +11582,6 @@ 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
@ -11701,6 +11701,46 @@ 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
@ -11787,6 +11827,52 @@ 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
@ -11795,7 +11881,6 @@ 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
@ -11964,7 +12049,6 @@ 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
@ -11982,8 +12066,6 @@ 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

@ -3524,6 +3524,7 @@ grpc_cc_library(
"grpc_xds_channel_stack_modifier",
"grpc_xds_client",
"iomgr_fwd",
"match",
"resolved_address",
"slice_refcount",
"status_helper",

@ -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 current_listener_;
XdsListenerResource::HttpConnectionManager current_listener_;
std::string route_config_name_;
RouteConfigWatcher* route_config_watcher_ = nullptr;
@ -468,8 +468,7 @@ XdsResolver::XdsConfigSelector::XdsConfigSelector(
// one.
if (!route_action->max_stream_duration.has_value()) {
route_action->max_stream_duration =
resolver_->current_listener_.http_connection_manager
.http_max_stream_duration;
resolver_->current_listener_.http_max_stream_duration;
}
Match(
route_action->action,
@ -524,8 +523,7 @@ XdsResolver::XdsConfigSelector::XdsConfigSelector(
}
}
// Populate filter list.
for (const auto& http_filter :
resolver_->current_listener_.http_connection_manager.http_filters) {
for (const auto& http_filter : resolver_->current_listener_.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 =
@ -602,7 +600,7 @@ XdsResolver::XdsConfigSelector::CreateMethodConfig(
}
// Handle xDS HTTP filters.
auto result = XdsRouting::GeneratePerHTTPFilterConfigs(
resolver_->current_listener_.http_connection_manager.http_filters,
resolver_->current_listener_.http_filters,
resolver_->current_virtual_host_, route, cluster_weight,
resolver_->args_);
if (!result.ok()) return result.status();
@ -889,39 +887,35 @@ 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;
}
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<RouteConfigWatcher>(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();
}
if (xds_client_ == nullptr) return;
current_listener_ = std::move(
absl::get<XdsListenerResource::HttpConnectionManager>(listener.listener));
MatchMutable(
&current_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<RouteConfigWatcher>(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));
});
}
namespace {

@ -34,10 +34,6 @@
#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

@ -25,7 +25,6 @@
#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"

@ -39,6 +39,10 @@
#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

@ -27,6 +27,7 @@
#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"
@ -53,6 +54,7 @@
#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 {
@ -100,39 +102,34 @@ std::string XdsClusterResource::ToString() const {
namespace {
absl::optional<CommonTlsContext> UpstreamTlsContextParse(
CommonTlsContext 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);
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 !=
auto extension = ExtractXdsExtension(context, typed_config, errors);
if (!extension.has_value()) return {};
if (extension->type !=
"envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext") {
ValidationErrors::ScopedField field(errors, ".type_url");
errors->AddError(
absl::StrCat("unrecognized transport socket type: ", type_url));
return absl::nullopt;
errors->AddError("unsupported transport socket type");
return {};
}
absl::string_view* serialized_upstream_tls_context =
absl::get_if<absl::string_view>(&extension->value);
if (serialized_upstream_tls_context == nullptr) {
errors->AddError("can't decode UpstreamTlsContext");
return {};
}
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 absl::nullopt;
return {};
}
ValidationErrors::ScopedField field3(errors, ".common_tls_context");
const auto* common_tls_context_proto =
@ -394,11 +391,8 @@ absl::StatusOr<XdsClusterResource> CdsResourceParse(
envoy_config_cluster_v3_Cluster_transport_socket(cluster);
if (transport_socket != nullptr) {
ValidationErrors::ScopedField field(&errors, ".transport_socket");
auto common_tls_context =
cds_update.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 =

@ -24,7 +24,6 @@
#include <string>
#include <utility>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
@ -39,6 +38,7 @@
#include <grpc/status.h>
#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,9 +50,6 @@
namespace grpc_core {
const char* kXdsHttpFaultFilterConfigName =
"envoy.extensions.filters.http.fault.v3.HTTPFault";
namespace {
uint32_t GetDenominator(const envoy_type_v3_FractionalPercent* fraction) {
@ -74,13 +71,36 @@ uint32_t GetDenominator(const envoy_type_v3_FractionalPercent* fraction) {
return 100;
}
absl::StatusOr<Json> ParseHttpFaultIntoJson(
absl::string_view serialized_http_fault, upb_Arena* arena) {
} // 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<XdsHttpFilterImpl::FilterConfig>
XdsHttpFaultFilter::GenerateFilterConfig(XdsExtension extension,
upb_Arena* arena,
ValidationErrors* errors) const {
absl::string_view* serialized_filter_config =
absl::get_if<absl::string_view>(&extension.value);
if (serialized_filter_config == nullptr) {
errors->AddError("could not parse fault injection filter config");
return absl::nullopt;
}
auto* http_fault = envoy_extensions_filters_http_fault_v3_HTTPFault_parse(
serialized_http_fault.data(), serialized_http_fault.size(), arena);
serialized_filter_config->data(), serialized_filter_config->size(),
arena);
if (http_fault == nullptr) {
return absl::InvalidArgumentError(
"could not parse fault injection filter config");
errors->AddError("could not parse fault injection filter config");
return absl::nullopt;
}
// 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
@ -95,6 +115,7 @@ absl::StatusOr<Json> ParseHttpFaultIntoJson(
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 =
@ -103,8 +124,9 @@ absl::StatusOr<Json> ParseHttpFaultIntoJson(
if (abort_grpc_status_code_raw != 0) {
if (!grpc_status_code_from_int(abort_grpc_status_code_raw,
&abort_grpc_status_code)) {
return absl::InvalidArgumentError(absl::StrCat(
"invalid gRPC status code: ", abort_grpc_status_code_raw));
ValidationErrors::ScopedField field(errors, ".grpc_status");
errors->AddError(absl::StrCat("invalid gRPC status code: ",
abort_grpc_status_code_raw));
}
} else {
// if gRPC status code is empty, check http status
@ -131,23 +153,25 @@ absl::StatusOr<Json> ParseHttpFaultIntoJson(
auto* percent =
envoy_extensions_filters_http_fault_v3_FaultAbort_percentage(
fault_abort);
fault_injection_policy_json["abortPercentageNumerator"] =
Json(envoy_type_v3_FractionalPercent_numerator(percent));
fault_injection_policy_json["abortPercentageDenominator"] =
Json(GetDenominator(percent));
if (percent != nullptr) {
fault_injection_policy_json["abortPercentageNumerator"] =
envoy_type_v3_FractionalPercent_numerator(percent);
fault_injection_policy_json["abortPercentageDenominator"] =
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 errors;
Duration duration = ParseDuration(delay_duration, &errors);
if (!errors.ok()) return errors.status("fixed_delay");
ValidationErrors::ScopedField field(errors, ".fixed_delay");
Duration duration = ParseDuration(delay_duration, errors);
fault_injection_policy_json["delay"] = duration.ToJsonString();
}
// Set the headers if we enabled header delay injection control
@ -162,10 +186,12 @@ absl::StatusOr<Json> ParseHttpFaultIntoJson(
auto* percent =
envoy_extensions_filters_common_fault_v3_FaultDelay_percentage(
fault_delay);
fault_injection_policy_json["delayPercentageNumerator"] =
Json(envoy_type_v3_FractionalPercent_numerator(percent));
fault_injection_policy_json["delayPercentageDenominator"] =
Json(GetDenominator(percent));
if (percent != nullptr) {
fault_injection_policy_json["delayPercentageNumerator"] =
envoy_type_v3_FractionalPercent_numerator(percent);
fault_injection_policy_json["delayPercentageDenominator"] =
GetDenominator(percent);
}
}
// Section 3: Parse the maximum active faults
const auto* max_fault_wrapper =
@ -175,38 +201,16 @@ absl::StatusOr<Json> ParseHttpFaultIntoJson(
fault_injection_policy_json["maxFaults"] =
google_protobuf_UInt32Value_value(max_fault_wrapper);
}
return fault_injection_policy_json;
}
} // namespace
void XdsHttpFaultFilter::PopulateSymtab(upb_DefPool* symtab) const {
envoy_extensions_filters_http_fault_v3_HTTPFault_getmsgdef(symtab);
}
absl::StatusOr<XdsHttpFilterImpl::FilterConfig>
XdsHttpFaultFilter::GenerateFilterConfig(XdsExtension extension,
upb_Arena* arena) const {
absl::string_view* serialized_filter_config =
absl::get_if<absl::string_view>(&extension.value);
if (serialized_filter_config == nullptr) {
return absl::InvalidArgumentError(
"could not parse fault injection filter config");
}
absl::StatusOr<Json> parse_result =
ParseHttpFaultIntoJson(*serialized_filter_config, arena);
if (!parse_result.ok()) {
return parse_result.status();
}
return FilterConfig{kXdsHttpFaultFilterConfigName, std::move(*parse_result)};
return FilterConfig{ConfigProtoName(),
std::move(fault_injection_policy_json)};
}
absl::StatusOr<XdsHttpFilterImpl::FilterConfig>
XdsHttpFaultFilter::GenerateFilterConfigOverride(XdsExtension extension,
upb_Arena* arena) const {
absl::optional<XdsHttpFilterImpl::FilterConfig>
XdsHttpFaultFilter::GenerateFilterConfigOverride(
XdsExtension extension, upb_Arena* arena, ValidationErrors* errors) 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);
return GenerateFilterConfig(std::move(extension), arena, errors);
}
const grpc_channel_filter* XdsHttpFaultFilter::channel_filter() const {

@ -20,6 +20,8 @@
#include <grpc/support/port_platform.h>
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "upb/arena.h"
#include "upb/def.h"
@ -27,40 +29,30 @@
#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:
// Overrides the PopulateSymtab method
absl::string_view ConfigProtoName() const override;
absl::string_view OverrideConfigProtoName() const override;
void PopulateSymtab(upb_DefPool* symtab) const override;
// Overrides the GenerateFilterConfig method
absl::StatusOr<FilterConfig> GenerateFilterConfig(
XdsExtension extension, upb_Arena* arena) const override;
// Overrides the GenerateFilterConfigOverride method
absl::StatusOr<FilterConfig> GenerateFilterConfigOverride(
XdsExtension extension, upb_Arena* arena) const override;
// Overrides the channel_filter method
absl::optional<FilterConfig> GenerateFilterConfig(
XdsExtension extension, upb_Arena* arena,
ValidationErrors* errors) const override;
absl::optional<FilterConfig> GenerateFilterConfigOverride(
XdsExtension extension, upb_Arena* arena,
ValidationErrors* errors) const override;
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<ServiceConfigJsonEntry> 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

@ -23,63 +23,65 @@
#include <utility>
#include <vector>
#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 <grpc/support/log.h>
#include "src/core/ext/xds/xds_http_fault_filter.h"
#include "src/core/ext/xds/xds_http_rbac_filter.h"
namespace grpc_core {
const char* kXdsHttpRouterFilterConfigName =
"envoy.extensions.filters.http.router.v3.Router";
//
// XdsHttpRouterFilter
//
namespace {
absl::string_view XdsHttpRouterFilter::ConfigProtoName() const {
return "envoy.extensions.filters.http.router.v3.Router";
}
class XdsHttpRouterFilter : public XdsHttpFilterImpl {
public:
void PopulateSymtab(upb_DefPool* symtab) const override {
envoy_extensions_filters_http_router_v3_Router_getmsgdef(symtab);
}
absl::string_view XdsHttpRouterFilter::OverrideConfigProtoName() const {
return "";
}
absl::StatusOr<FilterConfig> GenerateFilterConfig(
XdsExtension extension, upb_Arena* arena) const override {
absl::string_view* serialized_filter_config =
absl::get_if<absl::string_view>(&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()};
}
void XdsHttpRouterFilter::PopulateSymtab(upb_DefPool* symtab) const {
envoy_extensions_filters_http_router_v3_Router_getmsgdef(symtab);
}
absl::StatusOr<FilterConfig> GenerateFilterConfigOverride(
XdsExtension /*extension*/, upb_Arena* /*arena*/) const override {
return absl::InvalidArgumentError(
"router filter does not support config override");
absl::optional<XdsHttpFilterImpl::FilterConfig>
XdsHttpRouterFilter::GenerateFilterConfig(XdsExtension extension,
upb_Arena* arena,
ValidationErrors* errors) const {
absl::string_view* serialized_filter_config =
absl::get_if<absl::string_view>(&extension.value);
if (serialized_filter_config == 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<ServiceConfigJsonEntry> GenerateServiceConfig(
const FilterConfig& /*hcm_filter_config*/,
const FilterConfig* /*filter_config_override*/) const override {
return absl::UnimplementedError("router filter should never be called");
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;
}
return FilterConfig{ConfigProtoName(), Json()};
}
bool IsSupportedOnClients() const override { return true; }
absl::optional<XdsHttpFilterImpl::FilterConfig>
XdsHttpRouterFilter::GenerateFilterConfigOverride(
XdsExtension /*extension*/, upb_Arena* /*arena*/,
ValidationErrors* errors) const {
errors->AddError("router filter does not support config override");
return absl::nullopt;
}
bool IsSupportedOnServers() const override { return true; }
//
// XdsHttpFilterRegistry
//
bool IsTerminalFilter() const override { return true; }
};
namespace {
using FilterOwnerList = std::vector<std::unique_ptr<XdsHttpFilterImpl>>;
using FilterRegistryMap = std::map<absl::string_view, XdsHttpFilterImpl*>;
@ -90,10 +92,13 @@ FilterRegistryMap* g_filter_registry = nullptr;
} // namespace
void XdsHttpFilterRegistry::RegisterFilter(
std::unique_ptr<XdsHttpFilterImpl> filter,
const std::set<absl::string_view>& config_proto_type_names) {
for (auto config_proto_type_name : config_proto_type_names) {
(*g_filter_registry)[config_proto_type_name] = filter.get();
std::unique_ptr<XdsHttpFilterImpl> 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);
}
g_filters->push_back(std::move(filter));
}
@ -111,17 +116,14 @@ void XdsHttpFilterRegistry::PopulateSymtab(upb_DefPool* symtab) {
}
}
void XdsHttpFilterRegistry::Init() {
void XdsHttpFilterRegistry::Init(bool register_builtins) {
g_filters = new FilterOwnerList;
g_filter_registry = new FilterRegistryMap;
RegisterFilter(std::make_unique<XdsHttpRouterFilter>(),
{kXdsHttpRouterFilterConfigName});
RegisterFilter(std::make_unique<XdsHttpFaultFilter>(),
{kXdsHttpFaultFilterConfigName});
RegisterFilter(std::make_unique<XdsHttpRbacFilter>(),
{kXdsHttpRbacFilterConfigName});
RegisterFilter(std::make_unique<XdsHttpRbacFilter>(),
{kXdsHttpRbacFilterConfigOverrideName});
if (register_builtins) {
RegisterFilter(std::make_unique<XdsHttpRouterFilter>());
RegisterFilter(std::make_unique<XdsHttpFaultFilter>());
RegisterFilter(std::make_unique<XdsHttpRbacFilter>());
}
}
void XdsHttpFilterRegistry::Shutdown() {

@ -20,24 +20,24 @@
#include <grpc/support/port_platform.h>
#include <memory>
#include <set>
#include <string>
#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,25 +69,33 @@ 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::StatusOr<FilterConfig> GenerateFilterConfig(
XdsExtension extension, upb_Arena* arena) const = 0;
virtual absl::optional<FilterConfig> GenerateFilterConfig(
XdsExtension extension, upb_Arena* arena,
ValidationErrors* errors) 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::StatusOr<FilterConfig> GenerateFilterConfigOverride(
XdsExtension extension, upb_Arena* arena) const = 0;
virtual absl::optional<FilterConfig> GenerateFilterConfigOverride(
XdsExtension extension, upb_Arena* arena,
ValidationErrors* errors) 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;
}
@ -112,11 +120,32 @@ 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<FilterConfig> GenerateFilterConfig(
XdsExtension extension, upb_Arena* arena,
ValidationErrors* errors) const override;
absl::optional<FilterConfig> GenerateFilterConfigOverride(
XdsExtension extension, upb_Arena* arena,
ValidationErrors* errors) const override;
const grpc_channel_filter* channel_filter() const override { return nullptr; }
absl::StatusOr<ServiceConfigJsonEntry> 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<XdsHttpFilterImpl> filter,
const std::set<absl::string_view>& config_proto_type_names);
static void RegisterFilter(std::unique_ptr<XdsHttpFilterImpl> filter);
static const XdsHttpFilterImpl* GetFilterForType(
absl::string_view proto_type_name);
@ -124,10 +153,10 @@ class XdsHttpFilterRegistry {
static void PopulateSymtab(upb_DefPool* symtab);
// Global init and shutdown.
static void Init();
static void Init(bool register_builtins = true);
static void Shutdown();
};
} // namespace grpc_core
#endif /* GRPC_CORE_EXT_XDS_XDS_HTTP_FILTERS_H */
#endif // GRPC_CORE_EXT_XDS_XDS_HTTP_FILTERS_H

@ -25,12 +25,9 @@
#include <map>
#include <string>
#include <utility>
#include <vector>
#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"
@ -54,12 +51,6 @@
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(
@ -74,18 +65,20 @@ Json ParseInt64RangeToJson(const envoy_type_v3_Int64Range* range) {
{"end", envoy_type_v3_Int64Range_end(range)}};
}
absl::StatusOr<Json> ParseHeaderMatcherToJson(
const envoy_config_route_v3_HeaderMatcher* header) {
Json ParseHeaderMatcherToJson(const envoy_config_route_v3_HeaderMatcher* header,
ValidationErrors* errors) {
Json::Object header_json;
std::vector<std::string> 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");
{
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));
}
header_json.emplace("name", std::move(name));
if (envoy_config_route_v3_HeaderMatcher_has_exact_match(header)) {
header_json.emplace(
"exactMatch",
@ -121,19 +114,16 @@ absl::StatusOr<Json> ParseHeaderMatcherToJson(
UpbStringToStdString(
envoy_config_route_v3_HeaderMatcher_contains_match(header)));
} else {
errors.emplace_back("Invalid route header matcher specified.");
}
if (!errors.empty()) {
return absl::InvalidArgumentError(absl::StrCat(
"errors parsing HeaderMatcher: [", absl::StrJoin(errors, "; "), "]"));
errors->AddError("invalid route header matcher specified");
}
header_json.emplace("invertMatch",
envoy_config_route_v3_HeaderMatcher_invert_match(header));
return header_json;
}
absl::StatusOr<Json> ParseStringMatcherToJson(
const envoy_type_matcher_v3_StringMatcher* matcher) {
Json ParseStringMatcherToJson(
const envoy_type_matcher_v3_StringMatcher* matcher,
ValidationErrors* errors) {
Json::Object json;
if (envoy_type_matcher_v3_StringMatcher_has_exact(matcher)) {
json.emplace("exact",
@ -156,26 +146,23 @@ absl::StatusOr<Json> ParseStringMatcherToJson(
UpbStringToStdString(
envoy_type_matcher_v3_StringMatcher_contains(matcher)));
} else {
return absl::InvalidArgumentError("StringMatcher: Invalid match pattern");
errors->AddError("invalid match pattern");
}
json.emplace("ignoreCase",
envoy_type_matcher_v3_StringMatcher_ignore_case(matcher));
return json;
}
absl::StatusOr<Json> ParsePathMatcherToJson(
const envoy_type_matcher_v3_PathMatcher* matcher) {
Json ParsePathMatcherToJson(const envoy_type_matcher_v3_PathMatcher* matcher,
ValidationErrors* errors) {
ValidationErrors::ScopedField field(errors, ".path");
const auto* path = envoy_type_matcher_v3_PathMatcher_path(matcher);
if (path == nullptr) {
return absl::InvalidArgumentError("PathMatcher has empty path");
errors->AddError("field not present");
return 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 path_json = ParseStringMatcherToJson(path, errors);
return Json::Object{{"path", std::move(path_json)}};
}
Json ParseUInt32ValueToJson(const google_protobuf_UInt32Value* value) {
@ -205,65 +192,49 @@ Json ParseMetadataMatcherToJson(
return json;
}
absl::StatusOr<Json> ParsePermissionToJson(
const envoy_config_rbac_v3_Permission* permission) {
Json ParsePermissionToJson(const envoy_config_rbac_v3_Permission* permission,
ValidationErrors* errors) {
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 =
[](const envoy_config_rbac_v3_Permission_Set* set)
-> absl::StatusOr<Json> {
std::vector<std::string> errors;
[errors](const envoy_config_rbac_v3_Permission_Set* set) -> Json {
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) {
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, "; "), "]"));
ValidationErrors::ScopedField field(errors,
absl::StrCat(".rules[", i, "]"));
Json permission_json = ParsePermissionToJson(rules[i], errors);
rules_json.emplace_back(std::move(permission_json));
}
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);
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));
Json permission_set_json = parse_permission_set_to_json(and_rules);
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);
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));
Json permission_set_json = parse_permission_set_to_json(or_rules);
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)) {
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));
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));
} else if (envoy_config_rbac_v3_Permission_has_url_path(permission)) {
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));
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));
} else if (envoy_config_rbac_v3_Permission_has_destination_ip(permission)) {
permission_json.emplace(
"destinationIp",
@ -278,69 +249,53 @@ absl::StatusOr<Json> ParsePermissionToJson(
"metadata", ParseMetadataMatcherToJson(
envoy_config_rbac_v3_Permission_metadata(permission)));
} else if (envoy_config_rbac_v3_Permission_has_not_rule(permission)) {
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));
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));
} else if (envoy_config_rbac_v3_Permission_has_requested_server_name(
permission)) {
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;
}
ValidationErrors::ScopedField field(errors, ".requested_server_name");
Json requested_server_name_json = ParseStringMatcherToJson(
envoy_config_rbac_v3_Permission_requested_server_name(permission),
errors);
permission_json.emplace("requestedServerName",
std::move(*requested_server_name_json));
std::move(requested_server_name_json));
} else {
return absl::InvalidArgumentError("Permission: Invalid rule");
errors->AddError("invalid rule");
}
return permission_json;
}
absl::StatusOr<Json> ParsePrincipalToJson(
const envoy_config_rbac_v3_Principal* principal) {
Json ParsePrincipalToJson(const envoy_config_rbac_v3_Principal* principal,
ValidationErrors* errors) {
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 =
[](const envoy_config_rbac_v3_Principal_Set* set)
-> absl::StatusOr<Json> {
Json::Object json;
std::vector<std::string> errors;
[errors](const envoy_config_rbac_v3_Principal_Set* set) -> Json {
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) {
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, "; "), "]"));
ValidationErrors::ScopedField field(errors,
absl::StrCat(".ids[", i, "]"));
Json principal_json = ParsePrincipalToJson(ids[i], errors);
ids_json.emplace_back(std::move(principal_json));
}
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);
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));
Json principal_set_json = parse_principal_set_to_json(and_rules);
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);
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));
Json principal_set_json = parse_principal_set_to_json(or_rules);
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));
@ -352,12 +307,12 @@ absl::StatusOr<Json> ParsePrincipalToJson(
envoy_config_rbac_v3_Principal_Authenticated_principal_name(
envoy_config_rbac_v3_Principal_authenticated(principal));
if (principal_name != nullptr) {
auto principal_name_json = ParseStringMatcherToJson(principal_name);
if (!principal_name_json.ok()) {
return principal_name_json;
}
ValidationErrors::ScopedField field(errors,
".authenticated.principal_name");
Json principal_name_json =
ParseStringMatcherToJson(principal_name, errors);
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(
@ -373,84 +328,71 @@ absl::StatusOr<Json> ParsePrincipalToJson(
"remoteIp", ParseCidrRangeToJson(
envoy_config_rbac_v3_Principal_remote_ip(principal)));
} else if (envoy_config_rbac_v3_Principal_has_header(principal)) {
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));
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));
} else if (envoy_config_rbac_v3_Principal_has_url_path(principal)) {
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));
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));
} 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)) {
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));
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));
} else {
return absl::InvalidArgumentError("Principal: Invalid rule");
errors->AddError("invalid rule");
}
return principal_json;
}
absl::StatusOr<Json> ParsePolicyToJson(
const envoy_config_rbac_v3_Policy* policy) {
Json ParsePolicyToJson(const envoy_config_rbac_v3_Policy* policy,
ValidationErrors* errors) {
Json::Object policy_json;
std::vector<std::string> 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) {
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));
}
ValidationErrors::ScopedField field(errors,
absl::StrCat(".permissions[", i, "]"));
Json permission_json = ParsePermissionToJson(permissions[i], errors);
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) {
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));
}
ValidationErrors::ScopedField field(errors,
absl::StrCat(".principals[", i, "]"));
Json principal_json = ParsePrincipalToJson(principals[i], errors);
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)) {
errors.emplace_back("Policy: condition not supported");
ValidationErrors::ScopedField field(errors, ".condition");
errors->AddError("condition not supported");
}
if (envoy_config_rbac_v3_Policy_has_checked_condition(policy)) {
errors.emplace_back("Policy: checked condition not supported");
}
if (!errors.empty()) {
return absl::InvalidArgumentError(absl::StrCat(
"errors parsing Policy: [", absl::StrJoin(errors, "; "), "]"));
ValidationErrors::ScopedField field(errors, ".checked_condition");
errors->AddError("checked condition not supported");
}
return policy_json;
}
absl::StatusOr<Json> ParseHttpRbacToJson(
const envoy_extensions_filters_http_rbac_v3_RBAC* rbac) {
Json ParseHttpRbacToJson(const envoy_extensions_filters_http_rbac_v3_RBAC* rbac,
ValidationErrors* errors) {
Json::Object rbac_json;
std::vector<std::string> 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) {
@ -466,89 +408,82 @@ absl::StatusOr<Json> ParseHttpRbacToJson(
if (entry == nullptr) {
break;
}
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));
}
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));
}
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::StatusOr<XdsHttpFilterImpl::FilterConfig>
absl::optional<XdsHttpFilterImpl::FilterConfig>
XdsHttpRbacFilter::GenerateFilterConfig(XdsExtension extension,
upb_Arena* arena) const {
upb_Arena* arena,
ValidationErrors* errors) const {
absl::string_view* serialized_filter_config =
absl::get_if<absl::string_view>(&extension.value);
if (serialized_filter_config == nullptr) {
return absl::InvalidArgumentError(
"could not parse HTTP RBAC filter config");
errors->AddError("could not parse HTTP RBAC filter config");
return absl::nullopt;
}
auto* rbac = envoy_extensions_filters_http_rbac_v3_RBAC_parse(
serialized_filter_config->data(), serialized_filter_config->size(),
arena);
if (rbac == nullptr) {
return absl::InvalidArgumentError(
"could not parse HTTP RBAC filter config");
errors->AddError("could not parse HTTP RBAC filter config");
return absl::nullopt;
}
absl::StatusOr<Json> rbac_json = ParseHttpRbacToJson(rbac);
if (!rbac_json.ok()) {
return rbac_json.status();
}
return FilterConfig{kXdsHttpRbacFilterConfigName, std::move(*rbac_json)};
return FilterConfig{ConfigProtoName(), ParseHttpRbacToJson(rbac, errors)};
}
absl::StatusOr<XdsHttpFilterImpl::FilterConfig>
XdsHttpRbacFilter::GenerateFilterConfigOverride(XdsExtension extension,
upb_Arena* arena) const {
absl::optional<XdsHttpFilterImpl::FilterConfig>
XdsHttpRbacFilter::GenerateFilterConfigOverride(
XdsExtension extension, upb_Arena* arena, ValidationErrors* errors) const {
absl::string_view* serialized_filter_config =
absl::get_if<absl::string_view>(&extension.value);
if (serialized_filter_config == nullptr) {
return absl::InvalidArgumentError("could not parse RBACPerRoute");
errors->AddError("could not parse RBACPerRoute");
return absl::nullopt;
}
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) {
return absl::InvalidArgumentError("could not parse RBACPerRoute");
errors->AddError("could not parse RBACPerRoute");
return absl::nullopt;
}
absl::StatusOr<Json> rbac_json;
Json rbac_json;
const auto* rbac =
envoy_extensions_filters_http_rbac_v3_RBACPerRoute_rbac(rbac_per_route);
if (rbac == nullptr) {
rbac_json = Json::Object();
} else {
rbac_json = ParseHttpRbacToJson(rbac);
if (!rbac_json.ok()) {
return rbac_json.status();
}
ValidationErrors::ScopedField field(errors, ".rbac");
rbac_json = ParseHttpRbacToJson(rbac, errors);
}
return FilterConfig{kXdsHttpRbacFilterConfigOverrideName,
std::move(*rbac_json)};
return FilterConfig{OverrideConfigProtoName(), std::move(rbac_json)};
}
const grpc_channel_filter* XdsHttpRbacFilter::channel_filter() const {

@ -20,6 +20,8 @@
#include <grpc/support/port_platform.h>
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "upb/arena.h"
#include "upb/def.h"
@ -27,32 +29,27 @@
#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::StatusOr<FilterConfig> GenerateFilterConfig(
XdsExtension extension, upb_Arena* arena) const override;
absl::StatusOr<FilterConfig> GenerateFilterConfigOverride(
XdsExtension extension, upb_Arena* arena) const override;
absl::optional<FilterConfig> GenerateFilterConfig(
XdsExtension extension, upb_Arena* arena,
ValidationErrors* errors) const override;
absl::optional<FilterConfig> GenerateFilterConfigOverride(
XdsExtension extension, upb_Arena* arena,
ValidationErrors* errors) const override;
const grpc_channel_filter* channel_filter() const override;
ChannelArgs ModifyChannelArgs(const ChannelArgs& args) const override;
absl::StatusOr<ServiceConfigJsonEntry> 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; }
};

File diff suppressed because it is too large Load Diff

@ -32,6 +32,7 @@
#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"
@ -46,36 +47,14 @@
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 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;
// The RDS resource name or inline RouteConfiguration.
absl::variant<std::string, XdsRouteConfigResource> route_config;
// 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<XdsRouteConfigResource> rds_update;
struct HttpFilter {
std::string name;
@ -90,21 +69,26 @@ struct XdsListenerResource : public XdsResourceType::ResourceData {
std::vector<HttpFilter> http_filters;
bool operator==(const HttpConnectionManager& other) const {
return route_config_name == other.route_config_name &&
return route_config == other.route_config &&
http_max_stream_duration == other.http_max_stream_duration &&
rds_update == other.rds_update &&
http_filters == other.http_filters;
}
std::string ToString() const;
};
// Populated for type=kHttpApiListener.
HttpConnectionManager http_connection_manager;
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=kTcpListener.
// host:port listening_address set when type is kTcpListener
std::string address;
std::string ToString() const;
bool Empty() const;
};
struct FilterChainData {
DownstreamTlsContext downstream_tls_context;
@ -185,15 +169,26 @@ struct XdsListenerResource : public XdsResourceType::ResourceData {
}
std::string ToString() const;
} filter_chain_map;
};
struct TcpListener {
std::string address; // host:port listening address
FilterChainMap filter_chain_map;
absl::optional<FilterChainData> 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;
};
absl::optional<FilterChainData> default_filter_chain;
absl::variant<HttpConnectionManager, TcpListener> listener;
bool operator==(const XdsListenerResource& other) const {
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;
return listener == other.listener;
}
std::string ToString() const;

@ -638,14 +638,13 @@ ParseTypedPerFilterConfig(
return absl::InvalidArgumentError(absl::StrCat(
"no filter registered for config type ", extension->type));
}
absl::StatusOr<XdsHttpFilterImpl::FilterConfig> filter_config =
absl::optional<XdsHttpFilterImpl::FilterConfig> filter_config =
filter_impl->GenerateFilterConfigOverride(std::move(*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()));
context.arena, &errors);
if (!errors.ok()) {
return errors.status("errors validation extension");
}
GPR_ASSERT(filter_config.has_value());
typed_per_filter_config[std::string(key)] = std::move(*filter_config);
}
return typed_per_filter_config;

@ -70,6 +70,7 @@
#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"
@ -550,7 +551,9 @@ void XdsServerConfigFetcher::ListenerWatcher::OnResourceChanged(
"[ListenerWatcher %p] Received LDS update from xds client %p: %s",
this, xds_client_.get(), listener.ToString().c_str());
}
if (listener.address != listening_address_) {
auto& tcp_listener =
absl::get<XdsListenerResource::TcpListener>(listener.listener);
if (tcp_listener.address != listening_address_) {
MutexLock lock(&mu_);
OnFatalError(absl::FailedPreconditionError(
"Address in LDS update does not match listening address"));
@ -558,8 +561,8 @@ void XdsServerConfigFetcher::ListenerWatcher::OnResourceChanged(
}
auto new_filter_chain_match_manager = MakeRefCounted<FilterChainMatchManager>(
xds_client_->Ref(DEBUG_LOCATION, "FilterChainMatchManager"),
std::move(listener.filter_chain_map),
std::move(listener.default_filter_chain));
std::move(tcp_listener.filter_chain_map),
std::move(tcp_listener.default_filter_chain));
MutexLock lock(&mu_);
if (filter_chain_match_manager_ == nullptr ||
!(new_filter_chain_match_manager->filter_chain_map() ==
@ -675,32 +678,25 @@ 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) {
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());
auto* filter_chain_data = source_port_pair.second.data.get();
const auto* rds_name = absl::get_if<std::string>(
&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 (default_filter_chain_.has_value()) {
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());
auto& hcm = default_filter_chain_->http_connection_manager;
const auto* rds_name = absl::get_if<std::string>(&hcm.route_config);
if (rds_name != nullptr) resource_names.insert(*rds_name);
std::reverse(hcm.http_filters.begin(), hcm.http_filters.end());
}
// Reverse the lists of HTTP filters in all the filter chains
for (auto* filter_chain_data : filter_chain_data_set) {
std::reverse(
filter_chain_data->http_connection_manager.http_filters.begin(),
filter_chain_data->http_connection_manager.http_filters.end());
auto& hcm = filter_chain_data->http_connection_manager;
std::reverse(hcm.http_filters.begin(), hcm.http_filters.end());
}
// Start watching on referenced RDS resources
struct WatcherToStart {
@ -1076,27 +1072,29 @@ absl::StatusOr<ChannelArgs> XdsServerConfigFetcher::ListenerWatcher::
filters.push_back(&kServerConfigSelectorFilter);
channel_stack_modifier =
MakeRefCounted<XdsChannelStackModifier>(std::move(filters));
if (filter_chain->http_connection_manager.rds_update.has_value()) {
server_config_selector_provider =
MakeRefCounted<StaticXdsServerConfigSelectorProvider>(
filter_chain->http_connection_manager.rds_update.value(),
filter_chain->http_connection_manager.http_filters);
} else {
absl::StatusOr<XdsRouteConfigResource> 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<DynamicXdsServerConfigSelectorProvider>(
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);
}
Match(
filter_chain->http_connection_manager.route_config,
// RDS resource name
[&](const std::string& rds_name) {
absl::StatusOr<XdsRouteConfigResource> initial_resource;
{
MutexLock lock(&mu_);
initial_resource = rds_map_[rds_name].rds_update.value();
}
server_config_selector_provider =
MakeRefCounted<DynamicXdsServerConfigSelectorProvider>(
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<StaticXdsServerConfigSelectorProvider>(
route_config,
filter_chain->http_connection_manager.http_filters);
});
args = args.SetObject(server_config_selector_provider)
.SetObject(channel_stack_modifier);
// Add XdsCertificateProvider if credentials are xDS.

@ -24,6 +24,12 @@ 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 <config_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:

@ -426,6 +426,8 @@ 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.

@ -107,6 +107,7 @@ grpc_cc_test(
srcs = ["xds_lb_policy_registry_test.cc"],
external_deps = ["gtest"],
language = "C++",
uses_event_engine = False,
uses_polling = False,
deps = [
"//:gpr",
@ -174,6 +175,47 @@ 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"],
@ -188,6 +230,7 @@ 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",
],
)

@ -53,6 +53,7 @@
#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;
@ -809,7 +810,7 @@ TEST_F(TlsConfigTest, UnknownCertificateProviderInstance) {
<< decode_result.resource.status();
}
TEST_F(TlsConfigTest, TransportSocketTypedConfigUnset) {
TEST_F(TlsConfigTest, UnknownTransportSocketType) {
Cluster cluster;
cluster.set_name("foo");
cluster.set_type(cluster.EDS);
@ -827,13 +828,13 @@ TEST_F(TlsConfigTest, TransportSocketTypedConfigUnset) {
absl::StatusCode::kInvalidArgument);
EXPECT_EQ(decode_result.resource.status().message(),
"errors validating Cluster resource: ["
"field:transport_socket.typed_config.type_url "
"error:unrecognized transport socket type: "
"envoy.config.cluster.v3.Cluster]")
"field:transport_socket.typed_config.value["
"envoy.config.cluster.v3.Cluster].type_url "
"error:unsupported transport socket type]")
<< decode_result.resource.status();
}
TEST_F(TlsConfigTest, UnknownTransportSocketType) {
TEST_F(TlsConfigTest, UnparseableUpstreamTlsContext) {
Cluster cluster;
cluster.set_name("foo");
cluster.set_type(cluster.EDS);
@ -859,6 +860,35 @@ TEST_F(TlsConfigTest, UnknownTransportSocketType) {
<< 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");

@ -0,0 +1,929 @@
//
// 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 <algorithm>
#include <string>
#include <utility>
#include <vector>
#include <google/protobuf/any.pb.h>
#include <google/protobuf/duration.pb.h>
#include <google/protobuf/wrappers.pb.h>
#include "absl/status/status.h"
#include "absl/strings/strip.h"
#include "absl/types/variant.h"
#include "gtest/gtest.h"
#include "upb/upb.hpp"
#include <grpc/grpc.h>
#include <grpc/status.h>
#include <grpc/support/log.h>
#include <grpcpp/impl/codegen/config_protobuf.h>
#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"
// IWYU pragma: no_include <google/protobuf/message.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 grpc::protobuf::Message& message) {
google::protobuf::Any any;
any.PackFrom(message);
type_url_storage_ =
std::string(absl::StripPrefix(any.type_url(), "type.googleapis.com/"));
serialized_storage_ = std::string(any.value());
ValidationErrors::ScopedField field(
&errors_, absl::StrCat("http_filter.value[", type_url_storage_, "]"));
XdsExtension extension;
extension.type = absl::string_view(type_url_storage_);
extension.value = absl::string_view(serialized_storage_);
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_;
std::string type_url_storage_;
std::string serialized_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<XdsHttpRouterFilter>();
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<XdsHttpRouterFilter>()),
"");
}
//
// 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<bool> {
protected:
absl::optional<XdsHttpFilterImpl::FilterConfig> 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<bool> {
protected:
absl::optional<XdsHttpFilterImpl::FilterConfig> 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;
}

File diff suppressed because it is too large Load Diff

@ -45,15 +45,6 @@ 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,
@ -112,7 +103,6 @@ grpc_cc_test(
"no_windows",
], # TODO(jtattermusch): fix test on windows
deps = [
":no_op_http_filter",
":xds_end2end_test_lib",
"//:gpr",
"//:grpc",
@ -279,7 +269,6 @@ grpc_cc_test(
"no_windows",
], # TODO(jtattermusch): fix test on windows
deps = [
":no_op_http_filter",
":xds_end2end_test_lib",
"//:gpr",
"//:grpc",
@ -360,12 +349,10 @@ 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",
],

@ -1,70 +0,0 @@
// Copyright 2017 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string>
#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<grpc_core::XdsHttpFilterImpl::FilterConfig>
GenerateFilterConfig(grpc_core::XdsExtension /*extension*/,
upb_Arena* /*arena*/) const override {
return grpc_core::XdsHttpFilterImpl::FilterConfig{name_, grpc_core::Json()};
}
absl::StatusOr<grpc_core::XdsHttpFilterImpl::FilterConfig>
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<grpc_core::XdsHttpFilterImpl::ServiceConfigJsonEntry>
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

@ -101,7 +101,6 @@
#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"
@ -767,183 +766,24 @@ 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(
absl::StrCat("grpc/server?xds.resource.listening_address=",
ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()));
listener.set_name(GetServerListenerName(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_THAT(
EXPECT_EQ(
response_state->error_message,
::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);
absl::StrCat(
"xDS response validation errors: [resource index 0: ",
GetServerListenerName(backends_[0]->port()),
": INVALID_ARGUMENT: Listener has neither address nor ApiListener]"));
}
// Verify that a mismatch of listening address results in "not serving"
@ -963,21 +803,6 @@ 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 {
@ -1202,173 +1027,6 @@ class XdsServerSecurityTest : public XdsEnd2endTest {
std::vector<std::string> 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_}}});
@ -2130,232 +1788,6 @@ 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) {
@ -2608,116 +2040,6 @@ 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;
@ -3587,30 +2909,6 @@ 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
@ -3714,24 +3012,6 @@ 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::NoOpHttpFilter>(
"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::NoOpHttpFilter>(
"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::NoOpHttpFilter>(
"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;

@ -25,7 +25,6 @@
#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 {

@ -21,16 +21,13 @@
#include <gtest/gtest.h>
#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;
@ -38,9 +35,10 @@ using LdsTest = XdsEnd2endTest;
INSTANTIATE_TEST_SUITE_P(XdsTest, LdsTest, ::testing::Values(XdsTestType()),
&XdsTestType::Name);
// 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) {
// 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) {
auto listener = default_listener_;
listener.clear_api_listener();
balancer_->ads_service()->SetLdsResource(listener);
@ -51,382 +49,6 @@ TEST_P(LdsTest, NoApiListener) {
::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().
@ -3126,24 +2748,6 @@ int main(int argc, char** argv) {
grpc_core::SetEnv("grpc_cfstream", "0");
#endif
grpc_init();
grpc_core::XdsHttpFilterRegistry::RegisterFilter(
std::make_unique<grpc::testing::NoOpHttpFilter>(
"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::NoOpHttpFilter>(
"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::NoOpHttpFilter>(
"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;

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

Loading…
Cancel
Save