Skip to content

Commit bf68cf0

Browse files
authored
Merge pull request #536 from code0-tech/add-grpc-healthcheck
Add grpc health check service
2 parents 8387fcc + 02fd861 commit bf68cf0

File tree

4 files changed

+71
-10
lines changed

4 files changed

+71
-10
lines changed

app/grpc/health_handler.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# frozen_string_literal: true
2+
3+
require 'grpc/health/v1/health_services_pb'
4+
5+
class HealthHandler < Grpc::Health::V1::Health::Service
6+
include GrpcHandler
7+
8+
def check(req, _call)
9+
return Grpc::Health::V1::HealthCheckResponse.new(status: :SERVING) if req.service == 'liveness'
10+
11+
if req.service == 'readiness'
12+
ActiveRecord::Base.connection_pool.with_connection do |connection|
13+
connection.execute('SELECT pg_backend_pid();')
14+
return Grpc::Health::V1::HealthCheckResponse.new(status: :SERVING)
15+
rescue PG::Error, ActiveRecord::ActiveRecordError
16+
return Grpc::Health::V1::HealthCheckResponse.new(status: :NOT_SERVING)
17+
end
18+
end
19+
20+
raise GRPC::BadStatus.new_status_exception(GRPC::Core::StatusCodes::NOT_FOUND, 'Unknown service')
21+
end
22+
end

bin/docker-entrypoint

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
#!/bin/bash -e
22

33
if [ "${1}" == "./bin/rails" ] && [ "${2}" == "server" ]; then
4-
# prepare database
4+
SAGITTARIUS_PREPARE_DATABASE="true"
5+
fi
6+
7+
if [ "${SAGITTARIUS_PREPARE_DATABASE}" == "true" ]; then
58
bundle exec rake db:prepare
69
FILTER=01_application_settings bundle exec rake db:seed_fu
710
fi

lib/sagittarius/middleware/grpc/authentication.rb

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@ module Sagittarius
44
module Middleware
55
module Grpc
66
class Authentication < Grpc::AllMethodServerInterceptor
7-
def execute(call:, **_)
8-
authorization_token = call.metadata['authorization']
9-
runtime = Runtime.find_by(token: authorization_token)
7+
ANONYMOUS_SERVICES = %w[grpc.health.v1.Health].freeze
108

11-
raise GRPC::Unauthenticated, 'No valid runtime token provided' if runtime.nil?
9+
def execute(call:, method:, **_)
10+
authorization_token = call.metadata['authorization']
11+
runtime = Runtime.find_by(token: authorization_token) if authorization_token.present?
1212

13-
Code0::ZeroTrack::Context.push(runtime: { id: runtime.id, namespace_id: runtime.namespace&.id })
13+
if runtime.present?
14+
Code0::ZeroTrack::Context.push(runtime: { id: runtime.id, namespace_id: runtime.namespace&.id })
15+
elsif ANONYMOUS_SERVICES.exclude?(method.owner.service_name) || authorization_token.present?
16+
raise GRPC::Unauthenticated, 'No valid runtime token provided' if runtime.nil?
17+
end
1418

1519
yield
1620
end

spec/lib/sagittarius/middleware/grpc/authentication_spec.rb

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
require 'rails_helper'
44
require 'google/protobuf/well_known_types'
5+
require 'grpc/health/v1/health_services_pb'
56

67
RSpec.describe Sagittarius::Middleware::Grpc::Authentication do
78
let(:rpc_class) do
@@ -45,11 +46,20 @@ def test(_msg, _call)
4546
end.to raise_error(GRPC::Unauthenticated)
4647
end
4748
# rubocop:enable Lint/EmptyBlock
49+
50+
context 'when anonymous service is called' do
51+
let(:service_class) { Grpc::Health::V1::Health::Service }
52+
let(:method) { service_class.new.method(:check) }
53+
54+
it do
55+
expect { |b| interceptor.request_response(request: request, call: call, method: method, &b) }.to yield_control
56+
end
57+
end
4858
end
4959

5060
context 'when invalid authentication is passed' do
5161
let(:metadata) do
52-
{ authorization: 'token' }
62+
{ 'authorization' => 'token' }
5363
end
5464

5565
# rubocop:disable Lint/EmptyBlock -- the block is part of the api and needs to be given
@@ -59,6 +69,19 @@ def test(_msg, _call)
5969
end.to raise_error(GRPC::Unauthenticated)
6070
end
6171
# rubocop:enable Lint/EmptyBlock
72+
73+
context 'when anonymous service is called' do
74+
let(:service_class) { Grpc::Health::V1::Health::Service }
75+
let(:method) { service_class.new.method(:check) }
76+
77+
# rubocop:disable Lint/EmptyBlock -- the block is part of the api and needs to be given
78+
it do
79+
expect do
80+
interceptor.request_response(request: request, call: call, method: method) {}
81+
end.to raise_error(GRPC::Unauthenticated)
82+
end
83+
# rubocop:enable Lint/EmptyBlock
84+
end
6285
end
6386

6487
context 'when valid authentication is passed' do
@@ -77,6 +100,15 @@ def test(_msg, _call)
77100
namespace_id: nil })
78101
end
79102
# rubocop:enable Lint/EmptyBlock
103+
104+
context 'when anonymous service is called' do
105+
let(:service_class) { Grpc::Health::V1::Health::Service }
106+
let(:method) { service_class.new.method(:check) }
107+
108+
it do
109+
expect { |b| interceptor.request_response(request: request, call: call, method: method, &b) }.to yield_control
110+
end
111+
end
80112
end
81113
end
82114

@@ -93,7 +125,7 @@ def test(_msg, _call)
93125

94126
context 'when invalid authentication is passed' do
95127
let(:metadata) do
96-
{ authorization: 'token' }
128+
{ 'authorization' => 'token' }
97129
end
98130

99131
# rubocop:disable Lint/EmptyBlock -- the block is part of the api and needs to be given
@@ -137,7 +169,7 @@ def test(_msg, _call)
137169

138170
context 'when invalid authentication is passed' do
139171
let(:metadata) do
140-
{ authorization: 'token' }
172+
{ 'authorization' => 'token' }
141173
end
142174

143175
# rubocop:disable Lint/EmptyBlock -- the block is part of the api and needs to be given
@@ -181,7 +213,7 @@ def test(_msg, _call)
181213

182214
context 'when invalid authentication is passed' do
183215
let(:metadata) do
184-
{ authorization: 'token' }
216+
{ 'authorization' => 'token' }
185217
end
186218

187219
# rubocop:disable Lint/EmptyBlock -- the block is part of the api and needs to be given

0 commit comments

Comments
 (0)