train 3.2.0 → 3.2.3

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: c8dd3de7c1923ee9b164a40fe8b3fb6bfdfe318c3b9cb3b322397551e8a17a5f
4
- data.tar.gz: 7b3b63a44c454963d8b47b1c3e7772b17db8f73b20a6519da955a475823d6b3f
3
+ metadata.gz: 190dd867daf0d960422c03d54854a6da292346e9728e0c54c56ba733dcfd9746
4
+ data.tar.gz: 41822552cb18b121b2e75f16a34bf51c568c6eb9b5dff9a7bc75d4e365c9552f
5
5
  SHA512:
6
- metadata.gz: 13928685d3aa1d35942241ce81be9cda15cbdfdfadf2e9c04fdb63da9d0573eba79bfb5431fb907ca57b0732c2a9a6be94abc60dd43261d950297ae764bd2d1e
7
- data.tar.gz: 0eb217f2809ff17bc7a0715b9c49566a45d16db343131c208a6dabc9765771095020dc8618af13a3003e5e7c0c72d9804ae649bef56246fd3a77ee690e0e6d15
6
+ metadata.gz: 1b8eeb403d7ed720b53603b34ea6aff2f807c3de2c3145062da88669212dcc56ce54666436b39ce7b2b3ffdf8f983d04ae0a4a5743d01f78760689bc6790d165
7
+ data.tar.gz: 9477cfde739dffb54cee8c17c1540a8a903b6418b302b4f958251aa2498049c16ec698be72dcc0467a5e6e31fe0aa867cb0bcfebd3be82701af74948284891d7
@@ -28,32 +28,53 @@ module Train::Extras
28
28
  class LinuxCommand < CommandWrapperBase
29
29
  Train::Options.attach(self)
30
30
 
31
- option :shell, default: false
31
+ option :shell, default: false
32
32
  option :shell_options, default: nil
33
33
  option :shell_command, default: nil
34
- option :sudo, default: false
35
- option :sudo_options, default: nil
34
+ option :sudo, default: false
35
+ option :sudo_options, default: nil
36
36
  option :sudo_password, default: nil
37
- option :sudo_command, default: nil
37
+ option :sudo_command, default: nil
38
38
  option :user
39
39
 
40
+ attr_reader :backend
41
+
40
42
  def initialize(backend, options)
41
43
  @backend = backend
42
44
  validate_options(options)
43
45
 
44
- @shell = options[:shell]
46
+ @shell = options[:shell]
45
47
  @shell_options = options[:shell_options] # e.g. '--login'
46
48
  @shell_command = options[:shell_command] # e.g. '/bin/sh'
47
- @sudo = options[:sudo]
48
- @sudo_options = options[:sudo_options]
49
+ @sudo = options[:sudo]
50
+ @sudo_options = options[:sudo_options]
49
51
  @sudo_password = options[:sudo_password]
50
- @sudo_command = options[:sudo_command]
51
- @user = options[:user]
52
+ @sudo_command = options[:sudo_command]
53
+ @user = options[:user]
54
+ end
55
+
56
+ def with_sudo_pty
57
+ old_pty = backend.transport_options[:pty]
58
+ backend.transport_options[:pty] = true if @sudo
59
+
60
+ yield
61
+ ensure
62
+ backend.transport_options[:pty] = old_pty
52
63
  end
53
64
 
54
65
  # (see CommandWrapperBase::verify)
55
66
  def verify
56
- res = @backend.run_command(run("echo"))
67
+ cmd = if @sudo
68
+ # Wrap it up. It needs /dev/null on the outside to disable stdin
69
+ "bash -c '(#{run("-v")}) < /dev/null'"
70
+ else
71
+ run("echo")
72
+ end
73
+
74
+ # rubocop:disable Style/BlockDelimiters
75
+ res = with_sudo_pty {
76
+ @backend.run_command(cmd)
77
+ }
57
78
  return nil if res.exit_status == 0
58
79
 
59
80
  rawerr = res.stdout + " " + res.stderr
@@ -96,9 +117,12 @@ module Train::Extras
96
117
 
97
118
  res = (@sudo_command || "sudo") + " "
98
119
 
99
- res = "#{safe_string(@sudo_password + "\n")} | #{res}-S " unless @sudo_password.nil?
120
+ if @sudo_password
121
+ str = safe_string(@sudo_password + "\n")
122
+ res = "#{str} | #{res}-S "
123
+ end
100
124
 
101
- res << @sudo_options.to_s + " " unless @sudo_options.nil?
125
+ res << "#{@sudo_options} " if @sudo_options
102
126
 
103
127
  res + cmd
104
128
  end
@@ -109,7 +133,7 @@ module Train::Extras
109
133
  return cmd unless @shell
110
134
 
111
135
  shell = @shell_command || "$SHELL"
112
- options = " " + @shell_options.to_s unless @shell_options.nil?
136
+ options = " #{@shell_options}" if @shell_options
113
137
 
114
138
  "#{safe_string(cmd)} | #{shell}#{options}"
115
139
  end
@@ -146,6 +146,12 @@ module Train::Transports
146
146
  raise PipeError if @pipe.nil?
147
147
  end
148
148
 
149
+ # @param cmd The command to execute
150
+ # @return Local::ComandResult with stdout, stderr and exitstatus
151
+ # Note that exitstatus ($?) in PowerShell is boolean, but we use a numeric exit code.
152
+ # A command that succeeds without setting an exit code will have exitstatus 0
153
+ # A command that exits with an exit code will have that value as exitstatus
154
+ # A command that fails (e.g. throws exception) before setting an exit code will have exitstatus 1
149
155
  def run_command(cmd)
150
156
  script = "$ProgressPreference='SilentlyContinue';" + cmd
151
157
  encoded_script = Base64.strict_encode64(script)
@@ -199,10 +205,16 @@ module Train::Transports
199
205
  $scriptBlock = $ExecutionContext.InvokeCommand.NewScriptBlock($command)
200
206
  try {
201
207
  $stdout = & $scriptBlock | Out-String
202
- $result = @{ 'stdout' = $stdout ; 'stderr' = ''; 'exitstatus' = 0 }
208
+ $exit_code = $LastExitCode
209
+ if ($exit_code -eq $null)
210
+ {
211
+ $exit_code = 0
212
+ }
213
+ $result = @{ 'stdout' = $stdout ; 'stderr' = ''; 'exitstatus' = $exit_code }
203
214
  } catch {
204
215
  $stderr = $_ | Out-String
205
- $result = @{ 'stdout' = ''; 'stderr' = $stderr; 'exitstatus' = 1 }
216
+ $exit_code = $LastExitCode
217
+ $result = @{ 'stdout' = ''; 'stderr' = $stderr; 'exitstatus' = $exit_code }
206
218
  }
207
219
  $resultJSON = $result | ConvertTo-JSON
208
220
 
@@ -31,11 +31,16 @@ class Train::Transports::SSH
31
31
  # @author Fletcher Nichol <fnichol@nichol.ca>
32
32
  class Connection < BaseConnection # rubocop:disable Metrics/ClassLength
33
33
  attr_reader :hostname
34
+ attr_reader :transport_options
35
+
34
36
  def initialize(options)
35
37
  # Track IOS command retries to prevent infinite loop on IOError. This must
36
38
  # be done before `super()` because the parent runs detection commands.
37
39
  @ios_cmd_retries = 0
40
+
38
41
  super(options)
42
+
43
+ @session = nil
39
44
  @username = @options.delete(:username)
40
45
  @hostname = @options.delete(:hostname)
41
46
  @port = @options[:port] # don't delete from options
@@ -43,13 +48,12 @@ class Train::Transports::SSH
43
48
  @connection_retry_sleep = @options.delete(:connection_retry_sleep)
44
49
  @max_wait_until_ready = @options.delete(:max_wait_until_ready)
45
50
  @max_ssh_sessions = @options.delete(:max_ssh_connections) { 9 }
46
- @session = nil
47
51
  @transport_options = @options.delete(:transport_options)
48
- @cmd_wrapper = nil
49
52
  @proxy_command = @options.delete(:proxy_command)
50
53
  @bastion_host = @options.delete(:bastion_host)
51
54
  @bastion_user = @options.delete(:bastion_user)
52
55
  @bastion_port = @options.delete(:bastion_port)
56
+
53
57
  @cmd_wrapper = CommandWrapper.load(self, @transport_options)
54
58
  end
55
59
 
@@ -69,8 +73,8 @@ class Train::Transports::SSH
69
73
 
70
74
  args = %w{ -o UserKnownHostsFile=/dev/null }
71
75
  args += %w{ -o StrictHostKeyChecking=no }
72
- args += %w{ -o IdentitiesOnly=yes } if options[:keys]
73
- args += %w{ -o BatchMode=yes } if options[:non_interactive]
76
+ args += %w{ -o IdentitiesOnly=yes } if options[:keys]
77
+ args += %w{ -o BatchMode=yes } if options[:non_interactive]
74
78
  args += %W{ -o LogLevel=#{level} }
75
79
  args += %W{ -o ForwardAgent=#{fwd_agent} } if options.key?(:forward_agent)
76
80
  Array(options[:keys]).each do |ssh_key|
@@ -154,6 +158,12 @@ class Train::Transports::SSH
154
158
  @session.forward.remote(port, host, remote_port, remote_host)
155
159
  end
156
160
 
161
+ def obscured_options
162
+ options_to_print = @options.clone
163
+ options_to_print[:password] = "<hidden>" if options_to_print.key?(:password)
164
+ options_to_print
165
+ end
166
+
157
167
  private
158
168
 
159
169
  PING_COMMAND = "echo '[SSH] Established'".freeze
@@ -178,6 +188,7 @@ class Train::Transports::SSH
178
188
  # @api private
179
189
  def establish_connection(opts)
180
190
  logger.debug("[SSH] opening connection to #{self}")
191
+ logger.debug("[SSH] using options %p" % [obscured_options])
181
192
  if check_proxy
182
193
  require "net/ssh/proxy/command"
183
194
  @options[:proxy] = Net::SSH::Proxy::Command.new(generate_proxy_command)
@@ -218,9 +229,9 @@ class Train::Transports::SSH
218
229
 
219
230
  def run_command_via_connection(cmd, &data_handler)
220
231
  cmd.dup.force_encoding("binary") if cmd.respond_to?(:force_encoding)
221
- logger.debug("[SSH] #{self} (#{cmd})")
222
232
 
223
233
  reset_session if session.closed?
234
+
224
235
  exit_status, stdout, stderr = execute_on_channel(cmd, &data_handler)
225
236
 
226
237
  # Since `@session.loop` succeeded, reset the IOS command retry counter
@@ -266,9 +277,7 @@ class Train::Transports::SSH
266
277
  #
267
278
  # @api private
268
279
  def to_s
269
- options_to_print = @options.clone
270
- options_to_print[:password] = "<hidden>" if options_to_print.key?(:password)
271
- "#{@username}@#{@hostname}<#{options_to_print.inspect}>"
280
+ "#{@username}@#{@hostname}"
272
281
  end
273
282
 
274
283
  # Given a channel and a command string, it will execute the command on the channel
@@ -280,27 +289,31 @@ class Train::Transports::SSH
280
289
  # not received.
281
290
  #
282
291
  # @api private
283
- def execute_on_channel(cmd, &data_handler)
284
- stdout = stderr = ""
292
+ def execute_on_channel(cmd)
293
+ stdout = ""
294
+ stderr = ""
285
295
  exit_status = nil
286
296
  session.open_channel do |channel|
287
297
  # wrap commands if that is configured
288
298
  cmd = @cmd_wrapper.run(cmd) if @cmd_wrapper
289
299
 
300
+ logger.debug("[SSH] #{self} cmd = #{cmd}")
301
+
290
302
  if @transport_options[:pty]
291
303
  channel.request_pty do |_ch, success|
292
304
  raise Train::Transports::SSHPTYFailed, "Requesting PTY failed" unless success
293
305
  end
294
306
  end
307
+
295
308
  channel.exec(cmd) do |_, success|
296
309
  abort "Couldn't execute command on SSH." unless success
297
310
  channel.on_data do |_, data|
298
- yield(data) unless data_handler.nil?
311
+ yield(data) if block_given?
299
312
  stdout += data
300
313
  end
301
314
 
302
315
  channel.on_extended_data do |_, _type, data|
303
- yield(data) unless data_handler.nil?
316
+ yield(data) if block_given?
304
317
  stderr += data
305
318
  end
306
319
 
data/lib/train/version.rb CHANGED
@@ -3,5 +3,5 @@
3
3
  # Author:: Dominik Richter (<dominik.richter@gmail.com>)
4
4
 
5
5
  module Train
6
- VERSION = "3.2.0".freeze
6
+ VERSION = "3.2.3".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: 3.2.0
4
+ version: 3.2.3
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-12-02 00:00:00.000000000 Z
11
+ date: 2019-12-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json