nonnative 3.2.1 → 3.6.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 +30 -21
- data/.gitignore +11 -2
- data/AGENTS.md +8 -8
- data/Gemfile.lock +10 -10
- data/README.md +10 -5
- data/lib/nonnative/configuration.rb +15 -2
- data/lib/nonnative/configuration_process.rb +13 -1
- data/lib/nonnative/configuration_proxy.rb +10 -1
- data/lib/nonnative/configuration_runner.rb +8 -2
- data/lib/nonnative/configuration_server.rb +19 -3
- data/lib/nonnative/cucumber.rb +20 -0
- data/lib/nonnative/fault_injection_proxy.rb +2 -2
- data/lib/nonnative/http_proxy_server.rb +9 -1
- data/lib/nonnative/http_server.rb +19 -6
- data/lib/nonnative/process.rb +8 -0
- data/lib/nonnative/proxy.rb +4 -1
- data/lib/nonnative/version.rb +1 -1
- data/lib/nonnative.rb +3 -3
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0fb25075ea94da70b097c6b60e7a47fea1a73b7e80d4903f32b2a2d00675b50e
|
|
4
|
+
data.tar.gz: 33c1c4310fa48a1a8dacf792d9ce6af73f9a406f67185f61fb292f07d06d2345
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c1c7e788eacbf1ed02100d4fc954f3ab7781c17a4e8c46c8ca8764becea615d54c6dee3be60404901228b37397533a809873c3ae27a157ac53463ff048ce8d50
|
|
7
|
+
data.tar.gz: fc73665d0141f943beca8dd9d43adbee088a214d1d31dad757fed1502df707f8ae599f10f4d5b3eb0b0c7e8c7db4b43b860cdd655e90d49c194320148df65439
|
data/.circleci/config.yml
CHANGED
|
@@ -1,15 +1,33 @@
|
|
|
1
1
|
version: 2.1
|
|
2
2
|
|
|
3
|
+
commands:
|
|
4
|
+
sync-submodules:
|
|
5
|
+
steps:
|
|
6
|
+
- run: git submodule sync
|
|
7
|
+
- run: git submodule update --init
|
|
8
|
+
checkout-submodules:
|
|
9
|
+
steps:
|
|
10
|
+
- checkout:
|
|
11
|
+
method: blobless
|
|
12
|
+
- sync-submodules
|
|
13
|
+
|
|
14
|
+
executors:
|
|
15
|
+
ruby:
|
|
16
|
+
docker:
|
|
17
|
+
- image: alexfalkowski/ruby:3.17
|
|
18
|
+
release:
|
|
19
|
+
docker:
|
|
20
|
+
- image: alexfalkowski/release:8.18
|
|
21
|
+
alpine:
|
|
22
|
+
docker:
|
|
23
|
+
- image: alpine:latest
|
|
24
|
+
|
|
3
25
|
jobs:
|
|
4
26
|
build:
|
|
5
|
-
|
|
6
|
-
- image: alexfalkowski/ruby:3.14
|
|
27
|
+
executor: ruby
|
|
7
28
|
working_directory: ~/nonnative
|
|
8
29
|
steps:
|
|
9
|
-
- checkout
|
|
10
|
-
method: blobless
|
|
11
|
-
- run: git submodule sync
|
|
12
|
-
- run: git submodule update --init
|
|
30
|
+
- checkout-submodules
|
|
13
31
|
- run: make source-key
|
|
14
32
|
- restore_cache:
|
|
15
33
|
name: restore deps
|
|
@@ -34,34 +52,25 @@ jobs:
|
|
|
34
52
|
- run: make codecov-upload
|
|
35
53
|
resource_class: arm.large
|
|
36
54
|
sync:
|
|
37
|
-
|
|
38
|
-
- image: alexfalkowski/release:8.15
|
|
55
|
+
executor: release
|
|
39
56
|
working_directory: ~/nonnative
|
|
40
57
|
steps:
|
|
41
|
-
- checkout
|
|
42
|
-
method: blobless
|
|
43
|
-
- run: git submodule sync
|
|
44
|
-
- run: git submodule update --init
|
|
58
|
+
- checkout-submodules
|
|
45
59
|
- run: make sync push
|
|
46
60
|
resource_class: arm.large
|
|
47
61
|
version:
|
|
48
|
-
|
|
49
|
-
- image: alexfalkowski/release:8.15
|
|
62
|
+
executor: release
|
|
50
63
|
working_directory: ~/nonnative
|
|
51
64
|
steps:
|
|
52
|
-
- checkout
|
|
53
|
-
method: blobless
|
|
54
|
-
- run: git submodule sync
|
|
55
|
-
- run: git submodule update --init
|
|
65
|
+
- checkout-submodules
|
|
56
66
|
- run: version
|
|
57
67
|
- run: package
|
|
58
68
|
resource_class: arm.large
|
|
59
69
|
wait-all:
|
|
60
|
-
|
|
61
|
-
|
|
70
|
+
executor: alpine
|
|
71
|
+
resource_class: small
|
|
62
72
|
steps:
|
|
63
73
|
- run: echo "all applicable jobs finished"
|
|
64
|
-
resource_class: arm.large
|
|
65
74
|
|
|
66
75
|
workflows:
|
|
67
76
|
nonnative:
|
data/.gitignore
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
/.bundle/
|
|
2
|
+
.crush/
|
|
3
|
+
.ruby-lsp/
|
|
4
|
+
.source-key
|
|
2
5
|
/.yardoc
|
|
3
6
|
/_yardoc/
|
|
4
7
|
/doc/
|
|
5
8
|
/pkg/
|
|
9
|
+
ISSUES.md
|
|
10
|
+
TESTS.md
|
|
11
|
+
DOCS.md
|
|
12
|
+
FEATURES.md
|
|
13
|
+
PROJECTS.md
|
|
14
|
+
RELIABILITY.md
|
|
6
15
|
test/reports/*
|
|
16
|
+
!test/reports/.keep
|
|
7
17
|
/tmp/
|
|
8
|
-
vendor
|
|
9
|
-
ISSUES.md
|
|
18
|
+
vendor/
|
data/AGENTS.md
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# AGENTS.md
|
|
2
2
|
|
|
3
|
-
## Shared
|
|
3
|
+
## Shared guidance
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
`bin/AGENTS.md` for the canonical shared skill list and use the smallest
|
|
7
|
-
matching skill for the task.
|
|
5
|
+
Use `bin/AGENTS.md` for shared skills and cross-repository defaults.
|
|
8
6
|
|
|
9
7
|
`nonnative` is a Ruby gem for end-to-end testing systems implemented in other
|
|
10
8
|
languages. It starts processes, in-process servers, and proxy-only services,
|
|
@@ -18,18 +16,16 @@ of dependencies.
|
|
|
18
16
|
- Generated gRPC test stubs: `test/grpc/**/*`
|
|
19
17
|
- Test protos: `test/nonnative/v1/*.proto`
|
|
20
18
|
- Build wiring: root `Makefile` includes `bin/build/make/*.mak`
|
|
21
|
-
- Required submodule: `bin
|
|
19
|
+
- Required submodule: `bin/`; missing submodule setup breaks `make`
|
|
22
20
|
- Install deps: `make dep`
|
|
23
21
|
- Lint: `make lint`
|
|
22
|
+
- Security checks: `make sec`
|
|
24
23
|
- Features: `make features`
|
|
25
24
|
- Benchmarks only: `make benchmarks`
|
|
26
25
|
- Cleanup: `make clean-dep`, `make clean-reports`
|
|
27
26
|
|
|
28
27
|
## Intentional Design Choices
|
|
29
28
|
|
|
30
|
-
- Root `make` compatibility is GNU Make 4+/`gmake` oriented through the shared
|
|
31
|
-
`bin/build/make/*.mak` fragments. Do not flag GNU Make 3.81 parsing failures
|
|
32
|
-
as code issues unless the task is explicitly about supporting GNU Make 3.81.
|
|
33
29
|
- The configured HTTP proxy feature intentionally uses the external
|
|
34
30
|
`www.afalkowski.com` host through `features/support/http_proxy_server.rb`.
|
|
35
31
|
Do not flag this external dependency as a code issue unless the task is
|
|
@@ -54,6 +50,10 @@ of dependencies.
|
|
|
54
50
|
test proto changes. Do not flag the absence of an automatic generated-stub
|
|
55
51
|
freshness check as a test gap unless the task is explicitly about changing
|
|
56
52
|
test proto generation or generated-file validation.
|
|
53
|
+
- Generated gRPC test stubs may carry manual require-path adjustments after
|
|
54
|
+
generation. Do not treat `make -C test stale` as a required validation target
|
|
55
|
+
unless the task is explicitly about changing test proto generation; verify
|
|
56
|
+
the generated Ruby loads before accepting generator-only rewrites.
|
|
57
57
|
- Buf linting for the test-only proto module is intentionally not part of the
|
|
58
58
|
required local validation surface. Do not flag missing root-level validation
|
|
59
59
|
for `test/buf.yaml` as a test gap unless the task is explicitly about test
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
nonnative (3.
|
|
4
|
+
nonnative (3.6.0)
|
|
5
5
|
concurrent-ruby (>= 1, < 2)
|
|
6
6
|
config (>= 5, < 6)
|
|
7
7
|
cucumber (>= 7, < 12)
|
|
@@ -26,7 +26,7 @@ GEM
|
|
|
26
26
|
benchmark-trend (0.4.0)
|
|
27
27
|
bigdecimal (4.1.2)
|
|
28
28
|
builder (3.3.0)
|
|
29
|
-
concurrent-ruby (1.3.
|
|
29
|
+
concurrent-ruby (1.3.7)
|
|
30
30
|
config (5.6.1)
|
|
31
31
|
deep_merge (~> 1.2, >= 1.2.1)
|
|
32
32
|
ostruct
|
|
@@ -64,24 +64,24 @@ GEM
|
|
|
64
64
|
get_process_mem (1.0.0)
|
|
65
65
|
bigdecimal (>= 2.0)
|
|
66
66
|
ffi (~> 1.0)
|
|
67
|
-
google-protobuf (4.35.
|
|
67
|
+
google-protobuf (4.35.1-x86_64-darwin)
|
|
68
68
|
bigdecimal
|
|
69
69
|
rake (~> 13.3)
|
|
70
|
-
google-protobuf (4.35.
|
|
70
|
+
google-protobuf (4.35.1-x86_64-linux-gnu)
|
|
71
71
|
bigdecimal
|
|
72
72
|
rake (~> 13.3)
|
|
73
73
|
googleapis-common-protos-types (1.23.0)
|
|
74
74
|
google-protobuf (~> 4.26)
|
|
75
|
-
grpc (1.81.
|
|
75
|
+
grpc (1.81.1-x86_64-darwin)
|
|
76
76
|
google-protobuf (>= 3.25, < 5.0)
|
|
77
77
|
googleapis-common-protos-types (~> 1.0)
|
|
78
|
-
grpc (1.81.
|
|
78
|
+
grpc (1.81.1-x86_64-linux-gnu)
|
|
79
79
|
google-protobuf (>= 3.25, < 5.0)
|
|
80
80
|
googleapis-common-protos-types (~> 1.0)
|
|
81
81
|
http-accept (1.7.0)
|
|
82
82
|
http-cookie (1.1.6)
|
|
83
83
|
domain_name (~> 0.5)
|
|
84
|
-
json (2.
|
|
84
|
+
json (2.20.0)
|
|
85
85
|
language_server-protocol (3.17.0.5)
|
|
86
86
|
lint_roller (1.1.0)
|
|
87
87
|
logger (1.7.0)
|
|
@@ -114,7 +114,7 @@ GEM
|
|
|
114
114
|
rack (>= 3.0.0)
|
|
115
115
|
rainbow (3.1.1)
|
|
116
116
|
rake (13.4.2)
|
|
117
|
-
rbs (4.0.
|
|
117
|
+
rbs (4.0.3)
|
|
118
118
|
logger
|
|
119
119
|
prism (>= 1.6.0)
|
|
120
120
|
tsort
|
|
@@ -146,7 +146,7 @@ GEM
|
|
|
146
146
|
rspec-support (3.13.7)
|
|
147
147
|
rspec-wait (1.0.2)
|
|
148
148
|
rspec (>= 3.4)
|
|
149
|
-
rubocop (1.
|
|
149
|
+
rubocop (1.88.0)
|
|
150
150
|
json (~> 2.3)
|
|
151
151
|
language_server-protocol (~> 3.17.0.2)
|
|
152
152
|
lint_roller (~> 1.1.0)
|
|
@@ -169,7 +169,7 @@ GEM
|
|
|
169
169
|
docile (~> 1.1)
|
|
170
170
|
simplecov-html (~> 0.11)
|
|
171
171
|
simplecov_json_formatter (~> 0.1)
|
|
172
|
-
simplecov-cobertura (3.
|
|
172
|
+
simplecov-cobertura (3.2.0)
|
|
173
173
|
rexml
|
|
174
174
|
simplecov (~> 0.19)
|
|
175
175
|
simplecov-html (0.13.2)
|
data/README.md
CHANGED
|
@@ -62,7 +62,7 @@ Common runner fields:
|
|
|
62
62
|
|
|
63
63
|
Process/server fields:
|
|
64
64
|
- `ports`: client-facing ports. These are also used for readiness/shutdown port checks.
|
|
65
|
-
- `timeout`: max time (seconds) for readiness/shutdown port checks.
|
|
65
|
+
- `timeout`: max time (seconds) for readiness/shutdown port checks. Defaults to `1.0`.
|
|
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
|
|
|
@@ -164,7 +164,7 @@ Nonnative.configure do |config|
|
|
|
164
164
|
|
|
165
165
|
config.process do |p|
|
|
166
166
|
p.name = 'start_1'
|
|
167
|
-
p.command = -> { ['
|
|
167
|
+
p.command = -> { ['bin/start-test-service', '12_321'] }
|
|
168
168
|
p.timeout = 5
|
|
169
169
|
p.wait = 0.1
|
|
170
170
|
p.ports = [12_321]
|
|
@@ -177,7 +177,7 @@ Nonnative.configure do |config|
|
|
|
177
177
|
|
|
178
178
|
config.process do |p|
|
|
179
179
|
p.name = 'start_2'
|
|
180
|
-
p.command = -> { ['
|
|
180
|
+
p.command = -> { ['bin/start-test-service', '12_322'] }
|
|
181
181
|
p.timeout = 0.5
|
|
182
182
|
p.wait = 0.1
|
|
183
183
|
p.ports = [12_322]
|
|
@@ -197,7 +197,7 @@ processes:
|
|
|
197
197
|
-
|
|
198
198
|
name: start_1
|
|
199
199
|
command:
|
|
200
|
-
-
|
|
200
|
+
- bin/start-test-service
|
|
201
201
|
- "12_321"
|
|
202
202
|
timeout: 5
|
|
203
203
|
wait: 1
|
|
@@ -210,7 +210,7 @@ processes:
|
|
|
210
210
|
-
|
|
211
211
|
name: start_2
|
|
212
212
|
command:
|
|
213
|
-
-
|
|
213
|
+
- bin/start-test-service
|
|
214
214
|
- "12_322"
|
|
215
215
|
timeout: 5
|
|
216
216
|
wait: 1
|
|
@@ -474,6 +474,8 @@ end
|
|
|
474
474
|
|
|
475
475
|
Define your server:
|
|
476
476
|
|
|
477
|
+
Assume the gRPC service base class and response types below come from your generated gRPC stubs.
|
|
478
|
+
|
|
477
479
|
```ruby
|
|
478
480
|
module Nonnative
|
|
479
481
|
module Features
|
|
@@ -530,6 +532,9 @@ servers:
|
|
|
530
532
|
log: grpc_server_1.log
|
|
531
533
|
```
|
|
532
534
|
|
|
535
|
+
The `grpc` gem uses a global logger, so per-server gRPC log files are not independent. The first
|
|
536
|
+
initialized gRPC server sets the logger used by later gRPC servers in the same Ruby process.
|
|
537
|
+
|
|
533
538
|
Then load the file with:
|
|
534
539
|
|
|
535
540
|
```ruby
|
|
@@ -42,13 +42,25 @@ module Nonnative
|
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
# @return [String, nil] logical system name (used for observability endpoints)
|
|
45
|
+
attr_accessor :name
|
|
46
|
+
|
|
45
47
|
# @return [String, nil] configuration version
|
|
48
|
+
attr_accessor :version
|
|
49
|
+
|
|
46
50
|
# @return [String, nil] base URL for observability queries (for example `"http://127.0.0.1:8080"`)
|
|
51
|
+
attr_accessor :url
|
|
52
|
+
|
|
47
53
|
# @return [String, nil] path to the Nonnative log file
|
|
54
|
+
attr_accessor :log
|
|
55
|
+
|
|
48
56
|
# @return [Array<Nonnative::ConfigurationProcess>] configured processes
|
|
57
|
+
attr_accessor :processes
|
|
58
|
+
|
|
49
59
|
# @return [Array<Nonnative::ConfigurationServer>] configured in-process servers
|
|
60
|
+
attr_accessor :servers
|
|
61
|
+
|
|
50
62
|
# @return [Array<Nonnative::ConfigurationService>] configured services (proxy-only)
|
|
51
|
-
attr_accessor :
|
|
63
|
+
attr_accessor :services
|
|
52
64
|
|
|
53
65
|
# Loads a configuration file and appends its runners to this instance.
|
|
54
66
|
#
|
|
@@ -179,7 +191,8 @@ module Nonnative
|
|
|
179
191
|
|
|
180
192
|
def runner_attributes(runner, loaded)
|
|
181
193
|
runner.name = loaded.name
|
|
182
|
-
|
|
194
|
+
timeout = loaded.timeout
|
|
195
|
+
runner.timeout = timeout unless timeout.nil?
|
|
183
196
|
runner.wait = loaded.wait if loaded.wait
|
|
184
197
|
runner.host = loaded.host if loaded.host
|
|
185
198
|
assign_ports(runner, loaded)
|
|
@@ -19,7 +19,7 @@ module Nonnative
|
|
|
19
19
|
# @return [String, nil] signal name to use for stopping (defaults to `"INT"` when not set)
|
|
20
20
|
attr_accessor :signal
|
|
21
21
|
|
|
22
|
-
# @return [Numeric] readiness timeout (seconds) used when waiting for ports to open/close
|
|
22
|
+
# @return [Numeric] readiness timeout (seconds) used when waiting for ports to open/close (defaults to `1.0`)
|
|
23
23
|
attr_accessor :timeout
|
|
24
24
|
|
|
25
25
|
# @return [String] log file path to append process stdout/stderr to
|
|
@@ -27,5 +27,17 @@ module Nonnative
|
|
|
27
27
|
|
|
28
28
|
# @return [Hash, nil] environment variables to pass to the spawned process
|
|
29
29
|
attr_accessor :environment
|
|
30
|
+
|
|
31
|
+
# Creates a process configuration with bounded lifecycle defaults.
|
|
32
|
+
#
|
|
33
|
+
# Defaults:
|
|
34
|
+
# - `timeout`: `1.0`
|
|
35
|
+
#
|
|
36
|
+
# @return [void]
|
|
37
|
+
def initialize
|
|
38
|
+
super
|
|
39
|
+
|
|
40
|
+
self.timeout = DEFAULT_TIMEOUT
|
|
41
|
+
end
|
|
30
42
|
end
|
|
31
43
|
end
|
|
@@ -15,12 +15,21 @@ module Nonnative
|
|
|
15
15
|
# @see Nonnative.proxies
|
|
16
16
|
class ConfigurationProxy
|
|
17
17
|
# @return [String] proxy kind name (for example `"none"` or `"fault_injection"`)
|
|
18
|
+
attr_accessor :kind
|
|
19
|
+
|
|
18
20
|
# @return [String] upstream host used by proxy implementations (defaults to `"127.0.0.1"`)
|
|
21
|
+
attr_accessor :host
|
|
22
|
+
|
|
19
23
|
# @return [Integer] upstream port used by proxy implementations (defaults to `0`)
|
|
24
|
+
attr_accessor :port
|
|
25
|
+
|
|
20
26
|
# @return [String, nil] path to proxy log file (implementation-dependent)
|
|
27
|
+
attr_accessor :log
|
|
28
|
+
|
|
21
29
|
# @return [Numeric] wait interval (seconds) after proxy state changes (defaults to `0.1`)
|
|
30
|
+
attr_accessor :wait
|
|
31
|
+
|
|
22
32
|
# @return [Hash] proxy implementation options (implementation-dependent)
|
|
23
|
-
attr_accessor :kind, :host, :port, :log, :wait
|
|
24
33
|
attr_reader :options
|
|
25
34
|
|
|
26
35
|
# Creates a proxy configuration with defaults.
|
|
@@ -12,11 +12,17 @@ module Nonnative
|
|
|
12
12
|
# @see Nonnative::ConfigurationServer
|
|
13
13
|
# @see Nonnative::ConfigurationService
|
|
14
14
|
class ConfigurationRunner
|
|
15
|
+
# Default bounded readiness/shutdown timeout for process and server runners.
|
|
16
|
+
DEFAULT_TIMEOUT = 1.0
|
|
17
|
+
|
|
15
18
|
# @return [String, nil] runner name used for lookup (for example via `pool.process_by_name`)
|
|
19
|
+
attr_accessor :name
|
|
20
|
+
|
|
16
21
|
# @return [String] host to bind/connect to (defaults to `"127.0.0.1"`)
|
|
17
|
-
|
|
22
|
+
attr_accessor :host
|
|
23
|
+
|
|
18
24
|
# @return [Numeric] wait interval (seconds) used by runners between lifecycle steps
|
|
19
|
-
attr_accessor :
|
|
25
|
+
attr_accessor :wait
|
|
20
26
|
|
|
21
27
|
# @return [Array<Integer>] client-facing ports used for readiness/shutdown checks
|
|
22
28
|
attr_reader :ports
|
|
@@ -11,9 +11,25 @@ module Nonnative
|
|
|
11
11
|
# @see Nonnative::Configuration
|
|
12
12
|
# @see Nonnative::Server
|
|
13
13
|
class ConfigurationServer < ConfigurationRunner
|
|
14
|
-
# @return [Class] a class that implements `#initialize(service)
|
|
15
|
-
|
|
14
|
+
# @return [Class] a class that implements `#initialize(service)` and the hooks expected by {Nonnative::Server}
|
|
15
|
+
attr_accessor :klass
|
|
16
|
+
|
|
17
|
+
# @return [Numeric] readiness timeout (seconds) used when waiting for ports to open/close (defaults to `1.0`)
|
|
18
|
+
attr_accessor :timeout
|
|
19
|
+
|
|
16
20
|
# @return [String] log file path used by server implementations (for example Puma/gRPC log files)
|
|
17
|
-
attr_accessor :
|
|
21
|
+
attr_accessor :log
|
|
22
|
+
|
|
23
|
+
# Creates a server configuration with bounded lifecycle defaults.
|
|
24
|
+
#
|
|
25
|
+
# Defaults:
|
|
26
|
+
# - `timeout`: `1.0`
|
|
27
|
+
#
|
|
28
|
+
# @return [void]
|
|
29
|
+
def initialize
|
|
30
|
+
super
|
|
31
|
+
|
|
32
|
+
self.timeout = DEFAULT_TIMEOUT
|
|
33
|
+
end
|
|
18
34
|
end
|
|
19
35
|
end
|
data/lib/nonnative/cucumber.rb
CHANGED
|
@@ -8,6 +8,26 @@ module Nonnative
|
|
|
8
8
|
# Requiring `nonnative` outside a running Cucumber environment should not fail, but when Cucumber
|
|
9
9
|
# does finish booting its support-code registry this installer still needs to register the hooks
|
|
10
10
|
# and step definitions defined here.
|
|
11
|
+
#
|
|
12
|
+
# Supported hooks:
|
|
13
|
+
# - `@startup`: start before scenario, stop after scenario
|
|
14
|
+
# - `@manual`: stop after scenario; use `When I start the system` to start manually
|
|
15
|
+
# - `@clear`: clear memoized Nonnative state before scenario
|
|
16
|
+
# - `@reset`: reset proxies after scenario
|
|
17
|
+
#
|
|
18
|
+
# Installed step definitions:
|
|
19
|
+
# - `Given I set the proxy for service {string} to {string}`
|
|
20
|
+
# - `Then I should reset the proxy for service {string}`
|
|
21
|
+
# - `When I start the system`
|
|
22
|
+
# - `When I attempt to start the system`
|
|
23
|
+
# - `When I attempt to stop the system`
|
|
24
|
+
# - `Then I should see {string} as unhealthy`
|
|
25
|
+
# - `Then I should see {string} as healthy`
|
|
26
|
+
# - `Then the process {string} should consume less than {string} of memory`
|
|
27
|
+
# - `Then starting the system should raise an error`
|
|
28
|
+
# - `Then stopping the system should raise an error`
|
|
29
|
+
# - `Then I should see a log entry of {string} for process {string}`
|
|
30
|
+
# - `Then I should see a log entry of {string} in the file {string}`
|
|
11
31
|
module Cucumber
|
|
12
32
|
module LanguageHook
|
|
13
33
|
def rb_language=(value)
|
|
@@ -11,7 +11,7 @@ module Nonnative
|
|
|
11
11
|
#
|
|
12
12
|
# - {#close_all}: close connections immediately on accept
|
|
13
13
|
# - {#delay}: delay reads by a configured duration (default: 2 seconds)
|
|
14
|
-
# - {#invalid_data}:
|
|
14
|
+
# - {#invalid_data}: forward requests unchanged and mutate upstream responses before they reach clients
|
|
15
15
|
# - {#reset}: return to healthy pass-through behavior
|
|
16
16
|
#
|
|
17
17
|
# State changes terminate any active connections so new connections observe the new behavior.
|
|
@@ -107,7 +107,7 @@ module Nonnative
|
|
|
107
107
|
apply_state :delay
|
|
108
108
|
end
|
|
109
109
|
|
|
110
|
-
#
|
|
110
|
+
# Mutates upstream responses while forwarding client requests unchanged.
|
|
111
111
|
#
|
|
112
112
|
# @return [void]
|
|
113
113
|
def invalid_data
|
|
@@ -23,6 +23,14 @@ module Nonnative
|
|
|
23
23
|
#
|
|
24
24
|
# Supported HTTP verbs: GET, POST, PUT, PATCH, DELETE.
|
|
25
25
|
class HTTPProxy < Sinatra::Application
|
|
26
|
+
NON_FORWARDABLE_HEADERS = %w[
|
|
27
|
+
Host
|
|
28
|
+
Accept-Encoding
|
|
29
|
+
Version
|
|
30
|
+
Proxy-Authenticate
|
|
31
|
+
Proxy-Authorization
|
|
32
|
+
].freeze
|
|
33
|
+
|
|
26
34
|
# Extracts request headers from the Rack environment and normalizes them to standard HTTP names.
|
|
27
35
|
#
|
|
28
36
|
# Certain hop-by-hop or proxy-specific headers are removed.
|
|
@@ -36,7 +44,7 @@ module Nonnative
|
|
|
36
44
|
result[normalized_header_name(header)] = value
|
|
37
45
|
end
|
|
38
46
|
|
|
39
|
-
headers.except(
|
|
47
|
+
headers.except(*NON_FORWARDABLE_HEADERS)
|
|
40
48
|
end
|
|
41
49
|
|
|
42
50
|
# Builds the upstream URL for the given request.
|
|
@@ -9,14 +9,20 @@ module Nonnative
|
|
|
9
9
|
# The server is started and stopped by {Nonnative::Server} via {#perform_start} / {#perform_stop}.
|
|
10
10
|
#
|
|
11
11
|
# @example Running a Sinatra app
|
|
12
|
-
#
|
|
13
|
-
#
|
|
12
|
+
# class HelloHTTPServer < Nonnative::HTTPServer
|
|
13
|
+
# def initialize(service)
|
|
14
|
+
# app = Sinatra.new do
|
|
15
|
+
# get('/hello') { 'Hello World!' }
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# super(app, service)
|
|
19
|
+
# end
|
|
14
20
|
# end
|
|
15
21
|
#
|
|
16
22
|
# Nonnative.configure do |config|
|
|
17
23
|
# config.server do |s|
|
|
18
24
|
# s.name = 'http'
|
|
19
|
-
# s.klass =
|
|
25
|
+
# s.klass = HelloHTTPServer
|
|
20
26
|
# s.timeout = 2
|
|
21
27
|
# s.host = '127.0.0.1'
|
|
22
28
|
# s.ports = [4567]
|
|
@@ -24,7 +30,7 @@ module Nonnative
|
|
|
24
30
|
# end
|
|
25
31
|
# end
|
|
26
32
|
#
|
|
27
|
-
#
|
|
33
|
+
# YAML configuration uses the same concrete subclass name in its `class` field.
|
|
28
34
|
#
|
|
29
35
|
# @see Nonnative::Server
|
|
30
36
|
class HTTPServer < Nonnative::Server
|
|
@@ -33,7 +39,8 @@ module Nonnative
|
|
|
33
39
|
# @param app [#call] a Rack-compatible application (e.g. Sinatra/Rack app)
|
|
34
40
|
# @param service [Nonnative::ConfigurationServer] server configuration
|
|
35
41
|
def initialize(app, service)
|
|
36
|
-
|
|
42
|
+
# Keep the log IO so the server lifecycle can release Puma's file handle on stop.
|
|
43
|
+
@log = File.open(service.log, 'a')
|
|
37
44
|
options = {
|
|
38
45
|
log_writer: Puma::LogWriter.new(log, log),
|
|
39
46
|
force_shutdown_after: service.timeout
|
|
@@ -60,10 +67,16 @@ module Nonnative
|
|
|
60
67
|
# @return [void]
|
|
61
68
|
def perform_stop
|
|
62
69
|
server.graceful_shutdown
|
|
70
|
+
ensure
|
|
71
|
+
close_log
|
|
63
72
|
end
|
|
64
73
|
|
|
65
74
|
private
|
|
66
75
|
|
|
67
|
-
attr_reader :server
|
|
76
|
+
attr_reader :server, :log
|
|
77
|
+
|
|
78
|
+
def close_log
|
|
79
|
+
log.close unless log.closed?
|
|
80
|
+
end
|
|
68
81
|
end
|
|
69
82
|
end
|
data/lib/nonnative/process.rb
CHANGED
|
@@ -49,6 +49,7 @@ module Nonnative
|
|
|
49
49
|
if process_exists?
|
|
50
50
|
process_kill
|
|
51
51
|
stopped = wait_stop != false
|
|
52
|
+
force_stop unless stopped
|
|
52
53
|
end
|
|
53
54
|
|
|
54
55
|
[pid, stopped]
|
|
@@ -82,6 +83,13 @@ module Nonnative
|
|
|
82
83
|
::Process.kill(signal, pid)
|
|
83
84
|
end
|
|
84
85
|
|
|
86
|
+
def force_stop
|
|
87
|
+
::Process.kill('KILL', pid)
|
|
88
|
+
wait_stop
|
|
89
|
+
rescue Errno::ESRCH, Errno::ECHILD
|
|
90
|
+
true
|
|
91
|
+
end
|
|
92
|
+
|
|
85
93
|
def process_spawn
|
|
86
94
|
environment = service.environment.to_h
|
|
87
95
|
environment = environment.transform_keys(&:to_s).transform_values(&:to_s)
|
data/lib/nonnative/proxy.rb
CHANGED
|
@@ -6,11 +6,14 @@ module Nonnative
|
|
|
6
6
|
# A proxy is responsible for interposing behavior between a client and a target service.
|
|
7
7
|
# Runtime services create a proxy instance via {Nonnative::ProxyFactory} based on `service.proxy.kind`.
|
|
8
8
|
#
|
|
9
|
+
# Service configuration `host` and `port` are the client-facing endpoint. Concrete proxy methods are
|
|
10
|
+
# implementation-specific; for example {Nonnative::FaultInjectionProxy#host} and
|
|
11
|
+
# {Nonnative::FaultInjectionProxy#port} return the upstream target behind the proxy.
|
|
12
|
+
#
|
|
9
13
|
# Concrete proxies typically implement these public methods:
|
|
10
14
|
# - `start`: begin proxying (bind/listen, start threads, etc)
|
|
11
15
|
# - `stop`: stop proxying and release resources
|
|
12
16
|
# - `reset`: return proxy behavior to a healthy/default state
|
|
13
|
-
# - `host` / `port`: endpoint clients should connect to when the proxy is enabled
|
|
14
17
|
#
|
|
15
18
|
# @see Nonnative::ProxyFactory
|
|
16
19
|
# @see Nonnative::NoProxy
|
data/lib/nonnative/version.rb
CHANGED
data/lib/nonnative.rb
CHANGED
|
@@ -36,10 +36,10 @@
|
|
|
36
36
|
# # run tests...
|
|
37
37
|
# Nonnative.stop
|
|
38
38
|
#
|
|
39
|
-
# ==
|
|
39
|
+
# == Cucumber integration
|
|
40
40
|
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
41
|
+
# Requiring `nonnative` loads the lazy Cucumber integration. It is safe outside a booted Cucumber
|
|
42
|
+
# runtime; hooks and step definitions are installed once Cucumber's Ruby DSL is ready.
|
|
43
43
|
#
|
|
44
44
|
require 'socket'
|
|
45
45
|
require 'timeout'
|