bolt 2.19.0 → 2.24.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bolt might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Puppetfile +3 -1
- data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +123 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +6 -0
- data/bolt-modules/ctrl/lib/puppet/functions/ctrl/do_until.rb +12 -6
- data/bolt-modules/dir/lib/puppet/functions/dir/children.rb +35 -0
- data/bolt-modules/out/lib/puppet/functions/out/message.rb +1 -1
- data/exe/bolt +1 -0
- data/guides/inventory.txt +19 -0
- data/guides/project.txt +22 -0
- data/lib/bolt/analytics.rb +5 -5
- data/lib/bolt/applicator.rb +4 -3
- data/lib/bolt/bolt_option_parser.rb +100 -27
- data/lib/bolt/catalog.rb +12 -3
- data/lib/bolt/cli.rb +356 -156
- data/lib/bolt/config.rb +2 -2
- data/lib/bolt/config/options.rb +18 -4
- data/lib/bolt/executor.rb +30 -7
- data/lib/bolt/inventory/group.rb +6 -5
- data/lib/bolt/inventory/inventory.rb +4 -3
- data/lib/bolt/logger.rb +3 -4
- data/lib/bolt/module.rb +2 -1
- data/lib/bolt/outputter.rb +56 -0
- data/lib/bolt/outputter/human.rb +10 -9
- data/lib/bolt/outputter/json.rb +11 -4
- data/lib/bolt/outputter/logger.rb +2 -2
- data/lib/bolt/outputter/rainbow.rb +18 -2
- data/lib/bolt/pal.rb +13 -11
- data/lib/bolt/pal/yaml_plan/evaluator.rb +22 -1
- data/lib/bolt/pal/yaml_plan/step.rb +24 -2
- data/lib/bolt/pal/yaml_plan/step/download.rb +38 -0
- data/lib/bolt/pal/yaml_plan/step/message.rb +30 -0
- data/lib/bolt/pal/yaml_plan/step/upload.rb +3 -3
- data/lib/bolt/pal/yaml_plan/transpiler.rb +11 -3
- data/lib/bolt/plugin/prompt.rb +3 -3
- data/lib/bolt/plugin/puppetdb.rb +3 -2
- data/lib/bolt/project.rb +7 -4
- data/lib/bolt/project_migrate.rb +138 -0
- data/lib/bolt/puppetdb/client.rb +2 -0
- data/lib/bolt/puppetdb/config.rb +16 -0
- data/lib/bolt/result.rb +7 -0
- data/lib/bolt/shell/bash.rb +31 -11
- data/lib/bolt/shell/powershell.rb +10 -4
- data/lib/bolt/transport/base.rb +24 -0
- data/lib/bolt/transport/docker.rb +8 -0
- data/lib/bolt/transport/docker/connection.rb +28 -10
- data/lib/bolt/transport/local/connection.rb +15 -2
- data/lib/bolt/transport/orch.rb +15 -3
- data/lib/bolt/transport/simple.rb +6 -0
- data/lib/bolt/transport/ssh/connection.rb +13 -5
- data/lib/bolt/transport/ssh/exec_connection.rb +24 -3
- data/lib/bolt/transport/winrm/connection.rb +125 -15
- data/lib/bolt/util.rb +27 -12
- data/lib/bolt/util/puppet_log_level.rb +4 -3
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/base_config.rb +1 -1
- data/lib/bolt_server/pe/pal.rb +1 -1
- data/lib/bolt_server/transport_app.rb +79 -2
- data/lib/bolt_spec/bolt_context.rb +7 -2
- data/lib/bolt_spec/plans.rb +16 -3
- data/lib/bolt_spec/plans/action_stubs.rb +3 -2
- data/lib/bolt_spec/plans/action_stubs/download_stub.rb +66 -0
- data/lib/bolt_spec/plans/mock_executor.rb +14 -1
- data/lib/bolt_spec/run.rb +22 -0
- data/libexec/apply_catalog.rb +2 -2
- data/libexec/bolt_catalog +4 -3
- data/libexec/custom_facts.rb +1 -1
- data/libexec/query_resources.rb +1 -1
- data/modules/secure_env_vars/plans/init.pp +20 -0
- metadata +11 -2
data/lib/bolt/result.rb
CHANGED
@@ -79,6 +79,13 @@ module Bolt
|
|
79
79
|
new(target, message: "Uploaded '#{source}' to '#{target.host}:#{destination}'", action: 'upload', object: source)
|
80
80
|
end
|
81
81
|
|
82
|
+
def self.for_download(target, source, destination, download)
|
83
|
+
msg = "Downloaded '#{target.host}:#{source}' to '#{destination}'"
|
84
|
+
value = { 'path' => download }
|
85
|
+
|
86
|
+
new(target, value: value, message: msg, action: 'download', object: source)
|
87
|
+
end
|
88
|
+
|
82
89
|
# Satisfies the Puppet datatypes API
|
83
90
|
def self.from_asserted_args(target, value)
|
84
91
|
new(target, value: value)
|
data/lib/bolt/shell/bash.rb
CHANGED
@@ -37,7 +37,7 @@ module Bolt
|
|
37
37
|
with_tmpdir do |dir|
|
38
38
|
basename = File.basename(source)
|
39
39
|
tmpfile = File.join(dir.to_s, basename)
|
40
|
-
conn.
|
40
|
+
conn.upload_file(source, tmpfile)
|
41
41
|
# pass over file ownership if we're using run-as to be a different user
|
42
42
|
dir.chown(run_as)
|
43
43
|
result = execute(['mv', '-f', tmpfile, destination], sudoable: true)
|
@@ -50,6 +50,27 @@ module Bolt
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
+
def download(source, destination, options = {})
|
54
|
+
running_as(options[:run_as]) do
|
55
|
+
# Target OS may be either Unix or Windows. Without knowing the target OS before-hand
|
56
|
+
# we can't assume whether the path separator is '/' or '\'. Assume we're connecting
|
57
|
+
# to a target with Unix and then check if the path exists after downloading.
|
58
|
+
download = File.join(destination, Bolt::Util.unix_basename(source))
|
59
|
+
|
60
|
+
conn.download_file(source, destination, download)
|
61
|
+
|
62
|
+
# If the download path doesn't exist, then the file was likely downloaded from Windows
|
63
|
+
# using a source path with backslashes (e.g. 'C:\Users\Administrator\foo'). The file
|
64
|
+
# should be saved to the expected location, so update the download path assuming a
|
65
|
+
# Windows basename so the result shows the correct local path.
|
66
|
+
unless File.exist?(download)
|
67
|
+
download = File.join(destination, Bolt::Util.windows_basename(source))
|
68
|
+
end
|
69
|
+
|
70
|
+
Bolt::Result.for_download(target, source, destination, download)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
53
74
|
def run_script(script, arguments, options = {})
|
54
75
|
# unpack any Sensitive data
|
55
76
|
arguments = unwrap_sensitive_args(arguments)
|
@@ -82,7 +103,7 @@ module Bolt
|
|
82
103
|
" using '#{execute_options[:interpreter]}' interpreter"
|
83
104
|
end
|
84
105
|
# log the arguments with sensitive data redacted, do NOT log unwrapped_arguments
|
85
|
-
logger.
|
106
|
+
logger.trace("Running '#{executable}' with #{arguments.to_json}#{interpreter_debug}")
|
86
107
|
# unpack any Sensitive data
|
87
108
|
arguments = unwrap_sensitive_args(arguments)
|
88
109
|
|
@@ -95,7 +116,7 @@ module Bolt
|
|
95
116
|
task_dir = File.join(dir.to_s, task.tasks_dir)
|
96
117
|
dir.mkdirs([task.tasks_dir] + extra_files.map { |file| File.dirname(file['name']) })
|
97
118
|
extra_files.each do |file|
|
98
|
-
conn.
|
119
|
+
conn.upload_file(file['path'], File.join(dir.to_s, file['name']))
|
99
120
|
end
|
100
121
|
end
|
101
122
|
|
@@ -182,13 +203,13 @@ module Bolt
|
|
182
203
|
|
183
204
|
def handle_sudo_errors(err)
|
184
205
|
if err =~ /^#{conn.user} is not in the sudoers file\./
|
185
|
-
@logger.
|
206
|
+
@logger.trace { err }
|
186
207
|
raise Bolt::Node::EscalateError.new(
|
187
208
|
"User #{conn.user} does not have sudo permission on #{target}",
|
188
209
|
'SUDO_DENIED'
|
189
210
|
)
|
190
211
|
elsif err =~ /^Sorry, try again\./
|
191
|
-
@logger.
|
212
|
+
@logger.trace { err }
|
192
213
|
raise Bolt::Node::EscalateError.new(
|
193
214
|
"Sudo password for user #{conn.user} not recognized on #{target}",
|
194
215
|
'BAD_PASSWORD'
|
@@ -257,7 +278,7 @@ module Bolt
|
|
257
278
|
def write_executable(dir, file, filename = nil)
|
258
279
|
filename ||= File.basename(file)
|
259
280
|
remote_path = File.join(dir.to_s, filename)
|
260
|
-
conn.
|
281
|
+
conn.upload_file(file, remote_path)
|
261
282
|
make_executable(remote_path)
|
262
283
|
remote_path
|
263
284
|
end
|
@@ -314,7 +335,6 @@ module Bolt
|
|
314
335
|
sudo_str = if use_sudo
|
315
336
|
sudo_exec = target.options['sudo-executable'] || "sudo"
|
316
337
|
sudo_flags = [sudo_exec, "-S", "-H", "-u", run_as, "-p", sudo_prompt]
|
317
|
-
sudo_flags += ["-E"] if options[:environment]
|
318
338
|
Shellwords.shelljoin(sudo_flags)
|
319
339
|
else
|
320
340
|
Shellwords.shelljoin(@target.options['run-as-command'] + [run_as])
|
@@ -331,7 +351,7 @@ module Bolt
|
|
331
351
|
|
332
352
|
command_str = [sudo_str, env_decl, command_str].compact.join(' ')
|
333
353
|
|
334
|
-
@logger.
|
354
|
+
@logger.trace { "Executing `#{command_str}`" }
|
335
355
|
|
336
356
|
in_buffer = if !use_sudo && options[:stdin]
|
337
357
|
String.new(options[:stdin], encoding: 'binary')
|
@@ -411,16 +431,16 @@ module Bolt
|
|
411
431
|
result_output.exit_code = t.value.respond_to?(:exitstatus) ? t.value.exitstatus : t.value
|
412
432
|
|
413
433
|
if result_output.exit_code == 0
|
414
|
-
@logger.
|
434
|
+
@logger.trace { "Command `#{command_str}` returned successfully" }
|
415
435
|
else
|
416
|
-
@logger.
|
436
|
+
@logger.trace { "Command #{command_str} failed with exit code #{result_output.exit_code}" }
|
417
437
|
end
|
418
438
|
result_output
|
419
439
|
rescue StandardError
|
420
440
|
# Ensure we close stdin and kill the child process
|
421
441
|
inp&.close
|
422
442
|
t&.terminate if t&.alive?
|
423
|
-
@logger.
|
443
|
+
@logger.trace { "Command aborted" }
|
424
444
|
raise
|
425
445
|
end
|
426
446
|
|
@@ -85,7 +85,7 @@ module Bolt
|
|
85
85
|
filename ||= File.basename(file)
|
86
86
|
validate_extensions(File.extname(filename))
|
87
87
|
destination = "#{dir}\\#{filename}"
|
88
|
-
conn.
|
88
|
+
conn.upload_file(file, destination)
|
89
89
|
destination
|
90
90
|
end
|
91
91
|
|
@@ -164,10 +164,16 @@ module Bolt
|
|
164
164
|
end
|
165
165
|
|
166
166
|
def upload(source, destination, _options = {})
|
167
|
-
conn.
|
167
|
+
conn.upload_file(source, destination)
|
168
168
|
Bolt::Result.for_upload(target, source, destination)
|
169
169
|
end
|
170
170
|
|
171
|
+
def download(source, destination, _options = {})
|
172
|
+
download = File.join(destination, Bolt::Util.windows_basename(source))
|
173
|
+
conn.download_file(source, destination, download)
|
174
|
+
Bolt::Result.for_download(target, source, destination, download)
|
175
|
+
end
|
176
|
+
|
171
177
|
def run_command(command, options = {})
|
172
178
|
command = [*env_declarations(options[:env_vars]), command].join("\r\n") if options[:env_vars]
|
173
179
|
|
@@ -220,7 +226,7 @@ module Bolt
|
|
220
226
|
task_dir = File.join(dir, task.tasks_dir)
|
221
227
|
mkdirs([task_dir] + extra_files.map { |file| File.join(dir, File.dirname(file['name'])) })
|
222
228
|
extra_files.each do |file|
|
223
|
-
conn.
|
229
|
+
conn.upload_file(file['path'], File.join(dir, file['name']))
|
224
230
|
end
|
225
231
|
end
|
226
232
|
|
@@ -262,7 +268,7 @@ module Bolt
|
|
262
268
|
return with_tmpdir do |dir|
|
263
269
|
command += "\r\nif (!$?) { if($LASTEXITCODE) { exit $LASTEXITCODE } else { exit 1 } }"
|
264
270
|
script_file = File.join(dir, "#{SecureRandom.uuid}_wrapper.ps1")
|
265
|
-
conn.
|
271
|
+
conn.upload_file(StringIO.new(command), script_file)
|
266
272
|
args = escape_arguments([script_file])
|
267
273
|
script_invocation = ['powershell.exe', *PS_ARGS, '-File', *args].join(' ')
|
268
274
|
execute(script_invocation)
|
data/lib/bolt/transport/base.rb
CHANGED
@@ -167,6 +167,25 @@ module Bolt
|
|
167
167
|
end
|
168
168
|
end
|
169
169
|
|
170
|
+
# Downloads the given source file from a batch of targets to the destination location
|
171
|
+
# on the host.
|
172
|
+
#
|
173
|
+
# The default implementation only supports batches of size 1 and will fail otherwise.
|
174
|
+
#
|
175
|
+
# Transports may override this method to implement their own batch processing.
|
176
|
+
def batch_download(targets, source, destination, options = {}, &callback)
|
177
|
+
require 'erb'
|
178
|
+
|
179
|
+
assert_batch_size_one("batch_download()", targets)
|
180
|
+
target = targets.first
|
181
|
+
with_events(target, callback, 'download') do
|
182
|
+
escaped_name = ERB::Util.url_encode(target.safe_name)
|
183
|
+
target_destination = File.expand_path(escaped_name, destination)
|
184
|
+
@logger.debug { "Downloading: '#{source}' on #{target.safe_name} to #{target_destination}" }
|
185
|
+
download(target, source, target_destination, options)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
170
189
|
def batch_connected?(targets)
|
171
190
|
assert_batch_size_one("connected?()", targets)
|
172
191
|
connected?(targets.first)
|
@@ -201,6 +220,11 @@ module Bolt
|
|
201
220
|
raise NotImplementedError, "upload() must be implemented by the transport class"
|
202
221
|
end
|
203
222
|
|
223
|
+
# Transports should override this method with their own implementation of file download.
|
224
|
+
def download(*_args)
|
225
|
+
raise NotImplementedError, "download() must be implemented by the transport class"
|
226
|
+
end
|
227
|
+
|
204
228
|
# Transports should override this method with their own implementation of a connection test.
|
205
229
|
def connected?(_targets)
|
206
230
|
raise NotImplementedError, "connected?() must be implemented by the transport class"
|
@@ -38,6 +38,14 @@ module Bolt
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
+
def download(target, source, destination, _options = {})
|
42
|
+
with_connection(target) do |conn|
|
43
|
+
download = File.join(destination, Bolt::Util.unix_basename(source))
|
44
|
+
conn.download_remote_content(source, destination)
|
45
|
+
Bolt::Result.for_download(target, source, destination, download)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
41
49
|
def run_command(target, command, options = {})
|
42
50
|
execute_options = {}
|
43
51
|
execute_options[:tty] = target.options['tty']
|
@@ -12,7 +12,7 @@ module Bolt
|
|
12
12
|
@target = target
|
13
13
|
@logger = Logging.logger[target.safe_name]
|
14
14
|
@docker_host = @target.options['service-url']
|
15
|
-
@logger.
|
15
|
+
@logger.trace("Initializing docker connection to #{@target.safe_name}")
|
16
16
|
end
|
17
17
|
|
18
18
|
def connect
|
@@ -25,7 +25,7 @@ module Bolt
|
|
25
25
|
output = execute_local_docker_json_command('inspect', [output[index]["ID"]])
|
26
26
|
# Store the container information for later
|
27
27
|
@container_info = output[0]
|
28
|
-
@logger.
|
28
|
+
@logger.trace { "Opened session" }
|
29
29
|
true
|
30
30
|
rescue StandardError => e
|
31
31
|
raise Bolt::Node::ConnectError.new(
|
@@ -57,16 +57,16 @@ module Bolt
|
|
57
57
|
command_options << container_id
|
58
58
|
command_options.concat(command)
|
59
59
|
|
60
|
-
@logger.
|
60
|
+
@logger.trace { "Executing: exec #{command_options}" }
|
61
61
|
|
62
62
|
stdout_str, stderr_str, status = execute_local_docker_command('exec', command_options, options[:stdin])
|
63
63
|
|
64
64
|
# The actual result is the exitstatus not the process object
|
65
65
|
status = status.nil? ? -32768 : status.exitstatus
|
66
66
|
if status == 0
|
67
|
-
@logger.
|
67
|
+
@logger.trace { "Command returned successfully" }
|
68
68
|
else
|
69
|
-
@logger.
|
69
|
+
@logger.trace { "Command failed with exit code #{status}" }
|
70
70
|
end
|
71
71
|
stdout_str.force_encoding(Encoding::UTF_8)
|
72
72
|
stderr_str.force_encoding(Encoding::UTF_8)
|
@@ -75,22 +75,40 @@ module Bolt
|
|
75
75
|
stderr_str.gsub!("\r\n", "\n")
|
76
76
|
[stdout_str, stderr_str, status]
|
77
77
|
rescue StandardError
|
78
|
-
@logger.
|
78
|
+
@logger.trace { "Command aborted" }
|
79
79
|
raise
|
80
80
|
end
|
81
81
|
|
82
82
|
def write_remote_file(source, destination)
|
83
|
-
@logger.
|
83
|
+
@logger.trace { "Uploading #{source} to #{destination}" }
|
84
84
|
_, stdout_str, status = execute_local_docker_command('cp', [source, "#{container_id}:#{destination}"])
|
85
|
-
|
85
|
+
unless status.exitstatus.zero?
|
86
|
+
raise "Error writing file to container #{@container_id}: #{stdout_str}"
|
87
|
+
end
|
86
88
|
rescue StandardError => e
|
87
89
|
raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
|
88
90
|
end
|
89
91
|
|
90
92
|
def write_remote_directory(source, destination)
|
91
|
-
@logger.
|
93
|
+
@logger.trace { "Uploading #{source} to #{destination}" }
|
92
94
|
_, stdout_str, status = execute_local_docker_command('cp', [source, "#{container_id}:#{destination}"])
|
93
|
-
|
95
|
+
unless status.exitstatus.zero?
|
96
|
+
raise "Error writing directory to container #{@container_id}: #{stdout_str}"
|
97
|
+
end
|
98
|
+
rescue StandardError => e
|
99
|
+
raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
|
100
|
+
end
|
101
|
+
|
102
|
+
def download_remote_content(source, destination)
|
103
|
+
@logger.trace { "Downloading #{source} to #{destination}" }
|
104
|
+
# Create the destination directory, otherwise copying a source directory with Docker will
|
105
|
+
# copy the *contents* of the directory.
|
106
|
+
# https://docs.docker.com/engine/reference/commandline/cp/
|
107
|
+
FileUtils.mkdir_p(destination)
|
108
|
+
_, stdout_str, status = execute_local_docker_command('cp', ["#{container_id}:#{source}", destination])
|
109
|
+
unless status.exitstatus.zero?
|
110
|
+
raise "Error downloading content from container #{@container_id}: #{stdout_str}"
|
111
|
+
end
|
94
112
|
rescue StandardError => e
|
95
113
|
raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
|
96
114
|
end
|
@@ -27,8 +27,8 @@ module Bolt
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
def
|
31
|
-
@logger.
|
30
|
+
def upload_file(source, dest)
|
31
|
+
@logger.trace { "Uploading #{source} to #{dest}" }
|
32
32
|
if source.is_a?(StringIO)
|
33
33
|
Tempfile.create(File.basename(dest)) do |f|
|
34
34
|
f.write(source.read)
|
@@ -45,6 +45,19 @@ module Bolt
|
|
45
45
|
raise Bolt::Node::FileError.new(message, 'COPY_ERROR')
|
46
46
|
end
|
47
47
|
|
48
|
+
def download_file(source, dest, _download)
|
49
|
+
@logger.trace { "Downloading #{source} to #{dest}" }
|
50
|
+
# Create the destination directory for the target, or the
|
51
|
+
# copied file will have the target's name
|
52
|
+
FileUtils.mkdir_p(dest)
|
53
|
+
# Mimic the behavior of `cp --remove-destination`
|
54
|
+
# since the flag isn't supported on MacOS
|
55
|
+
FileUtils.cp_r(source, dest, remove_destination: true)
|
56
|
+
rescue StandardError => e
|
57
|
+
message = "Could not download file to #{dest}: #{e}"
|
58
|
+
raise Bolt::Node::FileError.new(message, 'DOWNLOAD_ERROR')
|
59
|
+
end
|
60
|
+
|
48
61
|
def execute(command)
|
49
62
|
if Bolt::Util.windows?
|
50
63
|
# If it's already a powershell command then invoke it normally.
|
data/lib/bolt/transport/orch.rb
CHANGED
@@ -38,7 +38,7 @@ module Bolt
|
|
38
38
|
@connections.each_value do |conn|
|
39
39
|
conn.finish_plan(result)
|
40
40
|
rescue StandardError => e
|
41
|
-
@logger.
|
41
|
+
@logger.trace("Failed to finish plan on #{conn.key}: #{e.message}")
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
@@ -133,7 +133,7 @@ module Bolt
|
|
133
133
|
next unless File.file?(file)
|
134
134
|
|
135
135
|
tar_path = Pathname.new(file).relative_path_from(Pathname.new(directory))
|
136
|
-
@logger.
|
136
|
+
@logger.trace("Packing #{file} to #{tar_path}")
|
137
137
|
stat = File.stat(file)
|
138
138
|
content = File.binread(file)
|
139
139
|
output.tar.add_file_simple(
|
@@ -146,7 +146,7 @@ module Bolt
|
|
146
146
|
end
|
147
147
|
|
148
148
|
duration = Time.now - start_time
|
149
|
-
@logger.
|
149
|
+
@logger.trace("Packed upload in #{duration * 1000} ms")
|
150
150
|
|
151
151
|
output.close
|
152
152
|
io.string
|
@@ -184,6 +184,18 @@ module Bolt
|
|
184
184
|
end
|
185
185
|
end
|
186
186
|
|
187
|
+
def batch_download(targets, *_args)
|
188
|
+
error = {
|
189
|
+
'kind' => 'bolt/not-supported-error',
|
190
|
+
'msg' => 'pcp transport does not support downloading files',
|
191
|
+
'details' => {}
|
192
|
+
}
|
193
|
+
|
194
|
+
targets.map do |target|
|
195
|
+
Bolt::Result.new(target, error: error, action: 'download')
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
187
199
|
def batches(targets)
|
188
200
|
targets.group_by { |target| Connection.get_key(target.options) }.values
|
189
201
|
end
|
@@ -32,6 +32,12 @@ module Bolt
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
+
def download(target, source, destination, options = {})
|
36
|
+
with_connection(target) do |conn|
|
37
|
+
conn.shell.download(source, destination, options)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
35
41
|
def run_script(target, script, arguments, options = {})
|
36
42
|
with_connection(target) do |conn|
|
37
43
|
conn.shell.run_script(script, arguments, options)
|
@@ -28,7 +28,7 @@ module Bolt
|
|
28
28
|
|
29
29
|
@logger = Logging.logger[@target.safe_name]
|
30
30
|
@transport_logger = transport_logger
|
31
|
-
@logger.
|
31
|
+
@logger.trace("Initializing ssh connection to #{@target.safe_name}")
|
32
32
|
|
33
33
|
if target.options['private-key']&.instance_of?(String)
|
34
34
|
begin
|
@@ -131,7 +131,7 @@ module Bolt
|
|
131
131
|
|
132
132
|
@session = Net::SSH.start(target.host, @user, options)
|
133
133
|
validate_ssh_version
|
134
|
-
@logger.
|
134
|
+
@logger.trace { "Opened session" }
|
135
135
|
rescue Net::SSH::AuthenticationFailed => e
|
136
136
|
raise Bolt::Node::ConnectError.new(
|
137
137
|
e.message,
|
@@ -161,7 +161,7 @@ module Bolt
|
|
161
161
|
rescue Timeout::Error
|
162
162
|
@session.shutdown!
|
163
163
|
end
|
164
|
-
@logger.
|
164
|
+
@logger.trace { "Closed session" }
|
165
165
|
end
|
166
166
|
end
|
167
167
|
|
@@ -235,14 +235,22 @@ module Bolt
|
|
235
235
|
raise Bolt::Error.new(msg, 'bolt/too-many-files')
|
236
236
|
end
|
237
237
|
|
238
|
-
def
|
238
|
+
def upload_file(source, destination)
|
239
239
|
# Do not log wrapper script content
|
240
|
-
@logger.
|
240
|
+
@logger.trace { "Uploading #{source} to #{destination}" } unless source.is_a?(StringIO)
|
241
241
|
@session.scp.upload!(source, destination, recursive: true)
|
242
242
|
rescue StandardError => e
|
243
243
|
raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
|
244
244
|
end
|
245
245
|
|
246
|
+
def download_file(source, destination, _download)
|
247
|
+
# Do not log wrapper script content
|
248
|
+
@logger.trace { "Downloading #{source} to #{destination}" }
|
249
|
+
@session.scp.download!(source, destination, recursive: true)
|
250
|
+
rescue StandardError => e
|
251
|
+
raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
|
252
|
+
end
|
253
|
+
|
246
254
|
# This handles renaming Net::SSH verifiers between version 4.x and 5.x
|
247
255
|
# of the gem
|
248
256
|
def net_ssh_verifier(verifier)
|