nonnative 3.6.0 → 3.10.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 +4 -4
- data/.circleci/config.yml +2 -2
- data/AGENTS.md +5 -0
- data/Gemfile.lock +1 -1
- data/README.md +13 -4
- data/lib/nonnative/configuration.rb +12 -0
- data/lib/nonnative/configuration_process.rb +11 -0
- data/lib/nonnative/configuration_readiness.rb +35 -0
- data/lib/nonnative/http_probe.rb +64 -0
- data/lib/nonnative/no_proxy.rb +1 -1
- data/lib/nonnative/pool.rb +6 -3
- data/lib/nonnative/port.rb +7 -0
- data/lib/nonnative/ports.rb +23 -2
- data/lib/nonnative/process.rb +7 -11
- data/lib/nonnative/proxy_factory.rb +3 -1
- data/lib/nonnative/version.rb +1 -1
- data/lib/nonnative.rb +14 -8
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6a276b7ac5edae149fc6eb065909773b73c5c7e1684e4502244d88a0cdd6c521
|
|
4
|
+
data.tar.gz: 82545f52a383ba1f67af5c1d01c72653adee7339a3e1968fb987eabc9236421e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0eea335efd1554822d7153077244d2f8ff5d19c46af6b19e153bd0298e99e4902f090b933899d4614e46a88252d9b95e9f7ec92b3d6e9e824c96f00694d579f9
|
|
7
|
+
data.tar.gz: ce2d8e41f9313b289c3531fe8c605e869dd856a1eb8ae47bb1671ebc12aa1f97a3a8e795cad16681c11857b2a70ce4f2250b0310dd57286162cc9aed7efa764e
|
data/.circleci/config.yml
CHANGED
|
@@ -32,13 +32,13 @@ jobs:
|
|
|
32
32
|
- restore_cache:
|
|
33
33
|
name: restore deps
|
|
34
34
|
keys:
|
|
35
|
-
- nonnative-ruby-cache-{{ checksum "Gemfile.lock" }}-{{ checksum "~/.ruby-version" }}
|
|
35
|
+
- nonnative-ruby-cache-{{ checksum "Gemfile.lock" }}-{{ checksum "~/.ruby-version" }}
|
|
36
36
|
- nonnative-ruby-cache-
|
|
37
37
|
- run: make dep
|
|
38
38
|
- run: make clean-dep
|
|
39
39
|
- save_cache:
|
|
40
40
|
name: save deps
|
|
41
|
-
key: nonnative-ruby-cache-{{ checksum "Gemfile.lock" }}-{{ checksum "~/.ruby-version" }}
|
|
41
|
+
key: nonnative-ruby-cache-{{ checksum "Gemfile.lock" }}-{{ checksum "~/.ruby-version" }}
|
|
42
42
|
paths:
|
|
43
43
|
- vendor
|
|
44
44
|
- run: make lint
|
data/AGENTS.md
CHANGED
|
@@ -74,6 +74,11 @@ of dependencies.
|
|
|
74
74
|
use `nonnative` against real dependencies. Do not flag the in-repository
|
|
75
75
|
service proxy success scenario's connection-only assertion as a test gap
|
|
76
76
|
unless the task is explicitly about changing service proxy forwarding.
|
|
77
|
+
- `Nonnative::FaultInjectionProxy` owns its listener thread and stops it by
|
|
78
|
+
closing the owned `TCPServer`, which wakes the accept loop before joining the
|
|
79
|
+
thread. Do not flag the listener `join` as an unbounded reliability gap unless
|
|
80
|
+
the task is explicitly about proxy shutdown behavior or there is a normal-use
|
|
81
|
+
reproducer, failing CI evidence, or platform-specific hang evidence.
|
|
77
82
|
|
|
78
83
|
## Runtime Model
|
|
79
84
|
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -66,13 +66,16 @@ Process/server fields:
|
|
|
66
66
|
- `wait`: small sleep (seconds) between lifecycle steps.
|
|
67
67
|
- `log`: per-runner log file used by process output redirection or server implementations.
|
|
68
68
|
|
|
69
|
+
Process-only fields:
|
|
70
|
+
- `readiness`: optional HTTP startup readiness check with explicit `port` and `path`.
|
|
71
|
+
|
|
69
72
|
Service fields:
|
|
70
73
|
- `port`: client-facing service port. Services do not get TCP readiness/shutdown checks from Nonnative.
|
|
71
74
|
|
|
72
|
-
Nonnative readiness and shutdown checks are TCP
|
|
75
|
+
Nonnative readiness and shutdown checks are TCP port checks by default. Configure process/server ports that are dedicated to the test run; if another process is already listening on the same endpoint, results are undefined. Processes can also opt into an HTTP readiness check that runs after TCP readiness succeeds.
|
|
73
76
|
|
|
74
77
|
> [!WARNING]
|
|
75
|
-
>
|
|
78
|
+
> TCP readiness and shutdown checks only prove that a TCP port opened or closed. HTTP readiness is process-only, checks for a 2xx response, and does not verify gRPC health, schema readiness, migrations, or other application-specific health.
|
|
76
79
|
|
|
77
80
|
Start and stop Nonnative around the test scope that should own the configured runners:
|
|
78
81
|
|
|
@@ -91,7 +94,9 @@ Nonnative.stop
|
|
|
91
94
|
`Nonnative.start` starts services first, then servers and processes. `Nonnative.stop` stops processes and servers first, then services. If startup fails, Nonnative rolls back runners that already started and raises `Nonnative::StartError`; shutdown failures raise `Nonnative::StopError`.
|
|
92
95
|
|
|
93
96
|
> [!NOTE]
|
|
94
|
-
> `Nonnative.
|
|
97
|
+
> `Nonnative.start` / `Nonnative.stop` manage one lifecycle for the current pool.
|
|
98
|
+
> Call `Nonnative.clear` before reconfiguring Nonnative or starting a new lifecycle in the same Ruby process.
|
|
99
|
+
> `Nonnative.clear` clears memoized configuration, logger, observability client, and pool.
|
|
95
100
|
|
|
96
101
|
### 📈 Observability
|
|
97
102
|
|
|
@@ -170,6 +175,7 @@ Nonnative.configure do |config|
|
|
|
170
175
|
p.ports = [12_321]
|
|
171
176
|
p.log = '12_321.log'
|
|
172
177
|
p.signal = 'INT' # Possible values are described in Signal.list.keys.
|
|
178
|
+
p.readiness = { port: 12_321, path: '/test/readyz' }
|
|
173
179
|
p.environment = { # Pass environment variables to process.
|
|
174
180
|
'TEST' => 'true'
|
|
175
181
|
}
|
|
@@ -205,6 +211,9 @@ processes:
|
|
|
205
211
|
- 12321
|
|
206
212
|
log: 12_321.log
|
|
207
213
|
signal: INT # Possible values are described in Signal.list.keys.
|
|
214
|
+
readiness:
|
|
215
|
+
port: 12321
|
|
216
|
+
path: /test/readyz
|
|
208
217
|
environment: # Pass environment variables to process.
|
|
209
218
|
TEST: true
|
|
210
219
|
-
|
|
@@ -612,7 +621,7 @@ These proxies can simulate different situations. Available proxy kinds are:
|
|
|
612
621
|
- `fault_injection`
|
|
613
622
|
|
|
614
623
|
> [!WARNING]
|
|
615
|
-
> Unknown proxy kinds
|
|
624
|
+
> Unknown proxy kinds raise an error. If fault injection is not taking effect, check the `kind` spelling or register the custom kind before starting the system.
|
|
616
625
|
|
|
617
626
|
Custom proxy kinds can be registered through `Nonnative.proxies`:
|
|
618
627
|
|
|
@@ -22,6 +22,7 @@ module Nonnative
|
|
|
22
22
|
# p.ports = [8080, 9090]
|
|
23
23
|
# p.timeout = 10
|
|
24
24
|
# p.log = 'api.log'
|
|
25
|
+
# p.readiness = { port: 8080, path: '/example/readyz' }
|
|
25
26
|
# end
|
|
26
27
|
# end
|
|
27
28
|
#
|
|
@@ -142,6 +143,7 @@ module Nonnative
|
|
|
142
143
|
process_config.command = command(loaded_process)
|
|
143
144
|
process_config.signal = loaded_process.signal
|
|
144
145
|
process_config.environment = loaded_process.environment
|
|
146
|
+
process_config.readiness = loaded_process.readiness if loaded_process.readiness
|
|
145
147
|
runner_attributes(process_config, loaded_process)
|
|
146
148
|
end
|
|
147
149
|
end
|
|
@@ -163,6 +165,7 @@ module Nonnative
|
|
|
163
165
|
servers = cfg.servers || []
|
|
164
166
|
servers.each do |loaded_server|
|
|
165
167
|
reject_proxy(loaded_server, 'servers')
|
|
168
|
+
reject_readiness(loaded_server, 'servers')
|
|
166
169
|
|
|
167
170
|
server do |server_config|
|
|
168
171
|
server_config.klass = Object.const_get(server_class_name(loaded_server))
|
|
@@ -174,6 +177,8 @@ module Nonnative
|
|
|
174
177
|
def add_services(cfg)
|
|
175
178
|
services = cfg.services || []
|
|
176
179
|
services.each do |loaded_service|
|
|
180
|
+
reject_readiness(loaded_service, 'services')
|
|
181
|
+
|
|
177
182
|
service do |service_config|
|
|
178
183
|
service_config.name = loaded_service.name
|
|
179
184
|
service_config.host = loaded_service.host if loaded_service.host
|
|
@@ -220,6 +225,13 @@ module Nonnative
|
|
|
220
225
|
raise ArgumentError, "Use 'services' for proxy configuration; #{kind} do not support 'proxy'"
|
|
221
226
|
end
|
|
222
227
|
|
|
228
|
+
def reject_readiness(loaded, kind)
|
|
229
|
+
values = loaded.to_h
|
|
230
|
+
return unless values.key?(:readiness) || values.key?('readiness')
|
|
231
|
+
|
|
232
|
+
raise ArgumentError, "Use 'processes' for readiness configuration; #{kind} do not support 'readiness'"
|
|
233
|
+
end
|
|
234
|
+
|
|
223
235
|
def assign_proxy(service, loaded_proxy)
|
|
224
236
|
return unless loaded_proxy
|
|
225
237
|
|
|
@@ -28,6 +28,9 @@ module Nonnative
|
|
|
28
28
|
# @return [Hash, nil] environment variables to pass to the spawned process
|
|
29
29
|
attr_accessor :environment
|
|
30
30
|
|
|
31
|
+
# @return [Nonnative::ConfigurationReadiness, nil] optional HTTP readiness check
|
|
32
|
+
attr_reader :readiness
|
|
33
|
+
|
|
31
34
|
# Creates a process configuration with bounded lifecycle defaults.
|
|
32
35
|
#
|
|
33
36
|
# Defaults:
|
|
@@ -39,5 +42,13 @@ module Nonnative
|
|
|
39
42
|
|
|
40
43
|
self.timeout = DEFAULT_TIMEOUT
|
|
41
44
|
end
|
|
45
|
+
|
|
46
|
+
# Sets optional HTTP readiness configuration.
|
|
47
|
+
#
|
|
48
|
+
# @param value [Hash, #to_h, nil] readiness attributes with required `port` and `path`
|
|
49
|
+
# @return [void]
|
|
50
|
+
def readiness=(value)
|
|
51
|
+
@readiness = value.nil? ? nil : Nonnative::ConfigurationReadiness.new(value)
|
|
52
|
+
end
|
|
42
53
|
end
|
|
43
54
|
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Nonnative
|
|
4
|
+
# HTTP readiness configuration for a managed process.
|
|
5
|
+
#
|
|
6
|
+
# Readiness is optional. When present, both `port` and `path` are required so the startup
|
|
7
|
+
# check has an explicit application endpoint to poll after TCP readiness succeeds.
|
|
8
|
+
class ConfigurationReadiness
|
|
9
|
+
# @return [Integer] process HTTP readiness port
|
|
10
|
+
attr_accessor :port
|
|
11
|
+
|
|
12
|
+
# @return [String] HTTP readiness path
|
|
13
|
+
attr_accessor :path
|
|
14
|
+
|
|
15
|
+
# @param value [Hash, #to_h] readiness attributes
|
|
16
|
+
def initialize(value)
|
|
17
|
+
attributes = value.respond_to?(:to_h) ? value.to_h : value
|
|
18
|
+
self.port = attribute(attributes, :port)
|
|
19
|
+
self.path = attribute(attributes, :path)
|
|
20
|
+
|
|
21
|
+
validate!
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def attribute(attributes, name)
|
|
27
|
+
attributes[name] || attributes[name.to_s]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def validate!
|
|
31
|
+
raise ArgumentError, "Process readiness requires 'port'" if port.nil?
|
|
32
|
+
raise ArgumentError, "Process readiness requires 'path'" if path.nil?
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Nonnative
|
|
4
|
+
# Probes a managed process HTTP readiness endpoint.
|
|
5
|
+
class HTTPProbe < Nonnative::HTTPClient
|
|
6
|
+
NETWORK_ERRORS = [
|
|
7
|
+
Errno::ECONNREFUSED,
|
|
8
|
+
Errno::EHOSTUNREACH,
|
|
9
|
+
Errno::ECONNRESET,
|
|
10
|
+
SocketError,
|
|
11
|
+
RestClient::Exceptions::Timeout,
|
|
12
|
+
RestClient::ServerBrokeConnection
|
|
13
|
+
].freeze
|
|
14
|
+
|
|
15
|
+
# @param process [Nonnative::ConfigurationProcess] process configuration with readiness attributes
|
|
16
|
+
def initialize(process)
|
|
17
|
+
@readiness = process.readiness
|
|
18
|
+
@base_url = "http://#{process.host}:#{readiness.port}"
|
|
19
|
+
@timeout = Nonnative::Timeout.new(process.timeout)
|
|
20
|
+
|
|
21
|
+
super(base_url)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Returns whether the configured HTTP endpoint returns a 2xx response before timeout.
|
|
25
|
+
#
|
|
26
|
+
# @return [Boolean]
|
|
27
|
+
def ready?
|
|
28
|
+
Nonnative.logger.info "checking if readiness '#{endpoint}' is ready"
|
|
29
|
+
|
|
30
|
+
timeout.perform do
|
|
31
|
+
response = get(readiness.path)
|
|
32
|
+
raise Nonnative::Error unless ready_response?(response)
|
|
33
|
+
|
|
34
|
+
true
|
|
35
|
+
rescue Nonnative::Error, *NETWORK_ERRORS
|
|
36
|
+
sleep_interval
|
|
37
|
+
retry
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Returns the HTTP readiness endpoint for lifecycle diagnostics.
|
|
42
|
+
#
|
|
43
|
+
# @return [String]
|
|
44
|
+
def endpoint
|
|
45
|
+
"#{base_url}#{path}"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
attr_reader :base_url, :readiness, :timeout
|
|
51
|
+
|
|
52
|
+
def path
|
|
53
|
+
readiness.path.start_with?('/') ? readiness.path : "/#{readiness.path}"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def ready_response?(response)
|
|
57
|
+
response.respond_to?(:code) && response.code.to_i.between?(200, 299)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def sleep_interval
|
|
61
|
+
sleep 0.01
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
data/lib/nonnative/no_proxy.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Nonnative
|
|
4
4
|
# No-op proxy implementation.
|
|
5
5
|
#
|
|
6
|
-
# This is the default proxy when `service.proxy.kind` is `"none"
|
|
6
|
+
# This is the default proxy when `service.proxy.kind` is `"none"`.
|
|
7
7
|
# It does not bind/listen or alter traffic; it simply exposes the underlying runner's configured
|
|
8
8
|
# `host` and primary `port`.
|
|
9
9
|
#
|
data/lib/nonnative/pool.rb
CHANGED
|
@@ -30,6 +30,7 @@ module Nonnative
|
|
|
30
30
|
# @yieldparam name [String, nil] runner name
|
|
31
31
|
# @yieldparam values [Object] runner-specific return value from `start` (e.g. `[pid, running]` for processes)
|
|
32
32
|
# @yieldparam result [Boolean] result of the port readiness check (`true` if ready in time)
|
|
33
|
+
# @yieldparam port [Nonnative::Ports] checked port group
|
|
33
34
|
# @return [Array<String>] lifecycle and readiness-check errors collected while starting
|
|
34
35
|
def start(&)
|
|
35
36
|
errors = []
|
|
@@ -47,6 +48,7 @@ module Nonnative
|
|
|
47
48
|
# @yieldparam name [String, nil] runner name
|
|
48
49
|
# @yieldparam id [Object] runner-specific identifier returned by `stop` (e.g. pid or object_id)
|
|
49
50
|
# @yieldparam result [Boolean] result of the port shutdown check (`true` if closed in time)
|
|
51
|
+
# @yieldparam port [Nonnative::Ports] checked port group
|
|
50
52
|
# @return [Array<String>] lifecycle and shutdown-check errors collected while stopping
|
|
51
53
|
def stop(&)
|
|
52
54
|
errors = []
|
|
@@ -65,6 +67,7 @@ module Nonnative
|
|
|
65
67
|
# @yieldparam name [String, nil] runner name
|
|
66
68
|
# @yieldparam id [Object] runner-specific identifier returned by `stop`
|
|
67
69
|
# @yieldparam result [Boolean] result of the port shutdown check (`true` if closed in time)
|
|
70
|
+
# @yieldparam port [Nonnative::Ports] checked port group
|
|
68
71
|
# @return [Array<String>] lifecycle and shutdown-check errors collected while rolling back
|
|
69
72
|
def rollback(&)
|
|
70
73
|
errors = []
|
|
@@ -183,7 +186,7 @@ module Nonnative
|
|
|
183
186
|
|
|
184
187
|
runners.each do |runner, port|
|
|
185
188
|
values = runner.send(lifecycle_method)
|
|
186
|
-
checks << [runner, values, Thread.new { check_port(port, port_method) }]
|
|
189
|
+
checks << [runner, values, port, Thread.new { check_port(port, port_method) }]
|
|
187
190
|
rescue StandardError => e
|
|
188
191
|
errors << lifecycle_error(action, runner, e)
|
|
189
192
|
end
|
|
@@ -198,12 +201,12 @@ module Nonnative
|
|
|
198
201
|
end
|
|
199
202
|
|
|
200
203
|
def yield_results(checks, action, &)
|
|
201
|
-
checks.each_with_object([]) do |(type, values, thread), errors|
|
|
204
|
+
checks.each_with_object([]) do |(type, values, port, thread), errors|
|
|
202
205
|
result = thread.value
|
|
203
206
|
if result[:error]
|
|
204
207
|
errors << port_error(action, type, result[:error])
|
|
205
208
|
elsif block_given?
|
|
206
|
-
yield type.name, values, result[:result]
|
|
209
|
+
yield type.name, values, result[:result], port
|
|
207
210
|
end
|
|
208
211
|
end
|
|
209
212
|
end
|
data/lib/nonnative/port.rb
CHANGED
data/lib/nonnative/ports.rb
CHANGED
|
@@ -10,14 +10,16 @@ module Nonnative
|
|
|
10
10
|
class Ports
|
|
11
11
|
# @param runner [#host, #ports, #timeout] runner configuration providing connection details
|
|
12
12
|
def initialize(runner)
|
|
13
|
+
@runner = runner
|
|
13
14
|
@ports = runner.ports.map { |port| Nonnative::Port.new(runner, port) }
|
|
15
|
+
@readiness = Nonnative::HTTPProbe.new(runner) if runner.respond_to?(:readiness) && runner.readiness
|
|
14
16
|
end
|
|
15
17
|
|
|
16
18
|
# Returns whether all configured ports become connectable before their timeouts elapse.
|
|
17
19
|
#
|
|
18
20
|
# @return [Boolean]
|
|
19
21
|
def open?
|
|
20
|
-
ports.all?(&:open?)
|
|
22
|
+
ports.all?(&:open?) && (readiness.nil? || readiness.ready?)
|
|
21
23
|
end
|
|
22
24
|
|
|
23
25
|
# Returns whether all configured ports become non-connectable before their timeouts elapse.
|
|
@@ -27,8 +29,27 @@ module Nonnative
|
|
|
27
29
|
ports.all?(&:closed?)
|
|
28
30
|
end
|
|
29
31
|
|
|
32
|
+
# Returns the checked endpoints for lifecycle diagnostics.
|
|
33
|
+
#
|
|
34
|
+
# @return [String]
|
|
35
|
+
def endpoints
|
|
36
|
+
ports.map(&:endpoint).join(', ')
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Returns endpoint and log context for lifecycle errors.
|
|
40
|
+
#
|
|
41
|
+
# @return [String]
|
|
42
|
+
def description
|
|
43
|
+
details = []
|
|
44
|
+
details << "readiness: #{readiness.endpoint}" if readiness
|
|
45
|
+
log = runner.log if runner.respond_to?(:log)
|
|
46
|
+
details << "log: #{log}" if log
|
|
47
|
+
|
|
48
|
+
details.empty? ? endpoints : "#{endpoints} (#{details.join('; ')})"
|
|
49
|
+
end
|
|
50
|
+
|
|
30
51
|
private
|
|
31
52
|
|
|
32
|
-
attr_reader :ports
|
|
53
|
+
attr_reader :ports, :readiness, :runner
|
|
33
54
|
end
|
|
34
55
|
end
|
data/lib/nonnative/process.rb
CHANGED
|
@@ -20,6 +20,9 @@ module Nonnative
|
|
|
20
20
|
@timeout = Nonnative::Timeout.new(service.timeout)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
# @return [GetProcessMem, nil] memory reader for the current process lifecycle
|
|
24
|
+
attr_reader :memory
|
|
25
|
+
|
|
23
26
|
# Spawns the configured process if it is not already running.
|
|
24
27
|
#
|
|
25
28
|
# @return [Array<(Integer, Boolean)>]
|
|
@@ -29,6 +32,8 @@ module Nonnative
|
|
|
29
32
|
def start
|
|
30
33
|
unless process_exists?
|
|
31
34
|
@pid = process_spawn
|
|
35
|
+
# Keep memory reads bound to the child spawned for this lifecycle.
|
|
36
|
+
@memory = GetProcessMem.new(pid)
|
|
32
37
|
wait_start
|
|
33
38
|
end
|
|
34
39
|
|
|
@@ -53,17 +58,8 @@ module Nonnative
|
|
|
53
58
|
end
|
|
54
59
|
|
|
55
60
|
[pid, stopped]
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
# Returns a memoized memory reader for the spawned process.
|
|
59
|
-
#
|
|
60
|
-
# This is primarily used by acceptance tests to assert memory usage.
|
|
61
|
-
#
|
|
62
|
-
# @return [GetProcessMem, nil] a memory reader for the pid, or `nil` if not started
|
|
63
|
-
def memory
|
|
64
|
-
return if pid.nil?
|
|
65
|
-
|
|
66
|
-
@memory ||= GetProcessMem.new(pid)
|
|
61
|
+
ensure
|
|
62
|
+
@memory = nil
|
|
67
63
|
end
|
|
68
64
|
|
|
69
65
|
protected
|
|
@@ -6,7 +6,9 @@ module Nonnative
|
|
|
6
6
|
# A runtime service constructs a proxy via this factory. The proxy implementation is selected by
|
|
7
7
|
# `service.proxy.kind` and resolved using {Nonnative.proxy}.
|
|
8
8
|
#
|
|
9
|
-
# If the kind is
|
|
9
|
+
# If the kind is `"none"`, {Nonnative.proxy} returns {Nonnative::NoProxy}.
|
|
10
|
+
# Unknown non-`"none"` kinds raise an error so proxy configuration typos do not silently disable
|
|
11
|
+
# fault injection.
|
|
10
12
|
#
|
|
11
13
|
# @see Nonnative.proxy
|
|
12
14
|
# @see Nonnative.proxies
|
data/lib/nonnative/version.rb
CHANGED
data/lib/nonnative.rb
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# It can:
|
|
7
7
|
#
|
|
8
8
|
# - start external processes and in-process servers
|
|
9
|
-
# - wait for readiness via port checks
|
|
9
|
+
# - wait for readiness via port checks and optional process HTTP readiness
|
|
10
10
|
# - optionally run fault-injection proxies in front of services
|
|
11
11
|
#
|
|
12
12
|
# The public entry points are exposed as module-level methods on {Nonnative}.
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
# p.ports = [8080, 9090]
|
|
28
28
|
# p.timeout = 10
|
|
29
29
|
# p.log = 'api.log'
|
|
30
|
+
# p.readiness = { port: 8080, path: '/example/readyz' }
|
|
30
31
|
# end
|
|
31
32
|
# end
|
|
32
33
|
#
|
|
@@ -72,6 +73,7 @@ require 'nonnative/ports'
|
|
|
72
73
|
require 'nonnative/configuration_file'
|
|
73
74
|
require 'nonnative/configuration'
|
|
74
75
|
require 'nonnative/configuration_runner'
|
|
76
|
+
require 'nonnative/configuration_readiness'
|
|
75
77
|
require 'nonnative/configuration_process'
|
|
76
78
|
require 'nonnative/configuration_server'
|
|
77
79
|
require 'nonnative/configuration_service'
|
|
@@ -82,6 +84,7 @@ require 'nonnative/server'
|
|
|
82
84
|
require 'nonnative/service'
|
|
83
85
|
require 'nonnative/pool'
|
|
84
86
|
require 'nonnative/http_client'
|
|
87
|
+
require 'nonnative/http_probe'
|
|
85
88
|
require 'nonnative/http_server'
|
|
86
89
|
require 'nonnative/http_proxy_server'
|
|
87
90
|
require 'nonnative/grpc_server'
|
|
@@ -204,7 +207,7 @@ module Nonnative
|
|
|
204
207
|
# @param kind [String] proxy kind name (for example `"fault_injection"`)
|
|
205
208
|
# @return [Class] a subclass of {Nonnative::Proxy}
|
|
206
209
|
def proxy(kind)
|
|
207
|
-
|
|
210
|
+
kind.nil? || kind == 'none' ? NoProxy : proxies.fetch(kind) { raise ArgumentError, "Unsupported proxy kind '#{kind}'" }
|
|
208
211
|
end
|
|
209
212
|
|
|
210
213
|
# Starts all configured services, servers, and processes, and waits for readiness.
|
|
@@ -216,9 +219,9 @@ module Nonnative
|
|
|
216
219
|
def start
|
|
217
220
|
@pool ||= Nonnative::Pool.new(configuration)
|
|
218
221
|
errors = []
|
|
219
|
-
errors.concat(@pool.start do |name, values, result|
|
|
222
|
+
errors.concat(@pool.start do |name, values, result, ports|
|
|
220
223
|
id, started = values
|
|
221
|
-
errors << "Started #{name} with id #{id}, though did not respond in time" if !started || !result
|
|
224
|
+
errors << "Started #{name} with id #{id}, though did not respond in time for #{ports.description}" if !started || !result
|
|
222
225
|
end)
|
|
223
226
|
nil
|
|
224
227
|
rescue StandardError => e
|
|
@@ -239,9 +242,9 @@ module Nonnative
|
|
|
239
242
|
errors = []
|
|
240
243
|
return if @pool.nil?
|
|
241
244
|
|
|
242
|
-
errors.concat(@pool.stop do |name, values, result|
|
|
245
|
+
errors.concat(@pool.stop do |name, values, result, ports|
|
|
243
246
|
id, stopped = Array(values).then { |v| [v.first, v.fetch(1, true)] }
|
|
244
|
-
errors << "Stopped #{name} with id #{id}, though did not respond in time" unless result
|
|
247
|
+
errors << "Stopped #{name} with id #{id}, though did not respond in time for #{ports.description}" unless result
|
|
245
248
|
errors << "Stopped #{name} with id #{id}, though the process did not exit in time" unless stopped
|
|
246
249
|
end)
|
|
247
250
|
nil
|
|
@@ -283,6 +286,9 @@ module Nonnative
|
|
|
283
286
|
|
|
284
287
|
# Clears memoized configuration, logger, observability client, and pool.
|
|
285
288
|
#
|
|
289
|
+
# Call this before reconfiguring Nonnative or starting a new lifecycle in the same Ruby process.
|
|
290
|
+
# `start`/`stop` are intended to manage one lifecycle for the current pool.
|
|
291
|
+
#
|
|
286
292
|
# @return [void]
|
|
287
293
|
def clear
|
|
288
294
|
clear_logger
|
|
@@ -305,9 +311,9 @@ module Nonnative
|
|
|
305
311
|
errors = []
|
|
306
312
|
return errors if @pool.nil?
|
|
307
313
|
|
|
308
|
-
errors.concat(@pool.rollback do |name, values, result|
|
|
314
|
+
errors.concat(@pool.rollback do |name, values, result, ports|
|
|
309
315
|
id, stopped = Array(values).then { |v| [v.first, v.fetch(1, true)] }
|
|
310
|
-
errors << "Rollback failed for #{name} with id #{id}, because it did not stop in time" unless result
|
|
316
|
+
errors << "Rollback failed for #{name} with id #{id}, because it did not stop in time for #{ports.description}" unless result
|
|
311
317
|
errors << "Rollback failed for #{name} with id #{id}, because the process did not exit in time" unless stopped
|
|
312
318
|
end)
|
|
313
319
|
rescue StandardError => e
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: nonnative
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alejandro Falkowski
|
|
@@ -291,6 +291,7 @@ files:
|
|
|
291
291
|
- lib/nonnative/configuration_file.rb
|
|
292
292
|
- lib/nonnative/configuration_process.rb
|
|
293
293
|
- lib/nonnative/configuration_proxy.rb
|
|
294
|
+
- lib/nonnative/configuration_readiness.rb
|
|
294
295
|
- lib/nonnative/configuration_runner.rb
|
|
295
296
|
- lib/nonnative/configuration_server.rb
|
|
296
297
|
- lib/nonnative/configuration_service.rb
|
|
@@ -302,6 +303,7 @@ files:
|
|
|
302
303
|
- lib/nonnative/grpc_server.rb
|
|
303
304
|
- lib/nonnative/header.rb
|
|
304
305
|
- lib/nonnative/http_client.rb
|
|
306
|
+
- lib/nonnative/http_probe.rb
|
|
305
307
|
- lib/nonnative/http_proxy_server.rb
|
|
306
308
|
- lib/nonnative/http_server.rb
|
|
307
309
|
- lib/nonnative/invalid_data_socket_pair.rb
|