nonnative 1.107.0 → 1.109.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 +36 -6
- data/.rubocop.yml +8 -2
- data/AGENTS.md +248 -0
- data/CHANGELOG.md +141 -0
- data/Gemfile.lock +69 -58
- data/README.md +51 -32
- data/lib/nonnative/close_all_socket_pair.rb +14 -0
- data/lib/nonnative/configuration.rb +67 -0
- data/lib/nonnative/configuration_process.rb +14 -0
- data/lib/nonnative/configuration_proxy.rb +28 -0
- data/lib/nonnative/configuration_runner.rb +44 -0
- data/lib/nonnative/configuration_server.rb +12 -0
- data/lib/nonnative/configuration_service.rb +9 -0
- data/lib/nonnative/cucumber.rb +4 -12
- data/lib/nonnative/delay_socket_pair.rb +15 -0
- data/lib/nonnative/error.rb +7 -0
- data/lib/nonnative/fault_injection_proxy.rb +63 -0
- data/lib/nonnative/go_command.rb +34 -0
- data/lib/nonnative/grpc_server.rb +30 -0
- data/lib/nonnative/header.rb +44 -0
- data/lib/nonnative/http_client.rb +45 -0
- data/lib/nonnative/http_proxy_server.rb +62 -1
- data/lib/nonnative/http_server.rb +40 -0
- data/lib/nonnative/invalid_data_socket_pair.rb +15 -0
- data/lib/nonnative/no_proxy.rb +35 -0
- data/lib/nonnative/not_found_error.rb +7 -0
- data/lib/nonnative/observability.rb +44 -0
- data/lib/nonnative/pool.rb +50 -0
- data/lib/nonnative/port.rb +26 -0
- data/lib/nonnative/process.rb +29 -0
- data/lib/nonnative/proxy.rb +24 -0
- data/lib/nonnative/proxy_factory.rb +16 -0
- data/lib/nonnative/runner.rb +30 -0
- data/lib/nonnative/server.rb +28 -0
- data/lib/nonnative/service.rb +16 -0
- data/lib/nonnative/socket_pair.rb +46 -0
- data/lib/nonnative/socket_pair_factory.rb +21 -0
- data/lib/nonnative/start_error.rb +5 -0
- data/lib/nonnative/stop_error.rb +5 -0
- data/lib/nonnative/timeout.rb +20 -0
- data/lib/nonnative/version.rb +4 -1
- data/lib/nonnative.rb +128 -0
- metadata +3 -2
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
nonnative (1.
|
|
4
|
+
nonnative (1.109.0)
|
|
5
5
|
concurrent-ruby (>= 1, < 2)
|
|
6
6
|
config (>= 5, < 6)
|
|
7
7
|
cucumber (>= 7, < 11)
|
|
@@ -18,94 +18,102 @@ PATH
|
|
|
18
18
|
GEM
|
|
19
19
|
remote: https://rubygems.org/
|
|
20
20
|
specs:
|
|
21
|
+
addressable (2.8.9)
|
|
22
|
+
public_suffix (>= 2.0.2, < 8.0)
|
|
21
23
|
ast (2.4.3)
|
|
22
24
|
base64 (0.3.0)
|
|
23
25
|
benchmark-malloc (0.2.0)
|
|
24
26
|
benchmark-perf (0.6.0)
|
|
25
27
|
benchmark-trend (0.4.0)
|
|
26
|
-
bigdecimal (
|
|
28
|
+
bigdecimal (4.0.1)
|
|
27
29
|
builder (3.3.0)
|
|
28
|
-
concurrent-ruby (1.3.
|
|
30
|
+
concurrent-ruby (1.3.6)
|
|
29
31
|
config (5.6.1)
|
|
30
32
|
deep_merge (~> 1.2, >= 1.2.1)
|
|
31
33
|
ostruct
|
|
32
|
-
cucumber (10.
|
|
34
|
+
cucumber (10.2.0)
|
|
33
35
|
base64 (~> 0.2)
|
|
34
36
|
builder (~> 3.2)
|
|
35
|
-
cucumber-ci-environment (> 9, <
|
|
37
|
+
cucumber-ci-environment (> 9, < 12)
|
|
36
38
|
cucumber-core (> 15, < 17)
|
|
37
|
-
cucumber-cucumber-expressions (> 17, <
|
|
38
|
-
cucumber-html-formatter (>
|
|
39
|
+
cucumber-cucumber-expressions (> 17, < 20)
|
|
40
|
+
cucumber-html-formatter (> 21, < 23)
|
|
39
41
|
diff-lcs (~> 1.5)
|
|
40
42
|
logger (~> 1.6)
|
|
41
43
|
mini_mime (~> 1.1)
|
|
42
44
|
multi_test (~> 1.1)
|
|
43
45
|
sys-uname (~> 1.3)
|
|
44
|
-
cucumber-ci-environment (
|
|
45
|
-
cucumber-core (
|
|
46
|
-
cucumber-gherkin (>
|
|
47
|
-
cucumber-messages (>
|
|
48
|
-
cucumber-tag-expressions (>
|
|
46
|
+
cucumber-ci-environment (11.0.0)
|
|
47
|
+
cucumber-core (16.2.0)
|
|
48
|
+
cucumber-gherkin (> 36, < 40)
|
|
49
|
+
cucumber-messages (> 31, < 33)
|
|
50
|
+
cucumber-tag-expressions (> 6, < 9)
|
|
49
51
|
cucumber-cucumber-expressions (18.0.1)
|
|
50
52
|
bigdecimal
|
|
51
|
-
cucumber-gherkin (
|
|
52
|
-
cucumber-messages (
|
|
53
|
-
cucumber-html-formatter (
|
|
54
|
-
cucumber-messages (>
|
|
55
|
-
cucumber-messages (
|
|
56
|
-
cucumber-tag-expressions (
|
|
53
|
+
cucumber-gherkin (39.0.0)
|
|
54
|
+
cucumber-messages (>= 31, < 33)
|
|
55
|
+
cucumber-html-formatter (22.3.0)
|
|
56
|
+
cucumber-messages (> 23, < 33)
|
|
57
|
+
cucumber-messages (32.2.0)
|
|
58
|
+
cucumber-tag-expressions (8.1.0)
|
|
57
59
|
deep_merge (1.2.2)
|
|
58
60
|
diff-lcs (1.6.2)
|
|
59
61
|
docile (1.4.1)
|
|
60
62
|
domain_name (0.6.20240107)
|
|
61
|
-
ffi (1.17.
|
|
62
|
-
ffi (1.17.
|
|
63
|
+
ffi (1.17.3-x86_64-darwin)
|
|
64
|
+
ffi (1.17.3-x86_64-linux-gnu)
|
|
63
65
|
get_process_mem (1.0.0)
|
|
64
66
|
bigdecimal (>= 2.0)
|
|
65
67
|
ffi (~> 1.0)
|
|
66
|
-
google-protobuf (4.
|
|
68
|
+
google-protobuf (4.34.0-x86_64-darwin)
|
|
67
69
|
bigdecimal
|
|
68
|
-
rake (
|
|
69
|
-
google-protobuf (4.
|
|
70
|
+
rake (~> 13.3)
|
|
71
|
+
google-protobuf (4.34.0-x86_64-linux-gnu)
|
|
70
72
|
bigdecimal
|
|
71
|
-
rake (
|
|
72
|
-
googleapis-common-protos-types (1.
|
|
73
|
+
rake (~> 13.3)
|
|
74
|
+
googleapis-common-protos-types (1.22.0)
|
|
73
75
|
google-protobuf (~> 4.26)
|
|
74
|
-
grpc (1.
|
|
76
|
+
grpc (1.78.1-x86_64-darwin)
|
|
75
77
|
google-protobuf (>= 3.25, < 5.0)
|
|
76
78
|
googleapis-common-protos-types (~> 1.0)
|
|
77
|
-
grpc (1.
|
|
79
|
+
grpc (1.78.1-x86_64-linux-gnu)
|
|
78
80
|
google-protobuf (>= 3.25, < 5.0)
|
|
79
81
|
googleapis-common-protos-types (~> 1.0)
|
|
80
82
|
http-accept (1.7.0)
|
|
81
|
-
http-cookie (1.0
|
|
83
|
+
http-cookie (1.1.0)
|
|
82
84
|
domain_name (~> 0.5)
|
|
83
|
-
json (2.
|
|
85
|
+
json (2.18.1)
|
|
86
|
+
json-schema (6.1.0)
|
|
87
|
+
addressable (~> 2.8)
|
|
88
|
+
bigdecimal (>= 3.1, < 5)
|
|
84
89
|
language_server-protocol (3.17.0.5)
|
|
85
90
|
lint_roller (1.1.0)
|
|
86
91
|
logger (1.7.0)
|
|
92
|
+
mcp (0.8.0)
|
|
93
|
+
json-schema (>= 4.1)
|
|
87
94
|
memoist3 (1.0.0)
|
|
88
95
|
mime-types (3.7.0)
|
|
89
96
|
logger
|
|
90
97
|
mime-types-data (~> 3.2025, >= 3.2025.0507)
|
|
91
|
-
mime-types-data (3.
|
|
98
|
+
mime-types-data (3.2026.0303)
|
|
92
99
|
mini_mime (1.1.5)
|
|
93
100
|
multi_test (1.1.0)
|
|
94
101
|
mustermann (3.0.4)
|
|
95
102
|
ruby2_keywords (~> 0.0.1)
|
|
96
103
|
netrc (0.11.0)
|
|
97
|
-
nio4r (2.7.
|
|
104
|
+
nio4r (2.7.5)
|
|
98
105
|
ostruct (0.6.3)
|
|
99
106
|
parallel (1.27.0)
|
|
100
|
-
parser (3.3.
|
|
107
|
+
parser (3.3.10.2)
|
|
101
108
|
ast (~> 2.4.1)
|
|
102
109
|
racc
|
|
103
|
-
prism (1.
|
|
104
|
-
|
|
110
|
+
prism (1.9.0)
|
|
111
|
+
public_suffix (7.0.5)
|
|
112
|
+
puma (7.2.0)
|
|
105
113
|
nio4r (~> 2.0)
|
|
106
114
|
racc (1.8.1)
|
|
107
|
-
rack (3.2.
|
|
108
|
-
rack-protection (4.
|
|
115
|
+
rack (3.2.5)
|
|
116
|
+
rack-protection (4.2.1)
|
|
109
117
|
base64 (>= 0.1.0)
|
|
110
118
|
logger (>= 1.6.0)
|
|
111
119
|
rack (>= 3.0.0, < 4)
|
|
@@ -113,18 +121,19 @@ GEM
|
|
|
113
121
|
base64 (>= 0.1.0)
|
|
114
122
|
rack (>= 3.0.0)
|
|
115
123
|
rainbow (3.1.1)
|
|
116
|
-
rake (13.3.
|
|
117
|
-
rbs (3.
|
|
124
|
+
rake (13.3.1)
|
|
125
|
+
rbs (3.10.3)
|
|
118
126
|
logger
|
|
119
|
-
|
|
127
|
+
tsort
|
|
128
|
+
regexp_parser (2.11.3)
|
|
120
129
|
rest-client (2.1.0)
|
|
121
130
|
http-accept (>= 1.7.0, < 2.0)
|
|
122
131
|
http-cookie (>= 1.0.2, < 2.0)
|
|
123
132
|
mime-types (>= 1.16, < 4.0)
|
|
124
133
|
netrc (~> 0.8)
|
|
125
|
-
retriable (3.1
|
|
126
|
-
rexml (3.4.
|
|
127
|
-
rspec (3.13.
|
|
134
|
+
retriable (3.4.1)
|
|
135
|
+
rexml (3.4.4)
|
|
136
|
+
rspec (3.13.2)
|
|
128
137
|
rspec-core (~> 3.13.0)
|
|
129
138
|
rspec-expectations (~> 3.13.0)
|
|
130
139
|
rspec-mocks (~> 3.13.0)
|
|
@@ -133,32 +142,33 @@ GEM
|
|
|
133
142
|
benchmark-perf (~> 0.6)
|
|
134
143
|
benchmark-trend (~> 0.4)
|
|
135
144
|
rspec (>= 3.0)
|
|
136
|
-
rspec-core (3.13.
|
|
145
|
+
rspec-core (3.13.6)
|
|
137
146
|
rspec-support (~> 3.13.0)
|
|
138
147
|
rspec-expectations (3.13.5)
|
|
139
148
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
140
149
|
rspec-support (~> 3.13.0)
|
|
141
|
-
rspec-mocks (3.13.
|
|
150
|
+
rspec-mocks (3.13.8)
|
|
142
151
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
143
152
|
rspec-support (~> 3.13.0)
|
|
144
|
-
rspec-support (3.13.
|
|
153
|
+
rspec-support (3.13.7)
|
|
145
154
|
rspec-wait (1.0.2)
|
|
146
155
|
rspec (>= 3.4)
|
|
147
|
-
rubocop (1.
|
|
156
|
+
rubocop (1.85.1)
|
|
148
157
|
json (~> 2.3)
|
|
149
158
|
language_server-protocol (~> 3.17.0.2)
|
|
150
159
|
lint_roller (~> 1.1.0)
|
|
160
|
+
mcp (~> 0.6)
|
|
151
161
|
parallel (~> 1.10)
|
|
152
162
|
parser (>= 3.3.0.2)
|
|
153
163
|
rainbow (>= 2.2.2, < 4.0)
|
|
154
164
|
regexp_parser (>= 2.9.3, < 3.0)
|
|
155
|
-
rubocop-ast (>= 1.
|
|
165
|
+
rubocop-ast (>= 1.49.0, < 2.0)
|
|
156
166
|
ruby-progressbar (~> 1.7)
|
|
157
167
|
unicode-display_width (>= 2.4.0, < 4.0)
|
|
158
|
-
rubocop-ast (1.
|
|
168
|
+
rubocop-ast (1.49.0)
|
|
159
169
|
parser (>= 3.3.7.2)
|
|
160
|
-
prism (~> 1.
|
|
161
|
-
ruby-lsp (0.26.
|
|
170
|
+
prism (~> 1.7)
|
|
171
|
+
ruby-lsp (0.26.8)
|
|
162
172
|
language_server-protocol (~> 3.17.0)
|
|
163
173
|
prism (>= 1.2, < 2.0)
|
|
164
174
|
rbs (>= 3, < 5)
|
|
@@ -173,20 +183,21 @@ GEM
|
|
|
173
183
|
simplecov (~> 0.19)
|
|
174
184
|
simplecov-html (0.13.2)
|
|
175
185
|
simplecov_json_formatter (0.1.4)
|
|
176
|
-
sinatra (4.
|
|
186
|
+
sinatra (4.2.1)
|
|
177
187
|
logger (>= 1.6.0)
|
|
178
188
|
mustermann (~> 3.0)
|
|
179
189
|
rack (>= 3.0.0, < 4)
|
|
180
|
-
rack-protection (= 4.
|
|
190
|
+
rack-protection (= 4.2.1)
|
|
181
191
|
rack-session (>= 2.0.0, < 3)
|
|
182
192
|
tilt (~> 2.0)
|
|
183
|
-
sys-uname (1.
|
|
193
|
+
sys-uname (1.5.0)
|
|
184
194
|
ffi (~> 1.1)
|
|
185
195
|
memoist3 (~> 1.0.0)
|
|
186
|
-
tilt (2.
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
196
|
+
tilt (2.7.0)
|
|
197
|
+
tsort (0.2.0)
|
|
198
|
+
unicode-display_width (3.2.0)
|
|
199
|
+
unicode-emoji (~> 4.1)
|
|
200
|
+
unicode-emoji (4.2.0)
|
|
190
201
|
|
|
191
202
|
PLATFORMS
|
|
192
203
|
x86_64-darwin-21
|
|
@@ -202,4 +213,4 @@ DEPENDENCIES
|
|
|
202
213
|
simplecov-cobertura
|
|
203
214
|
|
|
204
215
|
BUNDLED WITH
|
|
205
|
-
|
|
216
|
+
4.0.8
|
data/README.md
CHANGED
|
@@ -5,15 +5,15 @@
|
|
|
5
5
|
|
|
6
6
|
# Nonnative
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Nonnative is a Ruby-first harness for end-to-end testing of systems implemented in other languages.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
It helps you:
|
|
11
|
+
- start **OS processes** (e.g. your Go/Java/Rust service binary),
|
|
12
|
+
- start **in-process Ruby servers** (e.g. small HTTP/TCP/gRPC fakes for dependencies),
|
|
13
|
+
- optionally start **proxies** in front of processes/servers/services for fault-injection,
|
|
14
|
+
- wait for readiness/shutdown using **TCP port checks**.
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
So why not test the way you want and build the microservice how you want. These kind of tests will make sure your application is tested properly by going end-to-end.
|
|
15
|
-
|
|
16
|
-
The way it works is it spawns processes or servers you configure and waits for it to start. Then you communicate with your microservice however you like (TCP, HTTP, gRPC, etc)
|
|
16
|
+
Once started, you can test however you like (TCP, HTTP, gRPC, etc).
|
|
17
17
|
|
|
18
18
|
## Installation
|
|
19
19
|
|
|
@@ -37,27 +37,38 @@ gem install nonnative
|
|
|
37
37
|
|
|
38
38
|
## Usage
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
Nonnative is configured via {#Nonnative.configure} (programmatic) or `config.load_file(...)` (YAML).
|
|
41
|
+
|
|
42
|
+
High-level configuration fields:
|
|
43
|
+
- `version`: configuration version (example: `"1.0"`).
|
|
44
|
+
- `name`: logical system name (used by `Nonnative.observability` for `/<name>/healthz`, etc).
|
|
45
|
+
- `url`: base URL for observability queries (example: `http://localhost:4567`).
|
|
46
|
+
- `log`: path for the Nonnative logger output.
|
|
47
|
+
- `processes`: child processes to `spawn`.
|
|
48
|
+
- `servers`: in-process Ruby servers started in threads.
|
|
49
|
+
- `services`: external dependencies (proxy-only; no process/thread started by Nonnative).
|
|
41
50
|
|
|
42
|
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
- A timeout value.
|
|
48
|
-
- A time to wait.
|
|
49
|
-
- Port to verify.
|
|
50
|
-
- The class for servers.
|
|
51
|
-
- The log for servers/processes
|
|
52
|
-
- The strategy for processes, servers and services.
|
|
51
|
+
Runner fields (process/server/service):
|
|
52
|
+
- `timeout`: max time (seconds) for readiness/shutdown port checks.
|
|
53
|
+
- `wait`: small sleep (seconds) between lifecycle steps.
|
|
54
|
+
- `host`/`port`: address used for port checks; when a proxy is enabled, reads happen via the proxy.
|
|
55
|
+
- `log`: per-runner log file (used by process output redirection or server implementations).
|
|
53
56
|
|
|
54
|
-
###
|
|
57
|
+
### Lifecycle strategies (Cucumber integration)
|
|
55
58
|
|
|
56
|
-
|
|
59
|
+
Nonnative ships Cucumber hooks (when loaded) that support these tags/strategies:
|
|
60
|
+
- `@startup`: start before scenario; stop after scenario
|
|
61
|
+
- `@manual`: stop after scenario (start is expected to be triggered manually in steps)
|
|
62
|
+
- `@clear`: clears memoized configuration and pool before scenario
|
|
63
|
+
- `@reset`: resets proxies after scenario
|
|
57
64
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
If you want “start once per test run”, require:
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
require 'nonnative/startup'
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
This calls `Nonnative.start` immediately and registers an `at_exit` stop.
|
|
61
72
|
|
|
62
73
|
### Processes
|
|
63
74
|
|
|
@@ -443,9 +454,9 @@ end
|
|
|
443
454
|
|
|
444
455
|
### Services
|
|
445
456
|
|
|
446
|
-
A service is an external dependency to your system
|
|
457
|
+
A service is an external dependency to your system that you **do not** want Nonnative to start (no OS process, no Ruby thread). Services are primarily useful when paired with proxies, because they let you inject failures into dependencies that are managed elsewhere (e.g. a DB running in Docker).
|
|
447
458
|
|
|
448
|
-
|
|
459
|
+
Set it up programmatically:
|
|
449
460
|
|
|
450
461
|
```ruby
|
|
451
462
|
require 'nonnative'
|
|
@@ -458,33 +469,37 @@ Nonnative.configure do |config|
|
|
|
458
469
|
|
|
459
470
|
config.service do |s|
|
|
460
471
|
s.name = 'postgres'
|
|
461
|
-
|
|
472
|
+
s.host = '127.0.0.1'
|
|
473
|
+
s.port = 5432
|
|
462
474
|
end
|
|
463
475
|
|
|
464
476
|
config.service do |s|
|
|
465
477
|
s.name = 'redis'
|
|
478
|
+
s.host = '127.0.0.1'
|
|
466
479
|
s.port = 6379
|
|
467
480
|
end
|
|
468
481
|
end
|
|
469
482
|
```
|
|
470
483
|
|
|
471
|
-
|
|
484
|
+
Set it up through configuration (YAML):
|
|
472
485
|
|
|
473
486
|
```yaml
|
|
474
487
|
version: "1.0"
|
|
475
488
|
name: test
|
|
476
489
|
url: http://localhost:4567
|
|
477
490
|
log: nonnative.log
|
|
478
|
-
|
|
491
|
+
services:
|
|
479
492
|
-
|
|
480
493
|
name: postgres
|
|
494
|
+
host: 127.0.0.1
|
|
481
495
|
port: 5432
|
|
482
496
|
-
|
|
483
497
|
name: redis
|
|
498
|
+
host: 127.0.0.1
|
|
484
499
|
port: 6379
|
|
485
500
|
```
|
|
486
501
|
|
|
487
|
-
Then load the file with
|
|
502
|
+
Then load the file with:
|
|
488
503
|
|
|
489
504
|
```ruby
|
|
490
505
|
require 'nonnative'
|
|
@@ -593,7 +608,7 @@ servers:
|
|
|
593
608
|
|
|
594
609
|
##### Proxies Services
|
|
595
610
|
|
|
596
|
-
|
|
611
|
+
Set it up programmatically:
|
|
597
612
|
|
|
598
613
|
```ruby
|
|
599
614
|
require 'nonnative'
|
|
@@ -603,11 +618,15 @@ Nonnative.configure do |config|
|
|
|
603
618
|
config.name = 'test'
|
|
604
619
|
config.url = 'http://localhost:4567'
|
|
605
620
|
config.log = 'nonnative.log'
|
|
606
|
-
config.wait = 1
|
|
607
621
|
|
|
608
622
|
config.service do |s|
|
|
623
|
+
s.name = 'redis'
|
|
624
|
+
s.host = '127.0.0.1'
|
|
625
|
+
s.port = 6379
|
|
626
|
+
|
|
609
627
|
s.proxy = {
|
|
610
628
|
kind: 'fault_injection',
|
|
629
|
+
host: '127.0.0.1',
|
|
611
630
|
port: 20_000,
|
|
612
631
|
log: 'proxy_server.log',
|
|
613
632
|
wait: 1,
|
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Nonnative
|
|
4
|
+
# Socket-pair variant used by the fault-injection proxy to simulate immediate connection failure.
|
|
5
|
+
#
|
|
6
|
+
# When active, the proxy accepts a TCP connection and closes it immediately without forwarding any
|
|
7
|
+
# bytes to the upstream service.
|
|
8
|
+
#
|
|
9
|
+
# This behavior is enabled by calling {Nonnative::FaultInjectionProxy#close_all}.
|
|
10
|
+
#
|
|
11
|
+
# @see Nonnative::FaultInjectionProxy
|
|
12
|
+
# @see Nonnative::SocketPairFactory
|
|
13
|
+
# @see Nonnative::SocketPair
|
|
4
14
|
class CloseAllSocketPair < SocketPair
|
|
15
|
+
# Closes the accepted socket immediately.
|
|
16
|
+
#
|
|
17
|
+
# @param local_socket [TCPSocket] the accepted client socket
|
|
18
|
+
# @return [void]
|
|
5
19
|
def connect(local_socket)
|
|
6
20
|
Nonnative.logger.info "closing socket '#{local_socket.inspect}' for 'close_all' pair"
|
|
7
21
|
|
|
@@ -1,15 +1,62 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Nonnative
|
|
4
|
+
# The gem configuration object.
|
|
5
|
+
#
|
|
6
|
+
# You can populate configuration either programmatically via the DSL ({#process}, {#server}, {#service}),
|
|
7
|
+
# or by loading a YAML file via {#load_file}.
|
|
8
|
+
#
|
|
9
|
+
# The configuration is consumed when {Nonnative.start} is called.
|
|
10
|
+
#
|
|
11
|
+
# == Programmatic configuration
|
|
12
|
+
#
|
|
13
|
+
# Nonnative.configure do |config|
|
|
14
|
+
# config.name = 'example'
|
|
15
|
+
# config.url = 'http://127.0.0.1:8080'
|
|
16
|
+
# config.log = 'test.log'
|
|
17
|
+
#
|
|
18
|
+
# config.process do |p|
|
|
19
|
+
# p.name = 'api'
|
|
20
|
+
# p.command = -> { './bin/api' }
|
|
21
|
+
# p.host = '127.0.0.1'
|
|
22
|
+
# p.port = 8080
|
|
23
|
+
# p.timeout = 10
|
|
24
|
+
# p.log = 'api.log'
|
|
25
|
+
# end
|
|
26
|
+
# end
|
|
27
|
+
#
|
|
28
|
+
# == File-based configuration
|
|
29
|
+
#
|
|
30
|
+
# Nonnative.configure do |config|
|
|
31
|
+
# config.load_file('features/configs/processes.yml')
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
4
34
|
class Configuration
|
|
35
|
+
# Creates an empty configuration.
|
|
36
|
+
#
|
|
37
|
+
# @return [void]
|
|
5
38
|
def initialize
|
|
6
39
|
@processes = []
|
|
7
40
|
@servers = []
|
|
8
41
|
@services = []
|
|
9
42
|
end
|
|
10
43
|
|
|
44
|
+
# @return [String, nil] logical system name (used for observability endpoints)
|
|
45
|
+
# @return [String, nil] configuration version
|
|
46
|
+
# @return [String, nil] base URL for observability queries (for example `"http://127.0.0.1:8080"`)
|
|
47
|
+
# @return [String, nil] path to the Nonnative log file
|
|
48
|
+
# @return [Array<Nonnative::ConfigurationProcess>] configured processes
|
|
49
|
+
# @return [Array<Nonnative::ConfigurationServer>] configured in-process servers
|
|
50
|
+
# @return [Array<Nonnative::ConfigurationService>] configured services (proxy-only)
|
|
11
51
|
attr_accessor :name, :version, :url, :log, :processes, :servers, :services
|
|
12
52
|
|
|
53
|
+
# Loads a configuration file and appends its runners to this instance.
|
|
54
|
+
#
|
|
55
|
+
# The file is loaded using the `config` gem via {Nonnative.configurations}. Top-level attributes are
|
|
56
|
+
# copied onto this object, and runner sections are transformed into configuration runner objects.
|
|
57
|
+
#
|
|
58
|
+
# @param path [String] path to a configuration file (typically YAML)
|
|
59
|
+
# @return [void]
|
|
13
60
|
def load_file(path)
|
|
14
61
|
cfg = Nonnative.configurations(path)
|
|
15
62
|
|
|
@@ -23,6 +70,10 @@ module Nonnative
|
|
|
23
70
|
add_services(cfg)
|
|
24
71
|
end
|
|
25
72
|
|
|
73
|
+
# Adds a process configuration entry.
|
|
74
|
+
#
|
|
75
|
+
# @yieldparam process [Nonnative::ConfigurationProcess]
|
|
76
|
+
# @return [void]
|
|
26
77
|
def process
|
|
27
78
|
process = Nonnative::ConfigurationProcess.new
|
|
28
79
|
yield process
|
|
@@ -30,6 +81,10 @@ module Nonnative
|
|
|
30
81
|
processes << process
|
|
31
82
|
end
|
|
32
83
|
|
|
84
|
+
# Adds a server configuration entry.
|
|
85
|
+
#
|
|
86
|
+
# @yieldparam server [Nonnative::ConfigurationServer]
|
|
87
|
+
# @return [void]
|
|
33
88
|
def server
|
|
34
89
|
server = Nonnative::ConfigurationServer.new
|
|
35
90
|
yield server
|
|
@@ -37,6 +92,13 @@ module Nonnative
|
|
|
37
92
|
servers << server
|
|
38
93
|
end
|
|
39
94
|
|
|
95
|
+
# Adds a service configuration entry.
|
|
96
|
+
#
|
|
97
|
+
# A "service" does not manage a Ruby thread or OS process; it exists so that a proxy can be started
|
|
98
|
+
# and controlled for an external dependency.
|
|
99
|
+
#
|
|
100
|
+
# @yieldparam service [Nonnative::ConfigurationService]
|
|
101
|
+
# @return [void]
|
|
40
102
|
def service
|
|
41
103
|
service = Nonnative::ConfigurationService.new
|
|
42
104
|
yield service
|
|
@@ -44,6 +106,11 @@ module Nonnative
|
|
|
44
106
|
services << service
|
|
45
107
|
end
|
|
46
108
|
|
|
109
|
+
# Finds a configured process by name.
|
|
110
|
+
#
|
|
111
|
+
# @param name [String]
|
|
112
|
+
# @return [Nonnative::ConfigurationProcess]
|
|
113
|
+
# @raise [Nonnative::NotFoundError] if no matching process exists
|
|
47
114
|
def process_by_name(name)
|
|
48
115
|
process = processes.find { |s| s.name == name }
|
|
49
116
|
raise NotFoundError, "Could not find process with name '#{name}'" if process.nil?
|
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Nonnative
|
|
4
|
+
# Process-specific configuration.
|
|
5
|
+
#
|
|
6
|
+
# A "process" is an OS-level child process started via `spawn` and stopped via signals.
|
|
7
|
+
# It is managed by {Nonnative::Process} at runtime.
|
|
8
|
+
#
|
|
9
|
+
# Instances are usually created through {Nonnative::Configuration#process}.
|
|
10
|
+
#
|
|
11
|
+
# @see Nonnative::Configuration
|
|
12
|
+
# @see Nonnative::Process
|
|
4
13
|
class ConfigurationProcess < ConfigurationRunner
|
|
14
|
+
# @return [Proc] a callable that returns the command string to execute (e.g. `-> { "./bin/api" }`)
|
|
15
|
+
# @return [String, nil] signal name to use for stopping (defaults to `"INT"` when not set)
|
|
16
|
+
# @return [Numeric] readiness timeout (seconds) used when waiting for the port to open/close
|
|
17
|
+
# @return [String] log file path to append process stdout/stderr to
|
|
18
|
+
# @return [Hash, nil] environment variables to pass to the spawned process
|
|
5
19
|
attr_accessor :command, :signal, :timeout, :log, :environment
|
|
6
20
|
end
|
|
7
21
|
end
|
|
@@ -1,9 +1,37 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Nonnative
|
|
4
|
+
# Proxy configuration attached to a runner configuration.
|
|
5
|
+
#
|
|
6
|
+
# A proxy allows you to interpose behavior between a client and a real service. For example,
|
|
7
|
+
# the built-in `"fault_injection"` proxy can close connections, introduce delays, or corrupt data
|
|
8
|
+
# for resilience testing.
|
|
9
|
+
#
|
|
10
|
+
# This object is created automatically for each runner via {Nonnative::ConfigurationRunner}.
|
|
11
|
+
# When `kind` is set to `"none"`, no proxy is started and the runner will use its configured
|
|
12
|
+
# `host`/`port` directly.
|
|
13
|
+
#
|
|
14
|
+
# @see Nonnative::ConfigurationRunner#proxy
|
|
15
|
+
# @see Nonnative.proxies
|
|
4
16
|
class ConfigurationProxy
|
|
17
|
+
# @return [String] proxy kind name (for example `"none"` or `"fault_injection"`)
|
|
18
|
+
# @return [String] proxy bind host (defaults to `"0.0.0.0"`)
|
|
19
|
+
# @return [Integer] proxy bind port (defaults to `0`)
|
|
20
|
+
# @return [String, nil] path to proxy log file (implementation-dependent)
|
|
21
|
+
# @return [Numeric] wait interval (seconds) after proxy state changes (defaults to `0.1`)
|
|
22
|
+
# @return [Hash] proxy implementation options (implementation-dependent)
|
|
5
23
|
attr_accessor :kind, :host, :port, :log, :wait, :options
|
|
6
24
|
|
|
25
|
+
# Creates a proxy configuration with defaults.
|
|
26
|
+
#
|
|
27
|
+
# Defaults:
|
|
28
|
+
# - `kind`: `"none"`
|
|
29
|
+
# - `host`: `"0.0.0.0"`
|
|
30
|
+
# - `port`: `0`
|
|
31
|
+
# - `wait`: `0.1`
|
|
32
|
+
# - `options`: `{}`
|
|
33
|
+
#
|
|
34
|
+
# @return [void]
|
|
7
35
|
def initialize
|
|
8
36
|
self.kind = 'none'
|
|
9
37
|
self.host = '0.0.0.0'
|
|
@@ -1,10 +1,41 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Nonnative
|
|
4
|
+
# Base configuration for a runnable unit managed by Nonnative.
|
|
5
|
+
#
|
|
6
|
+
# This class holds connection and timing attributes common to processes, servers and services,
|
|
7
|
+
# as well as a nested {Nonnative::ConfigurationProxy} describing how/if a proxy should be started.
|
|
8
|
+
#
|
|
9
|
+
# Instances of this type are typically created via {Nonnative::Configuration#process},
|
|
10
|
+
# {Nonnative::Configuration#server}, or {Nonnative::Configuration#service}.
|
|
11
|
+
#
|
|
12
|
+
# @see Nonnative::ConfigurationProcess
|
|
13
|
+
# @see Nonnative::ConfigurationServer
|
|
14
|
+
# @see Nonnative::ConfigurationService
|
|
4
15
|
class ConfigurationRunner
|
|
16
|
+
# @return [String, nil] runner name used for lookup (for example via `pool.process_by_name`)
|
|
17
|
+
# @return [String] host to bind/connect to (defaults to `"0.0.0.0"`)
|
|
18
|
+
# @return [Integer] port to bind/connect to
|
|
19
|
+
# @return [Numeric] wait interval (seconds) used by runners between lifecycle steps
|
|
5
20
|
attr_accessor :name, :host, :port, :wait
|
|
21
|
+
|
|
22
|
+
# Proxy configuration for this runner.
|
|
23
|
+
#
|
|
24
|
+
# Note that this returns a configuration object even if no proxy is enabled; by default
|
|
25
|
+
# the proxy kind is `"none"`.
|
|
26
|
+
#
|
|
27
|
+
# @return [Nonnative::ConfigurationProxy]
|
|
6
28
|
attr_reader :proxy
|
|
7
29
|
|
|
30
|
+
# Creates a runner configuration with defaults.
|
|
31
|
+
#
|
|
32
|
+
# Defaults:
|
|
33
|
+
# - `host`: `"0.0.0.0"`
|
|
34
|
+
# - `port`: `0`
|
|
35
|
+
# - `wait`: `0.1`
|
|
36
|
+
# - `proxy`: a new {Nonnative::ConfigurationProxy} with its own defaults
|
|
37
|
+
#
|
|
38
|
+
# @return [void]
|
|
8
39
|
def initialize
|
|
9
40
|
self.host = '0.0.0.0'
|
|
10
41
|
self.port = 0
|
|
@@ -13,6 +44,19 @@ module Nonnative
|
|
|
13
44
|
@proxy = Nonnative::ConfigurationProxy.new
|
|
14
45
|
end
|
|
15
46
|
|
|
47
|
+
# Sets proxy configuration using a hash-like value.
|
|
48
|
+
#
|
|
49
|
+
# This is primarily used when loading YAML configuration files, where proxy attributes are
|
|
50
|
+
# represented as scalar values.
|
|
51
|
+
#
|
|
52
|
+
# @param value [Hash] proxy attributes
|
|
53
|
+
# @option value [String] :kind proxy kind name (for example `"fault_injection"`)
|
|
54
|
+
# @option value [String] :host proxy bind host (optional)
|
|
55
|
+
# @option value [Integer] :port proxy bind port
|
|
56
|
+
# @option value [String] :log proxy log file path
|
|
57
|
+
# @option value [Numeric] :wait wait interval (seconds) after state changes (optional)
|
|
58
|
+
# @option value [Hash] :options proxy implementation specific options
|
|
59
|
+
# @return [void]
|
|
16
60
|
def proxy=(value)
|
|
17
61
|
proxy.kind = value[:kind]
|
|
18
62
|
proxy.host = value[:host] if value[:host]
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Nonnative
|
|
4
|
+
# Server-specific configuration.
|
|
5
|
+
#
|
|
6
|
+
# A "server" is an in-process Ruby component started in a background thread and stopped via a
|
|
7
|
+
# server-specific shutdown routine. It is managed by {Nonnative::Server} at runtime.
|
|
8
|
+
#
|
|
9
|
+
# Instances are usually created through {Nonnative::Configuration#server}.
|
|
10
|
+
#
|
|
11
|
+
# @see Nonnative::Configuration
|
|
12
|
+
# @see Nonnative::Server
|
|
4
13
|
class ConfigurationServer < ConfigurationRunner
|
|
14
|
+
# @return [Class] a class that implements `#initialize(service)`, and lifecycle hooks expected by {Nonnative::Server}
|
|
15
|
+
# @return [Numeric] readiness timeout (seconds) used when waiting for the port to open/close
|
|
16
|
+
# @return [String] log file path used by server implementations (for example Puma/gRPC log files)
|
|
5
17
|
attr_accessor :klass, :timeout, :log
|
|
6
18
|
end
|
|
7
19
|
end
|