nonnative 1.22.0 → 1.23.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: 5a6ee4ae9e2629fcae27b82ea0f4db839d6c131ab1687ed286b33eb80fc5a41a
4
- data.tar.gz: 53192dec8216ce696fe9774f77b98904a6f13b041cbbe131bbaacaafc45cffda
3
+ metadata.gz: 2265811d2fdb090bd12cb35065038272974519ad6a7758bb2b7c281ca7b804c1
4
+ data.tar.gz: c08f324e5de548b1457f3ff16585569f7917340b6367dc0b9d1cc1ceae2d4645
5
5
  SHA512:
6
- metadata.gz: 6da13105ad656ebfe2f5bc5c1ba643e12776b2747da9453c3e1da62729475e2490d67942482b7f0a9de480ccebd245fe798cb57cf5db4ee6ef483781b8657be4
7
- data.tar.gz: 787afe51f6eed81281d439f91824c795a96f4673712302983977c976df949e23eeeee58a3d3c5e71829f4c69b6b8931531488f7bf6a7674c3156322b81d10195
6
+ metadata.gz: dfe2d40e5c27f6d9425fbfc7059ce3d3723fdb00451a7a72a399d359627f7424699b044115848c45a7e180606919f81d03a439d6942acfbbcd8584350e35499a
7
+ data.tar.gz: 6b2221ac50779bf621e5377ae1940f0621e4f1b4d6349aead6f9adac575d37f9f3d04e12aa65f9118fdc8b277d8021e653566c77cc1dee2815ef501d8b936073
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nonnative (1.22.0)
4
+ nonnative (1.23.0)
5
5
  concurrent-ruby (~> 1.0, >= 1.0.5)
6
6
  cucumber (>= 4, < 5)
7
7
  grpc (>= 1, < 2)
data/README.md CHANGED
@@ -297,8 +297,8 @@ Nonnative.load_configuration('configuration.yml')
297
297
  #### Proxies
298
298
 
299
299
  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
300
+ - `none` (this is the default)
301
+ - `chaos`
302
302
 
303
303
  Setup it up programmatically:
304
304
 
@@ -328,3 +328,20 @@ servers:
328
328
  type: chaos
329
329
  port: 20000
330
330
  ```
331
+
332
+ ##### Fault Injection
333
+
334
+ The `chaos` proxy allows you to simulate failures by injecting them. We currently support the following:
335
+ - `close_all` - Closes the socket as soon as it connects.
336
+ - `delay` - This delays the communication between the connection.
337
+ - `invalid_data` - This takes the input and rearranges it to produce invalid data.
338
+
339
+ Setup it up programmatically:
340
+
341
+ ```ruby
342
+ name = 'name of server in configuration'
343
+ server = Nonnative.pool.server_by_name(name)
344
+
345
+ server.proxy.close_all # To use close_all.
346
+ server.proxy.reset # To reset it back to a good state.
347
+ ```
@@ -35,6 +35,11 @@ require 'nonnative/proxy_factory'
35
35
  require 'nonnative/proxy'
36
36
  require 'nonnative/no_proxy'
37
37
  require 'nonnative/chaos_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
@@ -24,6 +24,14 @@ module Nonnative
24
24
  apply_state :close_all
25
25
  end
26
26
 
27
+ def delay
28
+ apply_state :delay
29
+ end
30
+
31
+ def invalid_data
32
+ apply_state :invalid_data
33
+ end
34
+
27
35
  def reset
28
36
  apply_state :none
29
37
  end
@@ -38,57 +46,21 @@ module Nonnative
38
46
 
39
47
  def perform_start
40
48
  loop do
41
- thread = Thread.start(tcp_server.accept) { |local_socket| connect(local_socket) }
49
+ thread = Thread.start(tcp_server.accept) do |local_socket|
50
+ SocketPairFactory.create(read_state, port).connect(local_socket)
51
+ connections.delete(Thread.current.object_id)
52
+ end
53
+ thread.report_on_exception = false
42
54
  connections[thread.object_id] = thread
43
55
  end
44
56
  end
45
57
 
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
58
  def apply_state(state)
87
59
  mutex.synchronize { @state = state }
88
60
  end
89
61
 
90
- def state?(state)
91
- mutex.synchronize { @state == state }
62
+ def read_state
63
+ mutex.synchronize { state }
92
64
  end
93
65
  end
94
66
  end
@@ -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
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ class DelaySocketPair < SocketPair
5
+ def read(socket)
6
+ sleep 2
7
+
8
+ super socket
9
+ end
10
+ end
11
+ end
@@ -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
@@ -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
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ class SocketPair
5
+ def initialize(port)
6
+ @port = port
7
+ end
8
+
9
+ def connect(local_socket)
10
+ remote_socket = create_remote_socket
11
+
12
+ loop do
13
+ ready = select([local_socket, remote_socket], nil, nil)
14
+
15
+ break if pipe(ready, local_socket, remote_socket)
16
+ break if pipe(ready, remote_socket, local_socket)
17
+ end
18
+ ensure
19
+ local_socket.close
20
+ remote_socket&.close
21
+ end
22
+
23
+ protected
24
+
25
+ attr_reader :port
26
+
27
+ def create_remote_socket
28
+ ::TCPSocket.new('0.0.0.0', port)
29
+ end
30
+
31
+ def pipe(ready, socket1, socket2)
32
+ if ready[0].include?(socket1)
33
+ data = read(socket1)
34
+ return true if data.empty?
35
+
36
+ write socket2, data
37
+ end
38
+
39
+ false
40
+ end
41
+
42
+ def read(socket)
43
+ socket.recv(1024)
44
+ end
45
+
46
+ def write(socket, data)
47
+ socket.write(data)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ class SocketPairFactory
5
+ class << self
6
+ def create(type, port)
7
+ case type
8
+ when :close_all
9
+ CloseAllSocketPair.new(port)
10
+ when :delay
11
+ DelaySocketPair.new(port)
12
+ when :invalid_data
13
+ InvalidDataSocketPair.new(port)
14
+ else
15
+ SocketPair.new(port)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
- VERSION = '1.22.0'
4
+ VERSION = '1.23.0'
5
5
  end
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.22.0
4
+ version: 1.23.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-10 00:00:00.000000000 Z
11
+ date: 2020-07-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -284,15 +284,18 @@ files:
284
284
  - lib/nonnative.rb
285
285
  - lib/nonnative/before.rb
286
286
  - lib/nonnative/chaos_proxy.rb
287
+ - lib/nonnative/close_all_socket_pair.rb
287
288
  - lib/nonnative/command.rb
288
289
  - lib/nonnative/configuration.rb
289
290
  - lib/nonnative/configuration_process.rb
290
291
  - lib/nonnative/configuration_proxy.rb
291
292
  - lib/nonnative/configuration_server.rb
293
+ - lib/nonnative/delay_socket_pair.rb
292
294
  - lib/nonnative/error.rb
293
295
  - lib/nonnative/grpc_server.rb
294
296
  - lib/nonnative/http_client.rb
295
297
  - lib/nonnative/http_server.rb
298
+ - lib/nonnative/invalid_data_socket_pair.rb
296
299
  - lib/nonnative/manual.rb
297
300
  - lib/nonnative/no_proxy.rb
298
301
  - lib/nonnative/observability.rb
@@ -302,6 +305,8 @@ files:
302
305
  - lib/nonnative/proxy_factory.rb
303
306
  - lib/nonnative/server.rb
304
307
  - lib/nonnative/service.rb
308
+ - lib/nonnative/socket_pair.rb
309
+ - lib/nonnative/socket_pair_factory.rb
305
310
  - lib/nonnative/start_error.rb
306
311
  - lib/nonnative/startup.rb
307
312
  - lib/nonnative/stop_error.rb
@@ -313,7 +318,7 @@ homepage: https://github.com/alexfalkowski/nonnative
313
318
  licenses:
314
319
  - Unlicense
315
320
  metadata: {}
316
- post_install_message:
321
+ post_install_message:
317
322
  rdoc_options: []
318
323
  require_paths:
319
324
  - lib
@@ -328,8 +333,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
328
333
  - !ruby/object:Gem::Version
329
334
  version: '0'
330
335
  requirements: []
331
- rubygems_version: 3.0.3
332
- signing_key:
336
+ rubygems_version: 3.0.8
337
+ signing_key:
333
338
  specification_version: 4
334
339
  summary: Allows you to keep using the power of ruby to test other systems
335
340
  test_files: []