1 /*
2 *
3 * Copyright 2016 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 #include <memory>
20 #include <mutex>
21 #include <thread>
22 #include <vector>
23
24 #include <grpc/grpc.h>
25 #include <grpc/support/log.h>
26 #include <grpcpp/channel.h>
27 #include <grpcpp/client_context.h>
28 #include <grpcpp/create_channel.h>
29 #include <grpcpp/ext/health_check_service_server_builder_option.h>
30 #include <grpcpp/health_check_service_interface.h>
31 #include <grpcpp/server.h>
32 #include <grpcpp/server_builder.h>
33 #include <grpcpp/server_context.h>
34
35 #include "src/proto/grpc/health/v1/health.grpc.pb.h"
36 #include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
37 #include "src/proto/grpc/testing/echo.grpc.pb.h"
38 #include "test/core/util/port.h"
39 #include "test/core/util/test_config.h"
40 #include "test/cpp/end2end/test_service_impl.h"
41
42 #include <gtest/gtest.h>
43
44 using grpc::health::v1::Health;
45 using grpc::health::v1::HealthCheckRequest;
46 using grpc::health::v1::HealthCheckResponse;
47
48 namespace grpc {
49 namespace testing {
50 namespace {
51
52 // A sample sync implementation of the health checking service. This does the
53 // same thing as the default one.
54 class HealthCheckServiceImpl : public ::grpc::health::v1::Health::Service {
55 public:
Check(ServerContext * context,const HealthCheckRequest * request,HealthCheckResponse * response)56 Status Check(ServerContext* context, const HealthCheckRequest* request,
57 HealthCheckResponse* response) override {
58 std::lock_guard<std::mutex> lock(mu_);
59 auto iter = status_map_.find(request->service());
60 if (iter == status_map_.end()) {
61 return Status(StatusCode::NOT_FOUND, "");
62 }
63 response->set_status(iter->second);
64 return Status::OK;
65 }
66
SetStatus(const grpc::string & service_name,HealthCheckResponse::ServingStatus status)67 void SetStatus(const grpc::string& service_name,
68 HealthCheckResponse::ServingStatus status) {
69 std::lock_guard<std::mutex> lock(mu_);
70 status_map_[service_name] = status;
71 }
72
SetAll(HealthCheckResponse::ServingStatus status)73 void SetAll(HealthCheckResponse::ServingStatus status) {
74 std::lock_guard<std::mutex> lock(mu_);
75 for (auto iter = status_map_.begin(); iter != status_map_.end(); ++iter) {
76 iter->second = status;
77 }
78 }
79
80 private:
81 std::mutex mu_;
82 std::map<const grpc::string, HealthCheckResponse::ServingStatus> status_map_;
83 };
84
85 // A custom implementation of the health checking service interface. This is
86 // used to test that it prevents the server from creating a default service and
87 // also serves as an example of how to override the default service.
88 class CustomHealthCheckService : public HealthCheckServiceInterface {
89 public:
CustomHealthCheckService(HealthCheckServiceImpl * impl)90 explicit CustomHealthCheckService(HealthCheckServiceImpl* impl)
91 : impl_(impl) {
92 impl_->SetStatus("", HealthCheckResponse::SERVING);
93 }
SetServingStatus(const grpc::string & service_name,bool serving)94 void SetServingStatus(const grpc::string& service_name,
95 bool serving) override {
96 impl_->SetStatus(service_name, serving ? HealthCheckResponse::SERVING
97 : HealthCheckResponse::NOT_SERVING);
98 }
99
SetServingStatus(bool serving)100 void SetServingStatus(bool serving) override {
101 impl_->SetAll(serving ? HealthCheckResponse::SERVING
102 : HealthCheckResponse::NOT_SERVING);
103 }
104
105 private:
106 HealthCheckServiceImpl* impl_; // not owned
107 };
108
LoopCompletionQueue(ServerCompletionQueue * cq)109 void LoopCompletionQueue(ServerCompletionQueue* cq) {
110 void* tag;
111 bool ok;
112 while (cq->Next(&tag, &ok)) {
113 abort(); // Nothing should come out of the cq.
114 }
115 }
116
117 class HealthServiceEnd2endTest : public ::testing::Test {
118 protected:
HealthServiceEnd2endTest()119 HealthServiceEnd2endTest() {}
120
SetUpServer(bool register_sync_test_service,bool add_async_cq,bool explicit_health_service,std::unique_ptr<HealthCheckServiceInterface> service)121 void SetUpServer(bool register_sync_test_service, bool add_async_cq,
122 bool explicit_health_service,
123 std::unique_ptr<HealthCheckServiceInterface> service) {
124 int port = grpc_pick_unused_port_or_die();
125 server_address_ << "localhost:" << port;
126
127 bool register_sync_health_service_impl =
128 explicit_health_service && service != nullptr;
129
130 // Setup server
131 ServerBuilder builder;
132 if (explicit_health_service) {
133 std::unique_ptr<ServerBuilderOption> option(
134 new HealthCheckServiceServerBuilderOption(std::move(service)));
135 builder.SetOption(std::move(option));
136 }
137 builder.AddListeningPort(server_address_.str(),
138 grpc::InsecureServerCredentials());
139 if (register_sync_test_service) {
140 // Register a sync service.
141 builder.RegisterService(&echo_test_service_);
142 }
143 if (register_sync_health_service_impl) {
144 builder.RegisterService(&health_check_service_impl_);
145 }
146 if (add_async_cq) {
147 cq_ = builder.AddCompletionQueue();
148 }
149 server_ = builder.BuildAndStart();
150 }
151
TearDown()152 void TearDown() override {
153 if (server_) {
154 server_->Shutdown();
155 if (cq_ != nullptr) {
156 cq_->Shutdown();
157 }
158 if (cq_thread_.joinable()) {
159 cq_thread_.join();
160 }
161 }
162 }
163
ResetStubs()164 void ResetStubs() {
165 std::shared_ptr<Channel> channel =
166 CreateChannel(server_address_.str(), InsecureChannelCredentials());
167 hc_stub_ = grpc::health::v1::Health::NewStub(channel);
168 }
169
170 // When the expected_status is NOT OK, we do not care about the response.
SendHealthCheckRpc(const grpc::string & service_name,const Status & expected_status)171 void SendHealthCheckRpc(const grpc::string& service_name,
172 const Status& expected_status) {
173 EXPECT_FALSE(expected_status.ok());
174 SendHealthCheckRpc(service_name, expected_status,
175 HealthCheckResponse::UNKNOWN);
176 }
177
SendHealthCheckRpc(const grpc::string & service_name,const Status & expected_status,HealthCheckResponse::ServingStatus expected_serving_status)178 void SendHealthCheckRpc(
179 const grpc::string& service_name, const Status& expected_status,
180 HealthCheckResponse::ServingStatus expected_serving_status) {
181 HealthCheckRequest request;
182 request.set_service(service_name);
183 HealthCheckResponse response;
184 ClientContext context;
185 Status s = hc_stub_->Check(&context, request, &response);
186 EXPECT_EQ(expected_status.error_code(), s.error_code());
187 if (s.ok()) {
188 EXPECT_EQ(expected_serving_status, response.status());
189 }
190 }
191
VerifyHealthCheckService()192 void VerifyHealthCheckService() {
193 HealthCheckServiceInterface* service = server_->GetHealthCheckService();
194 EXPECT_TRUE(service != nullptr);
195 const grpc::string kHealthyService("healthy_service");
196 const grpc::string kUnhealthyService("unhealthy_service");
197 const grpc::string kNotRegisteredService("not_registered");
198 service->SetServingStatus(kHealthyService, true);
199 service->SetServingStatus(kUnhealthyService, false);
200
201 ResetStubs();
202
203 SendHealthCheckRpc("", Status::OK, HealthCheckResponse::SERVING);
204 SendHealthCheckRpc(kHealthyService, Status::OK,
205 HealthCheckResponse::SERVING);
206 SendHealthCheckRpc(kUnhealthyService, Status::OK,
207 HealthCheckResponse::NOT_SERVING);
208 SendHealthCheckRpc(kNotRegisteredService,
209 Status(StatusCode::NOT_FOUND, ""));
210
211 service->SetServingStatus(false);
212 SendHealthCheckRpc("", Status::OK, HealthCheckResponse::NOT_SERVING);
213 SendHealthCheckRpc(kHealthyService, Status::OK,
214 HealthCheckResponse::NOT_SERVING);
215 SendHealthCheckRpc(kUnhealthyService, Status::OK,
216 HealthCheckResponse::NOT_SERVING);
217 SendHealthCheckRpc(kNotRegisteredService,
218 Status(StatusCode::NOT_FOUND, ""));
219 }
220
221 TestServiceImpl echo_test_service_;
222 HealthCheckServiceImpl health_check_service_impl_;
223 std::unique_ptr<Health::Stub> hc_stub_;
224 std::unique_ptr<ServerCompletionQueue> cq_;
225 std::unique_ptr<Server> server_;
226 std::ostringstream server_address_;
227 std::thread cq_thread_;
228 };
229
TEST_F(HealthServiceEnd2endTest,DefaultHealthServiceDisabled)230 TEST_F(HealthServiceEnd2endTest, DefaultHealthServiceDisabled) {
231 EnableDefaultHealthCheckService(false);
232 EXPECT_FALSE(DefaultHealthCheckServiceEnabled());
233 SetUpServer(true, false, false, nullptr);
234 HealthCheckServiceInterface* default_service =
235 server_->GetHealthCheckService();
236 EXPECT_TRUE(default_service == nullptr);
237
238 ResetStubs();
239
240 SendHealthCheckRpc("", Status(StatusCode::UNIMPLEMENTED, ""));
241 }
242
TEST_F(HealthServiceEnd2endTest,DefaultHealthService)243 TEST_F(HealthServiceEnd2endTest, DefaultHealthService) {
244 EnableDefaultHealthCheckService(true);
245 EXPECT_TRUE(DefaultHealthCheckServiceEnabled());
246 SetUpServer(true, false, false, nullptr);
247 VerifyHealthCheckService();
248
249 // The default service has a size limit of the service name.
250 const grpc::string kTooLongServiceName(201, 'x');
251 SendHealthCheckRpc(kTooLongServiceName,
252 Status(StatusCode::INVALID_ARGUMENT, ""));
253 }
254
255 // The server has no sync service.
TEST_F(HealthServiceEnd2endTest,DefaultHealthServiceAsyncOnly)256 TEST_F(HealthServiceEnd2endTest, DefaultHealthServiceAsyncOnly) {
257 EnableDefaultHealthCheckService(true);
258 EXPECT_TRUE(DefaultHealthCheckServiceEnabled());
259 SetUpServer(false, true, false, nullptr);
260 cq_thread_ = std::thread(LoopCompletionQueue, cq_.get());
261
262 HealthCheckServiceInterface* default_service =
263 server_->GetHealthCheckService();
264 EXPECT_TRUE(default_service == nullptr);
265
266 ResetStubs();
267
268 SendHealthCheckRpc("", Status(StatusCode::UNIMPLEMENTED, ""));
269 }
270
271 // Provide an empty service to disable the default service.
TEST_F(HealthServiceEnd2endTest,ExplicitlyDisableViaOverride)272 TEST_F(HealthServiceEnd2endTest, ExplicitlyDisableViaOverride) {
273 EnableDefaultHealthCheckService(true);
274 EXPECT_TRUE(DefaultHealthCheckServiceEnabled());
275 std::unique_ptr<HealthCheckServiceInterface> empty_service;
276 SetUpServer(true, false, true, std::move(empty_service));
277 HealthCheckServiceInterface* service = server_->GetHealthCheckService();
278 EXPECT_TRUE(service == nullptr);
279
280 ResetStubs();
281
282 SendHealthCheckRpc("", Status(StatusCode::UNIMPLEMENTED, ""));
283 }
284
285 // Provide an explicit override of health checking service interface.
TEST_F(HealthServiceEnd2endTest,ExplicitlyOverride)286 TEST_F(HealthServiceEnd2endTest, ExplicitlyOverride) {
287 EnableDefaultHealthCheckService(true);
288 EXPECT_TRUE(DefaultHealthCheckServiceEnabled());
289 std::unique_ptr<HealthCheckServiceInterface> override_service(
290 new CustomHealthCheckService(&health_check_service_impl_));
291 HealthCheckServiceInterface* underlying_service = override_service.get();
292 SetUpServer(false, false, true, std::move(override_service));
293 HealthCheckServiceInterface* service = server_->GetHealthCheckService();
294 EXPECT_TRUE(service == underlying_service);
295
296 ResetStubs();
297
298 VerifyHealthCheckService();
299 }
300
301 } // namespace
302 } // namespace testing
303 } // namespace grpc
304
main(int argc,char ** argv)305 int main(int argc, char** argv) {
306 grpc_test_init(argc, argv);
307 ::testing::InitGoogleTest(&argc, argv);
308 return RUN_ALL_TESTS();
309 }
310