From b457f4322755aaf27cf95d6c442f86b908746814 Mon Sep 17 00:00:00 2001 From: Lidi Zheng Date: Tue, 23 Mar 2021 09:53:12 -0700 Subject: [PATCH] Implement C++ Admin Interface API (#25753) * Implement C++ Admin Interface API * Address reviewer's requests * Remove static asserts for raw pointers * Make sanity tests happy * Windows: pacify conflict between ifndef and macros * Disable admin services test on iOS * Make iOS happy by: * Letting grpcpp_admin conditionally depend on grpcpp_csds * Fix an unexpected side-effect of dependency update --- BUILD | 28 ++++- CMakeLists.txt | 56 ++++++++++ build_autogenerated.yaml | 18 +++ include/grpcpp/ext/admin_services.h | 33 ++++++ src/cpp/server/admin/admin_services.cc | 52 +++++++++ test/cpp/end2end/BUILD | 15 +++ .../end2end/admin_services_end2end_test.cc | 103 ++++++++++++++++++ .../linux/grpc_bazel_test_in_docker.sh | 1 + tools/run_tests/generated/tests.json | 24 ++++ 9 files changed, 329 insertions(+), 1 deletion(-) create mode 100644 include/grpcpp/ext/admin_services.h create mode 100644 src/cpp/server/admin/admin_services.cc create mode 100644 test/cpp/end2end/admin_services_end2end_test.cc diff --git a/BUILD b/BUILD index c17ebce5361..2e8ab829341 100644 --- a/BUILD +++ b/BUILD @@ -2623,11 +2623,37 @@ grpc_cc_library_xds( "src/cpp/server/csds/csds.h", ], language = "c++", + deps = [ + ":grpc++_internals", + "//src/proto/grpc/testing/xds/v3:csds_proto", + ], + alwayslink = 1, +) + +grpc_cc_library( + name = "grpcpp_admin", + srcs = [ + "src/cpp/server/admin/admin_services.cc", + ], + hdrs = [], + defines = select({ + "grpc_no_xds": ["GRPC_NO_XDS"], + "//conditions:default": [], + }), + external_deps = [ + "absl/memory", + ], + language = "c++", public_hdrs = [ + "include/grpcpp/ext/admin_services.h", ], + select_deps = { + "grpc_no_xds": [], + "//conditions:default": ["//:grpcpp_csds"], + }, deps = [ ":grpc++", - "//src/proto/grpc/testing/xds/v3:csds_proto", + ":grpcpp_channelz", ], alwayslink = 1, ) diff --git a/CMakeLists.txt b/CMakeLists.txt index 90d871bee8a..f463115ee6f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -726,6 +726,7 @@ if(gRPC_BUILD_TESTS) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_cxx address_sorting_test_unsecure) endif() + add_dependencies(buildtests_cxx admin_services_end2end_test) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_cxx alarm_test) endif() @@ -7755,6 +7756,61 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) endif() +endif() +if(gRPC_BUILD_TESTS) + +add_executable(admin_services_end2end_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/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/admin_services_end2end_test.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc +) + +target_include_directories(admin_services_end2end_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(admin_services_end2end_test + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc++_reflection + grpcpp_channelz + grpc++_test_util +) + + endif() if(gRPC_BUILD_TESTS) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index 0d7a96ea72d..f1dfdaca646 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -4131,6 +4131,24 @@ targets: - linux - posix - mac +- name: admin_services_end2end_test + gtest: true + build: test + language: c++ + headers: + - src/cpp/server/csds/csds.h + src: + - 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/admin_services_end2end_test.cc + deps: + - grpc++_reflection + - grpcpp_channelz + - grpc++_test_util - name: alarm_test gtest: true build: test diff --git a/include/grpcpp/ext/admin_services.h b/include/grpcpp/ext/admin_services.h new file mode 100644 index 00000000000..898a87e5f0a --- /dev/null +++ b/include/grpcpp/ext/admin_services.h @@ -0,0 +1,33 @@ +// +// +// Copyright 2021 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 GRPCPP_EXT_ADMIN_SERVICES_H +#define GRPCPP_EXT_ADMIN_SERVICES_H + +#include + +namespace grpc { + +// Registers admin services to the given ServerBuilder. This function will add +// admin services based on build time dependencies, for example, it only adds +// CSDS service if xDS is enabled in this binary. +void AddAdminServices(grpc::ServerBuilder* builder); + +} // namespace grpc + +#endif // GRPCPP_EXT_ADMIN_SERVICES_H diff --git a/src/cpp/server/admin/admin_services.cc b/src/cpp/server/admin/admin_services.cc new file mode 100644 index 00000000000..3db355ab75f --- /dev/null +++ b/src/cpp/server/admin/admin_services.cc @@ -0,0 +1,52 @@ +// +// +// Copyright 2021 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 "absl/memory/memory.h" + +#include +#include + +#include + +// TODO(lidiz) build a real registration system that can pull in services +// automatically with minimum amount of code. +#include "src/cpp/server/channelz/channelz_service.h" +#ifndef GRPC_NO_XDS +#include "src/cpp/server/csds/csds.h" +#endif // GRPC_NO_XDS +namespace grpc { + +namespace { + +static auto* g_channelz_service = new ChannelzService(); +#ifndef GRPC_NO_XDS +static auto* g_csds = new xds::experimental::ClientStatusDiscoveryService(); +#endif // GRPC_NO_XDS + +} // namespace + +void AddAdminServices(ServerBuilder* builder) { + builder->RegisterService(g_channelz_service); +#ifndef GRPC_NO_XDS + builder->RegisterService(g_csds); +#endif // GRPC_NO_XDS +} + +} // namespace grpc diff --git a/test/cpp/end2end/BUILD b/test/cpp/end2end/BUILD index 1943556aaa9..4afead2e888 100644 --- a/test/cpp/end2end/BUILD +++ b/test/cpp/end2end/BUILD @@ -873,3 +873,18 @@ grpc_cc_test( "//test/cpp/util:test_util", ], ) + +grpc_cc_test( + name = "admin_services_end2end_test", + srcs = ["admin_services_end2end_test.cc"], + external_deps = [ + "gtest", + ], + deps = [ + "//:grpc++", + "//:grpc++_reflection", + "//:grpcpp_admin", + "//test/core/util:grpc_test_util", + "//test/cpp/util:test_util", + ], +) diff --git a/test/cpp/end2end/admin_services_end2end_test.cc b/test/cpp/end2end/admin_services_end2end_test.cc new file mode 100644 index 00000000000..6e4c6fbfeb7 --- /dev/null +++ b/test/cpp/end2end/admin_services_end2end_test.cc @@ -0,0 +1,103 @@ +// +// +// Copyright 2021 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 "absl/strings/str_cat.h" + +#include +#include +#include + +#include "src/proto/grpc/reflection/v1alpha/reflection.grpc.pb.h" +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" + +namespace grpc { +namespace testing { + +class AdminServicesTest : public ::testing::Test { + public: + void SetUp() override { + std::string address = + absl::StrCat("localhost:", grpc_pick_unused_port_or_die()); + // Create admin server + grpc::reflection::InitProtoReflectionServerBuilderPlugin(); + ServerBuilder builder; + builder.AddListeningPort(address, InsecureServerCredentials()); + ::grpc::AddAdminServices(&builder); + server_ = builder.BuildAndStart(); + // Create channel + auto reflection_stub = reflection::v1alpha::ServerReflection::NewStub( + CreateChannel(address, InsecureChannelCredentials())); + stream_ = reflection_stub->ServerReflectionInfo(&reflection_ctx_); + } + + std::vector GetServiceList() { + std::vector services; + reflection::v1alpha::ServerReflectionRequest request; + reflection::v1alpha::ServerReflectionResponse response; + request.set_list_services(""); + stream_->Write(request); + stream_->Read(&response); + for (auto& service : response.list_services_response().service()) { + services.push_back(service.name()); + } + return services; + } + + private: + std::unique_ptr server_; + ClientContext reflection_ctx_; + std::shared_ptr< + ClientReaderWriter> + stream_; +}; + +#ifndef GRPC_NO_XDS +// The ifndef conflicts with TEST_F and EXPECT_THAT macros, so we better isolate +// the condition at test case level. +TEST_F(AdminServicesTest, XdsEnabled) { + EXPECT_THAT(GetServiceList(), + ::testing::UnorderedElementsAre( + "envoy.service.status.v3.ClientStatusDiscoveryService", + "grpc.channelz.v1.Channelz", + "grpc.reflection.v1alpha.ServerReflection")); +} +#endif // GRPC_NO_XDS + +#ifdef GRPC_NO_XDS +TEST_F(AdminServicesTest, XdsDisabled) { + EXPECT_THAT(GetServiceList(), + ::testing::UnorderedElementsAre( + "grpc.channelz.v1.Channelz", + "grpc.reflection.v1alpha.ServerReflection")); +} +#endif // GRPC_NO_XDS + +} // namespace testing +} // namespace grpc + +int main(int argc, char** argv) { + grpc::testing::TestEnvironment env(argc, argv); + ::testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + return ret; +} diff --git a/tools/internal_ci/linux/grpc_bazel_test_in_docker.sh b/tools/internal_ci/linux/grpc_bazel_test_in_docker.sh index f65ac7a7e79..639a0855ae1 100755 --- a/tools/internal_ci/linux/grpc_bazel_test_in_docker.sh +++ b/tools/internal_ci/linux/grpc_bazel_test_in_docker.sh @@ -26,3 +26,4 @@ ${name}') cd /var/local/git/grpc bazel test //test/... +bazel test //test/cpp/end2end:admin_services_end2end_test --define=grpc_no_xds=true diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index 250c02d8b64..d902312b13e 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -3171,6 +3171,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": "admin_services_end2end_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": true + }, { "args": [], "benchmark": false,