nonnative 1.22.0 → 1.23.0

Sign up to get free protection for your applications and to get access to all the features.
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: []