nonnative 1.21.0 → 1.26.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f32a99fc33eb1c0fd62360648c9b1de943c500ae674ae713b7c2ec56693ff165
4
- data.tar.gz: c46a33b6451b3f69c4ae226106eaf58d2f54683627cde12217859179a396bc0e
3
+ metadata.gz: 12206912ca29ab82602d7d07fdc15f5b42124e890572d463ad1c4358c7de9f08
4
+ data.tar.gz: ee0393650b33d8a3c5b1d31019f717a3fb680f8872171c7fea1ac4892e186ad5
5
5
  SHA512:
6
- metadata.gz: 965c0c84a1be47fd5d9cf05cf887ec9572dbdddf5ec579a29519800891b68727152d0f4559bb90e20870beaa7b27f3052ee2ea3edf2e31e40db6d04b46c6ef98
7
- data.tar.gz: 583c38fafc5eb0d6b2762b9a23f83319ade0d381c5ab262825e78b1cf9a3b777e1828baf1f1e696bc8f2b2564ce291328c0bce515b92368404c634f776f93d3a
6
+ metadata.gz: 9bbd95d62ddf01d04fd7f6bc4a783276e65dcc7775fbc1844316ee5f12ce150cf06cb3536c77e98f2b600fa5126fd2ed6e2946466559a53a4444ea4e1448b62f
7
+ data.tar.gz: 4417fe28fb3f291a7ac81bab44cbf398fc897acbf815397bd58aba7a72548c0320faba7aeab15778f7037c05b9fb84e2429cbd0a19ec6daba2d0c4f9f646318e
@@ -9,7 +9,7 @@ Metrics/MethodLength:
9
9
  Max: 20
10
10
 
11
11
  Metrics/BlockLength:
12
- Max: 60
12
+ Max: 80
13
13
 
14
14
  Metrics/AbcSize:
15
15
  Max: 20
@@ -1,9 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nonnative (1.21.0)
4
+ nonnative (1.26.0)
5
5
  concurrent-ruby (~> 1.0, >= 1.0.5)
6
- cucumber (~> 3.1, >= 3.1.2)
6
+ cucumber (>= 4, < 5)
7
7
  grpc (>= 1, < 2)
8
8
  puma (~> 4.3, >= 4.3.3)
9
9
  rest-client (~> 2.1)
@@ -14,47 +14,57 @@ PATH
14
14
  GEM
15
15
  remote: https://rubygems.org/
16
16
  specs:
17
- amatch (0.4.0)
18
- mize
19
- tins (~> 1.0)
17
+ activesupport (6.0.3.2)
18
+ concurrent-ruby (~> 1.0, >= 1.0.2)
19
+ i18n (>= 0.7, < 2)
20
+ minitest (~> 5.1)
21
+ tzinfo (~> 1.1)
22
+ zeitwerk (~> 2.2, >= 2.2.2)
20
23
  ast (2.4.1)
21
24
  backport (1.1.2)
22
- backports (3.18.1)
23
25
  benchmark (0.1.0)
24
26
  benchmark-malloc (0.2.0)
25
27
  benchmark-perf (0.6.0)
26
28
  benchmark-trend (0.4.0)
27
29
  builder (3.2.4)
28
- chutney (2.0.3.1)
29
- amatch (~> 0.4.0)
30
- gherkin (~> 5.1.0)
31
- i18n (~> 1.8.2)
32
- pastel (~> 0.7)
33
- tty-pie (~> 0.3)
34
30
  concurrent-ruby (1.1.6)
35
- cucumber (3.2.0)
36
- builder (>= 2.1.2)
37
- cucumber-core (~> 3.2.0)
38
- cucumber-expressions (~> 6.0.1)
39
- cucumber-wire (~> 0.0.1)
40
- diff-lcs (~> 1.3)
41
- gherkin (~> 5.1.0)
42
- multi_json (>= 1.7.5, < 2.0)
43
- multi_test (>= 0.1.2)
44
- cucumber-core (3.2.1)
45
- backports (>= 3.8.0)
46
- cucumber-tag_expressions (~> 1.1.0)
47
- gherkin (~> 5.0)
48
- cucumber-expressions (6.0.1)
49
- cucumber-tag_expressions (1.1.1)
50
- cucumber-wire (0.0.1)
51
- diff-lcs (1.4.4)
31
+ cucumber (4.1.0)
32
+ builder (~> 3.2, >= 3.2.3)
33
+ cucumber-core (~> 7.1, >= 7.1.0)
34
+ cucumber-create-meta (~> 1.0.0, >= 1.0.0)
35
+ cucumber-cucumber-expressions (~> 10.1, >= 10.1.0)
36
+ cucumber-gherkin (~> 14.0, >= 14.0.1)
37
+ cucumber-html-formatter (~> 7.0, >= 7.0.0)
38
+ cucumber-messages (~> 12.2, >= 12.2.0)
39
+ cucumber-wire (~> 3.1, >= 3.1.0)
40
+ diff-lcs (~> 1.3, >= 1.3, < 1.4)
41
+ multi_test (~> 0.1, >= 0.1.2)
42
+ sys-uname (~> 1.0, >= 1.0.2)
43
+ cucumber-core (7.1.0)
44
+ cucumber-gherkin (~> 14.0, >= 14.0.1)
45
+ cucumber-messages (~> 12.2, >= 12.2.0)
46
+ cucumber-tag-expressions (~> 2.0, >= 2.0.4)
47
+ cucumber-create-meta (1.0.0)
48
+ cucumber-messages (~> 12.2, >= 12.2.0)
49
+ sys-uname (~> 1.2, >= 1.2.1)
50
+ cucumber-cucumber-expressions (10.2.1)
51
+ cucumber-gherkin (14.0.1)
52
+ cucumber-messages (~> 12.2, >= 12.2.0)
53
+ cucumber-html-formatter (7.0.0)
54
+ cucumber-messages (~> 12.2, >= 12.2.0)
55
+ cucumber-messages (12.2.0)
56
+ protobuf-cucumber (~> 3.10, >= 3.10.8)
57
+ cucumber-tag-expressions (2.0.4)
58
+ cucumber-wire (3.1.0)
59
+ cucumber-core (~> 7.1, >= 7.1.0)
60
+ cucumber-cucumber-expressions (~> 10.1, >= 10.1.0)
61
+ cucumber-messages (~> 12.2, >= 12.2.0)
62
+ diff-lcs (1.3)
52
63
  docile (1.3.2)
53
64
  domain_name (0.5.20190701)
54
65
  unf (>= 0.0.5, < 1.0.0)
55
66
  e2mmap (0.1.0)
56
- equatable (0.6.1)
57
- gherkin (5.1.0)
67
+ ffi (1.13.1)
58
68
  google-protobuf (3.12.2)
59
69
  googleapis-common-protos-types (1.0.5)
60
70
  google-protobuf (~> 3.11)
@@ -70,13 +80,12 @@ GEM
70
80
  jaro_winkler (1.5.4)
71
81
  json (2.3.0)
72
82
  maruku (0.7.3)
83
+ middleware (0.1.0)
73
84
  mime-types (3.3.1)
74
85
  mime-types-data (~> 3.2015)
75
86
  mime-types-data (3.2020.0512)
76
87
  mini_portile2 (2.4.0)
77
- mize (0.4.0)
78
- protocol (~> 2.0)
79
- multi_json (1.14.1)
88
+ minitest (5.14.1)
80
89
  multi_test (0.1.2)
81
90
  mustermann (1.1.1)
82
91
  ruby2_keywords (~> 0.0.1)
@@ -87,11 +96,11 @@ GEM
87
96
  parallel (1.19.2)
88
97
  parser (2.7.1.4)
89
98
  ast (~> 2.4.1)
90
- pastel (0.7.4)
91
- equatable (~> 0.6)
92
- tty-color (~> 0.5)
93
- protocol (2.0.0)
94
- ruby_parser (~> 3.0)
99
+ protobuf-cucumber (3.10.8)
100
+ activesupport (>= 3.2)
101
+ middleware
102
+ thor
103
+ thread_safe
95
104
  puma (4.3.5)
96
105
  nio4r (~> 2.0)
97
106
  rack (2.2.3)
@@ -139,9 +148,6 @@ GEM
139
148
  parser (>= 2.7.0.1)
140
149
  ruby-progressbar (1.10.1)
141
150
  ruby2_keywords (0.0.2)
142
- ruby_parser (3.14.2)
143
- sexp_processor (~> 4.9)
144
- sexp_processor (4.15.0)
145
151
  simplecov (0.17.1)
146
152
  docile (~> 1.1)
147
153
  json (>= 1.8, < 3)
@@ -166,28 +172,25 @@ GEM
166
172
  thor (~> 1.0)
167
173
  tilt (~> 2.0)
168
174
  yard (~> 0.9, >= 0.9.24)
169
- sync (0.5.0)
175
+ sys-uname (1.2.1)
176
+ ffi (>= 1.0.0)
170
177
  thor (1.0.1)
178
+ thread_safe (0.3.6)
171
179
  tilt (2.0.10)
172
- tins (1.25.0)
173
- sync
174
- tty-color (0.5.1)
175
- tty-cursor (0.7.1)
176
- tty-pie (0.3.0)
177
- pastel (~> 0.7.3)
178
- tty-cursor (~> 0.7)
180
+ tzinfo (1.2.7)
181
+ thread_safe (~> 0.1)
179
182
  unf (0.1.4)
180
183
  unf_ext
181
184
  unf_ext (0.0.7.7)
182
185
  unicode-display_width (1.7.0)
183
186
  yard (0.9.25)
187
+ zeitwerk (2.3.1)
184
188
 
185
189
  PLATFORMS
186
190
  ruby
187
191
 
188
192
  DEPENDENCIES
189
193
  bundler (~> 2.1, >= 2.1.4)
190
- chutney (~> 2.0, >= 2.0.3.1)
191
194
  grpc-tools (>= 1, < 2)
192
195
  nonnative!
193
196
  rake (~> 13.0, >= 13.0.1)
data/Makefile CHANGED
@@ -11,7 +11,6 @@ features: clean
11
11
 
12
12
  analysis:
13
13
  bundle exec rubocop
14
- bundle exec chutney
15
14
 
16
15
  cleanup-analysis:
17
16
  bundle exec rubocop -a
data/README.md CHANGED
@@ -57,7 +57,7 @@ Nonnative.configure do |config|
57
57
  d.command = 'features/support/bin/start 12_321'
58
58
  d.timeout = 0.5
59
59
  d.port = 12_321
60
- d.file = 'features/logs/12_321.log'
60
+ d.log = 'features/logs/12_321.log'
61
61
  d.signal = 'INT' # Possible values are described in Signal.list.keys
62
62
  end
63
63
 
@@ -66,7 +66,7 @@ Nonnative.configure do |config|
66
66
  d.command = 'features/support/bin/start 12_322'
67
67
  d.timeout = 0.5
68
68
  d.port = 12_322
69
- d.file = 'features/logs/12_322.log'
69
+ d.log = 'features/logs/12_322.log'
70
70
  end
71
71
  end
72
72
  ```
@@ -82,14 +82,14 @@ processes:
82
82
  command: features/support/bin/start 12_321
83
83
  timeout: 5
84
84
  port: 12321
85
- file: features/logs/12_321.log
85
+ log: features/logs/12_321.log
86
86
  signal: INT # Possible values are described in Signal.list.keys
87
87
  -
88
88
  name: start_2
89
89
  command: features/support/bin/start 12_322
90
90
  timeout: 5
91
91
  port: 12322
92
- file: features/logs/12_322.log
92
+ log: features/logs/12_322.log
93
93
  ```
94
94
 
95
95
  Then load the file with
@@ -133,18 +133,20 @@ require 'nonnative'
133
133
  Nonnative.configure do |config|
134
134
  config.strategy = :manual
135
135
 
136
- config.server do |d|
137
- d.name = 'server_1'
138
- d.klass = Nonnative::EchoServer
139
- d.timeout = 1
140
- d.port = 12_323
136
+ config.server do |s|
137
+ s.name = 'server_1'
138
+ s.klass = Nonnative::EchoServer
139
+ s.timeout = 1
140
+ s.port = 12_323
141
+ s.log = 'features/logs/server_1.log'
141
142
  end
142
143
 
143
- config.server do |d|
144
- d.name = 'server_2'
145
- d.klass = Nonnative::EchoServer
146
- d.timeout = 1
147
- d.port = 12_324
144
+ config.server do |s|
145
+ s.name = 'server_2'
146
+ s.klass = Nonnative::EchoServer
147
+ s.timeout = 1
148
+ s.port = 12_324
149
+ s.log = 'features/logs/server_2.log'
148
150
  end
149
151
  end
150
152
  ```
@@ -160,11 +162,13 @@ servers:
160
162
  klass: Nonnative::EchoServer
161
163
  timeout: 1
162
164
  port: 12323
165
+ log: features/logs/server_1.log
163
166
  -
164
167
  name: server_2
165
168
  klass: Nonnative::EchoServer
166
169
  timeout: 1
167
170
  port: 12324
171
+ log: features/logs/server_2.log
168
172
  ```
169
173
 
170
174
  Then load the file with:
@@ -182,7 +186,11 @@ Define your server:
182
186
  ```ruby
183
187
  module Nonnative
184
188
  module Features
185
- class Application < Sinatra::Base
189
+ class Application < Sinatra::Application
190
+ configure do
191
+ set :server_settings, log_requests: true
192
+ end
193
+
186
194
  get '/hello' do
187
195
  'Hello World!'
188
196
  end
@@ -205,11 +213,12 @@ require 'nonnative'
205
213
  Nonnative.configure do |config|
206
214
  config.strategy = :manual
207
215
 
208
- config.server do |d|
209
- d.name = 'http_server_1'
210
- d.klass = Nonnative::Features::HTTPServer
211
- d.timeout = 1
212
- d.port = 4567
216
+ config.server do |s|
217
+ s.name = 'http_server_1'
218
+ s.klass = Nonnative::Features::HTTPServer
219
+ s.timeout = 1
220
+ s.port = 4567
221
+ s.log = 'features/logs/http_server_1.log'
213
222
  end
214
223
  end
215
224
  ```
@@ -225,6 +234,7 @@ servers:
225
234
  klass: Nonnative::Features::HTTPServer
226
235
  timeout: 1
227
236
  port: 4567
237
+ log: features/logs/http_server_1.log
228
238
  ```
229
239
 
230
240
  Then load the file with:
@@ -265,11 +275,12 @@ require 'nonnative'
265
275
  Nonnative.configure do |config|
266
276
  config.strategy = :manual
267
277
 
268
- config.server do |d|
269
- d.name = 'grpc_server_1'
270
- d.klass = Nonnative::Features::GRPCServer
271
- d.timeout = 1
272
- d.port = 9002
278
+ config.server do |s|
279
+ s.name = 'grpc_server_1'
280
+ s.klass = Nonnative::Features::GRPCServer
281
+ s.timeout = 1
282
+ s.port = 9002
283
+ s.log = 'features/logs/grpc_server_1.log'
273
284
  end
274
285
  end
275
286
  ```
@@ -285,6 +296,7 @@ servers:
285
296
  klass: Nonnative::Features::GRPCServer
286
297
  timeout: 1
287
298
  port: 9002
299
+ log: features/logs/grpc_server_1.log
288
300
  ```
289
301
 
290
302
  Then load the file with:
@@ -297,8 +309,8 @@ Nonnative.load_configuration('configuration.yml')
297
309
  #### Proxies
298
310
 
299
311
  We allow different proxies to be configured. These proxies can be used to simulate all kind of situations. The proxies that can be configured are:
300
- - none (this is the default)
301
- - chaos
312
+ - `none` (this is the default)
313
+ - `fault_injection`
302
314
 
303
315
  Setup it up programmatically:
304
316
 
@@ -310,8 +322,12 @@ Nonnative.configure do |config|
310
322
 
311
323
  config.server do |d|
312
324
  d.proxy = {
313
- type: 'chaos',
314
- port: 20_000
325
+ type: 'fault_injection',
326
+ port: 20_000,
327
+ log: 'features/logs/proxy_server.log',
328
+ options: {
329
+ delay: 5
330
+ }
315
331
  }
316
332
  end
317
333
  end
@@ -325,6 +341,26 @@ strategy: manual
325
341
  servers:
326
342
  -
327
343
  proxy:
328
- type: chaos
344
+ type: fault_injection
329
345
  port: 20000
346
+ log: features/logs/proxy_server.log
347
+ options:
348
+ delay: 5
349
+ ```
350
+
351
+ ##### Fault Injection
352
+
353
+ The `fault_injection` proxy allows you to simulate failures by injecting them. We currently support the following:
354
+ - `close_all` - Closes the socket as soon as it connects.
355
+ - `delay` - This delays the communication between the connection. Default is 2 secs can be configured through options.
356
+ - `invalid_data` - This takes the input and rearranges it to produce invalid data.
357
+
358
+ Setup it up programmatically:
359
+
360
+ ```ruby
361
+ name = 'name of server in configuration'
362
+ server = Nonnative.pool.server_by_name(name)
363
+
364
+ server.proxy.close_all # To use close_all.
365
+ server.proxy.reset # To reset it back to a good state.
330
366
  ```
@@ -34,7 +34,12 @@ require 'nonnative/observability'
34
34
  require 'nonnative/proxy_factory'
35
35
  require 'nonnative/proxy'
36
36
  require 'nonnative/no_proxy'
37
- require 'nonnative/chaos_proxy'
37
+ require 'nonnative/fault_injection_proxy'
38
+ require 'nonnative/socket_pair'
39
+ require 'nonnative/close_all_socket_pair'
40
+ require 'nonnative/delay_socket_pair'
41
+ require 'nonnative/invalid_data_socket_pair'
42
+ require 'nonnative/socket_pair_factory'
38
43
 
39
44
  module Nonnative
40
45
  class << self
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ class CloseAllSocketPair < SocketPair
5
+ def connect(local_socket)
6
+ local_socket.close
7
+ end
8
+ end
9
+ end
@@ -38,7 +38,7 @@ module Nonnative
38
38
  end
39
39
 
40
40
  def command_spawn
41
- spawn(service.command, %i[out err] => [service.file, 'a'])
41
+ spawn(service.command, %i[out err] => [service.log, 'a'])
42
42
  end
43
43
 
44
44
  def command_exists?
@@ -24,7 +24,7 @@ module Nonnative
24
24
  d.command = fd['command']
25
25
  d.timeout = fd['timeout']
26
26
  d.port = fd['port']
27
- d.file = fd['file']
27
+ d.log = fd['log']
28
28
  d.signal = fd['signal']
29
29
  end
30
30
  end
@@ -38,13 +38,16 @@ module Nonnative
38
38
  s.klass = Object.const_get(fd['klass'])
39
39
  s.timeout = fd['timeout']
40
40
  s.port = fd['port']
41
+ s.log = fd['log']
41
42
 
42
43
  proxy = fd['proxy']
43
44
 
44
45
  if proxy
45
46
  s.proxy = {
46
47
  type: proxy['type'],
47
- port: proxy['port']
48
+ port: proxy['port'],
49
+ log: proxy['log'],
50
+ options: proxy['options']
48
51
  }
49
52
  end
50
53
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Nonnative
4
4
  class ConfigurationProcess
5
- attr_accessor :name, :command, :timeout, :port, :file, :signal
5
+ attr_accessor :name, :command, :timeout, :port, :log, :signal
6
6
  end
7
7
  end
@@ -2,11 +2,12 @@
2
2
 
3
3
  module Nonnative
4
4
  class ConfigurationProxy
5
- attr_accessor :type, :port
5
+ attr_accessor :type, :port, :log, :options
6
6
 
7
7
  def initialize
8
8
  self.type = 'none'
9
9
  self.port = 0
10
+ self.options = {}
10
11
  end
11
12
  end
12
13
  end
@@ -2,8 +2,7 @@
2
2
 
3
3
  module Nonnative
4
4
  class ConfigurationServer
5
- attr_accessor :name, :klass, :timeout, :port
6
-
5
+ attr_accessor :name, :klass, :timeout, :port, :log
7
6
  attr_reader :proxy
8
7
 
9
8
  def initialize
@@ -13,6 +12,8 @@ module Nonnative
13
12
  def proxy=(value)
14
13
  proxy.type = value[:type]
15
14
  proxy.port = value[:port]
15
+ proxy.log = value[:log]
16
+ proxy.options = value[:options]
16
17
  end
17
18
  end
18
19
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ class DelaySocketPair < SocketPair
5
+ def read(socket)
6
+ duration = proxy.options.dig(:delay) || 2
7
+ sleep duration
8
+
9
+ super socket
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ class FaultInjectionProxy < Nonnative::Proxy
5
+ def initialize(service)
6
+ @connections = Concurrent::Hash.new
7
+ @logger = Logger.new(service.proxy.log)
8
+ @mutex = Mutex.new
9
+ @state = :none
10
+
11
+ super service
12
+ end
13
+
14
+ def start
15
+ @tcp_server = ::TCPServer.new('0.0.0.0', service.port)
16
+ @thread = Thread.new { perform_start }
17
+ end
18
+
19
+ def stop
20
+ thread.terminate
21
+ tcp_server.close
22
+ end
23
+
24
+ def close_all
25
+ apply_state :close_all
26
+ end
27
+
28
+ def delay
29
+ apply_state :delay
30
+ end
31
+
32
+ def invalid_data
33
+ apply_state :invalid_data
34
+ end
35
+
36
+ def reset
37
+ apply_state :none
38
+ end
39
+
40
+ def port
41
+ service.proxy.port
42
+ end
43
+
44
+ private
45
+
46
+ attr_reader :tcp_server, :thread, :connections, :mutex, :state, :logger
47
+
48
+ def perform_start
49
+ loop do
50
+ thread = Thread.start(tcp_server.accept) do |local_socket|
51
+ id = Thread.current.object_id
52
+
53
+ logger.info "started connection for #{id} with socket #{local_socket.inspect}"
54
+
55
+ connect local_socket
56
+ connections.delete(id)
57
+
58
+ logger.info "finished connection for #{id} with socket #{local_socket.inspect}"
59
+ end
60
+
61
+ thread.report_on_exception = false
62
+ connections[thread.object_id] = thread
63
+ end
64
+ end
65
+
66
+ def connect(local_socket)
67
+ SocketPairFactory.create(read_state, service.proxy, logger).connect(local_socket)
68
+ end
69
+
70
+ def apply_state(state)
71
+ mutex.synchronize { @state = state }
72
+ end
73
+
74
+ def read_state
75
+ mutex.synchronize { state }
76
+ end
77
+ end
78
+ end
@@ -6,6 +6,11 @@ module Nonnative
6
6
  @server = GRPC::RpcServer.new
7
7
  server.handle(svc)
8
8
 
9
+ # Unfortunately gRPC has only one logger so the first server wins.
10
+ GRPC.define_singleton_method(:logger) do
11
+ @logger ||= Logger.new(service.log)
12
+ end
13
+
9
14
  super service
10
15
  end
11
16
 
@@ -8,31 +8,33 @@ module Nonnative
8
8
 
9
9
  protected
10
10
 
11
- def get(pathname, headers = {})
11
+ def get(pathname, headers = {}, timeout = 60)
12
12
  with_exception do
13
13
  uri = URI.join(host, pathname)
14
- RestClient.get(uri.to_s, headers)
14
+ RestClient::Request.execute(method: :get, url: uri.to_s, headers: headers, timeout: timeout)
15
15
  end
16
16
  end
17
17
 
18
- def post(pathname, payload, headers = {})
18
+ def post(pathname, payload, headers = {}, timeout = 60)
19
19
  with_exception do
20
20
  uri = URI.join(host, pathname)
21
- RestClient.post(uri.to_s, payload.to_json, headers)
21
+ RestClient::Request.execute(method: :post, url: uri.to_s, payload: payload.to_json, headers: headers,
22
+ timeout: timeout)
22
23
  end
23
24
  end
24
25
 
25
- def delete(pathname, headers = {})
26
+ def delete(pathname, headers = {}, timeout = 60)
26
27
  with_exception do
27
28
  uri = URI.join(host, pathname)
28
- RestClient.delete(uri.to_s, headers)
29
+ RestClient::Request.execute(method: :delete, url: uri.to_s, headers: headers, timeout: timeout)
29
30
  end
30
31
  end
31
32
 
32
- def put(pathname, payload, headers = {})
33
+ def put(pathname, payload, headers = {}, timeout = 60)
33
34
  with_exception do
34
35
  uri = URI.join(host, pathname)
35
- RestClient.put(uri.to_s, payload.to_json, headers)
36
+ RestClient::Request.execute(method: :put, url: uri.to_s, payload: payload.to_json, headers: headers,
37
+ timeout: timeout)
36
38
  end
37
39
  end
38
40
 
@@ -42,6 +44,8 @@ module Nonnative
42
44
 
43
45
  def with_exception
44
46
  yield
47
+ rescue RestClient::Exceptions::ReadTimeout => e
48
+ raise e
45
49
  rescue RestClient::ExceptionWithResponse => e
46
50
  e.response
47
51
  end
@@ -3,7 +3,9 @@
3
3
  module Nonnative
4
4
  class HTTPServer < Nonnative::Server
5
5
  def initialize(service)
6
- @server = Puma::Server.new(app, Puma::Events.strings)
6
+ log = File.open(service.log, 'a')
7
+ events = Puma::Events.new(log, log)
8
+ @server = Puma::Server.new(app, events)
7
9
 
8
10
  super service
9
11
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ class InvalidDataSocketPair < SocketPair
5
+ def write(socket, data)
6
+ data = data.chars.shuffle.join
7
+
8
+ super socket, data
9
+ end
10
+ end
11
+ end
@@ -4,11 +4,10 @@ module Nonnative
4
4
  class Proxy
5
5
  def initialize(service)
6
6
  @service = service
7
- @timeout = Nonnative::Timeout.new(service.timeout)
8
7
  end
9
8
 
10
9
  protected
11
10
 
12
- attr_reader :service, :timeout
11
+ attr_reader :service
13
12
  end
14
13
  end
@@ -4,13 +4,14 @@ module Nonnative
4
4
  class ProxyFactory
5
5
  class << self
6
6
  def create(service)
7
- case service.proxy.type
8
- when 'chaos'
9
- ChaosProxy.new(service)
10
- else
11
- # By default we want no proxy.
12
- NoProxy.new(service)
13
- end
7
+ proxy = case service.proxy.type
8
+ when 'fault_injection'
9
+ FaultInjectionProxy
10
+ else
11
+ NoProxy
12
+ end
13
+
14
+ proxy.new(service)
14
15
  end
15
16
  end
16
17
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ class SocketPair
5
+ def initialize(proxy, logger)
6
+ @proxy = proxy
7
+ @logger = logger
8
+ end
9
+
10
+ def connect(local_socket)
11
+ remote_socket = create_remote_socket
12
+
13
+ loop do
14
+ ready = select([local_socket, remote_socket], nil, nil)
15
+
16
+ break if pipe(ready, local_socket, remote_socket)
17
+ break if pipe(ready, remote_socket, local_socket)
18
+ end
19
+ rescue StandardError => e
20
+ logger.error e
21
+ ensure
22
+ local_socket.close
23
+ remote_socket&.close
24
+ end
25
+
26
+ protected
27
+
28
+ attr_reader :proxy, :logger
29
+
30
+ def create_remote_socket
31
+ ::TCPSocket.new('0.0.0.0', proxy.port)
32
+ end
33
+
34
+ def pipe(ready, socket1, socket2)
35
+ if ready[0].include?(socket1)
36
+ data = read(socket1)
37
+ return true if data.empty?
38
+
39
+ write socket2, data
40
+ end
41
+
42
+ false
43
+ end
44
+
45
+ def read(socket)
46
+ socket.recv(1024)
47
+ end
48
+
49
+ def write(socket, data)
50
+ socket.write(data)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ class SocketPairFactory
5
+ class << self
6
+ def create(type, proxy, logger)
7
+ pair = case type
8
+ when :close_all
9
+ CloseAllSocketPair
10
+ when :delay
11
+ DelaySocketPair
12
+ when :invalid_data
13
+ InvalidDataSocketPair
14
+ else
15
+ SocketPair
16
+ end
17
+
18
+ pair.new(proxy, logger)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
- VERSION = '1.21.0'
4
+ VERSION = '1.26.0'
5
5
  end
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
25
25
  spec.require_paths = ['lib']
26
26
 
27
27
  spec.add_dependency 'concurrent-ruby', '~> 1.0', '>= 1.0.5'
28
- spec.add_dependency 'cucumber', '~> 3.1', '>= 3.1.2'
28
+ spec.add_dependency 'cucumber', ['>= 4', '< 5']
29
29
  spec.add_dependency 'grpc', ['>= 1', '< 2']
30
30
  spec.add_dependency 'puma', '~> 4.3', '>= 4.3.3'
31
31
  spec.add_dependency 'rest-client', '~> 2.1'
@@ -34,7 +34,6 @@ Gem::Specification.new do |spec|
34
34
  spec.add_dependency 'sinatra', '~> 2.0', '>= 2.0.8.1'
35
35
 
36
36
  spec.add_development_dependency 'bundler', '~> 2.1', '>= 2.1.4'
37
- spec.add_development_dependency 'chutney', '~> 2.0', '>= 2.0.3.1'
38
37
  spec.add_development_dependency 'grpc-tools', ['>= 1', '< 2']
39
38
  spec.add_development_dependency 'rake', '~> 13.0', '>= 13.0.1'
40
39
  spec.add_development_dependency 'rubocop', '~> 0.87.1'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nonnative
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.21.0
4
+ version: 1.26.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Falkowski
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-07-09 00:00:00.000000000 Z
11
+ date: 2020-07-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -34,22 +34,22 @@ dependencies:
34
34
  name: cucumber
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '3.1'
40
37
  - - ">="
41
38
  - !ruby/object:Gem::Version
42
- version: 3.1.2
39
+ version: '4'
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '5'
43
43
  type: :runtime
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
46
46
  requirements:
47
- - - "~>"
48
- - !ruby/object:Gem::Version
49
- version: '3.1'
50
47
  - - ">="
51
48
  - !ruby/object:Gem::Version
52
- version: 3.1.2
49
+ version: '4'
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '5'
53
53
  - !ruby/object:Gem::Dependency
54
54
  name: grpc
55
55
  requirement: !ruby/object:Gem::Requirement
@@ -178,26 +178,6 @@ dependencies:
178
178
  - - ">="
179
179
  - !ruby/object:Gem::Version
180
180
  version: 2.1.4
181
- - !ruby/object:Gem::Dependency
182
- name: chutney
183
- requirement: !ruby/object:Gem::Requirement
184
- requirements:
185
- - - "~>"
186
- - !ruby/object:Gem::Version
187
- version: '2.0'
188
- - - ">="
189
- - !ruby/object:Gem::Version
190
- version: 2.0.3.1
191
- type: :development
192
- prerelease: false
193
- version_requirements: !ruby/object:Gem::Requirement
194
- requirements:
195
- - - "~>"
196
- - !ruby/object:Gem::Version
197
- version: '2.0'
198
- - - ">="
199
- - !ruby/object:Gem::Version
200
- version: 2.0.3.1
201
181
  - !ruby/object:Gem::Dependency
202
182
  name: grpc-tools
203
183
  requirement: !ruby/object:Gem::Requirement
@@ -303,16 +283,19 @@ files:
303
283
  - bin/setup
304
284
  - lib/nonnative.rb
305
285
  - lib/nonnative/before.rb
306
- - lib/nonnative/chaos_proxy.rb
286
+ - lib/nonnative/close_all_socket_pair.rb
307
287
  - lib/nonnative/command.rb
308
288
  - lib/nonnative/configuration.rb
309
289
  - lib/nonnative/configuration_process.rb
310
290
  - lib/nonnative/configuration_proxy.rb
311
291
  - lib/nonnative/configuration_server.rb
292
+ - lib/nonnative/delay_socket_pair.rb
312
293
  - lib/nonnative/error.rb
294
+ - lib/nonnative/fault_injection_proxy.rb
313
295
  - lib/nonnative/grpc_server.rb
314
296
  - lib/nonnative/http_client.rb
315
297
  - lib/nonnative/http_server.rb
298
+ - lib/nonnative/invalid_data_socket_pair.rb
316
299
  - lib/nonnative/manual.rb
317
300
  - lib/nonnative/no_proxy.rb
318
301
  - lib/nonnative/observability.rb
@@ -322,6 +305,8 @@ files:
322
305
  - lib/nonnative/proxy_factory.rb
323
306
  - lib/nonnative/server.rb
324
307
  - lib/nonnative/service.rb
308
+ - lib/nonnative/socket_pair.rb
309
+ - lib/nonnative/socket_pair_factory.rb
325
310
  - lib/nonnative/start_error.rb
326
311
  - lib/nonnative/startup.rb
327
312
  - lib/nonnative/stop_error.rb
@@ -333,7 +318,7 @@ homepage: https://github.com/alexfalkowski/nonnative
333
318
  licenses:
334
319
  - Unlicense
335
320
  metadata: {}
336
- post_install_message:
321
+ post_install_message:
337
322
  rdoc_options: []
338
323
  require_paths:
339
324
  - lib
@@ -348,8 +333,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
348
333
  - !ruby/object:Gem::Version
349
334
  version: '0'
350
335
  requirements: []
351
- rubygems_version: 3.0.3
352
- signing_key:
336
+ rubygems_version: 3.0.8
337
+ signing_key:
353
338
  specification_version: 4
354
339
  summary: Allows you to keep using the power of ruby to test other systems
355
340
  test_files: []
@@ -1,94 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Nonnative
4
- class ChaosProxy < Nonnative::Proxy
5
- def initialize(service)
6
- @connections = Concurrent::Hash.new
7
- @mutex = Mutex.new
8
- @state = :none
9
-
10
- super service
11
- end
12
-
13
- def start
14
- @tcp_server = ::TCPServer.new('0.0.0.0', service.port)
15
- @thread = Thread.new { perform_start }
16
- end
17
-
18
- def stop
19
- thread.terminate
20
- tcp_server.close
21
- end
22
-
23
- def close_all
24
- apply_state :close_all
25
- end
26
-
27
- def reset
28
- apply_state :none
29
- end
30
-
31
- def port
32
- service.proxy.port
33
- end
34
-
35
- private
36
-
37
- attr_reader :tcp_server, :thread, :connections, :mutex, :state
38
-
39
- def perform_start
40
- loop do
41
- thread = Thread.start(tcp_server.accept) { |local_socket| connect(local_socket) }
42
- connections[thread.object_id] = thread
43
- end
44
- end
45
-
46
- def connect(local_socket)
47
- return local_socket.close if state?(:close_all)
48
-
49
- remote_socket = create_remote_socket
50
- return unless remote_socket
51
-
52
- loop do
53
- ready = select([local_socket, remote_socket], nil, nil)
54
-
55
- break if write(ready, local_socket, remote_socket)
56
- break if write(ready, remote_socket, local_socket)
57
- end
58
- rescue Errno::ECONNRESET
59
- # Just ignore it.
60
- ensure
61
- local_socket.close
62
- remote_socket&.close
63
- connections.delete(Thread.current.object_id)
64
- end
65
-
66
- def create_remote_socket
67
- timeout.perform do
68
- ::TCPSocket.new('0.0.0.0', port)
69
- rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
70
- sleep 0.01
71
- retry
72
- end
73
- end
74
-
75
- def write(ready, socket1, socket2)
76
- if ready[0].include?(socket1)
77
- data = socket1.recv(1024)
78
- return true if data.empty?
79
-
80
- socket2.write(data)
81
- end
82
-
83
- false
84
- end
85
-
86
- def apply_state(state)
87
- mutex.synchronize { @state = state }
88
- end
89
-
90
- def state?(state)
91
- mutex.synchronize { @state == state }
92
- end
93
- end
94
- end