train-core 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: 753d229b222d0b3db2d8cdaaa8f71232a8eb789c764b26ba57663438ce2b112f
4
- data.tar.gz: d11b1c63d3aeb30d3061038948dca02c1a8cc205241865c829d104f53f87ea2c
3
+ metadata.gz: 3ba84c8e3b6f6cddfdafd641d7bea565547acfdb240384b5521767df2ab31f9a
4
+ data.tar.gz: 5148949939422554373a31fcff66d14d74db7ff1f34d2c76f848ab71e365a347
5
5
  SHA512:
6
- metadata.gz: 8653c7e7c3180d367b60ccc9134d670a74701354cf3009d91a416e6b09dd2fb8112835c010a935f22900e6b27fc974df83d5d7a1c945fc6a6fd7f32f5be13df2
7
- data.tar.gz: 8e03272617627c66ee8e27437d3bc052bfaf320c7d6c7eb99ebff240693a70acb086e33d73643d0bf5b0391f490b52a993742e0a04b84c7a76fe2283dd8f1453
6
+ metadata.gz: db6715c64126c3f4b4716bc7cc7114fbaed5d06b421555234f46f0e9b5f5b9e92143496248b710940ca5420a290259dabeb30056635c6762639e8cf265ba4c0e
7
+ data.tar.gz: f96cc5789c4edc89b4d99568d973a82db1561b49587b98202817757edf2ff5a436a5327a0ad6fa7252ddd83ee95b49b48701d68c4f0ce2d7c90a5cb907cf30ca
@@ -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
 
@@ -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-core
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