train 2.0.2 → 2.0.5

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: c4765459fcc15937793cf65643f2d95ffb9deb6a4902364adc38ee8c9194b609
4
- data.tar.gz: 9149263a29d8760dafa83824c4f25a1aa252553dbc58b9e74e31edab7db8ffad
3
+ metadata.gz: fc5c0aaf06370bfbf2ee1308c13dfd87a0da5a736536c4bf3024dd3400a62861
4
+ data.tar.gz: 04e7e0b56d0acd587ce5a567e8c81b2a2b701fd87c0165271048158340958d05
5
5
  SHA512:
6
- metadata.gz: f0da823f4a6e406654b38a6c0cca183d8b4720425e2d2b9eac7772eaa41d1f4fdd105b8d0fc70d1748ac9dfc8a7d48cee52b256696b4c244327ccbcfbccb0bad
7
- data.tar.gz: f5d33756dd4e9f0ab66f762aeeb1800ec69cdd8732afcebd8ce7580336c967f439accd4891db600a9d281d52f2f23752ab31f5bfcec081c4fa46c53e81c65f84
6
+ metadata.gz: 68a9846a2638f11c3d5d34f340f20cd42748206307f10b930c4967745d6c6db7e803109322c36f9ee15c5b4c7e15c4c05bcbbc58acad15878ca058b2c05e1f34
7
+ data.tar.gz: 343a8c70c0e73ff1c3169e28696ad073c5611afa8bcd8425b27b25ff6bd0220084c02139ef96a95c074737df29e8418b03bb91790646c1675b552010b1514f19
@@ -14,8 +14,7 @@ module Train::Platforms::Detect::Helpers
14
14
  end
15
15
 
16
16
  def winrm?
17
- Object.const_defined?('Train::Transports::WinRM::Connection') &&
18
- @backend.class == Train::Transports::WinRM::Connection
17
+ @backend.class.to_s == 'Train::Transports::WinRM::Connection'
19
18
  end
20
19
 
21
20
  def unix_file_contents(path)
@@ -37,6 +37,7 @@ module Train::Platforms::Detect::Helpers
37
37
 
38
38
  raw.lines.each_with_object({}) do |line, memo|
39
39
  line.strip!
40
+ next if line.start_with?('#')
40
41
  next if line.empty?
41
42
  key, value = line.split('=', 2)
42
43
  memo[key] = value.gsub(/\A"|"\Z/, '') unless value.empty?
@@ -114,10 +114,14 @@ class Train::Plugins::Transport
114
114
 
115
115
  # This is the main command call for all connections. This will call the private
116
116
  # run_command_via_connection on the connection with optional caching
117
- def run_command(cmd)
118
- return run_command_via_connection(cmd) unless cache_enabled?(:command)
117
+ #
118
+ # This command accepts an optional data handler block. When provided,
119
+ # inbound data will be published vi `data_handler.call(data)`. This can allow
120
+ # callers to receive and render updates from remote command execution.
121
+ def run_command(cmd, &data_handler)
122
+ return run_command_via_connection(cmd, &data_handler) unless cache_enabled?(:command)
119
123
 
120
- @cache[:command][cmd] ||= run_command_via_connection(cmd)
124
+ @cache[:command][cmd] ||= run_command_via_connection(cmd, &data_handler)
121
125
  end
122
126
 
123
127
  # This is the main file call for all connections. This will call the private
@@ -150,8 +154,13 @@ class Train::Plugins::Transport
150
154
  # Execute a command using this connection.
151
155
  #
152
156
  # @param command [String] command string to execute
157
+ # @param &data_handler(data) [Proc] proc that is called when data arrives from
158
+ # the connection. This block is optional. Individual transports will need
159
+ # to explicitly invoke the block in their implementation of run_command_via_connection;
160
+ # if they do not, the block is ignored and will not be used to report data back to the caller.
161
+ #
153
162
  # @return [CommandResult] contains the result of running the command
154
- def run_command_via_connection(_command)
163
+ def run_command_via_connection(_command, &_data_handler)
155
164
  fail NotImplementedError, "#{self.class} does not implement #run_command_via_connection()"
156
165
  end
157
166
 
@@ -56,7 +56,7 @@ class Train::Transports::SSH
56
56
  @session
57
57
  end
58
58
 
59
- def run_command_via_connection(cmd)
59
+ def run_command_via_connection(cmd, &_data_handler)
60
60
  # Ensure buffer is empty before sending data
61
61
  @buf = ''
62
62
 
@@ -89,7 +89,7 @@ class Train::Transports::Docker
89
89
  end
90
90
  end
91
91
 
92
- def run_command_via_connection(cmd)
92
+ def run_command_via_connection(cmd, &_data_handler)
93
93
  cmd = @cmd_wrapper.run(cmd) unless @cmd_wrapper.nil?
94
94
  stdout, stderr, exit_status = @container.exec(
95
95
  [
@@ -71,7 +71,7 @@ module Train::Transports
71
71
  end
72
72
  end
73
73
 
74
- def run_command_via_connection(cmd)
74
+ def run_command_via_connection(cmd, &_data_handler)
75
75
  # Use the runner if it is available
76
76
  return @runner.run_command(cmd) if defined?(@runner)
77
77
 
@@ -124,7 +124,7 @@ class Train::Transports::Mock
124
124
 
125
125
  private
126
126
 
127
- def run_command_via_connection(cmd)
127
+ def run_command_via_connection(cmd, &_data_handler)
128
128
  @cache[:command][Digest::SHA256.hexdigest cmd.to_s] ||
129
129
  command_not_found(cmd)
130
130
  end
@@ -63,6 +63,7 @@ module Train::Transports
63
63
  option :bastion_user, default: 'root'
64
64
  option :bastion_port, default: 22
65
65
  option :non_interactive, default: false
66
+ option :verify_host_key, default: false
66
67
 
67
68
  option :compression_level do |opts|
68
69
  # on nil or false: set compression level to 0
@@ -168,7 +169,7 @@ module Train::Transports
168
169
  }
169
170
  # disable host key verification. The hash key to use
170
171
  # depends on the version of net-ssh in use.
171
- connection_options[verify_host_key_option] = false
172
+ connection_options[verify_host_key_option] = opts[:verify_host_key] || false
172
173
 
173
174
  connection_options
174
175
  end
@@ -207,44 +207,12 @@ class Train::Transports::SSH
207
207
  end
208
208
  end
209
209
 
210
- def run_command_via_connection(cmd) # rubocop:disable AbcSize
211
- stdout = stderr = ''
212
- exit_status = nil
210
+ def run_command_via_connection(cmd, &data_handler)
213
211
  cmd.dup.force_encoding('binary') if cmd.respond_to?(:force_encoding)
214
212
  logger.debug("[SSH] #{self} (#{cmd})")
215
213
 
216
214
  reset_session if session.closed?
217
- session.open_channel do |channel|
218
- # wrap commands if that is configured
219
- cmd = @cmd_wrapper.run(cmd) unless @cmd_wrapper.nil?
220
-
221
- if @transport_options[:pty]
222
- channel.request_pty do |_ch, success|
223
- fail Train::Transports::SSHPTYFailed, 'Requesting PTY failed' unless success
224
- end
225
- end
226
-
227
- channel.exec(cmd) do |_, success|
228
- abort 'Couldn\'t execute command on SSH.' unless success
229
-
230
- channel.on_data do |_, data|
231
- stdout += data
232
- end
233
-
234
- channel.on_extended_data do |_, _type, data|
235
- stderr += data
236
- end
237
-
238
- channel.on_request('exit-status') do |_, data|
239
- exit_status = data.read_long
240
- end
241
-
242
- channel.on_request('exit-signal') do |_, data|
243
- exit_status = data.read_long
244
- end
245
- end
246
- end
247
- @session.loop
215
+ exit_status, stdout, stderr = execute_on_channel(cmd, &data_handler)
248
216
 
249
217
  # Since `@session.loop` succeeded, reset the IOS command retry counter
250
218
  @ios_cmd_retries = 0
@@ -293,5 +261,51 @@ class Train::Transports::SSH
293
261
  options_to_print[:password] = '<hidden>' if options_to_print.key?(:password)
294
262
  "#{@username}@#{@hostname}<#{options_to_print.inspect}>"
295
263
  end
264
+
265
+ # Given a channel and a command string, it will execute the command on the channel
266
+ # and accumulate results in @stdout/@stderr.
267
+ #
268
+ # @param channel [Net::SSH::Connection::Channel] an open ssh channel
269
+ # @param cmd [String] the command to execute
270
+ # @return [Integer] exit status or nil if exit-status/exit-signal requests
271
+ # not received.
272
+ #
273
+ # @api private
274
+ def execute_on_channel(cmd, &data_handler)
275
+ stdout = stderr = ''
276
+ exit_status = nil
277
+ session.open_channel do |channel|
278
+ # wrap commands if that is configured
279
+ cmd = @cmd_wrapper.run(cmd) unless @cmd_wrapper.nil?
280
+
281
+ if @transport_options[:pty]
282
+ channel.request_pty do |_ch, success|
283
+ fail Train::Transports::SSHPTYFailed, 'Requesting PTY failed' unless success
284
+ end
285
+ end
286
+ channel.exec(cmd) do |_, success|
287
+ abort 'Couldn\'t execute command on SSH.' unless success
288
+ channel.on_data do |_, data|
289
+ yield(data) unless data_handler.nil?
290
+ stdout += data
291
+ end
292
+
293
+ channel.on_extended_data do |_, _type, data|
294
+ yield(data) unless data_handler.nil?
295
+ stderr += data
296
+ end
297
+
298
+ channel.on_request('exit-status') do |_, data|
299
+ exit_status = data.read_long
300
+ end
301
+
302
+ channel.on_request('exit-signal') do |_, data|
303
+ exit_status = data.read_long
304
+ end
305
+ end
306
+ end
307
+ session.loop
308
+ [exit_status, stdout, stderr]
309
+ end
296
310
  end
297
311
  end
@@ -73,7 +73,7 @@ module Train::Transports
73
73
  force_platform!('vmware', @platform_details)
74
74
  end
75
75
 
76
- def run_command_via_connection(cmd)
76
+ def run_command_via_connection(cmd, &_data_handler)
77
77
  if @powershell_binary == :pwsh
78
78
  result = parse_pwsh_output(cmd)
79
79
 
@@ -64,6 +64,10 @@ module Train::Transports
64
64
  option :kerberos_realm, default: nil
65
65
  option :kerberos_service, default: nil
66
66
  option :ca_trust_file, default: nil
67
+ # The amount of time in SECONDS for which each operation must get an ack
68
+ # from the winrm endpoint. Does not mean that the command has
69
+ # completed in this time, only that the server has ack'd the request.
70
+ option :operation_timeout, default: nil
67
71
 
68
72
  def initialize(opts)
69
73
  super(opts)
@@ -36,6 +36,7 @@ class Train::Transports::WinRM
36
36
  @connection_retries = @options.delete(:connection_retries)
37
37
  @connection_retry_sleep = @options.delete(:connection_retry_sleep)
38
38
  @max_wait_until_ready = @options.delete(:max_wait_until_ready)
39
+ @operation_timeout = @options.delete(:operation_timeout)
39
40
  end
40
41
 
41
42
  # (see Base::Connection#close)
@@ -95,12 +96,13 @@ class Train::Transports::WinRM
95
96
  Train::File::Remote::Windows.new(self, path)
96
97
  end
97
98
 
98
- def run_command_via_connection(command)
99
+ def run_command_via_connection(command, &data_handler)
99
100
  return if command.nil?
100
101
  logger.debug("[WinRM] #{self} (#{command})")
101
102
  out = ''
102
103
 
103
104
  response = session.run(command) do |stdout, _|
105
+ yield(stdout) if data_handler && stdout
104
106
  out << stdout if stdout
105
107
  end
106
108
 
@@ -178,6 +180,7 @@ class Train::Transports::WinRM
178
180
  retry_delay: @connection_retry_sleep.to_i,
179
181
  }.merge(retry_options)
180
182
 
183
+ opts[:operation_timeout] = @operation_timeout unless @operation_timeout.nil?
181
184
  @service = ::WinRM::Connection.new(options.merge(opts))
182
185
  @service.logger = logger
183
186
  @service.shell(:powershell)
@@ -3,5 +3,5 @@
3
3
  # Author:: Dominik Richter (<dominik.richter@gmail.com>)
4
4
 
5
5
  module Train
6
- VERSION = '2.0.2'.freeze
6
+ VERSION = '2.0.5'.freeze
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: train
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 2.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dominik Richter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-19 00:00:00.000000000 Z
11
+ date: 2019-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -308,7 +308,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
308
308
  - !ruby/object:Gem::Version
309
309
  version: '0'
310
310
  requirements: []
311
- rubygems_version: 3.0.1
311
+ rubygems_version: 3.0.3
312
312
  signing_key:
313
313
  specification_version: 4
314
314
  summary: Transport interface to talk to different backends.