mirror of https://github.com/grpc/grpc.git
Merge pull request #2943 from tbetbetbe/grpc-ruby-add-health-check-service
Add a health checker service implementation.pull/2949/head
commit
723569b260
11 changed files with 440 additions and 3 deletions
@ -1,4 +1,5 @@ |
|||||||
-I. |
-I. |
||||||
|
-Ipb |
||||||
--require spec_helper |
--require spec_helper |
||||||
--format documentation |
--format documentation |
||||||
--color |
--color |
||||||
|
@ -0,0 +1,27 @@ |
|||||||
|
Protocol Buffers |
||||||
|
================ |
||||||
|
|
||||||
|
This folder contains protocol buffers provided with gRPC ruby, and the generated |
||||||
|
code to them. |
||||||
|
|
||||||
|
PREREQUISITES |
||||||
|
------------- |
||||||
|
|
||||||
|
The code is is generated using the protoc (> 3.0.0.alpha.1) and the |
||||||
|
grpc_ruby_plugin. These must be installed to regenerate the IDL defined |
||||||
|
classes, but that's not necessary just to use them. |
||||||
|
|
||||||
|
health_check/v1alpha |
||||||
|
-------------------- |
||||||
|
|
||||||
|
This package defines the surface of a simple health check service that gRPC |
||||||
|
servers may choose to implement, and provides an implementation for it. To |
||||||
|
re-generate the surface. |
||||||
|
|
||||||
|
```bash |
||||||
|
$ # (from this directory) |
||||||
|
$ protoc -I . grpc/health/v1alpha/health.proto \ |
||||||
|
--grpc_out=. \ |
||||||
|
--ruby_out=. \ |
||||||
|
--plugin=protoc-gen-grpc=`which grpc_ruby_plugin` |
||||||
|
``` |
@ -0,0 +1,75 @@ |
|||||||
|
# Copyright 2015, Google Inc. |
||||||
|
# All rights reserved. |
||||||
|
# |
||||||
|
# Redistribution and use in source and binary forms, with or without |
||||||
|
# modification, are permitted provided that the following conditions are |
||||||
|
# met: |
||||||
|
# |
||||||
|
# * Redistributions of source code must retain the above copyright |
||||||
|
# notice, this list of conditions and the following disclaimer. |
||||||
|
# * Redistributions in binary form must reproduce the above |
||||||
|
# copyright notice, this list of conditions and the following disclaimer |
||||||
|
# in the documentation and/or other materials provided with the |
||||||
|
# distribution. |
||||||
|
# * Neither the name of Google Inc. nor the names of its |
||||||
|
# contributors may be used to endorse or promote products derived from |
||||||
|
# this software without specific prior written permission. |
||||||
|
# |
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
|
||||||
|
require 'grpc' |
||||||
|
require 'grpc/health/v1alpha/health_services' |
||||||
|
require 'thread' |
||||||
|
|
||||||
|
module Grpc |
||||||
|
# Health contains classes and modules that support providing a health check |
||||||
|
# service. |
||||||
|
module Health |
||||||
|
# Checker is implementation of the schema-specified health checking service. |
||||||
|
class Checker < V1alpha::Health::Service |
||||||
|
StatusCodes = GRPC::Core::StatusCodes |
||||||
|
HealthCheckResponse = V1alpha::HealthCheckResponse |
||||||
|
|
||||||
|
# Initializes the statuses of participating services |
||||||
|
def initialize |
||||||
|
@statuses = {} |
||||||
|
@status_mutex = Mutex.new # guards access to @statuses |
||||||
|
end |
||||||
|
|
||||||
|
# Implements the rpc IDL API method |
||||||
|
def check(req, _call) |
||||||
|
status = nil |
||||||
|
@status_mutex.synchronize do |
||||||
|
status = @statuses["#{req.host}/#{req.service}"] |
||||||
|
end |
||||||
|
fail GRPC::BadStatus, StatusCodes::NOT_FOUND if status.nil? |
||||||
|
HealthCheckResponse.new(status: status) |
||||||
|
end |
||||||
|
|
||||||
|
# Adds the health status for a given host and service. |
||||||
|
def add_status(host, service, status) |
||||||
|
@status_mutex.synchronize { @statuses["#{host}/#{service}"] = status } |
||||||
|
end |
||||||
|
|
||||||
|
# Clears the status for the given host or service. |
||||||
|
def clear_status(host, service) |
||||||
|
@status_mutex.synchronize { @statuses.delete("#{host}/#{service}") } |
||||||
|
end |
||||||
|
|
||||||
|
# Clears alls the statuses. |
||||||
|
def clear_all |
||||||
|
@status_mutex.synchronize { @statuses = {} } |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,50 @@ |
|||||||
|
// Copyright 2015, Google Inc. |
||||||
|
// All rights reserved. |
||||||
|
// |
||||||
|
// Redistribution and use in source and binary forms, with or without |
||||||
|
// modification, are permitted provided that the following conditions are |
||||||
|
// met: |
||||||
|
// |
||||||
|
// * Redistributions of source code must retain the above copyright |
||||||
|
// notice, this list of conditions and the following disclaimer. |
||||||
|
// * Redistributions in binary form must reproduce the above |
||||||
|
// copyright notice, this list of conditions and the following disclaimer |
||||||
|
// in the documentation and/or other materials provided with the |
||||||
|
// distribution. |
||||||
|
// * Neither the name of Google Inc. nor the names of its |
||||||
|
// contributors may be used to endorse or promote products derived from |
||||||
|
// this software without specific prior written permission. |
||||||
|
// |
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
|
||||||
|
syntax = "proto3"; |
||||||
|
|
||||||
|
package grpc.health.v1alpha; |
||||||
|
|
||||||
|
message HealthCheckRequest { |
||||||
|
string host = 1; |
||||||
|
string service = 2; |
||||||
|
} |
||||||
|
|
||||||
|
message HealthCheckResponse { |
||||||
|
enum ServingStatus { |
||||||
|
UNKNOWN = 0; |
||||||
|
SERVING = 1; |
||||||
|
NOT_SERVING = 2; |
||||||
|
} |
||||||
|
ServingStatus status = 1; |
||||||
|
} |
||||||
|
|
||||||
|
service Health { |
||||||
|
rpc Check(HealthCheckRequest) returns (HealthCheckResponse); |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
# Generated by the protocol buffer compiler. DO NOT EDIT! |
||||||
|
# source: grpc/health/v1alpha/health.proto |
||||||
|
|
||||||
|
require 'google/protobuf' |
||||||
|
|
||||||
|
Google::Protobuf::DescriptorPool.generated_pool.build do |
||||||
|
add_message "grpc.health.v1alpha.HealthCheckRequest" do |
||||||
|
optional :host, :string, 1 |
||||||
|
optional :service, :string, 2 |
||||||
|
end |
||||||
|
add_message "grpc.health.v1alpha.HealthCheckResponse" do |
||||||
|
optional :status, :enum, 1, "grpc.health.v1alpha.HealthCheckResponse.ServingStatus" |
||||||
|
end |
||||||
|
add_enum "grpc.health.v1alpha.HealthCheckResponse.ServingStatus" do |
||||||
|
value :UNKNOWN, 0 |
||||||
|
value :SERVING, 1 |
||||||
|
value :NOT_SERVING, 2 |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
module Grpc |
||||||
|
module Health |
||||||
|
module V1alpha |
||||||
|
HealthCheckRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.health.v1alpha.HealthCheckRequest").msgclass |
||||||
|
HealthCheckResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.health.v1alpha.HealthCheckResponse").msgclass |
||||||
|
HealthCheckResponse::ServingStatus = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.health.v1alpha.HealthCheckResponse.ServingStatus").enummodule |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,39 @@ |
|||||||
|
# Copyright 2015, Google Inc. |
||||||
|
# All rights reserved. |
||||||
|
# |
||||||
|
# Redistribution and use in source and binary forms, with or without |
||||||
|
# modification, are permitted provided that the following conditions are |
||||||
|
# met: |
||||||
|
# |
||||||
|
# * Redistributions of source code must retain the above copyright |
||||||
|
# notice, this list of conditions and the following disclaimer. |
||||||
|
# * Redistributions in binary form must reproduce the above |
||||||
|
# copyright notice, this list of conditions and the following disclaimer |
||||||
|
# in the documentation and/or other materials provided with the |
||||||
|
# distribution. |
||||||
|
# * Neither the name of Google Inc. nor the names of its |
||||||
|
# contributors may be used to endorse or promote products derived from |
||||||
|
# this software without specific prior written permission. |
||||||
|
# |
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
|
||||||
|
require 'grpc/health/v1alpha/health_services' |
||||||
|
|
||||||
|
module Grpc |
||||||
|
# Health contains classes and modules that support providing a health check |
||||||
|
# service. |
||||||
|
module Health |
||||||
|
class Checker |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,28 @@ |
|||||||
|
# Generated by the protocol buffer compiler. DO NOT EDIT! |
||||||
|
# Source: grpc/health/v1alpha/health.proto for package 'grpc.health.v1alpha' |
||||||
|
|
||||||
|
require 'grpc' |
||||||
|
require 'grpc/health/v1alpha/health' |
||||||
|
|
||||||
|
module Grpc |
||||||
|
module Health |
||||||
|
module V1alpha |
||||||
|
module Health |
||||||
|
|
||||||
|
# TODO: add proto service documentation here |
||||||
|
class Service |
||||||
|
|
||||||
|
include GRPC::GenericService |
||||||
|
|
||||||
|
self.marshal_class_method = :encode |
||||||
|
self.unmarshal_class_method = :decode |
||||||
|
self.service_name = 'grpc.health.v1alpha.Health' |
||||||
|
|
||||||
|
rpc :Check, HealthCheckRequest, HealthCheckResponse |
||||||
|
end |
||||||
|
|
||||||
|
Stub = Service.rpc_stub_class |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,185 @@ |
|||||||
|
# Copyright 2015, Google Inc. |
||||||
|
# All rights reserved. |
||||||
|
# |
||||||
|
# Redistribution and use in source and binary forms, with or without |
||||||
|
# modification, are permitted provided that the following conditions are |
||||||
|
# met: |
||||||
|
# |
||||||
|
# * Redistributions of source code must retain the above copyright |
||||||
|
# notice, this list of conditions and the following disclaimer. |
||||||
|
# * Redistributions in binary form must reproduce the above |
||||||
|
# copyright notice, this list of conditions and the following disclaimer |
||||||
|
# in the documentation and/or other materials provided with the |
||||||
|
# distribution. |
||||||
|
# * Neither the name of Google Inc. nor the names of its |
||||||
|
# contributors may be used to endorse or promote products derived from |
||||||
|
# this software without specific prior written permission. |
||||||
|
# |
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
|
||||||
|
require 'grpc' |
||||||
|
require 'grpc/health/v1alpha/health' |
||||||
|
require 'grpc/health/checker' |
||||||
|
|
||||||
|
describe Grpc::Health::Checker do |
||||||
|
StatusCodes = GRPC::Core::StatusCodes |
||||||
|
ServingStatus = Grpc::Health::V1alpha::HealthCheckResponse::ServingStatus |
||||||
|
HCResp = Grpc::Health::V1alpha::HealthCheckResponse |
||||||
|
HCReq = Grpc::Health::V1alpha::HealthCheckRequest |
||||||
|
success_tests = |
||||||
|
[ |
||||||
|
{ |
||||||
|
desc: 'neither host or service are specified', |
||||||
|
host: '', |
||||||
|
service: '' |
||||||
|
}, { |
||||||
|
desc: 'only the host is specified', |
||||||
|
host: 'test-fake-host', |
||||||
|
service: '' |
||||||
|
}, { |
||||||
|
desc: 'the host and service are specified', |
||||||
|
host: 'test-fake-host', |
||||||
|
service: 'fake-service-1' |
||||||
|
}, { |
||||||
|
desc: 'only the service is specified', |
||||||
|
host: '', |
||||||
|
service: 'fake-service-2' |
||||||
|
} |
||||||
|
] |
||||||
|
|
||||||
|
context 'initialization' do |
||||||
|
it 'can be constructed with no args' do |
||||||
|
expect(subject).to_not be(nil) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'method `add_status` and `check`' do |
||||||
|
success_tests.each do |t| |
||||||
|
it "should succeed when #{t[:desc]}" do |
||||||
|
subject.add_status(t[:host], t[:service], ServingStatus::NOT_SERVING) |
||||||
|
got = subject.check(HCReq.new(host: t[:host], service: t[:service]), |
||||||
|
nil) |
||||||
|
want = HCResp.new(status: ServingStatus::NOT_SERVING) |
||||||
|
expect(got).to eq(want) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'method `check`' do |
||||||
|
success_tests.each do |t| |
||||||
|
it "should fail with NOT_FOUND when #{t[:desc]}" do |
||||||
|
blk = proc do |
||||||
|
subject.check(HCReq.new(host: t[:host], service: t[:service]), nil) |
||||||
|
end |
||||||
|
expected_msg = /#{StatusCodes::NOT_FOUND}/ |
||||||
|
expect(&blk).to raise_error GRPC::BadStatus, expected_msg |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'method `clear_status`' do |
||||||
|
success_tests.each do |t| |
||||||
|
it "should fail after clearing status when #{t[:desc]}" do |
||||||
|
subject.add_status(t[:host], t[:service], ServingStatus::NOT_SERVING) |
||||||
|
got = subject.check(HCReq.new(host: t[:host], service: t[:service]), |
||||||
|
nil) |
||||||
|
want = HCResp.new(status: ServingStatus::NOT_SERVING) |
||||||
|
expect(got).to eq(want) |
||||||
|
|
||||||
|
subject.clear_status(t[:host], t[:service]) |
||||||
|
blk = proc do |
||||||
|
subject.check(HCReq.new(host: t[:host], service: t[:service]), |
||||||
|
nil) |
||||||
|
end |
||||||
|
expected_msg = /#{StatusCodes::NOT_FOUND}/ |
||||||
|
expect(&blk).to raise_error GRPC::BadStatus, expected_msg |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'method `clear_all`' do |
||||||
|
it 'should return NOT_FOUND after being invoked' do |
||||||
|
success_tests.each do |t| |
||||||
|
subject.add_status(t[:host], t[:service], ServingStatus::NOT_SERVING) |
||||||
|
got = subject.check(HCReq.new(host: t[:host], service: t[:service]), |
||||||
|
nil) |
||||||
|
want = HCResp.new(status: ServingStatus::NOT_SERVING) |
||||||
|
expect(got).to eq(want) |
||||||
|
end |
||||||
|
|
||||||
|
subject.clear_all |
||||||
|
|
||||||
|
success_tests.each do |t| |
||||||
|
blk = proc do |
||||||
|
subject.check(HCReq.new(host: t[:host], service: t[:service]), nil) |
||||||
|
end |
||||||
|
expected_msg = /#{StatusCodes::NOT_FOUND}/ |
||||||
|
expect(&blk).to raise_error GRPC::BadStatus, expected_msg |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'running on RpcServer' do |
||||||
|
RpcServer = GRPC::RpcServer |
||||||
|
StatusCodes = GRPC::Core::StatusCodes |
||||||
|
CheckerStub = Grpc::Health::Checker.rpc_stub_class |
||||||
|
|
||||||
|
before(:each) do |
||||||
|
@server_queue = GRPC::Core::CompletionQueue.new |
||||||
|
server_host = '0.0.0.0:0' |
||||||
|
@server = GRPC::Core::Server.new(@server_queue, nil) |
||||||
|
server_port = @server.add_http2_port(server_host) |
||||||
|
@host = "localhost:#{server_port}" |
||||||
|
@ch = GRPC::Core::Channel.new(@host, nil) |
||||||
|
@client_opts = { channel_override: @ch } |
||||||
|
server_opts = { |
||||||
|
server_override: @server, |
||||||
|
completion_queue_override: @server_queue, |
||||||
|
poll_period: 1 |
||||||
|
} |
||||||
|
@srv = RpcServer.new(**server_opts) |
||||||
|
end |
||||||
|
|
||||||
|
after(:each) do |
||||||
|
@srv.stop |
||||||
|
end |
||||||
|
|
||||||
|
it 'should receive the correct status', server: true do |
||||||
|
@srv.handle(subject) |
||||||
|
subject.add_status('', '', ServingStatus::NOT_SERVING) |
||||||
|
t = Thread.new { @srv.run } |
||||||
|
@srv.wait_till_running |
||||||
|
|
||||||
|
stub = CheckerStub.new(@host, **@client_opts) |
||||||
|
got = stub.check(HCReq.new) |
||||||
|
want = HCResp.new(status: ServingStatus::NOT_SERVING) |
||||||
|
expect(got).to eq(want) |
||||||
|
@srv.stop |
||||||
|
t.join |
||||||
|
end |
||||||
|
|
||||||
|
it 'should fail on unknown services', server: true do |
||||||
|
@srv.handle(subject) |
||||||
|
t = Thread.new { @srv.run } |
||||||
|
@srv.wait_till_running |
||||||
|
blk = proc do |
||||||
|
stub = CheckerStub.new(@host, **@client_opts) |
||||||
|
stub.check(HCReq.new(host: 'unknown', service: 'unknown')) |
||||||
|
end |
||||||
|
expected_msg = /#{StatusCodes::NOT_FOUND}/ |
||||||
|
expect(&blk).to raise_error GRPC::BadStatus, expected_msg |
||||||
|
@srv.stop |
||||||
|
t.join |
||||||
|
end |
||||||
|
end |
||||||
|
end |
Loading…
Reference in new issue