bolt 2.13.0 → 2.18.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 +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/add_to_group.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +20 -9
- data/bolt-modules/boltlib/lib/puppet/functions/catch_errors.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/facts.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/fail_plan.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_resources.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_target.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_targets.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_fact.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/remove_from_group.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/resolve_references.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/resource.rb +53 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +3 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +7 -2
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +7 -4
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +2 -1
- data/bolt-modules/boltlib/lib/puppet/functions/set_config.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_feature.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_resources.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_var.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/vars.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/wait_until_available.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/without_default_logging.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/write_file.rb +1 -0
- data/bolt-modules/ctrl/lib/puppet/functions/ctrl/do_until.rb +2 -0
- data/bolt-modules/ctrl/lib/puppet/functions/ctrl/sleep.rb +2 -0
- data/bolt-modules/file/lib/puppet/functions/file/exists.rb +2 -1
- data/bolt-modules/file/lib/puppet/functions/file/join.rb +2 -0
- data/bolt-modules/file/lib/puppet/functions/file/read.rb +3 -1
- data/bolt-modules/file/lib/puppet/functions/file/readable.rb +3 -1
- data/bolt-modules/file/lib/puppet/functions/file/write.rb +2 -0
- data/bolt-modules/out/lib/puppet/functions/out/message.rb +2 -0
- data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +1 -0
- data/bolt-modules/system/lib/puppet/functions/system/env.rb +2 -0
- data/lib/bolt/applicator.rb +36 -21
- data/lib/bolt/apply_inventory.rb +4 -0
- data/lib/bolt/apply_result.rb +1 -1
- data/lib/bolt/apply_target.rb +4 -0
- data/lib/bolt/bolt_option_parser.rb +27 -16
- data/lib/bolt/catalog.rb +79 -69
- data/lib/bolt/cli.rb +78 -58
- data/lib/bolt/config.rb +163 -136
- data/lib/bolt/config/options.rb +474 -0
- data/lib/bolt/config/transport/base.rb +16 -16
- data/lib/bolt/config/transport/docker.rb +9 -23
- data/lib/bolt/config/transport/local.rb +6 -44
- data/lib/bolt/config/transport/options.rb +460 -0
- data/lib/bolt/config/transport/orch.rb +9 -18
- data/lib/bolt/config/transport/remote.rb +3 -6
- data/lib/bolt/config/transport/ssh.rb +78 -119
- data/lib/bolt/config/transport/winrm.rb +18 -47
- data/lib/bolt/executor.rb +14 -1
- data/lib/bolt/inventory/group.rb +1 -1
- data/lib/bolt/inventory/inventory.rb +4 -14
- data/lib/bolt/inventory/target.rb +22 -5
- data/lib/bolt/logger.rb +15 -1
- data/lib/bolt/outputter.rb +3 -0
- data/lib/bolt/outputter/rainbow.rb +84 -0
- data/lib/bolt/pal.rb +23 -9
- data/lib/bolt/project.rb +75 -55
- data/lib/bolt/resource_instance.rb +5 -1
- data/lib/bolt/shell/bash.rb +30 -42
- data/lib/bolt/shell/powershell.rb +13 -8
- data/lib/bolt/shell/powershell/snippets.rb +23 -6
- data/lib/bolt/transport/docker.rb +9 -5
- data/lib/bolt/transport/local/connection.rb +2 -1
- data/lib/bolt/transport/orch.rb +8 -0
- data/lib/bolt/transport/ssh.rb +7 -1
- data/lib/bolt/transport/ssh/connection.rb +35 -0
- data/lib/bolt/transport/ssh/exec_connection.rb +1 -1
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_spec/bolt_context.rb +1 -1
- data/lib/bolt_spec/run.rb +1 -1
- metadata +22 -18
data/lib/bolt/cli.rb
CHANGED
@@ -46,7 +46,6 @@ module Bolt
|
|
46
46
|
Bolt::Logger.initialize_logging
|
47
47
|
@logger = Logging.logger[self]
|
48
48
|
@argv = argv
|
49
|
-
@config = Bolt::Config.default
|
50
49
|
@options = {}
|
51
50
|
end
|
52
51
|
|
@@ -77,56 +76,74 @@ module Bolt
|
|
77
76
|
private :help?
|
78
77
|
|
79
78
|
def parse
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
79
|
+
begin
|
80
|
+
parser = BoltOptionParser.new(options)
|
81
|
+
# This part aims to handle both `bolt <mode> --help` and `bolt help <mode>`.
|
82
|
+
remaining = handle_parser_errors { parser.permute(@argv) } unless @argv.empty?
|
83
|
+
if @argv.empty? || help?(remaining)
|
84
|
+
# Update the parser for the subcommand (or lack thereof)
|
85
|
+
parser.update
|
86
|
+
puts parser.help
|
87
|
+
raise Bolt::CLIExit
|
88
|
+
end
|
89
|
+
|
90
|
+
options[:object] = remaining.shift
|
91
|
+
|
92
|
+
# Only parse task_options for task or plan
|
93
|
+
if %w[task plan].include?(options[:subcommand])
|
94
|
+
task_options, remaining = remaining.partition { |s| s =~ /.+=/ }
|
95
|
+
if options[:task_options]
|
96
|
+
unless task_options.empty?
|
97
|
+
raise Bolt::CLIError,
|
98
|
+
"Parameters must be specified through either the --params " \
|
99
|
+
"option or param=value pairs, not both"
|
100
|
+
end
|
101
|
+
options[:params_parsed] = true
|
102
|
+
elsif task_options.any?
|
103
|
+
options[:params_parsed] = false
|
104
|
+
options[:task_options] = Hash[task_options.map { |a| a.split('=', 2) }]
|
105
|
+
else
|
106
|
+
options[:params_parsed] = true
|
107
|
+
options[:task_options] = {}
|
100
108
|
end
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
109
|
+
end
|
110
|
+
options[:leftovers] = remaining
|
111
|
+
|
112
|
+
validate(options)
|
113
|
+
|
114
|
+
@config = if ENV['BOLT_PROJECT']
|
115
|
+
project = Bolt::Project.create_project(ENV['BOLT_PROJECT'], 'environment')
|
116
|
+
Bolt::Config.from_project(project, options)
|
117
|
+
elsif options[:configfile]
|
118
|
+
Bolt::Config.from_file(options[:configfile], options)
|
119
|
+
else
|
120
|
+
project = if options[:boltdir]
|
121
|
+
dir = Pathname.new(options[:boltdir])
|
122
|
+
if (dir + Bolt::Project::BOLTDIR_NAME).directory?
|
123
|
+
Bolt::Project.create_project(dir + Bolt::Project::BOLTDIR_NAME)
|
124
|
+
else
|
125
|
+
Bolt::Project.create_project(dir)
|
126
|
+
end
|
127
|
+
else
|
128
|
+
Bolt::Project.find_boltdir(Dir.pwd)
|
129
|
+
end
|
130
|
+
Bolt::Config.from_project(project, options)
|
131
|
+
end
|
132
|
+
|
133
|
+
Bolt::Logger.configure(config.log, config.color)
|
134
|
+
rescue Bolt::Error => e
|
135
|
+
if $stdout.isatty
|
136
|
+
# Print the error message in red, mimicking outputter.fatal_error
|
137
|
+
$stdout.puts("\033[31m#{e.message}\033[0m")
|
105
138
|
else
|
106
|
-
|
107
|
-
options[:task_options] = {}
|
139
|
+
$stdout.puts(e.message)
|
108
140
|
end
|
141
|
+
raise e
|
109
142
|
end
|
110
|
-
options[:leftovers] = remaining
|
111
|
-
|
112
|
-
validate(options)
|
113
|
-
|
114
|
-
@config = if options[:configfile]
|
115
|
-
Bolt::Config.from_file(options[:configfile], options)
|
116
|
-
else
|
117
|
-
project = if options[:boltdir]
|
118
|
-
Bolt::Project.new(options[:boltdir])
|
119
|
-
else
|
120
|
-
Bolt::Project.find_boltdir(Dir.pwd)
|
121
|
-
end
|
122
|
-
Bolt::Config.from_project(project, options)
|
123
|
-
end
|
124
|
-
|
125
|
-
Bolt::Logger.configure(config.log, config.color)
|
126
143
|
|
127
144
|
# Logger must be configured before checking path case and project file, otherwise warnings will not display
|
128
|
-
|
129
|
-
|
145
|
+
config.check_path_case('modulepath', config.modulepath)
|
146
|
+
config.project.check_deprecated_file
|
130
147
|
|
131
148
|
# Log the file paths for loaded config files
|
132
149
|
config_loaded
|
@@ -219,7 +236,7 @@ module Bolt
|
|
219
236
|
end
|
220
237
|
|
221
238
|
if options[:boltdir] && options[:configfile]
|
222
|
-
raise Bolt::CLIError, "Only one of '--boltdir' or '--configfile' may be specified"
|
239
|
+
raise Bolt::CLIError, "Only one of '--boltdir', '--project', or '--configfile' may be specified"
|
223
240
|
end
|
224
241
|
|
225
242
|
if options[:noop] &&
|
@@ -245,6 +262,10 @@ module Bolt
|
|
245
262
|
!options[:object]
|
246
263
|
raise Bolt::CLIError, "Must specify a value to #{options[:action]}"
|
247
264
|
end
|
265
|
+
|
266
|
+
if options.key?(:debug) && options.key?(:log)
|
267
|
+
raise Bolt::CLIError, "Only one of '--debug' or '--log-level' may be specified"
|
268
|
+
end
|
248
269
|
end
|
249
270
|
|
250
271
|
def handle_parser_errors
|
@@ -272,12 +293,12 @@ module Bolt
|
|
272
293
|
def warn_inventory_overrides_cli(opts)
|
273
294
|
inventory_source = if ENV[Bolt::Inventory::ENVIRONMENT_VAR]
|
274
295
|
Bolt::Inventory::ENVIRONMENT_VAR
|
275
|
-
elsif
|
276
|
-
|
296
|
+
elsif config.inventoryfile && Bolt::Util.file_stat(config.inventoryfile)
|
297
|
+
config.inventoryfile
|
277
298
|
else
|
278
299
|
begin
|
279
|
-
Bolt::Util.file_stat(
|
280
|
-
|
300
|
+
Bolt::Util.file_stat(config.default_inventoryfile)
|
301
|
+
config.default_inventoryfile
|
281
302
|
rescue Errno::ENOENT
|
282
303
|
nil
|
283
304
|
end
|
@@ -381,7 +402,7 @@ module Bolt
|
|
381
402
|
if options[:action] == 'generate-types'
|
382
403
|
code = generate_types
|
383
404
|
elsif options[:action] == 'install'
|
384
|
-
code = install_puppetfile(
|
405
|
+
code = install_puppetfile(config.puppetfile_config, config.puppetfile, config.modulepath)
|
385
406
|
end
|
386
407
|
when 'secret'
|
387
408
|
code = Bolt::Secret.execute(plugins, outputter, options)
|
@@ -392,7 +413,7 @@ module Bolt
|
|
392
413
|
end
|
393
414
|
code = apply_manifest(options[:code], options[:targets], options[:object], options[:noop])
|
394
415
|
else
|
395
|
-
executor = Bolt::Executor.new(config.concurrency, analytics, options[:noop])
|
416
|
+
executor = Bolt::Executor.new(config.concurrency, analytics, options[:noop], config.modified_concurrency)
|
396
417
|
targets = options[:targets]
|
397
418
|
|
398
419
|
results = nil
|
@@ -512,8 +533,8 @@ module Bolt
|
|
512
533
|
params: plan_arguments }
|
513
534
|
plan_context[:description] = options[:description] if options[:description]
|
514
535
|
|
515
|
-
executor = Bolt::Executor.new(config.concurrency, analytics, options[:noop])
|
516
|
-
if options.fetch(:format, 'human')
|
536
|
+
executor = Bolt::Executor.new(config.concurrency, analytics, options[:noop], config.modified_concurrency)
|
537
|
+
if %w[human rainbow].include?(options.fetch(:format, 'human'))
|
517
538
|
executor.subscribe(outputter)
|
518
539
|
else
|
519
540
|
# Only subscribe to out::message events for JSON outputter
|
@@ -548,7 +569,7 @@ module Bolt
|
|
548
569
|
@logger.warn(message)
|
549
570
|
end
|
550
571
|
|
551
|
-
executor = Bolt::Executor.new(config.concurrency, analytics, noop)
|
572
|
+
executor = Bolt::Executor.new(config.concurrency, analytics, noop, config.modified_concurrency)
|
552
573
|
executor.subscribe(outputter) if options.fetch(:format, 'human') == 'human'
|
553
574
|
executor.subscribe(log_outputter)
|
554
575
|
# apply logging looks like plan logging, so tell the outputter we're in a
|
@@ -771,14 +792,13 @@ module Bolt
|
|
771
792
|
end
|
772
793
|
|
773
794
|
def pal
|
774
|
-
project = config.project.load_as_module? ? config.project : nil
|
775
795
|
@pal ||= Bolt::PAL.new(config.modulepath,
|
776
796
|
config.hiera_config,
|
777
797
|
config.project.resource_types,
|
778
798
|
config.compile_concurrency,
|
779
799
|
config.trusted_external,
|
780
800
|
config.apply_settings,
|
781
|
-
project)
|
801
|
+
config.project)
|
782
802
|
end
|
783
803
|
|
784
804
|
def convert_plan(plan)
|
@@ -794,7 +814,7 @@ module Bolt
|
|
794
814
|
end
|
795
815
|
|
796
816
|
def rerun
|
797
|
-
@rerun ||= Bolt::Rerun.new(
|
817
|
+
@rerun ||= Bolt::Rerun.new(config.rerunfile, config.save_rerun)
|
798
818
|
end
|
799
819
|
|
800
820
|
def outputter
|
data/lib/bolt/config.rb
CHANGED
@@ -6,13 +6,7 @@ require 'pathname'
|
|
6
6
|
require 'bolt/project'
|
7
7
|
require 'bolt/logger'
|
8
8
|
require 'bolt/util'
|
9
|
-
|
10
|
-
require 'bolt/config/transport/ssh'
|
11
|
-
require 'bolt/config/transport/winrm'
|
12
|
-
require 'bolt/config/transport/orch'
|
13
|
-
require 'bolt/config/transport/local'
|
14
|
-
require 'bolt/config/transport/docker'
|
15
|
-
require 'bolt/config/transport/remote'
|
9
|
+
require 'bolt/config/options'
|
16
10
|
|
17
11
|
module Bolt
|
18
12
|
class UnknownTransportError < Bolt::Error
|
@@ -23,144 +17,178 @@ module Bolt
|
|
23
17
|
end
|
24
18
|
|
25
19
|
class Config
|
26
|
-
|
27
|
-
|
28
|
-
TRANSPORT_CONFIG = {
|
29
|
-
'ssh' => Bolt::Config::Transport::SSH,
|
30
|
-
'winrm' => Bolt::Config::Transport::WinRM,
|
31
|
-
'pcp' => Bolt::Config::Transport::Orch,
|
32
|
-
'local' => Bolt::Config::Transport::Local,
|
33
|
-
'docker' => Bolt::Config::Transport::Docker,
|
34
|
-
'remote' => Bolt::Config::Transport::Remote
|
35
|
-
}.freeze
|
36
|
-
|
37
|
-
# NOTE: All configuration options should have a corresponding schema property
|
38
|
-
# in schemas/bolt-config.schema.json
|
39
|
-
OPTIONS = {
|
40
|
-
"apply_settings" => "A map of Puppet settings to use when applying Puppet code",
|
41
|
-
"color" => "Whether to use colored output when printing messages to the console.",
|
42
|
-
"compile-concurrency" => "The maximum number of simultaneous manifest block compiles.",
|
43
|
-
"concurrency" => "The number of threads to use when executing on remote targets.",
|
44
|
-
"format" => "The format to use when printing results. Options are `human` and `json`.",
|
45
|
-
"hiera-config" => "The path to your Hiera config.",
|
46
|
-
"inventoryfile" => "The path to a structured data inventory file used to refer to groups of "\
|
47
|
-
"targets on the command line and from plans.",
|
48
|
-
"log" => "The configuration of the logfile output. Configuration can be set for "\
|
49
|
-
"`console` and the path to a log file, such as `~/.puppetlabs/bolt/debug.log`.",
|
50
|
-
"modulepath" => "An array of directories that Bolt loads content (e.g. plans and tasks) from.",
|
51
|
-
"plugin_hooks" => "Which plugins a specific hook should use.",
|
52
|
-
"plugins" => "A map of plugins and their configuration data.",
|
53
|
-
"puppetdb" => "A map containing options for configuring the Bolt PuppetDB client.",
|
54
|
-
"puppetfile" => "A map containing options for the `bolt puppetfile install` command.",
|
55
|
-
"save-rerun" => "Whether to update `.rerun.json` in the Bolt project directory. If "\
|
56
|
-
"your target names include passwords, set this value to `false` to avoid "\
|
57
|
-
"writing passwords to disk.",
|
58
|
-
"transport" => "The default transport to use when the transport for a target is not "\
|
59
|
-
"specified in the URL or inventory.",
|
60
|
-
"trusted-external-command" => "The path to an executable on the Bolt controller that can produce "\
|
61
|
-
"external trusted facts. **External trusted facts are experimental in both "\
|
62
|
-
"Puppet and Bolt and this API may change or be removed.**"
|
63
|
-
}.freeze
|
64
|
-
|
65
|
-
DEFAULT_OPTIONS = {
|
66
|
-
"color" => true,
|
67
|
-
"compile-concurrency" => "Number of cores",
|
68
|
-
"concurrency" => "100 or one-third of the ulimit, whichever is lower",
|
69
|
-
"format" => "human",
|
70
|
-
"hiera-config" => "Boltdir/hiera.yaml",
|
71
|
-
"inventoryfile" => "Boltdir/inventory.yaml",
|
72
|
-
"modulepath" => ["Boltdir/modules", "Boltdir/site-modules", "Boltdir/site"],
|
73
|
-
"save-rerun" => true
|
74
|
-
}.freeze
|
75
|
-
|
76
|
-
PUPPETFILE_OPTIONS = {
|
77
|
-
"forge" => "A subsection that can have its own `proxy` setting to set an HTTP proxy for Forge operations "\
|
78
|
-
"only, and a `baseurl` setting to specify a different Forge host.",
|
79
|
-
"proxy" => "The HTTP proxy to use for Git and Forge operations."
|
80
|
-
}.freeze
|
81
|
-
|
82
|
-
LOG_OPTIONS = {
|
83
|
-
"append" => "Add output to an existing log file. Available only for logs output to a "\
|
84
|
-
"filepath.",
|
85
|
-
"level" => "The type of information in the log. Either `debug`, `info`, `notice`, "\
|
86
|
-
"`warn`, or `error`."
|
87
|
-
}.freeze
|
88
|
-
|
89
|
-
DEFAULT_LOG_OPTIONS = {
|
90
|
-
"append" => true,
|
91
|
-
"level" => "`warn` for console, `notice` for file"
|
92
|
-
}.freeze
|
93
|
-
|
94
|
-
APPLY_SETTINGS = {
|
95
|
-
"show_diff" => "Whether to log and report a contextual diff when files are being replaced. "\
|
96
|
-
"See [Puppet documentation](https://puppet.com/docs/puppet/latest/configuration.html#showdiff) "\
|
97
|
-
"for details"
|
98
|
-
}.freeze
|
99
|
-
|
100
|
-
DEFAULT_APPLY_SETTINGS = {
|
101
|
-
"show_diff" => false
|
102
|
-
}.freeze
|
20
|
+
include Bolt::Config::Options
|
103
21
|
|
22
|
+
attr_reader :config_files, :warnings, :data, :transports, :project, :modified_concurrency
|
23
|
+
|
24
|
+
BOLT_CONFIG_NAME = 'bolt.yaml'
|
25
|
+
BOLT_DEFAULTS_NAME = 'bolt-defaults.yaml'
|
26
|
+
|
27
|
+
# The default concurrency value that is used when the ulimit is not low (i.e. < 700)
|
104
28
|
DEFAULT_DEFAULT_CONCURRENCY = 100
|
105
29
|
|
106
30
|
def self.default
|
107
|
-
new(Bolt::Project.
|
31
|
+
new(Bolt::Project.default_project, {})
|
108
32
|
end
|
109
33
|
|
110
34
|
def self.from_project(project, overrides = {})
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
35
|
+
conf = if project.project_file == project.config_file
|
36
|
+
project.data
|
37
|
+
else
|
38
|
+
Bolt::Util.read_optional_yaml_hash(project.config_file, 'config')
|
39
|
+
end
|
115
40
|
|
116
|
-
data = load_defaults(project).push(
|
41
|
+
data = load_defaults(project).push(
|
42
|
+
filepath: project.config_file,
|
43
|
+
data: conf,
|
44
|
+
warnings: []
|
45
|
+
)
|
117
46
|
|
118
47
|
new(project, data, overrides)
|
119
48
|
end
|
120
49
|
|
121
50
|
def self.from_file(configfile, overrides = {})
|
122
|
-
project = Bolt::Project.
|
51
|
+
project = Bolt::Project.create_project(Pathname.new(configfile).expand_path.dirname)
|
52
|
+
|
53
|
+
conf = if project.project_file == project.config_file
|
54
|
+
project.data
|
55
|
+
else
|
56
|
+
Bolt::Util.read_yaml_hash(configfile, 'config')
|
57
|
+
end
|
123
58
|
|
124
|
-
data =
|
59
|
+
data = load_defaults(project).push(
|
125
60
|
filepath: project.config_file,
|
126
|
-
data:
|
127
|
-
|
128
|
-
|
61
|
+
data: conf,
|
62
|
+
warnings: []
|
63
|
+
)
|
129
64
|
|
130
65
|
new(project, data, overrides)
|
131
66
|
end
|
132
67
|
|
133
|
-
def self.
|
68
|
+
def self.system_path
|
134
69
|
# Lazy-load expensive gem code
|
135
70
|
require 'win32/dir' if Bolt::Util.windows?
|
136
71
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
72
|
+
if Bolt::Util.windows?
|
73
|
+
Pathname.new(File.join(Dir::COMMON_APPDATA, 'PuppetLabs', 'bolt', 'etc'))
|
74
|
+
else
|
75
|
+
Pathname.new(File.join('/etc', 'puppetlabs', 'bolt'))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.user_path
|
80
|
+
Pathname.new(File.expand_path(File.join('~', '.puppetlabs', 'etc', 'bolt')))
|
81
|
+
rescue StandardError
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
|
85
|
+
# Loads a 'bolt-defaults.yaml' file, which contains default configuration that applies to all
|
86
|
+
# projects. This file does not allow project-specific configuration such as 'hiera-config' and
|
87
|
+
# 'inventoryfile', and nests all default inventory configuration under an 'inventory-config' key.
|
88
|
+
def self.load_bolt_defaults_yaml(dir)
|
89
|
+
filepath = dir + BOLT_DEFAULTS_NAME
|
90
|
+
data = Bolt::Util.read_yaml_hash(filepath, 'config')
|
91
|
+
warnings = []
|
92
|
+
|
93
|
+
# Warn if 'bolt.yaml' detected in same directory.
|
94
|
+
if File.exist?(bolt_yaml = dir + BOLT_CONFIG_NAME)
|
95
|
+
warnings.push(
|
96
|
+
msg: "Detected multiple configuration files: ['#{bolt_yaml}', '#{filepath}']. '#{bolt_yaml}' "\
|
97
|
+
"will be ignored."
|
98
|
+
)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Remove project-specific config such as hiera-config, etc.
|
102
|
+
project_config = data.slice(*(BOLT_PROJECT_OPTIONS - BOLT_DEFAULTS_OPTIONS))
|
103
|
+
|
104
|
+
if project_config.any?
|
105
|
+
data.reject! { |key, _| project_config.include?(key) }
|
106
|
+
warnings.push(
|
107
|
+
msg: "Unsupported project configuration detected in '#{filepath}': #{project_config.keys}. "\
|
108
|
+
"Project configuration should be set in 'bolt-project.yaml'."
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Remove top-level transport config such as transport, ssh, etc.
|
113
|
+
transport_config = data.slice(*INVENTORY_OPTIONS.keys)
|
114
|
+
|
115
|
+
if transport_config.any?
|
116
|
+
data.reject! { |key, _| transport_config.include?(key) }
|
117
|
+
warnings.push(
|
118
|
+
msg: "Unsupported inventory configuration detected in '#{filepath}': #{transport_config.keys}. "\
|
119
|
+
"Transport configuration should be set under the 'inventory-config' option or "\
|
120
|
+
"in 'inventory.yaml'."
|
121
|
+
)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Move data under inventory-config to top-level so it can be easily merged with
|
125
|
+
# config from other sources. Error early if inventory-config is not a hash or
|
126
|
+
# has a plugin reference.
|
127
|
+
if data.key?('inventory-config')
|
128
|
+
unless data['inventory-config'].is_a?(Hash)
|
129
|
+
raise Bolt::ValidationError,
|
130
|
+
"Option 'inventory-config' must be of type Hash, received #{data['inventory-config']} "\
|
131
|
+
"#{data['inventory-config']} (file: #{filepath})"
|
132
|
+
end
|
133
|
+
|
134
|
+
if data['inventory-config'].key?('_plugin')
|
135
|
+
raise Bolt::ValidationError,
|
136
|
+
"Found unsupported key '_plugin' for option 'inventory-config'; supported keys are "\
|
137
|
+
"'#{INVENTORY_OPTIONS.keys.join("', '")}' (file: #{filepath})"
|
138
|
+
end
|
139
|
+
|
140
|
+
data = data.merge(data.delete('inventory-config'))
|
141
|
+
end
|
142
|
+
|
143
|
+
{ filepath: filepath, data: data, warnings: warnings }
|
144
|
+
end
|
145
|
+
|
146
|
+
# Loads a 'bolt.yaml' file, the legacy configuration file. There's no special munging needed
|
147
|
+
# here since Bolt::Config will just ignore any invalid keys.
|
148
|
+
def self.load_bolt_yaml(dir)
|
149
|
+
filepath = dir + BOLT_CONFIG_NAME
|
150
|
+
data = Bolt::Util.read_yaml_hash(filepath, 'config')
|
151
|
+
warnings = [msg: "Configuration file #{filepath} is deprecated and will be removed in a future version "\
|
152
|
+
"of Bolt. Use '#{dir + BOLT_DEFAULTS_NAME}' instead."]
|
153
|
+
|
154
|
+
{ filepath: filepath, data: data, warnings: warnings }
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.load_defaults(project)
|
158
|
+
confs = []
|
159
|
+
|
160
|
+
# Load system-level config. Prefer a 'bolt-defaults.yaml' file, but fall back to the
|
161
|
+
# legacy 'bolt.yaml' file. If the project-level config file is also the system-level
|
162
|
+
# config file, don't load it a second time.
|
163
|
+
if File.exist?(system_path + BOLT_DEFAULTS_NAME)
|
164
|
+
confs << load_bolt_defaults_yaml(system_path)
|
165
|
+
elsif File.exist?(system_path + BOLT_CONFIG_NAME) &&
|
166
|
+
(system_path + BOLT_CONFIG_NAME) != project.config_file
|
167
|
+
confs << load_bolt_yaml(system_path)
|
168
|
+
end
|
169
|
+
|
170
|
+
# Load user-level config if there is a homedir. Prefer a 'bolt-defaults.yaml' file, but
|
171
|
+
# fall back to the legacy 'bolt.yaml' file.
|
172
|
+
if user_path
|
173
|
+
if File.exist?(user_path + BOLT_DEFAULTS_NAME)
|
174
|
+
confs << load_bolt_defaults_yaml(user_path)
|
175
|
+
elsif File.exist?(user_path + BOLT_CONFIG_NAME)
|
176
|
+
confs << load_bolt_yaml(user_path)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
152
180
|
confs
|
153
181
|
end
|
154
182
|
|
155
183
|
def initialize(project, config_data, overrides = {})
|
156
184
|
unless config_data.is_a?(Array)
|
157
|
-
config_data = [{ filepath: project.config_file, data: config_data }]
|
185
|
+
config_data = [{ filepath: project.config_file, data: config_data, warnings: [] }]
|
158
186
|
end
|
159
187
|
|
160
|
-
@logger
|
161
|
-
@
|
162
|
-
@
|
163
|
-
@transports
|
188
|
+
@logger = Logging.logger[self]
|
189
|
+
@project = project
|
190
|
+
@warnings = @project.warnings.dup
|
191
|
+
@transports = {}
|
164
192
|
@config_files = []
|
165
193
|
|
166
194
|
default_data = {
|
@@ -178,24 +206,22 @@ module Bolt
|
|
178
206
|
'transport' => 'ssh'
|
179
207
|
}
|
180
208
|
|
181
|
-
loaded_data = config_data.
|
182
|
-
@
|
183
|
-
|
209
|
+
loaded_data = config_data.each_with_object([]) do |data, acc|
|
210
|
+
@warnings.concat(data[:warnings]) if data[:warnings].any?
|
211
|
+
|
212
|
+
if data[:data].any?
|
213
|
+
@config_files.push(data[:filepath])
|
214
|
+
acc.push(data[:data])
|
215
|
+
end
|
184
216
|
end
|
185
217
|
|
186
218
|
override_data = normalize_overrides(overrides)
|
187
219
|
|
188
220
|
# If we need to lower concurrency and concurrency is not configured
|
189
221
|
ld_concurrency = loaded_data.map(&:keys).flatten.include?('concurrency')
|
190
|
-
|
191
|
-
|
192
|
-
|
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
|
222
|
+
@modified_concurrency = default_concurrency != DEFAULT_DEFAULT_CONCURRENCY &&
|
223
|
+
!ld_concurrency &&
|
224
|
+
!override_data.key?('concurrency')
|
199
225
|
|
200
226
|
@data = merge_config_layers(default_data, *loaded_data, override_data)
|
201
227
|
|
@@ -212,12 +238,13 @@ module Bolt
|
|
212
238
|
def normalize_overrides(options)
|
213
239
|
opts = options.transform_keys(&:to_s)
|
214
240
|
|
215
|
-
# Pull out config options
|
216
|
-
|
241
|
+
# Pull out config options. We need to add 'transport' as it's not part of the
|
242
|
+
# OPTIONS hash but is a valid option that can be set with the --transport CLI option
|
243
|
+
overrides = opts.slice(*OPTIONS.keys, 'transport')
|
217
244
|
|
218
245
|
# Pull out transport config options
|
219
246
|
TRANSPORT_CONFIG.each do |transport, config|
|
220
|
-
overrides[transport] = opts.slice(*config.options
|
247
|
+
overrides[transport] = opts.slice(*config.options)
|
221
248
|
end
|
222
249
|
|
223
250
|
# Set console log to debug if in debug mode
|
@@ -284,8 +311,8 @@ module Bolt
|
|
284
311
|
end
|
285
312
|
|
286
313
|
# Filter hashes to only include valid options
|
287
|
-
@data['apply_settings'] = @data['apply_settings'].slice(*
|
288
|
-
@data['puppetfile'] = @data['puppetfile'].slice(*
|
314
|
+
@data['apply_settings'] = @data['apply_settings'].slice(*OPTIONS['apply_settings'][:properties].keys)
|
315
|
+
@data['puppetfile'] = @data['puppetfile'].slice(*OPTIONS['puppetfile'][:properties].keys)
|
289
316
|
end
|
290
317
|
|
291
318
|
private def normalize_log(target)
|
@@ -299,7 +326,7 @@ module Bolt
|
|
299
326
|
next unless val.is_a?(Hash)
|
300
327
|
|
301
328
|
name = normalize_log(key)
|
302
|
-
acc[name] = val.slice(
|
329
|
+
acc[name] = val.slice('append', 'level')
|
303
330
|
.transform_keys(&:to_sym)
|
304
331
|
|
305
332
|
if (v = acc[name][:level])
|
@@ -351,7 +378,7 @@ module Bolt
|
|
351
378
|
raise Bolt::ValidationError, "Compilation is CPU-intensive, set concurrency less than #{compile_limit}"
|
352
379
|
end
|
353
380
|
|
354
|
-
|
381
|
+
if (format == 'rainbow' && Bolt::Util.windows?) || !(%w[human json rainbow].include? format)
|
355
382
|
raise Bolt::ValidationError, "Unsupported format: '#{format}'"
|
356
383
|
end
|
357
384
|
|
@@ -483,7 +510,7 @@ module Bolt
|
|
483
510
|
@default_concurrency ||= if !sc_open_max_available? || Etc.sysconf(Etc::SC_OPEN_MAX) >= 300
|
484
511
|
DEFAULT_DEFAULT_CONCURRENCY
|
485
512
|
else
|
486
|
-
Etc.sysconf(Etc::SC_OPEN_MAX) /
|
513
|
+
Etc.sysconf(Etc::SC_OPEN_MAX) / 7
|
487
514
|
end
|
488
515
|
end
|
489
516
|
end
|