class Node::Environment

Puppet::Node::Environment acts as a container for all configuration that is expected to vary between environments.

## The root environment

In addition to normal environments that are defined by the user,there is a special 'root' environment. It is defined as an instance variable on the Puppet::Node::Environment metaclass. The environment name is `root` and can be accessed by looking up the `:root_environment` using {Puppet.lookup}.

The primary purpose of the root environment is to contain parser functions that are not bound to a specific environment. The main case for this is for logging functions. Logging functions are attached to the 'root' environment when {Puppet::Parser::Functions.reset} is called.

Constants

NONE

A special “null” environment

This environment should be used when there is no specific environment in effect.

NO_MANIFEST

Attributes

config_version[R]

@!attribute [r] config_version

@api public
@return [String] path to a script whose output will be added to report logs
  (optional)
configured_path[RW]

For use with versioned dirs our environment path may contain symlinks, while we want to resolve the path while reading the manifests we may want to report the resources as coming from the configured path.

loaders[RW]

Cached loaders - management of value handled by Puppet::Pops::Loaders @api private

lock[R]

Lock for compilation that needs exclusive access to the environment @api private

manifest[R]

@!attribute [r] manifest

@api public
@return [String] path to the manifest file or directory.
name[R]

@!attribute [r] name

@api public
@return [Symbol] the human readable environment name that serves as the
  environment identifier
resolved_path[RW]

See :configured_path above

Public Class Methods

create(name, modulepath, manifest = NO_MANIFEST, config_version = nil) click to toggle source

Create a new environment with the given name

@param name [Symbol] the name of the environment @param modulepath [Array<String>] the list of paths from which to load modules @param manifest [String] the path to the manifest for the environment or

the constant Puppet::Node::Environment::NO_MANIFEST if there is none.

@param config_version [String] path to a script whose output will be added

to report logs (optional)

@return [Puppet::Node::Environment]

@api public

   # File lib/puppet/node/environment.rb
43 def self.create(name, modulepath, manifest = NO_MANIFEST, config_version = nil)
44   new(name, modulepath, manifest, config_version)
45 end
expand_dirs(dirs) click to toggle source

not private so it can be called in initialize

    # File lib/puppet/node/environment.rb
561 def self.expand_dirs(dirs)
562   dirs.collect do |dir|
563     Puppet::FileSystem.expand_path(dir)
564   end
565 end
extralibs() click to toggle source

not private so it can be called in tests

    # File lib/puppet/node/environment.rb
552 def self.extralibs()
553   if ENV['PUPPETLIB']
554     split_path(ENV['PUPPETLIB'])
555   else
556     []
557   end
558 end
new(name, modulepath, manifest, config_version) click to toggle source

Instantiate a new environment

@note {Puppet::Node::Environment.new} is private for historical reasons, as

previously it had been overridden to return memoized objects and was
replaced with {Puppet::Node::Environment.create}, so this will not be
invoked with the normal Ruby initialization semantics.

@param name [Symbol] The environment name

   # File lib/puppet/node/environment.rb
74 def initialize(name, modulepath, manifest, config_version)
75   @lock = Puppet::Concurrent::Lock.new
76   @name = name.intern
77   @modulepath = self.class.expand_dirs(self.class.extralibs() + modulepath)
78   @manifest = manifest == NO_MANIFEST ? manifest : Puppet::FileSystem.expand_path(manifest)
79 
80   @config_version = config_version
81 end
remote(name) click to toggle source

A “reference” to a remote environment. The created environment instance isn't expected to exist on the local system, but is instead a reference to environment information on a remote system. For instance when a catalog is being applied, this will be used on the agent.

@note This does not provide access to the information of the remote environment's modules, manifest, or anything else. It is simply a value object to pass around and use as an environment.

@param name [Symbol] The name of the remote environment

   # File lib/puppet/node/environment.rb
62 def self.remote(name)
63   Remote.create(name, [], NO_MANIFEST)
64 end
split_path(path_string) click to toggle source
    # File lib/puppet/node/environment.rb
534 def self.split_path(path_string)
535   path_string.split(File::PATH_SEPARATOR)
536 end
valid_name?(name) click to toggle source

@param [String] name Environment name to check for valid syntax. @return [Boolean] true if name is valid @api public

    # File lib/puppet/node/environment.rb
127 def self.valid_name?(name)
128   !!name.match(/\A\w+\Z/)
129 end

Private Class Methods

new(closure_scope, loader) click to toggle source
Calls superclass method Object::new
   # File lib/puppet/functions/match.rb
49 def initialize(closure_scope, loader)
50   super
51 
52   # Make this visitor shared among all instantiations of this function since it is faster.
53   # This can be used because it is not possible to replace
54   # a puppet runtime (where this function is) without a reboot. If you model a function in a module after
55   # this class, use a regular instance variable instead to enable reloading of the module without reboot
56   #
57   @@match_visitor   ||= Puppet::Pops::Visitor.new(self, "match", 1, 1)
58 end

Public Instance Methods

==(other) click to toggle source
    # File lib/puppet/node/environment.rb
538 def ==(other)
539   return true if other.kind_of?(Puppet::Node::Environment) &&
540     self.name == other.name &&
541     self.full_modulepath == other.full_modulepath &&
542     self.manifest == other.manifest
543 end
Also aliased as: eql?
[](param) click to toggle source

Return an environment-specific Puppet setting.

@api public

@param param [String, Symbol] The environment setting to look up @return [Object] The resolved setting value

    # File lib/puppet/node/environment.rb
280 def [](param)
281   Puppet.settings.value(param, self.name)
282 end
check_for_reparse() click to toggle source

Checks if a reparse is required (cache of files is stale).

    # File lib/puppet/node/environment.rb
497 def check_for_reparse
498   @lock.synchronize do
499     if (Puppet[:code] != @parsed_code || @known_resource_types.parse_failed?)
500       @parsed_code = nil
501       @known_resource_types = nil
502     end
503   end
504 end
configuration() click to toggle source

Return the environment configuration @return [Puppet::Settings::EnvironmentConf] The configuration

@api private

    # File lib/puppet/node/environment.rb
241 def configuration
242   Puppet.lookup(:environments).get_conf(name)
243 end
conflicting_manifest_settings?() click to toggle source

Checks to make sure that this environment did not have a manifest set in its original environment.conf if Puppet is configured with disable_per_environment_manifest set true. If it did, the environment's modules may not function as intended by the original authors, and we may seek to halt a puppet compilation for a node in this environment.

The only exception to this would be if the environment.conf manifest is an exact, uninterpolated match for the current default_manifest setting.

@return [Boolean] true if using directory environments, and

Puppet[:disable_per_environment_manifest] is true, and this environment's
original environment.conf had a manifest setting that is not the
Puppet[:default_manifest].

@api private

    # File lib/puppet/node/environment.rb
222 def conflicting_manifest_settings?
223   return false if !Puppet[:disable_per_environment_manifest]
224   original_manifest = configuration.raw_setting(:manifest)
225   !original_manifest.nil? && !original_manifest.empty? && original_manifest != Puppet[:default_manifest]
226 end
each_plugin_directory() { |lib| ... } click to toggle source

Yields each modules' plugin directory if the plugin directory (modulename/lib) is present on the filesystem.

@yield [String] Yields the plugin directory from each module to the block. @api public

    # File lib/puppet/node/environment.rb
301 def each_plugin_directory(&block)
302   modules.map(&:plugin_directory).each do |lib|
303     lib = Puppet::Util::Autoload.cleanpath(lib)
304     yield lib if File.directory?(lib)
305   end
306 end
eql?(other)
Alias for: ==
externalize_path(filepath) click to toggle source

Ensure the path given is of the format we want in the catalog/report.

Intended for use with versioned symlinked environments. If this environment is configured with “/etc/puppetlabs/code/environments/production” but the resolved path is

“/opt/puppetlabs/server/puppetserver/filesync/client/puppet-code/production_abcdef1234”

this changes the filepath

“/opt/puppetlabs/server/puppetserver/filesync/client/puppet-code/production_abcdef1234/modules/foo/manifests/init.pp”

to

“/etc/puppetlabs/code/environments/production/modules/foo/manifests/init.pp”

    # File lib/puppet/node/environment.rb
194 def externalize_path(filepath)
195   paths_set        = configured_path && resolved_path
196   munging_possible = paths_set && configured_path != resolved_path
197   munging_desired  = munging_possible &&
198                        Puppet[:report_configured_environmentpath] &&
199                        filepath.to_s.start_with?(resolved_path)
200 
201   if munging_desired
202     File.join(configured_path, filepath.delete_prefix(resolved_path))
203   else
204     filepath
205   end
206 end
full_modulepath() click to toggle source

@api public @return [Array<String>] All directories in the modulepath (even if they are not present on disk)

    # File lib/puppet/node/environment.rb
147 def full_modulepath
148   @modulepath
149 end
hash() click to toggle source
    # File lib/puppet/node/environment.rb
547 def hash
548   [self.class, name, full_modulepath, manifest].hash
549 end
inspect() click to toggle source

@api public

    # File lib/puppet/node/environment.rb
519 def inspect
520   %Q{<#{self.class}:#{self.object_id} @name="#{name}" @manifest="#{manifest}" @modulepath="#{full_modulepath.join(":")}" >}
521 end
known_resource_types() click to toggle source

@api public @return [Puppet::Resource::TypeCollection] The current global TypeCollection

    # File lib/puppet/node/environment.rb
286 def known_resource_types
287   @lock.synchronize do
288     if @known_resource_types.nil?
289       @known_resource_types = Puppet::Resource::TypeCollection.new(self)
290       @known_resource_types.import_ast(perform_initial_import(), '')
291     end
292     @known_resource_types
293   end
294 end
module(name) click to toggle source

Locate a module instance by the module name alone.

@api public

@param name [String] The module name @return [Puppet::Module, nil] The module if found, else nil

    # File lib/puppet/node/environment.rb
314 def module(name)
315   modules_by_name[name]
316 end
module_by_forge_name(forge_name) click to toggle source

Locate a module instance by the full forge name (EG authorname/module)

@api public

@param forge_name [String] The module name @return [Puppet::Module, nil] The module if found, else nil

    # File lib/puppet/node/environment.rb
324 def module_by_forge_name(forge_name)
325   _, modname = forge_name.split('/')
326   found_mod = self.module(modname)
327   found_mod and found_mod.forge_name == forge_name ?
328     found_mod :
329     nil
330 end
module_requirements() click to toggle source

All module requirements for all modules in the environment modulepath

@api public

@comment This has nothing to do with an environment. It seems like it was

stuffed into the first convenient class that vaguely involved modules.

@example

environment.module_requirements
# => {
#   'username/amodule' => [
#     {
#       'name'    => 'username/moduledep',
#       'version' => '1.2.3',
#       'version_requirement' => '>= 1.0.0',
#     },
#     {
#       'name'    => 'username/anotherdep',
#       'version' => '4.5.6',
#       'version_requirement' => '>= 3.0.0',
#     }
#   ]
# }
#

@return [Hash<String, Array<Hash<String, String>>>] See the method example

for an explanation of the return value.
    # File lib/puppet/node/environment.rb
451 def module_requirements
452   deps = {}
453 
454   modules.each do |mod|
455     next unless mod.forge_name
456     deps[mod.forge_name] ||= []
457 
458     mod.dependencies and mod.dependencies.each do |mod_dep|
459       dep_name = mod_dep['name'].tr('-', '/')
460       (deps[dep_name] ||= []) << {
461         'name'                => mod.forge_name,
462         'version'             => mod.version,
463         'version_requirement' => mod_dep['version_requirement']
464       }
465     end
466   end
467 
468   deps.each do |mod, mod_deps|
469     deps[mod] = mod_deps.sort_by { |d| d['name'] }
470   end
471 
472   deps
473 end
modulepath() click to toggle source

@api public @return [Array<String>] All directories present on disk in the modulepath

    # File lib/puppet/node/environment.rb
139 def modulepath
140   @modulepath.find_all do |p|
141     Puppet::FileSystem.directory?(p)
142   end
143 end
modules() click to toggle source

Return all modules for this environment in the order they appear in the modulepath. @note If multiple modules with the same name are present they will

both be added, but methods like {#module} and {#module_by_forge_name}
will return the first matching entry in this list.

@note This value is cached so that the filesystem doesn't have to be

re-enumerated every time this method is invoked, since that
enumeration could be a costly operation and this method is called
frequently. The cache expiry is determined by `Puppet[:filetimeout]`.

@api public @return [Array<Puppet::Module>] All modules for this environment

    # File lib/puppet/node/environment.rb
343 def modules
344   if @modules.nil?
345     module_references = []
346     project = Puppet.lookup(:bolt_project) { nil }
347     seen_modules = if project && project.load_as_module?
348                      module_references << project.to_h
349                      { project.name => true }
350                    else
351                      {}
352                    end
353     modulepath.each do |path|
354       Puppet::FileSystem.children(path).map do |p|
355         Puppet::FileSystem.basename_string(p)
356       end.each do |name|
357         next unless Puppet::Module.is_module_directory?(name, path)
358         warn_about_mistaken_path(path, name)
359         if not seen_modules[name]
360           module_references << {:name => name, :path => File.join(path, name)}
361           seen_modules[name] = true
362         end
363       end
364     end
365 
366     @modules = module_references.collect do |reference|
367       begin
368         Puppet::Module.new(reference[:name], reference[:path], self)
369       rescue Puppet::Module::Error => e
370         Puppet.log_exception(e)
371         nil
372       end
373     end.compact
374   end
375   @modules
376 end
modules_by_path() click to toggle source

Modules broken out by directory in the modulepath

@api public

@return [Hash<String, Array<Puppet::Module>>] A hash whose keys are file

paths, and whose values is an array of Puppet Modules for that path
    # File lib/puppet/node/environment.rb
405 def modules_by_path
406   modules_by_path = {}
407   modulepath.each do |path|
408     if Puppet::FileSystem.exist?(path)
409       module_names = Puppet::FileSystem.children(path).map do |p|
410         Puppet::FileSystem.basename_string(p)
411       end.select do |name|
412         Puppet::Module.is_module_directory?(name, path)
413       end
414       modules_by_path[path] = module_names.sort.map do |name|
415         Puppet::Module.new(name, File.join(path, name), self)
416       end
417     else
418       modules_by_path[path] = []
419     end
420   end
421   modules_by_path
422 end
override_from_commandline(settings) click to toggle source

Creates a new Puppet::Node::Environment instance, overriding :manifest, :modulepath, or :config_version from the passed settings if they were originally set from the commandline, or returns self if there is nothing to override.

@param settings [Puppet::Settings] an initialized puppet settings instance @return [Puppet::Node::Environment] new overridden environment or self if

there are no commandline changes from settings.
    # File lib/puppet/node/environment.rb
104 def override_from_commandline(settings)
105   overrides = {}
106 
107   if settings.set_by_cli?(:modulepath)
108     overrides[:modulepath] = self.class.split_path(settings.value(:modulepath))
109   end
110 
111   if settings.set_by_cli?(:config_version)
112     overrides[:config_version] = settings.value(:config_version)
113   end
114 
115   if settings.set_by_cli?(:manifest)
116     overrides[:manifest] = settings.value(:manifest)
117   end
118 
119   overrides.empty? ?
120     self :
121     self.override_with(overrides)
122 end
override_with(env_params) click to toggle source

Creates a new Puppet::Node::Environment instance, overriding any of the passed parameters.

@param env_params [Hash<{Symbol => String,Array<String>}>] new environment

parameters (:modulepath, :manifest, :config_version)

@return [Puppet::Node::Environment]

   # File lib/puppet/node/environment.rb
89 def override_with(env_params)
90   return self.class.create(name,
91                     env_params[:modulepath] || modulepath,
92                     env_params[:manifest] || manifest,
93                     env_params[:config_version] || config_version)
94 end
rich_data?() click to toggle source

Checks if this environment permits use of rich data types in the catalog Checks the environment conf for an override on first query, then going forward either uses that, or if unset, uses the current value of the `rich_data` setting. @return [Boolean] `true` if rich data is permitted. @api private

    # File lib/puppet/node/environment.rb
270 def rich_data?
271   @rich_data = rich_data_from_env_conf.nil? ? Puppet[:rich_data] : rich_data_from_env_conf
272 end
rich_data_from_env_conf() click to toggle source
    # File lib/puppet/node/environment.rb
256 def rich_data_from_env_conf
257   unless @checked_conf_for_rich_data
258     environment_conf = Puppet.lookup(:environments).get_conf(name)
259     @rich_data_from_conf = environment_conf&.rich_data
260     @checked_conf_for_rich_data = true
261   end
262   @rich_data_from_conf
263 end
static_catalogs?() click to toggle source

@api private

    # File lib/puppet/node/environment.rb
229 def static_catalogs?
230   if @static_catalogs.nil?
231     environment_conf = Puppet.lookup(:environments).get_conf(name)
232     @static_catalogs = (environment_conf.nil? ? Puppet[:static_catalogs] : environment_conf.static_catalogs)
233   end
234   @static_catalogs
235 end
to_s() click to toggle source

@return [String] The stringified value of the `name` instance variable @api public

    # File lib/puppet/node/environment.rb
514 def to_s
515   name.to_s
516 end
to_sym() click to toggle source

@return [Symbol] The `name` value, cast to a string, then cast to a symbol.

@api public

@note the `name` instance variable is a Symbol, but this casts the value

to a String and then converts it back into a Symbol which will needlessly
create an object that needs to be garbage collected
    # File lib/puppet/node/environment.rb
530 def to_sym
531   to_s.to_sym
532 end
to_yaml() click to toggle source

@return [String] The YAML interpretation of the object Return the name of the environment as a string interpretation of the object

    # File lib/puppet/node/environment.rb
508 def to_yaml
509   to_s.to_yaml
510 end
validation_errors() click to toggle source

Checks the environment and settings for any conflicts @return [Array<String>] an array of validation errors @api public

    # File lib/puppet/node/environment.rb
248 def validation_errors
249   errors = []
250   if conflicting_manifest_settings?
251     errors << _("The 'disable_per_environment_manifest' setting is true, and the '%{env_name}' environment has an environment.conf manifest that conflicts with the 'default_manifest' setting.") % { env_name: name }
252   end
253   errors
254 end
warn_about_mistaken_path(path, name) click to toggle source

Generate a warning if the given directory in a module path entry is named `lib`.

@api private

@param path [String] The module directory containing the given directory @param name [String] The directory name

    # File lib/puppet/node/environment.rb
390 def warn_about_mistaken_path(path, name)
391   if name == "lib"
392     Puppet.debug {
393       "Warning: Found directory named 'lib' in module path ('#{path}/lib'); unless you \
394       are expecting to load a module named 'lib', your module path may be set incorrectly."
395     }
396   end
397 end
with_text_domain() { || ... } click to toggle source

Loads module translations for the current environment once for the lifetime of the environment. Execute a block in the context of that translation domain.

    # File lib/puppet/node/environment.rb
478 def with_text_domain
479   return yield if Puppet[:disable_i18n]
480 
481   if @text_domain.nil?
482     @text_domain = @name
483     Puppet::GettextConfig.reset_text_domain(@text_domain)
484     Puppet::ModuleTranslations.load_from_modulepath(modules)
485   else
486     Puppet::GettextConfig.use_text_domain(@text_domain)
487   end
488 
489   yield
490 ensure
491   # Is a noop if disable_i18n is true
492   Puppet::GettextConfig.clear_text_domain
493 end

Private Instance Methods

empty_parse_result() click to toggle source

Return an empty top-level hostclass to indicate that no file was loaded

@return [Puppet::Parser::AST::Hostclass]

    # File lib/puppet/node/environment.rb
622 def empty_parse_result
623   return Puppet::Parser::AST::Hostclass.new('')
624 end
modules_by_name() click to toggle source

@api private

    # File lib/puppet/node/environment.rb
379 def modules_by_name
380   @modules_by_name ||= Hash[modules.map { |mod| [mod.name, mod] }]
381 end
perform_initial_import() click to toggle source

Reparse the manifests for the given environment

There are two sources that can be used for the initial parse:

1. The value of `Puppet[:code]`: Puppet can take a string from
  its settings and parse that as a manifest. This is used by various
  Puppet applications to read in a manifest and pass it to the
  environment as a side effect. This is attempted first.
2. The contents of this environment's +manifest+ attribute: Puppet will
  try to load the environment manifest.

@return [Puppet::Parser::AST::Hostclass] The AST hostclass object

representing the 'main' hostclass
    # File lib/puppet/node/environment.rb
582 def perform_initial_import
583   parser = Puppet::Parser::ParserFactory.parser
584   @parsed_code = Puppet[:code]
585   if @parsed_code != ""
586     parser.string = @parsed_code
587     parser.parse
588   else
589     file = self.manifest
590     # if the manifest file is a reference to a directory, parse and combine
591     # all .pp files in that directory
592     if file == NO_MANIFEST
593       empty_parse_result
594     elsif File.directory?(file)
595       parse_results = Puppet::FileSystem::PathPattern.absolute(File.join(file, '**/*.pp')).glob.sort.map do | file_to_parse |
596         parser.file = file_to_parse
597         parser.parse
598         end
599       # Use a parser type specific merger to concatenate the results
600       Puppet::Parser::AST::Hostclass.new('', :code => Puppet::Parser::ParserFactory.code_merger.concatenate(parse_results))
601     else
602       parser.file = file
603       parser.parse
604     end
605   end
606 rescue Puppet::ParseErrorWithIssue => detail
607   @known_resource_types.parse_failed = true
608   detail.environment = self.name
609   raise
610 rescue => detail
611   @known_resource_types.parse_failed = true
612 
613   msg = _("Could not parse for environment %{env}: %{detail}") % { env: self, detail: detail }
614   error = Puppet::Error.new(msg)
615   error.set_backtrace(detail.backtrace)
616   raise error
617 end