From d55431995c77573c17cae1b78a931aa7b2ecf991 Mon Sep 17 00:00:00 2001 From: Eugene Ostroukhov Date: Fri, 16 Jun 2023 11:14:01 -0700 Subject: [PATCH] [interop] Implement "hostname" for RPC behavior (#33446) This enables outlier detection test. See #33135 --- CMakeLists.txt | 82 +++++++ build_autogenerated.yaml | 30 +++ test/cpp/interop/BUILD | 39 +++- test/cpp/interop/xds_interop_server.cc | 177 +-------------- test/cpp/interop/xds_interop_server_lib.cc | 227 ++++++++++++++++++++ test/cpp/interop/xds_interop_server_lib.h | 39 ++++ test/cpp/interop/xds_interop_server_test.cc | 83 +++++++ tools/run_tests/generated/tests.json | 24 +++ 8 files changed, 524 insertions(+), 177 deletions(-) create mode 100644 test/cpp/interop/xds_interop_server_lib.cc create mode 100644 test/cpp/interop/xds_interop_server_lib.h create mode 100644 test/cpp/interop/xds_interop_server_test.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 959a5185cf7..42c4382a7b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1425,6 +1425,7 @@ if(gRPC_BUILD_TESTS) add_dependencies(buildtests_cxx xds_http_filters_test) add_dependencies(buildtests_cxx xds_interop_client) add_dependencies(buildtests_cxx xds_interop_server) + add_dependencies(buildtests_cxx xds_interop_server_test) add_dependencies(buildtests_cxx xds_lb_policy_registry_test) add_dependencies(buildtests_cxx xds_listener_resource_type_test) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) @@ -29137,6 +29138,7 @@ add_executable(xds_interop_server src/cpp/server/csds/csds.cc test/cpp/end2end/test_health_check_service_impl.cc test/cpp/interop/xds_interop_server.cc + test/cpp/interop/xds_interop_server_lib.cc third_party/googletest/googletest/src/gtest-all.cc third_party/googletest/googlemock/src/gmock-all.cc ) @@ -29172,6 +29174,86 @@ target_link_libraries(xds_interop_server ) +endif() +if(gRPC_BUILD_TESTS) + +add_executable(xds_interop_server_test + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/health/v1/health.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/health/v1/health.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/health/v1/health.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/health/v1/health.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/empty.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/empty.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/empty.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/empty.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/istio_echo.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/istio_echo.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/istio_echo.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/istio_echo.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/messages.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/messages.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/messages.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/messages.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/test.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/test.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/test.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/test.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/base.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/base.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/base.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/base.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/config_dump.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/config_dump.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/config_dump.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/config_dump.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/csds.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/csds.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/csds.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/csds.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 + src/cpp/server/admin/admin_services.cc + src/cpp/server/csds/csds.cc + test/cpp/end2end/test_health_check_service_impl.cc + test/cpp/interop/xds_interop_server_lib.cc + test/cpp/interop/xds_interop_server_test.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc +) +target_compile_features(xds_interop_server_test PUBLIC cxx_std_14) +target_include_directories(xds_interop_server_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_interop_server_test + ${_gRPC_BASELIB_LIBRARIES} + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ZLIB_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc++_reflection + grpcpp_channelz + grpc_test_util + grpc++_test_config +) + + endif() if(gRPC_BUILD_TESTS) diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index 6d0f6edfdb4..b59a72c8a07 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -16586,6 +16586,7 @@ targets: headers: - src/cpp/server/csds/csds.h - test/cpp/end2end/test_health_check_service_impl.h + - test/cpp/interop/xds_interop_server_lib.h src: - src/proto/grpc/health/v1/health.proto - src/proto/grpc/testing/empty.proto @@ -16599,6 +16600,35 @@ targets: - src/cpp/server/csds/csds.cc - test/cpp/end2end/test_health_check_service_impl.cc - test/cpp/interop/xds_interop_server.cc + - test/cpp/interop/xds_interop_server_lib.cc + deps: + - grpc++_reflection + - grpcpp_channelz + - grpc_test_util + - grpc++_test_config +- name: xds_interop_server_test + gtest: true + build: test + language: c++ + headers: + - src/cpp/server/csds/csds.h + - test/cpp/end2end/test_health_check_service_impl.h + - test/cpp/interop/xds_interop_server_lib.h + src: + - src/proto/grpc/health/v1/health.proto + - src/proto/grpc/testing/empty.proto + - src/proto/grpc/testing/istio_echo.proto + - src/proto/grpc/testing/messages.proto + - src/proto/grpc/testing/test.proto + - src/proto/grpc/testing/xds/v3/base.proto + - src/proto/grpc/testing/xds/v3/config_dump.proto + - src/proto/grpc/testing/xds/v3/csds.proto + - src/proto/grpc/testing/xds/v3/percent.proto + - src/cpp/server/admin/admin_services.cc + - src/cpp/server/csds/csds.cc + - test/cpp/end2end/test_health_check_service_impl.cc + - test/cpp/interop/xds_interop_server_lib.cc + - test/cpp/interop/xds_interop_server_test.cc deps: - grpc++_reflection - grpcpp_channelz diff --git a/test/cpp/interop/BUILD b/test/cpp/interop/BUILD index 48557290c58..cbb716f1838 100644 --- a/test/cpp/interop/BUILD +++ b/test/cpp/interop/BUILD @@ -256,6 +256,24 @@ grpc_cc_binary( ], ) +grpc_cc_library( + name = "xds_interop_server_lib", + testonly = True, + srcs = [ + "xds_interop_server_lib.cc", + ], + hdrs = ["xds_interop_server_lib.h"], + deps = [ + "//:grpc++", + "//:grpc++_reflection", + "//:grpcpp_admin", + "//src/proto/grpc/testing:empty_proto", + "//src/proto/grpc/testing:messages_proto", + "//src/proto/grpc/testing:test_proto", + "//test/cpp/end2end:test_health_check_service_impl", + ], +) + grpc_cc_binary( name = "xds_interop_server", testonly = True, @@ -266,18 +284,29 @@ grpc_cc_binary( "absl/flags:flag", ], deps = [ + ":xds_interop_server_lib", "//:grpc++", - "//:grpc++_reflection", - "//:grpcpp_admin", - "//src/proto/grpc/testing:empty_proto", - "//src/proto/grpc/testing:messages_proto", - "//src/proto/grpc/testing:test_proto", "//test/core/util:grpc_test_util", "//test/cpp/end2end:test_health_check_service_impl", "//test/cpp/util:test_config", ], ) +grpc_cc_test( + name = "xds_interop_server_test", + srcs = [ + "xds_interop_server_test.cc", + ], + external_deps = ["gtest"], + deps = [ + ":xds_interop_server_lib", + "//:grpc++", + "//src/proto/grpc/testing:istio_echo_proto", + "//test/core/util:grpc_test_util", + "//test/cpp/util:test_config", + ], +) + grpc_cc_library( name = "istio_echo_server_lib", srcs = [ diff --git a/test/cpp/interop/xds_interop_server.cc b/test/cpp/interop/xds_interop_server.cc index 481d43737ee..0c86cccb391 100644 --- a/test/cpp/interop/xds_interop_server.cc +++ b/test/cpp/interop/xds_interop_server.cc @@ -16,33 +16,16 @@ // // -#include +#include #include "absl/flags/flag.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_split.h" -#include "absl/synchronization/mutex.h" #include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include "src/core/lib/gpr/string.h" -#include "src/core/lib/gprpp/crash.h" #include "src/core/lib/iomgr/gethostname.h" -#include "src/core/lib/transport/transport.h" -#include "src/proto/grpc/testing/empty.pb.h" -#include "src/proto/grpc/testing/messages.pb.h" -#include "src/proto/grpc/testing/test.grpc.pb.h" #include "test/core/util/test_config.h" -#include "test/cpp/end2end/test_health_check_service_impl.h" +#include "test/cpp/interop/xds_interop_server_lib.h" #include "test/cpp/util/test_config.h" ABSL_FLAG(int32_t, port, 8080, "Server port for service."); @@ -54,158 +37,6 @@ ABSL_FLAG(bool, secure_mode, false, "If true, XdsServerCredentials are used, InsecureServerCredentials " "otherwise"); -using grpc::Server; -using grpc::ServerBuilder; -using grpc::ServerContext; -using grpc::Status; -using grpc::XdsServerBuilder; -using grpc::testing::Empty; -using grpc::testing::HealthCheckServiceImpl; -using grpc::testing::SimpleRequest; -using grpc::testing::SimpleResponse; -using grpc::testing::TestService; -using grpc::testing::XdsUpdateHealthService; - -namespace { -constexpr absl::string_view kRpcBehaviorMetadataKey = "rpc-behavior"; -constexpr absl::string_view kErrorCodeRpcBehavior = "error-code-"; - -std::set GetRpcBehaviorMetadata(ServerContext* context) { - std::set rpc_behaviors; - auto rpc_behavior_metadata = - context->client_metadata().equal_range(grpc::string_ref( - kRpcBehaviorMetadataKey.data(), kRpcBehaviorMetadataKey.length())); - for (auto metadata = rpc_behavior_metadata.first; - metadata != rpc_behavior_metadata.second; ++metadata) { - auto value = metadata->second; - for (auto behavior : - absl::StrSplit(absl::string_view(value.data(), value.length()), ',')) { - rpc_behaviors.emplace(behavior); - } - } - return rpc_behaviors; -} - -absl::optional GetStatusForRpcBehaviorMetadata( - absl::string_view header_value) { - if (absl::StartsWith(header_value, kErrorCodeRpcBehavior)) { - grpc::StatusCode code; - if (absl::SimpleAtoi(header_value.substr(kErrorCodeRpcBehavior.length()), - &code)) { - std::string message = absl::StrCat( - "Rpc failed as per the rpc-behavior header value: ", header_value); - return Status(code, message); - } else { - std::string message = absl::StrCat( - "Invalid format for rpc-behavior header: ", header_value); - return Status(grpc::StatusCode::INVALID_ARGUMENT, message); - } - } else { - return absl::nullopt; - } -} -} // namespace - -class TestServiceImpl : public TestService::Service { - public: - explicit TestServiceImpl(const std::string& hostname) : hostname_(hostname) {} - - Status UnaryCall(ServerContext* context, const SimpleRequest* /*request*/, - SimpleResponse* response) override { - response->set_server_id(absl::GetFlag(FLAGS_server_id)); - for (const auto& rpc_behavior : GetRpcBehaviorMetadata(context)) { - auto maybe_status = GetStatusForRpcBehaviorMetadata(rpc_behavior); - if (maybe_status.has_value()) { - return *maybe_status; - } - } - response->set_hostname(hostname_); - context->AddInitialMetadata("hostname", hostname_); - return Status::OK; - } - - Status EmptyCall(ServerContext* context, const Empty* /*request*/, - Empty* /*response*/) override { - context->AddInitialMetadata("hostname", hostname_); - return Status::OK; - } - - private: - std::string hostname_; -}; - -class XdsUpdateHealthServiceImpl : public XdsUpdateHealthService::Service { - public: - explicit XdsUpdateHealthServiceImpl( - HealthCheckServiceImpl* health_check_service) - : health_check_service_(health_check_service) {} - - Status SetServing(ServerContext* /* context */, const Empty* /* request */, - Empty* /* response */) override { - health_check_service_->SetAll( - grpc::health::v1::HealthCheckResponse::SERVING); - return Status::OK; - } - - Status SetNotServing(ServerContext* /* context */, const Empty* /* request */, - Empty* /* response */) override { - health_check_service_->SetAll( - grpc::health::v1::HealthCheckResponse::NOT_SERVING); - return Status::OK; - } - - private: - HealthCheckServiceImpl* const health_check_service_; -}; - -void RunServer(bool secure_mode, const int port, const int maintenance_port, - const std::string& hostname) { - std::unique_ptr xds_enabled_server; - std::unique_ptr server; - TestServiceImpl service(hostname); - HealthCheckServiceImpl health_check_service; - health_check_service.SetStatus( - "", grpc::health::v1::HealthCheckResponse::SERVING); - health_check_service.SetStatus( - "grpc.testing.TestService", - grpc::health::v1::HealthCheckResponse::SERVING); - health_check_service.SetStatus( - "grpc.testing.XdsUpdateHealthService", - grpc::health::v1::HealthCheckResponse::SERVING); - XdsUpdateHealthServiceImpl update_health_service(&health_check_service); - - grpc::reflection::InitProtoReflectionServerBuilderPlugin(); - ServerBuilder builder; - if (secure_mode) { - XdsServerBuilder xds_builder; - xds_builder.RegisterService(&service); - xds_builder.AddListeningPort( - absl::StrCat("0.0.0.0:", port), - grpc::XdsServerCredentials(grpc::InsecureServerCredentials())); - xds_enabled_server = xds_builder.BuildAndStart(); - gpr_log(GPR_INFO, "Server starting on 0.0.0.0:%d", port); - builder.RegisterService(&health_check_service); - builder.RegisterService(&update_health_service); - grpc::AddAdminServices(&builder); - builder.AddListeningPort(absl::StrCat("0.0.0.0:", maintenance_port), - grpc::InsecureServerCredentials()); - server = builder.BuildAndStart(); - gpr_log(GPR_INFO, "Maintenance server listening on 0.0.0.0:%d", - maintenance_port); - } else { - builder.RegisterService(&service); - builder.RegisterService(&health_check_service); - builder.RegisterService(&update_health_service); - grpc::AddAdminServices(&builder); - builder.AddListeningPort(absl::StrCat("0.0.0.0:", port), - grpc::InsecureServerCredentials()); - server = builder.BuildAndStart(); - gpr_log(GPR_INFO, "Server listening on 0.0.0.0:%d", port); - } - - server->Wait(); -} - int main(int argc, char** argv) { grpc::testing::TestEnvironment env(&argc, argv); grpc::testing::InitTest(&argc, &argv, true); @@ -225,7 +56,9 @@ int main(int argc, char** argv) { return 1; } grpc::EnableDefaultHealthCheckService(false); - RunServer(absl::GetFlag(FLAGS_secure_mode), port, maintenance_port, hostname); + grpc::testing::RunServer(absl::GetFlag(FLAGS_secure_mode), port, + maintenance_port, hostname, + absl::GetFlag(FLAGS_server_id)); return 0; } diff --git a/test/cpp/interop/xds_interop_server_lib.cc b/test/cpp/interop/xds_interop_server_lib.cc new file mode 100644 index 00000000000..310a5d44948 --- /dev/null +++ b/test/cpp/interop/xds_interop_server_lib.cc @@ -0,0 +1,227 @@ +// +// +// Copyright 2023 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 "test/cpp/interop/xds_interop_server_lib.h" + +#include + +#include "absl/strings/str_cat.h" +#include "absl/strings/str_split.h" +#include "absl/synchronization/mutex.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/crash.h" +#include "src/core/lib/transport/transport.h" +#include "src/proto/grpc/testing/empty.pb.h" +#include "src/proto/grpc/testing/messages.pb.h" +#include "src/proto/grpc/testing/test.grpc.pb.h" +#include "test/cpp/end2end/test_health_check_service_impl.h" + +namespace grpc { +namespace testing { +namespace { + +using grpc::Server; +using grpc::ServerBuilder; +using grpc::ServerContext; +using grpc::Status; +using grpc::XdsServerBuilder; +using grpc::testing::Empty; +using grpc::testing::HealthCheckServiceImpl; +using grpc::testing::SimpleRequest; +using grpc::testing::SimpleResponse; +using grpc::testing::TestService; +using grpc::testing::XdsUpdateHealthService; + +constexpr absl::string_view kRpcBehaviorMetadataKey = "rpc-behavior"; +constexpr absl::string_view kErrorCodeRpcBehavior = "error-code-"; +constexpr absl::string_view kHostnameRpcBehaviorFilter = "hostname="; + +std::set GetRpcBehaviorMetadata(ServerContext* context) { + std::set rpc_behaviors; + auto rpc_behavior_metadata = + context->client_metadata().equal_range(grpc::string_ref( + kRpcBehaviorMetadataKey.data(), kRpcBehaviorMetadataKey.length())); + for (auto metadata = rpc_behavior_metadata.first; + metadata != rpc_behavior_metadata.second; ++metadata) { + auto value = metadata->second; + for (auto behavior : + absl::StrSplit(absl::string_view(value.data(), value.length()), ',')) { + rpc_behaviors.emplace(behavior); + } + } + return rpc_behaviors; +} + +class TestServiceImpl : public TestService::Service { + public: + explicit TestServiceImpl(absl::string_view hostname, + absl::string_view server_id) + : hostname_(hostname), server_id_(server_id) {} + + Status UnaryCall(ServerContext* context, const SimpleRequest* /*request*/, + SimpleResponse* response) override { + response->set_server_id(server_id_); + for (const auto& rpc_behavior : GetRpcBehaviorMetadata(context)) { + auto maybe_status = + GetStatusForRpcBehaviorMetadata(rpc_behavior, hostname_); + if (maybe_status.has_value()) { + return *maybe_status; + } + } + response->set_hostname(hostname_); + context->AddInitialMetadata("hostname", hostname_); + return Status::OK; + } + + Status EmptyCall(ServerContext* context, const Empty* /*request*/, + Empty* /*response*/) override { + context->AddInitialMetadata("hostname", hostname_); + return Status::OK; + } + + private: + std::string hostname_; + std::string server_id_; +}; + +class XdsUpdateHealthServiceImpl : public XdsUpdateHealthService::Service { + public: + explicit XdsUpdateHealthServiceImpl( + HealthCheckServiceImpl* health_check_service) + : health_check_service_(health_check_service) {} + + Status SetServing(ServerContext* /* context */, const Empty* /* request */, + Empty* /* response */) override { + health_check_service_->SetAll( + grpc::health::v1::HealthCheckResponse::SERVING); + return Status::OK; + } + + Status SetNotServing(ServerContext* /* context */, const Empty* /* request */, + Empty* /* response */) override { + health_check_service_->SetAll( + grpc::health::v1::HealthCheckResponse::NOT_SERVING); + return Status::OK; + } + + private: + HealthCheckServiceImpl* const health_check_service_; +}; +} // namespace + +absl::optional GetStatusForRpcBehaviorMetadata( + absl::string_view header_value, absl::string_view hostname) { + for (auto part : absl::StrSplit(header_value, ' ')) { + if (absl::ConsumePrefix(&part, kHostnameRpcBehaviorFilter)) { + gpr_log(GPR_INFO, "%s", std::string(part).c_str()); + if (part.empty()) { + return Status( + grpc::StatusCode::INVALID_ARGUMENT, + absl::StrCat("Empty host name in the RPC behavior header: ", + header_value)); + } + if (part != hostname) { + gpr_log( + GPR_DEBUG, + "RPC behavior for a different host: \"%s\", this one is: \"%s\"", + std::string(part).c_str(), std::string(hostname).c_str()); + return absl::nullopt; + } + } else if (absl::ConsumePrefix(&part, kErrorCodeRpcBehavior)) { + grpc::StatusCode code; + if (absl::SimpleAtoi(part, &code)) { + return Status( + code, + absl::StrCat("Rpc failed as per the rpc-behavior header value: ", + header_value)); + } else { + return Status(grpc::StatusCode::INVALID_ARGUMENT, + absl::StrCat("Invalid format for rpc-behavior header: ", + header_value)); + } + } else { + // TODO (eugeneo): Add support for other behaviors as needed + return Status( + grpc::StatusCode::INVALID_ARGUMENT, + absl::StrCat("Unsupported rpc behavior header: ", header_value)); + } + } + return absl::nullopt; +} + +void RunServer(bool secure_mode, const int port, const int maintenance_port, + absl::string_view hostname, absl::string_view server_id) { + std::unique_ptr xds_enabled_server; + std::unique_ptr server; + TestServiceImpl service(hostname, server_id); + HealthCheckServiceImpl health_check_service; + health_check_service.SetStatus( + "", grpc::health::v1::HealthCheckResponse::SERVING); + health_check_service.SetStatus( + "grpc.testing.TestService", + grpc::health::v1::HealthCheckResponse::SERVING); + health_check_service.SetStatus( + "grpc.testing.XdsUpdateHealthService", + grpc::health::v1::HealthCheckResponse::SERVING); + XdsUpdateHealthServiceImpl update_health_service(&health_check_service); + + grpc::reflection::InitProtoReflectionServerBuilderPlugin(); + ServerBuilder builder; + if (secure_mode) { + XdsServerBuilder xds_builder; + xds_builder.RegisterService(&service); + xds_builder.AddListeningPort( + absl::StrCat("0.0.0.0:", port), + grpc::XdsServerCredentials(grpc::InsecureServerCredentials())); + xds_enabled_server = xds_builder.BuildAndStart(); + gpr_log(GPR_INFO, "Server starting on 0.0.0.0:%d", port); + builder.RegisterService(&health_check_service); + builder.RegisterService(&update_health_service); + grpc::AddAdminServices(&builder); + builder.AddListeningPort(absl::StrCat("0.0.0.0:", maintenance_port), + grpc::InsecureServerCredentials()); + server = builder.BuildAndStart(); + gpr_log(GPR_INFO, "Maintenance server listening on 0.0.0.0:%d", + maintenance_port); + } else { + builder.RegisterService(&service); + builder.RegisterService(&health_check_service); + builder.RegisterService(&update_health_service); + grpc::AddAdminServices(&builder); + builder.AddListeningPort(absl::StrCat("0.0.0.0:", port), + grpc::InsecureServerCredentials()); + server = builder.BuildAndStart(); + gpr_log(GPR_INFO, "Server listening on 0.0.0.0:%d", port); + } + + server->Wait(); +} + +} // namespace testing +} // namespace grpc diff --git a/test/cpp/interop/xds_interop_server_lib.h b/test/cpp/interop/xds_interop_server_lib.h new file mode 100644 index 00000000000..e0a23f27d45 --- /dev/null +++ b/test/cpp/interop/xds_interop_server_lib.h @@ -0,0 +1,39 @@ +// +// +// Copyright 2023 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_CPP_INTEROP_XDS_INTEROP_SERVER_LIB_H +#define GRPC_TEST_CPP_INTEROP_XDS_INTEROP_SERVER_LIB_H +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" + +#include + +namespace grpc { +namespace testing { + +// Exposed for the tests +absl::optional GetStatusForRpcBehaviorMetadata( + absl::string_view header_value, absl::string_view hostname); + +void RunServer(bool secure_mode, const int port, const int maintenance_port, + absl::string_view hostname, absl::string_view server_id); + +} // namespace testing +} // namespace grpc + +#endif // GRPC_TEST_CPP_INTEROP_XDS_INTEROP_SERVER_LIB_H diff --git a/test/cpp/interop/xds_interop_server_test.cc b/test/cpp/interop/xds_interop_server_test.cc new file mode 100644 index 00000000000..87c4cd1e0f3 --- /dev/null +++ b/test/cpp/interop/xds_interop_server_test.cc @@ -0,0 +1,83 @@ +// +// Copyright 2022 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include + +#include +#include + +#include + +#include "test/core/util/test_config.h" +#include "test/cpp/interop/xds_interop_server_lib.h" + +namespace grpc { +namespace testing { +namespace { + +TEST(GetRpcBehaviorMetadataTest, ErrorCodeNoFilter) { + auto status = GetStatusForRpcBehaviorMetadata("error-code-16", "hostname"); + ASSERT_TRUE(status.has_value()); + ASSERT_EQ(status->error_code(), 16) << status->error_message(); +} + +TEST(GetRpcBehaviorMetadataTest, ErrorCodeThisHost) { + auto status = GetStatusForRpcBehaviorMetadata( + "hostname=hostname error-code-16", "hostname"); + ASSERT_TRUE(status.has_value()); + ASSERT_EQ(status->error_code(), 16) << status->error_message(); +} + +TEST(GetRpcBehaviorMetadataTest, ErrorCodeOtherHost) { + auto status = GetStatusForRpcBehaviorMetadata( + "hostname=hostname2 error-code-16", "hostname"); + ASSERT_FALSE(status.has_value()); +} + +TEST(GetRpcBehaviorMetadataTest, MalformedErrorCode) { + auto status = GetStatusForRpcBehaviorMetadata("error-code-", "hostname"); + ASSERT_TRUE(status.has_value()); + ASSERT_EQ(status->error_code(), grpc::StatusCode::INVALID_ARGUMENT) + << status->error_message(); +} + +TEST(GetRpcBehaviorMetadataTest, MalformedHostName) { + auto status = + GetStatusForRpcBehaviorMetadata("hostname= error-code-16", "hostname"); + ASSERT_TRUE(status.has_value()); + ASSERT_EQ(status->error_code(), grpc::StatusCode::INVALID_ARGUMENT) + << status->error_message(); +} + +TEST(GetRpcBehaviorMetadataTest, ErrorWhenUnsupported) { + auto status = GetStatusForRpcBehaviorMetadata("unsupported", "hostname"); + ASSERT_TRUE(status.has_value()); + ASSERT_EQ(status->error_code(), grpc::StatusCode::INVALID_ARGUMENT) + << status->error_message(); +} + +} // namespace +} // namespace testing +} // namespace grpc + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + grpc::testing::TestEnvironment env(&argc, argv); + grpc_init(); + auto result = RUN_ALL_TESTS(); + grpc_shutdown(); + return result; +} diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index 563d0eba66e..04e37a84e26 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -11127,6 +11127,30 @@ ], "uses_polling": false }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": true, + "language": "c++", + "name": "xds_interop_server_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": true + }, { "args": [], "benchmark": false,