train 2.0.2 → 2.0.5

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: 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.