nonnative 2.16.0 → 2.19.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/AGENTS.md +4 -1
- data/Gemfile.lock +1 -1
- data/README.md +19 -9
- data/lib/nonnative/configuration.rb +5 -4
- data/lib/nonnative/configuration_file.rb +33 -0
- data/lib/nonnative/configuration_process.rb +12 -2
- data/lib/nonnative/configuration_proxy.rb +3 -3
- data/lib/nonnative/configuration_runner.rb +3 -3
- data/lib/nonnative/go_command.rb +10 -11
- data/lib/nonnative/process.rb +3 -3
- data/lib/nonnative/version.rb +1 -1
- data/lib/nonnative.rb +5 -14
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e83e6a409c77b42f73d52473d360f136255a62f5227bb08b770d129082ccd69b
|
|
4
|
+
data.tar.gz: f2cb0c6bf4b4bcfa7a9ab95851735b987b16ab180aa4ff9278e334e473863aca
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e282a3e79b1319d47154ffac90b305ef5344eb78d9f960e30b3d6a851303f1b736b2f0456e24816a8459db9b5368f4e90d10777fd385e26da3748ac7a894f29f
|
|
7
|
+
data.tar.gz: 10b38f3e4b0d3d279add988ae32af3c60990715b1fa0c96e94efa6adad29b6f86868b243d2d000e0005815c1c80c4c28a79889a105ef0cf265555b80e4b60b39
|
data/AGENTS.md
CHANGED
|
@@ -79,6 +79,9 @@ Fault-injection states: `none`, `close_all`, `delay`, `invalid_data`.
|
|
|
79
79
|
|
|
80
80
|
Config rules:
|
|
81
81
|
|
|
82
|
+
- YAML config is loaded as data only via `Nonnative::ConfigurationFile`; ERB is not evaluated and arbitrary Ruby object tags are rejected
|
|
83
|
+
- Runner `host` and nested `proxy.host` default to `127.0.0.1`; use explicit `0.0.0.0` only when external access is intended
|
|
84
|
+
- Process `command` can be a legacy shell string or an argv array; prefer argv arrays for new config, and `go:` config builds argv internally
|
|
82
85
|
- YAML services belong under `services:`, not `processes:`
|
|
83
86
|
- There is no top-level `config.wait`; `wait` is per runner
|
|
84
87
|
- Programmatic service config uses `config.service do |s| ... end`, so use `s.host` / `s.port`
|
|
@@ -107,4 +110,4 @@ Limitations:
|
|
|
107
110
|
- Process lifecycle: `lib/nonnative/process.rb`
|
|
108
111
|
- Proxies: `lib/nonnative/fault_injection_proxy.rb`, `lib/nonnative/socket_pair_factory.rb`
|
|
109
112
|
- Cucumber: `lib/nonnative/cucumber.rb`, `lib/nonnative/startup.rb`, `features/support/env.rb`
|
|
110
|
-
- Config loading: `lib/nonnative/configuration.rb`, `lib/nonnative/configuration_runner.rb`, `lib/nonnative/configuration_proxy.rb`
|
|
113
|
+
- Config loading: `lib/nonnative/configuration.rb`, `lib/nonnative/configuration_file.rb`, `lib/nonnative/configuration_runner.rb`, `lib/nonnative/configuration_proxy.rb`
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -38,6 +38,8 @@ gem install nonnative
|
|
|
38
38
|
## Usage
|
|
39
39
|
|
|
40
40
|
Nonnative is configured via {#Nonnative.configure} (programmatic) or `config.load_file(...)` (YAML).
|
|
41
|
+
YAML configuration is loaded as data only: ERB is not evaluated and arbitrary Ruby objects are not
|
|
42
|
+
deserialized.
|
|
41
43
|
|
|
42
44
|
High-level configuration fields:
|
|
43
45
|
- `version`: configuration version (example: `"1.0"`).
|
|
@@ -51,10 +53,10 @@ High-level configuration fields:
|
|
|
51
53
|
Runner fields (process/server/service):
|
|
52
54
|
- `timeout`: max time (seconds) for readiness/shutdown port checks.
|
|
53
55
|
- `wait`: small sleep (seconds) between lifecycle steps.
|
|
54
|
-
- `host`/`port`: client-facing address used for readiness/shutdown port checks. When a `fault_injection` proxy is enabled, this is the endpoint your tests/clients should hit.
|
|
56
|
+
- `host`/`port`: client-facing address used for readiness/shutdown port checks. `host` defaults to `127.0.0.1`. When a `fault_injection` proxy is enabled, this is the endpoint your tests/clients should hit.
|
|
55
57
|
- `log`: per-runner log file (used by process output redirection or server implementations).
|
|
56
58
|
|
|
57
|
-
For `fault_injection`, the nested `proxy.host`/`proxy.port` describe the upstream target behind the proxy. In-process server implementations typically bind there via `proxy.host` / `proxy.port`.
|
|
59
|
+
For `fault_injection`, the nested `proxy.host`/`proxy.port` describe the upstream target behind the proxy. Nested `proxy.host` also defaults to `127.0.0.1`. In-process server implementations typically bind there via `proxy.host` / `proxy.port`.
|
|
58
60
|
|
|
59
61
|
Nonnative readiness and shutdown checks are TCP-only. Configure ports that are dedicated to the test run; if another process is already listening on the same `host`/`port`, results are undefined.
|
|
60
62
|
|
|
@@ -90,6 +92,8 @@ This calls `Nonnative.start` immediately and registers an `at_exit` stop.
|
|
|
90
92
|
### Processes
|
|
91
93
|
|
|
92
94
|
A process is some sort of command that you would run locally.
|
|
95
|
+
Commands can be strings or argv arrays. String commands preserve legacy shell semantics, while argv arrays
|
|
96
|
+
avoid shell interpretation and are preferred for new configuration.
|
|
93
97
|
|
|
94
98
|
Setup it up programmatically:
|
|
95
99
|
|
|
@@ -104,7 +108,7 @@ Nonnative.configure do |config|
|
|
|
104
108
|
|
|
105
109
|
config.process do |p|
|
|
106
110
|
p.name = 'start_1'
|
|
107
|
-
p.command = -> { 'features/support/bin/start 12_321' }
|
|
111
|
+
p.command = -> { ['features/support/bin/start', '12_321'] }
|
|
108
112
|
p.timeout = 5
|
|
109
113
|
p.wait = 0.1
|
|
110
114
|
p.port = 12_321
|
|
@@ -136,7 +140,9 @@ log: nonnative.log
|
|
|
136
140
|
processes:
|
|
137
141
|
-
|
|
138
142
|
name: start_1
|
|
139
|
-
command:
|
|
143
|
+
command:
|
|
144
|
+
- features/support/bin/start
|
|
145
|
+
- "12_321"
|
|
140
146
|
timeout: 5
|
|
141
147
|
wait: 1
|
|
142
148
|
port: 12321
|
|
@@ -762,7 +768,7 @@ Then I should reset the proxy for service 'service_1'
|
|
|
762
768
|
|
|
763
769
|
### Go
|
|
764
770
|
|
|
765
|
-
As we love using go as a language for services we have added support to start binaries with defined parameters. This expects that you build your services in the format of `command sub_command --params`
|
|
771
|
+
As we love using go as a language for services we have added support to start binaries with defined parameters. This expects that you build your services in the format of `command sub_command --params`. YAML `go:` configuration is executed as argv entries, without shell interpretation.
|
|
766
772
|
|
|
767
773
|
To get this to work you will need to create a `main_test.go` file with these contents:
|
|
768
774
|
|
|
@@ -784,12 +790,16 @@ Then to compile this binary you will need to do the following:
|
|
|
784
790
|
go test -mod vendor -c -tags features -covermode=count -o your_binary -coverpkg=./... github.com/your_location
|
|
785
791
|
```
|
|
786
792
|
|
|
787
|
-
Setup it up programmatically:
|
|
793
|
+
Setup it up programmatically as an argv process command:
|
|
788
794
|
|
|
789
795
|
```ruby
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
796
|
+
Nonnative.configure do |config|
|
|
797
|
+
config.process do |p|
|
|
798
|
+
p.name = 'go'
|
|
799
|
+
p.command = -> { ['your_binary', 'sub_command', '--config', 'config.yaml'] }
|
|
800
|
+
p.port = 12_345
|
|
801
|
+
end
|
|
802
|
+
end
|
|
793
803
|
```
|
|
794
804
|
|
|
795
805
|
Setup it up through configuration:
|
|
@@ -52,13 +52,14 @@ module Nonnative
|
|
|
52
52
|
|
|
53
53
|
# Loads a configuration file and appends its runners to this instance.
|
|
54
54
|
#
|
|
55
|
-
# The file is loaded using
|
|
56
|
-
#
|
|
55
|
+
# The file is loaded using safe YAML parsing. ERB is not evaluated,
|
|
56
|
+
# arbitrary object deserialization is not allowed, top-level attributes are copied onto this object,
|
|
57
|
+
# and runner sections are transformed into configuration runner objects.
|
|
57
58
|
#
|
|
58
59
|
# @param path [String] path to a configuration file (typically YAML)
|
|
59
60
|
# @return [void]
|
|
60
61
|
def load_file(path)
|
|
61
|
-
cfg = Nonnative.
|
|
62
|
+
cfg = Nonnative::ConfigurationFile.load(path)
|
|
62
63
|
|
|
63
64
|
self.version = cfg.version
|
|
64
65
|
self.name = cfg.name
|
|
@@ -140,7 +141,7 @@ module Nonnative
|
|
|
140
141
|
params = go.parameters || []
|
|
141
142
|
tools = go.tools || []
|
|
142
143
|
|
|
143
|
-
-> { Nonnative.
|
|
144
|
+
-> { Nonnative.go_executable_args(tools, go.output, go.executable, go.command, *params) }
|
|
144
145
|
else
|
|
145
146
|
-> { process.command }
|
|
146
147
|
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Nonnative
|
|
4
|
+
# Safely loads a YAML configuration file into the Config::Options shape used by Nonnative.
|
|
5
|
+
class ConfigurationFile
|
|
6
|
+
class << self
|
|
7
|
+
# Loads a file into a Config::Options instance.
|
|
8
|
+
#
|
|
9
|
+
# YAML files are parsed as data only: ERB is not evaluated and arbitrary object deserialization is not allowed.
|
|
10
|
+
#
|
|
11
|
+
# @param path [String] file path
|
|
12
|
+
# @return [Config::Options]
|
|
13
|
+
def load(path)
|
|
14
|
+
Config::Options.new.tap do |config|
|
|
15
|
+
config.add_source!(safe_load_yaml(path))
|
|
16
|
+
config.load!
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def safe_load_yaml(path)
|
|
23
|
+
contents = File.read(path)
|
|
24
|
+
config = YAML.safe_load(contents, aliases: true) || {}
|
|
25
|
+
return config if config.is_a?(Hash)
|
|
26
|
+
|
|
27
|
+
raise ArgumentError, "Configuration file '#{path}' must contain a YAML mapping"
|
|
28
|
+
rescue Psych::SyntaxError => e
|
|
29
|
+
raise ArgumentError, "YAML syntax error occurred while parsing #{path}: #{e.message}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -11,11 +11,21 @@ module Nonnative
|
|
|
11
11
|
# @see Nonnative::Configuration
|
|
12
12
|
# @see Nonnative::Process
|
|
13
13
|
class ConfigurationProcess < ConfigurationRunner
|
|
14
|
-
# @return [Proc] a callable that returns the command
|
|
14
|
+
# @return [Proc] a callable that returns the command to execute
|
|
15
|
+
# as a shell string or argv array
|
|
16
|
+
# (e.g. `-> { "./bin/api" }` or `-> { ["./bin/api", "--port", "8080"] }`)
|
|
17
|
+
attr_accessor :command
|
|
18
|
+
|
|
15
19
|
# @return [String, nil] signal name to use for stopping (defaults to `"INT"` when not set)
|
|
20
|
+
attr_accessor :signal
|
|
21
|
+
|
|
16
22
|
# @return [Numeric] readiness timeout (seconds) used when waiting for the port to open/close
|
|
23
|
+
attr_accessor :timeout
|
|
24
|
+
|
|
17
25
|
# @return [String] log file path to append process stdout/stderr to
|
|
26
|
+
attr_accessor :log
|
|
27
|
+
|
|
18
28
|
# @return [Hash, nil] environment variables to pass to the spawned process
|
|
19
|
-
attr_accessor :
|
|
29
|
+
attr_accessor :environment
|
|
20
30
|
end
|
|
21
31
|
end
|
|
@@ -15,7 +15,7 @@ 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
|
-
# @return [String] upstream host used by proxy implementations (defaults to `"
|
|
18
|
+
# @return [String] upstream host used by proxy implementations (defaults to `"127.0.0.1"`)
|
|
19
19
|
# @return [Integer] upstream port used by proxy implementations (defaults to `0`)
|
|
20
20
|
# @return [String, nil] path to proxy log file (implementation-dependent)
|
|
21
21
|
# @return [Numeric] wait interval (seconds) after proxy state changes (defaults to `0.1`)
|
|
@@ -27,7 +27,7 @@ module Nonnative
|
|
|
27
27
|
#
|
|
28
28
|
# Defaults:
|
|
29
29
|
# - `kind`: `"none"`
|
|
30
|
-
# - `host`: `"
|
|
30
|
+
# - `host`: `"127.0.0.1"`
|
|
31
31
|
# - `port`: `0`
|
|
32
32
|
# - `wait`: `0.1`
|
|
33
33
|
# - `options`: `{}`
|
|
@@ -35,7 +35,7 @@ module Nonnative
|
|
|
35
35
|
# @return [void]
|
|
36
36
|
def initialize
|
|
37
37
|
self.kind = 'none'
|
|
38
|
-
self.host = '
|
|
38
|
+
self.host = '127.0.0.1'
|
|
39
39
|
self.port = 0
|
|
40
40
|
self.wait = 0.1
|
|
41
41
|
self.options = {}
|
|
@@ -14,7 +14,7 @@ module Nonnative
|
|
|
14
14
|
# @see Nonnative::ConfigurationService
|
|
15
15
|
class ConfigurationRunner
|
|
16
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 `"
|
|
17
|
+
# @return [String] host to bind/connect to (defaults to `"127.0.0.1"`)
|
|
18
18
|
# @return [Integer] port to bind/connect to
|
|
19
19
|
# @return [Numeric] wait interval (seconds) used by runners between lifecycle steps
|
|
20
20
|
attr_accessor :name, :host, :port, :wait
|
|
@@ -30,14 +30,14 @@ module Nonnative
|
|
|
30
30
|
# Creates a runner configuration with defaults.
|
|
31
31
|
#
|
|
32
32
|
# Defaults:
|
|
33
|
-
# - `host`: `"
|
|
33
|
+
# - `host`: `"127.0.0.1"`
|
|
34
34
|
# - `port`: `0`
|
|
35
35
|
# - `wait`: `0.1`
|
|
36
36
|
# - `proxy`: a new {Nonnative::ConfigurationProxy} with its own defaults
|
|
37
37
|
#
|
|
38
38
|
# @return [void]
|
|
39
39
|
def initialize
|
|
40
|
-
self.host = '
|
|
40
|
+
self.host = '127.0.0.1'
|
|
41
41
|
self.port = 0
|
|
42
42
|
self.wait = 0.1
|
|
43
43
|
|
data/lib/nonnative/go_command.rb
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Nonnative
|
|
4
|
-
# Builds
|
|
4
|
+
# Builds commands for running a Go test binary with optional profiling/trace/coverage flags.
|
|
5
5
|
#
|
|
6
|
-
# This helper is used by
|
|
7
|
-
#
|
|
6
|
+
# This helper is used by YAML configuration when a process has a `go:` section
|
|
7
|
+
# (see {Nonnative::Configuration}).
|
|
8
8
|
#
|
|
9
9
|
# The generated flags use Go's `testing` package flags (e.g. `-test.cpuprofile=...`), so this
|
|
10
10
|
# is intended to run a binary compiled from `go test -c`.
|
|
@@ -21,10 +21,10 @@ module Nonnative
|
|
|
21
21
|
#
|
|
22
22
|
# @example
|
|
23
23
|
# cmd = Nonnative::GoCommand.new(%w[prof cover], './svc.test', 'reports')
|
|
24
|
-
# cmd.
|
|
25
|
-
# # => "./svc.test -test.cpuprofile=... -test.coverprofile=... serve --config config.yaml"
|
|
24
|
+
# cmd.executable_args('serve', '--config', 'config.yaml')
|
|
25
|
+
# # => ["./svc.test", "-test.cpuprofile=...", "-test.coverprofile=...", "serve", "--config", "config.yaml"]
|
|
26
26
|
#
|
|
27
|
-
# @see Nonnative.
|
|
27
|
+
# @see Nonnative.go_executable_args
|
|
28
28
|
class GoCommand
|
|
29
29
|
# @param tools [Array<String>, nil] tool names to enable (see class docs)
|
|
30
30
|
# @param exec [String] path to the compiled Go test binary
|
|
@@ -35,16 +35,15 @@ module Nonnative
|
|
|
35
35
|
@output = output
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
# Returns an executable
|
|
38
|
+
# Returns an executable argv array including enabled `-test.*` flags.
|
|
39
39
|
#
|
|
40
40
|
# A short random suffix is appended to output filenames to reduce collisions across runs.
|
|
41
41
|
#
|
|
42
42
|
# @param cmd [String] command/sub-command argument passed to the Go test binary
|
|
43
43
|
# @param params [Array<String>] additional parameters passed after `cmd`
|
|
44
|
-
# @return [String]
|
|
45
|
-
def
|
|
46
|
-
|
|
47
|
-
"#{exec} #{flags(cmd).join(' ')} #{cmd} #{params}".strip
|
|
44
|
+
# @return [Array<String>] argv entries to execute
|
|
45
|
+
def executable_args(cmd, *params)
|
|
46
|
+
[exec, *flags(cmd), cmd, *params.flatten.compact.map(&:to_s)]
|
|
48
47
|
end
|
|
49
48
|
|
|
50
49
|
private
|
data/lib/nonnative/process.rb
CHANGED
|
@@ -89,10 +89,10 @@ module Nonnative
|
|
|
89
89
|
environment[k] = ENV.fetch(k, nil) || environment[k]
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
-
command = service.command.call
|
|
92
|
+
command = Array(service.command.call)
|
|
93
93
|
|
|
94
|
-
spawn(environment, command, %i[out err] => [service.log, 'a']).tap do |pid|
|
|
95
|
-
Nonnative.logger.info "started '#{command}' with pid '#{pid}'"
|
|
94
|
+
spawn(environment, *command, %i[out err] => [service.log, 'a']).tap do |pid|
|
|
95
|
+
Nonnative.logger.info "started '#{command.join(' ')}' with pid '#{pid}'"
|
|
96
96
|
end
|
|
97
97
|
end
|
|
98
98
|
|
data/lib/nonnative/version.rb
CHANGED
data/lib/nonnative.rb
CHANGED
|
@@ -67,6 +67,7 @@ require 'nonnative/stop_error'
|
|
|
67
67
|
require 'nonnative/not_found_error'
|
|
68
68
|
require 'nonnative/timeout'
|
|
69
69
|
require 'nonnative/port'
|
|
70
|
+
require 'nonnative/configuration_file'
|
|
70
71
|
require 'nonnative/configuration'
|
|
71
72
|
require 'nonnative/configuration_runner'
|
|
72
73
|
require 'nonnative/configuration_process'
|
|
@@ -113,16 +114,6 @@ module Nonnative
|
|
|
113
114
|
# @return [Nonnative::Pool, nil] the pool instance, or `nil` if not started yet
|
|
114
115
|
attr_accessor :pool
|
|
115
116
|
|
|
116
|
-
# Loads one or more configuration files using the `config` gem.
|
|
117
|
-
#
|
|
118
|
-
# This is primarily used by {Nonnative::Configuration#load_file}, but is public for advanced cases.
|
|
119
|
-
#
|
|
120
|
-
# @param files [Array<String>] paths to configuration files
|
|
121
|
-
# @return [Config::Options] the loaded configuration object
|
|
122
|
-
def configurations(*files)
|
|
123
|
-
Config.load_files(files)
|
|
124
|
-
end
|
|
125
|
-
|
|
126
117
|
# Returns the current configuration (memoized).
|
|
127
118
|
#
|
|
128
119
|
# @return [Nonnative::Configuration]
|
|
@@ -162,7 +153,7 @@ module Nonnative
|
|
|
162
153
|
File.readlines(path).select { |l| predicate.call(l) }
|
|
163
154
|
end
|
|
164
155
|
|
|
165
|
-
# Builds a Go test executable
|
|
156
|
+
# Builds a Go test executable argv array with optional profiling/trace/coverage flags.
|
|
166
157
|
#
|
|
167
158
|
# This is used when process configuration specifies a `go` section.
|
|
168
159
|
#
|
|
@@ -171,9 +162,9 @@ module Nonnative
|
|
|
171
162
|
# @param exec [String] the test binary (or wrapper) to execute
|
|
172
163
|
# @param cmd [String] the command argument passed to the test binary
|
|
173
164
|
# @param params [Array<String>] extra parameters for the command
|
|
174
|
-
# @return [String] executable
|
|
175
|
-
def
|
|
176
|
-
Nonnative::GoCommand.new(tools, exec, output).
|
|
165
|
+
# @return [Array<String>] executable argv entries
|
|
166
|
+
def go_executable_args(tools, output, exec, cmd, *params)
|
|
167
|
+
Nonnative::GoCommand.new(tools, exec, output).executable_args(cmd, *params)
|
|
177
168
|
end
|
|
178
169
|
|
|
179
170
|
# Returns an HTTP client for common health/readiness endpoints.
|
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: 2.
|
|
4
|
+
version: 2.19.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alejandro Falkowski
|
|
@@ -288,6 +288,7 @@ files:
|
|
|
288
288
|
- lib/nonnative.rb
|
|
289
289
|
- lib/nonnative/close_all_socket_pair.rb
|
|
290
290
|
- lib/nonnative/configuration.rb
|
|
291
|
+
- lib/nonnative/configuration_file.rb
|
|
291
292
|
- lib/nonnative/configuration_process.rb
|
|
292
293
|
- lib/nonnative/configuration_proxy.rb
|
|
293
294
|
- lib/nonnative/configuration_runner.rb
|