lib/bolt/config.rb in bolt-1.47.0 vs lib/bolt/config.rb in bolt-1.48.0
- old
+ new
@@ -32,34 +32,32 @@
end
class Config
attr_accessor :concurrency, :format, :trace, :log, :puppetdb, :color, :save_rerun,
:transport, :transports, :inventoryfile, :compile_concurrency, :boltdir,
- :puppetfile_config, :plugins, :plugin_hooks, :future, :trusted_external
+ :puppetfile_config, :plugins, :plugin_hooks, :future, :trusted_external,
+ :apply_settings
attr_writer :modulepath
+ attr_reader :config_files
OPTIONS = {
+ "apply_settings" => "A map of Puppet settings to use when applying Puppet code",
"color" => "Whether to use colored output when printing messages to the console.",
"compile-concurrency" => "The maximum number of simultaneous manifest block compiles.",
"concurrency" => "The number of threads to use when executing on remote targets.",
"format" => "The format to use when printing results. Options are `human` and `json`.",
"hiera-config" => "The path to your Hiera config.",
- "interpreters" => "A map of an extension name to the absolute path of an executable, "\
- "enabling you to override the shebang defined in a task executable. The "\
- "extension can optionally be specified with the `.` character (`.py` and "\
- "`py` both map to a task executable `task.py`) and the extension is case "\
- "sensitive. The transports that support interpreter configuration are "\
- "`docker`, `local`, `ssh`, and `winrm`. When a target's name is `localhost`, "\
- "Ruby tasks run with the Bolt Ruby interpreter by default.",
"inventoryfile" => "The path to a structured data inventory file used to refer to groups of "\
"targets on the command line and from plans.",
"log" => "The configuration of the logfile output. Configuration can be set for "\
"`console` and the path to a log file, such as `~/.puppetlabs/bolt/debug.log`.",
"modulepath" => "The module path for loading tasks and plan code. This is either an array "\
"of directories or a string containing a list of directories separated by the "\
"OS-specific PATH separator.",
"plugin_hooks" => "Which plugins a specific hook should use.",
+ "plugins" => "A map of plugins and their configuration data.",
+ "puppetdb" => "A map containing options for configuring the Bolt PuppetDB client.",
"puppetfile" => "A map containing options for the `bolt puppetfile install` command.",
"save-rerun" => "Whether to update `.rerun.json` in the Bolt project directory. If "\
"your target names include passwords, set this value to `false` to avoid "\
"writing passwords to disk.",
"transport" => "The default transport to use when the transport for a target is not "\
@@ -99,27 +97,68 @@
DEFAULT_LOG_OPTIONS = {
"append" => true,
"level" => "`warn` for console, `notice` for file"
}.freeze
+ APPLY_SETTINGS = {
+ "show_diff" => "Whether to log and report a contextual diff when files are being replaced. "\
+ "See [Puppet documentation](https://puppet.com/docs/puppet/latest/configuration.html#showdiff) "\
+ "for details"
+ }.freeze
+
+ DEFAULT_APPLY_SETTINGS = {
+ "show_diff" => false
+ }.freeze
+
def self.default
new(Bolt::Boltdir.new('.'), {})
end
def self.from_boltdir(boltdir, overrides = {})
- data = Bolt::Util.read_config_file(nil, [boltdir.config_file], 'config') || {}
+ data = {
+ filepath: boltdir.config_file,
+ data: Bolt::Util.read_config_file(nil, [boltdir.config_file], 'config')
+ }
+
+ data = load_defaults.push(data).select { |config| config[:data]&.any? }
+
new(boltdir, data, overrides)
end
def self.from_file(configfile, overrides = {})
boltdir = Bolt::Boltdir.new(Pathname.new(configfile).expand_path.dirname)
- data = Bolt::Util.read_config_file(configfile, [], 'config') || {}
+ data = {
+ filepath: boltdir.config_file,
+ data: Bolt::Util.read_config_file(configfile, [], 'config')
+ }
+
+ data = load_defaults.push(data).select { |config| config[:data]&.any? }
+
new(boltdir, data, overrides)
end
+ def self.load_defaults
+ # Lazy-load expensive gem code
+ require 'win32/dir' if Bolt::Util.windows?
+
+ system_path = if Bolt::Util.windows?
+ Pathname.new(File.join(Dir::COMMON_APPDATA, 'PuppetLabs', 'bolt', 'etc', 'bolt.yaml'))
+ else
+ Pathname.new(File.join('/etc', 'puppetlabs', 'bolt', 'bolt.yaml'))
+ end
+ user_path = Pathname.new(File.expand_path(File.join('~', '.puppetlabs', 'etc', 'bolt', 'bolt.yaml')))
+
+ [{ filepath: system_path, data: Bolt::Util.read_config_file(nil, [system_path], 'config') },
+ { filepath: user_path, data: Bolt::Util.read_config_file(nil, [user_path], 'config') }]
+ end
+
def initialize(boltdir, config_data, overrides = {})
+ unless config_data.is_a?(Array)
+ config_data = [{ filepath: boltdir.config_file, data: config_data }]
+ end
+
@logger = Logging.logger[self]
@boltdir = boltdir
@concurrency = 100
@compile_concurrency = Etc.nprocessors
@@ -129,26 +168,54 @@
@color = true
@save_rerun = true
@puppetfile_config = {}
@plugins = {}
@plugin_hooks = {}
+ @apply_settings = {}
# add an entry for the default console logger
@log = { 'console' => {} }
@transports = {}
TRANSPORTS.each do |key, transport|
@transports[key] = transport.default_options
end
+ @config_files = config_data.map { |config| config[:filepath] }
+
+ config_data = merge_config_data(config_data)
update_from_file(config_data)
+
apply_overrides(overrides)
validate
end
+ # Merge configuration
+ # Precedence from highest to lowest is: project, user-level, system-wide
+ def merge_config_data(config_data)
+ config_data.inject({}) do |acc, config|
+ acc.merge(config[:data]) do |key, val1, val2|
+ case key
+ # Plugin config is shallow merged for each plugin
+ when 'plugins'
+ val1.merge(val2) { |_, v1, v2| v1.merge(v2) }
+ # Transports are deep merged
+ when *TRANSPORTS.keys.map(&:to_s)
+ Bolt::Util.deep_merge(val1, val2)
+ # Hash values are shallow mergeed
+ when 'puppetdb', 'plugin_hooks', 'apply_settings', 'log'
+ val1.merge(val2)
+ # All other values are overwritten
+ else
+ val2
+ end
+ end
+ end
+ end
+
def overwrite_transport_data(transport, transports)
@transport = transport
@transports = transports
end
@@ -222,17 +289,19 @@
@hiera_config = File.expand_path(data['hiera-config'], @boltdir.path) if data.key?('hiera-config')
@trusted_external = if data.key?('trusted-external-command')
File.expand_path(data['trusted-external-command'], @boltdir.path)
end
+
+ if data.key?('apply_settings')
+ @apply_settings = data['apply_settings'].select { |k, _| APPLY_SETTINGS.keys.include?(k) }
+ end
+
@compile_concurrency = data['compile-concurrency'] if data.key?('compile-concurrency')
@save_rerun = data['save-rerun'] if data.key?('save-rerun')
- @plugins = data['plugins'] if data.key?('plugins')
- @plugin_hooks = data['plugin_hooks'] if data.key?('plugin_hooks')
-
- %w[concurrency format puppetdb color].each do |key|
+ %w[concurrency format puppetdb color plugins plugin_hooks].each do |key|
send("#{key}=", data[key]) if data.key?(key)
end
update_transports(data)
end