The class for handling configuration files.
local reference for convenience
These are the settings that every app is required to specify; there are reasonable defaults defined in application.rb.
This method is intended for puppet internal use only; it is a convenience method that returns reasonable application default settings values for a given run_mode.
# File lib/puppet/settings.rb, line 32 def self.app_defaults_for_run_mode(run_mode) { :name => run_mode.to_s, :run_mode => run_mode.name, :confdir => run_mode.conf_dir, :vardir => run_mode.var_dir, :rundir => run_mode.run_dir, :logdir => run_mode.log_dir, } end
A utility method (public, is used by application.rb and perhaps elsewhere) that munges a command-line option string into the format that Puppet.settings expects. (This mostly has to deal with handling the “no-” prefix on flag/boolean options).
@param [String] opt the command line option that we are munging @param [String, TrueClass, FalseClass] the value for the setting (as determined by the OptionParser)
# File lib/puppet/settings.rb, line 226 def self.clean_opt(opt, val) # rewrite --[no-]option to --no-option if that's what was given if opt =~ /\[no-\]/ and !val opt = opt.gsub(/\[no-\]/,'no-') end # otherwise remove the [no-] prefix to not confuse everybody opt = opt.gsub(/\[no-\]/, '') [opt, val] end
# File lib/puppet/settings.rb, line 43 def self.default_certname() hostname = hostname_fact domain = domain_fact if domain and domain != "" fqdn = [hostname, domain].join(".") else fqdn = hostname end fqdn.to_s.gsub(/\.$/, '') end
# File lib/puppet/settings.rb, line 62 def self.default_config_file_name "puppet.conf" end
# File lib/puppet/settings.rb, line 58 def self.domain_fact() Facter["domain"].value end
# File lib/puppet/settings.rb, line 54 def self.hostname_fact() Facter["hostname"].value end
Create a new collection of config settings.
# File lib/puppet/settings.rb, line 67 def initialize @config = {} @shortnames = {} @created = [] @searchpath = nil # Mutex-like thing to protect @values @sync = Sync.new # Keep track of set values. @values = Hash.new { |hash, key| hash[key] = {} } # Hold parsed metadata until run_mode is known @metas = {} # And keep a per-environment cache @cache = Hash.new { |hash, key| hash[key] = {} } # The list of sections we've used. @used = [] @hooks_to_call_on_application_initialization = [] @translate = Puppet::Settings::ValueTranslator.new @config_file_parser = Puppet::Settings::ConfigFile.new(@translate) end
Retrieve a config value
# File lib/puppet/settings.rb, line 96 def [](param) value(param) end
Set a config value. This doesn’t set the defaults, it sets the value itself.
# File lib/puppet/settings.rb, line 101 def []=(param, value) set_value(param, value, :memory) end
Generate the list of valid arguments, in a format that GetoptLong can understand, and add them to the passed option list.
# File lib/puppet/settings.rb, line 107 def addargs(options) # Add all of the config parameters as valid options. self.each { |name, setting| setting.getopt_args.each { |args| options << args } } options end
# File lib/puppet/settings.rb, line 237 def app_defaults_initialized? @app_defaults_initialized end
Is our parameter a boolean parameter?
# File lib/puppet/settings.rb, line 128 def boolean?(param) param = param.to_sym @config.include?(param) and @config[param].kind_of?(BooleanSetting) end
Remove all set values, potentially skipping cli values.
# File lib/puppet/settings.rb, line 134 def clear @sync.synchronize do unsafe_clear end end
This is mostly just used for testing.
# File lib/puppet/settings.rb, line 160 def clearused @cache.clear @used = [] end
Do variable interpolation on the value.
# File lib/puppet/settings.rb, line 273 def convert(value, environment = nil) return nil if value.nil? return value unless value.is_a? String newval = value.gsub(/\$(\w+)|\$\{(\w+)\}/) do |value| varname = $2 || $1 if varname == "environment" and environment environment elsif varname == "run_mode" preferred_run_mode elsif pval = self.value(varname, environment) pval else raise InterpolationError, "Could not find value for #{value}" end end newval end
Define a group of settings.
@param [Symbol] section a symbol to use for grouping multiple settings together into a conceptual unit. This value
(and the conceptual separation) is not used very often; the main place where it will have a potential impact is when code calls Settings#use method. See docs on that method for further details, but basically that method just attempts to do any preparation that may be necessary before code attempts to leverage the value of a particular setting. This has the most impact for file/directory settings, where #use will attempt to "ensure" those files / directories.
@param [Hash] defs the settings to be defined. This argument is a hash of hashes; each key should be a symbol,
which is basically the name of the setting that you are defining. The value should be another hash that specifies the parameters for the particular setting. Legal values include: [:default] => required; this is a string value that will be used as a default value for a setting if no other value is specified (via cli, config file, etc.) This string may include "variables", demarcated with $ or ${}, which will be interpolated with values of other settings. [:desc] => required; a description of the setting, used in documentation / help generation [:type] => not required, but highly encouraged! This specifies the data type that the setting represents. If you do not specify it, it will default to "string". Legal values include: :string - A generic string setting :boolean - A boolean setting; values are expected to be "true" or "false" :file - A (single) file path; puppet may attempt to create this file depending on how the settings are used. This type also supports additional options such as "mode", "owner", "group" :directory - A (single) directory path; puppet may attempt to create this file depending on how the settings are used. This type also supports additional options such as "mode", "owner", "group" :path - This is intended to be used for settings whose value can contain multiple directory paths, respresented as strings separated by the system path separator (e.g. system path, module path, etc.). [:mode] => an (optional) octal value to be used as the permissions/mode for :file and :directory settings [:owner] => optional owner username/uid for :file and :directory settings [:group] => optional group name/gid for :file and :directory settings
# File lib/puppet/settings.rb, line 809 def define_settings(section, defs) section = section.to_sym call = [] defs.each { |name, hash| raise ArgumentError, "setting definition for '#{name}' is not a hash!" unless hash.is_a? Hash name = name.to_sym hash[:name] = name hash[:section] = section raise ArgumentError, "Parameter #{name} is already defined" if @config.include?(name) tryconfig = newsetting(hash) if short = tryconfig.short if other = @shortnames[short] raise ArgumentError, "Parameter #{other.name} is already using short name '#{short}'" end @shortnames[short] = tryconfig end @config[name] = tryconfig # Collect the settings that need to have their hooks called immediately. # We have to collect them so that we can be sure we're fully initialized before # the hook is called. call << tryconfig if tryconfig.call_hook_on_define? @hooks_to_call_on_application_initialization << tryconfig if tryconfig.call_hook_on_initialize? } call.each { |setting| setting.handle(self.value(setting.name)) } end
Return a value’s description.
# File lib/puppet/settings.rb, line 293 def description(name) if obj = @config[name.to_sym] obj.desc else nil end end
# File lib/puppet/settings.rb, line 301 def each @config.each { |name, object| yield name, object } end
Iterate over each section name.
# File lib/puppet/settings.rb, line 308 def eachsection yielded = [] @config.each do |name, object| section = object.section unless yielded.include? section yield section yielded << section end end end
# File lib/puppet/settings.rb, line 399 def generate_config puts to_config true end
# File lib/puppet/settings.rb, line 404 def generate_manifest puts to_manifest true end
# File lib/puppet/settings.rb, line 165 def global_defaults_initialized?() @global_defaults_initialized end
Handle a command-line argument.
# File lib/puppet/settings.rb, line 326 def handlearg(opt, value = nil) @cache.clear if value.is_a?(FalseClass) value = "false" elsif value.is_a?(TrueClass) value = "true" end value &&= @translate[value] str = opt.sub(/^--/,'') bool = true newstr = str.sub(/^no-/, '') if newstr != str str = newstr bool = false end str = str.intern if @config[str].is_a?(Puppet::Settings::BooleanSetting) if value == "" or value.nil? value = bool end end set_value(str, value, :cli) end
# File lib/puppet/settings.rb, line 355 def include?(name) name = name.intern if name.is_a? String @config.include?(name) end
# File lib/puppet/settings.rb, line 241 def initialize_app_defaults(app_defaults) REQUIRED_APP_SETTINGS.each do |key| raise SettingsError, "missing required app default setting '#{key}'" unless app_defaults.has_key?(key) end app_defaults.each do |key, value| if key == :run_mode self.preferred_run_mode = value else set_value(key, value, :application_defaults) end end apply_metadata call_hooks_deferred_to_application_initialization @app_defaults_initialized = true end
# File lib/puppet/settings.rb, line 169 def initialize_global_settings(args = []) raise Puppet::DevError, "Attempting to initialize global default settings more than once!" if global_defaults_initialized? # The first two phases of the lifecycle of a puppet application are: # 1) Parse the command line options and handle any of them that are # registered, defined "global" puppet settings (mostly from defaults.rb). # 2) Parse the puppet config file(s). parse_global_options(args) parse_config_files @global_defaults_initialized = true end
Return a given object’s file metadata.
# File lib/puppet/settings.rb, line 420 def metadata(param) if obj = @config[param.to_sym] and obj.is_a?(FileSetting) return [:owner, :group, :mode].inject({}) do |meta, p| if v = obj.send(p) meta[p] = v end meta end else nil end end
Make a directory with the appropriate user, group, and mode
# File lib/puppet/settings.rb, line 434 def mkdir(default) obj = get_config_file_default(default) Puppet::Util::SUIDManager.asuser(obj.owner, obj.group) do mode = obj.mode || 0750 Dir.mkdir(obj.value, mode) end end
Generate the list of valid arguments, in a format that OptionParser can understand, and add them to the passed option list.
# File lib/puppet/settings.rb, line 118 def optparse_addargs(options) # Add all of the config parameters as valid options. self.each { |name, setting| options << setting.optparse_args } options end
Return all of the parameters associated with a given section.
# File lib/puppet/settings.rb, line 461 def params(section = nil) if section section = section.intern if section.is_a? String @config.find_all { |name, obj| obj.section == section }.collect { |name, obj| name } else @config.keys end end
Iterate across all of the objects in a given section.
# File lib/puppet/settings.rb, line 634 def persection(section) section = section.to_sym self.each { |name, obj| if obj.section == section yield obj end } end
The currently configured run mode that is preferred for constructing the application configuration.
# File lib/puppet/settings.rb, line 444 def preferred_run_mode @preferred_run_mode_name || :user end
PRIVATE! This only exists because we need a hook to validate the run mode when it’s being set, and
it should never, ever, ever, ever be called from outside of this file.
This method is also called when –run_mode MODE is used on the command line to set the default
@param mode [String|Symbol] the name of the mode to have in effect @api private
# File lib/puppet/settings.rb, line 454 def preferred_run_mode=(mode) mode = mode.to_s.downcase.intern raise ValidationError, "Invalid run mode '#{mode}'" unless [:master, :agent, :user].include?(mode) @preferred_run_mode_name = mode end
Prints the contents of a config file with the available config settings, or it prints a single value of a config setting.
# File lib/puppet/settings.rb, line 368 def print_config_options env = value(:environment) val = value(:configprint) if val == "all" hash = {} each do |name, obj| val = value(name,env) val = val.inspect if val == "" hash[name] = val end hash.sort { |a,b| a[0].to_s <=> b[0].to_s }.each do |name, val| puts "#{name} = #{val}" end else val.split(/\s*,\s*/).sort.each do |v| if include?(v) #if there is only one value, just print it for back compatibility if v == val puts value(val,env) break end puts "#{v} = #{value(v,env)}" else puts "invalid parameter: #{v}" return false end end end true end
# File lib/puppet/settings.rb, line 409 def print_configs return print_config_options if value(:configprint) != "" return generate_config if value(:genconfig) generate_manifest if value(:genmanifest) end
# File lib/puppet/settings.rb, line 415 def print_configs? (value(:configprint) != "" || value(:genconfig) || value(:genmanifest)) && true end
# File lib/puppet/settings.rb, line 1031 def readwritelock(default, *args, &bloc) file = value(get_config_file_default(default).name) tmpfile = file + ".tmp" sync = Sync.new raise Puppet::DevError, "Cannot create #{file}; directory #{File.dirname(file)} does not exist" unless FileTest.directory?(File.dirname(tmpfile)) sync.synchronize(Sync::EX) do File.open(file, ::File::CREAT|::File::RDWR, 0600) do |rf| rf.lock_exclusive do if File.exist?(tmpfile) raise Puppet::Error, ".tmp file already exists for #{file}; Aborting locked write. Check the .tmp file and delete if appropriate" end # If there's a failure, remove our tmpfile begin writesub(default, tmpfile, *args, &bloc) rescue File.unlink(tmpfile) if FileTest.exist?(tmpfile) raise end begin File.rename(tmpfile, file) rescue => detail Puppet.err "Could not rename #{file} to #{tmpfile}: #{detail}" File.unlink(tmpfile) if FileTest.exist?(tmpfile) end end end end end
Reparse our config file, if necessary.
# File lib/puppet/settings.rb, line 644 def reparse_config_files if files if filename = any_files_changed? Puppet.notice "Config file #{filename} changed; triggering re-parse of all config files." parse_config_files reuse end end end
# File lib/puppet/settings.rb, line 676 def reuse return unless defined?(@used) @sync.synchronize do # yay, thread-safe new = @used @used = [] self.use(*new) end end
The order in which to search for values.
# File lib/puppet/settings.rb, line 686 def searchpath(environment = nil) if environment [:cli, :memory, environment, :run_mode, :main, :application_defaults] else [:cli, :memory, :run_mode, :main, :application_defaults] end end
Get a list of objects per section
# File lib/puppet/settings.rb, line 695 def sectionlist sectionlist = [] self.each { |name, obj| section = obj.section || "puppet" sections[section] ||= [] sectionlist << section unless sectionlist.include?(section) sections[section] << obj } return sectionlist, sections end
# File lib/puppet/settings.rb, line 719 def service_group_available? return @service_group_available if defined?(@service_group_available) if self[:group] group = Puppet::Type.type(:group).new :name => self[:group], :audit => :ensure @service_group_available = group.exists? else @service_group_available = false end end
# File lib/puppet/settings.rb, line 707 def service_user_available? return @service_user_available if defined?(@service_user_available) if self[:user] user = Puppet::Type.type(:user).new :name => self[:user], :audit => :ensure @service_user_available = user.exists? else @service_user_available = false end end
Allow later inspection to determine if the setting was set on the command line, or through some other code path. Used for the `dns_alt_names` option during cert generate. –daniel 2011-10-18
# File lib/puppet/settings.rb, line 734 def set_by_cli?(param) param = param.to_sym !@values[:cli][param].nil? end
# File lib/puppet/settings.rb, line 739 def set_value(param, value, type, options = {}) param = param.to_sym if !(setting = @config[param]) if options[:ignore_bad_settings] return else raise ArgumentError, "Attempt to assign a value to unknown configuration parameter #{param.inspect}" end end setting.handle(value) if setting.has_hook? and not options[:dont_trigger_handles] @sync.synchronize do # yay, thread-safe @values[type][param] = value @cache.clear clearused # Clear the list of environments, because they cache, at least, the module path. # We *could* preferentially just clear them if the modulepath is changed, # but we don't really know if, say, the vardir is changed and the modulepath # is defined relative to it. We need the defined?(stuff) because of loading # order issues. Puppet::Node::Environment.clear if defined?(Puppet::Node) and defined?(Puppet::Node::Environment) end value end
Deprecated; use define_settings instead
# File lib/puppet/settings.rb, line 775 def setdefaults(section, defs) Puppet.deprecation_warning("'setdefaults' is deprecated and will be removed; please call 'define_settings' instead") define_settings(section, defs) end
Return an object by name.
# File lib/puppet/settings.rb, line 320 def setting(param) param = param.to_sym @config[param] end
check to see if a short name is already defined
# File lib/puppet/settings.rb, line 361 def shortinclude?(short) short = short.intern if name.is_a? String @shortnames.include?(short) end
Convert the settings we manage into a catalog full of resources that model those settings.
# File lib/puppet/settings.rb, line 839 def to_catalog(*sections) sections = nil if sections.empty? catalog = Puppet::Resource::Catalog.new("Settings") @config.keys.find_all { |key| @config[key].is_a?(FileSetting) }.each do |key| file = @config[key] next unless (sections.nil? or sections.include?(file.section)) next unless resource = file.to_resource next if catalog.resource(resource.ref) Puppet.debug("Using settings: adding file resource '#{key}': '#{resource.inspect}'") catalog.add_resource(resource) end add_user_resources(catalog, sections) catalog end
Convert our list of config settings into a configuration file.
# File lib/puppet/settings.rb, line 861 def to_config str = %Q{The configuration file for #{Puppet.run_mode.name}. Note that this file is likely to have unused configuration parameters in it; any parameter that's valid anywhere in Puppet can be in any config file, even if it's not used. Every section can specify three special parameters: owner, group, and mode. These parameters affect the required permissions of any files specified after their specification. Puppet will sometimes use these parameters to check its own configured state, so they can be used to make Puppet a bit more self-managing. The file format supports octothorpe-commented lines, but not partial-line comments. Generated on #{Time.now}. }.gsub(/^/, "# ") # Add a section heading that matches our name. str += "[#{preferred_run_mode}]\n" eachsection do |section| persection(section) do |obj| str += obj.to_config + "\n" unless obj.name == :genconfig end end return str end
Convert to a parseable manifest
# File lib/puppet/settings.rb, line 889 def to_manifest catalog = to_catalog catalog.resource_refs.collect do |ref| catalog.resource(ref).to_manifest end.join("\n\n") end
# File lib/puppet/settings.rb, line 930 def uninterpolated_value(param, environment = nil) param = param.to_sym environment &&= environment.to_sym # See if we can find it within our searchable list of values val = find_value(environment, param) # If we didn't get a value, use the default val = @config[param].default if val.nil? val end
Create the necessary objects to use a section. This is idempotent; you can ‘use’ a section as many times as you want.
# File lib/puppet/settings.rb, line 898 def use(*sections) sections = sections.collect { |s| s.to_sym } @sync.synchronize do # yay, thread-safe sections = sections.reject { |s| @used.include?(s) } return if sections.empty? begin catalog = to_catalog(*sections).to_ral rescue => detail Puppet.log_and_raise(detail, "Could not create resources for managing Puppet's files and directories in sections #{sections.inspect}: #{detail}") end catalog.host_config = false catalog.apply do |transaction| if transaction.any_failed? report = transaction.report failures = report.logs.find_all { |log| log.level == :err } raise "Got #{failures.length} failure(s) while initializing: #{failures.collect { |l| l.to_s }.join("; ")}" end end sections.each { |s| @used << s } @used.uniq! end end
# File lib/puppet/settings.rb, line 925 def valid?(param) param = param.to_sym @config.has_key?(param) end
Find the correct value using our search path. Optionally accept an environment in which to search before the other configuration sections.
# File lib/puppet/settings.rb, line 957 def value(param, environment = nil, bypass_interpolation = false) param = param.to_sym environment &&= environment.to_sym setting = @config[param] # Short circuit to nil for undefined parameters. return nil unless @config.include?(param) # Yay, recursion. #self.reparse unless [:config, :filetimeout].include?(param) # Check the cache first. It needs to be a per-environment # cache so that we don't spread values from one env # to another. if cached = @cache[environment||"none"][param] return cached end val = uninterpolated_value(param, environment) return val if bypass_interpolation if param == :code # if we interpolate code, all hell breaks loose. return val end # Convert it if necessary begin val = convert(val, environment) rescue InterpolationError => err # This happens because we don't have access to the param name when the # exception is originally raised, but we want it in the message raise InterpolationError, "Error converting value for param '#{param}': #{err}", err.backtrace end val = setting.munge(val) if setting.respond_to?(:munge) # And cache it @cache[environment||"none"][param] = val val end
Open a file with the appropriate user, group, and mode
# File lib/puppet/settings.rb, line 1000 def write(default, *args, &bloc) obj = get_config_file_default(default) writesub(default, value(obj.name), *args, &bloc) end
Open a non-default file under a default dir with the appropriate user, group, and mode
# File lib/puppet/settings.rb, line 1007 def writesub(default, file, *args, &bloc) obj = get_config_file_default(default) chown = nil if Puppet.features.root? chown = [obj.owner, obj.group] else chown = [nil, nil] end Puppet::Util::SUIDManager.asuser(*chown) do mode = obj.mode ? obj.mode.to_i : 0640 args << "w" if args.empty? args << mode # Update the umask to make non-executable files Puppet::Util.withumask(File.umask ^ 0111) do File.open(file, *args) do |file| yield file end end end end