From f1f557bc43992ade0f09c9240c2d5c71c3478f0a Mon Sep 17 00:00:00 2001 From: yang-g Date: Thu, 6 Dec 2018 16:16:58 -0800 Subject: [PATCH 1/3] Add a Shutdown call to HealthCheckServiceInterface --- .../grpcpp/health_check_service_interface.h | 4 + .../health/default_health_check_service.cc | 18 +++ .../health/default_health_check_service.h | 3 + .../end2end/health_service_end2end_test.cc | 107 ++++++++++++++++++ 4 files changed, 132 insertions(+) diff --git a/include/grpcpp/health_check_service_interface.h b/include/grpcpp/health_check_service_interface.h index b45a699bda9..dfd4c3983af 100644 --- a/include/grpcpp/health_check_service_interface.h +++ b/include/grpcpp/health_check_service_interface.h @@ -37,6 +37,10 @@ class HealthCheckServiceInterface { bool serving) = 0; /// Apply to all registered service names. virtual void SetServingStatus(bool serving) = 0; + + /// Set all registered service names to not serving and prevent future + /// state changes. + virtual void Shutdown() {} }; /// Enable/disable the default health checking service. This applies to all C++ diff --git a/src/cpp/server/health/default_health_check_service.cc b/src/cpp/server/health/default_health_check_service.cc index c951c69d513..db6286d240e 100644 --- a/src/cpp/server/health/default_health_check_service.cc +++ b/src/cpp/server/health/default_health_check_service.cc @@ -42,18 +42,36 @@ DefaultHealthCheckService::DefaultHealthCheckService() { void DefaultHealthCheckService::SetServingStatus( const grpc::string& service_name, bool serving) { std::unique_lock lock(mu_); + if (shutdown_) { + return; + } services_map_[service_name].SetServingStatus(serving ? SERVING : NOT_SERVING); } void DefaultHealthCheckService::SetServingStatus(bool serving) { const ServingStatus status = serving ? SERVING : NOT_SERVING; std::unique_lock lock(mu_); + if (shutdown_) { + return; + } for (auto& p : services_map_) { ServiceData& service_data = p.second; service_data.SetServingStatus(status); } } +void DefaultHealthCheckService::Shutdown() { + std::unique_lock lock(mu_); + if (shutdown_) { + return; + } + shutdown_ = true; + for (auto& p : services_map_) { + ServiceData& service_data = p.second; + service_data.SetServingStatus(NOT_SERVING); + } +} + DefaultHealthCheckService::ServingStatus DefaultHealthCheckService::GetServingStatus( const grpc::string& service_name) const { diff --git a/src/cpp/server/health/default_health_check_service.h b/src/cpp/server/health/default_health_check_service.h index 450bd543f55..9551cd2e2cf 100644 --- a/src/cpp/server/health/default_health_check_service.h +++ b/src/cpp/server/health/default_health_check_service.h @@ -237,6 +237,8 @@ class DefaultHealthCheckService final : public HealthCheckServiceInterface { bool serving) override; void SetServingStatus(bool serving) override; + void Shutdown() override; + ServingStatus GetServingStatus(const grpc::string& service_name) const; HealthCheckServiceImpl* GetHealthCheckService( @@ -272,6 +274,7 @@ class DefaultHealthCheckService final : public HealthCheckServiceInterface { const std::shared_ptr& handler); mutable std::mutex mu_; + bool shutdown_ = false; // Guarded by mu_. std::map services_map_; // Guarded by mu_. std::unique_ptr impl_; }; diff --git a/test/cpp/end2end/health_service_end2end_test.cc b/test/cpp/end2end/health_service_end2end_test.cc index 89c4bef09cf..a439d42b031 100644 --- a/test/cpp/end2end/health_service_end2end_test.cc +++ b/test/cpp/end2end/health_service_end2end_test.cc @@ -90,18 +90,36 @@ class HealthCheckServiceImpl : public ::grpc::health::v1::Health::Service { void SetStatus(const grpc::string& service_name, HealthCheckResponse::ServingStatus status) { std::lock_guard lock(mu_); + if (shutdown_) { + return; + } status_map_[service_name] = status; } void SetAll(HealthCheckResponse::ServingStatus status) { std::lock_guard lock(mu_); + if (shutdown_) { + return; + } for (auto iter = status_map_.begin(); iter != status_map_.end(); ++iter) { iter->second = status; } } + void Shutdown() { + std::lock_guard lock(mu_); + if (shutdown_) { + return; + } + shutdown_ = true; + for (auto iter = status_map_.begin(); iter != status_map_.end(); ++iter) { + iter->second = HealthCheckResponse::NOT_SERVING; + } + } + private: std::mutex mu_; + bool shutdown_ = false; std::map status_map_; }; @@ -125,6 +143,8 @@ class CustomHealthCheckService : public HealthCheckServiceInterface { : HealthCheckResponse::NOT_SERVING); } + void Shutdown() override { impl_->Shutdown(); } + private: HealthCheckServiceImpl* impl_; // not owned }; @@ -260,6 +280,71 @@ class HealthServiceEnd2endTest : public ::testing::Test { context.TryCancel(); } + // Verify that after HealthCheckServiceInterface::Shutdown is called + // 1. unary client will see NOT_SERVING. + // 2. unary client still sees NOT_SERVING after a SetServing(true) is called. + // 3. streaming (Watch) client will see an update. + // This has to be called last. + void VerifyHealthCheckServiceShutdown() { + const grpc::string kServiceName("service_name"); + HealthCheckServiceInterface* service = server_->GetHealthCheckService(); + EXPECT_TRUE(service != nullptr); + const grpc::string kHealthyService("healthy_service"); + const grpc::string kUnhealthyService("unhealthy_service"); + const grpc::string kNotRegisteredService("not_registered"); + service->SetServingStatus(kHealthyService, true); + service->SetServingStatus(kUnhealthyService, false); + + ResetStubs(); + + // Start Watch for service. + ClientContext context; + HealthCheckRequest request; + request.set_service(kServiceName); + std::unique_ptr<::grpc::ClientReaderInterface> reader = + hc_stub_->Watch(&context, request); + + // Initial response will be SERVICE_UNKNOWN. + HealthCheckResponse response; + EXPECT_TRUE(reader->Read(&response)); + EXPECT_EQ(response.SERVICE_UNKNOWN, response.status()); + + // Set service to SERVING and make sure we get an update. + service->SetServingStatus(kServiceName, true); + EXPECT_TRUE(reader->Read(&response)); + EXPECT_EQ(response.SERVING, response.status()); + + SendHealthCheckRpc("", Status::OK, HealthCheckResponse::SERVING); + SendHealthCheckRpc(kHealthyService, Status::OK, + HealthCheckResponse::SERVING); + SendHealthCheckRpc(kUnhealthyService, Status::OK, + HealthCheckResponse::NOT_SERVING); + SendHealthCheckRpc(kNotRegisteredService, + Status(StatusCode::NOT_FOUND, "")); + + // Shutdown health check service. + service->Shutdown(); + + // Watch client gets another update. + EXPECT_TRUE(reader->Read(&response)); + EXPECT_EQ(response.NOT_SERVING, response.status()); + // Finish Watch call. + context.TryCancel(); + + SendHealthCheckRpc("", Status::OK, HealthCheckResponse::NOT_SERVING); + SendHealthCheckRpc(kHealthyService, Status::OK, + HealthCheckResponse::NOT_SERVING); + SendHealthCheckRpc(kUnhealthyService, Status::OK, + HealthCheckResponse::NOT_SERVING); + SendHealthCheckRpc(kNotRegisteredService, + Status(StatusCode::NOT_FOUND, "")); + + // Setting status after Shutdown has no effect. + service->SetServingStatus(kHealthyService, true); + SendHealthCheckRpc(kHealthyService, Status::OK, + HealthCheckResponse::NOT_SERVING); + } + TestServiceImpl echo_test_service_; HealthCheckServiceImpl health_check_service_impl_; std::unique_ptr hc_stub_; @@ -295,6 +380,13 @@ TEST_F(HealthServiceEnd2endTest, DefaultHealthService) { Status(StatusCode::INVALID_ARGUMENT, "")); } +TEST_F(HealthServiceEnd2endTest, DefaultHealthServiceShutdown) { + EnableDefaultHealthCheckService(true); + EXPECT_TRUE(DefaultHealthCheckServiceEnabled()); + SetUpServer(true, false, false, nullptr); + VerifyHealthCheckServiceShutdown(); +} + // Provide an empty service to disable the default service. TEST_F(HealthServiceEnd2endTest, ExplicitlyDisableViaOverride) { EnableDefaultHealthCheckService(true); @@ -326,6 +418,21 @@ TEST_F(HealthServiceEnd2endTest, ExplicitlyOverride) { VerifyHealthCheckServiceStreaming(); } +TEST_F(HealthServiceEnd2endTest, ExplicitlyHealthServiceShutdown) { + EnableDefaultHealthCheckService(true); + EXPECT_TRUE(DefaultHealthCheckServiceEnabled()); + std::unique_ptr override_service( + new CustomHealthCheckService(&health_check_service_impl_)); + HealthCheckServiceInterface* underlying_service = override_service.get(); + SetUpServer(false, false, true, std::move(override_service)); + HealthCheckServiceInterface* service = server_->GetHealthCheckService(); + EXPECT_TRUE(service == underlying_service); + + ResetStubs(); + + VerifyHealthCheckServiceShutdown(); +} + } // namespace } // namespace testing } // namespace grpc From 47233225cafb2e9366e23fb4dd4bafee1005b0ef Mon Sep 17 00:00:00 2001 From: yang-g Date: Thu, 6 Dec 2018 17:12:43 -0800 Subject: [PATCH 2/3] Split out the test service to separate library so that it can be reused --- CMakeLists.txt | 2 + Makefile | 5 + build.yaml | 4 + grpc.gyp | 2 + test/cpp/end2end/BUILD | 13 +++ .../end2end/health_service_end2end_test.cc | 75 +-------------- .../end2end/test_health_check_service_impl.cc | 96 +++++++++++++++++++ .../end2end/test_health_check_service_impl.h | 58 +++++++++++ .../generated/sources_and_headers.json | 6 ++ 9 files changed, 187 insertions(+), 74 deletions(-) create mode 100644 test/cpp/end2end/test_health_check_service_impl.cc create mode 100644 test/cpp/end2end/test_health_check_service_impl.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1194d0072e3..6b02d778d1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4024,6 +4024,7 @@ add_library(grpc++_test_util ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/simple_messages.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/simple_messages.pb.h ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/simple_messages.grpc.pb.h + test/cpp/end2end/test_health_check_service_impl.cc test/cpp/end2end/test_service_impl.cc test/cpp/util/byte_buffer_proto_helper.cc test/cpp/util/channel_trace_proto_helper.cc @@ -4224,6 +4225,7 @@ add_library(grpc++_test_util_unsecure ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/simple_messages.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/simple_messages.pb.h ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/simple_messages.grpc.pb.h + test/cpp/end2end/test_health_check_service_impl.cc test/cpp/end2end/test_service_impl.cc test/cpp/util/byte_buffer_proto_helper.cc test/cpp/util/string_ref_helper.cc diff --git a/Makefile b/Makefile index 7dfce79c922..ed4e219f8b7 100644 --- a/Makefile +++ b/Makefile @@ -6439,6 +6439,7 @@ LIBGRPC++_TEST_UTIL_SRC = \ $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc \ $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc \ $(GENDIR)/src/proto/grpc/testing/simple_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.grpc.pb.cc \ + test/cpp/end2end/test_health_check_service_impl.cc \ test/cpp/end2end/test_service_impl.cc \ test/cpp/util/byte_buffer_proto_helper.cc \ test/cpp/util/channel_trace_proto_helper.cc \ @@ -6591,6 +6592,7 @@ ifneq ($(NO_DEPS),true) -include $(LIBGRPC++_TEST_UTIL_OBJS:.o=.dep) endif endif +$(OBJDIR)/$(CONFIG)/test/cpp/end2end/test_health_check_service_impl.o: $(GENDIR)/src/proto/grpc/channelz/channelz.pb.cc $(GENDIR)/src/proto/grpc/channelz/channelz.grpc.pb.cc $(GENDIR)/src/proto/grpc/health/v1/health.pb.cc $(GENDIR)/src/proto/grpc/health/v1/health.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.grpc.pb.cc $(OBJDIR)/$(CONFIG)/test/cpp/end2end/test_service_impl.o: $(GENDIR)/src/proto/grpc/channelz/channelz.pb.cc $(GENDIR)/src/proto/grpc/channelz/channelz.grpc.pb.cc $(GENDIR)/src/proto/grpc/health/v1/health.pb.cc $(GENDIR)/src/proto/grpc/health/v1/health.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.grpc.pb.cc $(OBJDIR)/$(CONFIG)/test/cpp/util/byte_buffer_proto_helper.o: $(GENDIR)/src/proto/grpc/channelz/channelz.pb.cc $(GENDIR)/src/proto/grpc/channelz/channelz.grpc.pb.cc $(GENDIR)/src/proto/grpc/health/v1/health.pb.cc $(GENDIR)/src/proto/grpc/health/v1/health.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.grpc.pb.cc $(OBJDIR)/$(CONFIG)/test/cpp/util/channel_trace_proto_helper.o: $(GENDIR)/src/proto/grpc/channelz/channelz.pb.cc $(GENDIR)/src/proto/grpc/channelz/channelz.grpc.pb.cc $(GENDIR)/src/proto/grpc/health/v1/health.pb.cc $(GENDIR)/src/proto/grpc/health/v1/health.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.grpc.pb.cc @@ -6607,6 +6609,7 @@ LIBGRPC++_TEST_UTIL_UNSECURE_SRC = \ $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc \ $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc \ $(GENDIR)/src/proto/grpc/testing/simple_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.grpc.pb.cc \ + test/cpp/end2end/test_health_check_service_impl.cc \ test/cpp/end2end/test_service_impl.cc \ test/cpp/util/byte_buffer_proto_helper.cc \ test/cpp/util/string_ref_helper.cc \ @@ -6756,6 +6759,7 @@ ifneq ($(NO_DEPS),true) -include $(LIBGRPC++_TEST_UTIL_UNSECURE_OBJS:.o=.dep) endif endif +$(OBJDIR)/$(CONFIG)/test/cpp/end2end/test_health_check_service_impl.o: $(GENDIR)/src/proto/grpc/health/v1/health.pb.cc $(GENDIR)/src/proto/grpc/health/v1/health.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.grpc.pb.cc $(OBJDIR)/$(CONFIG)/test/cpp/end2end/test_service_impl.o: $(GENDIR)/src/proto/grpc/health/v1/health.pb.cc $(GENDIR)/src/proto/grpc/health/v1/health.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.grpc.pb.cc $(OBJDIR)/$(CONFIG)/test/cpp/util/byte_buffer_proto_helper.o: $(GENDIR)/src/proto/grpc/health/v1/health.pb.cc $(GENDIR)/src/proto/grpc/health/v1/health.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.grpc.pb.cc $(OBJDIR)/$(CONFIG)/test/cpp/util/string_ref_helper.o: $(GENDIR)/src/proto/grpc/health/v1/health.pb.cc $(GENDIR)/src/proto/grpc/health/v1/health.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/simple_messages.grpc.pb.cc @@ -25142,6 +25146,7 @@ test/core/tsi/alts/crypt/gsec_test_util.cc: $(OPENSSL_DEP) test/core/tsi/alts/handshaker/alts_handshaker_service_api_test_lib.cc: $(OPENSSL_DEP) test/core/util/reconnect_server.cc: $(OPENSSL_DEP) test/core/util/test_tcp_server.cc: $(OPENSSL_DEP) +test/cpp/end2end/test_health_check_service_impl.cc: $(OPENSSL_DEP) test/cpp/end2end/test_service_impl.cc: $(OPENSSL_DEP) test/cpp/interop/client.cc: $(OPENSSL_DEP) test/cpp/interop/client_helper.cc: $(OPENSSL_DEP) diff --git a/build.yaml b/build.yaml index 1e63933f555..af70be8459a 100644 --- a/build.yaml +++ b/build.yaml @@ -1755,6 +1755,7 @@ libs: build: private language: c++ headers: + - test/cpp/end2end/test_health_check_service_impl.h - test/cpp/end2end/test_service_impl.h - test/cpp/util/byte_buffer_proto_helper.h - test/cpp/util/channel_trace_proto_helper.h @@ -1769,6 +1770,7 @@ libs: - src/proto/grpc/testing/echo.proto - src/proto/grpc/testing/duplicate/echo_duplicate.proto - src/proto/grpc/testing/simple_messages.proto + - test/cpp/end2end/test_health_check_service_impl.cc - test/cpp/end2end/test_service_impl.cc - test/cpp/util/byte_buffer_proto_helper.cc - test/cpp/util/channel_trace_proto_helper.cc @@ -1789,6 +1791,7 @@ libs: build: private language: c++ headers: + - test/cpp/end2end/test_health_check_service_impl.h - test/cpp/end2end/test_service_impl.h - test/cpp/util/byte_buffer_proto_helper.h - test/cpp/util/string_ref_helper.h @@ -1799,6 +1802,7 @@ libs: - src/proto/grpc/testing/echo.proto - src/proto/grpc/testing/duplicate/echo_duplicate.proto - src/proto/grpc/testing/simple_messages.proto + - test/cpp/end2end/test_health_check_service_impl.cc - test/cpp/end2end/test_service_impl.cc - test/cpp/util/byte_buffer_proto_helper.cc - test/cpp/util/string_ref_helper.cc diff --git a/grpc.gyp b/grpc.gyp index 2b841354baf..564922ff727 100644 --- a/grpc.gyp +++ b/grpc.gyp @@ -1498,6 +1498,7 @@ 'src/proto/grpc/testing/echo.proto', 'src/proto/grpc/testing/duplicate/echo_duplicate.proto', 'src/proto/grpc/testing/simple_messages.proto', + 'test/cpp/end2end/test_health_check_service_impl.cc', 'test/cpp/end2end/test_service_impl.cc', 'test/cpp/util/byte_buffer_proto_helper.cc', 'test/cpp/util/channel_trace_proto_helper.cc', @@ -1522,6 +1523,7 @@ 'src/proto/grpc/testing/echo.proto', 'src/proto/grpc/testing/duplicate/echo_duplicate.proto', 'src/proto/grpc/testing/simple_messages.proto', + 'test/cpp/end2end/test_health_check_service_impl.cc', 'test/cpp/end2end/test_service_impl.cc', 'test/cpp/util/byte_buffer_proto_helper.cc', 'test/cpp/util/string_ref_helper.cc', diff --git a/test/cpp/end2end/BUILD b/test/cpp/end2end/BUILD index 446804401a7..eb600ffb172 100644 --- a/test/cpp/end2end/BUILD +++ b/test/cpp/end2end/BUILD @@ -35,6 +35,18 @@ grpc_cc_library( ], ) +grpc_cc_library( + name = "test_health_check_service_impl", + testonly = True, + srcs = ["test_health_check_service_impl.cc"], + hdrs = ["test_health_check_service_impl.h"], + deps = [ + "//:grpc", + "//:grpc++", + "//src/proto/grpc/health/v1:health_proto", + ], +) + grpc_cc_library( name = "interceptors_util", testonly = True, @@ -283,6 +295,7 @@ grpc_cc_test( "gtest", ], deps = [ + ":test_health_check_service_impl", ":test_service_impl", "//:gpr", "//:grpc", diff --git a/test/cpp/end2end/health_service_end2end_test.cc b/test/cpp/end2end/health_service_end2end_test.cc index a439d42b031..94c92327c87 100644 --- a/test/cpp/end2end/health_service_end2end_test.cc +++ b/test/cpp/end2end/health_service_end2end_test.cc @@ -37,6 +37,7 @@ #include "src/proto/grpc/testing/echo.grpc.pb.h" #include "test/core/util/port.h" #include "test/core/util/test_config.h" +#include "test/cpp/end2end/test_health_check_service_impl.h" #include "test/cpp/end2end/test_service_impl.h" #include @@ -49,80 +50,6 @@ namespace grpc { namespace testing { namespace { -// A sample sync implementation of the health checking service. This does the -// same thing as the default one. -class HealthCheckServiceImpl : public ::grpc::health::v1::Health::Service { - public: - Status Check(ServerContext* context, const HealthCheckRequest* request, - HealthCheckResponse* response) override { - std::lock_guard lock(mu_); - auto iter = status_map_.find(request->service()); - if (iter == status_map_.end()) { - return Status(StatusCode::NOT_FOUND, ""); - } - response->set_status(iter->second); - return Status::OK; - } - - Status Watch(ServerContext* context, const HealthCheckRequest* request, - ::grpc::ServerWriter* writer) override { - auto last_state = HealthCheckResponse::UNKNOWN; - while (!context->IsCancelled()) { - { - std::lock_guard lock(mu_); - HealthCheckResponse response; - auto iter = status_map_.find(request->service()); - if (iter == status_map_.end()) { - response.set_status(response.SERVICE_UNKNOWN); - } else { - response.set_status(iter->second); - } - if (response.status() != last_state) { - writer->Write(response, ::grpc::WriteOptions()); - } - } - gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), - gpr_time_from_millis(1000, GPR_TIMESPAN))); - } - return Status::OK; - } - - void SetStatus(const grpc::string& service_name, - HealthCheckResponse::ServingStatus status) { - std::lock_guard lock(mu_); - if (shutdown_) { - return; - } - status_map_[service_name] = status; - } - - void SetAll(HealthCheckResponse::ServingStatus status) { - std::lock_guard lock(mu_); - if (shutdown_) { - return; - } - for (auto iter = status_map_.begin(); iter != status_map_.end(); ++iter) { - iter->second = status; - } - } - - void Shutdown() { - std::lock_guard lock(mu_); - if (shutdown_) { - return; - } - shutdown_ = true; - for (auto iter = status_map_.begin(); iter != status_map_.end(); ++iter) { - iter->second = HealthCheckResponse::NOT_SERVING; - } - } - - private: - std::mutex mu_; - bool shutdown_ = false; - std::map status_map_; -}; - // A custom implementation of the health checking service interface. This is // used to test that it prevents the server from creating a default service and // also serves as an example of how to override the default service. diff --git a/test/cpp/end2end/test_health_check_service_impl.cc b/test/cpp/end2end/test_health_check_service_impl.cc new file mode 100644 index 00000000000..fa70a44d24f --- /dev/null +++ b/test/cpp/end2end/test_health_check_service_impl.cc @@ -0,0 +1,96 @@ +/* + * + * Copyright 2018 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/end2end/test_health_check_service_impl.h" + +#include + +using grpc::health::v1::HealthCheckRequest; +using grpc::health::v1::HealthCheckResponse; + +namespace grpc { +namespace testing { + +Status HealthCheckServiceImpl::Check(ServerContext* context, + const HealthCheckRequest* request, + HealthCheckResponse* response) { + std::lock_guard lock(mu_); + auto iter = status_map_.find(request->service()); + if (iter == status_map_.end()) { + return Status(StatusCode::NOT_FOUND, ""); + } + response->set_status(iter->second); + return Status::OK; +} +Status HealthCheckServiceImpl::Watch( + ServerContext* context, const HealthCheckRequest* request, + ::grpc::ServerWriter* writer) { + auto last_state = HealthCheckResponse::UNKNOWN; + while (!context->IsCancelled()) { + { + std::lock_guard lock(mu_); + HealthCheckResponse response; + auto iter = status_map_.find(request->service()); + if (iter == status_map_.end()) { + response.set_status(response.SERVICE_UNKNOWN); + } else { + response.set_status(iter->second); + } + if (response.status() != last_state) { + writer->Write(response, ::grpc::WriteOptions()); + } + } + gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), + gpr_time_from_millis(1000, GPR_TIMESPAN))); + } + return Status::OK; +} + +void HealthCheckServiceImpl::SetStatus( + const grpc::string& service_name, + HealthCheckResponse::ServingStatus status) { + std::lock_guard lock(mu_); + if (shutdown_) { + return; + } + status_map_[service_name] = status; +} + +void HealthCheckServiceImpl::SetAll(HealthCheckResponse::ServingStatus status) { + std::lock_guard lock(mu_); + if (shutdown_) { + return; + } + for (auto iter = status_map_.begin(); iter != status_map_.end(); ++iter) { + iter->second = status; + } +} + +void HealthCheckServiceImpl::Shutdown() { + std::lock_guard lock(mu_); + if (shutdown_) { + return; + } + shutdown_ = true; + for (auto iter = status_map_.begin(); iter != status_map_.end(); ++iter) { + iter->second = HealthCheckResponse::NOT_SERVING; + } +} + +} // namespace testing +} // namespace grpc diff --git a/test/cpp/end2end/test_health_check_service_impl.h b/test/cpp/end2end/test_health_check_service_impl.h new file mode 100644 index 00000000000..5d36ce5320a --- /dev/null +++ b/test/cpp/end2end/test_health_check_service_impl.h @@ -0,0 +1,58 @@ +/* + * + * Copyright 2018 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_END2END_TEST_HEALTH_CHECK_SERVICE_IMPL_H +#define GRPC_TEST_CPP_END2END_TEST_HEALTH_CHECK_SERVICE_IMPL_H + +#include +#include + +#include +#include + +#include "src/proto/grpc/health/v1/health.grpc.pb.h" + +namespace grpc { +namespace testing { + +// A sample sync implementation of the health checking service. This does the +// same thing as the default one. +class HealthCheckServiceImpl : public health::v1::Health::Service { + public: + Status Check(ServerContext* context, + const health::v1::HealthCheckRequest* request, + health::v1::HealthCheckResponse* response) override; + Status Watch(ServerContext* context, + const health::v1::HealthCheckRequest* request, + ServerWriter* writer) override; + void SetStatus(const grpc::string& service_name, + health::v1::HealthCheckResponse::ServingStatus status); + void SetAll(health::v1::HealthCheckResponse::ServingStatus status); + + void Shutdown(); + + private: + std::mutex mu_; + bool shutdown_ = false; + std::map + status_map_; +}; + +} // namespace testing +} // namespace grpc + +#endif // GRPC_TEST_CPP_END2END_TEST_HEALTH_CHECK_SERVICE_IMPL_H diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json index a7231554e3d..0a7a4daf7de 100644 --- a/tools/run_tests/generated/sources_and_headers.json +++ b/tools/run_tests/generated/sources_and_headers.json @@ -7447,6 +7447,7 @@ "src/proto/grpc/testing/simple_messages.grpc.pb.h", "src/proto/grpc/testing/simple_messages.pb.h", "src/proto/grpc/testing/simple_messages_mock.grpc.pb.h", + "test/cpp/end2end/test_health_check_service_impl.h", "test/cpp/end2end/test_service_impl.h", "test/cpp/util/byte_buffer_proto_helper.h", "test/cpp/util/channel_trace_proto_helper.h", @@ -7459,6 +7460,8 @@ "language": "c++", "name": "grpc++_test_util", "src": [ + "test/cpp/end2end/test_health_check_service_impl.cc", + "test/cpp/end2end/test_health_check_service_impl.h", "test/cpp/end2end/test_service_impl.cc", "test/cpp/end2end/test_service_impl.h", "test/cpp/util/byte_buffer_proto_helper.cc", @@ -7503,6 +7506,7 @@ "src/proto/grpc/testing/simple_messages.grpc.pb.h", "src/proto/grpc/testing/simple_messages.pb.h", "src/proto/grpc/testing/simple_messages_mock.grpc.pb.h", + "test/cpp/end2end/test_health_check_service_impl.h", "test/cpp/end2end/test_service_impl.h", "test/cpp/util/byte_buffer_proto_helper.h", "test/cpp/util/string_ref_helper.h", @@ -7512,6 +7516,8 @@ "language": "c++", "name": "grpc++_test_util_unsecure", "src": [ + "test/cpp/end2end/test_health_check_service_impl.cc", + "test/cpp/end2end/test_health_check_service_impl.h", "test/cpp/end2end/test_service_impl.cc", "test/cpp/end2end/test_service_impl.h", "test/cpp/util/byte_buffer_proto_helper.cc", From 2246607dedfbad98c3aa4f39997907a203985863 Mon Sep 17 00:00:00 2001 From: yang-g Date: Fri, 7 Dec 2018 08:54:47 -0800 Subject: [PATCH 3/3] Review comments --- .../health/default_health_check_service.cc | 3 ++- .../end2end/health_service_end2end_test.cc | 19 +++++++++++-------- .../end2end/test_health_check_service_impl.cc | 3 ++- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/cpp/server/health/default_health_check_service.cc b/src/cpp/server/health/default_health_check_service.cc index db6286d240e..44aebd2f9d9 100644 --- a/src/cpp/server/health/default_health_check_service.cc +++ b/src/cpp/server/health/default_health_check_service.cc @@ -43,7 +43,8 @@ void DefaultHealthCheckService::SetServingStatus( const grpc::string& service_name, bool serving) { std::unique_lock lock(mu_); if (shutdown_) { - return; + // Set to NOT_SERVING in case service_name is not in the map. + serving = false; } services_map_[service_name].SetServingStatus(serving ? SERVING : NOT_SERVING); } diff --git a/test/cpp/end2end/health_service_end2end_test.cc b/test/cpp/end2end/health_service_end2end_test.cc index 94c92327c87..b96ff53a3ea 100644 --- a/test/cpp/end2end/health_service_end2end_test.cc +++ b/test/cpp/end2end/health_service_end2end_test.cc @@ -211,14 +211,16 @@ class HealthServiceEnd2endTest : public ::testing::Test { // 1. unary client will see NOT_SERVING. // 2. unary client still sees NOT_SERVING after a SetServing(true) is called. // 3. streaming (Watch) client will see an update. + // 4. setting a new service to serving after shutdown will add the service + // name but return NOT_SERVING to client. // This has to be called last. void VerifyHealthCheckServiceShutdown() { - const grpc::string kServiceName("service_name"); HealthCheckServiceInterface* service = server_->GetHealthCheckService(); EXPECT_TRUE(service != nullptr); const grpc::string kHealthyService("healthy_service"); const grpc::string kUnhealthyService("unhealthy_service"); const grpc::string kNotRegisteredService("not_registered"); + const grpc::string kNewService("add_after_shutdown"); service->SetServingStatus(kHealthyService, true); service->SetServingStatus(kUnhealthyService, false); @@ -227,18 +229,12 @@ class HealthServiceEnd2endTest : public ::testing::Test { // Start Watch for service. ClientContext context; HealthCheckRequest request; - request.set_service(kServiceName); + request.set_service(kHealthyService); std::unique_ptr<::grpc::ClientReaderInterface> reader = hc_stub_->Watch(&context, request); - // Initial response will be SERVICE_UNKNOWN. HealthCheckResponse response; EXPECT_TRUE(reader->Read(&response)); - EXPECT_EQ(response.SERVICE_UNKNOWN, response.status()); - - // Set service to SERVING and make sure we get an update. - service->SetServingStatus(kServiceName, true); - EXPECT_TRUE(reader->Read(&response)); EXPECT_EQ(response.SERVING, response.status()); SendHealthCheckRpc("", Status::OK, HealthCheckResponse::SERVING); @@ -248,6 +244,7 @@ class HealthServiceEnd2endTest : public ::testing::Test { HealthCheckResponse::NOT_SERVING); SendHealthCheckRpc(kNotRegisteredService, Status(StatusCode::NOT_FOUND, "")); + SendHealthCheckRpc(kNewService, Status(StatusCode::NOT_FOUND, "")); // Shutdown health check service. service->Shutdown(); @@ -270,6 +267,12 @@ class HealthServiceEnd2endTest : public ::testing::Test { service->SetServingStatus(kHealthyService, true); SendHealthCheckRpc(kHealthyService, Status::OK, HealthCheckResponse::NOT_SERVING); + + // Adding serving status for a new service after shutdown will return + // NOT_SERVING. + service->SetServingStatus(kNewService, true); + SendHealthCheckRpc(kNewService, Status::OK, + HealthCheckResponse::NOT_SERVING); } TestServiceImpl echo_test_service_; diff --git a/test/cpp/end2end/test_health_check_service_impl.cc b/test/cpp/end2end/test_health_check_service_impl.cc index fa70a44d24f..0801e301996 100644 --- a/test/cpp/end2end/test_health_check_service_impl.cc +++ b/test/cpp/end2end/test_health_check_service_impl.cc @@ -37,6 +37,7 @@ Status HealthCheckServiceImpl::Check(ServerContext* context, response->set_status(iter->second); return Status::OK; } + Status HealthCheckServiceImpl::Watch( ServerContext* context, const HealthCheckRequest* request, ::grpc::ServerWriter* writer) { @@ -66,7 +67,7 @@ void HealthCheckServiceImpl::SetStatus( HealthCheckResponse::ServingStatus status) { std::lock_guard lock(mu_); if (shutdown_) { - return; + status = HealthCheckResponse::NOT_SERVING; } status_map_[service_name] = status; }