gruf 2.15.0 → 2.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|