train-core 3.5.4 → 3.7.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 42c74943ab6ac7282ce80dc0ba284dde6552262b4309015e58bbe71a9864bb8a
4
- data.tar.gz: 6aa5084e8141a4a47b5ced46793a163871ea3e2712882422d2e124daf7e8da3d
3
+ metadata.gz: 017550b023185b37f89bc36e7e1cc920079543ac11fe6d7788813b5363934ed4
4
+ data.tar.gz: bbd262ace02d7da95ae1ecce35e1c42514cf7a1fcd83f2ae7725b82b8fa65a3f
5
5
  SHA512:
6
- metadata.gz: 39c6dc8ba54238f20aa45982a3153dc6df1dc203d2e2345b4a03246463b3d1bbd8ff332c9da1fc9c5f751373d0ccdea92a42737cf384f3bf240b7c28890180f6
7
- data.tar.gz: efd9e6b920b5a800df45d907f6440829e6639eb6e2f37084a4426724708042c231e7538976be3920dff180559543aa6bb68620445b11fd9c3126470411e17e8d
6
+ metadata.gz: e7792c1fa21fb7207d23b1c283dbc0a4c989f0d1593747b5d1954b8e349ab9d5b209d5146f2af7442de1e79c519de152e376d7cf240cd1983440da0ed4ddea17
7
+ data.tar.gz: 55f506d5dbc20b38f89c5f150ffa69f4d6067a20311c891068ece4ce705ee5af01c6c740ced8fc0e52788cd9ab13c25000d47d2800ab7d023580bf18e1a147a8
@@ -34,7 +34,7 @@ module Train::Extras
34
34
 
35
35
  def self.linux_stat(shell_escaped_path, backend, follow_symlink)
36
36
  lstat = follow_symlink ? " -L" : ""
37
- format = (backend.os.esx? || backend.os[:name] == "alpine" || backend.os[:name] == "yocto") ? "-c" : "--printf"
37
+ format = (backend.os.esx? || %w{alpine yocto ubios}.include?(backend.os[:name])) ? "-c" : "--printf"
38
38
  res = backend.run_command("stat#{lstat} #{shell_escaped_path} 2>/dev/null #{format} '%s\n%f\n%U\n%u\n%G\n%g\n%X\n%Y\n%C'")
39
39
  # ignore the exit_code: it is != 0 if selinux labels are not supported
40
40
  # on the system.
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
@@ -74,6 +74,14 @@ module Train::Platforms::Detect::Specifications
74
74
  end
75
75
  end
76
76
 
77
+ declare_instance("ubios", "Ubiquiti UbiOS", "ubios") do
78
+ l_o_r = linux_os_release
79
+ if l_o_r && l_o_r["ID"] == "ubios"
80
+ @platform[:release] = l_o_r["VERSION_ID"]
81
+ true
82
+ end
83
+ end
84
+
77
85
  declare_instance("debian", "Debian Linux", "debian") do
78
86
  # if we get this far we have to be some type of debian
79
87
  @platform[:release] = unix_file_contents("/etc/debian_version").chomp
@@ -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.run_command
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.run_command
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)))
@@ -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] || timeout
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
- thr = Thread.new { session.loop }
355
-
356
- if timeout
357
- res = thr.join(timeout)
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
@@ -2,5 +2,5 @@
2
2
  # Author:: Dominik Richter (<dominik.richter@gmail.com>)
3
3
 
4
4
  module Train
5
- VERSION = "3.5.4".freeze
5
+ VERSION = "3.7.2".freeze
6
6
  end
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.5.4
4
+ version: 3.7.2
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-03-15 00:00:00.000000000 Z
11
+ date: 2021-05-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -181,7 +181,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
181
181
  requirements:
182
182
  - - ">="
183
183
  - !ruby/object:Gem::Version
184
- version: '2.4'
184
+ version: '2.5'
185
185
  required_rubygems_version: !ruby/object:Gem::Requirement
186
186
  requirements:
187
187
  - - ">="