train-core 3.3.21 → 3.4.7

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: c4bfda9949a3f24a3c7d48f8bb23384c73f064eccfa0f44029872e13913880c9
4
- data.tar.gz: 6598c63e265c79e1344010ace35b40d1f5bd1919bb8039d4ae868c54f9f85dc5
3
+ metadata.gz: e85ed8cc37da145f7dabcff7855b85b9c2a6bc8f216945aeaa693843c07f8b95
4
+ data.tar.gz: a4ac06f8b1c2ced955d933ba1a9553d9434c9d255d11ec5d8cc43e14e1cc0469
5
5
  SHA512:
6
- metadata.gz: aeb75022b5ff8c0f93ce22b35a6d8af8b31c8f3906fe357e502cd6f7ab8e7cb0bf55df61c8e891579b5338f875413b6c1372535e5af30d3011d54fd8b7822968
7
- data.tar.gz: 3c23b61031fe8c0e8d691929ccb9007a8272d1de9736b8c5bef1ffcfd8a3078b31f556454f6676cc9d9a2515333015852f64f8e5ce47d2313e393eb4b230f9f7
6
+ metadata.gz: 0b920c8d01dbdd94eae308fcca501cf9dc5f24619f8e023e4c5cedbdd11eebcfc4b4bcb41683435325f3ede4afa5b6a7981164e1cf8242aa472e2e801282c284
7
+ data.tar.gz: e3d5e835b15d0cc84f86066fe66aafc7456544f53fbe91d2a66c7341ed4db3b2f6a3d890e98aab26d0fe294af587fc246a89a10c7d56b9f4ac1cf09a99fae9d8
@@ -113,6 +113,9 @@ module Train
113
113
  # TODO: rewrite next line using compact! once we drop support for ruby 2.3
114
114
  creds = creds.delete_if { |_, value| value.nil? }
115
115
 
116
+ # merge train options in from the URI query string
117
+ creds.merge!(uri.query_values.map { |k, v| [k.to_sym, v] }.to_h) unless uri.query_values.nil?
118
+
116
119
  # return the updated config
117
120
  creds
118
121
  end
@@ -40,4 +40,7 @@ module Train
40
40
 
41
41
  # Exception for when a invalid cache type is passed.
42
42
  class UnknownCacheType < Error; end
43
+
44
+ # Exception for when a command reaches configured timeout
45
+ class CommandTimeoutReached < Error; end
43
46
  end
@@ -1,7 +1,7 @@
1
1
  # author: Dominik Richter
2
2
  # author: Christoph Hartmann
3
3
 
4
- require "base64"
4
+ require "base64" unless defined?(Base64)
5
5
  require_relative "../errors"
6
6
 
7
7
  module Train::Extras
@@ -33,15 +33,20 @@ module Train::Platforms::Detect::Helpers
33
33
  command = @backend.run_command(
34
34
  "Get-WmiObject Win32_OperatingSystem | Select Caption,Version | ConvertTo-Json"
35
35
  )
36
- return false if (command.exit_status != 0) || command.stdout.empty?
37
-
38
- payload = JSON.parse(command.stdout)
39
- @platform[:family] = "windows"
40
- @platform[:release] = payload["Version"]
41
- @platform[:name] = payload["Caption"]
42
-
43
- read_wmic
44
- true
36
+ # some targets (e.g. Cisco) may return 0 and print an error to stdout
37
+ return false if (command.exit_status != 0) || command.stdout.downcase !~ /window/
38
+
39
+ begin
40
+ payload = JSON.parse(command.stdout)
41
+ @platform[:family] = "windows"
42
+ @platform[:release] = payload["Version"]
43
+ @platform[:name] = payload["Caption"]
44
+
45
+ read_wmic
46
+ true
47
+ rescue
48
+ false
49
+ end
45
50
  end
46
51
 
47
52
  def local_windows?
@@ -126,7 +131,7 @@ module Train::Platforms::Detect::Helpers
126
131
  return if !file.exist? || file.size == 0
127
132
 
128
133
  json = JSON.parse(file.content)
129
- json["node_uuid"] if json["node_uuid"]
134
+ json["node_uuid"]
130
135
  end
131
136
 
132
137
  def windows_uuid_from_wmic
@@ -1,4 +1,4 @@
1
- require "digest/sha1"
1
+ require "digest/sha1" unless defined?(Digest::SHA1)
2
2
  require "securerandom" unless defined?(SecureRandom)
3
3
  require "json" unless defined?(JSON)
4
4
 
@@ -130,10 +130,27 @@ class Train::Plugins::Transport
130
130
  # This command accepts an optional data handler block. When provided,
131
131
  # inbound data will be published vi `data_handler.call(data)`. This can allow
132
132
  # callers to receive and render updates from remote command execution.
133
- def run_command(cmd, &data_handler)
134
- return run_command_via_connection(cmd, &data_handler) unless cache_enabled?(:command)
135
-
136
- @cache[:command][cmd] ||= run_command_via_connection(cmd, &data_handler)
133
+ #
134
+ # @param [String] the command to run
135
+ # @param [Hash] optional hash of options for this command. The derived connection
136
+ # class's implementation of run_command_via_connection should receive
137
+ # and apply these options.
138
+ def run_command(cmd, opts = {}, &data_handler)
139
+ # Some implementations do not accept an opts argument.
140
+ # We cannot update all implementations to accept opts due to them being separate plugins.
141
+ # Therefore here we check the implementation's arity to maintain compatibility.
142
+ case method(:run_command_via_connection).arity.abs
143
+ when 1
144
+ return run_command_via_connection(cmd, &data_handler) unless cache_enabled?(:command)
145
+
146
+ @cache[:command][cmd] ||= run_command_via_connection(cmd, &data_handler)
147
+ when 2
148
+ return run_command_via_connection(cmd, opts, &data_handler) unless cache_enabled?(:command)
149
+
150
+ @cache[:command][cmd] ||= run_command_via_connection(cmd, opts, &data_handler)
151
+ else
152
+ raise NotImplementedError, "#{self.class} does not implement run_command_via_connection with arity #{method(:run_command_via_connection).arity}"
153
+ end
137
154
  end
138
155
 
139
156
  # This is the main file call for all connections. This will call the private
@@ -112,7 +112,7 @@ module Train::Transports
112
112
 
113
113
  class WindowsShellRunner
114
114
  require "json" unless defined?(JSON)
115
- require "base64"
115
+ require "base64" unless defined?(Base64)
116
116
 
117
117
  def initialize(powershell_cmd = "powershell")
118
118
  @powershell_cmd = powershell_cmd
@@ -136,7 +136,7 @@ module Train::Transports
136
136
 
137
137
  class WindowsPipeRunner
138
138
  require "json" unless defined?(JSON)
139
- require "base64"
139
+ require "base64" unless defined?(Base64)
140
140
  require "securerandom" unless defined?(SecureRandom)
141
141
 
142
142
  def initialize(powershell_cmd = "powershell")
@@ -1,5 +1,5 @@
1
1
  require_relative "../plugins"
2
- require "digest"
2
+ require "digest" unless defined?(Digest)
3
3
 
4
4
  module Train::Transports
5
5
  class Mock < Train.plugin(1)
@@ -63,6 +63,7 @@ module Train::Transports
63
63
  option :bastion_port, default: 22
64
64
  option :non_interactive, default: false
65
65
  option :verify_host_key, default: false
66
+ option :forward_agent, default: false
66
67
 
67
68
  # Allow connecting with older algorithms
68
69
  option :append_all_supported_algorithms, default: true
@@ -72,13 +72,18 @@ class Train::Transports::SSH
72
72
 
73
73
  args = %w{ -o UserKnownHostsFile=/dev/null }
74
74
  args += %w{ -o StrictHostKeyChecking=no }
75
- args += %w{ -o IdentitiesOnly=yes } if options[:keys]
76
- args += %w{ -o BatchMode=yes } if options[:non_interactive]
75
+ args += %w{ -o BatchMode=yes } if options[:non_interactive]
77
76
  args += %W{ -o LogLevel=#{level} }
78
77
  args += %W{ -o ForwardAgent=#{fwd_agent} } if options.key?(:forward_agent)
79
- Array(options[:keys]).each do |ssh_key|
80
- args += %W{ -i #{ssh_key} }
78
+
79
+ keys = Array(options[:keys])
80
+ unless keys.empty?
81
+ args += %w{ -o IdentitiesOnly=yes }
82
+ keys.each do |ssh_key|
83
+ args += %W{ -i #{ssh_key} }
84
+ end
81
85
  end
86
+
82
87
  args
83
88
  end
84
89
 
@@ -235,12 +240,12 @@ class Train::Transports::SSH
235
240
  end
236
241
  end
237
242
 
238
- def run_command_via_connection(cmd, &data_handler)
243
+ def run_command_via_connection(cmd, opts = {}, &data_handler)
239
244
  cmd.dup.force_encoding("binary") if cmd.respond_to?(:force_encoding)
240
245
 
241
246
  reset_session if session.closed?
242
247
 
243
- exit_status, stdout, stderr = execute_on_channel(cmd, &data_handler)
248
+ exit_status, stdout, stderr = execute_on_channel(cmd, opts, &data_handler)
244
249
 
245
250
  # Since `@session.loop` succeeded, reset the IOS command retry counter
246
251
  @ios_cmd_retries = 0
@@ -297,7 +302,8 @@ class Train::Transports::SSH
297
302
  # not received.
298
303
  #
299
304
  # @api private
300
- def execute_on_channel(cmd)
305
+ def execute_on_channel(cmd, opts)
306
+ timeout = opts[:timeout]&.to_i
301
307
  stdout = ""
302
308
  stderr = ""
303
309
  exit_status = nil
@@ -307,7 +313,7 @@ class Train::Transports::SSH
307
313
 
308
314
  logger.debug("[SSH] #{self} cmd = #{cmd}")
309
315
 
310
- if @transport_options[:pty]
316
+ if @transport_options[:pty] || timeout
311
317
  channel.request_pty do |_ch, success|
312
318
  raise Train::Transports::SSHPTYFailed, "Requesting PTY failed" unless success
313
319
  end
@@ -334,7 +340,20 @@ class Train::Transports::SSH
334
340
  end
335
341
  end
336
342
  end
337
- session.loop
343
+
344
+ thr = Thread.new { session.loop }
345
+
346
+ if timeout
347
+ res = thr.join(timeout)
348
+ unless res
349
+ logger.debug("train ssh command '#{cmd}' reached requested timeout (#{timeout}s)")
350
+ session.channels.each_value { |c| c.eof!; c.close }
351
+ raise Train::CommandTimeoutReached.new "ssh command reached timeout (#{timeout}s)"
352
+ end
353
+ else
354
+ thr.join
355
+ end
356
+
338
357
  [exit_status, stdout, stderr]
339
358
  end
340
359
  end
@@ -2,5 +2,5 @@
2
2
  # Author:: Dominik Richter (<dominik.richter@gmail.com>)
3
3
 
4
4
  module Train
5
- VERSION = "3.3.21".freeze
5
+ VERSION = "3.4.7".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.3.21
4
+ version: 3.4.7
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: 2020-09-14 00:00:00.000000000 Z
11
+ date: 2021-01-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -188,7 +188,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
188
188
  - !ruby/object:Gem::Version
189
189
  version: '0'
190
190
  requirements: []
191
- rubygems_version: 3.0.3
191
+ rubygems_version: 3.1.4
192
192
  signing_key:
193
193
  specification_version: 4
194
194
  summary: Transport interface to talk to a selected set of backends.