train-core 3.4.9 → 3.6.2
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/file/local.rb +6 -0
- data/lib/train/file/remote/unix.rb +16 -0
- data/lib/train/file/remote/windows.rb +10 -0
- data/lib/train/plugins/base_connection.rb +43 -0
- data/lib/train/transports/cisco_ios_connection.rb +8 -0
- data/lib/train/transports/local.rb +32 -9
- data/lib/train/transports/ssh_connection.rb +1 -1
- 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: bc67d13ca7a06113a56b506c659ad2a8b7cb0ce9468a632aad6d9716d2bfbbb3
|
4
|
+
data.tar.gz: 8f77e2c0ed1e8e18f55b8d96f2a74c43cb135818a3d945c9740c26f37bed3475
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 32b328597cccde06aa86d32fc84d3255c66310c2b8544f4fe1aebf4bfd42b93e422a04e69a85b281954956f6e2ff8c035c672ac64c18fd89107edf94cd666049
|
7
|
+
data.tar.gz: 2eb61ddb2225dfa731fb32f1895b35703b152d80a23a46f0e2550b829bbbac955f753c028d50bb7bce020f692416934b908d35379ccefeaa6b4a17efbc58dd97
|
data/lib/train/file/local.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "base64" unless defined?(Base64)
|
1
2
|
require "shellwords" unless defined?(Shellwords)
|
2
3
|
|
3
4
|
module Train
|
@@ -19,6 +20,21 @@ module Train
|
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
23
|
+
def content=(new_content)
|
24
|
+
execute_result = @backend.run_command("base64 --help")
|
25
|
+
if execute_result.exit_status != 0
|
26
|
+
raise TransportError, "#{self.class} found no base64 binary for file writes"
|
27
|
+
end
|
28
|
+
|
29
|
+
unix_cmd = format("echo '%<base64>s' | base64 --decode > %<file>s",
|
30
|
+
base64: Base64.strict_encode64(new_content),
|
31
|
+
file: @spath)
|
32
|
+
|
33
|
+
@backend.run_command(unix_cmd)
|
34
|
+
|
35
|
+
@content = new_content
|
36
|
+
end
|
37
|
+
|
22
38
|
def exist?
|
23
39
|
@exist ||= begin
|
24
40
|
f = @follow_symlink ? "" : " || test -L #{@spath}"
|
@@ -26,6 +26,16 @@ module Train
|
|
26
26
|
@content
|
27
27
|
end
|
28
28
|
|
29
|
+
def content=(new_content)
|
30
|
+
win_cmd = format('[IO.File]::WriteAllBytes("%<file>s", [Convert]::FromBase64String("%<base64>s"))',
|
31
|
+
base64: Base64.strict_encode64(new_content),
|
32
|
+
file: @spath)
|
33
|
+
|
34
|
+
@backend.run_command(win_cmd)
|
35
|
+
|
36
|
+
@content = new_content
|
37
|
+
end
|
38
|
+
|
29
39
|
def exist?
|
30
40
|
return @exist if defined?(@exist)
|
31
41
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require_relative "../errors"
|
2
2
|
require_relative "../extras"
|
3
3
|
require_relative "../file"
|
4
|
+
require "fileutils" unless defined?(FileUtils)
|
4
5
|
require "logger"
|
5
6
|
|
6
7
|
class Train::Plugins::Transport
|
@@ -161,6 +162,48 @@ class Train::Plugins::Transport
|
|
161
162
|
@cache[:file][path] ||= file_via_connection(path, *args)
|
162
163
|
end
|
163
164
|
|
165
|
+
# Uploads local files or directories to remote host.
|
166
|
+
#
|
167
|
+
# @param locals [Array<String>] paths to local files or directories
|
168
|
+
# @param remote [String] path to remote destination
|
169
|
+
# @raise [TransportFailed] if the files could not all be uploaded
|
170
|
+
# successfully, which may vary by implementation
|
171
|
+
def upload(locals, remote)
|
172
|
+
unless file(remote).directory?
|
173
|
+
raise TransportError, "#{self.class} expects remote directory as second upload parameter"
|
174
|
+
end
|
175
|
+
|
176
|
+
Array(locals).each do |local|
|
177
|
+
new_content = File.read(local)
|
178
|
+
remote_file = File.join(remote, File.basename(local))
|
179
|
+
|
180
|
+
logger.debug("Attempting to upload '#{local}' as file #{remote_file}")
|
181
|
+
|
182
|
+
file(remote_file).content = new_content
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Download remote files or directories to local host.
|
187
|
+
#
|
188
|
+
# @param remotes [Array<String>] paths to remote files or directories
|
189
|
+
# @param local [String] path to local destination. If `local` is an
|
190
|
+
# existing directory, `remote` will be downloaded into the directory
|
191
|
+
# using its original name
|
192
|
+
# @raise [TransportFailed] if the files could not all be downloaded
|
193
|
+
# successfully, which may vary by implementation
|
194
|
+
def download(remotes, local)
|
195
|
+
FileUtils.mkdir_p(File.dirname(local))
|
196
|
+
|
197
|
+
Array(remotes).each do |remote|
|
198
|
+
new_content = file(remote).content
|
199
|
+
local_file = File.join(local, File.basename(remote))
|
200
|
+
|
201
|
+
logger.debug("Attempting to download '#{remote}' as file #{local_file}")
|
202
|
+
|
203
|
+
File.open(local_file, "w") { |fp| fp.write(new_content) }
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
164
207
|
# Builds a LoginCommand which can be used to open an interactive
|
165
208
|
# session on the remote host.
|
166
209
|
#
|
@@ -29,6 +29,14 @@ class Train::Transports::SSH
|
|
29
29
|
result.stdout.split(" ")[-1]
|
30
30
|
end
|
31
31
|
|
32
|
+
def upload(locals, remote)
|
33
|
+
raise NotImplementedError, "#{self.class} does not implement #upload()"
|
34
|
+
end
|
35
|
+
|
36
|
+
def download(remotes, local)
|
37
|
+
raise NotImplementedError, "#{self.class} does not implement #download()"
|
38
|
+
end
|
39
|
+
|
32
40
|
private
|
33
41
|
|
34
42
|
def establish_connection
|
@@ -39,6 +39,18 @@ module Train::Transports
|
|
39
39
|
"local://"
|
40
40
|
end
|
41
41
|
|
42
|
+
def upload(locals, remote)
|
43
|
+
FileUtils.mkdir_p(remote)
|
44
|
+
|
45
|
+
Array(locals).each do |local|
|
46
|
+
FileUtils.cp_r(local, remote)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def download(remotes, local)
|
51
|
+
upload(remotes, local)
|
52
|
+
end
|
53
|
+
|
42
54
|
private
|
43
55
|
|
44
56
|
def select_runner(options)
|
@@ -74,9 +86,9 @@ module Train::Transports
|
|
74
86
|
end
|
75
87
|
end
|
76
88
|
|
77
|
-
def run_command_via_connection(cmd, &_data_handler)
|
89
|
+
def run_command_via_connection(cmd, opts, &_data_handler)
|
78
90
|
# Use the runner if it is available
|
79
|
-
return @runner.run_command(cmd) if defined?(@runner)
|
91
|
+
return @runner.run_command(cmd, opts) if defined?(@runner)
|
80
92
|
|
81
93
|
# If we don't have a runner, such as at the beginning of setting up the
|
82
94
|
# transport and performing the first few steps of OS detection, fall
|
@@ -103,13 +115,18 @@ module Train::Transports
|
|
103
115
|
@cmd_wrapper = Local::CommandWrapper.load(connection, options)
|
104
116
|
end
|
105
117
|
|
106
|
-
def run_command(cmd)
|
118
|
+
def run_command(cmd, opts = {})
|
107
119
|
if defined?(@cmd_wrapper) && !@cmd_wrapper.nil?
|
108
120
|
cmd = @cmd_wrapper.run(cmd)
|
109
121
|
end
|
110
122
|
|
111
123
|
res = Mixlib::ShellOut.new(cmd)
|
112
|
-
res.
|
124
|
+
res.timeout = opts[:timeout]
|
125
|
+
begin
|
126
|
+
res.run_command
|
127
|
+
rescue Mixlib::ShellOut::CommandTimeout
|
128
|
+
raise Train::CommandTimeoutReached
|
129
|
+
end
|
113
130
|
Local::CommandResult.new(res.stdout, res.stderr, res.exitstatus)
|
114
131
|
end
|
115
132
|
|
@@ -126,7 +143,7 @@ module Train::Transports
|
|
126
143
|
@powershell_cmd = powershell_cmd
|
127
144
|
end
|
128
145
|
|
129
|
-
def run_command(script)
|
146
|
+
def run_command(script, opts)
|
130
147
|
# Prevent progress stream from leaking into stderr
|
131
148
|
script = "$ProgressPreference='SilentlyContinue';" + script
|
132
149
|
|
@@ -137,7 +154,12 @@ module Train::Transports
|
|
137
154
|
cmd = "#{@powershell_cmd} -NoProfile -EncodedCommand #{base64_script}"
|
138
155
|
|
139
156
|
res = Mixlib::ShellOut.new(cmd)
|
140
|
-
res.
|
157
|
+
res.timeout = opts[:timeout]
|
158
|
+
begin
|
159
|
+
res.run_command
|
160
|
+
rescue Mixlib::ShellOut::CommandTimeout
|
161
|
+
raise Train::CommandTimeoutReached
|
162
|
+
end
|
141
163
|
Local::CommandResult.new(res.stdout, res.stderr, res.exitstatus)
|
142
164
|
end
|
143
165
|
|
@@ -164,9 +186,10 @@ module Train::Transports
|
|
164
186
|
# A command that succeeds without setting an exit code will have exitstatus 0
|
165
187
|
# A command that exits with an exit code will have that value as exitstatus
|
166
188
|
# A command that fails (e.g. throws exception) before setting an exit code will have exitstatus 1
|
167
|
-
def run_command(cmd)
|
189
|
+
def run_command(cmd, _opts)
|
168
190
|
script = "$ProgressPreference='SilentlyContinue';" + cmd
|
169
191
|
encoded_script = Base64.strict_encode64(script)
|
192
|
+
# TODO: no way to safely implement timeouts here.
|
170
193
|
@pipe.puts(encoded_script)
|
171
194
|
@pipe.flush
|
172
195
|
res = OpenStruct.new(JSON.parse(Base64.decode64(@pipe.readline)))
|
@@ -187,7 +210,7 @@ module Train::Transports
|
|
187
210
|
@server_pid = start_pipe_server(pipe_name)
|
188
211
|
|
189
212
|
# Ensure process is killed when the Train process exits
|
190
|
-
at_exit { close }
|
213
|
+
at_exit { close rescue Errno::EIO }
|
191
214
|
|
192
215
|
pipe = nil
|
193
216
|
|
@@ -254,4 +277,4 @@ module Train::Transports
|
|
254
277
|
end
|
255
278
|
end
|
256
279
|
end
|
257
|
-
end
|
280
|
+
end
|
@@ -323,7 +323,7 @@ class Train::Transports::SSH
|
|
323
323
|
|
324
324
|
logger.debug("[SSH] #{self} cmd = #{cmd}")
|
325
325
|
|
326
|
-
if @transport_options[:pty]
|
326
|
+
if @transport_options[:pty]
|
327
327
|
channel.request_pty do |_ch, success|
|
328
328
|
raise Train::Transports::SSHPTYFailed, "Requesting PTY failed" unless success
|
329
329
|
end
|
data/lib/train/version.rb
CHANGED
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.
|
4
|
+
version: 3.6.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chef InSpec Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-04-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|