istox_gruf 2.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +246 -0
  3. data/CODE_OF_CONDUCT.md +46 -0
  4. data/README.md +544 -0
  5. data/bin/gruf +29 -0
  6. data/lib/gruf.rb +50 -0
  7. data/lib/gruf/cli/executor.rb +99 -0
  8. data/lib/gruf/client.rb +217 -0
  9. data/lib/gruf/client/error.rb +66 -0
  10. data/lib/gruf/client/error_factory.rb +105 -0
  11. data/lib/gruf/configuration.rb +137 -0
  12. data/lib/gruf/controllers/base.rb +102 -0
  13. data/lib/gruf/controllers/request.rb +121 -0
  14. data/lib/gruf/controllers/service_binder.rb +117 -0
  15. data/lib/gruf/error.rb +230 -0
  16. data/lib/gruf/errors/debug_info.rb +56 -0
  17. data/lib/gruf/errors/field.rb +56 -0
  18. data/lib/gruf/errors/helpers.rb +44 -0
  19. data/lib/gruf/hooks/base.rb +34 -0
  20. data/lib/gruf/hooks/executor.rb +47 -0
  21. data/lib/gruf/hooks/registry.rb +159 -0
  22. data/lib/gruf/instrumentable_grpc_server.rb +64 -0
  23. data/lib/gruf/integrations/rails/railtie.rb +10 -0
  24. data/lib/gruf/interceptors/active_record/connection_reset.rb +48 -0
  25. data/lib/gruf/interceptors/authentication/basic.rb +87 -0
  26. data/lib/gruf/interceptors/base.rb +53 -0
  27. data/lib/gruf/interceptors/client_interceptor.rb +125 -0
  28. data/lib/gruf/interceptors/context.rb +56 -0
  29. data/lib/gruf/interceptors/instrumentation/output_metadata_timer.rb +61 -0
  30. data/lib/gruf/interceptors/instrumentation/request_logging/formatters/base.rb +41 -0
  31. data/lib/gruf/interceptors/instrumentation/request_logging/formatters/logstash.rb +43 -0
  32. data/lib/gruf/interceptors/instrumentation/request_logging/formatters/plain.rb +48 -0
  33. data/lib/gruf/interceptors/instrumentation/request_logging/interceptor.rb +225 -0
  34. data/lib/gruf/interceptors/instrumentation/statsd.rb +82 -0
  35. data/lib/gruf/interceptors/registry.rb +161 -0
  36. data/lib/gruf/interceptors/server_interceptor.rb +34 -0
  37. data/lib/gruf/interceptors/timer.rb +85 -0
  38. data/lib/gruf/loggable.rb +30 -0
  39. data/lib/gruf/logging.rb +53 -0
  40. data/lib/gruf/outbound/request_context.rb +71 -0
  41. data/lib/gruf/response.rb +71 -0
  42. data/lib/gruf/serializers/errors/base.rb +57 -0
  43. data/lib/gruf/serializers/errors/json.rb +43 -0
  44. data/lib/gruf/server.rb +294 -0
  45. data/lib/gruf/synchronized_client.rb +97 -0
  46. data/lib/gruf/timer.rb +78 -0
  47. data/lib/gruf/version.rb +20 -0
  48. metadata +203 -0
@@ -0,0 +1,71 @@
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
+ module Gruf
19
+ module Outbound
20
+ ##
21
+ # Encapsulates the context of an outbound client request
22
+ #
23
+ class RequestContext
24
+ # @var [Symbol]
25
+ attr_reader :type
26
+ # @var [Enumerable] requests
27
+ attr_reader :requests
28
+ # @var [GRPC::ActiveCall]
29
+ attr_reader :call
30
+ # @var [Method] method
31
+ attr_reader :method
32
+ # @var [Hash] metadata
33
+ attr_reader :metadata
34
+
35
+ ##
36
+ # Initialize the new request context
37
+ #
38
+ # @param [Symbol] type The type of request
39
+ # @param [Enumerable] requests An enumerable of requests being sent
40
+ # @param [GRPC::ActiveCall] call The GRPC ActiveCall object
41
+ # @param [Method] method The method being called
42
+ # @param [Hash] metadata A hash of outgoing metadata
43
+ #
44
+ def initialize(type:, requests:, call:, method:, metadata:)
45
+ @type = type
46
+ @requests = requests
47
+ @call = call
48
+ @method = method
49
+ @metadata = metadata
50
+ end
51
+
52
+ ##
53
+ # Return the name of the method being called, e.g. GetThing
54
+ #
55
+ # @return [String]
56
+ #
57
+ def method_name
58
+ @method.to_s.split('/').last
59
+ end
60
+
61
+ ##
62
+ # Return the proper routing key for the request
63
+ #
64
+ # @return [String]
65
+ #
66
+ def route_key
67
+ @method[1..-1].underscore.tr('/', '.')
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,71 @@
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
+ module Gruf
19
+ ##
20
+ # Wraps the active call operation to provide metadata and timing around the request
21
+ #
22
+ class Response
23
+ # @return [GRPC::ActiveCall::Operation] The operation that was executed for the given request
24
+ attr_reader :operation
25
+ # @return [Hash] The metadata that was attached to the operation
26
+ attr_reader :metadata
27
+ # @return [Hash] The trailing metadata that the service returned
28
+ attr_reader :trailing_metadata
29
+ # @return [Time] The set deadline on the call
30
+ attr_reader :deadline
31
+ # @return [Boolean] Whether or not the operation was cancelled
32
+ attr_reader :cancelled
33
+ # @return [Float] The time that the request took to execute
34
+ attr_reader :execution_time
35
+
36
+ ##
37
+ # Initialize a response object with the given gRPC operation
38
+ #
39
+ # @param [GRPC::ActiveCall::Operation] operation The given operation for the current call
40
+ # @param [StdClass] message
41
+ # @param [Float] execution_time The amount of time that the response took to occur
42
+ #
43
+ def initialize(operation:, message:, execution_time: nil)
44
+ @operation = operation
45
+ @message = message
46
+ @metadata = operation.metadata
47
+ @trailing_metadata = operation.trailing_metadata
48
+ @deadline = operation.deadline
49
+ @cancelled = operation.cancelled?
50
+ @execution_time = execution_time || 0.0
51
+ end
52
+
53
+ ##
54
+ # Return the message returned by the request
55
+ #
56
+ # @return [Object] The protobuf response message
57
+ #
58
+ def message
59
+ @message ||= @operation.execute
60
+ end
61
+
62
+ ##
63
+ # Return execution time of the call internally on the server in ms
64
+ #
65
+ # @return [Float] The execution time of the response
66
+ #
67
+ def internal_execution_time
68
+ trailing_metadata['timer'].to_f
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,57 @@
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
+ module Gruf
19
+ module Serializers
20
+ module Errors
21
+ ##
22
+ # Base class for serialization of errors for transport across the grpc protocol
23
+ #
24
+ class Base
25
+ # @return [Gruf::Error|String] The error being serialized
26
+ attr_reader :error
27
+
28
+ ##
29
+ # @param [Gruf::Error|String] err The error to serialize
30
+ #
31
+ def initialize(err)
32
+ @error = err
33
+ end
34
+
35
+ ##
36
+ # Must be implemented in a derived class. This method should serialize the error into a transportable String
37
+ # that can be pushed into GRPC metadata across the wire.
38
+ #
39
+ # @return [String] The serialized error
40
+ #
41
+ def serialize
42
+ raise NotImplementedError
43
+ end
44
+
45
+ ##
46
+ # Must be implemented in a derived class. This method should deserialize the error object that is transported
47
+ # over the gRPC trailing metadata payload.
48
+ #
49
+ # @return [Object|Hash] The deserialized error object
50
+ #
51
+ def deserialize
52
+ raise NotImplementedError
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,43 @@
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 'json'
19
+
20
+ module Gruf
21
+ module Serializers
22
+ module Errors
23
+ ##
24
+ # Serializes the error via JSON for transport
25
+ #
26
+ class Json < Base
27
+ ##
28
+ # @return [String] The serialized JSON string
29
+ #
30
+ def serialize
31
+ @error.to_h.to_json
32
+ end
33
+
34
+ ##
35
+ # @return [Hash] A hash deserialized from the inputted JSON
36
+ #
37
+ def deserialize
38
+ JSON.parse(@error)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,294 @@
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
+ module Gruf
19
+ ##
20
+ # Represents a gRPC server. Automatically loads and augments gRPC handlers and services
21
+ # based on configuration values.
22
+ #
23
+ class Server
24
+ class ServerAlreadyStartedError < StandardError; end
25
+
26
+ include Gruf::Loggable
27
+
28
+ # @return [Integer] The port the server is bound to
29
+ attr_reader :port
30
+ # @return [Hash] Hash of options passed into the server
31
+ attr_reader :options
32
+
33
+ ##
34
+ # Initialize the server and load and setup the services
35
+ #
36
+ # @param [Hash] opts
37
+ #
38
+ def initialize(opts = {})
39
+ init(opts)
40
+ end
41
+
42
+ def init(opts = {})
43
+ @options = opts || {}
44
+ @interceptors = opts.fetch(:interceptor_registry, Gruf.interceptors)
45
+ @interceptors = Gruf::Interceptors::Registry.new unless @interceptors.is_a?(Gruf::Interceptors::Registry)
46
+ @services = []
47
+ @started = false
48
+ @stop_server = false
49
+ @stop_server_cv = ConditionVariable.new
50
+ @stop_server_mu = Monitor.new
51
+ @server_mu = Monitor.new
52
+ @hostname = opts.fetch(:hostname, Gruf.server_binding_url)
53
+ @event_listener_proc = opts.fetch(:event_listener_proc, Gruf.event_listener_proc)
54
+ setup
55
+ end
56
+
57
+ ##
58
+ # @return [GRPC::RpcServer] The GRPC server running
59
+ #
60
+ def server
61
+ @server_mu.synchronize do
62
+ @server ||= begin
63
+ # For backward compatibility, we allow these options to be passed directly
64
+ # in the Gruf::Server options, or via Gruf.rpc_server_options.
65
+ server_options = {
66
+ pool_size: options.fetch(:pool_size, Gruf.rpc_server_options[:pool_size]),
67
+ max_waiting_requests: options.fetch(:max_waiting_requests, Gruf.rpc_server_options[:max_waiting_requests]),
68
+ poll_period: options.fetch(:poll_period, Gruf.rpc_server_options[:poll_period]),
69
+ pool_keep_alive: options.fetch(:pool_keep_alive, Gruf.rpc_server_options[:pool_keep_alive]),
70
+ connect_md_proc: options.fetch(:connect_md_proc, Gruf.rpc_server_options[:connect_md_proc]),
71
+ server_args: options.fetch(:server_args, Gruf.rpc_server_options[:server_args])
72
+ }
73
+
74
+ server = if @event_listener_proc
75
+ server_options[:event_listener_proc] = @event_listener_proc
76
+ Gruf::InstrumentableGrpcServer.new(server_options)
77
+ else
78
+ GRPC::RpcServer.new(server_options)
79
+ end
80
+
81
+ @port = server.add_http2_port(@hostname, ssl_credentials)
82
+ @services.each { |s| server.handle(s) }
83
+ server
84
+ end
85
+ end
86
+ end
87
+
88
+ ##
89
+ # Start the gRPC server
90
+ #
91
+ # :nocov:
92
+ def start!
93
+ update_proc_title(:starting)
94
+
95
+ server_thread = Thread.new do
96
+ logger.info { "Starting gruf server at #{@hostname}..." }
97
+ server.run
98
+ end
99
+
100
+ stop_server_thread = Thread.new do
101
+ loop do
102
+ break if @stop_server
103
+
104
+ @stop_server_mu.synchronize { @stop_server_cv.wait(@stop_server_mu, 2) }
105
+ end
106
+ logger.info { 'Shutting down...' }
107
+ server.stop
108
+ end
109
+
110
+ server.wait_till_running
111
+ @started = true
112
+ update_proc_title(:serving)
113
+ stop_server_thread.join
114
+ server_thread.join
115
+ @started = false
116
+
117
+ update_proc_title(:stopped)
118
+ logger.info { 'Goodbye!' }
119
+ end
120
+
121
+ def init_restart
122
+ logger.info { 'Restarting gruf...' }
123
+ @restart = true
124
+ @stop_server = true
125
+ end
126
+
127
+ def required_restart
128
+ @restart
129
+ end
130
+
131
+ # :nocov:
132
+
133
+ ##
134
+ # Add a gRPC service stub to be served by gruf
135
+ #
136
+ # @param [Class] klass
137
+ # @raise [ServerAlreadyStartedError] if the server is already started
138
+ #
139
+ def add_service(klass)
140
+ raise ServerAlreadyStartedError if @started
141
+
142
+ @services << klass unless @services.include?(klass)
143
+ end
144
+
145
+ ##
146
+ # Add an interceptor to the server
147
+ #
148
+ # @param [Class] klass The Interceptor to add to the registry
149
+ # @param [Hash] opts A hash of options for the interceptor
150
+ # @raise [ServerAlreadyStartedError] if the server is already started
151
+ #
152
+ def add_interceptor(klass, opts = {})
153
+ raise ServerAlreadyStartedError if @started
154
+
155
+ @interceptors.use(klass, opts)
156
+ end
157
+
158
+ ##
159
+ # Insert an interceptor before another in the currently registered order of execution
160
+ #
161
+ # @param [Class] before_class The interceptor that you want to add the new interceptor before
162
+ # @param [Class] interceptor_class The Interceptor to add to the registry
163
+ # @param [Hash] opts A hash of options for the interceptor
164
+ #
165
+ def insert_interceptor_before(before_class, interceptor_class, opts = {})
166
+ raise ServerAlreadyStartedError if @started
167
+
168
+ @interceptors.insert_before(before_class, interceptor_class, opts)
169
+ end
170
+
171
+ ##
172
+ # Insert an interceptor after another in the currently registered order of execution
173
+ #
174
+ # @param [Class] after_class The interceptor that you want to add the new interceptor after
175
+ # @param [Class] interceptor_class The Interceptor to add to the registry
176
+ # @param [Hash] opts A hash of options for the interceptor
177
+ #
178
+ def insert_interceptor_after(after_class, interceptor_class, opts = {})
179
+ raise ServerAlreadyStartedError if @started
180
+
181
+ @interceptors.insert_after(after_class, interceptor_class, opts)
182
+ end
183
+
184
+ ##
185
+ # Return the current list of added interceptor classes
186
+ #
187
+ # @return [Array<Class>]
188
+ #
189
+ def list_interceptors
190
+ @interceptors.list
191
+ end
192
+
193
+ ##
194
+ # Remove an interceptor from the server
195
+ #
196
+ # @param [Class] klass
197
+ #
198
+ def remove_interceptor(klass)
199
+ raise ServerAlreadyStartedError if @started
200
+
201
+ @interceptors.remove(klass)
202
+ end
203
+
204
+ ##
205
+ # Clear the interceptor registry of interceptors
206
+ #
207
+ def clear_interceptors
208
+ raise ServerAlreadyStartedError if @started
209
+
210
+ @interceptors.clear
211
+ end
212
+
213
+ private
214
+
215
+ ##
216
+ # Setup server
217
+ #
218
+ # :nocov:
219
+ def setup
220
+ setup_signal_handlers
221
+ load_controllers
222
+ end
223
+ # :nocov:
224
+
225
+ ##
226
+ # Register signal handlers
227
+ #
228
+ # :nocov:
229
+ def setup_signal_handlers
230
+ Signal.trap('INT') do
231
+ @stop_server = true
232
+ @stop_server_cv.broadcast
233
+ end
234
+
235
+ Signal.trap('TERM') do
236
+ @stop_server = true
237
+ @stop_server_cv.broadcast
238
+ end
239
+ end
240
+ # :nocov:
241
+
242
+ ##
243
+ # Auto-load all gRPC handlers
244
+ #
245
+ # :nocov:
246
+ def load_controllers
247
+ return unless File.directory?(controllers_path)
248
+
249
+ path = File.realpath(controllers_path)
250
+ $LOAD_PATH.unshift(path)
251
+ Dir["#{path}/**/*.rb"].each do |f|
252
+ next if f.include?('_pb') # exclude if people include proto generated files in app/rpc
253
+
254
+ logger.info "- Loading gRPC service file: #{f}"
255
+ load File.realpath(f)
256
+ end
257
+ end
258
+ # :nocov:
259
+
260
+ ##
261
+ # @param [String]
262
+ #
263
+ def controllers_path
264
+ options.fetch(:controllers_path, Gruf.controllers_path)
265
+ end
266
+
267
+ ##
268
+ # Load the SSL/TLS credentials for this server
269
+ #
270
+ # @return [GRPC::Core::ServerCredentials|Symbol]
271
+ #
272
+ # :nocov:
273
+ def ssl_credentials
274
+ return :this_port_is_insecure unless options.fetch(:use_ssl, Gruf.use_ssl)
275
+
276
+ private_key = File.read(options.fetch(:ssl_key_file, Gruf.ssl_key_file))
277
+ cert_chain = File.read(options.fetch(:ssl_crt_file, Gruf.ssl_crt_file))
278
+ certs = [nil, [{ private_key: private_key, cert_chain: cert_chain }], false]
279
+ GRPC::Core::ServerCredentials.new(*certs)
280
+ end
281
+ # :nocov:
282
+
283
+ ##
284
+ # Updates proc name/title
285
+ #
286
+ # @param [Symbol] state
287
+ #
288
+ # :nocov:
289
+ def update_proc_title(state)
290
+ Process.setproctitle("gruf #{Gruf::VERSION} -- #{state}")
291
+ end
292
+ # :nocov:
293
+ end
294
+ end