bolt 2.26.0 → 2.31.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 +13 -12
- data/bolt-modules/boltlib/lib/puppet/functions/write_file.rb +2 -2
- data/lib/bolt/analytics.rb +4 -0
- data/lib/bolt/applicator.rb +19 -18
- data/lib/bolt/bolt_option_parser.rb +112 -22
- data/lib/bolt/catalog.rb +1 -1
- data/lib/bolt/cli.rb +210 -174
- data/lib/bolt/config.rb +22 -2
- data/lib/bolt/config/modulepath.rb +30 -0
- data/lib/bolt/config/options.rb +30 -0
- data/lib/bolt/config/transport/options.rb +1 -1
- data/lib/bolt/executor.rb +1 -1
- data/lib/bolt/inventory.rb +11 -10
- data/lib/bolt/logger.rb +26 -19
- data/lib/bolt/module_installer.rb +242 -0
- data/lib/bolt/outputter.rb +4 -0
- data/lib/bolt/outputter/human.rb +77 -17
- data/lib/bolt/outputter/json.rb +21 -6
- data/lib/bolt/outputter/logger.rb +2 -2
- data/lib/bolt/pal.rb +46 -25
- data/lib/bolt/plugin.rb +1 -1
- data/lib/bolt/plugin/module.rb +1 -1
- data/lib/bolt/project.rb +62 -12
- data/lib/bolt/project_migrator.rb +80 -0
- data/lib/bolt/project_migrator/base.rb +39 -0
- data/lib/bolt/project_migrator/config.rb +67 -0
- data/lib/bolt/project_migrator/inventory.rb +67 -0
- data/lib/bolt/project_migrator/modules.rb +198 -0
- data/lib/bolt/puppetfile.rb +149 -0
- data/lib/bolt/puppetfile/installer.rb +43 -0
- data/lib/bolt/puppetfile/module.rb +93 -0
- data/lib/bolt/rerun.rb +1 -1
- data/lib/bolt/result.rb +15 -0
- data/lib/bolt/shell/bash.rb +4 -3
- data/lib/bolt/transport/base.rb +4 -4
- data/lib/bolt/transport/ssh/connection.rb +1 -1
- data/lib/bolt/util.rb +51 -10
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/acl.rb +2 -2
- data/lib/bolt_server/base_config.rb +3 -3
- data/lib/bolt_server/config.rb +1 -1
- data/lib/bolt_server/file_cache.rb +11 -11
- data/lib/bolt_server/transport_app.rb +206 -27
- data/lib/bolt_spec/bolt_context.rb +8 -6
- data/lib/bolt_spec/plans.rb +1 -1
- data/lib/bolt_spec/plans/mock_executor.rb +1 -1
- data/lib/bolt_spec/run.rb +1 -1
- metadata +14 -6
- data/lib/bolt/project_migrate.rb +0 -138
- data/lib/bolt_server/pe/pal.rb +0 -67
data/lib/bolt/outputter.rb
CHANGED
@@ -35,6 +35,10 @@ module Bolt
|
|
35
35
|
raise NotImplementedError, "print_message() must be implemented by the outputter class"
|
36
36
|
end
|
37
37
|
|
38
|
+
def print_error
|
39
|
+
raise NotImplementedError, "print_error() must be implemented by the outputter class"
|
40
|
+
end
|
41
|
+
|
38
42
|
def stringify(message)
|
39
43
|
formatted = format_message(message)
|
40
44
|
if formatted.is_a?(Hash) || formatted.is_a?(Array)
|
data/lib/bolt/outputter/human.rb
CHANGED
@@ -5,9 +5,12 @@ require 'bolt/pal'
|
|
5
5
|
module Bolt
|
6
6
|
class Outputter
|
7
7
|
class Human < Bolt::Outputter
|
8
|
-
COLORS = {
|
9
|
-
|
10
|
-
|
8
|
+
COLORS = {
|
9
|
+
red: "31",
|
10
|
+
green: "32",
|
11
|
+
yellow: "33",
|
12
|
+
cyan: "36"
|
13
|
+
}.freeze
|
11
14
|
|
12
15
|
def print_head; end
|
13
16
|
|
@@ -31,6 +34,10 @@ module Bolt
|
|
31
34
|
string.sub(/\s\z/, '')
|
32
35
|
end
|
33
36
|
|
37
|
+
def wrap(string, width = 80)
|
38
|
+
string.gsub(/(.{1,#{width}})(\s+|\Z)/, "\\1\n")
|
39
|
+
end
|
40
|
+
|
34
41
|
def handle_event(event)
|
35
42
|
case event[:type]
|
36
43
|
when :enable_default_output
|
@@ -48,9 +55,9 @@ module Bolt
|
|
48
55
|
when :node_result
|
49
56
|
print_result(event[:result]) if @verbose
|
50
57
|
when :step_start
|
51
|
-
print_step_start(event) if plan_logging?
|
58
|
+
print_step_start(**event) if plan_logging?
|
52
59
|
when :step_finish
|
53
|
-
print_step_finish(event) if plan_logging?
|
60
|
+
print_step_finish(**event) if plan_logging?
|
54
61
|
when :plan_start
|
55
62
|
print_plan_start(event)
|
56
63
|
when :plan_finish
|
@@ -188,7 +195,7 @@ module Bolt
|
|
188
195
|
@stream.puts total_msg
|
189
196
|
end
|
190
197
|
|
191
|
-
def print_table(results)
|
198
|
+
def print_table(results, padding_left = 0, padding_right = 3)
|
192
199
|
# lazy-load expensive gem code
|
193
200
|
require 'terminal-table'
|
194
201
|
|
@@ -198,8 +205,8 @@ module Bolt
|
|
198
205
|
border_x: '',
|
199
206
|
border_y: '',
|
200
207
|
border_i: '',
|
201
|
-
padding_left:
|
202
|
-
padding_right:
|
208
|
+
padding_left: padding_left,
|
209
|
+
padding_right: padding_right,
|
203
210
|
border_top: false,
|
204
211
|
border_bottom: false
|
205
212
|
}
|
@@ -241,7 +248,7 @@ module Bolt
|
|
241
248
|
task_info << "MODULE:\n"
|
242
249
|
|
243
250
|
path = task.files.first['path'].chomp("/tasks/#{task.files.first['name']}")
|
244
|
-
task_info << if path.start_with?(Bolt::
|
251
|
+
task_info << if path.start_with?(Bolt::Config::Modulepath::MODULES_PATH)
|
245
252
|
"built-in module"
|
246
253
|
else
|
247
254
|
path
|
@@ -271,7 +278,7 @@ module Bolt
|
|
271
278
|
plan_info << "MODULE:\n"
|
272
279
|
|
273
280
|
path = plan['module']
|
274
|
-
plan_info << if path.start_with?(Bolt::
|
281
|
+
plan_info << if path.start_with?(Bolt::Config::Modulepath::MODULES_PATH)
|
275
282
|
"built-in module"
|
276
283
|
else
|
277
284
|
path
|
@@ -299,9 +306,9 @@ module Bolt
|
|
299
306
|
def print_module_list(module_list)
|
300
307
|
module_list.each do |path, modules|
|
301
308
|
if (mod = modules.find { |m| m[:internal_module_group] })
|
302
|
-
@stream.puts(mod[:internal_module_group])
|
309
|
+
@stream.puts(colorize(:cyan, mod[:internal_module_group]))
|
303
310
|
else
|
304
|
-
@stream.puts(path)
|
311
|
+
@stream.puts(colorize(:cyan, path))
|
305
312
|
end
|
306
313
|
|
307
314
|
if modules.empty?
|
@@ -317,17 +324,35 @@ module Bolt
|
|
317
324
|
[m[:name], version]
|
318
325
|
end
|
319
326
|
|
320
|
-
print_table(module_info)
|
327
|
+
print_table(module_info, 2, 1)
|
321
328
|
end
|
322
329
|
|
323
330
|
@stream.write("\n")
|
324
331
|
end
|
325
332
|
end
|
326
333
|
|
327
|
-
def print_targets(
|
328
|
-
|
329
|
-
|
330
|
-
|
334
|
+
def print_targets(target_list, inventoryfile)
|
335
|
+
adhoc = colorize(:yellow, "(Not found in inventory file)")
|
336
|
+
|
337
|
+
targets = []
|
338
|
+
targets += target_list[:inventory].map { |target| [target.name, nil] }
|
339
|
+
targets += target_list[:adhoc].map { |target| [target.name, adhoc] }
|
340
|
+
|
341
|
+
if targets.any?
|
342
|
+
print_table(targets, 0, 2)
|
343
|
+
@stream.puts
|
344
|
+
end
|
345
|
+
|
346
|
+
@stream.puts "INVENTORY FILE:"
|
347
|
+
if inventoryfile.exist?
|
348
|
+
@stream.puts inventoryfile
|
349
|
+
else
|
350
|
+
@stream.puts wrap("Tried to load inventory from #{inventoryfile}, but the file does not exist")
|
351
|
+
end
|
352
|
+
|
353
|
+
@stream.puts "\nTARGET COUNT:"
|
354
|
+
@stream.puts "#{targets.count} total, #{target_list[:inventory].count} from inventory, "\
|
355
|
+
"#{target_list[:adhoc].count} adhoc"
|
331
356
|
end
|
332
357
|
|
333
358
|
def print_target_info(targets)
|
@@ -394,6 +419,41 @@ module Bolt
|
|
394
419
|
@stream.puts(message)
|
395
420
|
end
|
396
421
|
|
422
|
+
def print_error(message)
|
423
|
+
@stream.puts(colorize(:red, message))
|
424
|
+
end
|
425
|
+
|
426
|
+
def print_prompt(prompt)
|
427
|
+
@stream.print(colorize(:cyan, indent(4, prompt)))
|
428
|
+
end
|
429
|
+
|
430
|
+
def print_prompt_error(message)
|
431
|
+
@stream.puts(colorize(:red, indent(4, message)))
|
432
|
+
end
|
433
|
+
|
434
|
+
def print_action_step(step)
|
435
|
+
first, *remaining = wrap(step, 76).lines
|
436
|
+
|
437
|
+
first = indent(2, "→ #{first}")
|
438
|
+
remaining = remaining.map { |line| indent(4, line) }
|
439
|
+
step = [first, *remaining, "\n"].join
|
440
|
+
|
441
|
+
@stream.puts(step)
|
442
|
+
end
|
443
|
+
|
444
|
+
def print_action_error(error)
|
445
|
+
# Running everything through 'wrap' messes with newlines. Separating
|
446
|
+
# into lines and wrapping each individually ensures separate errors are
|
447
|
+
# distinguishable.
|
448
|
+
first, *remaining = error.lines
|
449
|
+
first = colorize(:red, indent(2, "→ #{wrap(first, 76)}"))
|
450
|
+
wrapped = remaining.map { |l| wrap(l) }
|
451
|
+
to_print = wrapped.map { |line| colorize(:red, indent(4, line)) }
|
452
|
+
step = [first, *to_print, "\n"].join
|
453
|
+
|
454
|
+
@stream.puts(step)
|
455
|
+
end
|
456
|
+
|
397
457
|
def duration_to_string(duration)
|
398
458
|
hrs = (duration / 3600).floor
|
399
459
|
mins = ((duration % 3600) / 60).floor
|
data/lib/bolt/outputter/json.rb
CHANGED
@@ -48,7 +48,7 @@ module Bolt
|
|
48
48
|
|
49
49
|
def print_task_info(task)
|
50
50
|
path = task.files.first['path'].chomp("/tasks/#{task.files.first['name']}")
|
51
|
-
module_dir = if path.start_with?(Bolt::
|
51
|
+
module_dir = if path.start_with?(Bolt::Config::Modulepath::MODULES_PATH)
|
52
52
|
"built-in module"
|
53
53
|
else
|
54
54
|
path
|
@@ -62,7 +62,7 @@ module Bolt
|
|
62
62
|
|
63
63
|
def print_plan_info(plan)
|
64
64
|
path = plan.delete('module')
|
65
|
-
plan['module_dir'] = if path.start_with?(Bolt::
|
65
|
+
plan['module_dir'] = if path.start_with?(Bolt::Config::Modulepath::MODULES_PATH)
|
66
66
|
"built-in module"
|
67
67
|
else
|
68
68
|
path
|
@@ -97,13 +97,22 @@ module Bolt
|
|
97
97
|
def print_puppetfile_result(success, puppetfile, moduledir)
|
98
98
|
@stream.puts({ "success": success,
|
99
99
|
"puppetfile": puppetfile,
|
100
|
-
"moduledir": moduledir }.to_json)
|
100
|
+
"moduledir": moduledir.to_s }.to_json)
|
101
101
|
end
|
102
102
|
|
103
|
-
def print_targets(
|
103
|
+
def print_targets(target_list, inventoryfile)
|
104
104
|
@stream.puts ::JSON.pretty_generate(
|
105
|
-
"
|
106
|
-
|
105
|
+
"inventory": {
|
106
|
+
"targets": target_list[:inventory].map(&:name),
|
107
|
+
"count": target_list[:inventory].count,
|
108
|
+
"file": inventoryfile.to_s
|
109
|
+
},
|
110
|
+
"adhoc": {
|
111
|
+
"targets": target_list[:adhoc].map(&:name),
|
112
|
+
"count": target_list[:adhoc].count
|
113
|
+
},
|
114
|
+
"targets": target_list.values.flatten.map(&:name),
|
115
|
+
"count": target_list.values.flatten.count
|
107
116
|
)
|
108
117
|
end
|
109
118
|
|
@@ -135,6 +144,12 @@ module Bolt
|
|
135
144
|
def print_message(message)
|
136
145
|
$stderr.puts(message)
|
137
146
|
end
|
147
|
+
alias print_error print_message
|
148
|
+
|
149
|
+
def print_action_step(step)
|
150
|
+
$stderr.puts(step)
|
151
|
+
end
|
152
|
+
alias print_action_error print_action_step
|
138
153
|
end
|
139
154
|
end
|
140
155
|
end
|
@@ -13,9 +13,9 @@ module Bolt
|
|
13
13
|
def handle_event(event)
|
14
14
|
case event[:type]
|
15
15
|
when :step_start
|
16
|
-
log_step_start(event)
|
16
|
+
log_step_start(**event)
|
17
17
|
when :step_finish
|
18
|
-
log_step_finish(event)
|
18
|
+
log_step_finish(**event)
|
19
19
|
when :plan_start
|
20
20
|
log_plan_start(event)
|
21
21
|
when :plan_finish
|
data/lib/bolt/pal.rb
CHANGED
@@ -5,18 +5,16 @@ require 'bolt/executor'
|
|
5
5
|
require 'bolt/error'
|
6
6
|
require 'bolt/plan_result'
|
7
7
|
require 'bolt/util'
|
8
|
+
require 'bolt/config/modulepath'
|
8
9
|
require 'etc'
|
9
10
|
|
10
11
|
module Bolt
|
11
12
|
class PAL
|
12
|
-
BOLTLIB_PATH = File.expand_path('../../bolt-modules', __dir__)
|
13
|
-
MODULES_PATH = File.expand_path('../../modules', __dir__)
|
14
|
-
|
15
13
|
# PALError is used to convert errors from executing puppet code into
|
16
14
|
# Bolt::Errors
|
17
15
|
class PALError < Bolt::Error
|
18
16
|
def self.from_preformatted_error(err)
|
19
|
-
if err.cause
|
17
|
+
if err.cause.is_a? Bolt::Error
|
20
18
|
err.cause
|
21
19
|
else
|
22
20
|
from_error(err)
|
@@ -29,15 +27,12 @@ module Bolt
|
|
29
27
|
message = err.cause ? err.cause.message : err.message
|
30
28
|
|
31
29
|
# Provide the location of an error if it came from a plan
|
32
|
-
details =
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
else
|
37
|
-
{}
|
38
|
-
end
|
30
|
+
details = {}
|
31
|
+
details[:file] = err.file if defined?(err.file)
|
32
|
+
details[:line] = err.line if defined?(err.line)
|
33
|
+
details[:column] = err.pos if defined?(err.pos)
|
39
34
|
|
40
|
-
e = new(message, details)
|
35
|
+
e = new(message, details.compact)
|
41
36
|
|
42
37
|
e.set_backtrace(err.backtrace)
|
43
38
|
e
|
@@ -48,16 +43,16 @@ module Bolt
|
|
48
43
|
end
|
49
44
|
end
|
50
45
|
|
51
|
-
attr_reader :modulepath, :user_modulepath
|
52
|
-
|
53
46
|
def initialize(modulepath, hiera_config, resource_types, max_compiles = Etc.nprocessors,
|
54
47
|
trusted_external = nil, apply_settings = {}, project = nil)
|
48
|
+
unless modulepath.is_a?(Bolt::Config::Modulepath)
|
49
|
+
msg = "Type error in PAL: modulepath must be a Bolt::Config::Modulepath"
|
50
|
+
raise Bolt::Error.new(msg, "bolt/execution-error")
|
51
|
+
end
|
55
52
|
# Nothing works without initialized this global state. Reinitializing
|
56
53
|
# is safe and in practice only happens in tests
|
57
54
|
self.class.load_puppet
|
58
|
-
|
59
|
-
@user_modulepath = modulepath
|
60
|
-
@modulepath = [BOLTLIB_PATH, *modulepath, MODULES_PATH]
|
55
|
+
@modulepath = modulepath
|
61
56
|
@hiera_config = hiera_config
|
62
57
|
@trusted_external = trusted_external
|
63
58
|
@apply_settings = apply_settings
|
@@ -66,13 +61,21 @@ module Bolt
|
|
66
61
|
@project = project
|
67
62
|
|
68
63
|
@logger = Bolt::Logger.logger(self)
|
69
|
-
|
70
|
-
@logger.debug("Loading modules from #{
|
64
|
+
unless user_modulepath.empty?
|
65
|
+
@logger.debug("Loading modules from #{full_modulepath.join(File::PATH_SEPARATOR)}")
|
71
66
|
end
|
72
67
|
|
73
68
|
@loaded = false
|
74
69
|
end
|
75
70
|
|
71
|
+
def full_modulepath
|
72
|
+
@modulepath.full_modulepath
|
73
|
+
end
|
74
|
+
|
75
|
+
def user_modulepath
|
76
|
+
@modulepath.user_modulepath
|
77
|
+
end
|
78
|
+
|
76
79
|
# Puppet logging is global so this is class method to avoid confusion
|
77
80
|
def self.configure_logging
|
78
81
|
Puppet::Util::Log.destinations.clear
|
@@ -141,16 +144,33 @@ module Bolt
|
|
141
144
|
end
|
142
145
|
end
|
143
146
|
|
147
|
+
def detect_project_conflict(project, environment)
|
148
|
+
return unless project && project.load_as_module?
|
149
|
+
# The environment modulepath has stripped out non-existent directories,
|
150
|
+
# so we don't need to check for them
|
151
|
+
modules = environment.modulepath.flat_map do |path|
|
152
|
+
Dir.children(path).select { |name| Puppet::Module.is_module_directory?(name, path) }
|
153
|
+
end
|
154
|
+
if modules.include?(project.name)
|
155
|
+
Bolt::Logger.warn_once("project shadows module",
|
156
|
+
"The project '#{project.name}' shadows an existing module of the same name")
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
144
160
|
# Runs a block in a PAL script compiler configured for Bolt. Catches
|
145
161
|
# exceptions thrown by the block and re-raises them ensuring they are
|
146
162
|
# Bolt::Errors since the script compiler block will squash all exceptions.
|
147
163
|
def in_bolt_compiler
|
148
164
|
# TODO: If we always call this inside a bolt_executor we can remove this here
|
149
165
|
setup
|
150
|
-
r = Puppet::Pal.in_tmp_environment('bolt', modulepath:
|
166
|
+
r = Puppet::Pal.in_tmp_environment('bolt', modulepath: full_modulepath, facts: {}) do |pal|
|
151
167
|
# Only load the project if it a) exists, b) has a name it can be loaded with
|
152
168
|
Puppet.override(bolt_project: @project,
|
153
169
|
yaml_plan_instantiator: Bolt::PAL::YamlPlan::Loader) do
|
170
|
+
# Because this has the side effect of loading and caching the list
|
171
|
+
# of modules, it must happen *after* we have overridden
|
172
|
+
# bolt_project or the project will be ignored
|
173
|
+
detect_project_conflict(@project, Puppet.lookup(:environments).get('bolt'))
|
154
174
|
pal.with_script_compiler(set_local_facts: false) do |compiler|
|
155
175
|
alias_types(compiler)
|
156
176
|
register_resource_types(Puppet.lookup(:loaders)) if @resource_types
|
@@ -198,11 +218,11 @@ module Bolt
|
|
198
218
|
apply_executor: applicator || Applicator.new(
|
199
219
|
inventory,
|
200
220
|
executor,
|
201
|
-
|
221
|
+
full_modulepath,
|
202
222
|
# Skip syncing built-in plugins, since we vendor some Puppet 6
|
203
223
|
# versions of "core" types, which are already present on the agent,
|
204
224
|
# but may cause issues on Puppet 5 agents.
|
205
|
-
|
225
|
+
user_modulepath,
|
206
226
|
@project,
|
207
227
|
pdb_client,
|
208
228
|
@hiera_config,
|
@@ -424,13 +444,14 @@ module Bolt
|
|
424
444
|
# Returns a mapping of all modules available to the Bolt compiler
|
425
445
|
#
|
426
446
|
# @return [Hash{String => Array<Hash{Symbol => String,nil}>}]
|
427
|
-
# A hash that associates each directory on the
|
447
|
+
# A hash that associates each directory on the modulepath with an array
|
428
448
|
# containing a hash of information for each module in that directory.
|
429
449
|
# The information hash provides the name, version, and a string
|
430
450
|
# indicating whether the module belongs to an internal module group.
|
431
451
|
def list_modules
|
432
|
-
internal_module_groups = { BOLTLIB_PATH => 'Plan Language Modules',
|
433
|
-
MODULES_PATH => 'Packaged Modules'
|
452
|
+
internal_module_groups = { Bolt::Config::Modulepath::BOLTLIB_PATH => 'Plan Language Modules',
|
453
|
+
Bolt::Config::Modulepath::MODULES_PATH => 'Packaged Modules',
|
454
|
+
@project.managed_moduledir.to_s => 'Project Dependencies' }
|
434
455
|
|
435
456
|
in_bolt_compiler do
|
436
457
|
# NOTE: Can replace map+to_h with transform_values when Ruby 2.4
|
data/lib/bolt/plugin.rb
CHANGED
data/lib/bolt/plugin/module.rb
CHANGED
data/lib/bolt/project.rb
CHANGED
@@ -16,9 +16,10 @@ module Bolt
|
|
16
16
|
"These tasks are included in `bolt task show` output"
|
17
17
|
}.freeze
|
18
18
|
|
19
|
-
attr_reader :path, :data, :config_file, :inventory_file, :
|
19
|
+
attr_reader :path, :data, :config_file, :inventory_file, :hiera_config,
|
20
20
|
:puppetfile, :rerunfile, :type, :resource_types, :logs, :project_file,
|
21
|
-
:deprecations, :downloads, :plans_path
|
21
|
+
:deprecations, :downloads, :plans_path, :modulepath, :managed_moduledir,
|
22
|
+
:backup_dir
|
22
23
|
|
23
24
|
def self.default_project(logs = [])
|
24
25
|
create_project(File.expand_path(File.join('~', '.puppetlabs', 'bolt')), 'user', logs)
|
@@ -50,6 +51,20 @@ module Bolt
|
|
50
51
|
def self.create_project(path, type = 'option', logs = [])
|
51
52
|
fullpath = Pathname.new(path).expand_path
|
52
53
|
|
54
|
+
if type == 'user'
|
55
|
+
begin
|
56
|
+
# This is already expanded if the type is user
|
57
|
+
FileUtils.mkdir_p(path)
|
58
|
+
rescue StandardError
|
59
|
+
logs << { warn: "Could not create default project at #{path}. Continuing without a writeable project. "\
|
60
|
+
"Log and rerun files will not be written." }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
if type == 'option' && !File.directory?(path)
|
65
|
+
raise Bolt::Error.new("Could not find project at #{path}", "bolt/project-error")
|
66
|
+
end
|
67
|
+
|
53
68
|
if !Bolt::Util.windows? && type != 'environment' && fullpath.world_writable?
|
54
69
|
raise Bolt::Error.new(
|
55
70
|
"Project directory '#{fullpath}' is world-writable which poses a security risk. Set "\
|
@@ -80,15 +95,16 @@ module Bolt
|
|
80
95
|
@deprecations << { type: 'Using bolt.yaml for project configuration', msg: msg }
|
81
96
|
end
|
82
97
|
|
83
|
-
@inventory_file
|
84
|
-
@
|
85
|
-
@
|
86
|
-
@
|
87
|
-
@
|
88
|
-
@
|
89
|
-
@
|
90
|
-
@
|
91
|
-
@
|
98
|
+
@inventory_file = @path + 'inventory.yaml'
|
99
|
+
@hiera_config = @path + 'hiera.yaml'
|
100
|
+
@puppetfile = @path + 'Puppetfile'
|
101
|
+
@rerunfile = @path + '.rerun.json'
|
102
|
+
@resource_types = @path + '.resource_types'
|
103
|
+
@type = type
|
104
|
+
@downloads = @path + 'downloads'
|
105
|
+
@plans_path = @path + 'plans'
|
106
|
+
@managed_moduledir = @path + '.modules'
|
107
|
+
@backup_dir = @path + '.bolt-bak'
|
92
108
|
|
93
109
|
tc = Bolt::Config::INVENTORY_OPTIONS.keys & raw_data.keys
|
94
110
|
if tc.any?
|
@@ -98,6 +114,14 @@ module Bolt
|
|
98
114
|
|
99
115
|
@data = raw_data.reject { |k, _| Bolt::Config::INVENTORY_OPTIONS.include?(k) }
|
100
116
|
|
117
|
+
# If the 'modules' key is present in the project configuration file,
|
118
|
+
# use the new, shorter modulepath.
|
119
|
+
@modulepath = if @data.key?('modules')
|
120
|
+
[(@path + 'modules').to_s]
|
121
|
+
else
|
122
|
+
[(@path + 'modules').to_s, (@path + 'site-modules').to_s, (@path + 'site').to_s]
|
123
|
+
end
|
124
|
+
|
101
125
|
# Once bolt.yaml deprecation is removed, this attribute should be removed
|
102
126
|
# and replaced with .project_file in lib/bolt/config.rb
|
103
127
|
@config_file = if (Bolt::Config::BOLT_OPTIONS & @data.keys).any?
|
@@ -149,6 +173,16 @@ module Bolt
|
|
149
173
|
@data['plans']
|
150
174
|
end
|
151
175
|
|
176
|
+
def modules
|
177
|
+
@modules ||= @data['modules']&.map do |mod|
|
178
|
+
if mod.is_a?(String)
|
179
|
+
{ 'name' => mod }
|
180
|
+
else
|
181
|
+
mod
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
152
186
|
def validate
|
153
187
|
if name
|
154
188
|
if name !~ Bolt::Module::MODULE_NAME_REGEX
|
@@ -156,7 +190,7 @@ module Bolt
|
|
156
190
|
Invalid project name '#{name}' in bolt-project.yaml; project name must begin with a lowercase letter
|
157
191
|
and can include lowercase letters, numbers, and underscores.
|
158
192
|
ERROR_STRING
|
159
|
-
elsif Dir.children(Bolt::
|
193
|
+
elsif Dir.children(Bolt::Config::Modulepath::BOLTLIB_PATH).include?(name)
|
160
194
|
raise Bolt::ValidationError, "The project '#{name}' will not be loaded. The project name conflicts "\
|
161
195
|
"with a built-in Bolt module of the same name."
|
162
196
|
end
|
@@ -170,6 +204,22 @@ module Bolt
|
|
170
204
|
raise Bolt::ValidationError, "'#{conf}' in bolt-project.yaml must be an array"
|
171
205
|
end
|
172
206
|
end
|
207
|
+
|
208
|
+
if @data['modules']
|
209
|
+
unless @data['modules'].is_a?(Array)
|
210
|
+
raise Bolt::ValidationError, "'modules' in bolt-project.yaml must be an array"
|
211
|
+
end
|
212
|
+
|
213
|
+
@data['modules'].each do |mod|
|
214
|
+
next if (mod.is_a?(Hash) && mod.key?('name')) || mod.is_a?(String)
|
215
|
+
raise Bolt::ValidationError, "Module declaration #{mod.inspect} must be a hash with a name key"
|
216
|
+
end
|
217
|
+
|
218
|
+
unknown_keys = modules.flat_map(&:keys).uniq - %w[name version_requirement]
|
219
|
+
if unknown_keys.any?
|
220
|
+
@logs << { warn: "Ignoring unknown keys in module declarations: #{unknown_keys.join(', ')}." }
|
221
|
+
end
|
222
|
+
end
|
173
223
|
end
|
174
224
|
|
175
225
|
def check_deprecated_file
|