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