bolt 2.7.0 → 2.11.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 +4 -3
- data/bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb +2 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/resourceinstance.rb +27 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/result.rb +2 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/resultset.rb +2 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/target.rb +4 -3
- data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +192 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_resources.rb +122 -0
- data/bolt-modules/boltlib/types/planresult.pp +12 -1
- data/bolt-modules/file/lib/puppet/functions/file/exists.rb +3 -1
- data/bolt-modules/file/lib/puppet/functions/file/join.rb +1 -1
- data/bolt-modules/file/lib/puppet/functions/file/read.rb +2 -1
- data/bolt-modules/file/lib/puppet/functions/file/readable.rb +3 -1
- data/bolt-modules/file/lib/puppet/functions/file/write.rb +3 -1
- data/lib/bolt/applicator.rb +3 -2
- data/lib/bolt/apply_inventory.rb +1 -1
- data/lib/bolt/apply_result.rb +1 -1
- data/lib/bolt/apply_target.rb +11 -2
- data/lib/bolt/bolt_option_parser.rb +22 -6
- data/lib/bolt/cli.rb +52 -22
- data/lib/bolt/config.rb +57 -27
- data/lib/bolt/config/transport/base.rb +3 -3
- data/lib/bolt/config/transport/docker.rb +2 -0
- data/lib/bolt/config/transport/local.rb +2 -0
- data/lib/bolt/config/transport/orch.rb +4 -2
- data/lib/bolt/config/transport/remote.rb +2 -0
- data/lib/bolt/config/transport/ssh.rb +51 -2
- data/lib/bolt/config/transport/winrm.rb +3 -1
- data/lib/bolt/executor.rb +16 -0
- data/lib/bolt/inventory.rb +2 -1
- data/lib/bolt/inventory/group.rb +1 -0
- data/lib/bolt/inventory/inventory.rb +5 -0
- data/lib/bolt/inventory/target.rb +17 -1
- data/lib/bolt/node/output.rb +1 -1
- data/lib/bolt/outputter/human.rb +5 -4
- data/lib/bolt/outputter/json.rb +1 -1
- data/lib/bolt/pal.rb +32 -14
- data/lib/bolt/pal/yaml_plan.rb +1 -0
- data/lib/bolt/plugin.rb +14 -8
- data/lib/bolt/plugin/module.rb +40 -7
- data/lib/bolt/plugin/puppetdb.rb +5 -2
- data/lib/bolt/project.rb +135 -0
- data/lib/bolt/puppetdb/config.rb +16 -28
- data/lib/bolt/rerun.rb +1 -1
- data/lib/bolt/resource_instance.rb +126 -0
- data/lib/bolt/result.rb +46 -23
- data/lib/bolt/result_set.rb +2 -5
- data/lib/bolt/secret.rb +20 -4
- data/lib/bolt/shell/bash.rb +12 -5
- data/lib/bolt/shell/powershell.rb +12 -4
- data/lib/bolt/target.rb +16 -1
- data/lib/bolt/transport/base.rb +24 -8
- data/lib/bolt/transport/orch.rb +4 -0
- data/lib/bolt/transport/ssh.rb +6 -2
- data/lib/bolt/transport/ssh/connection.rb +4 -0
- data/lib/bolt/transport/ssh/exec_connection.rb +110 -0
- data/lib/bolt/transport/winrm/connection.rb +6 -2
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/pe/pal.rb +1 -38
- data/lib/bolt_server/transport_app.rb +7 -7
- data/lib/bolt_spec/bolt_context.rb +3 -6
- data/lib/bolt_spec/plans.rb +1 -1
- data/lib/bolt_spec/plans/mock_executor.rb +1 -0
- data/lib/bolt_spec/run.rb +10 -13
- metadata +10 -7
- data/lib/bolt/boltdir.rb +0 -54
- data/lib/bolt/plugin/pkcs7.rb +0 -104
- data/lib/bolt/secret/base.rb +0 -41
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Check if a file exists
|
3
|
+
# Check if a local file exists using Puppet's
|
4
|
+
# `Puppet::Parser::Files.find_file()` function. This will only check files that
|
5
|
+
# are on the machine Bolt is run on.
|
4
6
|
Puppet::Functions.create_function(:'file::exists', Puppet::Functions::InternalFunction) do
|
5
7
|
# @param filename Absolute path or Puppet file path.
|
6
8
|
# @return Whether the file exists.
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Read a file and return its contents.
|
3
|
+
# Read a file on localhost and return its contents using ruby's `File.read`. This will
|
4
|
+
# only read files on the machine you run Bolt on.
|
4
5
|
Puppet::Functions.create_function(:'file::read', Puppet::Functions::InternalFunction) do
|
5
6
|
# @param filename Absolute path or Puppet file path.
|
6
7
|
# @return The file's contents.
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Check if a file is readable
|
3
|
+
# Check if a local file is readable using Puppet's
|
4
|
+
# `Puppet::Parser::Files.find_file()` function. This will only check files on the
|
5
|
+
# machine you run Bolt on.
|
4
6
|
Puppet::Functions.create_function(:'file::readable', Puppet::Functions::InternalFunction) do
|
5
7
|
# @param filename Absolute path or Puppet file path.
|
6
8
|
# @return Whether the file is readable.
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Write a string to a file.
|
3
|
+
# Write a string to a file on localhost using ruby's `File.write`. This will
|
4
|
+
# only write files to the machine you run Bolt on. Use `write_file()` to write
|
5
|
+
# to remote targets.
|
4
6
|
Puppet::Functions.create_function(:'file::write') do
|
5
7
|
# @param filename Absolute path.
|
6
8
|
# @param content File content to write.
|
data/lib/bolt/applicator.rb
CHANGED
@@ -34,6 +34,8 @@ module Bolt
|
|
34
34
|
search_dirs << mod.plugins if mod.plugins?
|
35
35
|
search_dirs << mod.pluginfacts if mod.pluginfacts?
|
36
36
|
search_dirs << mod.files if mod.files?
|
37
|
+
type_files = "#{mod.path}/types"
|
38
|
+
search_dirs << type_files if File.exist?(type_files)
|
37
39
|
search_dirs
|
38
40
|
end
|
39
41
|
end
|
@@ -155,7 +157,6 @@ module Bolt
|
|
155
157
|
end
|
156
158
|
|
157
159
|
plan_vars = scope.to_hash(true, true)
|
158
|
-
%w[trusted server_facts facts].each { |k| plan_vars.delete(k) }
|
159
160
|
|
160
161
|
targets = @inventory.get_targets(args[0])
|
161
162
|
|
@@ -182,7 +183,7 @@ module Bolt
|
|
182
183
|
rich_data: true,
|
183
184
|
symbol_as_string: true,
|
184
185
|
type_by_reference: true,
|
185
|
-
local_reference:
|
186
|
+
local_reference: true)
|
186
187
|
|
187
188
|
scope = {
|
188
189
|
code_ast: ast,
|
data/lib/bolt/apply_inventory.rb
CHANGED
data/lib/bolt/apply_result.rb
CHANGED
@@ -57,7 +57,7 @@ module Bolt
|
|
57
57
|
msg = "Report result contains an '_output' key. Catalog application may have printed extraneous output to stdout: #{result['_output']}"
|
58
58
|
# rubocop:enable Layout/LineLength
|
59
59
|
else
|
60
|
-
msg = "Report did not contain all expected keys missing: #{missing_keys.join('
|
60
|
+
msg = "Report did not contain all expected keys missing: #{missing_keys.join(', ')}"
|
61
61
|
end
|
62
62
|
|
63
63
|
{ 'msg' => msg,
|
data/lib/bolt/apply_target.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module Bolt
|
4
4
|
class ApplyTarget
|
5
5
|
ATTRIBUTES = %i[uri name target_alias config vars facts features
|
6
|
-
plugin_hooks safe_name].freeze
|
6
|
+
plugin_hooks resources safe_name].freeze
|
7
7
|
COMPUTED = %i[host password port protocol user].freeze
|
8
8
|
|
9
9
|
attr_reader(*ATTRIBUTES)
|
@@ -24,7 +24,8 @@ module Bolt
|
|
24
24
|
facts = nil,
|
25
25
|
vars = nil,
|
26
26
|
features = nil,
|
27
|
-
plugin_hooks = nil
|
27
|
+
plugin_hooks = nil,
|
28
|
+
resources = nil)
|
28
29
|
raise Bolt::Error.new("Target objects cannot be instantiated inside apply blocks", 'bolt/apply-error')
|
29
30
|
end
|
30
31
|
# rubocop:enable Lint/UnusedMethodArgument
|
@@ -57,6 +58,10 @@ module Bolt
|
|
57
58
|
@user = Addressable::URI.unencode_component(uri_obj.user) || t_conf['user']
|
58
59
|
end
|
59
60
|
|
61
|
+
def to_s
|
62
|
+
@safe_name
|
63
|
+
end
|
64
|
+
|
60
65
|
def parse_uri(string)
|
61
66
|
require 'addressable/uri'
|
62
67
|
if string.nil?
|
@@ -73,5 +78,9 @@ module Bolt
|
|
73
78
|
rescue Addressable::URI::InvalidURIError => e
|
74
79
|
raise Bolt::ParseError, "Could not parse target URI: #{e.message}"
|
75
80
|
end
|
81
|
+
|
82
|
+
def hash
|
83
|
+
@name.hash
|
84
|
+
end
|
76
85
|
end
|
77
86
|
end
|
@@ -11,7 +11,7 @@ module Bolt
|
|
11
11
|
escalation: %w[run-as sudo-password sudo-password-prompt sudo-executable],
|
12
12
|
run_context: %w[concurrency inventoryfile save-rerun cleanup],
|
13
13
|
global_config_setters: %w[modulepath boltdir configfile],
|
14
|
-
transports: %w[transport connect-timeout tty],
|
14
|
+
transports: %w[transport connect-timeout tty ssh-command copy-command],
|
15
15
|
display: %w[format color verbose trace],
|
16
16
|
global: %w[help version debug] }.freeze
|
17
17
|
|
@@ -64,7 +64,7 @@ module Bolt
|
|
64
64
|
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
|
65
65
|
banner: PLAN_CONVERT_HELP }
|
66
66
|
when 'run'
|
67
|
-
{ flags: ACTION_OPTS + %w[params compile-concurrency tmpdir],
|
67
|
+
{ flags: ACTION_OPTS + %w[params compile-concurrency tmpdir hiera-config],
|
68
68
|
banner: PLAN_RUN_HELP }
|
69
69
|
when 'show'
|
70
70
|
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters] + %w[filter format],
|
@@ -112,7 +112,7 @@ module Bolt
|
|
112
112
|
when 'secret'
|
113
113
|
case action
|
114
114
|
when 'createkeys'
|
115
|
-
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters] + %w[plugin],
|
115
|
+
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters] + %w[plugin force],
|
116
116
|
banner: SECRET_CREATEKEYS_HELP }
|
117
117
|
when 'decrypt'
|
118
118
|
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters] + %w[plugin],
|
@@ -594,6 +594,7 @@ module Bolt
|
|
594
594
|
HELP
|
595
595
|
|
596
596
|
attr_reader :warnings
|
597
|
+
|
597
598
|
def initialize(options)
|
598
599
|
super()
|
599
600
|
|
@@ -655,8 +656,8 @@ module Bolt
|
|
655
656
|
@options[:password] = STDIN.noecho(&:gets).chomp
|
656
657
|
STDERR.puts
|
657
658
|
end
|
658
|
-
define('--private-key KEY', '
|
659
|
-
@options[:'private-key'] = key
|
659
|
+
define('--private-key KEY', 'Path to private ssh key to authenticate with') do |key|
|
660
|
+
@options[:'private-key'] = File.expand_path(key)
|
660
661
|
end
|
661
662
|
define('--[no-]host-key-check', 'Check host keys with SSH') do |host_key_check|
|
662
663
|
@options[:'host-key-check'] = host_key_check
|
@@ -688,7 +689,7 @@ module Bolt
|
|
688
689
|
|
689
690
|
separator "\nRUN CONTEXT OPTIONS"
|
690
691
|
define('-c', '--concurrency CONCURRENCY', Integer,
|
691
|
-
'Maximum number of simultaneous connections
|
692
|
+
'Maximum number of simultaneous connections') do |concurrency|
|
692
693
|
@options[:concurrency] = concurrency
|
693
694
|
end
|
694
695
|
define('--compile-concurrency CONCURRENCY', Integer,
|
@@ -716,6 +717,10 @@ module Bolt
|
|
716
717
|
'Directory containing bolt.yaml will be used as the Boltdir.') do |path|
|
717
718
|
@options[:configfile] = path
|
718
719
|
end
|
720
|
+
define('--hiera-config FILEPATH',
|
721
|
+
'Specify where to load Hiera config from (default: ~/.puppetlabs/bolt/hiera.yaml)') do |path|
|
722
|
+
@options[:'hiera-config'] = File.expand_path(path)
|
723
|
+
end
|
719
724
|
define('-i', '--inventoryfile FILEPATH',
|
720
725
|
'Specify where to load inventory from (default: ~/.puppetlabs/bolt/inventory.yaml)') do |path|
|
721
726
|
if ENV.include?(Bolt::Inventory::ENVIRONMENT_VAR)
|
@@ -737,6 +742,14 @@ module Bolt
|
|
737
742
|
"Specify a default transport: #{TRANSPORTS.keys.join(', ')}") do |t|
|
738
743
|
@options[:transport] = t
|
739
744
|
end
|
745
|
+
define('--ssh-command EXEC', "Executable to use instead of the net-ssh ruby library. ",
|
746
|
+
"This option is experimental.") do |exec|
|
747
|
+
@options[:'ssh-command'] = exec
|
748
|
+
end
|
749
|
+
define('--copy-command EXEC', "Command to copy files to remote hosts if using external SSH. ",
|
750
|
+
"This option is experimental.") do |exec|
|
751
|
+
@options[:'copy-command'] = exec
|
752
|
+
end
|
740
753
|
define('--connect-timeout TIMEOUT', Integer, 'Connection timeout (defaults vary)') do |timeout|
|
741
754
|
@options[:'connect-timeout'] = timeout
|
742
755
|
end
|
@@ -775,6 +788,9 @@ module Bolt
|
|
775
788
|
'when initializing a project. Resolves and installs all dependencies.') do |modules|
|
776
789
|
@options[:modules] = modules.split(',')
|
777
790
|
end
|
791
|
+
define('--force', 'Overwrite existing key pairs') do |_force|
|
792
|
+
@options[:force] = true
|
793
|
+
end
|
778
794
|
|
779
795
|
separator "\nGLOBAL OPTIONS"
|
780
796
|
define('-h', '--help', 'Display help') do |_|
|
data/lib/bolt/cli.rb
CHANGED
@@ -114,18 +114,19 @@ module Bolt
|
|
114
114
|
@config = if options[:configfile]
|
115
115
|
Bolt::Config.from_file(options[:configfile], options)
|
116
116
|
else
|
117
|
-
|
118
|
-
Bolt::
|
117
|
+
project = if options[:boltdir]
|
118
|
+
Bolt::Project.new(options[:boltdir])
|
119
119
|
else
|
120
|
-
Bolt::
|
120
|
+
Bolt::Project.find_boltdir(Dir.pwd)
|
121
121
|
end
|
122
|
-
Bolt::Config.
|
122
|
+
Bolt::Config.from_project(project, options)
|
123
123
|
end
|
124
124
|
|
125
125
|
Bolt::Logger.configure(config.log, config.color)
|
126
126
|
|
127
|
-
# Logger must be configured before checking path case, otherwise warnings will not display
|
127
|
+
# Logger must be configured before checking path case and project file, otherwise warnings will not display
|
128
128
|
@config.check_path_case('modulepath', @config.modulepath)
|
129
|
+
@config.project.check_deprecated_file
|
129
130
|
|
130
131
|
# Log the file paths for loaded config files
|
131
132
|
config_loaded
|
@@ -238,6 +239,12 @@ module Bolt
|
|
238
239
|
if options[:subcommand] == 'command' && (!options[:object] || options[:object].empty?)
|
239
240
|
raise Bolt::CLIError, "Must specify a command to run"
|
240
241
|
end
|
242
|
+
|
243
|
+
if options[:subcommand] == 'secret' &&
|
244
|
+
(options[:action] == 'decrypt' || options[:action] == 'encrypt') &&
|
245
|
+
!options[:object]
|
246
|
+
raise Bolt::CLIError, "Must specify a value to #{options[:action]}"
|
247
|
+
end
|
241
248
|
end
|
242
249
|
|
243
250
|
def handle_parser_errors
|
@@ -251,13 +258,11 @@ module Bolt
|
|
251
258
|
end
|
252
259
|
|
253
260
|
def puppetdb_client
|
254
|
-
|
255
|
-
puppetdb_config = Bolt::PuppetDB::Config.load_config(nil, config.puppetdb, config.boltdir.path)
|
256
|
-
@puppetdb_client = Bolt::PuppetDB::Client.new(puppetdb_config)
|
261
|
+
plugins.puppetdb_client
|
257
262
|
end
|
258
263
|
|
259
264
|
def plugins
|
260
|
-
@plugins ||= Bolt::Plugin.setup(config, pal,
|
265
|
+
@plugins ||= Bolt::Plugin.setup(config, pal, analytics)
|
261
266
|
end
|
262
267
|
|
263
268
|
def query_puppetdb_nodes(query)
|
@@ -314,7 +319,8 @@ module Bolt
|
|
314
319
|
|
315
320
|
screen_view_fields = {
|
316
321
|
output_format: config.format,
|
317
|
-
|
322
|
+
# For continuity
|
323
|
+
boltdir_type: config.project.type
|
318
324
|
}
|
319
325
|
|
320
326
|
# Only include target and inventory info for commands that take a targets
|
@@ -447,6 +453,7 @@ module Bolt
|
|
447
453
|
def list_tasks
|
448
454
|
tasks = pal.list_tasks
|
449
455
|
tasks.select! { |task| task.first.include?(options[:filter]) } if options[:filter]
|
456
|
+
tasks.select! { |task| config.project.tasks.include?(task.first) } unless config.project.tasks.nil?
|
450
457
|
outputter.print_tasks(tasks, pal.list_modulepath)
|
451
458
|
end
|
452
459
|
|
@@ -457,6 +464,7 @@ module Bolt
|
|
457
464
|
def list_plans
|
458
465
|
plans = pal.list_plans
|
459
466
|
plans.select! { |plan| plan.first.include?(options[:filter]) } if options[:filter]
|
467
|
+
plans.select! { |plan| config.project.plans.include?(plan.first) } unless config.project.plans.nil?
|
460
468
|
outputter.print_plans(plans, pal.list_modulepath)
|
461
469
|
end
|
462
470
|
|
@@ -567,10 +575,10 @@ module Bolt
|
|
567
575
|
# Initializes a specified directory as a Bolt project and installs any modules
|
568
576
|
# specified by the user, along with their dependencies
|
569
577
|
def initialize_project
|
570
|
-
|
571
|
-
config =
|
572
|
-
puppetfile =
|
573
|
-
modulepath = [
|
578
|
+
project = Pathname.new(File.expand_path(options[:object] || Dir.pwd))
|
579
|
+
config = project + 'bolt.yaml'
|
580
|
+
puppetfile = project + 'Puppetfile'
|
581
|
+
modulepath = [project + 'modules']
|
574
582
|
|
575
583
|
# If modules were specified, first check if there is already a Puppetfile at the project
|
576
584
|
# directory, erroring if there is. If there is no Puppetfile, generate the Puppetfile
|
@@ -590,20 +598,20 @@ module Bolt
|
|
590
598
|
# Warn the user if the project directory already exists. We don't error here since users
|
591
599
|
# might not have installed any modules yet.
|
592
600
|
if config.exist?
|
593
|
-
@logger.warn "Found existing project directory at #{
|
601
|
+
@logger.warn "Found existing project directory at #{project}"
|
594
602
|
end
|
595
603
|
|
596
604
|
# Create the project directory
|
597
|
-
FileUtils.mkdir_p(
|
605
|
+
FileUtils.mkdir_p(project)
|
598
606
|
|
599
|
-
# Bless the project directory as a
|
607
|
+
# Bless the project directory as a...wait for it...project
|
600
608
|
if FileUtils.touch(config)
|
601
|
-
outputter.print_message "Successfully created Bolt project at #{
|
609
|
+
outputter.print_message "Successfully created Bolt project at #{project}"
|
602
610
|
else
|
603
|
-
raise Bolt::FileError.new("Could not create Bolt project directory at #{
|
611
|
+
raise Bolt::FileError.new("Could not create Bolt project directory at #{project}", nil)
|
604
612
|
end
|
605
613
|
|
606
|
-
# Write the generated Puppetfile to the fancy new
|
614
|
+
# Write the generated Puppetfile to the fancy new project
|
607
615
|
if puppetfile_specs
|
608
616
|
File.write(puppetfile, puppetfile_specs.join("\n"))
|
609
617
|
outputter.print_message "Successfully created Puppetfile at #{puppetfile}"
|
@@ -752,12 +760,14 @@ module Bolt
|
|
752
760
|
end
|
753
761
|
|
754
762
|
def pal
|
763
|
+
project = config.project.load_as_module? ? config.project : nil
|
755
764
|
@pal ||= Bolt::PAL.new(config.modulepath,
|
756
765
|
config.hiera_config,
|
757
|
-
config.
|
766
|
+
config.project.resource_types,
|
758
767
|
config.compile_concurrency,
|
759
768
|
config.trusted_external,
|
760
|
-
config.apply_settings
|
769
|
+
config.apply_settings,
|
770
|
+
project)
|
761
771
|
end
|
762
772
|
|
763
773
|
def convert_plan(plan)
|
@@ -793,6 +803,20 @@ module Bolt
|
|
793
803
|
end
|
794
804
|
|
795
805
|
def bundled_content
|
806
|
+
# If the bundled content directory is empty, Bolt is likely installed as a gem.
|
807
|
+
if ENV['BOLT_GEM'].nil? && incomplete_install?
|
808
|
+
msg = <<~MSG.chomp
|
809
|
+
Bolt may be installed as a gem. To use Bolt reliably and with all of its
|
810
|
+
dependencies, uninstall the 'bolt' gem and install Bolt as a package:
|
811
|
+
https://puppet.com/docs/bolt/latest/bolt_installing.html
|
812
|
+
|
813
|
+
If you meant to install Bolt as a gem and want to disable this warning,
|
814
|
+
set the BOLT_GEM environment variable.
|
815
|
+
MSG
|
816
|
+
|
817
|
+
@logger.warn(msg)
|
818
|
+
end
|
819
|
+
|
796
820
|
# We only need to enumerate bundled content when running a task or plan
|
797
821
|
content = { 'Plan' => [],
|
798
822
|
'Task' => [],
|
@@ -816,5 +840,11 @@ module Bolt
|
|
816
840
|
MSG
|
817
841
|
@logger.debug(msg)
|
818
842
|
end
|
843
|
+
|
844
|
+
# Gem installs include the aggregate, canary, and puppetdb_fact modules, while
|
845
|
+
# package installs include modules listed in the Bolt repo Puppetfile
|
846
|
+
def incomplete_install?
|
847
|
+
(Dir.children(Bolt::PAL::MODULES_PATH) - %w[aggregate canary puppetdb_fact]).empty?
|
848
|
+
end
|
819
849
|
end
|
820
850
|
end
|
data/lib/bolt/config.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
require 'etc'
|
4
4
|
require 'logging'
|
5
5
|
require 'pathname'
|
6
|
-
require 'bolt/
|
6
|
+
require 'bolt/project'
|
7
7
|
require 'bolt/logger'
|
8
8
|
require 'bolt/util'
|
9
9
|
# Transport config objects
|
@@ -23,7 +23,7 @@ module Bolt
|
|
23
23
|
end
|
24
24
|
|
25
25
|
class Config
|
26
|
-
attr_reader :config_files, :warnings, :data, :transports, :
|
26
|
+
attr_reader :config_files, :warnings, :data, :transports, :project
|
27
27
|
|
28
28
|
TRANSPORT_CONFIG = {
|
29
29
|
'ssh' => Bolt::Config::Transport::SSH,
|
@@ -34,6 +34,8 @@ module Bolt
|
|
34
34
|
'remote' => Bolt::Config::Transport::Remote
|
35
35
|
}.freeze
|
36
36
|
|
37
|
+
# NOTE: All configuration options should have a corresponding schema property
|
38
|
+
# in schemas/bolt-config.schema.json
|
37
39
|
OPTIONS = {
|
38
40
|
"apply_settings" => "A map of Puppet settings to use when applying Puppet code",
|
39
41
|
"color" => "Whether to use colored output when printing messages to the console.",
|
@@ -64,8 +66,8 @@ module Bolt
|
|
64
66
|
|
65
67
|
DEFAULT_OPTIONS = {
|
66
68
|
"color" => true,
|
67
|
-
"concurrency" => 100,
|
68
69
|
"compile-concurrency" => "Number of cores",
|
70
|
+
"concurrency" => "100 or one-third of the ulimit, whichever is lower",
|
69
71
|
"format" => "human",
|
70
72
|
"hiera-config" => "Boltdir/hiera.yaml",
|
71
73
|
"inventoryfile" => "Boltdir/inventory.yaml",
|
@@ -101,31 +103,33 @@ module Bolt
|
|
101
103
|
"show_diff" => false
|
102
104
|
}.freeze
|
103
105
|
|
106
|
+
DEFAULT_DEFAULT_CONCURRENCY = 100
|
107
|
+
|
104
108
|
def self.default
|
105
|
-
new(Bolt::
|
109
|
+
new(Bolt::Project.new('.'), {})
|
106
110
|
end
|
107
111
|
|
108
|
-
def self.
|
112
|
+
def self.from_project(project, overrides = {})
|
109
113
|
data = {
|
110
|
-
filepath:
|
111
|
-
data: Bolt::Util.read_optional_yaml_hash(
|
114
|
+
filepath: project.config_file,
|
115
|
+
data: Bolt::Util.read_optional_yaml_hash(project.config_file, 'config')
|
112
116
|
}
|
113
117
|
|
114
118
|
data = load_defaults.push(data).select { |config| config[:data]&.any? }
|
115
119
|
|
116
|
-
new(
|
120
|
+
new(project, data, overrides)
|
117
121
|
end
|
118
122
|
|
119
123
|
def self.from_file(configfile, overrides = {})
|
120
|
-
|
124
|
+
project = Bolt::Project.new(Pathname.new(configfile).expand_path.dirname)
|
121
125
|
|
122
126
|
data = {
|
123
|
-
filepath:
|
127
|
+
filepath: project.config_file,
|
124
128
|
data: Bolt::Util.read_yaml_hash(configfile, 'config')
|
125
129
|
}
|
126
130
|
data = load_defaults.push(data).select { |config| config[:data]&.any? }
|
127
131
|
|
128
|
-
new(
|
132
|
+
new(project, data, overrides)
|
129
133
|
end
|
130
134
|
|
131
135
|
def self.load_defaults
|
@@ -148,14 +152,14 @@ module Bolt
|
|
148
152
|
confs
|
149
153
|
end
|
150
154
|
|
151
|
-
def initialize(
|
155
|
+
def initialize(project, config_data, overrides = {})
|
152
156
|
unless config_data.is_a?(Array)
|
153
|
-
config_data = [{ filepath:
|
157
|
+
config_data = [{ filepath: project.config_file, data: config_data }]
|
154
158
|
end
|
155
159
|
|
156
160
|
@logger = Logging.logger[self]
|
157
161
|
@warnings = []
|
158
|
-
@
|
162
|
+
@project = project
|
159
163
|
@transports = {}
|
160
164
|
@config_files = []
|
161
165
|
|
@@ -163,7 +167,7 @@ module Bolt
|
|
163
167
|
'apply_settings' => {},
|
164
168
|
'color' => true,
|
165
169
|
'compile-concurrency' => Etc.nprocessors,
|
166
|
-
'concurrency' =>
|
170
|
+
'concurrency' => default_concurrency,
|
167
171
|
'format' => 'human',
|
168
172
|
'log' => { 'console' => {} },
|
169
173
|
'plugin_hooks' => {},
|
@@ -181,10 +185,22 @@ module Bolt
|
|
181
185
|
|
182
186
|
override_data = normalize_overrides(overrides)
|
183
187
|
|
188
|
+
# If we need to lower concurrency and concurrency is not configured
|
189
|
+
ld_concurrency = loaded_data.map(&:keys).flatten.include?('concurrency')
|
190
|
+
if default_concurrency != DEFAULT_DEFAULT_CONCURRENCY &&
|
191
|
+
!ld_concurrency &&
|
192
|
+
!override_data.key?('concurrency')
|
193
|
+
concurrency_warning = { option: 'concurrency',
|
194
|
+
msg: "Concurrency will default to #{default_concurrency} because ulimit "\
|
195
|
+
"is low: #{Etc.sysconf(Etc::SC_OPEN_MAX)}. Set concurrency with "\
|
196
|
+
"'--concurrency', or set your ulimit with 'ulimit -n <limit>'" }
|
197
|
+
@warnings << concurrency_warning
|
198
|
+
end
|
199
|
+
|
184
200
|
@data = merge_config_layers(default_data, *loaded_data, override_data)
|
185
201
|
|
186
202
|
TRANSPORT_CONFIG.each do |transport, config|
|
187
|
-
@transports[transport] = config.new(@data.delete(transport), @
|
203
|
+
@transports[transport] = config.new(@data.delete(transport), @project.path)
|
188
204
|
end
|
189
205
|
|
190
206
|
finalize_data
|
@@ -250,7 +266,7 @@ module Bolt
|
|
250
266
|
@data['log'] = update_logs(@data['log'])
|
251
267
|
end
|
252
268
|
|
253
|
-
# Expand paths relative to the
|
269
|
+
# Expand paths relative to the project. Any settings that came from the
|
254
270
|
# CLI will already be absolute, so the expand will be skipped.
|
255
271
|
if @data.key?('modulepath')
|
256
272
|
moduledirs = if data['modulepath'].is_a?(String)
|
@@ -259,12 +275,12 @@ module Bolt
|
|
259
275
|
data['modulepath']
|
260
276
|
end
|
261
277
|
@data['modulepath'] = moduledirs.map do |moduledir|
|
262
|
-
File.expand_path(moduledir, @
|
278
|
+
File.expand_path(moduledir, @project.path)
|
263
279
|
end
|
264
280
|
end
|
265
281
|
|
266
282
|
%w[hiera-config inventoryfile trusted-external-command].each do |opt|
|
267
|
-
@data[opt] = File.expand_path(@data[opt], @
|
283
|
+
@data[opt] = File.expand_path(@data[opt], @project.path) if @data.key?(opt)
|
268
284
|
end
|
269
285
|
|
270
286
|
# Filter hashes to only include valid options
|
@@ -275,7 +291,7 @@ module Bolt
|
|
275
291
|
private def normalize_log(target)
|
276
292
|
return target if target == 'console'
|
277
293
|
target = target[5..-1] if target.start_with?('file:')
|
278
|
-
'file:' + File.expand_path(target, @
|
294
|
+
'file:' + File.expand_path(target, @project.path)
|
279
295
|
end
|
280
296
|
|
281
297
|
private def update_logs(logs)
|
@@ -310,10 +326,10 @@ module Bolt
|
|
310
326
|
@warnings << { option: 'future', msg: msg }
|
311
327
|
end
|
312
328
|
|
313
|
-
keys = OPTIONS.keys - %w[plugins plugin_hooks]
|
329
|
+
keys = OPTIONS.keys - %w[plugins plugin_hooks puppetdb]
|
314
330
|
keys.each do |key|
|
315
331
|
next unless Bolt::Util.references?(@data[key])
|
316
|
-
valid_keys = TRANSPORT_CONFIG.keys + %w[plugins plugin_hooks]
|
332
|
+
valid_keys = TRANSPORT_CONFIG.keys + %w[plugins plugin_hooks puppetdb]
|
317
333
|
raise Bolt::ValidationError,
|
318
334
|
"Found unsupported key _plugin in config setting #{key}. Plugins are only available in "\
|
319
335
|
"#{valid_keys.join(', ')}."
|
@@ -348,23 +364,23 @@ module Bolt
|
|
348
364
|
end
|
349
365
|
|
350
366
|
def default_inventoryfile
|
351
|
-
@
|
367
|
+
@project.inventory_file
|
352
368
|
end
|
353
369
|
|
354
370
|
def rerunfile
|
355
|
-
@
|
371
|
+
@project.rerunfile
|
356
372
|
end
|
357
373
|
|
358
374
|
def hiera_config
|
359
|
-
@data['hiera-config'] || @
|
375
|
+
@data['hiera-config'] || @project.hiera_config
|
360
376
|
end
|
361
377
|
|
362
378
|
def puppetfile
|
363
|
-
@puppetfile || @
|
379
|
+
@puppetfile || @project.puppetfile
|
364
380
|
end
|
365
381
|
|
366
382
|
def modulepath
|
367
|
-
@data['modulepath'] || @
|
383
|
+
@data['modulepath'] || @project.modulepath
|
368
384
|
end
|
369
385
|
|
370
386
|
def modulepath=(value)
|
@@ -456,5 +472,19 @@ module Bolt
|
|
456
472
|
l =~ /[A-Za-z]/ ? "[#{l.upcase}#{l.downcase}]" : l
|
457
473
|
end.join
|
458
474
|
end
|
475
|
+
|
476
|
+
# Etc::SC_OPEN_MAX is meaningless on windows, not defined in PE Jruby and not available
|
477
|
+
# on some platforms. This method holds the logic to decide whether or not to even consider it.
|
478
|
+
def sc_open_max_available?
|
479
|
+
!Bolt::Util.windows? && defined?(Etc::SC_OPEN_MAX) && Etc.sysconf(Etc::SC_OPEN_MAX)
|
480
|
+
end
|
481
|
+
|
482
|
+
def default_concurrency
|
483
|
+
@default_concurrency ||= if !sc_open_max_available? || Etc.sysconf(Etc::SC_OPEN_MAX) >= 300
|
484
|
+
DEFAULT_DEFAULT_CONCURRENCY
|
485
|
+
else
|
486
|
+
Etc.sysconf(Etc::SC_OPEN_MAX) / 3
|
487
|
+
end
|
488
|
+
end
|
459
489
|
end
|
460
490
|
end
|