train 3.2.0 → 3.2.3

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