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. |
||||
-Ipb |
||||
--require spec_helper |
||||
--format documentation |
||||
--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