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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6272024b67e2dac1df86499e4687dc44d5df58b328146d52cb864433d7c6e6a7
4
- data.tar.gz: a4133877056cc7feed58ac9dab964ecfc435d28cc30a09d6dd104577a8a30374
3
+ metadata.gz: bc67d13ca7a06113a56b506c659ad2a8b7cb0ce9468a632aad6d9716d2bfbbb3
4
+ data.tar.gz: 8f77e2c0ed1e8e18f55b8d96f2a74c43cb135818a3d945c9740c26f37bed3475
5
5
  SHA512:
6
- metadata.gz: 53bcc25af9bade96ee63aa1f0940927cb4e4183220737269bb4882c800df94c1bf6cb924abf5cb6b354b18ae000b6fba3d7a35787a3764d9402cd63f7b90bdd2
7
- data.tar.gz: 775d8a24be41a87f8427f3850b829b1acbc78d2a9ef8e299e1e4c9f9dbd352838d7bd9ef906723832171d0f822a10dac39827ab8a1b92c7ebec220bf37e337dd
6
+ metadata.gz: 32b328597cccde06aa86d32fc84d3255c66310c2b8544f4fe1aebf4bfd42b93e422a04e69a85b281954956f6e2ff8c035c672ac64c18fd89107edf94cd666049
7
+ data.tar.gz: 2eb61ddb2225dfa731fb32f1895b35703b152d80a23a46f0e2550b829bbbac955f753c028d50bb7bce020f692416934b908d35379ccefeaa6b4a17efbc58dd97
@@ -15,6 +15,12 @@ module Train
15
15
  nil
16
16
  end
17
17
 
18
+ def content=(new_content)
19
+ ::File.open(@path, "w", encoding: "UTF-8") { |fp| fp.write(new_content) }
20
+
21
+ @content = new_content
22
+ end
23
+
18
24
  def link_path
19
25
  return nil unless symlink?
20
26
 
@@ -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.run_command
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.run_command
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] || timeout
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
@@ -2,5 +2,5 @@
2
2
  # Author:: Dominik Richter (<dominik.richter@gmail.com>)
3
3
 
4
4
  module Train
5
- VERSION = "3.4.9".freeze
5
+ VERSION = "3.6.2".freeze
6
6
  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.4.9
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-01-27 00:00:00.000000000 Z
11
+ date: 2021-04-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable