gruf 2.15.0 → 2.16.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -0
- data/gruf.gemspec +2 -7
- data/lib/gruf/cli/executor.rb +86 -14
- data/lib/gruf/configuration.rb +12 -2
- data/lib/gruf/controllers/health_controller.rb +39 -0
- data/lib/gruf/controllers/request.rb +6 -0
- data/lib/gruf/version.rb +1 -1
- data/lib/gruf.rb +2 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1f7ea5bc2b3a0c64008a565c470d19b948814f9c636ccdea72b93703f384ca77
|
4
|
+
data.tar.gz: f5a23c82e85208104baec460d93f650e66189dfb3b27dc0c409dc32b21bcda4a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b227b3fc5253dbace401291e9643e2703244e9e83020d9516358ed78caef24f56668d61f9411f1b2dc25fa5854d711cc5b54346937f71d6835e00f98cd003e46
|
7
|
+
data.tar.gz: 61f863b87320654ec2b247953b3817ae854ae4e601bc0c09206803a8cc2e453a8f80c77b0679b2cb03f870f5a0ba6c44ca8be8bc2ea6f1d34d0c03c7527b888b
|
data/CHANGELOG.md
CHANGED
@@ -2,8 +2,28 @@ Changelog for the gruf gem. This includes internal history before the gem was ma
|
|
2
2
|
|
3
3
|
### Pending release
|
4
4
|
|
5
|
+
### 2.16.1
|
6
|
+
|
7
|
+
* Fix issue where default gRPC health check was loaded even if unused or not desired; now only loaded when requested
|
8
|
+
|
9
|
+
### 2.16.0
|
10
|
+
|
11
|
+
- Add opt-in ability to serve the official [gRPC health check](https://github.com/grpc/grpc/blob/master/src/ruby/pb/grpc/health/v1/health_services_pb.rb)
|
12
|
+
automatically via `health_check_enabled` configuration option (or `GRUF_HEALTH_CHECK_ENABLED` environment
|
13
|
+
variable).
|
14
|
+
- Add `health_check_hook` configuration option to implement a custom response for the above gRPC built-in health check
|
15
|
+
- [#156] Allow passing a specific list of services to run via the gruf binstub
|
16
|
+
- [#163] Add `context` hash attribute to `Gruf::Controllers::Request` to allow interceptors to pass information down
|
17
|
+
to a gruf controller
|
18
|
+
- Drop Ruby 2.6 support (EOL'ed on March 31st, 2022)
|
19
|
+
|
20
|
+
### 2.15.1
|
21
|
+
|
22
|
+
- Fix issue where GRPC_SERVER_POOL_KEEP_ALIVE and GRPC_SERVER_POLL_PERIOD when set via ENV are not cast to int
|
23
|
+
|
5
24
|
### 2.15.0
|
6
25
|
|
26
|
+
- NOTE: This changes the way that gruf controllers are autoloaded. See [UPGRADING.md] for more details.
|
7
27
|
- Autoload Gruf Controllers with zeitwerk, allowing for code reloading in development environments
|
8
28
|
- Add `GRUF_CONTROLLERS_PATH` ENV to allow ENV-based runtime configuration of path to gruf controller files
|
9
29
|
- Move `ServiceBinder` from instance to static class for performance improvements
|
data/gruf.gemspec
CHANGED
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.executables << 'gruf'
|
33
33
|
spec.require_paths = ['lib']
|
34
34
|
|
35
|
-
spec.required_ruby_version = '>= 2.
|
35
|
+
spec.required_ruby_version = '>= 2.7', '< 3.2'
|
36
36
|
|
37
37
|
spec.metadata = {
|
38
38
|
'bug_tracker_uri' => 'https://github.com/bigcommerce/gruf/issues',
|
@@ -44,12 +44,7 @@ Gem::Specification.new do |spec|
|
|
44
44
|
}
|
45
45
|
|
46
46
|
spec.add_development_dependency 'bundler-audit', '>= 0.6'
|
47
|
-
|
48
|
-
spec.add_development_dependency(
|
49
|
-
'factory_bot',
|
50
|
-
(Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5') ? '>= 6.1' : '~> 5.2')
|
51
|
-
)
|
52
|
-
# rubocop:enable Gemspec/RubyVersionGlobalsUsage
|
47
|
+
spec.add_development_dependency 'factory_bot', '>= 6.1'
|
53
48
|
spec.add_development_dependency 'ffaker', '>= 2.15'
|
54
49
|
spec.add_development_dependency 'pry', '~> 0.12'
|
55
50
|
spec.add_development_dependency 'pry-byebug', '>= 3.9'
|
data/lib/gruf/cli/executor.rb
CHANGED
@@ -52,19 +52,11 @@ module Gruf
|
|
52
52
|
#
|
53
53
|
def run
|
54
54
|
exception = nil
|
55
|
-
# wait to load controllers until last possible second to allow late configuration
|
56
|
-
::Gruf.autoloaders.load!(controllers_path: Gruf.controllers_path)
|
57
|
-
# allow lazy registering globally as late as possible, this allows more flexible binstub injections
|
58
|
-
@services = ::Gruf.services unless @services&.any?
|
59
55
|
|
60
|
-
|
61
|
-
|
62
|
-
'No services bound to this gruf process; please bind a service to a Gruf controller ' \
|
63
|
-
'to start the server successfully'
|
64
|
-
end
|
56
|
+
# allow lazy registering globally as late as possible, this allows more flexible binstub injections
|
57
|
+
register_services!
|
65
58
|
|
66
59
|
begin
|
67
|
-
@services.each { |s| @server.add_service(s) }
|
68
60
|
@hook_executor.call(:before_server_start, server: @server)
|
69
61
|
@server.start!
|
70
62
|
rescue StandardError => e
|
@@ -84,14 +76,15 @@ module Gruf
|
|
84
76
|
# Setup options for CLI execution and configure Gruf based on inputs
|
85
77
|
#
|
86
78
|
def setup!
|
87
|
-
|
79
|
+
@options = parse_options
|
88
80
|
|
89
|
-
Gruf.server_binding_url =
|
90
|
-
if
|
81
|
+
Gruf.server_binding_url = @options[:host] if @options[:host]
|
82
|
+
if @options.suppress_default_interceptors?
|
91
83
|
Gruf.interceptors.remove(Gruf::Interceptors::ActiveRecord::ConnectionReset)
|
92
84
|
Gruf.interceptors.remove(Gruf::Interceptors::Instrumentation::OutputMetadataTimer)
|
93
85
|
end
|
94
|
-
Gruf.backtrace_on_error = true if
|
86
|
+
Gruf.backtrace_on_error = true if @options.backtrace_on_error?
|
87
|
+
Gruf.health_check_enabled = true if @options.health_check?
|
95
88
|
end
|
96
89
|
|
97
90
|
##
|
@@ -106,6 +99,8 @@ module Gruf
|
|
106
99
|
exit(0)
|
107
100
|
end
|
108
101
|
o.string '--host', 'Specify the binding url for the gRPC service'
|
102
|
+
o.string '--services', 'Optional. Run gruf with only the passed gRPC service classes (comma-separated)'
|
103
|
+
o.bool '--health-check', 'Serve the default gRPC health check (defaults to false). '
|
109
104
|
o.bool '--suppress-default-interceptors', 'Do not use the default interceptors'
|
110
105
|
o.bool '--backtrace-on-error', 'Push backtraces on exceptions to the error serializer'
|
111
106
|
o.null '-v', '--version', 'print gruf version' do
|
@@ -114,6 +109,83 @@ module Gruf
|
|
114
109
|
end
|
115
110
|
end
|
116
111
|
end
|
112
|
+
|
113
|
+
##
|
114
|
+
# Register services; note that this happens after gruf is initialized, and right before the server is run.
|
115
|
+
# This will interpret the services to run in the following precedence:
|
116
|
+
# 1. initializer arguments to the executor
|
117
|
+
# 2. ARGV options (the --services option)
|
118
|
+
# 3. services set to the global gruf configuration (Gruf.services)
|
119
|
+
#
|
120
|
+
def register_services!
|
121
|
+
# wait to load controllers until last possible second to allow late configuration
|
122
|
+
::Gruf.autoloaders.load!(controllers_path: Gruf.controllers_path)
|
123
|
+
|
124
|
+
services = determine_services(@services)
|
125
|
+
services = bind_health_check!(services) if health_check_enabled?
|
126
|
+
|
127
|
+
services.map! { |s| s.is_a?(Class) ? s : s.constantize }
|
128
|
+
|
129
|
+
if services.any?
|
130
|
+
services.each { |s| @server.add_service(s) }
|
131
|
+
return
|
132
|
+
end
|
133
|
+
|
134
|
+
raise NoServicesBoundError
|
135
|
+
rescue NoServicesBoundError
|
136
|
+
@logger.fatal 'FATAL ERROR: No services bound to this gruf process; please bind a service to a Gruf ' \
|
137
|
+
'controller to start the server successfully'
|
138
|
+
exit(1)
|
139
|
+
rescue NameError => e
|
140
|
+
@logger.fatal 'FATAL ERROR: Could not start server; passed services to run are not loaded or valid ' \
|
141
|
+
"constants: #{e.message}"
|
142
|
+
exit(1)
|
143
|
+
rescue StandardError => e
|
144
|
+
@logger.fatal "FATAL ERROR: Could not start server: #{e.message}"
|
145
|
+
exit(1)
|
146
|
+
end
|
147
|
+
|
148
|
+
##
|
149
|
+
# @return [Boolean]
|
150
|
+
#
|
151
|
+
def health_check_enabled?
|
152
|
+
::Gruf.health_check_enabled
|
153
|
+
end
|
154
|
+
|
155
|
+
##
|
156
|
+
# Load the health check if enabled into the services array
|
157
|
+
#
|
158
|
+
# @param [Array<Class>] services
|
159
|
+
# @return [Array<Class>]
|
160
|
+
#
|
161
|
+
def bind_health_check!(services)
|
162
|
+
# do this here to trigger autoloading the controller in zeitwerk, since we don't explicitly load this
|
163
|
+
# controller. This binds the service and makes sure the method handlers are setup.
|
164
|
+
require 'gruf/controllers/health_controller'
|
165
|
+
# if we're already bound to the services array (say someone explicitly passes the health check in, skip)
|
166
|
+
return services if services.include?(::Grpc::Health::V1::Health::Service)
|
167
|
+
|
168
|
+
# otherwise, manually add the grpc service
|
169
|
+
services << ::Grpc::Health::V1::Health::Service
|
170
|
+
services
|
171
|
+
end
|
172
|
+
|
173
|
+
##
|
174
|
+
# Determine how we load services (initializer -> ARGV -> Gruf.services)
|
175
|
+
#
|
176
|
+
# @return [Array<Class>]
|
177
|
+
#
|
178
|
+
def determine_services(services = [])
|
179
|
+
# first check initializer arguments
|
180
|
+
return services if services.any?
|
181
|
+
|
182
|
+
# next check CLI arguments
|
183
|
+
services = @options[:services].to_s.split(',').map(&:strip).uniq
|
184
|
+
# finally, if none, use global gruf autoloaded services
|
185
|
+
services = (::Gruf.services || []) unless services.any?
|
186
|
+
|
187
|
+
services
|
188
|
+
end
|
117
189
|
end
|
118
190
|
end
|
119
191
|
end
|
data/lib/gruf/configuration.rb
CHANGED
@@ -77,6 +77,13 @@ module Gruf
|
|
77
77
|
# @return [Integer] Internal cache expiry period (in seconds) for the SynchronizedClient
|
78
78
|
# @!attribute rpc_server_options
|
79
79
|
# @return [Hash] A hash of RPC options for GRPC server configuration
|
80
|
+
# @!attribute health_check_enabled
|
81
|
+
# @return [Boolean] If true, will load and register `Gruf::Controllers::HealthController` with the default gRPC
|
82
|
+
# health check to the loaded gRPC server
|
83
|
+
# @!attribute health_check_hook
|
84
|
+
# @return [NilClass]
|
85
|
+
# @return [Proc] If set, will call this in the gRPC health check. It is required to return a
|
86
|
+
# `::Grpc::Health::V1::HealthCheckResponse` object in this proc to indicate the health of the server.
|
80
87
|
VALID_CONFIG_KEYS = {
|
81
88
|
root_path: '',
|
82
89
|
server_binding_url: '0.0.0.0:9001',
|
@@ -101,6 +108,8 @@ module Gruf
|
|
101
108
|
use_exception_message: true,
|
102
109
|
internal_error_message: 'Internal Server Error',
|
103
110
|
event_listener_proc: nil,
|
111
|
+
health_check_enabled: false,
|
112
|
+
health_check_hook: nil,
|
104
113
|
synchronized_client_internal_cache_expiry: 60,
|
105
114
|
rpc_server_options: {
|
106
115
|
pool_size: GRPC::RpcServer::DEFAULT_POOL_SIZE,
|
@@ -172,8 +181,8 @@ module Gruf
|
|
172
181
|
max_waiting_requests: ::ENV.fetch('GRPC_SERVER_MAX_WAITING_REQUESTS',
|
173
182
|
GRPC::RpcServer::DEFAULT_MAX_WAITING_REQUESTS).to_i,
|
174
183
|
pool_size: ::ENV.fetch('GRPC_SERVER_POOL_SIZE', GRPC::RpcServer::DEFAULT_POOL_SIZE).to_i,
|
175
|
-
pool_keep_alive: ::ENV.fetch('GRPC_SERVER_POOL_KEEP_ALIVE', GRPC::Pool::DEFAULT_KEEP_ALIVE),
|
176
|
-
poll_period: ::ENV.fetch('GRPC_SERVER_POLL_PERIOD', GRPC::RpcServer::DEFAULT_POLL_PERIOD),
|
184
|
+
pool_keep_alive: ::ENV.fetch('GRPC_SERVER_POOL_KEEP_ALIVE', GRPC::Pool::DEFAULT_KEEP_ALIVE).to_i,
|
185
|
+
poll_period: ::ENV.fetch('GRPC_SERVER_POLL_PERIOD', GRPC::RpcServer::DEFAULT_POLL_PERIOD).to_i,
|
177
186
|
connect_md_proc: nil,
|
178
187
|
server_args: {}
|
179
188
|
}
|
@@ -181,6 +190,7 @@ module Gruf
|
|
181
190
|
interceptors.use(::Gruf::Interceptors::ActiveRecord::ConnectionReset)
|
182
191
|
interceptors.use(::Gruf::Interceptors::Instrumentation::OutputMetadataTimer)
|
183
192
|
end
|
193
|
+
self.health_check_enabled = ::ENV.fetch('GRUF_HEALTH_CHECK_ENABLED', 0).to_i.positive?
|
184
194
|
options
|
185
195
|
end
|
186
196
|
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
6
|
+
# documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
7
|
+
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
|
8
|
+
# persons to whom the Software is furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
11
|
+
# Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
14
|
+
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
15
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
16
|
+
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
17
|
+
#
|
18
|
+
require 'grpc/health/v1/health_services_pb'
|
19
|
+
|
20
|
+
module Gruf
|
21
|
+
module Controllers
|
22
|
+
##
|
23
|
+
# Dynamic standard grpc health check controller. Can be used as-is, or can use ::Gruf.health_check_hook to
|
24
|
+
# provide custom responses.
|
25
|
+
#
|
26
|
+
class HealthController < Gruf::Controllers::Base
|
27
|
+
bind ::Grpc::Health::V1::Health::Service
|
28
|
+
|
29
|
+
def check
|
30
|
+
health_proc = ::Gruf.health_check_hook
|
31
|
+
return health_proc.call(request, error) if !health_proc.nil? && health_proc.respond_to?(:call)
|
32
|
+
|
33
|
+
::Grpc::Health::V1::HealthCheckResponse.new(
|
34
|
+
status: ::Grpc::Health::V1::HealthCheckResponse::ServingStatus::SERVING
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -36,6 +36,11 @@ module Gruf
|
|
36
36
|
# @!attribute [r] service
|
37
37
|
# @return [Class] The GRPC service class for this request
|
38
38
|
attr_reader :service
|
39
|
+
# @!attribute [r] context
|
40
|
+
# @return [::ActiveSupport::HashWithIndifferentAccess] An arbitrary hash of key/value entries that are
|
41
|
+
# accessible for interceptors, that can be used to shared information between interceptors and pass down into
|
42
|
+
# the controller.
|
43
|
+
attr_reader :context
|
39
44
|
|
40
45
|
delegate :metadata, to: :active_call
|
41
46
|
delegate :messages, :client_streamer?, :server_streamer?, :bidi_streamer?, :request_response?, to: :type
|
@@ -72,6 +77,7 @@ module Gruf
|
|
72
77
|
@message = message
|
73
78
|
@rpc_desc = rpc_desc
|
74
79
|
@type = Type.new(rpc_desc)
|
80
|
+
@context = ::ActiveSupport::HashWithIndifferentAccess.new
|
75
81
|
end
|
76
82
|
|
77
83
|
##
|
data/lib/gruf/version.rb
CHANGED
data/lib/gruf.rb
CHANGED
@@ -17,6 +17,7 @@
|
|
17
17
|
#
|
18
18
|
require 'grpc'
|
19
19
|
require 'active_support/core_ext/module/delegation'
|
20
|
+
require 'active_support/hash_with_indifferent_access'
|
20
21
|
require 'active_support/concern'
|
21
22
|
require 'active_support/inflector'
|
22
23
|
require 'base64'
|
@@ -27,6 +28,7 @@ loader = ::Zeitwerk::Loader.new
|
|
27
28
|
loader.tag = File.basename(__FILE__, '.rb')
|
28
29
|
loader.inflector = ::Zeitwerk::GemInflector.new(__FILE__)
|
29
30
|
loader.ignore("#{__dir__}/gruf/integrations/rails/railtie.rb")
|
31
|
+
loader.ignore("#{__dir__}/gruf/controllers/health_controller.rb")
|
30
32
|
loader.push_dir(__dir__)
|
31
33
|
loader.setup
|
32
34
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gruf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.16.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shaun McCormick
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-08-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler-audit
|
@@ -341,6 +341,7 @@ files:
|
|
341
341
|
- lib/gruf/configuration.rb
|
342
342
|
- lib/gruf/controllers/autoloader.rb
|
343
343
|
- lib/gruf/controllers/base.rb
|
344
|
+
- lib/gruf/controllers/health_controller.rb
|
344
345
|
- lib/gruf/controllers/request.rb
|
345
346
|
- lib/gruf/controllers/service_binder.rb
|
346
347
|
- lib/gruf/error.rb
|
@@ -395,7 +396,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
395
396
|
requirements:
|
396
397
|
- - ">="
|
397
398
|
- !ruby/object:Gem::Version
|
398
|
-
version: '2.
|
399
|
+
version: '2.7'
|
399
400
|
- - "<"
|
400
401
|
- !ruby/object:Gem::Version
|
401
402
|
version: '3.2'
|
@@ -405,7 +406,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
405
406
|
- !ruby/object:Gem::Version
|
406
407
|
version: '0'
|
407
408
|
requirements: []
|
408
|
-
rubygems_version: 3.3.
|
409
|
+
rubygems_version: 3.3.3
|
409
410
|
signing_key:
|
410
411
|
specification_version: 4
|
411
412
|
summary: gRPC Ruby Framework
|