1# Copyright 2015 gRPC authors. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15require 'spec_helper' 16require 'grpc/health/v1/health_pb' 17require 'grpc/health/checker' 18require 'open3' 19require 'tmpdir' 20 21def can_run_codegen_check 22 system('which grpc_ruby_plugin') && system('which protoc') 23end 24 25describe 'Health protobuf code generation' do 26 context 'the health service file used by grpc/health/checker' do 27 if !can_run_codegen_check 28 skip 'protoc || grpc_ruby_plugin missing, cannot verify health code-gen' 29 else 30 it 'should already be loaded indirectly i.e, used by the other specs' do 31 expect(require('grpc/health/v1/health_services_pb')).to be(false) 32 end 33 34 it 'should have the same content as created by code generation' do 35 root_dir = File.join(File.dirname(__FILE__), '..', '..', '..', '..') 36 pb_dir = File.join(root_dir, 'proto') 37 38 # Get the current content 39 service_path = File.join(root_dir, 'ruby', 'pb', 'grpc', 40 'health', 'v1', 'health_services_pb.rb') 41 want = nil 42 File.open(service_path) { |f| want = f.read } 43 44 # Regenerate it 45 plugin, = Open3.capture2('which', 'grpc_ruby_plugin') 46 plugin = plugin.strip 47 got = nil 48 Dir.mktmpdir do |tmp_dir| 49 gen_out = File.join(tmp_dir, 'grpc', 'health', 'v1', 50 'health_services_pb.rb') 51 pid = spawn( 52 'protoc', 53 '-I.', 54 'grpc/health/v1/health.proto', 55 "--grpc_out=#{tmp_dir}", 56 "--plugin=protoc-gen-grpc=#{plugin}", 57 chdir: pb_dir) 58 Process.wait(pid) 59 File.open(gen_out) { |f| got = f.read } 60 end 61 expect(got).to eq(want) 62 end 63 end 64 end 65end 66 67describe Grpc::Health::Checker do 68 StatusCodes = GRPC::Core::StatusCodes 69 ServingStatus = Grpc::Health::V1::HealthCheckResponse::ServingStatus 70 HCResp = Grpc::Health::V1::HealthCheckResponse 71 HCReq = Grpc::Health::V1::HealthCheckRequest 72 success_tests = 73 [ 74 { 75 desc: 'the service is not specified', 76 service: '' 77 }, { 78 desc: 'the service is specified', 79 service: 'fake-service-1' 80 } 81 ] 82 83 context 'initialization' do 84 it 'can be constructed with no args' do 85 checker = Grpc::Health::Checker.new 86 expect(checker).to_not be(nil) 87 end 88 end 89 90 context 'method `add_status` and `check`' do 91 success_tests.each do |t| 92 it "should succeed when #{t[:desc]}" do 93 checker = Grpc::Health::Checker.new 94 checker.add_status(t[:service], ServingStatus::NOT_SERVING) 95 got = checker.check(HCReq.new(service: t[:service]), nil) 96 want = HCResp.new(status: ServingStatus::NOT_SERVING) 97 expect(got).to eq(want) 98 end 99 end 100 end 101 102 context 'method `add_statuses`' do 103 it 'should add status to each service' do 104 checker = Grpc::Health::Checker.new 105 checker.add_statuses( 106 'service1' => ServingStatus::SERVING, 107 'service2' => ServingStatus::NOT_SERVING 108 ) 109 service1_health = checker.check(HCReq.new(service: 'service1'), nil) 110 service2_health = checker.check(HCReq.new(service: 'service2'), nil) 111 expect(service1_health).to eq(HCResp.new(status: ServingStatus::SERVING)) 112 expect(service2_health).to eq(HCResp.new(status: ServingStatus::NOT_SERVING)) 113 end 114 end 115 116 context 'method `set_status_for_services`' do 117 it 'should add given status to all given services' do 118 checker = Grpc::Health::Checker.new 119 checker.set_status_for_services( 120 ServingStatus::SERVING, 121 'service1', 122 'service2' 123 ) 124 service1_health = checker.check(HCReq.new(service: 'service1'), nil) 125 service2_health = checker.check(HCReq.new(service: 'service2'), nil) 126 expect(service1_health).to eq(HCResp.new(status: ServingStatus::SERVING)) 127 expect(service2_health).to eq(HCResp.new(status: ServingStatus::SERVING)) 128 end 129 end 130 131 context 'method `check`' do 132 success_tests.each do |t| 133 it "should fail with NOT_FOUND when #{t[:desc]}" do 134 checker = Grpc::Health::Checker.new 135 blk = proc do 136 checker.check(HCReq.new(service: t[:service]), nil) 137 end 138 expected_msg = /#{StatusCodes::NOT_FOUND}/ 139 expect(&blk).to raise_error GRPC::NotFound, expected_msg 140 end 141 end 142 end 143 144 context 'method `clear_status`' do 145 success_tests.each do |t| 146 it "should fail after clearing status when #{t[:desc]}" do 147 checker = Grpc::Health::Checker.new 148 checker.add_status(t[:service], ServingStatus::NOT_SERVING) 149 got = checker.check(HCReq.new(service: t[:service]), nil) 150 want = HCResp.new(status: ServingStatus::NOT_SERVING) 151 expect(got).to eq(want) 152 153 checker.clear_status(t[:service]) 154 blk = proc do 155 checker.check(HCReq.new(service: t[:service]), nil) 156 end 157 expected_msg = /#{StatusCodes::NOT_FOUND}/ 158 expect(&blk).to raise_error GRPC::NotFound, expected_msg 159 end 160 end 161 end 162 163 context 'method `clear_all`' do 164 it 'should return NOT_FOUND after being invoked' do 165 checker = Grpc::Health::Checker.new 166 success_tests.each do |t| 167 checker.add_status(t[:service], ServingStatus::NOT_SERVING) 168 got = checker.check(HCReq.new(service: t[:service]), nil) 169 want = HCResp.new(status: ServingStatus::NOT_SERVING) 170 expect(got).to eq(want) 171 end 172 173 checker.clear_all 174 175 success_tests.each do |t| 176 blk = proc do 177 checker.check(HCReq.new(service: t[:service]), nil) 178 end 179 expected_msg = /#{StatusCodes::NOT_FOUND}/ 180 expect(&blk).to raise_error GRPC::NotFound, expected_msg 181 end 182 end 183 end 184 185 describe 'running on RpcServer' do 186 RpcServer = GRPC::RpcServer 187 CheckerStub = Grpc::Health::Checker.rpc_stub_class 188 189 before(:each) do 190 server_host = '0.0.0.0:0' 191 @client_opts = { channel_override: @ch } 192 server_opts = { 193 poll_period: 1 194 } 195 @srv = new_rpc_server_for_testing(**server_opts) 196 server_port = @srv.add_http2_port(server_host, :this_port_is_insecure) 197 @host = "localhost:#{server_port}" 198 @ch = GRPC::Core::Channel.new(@host, nil, :this_channel_is_insecure) 199 end 200 201 after(:each) do 202 @srv.stop 203 end 204 205 it 'should receive the correct status', server: true do 206 Thread.abort_on_exception = true 207 checker = Grpc::Health::Checker.new 208 @srv.handle(checker) 209 checker.add_status('', ServingStatus::NOT_SERVING) 210 t = Thread.new { @srv.run } 211 @srv.wait_till_running 212 213 stub = CheckerStub.new(@host, :this_channel_is_insecure, **@client_opts) 214 got = stub.check(HCReq.new) 215 want = HCResp.new(status: ServingStatus::NOT_SERVING) 216 expect(got).to eq(want) 217 @srv.stop 218 t.join 219 end 220 221 it 'should fail on unknown services', server: true do 222 checker = Grpc::Health::Checker.new 223 @srv.handle(checker) 224 t = Thread.new { @srv.run } 225 @srv.wait_till_running 226 blk = proc do 227 stub = CheckerStub.new(@host, :this_channel_is_insecure, **@client_opts) 228 stub.check(HCReq.new(service: 'unknown')) 229 end 230 expected_msg = /#{StatusCodes::NOT_FOUND}/ 231 expect(&blk).to raise_error GRPC::NotFound, expected_msg 232 @srv.stop 233 t.join 234 end 235 end 236end 237