bolt 2.15.0 → 2.20.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/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/add_to_group.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +20 -9
- data/bolt-modules/boltlib/lib/puppet/functions/catch_errors.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +123 -0
- data/bolt-modules/boltlib/lib/puppet/functions/facts.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/fail_plan.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_resources.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_target.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_targets.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_fact.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/remove_from_group.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/resolve_references.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/resource.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +3 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +2 -1
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +7 -4
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +2 -1
- data/bolt-modules/boltlib/lib/puppet/functions/set_config.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_feature.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_resources.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_var.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/vars.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/wait_until_available.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/without_default_logging.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/write_file.rb +1 -0
- data/bolt-modules/ctrl/lib/puppet/functions/ctrl/do_until.rb +2 -0
- data/bolt-modules/ctrl/lib/puppet/functions/ctrl/sleep.rb +2 -0
- data/bolt-modules/file/lib/puppet/functions/file/exists.rb +1 -0
- data/bolt-modules/file/lib/puppet/functions/file/join.rb +2 -0
- data/bolt-modules/file/lib/puppet/functions/file/read.rb +2 -0
- data/bolt-modules/file/lib/puppet/functions/file/readable.rb +2 -0
- data/bolt-modules/file/lib/puppet/functions/file/write.rb +2 -0
- data/bolt-modules/out/lib/puppet/functions/out/message.rb +2 -0
- data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +1 -0
- data/bolt-modules/system/lib/puppet/functions/system/env.rb +2 -0
- data/lib/bolt/applicator.rb +21 -15
- data/lib/bolt/apply_result.rb +1 -1
- data/lib/bolt/bolt_option_parser.rb +55 -20
- data/lib/bolt/catalog.rb +3 -2
- data/lib/bolt/cli.rb +116 -47
- data/lib/bolt/config.rb +48 -148
- data/lib/bolt/config/options.rb +488 -0
- data/lib/bolt/config/transport/base.rb +16 -16
- data/lib/bolt/config/transport/docker.rb +9 -23
- data/lib/bolt/config/transport/local.rb +6 -44
- data/lib/bolt/config/transport/options.rb +460 -0
- data/lib/bolt/config/transport/orch.rb +9 -18
- data/lib/bolt/config/transport/remote.rb +3 -6
- data/lib/bolt/config/transport/ssh.rb +74 -154
- data/lib/bolt/config/transport/winrm.rb +18 -47
- data/lib/bolt/executor.rb +15 -0
- data/lib/bolt/inventory/group.rb +4 -3
- data/lib/bolt/inventory/inventory.rb +4 -17
- data/lib/bolt/inventory/target.rb +18 -5
- data/lib/bolt/logger.rb +24 -1
- data/lib/bolt/outputter.rb +1 -1
- data/lib/bolt/outputter/rainbow.rb +14 -3
- data/lib/bolt/pal.rb +31 -11
- data/lib/bolt/pal/yaml_plan/evaluator.rb +19 -2
- data/lib/bolt/pal/yaml_plan/step.rb +11 -2
- data/lib/bolt/pal/yaml_plan/step/download.rb +38 -0
- data/lib/bolt/pal/yaml_plan/step/upload.rb +3 -3
- data/lib/bolt/plugin/module.rb +2 -4
- data/lib/bolt/plugin/puppetdb.rb +3 -2
- data/lib/bolt/project.rb +41 -44
- 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 +53 -45
- data/lib/bolt/shell/powershell.rb +23 -12
- data/lib/bolt/shell/powershell/snippets.rb +15 -6
- data/lib/bolt/transport/base.rb +24 -0
- data/lib/bolt/transport/docker.rb +17 -5
- data/lib/bolt/transport/docker/connection.rb +20 -2
- data/lib/bolt/transport/local/connection.rb +14 -1
- data/lib/bolt/transport/orch.rb +20 -0
- data/lib/bolt/transport/simple.rb +6 -0
- data/lib/bolt/transport/ssh.rb +7 -1
- data/lib/bolt/transport/ssh/connection.rb +9 -1
- data/lib/bolt/transport/ssh/exec_connection.rb +23 -2
- data/lib/bolt/transport/winrm/connection.rb +109 -8
- data/lib/bolt/util.rb +26 -11
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/transport_app.rb +3 -2
- data/lib/bolt_spec/bolt_context.rb +7 -2
- data/lib/bolt_spec/plans.rb +15 -2
- data/lib/bolt_spec/plans/action_stubs.rb +2 -1
- 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/bolt_catalog +3 -2
- metadata +20 -29
data/lib/bolt/puppetdb/client.rb
CHANGED
@@ -96,6 +96,8 @@ module Bolt
|
|
96
96
|
@http = HTTPClient.new
|
97
97
|
@http.ssl_config.set_client_cert_file(@config.cert, @config.key) if @config.cert
|
98
98
|
@http.ssl_config.add_trust_ca(@config.cacert)
|
99
|
+
@http.connect_timeout = @config.connect_timeout if @config.connect_timeout
|
100
|
+
@http.receive_timeout = @config.read_timeout if @config.read_timeout
|
99
101
|
|
100
102
|
@http
|
101
103
|
end
|
data/lib/bolt/puppetdb/config.rb
CHANGED
@@ -132,6 +132,22 @@ module Bolt
|
|
132
132
|
end
|
133
133
|
end
|
134
134
|
|
135
|
+
def connect_timeout
|
136
|
+
validate_timeout('connect_timeout')
|
137
|
+
@settings['connect_timeout']
|
138
|
+
end
|
139
|
+
|
140
|
+
def read_timeout
|
141
|
+
validate_timeout('read_timeout')
|
142
|
+
@settings['read_timeout']
|
143
|
+
end
|
144
|
+
|
145
|
+
def validate_timeout(timeout)
|
146
|
+
unless @settings[timeout].nil? || (@settings[timeout].is_a?(Integer) && @settings[timeout] > 0)
|
147
|
+
raise Bolt::PuppetDBError, "#{timeout} must be a positive integer, received #{@settings[timeout]}"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
135
151
|
def to_hash
|
136
152
|
@settings.dup
|
137
153
|
end
|
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
@@ -23,7 +23,7 @@ module Bolt
|
|
23
23
|
|
24
24
|
def run_command(command, options = {})
|
25
25
|
running_as(options[:run_as]) do
|
26
|
-
output = execute(command, sudoable: true)
|
26
|
+
output = execute(command, environment: options[:env_vars], sudoable: true)
|
27
27
|
Bolt::Result.for_command(target,
|
28
28
|
output.stdout.string,
|
29
29
|
output.stderr.string,
|
@@ -35,9 +35,9 @@ module Bolt
|
|
35
35
|
def upload(source, destination, options = {})
|
36
36
|
running_as(options[:run_as]) do
|
37
37
|
with_tmpdir do |dir|
|
38
|
-
basename = File.basename(
|
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)
|
@@ -58,7 +79,7 @@ module Bolt
|
|
58
79
|
with_tmpdir do |dir|
|
59
80
|
path = write_executable(dir.to_s, script)
|
60
81
|
dir.chown(run_as)
|
61
|
-
output = execute([path, *arguments], sudoable: true)
|
82
|
+
output = execute([path, *arguments], environment: options[:env_vars], sudoable: true)
|
62
83
|
Bolt::Result.for_command(target,
|
63
84
|
output.stdout.string,
|
64
85
|
output.stderr.string,
|
@@ -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
|
|
@@ -170,7 +191,7 @@ module Bolt
|
|
170
191
|
def check_sudo(out, inp, stdin)
|
171
192
|
buffer = out.readpartial(CHUNK_SIZE)
|
172
193
|
# Split on newlines, including the newline
|
173
|
-
lines = buffer.split(/(
|
194
|
+
lines = buffer.split(/(?<=\n)/)
|
174
195
|
# handle_sudo will return the line if it is not a sudo prompt or error
|
175
196
|
lines.map! { |line| handle_sudo(inp, line, stdin) }
|
176
197
|
lines.join("")
|
@@ -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
|
@@ -277,31 +298,8 @@ module Bolt
|
|
277
298
|
end
|
278
299
|
end
|
279
300
|
|
280
|
-
|
281
|
-
|
282
|
-
# for task input data because the sudo password has already either been
|
283
|
-
# provided on stdin or was not needed.
|
284
|
-
def prepend_sudo_success(sudo_id, command_str)
|
285
|
-
command_str = "cd; #{command_str}" if conn.reset_cwd?
|
286
|
-
"sh -c #{Shellwords.shellescape("echo #{sudo_id} 1>&2; #{command_str}")}"
|
287
|
-
end
|
288
|
-
|
289
|
-
def prepend_chdir(command_str)
|
290
|
-
"sh -c #{Shellwords.shellescape("cd; #{command_str}")}"
|
291
|
-
end
|
292
|
-
|
293
|
-
# A helper to build up a single string that contains all of the options for
|
294
|
-
# privilege escalation. A wrapper script is used to direct task input to stdin
|
295
|
-
# when a tty is allocated and thus we do not need to prepend_sudo_success when
|
296
|
-
# using the wrapper or when the task does not require stdin data.
|
297
|
-
def build_sudoable_command_str(command_str, sudo_str, sudo_id, options)
|
298
|
-
if options[:stdin] && !options[:wrapper]
|
299
|
-
"#{sudo_str} #{prepend_sudo_success(sudo_id, command_str)}"
|
300
|
-
elsif conn.reset_cwd?
|
301
|
-
"#{sudo_str} #{prepend_chdir(command_str)}"
|
302
|
-
else
|
303
|
-
"#{sudo_str} #{command_str}"
|
304
|
-
end
|
301
|
+
def sudo_success(sudo_id)
|
302
|
+
"echo #{sudo_id} 1>&2"
|
305
303
|
end
|
306
304
|
|
307
305
|
# Returns string with the interpreter conditionally prepended
|
@@ -322,27 +320,37 @@ module Bolt
|
|
322
320
|
escalate = sudoable && run_as && conn.user != run_as
|
323
321
|
use_sudo = escalate && @target.options['run-as-command'].nil?
|
324
322
|
|
325
|
-
|
323
|
+
# Depending on the transport, whether we're using sudo and whether
|
324
|
+
# there are environment variables to set, we may need to stitch
|
325
|
+
# together multiple commands into a single sh invocation
|
326
|
+
commands = [inject_interpreter(options[:interpreter], command)]
|
326
327
|
|
327
328
|
if options[:environment]
|
328
|
-
|
329
|
+
env_decl = options[:environment].map do |env, val|
|
329
330
|
"#{env}=#{Shellwords.shellescape(val)}"
|
330
|
-
end
|
331
|
-
command_str = "#{env_decls.join(' ')} #{command_str}"
|
331
|
+
end.join(' ')
|
332
332
|
end
|
333
333
|
|
334
334
|
if escalate
|
335
|
-
if use_sudo
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
335
|
+
sudo_str = if use_sudo
|
336
|
+
sudo_exec = target.options['sudo-executable'] || "sudo"
|
337
|
+
sudo_flags = [sudo_exec, "-S", "-H", "-u", run_as, "-p", sudo_prompt]
|
338
|
+
Shellwords.shelljoin(sudo_flags)
|
339
|
+
else
|
340
|
+
Shellwords.shelljoin(@target.options['run-as-command'] + [run_as])
|
341
|
+
end
|
342
|
+
commands.unshift('cd') if conn.reset_cwd?
|
343
|
+
commands.unshift(sudo_success(@sudo_id)) if options[:stdin] && !options[:wrapper]
|
344
344
|
end
|
345
345
|
|
346
|
+
command_str = if sudo_str || env_decl
|
347
|
+
"sh -c #{Shellwords.shellescape(commands.join('; '))}"
|
348
|
+
else
|
349
|
+
commands.last
|
350
|
+
end
|
351
|
+
|
352
|
+
command_str = [sudo_str, env_decl, command_str].compact.join(' ')
|
353
|
+
|
346
354
|
@logger.debug { "Executing: #{command_str}" }
|
347
355
|
|
348
356
|
in_buffer = if !use_sudo && options[:stdin]
|
@@ -71,8 +71,10 @@ module Bolt
|
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
|
-
def
|
75
|
-
|
74
|
+
def env_declarations(env_vars)
|
75
|
+
env_vars.map do |var, val|
|
76
|
+
"[Environment]::SetEnvironmentVariable('#{var}', @'\n#{val}\n'@)"
|
77
|
+
end
|
76
78
|
end
|
77
79
|
|
78
80
|
def quote_string(string)
|
@@ -83,7 +85,7 @@ module Bolt
|
|
83
85
|
filename ||= File.basename(file)
|
84
86
|
validate_extensions(File.extname(filename))
|
85
87
|
destination = "#{dir}\\#{filename}"
|
86
|
-
conn.
|
88
|
+
conn.upload_file(file, destination)
|
87
89
|
destination
|
88
90
|
end
|
89
91
|
|
@@ -111,7 +113,8 @@ module Bolt
|
|
111
113
|
end
|
112
114
|
|
113
115
|
def mkdirs(dirs)
|
114
|
-
|
116
|
+
paths = dirs.uniq.sort.join('","')
|
117
|
+
mkdir_command = "mkdir -Force -Path (\"#{paths}\")"
|
115
118
|
result = execute(mkdir_command)
|
116
119
|
if result.exit_code != 0
|
117
120
|
message = "Could not create directories: #{result.stderr.string}"
|
@@ -161,11 +164,19 @@ module Bolt
|
|
161
164
|
end
|
162
165
|
|
163
166
|
def upload(source, destination, _options = {})
|
164
|
-
conn.
|
167
|
+
conn.upload_file(source, destination)
|
165
168
|
Bolt::Result.for_upload(target, source, destination)
|
166
169
|
end
|
167
170
|
|
168
|
-
def
|
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
|
+
|
177
|
+
def run_command(command, options = {})
|
178
|
+
command = [*env_declarations(options[:env_vars]), command].join("\r\n") if options[:env_vars]
|
179
|
+
|
169
180
|
output = execute(command)
|
170
181
|
Bolt::Result.for_command(target,
|
171
182
|
output.stdout.string,
|
@@ -174,7 +185,7 @@ module Bolt
|
|
174
185
|
'command', command)
|
175
186
|
end
|
176
187
|
|
177
|
-
def run_script(script, arguments,
|
188
|
+
def run_script(script, arguments, options = {})
|
178
189
|
# unpack any Sensitive data
|
179
190
|
arguments = unwrap_sensitive_args(arguments)
|
180
191
|
with_tmpdir do |dir|
|
@@ -186,6 +197,8 @@ module Bolt
|
|
186
197
|
args += escape_arguments(arguments)
|
187
198
|
execute_process(path, args)
|
188
199
|
end
|
200
|
+
command = [*env_declarations(options[:env_vars]), command].join("\r\n") if options[:env_vars]
|
201
|
+
|
189
202
|
output = execute(command)
|
190
203
|
Bolt::Result.for_command(target,
|
191
204
|
output.stdout.string,
|
@@ -213,7 +226,7 @@ module Bolt
|
|
213
226
|
task_dir = File.join(dir, task.tasks_dir)
|
214
227
|
mkdirs([task_dir] + extra_files.map { |file| File.join(dir, File.dirname(file['name'])) })
|
215
228
|
extra_files.each do |file|
|
216
|
-
conn.
|
229
|
+
conn.upload_file(file['path'], File.join(dir, file['name']))
|
217
230
|
end
|
218
231
|
end
|
219
232
|
|
@@ -236,9 +249,7 @@ module Bolt
|
|
236
249
|
end
|
237
250
|
|
238
251
|
env_assignments = if Bolt::Task::ENVIRONMENT_METHODS.include?(input_method)
|
239
|
-
envify_params(arguments)
|
240
|
-
set_env(arg, val)
|
241
|
-
end
|
252
|
+
env_declarations(envify_params(arguments))
|
242
253
|
else
|
243
254
|
[]
|
244
255
|
end
|
@@ -257,7 +268,7 @@ module Bolt
|
|
257
268
|
return with_tmpdir do |dir|
|
258
269
|
command += "\r\nif (!$?) { if($LASTEXITCODE) { exit $LASTEXITCODE } else { exit 1 } }"
|
259
270
|
script_file = File.join(dir, "#{SecureRandom.uuid}_wrapper.ps1")
|
260
|
-
conn.
|
271
|
+
conn.upload_file(StringIO.new(command), script_file)
|
261
272
|
args = escape_arguments([script_file])
|
262
273
|
script_invocation = ['powershell.exe', *PS_ARGS, '-File', *args].join(' ')
|
263
274
|
execute(script_invocation)
|
@@ -85,12 +85,21 @@ module Bolt
|
|
85
85
|
|
86
86
|
def shell_init
|
87
87
|
<<~PS
|
88
|
-
$
|
89
|
-
|
90
|
-
|
91
|
-
$
|
92
|
-
|
93
|
-
|
88
|
+
$installRegKey = Get-ItemProperty -Path "HKLM:\\Software\\Puppet Labs\\Puppet" -ErrorAction 0
|
89
|
+
if(![string]::IsNullOrEmpty($installRegKey.RememberedInstallDir64)){
|
90
|
+
$boltBaseDir = $installRegKey.RememberedInstallDir64
|
91
|
+
}elseif(![string]::IsNullOrEmpty($installRegKey.RememberedInstallDir)){
|
92
|
+
$boltBaseDir = $installRegKey.RememberedInstallDir
|
93
|
+
}else{
|
94
|
+
$boltBaseDir = "${ENV:ProgramFiles}\\Puppet Labs\\Puppet"
|
95
|
+
}
|
96
|
+
|
97
|
+
$ENV:PATH += ";${boltBaseDir}\\bin\\;" +
|
98
|
+
"${boltBaseDir}\\puppet\\bin;" +
|
99
|
+
"${boltBaseDir}\\sys\\ruby\\bin\\"
|
100
|
+
$ENV:RUBYLIB = "${boltBaseDir}\\puppet\\lib;" +
|
101
|
+
"${boltBaseDir}\\facter\\lib;" +
|
102
|
+
"${boltBaseDir}\\hiera\\lib;" +
|
94
103
|
$ENV:RUBYLIB
|
95
104
|
|
96
105
|
Add-Type -AssemblyName System.ServiceModel.Web, System.Runtime.Serialization
|
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"
|
@@ -20,7 +20,7 @@ module Bolt
|
|
20
20
|
def upload(target, source, destination, _options = {})
|
21
21
|
with_connection(target) do |conn|
|
22
22
|
conn.with_remote_tmpdir do |dir|
|
23
|
-
basename = File.basename(
|
23
|
+
basename = File.basename(source)
|
24
24
|
tmpfile = "#{dir}/#{basename}"
|
25
25
|
if File.directory?(source)
|
26
26
|
conn.write_remote_directory(source, tmpfile)
|
@@ -38,8 +38,18 @@ 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 = {}
|
51
|
+
execute_options[:tty] = target.options['tty']
|
52
|
+
execute_options[:environment] = options[:env_vars]
|
43
53
|
|
44
54
|
if target.options['shell-command'] && !target.options['shell-command'].empty?
|
45
55
|
# escape any double quotes in command
|
@@ -47,19 +57,21 @@ module Bolt
|
|
47
57
|
command = "#{target.options['shell-command']} \" #{command}\""
|
48
58
|
end
|
49
59
|
with_connection(target) do |conn|
|
50
|
-
stdout, stderr, exitcode = conn.execute(*Shellwords.split(command),
|
60
|
+
stdout, stderr, exitcode = conn.execute(*Shellwords.split(command), execute_options)
|
51
61
|
Bolt::Result.for_command(target, stdout, stderr, exitcode, 'command', command)
|
52
62
|
end
|
53
63
|
end
|
54
64
|
|
55
|
-
def run_script(target, script, arguments,
|
65
|
+
def run_script(target, script, arguments, options = {})
|
56
66
|
# unpack any Sensitive data
|
57
67
|
arguments = unwrap_sensitive_args(arguments)
|
68
|
+
execute_options = {}
|
69
|
+
execute_options[:environment] = options[:env_vars]
|
58
70
|
|
59
71
|
with_connection(target) do |conn|
|
60
72
|
conn.with_remote_tmpdir do |dir|
|
61
73
|
remote_path = conn.write_remote_executable(dir, script)
|
62
|
-
stdout, stderr, exitcode = conn.execute(remote_path, *arguments,
|
74
|
+
stdout, stderr, exitcode = conn.execute(remote_path, *arguments, execute_options)
|
63
75
|
Bolt::Result.for_command(target, stdout, stderr, exitcode, 'script', script)
|
64
76
|
end
|
65
77
|
end
|
@@ -82,7 +82,9 @@ module Bolt
|
|
82
82
|
def write_remote_file(source, destination)
|
83
83
|
@logger.debug { "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
|
@@ -90,7 +92,23 @@ module Bolt
|
|
90
92
|
def write_remote_directory(source, destination)
|
91
93
|
@logger.debug { "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.debug { "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
|