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