1 /* 2 * 3 * Copyright 2018 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 <grpc/support/port_platform.h> 20 21 #include <grpc/grpc.h> 22 #include <grpcpp/channel.h> 23 #include <grpcpp/client_context.h> 24 #include <grpcpp/create_channel.h> 25 #include <grpcpp/security/credentials.h> 26 #include <grpcpp/security/server_credentials.h> 27 #include <grpcpp/server.h> 28 #include <grpcpp/server_builder.h> 29 #include <grpcpp/server_context.h> 30 31 #include <grpcpp/ext/channelz_service_plugin.h> 32 #include "src/proto/grpc/channelz/channelz.grpc.pb.h" 33 #include "src/proto/grpc/testing/echo.grpc.pb.h" 34 #include "test/core/util/port.h" 35 #include "test/core/util/test_config.h" 36 #include "test/cpp/end2end/test_service_impl.h" 37 38 #include <google/protobuf/text_format.h> 39 40 #include <gtest/gtest.h> 41 42 using grpc::channelz::v1::GetChannelRequest; 43 using grpc::channelz::v1::GetChannelResponse; 44 using grpc::channelz::v1::GetServersRequest; 45 using grpc::channelz::v1::GetServersResponse; 46 using grpc::channelz::v1::GetSubchannelRequest; 47 using grpc::channelz::v1::GetSubchannelResponse; 48 using grpc::channelz::v1::GetTopChannelsRequest; 49 using grpc::channelz::v1::GetTopChannelsResponse; 50 51 namespace grpc { 52 namespace testing { 53 namespace { 54 55 // Proxy service supports N backends. Sends RPC to backend dictated by 56 // request->backend_channel_idx(). 57 class Proxy : public ::grpc::testing::EchoTestService::Service { 58 public: 59 Proxy() {} 60 61 void AddChannelToBackend(const std::shared_ptr<Channel>& channel) { 62 stubs_.push_back(grpc::testing::EchoTestService::NewStub(channel)); 63 } 64 65 Status Echo(ServerContext* server_context, const EchoRequest* request, 66 EchoResponse* response) override { 67 std::unique_ptr<ClientContext> client_context = 68 ClientContext::FromServerContext(*server_context); 69 size_t idx = request->param().backend_channel_idx(); 70 GPR_ASSERT(idx < stubs_.size()); 71 return stubs_[idx]->Echo(client_context.get(), *request, response); 72 } 73 74 private: 75 std::vector<std::unique_ptr<::grpc::testing::EchoTestService::Stub>> stubs_; 76 }; 77 78 } // namespace 79 80 class ChannelzServerTest : public ::testing::Test { 81 public: 82 ChannelzServerTest() {} 83 84 void SetUp() override { 85 // ensure channel server is brought up on all severs we build. 86 ::grpc::channelz::experimental::InitChannelzService(); 87 88 // We set up a proxy server with channelz enabled. 89 proxy_port_ = grpc_pick_unused_port_or_die(); 90 ServerBuilder proxy_builder; 91 grpc::string proxy_server_address = "localhost:" + to_string(proxy_port_); 92 proxy_builder.AddListeningPort(proxy_server_address, 93 InsecureServerCredentials()); 94 // forces channelz and channel tracing to be enabled. 95 proxy_builder.AddChannelArgument(GRPC_ARG_ENABLE_CHANNELZ, 1); 96 proxy_builder.AddChannelArgument(GRPC_ARG_MAX_CHANNEL_TRACE_EVENTS_PER_NODE, 97 10); 98 proxy_builder.RegisterService(&proxy_service_); 99 proxy_server_ = proxy_builder.BuildAndStart(); 100 } 101 102 // Sets the proxy up to have an arbitrary number of backends. 103 void ConfigureProxy(size_t num_backends) { 104 backends_.resize(num_backends); 105 for (size_t i = 0; i < num_backends; ++i) { 106 // create a new backend. 107 backends_[i].port = grpc_pick_unused_port_or_die(); 108 ServerBuilder backend_builder; 109 grpc::string backend_server_address = 110 "localhost:" + to_string(backends_[i].port); 111 backend_builder.AddListeningPort(backend_server_address, 112 InsecureServerCredentials()); 113 backends_[i].service.reset(new TestServiceImpl); 114 // ensure that the backend itself has channelz disabled. 115 backend_builder.AddChannelArgument(GRPC_ARG_ENABLE_CHANNELZ, 0); 116 backend_builder.RegisterService(backends_[i].service.get()); 117 backends_[i].server = backend_builder.BuildAndStart(); 118 // set up a channel to the backend. We ensure that this channel has 119 // channelz enabled since these channels (proxy outbound to backends) 120 // are the ones that our test will actually be validating. 121 ChannelArguments args; 122 args.SetInt(GRPC_ARG_ENABLE_CHANNELZ, 1); 123 args.SetInt(GRPC_ARG_MAX_CHANNEL_TRACE_EVENTS_PER_NODE, 10); 124 std::shared_ptr<Channel> channel_to_backend = CreateCustomChannel( 125 backend_server_address, InsecureChannelCredentials(), args); 126 proxy_service_.AddChannelToBackend(channel_to_backend); 127 } 128 } 129 130 void ResetStubs() { 131 string target = "dns:localhost:" + to_string(proxy_port_); 132 ChannelArguments args; 133 // disable channelz. We only want to focus on proxy to backend outbound. 134 args.SetInt(GRPC_ARG_ENABLE_CHANNELZ, 0); 135 std::shared_ptr<Channel> channel = 136 CreateCustomChannel(target, InsecureChannelCredentials(), args); 137 channelz_stub_ = grpc::channelz::v1::Channelz::NewStub(channel); 138 echo_stub_ = grpc::testing::EchoTestService::NewStub(channel); 139 } 140 141 void SendSuccessfulEcho(int channel_idx) { 142 EchoRequest request; 143 EchoResponse response; 144 request.set_message("Hello channelz"); 145 request.mutable_param()->set_backend_channel_idx(channel_idx); 146 ClientContext context; 147 Status s = echo_stub_->Echo(&context, request, &response); 148 EXPECT_EQ(response.message(), request.message()); 149 EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message(); 150 } 151 152 void SendFailedEcho(int channel_idx) { 153 EchoRequest request; 154 EchoResponse response; 155 request.set_message("Hello channelz"); 156 request.mutable_param()->set_backend_channel_idx(channel_idx); 157 auto* error = request.mutable_param()->mutable_expected_error(); 158 error->set_code(13); // INTERNAL 159 error->set_error_message("error"); 160 ClientContext context; 161 Status s = echo_stub_->Echo(&context, request, &response); 162 EXPECT_FALSE(s.ok()); 163 } 164 165 // Uses GetTopChannels to return the channel_id of a particular channel, 166 // so that the unit tests may test GetChannel call. 167 intptr_t GetChannelId(int channel_idx) { 168 GetTopChannelsRequest request; 169 GetTopChannelsResponse response; 170 request.set_start_channel_id(0); 171 ClientContext context; 172 Status s = channelz_stub_->GetTopChannels(&context, request, &response); 173 EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message(); 174 EXPECT_GT(response.channel_size(), channel_idx); 175 return response.channel(channel_idx).ref().channel_id(); 176 } 177 178 static string to_string(const int number) { 179 std::stringstream strs; 180 strs << number; 181 return strs.str(); 182 } 183 184 protected: 185 // package of data needed for each backend server. 186 struct BackendData { 187 std::unique_ptr<Server> server; 188 int port; 189 std::unique_ptr<TestServiceImpl> service; 190 }; 191 192 std::unique_ptr<grpc::channelz::v1::Channelz::Stub> channelz_stub_; 193 std::unique_ptr<grpc::testing::EchoTestService::Stub> echo_stub_; 194 195 // proxy server to ping with channelz requests. 196 std::unique_ptr<Server> proxy_server_; 197 int proxy_port_; 198 Proxy proxy_service_; 199 200 // backends. All implement the echo service. 201 std::vector<BackendData> backends_; 202 }; 203 204 TEST_F(ChannelzServerTest, BasicTest) { 205 ResetStubs(); 206 ConfigureProxy(1); 207 GetTopChannelsRequest request; 208 GetTopChannelsResponse response; 209 request.set_start_channel_id(0); 210 ClientContext context; 211 Status s = channelz_stub_->GetTopChannels(&context, request, &response); 212 EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message(); 213 EXPECT_EQ(response.channel_size(), 1); 214 } 215 216 TEST_F(ChannelzServerTest, HighStartId) { 217 ResetStubs(); 218 ConfigureProxy(1); 219 GetTopChannelsRequest request; 220 GetTopChannelsResponse response; 221 request.set_start_channel_id(10000); 222 ClientContext context; 223 Status s = channelz_stub_->GetTopChannels(&context, request, &response); 224 EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message(); 225 EXPECT_EQ(response.channel_size(), 0); 226 } 227 228 TEST_F(ChannelzServerTest, SuccessfulRequestTest) { 229 ResetStubs(); 230 ConfigureProxy(1); 231 SendSuccessfulEcho(0); 232 GetChannelRequest request; 233 GetChannelResponse response; 234 request.set_channel_id(GetChannelId(0)); 235 ClientContext context; 236 Status s = channelz_stub_->GetChannel(&context, request, &response); 237 EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message(); 238 EXPECT_EQ(response.channel().data().calls_started(), 1); 239 EXPECT_EQ(response.channel().data().calls_succeeded(), 1); 240 EXPECT_EQ(response.channel().data().calls_failed(), 0); 241 } 242 243 TEST_F(ChannelzServerTest, FailedRequestTest) { 244 ResetStubs(); 245 ConfigureProxy(1); 246 SendFailedEcho(0); 247 GetChannelRequest request; 248 GetChannelResponse response; 249 request.set_channel_id(GetChannelId(0)); 250 ClientContext context; 251 Status s = channelz_stub_->GetChannel(&context, request, &response); 252 EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message(); 253 EXPECT_EQ(response.channel().data().calls_started(), 1); 254 EXPECT_EQ(response.channel().data().calls_succeeded(), 0); 255 EXPECT_EQ(response.channel().data().calls_failed(), 1); 256 } 257 258 TEST_F(ChannelzServerTest, ManyRequestsTest) { 259 ResetStubs(); 260 ConfigureProxy(1); 261 // send some RPCs 262 const int kNumSuccess = 10; 263 const int kNumFailed = 11; 264 for (int i = 0; i < kNumSuccess; ++i) { 265 SendSuccessfulEcho(0); 266 } 267 for (int i = 0; i < kNumFailed; ++i) { 268 SendFailedEcho(0); 269 } 270 GetChannelRequest request; 271 GetChannelResponse response; 272 request.set_channel_id(GetChannelId(0)); 273 ClientContext context; 274 Status s = channelz_stub_->GetChannel(&context, request, &response); 275 EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message(); 276 EXPECT_EQ(response.channel().data().calls_started(), 277 kNumSuccess + kNumFailed); 278 EXPECT_EQ(response.channel().data().calls_succeeded(), kNumSuccess); 279 EXPECT_EQ(response.channel().data().calls_failed(), kNumFailed); 280 } 281 282 TEST_F(ChannelzServerTest, ManyChannels) { 283 ResetStubs(); 284 const int kNumChannels = 4; 285 ConfigureProxy(kNumChannels); 286 GetTopChannelsRequest request; 287 GetTopChannelsResponse response; 288 request.set_start_channel_id(0); 289 ClientContext context; 290 Status s = channelz_stub_->GetTopChannels(&context, request, &response); 291 EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message(); 292 EXPECT_EQ(response.channel_size(), kNumChannels); 293 } 294 295 TEST_F(ChannelzServerTest, ManyRequestsManyChannels) { 296 ResetStubs(); 297 const int kNumChannels = 4; 298 ConfigureProxy(kNumChannels); 299 const int kNumSuccess = 10; 300 const int kNumFailed = 11; 301 for (int i = 0; i < kNumSuccess; ++i) { 302 SendSuccessfulEcho(0); 303 SendSuccessfulEcho(2); 304 } 305 for (int i = 0; i < kNumFailed; ++i) { 306 SendFailedEcho(1); 307 SendFailedEcho(2); 308 } 309 310 // the first channel saw only successes 311 { 312 GetChannelRequest request; 313 GetChannelResponse response; 314 request.set_channel_id(GetChannelId(0)); 315 ClientContext context; 316 Status s = channelz_stub_->GetChannel(&context, request, &response); 317 EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message(); 318 EXPECT_EQ(response.channel().data().calls_started(), kNumSuccess); 319 EXPECT_EQ(response.channel().data().calls_succeeded(), kNumSuccess); 320 EXPECT_EQ(response.channel().data().calls_failed(), 0); 321 } 322 323 // the second channel saw only failures 324 { 325 GetChannelRequest request; 326 GetChannelResponse response; 327 request.set_channel_id(GetChannelId(1)); 328 ClientContext context; 329 Status s = channelz_stub_->GetChannel(&context, request, &response); 330 EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message(); 331 EXPECT_EQ(response.channel().data().calls_started(), kNumFailed); 332 EXPECT_EQ(response.channel().data().calls_succeeded(), 0); 333 EXPECT_EQ(response.channel().data().calls_failed(), kNumFailed); 334 } 335 336 // the third channel saw both 337 { 338 GetChannelRequest request; 339 GetChannelResponse response; 340 request.set_channel_id(GetChannelId(2)); 341 ClientContext context; 342 Status s = channelz_stub_->GetChannel(&context, request, &response); 343 EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message(); 344 EXPECT_EQ(response.channel().data().calls_started(), 345 kNumSuccess + kNumFailed); 346 EXPECT_EQ(response.channel().data().calls_succeeded(), kNumSuccess); 347 EXPECT_EQ(response.channel().data().calls_failed(), kNumFailed); 348 } 349 350 // the fourth channel saw nothing 351 { 352 GetChannelRequest request; 353 GetChannelResponse response; 354 request.set_channel_id(GetChannelId(3)); 355 ClientContext context; 356 Status s = channelz_stub_->GetChannel(&context, request, &response); 357 EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message(); 358 EXPECT_EQ(response.channel().data().calls_started(), 0); 359 EXPECT_EQ(response.channel().data().calls_succeeded(), 0); 360 EXPECT_EQ(response.channel().data().calls_failed(), 0); 361 } 362 } 363 364 TEST_F(ChannelzServerTest, ManySubchannels) { 365 ResetStubs(); 366 const int kNumChannels = 4; 367 ConfigureProxy(kNumChannels); 368 const int kNumSuccess = 10; 369 const int kNumFailed = 11; 370 for (int i = 0; i < kNumSuccess; ++i) { 371 SendSuccessfulEcho(0); 372 SendSuccessfulEcho(2); 373 } 374 for (int i = 0; i < kNumFailed; ++i) { 375 SendFailedEcho(1); 376 SendFailedEcho(2); 377 } 378 GetTopChannelsRequest gtc_request; 379 GetTopChannelsResponse gtc_response; 380 gtc_request.set_start_channel_id(0); 381 ClientContext context; 382 Status s = 383 channelz_stub_->GetTopChannels(&context, gtc_request, >c_response); 384 EXPECT_TRUE(s.ok()) << s.error_message(); 385 EXPECT_EQ(gtc_response.channel_size(), kNumChannels); 386 for (int i = 0; i < gtc_response.channel_size(); ++i) { 387 // if the channel sent no RPCs, then expect no subchannels to have been 388 // created. 389 if (gtc_response.channel(i).data().calls_started() == 0) { 390 EXPECT_EQ(gtc_response.channel(i).subchannel_ref_size(), 0); 391 continue; 392 } 393 // The resolver must return at least one address. 394 ASSERT_GT(gtc_response.channel(i).subchannel_ref_size(), 0); 395 GetSubchannelRequest gsc_request; 396 GetSubchannelResponse gsc_response; 397 gsc_request.set_subchannel_id( 398 gtc_response.channel(i).subchannel_ref(0).subchannel_id()); 399 ClientContext context; 400 Status s = 401 channelz_stub_->GetSubchannel(&context, gsc_request, &gsc_response); 402 EXPECT_TRUE(s.ok()) << s.error_message(); 403 EXPECT_EQ(gtc_response.channel(i).data().calls_started(), 404 gsc_response.subchannel().data().calls_started()); 405 EXPECT_EQ(gtc_response.channel(i).data().calls_succeeded(), 406 gsc_response.subchannel().data().calls_succeeded()); 407 EXPECT_EQ(gtc_response.channel(i).data().calls_failed(), 408 gsc_response.subchannel().data().calls_failed()); 409 } 410 } 411 412 TEST_F(ChannelzServerTest, BasicServerTest) { 413 ResetStubs(); 414 ConfigureProxy(1); 415 GetServersRequest request; 416 GetServersResponse response; 417 request.set_start_server_id(0); 418 ClientContext context; 419 Status s = channelz_stub_->GetServers(&context, request, &response); 420 EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message(); 421 EXPECT_EQ(response.server_size(), 1); 422 } 423 424 TEST_F(ChannelzServerTest, ServerCallTest) { 425 ResetStubs(); 426 ConfigureProxy(1); 427 const int kNumSuccess = 10; 428 const int kNumFailed = 11; 429 for (int i = 0; i < kNumSuccess; ++i) { 430 SendSuccessfulEcho(0); 431 } 432 for (int i = 0; i < kNumFailed; ++i) { 433 SendFailedEcho(0); 434 } 435 GetServersRequest request; 436 GetServersResponse response; 437 request.set_start_server_id(0); 438 ClientContext context; 439 Status s = channelz_stub_->GetServers(&context, request, &response); 440 EXPECT_TRUE(s.ok()) << "s.error_message() = " << s.error_message(); 441 EXPECT_EQ(response.server_size(), 1); 442 EXPECT_EQ(response.server(0).data().calls_succeeded(), kNumSuccess); 443 EXPECT_EQ(response.server(0).data().calls_failed(), kNumFailed); 444 // This is success+failure+1 because the call that retrieved this information 445 // will be counted as started. It will not track success/failure until after 446 // it has returned, so that is not included in the response. 447 EXPECT_EQ(response.server(0).data().calls_started(), 448 kNumSuccess + kNumFailed + 1); 449 } 450 451 } // namespace testing 452 } // namespace grpc 453 454 int main(int argc, char** argv) { 455 grpc_test_init(argc, argv); 456 ::testing::InitGoogleTest(&argc, argv); 457 return RUN_ALL_TESTS(); 458 } 459