From 1d59b25dea44fe0bd66b2345e36d5e6205b77b74 Mon Sep 17 00:00:00 2001 From: Eric Gribkoff Date: Mon, 3 Feb 2020 05:11:01 -0800 Subject: [PATCH] Add xDS test client and server --- CMakeLists.txt | 110 +++++++++++ Makefile | 112 ++++++++++++ build.yaml | 34 ++++ src/proto/grpc/testing/messages.proto | 17 ++ src/proto/grpc/testing/test.proto | 7 + test/cpp/interop/BUILD | 30 +++ test/cpp/interop/xds_interop_client.cc | 242 +++++++++++++++++++++++++ test/cpp/interop/xds_interop_server.cc | 96 ++++++++++ 8 files changed, 648 insertions(+) create mode 100644 test/cpp/interop/xds_interop_client.cc create mode 100644 test/cpp/interop/xds_interop_server.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index e247fbbf8d2..df48cd22839 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -899,6 +899,12 @@ if(gRPC_BUILD_TESTS) endif() add_dependencies(buildtests_cxx xds_bootstrap_test) add_dependencies(buildtests_cxx xds_end2end_test) + if(_gRPC_PLATFORM_LINUX) + add_dependencies(buildtests_cxx xds_interop_client) + endif() + if(_gRPC_PLATFORM_LINUX) + add_dependencies(buildtests_cxx xds_interop_server) + endif() add_dependencies(buildtests_cxx bad_streaming_id_bad_client_test) add_dependencies(buildtests_cxx badreq_bad_client_test) add_dependencies(buildtests_cxx connection_prefix_bad_client_test) @@ -16197,6 +16203,110 @@ target_link_libraries(xds_end2end_test ) +endif() +if(gRPC_BUILD_TESTS) +if(_gRPC_PLATFORM_LINUX) + + add_executable(xds_interop_client + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/empty.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/empty.grpc.pb.cc + ${_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/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 + test/cpp/interop/xds_interop_client.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc + ) + + target_include_directories(xds_interop_client + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR} + ${_gRPC_SSL_INCLUDE_DIR} + ${_gRPC_UPB_GENERATED_DIR} + ${_gRPC_UPB_GRPC_GENERATED_DIR} + ${_gRPC_UPB_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_client + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc++_test_config + grpc_test_util + grpc++ + grpc + gpr + ${_gRPC_GFLAGS_LIBRARIES} + ) + + +endif() +endif() +if(gRPC_BUILD_TESTS) +if(_gRPC_PLATFORM_LINUX) + + add_executable(xds_interop_server + ${_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/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 + test/cpp/interop/xds_interop_server.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc + ) + + target_include_directories(xds_interop_server + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR} + ${_gRPC_SSL_INCLUDE_DIR} + ${_gRPC_UPB_GENERATED_DIR} + ${_gRPC_UPB_GRPC_GENERATED_DIR} + ${_gRPC_UPB_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 + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc++_test_config + grpc_test_util + grpc++ + grpc + gpr + ${_gRPC_GFLAGS_LIBRARIES} + ) + + +endif() endif() if(gRPC_BUILD_TESTS) diff --git a/Makefile b/Makefile index 64e6b94befa..f39174eee7a 100644 --- a/Makefile +++ b/Makefile @@ -1311,6 +1311,8 @@ transport_security_common_api_test: $(BINDIR)/$(CONFIG)/transport_security_commo writes_per_rpc_test: $(BINDIR)/$(CONFIG)/writes_per_rpc_test xds_bootstrap_test: $(BINDIR)/$(CONFIG)/xds_bootstrap_test xds_end2end_test: $(BINDIR)/$(CONFIG)/xds_end2end_test +xds_interop_client: $(BINDIR)/$(CONFIG)/xds_interop_client +xds_interop_server: $(BINDIR)/$(CONFIG)/xds_interop_server public_headers_must_be_c89: $(BINDIR)/$(CONFIG)/public_headers_must_be_c89 boringssl_ssl_test: $(BINDIR)/$(CONFIG)/boringssl_ssl_test boringssl_crypto_test: $(BINDIR)/$(CONFIG)/boringssl_crypto_test @@ -1782,6 +1784,8 @@ buildtests_cxx: privatelibs_cxx \ $(BINDIR)/$(CONFIG)/writes_per_rpc_test \ $(BINDIR)/$(CONFIG)/xds_bootstrap_test \ $(BINDIR)/$(CONFIG)/xds_end2end_test \ + $(BINDIR)/$(CONFIG)/xds_interop_client \ + $(BINDIR)/$(CONFIG)/xds_interop_server \ $(BINDIR)/$(CONFIG)/boringssl_ssl_test \ $(BINDIR)/$(CONFIG)/boringssl_crypto_test \ $(BINDIR)/$(CONFIG)/bad_streaming_id_bad_client_test \ @@ -1958,6 +1962,8 @@ buildtests_cxx: privatelibs_cxx \ $(BINDIR)/$(CONFIG)/writes_per_rpc_test \ $(BINDIR)/$(CONFIG)/xds_bootstrap_test \ $(BINDIR)/$(CONFIG)/xds_end2end_test \ + $(BINDIR)/$(CONFIG)/xds_interop_client \ + $(BINDIR)/$(CONFIG)/xds_interop_server \ $(BINDIR)/$(CONFIG)/bad_streaming_id_bad_client_test \ $(BINDIR)/$(CONFIG)/badreq_bad_client_test \ $(BINDIR)/$(CONFIG)/connection_prefix_bad_client_test \ @@ -20822,6 +20828,112 @@ endif $(OBJDIR)/$(CONFIG)/test/cpp/end2end/xds_end2end_test.o: $(GENDIR)/src/proto/grpc/testing/xds/ads_for_test.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/ads_for_test.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/cds_for_test.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/cds_for_test.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/eds_for_test.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/eds_for_test.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/lds_rds_for_test.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/lds_rds_for_test.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/lrs_for_test.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/lrs_for_test.grpc.pb.cc +XDS_INTEROP_CLIENT_SRC = \ + $(GENDIR)/src/proto/grpc/testing/empty.pb.cc $(GENDIR)/src/proto/grpc/testing/empty.grpc.pb.cc \ + $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc \ + $(GENDIR)/src/proto/grpc/testing/test.pb.cc $(GENDIR)/src/proto/grpc/testing/test.grpc.pb.cc \ + test/cpp/interop/xds_interop_client.cc \ + +XDS_INTEROP_CLIENT_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(XDS_INTEROP_CLIENT_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/xds_interop_client: openssl_dep_error + +else + + + + +ifeq ($(NO_PROTOBUF),true) + +# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+. + +$(BINDIR)/$(CONFIG)/xds_interop_client: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/xds_interop_client: $(PROTOBUF_DEP) $(XDS_INTEROP_CLIENT_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(XDS_INTEROP_CLIENT_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/xds_interop_client + +endif + +endif + +$(OBJDIR)/$(CONFIG)/src/proto/grpc/testing/empty.o: $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a + +$(OBJDIR)/$(CONFIG)/src/proto/grpc/testing/messages.o: $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a + +$(OBJDIR)/$(CONFIG)/src/proto/grpc/testing/test.o: $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a + +$(OBJDIR)/$(CONFIG)/test/cpp/interop/xds_interop_client.o: $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a + +deps_xds_interop_client: $(XDS_INTEROP_CLIENT_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(XDS_INTEROP_CLIENT_OBJS:.o=.dep) +endif +endif +$(OBJDIR)/$(CONFIG)/test/cpp/interop/xds_interop_client.o: $(GENDIR)/src/proto/grpc/testing/empty.pb.cc $(GENDIR)/src/proto/grpc/testing/empty.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/test.pb.cc $(GENDIR)/src/proto/grpc/testing/test.grpc.pb.cc + + +XDS_INTEROP_SERVER_SRC = \ + $(GENDIR)/src/proto/grpc/testing/empty.pb.cc $(GENDIR)/src/proto/grpc/testing/empty.grpc.pb.cc \ + $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc \ + $(GENDIR)/src/proto/grpc/testing/test.pb.cc $(GENDIR)/src/proto/grpc/testing/test.grpc.pb.cc \ + test/cpp/interop/xds_interop_server.cc \ + +XDS_INTEROP_SERVER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(XDS_INTEROP_SERVER_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/xds_interop_server: openssl_dep_error + +else + + + + +ifeq ($(NO_PROTOBUF),true) + +# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+. + +$(BINDIR)/$(CONFIG)/xds_interop_server: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/xds_interop_server: $(PROTOBUF_DEP) $(XDS_INTEROP_SERVER_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(XDS_INTEROP_SERVER_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/xds_interop_server + +endif + +endif + +$(OBJDIR)/$(CONFIG)/src/proto/grpc/testing/empty.o: $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a + +$(OBJDIR)/$(CONFIG)/src/proto/grpc/testing/messages.o: $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a + +$(OBJDIR)/$(CONFIG)/src/proto/grpc/testing/test.o: $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a + +$(OBJDIR)/$(CONFIG)/test/cpp/interop/xds_interop_server.o: $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a + +deps_xds_interop_server: $(XDS_INTEROP_SERVER_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(XDS_INTEROP_SERVER_OBJS:.o=.dep) +endif +endif +$(OBJDIR)/$(CONFIG)/test/cpp/interop/xds_interop_server.o: $(GENDIR)/src/proto/grpc/testing/empty.pb.cc $(GENDIR)/src/proto/grpc/testing/empty.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/test.pb.cc $(GENDIR)/src/proto/grpc/testing/test.grpc.pb.cc + + PUBLIC_HEADERS_MUST_BE_C89_SRC = \ test/core/surface/public_headers_must_be_c89.c \ diff --git a/build.yaml b/build.yaml index e05043d9ff5..f23d9b14854 100644 --- a/build.yaml +++ b/build.yaml @@ -6133,6 +6133,40 @@ targets: - grpc++ - grpc - gpr +- name: xds_interop_client + build: test + run: false + language: c++ + src: + - src/proto/grpc/testing/empty.proto + - src/proto/grpc/testing/messages.proto + - src/proto/grpc/testing/test.proto + - test/cpp/interop/xds_interop_client.cc + deps: + - grpc++_test_config + - grpc_test_util + - grpc++ + - grpc + - gpr + platforms: + - linux +- name: xds_interop_server + build: test + run: false + language: c++ + src: + - src/proto/grpc/testing/empty.proto + - src/proto/grpc/testing/messages.proto + - src/proto/grpc/testing/test.proto + - test/cpp/interop/xds_interop_server.cc + deps: + - grpc++_test_config + - grpc_test_util + - grpc++ + - grpc + - gpr + platforms: + - linux - name: public_headers_must_be_c89 build: test language: c89 diff --git a/src/proto/grpc/testing/messages.proto b/src/proto/grpc/testing/messages.proto index cda7753c7aa..5993bc6bf14 100644 --- a/src/proto/grpc/testing/messages.proto +++ b/src/proto/grpc/testing/messages.proto @@ -115,6 +115,9 @@ message SimpleResponse { string server_id = 4; // gRPCLB Path. GrpclbRouteType grpclb_route_type = 5; + + // Server hostname. + string hostname = 6; } // Client-streaming request. @@ -190,3 +193,17 @@ message ReconnectInfo { bool passed = 1; repeated int32 backoff_ms = 2; } + +message LoadBalancerStatsRequest { + // Request stats for the next num_rpcs sent by client. + int32 num_rpcs = 1; + // If num_rpcs have not completed within timeout_sec, return partial results. + int32 timeout_sec = 2; +} + +message LoadBalancerStatsResponse { + // The number of completed RPCs for each peer. + map rpcs_by_peer = 1; + // The number of RPCs that failed to record a remote peer. + int32 num_failures = 2; +} diff --git a/src/proto/grpc/testing/test.proto b/src/proto/grpc/testing/test.proto index c049c8fa079..0b198d8c260 100644 --- a/src/proto/grpc/testing/test.proto +++ b/src/proto/grpc/testing/test.proto @@ -77,3 +77,10 @@ service ReconnectService { rpc Start(grpc.testing.ReconnectParams) returns (grpc.testing.Empty); rpc Stop(grpc.testing.Empty) returns (grpc.testing.ReconnectInfo); } + +// A service used to obtain stats for verifying LB behavior. +service LoadBalancerStatsService { + // Gets the backend distribution for RPCs sent by a test client. + rpc GetClientStats(LoadBalancerStatsRequest) + returns (LoadBalancerStatsResponse) {} +} diff --git a/test/cpp/interop/BUILD b/test/cpp/interop/BUILD index d5141734fd0..c73305d18a5 100644 --- a/test/cpp/interop/BUILD +++ b/test/cpp/interop/BUILD @@ -197,3 +197,33 @@ grpc_cc_test( "//test/cpp/util:test_util", ], ) + +grpc_cc_binary( + name = "xds_interop_client", + srcs = [ + "xds_interop_client.cc", + ], + deps = [ + "//src/proto/grpc/testing:empty_proto", + "//src/proto/grpc/testing:messages_proto", + "//src/proto/grpc/testing:test_proto", + "//:grpc++", + "//test/core/util:grpc_test_util", + "//test/cpp/util:test_config", + ], +) + +grpc_cc_binary( + name = "xds_interop_server", + srcs = [ + "xds_interop_server.cc", + ], + deps = [ + "//src/proto/grpc/testing:empty_proto", + "//src/proto/grpc/testing:messages_proto", + "//src/proto/grpc/testing:test_proto", + "//:grpc++", + "//test/core/util:grpc_test_util", + "//test/cpp/util:test_config", + ], +) diff --git a/test/cpp/interop/xds_interop_client.cc b/test/cpp/interop/xds_interop_client.cc new file mode 100644 index 00000000000..69d7c64b865 --- /dev/null +++ b/test/cpp/interop/xds_interop_client.cc @@ -0,0 +1,242 @@ +/* + * + * Copyright 2020 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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/util/test_config.h" + +DEFINE_int32(num_channels, 1, "Number of channels."); +DEFINE_bool(print_response, false, "Write RPC response to stdout."); +DEFINE_int32(qps, 1, "Qps per channel."); +DEFINE_int32(rpc_timeout_sec, 10, "Per RPC timeout seconds."); +DEFINE_string(server, "localhost:50051", "Address of server."); +DEFINE_int32(stats_port, 50052, + "Port to expose peer distribution stats service."); + +using grpc::Channel; +using grpc::ClientContext; +using grpc::Server; +using grpc::ServerBuilder; +using grpc::ServerContext; +using grpc::ServerCredentials; +using grpc::ServerReader; +using grpc::ServerReaderWriter; +using grpc::ServerWriter; +using grpc::Status; +using grpc::testing::LoadBalancerStatsRequest; +using grpc::testing::LoadBalancerStatsResponse; +using grpc::testing::LoadBalancerStatsService; +using grpc::testing::SimpleRequest; +using grpc::testing::SimpleResponse; +using grpc::testing::TestService; + +class XdsStatsWatcher; + +// Unique ID for each outgoing RPC +int global_request_id; +// Stores a set of watchers that should be notified upon outgoing RPC completion +std::set watchers; +// Mutex for global_request_id and watchers +std::mutex mu; + +/** Records the remote peer distribution for a given range of RPCs. */ +class XdsStatsWatcher { + public: + XdsStatsWatcher(int start_id, int end_id) + : start_id_(start_id), end_id_(end_id), rpcs_needed_(end_id - start_id) {} + + void RpcCompleted(int request_id, std::string peer) { + if (start_id_ <= request_id && request_id < end_id_) { + { + std::lock_guard lk(m_); + if (peer.empty()) { + no_remote_peer_++; + } else { + rpcs_by_peer_[peer]++; + } + rpcs_needed_--; + } + cv_.notify_one(); + } + } + + void WaitForRpcStatsResponse(LoadBalancerStatsResponse* response, + int timeout_sec) { + { + std::unique_lock lk(m_); + cv_.wait_for(lk, std::chrono::seconds(timeout_sec), + [this] { return rpcs_needed_ == 0; }); + response->mutable_rpcs_by_peer()->insert(rpcs_by_peer_.begin(), + rpcs_by_peer_.end()); + response->set_num_failures(no_remote_peer_ + rpcs_needed_); + } + } + + private: + int start_id_; + int end_id_; + int rpcs_needed_; + std::map rpcs_by_peer_; + int no_remote_peer_; + std::mutex m_; + std::condition_variable cv_; +}; + +class TestClient { + public: + TestClient(std::shared_ptr channel) + : stub_(TestService::NewStub(channel)) {} + + void UnaryCall() { + SimpleResponse response; + ClientContext context; + + int saved_request_id; + { + std::lock_guard lk(mu); + saved_request_id = ++global_request_id; + } + std::chrono::system_clock::time_point deadline = + std::chrono::system_clock::now() + + std::chrono::seconds(FLAGS_rpc_timeout_sec); + context.set_deadline(deadline); + Status status = stub_->UnaryCall( + &context, SimpleRequest::default_instance(), &response); + + { + std::lock_guard lk(mu); + for (auto watcher : watchers) { + watcher->RpcCompleted(saved_request_id, response.hostname()); + } + } + + if (FLAGS_print_response) { + if (status.ok()) { + std::cout << "Greeting: Hello world, this is " << response.hostname() + << ", from " << context.peer() << std::endl; + } else { + std::cout << "RPC failed: " << status.error_code() << ": " + << status.error_message() << std::endl; + } + } + } + + private: + std::unique_ptr stub_; +}; + +class LoadBalancerStatsServiceImpl : public LoadBalancerStatsService::Service { + public: + Status GetClientStats(ServerContext* context, + const LoadBalancerStatsRequest* request, + LoadBalancerStatsResponse* response) { + int start_id; + int end_id; + XdsStatsWatcher* watcher; + { + std::lock_guard lk(mu); + start_id = global_request_id + 1; + end_id = start_id + request->num_rpcs(); + watcher = new XdsStatsWatcher(start_id, end_id); + watchers.insert(watcher); + } + watcher->WaitForRpcStatsResponse(response, request->timeout_sec()); + { + std::lock_guard lk(mu); + watchers.erase(watcher); + } + delete watcher; + return Status::OK; + } +}; + +void RunTestLoop(std::string server, + std::chrono::duration duration_per_query) { + TestClient client( + grpc::CreateChannel(server, grpc::InsecureChannelCredentials())); + std::chrono::time_point start = + std::chrono::system_clock::now(); + std::chrono::duration elapsed; + + while (true) { + elapsed = std::chrono::system_clock::now() - start; + if (elapsed > duration_per_query) { + start = std::chrono::system_clock::now(); + client.UnaryCall(); + } + } +} + +void RunServer(const int port) { + GPR_ASSERT(port != 0); + std::ostringstream server_address; + server_address << "0.0.0.0:" << port; + + LoadBalancerStatsServiceImpl service; + + ServerBuilder builder; + builder.RegisterService(&service); + builder.AddListeningPort(server_address.str(), + grpc::InsecureServerCredentials()); + std::unique_ptr server(builder.BuildAndStart()); + gpr_log(GPR_INFO, "Stats server listening on %s", + server_address.str().c_str()); + + server->Wait(); +} + +int main(int argc, char** argv) { + grpc::testing::TestEnvironment env(argc, argv); + grpc::testing::InitTest(&argc, &argv, true); + + std::chrono::duration duration_per_query = + std::chrono::nanoseconds(std::chrono::seconds(1)) / FLAGS_qps; + + std::vector test_threads; + + for (int i = 0; i < FLAGS_num_channels; i++) { + test_threads.emplace_back( + std::thread(&RunTestLoop, FLAGS_server, duration_per_query)); + } + + RunServer(FLAGS_stats_port); + + for (auto it = test_threads.begin(); it != test_threads.end(); it++) { + it->join(); + } + + return 0; +} diff --git a/test/cpp/interop/xds_interop_server.cc b/test/cpp/interop/xds_interop_server.cc new file mode 100644 index 00000000000..f5d8273c3f8 --- /dev/null +++ b/test/cpp/interop/xds_interop_server.cc @@ -0,0 +1,96 @@ +/* + * + * Copyright 2020 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/gethostname.h" +#include "src/core/lib/transport/byte_stream.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/util/test_config.h" + +DEFINE_int32(port, 50051, "Server port."); +DEFINE_string(server_id, "cpp_server", "Server ID to include in responses."); + +using grpc::Server; +using grpc::ServerBuilder; +using grpc::ServerContext; +using grpc::ServerCredentials; +using grpc::ServerReader; +using grpc::ServerReaderWriter; +using grpc::ServerWriter; +using grpc::Status; +using grpc::testing::SimpleRequest; +using grpc::testing::SimpleResponse; +using grpc::testing::TestService; + +class TestServiceImpl : public TestService::Service { + std::string hostname; + + public: + TestServiceImpl(std::string i) : hostname(i) {} + + Status UnaryCall(ServerContext* context, const SimpleRequest* request, + SimpleResponse* response) { + response->set_server_id(FLAGS_server_id); + response->set_hostname(hostname); + return Status::OK; + } +}; + +void RunServer(const int port, std::string hostname) { + GPR_ASSERT(port != 0); + std::ostringstream server_address; + server_address << "0.0.0.0:" << port; + + TestServiceImpl service(hostname); + ServerBuilder builder; + builder.RegisterService(&service); + builder.AddListeningPort(server_address.str(), + grpc::InsecureServerCredentials()); + std::unique_ptr server(builder.BuildAndStart()); + gpr_log(GPR_INFO, "Server listening on %s", server_address.str().c_str()); + + server->Wait(); +} + +int main(int argc, char** argv) { + grpc::testing::TestEnvironment env(argc, argv); + grpc::testing::InitTest(&argc, &argv, true); + + char* hostname = grpc_gethostname(); + if (hostname == nullptr) { + std::cout << "Failed to get hostname, aborting test" << std::endl; + return 1; + } + + RunServer(FLAGS_port, hostname); + + return 0; +}