xDS cluster resource type: use ValidationErrors and add unit test (#31025)

* XdsBootstrap: move two more methods out of the interface

* Automated change: Fix sanity tests

* XdsClient: add unit test

* Automated change: Fix sanity tests

* fix memory leaks

* add helper method

* add unsubscription

* add test for multiple subscriptions

* clang-format

* fix build

* fix flakiness

* add checking for other node fields

* add v2 test

* add response builder

* add test for update from server

* add test for update containing only changed resources

* clang-format

* fix build

* add test for resource not existing upon subscription

* add test for stream closed by server

* add test for multiple watchers for the same resource

* add test for connection failure

* clang-format

* add test for resources wrapped in Resource wrapper message

* add test for resource validation failure

* add test for multiple invalid resources, and fix a case in XdsClient

* add test for validation failure for already-cached resource

* add test for server not resending resources after stream disconnect

* clang-format

* fix XdsClient to report channel errors to newly started watchers

* fix XdsClient to send cached errors/does-not-exists to newly started watchers

* fix watcher to ensure events arrive in the expected order

* fix tests

* clang-format

* add test for multiple resource types

* fix xds_cluster_e2e_test

* Automated change: Fix sanity tests

* cleanup

* add federation tests

* clang-format

* remove now-unnecessary XdsCertificateProviderPluginMapInterface

* code review comments

* simplify XdsResourceType::Decode() API

* XdsClient: add unit tests for XdsClusterResourceType

* add XdsClient with gRPC bootstrap config

* add LB policy tests

* started adding CertificateProvider tests

* update for recent API changes

* fix merge bugs

* xDS resource validation: identify extensions by type_url instead of name

* fix build

* migrate to ValidationErrors

* add xds_common_types_test

* finish TLS tests and add LRS tests

* move ScopedExperimentalEnvVar to its own library and remove redundant e2e tests

* add circuit breaking and outlier detection tests

* add validation to outlier detection LB policy parsing

* clang-format

* Automated change: Fix sanity tests

* fix signedness

* fix sanity

* fix sanity

* iwyu

* update code for XdsResourceTypeImpl changes

Co-authored-by: markdroth <markdroth@users.noreply.github.com>
pull/31251/head
Mark D. Roth 2 years ago committed by GitHub
parent 126393c9c1
commit f2a377d35e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 180
      CMakeLists.txt
  2. 53
      build_autogenerated.yaml
  3. 28
      src/core/ext/filters/client_channel/lb_policy/outlier_detection/outlier_detection.cc
  4. 2
      src/core/ext/filters/client_channel/lb_policy/outlier_detection/outlier_detection.h
  5. 313
      src/core/ext/xds/xds_cluster.cc
  6. 236
      src/core/ext/xds/xds_common_types.cc
  7. 13
      src/core/ext/xds/xds_common_types.h
  8. 7
      src/core/ext/xds/xds_http_fault_filter.cc
  9. 19
      src/core/ext/xds/xds_listener.cc
  10. 20
      src/core/ext/xds/xds_route_config.cc
  11. 9
      src/core/lib/json/json_object_loader.cc
  12. 15
      test/core/client_channel/BUILD
  13. 166
      test/core/client_channel/outlier_detection_lb_config_parser_test.cc
  14. 6
      test/core/json/json_object_loader_test.cc
  15. 10
      test/core/util/BUILD
  16. 48
      test/core/util/scoped_env_var.h
  17. 35
      test/core/xds/BUILD
  18. 1160
      test/core/xds/xds_cluster_resource_type_test.cc
  19. 535
      test/core/xds/xds_common_types_test.cc
  20. 4
      test/cpp/end2end/xds/BUILD
  21. 74
      test/cpp/end2end/xds/xds_cluster_end2end_test.cc
  22. 169
      test/cpp/end2end/xds/xds_cluster_type_end2end_test.cc
  23. 16
      test/cpp/end2end/xds/xds_core_end2end_test.cc
  24. 4
      test/cpp/end2end/xds/xds_csds_end2end_test.cc
  25. 385
      test/cpp/end2end/xds/xds_end2end_test.cc
  26. 13
      test/cpp/end2end/xds/xds_end2end_test_lib.h
  27. 3
      test/cpp/end2end/xds/xds_outlier_detection_end2end_test.cc
  28. 95
      test/cpp/end2end/xds/xds_ring_hash_end2end_test.cc
  29. 3
      test/cpp/end2end/xds/xds_rls_end2end_test.cc
  30. 72
      tools/run_tests/generated/tests.json

180
CMakeLists.txt generated

@ -1069,6 +1069,7 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_cxx orca_service_end2end_test)
add_dependencies(buildtests_cxx orphanable_test)
add_dependencies(buildtests_cxx out_of_bounds_bad_client_test)
add_dependencies(buildtests_cxx outlier_detection_lb_config_parser_test)
add_dependencies(buildtests_cxx overload_test)
add_dependencies(buildtests_cxx parse_address_test)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
@ -1261,9 +1262,11 @@ if(gRPC_BUILD_TESTS)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_dependencies(buildtests_cxx xds_cluster_end2end_test)
endif()
add_dependencies(buildtests_cxx xds_cluster_resource_type_test)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_dependencies(buildtests_cxx xds_cluster_type_end2end_test)
endif()
add_dependencies(buildtests_cxx xds_common_types_test)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_dependencies(buildtests_cxx xds_core_end2end_test)
endif()
@ -14041,6 +14044,41 @@ target_link_libraries(out_of_bounds_bad_client_test
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(outlier_detection_lb_config_parser_test
test/core/client_channel/outlier_detection_lb_config_parser_test.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(outlier_detection_lb_config_parser_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(outlier_detection_lb_config_parser_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
)
endif()
if(gRPC_BUILD_TESTS)
@ -20326,6 +20364,89 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
endif()
endif()
if(gRPC_BUILD_TESTS)
add_executable(xds_cluster_resource_type_test
${_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/aggregate_cluster.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/aggregate_cluster.grpc.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/aggregate_cluster.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/aggregate_cluster.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/cluster.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/cluster.grpc.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/cluster.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/cluster.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/endpoint.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/endpoint.grpc.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/endpoint.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/endpoint.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/outlier_detection.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/outlier_detection.grpc.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/outlier_detection.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/outlier_detection.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/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/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
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
)
target_include_directories(xds_cluster_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_cluster_resource_type_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
)
endif()
if(gRPC_BUILD_TESTS)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
@ -20504,6 +20625,65 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
endif()
endif()
if(gRPC_BUILD_TESTS)
add_executable(xds_common_types_test
${_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/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/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/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/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
test/core/xds/xds_common_types_test.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(xds_common_types_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_common_types_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
)
endif()
if(gRPC_BUILD_TESTS)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)

@ -7936,6 +7936,16 @@ targets:
- test/core/end2end/cq_verifier.cc
deps:
- grpc_test_util
- name: outlier_detection_lb_config_parser_test
gtest: true
build: test
language: c++
headers:
- test/core/util/scoped_env_var.h
src:
- test/core/client_channel/outlier_detection_lb_config_parser_test.cc
deps:
- grpc_test_util
- name: overload_test
gtest: true
build: test
@ -10985,6 +10995,29 @@ targets:
- linux
- posix
- mac
- name: xds_cluster_resource_type_test
gtest: true
build: test
language: c++
headers:
- test/core/util/scoped_env_var.h
src:
- src/proto/grpc/testing/xds/v3/address.proto
- src/proto/grpc/testing/xds/v3/aggregate_cluster.proto
- src/proto/grpc/testing/xds/v3/base.proto
- src/proto/grpc/testing/xds/v3/cluster.proto
- src/proto/grpc/testing/xds/v3/config_source.proto
- src/proto/grpc/testing/xds/v3/endpoint.proto
- src/proto/grpc/testing/xds/v3/extension.proto
- src/proto/grpc/testing/xds/v3/outlier_detection.proto
- src/proto/grpc/testing/xds/v3/percent.proto
- src/proto/grpc/testing/xds/v3/regex.proto
- src/proto/grpc/testing/xds/v3/string.proto
- src/proto/grpc/testing/xds/v3/tls.proto
- test/core/xds/xds_cluster_resource_type_test.cc
deps:
- grpc_test_util
uses_polling: false
- name: xds_cluster_type_end2end_test
gtest: true
build: test
@ -11044,12 +11077,29 @@ targets:
- linux
- posix
- mac
- name: xds_common_types_test
gtest: true
build: test
language: c++
headers: []
src:
- src/proto/grpc/testing/xds/v3/base.proto
- src/proto/grpc/testing/xds/v3/extension.proto
- src/proto/grpc/testing/xds/v3/percent.proto
- src/proto/grpc/testing/xds/v3/regex.proto
- src/proto/grpc/testing/xds/v3/string.proto
- src/proto/grpc/testing/xds/v3/tls.proto
- test/core/xds/xds_common_types_test.cc
deps:
- grpc_test_util
uses_polling: false
- name: xds_core_end2end_test
gtest: true
build: test
run: false
language: c++
headers:
- test/core/util/scoped_env_var.h
- test/cpp/end2end/counted_service.h
- test/cpp/end2end/test_service_impl.h
- test/cpp/end2end/xds/xds_end2end_test_lib.h
@ -11214,6 +11264,7 @@ targets:
run: false
language: c++
headers:
- test/core/util/scoped_env_var.h
- test/cpp/end2end/counted_service.h
- test/cpp/end2end/test_service_impl.h
- test/cpp/end2end/xds/no_op_http_filter.h
@ -11436,6 +11487,7 @@ targets:
run: false
language: c++
headers:
- test/core/util/scoped_env_var.h
- test/cpp/end2end/counted_service.h
- test/cpp/end2end/test_service_impl.h
- test/cpp/end2end/xds/no_op_http_filter.h
@ -11553,6 +11605,7 @@ targets:
build: test
language: c++
headers:
- test/core/util/scoped_env_var.h
- test/cpp/end2end/counted_service.h
- test/cpp/end2end/rls_server.h
- test/cpp/end2end/test_service_impl.h

@ -1032,8 +1032,6 @@ class OutlierDetectionLbFactory : public LoadBalancingPolicyFactory {
OutlierDetectionConfig outlier_detection_config;
RefCountedPtr<LoadBalancingPolicy::Config> child_policy;
{
ValidationErrors::ScopedField field(
&errors, "[\"outlier_detection_experimental\"]");
outlier_detection_config =
LoadFromJson<OutlierDetectionConfig>(json, JsonArgs(), &errors);
// Parse childPolicy manually.
@ -1082,6 +1080,14 @@ OutlierDetectionConfig::SuccessRateEjection::JsonLoader(const JsonArgs&) {
return loader;
}
void OutlierDetectionConfig::SuccessRateEjection::JsonPostLoad(
const Json&, const JsonArgs&, ValidationErrors* errors) {
if (enforcement_percentage > 100) {
ValidationErrors::ScopedField field(errors, ".enforcement_percentage");
errors->AddError("value must be <= 100");
}
}
const JsonLoaderInterface*
OutlierDetectionConfig::FailurePercentageEjection::JsonLoader(const JsonArgs&) {
static const auto* loader =
@ -1097,6 +1103,18 @@ OutlierDetectionConfig::FailurePercentageEjection::JsonLoader(const JsonArgs&) {
return loader;
}
void OutlierDetectionConfig::FailurePercentageEjection::JsonPostLoad(
const Json&, const JsonArgs&, ValidationErrors* errors) {
if (enforcement_percentage > 100) {
ValidationErrors::ScopedField field(errors, ".enforcement_percentage");
errors->AddError("value must be <= 100");
}
if (threshold > 100) {
ValidationErrors::ScopedField field(errors, ".threshold");
errors->AddError("value must be <= 100");
}
}
const JsonLoaderInterface* OutlierDetectionConfig::JsonLoader(const JsonArgs&) {
static const auto* loader =
JsonObjectLoader<OutlierDetectionConfig>()
@ -1116,11 +1134,15 @@ const JsonLoaderInterface* OutlierDetectionConfig::JsonLoader(const JsonArgs&) {
}
void OutlierDetectionConfig::JsonPostLoad(const Json& json, const JsonArgs&,
ValidationErrors* /*errors*/) {
ValidationErrors* errors) {
if (json.object_value().find("maxEjectionTime") ==
json.object_value().end()) {
max_ejection_time = std::max(base_ejection_time, Duration::Seconds(300));
}
if (max_ejection_percent > 100) {
ValidationErrors::ScopedField field(errors, ".max_ejection_percent");
errors->AddError("value must be <= 100");
}
}
//

@ -54,6 +54,7 @@ struct OutlierDetectionConfig {
}
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
void JsonPostLoad(const Json&, const JsonArgs&, ValidationErrors* errors);
};
struct FailurePercentageEjection {
uint32_t threshold = 85;
@ -71,6 +72,7 @@ struct OutlierDetectionConfig {
}
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
void JsonPostLoad(const Json&, const JsonArgs&, ValidationErrors* errors);
};
absl::optional<SuccessRateEjection> success_rate_ejection;
absl::optional<FailurePercentageEjection> failure_percentage_ejection;

@ -52,6 +52,7 @@
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/gprpp/host_port.h"
#include "src/core/lib/gprpp/time.h"
#include "src/core/lib/gprpp/validation_errors.h"
namespace grpc_core {
@ -102,193 +103,226 @@ std::string XdsClusterResource::ToString() const {
namespace {
absl::StatusOr<CommonTlsContext> UpstreamTlsContextParse(
absl::optional<CommonTlsContext> UpstreamTlsContextParse(
const XdsResourceType::DecodeContext& context,
const envoy_config_core_v3_TransportSocket* transport_socket) {
auto* typed_config =
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) {
return absl::InvalidArgumentError("transport_socket.typed_config not set");
errors->AddError("field not present");
return absl::nullopt;
}
absl::string_view type_url = absl::StripPrefix(
UpbStringToAbsl(google_protobuf_Any_type_url(typed_config)),
"type.googleapis.com/");
if (type_url !=
"envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext") {
return absl::InvalidArgumentError(
absl::StrCat("Unrecognized transport socket type: ", type_url));
ValidationErrors::ScopedField field(errors, ".type_url");
errors->AddError(
absl::StrCat("unrecognized transport socket type: ", type_url));
return absl::nullopt;
}
const upb_StringView encoded_upstream_tls_context =
google_protobuf_Any_value(typed_config);
auto* upstream_tls_context =
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(
encoded_upstream_tls_context.data, encoded_upstream_tls_context.size,
context.arena);
serialized_upstream_tls_context.data(),
serialized_upstream_tls_context.size(), context.arena);
if (upstream_tls_context == nullptr) {
return absl::InvalidArgumentError("Can't decode upstream tls context.");
errors->AddError("can't decode UpstreamTlsContext");
return absl::nullopt;
}
auto* common_tls_context_proto =
ValidationErrors::ScopedField field3(errors, ".common_tls_context");
const auto* common_tls_context_proto =
envoy_extensions_transport_sockets_tls_v3_UpstreamTlsContext_common_tls_context(
upstream_tls_context);
CommonTlsContext common_tls_context;
if (common_tls_context_proto != nullptr) {
auto common_context =
CommonTlsContext::Parse(context, common_tls_context_proto);
if (!common_context.ok()) {
return absl::InvalidArgumentError(
absl::StrCat("Error parsing UpstreamTlsContext: ",
common_context.status().message()));
}
common_tls_context = std::move(*common_context);
common_tls_context =
CommonTlsContext::Parse(context, common_tls_context_proto, errors);
}
if (common_tls_context.certificate_validation_context
.ca_certificate_provider_instance.instance_name.empty()) {
return absl::InvalidArgumentError(
"UpstreamTlsContext: TLS configuration provided but no "
"ca_certificate_provider_instance found.");
errors->AddError("no CA certificate provider instance configured");
}
return common_tls_context;
}
absl::Status CdsLogicalDnsParse(const envoy_config_cluster_v3_Cluster* cluster,
XdsClusterResource* cds_update) {
void EdsConfigParse(const envoy_config_cluster_v3_Cluster* cluster,
XdsClusterResource* cds_update, ValidationErrors* errors) {
ValidationErrors::ScopedField field(errors, ".eds_cluster_config");
const envoy_config_cluster_v3_Cluster_EdsClusterConfig* eds_cluster_config =
envoy_config_cluster_v3_Cluster_eds_cluster_config(cluster);
if (eds_cluster_config == nullptr) {
errors->AddError("field not present");
} else {
ValidationErrors::ScopedField field(errors, ".eds_config");
const envoy_config_core_v3_ConfigSource* eds_config =
envoy_config_cluster_v3_Cluster_EdsClusterConfig_eds_config(
eds_cluster_config);
if (eds_config == nullptr) {
errors->AddError("field not present");
} else {
if (!envoy_config_core_v3_ConfigSource_has_ads(eds_config) &&
!envoy_config_core_v3_ConfigSource_has_self(eds_config)) {
errors->AddError("ConfigSource is not ads or self");
}
// Record EDS service_name (if any).
upb_StringView service_name =
envoy_config_cluster_v3_Cluster_EdsClusterConfig_service_name(
eds_cluster_config);
if (service_name.size != 0) {
cds_update->eds_service_name = UpbStringToStdString(service_name);
}
}
}
}
void LogicalDnsParse(const envoy_config_cluster_v3_Cluster* cluster,
XdsClusterResource* cds_update, ValidationErrors* errors) {
ValidationErrors::ScopedField field(errors, ".load_assignment");
const auto* load_assignment =
envoy_config_cluster_v3_Cluster_load_assignment(cluster);
if (load_assignment == nullptr) {
return absl::InvalidArgumentError(
"load_assignment not present for LOGICAL_DNS cluster");
errors->AddError("field not present for LOGICAL_DNS cluster");
return;
}
ValidationErrors::ScopedField field2(errors, ".endpoints");
size_t num_localities;
const auto* const* localities =
envoy_config_endpoint_v3_ClusterLoadAssignment_endpoints(load_assignment,
&num_localities);
if (num_localities != 1) {
return absl::InvalidArgumentError(
absl::StrCat("load_assignment for LOGICAL_DNS cluster must have "
"exactly one locality, found ",
num_localities));
errors->AddError(absl::StrCat(
"must contain exactly one locality for LOGICAL_DNS cluster, found ",
num_localities));
return;
}
ValidationErrors::ScopedField field3(errors, "[0].lb_endpoints");
size_t num_endpoints;
const auto* const* endpoints =
envoy_config_endpoint_v3_LocalityLbEndpoints_lb_endpoints(localities[0],
&num_endpoints);
if (num_endpoints != 1) {
return absl::InvalidArgumentError(
absl::StrCat("locality for LOGICAL_DNS cluster must have "
"exactly one endpoint, found ",
num_endpoints));
errors->AddError(absl::StrCat(
"must contain exactly one endpoint for LOGICAL_DNS cluster, found ",
num_endpoints));
return;
}
ValidationErrors::ScopedField field4(errors, "[0].endpoint");
const auto* endpoint =
envoy_config_endpoint_v3_LbEndpoint_endpoint(endpoints[0]);
if (endpoint == nullptr) {
return absl::InvalidArgumentError("LbEndpoint endpoint field not set");
errors->AddError("field not present");
return;
}
ValidationErrors::ScopedField field5(errors, ".address");
const auto* address = envoy_config_endpoint_v3_Endpoint_address(endpoint);
if (address == nullptr) {
return absl::InvalidArgumentError("Endpoint address field not set");
errors->AddError("field not present");
return;
}
ValidationErrors::ScopedField field6(errors, ".socket_address");
const auto* socket_address =
envoy_config_core_v3_Address_socket_address(address);
if (socket_address == nullptr) {
return absl::InvalidArgumentError("Address socket_address field not set");
errors->AddError("field not present");
return;
}
if (envoy_config_core_v3_SocketAddress_resolver_name(socket_address).size !=
0) {
return absl::InvalidArgumentError(
ValidationErrors::ScopedField field(errors, ".resolver_name");
errors->AddError(
"LOGICAL_DNS clusters must NOT have a custom resolver name set");
}
absl::string_view address_str = UpbStringToAbsl(
envoy_config_core_v3_SocketAddress_address(socket_address));
if (address_str.empty()) {
return absl::InvalidArgumentError("SocketAddress address field not set");
ValidationErrors::ScopedField field(errors, ".address");
errors->AddError("field not present");
}
if (!envoy_config_core_v3_SocketAddress_has_port_value(socket_address)) {
return absl::InvalidArgumentError("SocketAddress port_value field not set");
ValidationErrors::ScopedField field(errors, ".port_value");
errors->AddError("field not present");
}
cds_update->dns_hostname = JoinHostPort(
address_str,
envoy_config_core_v3_SocketAddress_port_value(socket_address));
return absl::OkStatus();
}
void AggregateClusterParse(const XdsResourceType::DecodeContext& context,
absl::string_view serialized_config,
XdsClusterResource* cds_update,
ValidationErrors* errors) {
const auto* aggregate_cluster_config =
envoy_extensions_clusters_aggregate_v3_ClusterConfig_parse(
serialized_config.data(), serialized_config.size(), context.arena);
if (aggregate_cluster_config == nullptr) {
errors->AddError("can't parse aggregate cluster config");
return;
}
size_t size;
const upb_StringView* clusters =
envoy_extensions_clusters_aggregate_v3_ClusterConfig_clusters(
aggregate_cluster_config, &size);
for (size_t i = 0; i < size; ++i) {
cds_update->prioritized_cluster_names.emplace_back(
UpbStringToStdString(clusters[i]));
}
}
absl::StatusOr<XdsClusterResource> CdsResourceParse(
const XdsResourceType::DecodeContext& context,
const envoy_config_cluster_v3_Cluster* cluster, bool /*is_v2*/) {
XdsClusterResource cds_update;
std::vector<std::string> errors;
// Check the cluster_discovery_type.
if (!envoy_config_cluster_v3_Cluster_has_type(cluster) &&
!envoy_config_cluster_v3_Cluster_has_cluster_type(cluster)) {
errors.emplace_back("DiscoveryType not found.");
} else if (envoy_config_cluster_v3_Cluster_type(cluster) ==
envoy_config_cluster_v3_Cluster_EDS) {
ValidationErrors errors;
// Check the cluster discovery type.
if (envoy_config_cluster_v3_Cluster_type(cluster) ==
envoy_config_cluster_v3_Cluster_EDS) {
cds_update.cluster_type = XdsClusterResource::ClusterType::EDS;
// Check the EDS config source.
const envoy_config_cluster_v3_Cluster_EdsClusterConfig* eds_cluster_config =
envoy_config_cluster_v3_Cluster_eds_cluster_config(cluster);
const envoy_config_core_v3_ConfigSource* eds_config =
envoy_config_cluster_v3_Cluster_EdsClusterConfig_eds_config(
eds_cluster_config);
if (!envoy_config_core_v3_ConfigSource_has_ads(eds_config) &&
!envoy_config_core_v3_ConfigSource_has_self(eds_config)) {
errors.emplace_back("EDS ConfigSource is not ADS or SELF.");
}
// Record EDS service_name (if any).
upb_StringView service_name =
envoy_config_cluster_v3_Cluster_EdsClusterConfig_service_name(
eds_cluster_config);
if (service_name.size != 0) {
cds_update.eds_service_name = UpbStringToStdString(service_name);
}
EdsConfigParse(cluster, &cds_update, &errors);
} else if (envoy_config_cluster_v3_Cluster_type(cluster) ==
envoy_config_cluster_v3_Cluster_LOGICAL_DNS) {
cds_update.cluster_type = XdsClusterResource::ClusterType::LOGICAL_DNS;
absl::Status status = CdsLogicalDnsParse(cluster, &cds_update);
if (!status.ok()) errors.emplace_back(status.message());
} else {
LogicalDnsParse(cluster, &cds_update, &errors);
} else if (envoy_config_cluster_v3_Cluster_has_cluster_type(cluster)) {
ValidationErrors::ScopedField field(&errors, ".cluster_type");
const auto* custom_cluster_type =
envoy_config_cluster_v3_Cluster_cluster_type(cluster);
if (custom_cluster_type == nullptr) {
errors.push_back("DiscoveryType is not valid.");
GPR_ASSERT(custom_cluster_type != nullptr);
ValidationErrors::ScopedField field2(&errors, ".typed_config");
const auto* typed_config =
envoy_config_cluster_v3_Cluster_CustomClusterType_typed_config(
custom_cluster_type);
if (typed_config == nullptr) {
errors.AddError("field not present");
} else {
const auto* typed_config =
envoy_config_cluster_v3_Cluster_CustomClusterType_typed_config(
custom_cluster_type);
if (typed_config == nullptr) {
errors.push_back("cluster_type.typed_config not set");
absl::string_view type_url = absl::StripPrefix(
UpbStringToAbsl(google_protobuf_Any_type_url(typed_config)),
"type.googleapis.com/");
if (type_url != "envoy.extensions.clusters.aggregate.v3.ClusterConfig") {
ValidationErrors::ScopedField field(&errors, ".type_url");
errors.AddError(
absl::StrCat("unknown cluster_type extension: ", type_url));
} else {
absl::string_view type_url = absl::StripPrefix(
UpbStringToAbsl(google_protobuf_Any_type_url(typed_config)),
"type.googleapis.com/");
if (type_url !=
"envoy.extensions.clusters.aggregate.v3.ClusterConfig") {
errors.push_back(
absl::StrCat("unknown cluster_type extension: ", type_url));
} else {
cds_update.cluster_type = XdsClusterResource::ClusterType::AGGREGATE;
// Retrieve aggregate clusters.
const upb_StringView aggregate_cluster_config_upb_stringview =
google_protobuf_Any_value(typed_config);
const auto* aggregate_cluster_config =
envoy_extensions_clusters_aggregate_v3_ClusterConfig_parse(
aggregate_cluster_config_upb_stringview.data,
aggregate_cluster_config_upb_stringview.size, context.arena);
if (aggregate_cluster_config == nullptr) {
errors.emplace_back("Can't parse aggregate cluster.");
} else {
size_t size;
const upb_StringView* clusters =
envoy_extensions_clusters_aggregate_v3_ClusterConfig_clusters(
aggregate_cluster_config, &size);
for (size_t i = 0; i < size; ++i) {
const upb_StringView cluster = clusters[i];
cds_update.prioritized_cluster_names.emplace_back(
UpbStringToStdString(cluster));
}
}
}
cds_update.cluster_type = XdsClusterResource::ClusterType::AGGREGATE;
// Retrieve aggregate clusters.
ValidationErrors::ScopedField field(
&errors,
".value[envoy.extensions.clusters.aggregate.v3.ClusterConfig]");
absl::string_view serialized_config =
UpbStringToAbsl(google_protobuf_Any_value(typed_config));
AggregateClusterParse(context, serialized_config, &cds_update, &errors);
}
}
} else {
ValidationErrors::ScopedField field(&errors, ".type");
errors.AddError("unknown discovery type");
}
// Check the LB policy.
if (envoy_config_cluster_v3_Cluster_lb_policy(cluster) ==
@ -301,52 +335,53 @@ absl::StatusOr<XdsClusterResource> CdsResourceParse(
auto* ring_hash_config =
envoy_config_cluster_v3_Cluster_ring_hash_lb_config(cluster);
if (ring_hash_config != nullptr) {
ValidationErrors::ScopedField field(&errors, ".ring_hash_lb_config");
const google_protobuf_UInt64Value* max_ring_size =
envoy_config_cluster_v3_Cluster_RingHashLbConfig_maximum_ring_size(
ring_hash_config);
if (max_ring_size != nullptr) {
ValidationErrors::ScopedField field(&errors, ".maximum_ring_size");
cds_update.max_ring_size =
google_protobuf_UInt64Value_value(max_ring_size);
if (cds_update.max_ring_size > 8388608 ||
cds_update.max_ring_size == 0) {
errors.emplace_back(
"max_ring_size is not in the range of 1 to 8388608.");
errors.AddError("must be in the range of 1 to 8388608");
}
}
const google_protobuf_UInt64Value* min_ring_size =
envoy_config_cluster_v3_Cluster_RingHashLbConfig_minimum_ring_size(
ring_hash_config);
if (min_ring_size != nullptr) {
ValidationErrors::ScopedField field(&errors, ".minimum_ring_size");
cds_update.min_ring_size =
google_protobuf_UInt64Value_value(min_ring_size);
if (cds_update.min_ring_size > 8388608 ||
cds_update.min_ring_size == 0) {
errors.emplace_back(
"min_ring_size is not in the range of 1 to 8388608.");
errors.AddError("must be in the range of 1 to 8388608");
}
if (cds_update.min_ring_size > cds_update.max_ring_size) {
errors.emplace_back(
"min_ring_size cannot be greater than max_ring_size.");
errors.AddError("cannot be greater than maximum_ring_size");
}
}
if (envoy_config_cluster_v3_Cluster_RingHashLbConfig_hash_function(
ring_hash_config) !=
envoy_config_cluster_v3_Cluster_RingHashLbConfig_XX_HASH) {
errors.emplace_back("ring hash lb config has invalid hash function.");
ValidationErrors::ScopedField field(&errors, ".hash_function");
errors.AddError("invalid hash function");
}
}
} else {
errors.emplace_back("LB policy is not supported.");
ValidationErrors::ScopedField field(&errors, ".lb_policy");
errors.AddError("LB policy is not supported");
}
// transport_socket
auto* transport_socket =
envoy_config_cluster_v3_Cluster_transport_socket(cluster);
if (transport_socket != nullptr) {
ValidationErrors::ScopedField field(&errors, ".transport_socket");
auto common_tls_context =
UpstreamTlsContextParse(context, transport_socket);
if (!common_tls_context.ok()) {
errors.emplace_back(absl::StrCat("Error parsing security configuration: ",
common_tls_context.status().message()));
} else {
UpstreamTlsContextParse(context, transport_socket, &errors);
if (common_tls_context.has_value()) {
cds_update.common_tls_context = std::move(*common_tls_context);
}
}
@ -355,7 +390,8 @@ absl::StatusOr<XdsClusterResource> CdsResourceParse(
envoy_config_cluster_v3_Cluster_lrs_server(cluster);
if (lrs_server != nullptr) {
if (!envoy_config_core_v3_ConfigSource_has_self(lrs_server)) {
errors.emplace_back("LRS ConfigSource is not self.");
ValidationErrors::ScopedField field(&errors, ".lrs_server");
errors.AddError("ConfigSource is not self");
}
cds_update.lrs_load_reporting_server.emplace(
static_cast<const GrpcXdsBootstrap::GrpcXdsServer&>(context.server));
@ -393,23 +429,29 @@ absl::StatusOr<XdsClusterResource> CdsResourceParse(
// default values.
if (XdsOutlierDetectionEnabled() &&
envoy_config_cluster_v3_Cluster_has_outlier_detection(cluster)) {
ValidationErrors::ScopedField field(&errors, ".outlier_detection");
OutlierDetectionConfig outlier_detection_update;
const envoy_config_cluster_v3_OutlierDetection* outlier_detection =
envoy_config_cluster_v3_Cluster_outlier_detection(cluster);
const google_protobuf_Duration* duration =
envoy_config_cluster_v3_OutlierDetection_interval(outlier_detection);
if (duration != nullptr) {
outlier_detection_update.interval = ParseDuration(duration);
ValidationErrors::ScopedField field(&errors, ".interval");
outlier_detection_update.interval = ParseDuration(duration, &errors);
}
duration = envoy_config_cluster_v3_OutlierDetection_base_ejection_time(
outlier_detection);
if (duration != nullptr) {
outlier_detection_update.base_ejection_time = ParseDuration(duration);
ValidationErrors::ScopedField field(&errors, ".base_ejection_time");
outlier_detection_update.base_ejection_time =
ParseDuration(duration, &errors);
}
duration = envoy_config_cluster_v3_OutlierDetection_max_ejection_time(
outlier_detection);
if (duration != nullptr) {
outlier_detection_update.max_ejection_time = ParseDuration(duration);
ValidationErrors::ScopedField field(&errors, ".max_ejection_time");
outlier_detection_update.max_ejection_time =
ParseDuration(duration, &errors);
}
const google_protobuf_UInt32Value* max_ejection_percent =
envoy_config_cluster_v3_OutlierDetection_max_ejection_percent(
@ -417,6 +459,10 @@ absl::StatusOr<XdsClusterResource> CdsResourceParse(
if (max_ejection_percent != nullptr) {
outlier_detection_update.max_ejection_percent =
google_protobuf_UInt32Value_value(max_ejection_percent);
if (outlier_detection_update.max_ejection_percent > 100) {
ValidationErrors::ScopedField field(&errors, ".max_ejection_percent");
errors.AddError("value must be <= 100");
}
}
const google_protobuf_UInt32Value* enforcing_success_rate =
envoy_config_cluster_v3_OutlierDetection_enforcing_success_rate(
@ -424,6 +470,10 @@ absl::StatusOr<XdsClusterResource> CdsResourceParse(
if (enforcing_success_rate != nullptr) {
uint32_t enforcement_percentage =
google_protobuf_UInt32Value_value(enforcing_success_rate);
if (enforcement_percentage > 100) {
ValidationErrors::ScopedField field(&errors, ".enforcing_success_rate");
errors.AddError("value must be <= 100");
}
if (enforcement_percentage != 0) {
OutlierDetectionConfig::SuccessRateEjection success_rate_ejection;
success_rate_ejection.enforcement_percentage = enforcement_percentage;
@ -457,6 +507,11 @@ absl::StatusOr<XdsClusterResource> CdsResourceParse(
if (enforcing_failure_percentage != nullptr) {
uint32_t enforcement_percentage =
google_protobuf_UInt32Value_value(enforcing_failure_percentage);
if (enforcement_percentage > 100) {
ValidationErrors::ScopedField field(&errors,
".enforcing_failure_percentage");
errors.AddError("value must be <= 100");
}
if (enforcement_percentage != 0) {
OutlierDetectionConfig::FailurePercentageEjection
failure_percentage_ejection;
@ -482,6 +537,11 @@ absl::StatusOr<XdsClusterResource> CdsResourceParse(
if (threshold != nullptr) {
failure_percentage_ejection.threshold =
google_protobuf_UInt32Value_value(threshold);
if (enforcement_percentage > 100) {
ValidationErrors::ScopedField field(
&errors, ".failure_percentage_threshold");
errors.AddError("value must be <= 100");
}
}
outlier_detection_update.failure_percentage_ejection =
failure_percentage_ejection;
@ -490,10 +550,7 @@ absl::StatusOr<XdsClusterResource> CdsResourceParse(
cds_update.outlier_detection = outlier_detection_update;
}
// Return result.
if (!errors.empty()) {
return absl::InvalidArgumentError(absl::StrCat(
"errors parsing CDS resource: [", absl::StrJoin(errors, "; "), "]"));
}
if (!errors.ok()) return errors.status("errors validating Cluster resource");
return cds_update;
}

@ -19,6 +19,7 @@
#include "src/core/ext/xds/xds_common_types.h"
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <map>
@ -45,6 +46,25 @@
namespace grpc_core {
//
// ParseDuration()
//
Duration ParseDuration(const google_protobuf_Duration* proto_duration,
ValidationErrors* errors) {
int64_t seconds = google_protobuf_Duration_seconds(proto_duration);
if (seconds < 0 || seconds > 315576000000) {
ValidationErrors::ScopedField field(errors, ".seconds");
errors->AddError("value must be in the range [0, 315576000000]");
}
int32_t nanos = google_protobuf_Duration_nanos(proto_duration);
if (nanos < 0 || nanos > 999999999) {
ValidationErrors::ScopedField field(errors, ".nanos");
errors->AddError("value must be in the range [0, 999999999]");
}
return Duration::FromSecondsAndNanoseconds(seconds, nanos);
}
//
// CommonTlsContext::CertificateValidationContext
//
@ -114,68 +134,70 @@ namespace {
// same CertificateProviderPluginInstance struct since the fields are the same.
// TODO(yashykt): Remove this once we stop supporting the old way of fetching
// certificate provider instances.
absl::StatusOr<CommonTlsContext::CertificateProviderPluginInstance>
CommonTlsContext::CertificateProviderPluginInstance
CertificateProviderInstanceParse(
const XdsResourceType::DecodeContext& context,
const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance*
certificate_provider_instance_proto) {
CommonTlsContext::CertificateProviderPluginInstance
certificate_provider_plugin_instance = {
UpbStringToStdString(
envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance_instance_name(
certificate_provider_instance_proto)),
UpbStringToStdString(
envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance_certificate_name(
certificate_provider_instance_proto))};
certificate_provider_instance_proto,
ValidationErrors* errors) {
CommonTlsContext::CertificateProviderPluginInstance cert_provider;
cert_provider.instance_name = UpbStringToStdString(
envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance_instance_name(
certificate_provider_instance_proto));
const auto& bootstrap =
static_cast<const GrpcXdsBootstrap&>(context.client->bootstrap());
if (bootstrap.certificate_providers().find(
certificate_provider_plugin_instance.instance_name) ==
if (bootstrap.certificate_providers().find(cert_provider.instance_name) ==
bootstrap.certificate_providers().end()) {
return absl::InvalidArgumentError(
absl::StrCat("Unrecognized certificate provider instance name: ",
certificate_provider_plugin_instance.instance_name));
ValidationErrors::ScopedField field(errors, ".instance_name");
errors->AddError(
absl::StrCat("unrecognized certificate provider instance name: ",
cert_provider.instance_name));
}
return certificate_provider_plugin_instance;
cert_provider.certificate_name = UpbStringToStdString(
envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance_certificate_name(
certificate_provider_instance_proto));
return cert_provider;
}
absl::StatusOr<CommonTlsContext::CertificateProviderPluginInstance>
CommonTlsContext::CertificateProviderPluginInstance
CertificateProviderPluginInstanceParse(
const XdsResourceType::DecodeContext& context,
const envoy_extensions_transport_sockets_tls_v3_CertificateProviderPluginInstance*
certificate_provider_plugin_instance_proto) {
CommonTlsContext::CertificateProviderPluginInstance
certificate_provider_plugin_instance = {
UpbStringToStdString(
envoy_extensions_transport_sockets_tls_v3_CertificateProviderPluginInstance_instance_name(
certificate_provider_plugin_instance_proto)),
UpbStringToStdString(
envoy_extensions_transport_sockets_tls_v3_CertificateProviderPluginInstance_certificate_name(
certificate_provider_plugin_instance_proto))};
certificate_provider_plugin_instance_proto,
ValidationErrors* errors) {
CommonTlsContext::CertificateProviderPluginInstance cert_provider;
cert_provider.instance_name = UpbStringToStdString(
envoy_extensions_transport_sockets_tls_v3_CertificateProviderPluginInstance_instance_name(
certificate_provider_plugin_instance_proto));
const auto& bootstrap =
static_cast<const GrpcXdsBootstrap&>(context.client->bootstrap());
if (bootstrap.certificate_providers().find(
certificate_provider_plugin_instance.instance_name) ==
if (bootstrap.certificate_providers().find(cert_provider.instance_name) ==
bootstrap.certificate_providers().end()) {
return absl::InvalidArgumentError(
absl::StrCat("Unrecognized certificate provider instance name: ",
certificate_provider_plugin_instance.instance_name));
ValidationErrors::ScopedField field(errors, ".instance_name");
errors->AddError(
absl::StrCat("unrecognized certificate provider instance name: ",
cert_provider.instance_name));
}
return certificate_provider_plugin_instance;
cert_provider.certificate_name = UpbStringToStdString(
envoy_extensions_transport_sockets_tls_v3_CertificateProviderPluginInstance_certificate_name(
certificate_provider_plugin_instance_proto));
return cert_provider;
}
absl::StatusOr<CommonTlsContext::CertificateValidationContext>
CommonTlsContext::CertificateValidationContext
CertificateValidationContextParse(
const XdsResourceType::DecodeContext& context,
const envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext*
certificate_validation_context_proto) {
std::vector<std::string> errors;
certificate_validation_context_proto,
ValidationErrors* errors) {
CommonTlsContext::CertificateValidationContext certificate_validation_context;
size_t len = 0;
auto* subject_alt_names_matchers =
envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_match_subject_alt_names(
certificate_validation_context_proto, &len);
for (size_t i = 0; i < len; ++i) {
ValidationErrors::ScopedField field(
errors, absl::StrCat(".match_subject_alt_names[", i, "]"));
StringMatcher::Type type;
std::string matcher;
if (envoy_type_matcher_v3_StringMatcher_has_exact(
@ -207,7 +229,7 @@ CertificateValidationContextParse(
matcher = UpbStringToStdString(
envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher));
} else {
errors.push_back("Invalid StringMatcher specified");
errors->AddError("invalid StringMatcher specified");
continue;
}
bool ignore_case = envoy_type_matcher_v3_StringMatcher_ignore_case(
@ -216,13 +238,12 @@ CertificateValidationContextParse(
StringMatcher::Create(type, matcher,
/*case_sensitive=*/!ignore_case);
if (!string_matcher.ok()) {
errors.push_back(
absl::StrCat("string matcher: ", string_matcher.status().message()));
errors->AddError(string_matcher.status().message());
continue;
}
if (type == StringMatcher::Type::kSafeRegex && ignore_case) {
errors.push_back(
"StringMatcher: ignore_case has no effect for SAFE_REGEX.");
ValidationErrors::ScopedField field(errors, ".ignore_case");
errors->AddError("not supported for regex matcher");
continue;
}
certificate_validation_context.match_subject_alt_names.push_back(
@ -232,58 +253,51 @@ CertificateValidationContextParse(
envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_ca_certificate_provider_instance(
certificate_validation_context_proto);
if (ca_certificate_provider_instance != nullptr) {
auto certificate_provider_instance = CertificateProviderPluginInstanceParse(
context, ca_certificate_provider_instance);
if (!certificate_provider_instance.ok()) {
errors.emplace_back(certificate_provider_instance.status().message());
} else {
certificate_validation_context.ca_certificate_provider_instance =
std::move(*certificate_provider_instance);
}
ValidationErrors::ScopedField field(errors,
".ca_certificate_provider_instance");
certificate_validation_context.ca_certificate_provider_instance =
CertificateProviderPluginInstanceParse(
context, ca_certificate_provider_instance, errors);
}
if (envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_verify_certificate_spki(
certificate_validation_context_proto, nullptr) != nullptr) {
errors.push_back(
"CertificateValidationContext: verify_certificate_spki unsupported");
ValidationErrors::ScopedField field(errors, ".verify_certificate_spki");
errors->AddError("feature unsupported");
}
if (envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_verify_certificate_hash(
certificate_validation_context_proto, nullptr) != nullptr) {
errors.push_back(
"CertificateValidationContext: verify_certificate_hash unsupported");
ValidationErrors::ScopedField field(errors, ".verify_certificate_hash");
errors->AddError("feature unsupported");
}
auto* require_signed_certificate_timestamp =
envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_require_signed_certificate_timestamp(
certificate_validation_context_proto);
if (require_signed_certificate_timestamp != nullptr &&
google_protobuf_BoolValue_value(require_signed_certificate_timestamp)) {
errors.push_back(
"CertificateValidationContext: "
"require_signed_certificate_timestamp unsupported");
ValidationErrors::ScopedField field(
errors, ".require_signed_certificate_timestamp");
errors->AddError("feature unsupported");
}
if (envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_has_crl(
certificate_validation_context_proto)) {
errors.push_back("CertificateValidationContext: crl unsupported");
ValidationErrors::ScopedField field(errors, ".crl");
errors->AddError("feature unsupported");
}
if (envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_has_custom_validator_config(
certificate_validation_context_proto)) {
errors.push_back(
"CertificateValidationContext: custom_validator_config unsupported");
}
if (!errors.empty()) {
return absl::InvalidArgumentError(
absl::StrCat("Errors parsing CertificateValidationContext: ",
absl::StrJoin(errors, "; ")));
ValidationErrors::ScopedField field(errors, ".custom_validator_config");
errors->AddError("feature unsupported");
}
return certificate_validation_context;
}
} // namespace
absl::StatusOr<CommonTlsContext> CommonTlsContext::Parse(
CommonTlsContext CommonTlsContext::Parse(
const XdsResourceType::DecodeContext& context,
const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext*
common_tls_context_proto) {
std::vector<std::string> errors;
common_tls_context_proto,
ValidationErrors* errors) {
CommonTlsContext common_tls_context;
// The validation context is derived from the oneof in
// 'validation_context_type'. 'validation_context_sds_secret_config' is not
@ -292,18 +306,16 @@ absl::StatusOr<CommonTlsContext> CommonTlsContext::Parse(
envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_combined_validation_context(
common_tls_context_proto);
if (combined_validation_context != nullptr) {
ValidationErrors::ScopedField field(errors, ".combined_validation_context");
auto* default_validation_context =
envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CombinedCertificateValidationContext_default_validation_context(
combined_validation_context);
if (default_validation_context != nullptr) {
auto certificate_validation_context = CertificateValidationContextParse(
context, default_validation_context);
if (!certificate_validation_context.ok()) {
errors.emplace_back(certificate_validation_context.status().message());
} else {
common_tls_context.certificate_validation_context =
std::move(*certificate_validation_context);
}
ValidationErrors::ScopedField field(errors,
".default_validation_context");
common_tls_context.certificate_validation_context =
CertificateValidationContextParse(context, default_validation_context,
errors);
}
// If after parsing default_validation_context,
// common_tls_context->certificate_validation_context.ca_certificate_provider_instance
@ -312,55 +324,44 @@ absl::StatusOr<CommonTlsContext> CommonTlsContext::Parse(
// 'combined_validation_context'. Note that this way of fetching root
// certificates is deprecated and will be removed in the future.
// TODO(yashykt): Remove this once it's no longer needed.
auto* validation_context_certificate_provider_instance =
const auto* validation_context_certificate_provider_instance =
envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CombinedCertificateValidationContext_validation_context_certificate_provider_instance(
combined_validation_context);
if (common_tls_context.certificate_validation_context
.ca_certificate_provider_instance.Empty() &&
validation_context_certificate_provider_instance != nullptr) {
auto certificate_provider_instance = CertificateProviderInstanceParse(
context, validation_context_certificate_provider_instance);
if (!certificate_provider_instance.ok()) {
errors.emplace_back(certificate_provider_instance.status().message());
} else {
common_tls_context.certificate_validation_context
.ca_certificate_provider_instance =
std::move(*certificate_provider_instance);
}
ValidationErrors::ScopedField field(
errors, ".validation_context_certificate_provider_instance");
common_tls_context.certificate_validation_context
.ca_certificate_provider_instance = CertificateProviderInstanceParse(
context, validation_context_certificate_provider_instance, errors);
}
} else {
auto* validation_context =
envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_validation_context(
common_tls_context_proto);
if (validation_context != nullptr) {
auto certificate_validation_context =
CertificateValidationContextParse(context, validation_context);
if (!certificate_validation_context.ok()) {
errors.emplace_back(certificate_validation_context.status().message());
} else {
common_tls_context.certificate_validation_context =
std::move(*certificate_validation_context);
}
ValidationErrors::ScopedField field(errors, ".validation_context");
common_tls_context.certificate_validation_context =
CertificateValidationContextParse(context, validation_context,
errors);
} else if (
envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_has_validation_context_sds_secret_config(
common_tls_context_proto)) {
errors.push_back("validation_context_sds_secret_config unsupported");
ValidationErrors::ScopedField field(
errors, ".validation_context_sds_secret_config");
errors->AddError("feature unsupported");
}
}
auto* tls_certificate_provider_instance =
envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_tls_certificate_provider_instance(
common_tls_context_proto);
if (tls_certificate_provider_instance != nullptr) {
auto certificate_provider_plugin_instance =
ValidationErrors::ScopedField field(errors,
".tls_certificate_provider_instance");
common_tls_context.tls_certificate_provider_instance =
CertificateProviderPluginInstanceParse(
context, tls_certificate_provider_instance);
if (!certificate_provider_plugin_instance.ok()) {
errors.emplace_back(
certificate_provider_plugin_instance.status().message());
} else {
common_tls_context.tls_certificate_provider_instance =
std::move(*certificate_provider_plugin_instance);
}
context, tls_certificate_provider_instance, errors);
} else {
// Fall back onto 'tls_certificate_certificate_provider_instance'. Note that
// this way of fetching identity certificates is deprecated and will be
@ -370,37 +371,34 @@ absl::StatusOr<CommonTlsContext> CommonTlsContext::Parse(
envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_tls_certificate_certificate_provider_instance(
common_tls_context_proto);
if (tls_certificate_certificate_provider_instance != nullptr) {
auto certificate_provider_instance = CertificateProviderInstanceParse(
context, tls_certificate_certificate_provider_instance);
if (!certificate_provider_instance.ok()) {
errors.emplace_back(certificate_provider_instance.status().message());
} else {
common_tls_context.tls_certificate_provider_instance =
std::move(*certificate_provider_instance);
}
ValidationErrors::ScopedField field(
errors, ".tls_certificate_certificate_provider_instance");
common_tls_context.tls_certificate_provider_instance =
CertificateProviderInstanceParse(
context, tls_certificate_certificate_provider_instance, errors);
} else {
if (envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_has_tls_certificates(
common_tls_context_proto)) {
errors.push_back("tls_certificates unsupported");
ValidationErrors::ScopedField field(errors, ".tls_certificates");
errors->AddError("feature unsupported");
}
if (envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_has_tls_certificate_sds_secret_configs(
common_tls_context_proto)) {
errors.push_back("tls_certificate_sds_secret_configs unsupported");
ValidationErrors::ScopedField field(
errors, ".tls_certificate_sds_secret_configs");
errors->AddError("feature unsupported");
}
}
}
if (envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_has_tls_params(
common_tls_context_proto)) {
errors.push_back("tls_params unsupported");
ValidationErrors::ScopedField field(errors, ".tls_params");
errors->AddError("feature unsupported");
}
if (envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_has_custom_handshaker(
common_tls_context_proto)) {
errors.push_back("custom_handshaker unsupported");
}
if (!errors.empty()) {
return absl::InvalidArgumentError(
absl::StrCat("Errors parsing CommonTlsContext: [",
absl::StrJoin(errors, "; "), "]"));
ValidationErrors::ScopedField field(errors, ".custom_handshaker");
errors->AddError("feature unsupported");
}
return common_tls_context;
}

@ -31,15 +31,13 @@
#include "src/core/ext/xds/xds_resource_type.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 {
inline Duration ParseDuration(const google_protobuf_Duration* proto_duration) {
return Duration::FromSecondsAndNanoseconds(
google_protobuf_Duration_seconds(proto_duration),
google_protobuf_Duration_nanos(proto_duration));
}
Duration ParseDuration(const google_protobuf_Duration* proto_duration,
ValidationErrors* errors);
struct CommonTlsContext {
struct CertificateProviderPluginInstance {
@ -82,10 +80,11 @@ struct CommonTlsContext {
std::string ToString() const;
bool Empty() const;
static absl::StatusOr<CommonTlsContext> Parse(
static CommonTlsContext Parse(
const XdsResourceType::DecodeContext& context,
const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext*
common_tls_context_proto);
common_tls_context_proto,
ValidationErrors* errors);
};
struct ExtractExtensionTypeNameResult {

@ -43,6 +43,7 @@
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/status_util.h"
#include "src/core/lib/gprpp/time.h"
#include "src/core/lib/gprpp/validation_errors.h"
#include "src/core/lib/json/json.h"
#include "src/core/lib/transport/status_conversion.h"
@ -143,8 +144,10 @@ absl::StatusOr<Json> ParseHttpFaultIntoJson(
envoy_extensions_filters_common_fault_v3_FaultDelay_fixed_delay(
fault_delay);
if (delay_duration != nullptr) {
fault_injection_policy_json["delay"] =
ParseDuration(delay_duration).ToJsonString();
ValidationErrors errors;
Duration duration = ParseDuration(delay_duration, &errors);
if (!errors.ok()) return errors.status("fixed_delay");
fault_injection_policy_json["delay"] = duration.ToJsonString();
}
// Set the headers if we enabled header delay injection control
if (envoy_extensions_filters_common_fault_v3_FaultDelay_has_header_delay(

@ -57,6 +57,7 @@
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/gprpp/host_port.h"
#include "src/core/lib/gprpp/status_helper.h"
#include "src/core/lib/gprpp/validation_errors.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/iomgr/sockaddr.h"
#include "src/core/lib/json/json.h"
@ -314,8 +315,13 @@ HttpConnectionManagerParse(
const google_protobuf_Duration* duration =
envoy_config_core_v3_HttpProtocolOptions_max_stream_duration(options);
if (duration != nullptr) {
ValidationErrors validation_errors;
http_connection_manager.http_max_stream_duration =
ParseDuration(duration);
ParseDuration(duration, &validation_errors);
if (!validation_errors.ok()) {
errors.emplace_back(
validation_errors.status("max_stream_duration").message());
}
}
}
// Parse filters.
@ -534,11 +540,12 @@ DownstreamTlsContextParse(
envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_common_tls_context(
downstream_tls_context_proto);
if (common_tls_context != nullptr) {
auto common_context = CommonTlsContext::Parse(context, common_tls_context);
if (!common_context.ok()) {
errors.emplace_back(common_context.status().message());
} else {
downstream_tls_context.common_tls_context = std::move(*common_context);
ValidationErrors validation_errors;
downstream_tls_context.common_tls_context = CommonTlsContext::Parse(
context, common_tls_context, &validation_errors);
if (!validation_errors.ok()) {
errors.emplace_back(
validation_errors.status("errors in common_tls_context").message());
}
}
auto* require_client_certificate =

@ -69,6 +69,7 @@
#include "src/core/lib/gprpp/env.h"
#include "src/core/lib/gprpp/match.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 {
@ -695,14 +696,23 @@ absl::Status RetryPolicyParse(
errors.emplace_back(
"RouteAction RetryPolicy RetryBackoff missing base interval.");
} else {
ValidationErrors validation_errors;
retry_to_return.retry_back_off.base_interval =
ParseDuration(base_interval);
ParseDuration(base_interval, &validation_errors);
if (!validation_errors.ok()) {
errors.emplace_back(
validation_errors.status("base_interval").message());
}
}
const google_protobuf_Duration* max_interval =
envoy_config_route_v3_RetryPolicy_RetryBackOff_max_interval(backoff);
Duration max;
if (max_interval != nullptr) {
max = ParseDuration(max_interval);
ValidationErrors validation_errors;
max = ParseDuration(max_interval, &validation_errors);
if (!validation_errors.ok()) {
errors.emplace_back(validation_errors.status("max_interval").message());
}
} else {
// if max interval is not set, it is 10x the base.
max = 10 * retry_to_return.retry_back_off.base_interval;
@ -844,7 +854,11 @@ absl::StatusOr<XdsRouteConfigResource::Route::RouteAction> RouteActionParse(
max_stream_duration);
}
if (duration != nullptr) {
route.max_stream_duration = ParseDuration(duration);
ValidationErrors validation_errors;
route.max_stream_duration = ParseDuration(duration, &validation_errors);
if (!validation_errors.ok()) {
return validation_errors.status("max_stream_duration");
}
}
}
}

@ -56,7 +56,7 @@ void LoadDuration::LoadInto(const std::string& value, void* dst,
}
buf = absl::StripAsciiWhitespace(buf);
auto decimal_point = buf.find('.');
int nanos = 0;
int32_t nanos = 0;
if (decimal_point != absl::string_view::npos) {
absl::string_view after_decimal = buf.substr(decimal_point + 1);
buf = buf.substr(0, decimal_point);
@ -73,11 +73,16 @@ void LoadDuration::LoadInto(const std::string& value, void* dst,
nanos *= 10;
}
}
int seconds;
int64_t seconds;
if (!absl::SimpleAtoi(buf, &seconds)) {
errors->AddError("Not a duration (not a number of seconds)");
return;
}
// Acceptable range for seconds documented at
// https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#google.protobuf.Duration
if (seconds < 0 || seconds > 315576000000) {
errors->AddError("seconds must be in the range [0, 315576000000]");
}
*static_cast<Duration*>(dst) =
Duration::FromSecondsAndNanoseconds(seconds, nanos);
}

@ -86,3 +86,18 @@ grpc_cc_test(
"//test/core/util:grpc_test_util",
],
)
grpc_cc_test(
name = "outlier_detection_lb_config_parser_test",
srcs = ["outlier_detection_lb_config_parser_test.cc"],
external_deps = [
"gtest",
],
language = "C++",
tags = ["no_test_ios"],
deps = [
"//:grpc",
"//test/core/util:grpc_test_util",
"//test/core/util:scoped_env_var",
],
)

@ -0,0 +1,166 @@
//
// 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 "absl/status/status.h"
#include "absl/status/statusor.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <grpc/grpc.h>
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/service_config/service_config_impl.h"
#include "test/core/util/scoped_env_var.h"
#include "test/core/util/test_config.h"
namespace grpc_core {
namespace testing {
namespace {
class OutlierDetectionConfigParsingTest : public ::testing::Test {
public:
OutlierDetectionConfigParsingTest()
: env_var_("GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION") {}
static void SetUpTestSuite() { grpc_init(); }
static void TearDownTestSuite() { grpc_shutdown_blocking(); }
ScopedExperimentalEnvVar env_var_;
};
TEST_F(OutlierDetectionConfigParsingTest, ValidConfig) {
const char* service_config_json =
"{\n"
" \"loadBalancingConfig\":[{\n"
" \"outlier_detection_experimental\":{\n"
" \"interval\":\"1.2s\",\n"
" \"baseEjectionTime\":\"2.3s\",\n"
" \"maxEjectionTime\":\"3.4s\",\n"
" \"maxEjectionPercent\":3,\n"
" \"successRateEjection\":{\n"
" \"stdevFactor\":1,\n"
" \"enforcementPercentage\":2,\n"
" \"minimumHosts\":3,\n"
" \"requestVolume\":4\n"
" },\n"
" \"failurePercentageEjection\":{\n"
" \"threshold\":1,\n"
" \"enforcementPercentage\":2,\n"
" \"minimumHosts\":3,\n"
" \"requestVolume\":4\n"
" },\n"
" \"childPolicy\":[\n"
" {\"unknown\":{}},\n" // Okay, since the next one exists.
" {\"grpclb\":{}}\n"
" ]\n"
" }\n"
" }]\n"
"}\n";
auto service_config =
ServiceConfigImpl::Create(ChannelArgs(), service_config_json);
ASSERT_TRUE(service_config.ok()) << service_config.status();
EXPECT_NE(*service_config, nullptr);
}
TEST_F(OutlierDetectionConfigParsingTest, InvalidValues) {
const char* service_config_json =
"{\n"
" \"loadBalancingConfig\":[{\n"
" \"outlier_detection_experimental\":{\n"
" \"interval\":\"-1s\",\n"
" \"baseEjectionTime\":\"315576000001s\",\n"
" \"maxEjectionTime\":\"-3.4s\",\n"
" \"maxEjectionPercent\":101,\n"
" \"successRateEjection\":{\n"
" \"enforcementPercentage\":101\n"
" },\n"
" \"failurePercentageEjection\":{\n"
" \"threshold\":101,\n"
" \"enforcementPercentage\":101\n"
" },\n"
" \"childPolicy\":[\n"
" {\"unknown\":{}}\n"
" ]\n"
" }\n"
" }]\n"
"}\n";
auto service_config =
ServiceConfigImpl::Create(ChannelArgs(), service_config_json);
EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(service_config.status().message(),
::testing::HasSubstr(
"errors validating outlier_detection LB policy config: ["
"field:baseEjectionTime "
"error:seconds must be in the range [0, 315576000000]; "
"field:childPolicy error:No known policies in list: unknown; "
"field:failurePercentageEjection.enforcement_percentage "
"error:value must be <= 100; "
"field:failurePercentageEjection.threshold "
"error:value must be <= 100; "
"field:interval "
"error:seconds must be in the range [0, 315576000000]; "
"field:maxEjectionTime "
"error:seconds must be in the range [0, 315576000000]; "
"field:max_ejection_percent error:value must be <= 100; "
"field:successRateEjection.enforcement_percentage "
"error:value must be <= 100]"))
<< service_config.status();
}
TEST_F(OutlierDetectionConfigParsingTest, MissingChildPolicyField) {
const char* service_config_json =
"{\n"
" \"loadBalancingConfig\":[{\n"
" \"outlier_detection_experimental\":{\n"
" \"interval\":\"1.2s\",\n"
" \"baseEjectionTime\":\"2.3s\",\n"
" \"maxEjectionTime\":\"3.4s\",\n"
" \"maxEjectionPercent\":3,\n"
" \"successRateEjection\":{\n"
" \"stdevFactor\":1,\n"
" \"enforcementPercentage\":2,\n"
" \"minimumHosts\":3,\n"
" \"requestVolume\":4\n"
" },\n"
" \"failurePercentageEjection\":{\n"
" \"threshold\":1,\n"
" \"enforcementPercentage\":2,\n"
" \"minimumHosts\":3,\n"
" \"requestVolume\":4\n"
" }\n"
" }\n"
" }]\n"
"}\n";
auto service_config =
ServiceConfigImpl::Create(ChannelArgs(), service_config_json);
EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(service_config.status().message(),
::testing::HasSubstr(
"errors validating outlier_detection LB policy config: ["
"field:childPolicy error:field not present]"))
<< service_config.status();
}
} // namespace
} // namespace testing
} // namespace grpc_core
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
grpc::testing::TestEnvironment env(&argc, argv);
return RUN_ALL_TESTS();
}

@ -420,6 +420,12 @@ TEST(JsonObjectLoader, DurationFields) {
"Not a duration (not a number of seconds); "
"field:value error:Not a duration (no s suffix)]")
<< test_struct.status();
test_struct = Parse<TestStruct>("{\"value\": \"315576000001s\"}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),
"errors validating JSON: ["
"field:value error:seconds must be in the range [0, 315576000000]]")
<< test_struct.status();
test_struct = Parse<TestStruct>("{\"value\": \"3.xs\"}");
EXPECT_EQ(test_struct.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(test_struct.status().message(),

@ -333,3 +333,13 @@ grpc_cc_library(
srcs = ["build.cc"],
hdrs = ["build.h"],
)
grpc_cc_library(
name = "scoped_env_var",
testonly = True,
hdrs = ["scoped_env_var.h"],
deps = [
"//:env",
"//:gpr_platform",
],
)

@ -0,0 +1,48 @@
//
// 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.
//
#ifndef GRPC_TEST_CORE_UTIL_SCOPED_ENV_VAR_H
#define GRPC_TEST_CORE_UTIL_SCOPED_ENV_VAR_H
#include <grpc/support/port_platform.h>
#include "src/core/lib/gprpp/env.h"
namespace grpc_core {
namespace testing {
class ScopedEnvVar {
public:
ScopedEnvVar(const char* env_var, const char* value) : env_var_(env_var) {
SetEnv(env_var_, value);
}
virtual ~ScopedEnvVar() { UnsetEnv(env_var_); }
private:
const char* env_var_;
};
class ScopedExperimentalEnvVar : public ScopedEnvVar {
public:
explicit ScopedExperimentalEnvVar(const char* env_var)
: ScopedEnvVar(env_var, "true") {}
};
} // namespace testing
} // namespace grpc_core
#endif // GRPC_TEST_CORE_UTIL_SCOPED_ENV_VAR_H

@ -155,6 +155,41 @@ grpc_cc_test(
],
)
grpc_cc_test(
name = "xds_common_types_test",
srcs = ["xds_common_types_test.cc"],
external_deps = ["gtest"],
language = "C++",
uses_event_engine = False,
uses_polling = False,
deps = [
"//:gpr",
"//:grpc",
"//:grpc_xds_client",
"//src/proto/grpc/testing/xds/v3:tls_proto",
"//test/core/util:grpc_test_util",
],
)
grpc_cc_test(
name = "xds_cluster_resource_type_test",
srcs = ["xds_cluster_resource_type_test.cc"],
external_deps = ["gtest"],
language = "C++",
uses_event_engine = False,
uses_polling = False,
deps = [
"//:gpr",
"//:grpc",
"//:grpc_xds_client",
"//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",
"//test/core/util:grpc_test_util",
"//test/core/util:scoped_env_var",
],
)
grpc_cc_test(
name = "xds_endpoint_resource_type_test",
srcs = ["xds_endpoint_resource_type_test.cc"],

File diff suppressed because it is too large Load Diff

@ -0,0 +1,535 @@
//
// 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_common_types.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <google/protobuf/wrappers.pb.h>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "envoy/extensions/transport_sockets/tls/v3/tls.upb.h"
#include "gmock/gmock.h"
#include "google/protobuf/duration.upb.h"
#include "gtest/gtest.h"
#include "re2/re2.h"
#include "upb/def.hpp"
#include "upb/upb.hpp"
#include <grpc/grpc.h>
#include <grpc/support/log.h>
#include "src/core/ext/xds/xds_bootstrap.h"
#include "src/core/ext/xds/xds_bootstrap_grpc.h"
#include "src/core/ext/xds/xds_client.h"
#include "src/core/ext/xds/xds_resource_type.h"
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/gprpp/time.h"
#include "src/core/lib/gprpp/validation_errors.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/matchers/matchers.h"
#include "src/proto/grpc/testing/xds/v3/regex.pb.h"
#include "src/proto/grpc/testing/xds/v3/string.pb.h"
#include "src/proto/grpc/testing/xds/v3/tls.pb.h"
#include "test/core/util/test_config.h"
using CommonTlsContextProto =
envoy::extensions::transport_sockets::tls::v3::CommonTlsContext;
namespace grpc_core {
namespace testing {
namespace {
TraceFlag xds_common_types_test_trace(true, "xds_common_types_test");
class XdsCommonTypesTest : public ::testing::Test {
protected:
XdsCommonTypesTest()
: xds_client_(MakeXdsClient()),
decode_context_{xds_client_.get(), xds_client_->bootstrap().server(),
&xds_common_types_test_trace, upb_def_pool_.ptr(),
upb_arena_.ptr()} {}
static RefCountedPtr<XdsClient> MakeXdsClient() {
grpc_error_handle error = GRPC_ERROR_NONE;
auto bootstrap = GrpcXdsBootstrap::Create(
"{\n"
" \"xds_servers\": [\n"
" {\n"
" \"server_uri\": \"xds.example.com\",\n"
" \"channel_creds\": [\n"
" {\"type\": \"google_default\"}\n"
" ]\n"
" }\n"
" ],\n"
" \"certificate_providers\": {\n"
" \"provider1\": {\n"
" \"plugin_name\": \"file_watcher\",\n"
" \"config\": {\n"
" \"certificate_file\": \"/path/to/cert\",\n"
" \"private_key_file\": \"/path/to/key\"\n"
" }\n"
" }\n"
" }\n"
"}");
if (!bootstrap.ok()) {
gpr_log(GPR_ERROR, "Error parsing bootstrap: %s",
bootstrap.status().ToString().c_str());
GPR_ASSERT(false);
}
return MakeRefCounted<XdsClient>(std::move(*bootstrap),
/*transport_factory=*/nullptr);
}
RefCountedPtr<XdsClient> xds_client_;
upb::DefPool upb_def_pool_;
upb::Arena upb_arena_;
XdsResourceType::DecodeContext decode_context_;
};
//
// ParseDuration() tests
//
using DurationTest = XdsCommonTypesTest;
TEST_F(DurationTest, Basic) {
google_protobuf_Duration* duration_proto =
google_protobuf_Duration_new(upb_arena_.ptr());
google_protobuf_Duration_set_seconds(duration_proto, 1);
google_protobuf_Duration_set_nanos(duration_proto, 2000000);
ValidationErrors errors;
Duration duration = ParseDuration(duration_proto, &errors);
EXPECT_TRUE(errors.ok()) << errors.status("unexpected errors");
EXPECT_EQ(duration, Duration::Milliseconds(1002));
}
TEST_F(DurationTest, NegativeNumbers) {
google_protobuf_Duration* duration_proto =
google_protobuf_Duration_new(upb_arena_.ptr());
google_protobuf_Duration_set_seconds(duration_proto, -1);
google_protobuf_Duration_set_nanos(duration_proto, -2);
ValidationErrors errors;
ParseDuration(duration_proto, &errors);
absl::Status status = errors.status("validation failed");
EXPECT_EQ(status.message(),
"validation failed: ["
"field:nanos error:value must be in the range [0, 999999999]; "
"field:seconds error:value must be in the range [0, 315576000000]]")
<< status;
}
TEST_F(DurationTest, ValuesTooHigh) {
google_protobuf_Duration* duration_proto =
google_protobuf_Duration_new(upb_arena_.ptr());
google_protobuf_Duration_set_seconds(duration_proto, 315576000001);
google_protobuf_Duration_set_nanos(duration_proto, 1000000000);
ValidationErrors errors;
ParseDuration(duration_proto, &errors);
absl::Status status = errors.status("validation failed");
EXPECT_EQ(status.message(),
"validation failed: ["
"field:nanos error:value must be in the range [0, 999999999]; "
"field:seconds error:value must be in the range [0, 315576000000]]")
<< status;
}
//
// CommonTlsContext tests
//
class CommonTlsConfigTest : public XdsCommonTypesTest {
protected:
// For convenience, tests build protos using the protobuf API and then
// use this function to convert it to a upb object, which can be
// passed to CommonTlsConfig::Parse() for validation.
const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext*
ConvertToUpb(CommonTlsContextProto proto) {
// Serialize the protobuf proto.
std::string serialized_proto;
if (!proto.SerializeToString(&serialized_proto)) {
EXPECT_TRUE(false) << "protobuf serialization failed";
return nullptr;
}
// Deserialize as upb proto.
const auto* upb_proto =
envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_parse(
serialized_proto.data(), serialized_proto.size(), upb_arena_.ptr());
if (upb_proto == nullptr) {
EXPECT_TRUE(false) << "upb parsing failed";
return nullptr;
}
return upb_proto;
}
absl::StatusOr<CommonTlsContext> Parse(
const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext*
upb_proto) {
ValidationErrors errors;
CommonTlsContext common_tls_context =
CommonTlsContext::Parse(decode_context_, upb_proto, &errors);
if (!errors.ok()) return errors.status("validation failed");
return common_tls_context;
}
};
TEST_F(CommonTlsConfigTest, CaCertProviderInCombinedValidationContext) {
// Construct proto.
CommonTlsContextProto common_tls_context_proto;
auto* cert_provider =
common_tls_context_proto.mutable_combined_validation_context()
->mutable_default_validation_context()
->mutable_ca_certificate_provider_instance();
cert_provider->set_instance_name("provider1");
cert_provider->set_certificate_name("cert_name");
// Convert to upb.
const auto* upb_proto = ConvertToUpb(common_tls_context_proto);
ASSERT_NE(upb_proto, nullptr);
// Run test.
auto common_tls_context = Parse(upb_proto);
ASSERT_TRUE(common_tls_context.ok()) << common_tls_context.status();
EXPECT_EQ(common_tls_context->certificate_validation_context
.ca_certificate_provider_instance.instance_name,
"provider1");
EXPECT_EQ(common_tls_context->certificate_validation_context
.ca_certificate_provider_instance.certificate_name,
"cert_name");
EXPECT_THAT(common_tls_context->certificate_validation_context
.match_subject_alt_names,
::testing::ElementsAre());
EXPECT_TRUE(common_tls_context->tls_certificate_provider_instance.Empty())
<< common_tls_context->tls_certificate_provider_instance.ToString();
}
TEST_F(CommonTlsConfigTest, CaCertProviderInValidationContext) {
// Construct proto.
CommonTlsContextProto common_tls_context_proto;
auto* cert_provider = common_tls_context_proto.mutable_validation_context()
->mutable_ca_certificate_provider_instance();
cert_provider->set_instance_name("provider1");
cert_provider->set_certificate_name("cert_name");
// Convert to upb.
const auto* upb_proto = ConvertToUpb(common_tls_context_proto);
ASSERT_NE(upb_proto, nullptr);
// Run test.
auto common_tls_context = Parse(upb_proto);
ASSERT_TRUE(common_tls_context.ok()) << common_tls_context.status();
EXPECT_EQ(common_tls_context->certificate_validation_context
.ca_certificate_provider_instance.instance_name,
"provider1");
EXPECT_EQ(common_tls_context->certificate_validation_context
.ca_certificate_provider_instance.certificate_name,
"cert_name");
EXPECT_THAT(common_tls_context->certificate_validation_context
.match_subject_alt_names,
::testing::ElementsAre());
EXPECT_TRUE(common_tls_context->tls_certificate_provider_instance.Empty())
<< common_tls_context->tls_certificate_provider_instance.ToString();
}
TEST_F(CommonTlsConfigTest, ValidationSdsConfigUnsupported) {
// Construct proto.
CommonTlsContextProto common_tls_context_proto;
common_tls_context_proto.mutable_validation_context_sds_secret_config();
// Convert to upb.
const auto* upb_proto = ConvertToUpb(common_tls_context_proto);
ASSERT_NE(upb_proto, nullptr);
// Run test.
auto common_tls_context = Parse(upb_proto);
ASSERT_FALSE(common_tls_context.ok());
EXPECT_EQ(common_tls_context.status().message(),
"validation failed: ["
"field:validation_context_sds_secret_config "
"error:feature unsupported]")
<< common_tls_context.status();
}
TEST_F(CommonTlsConfigTest, TlsCertProvider) {
// Construct proto.
CommonTlsContextProto common_tls_context_proto;
auto* cert_provider =
common_tls_context_proto.mutable_tls_certificate_provider_instance();
cert_provider->set_instance_name("provider1");
cert_provider->set_certificate_name("cert_name");
// Convert to upb.
const auto* upb_proto = ConvertToUpb(common_tls_context_proto);
ASSERT_NE(upb_proto, nullptr);
// Run test.
auto common_tls_context = Parse(upb_proto);
ASSERT_TRUE(common_tls_context.ok()) << common_tls_context.status();
EXPECT_TRUE(common_tls_context->certificate_validation_context.Empty())
<< common_tls_context->certificate_validation_context.ToString();
EXPECT_EQ(common_tls_context->tls_certificate_provider_instance.instance_name,
"provider1");
EXPECT_EQ(
common_tls_context->tls_certificate_provider_instance.certificate_name,
"cert_name");
}
TEST_F(CommonTlsConfigTest, TlsCertificatesUnuspported) {
// Construct proto.
CommonTlsContextProto common_tls_context_proto;
common_tls_context_proto.add_tls_certificates();
// Convert to upb.
const auto* upb_proto = ConvertToUpb(common_tls_context_proto);
ASSERT_NE(upb_proto, nullptr);
// Run test.
auto common_tls_context = Parse(upb_proto);
ASSERT_FALSE(common_tls_context.ok());
EXPECT_EQ(common_tls_context.status().message(),
"validation failed: ["
"field:tls_certificates error:feature unsupported]")
<< common_tls_context.status();
}
TEST_F(CommonTlsConfigTest, TlsCertificatesSdsConfigUnuspported) {
// Construct proto.
CommonTlsContextProto common_tls_context_proto;
common_tls_context_proto.add_tls_certificate_sds_secret_configs();
// Convert to upb.
const auto* upb_proto = ConvertToUpb(common_tls_context_proto);
ASSERT_NE(upb_proto, nullptr);
// Run test.
auto common_tls_context = Parse(upb_proto);
ASSERT_FALSE(common_tls_context.ok());
EXPECT_EQ(common_tls_context.status().message(),
"validation failed: ["
"field:tls_certificate_sds_secret_configs "
"error:feature unsupported]")
<< common_tls_context.status();
}
TEST_F(CommonTlsConfigTest, TlsParamsUnuspported) {
// Construct proto.
CommonTlsContextProto common_tls_context_proto;
common_tls_context_proto.mutable_tls_params();
// Convert to upb.
const auto* upb_proto = ConvertToUpb(common_tls_context_proto);
ASSERT_NE(upb_proto, nullptr);
// Run test.
auto common_tls_context = Parse(upb_proto);
ASSERT_FALSE(common_tls_context.ok());
EXPECT_EQ(common_tls_context.status().message(),
"validation failed: ["
"field:tls_params error:feature unsupported]")
<< common_tls_context.status();
}
TEST_F(CommonTlsConfigTest, CustomHandshakerUnuspported) {
// Construct proto.
CommonTlsContextProto common_tls_context_proto;
common_tls_context_proto.mutable_custom_handshaker();
// Convert to upb.
const auto* upb_proto = ConvertToUpb(common_tls_context_proto);
ASSERT_NE(upb_proto, nullptr);
// Run test.
auto common_tls_context = Parse(upb_proto);
ASSERT_FALSE(common_tls_context.ok());
EXPECT_EQ(common_tls_context.status().message(),
"validation failed: ["
"field:custom_handshaker error:feature unsupported]")
<< common_tls_context.status();
}
TEST_F(CommonTlsConfigTest, UnknownCertificateProviderInstance) {
// Construct proto.
CommonTlsContextProto common_tls_context_proto;
auto* cert_provider = common_tls_context_proto.mutable_validation_context()
->mutable_ca_certificate_provider_instance();
cert_provider->set_instance_name("fake");
cert_provider->set_certificate_name("cert_name");
// Convert to upb.
const auto* upb_proto = ConvertToUpb(common_tls_context_proto);
ASSERT_NE(upb_proto, nullptr);
// Run test.
auto common_tls_context = Parse(upb_proto);
ASSERT_FALSE(common_tls_context.ok());
EXPECT_EQ(common_tls_context.status().message(),
"validation failed: ["
"field:validation_context.ca_certificate_provider_instance"
".instance_name "
"error:unrecognized certificate provider instance name: fake]")
<< common_tls_context.status();
}
TEST_F(CommonTlsConfigTest, MatchSubjectAltNames) {
// Construct proto.
CommonTlsContextProto common_tls_context_proto;
auto* validation_context =
common_tls_context_proto.mutable_validation_context();
auto* string_matcher = validation_context->add_match_subject_alt_names();
string_matcher->set_exact("exact");
string_matcher = validation_context->add_match_subject_alt_names();
string_matcher->set_prefix("prefix");
string_matcher = validation_context->add_match_subject_alt_names();
string_matcher->set_suffix("suffix");
string_matcher = validation_context->add_match_subject_alt_names();
string_matcher->set_contains("contains");
string_matcher = validation_context->add_match_subject_alt_names();
string_matcher->mutable_safe_regex()->set_regex("regex");
// Convert to upb.
const auto* upb_proto = ConvertToUpb(common_tls_context_proto);
ASSERT_NE(upb_proto, nullptr);
// Run test.
auto common_tls_context = Parse(upb_proto);
ASSERT_TRUE(common_tls_context.ok()) << common_tls_context.status();
const auto& match_subject_alt_names =
common_tls_context->certificate_validation_context
.match_subject_alt_names;
ASSERT_EQ(match_subject_alt_names.size(), 5);
EXPECT_EQ(match_subject_alt_names[0].type(), StringMatcher::Type::kExact);
EXPECT_EQ(match_subject_alt_names[0].string_matcher(), "exact");
EXPECT_TRUE(match_subject_alt_names[0].case_sensitive());
EXPECT_EQ(match_subject_alt_names[1].type(), StringMatcher::Type::kPrefix);
EXPECT_EQ(match_subject_alt_names[1].string_matcher(), "prefix");
EXPECT_TRUE(match_subject_alt_names[1].case_sensitive());
EXPECT_EQ(match_subject_alt_names[2].type(), StringMatcher::Type::kSuffix);
EXPECT_EQ(match_subject_alt_names[2].string_matcher(), "suffix");
EXPECT_TRUE(match_subject_alt_names[2].case_sensitive());
EXPECT_EQ(match_subject_alt_names[3].type(), StringMatcher::Type::kContains);
EXPECT_EQ(match_subject_alt_names[3].string_matcher(), "contains");
EXPECT_TRUE(match_subject_alt_names[3].case_sensitive());
EXPECT_EQ(match_subject_alt_names[4].type(), StringMatcher::Type::kSafeRegex);
EXPECT_EQ(match_subject_alt_names[4].regex_matcher()->pattern(), "regex");
EXPECT_TRUE(match_subject_alt_names[4].case_sensitive());
EXPECT_TRUE(common_tls_context->certificate_validation_context
.ca_certificate_provider_instance.Empty())
<< common_tls_context->certificate_validation_context
.ca_certificate_provider_instance.ToString();
EXPECT_TRUE(common_tls_context->tls_certificate_provider_instance.Empty())
<< common_tls_context->tls_certificate_provider_instance.ToString();
}
TEST_F(CommonTlsConfigTest, MatchSubjectAltNamesCaseInsensitive) {
// Construct proto.
CommonTlsContextProto common_tls_context_proto;
auto* validation_context =
common_tls_context_proto.mutable_validation_context();
auto* string_matcher = validation_context->add_match_subject_alt_names();
string_matcher->set_exact("exact");
string_matcher->set_ignore_case(true);
string_matcher = validation_context->add_match_subject_alt_names();
string_matcher->set_prefix("prefix");
string_matcher->set_ignore_case(true);
string_matcher = validation_context->add_match_subject_alt_names();
string_matcher->set_suffix("suffix");
string_matcher->set_ignore_case(true);
string_matcher = validation_context->add_match_subject_alt_names();
string_matcher->set_contains("contains");
string_matcher->set_ignore_case(true);
// Convert to upb.
const auto* upb_proto = ConvertToUpb(common_tls_context_proto);
ASSERT_NE(upb_proto, nullptr);
// Run test.
auto common_tls_context = Parse(upb_proto);
ASSERT_TRUE(common_tls_context.ok()) << common_tls_context.status();
const auto& match_subject_alt_names =
common_tls_context->certificate_validation_context
.match_subject_alt_names;
ASSERT_EQ(match_subject_alt_names.size(), 4);
EXPECT_EQ(match_subject_alt_names[0].type(), StringMatcher::Type::kExact);
EXPECT_EQ(match_subject_alt_names[0].string_matcher(), "exact");
EXPECT_FALSE(match_subject_alt_names[0].case_sensitive());
EXPECT_EQ(match_subject_alt_names[1].type(), StringMatcher::Type::kPrefix);
EXPECT_EQ(match_subject_alt_names[1].string_matcher(), "prefix");
EXPECT_FALSE(match_subject_alt_names[1].case_sensitive());
EXPECT_EQ(match_subject_alt_names[2].type(), StringMatcher::Type::kSuffix);
EXPECT_EQ(match_subject_alt_names[2].string_matcher(), "suffix");
EXPECT_FALSE(match_subject_alt_names[2].case_sensitive());
EXPECT_EQ(match_subject_alt_names[3].type(), StringMatcher::Type::kContains);
EXPECT_EQ(match_subject_alt_names[3].string_matcher(), "contains");
EXPECT_FALSE(match_subject_alt_names[3].case_sensitive());
EXPECT_TRUE(common_tls_context->certificate_validation_context
.ca_certificate_provider_instance.Empty())
<< common_tls_context->certificate_validation_context
.ca_certificate_provider_instance.ToString();
EXPECT_TRUE(common_tls_context->tls_certificate_provider_instance.Empty())
<< common_tls_context->tls_certificate_provider_instance.ToString();
}
TEST_F(CommonTlsConfigTest, MatchSubjectAltNamesInvalid) {
// Construct proto.
CommonTlsContextProto common_tls_context_proto;
auto* validation_context =
common_tls_context_proto.mutable_validation_context();
auto* string_matcher = validation_context->add_match_subject_alt_names();
string_matcher->mutable_safe_regex()->set_regex("regex");
string_matcher->set_ignore_case(true);
string_matcher = validation_context->add_match_subject_alt_names();
// Convert to upb.
const auto* upb_proto = ConvertToUpb(common_tls_context_proto);
ASSERT_NE(upb_proto, nullptr);
// Run test.
auto common_tls_context = Parse(upb_proto);
ASSERT_FALSE(common_tls_context.ok());
EXPECT_EQ(common_tls_context.status().message(),
"validation failed: ["
"field:validation_context.match_subject_alt_names[0].ignore_case "
"error:not supported for regex matcher; "
"field:validation_context.match_subject_alt_names[1] "
"error:invalid StringMatcher specified]")
<< common_tls_context.status();
}
TEST_F(CommonTlsConfigTest, ValidationContextUnsupportedFields) {
// Construct proto.
CommonTlsContextProto common_tls_context_proto;
auto* validation_context =
common_tls_context_proto.mutable_validation_context();
validation_context->add_verify_certificate_spki("foo");
validation_context->add_verify_certificate_hash("bar");
validation_context->mutable_require_signed_certificate_timestamp()->set_value(
true);
validation_context->mutable_crl();
validation_context->mutable_custom_validator_config();
// Convert to upb.
const auto* upb_proto = ConvertToUpb(common_tls_context_proto);
ASSERT_NE(upb_proto, nullptr);
// Run test.
auto common_tls_context = Parse(upb_proto);
ASSERT_FALSE(common_tls_context.ok());
EXPECT_EQ(common_tls_context.status().message(),
"validation failed: ["
"field:validation_context.crl "
"error:feature unsupported; "
"field:validation_context.custom_validator_config "
"error:feature unsupported; "
"field:validation_context.require_signed_certificate_timestamp "
"error:feature unsupported; "
"field:validation_context.verify_certificate_hash "
"error:feature unsupported; "
"field:validation_context.verify_certificate_spki "
"error:feature unsupported]")
<< common_tls_context.status();
}
} // namespace
} // namespace testing
} // namespace grpc_core
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
grpc::testing::TestEnvironment env(&argc, argv);
grpc_init();
int ret = RUN_ALL_TESTS();
grpc_shutdown();
return ret;
}

@ -140,6 +140,7 @@ grpc_cc_test(
"//src/proto/grpc/testing/xds/v3:router_proto",
"//src/proto/grpc/testing/xds/v3:tls_proto",
"//test/core/util:grpc_test_util",
"//test/core/util:scoped_env_var",
"//test/cpp/util:test_config",
"//test/cpp/util:test_util",
"//test/cpp/util:tls_test_utils",
@ -215,6 +216,7 @@ grpc_cc_test(
"//:grpc",
"//:grpc++",
"//test/core/util:grpc_test_util",
"//test/core/util:scoped_env_var",
],
)
@ -293,6 +295,7 @@ grpc_cc_test(
"//src/proto/grpc/testing/xds/v3:fault_proto",
"//src/proto/grpc/testing/xds/v3:router_proto",
"//test/core/util:grpc_test_util",
"//test/core/util:scoped_env_var",
],
)
@ -346,6 +349,7 @@ grpc_cc_test(
"//src/proto/grpc/lookup/v1:rls_config_proto",
"//src/proto/grpc/lookup/v1:rls_proto",
"//test/core/util:grpc_test_util",
"//test/core/util:scoped_env_var",
"//test/cpp/end2end:rls_server",
],
)

@ -60,16 +60,20 @@ TEST_P(CdsTest, Vanilla) {
EXPECT_EQ(response_state->state, AdsServiceImpl::ResponseState::ACKED);
}
// Tests that CDS client should send a NACK if the cluster type in CDS
// response is unsupported.
TEST_P(CdsTest, UnsupportedClusterType) {
// Testing just one example of an invalid resource here.
// Unit tests for XdsClusterResourceType have exhaustive tests for all
// of the invalid cases.
TEST_P(CdsTest, InvalidClusterResource) {
auto cluster = default_cluster_;
cluster.set_type(Cluster::STATIC);
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state = WaitForCdsNack(DEBUG_LOCATION);
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr("DiscoveryType is not valid."));
EXPECT_EQ(response_state->error_message,
"xDS response validation errors: ["
"resource index 0: cluster_name: "
"INVALID_ARGUMENT: errors validating Cluster resource: ["
"field:type error:unknown discovery type]]");
}
// Tests that we don't trigger does-not-exist callbacks for a resource
@ -88,65 +92,13 @@ TEST_P(CdsTest, InvalidClusterStillExistsIfPreviouslyCached) {
WaitForCdsNack(DEBUG_LOCATION, RpcOptions(), StatusCode::OK);
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_EQ(response_state->error_message,
"xDS response validation errors: [resource index 0: cluster_name: "
"INVALID_ARGUMENT: errors parsing CDS resource: ["
"DiscoveryType is not valid.]]");
"xDS response validation errors: ["
"resource index 0: cluster_name: "
"INVALID_ARGUMENT: errors validating Cluster resource: ["
"field:type error:unknown discovery type]]");
CheckRpcSendOk(DEBUG_LOCATION);
}
// Tests that CDS client should send a NACK if the eds_config in CDS response
// is other than ADS or SELF.
TEST_P(CdsTest, EdsConfigSourceDoesNotSpecifyAdsOrSelf) {
auto cluster = default_cluster_;
cluster.mutable_eds_cluster_config()->mutable_eds_config()->set_path(
"/foo/bar");
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state = WaitForCdsNack(DEBUG_LOCATION);
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr("EDS ConfigSource is not ADS or SELF."));
}
// Tests that CDS client accepts an eds_config of type ADS.
TEST_P(CdsTest, AcceptsEdsConfigSourceOfTypeAds) {
CreateAndStartBackends(1);
auto cluster = default_cluster_;
cluster.mutable_eds_cluster_config()->mutable_eds_config()->mutable_ads();
balancer_->ads_service()->SetCdsResource(cluster);
EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
WaitForAllBackends(DEBUG_LOCATION, /*start_index=*/0, /*stop_index=*/0,
/*check_status=*/nullptr, WaitForBackendOptions(),
RpcOptions().set_timeout_ms(5000));
auto response_state = balancer_->ads_service()->cds_response_state();
ASSERT_TRUE(response_state.has_value());
EXPECT_EQ(response_state->state, AdsServiceImpl::ResponseState::ACKED);
}
// Tests that CDS client should send a NACK if the lb_policy in CDS response
// is other than ROUND_ROBIN.
TEST_P(CdsTest, WrongLbPolicy) {
auto cluster = default_cluster_;
cluster.set_lb_policy(Cluster::LEAST_REQUEST);
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state = WaitForCdsNack(DEBUG_LOCATION);
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr("LB policy is not supported."));
}
// Tests that CDS client should send a NACK if the lrs_server in CDS response
// is other than SELF.
TEST_P(CdsTest, WrongLrsServer) {
auto cluster = default_cluster_;
cluster.mutable_lrs_server()->mutable_ads();
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state = WaitForCdsNack(DEBUG_LOCATION);
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr("LRS ConfigSource is not self."));
}
// Tests round robin is not implacted by the endpoint weight, and that the
// localities in a locality map are picked according to their weights.
TEST_P(CdsTest, EndpointWeightDoesNotImpactWeightedRoundRobin) {

@ -108,175 +108,6 @@ TEST_P(LogicalDNSClusterTest, Basic) {
CheckRpcSendOk(DEBUG_LOCATION);
}
TEST_P(LogicalDNSClusterTest, MissingLoadAssignment) {
// Create Logical DNS Cluster
auto cluster = default_cluster_;
cluster.set_type(Cluster::LOGICAL_DNS);
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state = WaitForCdsNack(DEBUG_LOCATION);
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr(
"load_assignment not present for LOGICAL_DNS cluster"));
}
TEST_P(LogicalDNSClusterTest, MissingLocalities) {
// Create Logical DNS Cluster
auto cluster = default_cluster_;
cluster.set_type(Cluster::LOGICAL_DNS);
cluster.mutable_load_assignment();
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state = WaitForCdsNack(DEBUG_LOCATION);
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(
response_state->error_message,
::testing::HasSubstr("load_assignment for LOGICAL_DNS cluster must have "
"exactly one locality, found 0"));
}
TEST_P(LogicalDNSClusterTest, MultipleLocalities) {
// Create Logical DNS Cluster
auto cluster = default_cluster_;
cluster.set_type(Cluster::LOGICAL_DNS);
auto* load_assignment = cluster.mutable_load_assignment();
load_assignment->add_endpoints();
load_assignment->add_endpoints();
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state = WaitForCdsNack(DEBUG_LOCATION);
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(
response_state->error_message,
::testing::HasSubstr("load_assignment for LOGICAL_DNS cluster must have "
"exactly one locality, found 2"));
}
TEST_P(LogicalDNSClusterTest, MissingEndpoints) {
// Create Logical DNS Cluster
auto cluster = default_cluster_;
cluster.set_type(Cluster::LOGICAL_DNS);
cluster.mutable_load_assignment()->add_endpoints();
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state = WaitForCdsNack(DEBUG_LOCATION);
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr(
"locality for LOGICAL_DNS cluster must have exactly one "
"endpoint, found 0"));
}
TEST_P(LogicalDNSClusterTest, MultipleEndpoints) {
// Create Logical DNS Cluster
auto cluster = default_cluster_;
cluster.set_type(Cluster::LOGICAL_DNS);
auto* locality = cluster.mutable_load_assignment()->add_endpoints();
locality->add_lb_endpoints();
locality->add_lb_endpoints();
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state = WaitForCdsNack(DEBUG_LOCATION);
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr(
"locality for LOGICAL_DNS cluster must have exactly one "
"endpoint, found 2"));
}
TEST_P(LogicalDNSClusterTest, EmptyEndpoint) {
// Create Logical DNS Cluster
auto cluster = default_cluster_;
cluster.set_type(Cluster::LOGICAL_DNS);
cluster.mutable_load_assignment()->add_endpoints()->add_lb_endpoints();
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state = WaitForCdsNack(DEBUG_LOCATION);
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr("LbEndpoint endpoint field not set"));
}
TEST_P(LogicalDNSClusterTest, EndpointMissingAddress) {
// Create Logical DNS Cluster
auto cluster = default_cluster_;
cluster.set_type(Cluster::LOGICAL_DNS);
cluster.mutable_load_assignment()
->add_endpoints()
->add_lb_endpoints()
->mutable_endpoint();
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state = WaitForCdsNack(DEBUG_LOCATION);
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr("Endpoint address field not set"));
}
TEST_P(LogicalDNSClusterTest, AddressMissingSocketAddress) {
// Create Logical DNS Cluster
auto cluster = default_cluster_;
cluster.set_type(Cluster::LOGICAL_DNS);
cluster.mutable_load_assignment()
->add_endpoints()
->add_lb_endpoints()
->mutable_endpoint()
->mutable_address();
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state = WaitForCdsNack(DEBUG_LOCATION);
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr("Address socket_address field not set"));
}
TEST_P(LogicalDNSClusterTest, SocketAddressHasResolverName) {
// Create Logical DNS Cluster
auto cluster = default_cluster_;
cluster.set_type(Cluster::LOGICAL_DNS);
cluster.mutable_load_assignment()
->add_endpoints()
->add_lb_endpoints()
->mutable_endpoint()
->mutable_address()
->mutable_socket_address()
->set_resolver_name("foo");
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state = WaitForCdsNack(DEBUG_LOCATION);
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr("LOGICAL_DNS clusters must NOT have a "
"custom resolver name set"));
}
TEST_P(LogicalDNSClusterTest, SocketAddressMissingAddress) {
// Create Logical DNS Cluster
auto cluster = default_cluster_;
cluster.set_type(Cluster::LOGICAL_DNS);
cluster.mutable_load_assignment()
->add_endpoints()
->add_lb_endpoints()
->mutable_endpoint()
->mutable_address()
->mutable_socket_address();
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state = WaitForCdsNack(DEBUG_LOCATION);
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr("SocketAddress address field not set"));
}
TEST_P(LogicalDNSClusterTest, SocketAddressMissingPort) {
// Create Logical DNS Cluster
auto cluster = default_cluster_;
cluster.set_type(Cluster::LOGICAL_DNS);
cluster.mutable_load_assignment()
->add_endpoints()
->add_lb_endpoints()
->mutable_endpoint()
->mutable_address()
->mutable_socket_address()
->set_address(kServerName);
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state = WaitForCdsNack(DEBUG_LOCATION);
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr("SocketAddress port_value field not set"));
}
//
// aggregate cluster tests
//

@ -23,6 +23,7 @@
#include "absl/strings/str_cat.h"
#include "src/core/ext/filters/client_channel/backup_poller.h"
#include "test/core/util/scoped_env_var.h"
#include "test/cpp/end2end/xds/xds_end2end_test_lib.h"
namespace grpc {
@ -31,6 +32,8 @@ namespace {
using ClientStats = LrsServiceImpl::ClientStats;
using ::grpc_core::testing::ScopedExperimentalEnvVar;
//
// XdsClientTest - basic tests of XdsClient functionality
//
@ -188,12 +191,12 @@ TEST_P(XdsClientTest, MultipleBadCdsResources) {
"INVALID_ARGUMENT: Can't parse Cluster resource.; "
"resource index 3: ",
kClusterName2,
": INVALID_ARGUMENT: errors parsing CDS resource: "
"[DiscoveryType is not valid.]; "
": INVALID_ARGUMENT: errors validating Cluster resource: ["
"field:type error:unknown discovery type]; "
"resource index 4: ",
kClusterName3,
": INVALID_ARGUMENT: errors parsing CDS resource: "
"[DiscoveryType is not valid.]]"));
": INVALID_ARGUMENT: errors validating Cluster resource: ["
"field:type error:unknown discovery type]]"));
// RPCs for default cluster should succeed.
std::vector<std::pair<std::string, std::string>> metadata_default_cluster = {
{"cluster", kDefaultClusterName},
@ -207,8 +210,9 @@ TEST_P(XdsClientTest, MultipleBadCdsResources) {
};
CheckRpcSendFailure(
DEBUG_LOCATION, StatusCode::UNAVAILABLE,
"cluster_name_2: UNAVAILABLE: invalid resource: INVALID_ARGUMENT:.*"
"errors parsing CDS resource.*DiscoveryType is not valid.*",
"cluster_name_2: UNAVAILABLE: invalid resource: "
"INVALID_ARGUMENT: errors validating Cluster resource: \\["
"field:type error:unknown discovery type\\] .*",
RpcOptions().set_metadata(std::move(metadata_cluster_2)));
}

@ -510,8 +510,8 @@ TEST_P(ClientStatusDiscoveryServiceTest, XdsConfigDumpClusterError) {
kCdsTypeUrl, kDefaultClusterName, "1",
UnpackCluster(EqCluster(kDefaultClusterName)),
ClientResourceStatus::NACKED,
EqUpdateFailureState(
::testing::HasSubstr("DiscoveryType not found"), "2"))));
EqUpdateFailureState(::testing::HasSubstr("unknown discovery type"),
"2"))));
if (ok) return; // TEST PASSED!
gpr_sleep_until(
grpc_timeout_milliseconds_to_deadline(kFetchIntervalMilliseconds));

@ -105,6 +105,7 @@
#include "src/proto/grpc/testing/xds/v3/router.grpc.pb.h"
#include "src/proto/grpc/testing/xds/v3/tls.grpc.pb.h"
#include "test/core/util/port.h"
#include "test/core/util/scoped_env_var.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"
@ -131,6 +132,8 @@ using ::grpc::experimental::ExternalCertificateVerifier;
using ::grpc::experimental::IdentityKeyCertPair;
using ::grpc::experimental::StaticDataCertificateProvider;
using ::grpc_core::testing::ScopedExperimentalEnvVar;
constexpr char kClientCertPath[] = "src/core/tsi/test_creds/client.pem";
constexpr char kClientKeyPath[] = "src/core/tsi/test_creds/client.key";
constexpr char kBadClientCertPath[] = "src/core/tsi/test_creds/badclient.pem";
@ -428,384 +431,6 @@ class XdsSecurityTest : public XdsEnd2endTest {
int backend_index_ = 0;
};
TEST_P(XdsSecurityTest, TransportSocketMissingTypedConfig) {
auto cluster = default_cluster_;
cluster.mutable_transport_socket();
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state =
WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000));
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_EQ(response_state->error_message,
"xDS response validation errors: [resource index 0: cluster_name: "
"INVALID_ARGUMENT: errors parsing CDS resource: ["
"Error parsing security configuration: "
"transport_socket.typed_config not set]]");
}
TEST_P(XdsSecurityTest, UnknownTransportSocket) {
auto cluster = default_cluster_;
auto* transport_socket = cluster.mutable_transport_socket();
transport_socket->mutable_typed_config()->PackFrom(Listener());
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state =
WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000));
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_EQ(response_state->error_message,
"xDS response validation errors: [resource index 0: cluster_name: "
"INVALID_ARGUMENT: errors parsing CDS resource: ["
"Error parsing security configuration: "
"Unrecognized transport socket type: "
"envoy.config.listener.v3.Listener]]");
}
TEST_P(XdsSecurityTest,
TLSConfigurationWithoutValidationContextCertificateProviderInstance) {
auto cluster = default_cluster_;
auto* transport_socket = cluster.mutable_transport_socket();
transport_socket->mutable_typed_config()->PackFrom(UpstreamTlsContext());
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state =
WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000));
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr("TLS configuration provided but no "
"ca_certificate_provider_instance found."));
}
TEST_P(
XdsSecurityTest,
MatchSubjectAltNamesProvidedWithoutValidationContextCertificateProviderInstance) {
auto cluster = default_cluster_;
auto* transport_socket = cluster.mutable_transport_socket();
transport_socket->set_name("envoy.transport_sockets.tls");
UpstreamTlsContext upstream_tls_context;
auto* validation_context = upstream_tls_context.mutable_common_tls_context()
->mutable_validation_context();
*validation_context->add_match_subject_alt_names() = server_san_exact_;
transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context);
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state =
WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000));
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr("TLS configuration provided but no "
"ca_certificate_provider_instance found."));
}
TEST_P(
XdsSecurityTest,
TlsCertificateProviderInstanceWithoutValidationContextCertificateProviderInstance) {
auto cluster = default_cluster_;
auto* transport_socket = cluster.mutable_transport_socket();
transport_socket->set_name("envoy.transport_sockets.tls");
UpstreamTlsContext upstream_tls_context;
upstream_tls_context.mutable_common_tls_context()
->mutable_tls_certificate_provider_instance()
->set_instance_name(std::string("fake_plugin1"));
transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context);
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state =
WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000));
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr("TLS configuration provided but no "
"ca_certificate_provider_instance found."));
}
TEST_P(XdsSecurityTest, RegexSanMatcherDoesNotAllowIgnoreCase) {
auto cluster = default_cluster_;
auto* transport_socket = cluster.mutable_transport_socket();
transport_socket->set_name("envoy.transport_sockets.tls");
UpstreamTlsContext upstream_tls_context;
upstream_tls_context.mutable_common_tls_context()
->mutable_validation_context()
->mutable_ca_certificate_provider_instance()
->set_instance_name(std::string("fake_plugin1"));
auto* validation_context = upstream_tls_context.mutable_common_tls_context()
->mutable_validation_context();
StringMatcher matcher;
matcher.mutable_safe_regex()->mutable_google_re2();
matcher.mutable_safe_regex()->set_regex(
"(foo|waterzooi).test.google.(fr|be)");
matcher.set_ignore_case(true);
*validation_context->add_match_subject_alt_names() = matcher;
transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context);
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state =
WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000));
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr(
"StringMatcher: ignore_case has no effect for SAFE_REGEX."));
}
TEST_P(XdsSecurityTest, UnknownRootCertificateProvider) {
auto cluster = default_cluster_;
auto* transport_socket = cluster.mutable_transport_socket();
transport_socket->set_name("envoy.transport_sockets.tls");
UpstreamTlsContext upstream_tls_context;
upstream_tls_context.mutable_common_tls_context()
->mutable_validation_context()
->mutable_ca_certificate_provider_instance()
->set_instance_name("unknown");
transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context);
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state =
WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000));
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(XdsSecurityTest, UnknownIdentityCertificateProvider) {
g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}});
auto cluster = default_cluster_;
auto* transport_socket = cluster.mutable_transport_socket();
transport_socket->set_name("envoy.transport_sockets.tls");
UpstreamTlsContext upstream_tls_context;
upstream_tls_context.mutable_common_tls_context()
->mutable_tls_certificate_provider_instance()
->set_instance_name("unknown");
upstream_tls_context.mutable_common_tls_context()
->mutable_validation_context()
->mutable_ca_certificate_provider_instance()
->set_instance_name("fake_plugin1");
transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context);
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state =
WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000));
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(XdsSecurityTest,
NacksCertificateValidationContextWithVerifyCertificateSpki) {
g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}});
auto cluster = default_cluster_;
auto* transport_socket = cluster.mutable_transport_socket();
transport_socket->set_name("envoy.transport_sockets.tls");
UpstreamTlsContext upstream_tls_context;
upstream_tls_context.mutable_common_tls_context()
->mutable_validation_context()
->mutable_ca_certificate_provider_instance()
->set_instance_name("fake_plugin1");
upstream_tls_context.mutable_common_tls_context()
->mutable_validation_context()
->add_verify_certificate_spki("spki");
transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context);
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state =
WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000));
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(
response_state->error_message,
::testing::HasSubstr(
"CertificateValidationContext: verify_certificate_spki unsupported"));
}
TEST_P(XdsSecurityTest,
NacksCertificateValidationContextWithVerifyCertificateHash) {
g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}});
auto cluster = default_cluster_;
auto* transport_socket = cluster.mutable_transport_socket();
transport_socket->set_name("envoy.transport_sockets.tls");
UpstreamTlsContext upstream_tls_context;
upstream_tls_context.mutable_common_tls_context()
->mutable_validation_context()
->mutable_ca_certificate_provider_instance()
->set_instance_name("fake_plugin1");
upstream_tls_context.mutable_common_tls_context()
->mutable_validation_context()
->add_verify_certificate_hash("hash");
transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context);
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state =
WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000));
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(
response_state->error_message,
::testing::HasSubstr(
"CertificateValidationContext: verify_certificate_hash unsupported"));
}
TEST_P(XdsSecurityTest,
NacksCertificateValidationContextWithRequireSignedCertificateTimes) {
g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}});
auto cluster = default_cluster_;
auto* transport_socket = cluster.mutable_transport_socket();
transport_socket->set_name("envoy.transport_sockets.tls");
UpstreamTlsContext upstream_tls_context;
upstream_tls_context.mutable_common_tls_context()
->mutable_validation_context()
->mutable_ca_certificate_provider_instance()
->set_instance_name("fake_plugin1");
upstream_tls_context.mutable_common_tls_context()
->mutable_validation_context()
->mutable_require_signed_certificate_timestamp()
->set_value(true);
transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context);
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state =
WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000));
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(
response_state->error_message,
::testing::HasSubstr("CertificateValidationContext: "
"require_signed_certificate_timestamp unsupported"));
}
TEST_P(XdsSecurityTest, NacksCertificateValidationContextWithCrl) {
g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}});
auto cluster = default_cluster_;
auto* transport_socket = cluster.mutable_transport_socket();
transport_socket->set_name("envoy.transport_sockets.tls");
UpstreamTlsContext upstream_tls_context;
upstream_tls_context.mutable_common_tls_context()
->mutable_validation_context()
->mutable_ca_certificate_provider_instance()
->set_instance_name("fake_plugin1");
upstream_tls_context.mutable_common_tls_context()
->mutable_validation_context()
->mutable_crl();
transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context);
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state =
WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000));
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(
response_state->error_message,
::testing::HasSubstr("CertificateValidationContext: crl unsupported"));
}
TEST_P(XdsSecurityTest,
NacksCertificateValidationContextWithCustomValidatorConfig) {
g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}});
auto cluster = default_cluster_;
auto* transport_socket = cluster.mutable_transport_socket();
transport_socket->set_name("envoy.transport_sockets.tls");
UpstreamTlsContext upstream_tls_context;
upstream_tls_context.mutable_common_tls_context()
->mutable_validation_context()
->mutable_ca_certificate_provider_instance()
->set_instance_name("fake_plugin1");
upstream_tls_context.mutable_common_tls_context()
->mutable_validation_context()
->mutable_custom_validator_config();
transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context);
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state =
WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000));
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(
response_state->error_message,
::testing::HasSubstr(
"CertificateValidationContext: custom_validator_config unsupported"));
}
TEST_P(XdsSecurityTest, NacksValidationContextSdsSecretConfig) {
g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}});
auto cluster = default_cluster_;
auto* transport_socket = cluster.mutable_transport_socket();
transport_socket->set_name("envoy.transport_sockets.tls");
UpstreamTlsContext upstream_tls_context;
upstream_tls_context.mutable_common_tls_context()
->mutable_validation_context_sds_secret_config();
transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context);
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state =
WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000));
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(
response_state->error_message,
::testing::HasSubstr("validation_context_sds_secret_config unsupported"));
}
TEST_P(XdsSecurityTest, NacksTlsParams) {
g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}});
auto cluster = default_cluster_;
auto* transport_socket = cluster.mutable_transport_socket();
transport_socket->set_name("envoy.transport_sockets.tls");
UpstreamTlsContext upstream_tls_context;
upstream_tls_context.mutable_common_tls_context()
->mutable_validation_context()
->mutable_ca_certificate_provider_instance()
->set_instance_name("fake_plugin1");
upstream_tls_context.mutable_common_tls_context()->mutable_tls_params();
transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context);
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state =
WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000));
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr("tls_params unsupported"));
}
TEST_P(XdsSecurityTest, NacksCustomHandshaker) {
g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}});
auto cluster = default_cluster_;
auto* transport_socket = cluster.mutable_transport_socket();
transport_socket->set_name("envoy.transport_sockets.tls");
UpstreamTlsContext upstream_tls_context;
upstream_tls_context.mutable_common_tls_context()
->mutable_validation_context()
->mutable_ca_certificate_provider_instance()
->set_instance_name("fake_plugin1");
upstream_tls_context.mutable_common_tls_context()
->mutable_custom_handshaker();
transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context);
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state =
WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000));
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr("custom_handshaker unsupported"));
}
TEST_P(XdsSecurityTest, NacksTlsCertificates) {
g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}});
auto cluster = default_cluster_;
auto* transport_socket = cluster.mutable_transport_socket();
transport_socket->set_name("envoy.transport_sockets.tls");
UpstreamTlsContext upstream_tls_context;
upstream_tls_context.mutable_common_tls_context()
->mutable_validation_context()
->mutable_ca_certificate_provider_instance()
->set_instance_name("fake_plugin1");
upstream_tls_context.mutable_common_tls_context()->add_tls_certificates();
transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context);
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state =
WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000));
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr("tls_certificates unsupported"));
}
TEST_P(XdsSecurityTest, NacksTlsCertificateSdsSecretConfigs) {
g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}});
auto cluster = default_cluster_;
auto* transport_socket = cluster.mutable_transport_socket();
transport_socket->set_name("envoy.transport_sockets.tls");
UpstreamTlsContext upstream_tls_context;
upstream_tls_context.mutable_common_tls_context()
->mutable_validation_context()
->mutable_ca_certificate_provider_instance()
->set_instance_name("fake_plugin1");
upstream_tls_context.mutable_common_tls_context()
->add_tls_certificate_sds_secret_configs();
transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context);
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state =
WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000));
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(
response_state->error_message,
::testing::HasSubstr("tls_certificate_sds_secret_configs unsupported"));
}
TEST_P(XdsSecurityTest, TestTlsConfigurationInCombinedValidationContext) {
g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}});
auto cluster = default_cluster_;
@ -1738,7 +1363,7 @@ TEST_P(XdsServerSecurityTest, UnknownIdentityCertificateProvider) {
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"));
"unrecognized certificate provider instance name: unknown"));
}
TEST_P(XdsServerSecurityTest, UnknownRootCertificateProvider) {
@ -1749,7 +1374,7 @@ TEST_P(XdsServerSecurityTest, UnknownRootCertificateProvider) {
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"));
"unrecognized certificate provider instance name: unknown"));
}
TEST_P(XdsServerSecurityTest,

@ -33,7 +33,6 @@
#include <grpcpp/client_context.h>
#include <grpcpp/xds_server_builder.h>
#include "src/core/lib/gprpp/env.h"
#include "src/core/lib/security/credentials/fake/fake_credentials.h"
#include "src/core/lib/security/security_connector/ssl_utils.h"
#include "src/cpp/server/secure_server_credentials.h"
@ -463,18 +462,6 @@ class XdsEnd2endTest : public ::testing::TestWithParam<XdsTestType> {
"grpc/server?xds.resource.listening_address=%s";
};
class ScopedExperimentalEnvVar {
public:
explicit ScopedExperimentalEnvVar(const char* env_var) : env_var_(env_var) {
grpc_core::SetEnv(env_var_, "true");
}
~ScopedExperimentalEnvVar() { grpc_core::UnsetEnv(env_var_); }
private:
const char* env_var_;
};
// RPC services used to talk to the backends.
enum RpcService {
SERVICE_ECHO,

@ -25,6 +25,7 @@
#include "src/proto/grpc/testing/xds/v3/fault.grpc.pb.h"
#include "src/proto/grpc/testing/xds/v3/outlier_detection.grpc.pb.h"
#include "src/proto/grpc/testing/xds/v3/router.grpc.pb.h"
#include "test/core/util/scoped_env_var.h"
#include "test/cpp/end2end/xds/no_op_http_filter.h"
#include "test/cpp/end2end/xds/xds_end2end_test_lib.h"
@ -32,6 +33,8 @@ namespace grpc {
namespace testing {
namespace {
using ::grpc_core::testing::ScopedExperimentalEnvVar;
class OutlierDetectionTest : public XdsEnd2endTest {
protected:
std::string CreateMetadataValueThatHashesToBackend(int index) {

@ -1167,101 +1167,6 @@ TEST_P(RingHashTest, UnsupportedHashPolicyUntilChannelIdHashing) {
EXPECT_TRUE(found);
}
// Test we nack when ring hash policy has invalid hash function (something
// other than XX_HASH.
TEST_P(RingHashTest, InvalidHashFunction) {
CreateAndStartBackends(1);
auto cluster = default_cluster_;
cluster.set_lb_policy(Cluster::RING_HASH);
cluster.mutable_ring_hash_lb_config()->set_hash_function(
Cluster::RingHashLbConfig::MURMUR_HASH_2);
balancer_->ads_service()->SetCdsResource(cluster);
auto new_route_config = default_route_config_;
auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
auto* hash_policy = route->mutable_route()->add_hash_policy();
hash_policy->mutable_filter_state()->set_key("io.grpc.channel_id");
SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
new_route_config);
EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
const auto response_state = WaitForCdsNack(DEBUG_LOCATION);
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(
response_state->error_message,
::testing::HasSubstr("ring hash lb config has invalid hash function."));
}
// Test we nack when ring hash policy has invalid ring size.
TEST_P(RingHashTest, InvalidMinimumRingSize) {
CreateAndStartBackends(1);
auto cluster = default_cluster_;
cluster.set_lb_policy(Cluster::RING_HASH);
cluster.mutable_ring_hash_lb_config()->mutable_minimum_ring_size()->set_value(
0);
balancer_->ads_service()->SetCdsResource(cluster);
auto new_route_config = default_route_config_;
auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
auto* hash_policy = route->mutable_route()->add_hash_policy();
hash_policy->mutable_filter_state()->set_key("io.grpc.channel_id");
SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
new_route_config);
EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
const auto response_state = WaitForCdsNack(DEBUG_LOCATION);
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr(
"min_ring_size is not in the range of 1 to 8388608."));
}
// Test we nack when ring hash policy has invalid ring size.
TEST_P(RingHashTest, InvalidMaxmumRingSize) {
CreateAndStartBackends(1);
auto cluster = default_cluster_;
cluster.set_lb_policy(Cluster::RING_HASH);
cluster.mutable_ring_hash_lb_config()->mutable_maximum_ring_size()->set_value(
8388609);
balancer_->ads_service()->SetCdsResource(cluster);
auto new_route_config = default_route_config_;
auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
auto* hash_policy = route->mutable_route()->add_hash_policy();
hash_policy->mutable_filter_state()->set_key("io.grpc.channel_id");
SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
new_route_config);
EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
const auto response_state = WaitForCdsNack(DEBUG_LOCATION);
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr(
"max_ring_size is not in the range of 1 to 8388608."));
}
// Test we nack when ring hash policy has invalid ring size.
TEST_P(RingHashTest, InvalidRingSizeMinGreaterThanMax) {
CreateAndStartBackends(1);
auto cluster = default_cluster_;
cluster.set_lb_policy(Cluster::RING_HASH);
cluster.mutable_ring_hash_lb_config()->mutable_maximum_ring_size()->set_value(
5000);
cluster.mutable_ring_hash_lb_config()->mutable_minimum_ring_size()->set_value(
5001);
balancer_->ads_service()->SetCdsResource(cluster);
auto new_route_config = default_route_config_;
auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
auto* hash_policy = route->mutable_route()->add_hash_policy();
hash_policy->mutable_filter_state()->set_key("io.grpc.channel_id");
SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
new_route_config);
EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
const auto response_state = WaitForCdsNack(DEBUG_LOCATION);
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr(
"min_ring_size cannot be greater than max_ring_size."));
}
} // namespace
} // namespace testing
} // namespace grpc

@ -26,6 +26,7 @@
#include "src/proto/grpc/lookup/v1/rls.grpc.pb.h"
#include "src/proto/grpc/lookup/v1/rls.pb.h"
#include "src/proto/grpc/lookup/v1/rls_config.pb.h"
#include "test/core/util/scoped_env_var.h"
#include "test/cpp/end2end/rls_server.h"
#include "test/cpp/end2end/xds/xds_end2end_test_lib.h"
@ -36,6 +37,8 @@ namespace {
using ::grpc::lookup::v1::RouteLookupClusterSpecifier;
using ::grpc::lookup::v1::RouteLookupConfig;
using ::grpc_core::testing::ScopedExperimentalEnvVar;
constexpr char kRlsTestKey[] = "test_key";
constexpr char kRlsTestKey1[] = "key1";
constexpr char kRlsTestValue[] = "test_value";

@ -4977,6 +4977,30 @@
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": true,
"language": "c++",
"name": "outlier_detection_lb_config_parser_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,
@ -8105,6 +8129,54 @@
],
"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_cluster_resource_type_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"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_common_types_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": false
},
{
"args": [],
"benchmark": false,

Loading…
Cancel
Save