grpc 1.42.0.pre1-x86_64-darwin

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of grpc might be problematic. Click here for more details.

Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/etc/roots.pem +4337 -0
  3. data/grpc_c.32.ruby +0 -0
  4. data/grpc_c.64.ruby +0 -0
  5. data/src/ruby/bin/math_client.rb +140 -0
  6. data/src/ruby/bin/math_pb.rb +34 -0
  7. data/src/ruby/bin/math_server.rb +191 -0
  8. data/src/ruby/bin/math_services_pb.rb +51 -0
  9. data/src/ruby/bin/noproto_client.rb +93 -0
  10. data/src/ruby/bin/noproto_server.rb +97 -0
  11. data/src/ruby/ext/grpc/ext-export.clang +1 -0
  12. data/src/ruby/ext/grpc/ext-export.gcc +6 -0
  13. data/src/ruby/ext/grpc/extconf.rb +123 -0
  14. data/src/ruby/ext/grpc/rb_byte_buffer.c +65 -0
  15. data/src/ruby/ext/grpc/rb_byte_buffer.h +35 -0
  16. data/src/ruby/ext/grpc/rb_call.c +1051 -0
  17. data/src/ruby/ext/grpc/rb_call.h +57 -0
  18. data/src/ruby/ext/grpc/rb_call_credentials.c +341 -0
  19. data/src/ruby/ext/grpc/rb_call_credentials.h +31 -0
  20. data/src/ruby/ext/grpc/rb_channel.c +846 -0
  21. data/src/ruby/ext/grpc/rb_channel.h +34 -0
  22. data/src/ruby/ext/grpc/rb_channel_args.c +155 -0
  23. data/src/ruby/ext/grpc/rb_channel_args.h +38 -0
  24. data/src/ruby/ext/grpc/rb_channel_credentials.c +286 -0
  25. data/src/ruby/ext/grpc/rb_channel_credentials.h +37 -0
  26. data/src/ruby/ext/grpc/rb_completion_queue.c +101 -0
  27. data/src/ruby/ext/grpc/rb_completion_queue.h +36 -0
  28. data/src/ruby/ext/grpc/rb_compression_options.c +471 -0
  29. data/src/ruby/ext/grpc/rb_compression_options.h +29 -0
  30. data/src/ruby/ext/grpc/rb_enable_cpp.cc +22 -0
  31. data/src/ruby/ext/grpc/rb_event_thread.c +145 -0
  32. data/src/ruby/ext/grpc/rb_event_thread.h +21 -0
  33. data/src/ruby/ext/grpc/rb_grpc.c +333 -0
  34. data/src/ruby/ext/grpc/rb_grpc.h +77 -0
  35. data/src/ruby/ext/grpc/rb_grpc_imports.generated.c +605 -0
  36. data/src/ruby/ext/grpc/rb_grpc_imports.generated.h +913 -0
  37. data/src/ruby/ext/grpc/rb_loader.c +57 -0
  38. data/src/ruby/ext/grpc/rb_loader.h +25 -0
  39. data/src/ruby/ext/grpc/rb_server.c +385 -0
  40. data/src/ruby/ext/grpc/rb_server.h +32 -0
  41. data/src/ruby/ext/grpc/rb_server_credentials.c +259 -0
  42. data/src/ruby/ext/grpc/rb_server_credentials.h +37 -0
  43. data/src/ruby/ext/grpc/rb_xds_channel_credentials.c +218 -0
  44. data/src/ruby/ext/grpc/rb_xds_channel_credentials.h +37 -0
  45. data/src/ruby/ext/grpc/rb_xds_server_credentials.c +170 -0
  46. data/src/ruby/ext/grpc/rb_xds_server_credentials.h +37 -0
  47. data/src/ruby/lib/grpc/2.4/grpc_c.bundle +0 -0
  48. data/src/ruby/lib/grpc/2.5/grpc_c.bundle +0 -0
  49. data/src/ruby/lib/grpc/2.6/grpc_c.bundle +0 -0
  50. data/src/ruby/lib/grpc/2.7/grpc_c.bundle +0 -0
  51. data/src/ruby/lib/grpc/3.0/grpc_c.bundle +0 -0
  52. data/src/ruby/lib/grpc/core/status_codes.rb +135 -0
  53. data/src/ruby/lib/grpc/core/time_consts.rb +56 -0
  54. data/src/ruby/lib/grpc/errors.rb +277 -0
  55. data/src/ruby/lib/grpc/generic/active_call.rb +669 -0
  56. data/src/ruby/lib/grpc/generic/bidi_call.rb +233 -0
  57. data/src/ruby/lib/grpc/generic/client_stub.rb +503 -0
  58. data/src/ruby/lib/grpc/generic/interceptor_registry.rb +53 -0
  59. data/src/ruby/lib/grpc/generic/interceptors.rb +186 -0
  60. data/src/ruby/lib/grpc/generic/rpc_desc.rb +204 -0
  61. data/src/ruby/lib/grpc/generic/rpc_server.rb +551 -0
  62. data/src/ruby/lib/grpc/generic/service.rb +211 -0
  63. data/src/ruby/lib/grpc/google_rpc_status_utils.rb +40 -0
  64. data/src/ruby/lib/grpc/grpc.rb +24 -0
  65. data/src/ruby/lib/grpc/logconfig.rb +44 -0
  66. data/src/ruby/lib/grpc/notifier.rb +45 -0
  67. data/src/ruby/lib/grpc/structs.rb +15 -0
  68. data/src/ruby/lib/grpc/version.rb +18 -0
  69. data/src/ruby/lib/grpc.rb +37 -0
  70. data/src/ruby/pb/README.md +42 -0
  71. data/src/ruby/pb/generate_proto_ruby.sh +51 -0
  72. data/src/ruby/pb/grpc/health/checker.rb +75 -0
  73. data/src/ruby/pb/grpc/health/v1/health_pb.rb +31 -0
  74. data/src/ruby/pb/grpc/health/v1/health_services_pb.rb +62 -0
  75. data/src/ruby/pb/grpc/testing/duplicate/echo_duplicate_services_pb.rb +44 -0
  76. data/src/ruby/pb/grpc/testing/metrics_pb.rb +28 -0
  77. data/src/ruby/pb/grpc/testing/metrics_services_pb.rb +49 -0
  78. data/src/ruby/pb/src/proto/grpc/testing/empty_pb.rb +17 -0
  79. data/src/ruby/pb/src/proto/grpc/testing/messages_pb.rb +145 -0
  80. data/src/ruby/pb/src/proto/grpc/testing/test_pb.rb +16 -0
  81. data/src/ruby/pb/src/proto/grpc/testing/test_services_pb.rb +152 -0
  82. data/src/ruby/pb/test/client.rb +769 -0
  83. data/src/ruby/pb/test/server.rb +252 -0
  84. data/src/ruby/pb/test/xds_client.rb +415 -0
  85. data/src/ruby/spec/call_credentials_spec.rb +42 -0
  86. data/src/ruby/spec/call_spec.rb +180 -0
  87. data/src/ruby/spec/channel_connection_spec.rb +126 -0
  88. data/src/ruby/spec/channel_credentials_spec.rb +124 -0
  89. data/src/ruby/spec/channel_spec.rb +245 -0
  90. data/src/ruby/spec/client_auth_spec.rb +152 -0
  91. data/src/ruby/spec/client_server_spec.rb +664 -0
  92. data/src/ruby/spec/compression_options_spec.rb +149 -0
  93. data/src/ruby/spec/debug_message_spec.rb +134 -0
  94. data/src/ruby/spec/error_sanity_spec.rb +49 -0
  95. data/src/ruby/spec/errors_spec.rb +142 -0
  96. data/src/ruby/spec/generic/active_call_spec.rb +683 -0
  97. data/src/ruby/spec/generic/client_interceptors_spec.rb +153 -0
  98. data/src/ruby/spec/generic/client_stub_spec.rb +1083 -0
  99. data/src/ruby/spec/generic/interceptor_registry_spec.rb +65 -0
  100. data/src/ruby/spec/generic/rpc_desc_spec.rb +374 -0
  101. data/src/ruby/spec/generic/rpc_server_pool_spec.rb +127 -0
  102. data/src/ruby/spec/generic/rpc_server_spec.rb +748 -0
  103. data/src/ruby/spec/generic/server_interceptors_spec.rb +218 -0
  104. data/src/ruby/spec/generic/service_spec.rb +263 -0
  105. data/src/ruby/spec/google_rpc_status_utils_spec.rb +282 -0
  106. data/src/ruby/spec/pb/codegen/grpc/testing/package_options.proto +28 -0
  107. data/src/ruby/spec/pb/codegen/grpc/testing/package_options_import.proto +22 -0
  108. data/src/ruby/spec/pb/codegen/grpc/testing/package_options_import2.proto +23 -0
  109. data/src/ruby/spec/pb/codegen/grpc/testing/package_options_ruby_style.proto +41 -0
  110. data/src/ruby/spec/pb/codegen/grpc/testing/same_package_service_name.proto +27 -0
  111. data/src/ruby/spec/pb/codegen/grpc/testing/same_ruby_package_service_name.proto +29 -0
  112. data/src/ruby/spec/pb/codegen/package_option_spec.rb +98 -0
  113. data/src/ruby/spec/pb/duplicate/codegen_spec.rb +57 -0
  114. data/src/ruby/spec/pb/health/checker_spec.rb +236 -0
  115. data/src/ruby/spec/server_credentials_spec.rb +104 -0
  116. data/src/ruby/spec/server_spec.rb +231 -0
  117. data/src/ruby/spec/spec_helper.rb +61 -0
  118. data/src/ruby/spec/support/helpers.rb +107 -0
  119. data/src/ruby/spec/support/services.rb +160 -0
  120. data/src/ruby/spec/testdata/README +1 -0
  121. data/src/ruby/spec/testdata/ca.pem +20 -0
  122. data/src/ruby/spec/testdata/client.key +28 -0
  123. data/src/ruby/spec/testdata/client.pem +20 -0
  124. data/src/ruby/spec/testdata/server1.key +28 -0
  125. data/src/ruby/spec/testdata/server1.pem +22 -0
  126. data/src/ruby/spec/time_consts_spec.rb +74 -0
  127. data/src/ruby/spec/user_agent_spec.rb +74 -0
  128. metadata +404 -0
@@ -0,0 +1,252 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Copyright 2015 gRPC authors.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ # interop_server is a Testing app that runs a gRPC interop testing server.
18
+ #
19
+ # It helps validate interoperation b/w gRPC in different environments
20
+ #
21
+ # Helps validate interoperation b/w different gRPC implementations.
22
+ #
23
+ # Usage: $ path/to/interop_server.rb --port
24
+
25
+ this_dir = File.expand_path(File.dirname(__FILE__))
26
+ lib_dir = File.join(File.dirname(File.dirname(this_dir)), 'lib')
27
+ pb_dir = File.dirname(this_dir)
28
+ $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
29
+ $LOAD_PATH.unshift(pb_dir) unless $LOAD_PATH.include?(pb_dir)
30
+ $LOAD_PATH.unshift(this_dir) unless $LOAD_PATH.include?(this_dir)
31
+
32
+ require 'forwardable'
33
+ require 'logger'
34
+ require 'optparse'
35
+
36
+ require 'grpc'
37
+
38
+ require_relative '../src/proto/grpc/testing/empty_pb'
39
+ require_relative '../src/proto/grpc/testing/messages_pb'
40
+ require_relative '../src/proto/grpc/testing/test_services_pb'
41
+
42
+ # DebugIsTruncated extends the default Logger to truncate debug messages
43
+ class DebugIsTruncated < Logger
44
+ def debug(s)
45
+ super(truncate(s, 1024))
46
+ end
47
+
48
+ # Truncates a given +text+ after a given <tt>length</tt> if +text+ is longer than <tt>length</tt>:
49
+ #
50
+ # 'Once upon a time in a world far far away'.truncate(27)
51
+ # # => "Once upon a time in a wo..."
52
+ #
53
+ # Pass a string or regexp <tt>:separator</tt> to truncate +text+ at a natural break:
54
+ #
55
+ # 'Once upon a time in a world far far away'.truncate(27, separator: ' ')
56
+ # # => "Once upon a time in a..."
57
+ #
58
+ # 'Once upon a time in a world far far away'.truncate(27, separator: /\s/)
59
+ # # => "Once upon a time in a..."
60
+ #
61
+ # The last characters will be replaced with the <tt>:omission</tt> string (defaults to "...")
62
+ # for a total length not exceeding <tt>length</tt>:
63
+ #
64
+ # 'And they found that many people were sleeping better.'.truncate(25, omission: '... (continued)')
65
+ # # => "And they f... (continued)"
66
+ def truncate(s, truncate_at, options = {})
67
+ return s unless s.length > truncate_at
68
+ omission = options[:omission] || '...'
69
+ with_extra_room = truncate_at - omission.length
70
+ stop = \
71
+ if options[:separator]
72
+ rindex(options[:separator], with_extra_room) || with_extra_room
73
+ else
74
+ with_extra_room
75
+ end
76
+ "#{s[0, stop]}#{omission}"
77
+ end
78
+ end
79
+
80
+ # RubyLogger defines a logger for gRPC based on the standard ruby logger.
81
+ module RubyLogger
82
+ def logger
83
+ LOGGER
84
+ end
85
+
86
+ LOGGER = DebugIsTruncated.new(STDOUT)
87
+ LOGGER.level = Logger::WARN
88
+ end
89
+
90
+ # GRPC is the general RPC module
91
+ module GRPC
92
+ # Inject the noop #logger if no module-level logger method has been injected.
93
+ extend RubyLogger
94
+ end
95
+
96
+ # loads the certificates by the test server.
97
+ def load_test_certs
98
+ this_dir = File.expand_path(File.dirname(__FILE__))
99
+ data_dir = File.join(File.dirname(File.dirname(this_dir)), 'spec/testdata')
100
+ files = ['ca.pem', 'server1.key', 'server1.pem']
101
+ files.map { |f| File.open(File.join(data_dir, f)).read }
102
+ end
103
+
104
+ # creates a ServerCredentials from the test certificates.
105
+ def test_server_creds
106
+ certs = load_test_certs
107
+ GRPC::Core::ServerCredentials.new(
108
+ nil, [{private_key: certs[1], cert_chain: certs[2]}], false)
109
+ end
110
+
111
+ # produces a string of null chars (\0) of length l.
112
+ def nulls(l)
113
+ fail 'requires #{l} to be +ve' if l < 0
114
+ [].pack('x' * l).force_encoding('ascii-8bit')
115
+ end
116
+
117
+ def maybe_echo_metadata(_call)
118
+
119
+ # these are consistent for all interop tests
120
+ initial_metadata_key = "x-grpc-test-echo-initial"
121
+ trailing_metadata_key = "x-grpc-test-echo-trailing-bin"
122
+
123
+ if _call.metadata.has_key?(initial_metadata_key)
124
+ _call.metadata_to_send[initial_metadata_key] = _call.metadata[initial_metadata_key]
125
+ end
126
+ if _call.metadata.has_key?(trailing_metadata_key)
127
+ _call.output_metadata[trailing_metadata_key] = _call.metadata[trailing_metadata_key]
128
+ end
129
+ end
130
+
131
+ def maybe_echo_status_and_message(req)
132
+ unless req.response_status.nil?
133
+ fail GRPC::BadStatus.new_status_exception(
134
+ req.response_status.code, req.response_status.message)
135
+ end
136
+ end
137
+
138
+ # A FullDuplexEnumerator passes requests to a block and yields generated responses
139
+ class FullDuplexEnumerator
140
+ include Grpc::Testing
141
+ include Grpc::Testing::PayloadType
142
+
143
+ def initialize(requests)
144
+ @requests = requests
145
+ end
146
+ def each_item
147
+ return enum_for(:each_item) unless block_given?
148
+ GRPC.logger.info('interop-server: started receiving')
149
+ begin
150
+ cls = StreamingOutputCallResponse
151
+ @requests.each do |req|
152
+ maybe_echo_status_and_message(req)
153
+ req.response_parameters.each do |params|
154
+ resp_size = params.size
155
+ GRPC.logger.info("read a req, response size is #{resp_size}")
156
+ yield cls.new(payload: Payload.new(type: req.response_type,
157
+ body: nulls(resp_size)))
158
+ end
159
+ end
160
+ GRPC.logger.info('interop-server: finished receiving')
161
+ rescue StandardError => e
162
+ GRPC.logger.info('interop-server: failed')
163
+ GRPC.logger.warn(e)
164
+ fail e
165
+ end
166
+ end
167
+ end
168
+
169
+ # A runnable implementation of the schema-specified testing service, with each
170
+ # service method implemented as required by the interop testing spec.
171
+ class TestTarget < Grpc::Testing::TestService::Service
172
+ include Grpc::Testing
173
+ include Grpc::Testing::PayloadType
174
+
175
+ def empty_call(_empty, _call)
176
+ Empty.new
177
+ end
178
+
179
+ def unary_call(simple_req, _call)
180
+ maybe_echo_metadata(_call)
181
+ maybe_echo_status_and_message(simple_req)
182
+ req_size = simple_req.response_size
183
+ SimpleResponse.new(payload: Payload.new(type: :COMPRESSABLE,
184
+ body: nulls(req_size)))
185
+ end
186
+
187
+ def streaming_input_call(call)
188
+ sizes = call.each_remote_read.map { |x| x.payload.body.length }
189
+ sum = sizes.inject(0) { |s, x| s + x }
190
+ StreamingInputCallResponse.new(aggregated_payload_size: sum)
191
+ end
192
+
193
+ def streaming_output_call(req, _call)
194
+ cls = StreamingOutputCallResponse
195
+ req.response_parameters.map do |p|
196
+ cls.new(payload: Payload.new(type: req.response_type,
197
+ body: nulls(p.size)))
198
+ end
199
+ end
200
+
201
+ def full_duplex_call(reqs, _call)
202
+ maybe_echo_metadata(_call)
203
+ # reqs is a lazy Enumerator of the requests sent by the client.
204
+ FullDuplexEnumerator.new(reqs).each_item
205
+ end
206
+
207
+ def half_duplex_call(reqs)
208
+ # TODO: update with unique behaviour of the half_duplex_call if that's
209
+ # ever required by any of the tests.
210
+ full_duplex_call(reqs)
211
+ end
212
+ end
213
+
214
+ # validates the command line options, returning them as a Hash.
215
+ def parse_options
216
+ options = {
217
+ 'port' => nil,
218
+ 'secure' => false
219
+ }
220
+ OptionParser.new do |opts|
221
+ opts.banner = 'Usage: --port port'
222
+ opts.on('--port PORT', 'server port') do |v|
223
+ options['port'] = v
224
+ end
225
+ opts.on('--use_tls USE_TLS', ['false', 'true'],
226
+ 'require a secure connection?') do |v|
227
+ options['secure'] = v == 'true'
228
+ end
229
+ end.parse!
230
+
231
+ if options['port'].nil?
232
+ fail(OptionParser::MissingArgument, 'please specify --port')
233
+ end
234
+ options
235
+ end
236
+
237
+ def main
238
+ opts = parse_options
239
+ host = "0.0.0.0:#{opts['port']}"
240
+ s = GRPC::RpcServer.new
241
+ if opts['secure']
242
+ s.add_http2_port(host, test_server_creds)
243
+ GRPC.logger.info("... running securely on #{host}")
244
+ else
245
+ s.add_http2_port(host, :this_port_is_insecure)
246
+ GRPC.logger.info("... running insecurely on #{host}")
247
+ end
248
+ s.handle(TestTarget)
249
+ s.run_till_terminated
250
+ end
251
+
252
+ main
@@ -0,0 +1,415 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Copyright 2015 gRPC authors.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ # This is the xDS interop test Ruby client. This is meant to be run by
18
+ # the run_xds_tests.py test runner.
19
+ #
20
+ # Usage: $ tools/run_tests/run_xds_tests.py --test_case=... ...
21
+ # --client_cmd="path/to/xds_client.rb --server=<hostname> \
22
+ # --stats_port=<port> \
23
+ # --qps=<qps>"
24
+
25
+ # These lines are required for the generated files to load grpc
26
+ this_dir = File.expand_path(File.dirname(__FILE__))
27
+ lib_dir = File.join(File.dirname(File.dirname(this_dir)), 'lib')
28
+ pb_dir = File.dirname(this_dir)
29
+ $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
30
+ $LOAD_PATH.unshift(pb_dir) unless $LOAD_PATH.include?(pb_dir)
31
+
32
+ require 'optparse'
33
+ require 'logger'
34
+
35
+ require_relative '../../lib/grpc'
36
+ require 'google/protobuf'
37
+
38
+ require_relative '../src/proto/grpc/testing/empty_pb'
39
+ require_relative '../src/proto/grpc/testing/messages_pb'
40
+ require_relative '../src/proto/grpc/testing/test_services_pb'
41
+
42
+ class RpcConfig
43
+ attr_reader :rpcs_to_send, :metadata_to_send, :timeout_sec
44
+ def init(rpcs_to_send, metadata_to_send, timeout_sec = 0)
45
+ @rpcs_to_send = rpcs_to_send
46
+ @metadata_to_send = metadata_to_send
47
+ @timeout_sec = timeout_sec
48
+ end
49
+ end
50
+
51
+ # Some global constant mappings
52
+ $RPC_MAP = {
53
+ 'UnaryCall' => :UNARY_CALL,
54
+ 'EmptyCall' => :EMPTY_CALL,
55
+ }
56
+
57
+ # Some global variables to be shared by server and client
58
+ $watchers = Array.new
59
+ $watchers_mutex = Mutex.new
60
+ $watchers_cv = ConditionVariable.new
61
+ $shutdown = false
62
+ # These can be configured by the test runner dynamically
63
+ $rpc_config = RpcConfig.new
64
+ $rpc_config.init([:UNARY_CALL], {})
65
+ # These stats are shared across threads
66
+ $thread_results = Array.new
67
+ $thread_results_mu = Mutex.new
68
+ $accumulated_stats_mu = Mutex.new
69
+ $num_rpcs_started_by_method = {}
70
+ $num_rpcs_succeeded_by_method = {}
71
+ $num_rpcs_failed_by_method = {}
72
+ $accumulated_method_stats = {}
73
+
74
+ # RubyLogger defines a logger for gRPC based on the standard ruby logger.
75
+ module RubyLogger
76
+ def logger
77
+ LOGGER
78
+ end
79
+
80
+ LOGGER = Logger.new(STDOUT)
81
+ LOGGER.level = Logger::INFO
82
+ end
83
+
84
+ # GRPC is the general RPC module
85
+ module GRPC
86
+ # Inject the noop #logger if no module-level logger method has been injected.
87
+ extend RubyLogger
88
+ end
89
+
90
+ # creates a test stub
91
+ def create_stub(opts)
92
+ address = "#{opts.server}"
93
+ GRPC.logger.info("... connecting insecurely to #{address}")
94
+ Grpc::Testing::TestService::Stub.new(
95
+ address,
96
+ :this_channel_is_insecure,
97
+ )
98
+ end
99
+
100
+ class StatsPerMethod
101
+ attr_reader :rpcs_started, :result
102
+ def initialize()
103
+ @rpcs_started = 0
104
+ @result = Hash.new(0)
105
+ end
106
+ def increment_rpcs_started()
107
+ @rpcs_started += 1
108
+ end
109
+ def add_result(status_code)
110
+ @result[status_code] += 1
111
+ end
112
+ end
113
+
114
+ class ConfigureTarget < Grpc::Testing::XdsUpdateClientConfigureService::Service
115
+ include Grpc::Testing
116
+
117
+ def configure(req, _call)
118
+ metadata_to_send = {}
119
+ req.metadata.each do |m|
120
+ rpc = m.type
121
+ if !metadata_to_send.key?(rpc)
122
+ metadata_to_send[rpc] = {}
123
+ end
124
+ metadata_key = m.key
125
+ metadata_value = m.value
126
+ metadata_to_send[rpc][metadata_key] = metadata_value
127
+ end
128
+ new_rpc_config = RpcConfig.new
129
+ new_rpc_config.init(req['types'], metadata_to_send, req['timeout_sec'])
130
+ $rpc_config = new_rpc_config
131
+ ClientConfigureResponse.new()
132
+ end
133
+ end
134
+
135
+ # This implements LoadBalancerStatsService required by the test runner
136
+ class TestTarget < Grpc::Testing::LoadBalancerStatsService::Service
137
+ include Grpc::Testing
138
+
139
+ def get_client_stats(req, _call)
140
+ finish_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) +
141
+ req['timeout_sec']
142
+ watcher = {}
143
+ $watchers_mutex.synchronize do
144
+ watcher = {
145
+ "rpcs_by_method" => Hash.new(),
146
+ "rpcs_by_peer" => Hash.new(0),
147
+ "rpcs_needed" => req['num_rpcs'],
148
+ "no_remote_peer" => 0
149
+ }
150
+ $watchers << watcher
151
+ seconds_remaining = finish_time -
152
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
153
+ while watcher['rpcs_needed'] > 0 && seconds_remaining > 0
154
+ $watchers_cv.wait($watchers_mutex, seconds_remaining)
155
+ seconds_remaining = finish_time -
156
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
157
+ end
158
+ $watchers.delete_at($watchers.index(watcher))
159
+ end
160
+ # convert results into proper proto object
161
+ rpcs_by_method = {}
162
+ watcher['rpcs_by_method'].each do |rpc_name, rpcs_by_peer|
163
+ rpcs_by_method[rpc_name] = LoadBalancerStatsResponse::RpcsByPeer.new(
164
+ rpcs_by_peer: rpcs_by_peer
165
+ )
166
+ end
167
+ LoadBalancerStatsResponse.new(
168
+ rpcs_by_method: rpcs_by_method,
169
+ rpcs_by_peer: watcher['rpcs_by_peer'],
170
+ num_failures: watcher['no_remote_peer'] + watcher['rpcs_needed']
171
+ )
172
+ end
173
+
174
+ def get_client_accumulated_stats(req, _call)
175
+ $accumulated_stats_mu.synchronize do
176
+ all_stats_per_method = $accumulated_method_stats.map { |rpc, stats_per_method|
177
+ [rpc,
178
+ LoadBalancerAccumulatedStatsResponse::MethodStats.new(
179
+ rpcs_started: stats_per_method.rpcs_started,
180
+ result: stats_per_method.result
181
+ )]
182
+ }.to_h
183
+ LoadBalancerAccumulatedStatsResponse.new(
184
+ num_rpcs_started_by_method: $num_rpcs_started_by_method,
185
+ num_rpcs_succeeded_by_method: $num_rpcs_succeeded_by_method,
186
+ num_rpcs_failed_by_method: $num_rpcs_failed_by_method,
187
+ stats_per_method: all_stats_per_method,
188
+ )
189
+ end
190
+ end
191
+ end
192
+
193
+ def execute_rpc_in_thread(op, rpc)
194
+ Thread.new {
195
+ rpc_stats_key = rpc.to_s
196
+ remote_peer = ""
197
+ begin
198
+ op.execute
199
+ if op.metadata.key?('hostname')
200
+ remote_peer = op.metadata['hostname']
201
+ end
202
+ $accumulated_stats_mu.synchronize do
203
+ $num_rpcs_succeeded_by_method[rpc_stats_key] += 1
204
+ $accumulated_method_stats[rpc_stats_key].add_result(0)
205
+ end
206
+ rescue GRPC::BadStatus => e
207
+ $accumulated_stats_mu.synchronize do
208
+ $num_rpcs_failed_by_method[rpc_stats_key] += 1
209
+ $accumulated_method_stats[rpc_stats_key].add_result(e.code)
210
+ end
211
+ end
212
+ $thread_results_mu.synchronize do
213
+ $thread_results << [rpc, remote_peer]
214
+ end
215
+ }
216
+ end
217
+
218
+ # send 1 rpc every 1/qps second
219
+ def run_test_loop(stub, target_seconds_between_rpcs, fail_on_failed_rpcs)
220
+ include Grpc::Testing
221
+ simple_req = SimpleRequest.new()
222
+ empty_req = Empty.new()
223
+ target_next_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
224
+ # Some RPCs are meant to be "kept open". Since Ruby does not have an
225
+ # async API, we are executing those RPCs in a thread so that they don't
226
+ # block.
227
+ keep_open_threads = Array.new
228
+ while !$shutdown
229
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
230
+ sleep_seconds = target_next_start - now
231
+ if sleep_seconds < 0
232
+ target_next_start = now + target_seconds_between_rpcs
233
+ else
234
+ target_next_start += target_seconds_between_rpcs
235
+ sleep(sleep_seconds)
236
+ end
237
+ deadline_sec = $rpc_config.timeout_sec > 0 ? $rpc_config.timeout_sec : 30
238
+ deadline = GRPC::Core::TimeConsts::from_relative_time(deadline_sec)
239
+ results = {}
240
+ $rpc_config.rpcs_to_send.each do |rpc|
241
+ # rpc is in the form of :UNARY_CALL or :EMPTY_CALL here
242
+ metadata = $rpc_config.metadata_to_send.key?(rpc) ?
243
+ $rpc_config.metadata_to_send[rpc] : {}
244
+ $accumulated_stats_mu.synchronize do
245
+ $num_rpcs_started_by_method[rpc.to_s] += 1
246
+ $accumulated_method_stats[rpc.to_s].increment_rpcs_started()
247
+ end
248
+ if rpc == :UNARY_CALL
249
+ op = stub.unary_call(simple_req,
250
+ metadata: metadata,
251
+ deadline: deadline,
252
+ return_op: true)
253
+ elsif rpc == :EMPTY_CALL
254
+ op = stub.empty_call(empty_req,
255
+ metadata: metadata,
256
+ deadline: deadline,
257
+ return_op: true)
258
+ else
259
+ raise "Unsupported rpc #{rpc}"
260
+ end
261
+ keep_open_threads << execute_rpc_in_thread(op, rpc)
262
+ end
263
+ # collect results from threads
264
+ $thread_results_mu.synchronize do
265
+ $thread_results.each do |r|
266
+ rpc_name, remote_peer = r
267
+ results[rpc_name] = remote_peer
268
+ end
269
+ $thread_results = Array.new
270
+ end
271
+ $watchers_mutex.synchronize do
272
+ $watchers.each do |watcher|
273
+ # this is counted once when each group of all rpcs_to_send were done
274
+ watcher['rpcs_needed'] -= 1
275
+ results.each do |rpc_name, remote_peer|
276
+ # These stats expect rpc_name to be in the form of
277
+ # UnaryCall or EmptyCall, not the underscore-case all-caps form
278
+ rpc_name = $RPC_MAP.invert()[rpc_name]
279
+ if remote_peer.strip.empty?
280
+ # error is counted per individual RPC
281
+ watcher['no_remote_peer'] += 1
282
+ else
283
+ if not watcher['rpcs_by_method'].key?(rpc_name)
284
+ watcher['rpcs_by_method'][rpc_name] = Hash.new(0)
285
+ end
286
+ # increment the remote hostname distribution histogram
287
+ # both by overall, and broken down per RPC
288
+ watcher['rpcs_by_method'][rpc_name][remote_peer] += 1
289
+ watcher['rpcs_by_peer'][remote_peer] += 1
290
+ end
291
+ end
292
+ end
293
+ $watchers_cv.broadcast
294
+ end
295
+ end
296
+ keep_open_threads.each { |thd| thd.join }
297
+ end
298
+
299
+ # Args is used to hold the command line info.
300
+ Args = Struct.new(:fail_on_failed_rpcs, :num_channels,
301
+ :rpc, :metadata,
302
+ :server, :stats_port, :qps)
303
+
304
+ # validates the command line options, returning them as a Hash.
305
+ def parse_args
306
+ args = Args.new
307
+ args['fail_on_failed_rpcs'] = false
308
+ args['num_channels'] = 1
309
+ args['rpc'] = 'UnaryCall'
310
+ args['metadata'] = ''
311
+ OptionParser.new do |opts|
312
+ opts.on('--fail_on_failed_rpcs BOOL', ['false', 'true']) do |v|
313
+ args['fail_on_failed_rpcs'] = v == 'true'
314
+ end
315
+ opts.on('--num_channels CHANNELS', 'number of channels') do |v|
316
+ args['num_channels'] = v.to_i
317
+ end
318
+ opts.on('--rpc RPCS_TO_SEND', 'list of RPCs to send') do |v|
319
+ args['rpc'] = v
320
+ end
321
+ opts.on('--metadata METADATA_TO_SEND', 'metadata to send per RPC') do |v|
322
+ args['metadata'] = v
323
+ end
324
+ opts.on('--server SERVER_HOST', 'server hostname') do |v|
325
+ GRPC.logger.info("ruby xds: server address is #{v}")
326
+ args['server'] = v
327
+ end
328
+ opts.on('--stats_port STATS_PORT', 'stats port') do |v|
329
+ GRPC.logger.info("ruby xds: stats port is #{v}")
330
+ args['stats_port'] = v
331
+ end
332
+ opts.on('--qps QPS', 'qps') do |v|
333
+ GRPC.logger.info("ruby xds: qps is #{v}")
334
+ args['qps'] = v
335
+ end
336
+ end.parse!
337
+ args
338
+ end
339
+
340
+ def main
341
+ opts = parse_args
342
+
343
+ # This server hosts the LoadBalancerStatsService
344
+ host = "0.0.0.0:#{opts['stats_port']}"
345
+ s = GRPC::RpcServer.new
346
+ s.add_http2_port(host, :this_port_is_insecure)
347
+ s.handle(TestTarget)
348
+ s.handle(ConfigureTarget)
349
+ server_thread = Thread.new {
350
+ # run the server until the main test runner terminates this process
351
+ s.run_till_terminated_or_interrupted(['TERM'])
352
+ }
353
+
354
+ # Initialize stats
355
+ $RPC_MAP.values.each do |rpc|
356
+ $num_rpcs_started_by_method[rpc.to_s] = 0
357
+ $num_rpcs_succeeded_by_method[rpc.to_s] = 0
358
+ $num_rpcs_failed_by_method[rpc.to_s] = 0
359
+ $accumulated_method_stats[rpc.to_s] = StatsPerMethod.new
360
+ end
361
+
362
+ # The client just sends rpcs continuously in a regular interval
363
+ stub = create_stub(opts)
364
+ target_seconds_between_rpcs = (1.0 / opts['qps'].to_f)
365
+ # Convert 'metadata' input in the form of
366
+ # rpc1:k1:v1,rpc2:k2:v2,rpc1:k3:v3
367
+ # into
368
+ # {
369
+ # 'rpc1' => {
370
+ # 'k1' => 'v1',
371
+ # 'k3' => 'v3',
372
+ # },
373
+ # 'rpc2' => {
374
+ # 'k2' => 'v2'
375
+ # },
376
+ # }
377
+ rpcs_to_send = []
378
+ metadata_to_send = {}
379
+ if opts['metadata']
380
+ metadata_entries = opts['metadata'].split(',')
381
+ metadata_entries.each do |e|
382
+ (rpc_name, metadata_key, metadata_value) = e.split(':')
383
+ rpc_name = $RPC_MAP[rpc_name]
384
+ # initialize if we haven't seen this rpc_name yet
385
+ if !metadata_to_send.key?(rpc_name)
386
+ metadata_to_send[rpc_name] = {}
387
+ end
388
+ metadata_to_send[rpc_name][metadata_key] = metadata_value
389
+ end
390
+ end
391
+ if opts['rpc']
392
+ rpcs_to_send = opts['rpc'].split(',')
393
+ end
394
+ if rpcs_to_send.size > 0
395
+ rpcs_to_send.map! { |rpc| $RPC_MAP[rpc] }
396
+ new_rpc_config = RpcConfig.new
397
+ new_rpc_config.init(rpcs_to_send, metadata_to_send)
398
+ $rpc_config = new_rpc_config
399
+ end
400
+ client_threads = Array.new
401
+ opts['num_channels'].times {
402
+ client_threads << Thread.new {
403
+ run_test_loop(stub, target_seconds_between_rpcs,
404
+ opts['fail_on_failed_rpcs'])
405
+ }
406
+ }
407
+
408
+ server_thread.join
409
+ $shutdown = true
410
+ client_threads.each { |thd| thd.join }
411
+ end
412
+
413
+ if __FILE__ == $0
414
+ main
415
+ end
@@ -0,0 +1,42 @@
1
+ # Copyright 2015 gRPC authors.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'spec_helper'
16
+
17
+ describe GRPC::Core::CallCredentials do
18
+ CallCredentials = GRPC::Core::CallCredentials
19
+
20
+ let(:auth_proc) { proc { { 'plugin_key' => 'plugin_value' } } }
21
+
22
+ describe '#new' do
23
+ it 'can successfully create a CallCredentials from a proc' do
24
+ expect { CallCredentials.new(auth_proc) }.not_to raise_error
25
+ end
26
+ end
27
+
28
+ describe '#compose' do
29
+ it 'can compose with another CallCredentials' do
30
+ creds1 = CallCredentials.new(auth_proc)
31
+ creds2 = CallCredentials.new(auth_proc)
32
+ expect { creds1.compose creds2 }.not_to raise_error
33
+ end
34
+
35
+ it 'can compose with multiple CallCredentials' do
36
+ creds1 = CallCredentials.new(auth_proc)
37
+ creds2 = CallCredentials.new(auth_proc)
38
+ creds3 = CallCredentials.new(auth_proc)
39
+ expect { creds1.compose(creds2, creds3) }.not_to raise_error
40
+ end
41
+ end
42
+ end