nonnative 1.27.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 +7 -0
- data/.circleci/config.yml +38 -0
- data/.config/cucumber.yml +1 -0
- data/.editorconfig +24 -0
- data/.gitignore +11 -0
- data/.rubocop.yml +87 -0
- data/.ruby-version +1 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +202 -0
- data/LICENSE +24 -0
- data/Makefile +27 -0
- data/README.md +366 -0
- data/Rakefile +4 -0
- data/bin/console +15 -0
- data/bin/setup +9 -0
- data/lib/nonnative.rb +89 -0
- data/lib/nonnative/before.rb +11 -0
- data/lib/nonnative/close_all_socket_pair.rb +9 -0
- data/lib/nonnative/command.rb +54 -0
- data/lib/nonnative/configuration.rb +80 -0
- data/lib/nonnative/configuration_process.rb +7 -0
- data/lib/nonnative/configuration_proxy.rb +13 -0
- data/lib/nonnative/configuration_server.rb +19 -0
- data/lib/nonnative/delay_socket_pair.rb +12 -0
- data/lib/nonnative/error.rb +6 -0
- data/lib/nonnative/fault_injection_proxy.rb +78 -0
- data/lib/nonnative/grpc_server.rb +44 -0
- data/lib/nonnative/http_client.rb +53 -0
- data/lib/nonnative/http_server.rb +40 -0
- data/lib/nonnative/invalid_data_socket_pair.rb +11 -0
- data/lib/nonnative/manual.rb +3 -0
- data/lib/nonnative/no_proxy.rb +17 -0
- data/lib/nonnative/observability.rb +13 -0
- data/lib/nonnative/pool.rb +60 -0
- data/lib/nonnative/port.rb +44 -0
- data/lib/nonnative/proxy.rb +13 -0
- data/lib/nonnative/proxy_factory.rb +18 -0
- data/lib/nonnative/server.rb +41 -0
- data/lib/nonnative/service.rb +26 -0
- data/lib/nonnative/socket_pair.rb +53 -0
- data/lib/nonnative/socket_pair_factory.rb +22 -0
- data/lib/nonnative/start_error.rb +6 -0
- data/lib/nonnative/startup.rb +7 -0
- data/lib/nonnative/stop_error.rb +6 -0
- data/lib/nonnative/timeout.rb +21 -0
- data/lib/nonnative/version.rb +5 -0
- data/nonnative.gemspec +42 -0
- data/sonar-project.properties +4 -0
- 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,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,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
|