train 2.1.7 → 2.1.13
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/train.rb +20 -20
- data/lib/train/errors.rb +1 -1
- data/lib/train/extras.rb +2 -2
- data/lib/train/extras/command_wrapper.rb +24 -24
- data/lib/train/extras/stat.rb +27 -27
- data/lib/train/file.rb +30 -30
- data/lib/train/file/local.rb +8 -8
- data/lib/train/file/local/unix.rb +5 -5
- data/lib/train/file/local/windows.rb +1 -1
- data/lib/train/file/remote.rb +8 -8
- data/lib/train/file/remote/aix.rb +1 -1
- data/lib/train/file/remote/linux.rb +2 -2
- data/lib/train/file/remote/qnx.rb +8 -8
- data/lib/train/file/remote/unix.rb +10 -14
- data/lib/train/file/remote/windows.rb +5 -5
- data/lib/train/globals.rb +1 -1
- data/lib/train/options.rb +8 -8
- data/lib/train/platforms.rb +8 -8
- data/lib/train/platforms/common.rb +1 -1
- data/lib/train/platforms/detect/helpers/os_common.rb +36 -32
- data/lib/train/platforms/detect/helpers/os_linux.rb +12 -12
- data/lib/train/platforms/detect/helpers/os_windows.rb +27 -29
- data/lib/train/platforms/detect/scanner.rb +4 -4
- data/lib/train/platforms/detect/specifications/api.rb +8 -8
- data/lib/train/platforms/detect/specifications/os.rb +252 -252
- data/lib/train/platforms/detect/uuid.rb +5 -7
- data/lib/train/platforms/platform.rb +9 -5
- data/lib/train/plugin_test_helper.rb +12 -12
- data/lib/train/plugins.rb +5 -5
- data/lib/train/plugins/base_connection.rb +13 -13
- data/lib/train/plugins/transport.rb +7 -7
- data/lib/train/transports/azure.rb +23 -23
- data/lib/train/transports/cisco_ios_connection.rb +20 -20
- data/lib/train/transports/clients/azure/graph_rbac.rb +2 -2
- data/lib/train/transports/clients/azure/vault.rb +4 -4
- data/lib/train/transports/docker.rb +4 -10
- data/lib/train/transports/gcp.rb +23 -23
- data/lib/train/transports/helpers/azure/file_credentials.rb +8 -8
- data/lib/train/transports/helpers/azure/file_parser.rb +1 -1
- data/lib/train/transports/helpers/azure/subscription_number_file_parser.rb +1 -1
- data/lib/train/transports/local.rb +22 -22
- data/lib/train/transports/mock.rb +33 -35
- data/lib/train/transports/ssh.rb +47 -47
- data/lib/train/transports/ssh_connection.rb +28 -28
- data/lib/train/transports/vmware.rb +32 -34
- data/lib/train/transports/winrm.rb +37 -37
- data/lib/train/transports/winrm_connection.rb +12 -12
- data/lib/train/version.rb +1 -1
- metadata +2 -2
data/lib/train/transports/ssh.rb
CHANGED
@@ -18,9 +18,9 @@
|
|
18
18
|
# See the License for the specific language governing permissions and
|
19
19
|
# limitations under the License.
|
20
20
|
|
21
|
-
require
|
22
|
-
require
|
23
|
-
require
|
21
|
+
require "net/ssh"
|
22
|
+
require "net/scp"
|
23
|
+
require "train/errors"
|
24
24
|
|
25
25
|
module Train::Transports
|
26
26
|
# Wrapped exception for any internally raised SSH-related errors.
|
@@ -34,10 +34,10 @@ module Train::Transports
|
|
34
34
|
#
|
35
35
|
# @author Fletcher Nichol <fnichol@nichol.ca>
|
36
36
|
class SSH < Train.plugin(1) # rubocop:disable Metrics/ClassLength
|
37
|
-
name
|
37
|
+
name "ssh"
|
38
38
|
|
39
|
-
require
|
40
|
-
require
|
39
|
+
require "train/transports/ssh_connection"
|
40
|
+
require "train/transports/cisco_ios_connection"
|
41
41
|
|
42
42
|
# add options for submodules
|
43
43
|
include_options Train::Extras::CommandWrapper
|
@@ -45,7 +45,7 @@ module Train::Transports
|
|
45
45
|
# common target configuration
|
46
46
|
option :host, required: true
|
47
47
|
option :port, default: 22, required: true
|
48
|
-
option :user, default:
|
48
|
+
option :user, default: "root", required: true
|
49
49
|
option :key_files, default: nil
|
50
50
|
option :password, default: nil
|
51
51
|
|
@@ -60,7 +60,7 @@ module Train::Transports
|
|
60
60
|
option :pty, default: false
|
61
61
|
option :proxy_command, default: nil
|
62
62
|
option :bastion_host, default: nil
|
63
|
-
option :bastion_user, default:
|
63
|
+
option :bastion_user, default: "root"
|
64
64
|
option :bastion_port, default: 22
|
65
65
|
option :non_interactive, default: false
|
66
66
|
option :verify_host_key, default: false
|
@@ -89,34 +89,34 @@ module Train::Transports
|
|
89
89
|
super(options)
|
90
90
|
|
91
91
|
key_files = Array(options[:key_files])
|
92
|
-
options[:auth_methods] ||= [
|
92
|
+
options[:auth_methods] ||= ["none"]
|
93
93
|
|
94
94
|
unless key_files.empty?
|
95
|
-
options[:auth_methods].push(
|
95
|
+
options[:auth_methods].push("publickey")
|
96
96
|
options[:keys_only] = true if options[:password].nil?
|
97
97
|
options[:key_files] = key_files
|
98
98
|
end
|
99
99
|
|
100
100
|
unless options[:password].nil?
|
101
|
-
options[:auth_methods].push(
|
101
|
+
options[:auth_methods].push("password", "keyboard-interactive")
|
102
102
|
end
|
103
103
|
|
104
|
-
if options[:auth_methods] == [
|
104
|
+
if options[:auth_methods] == ["none"]
|
105
105
|
if ssh_known_identities.empty?
|
106
|
-
|
107
|
-
|
106
|
+
raise Train::ClientError,
|
107
|
+
"Your SSH Agent has no keys added, and you have not specified a password or a key file"
|
108
108
|
else
|
109
|
-
logger.debug(
|
110
|
-
options[:auth_methods].push(
|
109
|
+
logger.debug("[SSH] Using Agent keys as no password or key file have been specified")
|
110
|
+
options[:auth_methods].push("publickey")
|
111
111
|
end
|
112
112
|
end
|
113
113
|
|
114
114
|
if options[:pty]
|
115
|
-
logger.warn(
|
115
|
+
logger.warn("[SSH] PTY requested: stderr will be merged into stdout")
|
116
116
|
end
|
117
117
|
|
118
118
|
if [options[:proxy_command], options[:bastion_host]].all? { |type| !type.nil? }
|
119
|
-
|
119
|
+
raise Train::ClientError, "Only one of proxy_command or bastion_host needs to be specified"
|
120
120
|
end
|
121
121
|
|
122
122
|
super
|
@@ -142,30 +142,30 @@ module Train::Transports
|
|
142
142
|
# @api private
|
143
143
|
def connection_options(opts)
|
144
144
|
connection_options = {
|
145
|
-
logger:
|
146
|
-
user_known_hosts_file:
|
147
|
-
hostname:
|
148
|
-
port:
|
149
|
-
username:
|
150
|
-
compression:
|
151
|
-
compression_level:
|
152
|
-
keepalive:
|
153
|
-
keepalive_interval:
|
154
|
-
timeout:
|
155
|
-
connection_retries:
|
145
|
+
logger: logger,
|
146
|
+
user_known_hosts_file: "/dev/null",
|
147
|
+
hostname: opts[:host],
|
148
|
+
port: opts[:port],
|
149
|
+
username: opts[:user],
|
150
|
+
compression: opts[:compression],
|
151
|
+
compression_level: opts[:compression_level],
|
152
|
+
keepalive: opts[:keepalive],
|
153
|
+
keepalive_interval: opts[:keepalive_interval],
|
154
|
+
timeout: opts[:connection_timeout],
|
155
|
+
connection_retries: opts[:connection_retries],
|
156
156
|
connection_retry_sleep: opts[:connection_retry_sleep],
|
157
|
-
max_wait_until_ready:
|
158
|
-
auth_methods:
|
159
|
-
keys_only:
|
160
|
-
keys:
|
161
|
-
password:
|
162
|
-
forward_agent:
|
163
|
-
proxy_command:
|
164
|
-
bastion_host:
|
165
|
-
bastion_user:
|
166
|
-
bastion_port:
|
167
|
-
non_interactive:
|
168
|
-
transport_options:
|
157
|
+
max_wait_until_ready: opts[:max_wait_until_ready],
|
158
|
+
auth_methods: opts[:auth_methods],
|
159
|
+
keys_only: opts[:keys_only],
|
160
|
+
keys: opts[:key_files],
|
161
|
+
password: opts[:password],
|
162
|
+
forward_agent: opts[:forward_agent],
|
163
|
+
proxy_command: opts[:proxy_command],
|
164
|
+
bastion_host: opts[:bastion_host],
|
165
|
+
bastion_user: opts[:bastion_user],
|
166
|
+
bastion_port: opts[:bastion_port],
|
167
|
+
non_interactive: opts[:non_interactive],
|
168
|
+
transport_options: opts,
|
169
169
|
}
|
170
170
|
# disable host key verification. The hash key and value to use
|
171
171
|
# depends on the version of net-ssh in use.
|
@@ -203,20 +203,20 @@ module Train::Transports
|
|
203
203
|
# 5.0+ style
|
204
204
|
{
|
205
205
|
# It's not a boolean anymore.
|
206
|
-
|
207
|
-
|
206
|
+
"true" => :always,
|
207
|
+
"false" => :never,
|
208
208
|
true => :always,
|
209
209
|
false => :never,
|
210
210
|
# May be correct value, but strings from JSON config
|
211
|
-
|
212
|
-
|
211
|
+
"always" => :always,
|
212
|
+
"never" => :never,
|
213
213
|
nil => :never,
|
214
214
|
}.fetch(given, given)
|
215
215
|
else
|
216
216
|
# up to 4.2 style
|
217
217
|
{
|
218
|
-
|
219
|
-
|
218
|
+
"true" => true,
|
219
|
+
"false" => false,
|
220
220
|
nil => false,
|
221
221
|
}.fetch(given, given)
|
222
222
|
end
|
@@ -18,9 +18,9 @@
|
|
18
18
|
# See the License for the specific language governing permissions and
|
19
19
|
# limitations under the License.
|
20
20
|
|
21
|
-
require
|
22
|
-
require
|
23
|
-
require
|
21
|
+
require "net/ssh"
|
22
|
+
require "net/scp"
|
23
|
+
require "timeout"
|
24
24
|
|
25
25
|
class Train::Transports::SSH
|
26
26
|
# A Connection instance can be generated and re-generated, given new
|
@@ -63,17 +63,17 @@ class Train::Transports::SSH
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def ssh_opts
|
66
|
-
level = logger.debug? ?
|
67
|
-
fwd_agent = options[:forward_agent] ?
|
66
|
+
level = logger.debug? ? "VERBOSE" : "ERROR"
|
67
|
+
fwd_agent = options[:forward_agent] ? "yes" : "no"
|
68
68
|
|
69
69
|
args = %w{ -o UserKnownHostsFile=/dev/null }
|
70
70
|
args += %w{ -o StrictHostKeyChecking=no }
|
71
71
|
args += %w{ -o IdentitiesOnly=yes } if options[:keys]
|
72
72
|
args += %w{ -o BatchMode=yes } if options[:non_interactive]
|
73
|
-
args += %W
|
74
|
-
args += %W
|
73
|
+
args += %W{ -o LogLevel=#{level} }
|
74
|
+
args += %W{ -o ForwardAgent=#{fwd_agent} } if options.key?(:forward_agent)
|
75
75
|
Array(options[:keys]).each do |ssh_key|
|
76
|
-
args += %W
|
76
|
+
args += %W{ -i #{ssh_key} }
|
77
77
|
end
|
78
78
|
args
|
79
79
|
end
|
@@ -86,19 +86,19 @@ class Train::Transports::SSH
|
|
86
86
|
return @proxy_command unless @proxy_command.nil?
|
87
87
|
args = %w{ ssh }
|
88
88
|
args += ssh_opts
|
89
|
-
args += %W
|
90
|
-
args += %W
|
89
|
+
args += %W{ #{@bastion_user}@#{@bastion_host} }
|
90
|
+
args += %W{ -p #{@bastion_port} }
|
91
91
|
args += %w{ -W %h:%p }
|
92
|
-
args.join(
|
92
|
+
args.join(" ")
|
93
93
|
end
|
94
94
|
|
95
95
|
# (see Base::Connection#login_command)
|
96
96
|
def login_command
|
97
97
|
args = ssh_opts
|
98
|
-
args += %W
|
99
|
-
args += %W
|
100
|
-
args += %W
|
101
|
-
LoginCommand.new(
|
98
|
+
args += %W{ -o ProxyCommand='#{generate_proxy_command}' } if check_proxy
|
99
|
+
args += %W{ -p #{@port} }
|
100
|
+
args += %W{ #{@username}@#{@hostname} }
|
101
|
+
LoginCommand.new("ssh", args)
|
102
102
|
end
|
103
103
|
|
104
104
|
# (see Base::Connection#upload)
|
@@ -138,7 +138,7 @@ class Train::Transports::SSH
|
|
138
138
|
retries: @max_wait_until_ready / delay,
|
139
139
|
delay: delay,
|
140
140
|
message: "Waiting for SSH service on #{@hostname}:#{@port}, " \
|
141
|
-
"retrying in #{delay} seconds"
|
141
|
+
"retrying in #{delay} seconds"
|
142
142
|
)
|
143
143
|
run_command(PING_COMMAND.dup)
|
144
144
|
end
|
@@ -172,14 +172,14 @@ class Train::Transports::SSH
|
|
172
172
|
def establish_connection(opts)
|
173
173
|
logger.debug("[SSH] opening connection to #{self}")
|
174
174
|
if check_proxy
|
175
|
-
require
|
175
|
+
require "net/ssh/proxy/command"
|
176
176
|
@options[:proxy] = Net::SSH::Proxy::Command.new(generate_proxy_command)
|
177
177
|
end
|
178
178
|
Net::SSH.start(@hostname, @username, @options.clone.delete_if { |_key, value| value.nil? })
|
179
179
|
rescue *RESCUE_EXCEPTIONS_ON_ESTABLISH => e
|
180
180
|
if (opts[:retries] -= 1) <= 0
|
181
181
|
logger.warn("[SSH] connection failed, terminating (#{e.inspect})")
|
182
|
-
raise Train::Transports::SSHFailed,
|
182
|
+
raise Train::Transports::SSHFailed, "SSH session could not be established"
|
183
183
|
end
|
184
184
|
|
185
185
|
if opts[:message]
|
@@ -200,7 +200,7 @@ class Train::Transports::SSH
|
|
200
200
|
Train::File::Remote::Aix.new(self, path)
|
201
201
|
elsif os.solaris?
|
202
202
|
Train::File::Remote::Unix.new(self, path)
|
203
|
-
elsif os[:name] ==
|
203
|
+
elsif os[:name] == "qnx"
|
204
204
|
Train::File::Remote::Qnx.new(self, path)
|
205
205
|
elsif os.windows?
|
206
206
|
Train::File::Remote::Windows.new(self, path)
|
@@ -210,7 +210,7 @@ class Train::Transports::SSH
|
|
210
210
|
end
|
211
211
|
|
212
212
|
def run_command_via_connection(cmd, &data_handler)
|
213
|
-
cmd.dup.force_encoding(
|
213
|
+
cmd.dup.force_encoding("binary") if cmd.respond_to?(:force_encoding)
|
214
214
|
logger.debug("[SSH] #{self} (#{cmd})")
|
215
215
|
|
216
216
|
reset_session if session.closed?
|
@@ -228,7 +228,7 @@ class Train::Transports::SSH
|
|
228
228
|
# transport. This retries the command if this is the case.
|
229
229
|
# See:
|
230
230
|
# https://github.com/inspec/train/pull/271
|
231
|
-
logger.debug(
|
231
|
+
logger.debug("[SSH] Possible Cisco IOS race condition, retrying command")
|
232
232
|
|
233
233
|
# Only attempt retry up to 5 times to avoid infinite loop
|
234
234
|
@ios_cmd_retries += 1
|
@@ -246,7 +246,7 @@ class Train::Transports::SSH
|
|
246
246
|
def session(retry_options = {})
|
247
247
|
@session ||= establish_connection({
|
248
248
|
retries: @connection_retries.to_i,
|
249
|
-
delay:
|
249
|
+
delay: @connection_retry_sleep.to_i,
|
250
250
|
}.merge(retry_options))
|
251
251
|
end
|
252
252
|
|
@@ -260,7 +260,7 @@ class Train::Transports::SSH
|
|
260
260
|
# @api private
|
261
261
|
def to_s
|
262
262
|
options_to_print = @options.clone
|
263
|
-
options_to_print[:password] =
|
263
|
+
options_to_print[:password] = "<hidden>" if options_to_print.key?(:password)
|
264
264
|
"#{@username}@#{@hostname}<#{options_to_print.inspect}>"
|
265
265
|
end
|
266
266
|
|
@@ -274,7 +274,7 @@ class Train::Transports::SSH
|
|
274
274
|
#
|
275
275
|
# @api private
|
276
276
|
def execute_on_channel(cmd, &data_handler)
|
277
|
-
stdout = stderr =
|
277
|
+
stdout = stderr = ""
|
278
278
|
exit_status = nil
|
279
279
|
session.open_channel do |channel|
|
280
280
|
# wrap commands if that is configured
|
@@ -282,11 +282,11 @@ class Train::Transports::SSH
|
|
282
282
|
|
283
283
|
if @transport_options[:pty]
|
284
284
|
channel.request_pty do |_ch, success|
|
285
|
-
|
285
|
+
raise Train::Transports::SSHPTYFailed, "Requesting PTY failed" unless success
|
286
286
|
end
|
287
287
|
end
|
288
288
|
channel.exec(cmd) do |_, success|
|
289
|
-
abort
|
289
|
+
abort "Couldn't execute command on SSH." unless success
|
290
290
|
channel.on_data do |_, data|
|
291
291
|
yield(data) unless data_handler.nil?
|
292
292
|
stdout += data
|
@@ -297,11 +297,11 @@ class Train::Transports::SSH
|
|
297
297
|
stderr += data
|
298
298
|
end
|
299
299
|
|
300
|
-
channel.on_request(
|
300
|
+
channel.on_request("exit-status") do |_, data|
|
301
301
|
exit_status = data.read_long
|
302
302
|
end
|
303
303
|
|
304
|
-
channel.on_request(
|
304
|
+
channel.on_request("exit-signal") do |_, data|
|
305
305
|
exit_status = data.read_long
|
306
306
|
end
|
307
307
|
end
|
@@ -1,16 +1,16 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
2
|
+
require "train/plugins"
|
3
|
+
require "open3"
|
4
|
+
require "ostruct"
|
5
|
+
require "json"
|
6
|
+
require "mkmf"
|
7
7
|
|
8
8
|
module Train::Transports
|
9
9
|
class VMware < Train.plugin(1)
|
10
|
-
name
|
11
|
-
option :viserver, default: ENV[
|
12
|
-
option :username, default: ENV[
|
13
|
-
option :password, default: ENV[
|
10
|
+
name "vmware"
|
11
|
+
option :viserver, default: proc { ENV["VISERVER"] }
|
12
|
+
option :username, default: proc { ENV["VISERVER_USERNAME"] }
|
13
|
+
option :password, default: proc { ENV["VISERVER_PASSWORD"] }
|
14
14
|
option :insecure, default: false
|
15
15
|
|
16
16
|
def connection(_ = nil)
|
@@ -18,7 +18,7 @@ module Train::Transports
|
|
18
18
|
end
|
19
19
|
|
20
20
|
class Connection < BaseConnection # rubocop:disable ClassLength
|
21
|
-
POWERSHELL_PROMPT_REGEX = /PS\s.*>
|
21
|
+
POWERSHELL_PROMPT_REGEX = /PS\s.*> $/.freeze
|
22
22
|
|
23
23
|
def initialize(options)
|
24
24
|
super(options)
|
@@ -29,18 +29,18 @@ module Train::Transports
|
|
29
29
|
@username = options[:username]
|
30
30
|
@viserver = options[:viserver]
|
31
31
|
@session = nil
|
32
|
-
@stdout_buffer =
|
33
|
-
@stderr_buffer =
|
32
|
+
@stdout_buffer = ""
|
33
|
+
@stderr_buffer = ""
|
34
34
|
|
35
35
|
@powershell_binary = detect_powershell_binary
|
36
36
|
|
37
37
|
if @powershell_binary == :powershell
|
38
|
-
require
|
38
|
+
require "train/transports/local"
|
39
39
|
@powershell = Train::Transports::Local::Connection.new(options)
|
40
40
|
end
|
41
41
|
|
42
42
|
if options[:insecure] == true
|
43
|
-
run_command_via_connection(
|
43
|
+
run_command_via_connection("Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Scope Session -Confirm:$False")
|
44
44
|
end
|
45
45
|
|
46
46
|
@platform_details = {
|
@@ -58,11 +58,11 @@ module Train::Transports
|
|
58
58
|
message = "Unable to connect to VIServer at #{options[:viserver]}. "
|
59
59
|
case result.stderr
|
60
60
|
when /Invalid server certificate/
|
61
|
-
message +=
|
61
|
+
message += "Certification verification failed. Please use `--insecure` or set `Set-PowerCLIConfiguration -InvalidCertificateAction Ignore` in PowerShell"
|
62
62
|
when /incorrect user name or password/
|
63
|
-
message +=
|
63
|
+
message += "Incorrect username or password"
|
64
64
|
else
|
65
|
-
message += result.stderr.gsub(/-Password .*\s/,
|
65
|
+
message += result.stderr.gsub(/-Password .*\s/, "-Password REDACTED")
|
66
66
|
end
|
67
67
|
|
68
68
|
raise message
|
@@ -70,7 +70,7 @@ module Train::Transports
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def platform
|
73
|
-
force_platform!(
|
73
|
+
force_platform!("vmware", @platform_details)
|
74
74
|
end
|
75
75
|
|
76
76
|
def run_command_via_connection(cmd, &_data_handler)
|
@@ -78,8 +78,8 @@ module Train::Transports
|
|
78
78
|
result = parse_pwsh_output(cmd)
|
79
79
|
|
80
80
|
# Attach exit status to result
|
81
|
-
exit_status = parse_pwsh_output(
|
82
|
-
result.exit_status = exit_status ==
|
81
|
+
exit_status = parse_pwsh_output("echo $?").stdout.chomp
|
82
|
+
result.exit_status = exit_status == "True" ? 0 : 1
|
83
83
|
|
84
84
|
result
|
85
85
|
else
|
@@ -88,7 +88,7 @@ module Train::Transports
|
|
88
88
|
end
|
89
89
|
|
90
90
|
def unique_identifier
|
91
|
-
uuid_command =
|
91
|
+
uuid_command = "(Get-VMHost | Get-View).hardware.systeminfo.uuid"
|
92
92
|
run_command_via_connection(uuid_command).stdout.chomp
|
93
93
|
end
|
94
94
|
|
@@ -99,26 +99,24 @@ module Train::Transports
|
|
99
99
|
private
|
100
100
|
|
101
101
|
def detect_powershell_binary
|
102
|
-
if find_executable0(
|
102
|
+
if find_executable0("pwsh")
|
103
103
|
:pwsh
|
104
|
-
elsif find_executable0(
|
104
|
+
elsif find_executable0("powershell")
|
105
105
|
:powershell
|
106
106
|
else
|
107
|
-
raise
|
107
|
+
raise "Cannot find PowerShell binary, is `pwsh` installed?"
|
108
108
|
end
|
109
109
|
end
|
110
110
|
|
111
111
|
# Read from stdout pipe until prompt is received
|
112
112
|
def flush_stdout(pipe)
|
113
|
-
while @stdout_buffer !~ POWERSHELL_PROMPT_REGEX
|
114
|
-
@stdout_buffer += pipe.read_nonblock(1)
|
115
|
-
end
|
113
|
+
@stdout_buffer += pipe.read_nonblock(1) while @stdout_buffer !~ POWERSHELL_PROMPT_REGEX
|
116
114
|
@stdout_buffer
|
117
115
|
rescue IO::EAGAINWaitReadable
|
118
116
|
# We cannot know when the stdout pipe is finished so we keep reading
|
119
117
|
retry
|
120
118
|
ensure
|
121
|
-
@stdout_buffer =
|
119
|
+
@stdout_buffer = ""
|
122
120
|
end
|
123
121
|
|
124
122
|
# This must be called after `flush_stdout` to ensure buffer is full
|
@@ -132,7 +130,7 @@ module Train::Transports
|
|
132
130
|
# is unreadable.
|
133
131
|
@stderr_buffer
|
134
132
|
ensure
|
135
|
-
@stderr_buffer =
|
133
|
+
@stderr_buffer = ""
|
136
134
|
end
|
137
135
|
|
138
136
|
def parse_pwsh_output(cmd)
|
@@ -141,10 +139,10 @@ module Train::Transports
|
|
141
139
|
stdout = flush_stdout(session.stdout)
|
142
140
|
|
143
141
|
# Remove stdin from stdout (including trailing newline)
|
144
|
-
stdout.slice!(0, cmd.length+1)
|
142
|
+
stdout.slice!(0, cmd.length + 1)
|
145
143
|
|
146
144
|
# Remove prompt from stdout
|
147
|
-
stdout.gsub!(POWERSHELL_PROMPT_REGEX,
|
145
|
+
stdout.gsub!(POWERSHELL_PROMPT_REGEX, "")
|
148
146
|
|
149
147
|
# Grab stderr
|
150
148
|
stderr = flush_stderr(session.stderr)
|
@@ -157,10 +155,10 @@ module Train::Transports
|
|
157
155
|
end
|
158
156
|
|
159
157
|
def powercli_version
|
160
|
-
version_command =
|
158
|
+
version_command = "[string](Get-Module -Name VMware.PowerCLI -ListAvailable | Select -ExpandProperty Version)"
|
161
159
|
result = run_command_via_connection(version_command)
|
162
160
|
if result.stdout.empty? || result.exit_status != 0
|
163
|
-
raise
|
161
|
+
raise "Unable to determine PowerCLI Module version, is it installed?"
|
164
162
|
end
|
165
163
|
|
166
164
|
result.stdout.chomp
|
@@ -169,7 +167,7 @@ module Train::Transports
|
|
169
167
|
def session
|
170
168
|
return @session unless @session.nil?
|
171
169
|
|
172
|
-
stdin, stdout, stderr = Open3.popen3(
|
170
|
+
stdin, stdout, stderr = Open3.popen3("pwsh")
|
173
171
|
|
174
172
|
# Remove leading prompt and intro text
|
175
173
|
flush_stdout(stdout)
|