train-core 3.5.2 → 3.7.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/lib/train/options.rb +3 -0
- data/lib/train/transports/local.rb +18 -7
- data/lib/train/transports/ssh.rb +11 -2
- data/lib/train/transports/ssh_connection.rb +28 -12
- data/lib/train/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7e0529be878892640599f401af47c6c012a8d060fea3113895872b0af7e24127
|
4
|
+
data.tar.gz: 4b387bc5129fbf652b37713a63d74173d8d04f3ea5dbdf9993cadf0d276f3add
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7fdf259b32a3dc0f033681b2f98e164f2ef76c8b4e7f51fb5bc1fe2412a1501039cdddfc06ef409f7b235dbcefef693f97c7fff21c02d7011618fdfc5ad09a9d
|
7
|
+
data.tar.gz: 2f90638d94542daf5e70488f6fd5e592986d3a76ac6b10f188414a109b53fffc8000202ce0a853f68bc1a8c1dfee9178107da5fee2b51728a01b1863b3976baf
|
data/lib/train/options.rb
CHANGED
@@ -59,6 +59,9 @@ module Train
|
|
59
59
|
default = hm[:default]
|
60
60
|
if default.is_a? Proc
|
61
61
|
res[field] = default.call(res)
|
62
|
+
elsif hm.key?(:coerce)
|
63
|
+
field_value = hm[:coerce].call(res)
|
64
|
+
res[field] = field_value.nil? ? default : field_value
|
62
65
|
else
|
63
66
|
res[field] = default
|
64
67
|
end
|
@@ -86,9 +86,9 @@ module Train::Transports
|
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
-
def run_command_via_connection(cmd, &_data_handler)
|
89
|
+
def run_command_via_connection(cmd, opts, &_data_handler)
|
90
90
|
# Use the runner if it is available
|
91
|
-
return @runner.run_command(cmd) if defined?(@runner)
|
91
|
+
return @runner.run_command(cmd, opts) if defined?(@runner)
|
92
92
|
|
93
93
|
# If we don't have a runner, such as at the beginning of setting up the
|
94
94
|
# transport and performing the first few steps of OS detection, fall
|
@@ -115,13 +115,18 @@ module Train::Transports
|
|
115
115
|
@cmd_wrapper = Local::CommandWrapper.load(connection, options)
|
116
116
|
end
|
117
117
|
|
118
|
-
def run_command(cmd)
|
118
|
+
def run_command(cmd, opts = {})
|
119
119
|
if defined?(@cmd_wrapper) && !@cmd_wrapper.nil?
|
120
120
|
cmd = @cmd_wrapper.run(cmd)
|
121
121
|
end
|
122
122
|
|
123
123
|
res = Mixlib::ShellOut.new(cmd)
|
124
|
-
res.
|
124
|
+
res.timeout = opts[:timeout]
|
125
|
+
begin
|
126
|
+
res.run_command
|
127
|
+
rescue Mixlib::ShellOut::CommandTimeout
|
128
|
+
raise Train::CommandTimeoutReached
|
129
|
+
end
|
125
130
|
Local::CommandResult.new(res.stdout, res.stderr, res.exitstatus)
|
126
131
|
end
|
127
132
|
|
@@ -138,7 +143,7 @@ module Train::Transports
|
|
138
143
|
@powershell_cmd = powershell_cmd
|
139
144
|
end
|
140
145
|
|
141
|
-
def run_command(script)
|
146
|
+
def run_command(script, opts)
|
142
147
|
# Prevent progress stream from leaking into stderr
|
143
148
|
script = "$ProgressPreference='SilentlyContinue';" + script
|
144
149
|
|
@@ -149,7 +154,12 @@ module Train::Transports
|
|
149
154
|
cmd = "#{@powershell_cmd} -NoProfile -EncodedCommand #{base64_script}"
|
150
155
|
|
151
156
|
res = Mixlib::ShellOut.new(cmd)
|
152
|
-
res.
|
157
|
+
res.timeout = opts[:timeout]
|
158
|
+
begin
|
159
|
+
res.run_command
|
160
|
+
rescue Mixlib::ShellOut::CommandTimeout
|
161
|
+
raise Train::CommandTimeoutReached
|
162
|
+
end
|
153
163
|
Local::CommandResult.new(res.stdout, res.stderr, res.exitstatus)
|
154
164
|
end
|
155
165
|
|
@@ -176,9 +186,10 @@ module Train::Transports
|
|
176
186
|
# A command that succeeds without setting an exit code will have exitstatus 0
|
177
187
|
# A command that exits with an exit code will have that value as exitstatus
|
178
188
|
# A command that fails (e.g. throws exception) before setting an exit code will have exitstatus 1
|
179
|
-
def run_command(cmd)
|
189
|
+
def run_command(cmd, _opts)
|
180
190
|
script = "$ProgressPreference='SilentlyContinue';" + cmd
|
181
191
|
encoded_script = Base64.strict_encode64(script)
|
192
|
+
# TODO: no way to safely implement timeouts here.
|
182
193
|
@pipe.puts(encoded_script)
|
183
194
|
@pipe.flush
|
184
195
|
res = OpenStruct.new(JSON.parse(Base64.decode64(@pipe.readline)))
|
data/lib/train/transports/ssh.rb
CHANGED
@@ -43,8 +43,8 @@ module Train::Transports
|
|
43
43
|
|
44
44
|
# common target configuration
|
45
45
|
option :host, required: true
|
46
|
-
option :port, default: 22, required: true
|
47
|
-
option :user, default: "root", required: true
|
46
|
+
option :port, default: 22, coerce: proc { |u| read_options_from_ssh_config(u, :port) }, required: true
|
47
|
+
option :user, default: "root", coerce: proc { |u| read_options_from_ssh_config(u, :user) }, required: true
|
48
48
|
option :key_files, default: nil
|
49
49
|
option :password, default: nil
|
50
50
|
|
@@ -86,6 +86,14 @@ module Train::Transports
|
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
+
# Returns the ssh config option like user, port from config files
|
90
|
+
# Params options [Hash], option_type [String]
|
91
|
+
# Return String
|
92
|
+
def self.read_options_from_ssh_config(options, option_type)
|
93
|
+
config_options = Net::SSH.configuration_for(options[:host], true)
|
94
|
+
config_options[option_type]
|
95
|
+
end
|
96
|
+
|
89
97
|
private
|
90
98
|
|
91
99
|
def reusable_connection?(conn_opts)
|
@@ -278,5 +286,6 @@ module Train::Transports
|
|
278
286
|
yield @connection if block_given?
|
279
287
|
@connection
|
280
288
|
end
|
289
|
+
|
281
290
|
end
|
282
291
|
end
|
@@ -32,6 +32,10 @@ class Train::Transports::SSH
|
|
32
32
|
attr_reader :hostname
|
33
33
|
attr_accessor :transport_options
|
34
34
|
|
35
|
+
# If we use the GNU timeout utility to timout a command server-side, it will
|
36
|
+
# exit with this status code if the command timed out.
|
37
|
+
GNU_TIMEOUT_EXIT_STATUS = 124
|
38
|
+
|
35
39
|
def initialize(options)
|
36
40
|
# Track IOS command retries to prevent infinite loop on IOError. This must
|
37
41
|
# be done before `super()` because the parent runs detection commands.
|
@@ -321,9 +325,12 @@ class Train::Transports::SSH
|
|
321
325
|
# wrap commands if that is configured
|
322
326
|
cmd = @cmd_wrapper.run(cmd) if @cmd_wrapper
|
323
327
|
|
328
|
+
# Timeout the command if requested and able
|
329
|
+
cmd = "timeout #{timeout}s #{cmd}" if timeout && timeoutable?(cmd)
|
330
|
+
|
324
331
|
logger.debug("[SSH] #{self} cmd = #{cmd}")
|
325
332
|
|
326
|
-
if @transport_options[:pty]
|
333
|
+
if @transport_options[:pty]
|
327
334
|
channel.request_pty do |_ch, success|
|
328
335
|
raise Train::Transports::SSHPTYFailed, "Requesting PTY failed" unless success
|
329
336
|
end
|
@@ -350,21 +357,30 @@ class Train::Transports::SSH
|
|
350
357
|
end
|
351
358
|
end
|
352
359
|
end
|
360
|
+
session.loop
|
353
361
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
unless res
|
359
|
-
logger.debug("train ssh command '#{cmd}' reached requested timeout (#{timeout}s)")
|
360
|
-
session.channels.each_value { |c| c.eof!; c.close }
|
361
|
-
raise Train::CommandTimeoutReached.new "ssh command reached timeout (#{timeout}s)"
|
362
|
-
end
|
363
|
-
else
|
364
|
-
thr.join
|
362
|
+
if timeout && timeoutable?(cmd) && exit_status == GNU_TIMEOUT_EXIT_STATUS
|
363
|
+
logger.debug("train ssh command '#{cmd}' reached requested timeout (#{timeout}s)")
|
364
|
+
session.channels.each_value { |c| c.eof!; c.close }
|
365
|
+
raise Train::CommandTimeoutReached.new "ssh command reached timeout (#{timeout}s)"
|
365
366
|
end
|
366
367
|
|
367
368
|
[exit_status, stdout, stderr]
|
368
369
|
end
|
370
|
+
|
371
|
+
# Returns true if we think we can attempt to timeout the command
|
372
|
+
def timeoutable?(cmd)
|
373
|
+
have_timeout_cli? && !cmd.include?("|") # Don't try to timeout a command that has pipes
|
374
|
+
end
|
375
|
+
|
376
|
+
# Returns true if the GNU timeout command is available
|
377
|
+
def have_timeout_cli?
|
378
|
+
return @have_timeout_cli unless @have_timeout_cli.nil?
|
379
|
+
|
380
|
+
res = session.exec!("timeout --version")
|
381
|
+
@have_timeout_cli = res.exitstatus == 0
|
382
|
+
logger.debug("train ssh have_timeout_cli status is '#{@have_timeout_cli}'")
|
383
|
+
@have_timeout_cli
|
384
|
+
end
|
369
385
|
end
|
370
386
|
end
|
data/lib/train/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: train-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chef InSpec Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-04-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|