semian 0.6.0 → 0.6.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/ext/semian/resource.c +257 -0
- data/ext/semian/resource.h +75 -0
- data/ext/semian/semian.c +42 -303
- data/ext/semian/semian.h +67 -0
- data/ext/semian/types.h +45 -0
- data/lib/semian/version.rb +1 -1
- metadata +7 -29
- data/.gitignore +0 -8
- data/.rubocop.yml +0 -113
- data/.ruby-version +0 -1
- data/.travis.yml +0 -15
- data/CHANGELOG.md +0 -11
- data/Gemfile +0 -10
- data/LICENSE.md +0 -21
- data/README.md +0 -576
- data/Rakefile +0 -56
- data/repodb.yml +0 -1
- data/scripts/install_toxiproxy.sh +0 -25
- data/semian.gemspec +0 -29
- data/test/circuit_breaker_test.rb +0 -133
- data/test/fixtures/binary.sql +0 -1
- data/test/helpers/background_helper.rb +0 -25
- data/test/instrumentation_test.rb +0 -61
- data/test/mysql2_test.rb +0 -296
- data/test/net_http_test.rb +0 -515
- data/test/redis_test.rb +0 -237
- data/test/resource_test.rb +0 -322
- data/test/semian_test.rb +0 -32
- data/test/simple_integer_test.rb +0 -49
- data/test/simple_sliding_window_test.rb +0 -65
- data/test/simple_state_test.rb +0 -45
- data/test/test_helper.rb +0 -33
- data/test/unprotected_resource_test.rb +0 -60
data/test/net_http_test.rb
DELETED
@@ -1,515 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
require 'semian/net_http'
|
3
|
-
require 'thin'
|
4
|
-
|
5
|
-
class TestNetHTTP < Minitest::Test
|
6
|
-
class RackServer
|
7
|
-
def self.call(env)
|
8
|
-
response_code = env['REQUEST_URI'].delete("/")
|
9
|
-
response_code = '200' if response_code == ""
|
10
|
-
[response_code, {'Content-Type' => 'text/html'}, ['Success']]
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
HOSTNAME = "127.0.0.1"
|
15
|
-
PORT = 31_050
|
16
|
-
TOXIC_PORT = PORT + 1
|
17
|
-
DEFAULT_SEMIAN_OPTIONS = {
|
18
|
-
tickets: 3,
|
19
|
-
success_threshold: 1,
|
20
|
-
error_threshold: 3,
|
21
|
-
error_timeout: 10,
|
22
|
-
}.freeze
|
23
|
-
DEFAULT_SEMIAN_CONFIGURATION = proc do |host, port|
|
24
|
-
next nil if host == "127.0.0.1" && port == 8474 # disable if toxiproxy
|
25
|
-
DEFAULT_SEMIAN_OPTIONS.merge(name: "#{host}_#{port}")
|
26
|
-
end
|
27
|
-
|
28
|
-
def test_with_server_raises_if_binding_fails
|
29
|
-
# Occurs when trying to bind to invalid addresses, like non-private
|
30
|
-
# addresses, or when the address is already bound to something else
|
31
|
-
with_server do
|
32
|
-
assert_raises RuntimeError do
|
33
|
-
with_server {}
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def test_semian_identifier
|
39
|
-
with_server do
|
40
|
-
with_semian_configuration do
|
41
|
-
Net::HTTP.start(HOSTNAME, TOXIC_PORT) do |http|
|
42
|
-
assert_equal "nethttp_#{HOSTNAME}_#{TOXIC_PORT}", http.semian_identifier
|
43
|
-
end
|
44
|
-
Net::HTTP.start("127.0.0.1", TOXIC_PORT) do |http|
|
45
|
-
assert_equal "nethttp_127.0.0.1_#{TOXIC_PORT}", http.semian_identifier
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def test_trigger_open
|
52
|
-
with_semian_configuration do
|
53
|
-
with_server do
|
54
|
-
open_circuit!
|
55
|
-
uri = URI("http://#{HOSTNAME}:#{TOXIC_PORT}/200")
|
56
|
-
assert_raises Net::CircuitOpenError do
|
57
|
-
Net::HTTP.get(uri)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def test_trigger_close_after_open
|
64
|
-
with_semian_configuration do
|
65
|
-
with_server do
|
66
|
-
open_circuit!
|
67
|
-
close_circuit!
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def test_bulkheads_tickets_are_working
|
73
|
-
options = proc do |host, port|
|
74
|
-
{
|
75
|
-
tickets: 2,
|
76
|
-
success_threshold: 1,
|
77
|
-
error_threshold: 3,
|
78
|
-
error_timeout: 10,
|
79
|
-
name: "#{host}_#{port}",
|
80
|
-
}
|
81
|
-
end
|
82
|
-
with_semian_configuration(options) do
|
83
|
-
with_server do
|
84
|
-
http_1 = Net::HTTP.new(HOSTNAME, TOXIC_PORT)
|
85
|
-
http_1.semian_resource.acquire do
|
86
|
-
http_2 = Net::HTTP.new(HOSTNAME, TOXIC_PORT)
|
87
|
-
http_2.semian_resource.acquire do
|
88
|
-
assert_raises Net::ResourceBusyError do
|
89
|
-
Net::HTTP.get(URI("http://#{HOSTNAME}:#{TOXIC_PORT}/"))
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def test_get_is_protected
|
98
|
-
with_semian_configuration do
|
99
|
-
with_server do
|
100
|
-
open_circuit!
|
101
|
-
assert_raises Net::CircuitOpenError do
|
102
|
-
Net::HTTP.get(URI("http://#{HOSTNAME}:#{TOXIC_PORT}/200"))
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
def test_instance_get_is_protected
|
109
|
-
with_semian_configuration do
|
110
|
-
with_server do
|
111
|
-
open_circuit!
|
112
|
-
assert_raises Net::CircuitOpenError do
|
113
|
-
http = Net::HTTP.new(HOSTNAME, TOXIC_PORT)
|
114
|
-
http.get("/")
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
def test_get_response_is_protected
|
121
|
-
with_semian_configuration do
|
122
|
-
with_server do
|
123
|
-
open_circuit!
|
124
|
-
assert_raises Net::CircuitOpenError do
|
125
|
-
uri = URI("http://#{HOSTNAME}:#{TOXIC_PORT}/200")
|
126
|
-
Net::HTTP.get_response(uri)
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
def test_post_form_is_protected
|
133
|
-
with_semian_configuration do
|
134
|
-
with_server do
|
135
|
-
open_circuit!
|
136
|
-
assert_raises Net::CircuitOpenError do
|
137
|
-
uri = URI("http://#{HOSTNAME}:#{TOXIC_PORT}/200")
|
138
|
-
Net::HTTP.post_form(uri, 'q' => 'ruby', 'max' => '50')
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def test_http_start_method_is_protected
|
145
|
-
with_semian_configuration do
|
146
|
-
with_server do
|
147
|
-
open_circuit!
|
148
|
-
uri = URI("http://#{HOSTNAME}:#{TOXIC_PORT}/200")
|
149
|
-
assert_raises Net::CircuitOpenError do
|
150
|
-
Net::HTTP.start(uri.host, uri.port) {}
|
151
|
-
end
|
152
|
-
close_circuit!
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
def test_http_action_request_inside_start_methods_are_protected
|
158
|
-
with_semian_configuration do
|
159
|
-
with_server do
|
160
|
-
uri = URI("http://#{HOSTNAME}:#{TOXIC_PORT}/200")
|
161
|
-
Net::HTTP.start(uri.host, uri.port) do |http|
|
162
|
-
open_circuit!
|
163
|
-
get_subclasses(Net::HTTPRequest).each do |action|
|
164
|
-
assert_raises(Net::CircuitOpenError, "#{action.name} did not raise a Net::CircuitOpenError") do
|
165
|
-
request = action.new uri
|
166
|
-
http.request(request)
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
def test_custom_raw_semian_options_work_with_lookup
|
175
|
-
with_server do
|
176
|
-
semian_config = {}
|
177
|
-
semian_config["development"] = {}
|
178
|
-
semian_config["development"]["nethttp_#{HOSTNAME}_#{TOXIC_PORT}"] = DEFAULT_SEMIAN_OPTIONS
|
179
|
-
sample_env = "development"
|
180
|
-
|
181
|
-
semian_configuration_proc = proc do |host, port|
|
182
|
-
semian_identifier = "nethttp_#{host}_#{port}"
|
183
|
-
semian_config[sample_env][semian_identifier].merge(name: "#{host}_#{port}")
|
184
|
-
end
|
185
|
-
|
186
|
-
with_semian_configuration(semian_configuration_proc) do
|
187
|
-
Net::HTTP.start(HOSTNAME, TOXIC_PORT) do |http|
|
188
|
-
assert_equal semian_config["development"][http.semian_identifier],
|
189
|
-
http.raw_semian_options.dup.tap { |o| o.delete(:name) }
|
190
|
-
end
|
191
|
-
end
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
def test_custom_raw_semian_options_can_only_assign_once
|
196
|
-
semian_configuration_proc = proc do |host, port|
|
197
|
-
DEFAULT_SEMIAN_OPTIONS.merge(name: "#{host}_#{port}")
|
198
|
-
end
|
199
|
-
with_semian_configuration(semian_configuration_proc) do
|
200
|
-
assert_raises(Semian::NetHTTP::SemianConfigurationChangedError) do
|
201
|
-
Semian::NetHTTP.semian_configuration = semian_configuration_proc
|
202
|
-
end
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
def test_custom_raw_semian_options_work_with_default_fallback
|
207
|
-
with_server do
|
208
|
-
semian_config = {}
|
209
|
-
semian_config["development"] = {}
|
210
|
-
semian_config["development"]["nethttp_default"] = DEFAULT_SEMIAN_OPTIONS
|
211
|
-
sample_env = "development"
|
212
|
-
|
213
|
-
semian_configuration_proc = proc do |host, port|
|
214
|
-
semian_identifier = "nethttp_#{host}_#{port}"
|
215
|
-
semian_identifier = "nethttp_default" unless semian_config[sample_env].key?(semian_identifier)
|
216
|
-
semian_config[sample_env][semian_identifier].merge(name: "default")
|
217
|
-
end
|
218
|
-
Semian["nethttp_default"].reset if Semian["nethttp_default"]
|
219
|
-
Semian.destroy("nethttp_default")
|
220
|
-
with_semian_configuration(semian_configuration_proc) do
|
221
|
-
Net::HTTP.start(HOSTNAME, PORT) do |http|
|
222
|
-
expected_config = semian_config["development"]["nethttp_default"].dup
|
223
|
-
assert_equal expected_config, http.raw_semian_options.dup.tap { |o| o.delete(:name) }
|
224
|
-
end
|
225
|
-
end
|
226
|
-
end
|
227
|
-
end
|
228
|
-
|
229
|
-
def test_custom_raw_semian_options_can_disable_using_nil
|
230
|
-
with_server do
|
231
|
-
semian_configuration_proc = proc { nil }
|
232
|
-
with_semian_configuration(semian_configuration_proc) do
|
233
|
-
http = Net::HTTP.new(HOSTNAME, TOXIC_PORT)
|
234
|
-
assert_equal true, http.disabled?
|
235
|
-
end
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
def test_use_custom_configuration_to_combine_endpoints_into_one_resource
|
240
|
-
semian_config = {}
|
241
|
-
semian_config["development"] = {}
|
242
|
-
semian_config["development"]["nethttp_default"] = DEFAULT_SEMIAN_OPTIONS
|
243
|
-
sample_env = "development"
|
244
|
-
|
245
|
-
semian_configuration_proc = proc do |host, port|
|
246
|
-
next nil if host == "127.0.0.1" && port == 8474 # # disable if toxiproxy
|
247
|
-
semian_identifier = "nethttp_default"
|
248
|
-
semian_config[sample_env][semian_identifier].merge(name: "default")
|
249
|
-
end
|
250
|
-
|
251
|
-
with_semian_configuration(semian_configuration_proc) do
|
252
|
-
Semian["nethttp_default"].reset if Semian["nethttp_default"]
|
253
|
-
Semian.destroy("nethttp_default")
|
254
|
-
with_server do
|
255
|
-
open_circuit!
|
256
|
-
end
|
257
|
-
with_server(addresses: ["#{HOSTNAME}:#{PORT}", "#{HOSTNAME}:#{PORT + 100}"], reset_semian_state: false) do
|
258
|
-
assert_raises Net::CircuitOpenError do
|
259
|
-
Net::HTTP.get(URI("http://#{HOSTNAME}:#{TOXIC_PORT}/200"))
|
260
|
-
end
|
261
|
-
end
|
262
|
-
end
|
263
|
-
end
|
264
|
-
|
265
|
-
def test_custom_raw_semian_options_can_disable_with_invalid_key
|
266
|
-
with_server do
|
267
|
-
semian_config = {}
|
268
|
-
semian_config["development"] = {}
|
269
|
-
semian_config["development"]["nethttp_#{HOSTNAME}_#{TOXIC_PORT}"] = DEFAULT_SEMIAN_OPTIONS
|
270
|
-
sample_env = "development"
|
271
|
-
|
272
|
-
semian_configuration_proc = proc do |host, port|
|
273
|
-
semian_identifier = "nethttp_#{host}_#{port}"
|
274
|
-
semian_config[sample_env][semian_identifier]
|
275
|
-
end
|
276
|
-
with_semian_configuration(semian_configuration_proc) do
|
277
|
-
http = Net::HTTP.new(HOSTNAME, TOXIC_PORT)
|
278
|
-
assert_equal false, http.disabled?
|
279
|
-
|
280
|
-
http = Net::HTTP.new(HOSTNAME, TOXIC_PORT + 100)
|
281
|
-
assert_equal true, http.disabled?
|
282
|
-
end
|
283
|
-
end
|
284
|
-
end
|
285
|
-
|
286
|
-
def test_adding_extra_errors_and_resetting_affects_exceptions_list
|
287
|
-
orig_errors = Semian::NetHTTP.exceptions.dup
|
288
|
-
Semian::NetHTTP.exceptions += [::OpenSSL::SSL::SSLError]
|
289
|
-
assert_equal(orig_errors + [::OpenSSL::SSL::SSLError], Semian::NetHTTP.exceptions)
|
290
|
-
Semian::NetHTTP.reset_exceptions
|
291
|
-
assert_equal(Semian::NetHTTP::DEFAULT_ERRORS, Semian::NetHTTP.exceptions)
|
292
|
-
ensure
|
293
|
-
Semian::NetHTTP.exceptions = orig_errors
|
294
|
-
end
|
295
|
-
|
296
|
-
def test_adding_custom_errors_do_trip_circuit
|
297
|
-
with_semian_configuration do
|
298
|
-
with_custom_errors([::OpenSSL::SSL::SSLError]) do
|
299
|
-
with_server do
|
300
|
-
http = Net::HTTP.new(HOSTNAME, TOXIC_PORT)
|
301
|
-
http.use_ssl = true
|
302
|
-
http.raw_semian_options[:error_threshold].times do
|
303
|
-
assert_raises ::OpenSSL::SSL::SSLError do
|
304
|
-
http.get("/200")
|
305
|
-
end
|
306
|
-
end
|
307
|
-
assert_raises Net::CircuitOpenError do
|
308
|
-
http.get("/200")
|
309
|
-
end
|
310
|
-
end
|
311
|
-
end
|
312
|
-
end
|
313
|
-
end
|
314
|
-
|
315
|
-
def test_multiple_different_endpoints_and_ports_are_tracked_differently
|
316
|
-
with_semian_configuration do
|
317
|
-
addresses = ["#{HOSTNAME}:#{PORT}", "#{HOSTNAME}:#{PORT + 100}"]
|
318
|
-
addresses.each do |address|
|
319
|
-
hostname, port = address.split(":")
|
320
|
-
port = port.to_i
|
321
|
-
reset_semian_resource(hostname: hostname, port: port)
|
322
|
-
end
|
323
|
-
with_server(addresses: addresses, reset_semian_state: false) do |hostname, port|
|
324
|
-
with_toxic(hostname: hostname, upstream_port: port, toxic_port: port + 1) do |name|
|
325
|
-
Net::HTTP.get(URI("http://#{hostname}:#{port + 1}/"))
|
326
|
-
open_circuit!(hostname: hostname, toxic_port: port + 1, toxic_name: name)
|
327
|
-
assert_raises Net::CircuitOpenError do
|
328
|
-
Net::HTTP.get(URI("http://#{hostname}:#{port + 1}/"))
|
329
|
-
end
|
330
|
-
end
|
331
|
-
end
|
332
|
-
with_server(addresses: ["127.0.0.1:#{PORT}"], reset_semian_state: false) do
|
333
|
-
# different endpoint, should not raise errors even though localhost == 127.0.0.1
|
334
|
-
Net::HTTP.get(URI("http://127.0.0.1:#{PORT + 1}/"))
|
335
|
-
end
|
336
|
-
end
|
337
|
-
end
|
338
|
-
|
339
|
-
def test_persistent_state_after_server_restart
|
340
|
-
with_semian_configuration do
|
341
|
-
with_server(addresses: ["#{HOSTNAME}:#{PORT + 100}"]) do |hostname, port|
|
342
|
-
with_toxic(hostname: hostname, upstream_port: port, toxic_port: port + 1) do |name|
|
343
|
-
open_circuit!(hostname: hostname, toxic_port: port + 1, toxic_name: name)
|
344
|
-
end
|
345
|
-
end
|
346
|
-
with_server(addresses: ["#{HOSTNAME}:#{PORT + 100}"], reset_semian_state: false) do |hostname, port|
|
347
|
-
with_toxic(hostname: hostname, upstream_port: port, toxic_port: port + 1) do |_|
|
348
|
-
assert_raises Net::CircuitOpenError do
|
349
|
-
Net::HTTP.get(URI("http://#{HOSTNAME}:#{port + 1}/200"))
|
350
|
-
end
|
351
|
-
end
|
352
|
-
end
|
353
|
-
end
|
354
|
-
end
|
355
|
-
|
356
|
-
private
|
357
|
-
|
358
|
-
def with_semian_configuration(options = DEFAULT_SEMIAN_CONFIGURATION)
|
359
|
-
orig_semian_options = Semian::NetHTTP.semian_configuration
|
360
|
-
Semian::NetHTTP.instance_variable_set(:@semian_configuration, nil)
|
361
|
-
mutated_objects = {}
|
362
|
-
Semian::NetHTTP.send(:alias_method, :orig_semian_resource, :semian_resource)
|
363
|
-
Semian::NetHTTP.send(:alias_method, :orig_raw_semian_options, :raw_semian_options)
|
364
|
-
Semian::NetHTTP.send(:define_method, :semian_resource) do
|
365
|
-
mutated_objects[self] = [@semian_resource, @raw_semian_options] unless mutated_objects.key?(self)
|
366
|
-
orig_semian_resource
|
367
|
-
end
|
368
|
-
Semian::NetHTTP.send(:define_method, :raw_semian_options) do
|
369
|
-
mutated_objects[self] = [@semian_resource, @raw_semian_options] unless mutated_objects.key?(self)
|
370
|
-
orig_raw_semian_options
|
371
|
-
end
|
372
|
-
|
373
|
-
Semian::NetHTTP.semian_configuration = options
|
374
|
-
yield
|
375
|
-
ensure
|
376
|
-
Semian::NetHTTP.instance_variable_set(:@semian_configuration, nil)
|
377
|
-
Semian::NetHTTP.semian_configuration = orig_semian_options
|
378
|
-
Semian::NetHTTP.send(:alias_method, :semian_resource, :orig_semian_resource)
|
379
|
-
Semian::NetHTTP.send(:alias_method, :raw_semian_options, :orig_raw_semian_options)
|
380
|
-
Semian::NetHTTP.send(:undef_method, :orig_semian_resource, :orig_raw_semian_options)
|
381
|
-
mutated_objects.each do |instance, (res, opt)| # Sadly, only way to fully restore cached properties
|
382
|
-
instance.instance_variable_set(:@semian_resource, res)
|
383
|
-
instance.instance_variable_set(:@raw_semian_options, opt)
|
384
|
-
end
|
385
|
-
end
|
386
|
-
|
387
|
-
def with_custom_errors(errors)
|
388
|
-
orig_errors = Semian::NetHTTP.exceptions.dup
|
389
|
-
Semian::NetHTTP.exceptions += errors
|
390
|
-
yield
|
391
|
-
ensure
|
392
|
-
Semian::NetHTTP.exceptions = orig_errors
|
393
|
-
end
|
394
|
-
|
395
|
-
def get_subclasses(klass)
|
396
|
-
ObjectSpace.each_object(klass.singleton_class).to_a - [klass]
|
397
|
-
end
|
398
|
-
|
399
|
-
def open_circuit!(hostname: HOSTNAME, toxic_port: TOXIC_PORT, toxic_name: "semian_test_net_http")
|
400
|
-
Net::HTTP.start(hostname, toxic_port) do |http|
|
401
|
-
http.read_timeout = 0.1
|
402
|
-
uri = URI("http://#{hostname}:#{toxic_port}/200")
|
403
|
-
http.raw_semian_options[:error_threshold].times do
|
404
|
-
# Cause error error_threshold times so circuit opens
|
405
|
-
Toxiproxy[toxic_name].downstream(:latency, latency: 150).apply do
|
406
|
-
request = Net::HTTP::Get.new(uri)
|
407
|
-
assert_raises Net::ReadTimeout do
|
408
|
-
http.request(request)
|
409
|
-
end
|
410
|
-
end
|
411
|
-
end
|
412
|
-
end
|
413
|
-
end
|
414
|
-
|
415
|
-
def close_circuit!(hostname: HOSTNAME, toxic_port: TOXIC_PORT)
|
416
|
-
http = Net::HTTP.new(hostname, toxic_port)
|
417
|
-
Timecop.travel(http.raw_semian_options[:error_timeout])
|
418
|
-
# Cause successes success_threshold times so circuit closes
|
419
|
-
http.raw_semian_options[:success_threshold].times do
|
420
|
-
response = http.get("/200")
|
421
|
-
assert(200, response.code)
|
422
|
-
end
|
423
|
-
end
|
424
|
-
|
425
|
-
def with_server(addresses: ["#{HOSTNAME}:#{PORT}"], reset_semian_state: true)
|
426
|
-
addresses.each do |address|
|
427
|
-
hostname, port = address.split(":")
|
428
|
-
begin
|
429
|
-
server = nil
|
430
|
-
server_threw_error = false
|
431
|
-
server_thread = Thread.new do
|
432
|
-
Thin::Logging.silent = true
|
433
|
-
server = Thin::Server.new(hostname, port, RackServer)
|
434
|
-
begin
|
435
|
-
server.start
|
436
|
-
rescue StandardError
|
437
|
-
server_threw_error = true
|
438
|
-
raise
|
439
|
-
end
|
440
|
-
end
|
441
|
-
|
442
|
-
begin
|
443
|
-
poll_until_ready(hostname: hostname, port: port)
|
444
|
-
rescue RuntimeError
|
445
|
-
server_thread.kill
|
446
|
-
server_thread.join if server_threw_error
|
447
|
-
raise
|
448
|
-
end
|
449
|
-
|
450
|
-
assert(server.running?)
|
451
|
-
reset_semian_resource(hostname: hostname, port: port) if reset_semian_state
|
452
|
-
@proxy = Toxiproxy[:semian_test_net_http]
|
453
|
-
yield(hostname, port.to_i)
|
454
|
-
ensure
|
455
|
-
server_thread.kill
|
456
|
-
poll_until_gone(hostname: hostname, port: port)
|
457
|
-
end
|
458
|
-
end
|
459
|
-
end
|
460
|
-
|
461
|
-
def reset_semian_resource(hostname:, port:)
|
462
|
-
Semian["nethttp_#{hostname}_#{port}"].reset if Semian["nethttp_#{hostname}_#{port}"]
|
463
|
-
Semian["nethttp_#{hostname}_#{port.to_i + 1}"].reset if Semian["nethttp_#{hostname}_#{port.to_i + 1}"]
|
464
|
-
Semian.destroy("nethttp_#{hostname}_#{port}")
|
465
|
-
Semian.destroy("nethttp_#{hostname}_#{port.to_i + 1}")
|
466
|
-
end
|
467
|
-
|
468
|
-
def with_toxic(hostname: HOSTNAME, upstream_port: PORT, toxic_port: upstream_port + 1)
|
469
|
-
old_proxy = @proxy
|
470
|
-
name = "semian_test_net_http_#{hostname}_#{upstream_port}<-#{toxic_port}"
|
471
|
-
Toxiproxy.populate([
|
472
|
-
{
|
473
|
-
name: name,
|
474
|
-
upstream: "#{hostname}:#{upstream_port}",
|
475
|
-
listen: "#{hostname}:#{toxic_port}",
|
476
|
-
},
|
477
|
-
])
|
478
|
-
@proxy = Toxiproxy[name]
|
479
|
-
yield(name)
|
480
|
-
rescue StandardError
|
481
|
-
ensure
|
482
|
-
@proxy = old_proxy
|
483
|
-
begin
|
484
|
-
Toxiproxy[name].destroy
|
485
|
-
rescue StandardError
|
486
|
-
end
|
487
|
-
end
|
488
|
-
|
489
|
-
def poll_until_ready(hostname: HOSTNAME, port: PORT, time_to_wait: 1)
|
490
|
-
start_time = Time.now.to_i
|
491
|
-
begin
|
492
|
-
TCPSocket.new(hostname, port).close
|
493
|
-
rescue Errno::ECONNREFUSED, Errno::ECONNRESET
|
494
|
-
if Time.now.to_i > start_time + time_to_wait
|
495
|
-
raise "Couldn't reach the service on hostname #{hostname} port #{port} after #{time_to_wait}s"
|
496
|
-
else
|
497
|
-
retry
|
498
|
-
end
|
499
|
-
end
|
500
|
-
end
|
501
|
-
|
502
|
-
def poll_until_gone(hostname: HOSTNAME, port: PORT, time_to_wait: 1)
|
503
|
-
start_time = Time.now.to_i
|
504
|
-
loop do
|
505
|
-
if Time.now.to_i > start_time + time_to_wait
|
506
|
-
raise "Could still reach the service on hostname #{hostname} port #{port} after #{time_to_wait}s"
|
507
|
-
end
|
508
|
-
begin
|
509
|
-
TCPSocket.new(hostname, port).close
|
510
|
-
rescue Errno::ECONNREFUSED, Errno::ECONNRESET
|
511
|
-
return true
|
512
|
-
end
|
513
|
-
end
|
514
|
-
end
|
515
|
-
end
|