train-core 3.4.9 → 3.6.2

Sign up to get free protection for your applications and to get access to all the features.
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