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 +4 -4
- data/lib/train/extras/command_wrapper.rb +37 -13
- data/lib/train/transports/local.rb +14 -2
- data/lib/train/transports/ssh_connection.rb +25 -12
- data/lib/train/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 190dd867daf0d960422c03d54854a6da292346e9728e0c54c56ba733dcfd9746
|
4
|
+
data.tar.gz: 41822552cb18b121b2e75f16a34bf51c568c6eb9b5dff9a7bc75d4e365c9552f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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,
|
31
|
+
option :shell, default: false
|
32
32
|
option :shell_options, default: nil
|
33
33
|
option :shell_command, default: nil
|
34
|
-
option :sudo,
|
35
|
-
option :sudo_options,
|
34
|
+
option :sudo, default: false
|
35
|
+
option :sudo_options, default: nil
|
36
36
|
option :sudo_password, default: nil
|
37
|
-
option :sudo_command,
|
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
|
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
|
48
|
-
@sudo_options
|
49
|
+
@sudo = options[:sudo]
|
50
|
+
@sudo_options = options[:sudo_options]
|
49
51
|
@sudo_password = options[:sudo_password]
|
50
|
-
@sudo_command
|
51
|
-
@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
|
-
|
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
|
-
|
120
|
+
if @sudo_password
|
121
|
+
str = safe_string(@sudo_password + "\n")
|
122
|
+
res = "#{str} | #{res}-S "
|
123
|
+
end
|
100
124
|
|
101
|
-
res << @sudo_options
|
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 = "
|
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
|
-
$
|
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
|
-
$
|
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 }
|
73
|
-
args += %w{ -o BatchMode=yes }
|
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
|
-
|
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
|
284
|
-
stdout =
|
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)
|
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)
|
316
|
+
yield(data) if block_given?
|
304
317
|
stderr += data
|
305
318
|
end
|
306
319
|
|
data/lib/train/version.rb
CHANGED
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.
|
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-
|
11
|
+
date: 2019-12-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|