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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9b79bc3b75303cd4d605a4ef81c7b3c1eedd188204d088864fefb65dc73f0922
4
- data.tar.gz: c8e4b5ef1bb12213e80d7084d5b3809206279a44bca610947114fd41dd188745
3
+ metadata.gz: e83e6a409c77b42f73d52473d360f136255a62f5227bb08b770d129082ccd69b
4
+ data.tar.gz: f2cb0c6bf4b4bcfa7a9ab95851735b987b16ab180aa4ff9278e334e473863aca
5
5
  SHA512:
6
- metadata.gz: a58c635d40b6eeb8b6aa7e9560900bad247fe768f9839b7e141f6be4e500b5fa2ec11267524621db66989f6eaf24601b790716a4508e28203e4845862ad5a289
7
- data.tar.gz: 111995ab26c566d24df0fb037c094883ac07adf2142254fde6822fb9e4fcea59c855196c650bac382e5ab02fe2610181611040a96f3d13515aef586495ffaf72
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nonnative (2.16.0)
4
+ nonnative (2.19.0)
5
5
  concurrent-ruby (>= 1, < 2)
6
6
  config (>= 5, < 6)
7
7
  cucumber (>= 7, < 12)
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: features/support/bin/start 12_321
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
- tools = %w[prof trace cover]
791
-
792
- Nonnative.go_executable(tools, 'reports', 'your_binary', 'sub_command', '--config config.yaml')
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 the `config` gem via {Nonnative.configurations}. Top-level attributes are
56
- # copied onto this object, and runner sections are transformed into configuration runner objects.
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.configurations(path)
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.go_executable(tools, go.output, go.executable, go.command, *params) }
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 string to execute (e.g. `-> { "./bin/api" }`)
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 :command, :signal, :timeout, :log, :environment
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 `"0.0.0.0"`)
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`: `"0.0.0.0"`
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 = '0.0.0.0'
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 `"0.0.0.0"`)
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`: `"0.0.0.0"`
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 = '0.0.0.0'
40
+ self.host = '127.0.0.1'
41
41
  self.port = 0
42
42
  self.wait = 0.1
43
43
 
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nonnative
4
- # Builds command lines for running a Go test binary with optional profiling/trace/coverage flags.
4
+ # Builds commands for running a Go test binary with optional profiling/trace/coverage flags.
5
5
  #
6
- # This helper is used by {Nonnative.go_executable} and by YAML configuration when a process has a
7
- # `go:` section (see {Nonnative::Configuration}).
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.executable('serve', '--config', 'config.yaml')
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.go_executable
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 command string including enabled `-test.*` flags.
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] the full command to execute
45
- def executable(cmd, *params)
46
- params = params.join(' ')
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
@@ -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
 
@@ -4,5 +4,5 @@ module Nonnative
4
4
  # The current gem version.
5
5
  #
6
6
  # @return [String]
7
- VERSION = '2.16.0'
7
+ VERSION = '2.19.0'
8
8
  end
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 command line with optional profiling/trace/coverage flags.
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 command string
175
- def go_executable(tools, output, exec, cmd, *params)
176
- Nonnative::GoCommand.new(tools, exec, output).executable(cmd, params)
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.16.0
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