diff --git a/acceptance/tests/ssl/certificate_extensions.rb b/acceptance/tests/ssl/certificate_extensions.rb index a1c66f569..7928249df 100644 --- a/acceptance/tests/ssl/certificate_extensions.rb +++ b/acceptance/tests/ssl/certificate_extensions.rb @@ -1,110 +1,109 @@ require 'puppet/acceptance/common_utils' require 'puppet/acceptance/temp_file_utils' extend Puppet::Acceptance::CAUtils extend Puppet::Acceptance::TempFileUtils require 'puppet/acceptance/classifier_utils' extend Puppet::Acceptance::ClassifierUtils disable_pe_enterprise_mcollective_agent_classes initialize_temp_dirs test_name "certificate extensions available as trusted data" do agent_certnames = [] teardown do step "Cleanup the test agent certs" agent_certnames.each do |cn| on(master, puppet("cert", "clean", cn), :acceptable_exit_codes => [0,24]) end end hostname = master.execute('facter hostname') fqdn = master.execute('facter fqdn') environments_dir = get_test_file_path(master, "environments") master_config = { 'main' => { 'environmentpath' => environments_dir, }, 'master' => { 'autosign' => '/bin/true', 'dns_alt_names' => "puppet,#{hostname},#{fqdn}", - 'trusted_node_data' => true, } } csr_attributes = YAML.dump({ 'extension_requests' => { # registered puppet extensions 'pp_uuid' => 'b5e63090-5167-11e3-8f96-0800200c9a66', 'pp_instance_id' => 'i-3fkva', # private (arbitrary) extensions '1.3.6.1.4.1.34380.1.2.1' => 'db-server', # node role '1.3.6.1.4.1.34380.1.2.2' => 'webops' # node group } }) step "Generate a production environment manifest to dump trusted data" apply_manifest_on(master, <<-MANIFEST, :catch_failures => true) File { ensure => directory, mode => "0770", owner => #{master.puppet['user']}, group => #{master.puppet['group']}, } file { '#{environments_dir}':; '#{environments_dir}/production':; '#{environments_dir}/production/manifests':; '#{environments_dir}/production/manifests/site.pp': ensure => file, content => ' file { "$test_dir/trusted.yaml": ensure => file, content => inline_template("<%= YAML.dump(@trusted) %>") } ', mode => "0640", } MANIFEST with_puppet_running_on(master, master_config) do agents.each do |agent| next if agent == master step "Create agent csr_attributes.yaml on #{agent}" agent_csr_attributes = get_test_file_path(agent, "csr_attributes.yaml") agent_ssldir = get_test_file_path(agent, "ssldir") create_remote_file(agent, agent_csr_attributes, csr_attributes) agent_certname = "#{agent}-extensions" agent_certnames << agent_certname step "Check in as #{agent_certname}" on(agent, puppet("agent", "--test", "--server", master, "--waitforcert", 0, "--csr_attributes", agent_csr_attributes, "--certname", agent_certname, "--ssldir", agent_ssldir, 'ENV' => { "FACTER_test_dir" => get_test_file_path(agent, "") }), :acceptable_exit_codes => [0, 2]) trusted_data = YAML.load(on(agent, "cat #{get_test_file_path(agent, 'trusted.yaml')}").stdout) step "Verify trusted data" assert_equal({ 'authenticated' => 'remote', 'certname' => agent_certname, 'extensions' => { 'pp_uuid' => 'b5e63090-5167-11e3-8f96-0800200c9a66', 'pp_instance_id' => 'i-3fkva', '1.3.6.1.4.1.34380.1.2.1' => 'db-server', '1.3.6.1.4.1.34380.1.2.2' => 'webops' } }, trusted_data) end end end diff --git a/lib/puppet/application/master.rb b/lib/puppet/application/master.rb index c69c85fe1..9648891ca 100644 --- a/lib/puppet/application/master.rb +++ b/lib/puppet/application/master.rb @@ -1,305 +1,305 @@ require 'puppet/application' require 'puppet/daemon' require 'puppet/util/pidlock' class Puppet::Application::Master < Puppet::Application run_mode :master option("--debug", "-d") option("--verbose", "-v") # internal option, only to be used by ext/rack/config.ru option("--rack") option("--compile host", "-c host") do |arg| options[:node] = arg end option("--logdest DEST", "-l DEST") do |arg| handle_logdest_arg(arg) end option("--parseonly") do |args| puts "--parseonly has been removed. Please use 'puppet parser validate '" exit 1 end def help <<-'HELP' puppet-master(8) -- The puppet master daemon ======== SYNOPSIS -------- The central puppet server. Functions as a certificate authority by default. USAGE ----- puppet master [-D|--daemonize|--no-daemonize] [-d|--debug] [-h|--help] [-l|--logdest syslog||console] [-v|--verbose] [-V|--version] [--compile ] DESCRIPTION ----------- This command starts an instance of puppet master, running as a daemon and using Ruby's built-in Webrick webserver. Puppet master can also be managed by other application servers; when this is the case, this executable is not used. OPTIONS ------- Note that any Puppet setting that's valid in the configuration file is also a valid long argument. For example, 'server' is a valid setting, so you can specify '--server ' as an argument. Boolean settings translate into '--setting' and '--no-setting' pairs. See the configuration file documentation at http://docs.puppetlabs.com/references/stable/configuration.html for the full list of acceptable settings. A commented list of all settings can also be generated by running puppet master with '--genconfig'. * --daemonize: Send the process into the background. This is the default. (This is a Puppet setting, and can go in puppet.conf. Note the special 'no-' prefix for boolean settings on the command line.) * --no-daemonize: Do not send the process into the background. (This is a Puppet setting, and can go in puppet.conf. Note the special 'no-' prefix for boolean settings on the command line.) * --debug: Enable full debugging. * --help: Print this help message. * --logdest: Where to send log messages. Choose between 'syslog' (the POSIX syslog service), 'console', or the path to a log file. If debugging or verbosity is enabled, this defaults to 'console'. Otherwise, it defaults to 'syslog'. * --masterport: The port on which to listen for traffic. (This is a Puppet setting, and can go in puppet.conf.) * --verbose: Enable verbosity. * --version: Print the puppet version number and exit. * --compile: Compile a catalogue and output it in JSON from the puppet master. Uses facts contained in the $vardir/yaml/ directory to compile the catalog. EXAMPLE ------- puppet master DIAGNOSTICS ----------- When running as a standalone daemon, puppet master accepts the following signals: * SIGHUP: Restart the puppet master server. * SIGINT and SIGTERM: Shut down the puppet master server. * SIGUSR2: Close file descriptors for log files and reopen them. Used with logrotate. AUTHOR ------ Luke Kanies COPYRIGHT --------- Copyright (c) 2012 Puppet Labs, LLC Licensed under the Apache 2.0 License HELP end # Sets up the 'node_cache_terminus' default to use the Write Only Yaml terminus :write_only_yaml. # If this is not wanted, the setting ´node_cache_terminus´ should be set to nil. # @see Puppet::Node::WriteOnlyYaml # @see #setup_node_cache # @see puppet issue 16753 # def app_defaults super.merge({ :node_cache_terminus => :write_only_yaml, :facts_terminus => 'yaml' }) end def preinit Signal.trap(:INT) do $stderr.puts "Canceling startup" exit(0) end # save ARGV to protect us from it being smashed later by something @argv = ARGV.dup end def run_command if options[:node] compile else main end end def compile begin unless catalog = Puppet::Resource::Catalog.indirection.find(options[:node]) raise "Could not compile catalog for #{options[:node]}" end puts PSON::pretty_generate(catalog.to_resource, :allow_nan => true, :max_nesting => false) rescue => detail Puppet.log_exception(detail, "Failed to compile catalog for node #{options[:node]}: #{detail}") exit(30) end exit(0) end def main require 'etc' # Make sure we've got a localhost ssl cert Puppet::SSL::Host.localhost # And now configure our server to *only* hit the CA for data, because that's # all it will have write access to. Puppet::SSL::Host.ca_location = :only if Puppet::SSL::CertificateAuthority.ca? if Puppet.features.root? begin Puppet::Util.chuser rescue => detail Puppet.log_exception(detail, "Could not change user to #{Puppet[:user]}: #{detail}") exit(39) end end if options[:rack] start_rack_master else start_webrick_master end end def setup_logs set_log_level if !options[:setdest] if options[:node] # We are compiling a catalog for a single node with '--compile' and logging # has not already been configured via '--logdest' so log to the console. Puppet::Util::Log.newdestination(:console) elsif !(Puppet[:daemonize] or options[:rack]) # We are running a webrick master which has been explicitly foregrounded # and '--logdest' has not been passed, assume users want to see logging # and log to the console. Puppet::Util::Log.newdestination(:console) else # No explicit log destination has been given with '--logdest' and we're # either a daemonized webrick master or running under rack, log to syslog. Puppet::Util::Log.newdestination(:syslog) end end end def setup_terminuses require 'puppet/file_serving/content' require 'puppet/file_serving/metadata' Puppet::FileServing::Content.indirection.terminus_class = :file_server Puppet::FileServing::Metadata.indirection.terminus_class = :file_server Puppet::FileBucket::File.indirection.terminus_class = :file end def setup_ssl # Configure all of the SSL stuff. if Puppet::SSL::CertificateAuthority.ca? Puppet::SSL::Host.ca_location = :local Puppet.settings.use :ca Puppet::SSL::CertificateAuthority.instance else Puppet::SSL::Host.ca_location = :none end - Puppet::SSL::Oids.load_custom_oid_file(Puppet[:trusted_oid_mapping_file]) if Puppet[:trusted_node_data] + Puppet::SSL::Oids.load_custom_oid_file(Puppet[:trusted_oid_mapping_file]) end # Sets up a special node cache "write only yaml" that collects and stores node data in yaml # but never finds or reads anything (this since a real cache causes stale data to be served # in circumstances when the cache can not be cleared). # @see puppet issue 16753 # @see Puppet::Node::WriteOnlyYaml # @return [void] def setup_node_cache Puppet::Node.indirection.cache_class = Puppet[:node_cache_terminus] end def setup raise Puppet::Error.new("Puppet master is not supported on Microsoft Windows") if Puppet.features.microsoft_windows? setup_logs exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs? Puppet.settings.use :main, :master, :ssl, :metrics setup_terminuses setup_node_cache setup_ssl end private # Start a master that will be using WeBrick. # # This method will block until the master exits. def start_webrick_master require 'puppet/network/server' daemon = Puppet::Daemon.new(Puppet::Util::Pidlock.new(Puppet[:pidfile])) daemon.argv = @argv daemon.server = Puppet::Network::Server.new(Puppet[:bindaddress], Puppet[:masterport]) daemon.daemonize if Puppet[:daemonize] announce_start_of_master daemon.start end # Start a master that will be used for a Rack container. # # This method immediately returns the Rack handler that must be returned to # the calling Rack container def start_rack_master require 'puppet/network/http/rack' announce_start_of_master return Puppet::Network::HTTP::Rack.new() end def announce_start_of_master Puppet.notice "Starting Puppet master version #{Puppet.version}" end end diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index b91cc543a..2a6636646 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -1,1830 +1,1819 @@ module Puppet def self.default_diffargs if (Facter.value(:kernel) == "AIX" && Facter.value(:kernelmajversion) == "5300") "" else "-u" end end ############################################################################################ # NOTE: For information about the available values for the ":type" property of settings, # see the docs for Settings.define_settings ############################################################################################ AS_DURATION = %q{This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y).} STORECONFIGS_ONLY = %q{This setting is only used by the ActiveRecord storeconfigs and inventory backends, which are deprecated.} # This is defined first so that the facter implementation is replaced before other setting defaults are evaluated. define_settings(:main, :cfacter => { :default => false, :type => :boolean, :desc => 'Whether or not to use the native facter (cfacter) implementation instead of the Ruby one (facter). Defaults to false.', :hook => proc do |value| return unless value raise ArgumentError, 'facter has already evaluated facts.' if Facter.instance_variable_get(:@collection) raise ArgumentError, 'cfacter version 0.2.0 or later is not installed.' unless Puppet.features.cfacter? CFacter.initialize end } ) define_settings(:main, :confdir => { :default => nil, :type => :directory, :desc => "The main Puppet configuration directory. The default for this setting is calculated based on the user. If the process is running as root or the user that Puppet is supposed to run as, it defaults to a system directory, but if it's running as any other user, it defaults to being in the user's home directory.", }, :vardir => { :default => nil, :type => :directory, :owner => "service", :group => "service", :desc => "Where Puppet stores dynamic and growing data. The default for this setting is calculated specially, like `confdir`_.", }, ### NOTE: this setting is usually being set to a symbol value. We don't officially have a ### setting type for that yet, but we might want to consider creating one. :name => { :default => nil, :desc => "The name of the application, if we are running as one. The default is essentially $0 without the path or `.rb`.", } ) define_settings(:main, :logdir => { :default => nil, :type => :directory, :mode => "0750", :owner => "service", :group => "service", :desc => "The directory in which to store log files", }, :log_level => { :default => 'notice', :type => :enum, :values => ["debug","info","notice","warning","err","alert","emerg","crit"], :desc => "Default logging level for messages from Puppet. Allowed values are: * debug * info * notice * warning * err * alert * emerg * crit ", :hook => proc {|value| Puppet::Util::Log.level = value }, :call_hook => :on_initialize_and_write, }, :disable_warnings => { :default => [], :type => :array, :desc => "A comma-separated list of warning types to suppress. If large numbers of warnings are making Puppet's logs too large or difficult to use, you can temporarily silence them with this setting. If you are preparing to upgrade Puppet to a new major version, you should re-enable all warnings for a while. Valid values for this setting are: * `deprecations` --- disables deprecation warnings.", :hook => proc do |value| values = munge(value) valid = %w[deprecations] invalid = values - (values & valid) if not invalid.empty? raise ArgumentError, "Cannot disable unrecognized warning types #{invalid.inspect}. Valid values are #{valid.inspect}." end end } ) define_settings(:main, :priority => { :default => nil, :type => :priority, :desc => "The scheduling priority of the process. Valid values are 'high', 'normal', 'low', or 'idle', which are mapped to platform-specific values. The priority can also be specified as an integer value and will be passed as is, e.g. -5. Puppet must be running as a privileged user in order to increase scheduling priority.", }, :trace => { :default => false, :type => :boolean, :desc => "Whether to print stack traces on some errors", }, :profile => { :default => false, :type => :boolean, :desc => "Whether to enable experimental performance profiling", }, :autoflush => { :default => true, :type => :boolean, :desc => "Whether log files should always flush to disk.", :hook => proc { |value| Log.autoflush = value } }, :syslogfacility => { :default => "daemon", :desc => "What syslog facility to use when logging to syslog. Syslog has a fixed list of valid facilities, and you must choose one of those; you cannot just make one up." }, :statedir => { :default => "$vardir/state", :type => :directory, :mode => "01755", :desc => "The directory where Puppet state is stored. Generally, this directory can be removed without causing harm (although it might result in spurious service restarts)." }, :rundir => { :default => nil, :type => :directory, :mode => "0755", :owner => "service", :group => "service", :desc => "Where Puppet PID files are kept." }, :genconfig => { :default => false, :type => :boolean, :desc => "When true, causes Puppet applications to print an example config file to stdout and exit. The example will include descriptions of each setting, and the current (or default) value of each setting, incorporating any settings overridden on the CLI (with the exception of `genconfig` itself). This setting only makes sense when specified on the command line as `--genconfig`.", }, :genmanifest => { :default => false, :type => :boolean, :desc => "Whether to just print a manifest to stdout and exit. Only makes sense when specified on the command line as `--genmanifest`. Takes into account arguments specified on the CLI.", }, :configprint => { :default => "", :desc => "Print the value of a specific configuration setting. If the name of a setting is provided for this, then the value is printed and puppet exits. Comma-separate multiple values. For a list of all values, specify 'all'.", }, :color => { :default => "ansi", :type => :string, :desc => "Whether to use colors when logging to the console. Valid values are `ansi` (equivalent to `true`), `html`, and `false`, which produces no color. Defaults to false on Windows, as its console does not support ansi colors.", }, :mkusers => { :default => false, :type => :boolean, :desc => "Whether to create the necessary user and group that puppet agent will run as.", }, :manage_internal_file_permissions => { :default => true, :type => :boolean, :desc => "Whether Puppet should manage the owner, group, and mode of files it uses internally", }, :onetime => { :default => false, :type => :boolean, :desc => "Perform one configuration run and exit, rather than spawning a long-running daemon. This is useful for interactively running puppet agent, or running puppet agent from cron.", :short => 'o', }, :path => { :default => "none", :desc => "The shell search path. Defaults to whatever is inherited from the parent process.", :call_hook => :on_define_and_write, :hook => proc do |value| ENV["PATH"] = "" if ENV["PATH"].nil? ENV["PATH"] = value unless value == "none" paths = ENV["PATH"].split(File::PATH_SEPARATOR) Puppet::Util::Platform.default_paths.each do |path| ENV["PATH"] += File::PATH_SEPARATOR + path unless paths.include?(path) end value end }, :libdir => { :type => :directory, :default => "$vardir/lib", :desc => "An extra search path for Puppet. This is only useful for those files that Puppet will load on demand, and is only guaranteed to work for those cases. In fact, the autoload mechanism is responsible for making sure this directory is in Ruby's search path\n", :call_hook => :on_initialize_and_write, :hook => proc do |value| $LOAD_PATH.delete(@oldlibdir) if defined?(@oldlibdir) and $LOAD_PATH.include?(@oldlibdir) @oldlibdir = value $LOAD_PATH << value end }, :ignoreimport => { :default => false, :type => :boolean, :desc => "If true, allows the parser to continue without requiring all files referenced with `import` statements to exist. This setting was primarily designed for use with commit hooks for parse-checking.", }, :environment => { :default => "production", :desc => "The environment Puppet is running in. For clients (e.g., `puppet agent`) this determines the environment itself, which is used to find modules and much more. For servers (i.e., `puppet master`) this provides the default environment for nodes we know nothing about." }, :environmentpath => { :default => "", :desc => "A search path for directory environments, as a list of directories separated by the system path separator character. (The POSIX path separator is ':', and the Windows path separator is ';'.) This setting must have a value set to enable **directory environments.** The recommended value is `$confdir/environments`. For more details, see http://docs.puppetlabs.com/puppet/latest/reference/environments.html", :type => :path, }, :always_cache_features => { :type => :boolean, :default => false, :desc => <<-'EOT' Affects how we cache attempts to load Puppet 'features'. If false, then calls to `Puppet.features.?` will always attempt to load the feature (which can be an expensive operation) unless it has already been loaded successfully. This makes it possible for a single agent run to, e.g., install a package that provides the underlying capabilities for a feature, and then later load that feature during the same run (even if the feature had been tested earlier and had not been available). If this setting is set to true, then features will only be checked once, and if they are not available, the negative result is cached and returned for all subsequent attempts to load the feature. This behavior is almost always appropriate for the server, and can result in a significant performance improvement for features that are checked frequently. EOT }, :diff_args => { :default => lambda { default_diffargs }, :desc => "Which arguments to pass to the diff command when printing differences between files. The command to use can be chosen with the `diff` setting.", }, :diff => { :default => (Puppet.features.microsoft_windows? ? "" : "diff"), :desc => "Which diff command to use when printing differences between files. This setting has no default value on Windows, as standard `diff` is not available, but Puppet can use many third-party diff tools.", }, :show_diff => { :type => :boolean, :default => false, :desc => "Whether to log and report a contextual diff when files are being replaced. This causes partial file contents to pass through Puppet's normal logging and reporting system, so this setting should be used with caution if you are sending Puppet's reports to an insecure destination. This feature currently requires the `diff/lcs` Ruby library.", }, :daemonize => { :type => :boolean, :default => (Puppet.features.microsoft_windows? ? false : true), :desc => "Whether to send the process into the background. This defaults to true on POSIX systems, and to false on Windows (where Puppet currently cannot daemonize).", :short => "D", :hook => proc do |value| if value and Puppet.features.microsoft_windows? raise "Cannot daemonize on Windows" end end }, :maximum_uid => { :default => 4294967290, :desc => "The maximum allowed UID. Some platforms use negative UIDs but then ship with tools that do not know how to handle signed ints, so the UIDs show up as huge numbers that can then not be fed back into the system. This is a hackish way to fail in a slightly more useful way when that happens.", }, :route_file => { :default => "$confdir/routes.yaml", :desc => "The YAML file containing indirector route configuration.", }, :node_terminus => { :type => :terminus, :default => "plain", :desc => "Where to find information about nodes.", }, :node_cache_terminus => { :type => :terminus, :default => nil, :desc => "How to store cached nodes. Valid values are (none), 'json', 'msgpack', 'yaml' or write only yaml ('write_only_yaml'). The master application defaults to 'write_only_yaml', all others to none.", }, :data_binding_terminus => { :type => :terminus, :default => "hiera", :desc => "Where to retrive information about data.", }, :hiera_config => { :default => "$confdir/hiera.yaml", :desc => "The hiera configuration file. Puppet only reads this file on startup, so you must restart the puppet master every time you edit it.", :type => :file, }, :binder => { :default => false, :desc => "Turns the binding system on or off. This includes bindings in modules. The binding system aggregates data from modules and other locations and makes them available for lookup. The binding system is experimental and any or all of it may change.", :type => :boolean, }, :binder_config => { :default => nil, :desc => "The binder configuration file. Puppet reads this file on each request to configure the bindings system. If set to nil (the default), a $confdir/binder_config.yaml is optionally loaded. If it does not exists, a default configuration is used. If the setting :binding_config is specified, it must reference a valid and existing yaml file.", :type => :file, }, :catalog_terminus => { :type => :terminus, :default => "compiler", :desc => "Where to get node catalogs. This is useful to change if, for instance, you'd like to pre-compile catalogs and store them in memcached or some other easily-accessed store.", }, :catalog_cache_terminus => { :type => :terminus, :default => nil, :desc => "How to store cached catalogs. Valid values are 'json', 'msgpack' and 'yaml'. The agent application defaults to 'json'." }, :facts_terminus => { :default => 'facter', :desc => "The node facts terminus.", }, :default_file_terminus => { :type => :terminus, :default => "rest", :desc => "The default source for files if no server is given in a uri, e.g. puppet:///file. The default of `rest` causes the file to be retrieved using the `server` setting. When running `apply` the default is `file_server`, causing requests to be filled locally." }, :httplog => { :default => "$logdir/http.log", :type => :file, :owner => "root", :mode => "0640", :desc => "Where the puppet agent web server logs.", }, :http_proxy_host => { :default => "none", :desc => "The HTTP proxy host to use for outgoing connections. Note: You may need to use a FQDN for the server hostname when using a proxy. Environment variable http_proxy or HTTP_PROXY will override this value", }, :http_proxy_port => { :default => 3128, :desc => "The HTTP proxy port to use for outgoing connections", }, :http_proxy_user => { :default => "none", :desc => "The user name for an authenticated HTTP proxy. Requires the `http_proxy_host` setting.", }, :http_proxy_password =>{ :default => "none", :hook => proc do |value| if Puppet.settings[:http_proxy_password] =~ /[@!# \/]/ raise "Passwords set in the http_proxy_password setting must be valid as part of a URL, and any reserved characters must be URL-encoded. We received: #{value}" end end, :desc => "The password for the user of an authenticated HTTP proxy. Requires the `http_proxy_user` setting. Note that passwords must be valid when used as part of a URL. If a password contains any characters with special meanings in URLs (as specified by RFC 3986 section 2.2), they must be URL-encoded. (For example, `#` would become `%23`.)", }, :http_keepalive_timeout => { :default => "4s", :type => :duration, :desc => "The maximum amount of time a persistent HTTP connection can remain idle in the connection pool, before it is closed. This timeout should be shorter than the keepalive timeout used on the HTTP server, e.g. Apache KeepAliveTimeout directive. #{AS_DURATION}" }, :http_debug => { :default => false, :type => :boolean, :desc => "Whether to write HTTP request and responses to stderr. This should never be used in a production environment." }, :filetimeout => { :default => "15s", :type => :duration, :desc => "The minimum time to wait between checking for updates in configuration files. This timeout determines how quickly Puppet checks whether a file (such as manifests or templates) has changed on disk. #{AS_DURATION}", }, :environment_timeout => { :default => "unlimited", :type => :ttl, :desc => "The time to live for a cached environment. #{AS_DURATION} This setting can also be set to `unlimited`, which causes the environment to be cached until the master is restarted." }, :thin_storeconfigs => { :default => false, :type => :boolean, :desc => "Boolean; whether Puppet should store only facts and exported resources in the storeconfigs database. This will improve the performance of exported resources with the older `active_record` backend, but will disable external tools that search the storeconfigs database. Thinning catalogs is generally unnecessary when using PuppetDB to store catalogs.", :hook => proc do |value| Puppet.settings.override_default(:storeconfigs, true) if value end }, :config_version => { :default => "", :desc => "How to determine the configuration version. By default, it will be the time that the configuration is parsed, but you can provide a shell script to override how the version is determined. The output of this script will be added to every log message in the reports, allowing you to correlate changes on your hosts to the source version on the server. Setting a global value for config_version in puppet.conf is deprecated. Please set a per-environment value in environment.conf instead. For more info, see http://docs.puppetlabs.com/puppet/latest/reference/environments.html", :deprecated => :allowed_on_commandline, }, :prerun_command => { :default => "", :desc => "A command to run before every agent run. If this command returns a non-zero return code, the entire Puppet run will fail.", }, :postrun_command => { :default => "", :desc => "A command to run after every agent run. If this command returns a non-zero return code, the entire Puppet run will be considered to have failed, even though it might have performed work during the normal run.", }, :freeze_main => { :default => false, :type => :boolean, :desc => "Freezes the 'main' class, disallowing any code to be added to it. This essentially means that you can't have any code outside of a node, class, or definition other than in the site manifest.", - }, - :trusted_node_data => { - :default => false, - :type => :boolean, - :desc => "Stores trusted node data in a hash called $trusted. - When true also prevents $trusted from being overridden in any scope.", - }, - :immutable_node_data => { - :default => '$trusted_node_data', - :type => :boolean, - :desc => "When true, also prevents $trusted and $facts from being overridden in any scope", } ) Puppet.define_settings(:module_tool, :module_repository => { :default => 'https://forgeapi.puppetlabs.com', :desc => "The module repository", }, :module_working_dir => { :default => '$vardir/puppet-module', :desc => "The directory into which module tool data is stored", }, :module_skeleton_dir => { :default => '$module_working_dir/skeleton', :desc => "The directory which the skeleton for module tool generate is stored.", }, :forge_authorization => { :default => nil, :desc => "The authorization key to connect to the Puppet Forge. Leave blank for unauthorized or license based connections", }, :module_groups => { :default => nil, :desc => "Extra module groups to request from the Puppet Forge", } ) Puppet.define_settings( :main, # We have to downcase the fqdn, because the current ssl stuff (as oppsed to in master) doesn't have good facilities for # manipulating naming. :certname => { :default => lambda { Puppet::Settings.default_certname.downcase }, :desc => "The name to use when handling certificates. When a node requests a certificate from the CA puppet master, it uses the value of the `certname` setting as its requested Subject CN. This is the name used when managing a node's permissions in [auth.conf](http://docs.puppetlabs.com/puppet/latest/reference/config_file_auth.html). In most cases, it is also used as the node's name when matching [node definitions](http://docs.puppetlabs.com/puppet/latest/reference/lang_node_definitions.html) and requesting data from an ENC. (This can be changed with the `node_name_value` and `node_name_fact` settings, although you should only do so if you have a compelling reason.) A node's certname is available in Puppet manifests as `$trusted['certname']`. (See [Facts and Built-In Variables](http://docs.puppetlabs.com/puppet/latest/reference/lang_facts_and_builtin_vars.html) for more details.) * For best compatibility, you should limit the value of `certname` to only use letters, numbers, periods, underscores, and dashes. (That is, it should match `/\A[a-z0-9._-]+\Z/`.) * The special value `ca` is reserved, and can't be used as the certname for a normal node. Defaults to the node's fully qualified domain name.", :hook => proc { |value| raise(ArgumentError, "Certificate names must be lower case; see #1168") unless value == value.downcase }}, :dns_alt_names => { :default => '', :desc => < { :default => "$confdir/csr_attributes.yaml", :type => :file, :desc => < { :default => "$ssldir/certs", :type => :directory, :mode => "0755", :owner => "service", :group => "service", :desc => "The certificate directory." }, :ssldir => { :default => "$confdir/ssl", :type => :directory, :mode => "0771", :owner => "service", :group => "service", :desc => "Where SSL certificates are kept." }, :publickeydir => { :default => "$ssldir/public_keys", :type => :directory, :mode => "0755", :owner => "service", :group => "service", :desc => "The public key directory." }, :requestdir => { :default => "$ssldir/certificate_requests", :type => :directory, :mode => "0755", :owner => "service", :group => "service", :desc => "Where host certificate requests are stored." }, :privatekeydir => { :default => "$ssldir/private_keys", :type => :directory, :mode => "0750", :owner => "service", :group => "service", :desc => "The private key directory." }, :privatedir => { :default => "$ssldir/private", :type => :directory, :mode => "0750", :owner => "service", :group => "service", :desc => "Where the client stores private certificate information." }, :passfile => { :default => "$privatedir/password", :type => :file, :mode => "0640", :owner => "service", :group => "service", :desc => "Where puppet agent stores the password for its private key. Generally unused." }, :hostcsr => { :default => "$ssldir/csr_$certname.pem", :type => :file, :mode => "0644", :owner => "service", :group => "service", :desc => "Where individual hosts store and look for their certificate requests." }, :hostcert => { :default => "$certdir/$certname.pem", :type => :file, :mode => "0644", :owner => "service", :group => "service", :desc => "Where individual hosts store and look for their certificates." }, :hostprivkey => { :default => "$privatekeydir/$certname.pem", :type => :file, :mode => "0640", :owner => "service", :group => "service", :desc => "Where individual hosts store and look for their private key." }, :hostpubkey => { :default => "$publickeydir/$certname.pem", :type => :file, :mode => "0644", :owner => "service", :group => "service", :desc => "Where individual hosts store and look for their public key." }, :localcacert => { :default => "$certdir/ca.pem", :type => :file, :mode => "0644", :owner => "service", :group => "service", :desc => "Where each client stores the CA certificate." }, :ssl_client_ca_auth => { :type => :file, :mode => "0644", :owner => "service", :group => "service", :desc => "Certificate authorities who issue server certificates. SSL servers will not be considered authentic unless they possess a certificate issued by an authority listed in this file. If this setting has no value then the Puppet master's CA certificate (localcacert) will be used." }, :ssl_server_ca_auth => { :type => :file, :mode => "0644", :owner => "service", :group => "service", :desc => "Certificate authorities who issue client certificates. SSL clients will not be considered authentic unless they possess a certificate issued by an authority listed in this file. If this setting has no value then the Puppet master's CA certificate (localcacert) will be used." }, :hostcrl => { :default => "$ssldir/crl.pem", :type => :file, :mode => "0644", :owner => "service", :group => "service", :desc => "Where the host's certificate revocation list can be found. This is distinct from the certificate authority's CRL." }, :certificate_revocation => { :default => true, :type => :boolean, :desc => "Whether certificate revocation should be supported by downloading a Certificate Revocation List (CRL) to all clients. If enabled, CA chaining will almost definitely not work.", }, :digest_algorithm => { :default => 'md5', :type => :enum, :values => ["md5", "sha256"], :desc => 'Which digest algorithm to use for file resources and the filebucket. Valid values are md5, sha256. Default is md5.', } ) define_settings( :ca, :ca_name => { :default => "Puppet CA: $certname", :desc => "The name to use the Certificate Authority certificate.", }, :cadir => { :default => "$ssldir/ca", :type => :directory, :owner => "service", :group => "service", :mode => "0755", :desc => "The root directory for the certificate authority." }, :cacert => { :default => "$cadir/ca_crt.pem", :type => :file, :owner => "service", :group => "service", :mode => "0644", :desc => "The CA certificate." }, :cakey => { :default => "$cadir/ca_key.pem", :type => :file, :owner => "service", :group => "service", :mode => "0640", :desc => "The CA private key." }, :capub => { :default => "$cadir/ca_pub.pem", :type => :file, :owner => "service", :group => "service", :mode => "0644", :desc => "The CA public key." }, :cacrl => { :default => "$cadir/ca_crl.pem", :type => :file, :owner => "service", :group => "service", :mode => "0644", :desc => "The certificate revocation list (CRL) for the CA. Will be used if present but otherwise ignored.", }, :caprivatedir => { :default => "$cadir/private", :type => :directory, :owner => "service", :group => "service", :mode => "0750", :desc => "Where the CA stores private certificate information." }, :csrdir => { :default => "$cadir/requests", :type => :directory, :owner => "service", :group => "service", :mode => "0755", :desc => "Where the CA stores certificate requests" }, :signeddir => { :default => "$cadir/signed", :type => :directory, :owner => "service", :group => "service", :mode => "0755", :desc => "Where the CA stores signed certificates." }, :capass => { :default => "$caprivatedir/ca.pass", :type => :file, :owner => "service", :group => "service", :mode => "0640", :desc => "Where the CA stores the password for the private key." }, :serial => { :default => "$cadir/serial", :type => :file, :owner => "service", :group => "service", :mode => "0644", :desc => "Where the serial number for certificates is stored." }, :autosign => { :default => "$confdir/autosign.conf", :type => :autosign, :desc => "Whether (and how) to autosign certificate requests. This setting is only relevant on a puppet master acting as a certificate authority (CA). Valid values are true (autosigns all certificate requests; not recommended), false (disables autosigning certificates), or the absolute path to a file. The file specified in this setting may be either a **configuration file** or a **custom policy executable.** Puppet will automatically determine what it is: If the Puppet user (see the `user` setting) can execute the file, it will be treated as a policy executable; otherwise, it will be treated as a config file. If a custom policy executable is configured, the CA puppet master will run it every time it receives a CSR. The executable will be passed the subject CN of the request _as a command line argument,_ and the contents of the CSR in PEM format _on stdin._ It should exit with a status of 0 if the cert should be autosigned and non-zero if the cert should not be autosigned. If a certificate request is not autosigned, it will persist for review. An admin user can use the `puppet cert sign` command to manually sign it, or can delete the request. For info on autosign configuration files, see [the guide to Puppet's config files](http://docs.puppetlabs.com/guides/configuring.html).", }, :allow_duplicate_certs => { :default => false, :type => :boolean, :desc => "Whether to allow a new certificate request to overwrite an existing certificate.", }, :ca_ttl => { :default => "5y", :type => :duration, :desc => "The default TTL for new certificates. #{AS_DURATION}" }, :req_bits => { :default => 4096, :desc => "The bit length of the certificates.", }, :keylength => { :default => 4096, :desc => "The bit length of keys.", }, :cert_inventory => { :default => "$cadir/inventory.txt", :type => :file, :mode => "0644", :owner => "service", :group => "service", :desc => "The inventory file. This is a text file to which the CA writes a complete listing of all certificates." } ) # Define the config default. define_settings(:application, :config_file_name => { :type => :string, :default => Puppet::Settings.default_config_file_name, :desc => "The name of the puppet config file.", }, :config => { :type => :file, :default => "$confdir/${config_file_name}", :desc => "The configuration file for the current puppet application.", }, :pidfile => { :type => :file, :default => "$rundir/${run_mode}.pid", :desc => "The file containing the PID of a running process. This file is intended to be used by service management frameworks and monitoring systems to determine if a puppet process is still in the process table.", }, :bindaddress => { :default => "0.0.0.0", :desc => "The address a listening server should bind to.", } ) define_settings(:master, :user => { :default => "puppet", :desc => "The user puppet master should run as.", }, :group => { :default => "puppet", :desc => "The group puppet master should run as.", }, :manifestdir => { :default => "$confdir/manifests", :type => :directory, :desc => "Used to build the default value of the `manifest` setting. Has no other purpose. This setting is deprecated.", :deprecated => :completely, }, :manifest => { :default => "$manifestdir/site.pp", :type => :file_or_directory, :desc => "The entry-point manifest for puppet master. This can be one file or a directory of manifests to be evaluated in alphabetical order. Puppet manages this path as a directory if one exists or if the path ends with a / or \\. Setting a global value for `manifest` in puppet.conf is deprecated. Please use directory environments instead. If you need to use something other than the environment's `manifests` directory as the main manifest, you can set `manifest` in environment.conf. For more info, see http://docs.puppetlabs.com/puppet/latest/reference/environments.html", :deprecated => :allowed_on_commandline, }, :default_manifest => { :default => "./manifests", :type => :string, :desc => "The default main manifest for directory environments. Any environment that doesn't set the `manifest` setting in its `environment.conf` file will use this manifest. This setting's value can be an absolute or relative path. An absolute path will make all environments default to the same main manifest; a relative path will allow each environment to use its own manifest, and Puppet will resolve the path relative to each environment's main directory. In either case, the path can point to a single file or to a directory of manifests to be evaluated in alphabetical order.", }, :disable_per_environment_manifest => { :default => false, :type => :boolean, :desc => "Whether to disallow an environment-specific main manifest. When set to `true`, Puppet will use the manifest specified in the `default_manifest` setting for all environments. If an environment specifies a different main manifest in its `environment.conf` file, catalog requests for that environment will fail with an error. This setting requires `default_manifest` to be set to an absolute path.", :hook => proc do |value| if value && !Pathname.new(Puppet[:default_manifest]).absolute? raise(Puppet::Settings::ValidationError, "The 'default_manifest' setting must be set to an absolute path when 'disable_per_environment_manifest' is true") end end, }, :code => { :default => "", :desc => "Code to parse directly. This is essentially only used by `puppet`, and should only be set if you're writing your own Puppet executable.", }, :masterhttplog => { :default => "$logdir/masterhttp.log", :type => :file, :owner => "service", :group => "service", :mode => "0660", :create => true, :desc => "Where the puppet master web server saves its access log. This is only used when running a WEBrick puppet master. When puppet master is running under a Rack server like Passenger, that web server will have its own logging behavior." }, :masterport => { :default => 8140, :desc => "The port for puppet master traffic. For puppet master, this is the port to listen on; for puppet agent, this is the port to make requests on. Both applications use this setting to get the port.", }, :node_name => { :default => "cert", :desc => "How the puppet master determines the client's identity and sets the 'hostname', 'fqdn' and 'domain' facts for use in the manifest, in particular for determining which 'node' statement applies to the client. Possible values are 'cert' (use the subject's CN in the client's certificate) and 'facter' (use the hostname that the client reported in its facts)", }, :bucketdir => { :default => "$vardir/bucket", :type => :directory, :mode => "0750", :owner => "service", :group => "service", :desc => "Where FileBucket files are stored." }, :rest_authconfig => { :default => "$confdir/auth.conf", :type => :file, :desc => "The configuration file that defines the rights to the different rest indirections. This can be used as a fine-grained authorization system for `puppet master`.", }, :ca => { :default => true, :type => :boolean, :desc => "Whether the master should function as a certificate authority.", }, :trusted_oid_mapping_file => { :default => "$confdir/custom_trusted_oid_mapping.yaml", :type => :file, :desc => "File that provides mapping between custom SSL oids and user-friendly names" }, :basemodulepath => { :default => "$confdir/modules#{File::PATH_SEPARATOR}/usr/share/puppet/modules", :type => :path, :desc => "The search path for **global** modules. Should be specified as a list of directories separated by the system path separator character. (The POSIX path separator is ':', and the Windows path separator is ';'.) If you are using directory environments, these are the modules that will be used by _all_ environments. Note that the `modules` directory of the active environment will have priority over any global directories. For more info, see http://docs.puppetlabs.com/puppet/latest/reference/environments.html This setting also provides the default value for the deprecated `modulepath` setting, which is used when directory environments are disabled.", }, :modulepath => { :default => "$basemodulepath", :type => :path, :desc => "The search path for modules, as a list of directories separated by the system path separator character. (The POSIX path separator is ':', and the Windows path separator is ';'.) Setting a global value for `modulepath` in puppet.conf is deprecated. Please use directory environments instead. If you need to use something other than the default modulepath of `:$basemodulepath`, you can set `modulepath` in environment.conf. For more info, see http://docs.puppetlabs.com/puppet/latest/reference/environments.html", :deprecated => :allowed_on_commandline, }, :ssl_client_header => { :default => "HTTP_X_CLIENT_DN", :desc => "The header containing an authenticated client's SSL DN. This header must be set by the proxy to the authenticated client's SSL DN (e.g., `/CN=puppet.puppetlabs.com`). Puppet will parse out the Common Name (CN) from the Distinguished Name (DN) and use the value of the CN field for authorization. Note that the name of the HTTP header gets munged by the web server common gateway inteface: an `HTTP_` prefix is added, dashes are converted to underscores, and all letters are uppercased. Thus, to use the `X-Client-DN` header, this setting should be `HTTP_X_CLIENT_DN`.", }, :ssl_client_verify_header => { :default => "HTTP_X_CLIENT_VERIFY", :desc => "The header containing the status message of the client verification. This header must be set by the proxy to 'SUCCESS' if the client successfully authenticated, and anything else otherwise. Note that the name of the HTTP header gets munged by the web server common gateway inteface: an `HTTP_` prefix is added, dashes are converted to underscores, and all letters are uppercased. Thus, to use the `X-Client-Verify` header, this setting should be `HTTP_X_CLIENT_VERIFY`.", }, # To make sure this directory is created before we try to use it on the server, we need # it to be in the server section (#1138). :yamldir => { :default => "$vardir/yaml", :type => :directory, :owner => "service", :group => "service", :mode => "0750", :desc => "The directory in which YAML data is stored, usually in a subdirectory."}, :server_datadir => { :default => "$vardir/server_data", :type => :directory, :owner => "service", :group => "service", :mode => "0750", :desc => "The directory in which serialized data is stored, usually in a subdirectory."}, :reports => { :default => "store", :desc => "The list of report handlers to use. When using multiple report handlers, their names should be comma-separated, with whitespace allowed. (For example, `reports = http, store`.) This setting is relevant to puppet master and puppet apply. The puppet master will call these report handlers with the reports it receives from agent nodes, and puppet apply will call them with its own report. (In all cases, the node applying the catalog must have `report = true`.) See the report reference for information on the built-in report handlers; custom report handlers can also be loaded from modules. (Report handlers are loaded from the lib directory, at `puppet/reports/NAME.rb`.)", }, :reportdir => { :default => "$vardir/reports", :type => :directory, :mode => "0750", :owner => "service", :group => "service", :desc => "The directory in which to store reports. Each node gets a separate subdirectory in this directory. This setting is only used when the `store` report processor is enabled (see the `reports` setting)."}, :reporturl => { :default => "http://localhost:3000/reports/upload", :desc => "The URL that reports should be forwarded to. This setting is only used when the `http` report processor is enabled (see the `reports` setting).", }, :fileserverconfig => { :default => "$confdir/fileserver.conf", :type => :file, :desc => "Where the fileserver configuration is stored.", }, :strict_hostname_checking => { :default => false, :desc => "Whether to only search for the complete hostname as it is in the certificate when searching for node information in the catalogs.", } ) define_settings(:device, :devicedir => { :default => "$vardir/devices", :type => :directory, :mode => "0750", :desc => "The root directory of devices' $vardir.", }, :deviceconfig => { :default => "$confdir/device.conf", :desc => "Path to the device config file for puppet device.", } ) define_settings(:agent, :node_name_value => { :default => "$certname", :desc => "The explicit value used for the node name for all requests the agent makes to the master. WARNING: This setting is mutually exclusive with node_name_fact. Changing this setting also requires changes to the default auth.conf configuration on the Puppet Master. Please see http://links.puppetlabs.com/node_name_value for more information." }, :node_name_fact => { :default => "", :desc => "The fact name used to determine the node name used for all requests the agent makes to the master. WARNING: This setting is mutually exclusive with node_name_value. Changing this setting also requires changes to the default auth.conf configuration on the Puppet Master. Please see http://links.puppetlabs.com/node_name_fact for more information.", :hook => proc do |value| if !value.empty? and Puppet[:node_name_value] != Puppet[:certname] raise "Cannot specify both the node_name_value and node_name_fact settings" end end }, :statefile => { :default => "$statedir/state.yaml", :type => :file, :mode => "0660", :desc => "Where puppet agent and puppet master store state associated with the running configuration. In the case of puppet master, this file reflects the state discovered through interacting with clients." }, :clientyamldir => { :default => "$vardir/client_yaml", :type => :directory, :mode => "0750", :desc => "The directory in which client-side YAML data is stored." }, :client_datadir => { :default => "$vardir/client_data", :type => :directory, :mode => "0750", :desc => "The directory in which serialized data is stored on the client." }, :classfile => { :default => "$statedir/classes.txt", :type => :file, :owner => "root", :mode => "0640", :desc => "The file in which puppet agent stores a list of the classes associated with the retrieved configuration. Can be loaded in the separate `puppet` executable using the `--loadclasses` option."}, :resourcefile => { :default => "$statedir/resources.txt", :type => :file, :owner => "root", :mode => "0640", :desc => "The file in which puppet agent stores a list of the resources associated with the retrieved configuration." }, :puppetdlog => { :default => "$logdir/puppetd.log", :type => :file, :owner => "root", :mode => "0640", :desc => "The fallback log file. This is only used when the `--logdest` option is not specified AND Puppet is running on an operating system where both the POSIX syslog service and the Windows Event Log are unavailable. (Currently, no supported operating systems match that description.) Despite the name, both puppet agent and puppet master will use this file as the fallback logging destination. For control over logging destinations, see the `--logdest` command line option in the manual pages for puppet master, puppet agent, and puppet apply. You can see man pages by running `puppet --help`, or read them online at http://docs.puppetlabs.com/references/latest/man/." }, :server => { :default => "puppet", :desc => "The puppet master server to which the puppet agent should connect." }, :use_srv_records => { :default => false, :type => :boolean, :desc => "Whether the server will search for SRV records in DNS for the current domain.", }, :srv_domain => { :default => lambda { Puppet::Settings.domain_fact }, :desc => "The domain which will be queried to find the SRV records of servers to use.", }, :ignoreschedules => { :default => false, :type => :boolean, :desc => "Boolean; whether puppet agent should ignore schedules. This is useful for initial puppet agent runs.", }, :default_schedules => { :default => true, :type => :boolean, :desc => "Boolean; whether to generate the default schedule resources. Setting this to false is useful for keeping external report processors clean of skipped schedule resources.", }, :noop => { :default => false, :type => :boolean, :desc => "Whether to apply catalogs in noop mode, which allows Puppet to partially simulate a normal run. This setting affects puppet agent and puppet apply. When running in noop mode, Puppet will check whether each resource is in sync, like it does when running normally. However, if a resource attribute is not in the desired state (as declared in the catalog), Puppet will take no action, and will instead report the changes it _would_ have made. These simulated changes will appear in the report sent to the puppet master, or be shown on the console if running puppet agent or puppet apply in the foreground. The simulated changes will not send refresh events to any subscribing or notified resources, although Puppet will log that a refresh event _would_ have been sent. **Important note:** [The `noop` metaparameter](http://docs.puppetlabs.com/references/latest/metaparameter.html#noop) allows you to apply individual resources in noop mode, and will override the global value of the `noop` setting. This means a resource with `noop => false` _will_ be changed if necessary, even when running puppet agent with `noop = true` or `--noop`. (Conversely, a resource with `noop => true` will only be simulated, even when noop mode is globally disabled.)", }, :runinterval => { :default => "30m", :type => :duration, :desc => "How often puppet agent applies the catalog. Note that a runinterval of 0 means \"run continuously\" rather than \"never run.\" If you want puppet agent to never run, you should start it with the `--no-client` option. #{AS_DURATION}", }, :ca_server => { :default => "$server", :desc => "The server to use for certificate authority requests. It's a separate server because it cannot and does not need to horizontally scale.", }, :ca_port => { :default => "$masterport", :desc => "The port to use for the certificate authority.", }, :preferred_serialization_format => { :default => "pson", :desc => "The preferred means of serializing ruby instances for passing over the wire. This won't guarantee that all instances will be serialized using this method, since not all classes can be guaranteed to support this format, but it will be used for all classes that support it.", }, :agent_catalog_run_lockfile => { :default => "$statedir/agent_catalog_run.lock", :type => :string, # (#2888) Ensure this file is not added to the settings catalog. :desc => "A lock file to indicate that a puppet agent catalog run is currently in progress. The file contains the pid of the process that holds the lock on the catalog run.", }, :agent_disabled_lockfile => { :default => "$statedir/agent_disabled.lock", :type => :file, :desc => "A lock file to indicate that puppet agent runs have been administratively disabled. File contains a JSON object with state information.", }, :usecacheonfailure => { :default => true, :type => :boolean, :desc => "Whether to use the cached configuration when the remote configuration will not compile. This option is useful for testing new configurations, where you want to fix the broken configuration rather than reverting to a known-good one.", }, :use_cached_catalog => { :default => false, :type => :boolean, :desc => "Whether to only use the cached catalog rather than compiling a new catalog on every run. Puppet can be run with this enabled by default and then selectively disabled when a recompile is desired.", }, :ignoremissingtypes => { :default => false, :type => :boolean, :desc => "Skip searching for classes and definitions that were missing during a prior compilation. The list of missing objects is maintained per-environment and persists until the environment is cleared or the master is restarted.", }, :ignorecache => { :default => false, :type => :boolean, :desc => "Ignore cache and always recompile the configuration. This is useful for testing new configurations, where the local cache may in fact be stale even if the timestamps are up to date - if the facts change or if the server changes.", }, :splaylimit => { :default => "$runinterval", :type => :duration, :desc => "The maximum time to delay before runs. Defaults to being the same as the run interval. #{AS_DURATION}", }, :splay => { :default => false, :type => :boolean, :desc => "Whether to sleep for a pseudo-random (but consistent) amount of time before a run.", }, :clientbucketdir => { :default => "$vardir/clientbucket", :type => :directory, :mode => "0750", :desc => "Where FileBucket files are stored locally." }, :configtimeout => { :default => "2m", :type => :duration, :desc => "How long the client should wait for the configuration to be retrieved before considering it a failure. This can help reduce flapping if too many clients contact the server at one time. #{AS_DURATION}", }, :report_server => { :default => "$server", :desc => "The server to send transaction reports to.", }, :report_port => { :default => "$masterport", :desc => "The port to communicate with the report_server.", }, :report => { :default => true, :type => :boolean, :desc => "Whether to send reports after every transaction.", }, :lastrunfile => { :default => "$statedir/last_run_summary.yaml", :type => :file, :mode => "0644", :desc => "Where puppet agent stores the last run report summary in yaml format." }, :lastrunreport => { :default => "$statedir/last_run_report.yaml", :type => :file, :mode => "0640", :desc => "Where puppet agent stores the last run report in yaml format." }, :graph => { :default => false, :type => :boolean, :desc => "Whether to create dot graph files for the different configuration graphs. These dot files can be interpreted by tools like OmniGraffle or dot (which is part of ImageMagick).", }, :graphdir => { :default => "$statedir/graphs", :type => :directory, :desc => "Where to store dot-outputted graphs.", }, :waitforcert => { :default => "2m", :type => :duration, :desc => "How frequently puppet agent should ask for a signed certificate. When starting for the first time, puppet agent will submit a certificate signing request (CSR) to the server named in the `ca_server` setting (usually the puppet master); this may be autosigned, or may need to be approved by a human, depending on the CA server's configuration. Puppet agent cannot apply configurations until its approved certificate is available. Since the certificate may or may not be available immediately, puppet agent will repeatedly try to fetch it at this interval. You can turn off waiting for certificates by specifying a time of 0, in which case puppet agent will exit if it cannot get a cert. #{AS_DURATION}", }, :ordering => { :type => :enum, :values => ["manifest", "title-hash", "random"], :default => "manifest", :desc => "How unrelated resources should be ordered when applying a catalog. Allowed values are `title-hash`, `manifest`, and `random`. This setting affects puppet agent and puppet apply, but not puppet master. * `manifest` (the default) will use the order in which the resources were declared in their manifest files. * `title-hash` (the default in 3.x) will order resources randomly, but will use the same order across runs and across nodes. It is only of value if you're migrating from 3.x and have errors running with `manifest`. * `random` will order resources randomly and change their order with each run. This can work like a fuzzer for shaking out undeclared dependencies. Regardless of this setting's value, Puppet will always obey explicit dependencies set with the before/require/notify/subscribe metaparameters and the `->`/`~>` chaining arrows; this setting only affects the relative ordering of _unrelated_ resources." } ) define_settings(:inspect, :archive_files => { :type => :boolean, :default => false, :desc => "During an inspect run, whether to archive files whose contents are audited to a file bucket.", }, :archive_file_server => { :default => "$server", :desc => "During an inspect run, the file bucket server to archive files to if archive_files is set.", } ) # Plugin information. define_settings( :main, :plugindest => { :type => :directory, :default => "$libdir", :desc => "Where Puppet should store plugins that it pulls down from the central server.", }, :pluginsource => { :default => "puppet://$server/plugins", :desc => "From where to retrieve plugins. The standard Puppet `file` type is used for retrieval, so anything that is a valid file source can be used here.", }, :pluginfactdest => { :type => :directory, :default => "$vardir/facts.d", :desc => "Where Puppet should store external facts that are being handled by pluginsync", }, :pluginfactsource => { :default => "puppet://$server/pluginfacts", :desc => "Where to retrieve external facts for pluginsync", }, :pluginsync => { :default => true, :type => :boolean, :desc => "Whether plugins should be synced with the central server.", }, :pluginsignore => { :default => ".svn CVS .git", :desc => "What files to ignore when pulling down plugins.", } ) # Central fact information. define_settings( :main, :factpath => { :type => :path, :default => "$vardir/lib/facter#{File::PATH_SEPARATOR}$vardir/facts", :desc => "Where Puppet should look for facts. Multiple directories should be separated by the system path separator character. (The POSIX path separator is ':', and the Windows path separator is ';'.)", :call_hook => :on_initialize_and_write, # Call our hook with the default value, so we always get the value added to facter. :hook => proc do |value| paths = value.split(File::PATH_SEPARATOR) Facter.search(*paths) end } ) define_settings( :rails, :dblocation => { :default => "$statedir/clientconfigs.sqlite3", :type => :file, :mode => "0660", :owner => "service", :group => "service", :desc => "The sqlite database file. #{STORECONFIGS_ONLY}" }, :dbadapter => { :default => "sqlite3", :desc => "The type of database to use. #{STORECONFIGS_ONLY}", }, :dbmigrate => { :default => false, :type => :boolean, :desc => "Whether to automatically migrate the database. #{STORECONFIGS_ONLY}", }, :dbname => { :default => "puppet", :desc => "The name of the database to use. #{STORECONFIGS_ONLY}", }, :dbserver => { :default => "localhost", :desc => "The database server for caching. Only used when networked databases are used.", }, :dbport => { :default => "", :desc => "The database password for caching. Only used when networked databases are used. #{STORECONFIGS_ONLY}", }, :dbuser => { :default => "puppet", :desc => "The database user for caching. Only used when networked databases are used. #{STORECONFIGS_ONLY}", }, :dbpassword => { :default => "puppet", :desc => "The database password for caching. Only used when networked databases are used. #{STORECONFIGS_ONLY}", }, :dbconnections => { :default => '', :desc => "The number of database connections for networked databases. Will be ignored unless the value is a positive integer. #{STORECONFIGS_ONLY}", }, :dbsocket => { :default => "", :desc => "The database socket location. Only used when networked databases are used. Will be ignored if the value is an empty string. #{STORECONFIGS_ONLY}", }, :railslog => { :default => "$logdir/rails.log", :type => :file, :mode => "0600", :owner => "service", :group => "service", :desc => "Where Rails-specific logs are sent. #{STORECONFIGS_ONLY}" }, :rails_loglevel => { :default => "info", :desc => "The log level for Rails connections. The value must be a valid log level within Rails. Production environments normally use `info` and other environments normally use `debug`. #{STORECONFIGS_ONLY}", } ) define_settings( :transaction, :tags => { :default => "", :desc => "Tags to use to find resources. If this is set, then only resources tagged with the specified tags will be applied. Values must be comma-separated.", }, :evaltrace => { :default => false, :type => :boolean, :desc => "Whether each resource should log when it is being evaluated. This allows you to interactively see exactly what is being done.", }, :summarize => { :default => false, :type => :boolean, :desc => "Whether to print a transaction summary.", } ) define_settings( :main, :external_nodes => { :default => "none", :desc => "An external command that can produce node information. The command's output must be a YAML dump of a hash, and that hash must have a `classes` key and/or a `parameters` key, where `classes` is an array or hash and `parameters` is a hash. For unknown nodes, the command should exit with a non-zero exit code. This command makes it straightforward to store your node mapping information in other data sources like databases.", } ) define_settings( :ldap, :ldapssl => { :default => false, :type => :boolean, :desc => "Whether SSL should be used when searching for nodes. Defaults to false because SSL usually requires certificates to be set up on the client side.", }, :ldaptls => { :default => false, :type => :boolean, :desc => "Whether TLS should be used when searching for nodes. Defaults to false because TLS usually requires certificates to be set up on the client side.", }, :ldapserver => { :default => "ldap", :desc => "The LDAP server. Only used if `node_terminus` is set to `ldap`.", }, :ldapport => { :default => 389, :desc => "The LDAP port. Only used if `node_terminus` is set to `ldap`.", }, :ldapstring => { :default => "(&(objectclass=puppetClient)(cn=%s))", :desc => "The search string used to find an LDAP node.", }, :ldapclassattrs => { :default => "puppetclass", :desc => "The LDAP attributes to use to define Puppet classes. Values should be comma-separated.", }, :ldapstackedattrs => { :default => "puppetvar", :desc => "The LDAP attributes that should be stacked to arrays by adding the values in all hierarchy elements of the tree. Values should be comma-separated.", }, :ldapattrs => { :default => "all", :desc => "The LDAP attributes to include when querying LDAP for nodes. All returned attributes are set as variables in the top-level scope. Multiple values should be comma-separated. The value 'all' returns all attributes.", }, :ldapparentattr => { :default => "parentnode", :desc => "The attribute to use to define the parent node.", }, :ldapuser => { :default => "", :desc => "The user to use to connect to LDAP. Must be specified as a full DN.", }, :ldappassword => { :default => "", :desc => "The password to use to connect to LDAP.", }, :ldapbase => { :default => "", :desc => "The search base for LDAP searches. It's impossible to provide a meaningful default here, although the LDAP libraries might have one already set. Generally, it should be the 'ou=Hosts' branch under your main directory.", } ) define_settings(:master, :storeconfigs => { :default => false, :type => :boolean, :desc => "Whether to store each client's configuration, including catalogs, facts, and related data. This also enables the import and export of resources in the Puppet language - a mechanism for exchange resources between nodes. By default this uses ActiveRecord and an SQL database to store and query the data; this, in turn, will depend on Rails being available. You can adjust the backend using the storeconfigs_backend setting.", # Call our hook with the default value, so we always get the libdir set. :call_hook => :on_initialize_and_write, :hook => proc do |value| require 'puppet/node' require 'puppet/node/facts' if value Puppet::Resource::Catalog.indirection.cache_class = :store_configs Puppet.settings.override_default(:catalog_cache_terminus, :store_configs) Puppet::Node::Facts.indirection.cache_class = :store_configs Puppet::Resource.indirection.terminus_class = :store_configs end end }, :storeconfigs_backend => { :type => :terminus, :default => "active_record", :desc => "Configure the backend terminus used for StoreConfigs. By default, this uses the ActiveRecord store, which directly talks to the database from within the Puppet Master process." } ) define_settings(:parser, :templatedir => { :default => "$vardir/templates", :type => :directory, :desc => "Where Puppet looks for template files. Can be a list of colon-separated directories. This setting is deprecated. Please put your templates in modules instead.", :deprecated => :completely, }, :allow_variables_with_dashes => { :default => false, :desc => <<-'EOT' Permit hyphens (`-`) in variable names and issue deprecation warnings about them. This setting **should always be `false`;** setting it to `true` will cause subtle and wide-ranging bugs. It will be removed in a future version. Hyphenated variables caused major problems in the language, but were allowed between Puppet 2.7.3 and 2.7.14. If you used them during this window, we apologize for the inconvenience --- you can temporarily set this to `true` in order to upgrade, and can rename your variables at your leisure. Please revert it to `false` after you have renamed all affected variables. EOT }, :parser => { :default => "current", :desc => <<-'EOT' Selects the parser to use for parsing puppet manifests (in puppet DSL language/'.pp' files). Available choices are `current` (the default) and `future`. The `current` parser means that the released version of the parser should be used. The `future` parser is a "time travel to the future" allowing early exposure to new language features. What these features are will vary from release to release and they may be invididually configurable. Available Since Puppet 3.2. EOT }, :max_errors => { :default => 10, :desc => <<-'EOT' Sets the max number of logged/displayed parser validation errors in case multiple errors have been detected. A value of 0 is the same as a value of 1; a minimum of one error is always raised. The count is per manifest. EOT }, :max_warnings => { :default => 10, :desc => <<-'EOT' Sets the max number of logged/displayed parser validation warnings in case multiple warnings have been detected. A value of 0 blocks logging of warnings. The count is per manifest. EOT }, :max_deprecations => { :default => 10, :desc => <<-'EOT' Sets the max number of logged/displayed parser validation deprecation warnings in case multiple deprecation warnings have been detected. A value of 0 blocks the logging of deprecation warnings. The count is per manifest. EOT }, :strict_variables => { :default => false, :type => :boolean, :desc => <<-'EOT' Makes the parser raise errors when referencing unknown variables. (This does not affect referencing variables that are explicitly set to undef). EOT } ) define_settings(:puppetdoc, :document_all => { :default => false, :type => :boolean, :desc => "Whether to document all resources when using `puppet doc` to generate manifest documentation.", } ) end diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb index 803f66de7..9be902ffe 100644 --- a/lib/puppet/parser/compiler.rb +++ b/lib/puppet/parser/compiler.rb @@ -1,628 +1,624 @@ require 'forwardable' require 'puppet/node' require 'puppet/resource/catalog' require 'puppet/util/errors' require 'puppet/resource/type_collection_helper' # Maintain a graph of scopes, along with a bunch of data # about the individual catalog we're compiling. class Puppet::Parser::Compiler extend Forwardable include Puppet::Util include Puppet::Util::Errors include Puppet::Util::MethodHelper include Puppet::Resource::TypeCollectionHelper def self.compile(node) $env_module_directories = nil node.environment.check_for_reparse errors = node.environment.validation_errors if !errors.empty? errors.each { |e| Puppet.err(e) } if errors.size > 1 errmsg = [ "Compilation has been halted because: #{errors.first}", "For more information, see http://docs.puppetlabs.com/puppet/latest/reference/environments.html", ] raise(Puppet::Error, errmsg.join(' ')) end new(node).compile.to_resource rescue => detail message = "#{detail} on node #{node.name}" Puppet.log_exception(detail, message) raise Puppet::Error, message, detail.backtrace end attr_reader :node, :facts, :collections, :catalog, :resources, :relationships, :topscope # The injector that provides lookup services, or nil if accessed before the compiler has started compiling and # bootstrapped. The injector is initialized and available before any manifests are evaluated. # # @return [Puppet::Pops::Binder::Injector, nil] The injector that provides lookup services for this compiler/environment # @api public # attr_accessor :injector # Access to the configured loaders for 4x # @return [Puppet::Pops::Loader::Loaders] the configured loaders # @api private attr_reader :loaders # The injector that provides lookup services during the creation of the {#injector}. # @return [Puppet::Pops::Binder::Injector, nil] The injector that provides lookup services during injector creation # for this compiler/environment # # @api private # attr_accessor :boot_injector # Add a collection to the global list. def_delegator :@collections, :<<, :add_collection def_delegator :@relationships, :<<, :add_relationship # Store a resource override. def add_override(override) # If possible, merge the override in immediately. if resource = @catalog.resource(override.ref) resource.merge(override) else # Otherwise, store the override for later; these # get evaluated in Resource#finish. @resource_overrides[override.ref] << override end end def add_resource(scope, resource) @resources << resource # Note that this will fail if the resource is not unique. @catalog.add_resource(resource) if not resource.class? and resource[:stage] raise ArgumentError, "Only classes can set 'stage'; normal resources like #{resource} cannot change run stage" end # Stages should not be inside of classes. They are always a # top-level container, regardless of where they appear in the # manifest. return if resource.stage? # This adds a resource to the class it lexically appears in in the # manifest. unless resource.class? return @catalog.add_edge(scope.resource, resource) end end # Do we use nodes found in the code, vs. the external node sources? def_delegator :known_resource_types, :nodes?, :ast_nodes? # Store the fact that we've evaluated a class def add_class(name) @catalog.add_class(name) unless name == "" end # Return a list of all of the defined classes. def_delegator :@catalog, :classes, :classlist # Compiler our catalog. This mostly revolves around finding and evaluating classes. # This is the main entry into our catalog. def compile Puppet.override( @context_overrides , "For compiling #{node.name}") do @catalog.environment_instance = environment # Set the client's parameters into the top scope. Puppet::Util::Profiler.profile("Compile: Set node parameters", [:compiler, :set_node_params]) { set_node_parameters } Puppet::Util::Profiler.profile("Compile: Created settings scope", [:compiler, :create_settings_scope]) { create_settings_scope } if is_binder_active? # create injector, if not already created - this is for 3x that does not trigger # lazy loading of injector via context Puppet::Util::Profiler.profile("Compile: Created injector", [:compiler, :create_injector]) { injector } end Puppet::Util::Profiler.profile("Compile: Evaluated main", [:compiler, :evaluate_main]) { evaluate_main } Puppet::Util::Profiler.profile("Compile: Evaluated AST node", [:compiler, :evaluate_ast_node]) { evaluate_ast_node } Puppet::Util::Profiler.profile("Compile: Evaluated node classes", [:compiler, :evaluate_node_classes]) { evaluate_node_classes } Puppet::Util::Profiler.profile("Compile: Evaluated generators", [:compiler, :evaluate_generators]) { evaluate_generators } Puppet::Util::Profiler.profile("Compile: Finished catalog", [:compiler, :finish_catalog]) { finish } fail_on_unevaluated @catalog end end # Constructs the overrides for the context def context_overrides() if Puppet[:parser] == 'future' require 'puppet/loaders' { :current_environment => environment, :global_scope => @topscope, # 4x placeholder for new global scope :loaders => lambda {|| loaders() }, # 4x loaders :injector => lambda {|| injector() } # 4x API - via context instead of via compiler } else { :current_environment => environment, } end end def_delegator :@collections, :delete, :delete_collection # Return the node's environment. def environment node.environment end # Evaluate all of the classes specified by the node. # Classes with parameters are evaluated as if they were declared. # Classes without parameters or with an empty set of parameters are evaluated # as if they were included. This means classes with an empty set of # parameters won't conflict even if the class has already been included. def evaluate_node_classes if @node.classes.is_a? Hash classes_with_params, classes_without_params = @node.classes.partition {|name,params| params and !params.empty?} # The results from Hash#partition are arrays of pairs rather than hashes, # so we have to convert to the forms evaluate_classes expects (Hash, and # Array of class names) classes_with_params = Hash[classes_with_params] classes_without_params.map!(&:first) else classes_with_params = {} classes_without_params = @node.classes end evaluate_classes(classes_with_params, @node_scope || topscope) evaluate_classes(classes_without_params, @node_scope || topscope) end # Evaluate each specified class in turn. If there are any classes we can't # find, raise an error. This method really just creates resource objects # that point back to the classes, and then the resources are themselves # evaluated later in the process. # # Sometimes we evaluate classes with a fully qualified name already, in which # case, we tell scope.find_hostclass we've pre-qualified the name so it # doesn't need to search its namespaces again. This gets around a weird # edge case of duplicate class names, one at top scope and one nested in our # namespace and the wrong one (or both!) getting selected. See ticket #13349 # for more detail. --jeffweiss 26 apr 2012 def evaluate_classes(classes, scope, lazy_evaluate = true, fqname = false) raise Puppet::DevError, "No source for scope passed to evaluate_classes" unless scope.source class_parameters = nil # if we are a param class, save the classes hash # and transform classes to be the keys if classes.class == Hash class_parameters = classes classes = classes.keys end hostclasses = classes.collect do |name| scope.find_hostclass(name, :assume_fqname => fqname) or raise Puppet::Error, "Could not find class #{name} for #{node.name}" end if class_parameters resources = ensure_classes_with_parameters(scope, hostclasses, class_parameters) if !lazy_evaluate resources.each(&:evaluate) end resources else already_included, newly_included = ensure_classes_without_parameters(scope, hostclasses) if !lazy_evaluate newly_included.each(&:evaluate) end already_included + newly_included end end def evaluate_relationships @relationships.each { |rel| rel.evaluate(catalog) } end # Return a resource by either its ref or its type and title. def_delegator :@catalog, :resource, :findresource def initialize(node, options = {}) @node = node set_options(options) initvars end # Create a new scope, with either a specified parent scope or # using the top scope. def newscope(parent, options = {}) parent ||= topscope scope = Puppet::Parser::Scope.new(self, options) scope.parent = parent scope end # Return any overrides for the given resource. def resource_overrides(resource) @resource_overrides[resource.ref] end def injector create_injector if @injector.nil? @injector end def loaders @loaders ||= Puppet::Pops::Loaders.new(environment) end def boot_injector create_boot_injector(nil) if @boot_injector.nil? @boot_injector end # Creates the boot injector from registered system, default, and injector config. # @return [Puppet::Pops::Binder::Injector] the created boot injector # @api private Cannot be 'private' since it is called from the BindingsComposer. # def create_boot_injector(env_boot_bindings) assert_binder_active() pb = Puppet::Pops::Binder boot_contribution = pb::SystemBindings.injector_boot_contribution(env_boot_bindings) final_contribution = pb::SystemBindings.final_contribution binder = pb::Binder.new(pb::BindingsFactory.layered_bindings(final_contribution, boot_contribution)) @boot_injector = pb::Injector.new(binder) end # Answers if Puppet Binder should be active or not, and if it should and is not active, then it is activated. # @return [Boolean] true if the Puppet Binder should be activated def is_binder_active? should_be_active = Puppet[:binder] || Puppet[:parser] == 'future' if should_be_active # TODO: this should be in a central place, not just for ParserFactory anymore... Puppet::Parser::ParserFactory.assert_rgen_installed() @@binder_loaded ||= false unless @@binder_loaded require 'puppet/pops' require 'puppet/plugins/configuration' @@binder_loaded = true end end should_be_active end private def ensure_classes_with_parameters(scope, hostclasses, parameters) hostclasses.collect do |klass| klass.ensure_in_catalog(scope, parameters[klass.name] || {}) end end def ensure_classes_without_parameters(scope, hostclasses) already_included = [] newly_included = [] hostclasses.each do |klass| class_scope = scope.class_scope(klass) if class_scope already_included << class_scope.resource else newly_included << klass.ensure_in_catalog(scope) end end [already_included, newly_included] end # If ast nodes are enabled, then see if we can find and evaluate one. def evaluate_ast_node return unless ast_nodes? # Now see if we can find the node. astnode = nil @node.names.each do |name| break if astnode = known_resource_types.node(name.to_s.downcase) end unless (astnode ||= known_resource_types.node("default")) raise Puppet::ParseError, "Could not find default node or by name with '#{node.names.join(", ")}'" end # Create a resource to model this node, and then add it to the list # of resources. resource = astnode.ensure_in_catalog(topscope) resource.evaluate @node_scope = topscope.class_scope(astnode) end # Evaluate our collections and return true if anything returned an object. # The 'true' is used to continue a loop, so it's important. def evaluate_collections return false if @collections.empty? exceptwrap do # We have to iterate over a dup of the array because # collections can delete themselves from the list, which # changes its length and causes some collections to get missed. Puppet::Util::Profiler.profile("Evaluated collections", [:compiler, :evaluate_collections]) do found_something = false @collections.dup.each do |collection| found_something = true if collection.evaluate end found_something end end end # Make sure all of our resources have been evaluated into native resources. # We return true if any resources have, so that we know to continue the # evaluate_generators loop. def evaluate_definitions exceptwrap do Puppet::Util::Profiler.profile("Evaluated definitions", [:compiler, :evaluate_definitions]) do !unevaluated_resources.each do |resource| Puppet::Util::Profiler.profile("Evaluated resource #{resource}", [:compiler, :evaluate_resource, resource]) do resource.evaluate end end.empty? end end end # Iterate over collections and resources until we're sure that the whole # compile is evaluated. This is necessary because both collections # and defined resources can generate new resources, which themselves could # be defined resources. def evaluate_generators count = 0 loop do done = true Puppet::Util::Profiler.profile("Iterated (#{count + 1}) on generators", [:compiler, :iterate_on_generators]) do # Call collections first, then definitions. done = false if evaluate_collections done = false if evaluate_definitions end break if done count += 1 if count > 1000 raise Puppet::ParseError, "Somehow looped more than 1000 times while evaluating host catalog" end end end # Find and evaluate our main object, if possible. def evaluate_main @main = known_resource_types.find_hostclass([""], "") || known_resource_types.add(Puppet::Resource::Type.new(:hostclass, "")) @topscope.source = @main @main_resource = Puppet::Parser::Resource.new("class", :main, :scope => @topscope, :source => @main) @topscope.resource = @main_resource add_resource(@topscope, @main_resource) @main_resource.evaluate end # Make sure the entire catalog is evaluated. def fail_on_unevaluated fail_on_unevaluated_overrides fail_on_unevaluated_resource_collections end # If there are any resource overrides remaining, then we could # not find the resource they were supposed to override, so we # want to throw an exception. def fail_on_unevaluated_overrides remaining = @resource_overrides.values.flatten.collect(&:ref) if !remaining.empty? fail Puppet::ParseError, "Could not find resource(s) #{remaining.join(', ')} for overriding" end end # Make sure we don't have any remaining collections that specifically # look for resources, because we want to consider those to be # parse errors. def fail_on_unevaluated_resource_collections if Puppet[:parser] == 'future' remaining = @collections.collect(&:unresolved_resources).flatten.compact else remaining = @collections.collect(&:resources).flatten.compact end if !remaining.empty? raise Puppet::ParseError, "Failed to realize virtual resources #{remaining.join(', ')}" end end # Make sure all of our resources and such have done any last work # necessary. def finish evaluate_relationships resources.each do |resource| # Add in any resource overrides. if overrides = resource_overrides(resource) overrides.each do |over| resource.merge(over) end # Remove the overrides, so that the configuration knows there # are none left. overrides.clear end resource.finish if resource.respond_to?(:finish) end add_resource_metaparams end def add_resource_metaparams unless main = catalog.resource(:class, :main) raise "Couldn't find main" end names = Puppet::Type.metaparams.select do |name| !Puppet::Parser::Resource.relationship_parameter?(name) end data = {} catalog.walk(main, :out) do |source, target| if source_data = data[source] || metaparams_as_data(source, names) # only store anything in the data hash if we've actually got # data data[source] ||= source_data source_data.each do |param, value| target[param] = value if target[param].nil? end data[target] = source_data.merge(metaparams_as_data(target, names)) end target.tag(*(source.tags)) end end def metaparams_as_data(resource, params) data = nil params.each do |param| unless resource[param].nil? # Because we could be creating a hash for every resource, # and we actually probably don't often have any data here at all, # we're optimizing a bit by only creating a hash if there's # any data to put in it. data ||= {} data[param] = resource[param] end end data end # Set up all of our internal variables. def initvars # The list of overrides. This is used to cache overrides on objects # that don't exist yet. We store an array of each override. @resource_overrides = Hash.new do |overs, ref| overs[ref] = [] end # The list of collections that have been created. This is a global list, # but they each refer back to the scope that created them. @collections = [] # The list of relationships to evaluate. @relationships = [] # For maintaining the relationship between scopes and their resources. @catalog = Puppet::Resource::Catalog.new(@node.name, @node.environment) # MOVED HERE - SCOPE IS NEEDED (MOVE-SCOPE) # Create the initial scope, it is needed early @topscope = Puppet::Parser::Scope.new(self) # Need to compute overrides here, and remember them, because we are about to # enter the magic zone of known_resource_types and initial import. # Expensive entries in the context are bound lazily. @context_overrides = context_overrides() # This construct ensures that initial import (triggered by instantiating # the structure 'known_resource_types') has a configured context # It cannot survive the initvars method, and is later reinstated # as part of compiling... # Puppet.override( @context_overrides , "For initializing compiler") do # THE MAGIC STARTS HERE ! This triggers parsing, loading etc. @catalog.version = known_resource_types.version end @catalog.add_resource(Puppet::Parser::Resource.new("stage", :main, :scope => @topscope)) # local resource array to maintain resource ordering @resources = [] # Make sure any external node classes are in our class list if @node.classes.class == Hash @catalog.add_class(*@node.classes.keys) else @catalog.add_class(*@node.classes) end end # Set the node's parameters into the top-scope as variables. def set_node_parameters node.parameters.each do |param, value| @topscope[param.to_s] = value end # These might be nil. catalog.client_version = node.parameters["clientversion"] catalog.server_version = node.parameters["serverversion"] - if Puppet[:trusted_node_data] - @topscope.set_trusted(node.trusted_data) - end - if(Puppet[:immutable_node_data]) - facts_hash = node.facts.nil? ? {} : node.facts.values - @topscope.set_facts(facts_hash) - end + @topscope.set_trusted(node.trusted_data) + facts_hash = node.facts.nil? ? {} : node.facts.values + @topscope.set_facts(facts_hash) end def create_settings_scope settings_type = Puppet::Resource::Type.new :hostclass, "settings" environment.known_resource_types.add(settings_type) settings_resource = Puppet::Parser::Resource.new("class", "settings", :scope => @topscope) @catalog.add_resource(settings_resource) settings_type.evaluate_code(settings_resource) scope = @topscope.class_scope(settings_type) env = environment Puppet.settings.each do |name, setting| next if name == :name scope[name.to_s] = env[name] end end # Return an array of all of the unevaluated resources. These will be definitions, # which need to get evaluated into native resources. def unevaluated_resources # The order of these is significant for speed due to short-circuting resources.reject { |resource| resource.evaluated? or resource.virtual? or resource.builtin_type? } end # Creates the injector from bindings found in the current environment. # @return [void] # @api private # def create_injector assert_binder_active() composer = Puppet::Pops::Binder::BindingsComposer.new() layered_bindings = composer.compose(topscope) @injector = Puppet::Pops::Binder::Injector.new(Puppet::Pops::Binder::Binder.new(layered_bindings)) end def assert_binder_active unless is_binder_active? raise ArgumentError, "The Puppet Binder is only available when either '--binder true' or '--parser future' is used" end end end diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index 6d8b49edc..342de8a2b 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -1,902 +1,900 @@ # The scope class, which handles storing and retrieving variables and types and # such. require 'forwardable' require 'puppet/parser' require 'puppet/parser/templatewrapper' require 'puppet/resource/type_collection_helper' require 'puppet/util/methodhelper' # This class is part of the internal parser/evaluator/compiler functionality of Puppet. # It is passed between the various classes that participate in evaluation. # None of its methods are API except those that are clearly marked as such. # # @api public class Puppet::Parser::Scope extend Forwardable include Puppet::Util::MethodHelper include Puppet::Resource::TypeCollectionHelper require 'puppet/parser/resource' AST = Puppet::Parser::AST Puppet::Util.logmethods(self) include Puppet::Util::Errors attr_accessor :source, :resource attr_accessor :compiler attr_accessor :parent attr_reader :namespaces # Hash of hashes of default values per type name attr_reader :defaults # Add some alias methods that forward to the compiler, since we reference # them frequently enough to justify the extra method call. def_delegators :compiler, :catalog, :environment # Abstract base class for LocalScope and MatchScope # class Ephemeral attr_reader :parent def initialize(parent = nil) @parent = parent end def is_local_scope? false end def [](name) if @parent @parent[name] end end def include?(name) (@parent and @parent.include?(name)) end def bound?(name) false end def add_entries_to(target = {}) @parent.add_entries_to(target) unless @parent.nil? # do not include match data ($0-$n) target end end class LocalScope < Ephemeral def initialize(parent=nil) super parent @symbols = {} end def [](name) if @symbols.include?(name) @symbols[name] else super end end def is_local_scope? true end def []=(name, value) @symbols[name] = value end def include?(name) bound?(name) || super end def delete(name) @symbols.delete(name) end def bound?(name) @symbols.include?(name) end def add_entries_to(target = {}) super @symbols.each do |k, v| if v == :undef || v.nil? target.delete(k) else target[ k ] = v end end target end end class MatchScope < Ephemeral attr_accessor :match_data def initialize(parent = nil, match_data = nil) super parent @match_data = match_data end def is_local_scope? false end def [](name) if bound?(name) @match_data[name.to_i] else super end end def include?(name) bound?(name) or super end def bound?(name) # A "match variables" scope reports all numeric variables to be bound if the scope has # match_data. Without match data the scope is transparent. # @match_data && name =~ /^\d+$/ end def []=(name, value) # TODO: Bad choice of exception raise Puppet::ParseError, "Numerical variables cannot be changed. Attempt to set $#{name}" end def delete(name) # TODO: Bad choice of exception raise Puppet::ParseError, "Numerical variables cannot be deleted: Attempt to delete: $#{name}" end def add_entries_to(target = {}) # do not include match data ($0-$n) super end end # Returns true if the variable of the given name has a non nil value. # TODO: This has vague semantics - does the variable exist or not? # use ['name'] to get nil or value, and if nil check with exist?('name') # this include? is only useful because of checking against the boolean value false. # def include?(name) ! self[name].nil? end # Returns true if the variable of the given name is set to any value (including nil) # def exist?(name) next_scope = inherited_scope || enclosing_scope effective_symtable(true).include?(name) || next_scope && next_scope.exist?(name) end # Returns true if the given name is bound in the current (most nested) scope for assignments. # def bound?(name) # Do not look in ephemeral (match scope), the semantics is to answer if an assignable variable is bound effective_symtable(false).bound?(name) end # Is the value true? This allows us to control the definition of truth # in one place. def self.true?(value) case value when '' false when :undef false else !!value end end # Coerce value to a number, or return `nil` if it isn't one. def self.number?(value) case value when Numeric value when /^-?\d+(:?\.\d+|(:?\.\d+)?e\d+)$/ value.to_f when /^0x[0-9a-f]+$/i value.to_i(16) when /^0[0-7]+$/ value.to_i(8) when /^-?\d+$/ value.to_i else nil end end # Add to our list of namespaces. def add_namespace(ns) return false if @namespaces.include?(ns) if @namespaces == [""] @namespaces = [ns] else @namespaces << ns end end def find_hostclass(name, options = {}) known_resource_types.find_hostclass(namespaces, name, options) end def find_definition(name) known_resource_types.find_definition(namespaces, name) end def find_global_scope() # walk upwards until first found node_scope or top_scope if is_nodescope? || is_topscope? self else next_scope = inherited_scope || enclosing_scope if next_scope.nil? # this happens when testing, and there is only a single test scope and no link to any # other scopes self else next_scope.find_global_scope() end end end # This just delegates directly. def_delegator :compiler, :findresource # Initialize our new scope. Defaults to having no parent. def initialize(compiler, options = {}) if compiler.is_a? Puppet::Parser::Compiler self.compiler = compiler else raise Puppet::DevError, "you must pass a compiler instance to a new scope object" end if n = options.delete(:namespace) @namespaces = [n] else @namespaces = [""] end raise Puppet::DevError, "compiler passed in options" if options.include? :compiler set_options(options) extend_with_functions_module # The symbol table for this scope. This is where we store variables. # @symtable = Ephemeral.new(nil, true) @symtable = LocalScope.new(nil) @ephemeral = [ MatchScope.new(@symtable, nil) ] # All of the defaults set for types. It's a hash of hashes, # with the first key being the type, then the second key being # the parameter. @defaults = Hash.new { |dhash,type| dhash[type] = {} } # The table for storing class singletons. This will only actually # be used by top scopes and node scopes. @class_scopes = {} - - @enable_immutable_data = Puppet[:immutable_node_data] end # Store the fact that we've evaluated a class, and store a reference to # the scope in which it was evaluated, so that we can look it up later. def class_set(name, scope) if parent parent.class_set(name, scope) else @class_scopes[name] = scope end end # Return the scope associated with a class. This is just here so # that subclasses can set their parent scopes to be the scope of # their parent class, and it's also used when looking up qualified # variables. def class_scope(klass) # They might pass in either the class or class name k = klass.respond_to?(:name) ? klass.name : klass @class_scopes[k] || (parent && parent.class_scope(k)) end # Collect all of the defaults set at any higher scopes. # This is a different type of lookup because it's # additive -- it collects all of the defaults, with defaults # in closer scopes overriding those in later scopes. # # The lookupdefaults searches in the the order: # # * inherited # * contained (recursive) # * self # def lookupdefaults(type) values = {} # first collect the values from the parents if parent parent.lookupdefaults(type).each { |var,value| values[var] = value } end # then override them with any current values # this should probably be done differently if @defaults.include?(type) @defaults[type].each { |var,value| values[var] = value } end values end # Look up a defined type. def lookuptype(name) find_definition(name) || find_hostclass(name) end def undef_as(x,v) if v.nil? or v == :undef x else v end end # Lookup a variable within this scope using the Puppet language's # scoping rules. Variables can be qualified using just as in a # manifest. # # @param [String] name the variable name to lookup # # @return Object the value of the variable, or nil if it's not found # # @api public def lookupvar(name, options = {}) unless name.is_a? String raise Puppet::ParseError, "Scope variable name #{name.inspect} is a #{name.class}, not a string" end table = @ephemeral.last if name =~ /^(.*)::(.+)$/ class_name = $1 variable_name = $2 lookup_qualified_variable(class_name, variable_name, options) # TODO: optimize with an assoc instead, this searches through scopes twice for a hit elsif table.include?(name) table[name] else next_scope = inherited_scope || enclosing_scope if next_scope next_scope.lookupvar(name, options) else variable_not_found(name) end end end def variable_not_found(name, reason=nil) if Puppet[:strict_variables] if Puppet[:parser] == 'future' throw :undefined_variable else reason_msg = reason.nil? ? '' : "; #{reason}" raise Puppet::ParseError, "Undefined variable #{name.inspect}#{reason_msg}" end else nil end end # Retrieves the variable value assigned to the name given as an argument. The name must be a String, # and namespace can be qualified with '::'. The value is looked up in this scope, its parent scopes, # or in a specific visible named scope. # # @param varname [String] the name of the variable (may be a qualified name using `(ns'::')*varname` # @param options [Hash] Additional options, not part of api. # @return [Object] the value assigned to the given varname # @see #[]= # @api public # def [](varname, options={}) lookupvar(varname, options) end # The scope of the inherited thing of this scope's resource. This could # either be a node that was inherited or the class. # # @return [Puppet::Parser::Scope] The scope or nil if there is not an inherited scope def inherited_scope if has_inherited_class? qualified_scope(resource.resource_type.parent) else nil end end # The enclosing scope (topscope or nodescope) of this scope. # The enclosing scopes are produced when a class or define is included at # some point. The parent scope of the included class or define becomes the # scope in which it was included. The chain of parent scopes is followed # until a node scope or the topscope is found # # @return [Puppet::Parser::Scope] The scope or nil if there is no enclosing scope def enclosing_scope if has_enclosing_scope? if parent.is_topscope? or parent.is_nodescope? parent else parent.enclosing_scope end else nil end end def is_classscope? resource and resource.type == "Class" end def is_nodescope? resource and resource.type == "Node" end def is_topscope? compiler and self == compiler.topscope end def lookup_qualified_variable(class_name, variable_name, position) begin if lookup_as_local_name?(class_name, variable_name) self[variable_name] else qualified_scope(class_name).lookupvar(variable_name, position) end rescue RuntimeError => e unless Puppet[:strict_variables] # Do not issue warning if strict variables are on, as an error will be raised by variable_not_found location = if position[:lineproc] " at #{position[:lineproc].call}" elsif position[:file] && position[:line] " at #{position[:file]}:#{position[:line]}" else "" end warning "Could not look up qualified variable '#{class_name}::#{variable_name}'; #{e.message}#{location}" end variable_not_found("#{class_name}::#{variable_name}", e.message) end end # Handles the special case of looking up fully qualified variable in not yet evaluated top scope # This is ok if the lookup request originated in topscope (this happens when evaluating # bindings; using the top scope to provide the values for facts. # @param class_name [String] the classname part of a variable name, may be special "" # @param variable_name [String] the variable name without the absolute leading '::' # @return [Boolean] true if the given variable name should be looked up directly in this scope # def lookup_as_local_name?(class_name, variable_name) # not a local if name has more than one segment return nil if variable_name =~ /::/ # partial only if the class for "" cannot be found return nil unless class_name == "" && klass = find_hostclass(class_name) && class_scope(klass).nil? is_topscope? end def has_inherited_class? is_classscope? and resource.resource_type.parent end private :has_inherited_class? def has_enclosing_scope? not parent.nil? end private :has_enclosing_scope? def qualified_scope(classname) raise "class #{classname} could not be found" unless klass = find_hostclass(classname) raise "class #{classname} has not been evaluated" unless kscope = class_scope(klass) kscope end private :qualified_scope # Returns a Hash containing all variables and their values, optionally (and # by default) including the values defined in parent. Local values # shadow parent values. Ephemeral scopes for match results ($0 - $n) are not included. # # This is currently a wrapper for to_hash_legacy or to_hash_future. # # @see to_hash_future # # @see to_hash_legacy def to_hash(recursive = true) @parser ||= Puppet[:parser] if @parser == 'future' to_hash_future(recursive) else to_hash_legacy(recursive) end end # Fixed version of to_hash that implements scoping correctly (i.e., with # dynamic scoping disabled #28200 / PUP-1220 # # @see to_hash def to_hash_future(recursive) if recursive and has_enclosing_scope? target = enclosing_scope.to_hash_future(recursive) if !(inherited = inherited_scope).nil? target.merge!(inherited.to_hash_future(recursive)) end else target = Hash.new end # add all local scopes @ephemeral.last.add_entries_to(target) target end # The old broken implementation of to_hash that retains the dynamic scoping # semantics # # @see to_hash def to_hash_legacy(recursive = true) if recursive and parent target = parent.to_hash_legacy(recursive) else target = Hash.new end # add all local scopes @ephemeral.last.add_entries_to(target) target end def namespaces @namespaces.dup end # Create a new scope and set these options. def newscope(options = {}) compiler.newscope(self, options) end def parent_module_name return nil unless @parent return nil unless @parent.source @parent.source.module_name end # Set defaults for a type. The typename should already be downcased, # so that the syntax is isolated. We don't do any kind of type-checking # here; instead we let the resource do it when the defaults are used. def define_settings(type, params) table = @defaults[type] # if we got a single param, it'll be in its own array params = [params] unless params.is_a?(Array) params.each { |param| if table.include?(param.name) raise Puppet::ParseError.new("Default already defined for #{type} { #{param.name} }; cannot redefine", param.line, param.file) end table[param.name] = param } end RESERVED_VARIABLE_NAMES = ['trusted', 'facts'].freeze # Set a variable in the current scope. This will override settings # in scopes above, but will not allow variables in the current scope # to be reassigned. # It's preferred that you use self[]= instead of this; only use this # when you need to set options. def setvar(name, value, options = {}) if name =~ /^[0-9]+$/ raise Puppet::ParseError.new("Cannot assign to a numeric match result variable '$#{name}'") # unless options[:ephemeral] end unless name.is_a? String raise Puppet::ParseError, "Scope variable name #{name.inspect} is a #{name.class}, not a string" end # Check for reserved variable names - if @enable_immutable_data && !options[:privileged] && RESERVED_VARIABLE_NAMES.include?(name) + if !options[:privileged] && RESERVED_VARIABLE_NAMES.include?(name) raise Puppet::ParseError, "Attempt to assign to a reserved variable name: '#{name}'" end table = effective_symtable(options[:ephemeral]) if table.bound?(name) if options[:append] error = Puppet::ParseError.new("Cannot append, variable #{name} is defined in this scope") else error = Puppet::ParseError.new("Cannot reassign variable #{name}") end error.file = options[:file] if options[:file] error.line = options[:line] if options[:line] raise error end if options[:append] # produced result (value) is the resulting appended value, note: the table[]= does not return the value table[name] = (value = append_value(undef_as('', self[name]), value)) else table[name] = value end value end def set_trusted(hash) setvar('trusted', deep_freeze(hash), :privileged => true) end def set_facts(hash) setvar('facts', deep_freeze(hash), :privileged => true) end # Deeply freezes the given object. The object and its content must be of the types: # Array, Hash, Numeric, Boolean, Symbol, Regexp, NilClass, or String. All other types raises an Error. # (i.e. if they are assignable to Puppet::Pops::Types::Data type). # def deep_freeze(object) case object when Array object.each {|v| deep_freeze(v) } object.freeze when Hash object.each {|k, v| deep_freeze(k); deep_freeze(v) } object.freeze when NilClass, Numeric, TrueClass, FalseClass # do nothing when String object.freeze else raise Puppet::Error, "Unsupported data type: '#{object.class}'" end object end private :deep_freeze # Return the effective "table" for setting variables. # This method returns the first ephemeral "table" that acts as a local scope, or this # scope's symtable. If the parameter `use_ephemeral` is true, the "top most" ephemeral "table" # will be returned (irrespective of it being a match scope or a local scope). # # @param use_ephemeral [Boolean] whether the top most ephemeral (of any kind) should be used or not def effective_symtable(use_ephemeral) s = @ephemeral.last if use_ephemeral return s || @symtable else while s && !s.is_local_scope?() s = s.parent end s || @symtable end end # Sets the variable value of the name given as an argument to the given value. The value is # set in the current scope and may shadow a variable with the same name in a visible outer scope. # It is illegal to re-assign a variable in the same scope. It is illegal to set a variable in some other # scope/namespace than the scope passed to a method. # # @param varname [String] The variable name to which the value is assigned. Must not contain `::` # @param value [String] The value to assign to the given variable name. # @param options [Hash] Additional options, not part of api. # # @api public # def []=(varname, value, options = {}) setvar(varname, value, options = {}) end def append_value(bound_value, new_value) case new_value when Array bound_value + new_value when Hash bound_value.merge(new_value) else if bound_value.is_a?(Hash) raise ArgumentError, "Trying to append to a hash with something which is not a hash is unsupported" end bound_value + new_value end end private :append_value # Return the tags associated with this scope. def_delegator :resource, :tags # Used mainly for logging def to_s "Scope(#{@resource})" end # remove ephemeral scope up to level # TODO: Who uses :all ? Remove ?? # def unset_ephemeral_var(level=:all) if level == :all @ephemeral = [ MatchScope.new(@symtable, nil)] else @ephemeral.pop(@ephemeral.size - level) end end def ephemeral_level @ephemeral.size end # TODO: Who calls this? def new_ephemeral(local_scope = false) if local_scope @ephemeral.push(LocalScope.new(@ephemeral.last)) else @ephemeral.push(MatchScope.new(@ephemeral.last, nil)) end end # Sets match data in the most nested scope (which always is a MatchScope), it clobbers match data already set there # def set_match_data(match_data) @ephemeral.last.match_data = match_data end # Nests a match data scope def new_match_scope(match_data) @ephemeral.push(MatchScope.new(@ephemeral.last, match_data)) end def ephemeral_from(match, file = nil, line = nil) case match when Hash # Create local scope ephemeral and set all values from hash new_ephemeral(true) match.each {|k,v| setvar(k, v, :file => file, :line => line, :ephemeral => true) } # Must always have an inner match data scope (that starts out as transparent) # In 3x slightly wasteful, since a new nested scope is created for a match # (TODO: Fix that problem) new_ephemeral(false) else raise(ArgumentError,"Invalid regex match data. Got a #{match.class}") unless match.is_a?(MatchData) # Create a match ephemeral and set values from match data new_match_scope(match) end end def find_resource_type(type) # It still works fine without the type == 'class' short-cut, but it is a lot slower. return nil if ["class", "node"].include? type.to_s.downcase find_builtin_resource_type(type) || find_defined_resource_type(type) end def find_builtin_resource_type(type) Puppet::Type.type(type.to_s.downcase.to_sym) end def find_defined_resource_type(type) known_resource_types.find_definition(namespaces, type.to_s.downcase) end def method_missing(method, *args, &block) method.to_s =~ /^function_(.*)$/ name = $1 super unless name super unless Puppet::Parser::Functions.function(name) # In odd circumstances, this might not end up defined by the previous # method, so we might as well be certain. if respond_to? method send(method, *args) else raise Puppet::DevError, "Function #{name} not defined despite being loaded!" end end def resolve_type_and_titles(type, titles) raise ArgumentError, "titles must be an array" unless titles.is_a?(Array) case type.downcase when "class" # resolve the titles titles = titles.collect do |a_title| hostclass = find_hostclass(a_title) hostclass ? hostclass.name : a_title end when "node" # no-op else # resolve the type resource_type = find_resource_type(type) type = resource_type.name if resource_type end return [type, titles] end # Transforms references to classes to the form suitable for # lookup in the compiler. # # Makes names passed in the names array absolute if they are relative # Names are now made absolute if Puppet[:parser] == 'future', this will # be the default behavior in Puppet 4.0 # # Transforms Class[] and Resource[] type referenes to class name # or raises an error if a Class[] is unspecific, if a Resource is not # a 'class' resource, or if unspecific (no title). # # TODO: Change this for 4.0 to always make names absolute # # @param names [Array] names to (optionally) make absolute # @return [Array] names after transformation # def transform_and_assert_classnames(names) if Puppet[:parser] == 'future' names.map do |name| case name when String name.sub(/^([^:]{1,2})/, '::\1') when Puppet::Resource assert_class_and_title(name.type, name.title) name.title.sub(/^([^:]{1,2})/, '::\1') when Puppet::Pops::Types::PHostClassType raise ArgumentError, "Cannot use an unspecific Class[] Type" unless name.class_name name.class_name.sub(/^([^:]{1,2})/, '::\1') when Puppet::Pops::Types::PResourceType assert_class_and_title(name.type_name, name.title) name.title.sub(/^([^:]{1,2})/, '::\1') end end else names end end private def assert_class_and_title(type_name, title) if type_name.nil? || type_name == '' raise ArgumentError, "Cannot use an unspecific Resource[] where a Resource['class', name] is expected" end unless type_name =~ /^[Cc]lass$/ raise ArgumentError, "Cannot use a Resource[#{type_name}] where a Resource['class', name] is expected" end if title.nil? raise ArgumentError, "Cannot use an unspecific Resource['class'] where a Resource['class', name] is expected" end end def extend_with_functions_module root = Puppet.lookup(:root_environment) extend Puppet::Parser::Functions.environment_module(root) extend Puppet::Parser::Functions.environment_module(environment) if environment != root end end diff --git a/man/man5/puppet.conf.5 b/man/man5/puppet.conf.5 index b47917132..f6a2d3c6d 100644 --- a/man/man5/puppet.conf.5 +++ b/man/man5/puppet.conf.5 @@ -1,1896 +1,1880 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "PUPPETCONF" "5" "November 2014" "Puppet Labs, LLC" "Puppet manual" \fBThis page is autogenerated; any changes will get overwritten\fR \fI(last generated on 2014\-11\-05 19:14:33 \-0800)\fR . .SH "Configuration Settings" . .IP "\(bu" 4 Each of these settings can be specified in \fBpuppet\.conf\fR or on the command line\. . .IP "\(bu" 4 When using boolean settings on the command line, use \fB\-\-setting\fR and \fB\-\-no\-setting\fR instead of \fB\-\-setting (true|false)\fR\. . .IP "\(bu" 4 Settings can be interpolated as \fB$variables\fR in other settings; \fB$environment\fR is special, in that puppet master will interpolate each agent node\'s environment instead of its own\. . .IP "\(bu" 4 Multiple values should be specified as comma\-separated lists; multiple directories should be separated with the system path separator (usually a colon)\. . .IP "\(bu" 4 Settings that represent time intervals should be specified in duration format: an integer immediately followed by one of the units \'y\' (years of 365 days), \'d\' (days), \'h\' (hours), \'m\' (minutes), or \'s\' (seconds)\. The unit cannot be combined with other units, and defaults to seconds when omitted\. Examples are \'3600\' which is equivalent to \'1h\' (one hour), and \'1825d\' which is equivalent to \'5y\' (5 years)\. . .IP "\(bu" 4 Settings that take a single file or directory can optionally set the owner, group, and mode for their value: \fBrundir = $vardir/run { owner = puppet, group = puppet, mode = 644 }\fR . .IP "\(bu" 4 The Puppet executables will ignore any setting that isn\'t relevant to their function\. . .IP "" 0 . .P See the configuration guide \fIhttp://docs\.puppetlabs\.com/guides/configuring\.html\fR for more details\. . .SS "agent_catalog_run_lockfile" A lock file to indicate that a puppet agent catalog run is currently in progress\. The file contains the pid of the process that holds the lock on the catalog run\. . .IP "\(bu" 4 \fIDefault\fR: $statedir/agent_catalog_run\.lock . .IP "" 0 . .SS "agent_disabled_lockfile" A lock file to indicate that puppet agent runs have been administratively disabled\. File contains a JSON object with state information\. . .IP "\(bu" 4 \fIDefault\fR: $statedir/agent_disabled\.lock . .IP "" 0 . .SS "allow_duplicate_certs" Whether to allow a new certificate request to overwrite an existing certificate\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "allow_variables_with_dashes" Permit hyphens (\fB\-\fR) in variable names and issue deprecation warnings about them\. This setting \fBshould always be \fBfalse\fR;\fR setting it to \fBtrue\fR will cause subtle and wide\-ranging bugs\. It will be removed in a future version\. . .P Hyphenated variables caused major problems in the language, but were allowed between Puppet 2\.7\.3 and 2\.7\.14\. If you used them during this window, we apologize for the inconvenience \-\-\- you can temporarily set this to \fBtrue\fR in order to upgrade, and can rename your variables at your leisure\. Please revert it to \fBfalse\fR after you have renamed all affected variables\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "always_cache_features" Affects how we cache attempts to load Puppet \'features\'\. If false, then calls to \fBPuppet\.features\.?\fR will always attempt to load the feature (which can be an expensive operation) unless it has already been loaded successfully\. This makes it possible for a single agent run to, e\.g\., install a package that provides the underlying capabilities for a feature, and then later load that feature during the same run (even if the feature had been tested earlier and had not been available)\. . .P If this setting is set to true, then features will only be checked once, and if they are not available, the negative result is cached and returned for all subsequent attempts to load the feature\. This behavior is almost always appropriate for the server, and can result in a significant performance improvement for features that are checked frequently\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "archive_file_server" During an inspect run, the file bucket server to archive files to if archive_files is set\. . .IP "\(bu" 4 \fIDefault\fR: $server . .IP "" 0 . .SS "archive_files" During an inspect run, whether to archive files whose contents are audited to a file bucket\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "autoflush" Whether log files should always flush to disk\. . .IP "\(bu" 4 \fIDefault\fR: true . .IP "" 0 . .SS "autosign" Whether (and how) to autosign certificate requests\. This setting is only relevant on a puppet master acting as a certificate authority (CA)\. . .P Valid values are true (autosigns all certificate requests; not recommended), false (disables autosigning certificates), or the absolute path to a file\. . .P The file specified in this setting may be either a \fBconfiguration file\fR or a \fBcustom policy executable\.\fR Puppet will automatically determine what it is: If the Puppet user (see the \fBuser\fR setting) can execute the file, it will be treated as a policy executable; otherwise, it will be treated as a config file\. . .P If a custom policy executable is configured, the CA puppet master will run it every time it receives a CSR\. The executable will be passed the subject CN of the request \fIas a command line argument,\fR and the contents of the CSR in PEM format \fIon stdin\.\fR It should exit with a status of 0 if the cert should be autosigned and non\-zero if the cert should not be autosigned\. . .P If a certificate request is not autosigned, it will persist for review\. An admin user can use the \fBpuppet cert sign\fR command to manually sign it, or can delete the request\. . .P For info on autosign configuration files, see the guide to Puppet\'s config files \fIhttp://docs\.puppetlabs\.com/guides/configuring\.html\fR\. . .IP "\(bu" 4 \fIDefault\fR: $confdir/autosign\.conf . .IP "" 0 . .SS "basemodulepath" The search path for \fBglobal\fR modules\. Should be specified as a list of directories separated by the system path separator character\. (The POSIX path separator is \':\', and the Windows path separator is \';\'\.) . .P If you are using directory environments, these are the modules that will be used by \fIall\fR environments\. Note that the \fBmodules\fR directory of the active environment will have priority over any global directories\. For more info, see http://docs\.puppetlabs\.com/puppet/latest/reference/environments\.html . .P This setting also provides the default value for the deprecated \fBmodulepath\fR setting, which is used when directory environments are disabled\. . .IP "\(bu" 4 \fIDefault\fR: $confdir/modules:/usr/share/puppet/modules . .IP "" 0 . .SS "bindaddress" The address a listening server should bind to\. . .IP "\(bu" 4 \fIDefault\fR: 0\.0\.0\.0 . .IP "" 0 . .SS "binder" Turns the binding system on or off\. This includes bindings in modules\. The binding system aggregates data from modules and other locations and makes them available for lookup\. The binding system is experimental and any or all of it may change\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "binder_config" The binder configuration file\. Puppet reads this file on each request to configure the bindings system\. If set to nil (the default), a $confdir/binder_config\.yaml is optionally loaded\. If it does not exists, a default configuration is used\. If the setting :binding_config is specified, it must reference a valid and existing yaml file\. . .TP \fIDefault\fR: . .SS "bucketdir" Where FileBucket files are stored\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/bucket . .IP "" 0 . .SS "ca" Whether the master should function as a certificate authority\. . .IP "\(bu" 4 \fIDefault\fR: true . .IP "" 0 . .SS "ca_name" The name to use the Certificate Authority certificate\. . .IP "\(bu" 4 \fIDefault\fR: Puppet CA: $certname . .IP "" 0 . .SS "ca_port" The port to use for the certificate authority\. . .IP "\(bu" 4 \fIDefault\fR: $masterport . .IP "" 0 . .SS "ca_server" The server to use for certificate authority requests\. It\'s a separate server because it cannot and does not need to horizontally scale\. . .IP "\(bu" 4 \fIDefault\fR: $server . .IP "" 0 . .SS "ca_ttl" The default TTL for new certificates\. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\. . .IP "\(bu" 4 \fIDefault\fR: 5y . .IP "" 0 . .SS "cacert" The CA certificate\. . .IP "\(bu" 4 \fIDefault\fR: $cadir/ca_crt\.pem . .IP "" 0 . .SS "cacrl" The certificate revocation list (CRL) for the CA\. Will be used if present but otherwise ignored\. . .IP "\(bu" 4 \fIDefault\fR: $cadir/ca_crl\.pem . .IP "" 0 . .SS "cadir" The root directory for the certificate authority\. . .IP "\(bu" 4 \fIDefault\fR: $ssldir/ca . .IP "" 0 . .SS "cakey" The CA private key\. . .IP "\(bu" 4 \fIDefault\fR: $cadir/ca_key\.pem . .IP "" 0 . .SS "capass" Where the CA stores the password for the private key\. . .IP "\(bu" 4 \fIDefault\fR: $caprivatedir/ca\.pass . .IP "" 0 . .SS "caprivatedir" Where the CA stores private certificate information\. . .IP "\(bu" 4 \fIDefault\fR: $cadir/private . .IP "" 0 . .SS "capub" The CA public key\. . .IP "\(bu" 4 \fIDefault\fR: $cadir/ca_pub\.pem . .IP "" 0 . .SS "catalog_cache_terminus" How to store cached catalogs\. Valid values are \'json\', \'msgpack\' and \'yaml\'\. The agent application defaults to \'json\'\. . .TP \fIDefault\fR: . .SS "catalog_terminus" Where to get node catalogs\. This is useful to change if, for instance, you\'d like to pre\-compile catalogs and store them in memcached or some other easily\-accessed store\. . .IP "\(bu" 4 \fIDefault\fR: compiler . .IP "" 0 . .SS "cert_inventory" The inventory file\. This is a text file to which the CA writes a complete listing of all certificates\. . .IP "\(bu" 4 \fIDefault\fR: $cadir/inventory\.txt . .IP "" 0 . .SS "certdir" The certificate directory\. . .IP "\(bu" 4 \fIDefault\fR: $ssldir/certs . .IP "" 0 . .SS "certificate_revocation" Whether certificate revocation should be supported by downloading a Certificate Revocation List (CRL) to all clients\. If enabled, CA chaining will almost definitely not work\. . .IP "\(bu" 4 \fIDefault\fR: true . .IP "" 0 . .SS "certname" The name to use when handling certificates\. When a node requests a certificate from the CA puppet master, it uses the value of the \fBcertname\fR setting as its requested Subject CN\. . .P This is the name used when managing a node\'s permissions in auth\.conf \fIhttp://docs\.puppetlabs\.com/puppet/latest/reference/config_file_auth\.html\fR\. In most cases, it is also used as the node\'s name when matching node definitions \fIhttp://docs\.puppetlabs\.com/puppet/latest/reference/lang_node_definitions\.html\fR and requesting data from an ENC\. (This can be changed with the \fBnode_name_value\fR and \fBnode_name_fact\fR settings, although you should only do so if you have a compelling reason\.) . .P A node\'s certname is available in Puppet manifests as \fB$trusted[\'certname\']\fR\. (See Facts and Built\-In Variables \fIhttp://docs\.puppetlabs\.com/puppet/latest/reference/lang_facts_and_builtin_vars\.html\fR for more details\.) . .IP "\(bu" 4 For best compatibility, you should limit the value of \fBcertname\fR to only use letters, numbers, periods, underscores, and dashes\. (That is, it should match \fB/A[a\-z0\-9\._\-]+Z/\fR\.) . .IP "\(bu" 4 The special value \fBca\fR is reserved, and can\'t be used as the certname for a normal node\. . .IP "" 0 . .P Defaults to the node\'s fully qualified domain name\. . .IP "\(bu" 4 \fIDefault\fR: kylo\.corp\.puppetlabs\.net . .IP "" 0 . .SS "cfacter" Whether or not to use the native facter (cfacter) implementation instead of the Ruby one (facter)\. Defaults to false\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "classfile" The file in which puppet agent stores a list of the classes associated with the retrieved configuration\. Can be loaded in the separate \fBpuppet\fR executable using the \fB\-\-loadclasses\fR option\. . .IP "\(bu" 4 \fIDefault\fR: $statedir/classes\.txt . .IP "" 0 . .SS "client_datadir" The directory in which serialized data is stored on the client\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/client_data . .IP "" 0 . .SS "clientbucketdir" Where FileBucket files are stored locally\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/clientbucket . .IP "" 0 . .SS "clientyamldir" The directory in which client\-side YAML data is stored\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/client_yaml . .IP "" 0 . .SS "code" Code to parse directly\. This is essentially only used by \fBpuppet\fR, and should only be set if you\'re writing your own Puppet executable\. . .SS "color" Whether to use colors when logging to the console\. Valid values are \fBansi\fR (equivalent to \fBtrue\fR), \fBhtml\fR, and \fBfalse\fR, which produces no color\. Defaults to false on Windows, as its console does not support ansi colors\. . .IP "\(bu" 4 \fIDefault\fR: ansi . .IP "" 0 . .SS "confdir" The main Puppet configuration directory\. The default for this setting is calculated based on the user\. If the process is running as root or the user that Puppet is supposed to run as, it defaults to a system directory, but if it\'s running as any other user, it defaults to being in the user\'s home directory\. . .IP "\(bu" 4 \fIDefault\fR: /etc/puppet . .IP "" 0 . .SS "config" The configuration file for the current puppet application\. . .IP "\(bu" 4 \fIDefault\fR: $confdir/${config_file_name} . .IP "" 0 . .SS "config_file_name" The name of the puppet config file\. . .IP "\(bu" 4 \fIDefault\fR: puppet\.conf . .IP "" 0 . .SS "config_version" How to determine the configuration version\. By default, it will be the time that the configuration is parsed, but you can provide a shell script to override how the version is determined\. The output of this script will be added to every log message in the reports, allowing you to correlate changes on your hosts to the source version on the server\. . .P Setting a global value for config_version in puppet\.conf is deprecated\. Please set a per\-environment value in environment\.conf instead\. For more info, see http://docs\.puppetlabs\.com/puppet/latest/reference/environments\.html . .SS "configprint" Print the value of a specific configuration setting\. If the name of a setting is provided for this, then the value is printed and puppet exits\. Comma\-separate multiple values\. For a list of all values, specify \'all\'\. . .SS "configtimeout" How long the client should wait for the configuration to be retrieved before considering it a failure\. This can help reduce flapping if too many clients contact the server at one time\. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\. . .IP "\(bu" 4 \fIDefault\fR: 2m . .IP "" 0 . .SS "csr_attributes" An optional file containing custom attributes to add to certificate signing requests (CSRs)\. You should ensure that this file does not exist on your CA puppet master; if it does, unwanted certificate extensions may leak into certificates created with the \fBpuppet cert generate\fR command\. . .P If present, this file must be a YAML hash containing a \fBcustom_attributes\fR key and/or an \fBextension_requests\fR key\. The value of each key must be a hash, where each key is a valid OID and each value is an object that can be cast to a string\. . .P Custom attributes can be used by the CA when deciding whether to sign the certificate, but are then discarded\. Attribute OIDs can be any OID value except the standard CSR attributes (i\.e\. attributes described in RFC 2985 section 5\.4)\. This is useful for embedding a pre\-shared key for autosigning policy executables (see the \fBautosign\fR setting), often by using the \fB1\.2\.840\.113549\.1\.9\.7\fR ("challenge password") OID\. . .P Extension requests will be permanently embedded in the final certificate\. Extension OIDs must be in the "ppRegCertExt" (\fB1\.3\.6\.1\.4\.1\.34380\.1\.1\fR) or "ppPrivCertExt" (\fB1\.3\.6\.1\.4\.1\.34380\.1\.2\fR) OID arcs\. The ppRegCertExt arc is reserved for four of the most common pieces of data to embed: \fBpp_uuid\fR (\fB\.1\fR), \fBpp_instance_id\fR (\fB\.2\fR), \fBpp_image_name\fR (\fB\.3\fR), and \fBpp_preshared_key\fR (\fB\.4\fR) \-\-\- in the YAML file, these can be referred to by their short descriptive names instead of their full OID\. The ppPrivCertExt arc is unregulated, and can be used for site\-specific extensions\. . .IP "\(bu" 4 \fIDefault\fR: $confdir/csr_attributes\.yaml . .IP "" 0 . .SS "csrdir" Where the CA stores certificate requests . .IP "\(bu" 4 \fIDefault\fR: $cadir/requests . .IP "" 0 . .SS "daemonize" Whether to send the process into the background\. This defaults to true on POSIX systems, and to false on Windows (where Puppet currently cannot daemonize)\. . .IP "\(bu" 4 \fIDefault\fR: true . .IP "" 0 . .SS "data_binding_terminus" Where to retrive information about data\. . .IP "\(bu" 4 \fIDefault\fR: hiera . .IP "" 0 . .SS "dbadapter" The type of database to use\. This setting is only used by the ActiveRecord storeconfigs and inventory backends, which are deprecated\. . .IP "\(bu" 4 \fIDefault\fR: sqlite3 . .IP "" 0 . .SS "dbconnections" The number of database connections for networked databases\. Will be ignored unless the value is a positive integer\. This setting is only used by the ActiveRecord storeconfigs and inventory backends, which are deprecated\. . .SS "dblocation" The sqlite database file\. This setting is only used by the ActiveRecord storeconfigs and inventory backends, which are deprecated\. . .IP "\(bu" 4 \fIDefault\fR: $statedir/clientconfigs\.sqlite3 . .IP "" 0 . .SS "dbmigrate" Whether to automatically migrate the database\. This setting is only used by the ActiveRecord storeconfigs and inventory backends, which are deprecated\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "dbname" The name of the database to use\. This setting is only used by the ActiveRecord storeconfigs and inventory backends, which are deprecated\. . .IP "\(bu" 4 \fIDefault\fR: puppet . .IP "" 0 . .SS "dbpassword" The database password for caching\. Only used when networked databases are used\. This setting is only used by the ActiveRecord storeconfigs and inventory backends, which are deprecated\. . .IP "\(bu" 4 \fIDefault\fR: puppet . .IP "" 0 . .SS "dbport" The database password for caching\. Only used when networked databases are used\. This setting is only used by the ActiveRecord storeconfigs and inventory backends, which are deprecated\. . .SS "dbserver" The database server for caching\. Only used when networked databases are used\. . .IP "\(bu" 4 \fIDefault\fR: localhost . .IP "" 0 . .SS "dbsocket" The database socket location\. Only used when networked databases are used\. Will be ignored if the value is an empty string\. This setting is only used by the ActiveRecord storeconfigs and inventory backends, which are deprecated\. . .SS "dbuser" The database user for caching\. Only used when networked databases are used\. This setting is only used by the ActiveRecord storeconfigs and inventory backends, which are deprecated\. . .IP "\(bu" 4 \fIDefault\fR: puppet . .IP "" 0 . .SS "default_file_terminus" The default source for files if no server is given in a uri, e\.g\. puppet:///file\. The default of \fBrest\fR causes the file to be retrieved using the \fBserver\fR setting\. When running \fBapply\fR the default is \fBfile_server\fR, causing requests to be filled locally\. . .IP "\(bu" 4 \fIDefault\fR: rest . .IP "" 0 . .SS "default_manifest" The default main manifest for directory environments\. Any environment that doesn\'t set the \fBmanifest\fR setting in its \fBenvironment\.conf\fR file will use this manifest\. . .P This setting\'s value can be an absolute or relative path\. An absolute path will make all environments default to the same main manifest; a relative path will allow each environment to use its own manifest, and Puppet will resolve the path relative to each environment\'s main directory\. . .P In either case, the path can point to a single file or to a directory of manifests to be evaluated in alphabetical order\. . .IP "\(bu" 4 \fIDefault\fR: \./manifests . .IP "" 0 . .SS "default_schedules" Boolean; whether to generate the default schedule resources\. Setting this to false is useful for keeping external report processors clean of skipped schedule resources\. . .IP "\(bu" 4 \fIDefault\fR: true . .IP "" 0 . .SS "deviceconfig" Path to the device config file for puppet device\. . .IP "\(bu" 4 \fIDefault\fR: $confdir/device\.conf . .IP "" 0 . .SS "devicedir" The root directory of devices\' $vardir\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/devices . .IP "" 0 . .SS "diff" Which diff command to use when printing differences between files\. This setting has no default value on Windows, as standard \fBdiff\fR is not available, but Puppet can use many third\-party diff tools\. . .IP "\(bu" 4 \fIDefault\fR: diff . .IP "" 0 . .SS "diff_args" Which arguments to pass to the diff command when printing differences between files\. The command to use can be chosen with the \fBdiff\fR setting\. . .IP "\(bu" 4 \fIDefault\fR: \-u . .IP "" 0 . .SS "digest_algorithm" Which digest algorithm to use for file resources and the filebucket\. Valid values are md5, sha256\. Default is md5\. . .IP "\(bu" 4 \fIDefault\fR: md5 . .IP "" 0 . .SS "disable_per_environment_manifest" Whether to disallow an environment\-specific main manifest\. When set to \fBtrue\fR, Puppet will use the manifest specified in the \fBdefault_manifest\fR setting for all environments\. If an environment specifies a different main manifest in its \fBenvironment\.conf\fR file, catalog requests for that environment will fail with an error\. . .P This setting requires \fBdefault_manifest\fR to be set to an absolute path\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "disable_warnings" A comma\-separated list of warning types to suppress\. If large numbers of warnings are making Puppet\'s logs too large or difficult to use, you can temporarily silence them with this setting\. . .P If you are preparing to upgrade Puppet to a new major version, you should re\-enable all warnings for a while\. . .P Valid values for this setting are: . .IP "\(bu" 4 \fBdeprecations\fR \-\-\- disables deprecation warnings\. . .IP "\(bu" 4 \fIDefault\fR: [] . .IP "" 0 . .SS "dns_alt_names" The comma\-separated list of alternative DNS names to use for the local host\. . .P When the node generates a CSR for itself, these are added to the request as the desired \fBsubjectAltName\fR in the certificate: additional DNS labels that the certificate is also valid answering as\. . .P This is generally required if you use a non\-hostname \fBcertname\fR, or if you want to use \fBpuppet kick\fR or \fBpuppet resource \-H\fR and the primary certname does not match the DNS name you use to communicate with the host\. . .P This is unnecessary for agents, unless you intend to use them as a server for \fBpuppet kick\fR or remote \fBpuppet resource\fR management\. . .P It is rarely necessary for servers; it is usually helpful only if you need to have a pool of multiple load balanced masters, or for the same master to respond on two physically separate networks under different names\. . .SS "document_all" Whether to document all resources when using \fBpuppet doc\fR to generate manifest documentation\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "environment" The environment Puppet is running in\. For clients (e\.g\., \fBpuppet agent\fR) this determines the environment itself, which is used to find modules and much more\. For servers (i\.e\., \fBpuppet master\fR) this provides the default environment for nodes we know nothing about\. . .IP "\(bu" 4 \fIDefault\fR: production . .IP "" 0 . .SS "environment_timeout" The time to live for a cached environment\. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\. This setting can also be set to \fBunlimited\fR, which causes the environment to be cached until the master is restarted\. . .IP "\(bu" 4 \fIDefault\fR: unlimited . .IP "" 0 . .SS "environmentpath" A search path for directory environments, as a list of directories separated by the system path separator character\. (The POSIX path separator is \':\', and the Windows path separator is \';\'\.) . .P This setting must have a value set to enable \fBdirectory environments\.\fR The recommended value is \fB$confdir/environments\fR\. For more details, see http://docs\.puppetlabs\.com/puppet/latest/reference/environments\.html . .SS "evaltrace" Whether each resource should log when it is being evaluated\. This allows you to interactively see exactly what is being done\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "external_nodes" An external command that can produce node information\. The command\'s output must be a YAML dump of a hash, and that hash must have a \fBclasses\fR key and/or a \fBparameters\fR key, where \fBclasses\fR is an array or hash and \fBparameters\fR is a hash\. For unknown nodes, the command should exit with a non\-zero exit code\. . .P This command makes it straightforward to store your node mapping information in other data sources like databases\. . .IP "\(bu" 4 \fIDefault\fR: none . .IP "" 0 . .SS "factpath" Where Puppet should look for facts\. Multiple directories should be separated by the system path separator character\. (The POSIX path separator is \':\', and the Windows path separator is \';\'\.) . .IP "\(bu" 4 \fIDefault\fR: $vardir/lib/facter:$vardir/facts . .IP "" 0 . .SS "facts_terminus" The node facts terminus\. . .IP "\(bu" 4 \fIDefault\fR: facter . .IP "" 0 . .SS "fileserverconfig" Where the fileserver configuration is stored\. . .IP "\(bu" 4 \fIDefault\fR: $confdir/fileserver\.conf . .IP "" 0 . .SS "filetimeout" The minimum time to wait between checking for updates in configuration files\. This timeout determines how quickly Puppet checks whether a file (such as manifests or templates) has changed on disk\. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\. . .IP "\(bu" 4 \fIDefault\fR: 15s . .IP "" 0 . .SS "forge_authorization" The authorization key to connect to the Puppet Forge\. Leave blank for unauthorized or license based connections . .TP \fIDefault\fR: . .SS "freeze_main" Freezes the \'main\' class, disallowing any code to be added to it\. This essentially means that you can\'t have any code outside of a node, class, or definition other than in the site manifest\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "genconfig" When true, causes Puppet applications to print an example config file to stdout and exit\. The example will include descriptions of each setting, and the current (or default) value of each setting, incorporating any settings overridden on the CLI (with the exception of \fBgenconfig\fR itself)\. This setting only makes sense when specified on the command line as \fB\-\-genconfig\fR\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "genmanifest" Whether to just print a manifest to stdout and exit\. Only makes sense when specified on the command line as \fB\-\-genmanifest\fR\. Takes into account arguments specified on the CLI\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "graph" Whether to create dot graph files for the different configuration graphs\. These dot files can be interpreted by tools like OmniGraffle or dot (which is part of ImageMagick)\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "graphdir" Where to store dot\-outputted graphs\. . .IP "\(bu" 4 \fIDefault\fR: $statedir/graphs . .IP "" 0 . .SS "group" The group puppet master should run as\. . .IP "\(bu" 4 \fIDefault\fR: puppet . .IP "" 0 . .SS "hiera_config" The hiera configuration file\. Puppet only reads this file on startup, so you must restart the puppet master every time you edit it\. . .IP "\(bu" 4 \fIDefault\fR: $confdir/hiera\.yaml . .IP "" 0 . .SS "hostcert" Where individual hosts store and look for their certificates\. . .IP "\(bu" 4 \fIDefault\fR: $certdir/$certname\.pem . .IP "" 0 . .SS "hostcrl" Where the host\'s certificate revocation list can be found\. This is distinct from the certificate authority\'s CRL\. . .IP "\(bu" 4 \fIDefault\fR: $ssldir/crl\.pem . .IP "" 0 . .SS "hostcsr" Where individual hosts store and look for their certificate requests\. . .IP "\(bu" 4 \fIDefault\fR: $ssldir/csr_$certname\.pem . .IP "" 0 . .SS "hostprivkey" Where individual hosts store and look for their private key\. . .IP "\(bu" 4 \fIDefault\fR: $privatekeydir/$certname\.pem . .IP "" 0 . .SS "hostpubkey" Where individual hosts store and look for their public key\. . .IP "\(bu" 4 \fIDefault\fR: $publickeydir/$certname\.pem . .IP "" 0 . .SS "http_debug" Whether to write HTTP request and responses to stderr\. This should never be used in a production environment\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "http_keepalive_timeout" The maximum amount of time a persistent HTTP connection can remain idle in the connection pool, before it is closed\. This timeout should be shorter than the keepalive timeout used on the HTTP server, e\.g\. Apache KeepAliveTimeout directive\. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\. . .IP "\(bu" 4 \fIDefault\fR: 4s . .IP "" 0 . .SS "http_proxy_host" The HTTP proxy host to use for outgoing connections\. Note: You may need to use a FQDN for the server hostname when using a proxy\. Environment variable http_proxy or HTTP_PROXY will override this value . .IP "\(bu" 4 \fIDefault\fR: none . .IP "" 0 . .SS "http_proxy_password" The password for the user of an authenticated HTTP proxy\. Requires the \fBhttp_proxy_user\fR setting\. . .P Note that passwords must be valid when used as part of a URL\. If a password contains any characters with special meanings in URLs (as specified by RFC 3986 section 2\.2), they must be URL\-encoded\. (For example, \fB#\fR would become \fB%23\fR\.) . .IP "\(bu" 4 \fIDefault\fR: none . .IP "" 0 . .SS "http_proxy_port" The HTTP proxy port to use for outgoing connections . .IP "\(bu" 4 \fIDefault\fR: 3128 . .IP "" 0 . .SS "http_proxy_user" The user name for an authenticated HTTP proxy\. Requires the \fBhttp_proxy_host\fR setting\. . .IP "\(bu" 4 \fIDefault\fR: none . .IP "" 0 . .SS "httplog" Where the puppet agent web server logs\. . .IP "\(bu" 4 \fIDefault\fR: $logdir/http\.log . .IP "" 0 . .SS "ignorecache" Ignore cache and always recompile the configuration\. This is useful for testing new configurations, where the local cache may in fact be stale even if the timestamps are up to date \- if the facts change or if the server changes\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "ignoreimport" If true, allows the parser to continue without requiring all files referenced with \fBimport\fR statements to exist\. This setting was primarily designed for use with commit hooks for parse\-checking\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "ignoremissingtypes" Skip searching for classes and definitions that were missing during a prior compilation\. The list of missing objects is maintained per\-environment and persists until the environment is cleared or the master is restarted\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "ignoreschedules" Boolean; whether puppet agent should ignore schedules\. This is useful for initial puppet agent runs\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . -.SS "immutable_node_data" -When true, also prevents $trusted and $facts from being overridden in any scope -. -.IP "\(bu" 4 -\fIDefault\fR: $trusted_node_data -. -.IP "" 0 -. .SS "keylength" The bit length of keys\. . .IP "\(bu" 4 \fIDefault\fR: 4096 . .IP "" 0 . .SS "lastrunfile" Where puppet agent stores the last run report summary in yaml format\. . .IP "\(bu" 4 \fIDefault\fR: $statedir/last_run_summary\.yaml . .IP "" 0 . .SS "lastrunreport" Where puppet agent stores the last run report in yaml format\. . .IP "\(bu" 4 \fIDefault\fR: $statedir/last_run_report\.yaml . .IP "" 0 . .SS "ldapattrs" The LDAP attributes to include when querying LDAP for nodes\. All returned attributes are set as variables in the top\-level scope\. Multiple values should be comma\-separated\. The value \'all\' returns all attributes\. . .IP "\(bu" 4 \fIDefault\fR: all . .IP "" 0 . .SS "ldapbase" The search base for LDAP searches\. It\'s impossible to provide a meaningful default here, although the LDAP libraries might have one already set\. Generally, it should be the \'ou=Hosts\' branch under your main directory\. . .SS "ldapclassattrs" The LDAP attributes to use to define Puppet classes\. Values should be comma\-separated\. . .IP "\(bu" 4 \fIDefault\fR: puppetclass . .IP "" 0 . .SS "ldapparentattr" The attribute to use to define the parent node\. . .IP "\(bu" 4 \fIDefault\fR: parentnode . .IP "" 0 . .SS "ldappassword" The password to use to connect to LDAP\. . .SS "ldapport" The LDAP port\. Only used if \fBnode_terminus\fR is set to \fBldap\fR\. . .IP "\(bu" 4 \fIDefault\fR: 389 . .IP "" 0 . .SS "ldapserver" The LDAP server\. Only used if \fBnode_terminus\fR is set to \fBldap\fR\. . .IP "\(bu" 4 \fIDefault\fR: ldap . .IP "" 0 . .SS "ldapssl" Whether SSL should be used when searching for nodes\. Defaults to false because SSL usually requires certificates to be set up on the client side\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "ldapstackedattrs" The LDAP attributes that should be stacked to arrays by adding the values in all hierarchy elements of the tree\. Values should be comma\-separated\. . .IP "\(bu" 4 \fIDefault\fR: puppetvar . .IP "" 0 . .SS "ldapstring" The search string used to find an LDAP node\. . .IP "\(bu" 4 \fIDefault\fR: (&(objectclass=puppetClient)(cn=%s)) . .IP "" 0 . .SS "ldaptls" Whether TLS should be used when searching for nodes\. Defaults to false because TLS usually requires certificates to be set up on the client side\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "ldapuser" The user to use to connect to LDAP\. Must be specified as a full DN\. . .SS "legacy_query_parameter_serialization" The serialization format to use when sending file_metadata query parameters\. Older versions of puppet master expect certain query parameters to be serialized as yaml, which is deprecated\. . .P This should almost always be false\. It can be temporarily set to true to let agents using this Puppet version connect to a puppet master running Puppet 3\.0\.0 through 3\.2\.x\. . .P Note that this is set to true automatically if the agent detects an older master, so should never need to be set explicitly\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "libdir" An extra search path for Puppet\. This is only useful for those files that Puppet will load on demand, and is only guaranteed to work for those cases\. In fact, the autoload mechanism is responsible for making sure this directory is in Ruby\'s search path . .IP "\(bu" 4 \fIDefault\fR: $vardir/lib . .IP "" 0 . .SS "localcacert" Where each client stores the CA certificate\. . .IP "\(bu" 4 \fIDefault\fR: $certdir/ca\.pem . .IP "" 0 . .SS "log_level" Default logging level for messages from Puppet\. Allowed values are: . .IP "\(bu" 4 debug . .IP "\(bu" 4 info . .IP "\(bu" 4 notice . .IP "\(bu" 4 warning . .IP "\(bu" 4 err . .IP "\(bu" 4 alert . .IP "\(bu" 4 emerg . .IP "\(bu" 4 crit . .IP "\(bu" 4 \fIDefault\fR: notice . .IP "" 0 . .SS "logdir" The directory in which to store log files . .TP \fIDefault\fR: . .SS "manage_internal_file_permissions" Whether Puppet should manage the owner, group, and mode of files it uses internally . .IP "\(bu" 4 \fIDefault\fR: true . .IP "" 0 . .SS "manifest" The entry\-point manifest for puppet master\. This can be one file or a directory of manifests to be evaluated in alphabetical order\. Puppet manages this path as a directory if one exists or if the path ends with a / or \. . .P Setting a global value for \fBmanifest\fR in puppet\.conf is deprecated\. Please use directory environments instead\. If you need to use something other than the environment\'s \fBmanifests\fR directory as the main manifest, you can set \fBmanifest\fR in environment\.conf\. For more info, see http://docs\.puppetlabs\.com/puppet/latest/reference/environments\.html . .IP "\(bu" 4 \fIDefault\fR: $manifestdir/site\.pp . .IP "" 0 . .SS "manifestdir" Used to build the default value of the \fBmanifest\fR setting\. Has no other purpose\. . .P This setting is deprecated\. . .IP "\(bu" 4 \fIDefault\fR: $confdir/manifests . .IP "" 0 . .SS "masterhttplog" Where the puppet master web server saves its access log\. This is only used when running a WEBrick puppet master\. When puppet master is running under a Rack server like Passenger, that web server will have its own logging behavior\. . .IP "\(bu" 4 \fIDefault\fR: $logdir/masterhttp\.log . .IP "" 0 . .SS "masterport" The port for puppet master traffic\. For puppet master, this is the port to listen on; for puppet agent, this is the port to make requests on\. Both applications use this setting to get the port\. . .IP "\(bu" 4 \fIDefault\fR: 8140 . .IP "" 0 . .SS "max_deprecations" Sets the max number of logged/displayed parser validation deprecation warnings in case multiple deprecation warnings have been detected\. A value of 0 blocks the logging of deprecation warnings\. The count is per manifest\. . .IP "\(bu" 4 \fIDefault\fR: 10 . .IP "" 0 . .SS "max_errors" Sets the max number of logged/displayed parser validation errors in case multiple errors have been detected\. A value of 0 is the same as a value of 1; a minimum of one error is always raised\. The count is per manifest\. . .IP "\(bu" 4 \fIDefault\fR: 10 . .IP "" 0 . .SS "max_warnings" Sets the max number of logged/displayed parser validation warnings in case multiple warnings have been detected\. A value of 0 blocks logging of warnings\. The count is per manifest\. . .IP "\(bu" 4 \fIDefault\fR: 10 . .IP "" 0 . .SS "maximum_uid" The maximum allowed UID\. Some platforms use negative UIDs but then ship with tools that do not know how to handle signed ints, so the UIDs show up as huge numbers that can then not be fed back into the system\. This is a hackish way to fail in a slightly more useful way when that happens\. . .IP "\(bu" 4 \fIDefault\fR: 4294967290 . .IP "" 0 . .SS "mkusers" Whether to create the necessary user and group that puppet agent will run as\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "module_groups" Extra module groups to request from the Puppet Forge . .TP \fIDefault\fR: . .SS "module_repository" The module repository . .IP "\(bu" 4 \fIDefault\fR: https://forgeapi\.puppetlabs\.com . .IP "" 0 . .SS "module_skeleton_dir" The directory which the skeleton for module tool generate is stored\. . .IP "\(bu" 4 \fIDefault\fR: $module_working_dir/skeleton . .IP "" 0 . .SS "module_working_dir" The directory into which module tool data is stored . .IP "\(bu" 4 \fIDefault\fR: $vardir/puppet\-module . .IP "" 0 . .SS "modulepath" The search path for modules, as a list of directories separated by the system path separator character\. (The POSIX path separator is \':\', and the Windows path separator is \';\'\.) . .P Setting a global value for \fBmodulepath\fR in puppet\.conf is deprecated\. Please use directory environments instead\. If you need to use something other than the default modulepath of \fB:$basemodulepath\fR, you can set \fBmodulepath\fR in environment\.conf\. For more info, see http://docs\.puppetlabs\.com/puppet/latest/reference/environments\.html . .IP "\(bu" 4 \fIDefault\fR: $basemodulepath . .IP "" 0 . .SS "name" The name of the application, if we are running as one\. The default is essentially $0 without the path or \fB\.rb\fR\. . .TP \fIDefault\fR: . .SS "node_cache_terminus" How to store cached nodes\. Valid values are (none), \'json\', \'msgpack\', \'yaml\' or write only yaml (\'write_only_yaml\')\. The master application defaults to \'write_only_yaml\', all others to none\. . .TP \fIDefault\fR: . .SS "node_name" How the puppet master determines the client\'s identity and sets the \'hostname\', \'fqdn\' and \'domain\' facts for use in the manifest, in particular for determining which \'node\' statement applies to the client\. Possible values are \'cert\' (use the subject\'s CN in the client\'s certificate) and \'facter\' (use the hostname that the client reported in its facts) . .IP "\(bu" 4 \fIDefault\fR: cert . .IP "" 0 . .SS "node_name_fact" The fact name used to determine the node name used for all requests the agent makes to the master\. WARNING: This setting is mutually exclusive with node_name_value\. Changing this setting also requires changes to the default auth\.conf configuration on the Puppet Master\. Please see http://links\.puppetlabs\.com/node_name_fact for more information\. . .SS "node_name_value" The explicit value used for the node name for all requests the agent makes to the master\. WARNING: This setting is mutually exclusive with node_name_fact\. Changing this setting also requires changes to the default auth\.conf configuration on the Puppet Master\. Please see http://links\.puppetlabs\.com/node_name_value for more information\. . .IP "\(bu" 4 \fIDefault\fR: $certname . .IP "" 0 . .SS "node_terminus" Where to find information about nodes\. . .IP "\(bu" 4 \fIDefault\fR: plain . .IP "" 0 . .SS "noop" Whether to apply catalogs in noop mode, which allows Puppet to partially simulate a normal run\. This setting affects puppet agent and puppet apply\. . .P When running in noop mode, Puppet will check whether each resource is in sync, like it does when running normally\. However, if a resource attribute is not in the desired state (as declared in the catalog), Puppet will take no action, and will instead report the changes it \fIwould\fR have made\. These simulated changes will appear in the report sent to the puppet master, or be shown on the console if running puppet agent or puppet apply in the foreground\. The simulated changes will not send refresh events to any subscribing or notified resources, although Puppet will log that a refresh event \fIwould\fR have been sent\. . .P \fBImportant note:\fR The \fBnoop\fR metaparameter \fIhttp://docs\.puppetlabs\.com/references/latest/metaparameter\.html#noop\fR allows you to apply individual resources in noop mode, and will override the global value of the \fBnoop\fR setting\. This means a resource with \fBnoop => false\fR \fIwill\fR be changed if necessary, even when running puppet agent with \fBnoop = true\fR or \fB\-\-noop\fR\. (Conversely, a resource with \fBnoop => true\fR will only be simulated, even when noop mode is globally disabled\.) . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "onetime" Perform one configuration run and exit, rather than spawning a long\-running daemon\. This is useful for interactively running puppet agent, or running puppet agent from cron\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "ordering" How unrelated resources should be ordered when applying a catalog\. Allowed values are \fBtitle\-hash\fR, \fBmanifest\fR, and \fBrandom\fR\. This setting affects puppet agent and puppet apply, but not puppet master\. . .IP "\(bu" 4 \fBmanifest\fR (the default) will use the order in which the resources were declared in their manifest files\. . .IP "\(bu" 4 \fBtitle\-hash\fR (the default in 3\.x) will order resources randomly, but will use the same order across runs and across nodes\. It is only of value if you\'re migrating from 3\.x and have errors running with \fBmanifest\fR\. . .IP "\(bu" 4 \fBrandom\fR will order resources randomly and change their order with each run\. This can work like a fuzzer for shaking out undeclared dependencies\. . .IP "" 0 . .P Regardless of this setting\'s value, Puppet will always obey explicit dependencies set with the before/require/notify/subscribe metaparameters and the \fB\->\fR/\fB~>\fR chaining arrows; this setting only affects the relative ordering of \fIunrelated\fR resources\. . .IP "\(bu" 4 \fIDefault\fR: manifest . .IP "" 0 . .SS "parser" Selects the parser to use for parsing puppet manifests (in puppet DSL language/\'\.pp\' files)\. Available choices are \fBcurrent\fR (the default) and \fBfuture\fR\. . .P The \fBcurrent\fR parser means that the released version of the parser should be used\. . .P The \fBfuture\fR parser is a "time travel to the future" allowing early exposure to new language features\. What these features are will vary from release to release and they may be invididually configurable\. . .P Available Since Puppet 3\.2\. . .IP "\(bu" 4 \fIDefault\fR: current . .IP "" 0 . .SS "passfile" Where puppet agent stores the password for its private key\. Generally unused\. . .IP "\(bu" 4 \fIDefault\fR: $privatedir/password . .IP "" 0 . .SS "path" The shell search path\. Defaults to whatever is inherited from the parent process\. . .IP "\(bu" 4 \fIDefault\fR: none . .IP "" 0 . .SS "pidfile" The file containing the PID of a running process\. This file is intended to be used by service management frameworks and monitoring systems to determine if a puppet process is still in the process table\. . .IP "\(bu" 4 \fIDefault\fR: $rundir/${run_mode}\.pid . .IP "" 0 . .SS "plugindest" Where Puppet should store plugins that it pulls down from the central server\. . .IP "\(bu" 4 \fIDefault\fR: $libdir . .IP "" 0 . .SS "pluginfactdest" Where Puppet should store external facts that are being handled by pluginsync . .IP "\(bu" 4 \fIDefault\fR: $vardir/facts\.d . .IP "" 0 . .SS "pluginfactsource" Where to retrieve external facts for pluginsync . .IP "\(bu" 4 \fIDefault\fR: puppet://$server/pluginfacts . .IP "" 0 . .SS "pluginsignore" What files to ignore when pulling down plugins\. . .IP "\(bu" 4 \fIDefault\fR: \.svn CVS \.git . .IP "" 0 . .SS "pluginsource" From where to retrieve plugins\. The standard Puppet \fBfile\fR type is used for retrieval, so anything that is a valid file source can be used here\. . .IP "\(bu" 4 \fIDefault\fR: puppet://$server/plugins . .IP "" 0 . .SS "pluginsync" Whether plugins should be synced with the central server\. . .IP "\(bu" 4 \fIDefault\fR: true . .IP "" 0 . .SS "postrun_command" A command to run after every agent run\. If this command returns a non\-zero return code, the entire Puppet run will be considered to have failed, even though it might have performed work during the normal run\. . .SS "preferred_serialization_format" The preferred means of serializing ruby instances for passing over the wire\. This won\'t guarantee that all instances will be serialized using this method, since not all classes can be guaranteed to support this format, but it will be used for all classes that support it\. . .IP "\(bu" 4 \fIDefault\fR: pson . .IP "" 0 . .SS "prerun_command" A command to run before every agent run\. If this command returns a non\-zero return code, the entire Puppet run will fail\. . .SS "priority" The scheduling priority of the process\. Valid values are \'high\', \'normal\', \'low\', or \'idle\', which are mapped to platform\-specific values\. The priority can also be specified as an integer value and will be passed as is, e\.g\. \-5\. Puppet must be running as a privileged user in order to increase scheduling priority\. . .TP \fIDefault\fR: . .SS "privatedir" Where the client stores private certificate information\. . .IP "\(bu" 4 \fIDefault\fR: $ssldir/private . .IP "" 0 . .SS "privatekeydir" The private key directory\. . .IP "\(bu" 4 \fIDefault\fR: $ssldir/private_keys . .IP "" 0 . .SS "profile" Whether to enable experimental performance profiling . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "publickeydir" The public key directory\. . .IP "\(bu" 4 \fIDefault\fR: $ssldir/public_keys . .IP "" 0 . .SS "puppetdlog" The fallback log file\. This is only used when the \fB\-\-logdest\fR option is not specified AND Puppet is running on an operating system where both the POSIX syslog service and the Windows Event Log are unavailable\. (Currently, no supported operating systems match that description\.) . .P Despite the name, both puppet agent and puppet master will use this file as the fallback logging destination\. . .P For control over logging destinations, see the \fB\-\-logdest\fR command line option in the manual pages for puppet master, puppet agent, and puppet apply\. You can see man pages by running \fBpuppet \-\-help\fR, or read them online at http://docs\.puppetlabs\.com/references/latest/man/\. . .IP "\(bu" 4 \fIDefault\fR: $logdir/puppetd\.log . .IP "" 0 . .SS "rails_loglevel" The log level for Rails connections\. The value must be a valid log level within Rails\. Production environments normally use \fBinfo\fR and other environments normally use \fBdebug\fR\. This setting is only used by the ActiveRecord storeconfigs and inventory backends, which are deprecated\. . .IP "\(bu" 4 \fIDefault\fR: info . .IP "" 0 . .SS "railslog" Where Rails\-specific logs are sent\. This setting is only used by the ActiveRecord storeconfigs and inventory backends, which are deprecated\. . .IP "\(bu" 4 \fIDefault\fR: $logdir/rails\.log . .IP "" 0 . .SS "report" Whether to send reports after every transaction\. . .IP "\(bu" 4 \fIDefault\fR: true . .IP "" 0 . .SS "report_port" The port to communicate with the report_server\. . .IP "\(bu" 4 \fIDefault\fR: $masterport . .IP "" 0 . .SS "report_serialization_format" The serialization format to use when sending reports to the \fBreport_server\fR\. Possible values are \fBpson\fR and \fByaml\fR\. This setting affects puppet agent, but not puppet apply (which processes its own reports)\. . .P This should almost always be set to \fBpson\fR\. It can be temporarily set to \fByaml\fR to let agents using this Puppet version connect to a puppet master running Puppet 3\.0\.0 through 3\.2\.x\. . .P Note that this is set to \'yaml\' automatically if the agent detects an older master, so should never need to be set explicitly\. . .IP "\(bu" 4 \fIDefault\fR: pson . .IP "" 0 . .SS "report_server" The server to send transaction reports to\. . .IP "\(bu" 4 \fIDefault\fR: $server . .IP "" 0 . .SS "reportdir" The directory in which to store reports\. Each node gets a separate subdirectory in this directory\. This setting is only used when the \fBstore\fR report processor is enabled (see the \fBreports\fR setting)\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/reports . .IP "" 0 . .SS "reports" The list of report handlers to use\. When using multiple report handlers, their names should be comma\-separated, with whitespace allowed\. (For example, \fBreports = http, store\fR\.) . .P This setting is relevant to puppet master and puppet apply\. The puppet master will call these report handlers with the reports it receives from agent nodes, and puppet apply will call them with its own report\. (In all cases, the node applying the catalog must have \fBreport = true\fR\.) . .P See the report reference for information on the built\-in report handlers; custom report handlers can also be loaded from modules\. (Report handlers are loaded from the lib directory, at \fBpuppet/reports/NAME\.rb\fR\.) . .IP "\(bu" 4 \fIDefault\fR: store . .IP "" 0 . .SS "reporturl" The URL that reports should be forwarded to\. This setting is only used when the \fBhttp\fR report processor is enabled (see the \fBreports\fR setting)\. . .IP "\(bu" 4 \fIDefault\fR: http://localhost:3000/reports/upload . .IP "" 0 . .SS "req_bits" The bit length of the certificates\. . .IP "\(bu" 4 \fIDefault\fR: 4096 . .IP "" 0 . .SS "requestdir" Where host certificate requests are stored\. . .IP "\(bu" 4 \fIDefault\fR: $ssldir/certificate_requests . .IP "" 0 . .SS "resourcefile" The file in which puppet agent stores a list of the resources associated with the retrieved configuration\. . .IP "\(bu" 4 \fIDefault\fR: $statedir/resources\.txt . .IP "" 0 . .SS "rest_authconfig" The configuration file that defines the rights to the different rest indirections\. This can be used as a fine\-grained authorization system for \fBpuppet master\fR\. . .IP "\(bu" 4 \fIDefault\fR: $confdir/auth\.conf . .IP "" 0 . .SS "route_file" The YAML file containing indirector route configuration\. . .IP "\(bu" 4 \fIDefault\fR: $confdir/routes\.yaml . .IP "" 0 . .SS "rundir" Where Puppet PID files are kept\. . .TP \fIDefault\fR: . .SS "runinterval" How often puppet agent applies the catalog\. Note that a runinterval of 0 means "run continuously" rather than "never run\." If you want puppet agent to never run, you should start it with the \fB\-\-no\-client\fR option\. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\. . .IP "\(bu" 4 \fIDefault\fR: 30m . .IP "" 0 . .SS "serial" Where the serial number for certificates is stored\. . .IP "\(bu" 4 \fIDefault\fR: $cadir/serial . .IP "" 0 . .SS "server" The puppet master server to which the puppet agent should connect\. . .IP "\(bu" 4 \fIDefault\fR: puppet . .IP "" 0 . .SS "server_datadir" The directory in which serialized data is stored, usually in a subdirectory\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/server_data . .IP "" 0 . .SS "show_diff" Whether to log and report a contextual diff when files are being replaced\. This causes partial file contents to pass through Puppet\'s normal logging and reporting system, so this setting should be used with caution if you are sending Puppet\'s reports to an insecure destination\. This feature currently requires the \fBdiff/lcs\fR Ruby library\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "signeddir" Where the CA stores signed certificates\. . .IP "\(bu" 4 \fIDefault\fR: $cadir/signed . .IP "" 0 . .SS "splay" Whether to sleep for a pseudo\-random (but consistent) amount of time before a run\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "splaylimit" The maximum time to delay before runs\. Defaults to being the same as the run interval\. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\. . .IP "\(bu" 4 \fIDefault\fR: $runinterval . .IP "" 0 . .SS "srv_domain" The domain which will be queried to find the SRV records of servers to use\. . .IP "\(bu" 4 \fIDefault\fR: corp\.puppetlabs\.net . .IP "" 0 . .SS "ssl_client_ca_auth" Certificate authorities who issue server certificates\. SSL servers will not be considered authentic unless they possess a certificate issued by an authority listed in this file\. If this setting has no value then the Puppet master\'s CA certificate (localcacert) will be used\. . .TP \fIDefault\fR: . .SS "ssl_client_header" The header containing an authenticated client\'s SSL DN\. This header must be set by the proxy to the authenticated client\'s SSL DN (e\.g\., \fB/CN=puppet\.puppetlabs\.com\fR)\. Puppet will parse out the Common Name (CN) from the Distinguished Name (DN) and use the value of the CN field for authorization\. . .P Note that the name of the HTTP header gets munged by the web server common gateway inteface: an \fBHTTP_\fR prefix is added, dashes are converted to underscores, and all letters are uppercased\. Thus, to use the \fBX\-Client\-DN\fR header, this setting should be \fBHTTP_X_CLIENT_DN\fR\. . .IP "\(bu" 4 \fIDefault\fR: HTTP_X_CLIENT_DN . .IP "" 0 . .SS "ssl_client_verify_header" The header containing the status message of the client verification\. This header must be set by the proxy to \'SUCCESS\' if the client successfully authenticated, and anything else otherwise\. . .P Note that the name of the HTTP header gets munged by the web server common gateway inteface: an \fBHTTP_\fR prefix is added, dashes are converted to underscores, and all letters are uppercased\. Thus, to use the \fBX\-Client\-Verify\fR header, this setting should be \fBHTTP_X_CLIENT_VERIFY\fR\. . .IP "\(bu" 4 \fIDefault\fR: HTTP_X_CLIENT_VERIFY . .IP "" 0 . .SS "ssl_server_ca_auth" Certificate authorities who issue client certificates\. SSL clients will not be considered authentic unless they possess a certificate issued by an authority listed in this file\. If this setting has no value then the Puppet master\'s CA certificate (localcacert) will be used\. . .TP \fIDefault\fR: . .SS "ssldir" Where SSL certificates are kept\. . .IP "\(bu" 4 \fIDefault\fR: $confdir/ssl . .IP "" 0 . .SS "statedir" The directory where Puppet state is stored\. Generally, this directory can be removed without causing harm (although it might result in spurious service restarts)\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/state . .IP "" 0 . .SS "statefile" Where puppet agent and puppet master store state associated with the running configuration\. In the case of puppet master, this file reflects the state discovered through interacting with clients\. . .IP "\(bu" 4 \fIDefault\fR: $statedir/state\.yaml . .IP "" 0 . .SS "storeconfigs" Whether to store each client\'s configuration, including catalogs, facts, and related data\. This also enables the import and export of resources in the Puppet language \- a mechanism for exchange resources between nodes\. . .P By default this uses ActiveRecord and an SQL database to store and query the data; this, in turn, will depend on Rails being available\. . .P You can adjust the backend using the storeconfigs_backend setting\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "storeconfigs_backend" Configure the backend terminus used for StoreConfigs\. By default, this uses the ActiveRecord store, which directly talks to the database from within the Puppet Master process\. . .IP "\(bu" 4 \fIDefault\fR: active_record . .IP "" 0 . .SS "strict_hostname_checking" Whether to only search for the complete hostname as it is in the certificate when searching for node information in the catalogs\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "strict_variables" Makes the parser raise errors when referencing unknown variables\. (This does not affect referencing variables that are explicitly set to undef)\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "summarize" Whether to print a transaction summary\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "syslogfacility" What syslog facility to use when logging to syslog\. Syslog has a fixed list of valid facilities, and you must choose one of those; you cannot just make one up\. . .IP "\(bu" 4 \fIDefault\fR: daemon . .IP "" 0 . .SS "tags" Tags to use to find resources\. If this is set, then only resources tagged with the specified tags will be applied\. Values must be comma\-separated\. . .SS "templatedir" Where Puppet looks for template files\. Can be a list of colon\-separated directories\. . .P This setting is deprecated\. Please put your templates in modules instead\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/templates . .IP "" 0 . .SS "thin_storeconfigs" Boolean; whether Puppet should store only facts and exported resources in the storeconfigs database\. This will improve the performance of exported resources with the older \fBactive_record\fR backend, but will disable external tools that search the storeconfigs database\. Thinning catalogs is generally unnecessary when using PuppetDB to store catalogs\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "trace" Whether to print stack traces on some errors . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . -.SS "trusted_node_data" -Stores trusted node data in a hash called $trusted\. When true also prevents $trusted from being overridden in any scope\. -. -.IP "\(bu" 4 -\fIDefault\fR: false -. -.IP "" 0 -. .SS "trusted_oid_mapping_file" File that provides mapping between custom SSL oids and user\-friendly names . .IP "\(bu" 4 \fIDefault\fR: $confdir/custom_trusted_oid_mapping\.yaml . .IP "" 0 . .SS "use_cached_catalog" Whether to only use the cached catalog rather than compiling a new catalog on every run\. Puppet can be run with this enabled by default and then selectively disabled when a recompile is desired\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "use_srv_records" Whether the server will search for SRV records in DNS for the current domain\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "usecacheonfailure" Whether to use the cached configuration when the remote configuration will not compile\. This option is useful for testing new configurations, where you want to fix the broken configuration rather than reverting to a known\-good one\. . .IP "\(bu" 4 \fIDefault\fR: true . .IP "" 0 . .SS "user" The user puppet master should run as\. . .IP "\(bu" 4 \fIDefault\fR: puppet . .IP "" 0 . .SS "vardir" Where Puppet stores dynamic and growing data\. The default for this setting is calculated specially, like \fBconfdir\fR_\. . .IP "\(bu" 4 \fIDefault\fR: /var/lib/puppet . .IP "" 0 . .SS "waitforcert" How frequently puppet agent should ask for a signed certificate\. . .P When starting for the first time, puppet agent will submit a certificate signing request (CSR) to the server named in the \fBca_server\fR setting (usually the puppet master); this may be autosigned, or may need to be approved by a human, depending on the CA server\'s configuration\. . .P Puppet agent cannot apply configurations until its approved certificate is available\. Since the certificate may or may not be available immediately, puppet agent will repeatedly try to fetch it at this interval\. You can turn off waiting for certificates by specifying a time of 0, in which case puppet agent will exit if it cannot get a cert\. This setting can be a time interval in seconds (30 or 30s), minutes (30m), hours (6h), days (2d), or years (5y)\. . .IP "\(bu" 4 \fIDefault\fR: 2m . .IP "" 0 . .SS "yamldir" The directory in which YAML data is stored, usually in a subdirectory\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/yaml . .IP "" 0 . .P \fIThis page autogenerated on 2014\-11\-05 19:14:33 \-0800\fR diff --git a/spec/integration/parser/compiler_spec.rb b/spec/integration/parser/compiler_spec.rb index e6069d834..5b59c5669 100755 --- a/spec/integration/parser/compiler_spec.rb +++ b/spec/integration/parser/compiler_spec.rb @@ -1,525 +1,466 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/parser/parser_factory' require 'puppet_spec/compiler' require 'matchers/resource' describe "Puppet::Parser::Compiler" do include PuppetSpec::Compiler include Matchers::Resource before :each do @node = Puppet::Node.new "testnode" @scope_resource = stub 'scope_resource', :builtin? => true, :finish => nil, :ref => 'Class[main]' @scope = stub 'scope', :resource => @scope_resource, :source => mock("source") end it "should be able to determine the configuration version from a local version control repository" do pending("Bug #14071 about semantics of Puppet::Util::Execute on Windows", :if => Puppet.features.microsoft_windows?) do # This should always work, because we should always be # in the puppet repo when we run this. version = %x{git rev-parse HEAD}.chomp Puppet.settings[:config_version] = 'git rev-parse HEAD' @parser = Puppet::Parser::ParserFactory.parser "development" @compiler = Puppet::Parser::Compiler.new(@node) @compiler.catalog.version.should == version end end it "should not create duplicate resources when a class is referenced both directly and indirectly by the node classifier (4792)" do Puppet[:code] = <<-PP class foo { notify { foo_notify: } include bar } class bar { notify { bar_notify: } } PP @node.stubs(:classes).returns(['foo', 'bar']) catalog = Puppet::Parser::Compiler.compile(@node) catalog.resource("Notify[foo_notify]").should_not be_nil catalog.resource("Notify[bar_notify]").should_not be_nil end describe "when resolving class references" do it "should favor local scope, even if there's an included class in topscope" do Puppet[:code] = <<-PP class experiment { class baz { } notify {"x" : require => Class[Baz] } } class baz { } include baz include experiment include experiment::baz PP catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) notify_resource = catalog.resource( "Notify[x]" ) notify_resource[:require].title.should == "Experiment::Baz" end it "should favor local scope, even if there's an unincluded class in topscope" do Puppet[:code] = <<-PP class experiment { class baz { } notify {"x" : require => Class[Baz] } } class baz { } include experiment include experiment::baz PP catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) notify_resource = catalog.resource( "Notify[x]" ) notify_resource[:require].title.should == "Experiment::Baz" end end describe "(ticket #13349) when explicitly specifying top scope" do ["class {'::bar::baz':}", "include ::bar::baz"].each do |include| describe "with #{include}" do it "should find the top level class" do Puppet[:code] = <<-MANIFEST class { 'foo::test': } class foo::test { #{include} } class bar::baz { notify { 'good!': } } class foo::bar::baz { notify { 'bad!': } } MANIFEST catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) catalog.resource("Class[Bar::Baz]").should_not be_nil catalog.resource("Notify[good!]").should_not be_nil catalog.resource("Class[Foo::Bar::Baz]").should be_nil catalog.resource("Notify[bad!]").should be_nil end end end end it "should recompute the version after input files are re-parsed" do Puppet[:code] = 'class foo { }' Time.stubs(:now).returns(1) node = Puppet::Node.new('mynode') Puppet::Parser::Compiler.compile(node).version.should == 1 Time.stubs(:now).returns(2) Puppet::Parser::Compiler.compile(node).version.should == 1 # no change because files didn't change Puppet::Resource::TypeCollection.any_instance.stubs(:stale?).returns(true).then.returns(false) # pretend change Puppet::Parser::Compiler.compile(node).version.should == 2 end ['class', 'define', 'node'].each do |thing| it "should not allow '#{thing}' inside evaluated conditional constructs" do Puppet[:code] = <<-PP if true { #{thing} foo { } notify { decoy: } } PP begin Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) raise "compilation should have raised Puppet::Error" rescue Puppet::Error => e e.message.should =~ /at line 2/ end end end it "should not allow classes inside unevaluated conditional constructs" do Puppet[:code] = <<-PP if false { class foo { } } PP lambda { Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) }.should raise_error(Puppet::Error) end describe "when defining relationships" do def extract_name(ref) ref.sub(/File\[(\w+)\]/, '\1') end let(:node) { Puppet::Node.new('mynode') } let(:code) do <<-MANIFEST file { [a,b,c]: mode => '0644', } file { [d,e]: mode => '0755', } MANIFEST end let(:expected_relationships) { [] } let(:expected_subscriptions) { [] } before :each do Puppet[:code] = code end after :each do catalog = Puppet::Parser::Compiler.compile(node) resources = catalog.resources.select { |res| res.type == 'File' } actual_relationships, actual_subscriptions = [:before, :notify].map do |relation| resources.map do |res| dependents = Array(res[relation]) dependents.map { |ref| [res.title, extract_name(ref)] } end.inject(&:concat) end actual_relationships.should =~ expected_relationships actual_subscriptions.should =~ expected_subscriptions end it "should create a relationship" do code << "File[a] -> File[b]" expected_relationships << ['a','b'] end it "should create a subscription" do code << "File[a] ~> File[b]" expected_subscriptions << ['a', 'b'] end it "should create relationships using title arrays" do code << "File[a,b] -> File[c,d]" expected_relationships.concat [ ['a', 'c'], ['b', 'c'], ['a', 'd'], ['b', 'd'], ] end it "should create relationships using collection expressions" do code << "File <| mode == 0644 |> -> File <| mode == 0755 |>" expected_relationships.concat [ ['a', 'd'], ['b', 'd'], ['c', 'd'], ['a', 'e'], ['b', 'e'], ['c', 'e'], ] end it "should create relationships using resource names" do code << "'File[a]' -> 'File[b]'" expected_relationships << ['a', 'b'] end it "should create relationships using variables" do code << <<-MANIFEST $var = File[a] $var -> File[b] MANIFEST expected_relationships << ['a', 'b'] end it "should create relationships using case statements" do code << <<-MANIFEST $var = 10 case $var { 10: { file { s1: } } 12: { file { s2: } } } -> case $var + 2 { 10: { file { t1: } } 12: { file { t2: } } } MANIFEST expected_relationships << ['s1', 't2'] end it "should create relationships using array members" do code << <<-MANIFEST $var = [ [ [ File[a], File[b] ] ] ] $var[0][0][0] -> $var[0][0][1] MANIFEST expected_relationships << ['a', 'b'] end it "should create relationships using hash members" do code << <<-MANIFEST $var = {'foo' => {'bar' => {'source' => File[a], 'target' => File[b]}}} $var[foo][bar][source] -> $var[foo][bar][target] MANIFEST expected_relationships << ['a', 'b'] end it "should create relationships using resource declarations" do code << "file { l: } -> file { r: }" expected_relationships << ['l', 'r'] end it "should chain relationships" do code << "File[a] -> File[b] ~> File[c] <- File[d] <~ File[e]" expected_relationships << ['a', 'b'] << ['d', 'c'] expected_subscriptions << ['b', 'c'] << ['e', 'd'] end end context 'when working with immutable node data' do - context 'and have opted in to immutable_node_data' do - before :each do - Puppet[:immutable_node_data] = true - end - - def node_with_facts(facts) - Puppet[:facts_terminus] = :memory - Puppet::Node::Facts.indirection.save(Puppet::Node::Facts.new("testing", facts)) - node = Puppet::Node.new("testing") - node.fact_merge - node - end - - matcher :fail_compile_with do |node, message_regex| - match do |manifest| - @error = nil - begin - PuppetSpec::Compiler.compile_to_catalog(manifest, node) - false - rescue Puppet::Error => e - @error = e - message_regex.match(e.message) - end - end + def node_with_facts(facts) + Puppet[:facts_terminus] = :memory + Puppet::Node::Facts.indirection.save(Puppet::Node::Facts.new("testing", facts)) + node = Puppet::Node.new("testing") + node.fact_merge + node + end - failure_message_for_should do - if @error - "failed with #{@error}\n#{@error.backtrace}" - else - "did not fail" - end + matcher :fail_compile_with do |node, message_regex| + match do |manifest| + @error = nil + begin + PuppetSpec::Compiler.compile_to_catalog(manifest, node) + false + rescue Puppet::Error => e + @error = e + message_regex.match(e.message) end end - it 'should make $facts available' do - node = node_with_facts('the_facts' => 'straight') - - catalog = compile_to_catalog(<<-MANIFEST, node) - notify { 'test': message => $facts[the_facts] } - MANIFEST - - catalog.resource("Notify[test]")[:message].should == "straight" + failure_message_for_should do + if @error + "failed with #{@error}\n#{@error.backtrace}" + else + "did not fail" + end end + end - it 'should make $facts reserved' do - node = node_with_facts('the_facts' => 'straight') + it 'should make $facts available' do + node = node_with_facts('the_facts' => 'straight') - expect('$facts = {}').to fail_compile_with(node, /assign to a reserved variable name: 'facts'/) - expect('class a { $facts = {} } include a').to fail_compile_with(node, /assign to a reserved variable name: 'facts'/) - end + catalog = compile_to_catalog(<<-MANIFEST, node) + notify { 'test': message => $facts[the_facts] } + MANIFEST - it 'should make $facts immutable' do - node = node_with_facts('string' => 'value', 'array' => ['string'], 'hash' => { 'a' => 'string' }, 'number' => 1, 'boolean' => true) + catalog.resource("Notify[test]")[:message].should == "straight" + end - expect('$i=inline_template("<% @facts[%q{new}] = 2 %>")').to fail_compile_with(node, /frozen Hash/i) - expect('$i=inline_template("<% @facts[%q{string}].chop! %>")').to fail_compile_with(node, /frozen String/i) + it 'should make $facts reserved' do + node = node_with_facts('the_facts' => 'straight') - expect('$i=inline_template("<% @facts[%q{array}][0].chop! %>")').to fail_compile_with(node, /frozen String/i) - expect('$i=inline_template("<% @facts[%q{array}][1] = 2 %>")').to fail_compile_with(node, /frozen Array/i) + expect('$facts = {}').to fail_compile_with(node, /assign to a reserved variable name: 'facts'/) + expect('class a { $facts = {} } include a').to fail_compile_with(node, /assign to a reserved variable name: 'facts'/) + end - expect('$i=inline_template("<% @facts[%q{hash}][%q{a}].chop! %>")').to fail_compile_with(node, /frozen String/i) - expect('$i=inline_template("<% @facts[%q{hash}][%q{b}] = 2 %>")').to fail_compile_with(node, /frozen Hash/i) - end + it 'should make $facts immutable' do + node = node_with_facts('string' => 'value', 'array' => ['string'], 'hash' => { 'a' => 'string' }, 'number' => 1, 'boolean' => true) - it 'should make $facts available even if there are no facts' do - Puppet[:facts_terminus] = :memory - node = Puppet::Node.new("testing2") - node.fact_merge + expect('$i=inline_template("<% @facts[%q{new}] = 2 %>")').to fail_compile_with(node, /frozen Hash/i) + expect('$i=inline_template("<% @facts[%q{string}].chop! %>")').to fail_compile_with(node, /frozen String/i) - catalog = compile_to_catalog(<<-MANIFEST, node) - notify { 'test': message => $facts } - MANIFEST + expect('$i=inline_template("<% @facts[%q{array}][0].chop! %>")').to fail_compile_with(node, /frozen String/i) + expect('$i=inline_template("<% @facts[%q{array}][1] = 2 %>")').to fail_compile_with(node, /frozen Array/i) - expect(catalog).to have_resource("Notify[test]").with_parameter(:message, {}) - end + expect('$i=inline_template("<% @facts[%q{hash}][%q{a}].chop! %>")').to fail_compile_with(node, /frozen String/i) + expect('$i=inline_template("<% @facts[%q{hash}][%q{b}] = 2 %>")').to fail_compile_with(node, /frozen Hash/i) end - context 'and have not opted in to immutable_node_data' do - before :each do - Puppet[:immutable_node_data] = false - end - - it 'should not make $facts available' do - Puppet[:facts_terminus] = :memory - facts = Puppet::Node::Facts.new("testing", 'the_facts' => 'straight') - Puppet::Node::Facts.indirection.save(facts) - node = Puppet::Node.new("testing") - node.fact_merge + it 'should make $facts available even if there are no facts' do + Puppet[:facts_terminus] = :memory + node = Puppet::Node.new("testing2") + node.fact_merge - catalog = compile_to_catalog(<<-MANIFEST, node) - notify { 'test': message => "An $facts space" } - MANIFEST + catalog = compile_to_catalog(<<-MANIFEST, node) + notify { 'test': message => $facts } + MANIFEST - catalog.resource("Notify[test]")[:message].should == "An space" - end + expect(catalog).to have_resource("Notify[test]").with_parameter(:message, {}) end end context 'when working with the trusted data hash' do - context 'and have opted in to trusted_node_data' do - before :each do - Puppet[:trusted_node_data] = true - end - - it 'should make $trusted available' do - node = Puppet::Node.new("testing") - node.trusted_data = { "data" => "value" } - catalog = compile_to_catalog(<<-MANIFEST, node) - notify { 'test': message => $trusted[data] } - MANIFEST - - catalog.resource("Notify[test]")[:message].should == "value" - end - - it 'should not allow assignment to $trusted' do - node = Puppet::Node.new("testing") - node.trusted_data = { "data" => "value" } - - expect do - catalog = compile_to_catalog(<<-MANIFEST, node) - $trusted = 'changed' - notify { 'test': message => $trusted == 'changed' } - MANIFEST - catalog.resource("Notify[test]")[:message].should == true - end.to raise_error(Puppet::Error, /Attempt to assign to a reserved variable name: 'trusted'/) - end + it 'should make $trusted available' do + node = Puppet::Node.new("testing") + node.trusted_data = { "data" => "value" } - it 'should not allow addition to $trusted hash' do - node = Puppet::Node.new("testing") - node.trusted_data = { "data" => "value" } + catalog = compile_to_catalog(<<-MANIFEST, node) + notify { 'test': message => $trusted[data] } + MANIFEST - expect do - catalog = compile_to_catalog(<<-MANIFEST, node) - $trusted['extra'] = 'added' - notify { 'test': message => $trusted['extra'] == 'added' } - MANIFEST - catalog.resource("Notify[test]")[:message].should == true - # different errors depending on regular or future parser - end.to raise_error(Puppet::Error, /(can't modify frozen [hH]ash)|(Illegal attempt to assign)/) - end + catalog.resource("Notify[test]")[:message].should == "value" + end - it 'should not allow addition to $trusted hash via Ruby inline template' do - node = Puppet::Node.new("testing") - node.trusted_data = { "data" => "value" } + it 'should not allow assignment to $trusted' do + node = Puppet::Node.new("testing") + node.trusted_data = { "data" => "value" } - expect do - catalog = compile_to_catalog(<<-MANIFEST, node) - $dummy = inline_template("<% @trusted['extra'] = 'added' %> lol") - notify { 'test': message => $trusted['extra'] == 'added' } - MANIFEST - catalog.resource("Notify[test]")[:message].should == true - end.to raise_error(Puppet::Error, /can't modify frozen [hH]ash/) - end + expect do + catalog = compile_to_catalog(<<-MANIFEST, node) + $trusted = 'changed' + notify { 'test': message => $trusted == 'changed' } + MANIFEST + catalog.resource("Notify[test]")[:message].should == true + end.to raise_error(Puppet::Error, /Attempt to assign to a reserved variable name: 'trusted'/) end - context 'and have not opted in to trusted_node_data' do - before :each do - Puppet[:trusted_node_data] = false - end - - it 'should not make $trusted available' do - node = Puppet::Node.new("testing") - node.trusted_data = { "data" => "value" } + it 'should not allow addition to $trusted hash' do + node = Puppet::Node.new("testing") + node.trusted_data = { "data" => "value" } + expect do catalog = compile_to_catalog(<<-MANIFEST, node) - notify { 'test': message => $trusted == undef } + $trusted['extra'] = 'added' + notify { 'test': message => $trusted['extra'] == 'added' } MANIFEST - catalog.resource("Notify[test]")[:message].should == true - end + # different errors depending on regular or future parser + end.to raise_error(Puppet::Error, /(can't modify frozen [hH]ash)|(Illegal attempt to assign)/) + end - it 'should allow assignment to $trusted' do - node = Puppet::Node.new("testing") + it 'should not allow addition to $trusted hash via Ruby inline template' do + node = Puppet::Node.new("testing") + node.trusted_data = { "data" => "value" } + expect do catalog = compile_to_catalog(<<-MANIFEST, node) - $trusted = 'changed' - notify { 'test': message => $trusted == 'changed' } + $dummy = inline_template("<% @trusted['extra'] = 'added' %> lol") + notify { 'test': message => $trusted['extra'] == 'added' } MANIFEST - catalog.resource("Notify[test]")[:message].should == true - end + end.to raise_error(Puppet::Error, /can't modify frozen [hH]ash/) end end context 'when evaluating collection' do it 'matches on container inherited tags' do Puppet[:code] = <<-MANIFEST class xport_test { tag 'foo_bar' @notify { 'nbr1': message => 'explicitly tagged', tag => 'foo_bar' } @notify { 'nbr2': message => 'implicitly tagged' } Notify <| tag == 'foo_bar' |> { message => 'overridden' } } include xport_test MANIFEST catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) expect(catalog).to have_resource("Notify[nbr1]").with_parameter(:message, 'overridden') expect(catalog).to have_resource("Notify[nbr2]").with_parameter(:message, 'overridden') end end end diff --git a/spec/integration/parser/future_compiler_spec.rb b/spec/integration/parser/future_compiler_spec.rb index dcd204f5f..f82f263d5 100644 --- a/spec/integration/parser/future_compiler_spec.rb +++ b/spec/integration/parser/future_compiler_spec.rb @@ -1,806 +1,775 @@ require 'spec_helper' require 'puppet/pops' require 'puppet/parser/parser_factory' require 'puppet_spec/compiler' require 'puppet_spec/pops' require 'puppet_spec/scope' require 'matchers/resource' require 'rgen/metamodel_builder' # Test compilation using the future evaluator describe "Puppet::Parser::Compiler" do include PuppetSpec::Compiler include Matchers::Resource before :each do Puppet[:parser] = 'future' end describe "the compiler when using future parser and evaluator" do it "should be able to determine the configuration version from a local version control repository" do pending("Bug #14071 about semantics of Puppet::Util::Execute on Windows", :if => Puppet.features.microsoft_windows?) do # This should always work, because we should always be # in the puppet repo when we run this. version = %x{git rev-parse HEAD}.chomp Puppet.settings[:config_version] = 'git rev-parse HEAD' compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("testnode")) compiler.catalog.version.should == version end end it "should not create duplicate resources when a class is referenced both directly and indirectly by the node classifier (4792)" do node = Puppet::Node.new("testnodex") node.classes = ['foo', 'bar'] catalog = compile_to_catalog(<<-PP, node) class foo { notify { foo_notify: } include bar } class bar { notify { bar_notify: } } PP catalog = Puppet::Parser::Compiler.compile(node) expect(catalog).to have_resource("Notify[foo_notify]") expect(catalog).to have_resource("Notify[bar_notify]") end it 'applies defaults for defines with qualified names (PUP-2302)' do catalog = compile_to_catalog(<<-CODE) define my::thing($msg = 'foo') { notify {'check_me': message => $msg } } My::Thing { msg => 'evoe' } my::thing { 'name': } CODE expect(catalog).to have_resource("Notify[check_me]").with_parameter(:message, "evoe") end it 'Applies defaults from dynamic scopes (3x and future with reverted PUP-867)' do catalog = compile_to_catalog(<<-CODE) class a { Notify { message => "defaulted" } include b notify { bye: } } class b { notify { hi: } } include a CODE expect(catalog).to have_resource("Notify[hi]").with_parameter(:message, "defaulted") expect(catalog).to have_resource("Notify[bye]").with_parameter(:message, "defaulted") end it 'gets default from inherited class (PUP-867)' do catalog = compile_to_catalog(<<-CODE) class a { Notify { message => "defaulted" } include c notify { bye: } } class b { Notify { message => "inherited" } } class c inherits b { notify { hi: } } include a CODE expect(catalog).to have_resource("Notify[hi]").with_parameter(:message, "inherited") expect(catalog).to have_resource("Notify[bye]").with_parameter(:message, "defaulted") end it 'looks up default parameter values from inherited class (PUP-2532)' do catalog = compile_to_catalog(<<-CODE) class a { Notify { message => "defaulted" } include c notify { bye: } } class b { Notify { message => "inherited" } } class c inherits b { notify { hi: } } include a notify {hi_test: message => Notify[hi][message] } notify {bye_test: message => Notify[bye][message] } CODE expect(catalog).to have_resource("Notify[hi_test]").with_parameter(:message, "inherited") expect(catalog).to have_resource("Notify[bye_test]").with_parameter(:message, "defaulted") end it 'does not allow override of class parameters using a resource override expression' do expect do compile_to_catalog(<<-CODE) Class[a] { x => 2} CODE end.to raise_error(/Resource Override can only.*got: Class\[a\].*/) end describe "when resolving class references" do it "should not favor local scope (with class included in topscope)" do catalog = compile_to_catalog(<<-PP) class experiment { class baz { } notify {"x" : require => Class[Baz] } notify {"y" : require => Class[Experiment::Baz] } } class baz { } include baz include experiment include experiment::baz PP expect(catalog).to have_resource("Notify[x]").with_parameter(:require, be_resource("Class[Baz]")) expect(catalog).to have_resource("Notify[y]").with_parameter(:require, be_resource("Class[Experiment::Baz]")) end it "should not favor local scope, (with class not included in topscope)" do catalog = compile_to_catalog(<<-PP) class experiment { class baz { } notify {"x" : require => Class[Baz] } notify {"y" : require => Class[Experiment::Baz] } } class baz { } include experiment include experiment::baz PP expect(catalog).to have_resource("Notify[x]").with_parameter(:require, be_resource("Class[Baz]")) expect(catalog).to have_resource("Notify[y]").with_parameter(:require, be_resource("Class[Experiment::Baz]")) end end describe "(ticket #13349) when explicitly specifying top scope" do ["class {'::bar::baz':}", "include ::bar::baz"].each do |include| describe "with #{include}" do it "should find the top level class" do catalog = compile_to_catalog(<<-MANIFEST) class { 'foo::test': } class foo::test { #{include} } class bar::baz { notify { 'good!': } } class foo::bar::baz { notify { 'bad!': } } MANIFEST expect(catalog).to have_resource("Class[Bar::Baz]") expect(catalog).to have_resource("Notify[good!]") expect(catalog).to_not have_resource("Class[Foo::Bar::Baz]") expect(catalog).to_not have_resource("Notify[bad!]") end end end end it "should recompute the version after input files are re-parsed" do Puppet[:code] = 'class foo { }' Time.stubs(:now).returns(1) node = Puppet::Node.new('mynode') Puppet::Parser::Compiler.compile(node).version.should == 1 Time.stubs(:now).returns(2) Puppet::Parser::Compiler.compile(node).version.should == 1 # no change because files didn't change Puppet::Resource::TypeCollection.any_instance.stubs(:stale?).returns(true).then.returns(false) # pretend change Puppet::Parser::Compiler.compile(node).version.should == 2 end ['define', 'class', 'node'].each do |thing| it "'#{thing}' is not allowed inside evaluated conditional constructs" do expect do compile_to_catalog(<<-PP) if true { #{thing} foo { } notify { decoy: } } PP end.to raise_error(Puppet::Error, /Classes, definitions, and nodes may only appear at toplevel/) end it "'#{thing}' is not allowed inside un-evaluated conditional constructs" do expect do compile_to_catalog(<<-PP) if false { #{thing} foo { } notify { decoy: } } PP end.to raise_error(Puppet::Error, /Classes, definitions, and nodes may only appear at toplevel/) end end describe "relationships can be formed" do def extract_name(ref) ref.sub(/File\[(\w+)\]/, '\1') end def assert_creates_relationships(relationship_code, expectations) base_manifest = <<-MANIFEST file { [a,b,c]: mode => '0644', } file { [d,e]: mode => '0755', } MANIFEST catalog = compile_to_catalog(base_manifest + relationship_code) resources = catalog.resources.select { |res| res.type == 'File' } actual_relationships, actual_subscriptions = [:before, :notify].map do |relation| resources.map do |res| dependents = Array(res[relation]) dependents.map { |ref| [res.title, extract_name(ref)] } end.inject(&:concat) end actual_relationships.should =~ (expectations[:relationships] || []) actual_subscriptions.should =~ (expectations[:subscriptions] || []) end it "of regular type" do assert_creates_relationships("File[a] -> File[b]", :relationships => [['a','b']]) end it "of subscription type" do assert_creates_relationships("File[a] ~> File[b]", :subscriptions => [['a', 'b']]) end it "between multiple resources expressed as resource with multiple titles" do assert_creates_relationships("File[a,b] -> File[c,d]", :relationships => [['a', 'c'], ['b', 'c'], ['a', 'd'], ['b', 'd']]) end it "between collection expressions" do assert_creates_relationships("File <| mode == '0644' |> -> File <| mode == '0755' |>", :relationships => [['a', 'd'], ['b', 'd'], ['c', 'd'], ['a', 'e'], ['b', 'e'], ['c', 'e']]) end it "between resources expressed as Strings" do assert_creates_relationships("'File[a]' -> 'File[b]'", :relationships => [['a', 'b']]) end it "between resources expressed as variables" do assert_creates_relationships(<<-MANIFEST, :relationships => [['a', 'b']]) $var = File[a] $var -> File[b] MANIFEST end it "between resources expressed as case statements" do assert_creates_relationships(<<-MANIFEST, :relationships => [['s1', 't2']]) $var = 10 case $var { 10: { file { s1: } } 12: { file { s2: } } } -> case $var + 2 { 10: { file { t1: } } 12: { file { t2: } } } MANIFEST end it "using deep access in array" do assert_creates_relationships(<<-MANIFEST, :relationships => [['a', 'b']]) $var = [ [ [ File[a], File[b] ] ] ] $var[0][0][0] -> $var[0][0][1] MANIFEST end it "using deep access in hash" do assert_creates_relationships(<<-MANIFEST, :relationships => [['a', 'b']]) $var = {'foo' => {'bar' => {'source' => File[a], 'target' => File[b]}}} $var[foo][bar][source] -> $var[foo][bar][target] MANIFEST end it "using resource declarations" do assert_creates_relationships("file { l: } -> file { r: }", :relationships => [['l', 'r']]) end it "between entries in a chain of relationships" do assert_creates_relationships("File[a] -> File[b] ~> File[c] <- File[d] <~ File[e]", :relationships => [['a', 'b'], ['d', 'c']], :subscriptions => [['b', 'c'], ['e', 'd']]) end end context "when dealing with variable references" do it 'an initial underscore in a variable name is ok' do catalog = compile_to_catalog(<<-MANIFEST) class a { $_a = 10} include a notify { 'test': message => $a::_a } MANIFEST expect(catalog).to have_resource("Notify[test]").with_parameter(:message, 10) end it 'an initial underscore in not ok if elsewhere than last segment' do expect do catalog = compile_to_catalog(<<-MANIFEST) class a { $_a = 10} include a notify { 'test': message => $_a::_a } MANIFEST end.to raise_error(/Illegal variable name/) end it 'a missing variable as default value becomes undef' do # strict variables not on catalog = compile_to_catalog(<<-MANIFEST) class a ($b=$x) { notify {test: message=>"yes ${undef == $b}" } } include a MANIFEST expect(catalog).to have_resource("Notify[test]").with_parameter(:message, "yes true") end end context 'when working with the trusted data hash' do - context 'and have opted in to hashed_node_data' do - before :each do - Puppet[:trusted_node_data] = true - end - - it 'should make $trusted available' do - node = Puppet::Node.new("testing") - node.trusted_data = { "data" => "value" } - catalog = compile_to_catalog(<<-MANIFEST, node) - notify { 'test': message => $trusted[data] } - MANIFEST - - expect(catalog).to have_resource("Notify[test]").with_parameter(:message, "value") - end + it 'should make $trusted available' do + node = Puppet::Node.new("testing") + node.trusted_data = { "data" => "value" } - it 'should not allow assignment to $trusted' do - node = Puppet::Node.new("testing") - node.trusted_data = { "data" => "value" } + catalog = compile_to_catalog(<<-MANIFEST, node) + notify { 'test': message => $trusted[data] } + MANIFEST - expect do - compile_to_catalog(<<-MANIFEST, node) - $trusted = 'changed' - notify { 'test': message => $trusted == 'changed' } - MANIFEST - end.to raise_error(Puppet::Error, /Attempt to assign to a reserved variable name: 'trusted'/) - end + expect(catalog).to have_resource("Notify[test]").with_parameter(:message, "value") end - context 'and have not opted in to hashed_node_data' do - before :each do - Puppet[:trusted_node_data] = false - end - - it 'should not make $trusted available' do - node = Puppet::Node.new("testing") - node.trusted_data = { "data" => "value" } - - catalog = compile_to_catalog(<<-MANIFEST, node) - notify { 'test': message => ($trusted == undef) } - MANIFEST - - expect(catalog).to have_resource("Notify[test]").with_parameter(:message, true) - end + it 'should not allow assignment to $trusted' do + node = Puppet::Node.new("testing") + node.trusted_data = { "data" => "value" } - it 'should allow assignment to $trusted' do - catalog = compile_to_catalog(<<-MANIFEST) + expect do + compile_to_catalog(<<-MANIFEST, node) $trusted = 'changed' notify { 'test': message => $trusted == 'changed' } MANIFEST - - expect(catalog).to have_resource("Notify[test]").with_parameter(:message, true) - end + end.to raise_error(Puppet::Error, /Attempt to assign to a reserved variable name: 'trusted'/) end end context 'when using typed parameters in definition' do it 'accepts type compliant arguments' do catalog = compile_to_catalog(<<-MANIFEST) define foo(String $x) { } foo { 'test': x =>'say friend' } MANIFEST expect(catalog).to have_resource("Foo[test]").with_parameter(:x, 'say friend') end it 'accepts undef as the default for an Optional argument' do catalog = compile_to_catalog(<<-MANIFEST) define foo(Optional[String] $x = undef) { notify { "expected": message => $x == undef } } foo { 'test': } MANIFEST expect(catalog).to have_resource("Notify[expected]").with_parameter(:message, true) end it 'accepts anything when parameters are untyped' do expect do catalog = compile_to_catalog(<<-MANIFEST) define foo($a, $b, $c) { } foo { 'test': a => String, b=>10, c=>undef } MANIFEST end.to_not raise_error() end it 'denies non type compliant arguments' do expect do catalog = compile_to_catalog(<<-MANIFEST) define foo(Integer $x) { } foo { 'test': x =>'say friend' } MANIFEST end.to raise_error(/type Integer, got String/) end it 'denies undef for a non-optional type' do expect do catalog = compile_to_catalog(<<-MANIFEST) define foo(Integer $x) { } foo { 'test': x => undef } MANIFEST end.to raise_error(/type Integer, got Undef/) end it 'denies non type compliant default argument' do expect do catalog = compile_to_catalog(<<-MANIFEST) define foo(Integer $x = 'pow') { } foo { 'test': } MANIFEST end.to raise_error(/type Integer, got String/) end it 'denies undef as the default for a non-optional type' do expect do catalog = compile_to_catalog(<<-MANIFEST) define foo(Integer $x = undef) { } foo { 'test': } MANIFEST end.to raise_error(/type Integer, got Undef/) end it 'accepts a Resource as a Type' do catalog = compile_to_catalog(<<-MANIFEST) define foo(Type[Bar] $x) { notify { 'test': message => $x[text] } } define bar($text) { } bar { 'joke': text => 'knock knock' } foo { 'test': x => Bar[joke] } MANIFEST expect(catalog).to have_resource("Notify[test]").with_parameter(:message, 'knock knock') end end context 'when using typed parameters in class' do it 'accepts type compliant arguments' do catalog = compile_to_catalog(<<-MANIFEST) class foo(String $x) { } class { 'foo': x =>'say friend' } MANIFEST expect(catalog).to have_resource("Class[Foo]").with_parameter(:x, 'say friend') end it 'accepts undef as the default for an Optional argument' do catalog = compile_to_catalog(<<-MANIFEST) class foo(Optional[String] $x = undef) { notify { "expected": message => $x == undef } } class { 'foo': } MANIFEST expect(catalog).to have_resource("Notify[expected]").with_parameter(:message, true) end it 'accepts anything when parameters are untyped' do expect do catalog = compile_to_catalog(<<-MANIFEST) class foo($a, $b, $c) { } class { 'foo': a => String, b=>10, c=>undef } MANIFEST end.to_not raise_error() end it 'denies non type compliant arguments' do expect do catalog = compile_to_catalog(<<-MANIFEST) class foo(Integer $x) { } class { 'foo': x =>'say friend' } MANIFEST end.to raise_error(/type Integer, got String/) end it 'denies undef for a non-optional type' do expect do catalog = compile_to_catalog(<<-MANIFEST) class foo(Integer $x) { } class { 'foo': x => undef } MANIFEST end.to raise_error(/type Integer, got Undef/) end it 'denies non type compliant default argument' do expect do catalog = compile_to_catalog(<<-MANIFEST) class foo(Integer $x = 'pow') { } class { 'foo': } MANIFEST end.to raise_error(/type Integer, got String/) end it 'denies undef as the default for a non-optional type' do expect do catalog = compile_to_catalog(<<-MANIFEST) class foo(Integer $x = undef) { } class { 'foo': } MANIFEST end.to raise_error(/type Integer, got Undef/) end it 'accepts a Resource as a Type' do catalog = compile_to_catalog(<<-MANIFEST) class foo(Type[Bar] $x) { notify { 'test': message => $x[text] } } define bar($text) { } bar { 'joke': text => 'knock knock' } class { 'foo': x => Bar[joke] } MANIFEST expect(catalog).to have_resource("Notify[test]").with_parameter(:message, 'knock knock') end end context 'when using typed parameters in lambdas' do it 'accepts type compliant arguments' do catalog = compile_to_catalog(<<-MANIFEST) with('value') |String $x| { notify { "$x": } } MANIFEST expect(catalog).to have_resource("Notify[value]") end it 'handles an array as a single argument' do catalog = compile_to_catalog(<<-MANIFEST) with(['value', 'second']) |$x| { notify { "${x[0]} ${x[1]}": } } MANIFEST expect(catalog).to have_resource("Notify[value second]") end it 'denies when missing required arguments' do expect do compile_to_catalog(<<-MANIFEST) with(1) |$x, $y| { } MANIFEST end.to raise_error(/Parameter \$y is required but no value was given/m) end it 'accepts anything when parameters are untyped' do catalog = compile_to_catalog(<<-MANIFEST) ['value', 1, true, undef].each |$x| { notify { "value: $x": } } MANIFEST expect(catalog).to have_resource("Notify[value: value]") expect(catalog).to have_resource("Notify[value: 1]") expect(catalog).to have_resource("Notify[value: true]") expect(catalog).to have_resource("Notify[value: ]") end it 'accepts type-compliant, slurped arguments' do catalog = compile_to_catalog(<<-MANIFEST) with(1, 2) |Integer *$x| { notify { "${$x[0] + $x[1]}": } } MANIFEST expect(catalog).to have_resource("Notify[3]") end it 'denies non-type-compliant arguments' do expect do compile_to_catalog(<<-MANIFEST) with(1) |String $x| { } MANIFEST end.to raise_error(/expected.*String.*actual.*Integer/m) end it 'denies non-type-compliant, slurped arguments' do expect do compile_to_catalog(<<-MANIFEST) with(1, "hello") |Integer *$x| { } MANIFEST end.to raise_error(/called with mis-matched arguments.*expected.*Integer.*actual.*Integer, String/m) end it 'denies non-type-compliant default argument' do expect do compile_to_catalog(<<-MANIFEST) with(1) |$x, String $defaulted = 1| { notify { "${$x + $defaulted}": }} MANIFEST end.to raise_error(/expected.*Any.*String.*actual.*Integer.*Integer/m) end it 'raises an error when a default argument value is an incorrect type and there are no arguments passed' do expect do compile_to_catalog(<<-MANIFEST) with() |String $defaulted = 1| {} MANIFEST end.to raise_error(/expected.*String.*actual.*Integer/m) end it 'raises an error when the default argument for a slurped parameter is an incorrect type' do expect do compile_to_catalog(<<-MANIFEST) with() |String *$defaulted = 1| {} MANIFEST end.to raise_error(/expected.*String.*actual.*Integer/m) end it 'allows using an array as the default slurped value' do catalog = compile_to_catalog(<<-MANIFEST) with() |String *$defaulted = [hi]| { notify { $defaulted[0]: } } MANIFEST expect(catalog).to have_resource('Notify[hi]') end it 'allows using a value of the type as the default slurped value' do catalog = compile_to_catalog(<<-MANIFEST) with() |String *$defaulted = hi| { notify { $defaulted[0]: } } MANIFEST expect(catalog).to have_resource('Notify[hi]') end it 'allows specifying the type of a slurped parameter as an array' do catalog = compile_to_catalog(<<-MANIFEST) with() |Array[String] *$defaulted = hi| { notify { $defaulted[0]: } } MANIFEST expect(catalog).to have_resource('Notify[hi]') end it 'raises an error when the number of default values does not match the parameter\'s size specification' do expect do compile_to_catalog(<<-MANIFEST) with() |Array[String, 2] *$defaulted = hi| { } MANIFEST end.to raise_error(/expected.*arg count \{2,\}.*actual.*arg count \{1\}/m) end it 'raises an error when the number of passed values does not match the parameter\'s size specification' do expect do compile_to_catalog(<<-MANIFEST) with(hi) |Array[String, 2] *$passed| { } MANIFEST end.to raise_error(/expected.*arg count \{2,\}.*actual.*arg count \{1\}/m) end it 'matches when the number of arguments passed for a slurp parameter match the size specification' do catalog = compile_to_catalog(<<-MANIFEST) with(hi, bye) |Array[String, 2] *$passed| { $passed.each |$n| { notify { $n: } } } MANIFEST expect(catalog).to have_resource('Notify[hi]') expect(catalog).to have_resource('Notify[bye]') end it 'raises an error when the number of allowed slurp parameters exceeds the size constraint' do expect do compile_to_catalog(<<-MANIFEST) with(hi, bye) |Array[String, 1, 1] *$passed| { } MANIFEST end.to raise_error(/expected.*arg count \{1\}.*actual.*arg count \{2\}/m) end it 'allows passing slurped arrays by specifying an array of arrays' do catalog = compile_to_catalog(<<-MANIFEST) with([hi], [bye]) |Array[Array[String, 1, 1]] *$passed| { notify { $passed[0][0]: } notify { $passed[1][0]: } } MANIFEST expect(catalog).to have_resource('Notify[hi]') expect(catalog).to have_resource('Notify[bye]') end it 'raises an error when a required argument follows an optional one' do expect do compile_to_catalog(<<-MANIFEST) with() |$y = first, $x, Array[String, 1] *$passed = bye| {} MANIFEST end.to raise_error(/Parameter \$x is required/) end it 'raises an error when the minimum size of a slurped argument makes it required and it follows an optional argument' do expect do compile_to_catalog(<<-MANIFEST) with() |$x = first, Array[String, 1] *$passed| {} MANIFEST end.to raise_error(/Parameter \$passed is required/) end it 'allows slurped arguments with a minimum size of 0 after an optional argument' do catalog = compile_to_catalog(<<-MANIFEST) with() |$x = first, Array[String, 0] *$passed| { notify { $x: } } MANIFEST expect(catalog).to have_resource('Notify[first]') end it 'accepts a Resource as a Type' do catalog = compile_to_catalog(<<-MANIFEST) define bar($text) { } bar { 'joke': text => 'knock knock' } with(Bar[joke]) |Type[Bar] $joke| { notify { "${joke[text]}": } } MANIFEST expect(catalog).to have_resource("Notify[knock knock]") end end end context 'when evaluating collection' do it 'matches on container inherited tags' do Puppet[:code] = <<-MANIFEST class xport_test { tag('foo_bar') @notify { 'nbr1': message => 'explicitly tagged', tag => 'foo_bar' } @notify { 'nbr2': message => 'implicitly tagged' } Notify <| tag == 'foo_bar' |> { message => 'overridden' } } include xport_test MANIFEST catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) expect(catalog).to have_resource("Notify[nbr1]").with_parameter(:message, 'overridden') expect(catalog).to have_resource("Notify[nbr2]").with_parameter(:message, 'overridden') end end end