nonnative 1.27.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +38 -0
  3. data/.config/cucumber.yml +1 -0
  4. data/.editorconfig +24 -0
  5. data/.gitignore +11 -0
  6. data/.rubocop.yml +87 -0
  7. data/.ruby-version +1 -0
  8. data/Gemfile +8 -0
  9. data/Gemfile.lock +202 -0
  10. data/LICENSE +24 -0
  11. data/Makefile +27 -0
  12. data/README.md +366 -0
  13. data/Rakefile +4 -0
  14. data/bin/console +15 -0
  15. data/bin/setup +9 -0
  16. data/lib/nonnative.rb +89 -0
  17. data/lib/nonnative/before.rb +11 -0
  18. data/lib/nonnative/close_all_socket_pair.rb +9 -0
  19. data/lib/nonnative/command.rb +54 -0
  20. data/lib/nonnative/configuration.rb +80 -0
  21. data/lib/nonnative/configuration_process.rb +7 -0
  22. data/lib/nonnative/configuration_proxy.rb +13 -0
  23. data/lib/nonnative/configuration_server.rb +19 -0
  24. data/lib/nonnative/delay_socket_pair.rb +12 -0
  25. data/lib/nonnative/error.rb +6 -0
  26. data/lib/nonnative/fault_injection_proxy.rb +78 -0
  27. data/lib/nonnative/grpc_server.rb +44 -0
  28. data/lib/nonnative/http_client.rb +53 -0
  29. data/lib/nonnative/http_server.rb +40 -0
  30. data/lib/nonnative/invalid_data_socket_pair.rb +11 -0
  31. data/lib/nonnative/manual.rb +3 -0
  32. data/lib/nonnative/no_proxy.rb +17 -0
  33. data/lib/nonnative/observability.rb +13 -0
  34. data/lib/nonnative/pool.rb +60 -0
  35. data/lib/nonnative/port.rb +44 -0
  36. data/lib/nonnative/proxy.rb +13 -0
  37. data/lib/nonnative/proxy_factory.rb +18 -0
  38. data/lib/nonnative/server.rb +41 -0
  39. data/lib/nonnative/service.rb +26 -0
  40. data/lib/nonnative/socket_pair.rb +53 -0
  41. data/lib/nonnative/socket_pair_factory.rb +22 -0
  42. data/lib/nonnative/start_error.rb +6 -0
  43. data/lib/nonnative/startup.rb +7 -0
  44. data/lib/nonnative/stop_error.rb +6 -0
  45. data/lib/nonnative/timeout.rb +21 -0
  46. data/lib/nonnative/version.rb +5 -0
  47. data/nonnative.gemspec +42 -0
  48. data/sonar-project.properties +4 -0
  49. metadata +340 -0
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ class HTTPClient
5
+ def initialize(host)
6
+ @host = host
7
+ end
8
+
9
+ protected
10
+
11
+ def get(pathname, headers = {}, timeout = 60)
12
+ with_exception do
13
+ uri = URI.join(host, pathname)
14
+ RestClient::Request.execute(method: :get, url: uri.to_s, headers: headers, timeout: timeout)
15
+ end
16
+ end
17
+
18
+ def post(pathname, payload, headers = {}, timeout = 60)
19
+ with_exception do
20
+ uri = URI.join(host, pathname)
21
+ RestClient::Request.execute(method: :post, url: uri.to_s, payload: payload.to_json, headers: headers,
22
+ timeout: timeout)
23
+ end
24
+ end
25
+
26
+ def delete(pathname, headers = {}, timeout = 60)
27
+ with_exception do
28
+ uri = URI.join(host, pathname)
29
+ RestClient::Request.execute(method: :delete, url: uri.to_s, headers: headers, timeout: timeout)
30
+ end
31
+ end
32
+
33
+ def put(pathname, payload, headers = {}, timeout = 60)
34
+ with_exception do
35
+ uri = URI.join(host, pathname)
36
+ RestClient::Request.execute(method: :put, url: uri.to_s, payload: payload.to_json, headers: headers,
37
+ timeout: timeout)
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ attr_reader :host
44
+
45
+ def with_exception
46
+ yield
47
+ rescue RestClient::Exceptions::ReadTimeout => e
48
+ raise e
49
+ rescue RestClient::ExceptionWithResponse => e
50
+ e.response
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ class HTTPServer < Nonnative::Server
5
+ def initialize(service)
6
+ log = File.open(service.log, 'a')
7
+ events = Puma::Events.new(log, log)
8
+ @server = Puma::Server.new(app, events)
9
+
10
+ super service
11
+ end
12
+
13
+ protected
14
+
15
+ def perform_start
16
+ server.add_tcp_listener '0.0.0.0', proxy.port
17
+ server.run.join
18
+ end
19
+
20
+ def perform_stop
21
+ server.stop(true)
22
+ end
23
+
24
+ def wait_start
25
+ timeout.perform do
26
+ super until server.running
27
+ end
28
+ end
29
+
30
+ def wait_stop
31
+ timeout.perform do
32
+ super while server.running
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ attr_reader :queue, :server
39
+ end
40
+ 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
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Do nothing as it's manual
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ class NoProxy < Proxy
5
+ def start
6
+ # Do nothing.
7
+ end
8
+
9
+ def stop
10
+ # Do nothing.
11
+ end
12
+
13
+ def port
14
+ service.port
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ class Observability < Nonnative::HTTPClient
5
+ def health
6
+ get('health', { content_type: :json, accept: :json })
7
+ end
8
+
9
+ def metrics
10
+ get('metrics')
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ class Pool
5
+ def initialize(configuration)
6
+ @configuration = configuration
7
+ end
8
+
9
+ def start(&block)
10
+ [servers, processes].each { |t| process(t, :start, :open?, &block) }
11
+ end
12
+
13
+ def stop(&block)
14
+ [processes, servers].each { |t| process(t, :stop, :closed?, &block) }
15
+ end
16
+
17
+ def server_by_name(name)
18
+ index = configuration.servers.find_index { |s| s.name == name }
19
+ servers[index].first
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :configuration
25
+
26
+ def processes
27
+ @processes ||= configuration.processes.map do |d|
28
+ [Nonnative::Command.new(d), Nonnative::Port.new(d)]
29
+ end
30
+ end
31
+
32
+ def servers
33
+ @servers ||= configuration.servers.map do |d|
34
+ [d.klass.new(d), Nonnative::Port.new(d)]
35
+ end
36
+ end
37
+
38
+ def process(all, type_method, port_method, &block)
39
+ types = []
40
+ pids = []
41
+ threads = []
42
+
43
+ all.each do |type, port|
44
+ types << type
45
+ pids << type.send(type_method)
46
+ threads << Thread.new { port.send(port_method) }
47
+ end
48
+
49
+ ports = threads.map(&:value)
50
+
51
+ yield_results(types, pids, ports, &block)
52
+ end
53
+
54
+ def yield_results(all, pids, ports)
55
+ all.zip(pids, ports).each do |type, id, result|
56
+ yield type.name, id, result
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ class Port
5
+ def initialize(process)
6
+ @process = process
7
+ @timeout = Nonnative::Timeout.new(process.timeout)
8
+ end
9
+
10
+ def open?
11
+ timeout.perform do
12
+ open_socket
13
+ true
14
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
15
+ sleep_interval
16
+ retry
17
+ end
18
+ end
19
+
20
+ def closed?
21
+ timeout.perform do
22
+ open_socket
23
+ raise Nonnative::Error
24
+ rescue Nonnative::Error
25
+ sleep_interval
26
+ retry
27
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ECONNRESET
28
+ true
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :process, :timeout
35
+
36
+ def open_socket
37
+ TCPSocket.new('0.0.0.0', process.port).close
38
+ end
39
+
40
+ def sleep_interval
41
+ sleep 0.01
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ class Proxy
5
+ def initialize(service)
6
+ @service = service
7
+ end
8
+
9
+ protected
10
+
11
+ attr_reader :service
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ class ProxyFactory
5
+ class << self
6
+ def create(service)
7
+ proxy = case service.proxy.type
8
+ when 'fault_injection'
9
+ FaultInjectionProxy
10
+ else
11
+ NoProxy
12
+ end
13
+
14
+ proxy.new(service)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ class Server < Nonnative::Service
5
+ attr_reader :proxy
6
+
7
+ def initialize(service)
8
+ @proxy = Nonnative::ProxyFactory.create(service)
9
+
10
+ super service
11
+ end
12
+
13
+ def start
14
+ unless thread
15
+ proxy.start
16
+ @thread = Thread.new { perform_start }
17
+
18
+ wait_start
19
+ end
20
+
21
+ object_id
22
+ end
23
+
24
+ def stop
25
+ if thread
26
+ perform_stop
27
+ thread.terminate
28
+ proxy.stop
29
+
30
+ @thread = nil
31
+ wait_stop
32
+ end
33
+
34
+ object_id
35
+ end
36
+
37
+ private
38
+
39
+ attr_reader :thread
40
+ end
41
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ class Service
5
+ def initialize(service)
6
+ @service = service
7
+ @timeout = Nonnative::Timeout.new(service.timeout)
8
+ end
9
+
10
+ def name
11
+ service.name
12
+ end
13
+
14
+ protected
15
+
16
+ attr_reader :service, :timeout
17
+
18
+ def wait_start
19
+ sleep 0.1
20
+ end
21
+
22
+ def wait_stop
23
+ sleep 0.1
24
+ end
25
+ end
26
+ 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
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ class StartError < Nonnative::Error
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ at_exit do
4
+ Nonnative.stop
5
+ end
6
+
7
+ Nonnative.start