bolt 2.34.0 → 2.40.1
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 +1 -1
- data/bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/catch_errors.rb +1 -3
- data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +17 -6
- data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +56 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +24 -6
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +27 -8
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +21 -1
- data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +18 -1
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +24 -6
- data/lib/bolt/analytics.rb +27 -8
- data/lib/bolt/apply_result.rb +3 -3
- data/lib/bolt/bolt_option_parser.rb +45 -18
- data/lib/bolt/cli.rb +98 -116
- data/lib/bolt/config.rb +184 -80
- data/lib/bolt/config/options.rb +148 -87
- data/lib/bolt/config/transport/base.rb +10 -19
- data/lib/bolt/config/transport/local.rb +1 -7
- data/lib/bolt/config/transport/options.rb +12 -69
- data/lib/bolt/config/transport/ssh.rb +8 -19
- data/lib/bolt/error.rb +24 -0
- data/lib/bolt/executor.rb +92 -18
- data/lib/bolt/inventory.rb +25 -0
- data/lib/bolt/inventory/group.rb +0 -8
- data/lib/bolt/inventory/options.rb +130 -0
- data/lib/bolt/inventory/target.rb +10 -11
- data/lib/bolt/module_installer.rb +21 -13
- data/lib/bolt/module_installer/resolver.rb +1 -1
- data/lib/bolt/outputter.rb +19 -5
- data/lib/bolt/outputter/human.rb +22 -3
- data/lib/bolt/outputter/json.rb +1 -1
- data/lib/bolt/outputter/logger.rb +1 -1
- data/lib/bolt/outputter/rainbow.rb +13 -2
- data/lib/bolt/pal.rb +18 -6
- data/lib/bolt/pal/yaml_plan.rb +7 -0
- data/lib/bolt/plugin.rb +41 -12
- data/lib/bolt/plugin/cache.rb +76 -0
- data/lib/bolt/plugin/module.rb +4 -4
- data/lib/bolt/plugin/puppetdb.rb +1 -1
- data/lib/bolt/project.rb +59 -40
- data/lib/bolt/project_manager.rb +201 -0
- data/lib/bolt/{project_migrator/config.rb → project_manager/config_migrator.rb} +49 -4
- data/lib/bolt/{project_migrator/inventory.rb → project_manager/inventory_migrator.rb} +3 -3
- data/lib/bolt/{project_migrator/base.rb → project_manager/migrator.rb} +2 -2
- data/lib/bolt/{project_migrator/modules.rb → project_manager/module_migrator.rb} +5 -3
- data/lib/bolt/puppetdb/client.rb +11 -2
- data/lib/bolt/puppetdb/config.rb +4 -3
- data/lib/bolt/rerun.rb +1 -5
- data/lib/bolt/shell/bash.rb +8 -2
- data/lib/bolt/shell/powershell.rb +21 -3
- data/lib/bolt/target.rb +4 -0
- data/lib/bolt/task/run.rb +1 -1
- data/lib/bolt/transport/local.rb +13 -0
- data/lib/bolt/transport/orch.rb +0 -5
- data/lib/bolt/transport/orch/connection.rb +10 -3
- data/lib/bolt/transport/ssh/exec_connection.rb +6 -2
- data/lib/bolt/util.rb +36 -7
- data/lib/bolt/validator.rb +227 -0
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt/yarn.rb +23 -0
- data/lib/bolt_server/base_config.rb +3 -1
- data/lib/bolt_server/config.rb +3 -1
- data/lib/bolt_server/plugin.rb +13 -0
- data/lib/bolt_server/plugin/puppet_connect_data.rb +37 -0
- data/lib/bolt_server/schemas/connect-data.json +22 -0
- data/lib/bolt_server/schemas/partials/task.json +2 -2
- data/lib/bolt_server/transport_app.rb +82 -23
- data/lib/bolt_spec/plans/mock_executor.rb +4 -1
- data/libexec/apply_catalog.rb +1 -1
- data/libexec/custom_facts.rb +1 -1
- data/libexec/query_resources.rb +1 -1
- metadata +22 -14
- data/lib/bolt/project_migrator.rb +0 -80
data/lib/bolt/cli.rb
CHANGED
@@ -21,7 +21,7 @@ require 'bolt/outputter'
|
|
21
21
|
require 'bolt/pal'
|
22
22
|
require 'bolt/plan_creator'
|
23
23
|
require 'bolt/plugin'
|
24
|
-
require 'bolt/
|
24
|
+
require 'bolt/project_manager'
|
25
25
|
require 'bolt/puppetdb'
|
26
26
|
require 'bolt/rerun'
|
27
27
|
require 'bolt/secret'
|
@@ -30,6 +30,7 @@ require 'bolt/version'
|
|
30
30
|
|
31
31
|
module Bolt
|
32
32
|
class CLIExit < StandardError; end
|
33
|
+
|
33
34
|
class CLI
|
34
35
|
COMMANDS = {
|
35
36
|
'command' => %w[run],
|
@@ -164,8 +165,9 @@ module Bolt
|
|
164
165
|
elsif options[:configfile]
|
165
166
|
Bolt::Config.from_file(options[:configfile], options)
|
166
167
|
else
|
167
|
-
|
168
|
-
|
168
|
+
cli_flag = options[:project] || options[:boltdir]
|
169
|
+
project = if cli_flag
|
170
|
+
dir = Pathname.new(cli_flag)
|
169
171
|
if (dir + Bolt::Project::BOLTDIR_NAME).directory?
|
170
172
|
Bolt::Project.create_project(dir + Bolt::Project::BOLTDIR_NAME)
|
171
173
|
else
|
@@ -195,7 +197,12 @@ module Bolt
|
|
195
197
|
@parser_deprecations.each { |dep| Bolt::Logger.deprecation_warning(dep[:type], dep[:msg]) }
|
196
198
|
config.deprecations.each { |dep| Bolt::Logger.deprecation_warning(dep[:type], dep[:msg]) }
|
197
199
|
|
200
|
+
if options[:clear_cache] && File.exist?(config.project.cache_file)
|
201
|
+
FileUtils.rm(config.project.cache_file)
|
202
|
+
end
|
203
|
+
|
198
204
|
warn_inventory_overrides_cli(options)
|
205
|
+
validate_ps_version
|
199
206
|
|
200
207
|
options
|
201
208
|
rescue Bolt::Error => e
|
@@ -203,6 +210,17 @@ module Bolt
|
|
203
210
|
raise e
|
204
211
|
end
|
205
212
|
|
213
|
+
private def validate_ps_version
|
214
|
+
if Bolt::Util.powershell?
|
215
|
+
target = inventory.get_target('localhost')
|
216
|
+
Bolt::Transport::Local.new.with_connection(target) do |conn|
|
217
|
+
# This will automatically validate the powershell version on the Bolt
|
218
|
+
# controller
|
219
|
+
Bolt::Shell::Powershell.new(target, conn)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
206
224
|
def update_targets(options)
|
207
225
|
target_opts = options.keys.select { |opt| %i[query rerun targets].include?(opt) }
|
208
226
|
target_string = "'--targets', '--rerun', or '--query'"
|
@@ -293,7 +311,7 @@ module Bolt
|
|
293
311
|
"Unknown argument(s) #{options[:leftovers].join(', ')}"
|
294
312
|
end
|
295
313
|
|
296
|
-
if options
|
314
|
+
if options.slice(:boltdir, :configfile, :project).length > 1
|
297
315
|
raise Bolt::CLIError, "Only one of '--boltdir', '--project', or '--configfile' may be specified"
|
298
316
|
end
|
299
317
|
|
@@ -340,15 +358,10 @@ module Bolt
|
|
340
358
|
def warn_inventory_overrides_cli(opts)
|
341
359
|
inventory_source = if ENV[Bolt::Inventory::ENVIRONMENT_VAR]
|
342
360
|
Bolt::Inventory::ENVIRONMENT_VAR
|
343
|
-
elsif config.inventoryfile
|
361
|
+
elsif config.inventoryfile
|
344
362
|
config.inventoryfile
|
345
|
-
|
346
|
-
|
347
|
-
Bolt::Util.file_stat(config.default_inventoryfile)
|
348
|
-
config.default_inventoryfile
|
349
|
-
rescue Errno::ENOENT
|
350
|
-
nil
|
351
|
-
end
|
363
|
+
elsif File.exist?(config.default_inventoryfile)
|
364
|
+
config.default_inventoryfile
|
352
365
|
end
|
353
366
|
|
354
367
|
inventory_cli_opts = %i[authentication escalation transports].each_with_object([]) do |key, acc|
|
@@ -392,7 +405,7 @@ module Bolt
|
|
392
405
|
output_format: config.format,
|
393
406
|
# For continuity
|
394
407
|
boltdir_type: config.project.type
|
395
|
-
}
|
408
|
+
}.merge!(analytics.plan_counts(config.project.plans_path))
|
396
409
|
|
397
410
|
# Only include target and inventory info for commands that take a targets
|
398
411
|
# list. This avoids loading inventory for commands that don't need it.
|
@@ -456,15 +469,14 @@ module Bolt
|
|
456
469
|
when 'project'
|
457
470
|
case options[:action]
|
458
471
|
when 'init'
|
459
|
-
code =
|
472
|
+
code = Bolt::ProjectManager.new(config, outputter, pal)
|
473
|
+
.create(Dir.pwd, options[:object], options[:modules])
|
460
474
|
when 'migrate'
|
461
|
-
code = Bolt::
|
475
|
+
code = Bolt::ProjectManager.new(config, outputter, pal).migrate
|
462
476
|
end
|
463
477
|
when 'plan'
|
464
478
|
case options[:action]
|
465
479
|
when 'new'
|
466
|
-
command = Bolt::Util.powershell? ? 'New-BoltPlan' : 'bolt plan new'
|
467
|
-
@logger.warn("Command '#{command}' is experimental and subject to changes.")
|
468
480
|
plan_name = options[:object]
|
469
481
|
|
470
482
|
# If this passes validation, it will return the path to the plan to create
|
@@ -479,9 +491,9 @@ module Bolt
|
|
479
491
|
when 'module'
|
480
492
|
case options[:action]
|
481
493
|
when 'add'
|
482
|
-
code = add_project_module(options[:object], config.project)
|
494
|
+
code = add_project_module(options[:object], config.project, config.module_install)
|
483
495
|
when 'install'
|
484
|
-
code = install_project_modules(config.project, options[:force], options[:resolve])
|
496
|
+
code = install_project_modules(config.project, config.module_install, options[:force], options[:resolve])
|
485
497
|
when 'generate-types'
|
486
498
|
code = generate_types
|
487
499
|
end
|
@@ -526,12 +538,15 @@ module Bolt
|
|
526
538
|
validate_file('script', script)
|
527
539
|
executor.run_script(targets, script, options[:leftovers], executor_opts)
|
528
540
|
when 'task'
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
541
|
+
r = outputter.spin do
|
542
|
+
pal.run_task(options[:object],
|
543
|
+
targets,
|
544
|
+
options[:task_options],
|
545
|
+
executor,
|
546
|
+
inventory,
|
547
|
+
options[:description])
|
548
|
+
end
|
549
|
+
r
|
535
550
|
when 'file'
|
536
551
|
src = options[:object]
|
537
552
|
dest = options[:leftovers].first
|
@@ -575,10 +590,15 @@ module Bolt
|
|
575
590
|
outputter.print_task_info(pal.get_task(task_name))
|
576
591
|
end
|
577
592
|
|
593
|
+
# Filters a list of content by matching substring.
|
594
|
+
#
|
595
|
+
private def filter_content(content, filter)
|
596
|
+
return content unless content && filter
|
597
|
+
content.select { |name,| name.include?(filter) }
|
598
|
+
end
|
599
|
+
|
578
600
|
def list_tasks
|
579
|
-
tasks = pal.list_tasks
|
580
|
-
tasks.select! { |task| task.first.include?(options[:filter]) } if options[:filter]
|
581
|
-
tasks.select! { |task| config.project.tasks.include?(task.first) } unless config.project.tasks.nil?
|
601
|
+
tasks = filter_content(pal.list_tasks(filter_content: true), options[:filter])
|
582
602
|
outputter.print_tasks(tasks, pal.user_modulepath)
|
583
603
|
end
|
584
604
|
|
@@ -587,9 +607,7 @@ module Bolt
|
|
587
607
|
end
|
588
608
|
|
589
609
|
def list_plans
|
590
|
-
plans = pal.list_plans
|
591
|
-
plans.select! { |plan| plan.first.include?(options[:filter]) } if options[:filter]
|
592
|
-
plans.select! { |plan| config.project.plans.include?(plan.first) } unless config.project.plans.nil?
|
610
|
+
plans = filter_content(pal.list_plans(filter_content: true), options[:filter])
|
593
611
|
outputter.print_plans(plans, pal.user_modulepath)
|
594
612
|
end
|
595
613
|
|
@@ -663,7 +681,9 @@ module Bolt
|
|
663
681
|
|
664
682
|
executor.subscribe(log_outputter)
|
665
683
|
executor.start_plan(plan_context)
|
666
|
-
result =
|
684
|
+
result = outputter.spin do
|
685
|
+
pal.run_plan(plan_name, plan_arguments, executor, inventory, puppetdb_client)
|
686
|
+
end
|
667
687
|
|
668
688
|
# If a non-bolt exception bubbles up the plan won't get finished
|
669
689
|
executor.finish_plan(result)
|
@@ -726,79 +746,9 @@ module Bolt
|
|
726
746
|
0
|
727
747
|
end
|
728
748
|
|
729
|
-
# Initializes a specified directory as a Bolt project and installs any modules
|
730
|
-
# specified by the user, along with their dependencies
|
731
|
-
def initialize_project
|
732
|
-
# Dir.pwd will return backslashes on Windows, but Pathname always uses
|
733
|
-
# forward slashes to concatenate paths. This results in paths like
|
734
|
-
# C:\User\Administrator/modules, which fail module install. This ensure
|
735
|
-
# forward slashes in the cwd path.
|
736
|
-
dir = File.expand_path(Dir.pwd)
|
737
|
-
name = options[:object] || File.basename(dir)
|
738
|
-
if name !~ Bolt::Module::MODULE_NAME_REGEX
|
739
|
-
if options[:object]
|
740
|
-
raise Bolt::ValidationError, "The provided project name '#{name}' is invalid; "\
|
741
|
-
"project name must begin with a lowercase letter and can include lowercase "\
|
742
|
-
"letters, numbers, and underscores."
|
743
|
-
else
|
744
|
-
command = Bolt::Util.powershell? ? 'New-BoltProject -Name <NAME>' : 'bolt project init <NAME>'
|
745
|
-
raise Bolt::ValidationError, "The current directory name '#{name}' is an invalid "\
|
746
|
-
"project name. Please specify a name using '#{command}'."
|
747
|
-
end
|
748
|
-
end
|
749
|
-
|
750
|
-
project = Pathname.new(dir)
|
751
|
-
old_config = project + 'bolt.yaml'
|
752
|
-
config = project + 'bolt-project.yaml'
|
753
|
-
puppetfile = project + 'Puppetfile'
|
754
|
-
moduledir = project + 'modules'
|
755
|
-
|
756
|
-
# Warn the user if the project directory already exists. We don't error
|
757
|
-
# here since users might not have installed any modules yet. If both
|
758
|
-
# bolt.yaml and bolt-project.yaml exist, this will just warn about
|
759
|
-
# bolt-project.yaml and subsequent Bolt actions will warn about both files
|
760
|
-
# existing.
|
761
|
-
if config.exist?
|
762
|
-
@logger.warn "Found existing project directory at #{project}. Skipping file creation."
|
763
|
-
elsif old_config.exist?
|
764
|
-
@logger.warn "Found existing #{old_config.basename} at #{project}. "\
|
765
|
-
"#{old_config.basename} is deprecated, please rename to #{config.basename}."
|
766
|
-
end
|
767
|
-
|
768
|
-
# If modules were specified, first check if there is already a Puppetfile
|
769
|
-
# at the project directory, erroring if there is. If there is no
|
770
|
-
# Puppetfile, install the specified modules. The module installer will
|
771
|
-
# resolve dependencies, generate a Puppetfile, and install the modules.
|
772
|
-
if options[:modules]
|
773
|
-
if puppetfile.exist?
|
774
|
-
raise Bolt::CLIError,
|
775
|
-
"Found existing Puppetfile at #{puppetfile}, unable to initialize "\
|
776
|
-
"project with modules."
|
777
|
-
end
|
778
|
-
|
779
|
-
installer = Bolt::ModuleInstaller.new(outputter, pal)
|
780
|
-
installer.install(options[:modules], puppetfile, moduledir)
|
781
|
-
end
|
782
|
-
|
783
|
-
# If either bolt.yaml or bolt-project.yaml exist, the user has already
|
784
|
-
# been warned and we can just finish project creation. Otherwise, create a
|
785
|
-
# bolt-project.yaml with the project name in it.
|
786
|
-
unless config.exist? || old_config.exist?
|
787
|
-
begin
|
788
|
-
content = { 'name' => name }
|
789
|
-
File.write(config.to_path, content.to_yaml)
|
790
|
-
outputter.print_message "Successfully created Bolt project at #{project}"
|
791
|
-
rescue StandardError => e
|
792
|
-
raise Bolt::FileError.new("Could not create bolt-project.yaml at #{project}: #{e.message}", nil)
|
793
|
-
end
|
794
|
-
end
|
795
|
-
|
796
|
-
0
|
797
|
-
end
|
798
|
-
|
799
749
|
# Installs modules declared in the project configuration file.
|
800
750
|
#
|
801
|
-
def install_project_modules(project, force, resolve)
|
751
|
+
def install_project_modules(project, config, force, resolve)
|
802
752
|
assert_project_file(project)
|
803
753
|
assert_puppetfile_or_module_command(project.modules)
|
804
754
|
|
@@ -808,30 +758,55 @@ module Bolt
|
|
808
758
|
return 0
|
809
759
|
end
|
810
760
|
|
761
|
+
if resolve != false && config.any?
|
762
|
+
@logger.warn(
|
763
|
+
"Detected configuration for 'module-install'. This configuration is currently "\
|
764
|
+
"only supported when installing modules, not when resolving module dependencies. "\
|
765
|
+
"For more information, see https://pup.pt/bolt-module-install"
|
766
|
+
)
|
767
|
+
end
|
768
|
+
|
769
|
+
modules = project.modules || []
|
811
770
|
installer = Bolt::ModuleInstaller.new(outputter, pal)
|
812
771
|
|
813
|
-
ok =
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
772
|
+
ok = outputter.spin do
|
773
|
+
installer.install(modules,
|
774
|
+
project.puppetfile,
|
775
|
+
project.managed_moduledir,
|
776
|
+
config,
|
777
|
+
force: force,
|
778
|
+
resolve: resolve)
|
779
|
+
end
|
780
|
+
|
818
781
|
ok ? 0 : 1
|
819
782
|
end
|
820
783
|
|
821
784
|
# Adds a single module to the project.
|
822
785
|
#
|
823
|
-
def add_project_module(name, project)
|
786
|
+
def add_project_module(name, project, config)
|
824
787
|
assert_project_file(project)
|
825
788
|
assert_puppetfile_or_module_command(project.modules)
|
826
789
|
|
790
|
+
if config.any?
|
791
|
+
@logger.warn(
|
792
|
+
"Detected configuration for 'module-install'. This configuration is currently "\
|
793
|
+
"only supported when installing modules, not when resolving module dependencies. "\
|
794
|
+
"For more information, see https://pup.pt/bolt-module-install"
|
795
|
+
)
|
796
|
+
end
|
797
|
+
|
827
798
|
modules = project.modules || []
|
828
799
|
installer = Bolt::ModuleInstaller.new(outputter, pal)
|
829
800
|
|
830
|
-
ok =
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
801
|
+
ok = outputter.spin do
|
802
|
+
installer.add(name,
|
803
|
+
modules,
|
804
|
+
project.puppetfile,
|
805
|
+
project.managed_moduledir,
|
806
|
+
project.project_file,
|
807
|
+
config)
|
808
|
+
end
|
809
|
+
|
835
810
|
ok ? 0 : 1
|
836
811
|
end
|
837
812
|
|
@@ -860,7 +835,10 @@ module Bolt
|
|
860
835
|
|
861
836
|
outputter.print_message("Installing modules from Puppetfile")
|
862
837
|
installer = Bolt::ModuleInstaller.new(outputter, pal)
|
863
|
-
ok =
|
838
|
+
ok = outputter.spin do
|
839
|
+
installer.install_puppetfile(puppetfile, moduledir, puppetfile_config)
|
840
|
+
end
|
841
|
+
|
864
842
|
ok ? 0 : 1
|
865
843
|
end
|
866
844
|
|
@@ -966,7 +944,11 @@ module Bolt
|
|
966
944
|
end
|
967
945
|
|
968
946
|
def outputter
|
969
|
-
@outputter ||= Bolt::Outputter.for_format(config.format,
|
947
|
+
@outputter ||= Bolt::Outputter.for_format(config.format,
|
948
|
+
config.color,
|
949
|
+
options[:verbose],
|
950
|
+
config.trace,
|
951
|
+
config.spinner)
|
970
952
|
end
|
971
953
|
|
972
954
|
def log_outputter
|
data/lib/bolt/config.rb
CHANGED
@@ -7,6 +7,7 @@ require 'bolt/project'
|
|
7
7
|
require 'bolt/logger'
|
8
8
|
require 'bolt/util'
|
9
9
|
require 'bolt/config/options'
|
10
|
+
require 'bolt/validator'
|
10
11
|
|
11
12
|
module Bolt
|
12
13
|
class UnknownTransportError < Bolt::Error
|
@@ -32,53 +33,110 @@ module Bolt
|
|
32
33
|
end
|
33
34
|
|
34
35
|
def self.from_project(project, overrides = {})
|
35
|
-
logs
|
36
|
+
logs = []
|
37
|
+
deprecations = []
|
38
|
+
|
36
39
|
conf = if project.project_file == project.config_file
|
37
40
|
project.data
|
38
41
|
else
|
39
42
|
c = Bolt::Util.read_optional_yaml_hash(project.config_file, 'config')
|
43
|
+
|
44
|
+
# Validate the config against the schema. This will raise a single error
|
45
|
+
# with all validation errors.
|
46
|
+
Bolt::Validator.new.tap do |validator|
|
47
|
+
validator.validate(c, bolt_schema, project.config_file.to_s)
|
48
|
+
|
49
|
+
validator.warnings.each { |warning| logs << { warn: warning } }
|
50
|
+
|
51
|
+
validator.deprecations.each do |dep|
|
52
|
+
deprecations << { type: "#{BOLT_CONFIG_NAME} #{dep[:option]}", msg: dep[:message] }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
40
56
|
logs << { debug: "Loaded configuration from #{project.config_file}" } if File.exist?(project.config_file)
|
41
57
|
c
|
42
58
|
end
|
43
|
-
|
44
59
|
data = load_defaults(project).push(
|
45
|
-
filepath:
|
46
|
-
data:
|
47
|
-
logs:
|
48
|
-
deprecations:
|
60
|
+
filepath: project.config_file,
|
61
|
+
data: conf,
|
62
|
+
logs: logs,
|
63
|
+
deprecations: deprecations
|
49
64
|
)
|
50
65
|
|
51
66
|
new(project, data, overrides)
|
52
67
|
end
|
53
68
|
|
54
69
|
def self.from_file(configfile, overrides = {})
|
55
|
-
project
|
56
|
-
logs
|
70
|
+
project = Bolt::Project.create_project(Pathname.new(configfile).expand_path.dirname)
|
71
|
+
logs = []
|
72
|
+
deprecations = []
|
57
73
|
|
58
74
|
conf = if project.project_file == project.config_file
|
59
75
|
project.data
|
60
76
|
else
|
61
77
|
c = Bolt::Util.read_yaml_hash(configfile, 'config')
|
78
|
+
|
79
|
+
# Validate the config against the schema. This will raise a single error
|
80
|
+
# with all validation errors.
|
81
|
+
Bolt::Validator.new.tap do |validator|
|
82
|
+
validator.validate(c, bolt_schema, project.config_file.to_s)
|
83
|
+
|
84
|
+
validator.warnings.each { |warning| logs << { warn: warning } }
|
85
|
+
|
86
|
+
validator.deprecations.each do |dep|
|
87
|
+
deprecations << { type: "#{BOLT_CONFIG_NAME} #{dep[:option]}", msg: dep[:message] }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
62
91
|
logs << { debug: "Loaded configuration from #{configfile}" }
|
63
92
|
c
|
64
93
|
end
|
65
94
|
|
66
95
|
data = load_defaults(project).push(
|
67
|
-
filepath:
|
68
|
-
data:
|
69
|
-
logs:
|
70
|
-
deprecations:
|
96
|
+
filepath: configfile,
|
97
|
+
data: conf,
|
98
|
+
logs: logs,
|
99
|
+
deprecations: deprecations
|
71
100
|
)
|
72
101
|
|
73
102
|
new(project, data, overrides)
|
74
103
|
end
|
75
104
|
|
76
|
-
|
77
|
-
|
78
|
-
|
105
|
+
# Builds a hash of definitions for transport configuration.
|
106
|
+
#
|
107
|
+
def self.transport_definitions
|
108
|
+
INVENTORY_OPTIONS.each_with_object({}) do |(option, definition), acc|
|
109
|
+
acc[option] = TRANSPORT_CONFIG.key?(option) ? definition.merge(TRANSPORT_CONFIG[option].schema) : definition
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Builds the schema for bolt-defaults.yaml used by the validator.
|
114
|
+
#
|
115
|
+
def self.defaults_schema
|
116
|
+
schema = {
|
117
|
+
type: Hash,
|
118
|
+
properties: BOLT_DEFAULTS_OPTIONS.map { |opt| [opt, _ref: opt] }.to_h,
|
119
|
+
definitions: OPTIONS.merge(transport_definitions)
|
120
|
+
}
|
121
|
+
|
122
|
+
schema[:definitions]['inventory-config'][:properties] = transport_definitions
|
123
|
+
|
124
|
+
schema
|
125
|
+
end
|
79
126
|
|
127
|
+
# Builds the schema for bolt.yaml used by the validator.
|
128
|
+
#
|
129
|
+
def self.bolt_schema
|
130
|
+
{
|
131
|
+
type: Hash,
|
132
|
+
properties: (BOLT_OPTIONS + INVENTORY_OPTIONS.keys).map { |opt| [opt, _ref: opt] }.to_h,
|
133
|
+
definitions: OPTIONS.merge(transport_definitions)
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.system_path
|
80
138
|
if Bolt::Util.windows?
|
81
|
-
Pathname.new(File.join(
|
139
|
+
Pathname.new(File.join(ENV['ALLUSERSPROFILE'], 'PuppetLabs', 'bolt', 'etc'))
|
82
140
|
else
|
83
141
|
Pathname.new(File.join('/etc', 'puppetlabs', 'bolt'))
|
84
142
|
end
|
@@ -94,9 +152,10 @@ module Bolt
|
|
94
152
|
# projects. This file does not allow project-specific configuration such as 'hiera-config' and
|
95
153
|
# 'inventoryfile', and nests all default inventory configuration under an 'inventory-config' key.
|
96
154
|
def self.load_bolt_defaults_yaml(dir)
|
97
|
-
filepath
|
98
|
-
data
|
99
|
-
logs
|
155
|
+
filepath = dir + BOLT_DEFAULTS_NAME
|
156
|
+
data = Bolt::Util.read_yaml_hash(filepath, 'config')
|
157
|
+
logs = [{ debug: "Loaded configuration from #{filepath}" }]
|
158
|
+
deprecations = []
|
100
159
|
|
101
160
|
# Warn if 'bolt.yaml' detected in same directory.
|
102
161
|
if File.exist?(bolt_yaml = dir + BOLT_CONFIG_NAME)
|
@@ -106,6 +165,18 @@ module Bolt
|
|
106
165
|
)
|
107
166
|
end
|
108
167
|
|
168
|
+
# Validate the config against the schema. This will raise a single error
|
169
|
+
# with all validation errors.
|
170
|
+
Bolt::Validator.new.tap do |validator|
|
171
|
+
validator.validate(data, defaults_schema, filepath)
|
172
|
+
|
173
|
+
validator.warnings.each { |warning| logs << { warn: warning } }
|
174
|
+
|
175
|
+
validator.deprecations.each do |dep|
|
176
|
+
deprecations << { type: "#{BOLT_DEFAULTS_NAME} #{dep[:option]}", msg: dep[:message] }
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
109
180
|
# Remove project-specific config such as hiera-config, etc.
|
110
181
|
project_config = data.slice(*(BOLT_PROJECT_OPTIONS - BOLT_DEFAULTS_OPTIONS))
|
111
182
|
|
@@ -148,18 +219,30 @@ module Bolt
|
|
148
219
|
data = data.merge(data.delete('inventory-config'))
|
149
220
|
end
|
150
221
|
|
151
|
-
{ filepath: filepath, data: data, logs: logs, deprecations:
|
222
|
+
{ filepath: filepath, data: data, logs: logs, deprecations: deprecations }
|
152
223
|
end
|
153
224
|
|
154
225
|
# Loads a 'bolt.yaml' file, the legacy configuration file. There's no special munging needed
|
155
226
|
# here since Bolt::Config will just ignore any invalid keys.
|
156
227
|
def self.load_bolt_yaml(dir)
|
157
|
-
filepath
|
158
|
-
data
|
159
|
-
logs
|
228
|
+
filepath = dir + BOLT_CONFIG_NAME
|
229
|
+
data = Bolt::Util.read_yaml_hash(filepath, 'config')
|
230
|
+
logs = [{ debug: "Loaded configuration from #{filepath}" }]
|
160
231
|
deprecations = [{ type: 'Using bolt.yaml for system configuration',
|
161
|
-
msg: "Configuration file #{filepath} is deprecated and will be removed in
|
162
|
-
"
|
232
|
+
msg: "Configuration file #{filepath} is deprecated and will be removed in Bolt 3.0. "\
|
233
|
+
"See https://pup.pt/update-bolt-config for how to update to the latest Bolt practices." }]
|
234
|
+
|
235
|
+
# Validate the config against the schema. This will raise a single error
|
236
|
+
# with all validation errors.
|
237
|
+
Bolt::Validator.new.tap do |validator|
|
238
|
+
validator.validate(data, bolt_schema, filepath)
|
239
|
+
|
240
|
+
validator.warnings.each { |warning| logs << { warn: warning } }
|
241
|
+
|
242
|
+
validator.deprecations.each do |dep|
|
243
|
+
deprecations << { type: "#{BOLT_CONFIG_NAME} #{dep[:option]}", msg: dep[:message] }
|
244
|
+
end
|
245
|
+
end
|
163
246
|
|
164
247
|
{ filepath: filepath, data: data, logs: logs, deprecations: deprecations }
|
165
248
|
end
|
@@ -206,17 +289,21 @@ module Bolt
|
|
206
289
|
@config_files = []
|
207
290
|
|
208
291
|
default_data = {
|
292
|
+
'apply-settings' => {},
|
209
293
|
'apply_settings' => {},
|
210
294
|
'color' => true,
|
211
295
|
'compile-concurrency' => Etc.nprocessors,
|
212
296
|
'concurrency' => default_concurrency,
|
213
297
|
'format' => 'human',
|
214
298
|
'log' => { 'console' => {} },
|
299
|
+
'module-install' => {},
|
300
|
+
'plugin-hooks' => {},
|
215
301
|
'plugin_hooks' => {},
|
216
302
|
'plugins' => {},
|
217
303
|
'puppetdb' => {},
|
218
304
|
'puppetfile' => {},
|
219
305
|
'save-rerun' => true,
|
306
|
+
'spinner' => true,
|
220
307
|
'transport' => 'ssh'
|
221
308
|
}
|
222
309
|
|
@@ -271,7 +358,7 @@ module Bolt
|
|
271
358
|
|
272
359
|
# Set console log to debug if in debug mode
|
273
360
|
if options[:debug]
|
274
|
-
overrides['log'] = { 'console' => { 'level' =>
|
361
|
+
overrides['log'] = { 'console' => { 'level' => 'debug' } }
|
275
362
|
end
|
276
363
|
|
277
364
|
if options[:puppetfile_path]
|
@@ -280,6 +367,9 @@ module Bolt
|
|
280
367
|
|
281
368
|
overrides['trace'] = opts['trace'] if opts.key?('trace')
|
282
369
|
|
370
|
+
# Validate the overrides
|
371
|
+
Bolt::Validator.new.validate(overrides, self.class.bolt_schema, 'command line')
|
372
|
+
|
283
373
|
overrides
|
284
374
|
end
|
285
375
|
|
@@ -296,7 +386,7 @@ module Bolt
|
|
296
386
|
when *TRANSPORT_CONFIG.keys
|
297
387
|
Bolt::Util.deep_merge(val1, val2)
|
298
388
|
# Hash values are shallow merged
|
299
|
-
when 'puppetdb', 'plugin_hooks', 'apply_settings', 'log'
|
389
|
+
when 'puppetdb', 'plugin-hooks', 'plugin_hooks', 'apply-settings', 'apply_settings', 'log'
|
300
390
|
val1.merge(val2)
|
301
391
|
# All other values are overwritten
|
302
392
|
else
|
@@ -333,8 +423,9 @@ module Bolt
|
|
333
423
|
end
|
334
424
|
|
335
425
|
# Filter hashes to only include valid options
|
336
|
-
|
337
|
-
|
426
|
+
%w[apply-settings apply_settings module-install puppetfile].each do |opt|
|
427
|
+
@data[opt] = @data[opt].slice(*OPTIONS.dig(opt, :properties).keys)
|
428
|
+
end
|
338
429
|
end
|
339
430
|
|
340
431
|
private def normalize_log(target)
|
@@ -357,31 +448,14 @@ module Bolt
|
|
357
448
|
next if val == 'disable'
|
358
449
|
|
359
450
|
name = normalize_log(key)
|
451
|
+
acc[name] = val.slice('append', 'level').transform_keys(&:to_sym)
|
360
452
|
|
361
|
-
|
362
|
-
unless val.is_a?(Hash)
|
363
|
-
raise Bolt::ValidationError,
|
364
|
-
"config of log #{name} must be a Hash, received #{val.class} #{val.inspect}"
|
365
|
-
end
|
366
|
-
|
367
|
-
acc[name] = val.slice('append', 'level')
|
368
|
-
.transform_keys(&:to_sym)
|
453
|
+
next unless acc[name][:level] == 'notice'
|
369
454
|
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
end
|
375
|
-
unless Bolt::Logger.valid_level?(v)
|
376
|
-
raise Bolt::ValidationError,
|
377
|
-
"level of log #{name} must be one of #{Bolt::Logger.levels.join(', ')}; received #{v}"
|
378
|
-
end
|
379
|
-
end
|
380
|
-
|
381
|
-
if (v = acc[name][:append]) && v != true && v != false
|
382
|
-
raise Bolt::ValidationError,
|
383
|
-
"append flag of log #{name} must be a Boolean, received #{v.class} #{v.inspect}"
|
384
|
-
end
|
455
|
+
@deprecations << {
|
456
|
+
type: 'notice log level',
|
457
|
+
msg: "Log level 'notice' is deprecated and will be removed in Bolt 3.0. Use 'info' instead."
|
458
|
+
}
|
385
459
|
end
|
386
460
|
end
|
387
461
|
|
@@ -397,40 +471,36 @@ module Bolt
|
|
397
471
|
"is automatically appended to the modulepath and cannot be configured."
|
398
472
|
end
|
399
473
|
|
400
|
-
keys = OPTIONS.keys - %w[plugins plugin_hooks puppetdb]
|
401
|
-
keys.each do |key|
|
402
|
-
next unless Bolt::Util.references?(@data[key])
|
403
|
-
valid_keys = TRANSPORT_CONFIG.keys + %w[plugins plugin_hooks puppetdb]
|
404
|
-
raise Bolt::ValidationError,
|
405
|
-
"Found unsupported key _plugin in config setting #{key}. Plugins are only available in "\
|
406
|
-
"#{valid_keys.join(', ')}."
|
407
|
-
end
|
408
|
-
|
409
|
-
unless concurrency.is_a?(Integer) && concurrency > 0
|
410
|
-
raise Bolt::ValidationError,
|
411
|
-
"Concurrency must be a positive Integer, received #{concurrency.class} #{concurrency}"
|
412
|
-
end
|
413
|
-
|
414
|
-
unless compile_concurrency.is_a?(Integer) && compile_concurrency > 0
|
415
|
-
raise Bolt::ValidationError,
|
416
|
-
"Compile concurrency must be a positive Integer, received #{compile_concurrency.class} "\
|
417
|
-
"#{compile_concurrency}"
|
418
|
-
end
|
419
|
-
|
420
474
|
compile_limit = 2 * Etc.nprocessors
|
421
475
|
unless compile_concurrency < compile_limit
|
422
476
|
raise Bolt::ValidationError, "Compilation is CPU-intensive, set concurrency less than #{compile_limit}"
|
423
477
|
end
|
424
478
|
|
425
|
-
|
426
|
-
|
479
|
+
%w[hiera-config trusted-external-command inventoryfile].each do |opt|
|
480
|
+
Bolt::Util.validate_file(opt, @data[opt]) if @data[opt]
|
427
481
|
end
|
428
482
|
|
429
|
-
|
430
|
-
|
483
|
+
if File.exist?(default_inventoryfile)
|
484
|
+
Bolt::Util.validate_file('inventory file', default_inventoryfile)
|
485
|
+
end
|
431
486
|
|
432
|
-
|
433
|
-
|
487
|
+
# Warn the user how they should be using the 'puppetfile' or
|
488
|
+
# 'module-install' config options. We don't error here since these
|
489
|
+
# settings can be set at the user or system level.
|
490
|
+
if @project.modules && puppetfile_config.any? && module_install.empty?
|
491
|
+
command = Bolt::Util.powershell? ? 'Update-BoltProject' : 'bolt project migrate'
|
492
|
+
@logs << { warn: "Detected configuration for 'puppetfile'. This setting is not "\
|
493
|
+
"used when 'modules' is configured. Use 'module-install' instead. "\
|
494
|
+
"To automatically update your project configuration, run '#{command}'." }
|
495
|
+
elsif @project.modules.nil? && puppetfile_config.empty? && module_install.any?
|
496
|
+
@logs << { warn: "Detected configuration for 'module-install'. This setting is not "\
|
497
|
+
"used when 'modules' is not configured. Use 'puppetfile' instead." }
|
498
|
+
elsif @project.modules && puppetfile_config.any? && module_install.any?
|
499
|
+
@logs << { warn: "Detected configuration for 'puppetfile' and 'module-install'. Using "\
|
500
|
+
"configuration for 'module-install' because 'modules' is also configured." }
|
501
|
+
elsif @project.modules.nil? && puppetfile_config.any? && module_install.any?
|
502
|
+
@logs << { warn: "Detected configuration for 'puppetfile' and 'module-install'. Using "\
|
503
|
+
"configuration for 'puppetfile' because 'modules' is not configured." }
|
434
504
|
end
|
435
505
|
end
|
436
506
|
|
@@ -464,6 +534,10 @@ module Bolt
|
|
464
534
|
@data['modulepath'] = value
|
465
535
|
end
|
466
536
|
|
537
|
+
def plugin_cache
|
538
|
+
@project.plugin_cache || @data['plugin-cache'] || {}
|
539
|
+
end
|
540
|
+
|
467
541
|
def concurrency
|
468
542
|
@data['concurrency']
|
469
543
|
end
|
@@ -496,6 +570,10 @@ module Bolt
|
|
496
570
|
@data['save-rerun']
|
497
571
|
end
|
498
572
|
|
573
|
+
def spinner
|
574
|
+
@data['spinner']
|
575
|
+
end
|
576
|
+
|
499
577
|
def inventoryfile
|
500
578
|
@data['inventoryfile']
|
501
579
|
end
|
@@ -513,7 +591,18 @@ module Bolt
|
|
513
591
|
end
|
514
592
|
|
515
593
|
def plugin_hooks
|
516
|
-
@data['plugin_hooks']
|
594
|
+
if @data['plugin-hooks'].any? && @data['plugin_hooks'].any?
|
595
|
+
Bolt::Logger.warn_once(
|
596
|
+
"plugin-hooks and plugin_hooks set",
|
597
|
+
"Detected configuration for 'plugin-hooks' and 'plugin_hooks'. Bolt will ignore 'plugin_hooks'."
|
598
|
+
)
|
599
|
+
|
600
|
+
@data['plugin-hooks']
|
601
|
+
elsif @data['plugin-hooks'].any?
|
602
|
+
@data['plugin-hooks']
|
603
|
+
else
|
604
|
+
@data['plugin_hooks']
|
605
|
+
end
|
517
606
|
end
|
518
607
|
|
519
608
|
def trusted_external
|
@@ -521,13 +610,28 @@ module Bolt
|
|
521
610
|
end
|
522
611
|
|
523
612
|
def apply_settings
|
524
|
-
@data['apply_settings']
|
613
|
+
if @data['apply-settings'].any? && @data['apply_settings'].any?
|
614
|
+
Bolt::Logger.warn_once(
|
615
|
+
"apply-settings and apply_settings set",
|
616
|
+
"Detected configuration for 'apply-settings' and 'apply_settings'. Bolt will ignore 'apply_settings'."
|
617
|
+
)
|
618
|
+
|
619
|
+
@data['apply-settings']
|
620
|
+
elsif @data['apply-settings'].any?
|
621
|
+
@data['apply-settings']
|
622
|
+
else
|
623
|
+
@data['apply_settings']
|
624
|
+
end
|
525
625
|
end
|
526
626
|
|
527
627
|
def transport
|
528
628
|
@data['transport']
|
529
629
|
end
|
530
630
|
|
631
|
+
def module_install
|
632
|
+
@project.module_install || @data['module-install']
|
633
|
+
end
|
634
|
+
|
531
635
|
# Check if there is a case-insensitive match to the path
|
532
636
|
def check_path_case(type, paths)
|
533
637
|
return if paths.nil?
|