bolt 2.18.0 → 2.23.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/lib/bolt/analytics.rb +1 -1
- data/lib/bolt/bolt_option_parser.rb +74 -24
- data/lib/bolt/catalog.rb +12 -3
- data/lib/bolt/cli.rb +305 -108
- data/lib/bolt/config.rb +18 -10
- data/lib/bolt/config/options.rb +14 -0
- data/lib/bolt/executor.rb +26 -5
- data/lib/bolt/inventory/group.rb +3 -2
- data/lib/bolt/inventory/inventory.rb +4 -3
- data/lib/bolt/logger.rb +9 -0
- data/lib/bolt/module.rb +2 -1
- data/lib/bolt/outputter.rb +56 -0
- data/lib/bolt/outputter/human.rb +0 -9
- data/lib/bolt/outputter/json.rb +0 -4
- data/lib/bolt/outputter/rainbow.rb +9 -2
- data/lib/bolt/pal.rb +11 -9
- data/lib/bolt/pal/yaml_plan/evaluator.rb +23 -2
- 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/plugin/module.rb +2 -4
- data/lib/bolt/plugin/prompt.rb +3 -3
- data/lib/bolt/plugin/puppetdb.rb +3 -2
- data/lib/bolt/project.rb +14 -9
- 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 +24 -4
- 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 +20 -2
- data/lib/bolt/transport/local/connection.rb +14 -1
- data/lib/bolt/transport/orch.rb +12 -0
- data/lib/bolt/transport/simple.rb +6 -0
- data/lib/bolt/transport/ssh/connection.rb +9 -1
- data/lib/bolt/transport/ssh/exec_connection.rb +22 -1
- data/lib/bolt/transport/winrm/connection.rb +118 -8
- data/lib/bolt/util.rb +26 -11
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/pe/pal.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 +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 +8 -2
@@ -12,7 +12,19 @@ module Bolt
|
|
12
12
|
Set['name', 'description', 'target', 'targets']
|
13
13
|
end
|
14
14
|
|
15
|
-
STEP_KEYS = %w[
|
15
|
+
STEP_KEYS = %w[
|
16
|
+
command
|
17
|
+
destination
|
18
|
+
download
|
19
|
+
eval
|
20
|
+
message
|
21
|
+
plan
|
22
|
+
resources
|
23
|
+
script
|
24
|
+
source
|
25
|
+
task
|
26
|
+
upload
|
27
|
+
].freeze
|
16
28
|
|
17
29
|
def self.create(step_body, step_number)
|
18
30
|
type_keys = (STEP_KEYS & step_body.keys)
|
@@ -22,8 +34,10 @@ module Bolt
|
|
22
34
|
when 1
|
23
35
|
type = type_keys.first
|
24
36
|
else
|
25
|
-
if
|
37
|
+
if [Set['source', 'destination'], Set['upload', 'destination']].include?(type_keys.to_set)
|
26
38
|
type = 'upload'
|
39
|
+
elsif type_keys.to_set == Set['download', 'destination']
|
40
|
+
type = 'download'
|
27
41
|
else
|
28
42
|
raise step_error("Multiple action keys detected: #{type_keys.inspect}", step_body['name'], step_number)
|
29
43
|
end
|
@@ -89,6 +103,12 @@ module Bolt
|
|
89
103
|
missing_keys -= ['targets']
|
90
104
|
end
|
91
105
|
|
106
|
+
# Handle cases where upload step uses deprecated 'source' key instead of 'upload'
|
107
|
+
# TODO: Remove when 'source' is removed
|
108
|
+
if body.include?('source')
|
109
|
+
missing_keys -= ['upload']
|
110
|
+
end
|
111
|
+
|
92
112
|
if missing_keys.any?
|
93
113
|
error_message = "The #{step_type.inspect} step requires: #{missing_keys.to_a.inspect} key(s)"
|
94
114
|
err = step_error(error_message, body['name'], step_number)
|
@@ -156,3 +176,5 @@ require 'bolt/pal/yaml_plan/step/resources'
|
|
156
176
|
require 'bolt/pal/yaml_plan/step/script'
|
157
177
|
require 'bolt/pal/yaml_plan/step/task'
|
158
178
|
require 'bolt/pal/yaml_plan/step/upload'
|
179
|
+
require 'bolt/pal/yaml_plan/step/download'
|
180
|
+
require 'bolt/pal/yaml_plan/step/message'
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bolt
|
4
|
+
class PAL
|
5
|
+
class YamlPlan
|
6
|
+
class Step
|
7
|
+
class Download < Step
|
8
|
+
def self.allowed_keys
|
9
|
+
super + Set['download', 'destination']
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.required_keys
|
13
|
+
Set['download', 'destination', 'targets']
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(step_body)
|
17
|
+
super
|
18
|
+
@source = step_body['download']
|
19
|
+
@destination = step_body['destination']
|
20
|
+
end
|
21
|
+
|
22
|
+
def transpile
|
23
|
+
code = String.new(" ")
|
24
|
+
code << "$#{@name} = " if @name
|
25
|
+
|
26
|
+
fn = 'download_file'
|
27
|
+
args = [@source, @destination, @targets]
|
28
|
+
args << @description if @description
|
29
|
+
|
30
|
+
code << function_call(fn, args)
|
31
|
+
|
32
|
+
code << "\n"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bolt
|
4
|
+
class PAL
|
5
|
+
class YamlPlan
|
6
|
+
class Step
|
7
|
+
class Message < Step
|
8
|
+
def self.allowed_keys
|
9
|
+
super + Set['message']
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.required_keys
|
13
|
+
Set['message']
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(step_body)
|
17
|
+
super
|
18
|
+
@message = step_body['message']
|
19
|
+
end
|
20
|
+
|
21
|
+
def transpile
|
22
|
+
code = String.new(" ")
|
23
|
+
code << function_call('out::message', [@message])
|
24
|
+
code << "\n"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -6,16 +6,16 @@ module Bolt
|
|
6
6
|
class Step
|
7
7
|
class Upload < Step
|
8
8
|
def self.allowed_keys
|
9
|
-
super + Set['source', 'destination']
|
9
|
+
super + Set['source', 'destination', 'upload']
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.required_keys
|
13
|
-
Set['
|
13
|
+
Set['upload', 'destination', 'targets']
|
14
14
|
end
|
15
15
|
|
16
16
|
def initialize(step_body)
|
17
17
|
super
|
18
|
-
@source = step_body['source']
|
18
|
+
@source = step_body['upload'] || step_body['source']
|
19
19
|
@destination = step_body['destination']
|
20
20
|
end
|
21
21
|
|
data/lib/bolt/plugin/module.rb
CHANGED
@@ -184,12 +184,10 @@ module Bolt
|
|
184
184
|
# Raises a deprecation warning if the pkcs7 plugin is using deprecated keys and
|
185
185
|
# modifies the keys so they are the correct format
|
186
186
|
def handle_deprecated_pkcs7_keys(params)
|
187
|
-
if
|
188
|
-
@deprecation_warning_issued = true
|
189
|
-
|
187
|
+
if params.key?('private-key') || params.key?('public-key')
|
190
188
|
message = "pkcs7 keys 'private-key' and 'public-key' have been deprecated and will be "\
|
191
189
|
"removed in a future version of Bolt; use 'private_key' and 'public_key' instead."
|
192
|
-
|
190
|
+
Bolt::Logger.deprecation_warning('PKCS7 keys using hyphens, not underscores', message)
|
193
191
|
end
|
194
192
|
|
195
193
|
params['private_key'] = params.delete('private-key') if params.key?('private-key')
|
data/lib/bolt/plugin/prompt.rb
CHANGED
@@ -18,9 +18,9 @@ module Bolt
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def resolve_reference(opts)
|
21
|
-
|
22
|
-
value =
|
23
|
-
|
21
|
+
$stderr.print("#{opts['message']}: ")
|
22
|
+
value = $stdin.noecho(&:gets).to_s.chomp
|
23
|
+
$stderr.puts
|
24
24
|
|
25
25
|
value
|
26
26
|
end
|
data/lib/bolt/plugin/puppetdb.rb
CHANGED
@@ -85,7 +85,8 @@ module Bolt
|
|
85
85
|
|
86
86
|
def resolve_facts(config, certname, target_data)
|
87
87
|
Bolt::Util.walk_vals(config) do |value|
|
88
|
-
|
88
|
+
case value
|
89
|
+
when String
|
89
90
|
if value == 'certname'
|
90
91
|
certname
|
91
92
|
else
|
@@ -94,7 +95,7 @@ module Bolt
|
|
94
95
|
# If there's no fact data this will be nil
|
95
96
|
data&.fetch('value', nil)
|
96
97
|
end
|
97
|
-
|
98
|
+
when Array, Hash
|
98
99
|
value
|
99
100
|
else
|
100
101
|
raise FactLookupError.new(value, "fact lookups must be a string")
|
data/lib/bolt/project.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'pathname'
|
4
|
-
require 'bolt/pal'
|
5
4
|
require 'bolt/config'
|
5
|
+
require 'bolt/pal'
|
6
|
+
require 'bolt/module'
|
6
7
|
|
7
8
|
module Bolt
|
8
9
|
class Project
|
@@ -16,7 +17,8 @@ module Bolt
|
|
16
17
|
}.freeze
|
17
18
|
|
18
19
|
attr_reader :path, :data, :config_file, :inventory_file, :modulepath, :hiera_config,
|
19
|
-
:puppetfile, :rerunfile, :type, :resource_types, :warnings, :project_file
|
20
|
+
:puppetfile, :rerunfile, :type, :resource_types, :warnings, :project_file,
|
21
|
+
:deprecations, :downloads, :plans_path
|
20
22
|
|
21
23
|
def self.default_project
|
22
24
|
create_project(File.expand_path(File.join('~', '.puppetlabs', 'bolt')), 'user')
|
@@ -65,11 +67,12 @@ module Bolt
|
|
65
67
|
@project_file = @path + 'bolt-project.yaml'
|
66
68
|
|
67
69
|
@warnings = []
|
70
|
+
@deprecations = []
|
68
71
|
if (@path + 'bolt.yaml').file? && project_file?
|
69
72
|
msg = "Project-level configuration in bolt.yaml is deprecated if using bolt-project.yaml. "\
|
70
73
|
"Transport config should be set in inventory.yaml, all other config should be set in "\
|
71
74
|
"bolt-project.yaml."
|
72
|
-
@
|
75
|
+
@deprecations << { type: 'Using bolt.yaml for project configuration', msg: msg }
|
73
76
|
end
|
74
77
|
|
75
78
|
@inventory_file = @path + 'inventory.yaml'
|
@@ -79,6 +82,8 @@ module Bolt
|
|
79
82
|
@rerunfile = @path + '.rerun.json'
|
80
83
|
@resource_types = @path + '.resource_types'
|
81
84
|
@type = type
|
85
|
+
@downloads = @path + 'downloads'
|
86
|
+
@plans_path = @path + 'plans'
|
82
87
|
|
83
88
|
tc = Bolt::Config::INVENTORY_OPTIONS.keys & raw_data.keys
|
84
89
|
if tc.any?
|
@@ -109,7 +114,7 @@ module Bolt
|
|
109
114
|
# This API is used to prepend the project as a module to Puppet's internal
|
110
115
|
# module_references list. CHANGE AT YOUR OWN RISK
|
111
116
|
def to_h
|
112
|
-
{ path: @path, name: name }
|
117
|
+
{ path: @path.to_s, name: name }
|
113
118
|
end
|
114
119
|
|
115
120
|
def eql?(other)
|
@@ -135,10 +140,10 @@ module Bolt
|
|
135
140
|
|
136
141
|
def validate
|
137
142
|
if name
|
138
|
-
|
139
|
-
if name !~ name_regex
|
143
|
+
if name !~ Bolt::Module::MODULE_NAME_REGEX
|
140
144
|
raise Bolt::ValidationError, <<~ERROR_STRING
|
141
|
-
Invalid project name '#{name}' in bolt-project.yaml; project name must
|
145
|
+
Invalid project name '#{name}' in bolt-project.yaml; project name must begin with a lowercase letter
|
146
|
+
and can include lowercase letters, numbers, and underscores.
|
142
147
|
ERROR_STRING
|
143
148
|
elsif Dir.children(Bolt::PAL::BOLTLIB_PATH).include?(name)
|
144
149
|
raise Bolt::ValidationError, "The project '#{name}' will not be loaded. The project name conflicts "\
|
@@ -158,8 +163,8 @@ module Bolt
|
|
158
163
|
|
159
164
|
def check_deprecated_file
|
160
165
|
if (@path + 'project.yaml').file?
|
161
|
-
|
162
|
-
|
166
|
+
msg = "Project configuration file 'project.yaml' is deprecated; use 'bolt-project.yaml' instead."
|
167
|
+
Bolt::Logger.deprecation_warning('Using project.yaml instead of bolt-project.yaml', msg)
|
163
168
|
end
|
164
169
|
end
|
165
170
|
end
|
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
@@ -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)
|
@@ -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
|
|
@@ -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])
|
@@ -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"
|