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)
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

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
42 def self.create(name, modulepath, manifest = NO_MANIFEST, config_version = nil)
43   new(name, modulepath, manifest, config_version)
44 end
expand_dirs(dirs) click to toggle source

not private so it can be called in initialize

    # File lib/puppet/node/environment.rb
522 def self.expand_dirs(dirs)
523   dirs.collect do |dir|
524     Puppet::FileSystem.expand_path(dir)
525   end
526 end
extralibs() click to toggle source

not private so it can be called in tests

    # File lib/puppet/node/environment.rb
513 def self.extralibs()
514   if Puppet::Util.get_env('PUPPETLIB')
515     split_path(Puppet::Util.get_env('PUPPETLIB'))
516   else
517     []
518   end
519 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
73 def initialize(name, modulepath, manifest, config_version)
74   @lock = Puppet::Concurrent::Lock.new
75   @name = name.intern
76   @modulepath = self.class.expand_dirs(self.class.extralibs() + modulepath)
77   @manifest = manifest == NO_MANIFEST ? manifest : Puppet::FileSystem.expand_path(manifest)
78 
79   @config_version = config_version
80 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
61 def self.remote(name)
62   Remote.create(name, [], NO_MANIFEST)
63 end
split_path(path_string) click to toggle source
    # File lib/puppet/node/environment.rb
495 def self.split_path(path_string)
496   path_string.split(File::PATH_SEPARATOR)
497 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
126 def self.valid_name?(name)
127   !!name.match(/\A\w+\Z/)
128 end

Private Class Methods

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

Public Instance Methods

==(other) click to toggle source
    # File lib/puppet/node/environment.rb
499 def ==(other)
500   return true if other.kind_of?(Puppet::Node::Environment) &&
501     self.name == other.name &&
502     self.full_modulepath == other.full_modulepath &&
503     self.manifest == other.manifest
504 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
241 def [](param)
242   Puppet.settings.value(param, self.name)
243 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
458 def check_for_reparse
459   @lock.synchronize do
460     if (Puppet[:code] != @parsed_code || @known_resource_types.parse_failed?)
461       @parsed_code = nil
462       @known_resource_types = nil
463     end
464   end
465 end
configuration() click to toggle source

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

@api private

    # File lib/puppet/node/environment.rb
202 def configuration
203   Puppet.lookup(:environments).get_conf(name)
204 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
183 def conflicting_manifest_settings?
184   return false if !Puppet[:disable_per_environment_manifest]
185   original_manifest = configuration.raw_setting(:manifest)
186   !original_manifest.nil? && !original_manifest.empty? && original_manifest != Puppet[:default_manifest]
187 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
262 def each_plugin_directory(&block)
263   modules.map(&:plugin_directory).each do |lib|
264     lib = Puppet::Util::Autoload.cleanpath(lib)
265     yield lib if File.directory?(lib)
266   end
267 end
eql?(other)
Alias for: ==
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
146 def full_modulepath
147   @modulepath
148 end
hash() click to toggle source
    # File lib/puppet/node/environment.rb
508 def hash
509   [self.class, name, full_modulepath, manifest].hash
510 end
inspect() click to toggle source

@api public

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

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

    # File lib/puppet/node/environment.rb
247 def known_resource_types
248   @lock.synchronize do
249     if @known_resource_types.nil?
250       @known_resource_types = Puppet::Resource::TypeCollection.new(self)
251       @known_resource_types.import_ast(perform_initial_import(), '')
252     end
253     @known_resource_types
254   end
255 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
275 def module(name)
276   modules_by_name[name]
277 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
285 def module_by_forge_name(forge_name)
286   _, modname = forge_name.split('/')
287   found_mod = self.module(modname)
288   found_mod and found_mod.forge_name == forge_name ?
289     found_mod :
290     nil
291 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
412 def module_requirements
413   deps = {}
414 
415   modules.each do |mod|
416     next unless mod.forge_name
417     deps[mod.forge_name] ||= []
418 
419     mod.dependencies and mod.dependencies.each do |mod_dep|
420       dep_name = mod_dep['name'].tr('-', '/')
421       (deps[dep_name] ||= []) << {
422         'name'                => mod.forge_name,
423         'version'             => mod.version,
424         'version_requirement' => mod_dep['version_requirement']
425       }
426     end
427   end
428 
429   deps.each do |mod, mod_deps|
430     deps[mod] = mod_deps.sort_by { |d| d['name'] }
431   end
432 
433   deps
434 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
138 def modulepath
139   @modulepath.find_all do |p|
140     Puppet::FileSystem.directory?(p)
141   end
142 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
304 def modules
305   if @modules.nil?
306     module_references = []
307     project = Puppet.lookup(:bolt_project) { nil }
308     seen_modules = if project && project.load_as_module?
309                      module_references << project.to_h
310                      { project.name => true }
311                    else
312                      {}
313                    end
314     modulepath.each do |path|
315       Puppet::FileSystem.children(path).map do |p|
316         Puppet::FileSystem.basename_string(p)
317       end.each do |name|
318         next unless Puppet::Module.is_module_directory?(name, path)
319         warn_about_mistaken_path(path, name)
320         if not seen_modules[name]
321           module_references << {:name => name, :path => File.join(path, name)}
322           seen_modules[name] = true
323         end
324       end
325     end
326 
327     @modules = module_references.collect do |reference|
328       begin
329         Puppet::Module.new(reference[:name], reference[:path], self)
330       rescue Puppet::Module::Error => e
331         Puppet.log_exception(e)
332         nil
333       end
334     end.compact
335   end
336   @modules
337 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
366 def modules_by_path
367   modules_by_path = {}
368   modulepath.each do |path|
369     if Puppet::FileSystem.exist?(path)
370       module_names = Puppet::FileSystem.children(path).map do |p|
371         Puppet::FileSystem.basename_string(p)
372       end.select do |name|
373         Puppet::Module.is_module_directory?(name, path)
374       end
375       modules_by_path[path] = module_names.sort.map do |name|
376         Puppet::Module.new(name, File.join(path, name), self)
377       end
378     else
379       modules_by_path[path] = []
380     end
381   end
382   modules_by_path
383 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
103 def override_from_commandline(settings)
104   overrides = {}
105 
106   if settings.set_by_cli?(:modulepath)
107     overrides[:modulepath] = self.class.split_path(settings.value(:modulepath))
108   end
109 
110   if settings.set_by_cli?(:config_version)
111     overrides[:config_version] = settings.value(:config_version)
112   end
113 
114   if settings.set_by_cli?(:manifest)
115     overrides[:manifest] = settings.value(:manifest)
116   end
117 
118   overrides.empty? ?
119     self :
120     self.override_with(overrides)
121 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
88 def override_with(env_params)
89   return self.class.create(name,
90                     env_params[:modulepath] || modulepath,
91                     env_params[:manifest] || manifest,
92                     env_params[:config_version] || config_version)
93 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
231 def rich_data?
232   @rich_data = rich_data_from_env_conf.nil? ? Puppet[:rich_data] : rich_data_from_env_conf
233 end
rich_data_from_env_conf() click to toggle source
    # File lib/puppet/node/environment.rb
217 def rich_data_from_env_conf
218   unless @checked_conf_for_rich_data
219     environment_conf = Puppet.lookup(:environments).get_conf(name)
220     @rich_data_from_conf = environment_conf&.rich_data
221     @checked_conf_for_rich_data = true
222   end
223   @rich_data_from_conf
224 end
static_catalogs?() click to toggle source

@api private

    # File lib/puppet/node/environment.rb
190 def static_catalogs?
191   if @static_catalogs.nil?
192     environment_conf = Puppet.lookup(:environments).get_conf(name)
193     @static_catalogs = (environment_conf.nil? ? Puppet[:static_catalogs] : environment_conf.static_catalogs)
194   end
195   @static_catalogs
196 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
475 def to_s
476   name.to_s
477 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
491 def to_sym
492   to_s.to_sym
493 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
469 def to_yaml
470   to_s.to_yaml
471 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
209 def validation_errors
210   errors = []
211   if conflicting_manifest_settings?
212     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 }
213   end
214   errors
215 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
351 def warn_about_mistaken_path(path, name)
352   if name == "lib"
353     Puppet.debug {
354       "Warning: Found directory named 'lib' in module path ('#{path}/lib'); unless you \
355       are expecting to load a module named 'lib', your module path may be set incorrectly."
356     }
357   end
358 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
439 def with_text_domain
440   return yield if Puppet[:disable_i18n]
441 
442   if @text_domain.nil?
443     @text_domain = @name
444     Puppet::GettextConfig.reset_text_domain(@text_domain)
445     Puppet::ModuleTranslations.load_from_modulepath(modules)
446   else
447     Puppet::GettextConfig.use_text_domain(@text_domain)
448   end
449 
450   yield
451 ensure
452   # Is a noop if disable_i18n is true
453   Puppet::GettextConfig.clear_text_domain
454 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
583 def empty_parse_result
584   return Puppet::Parser::AST::Hostclass.new('')
585 end
modules_by_name() click to toggle source

@api private

    # File lib/puppet/node/environment.rb
340 def modules_by_name
341   @modules_by_name ||= Hash[modules.map { |mod| [mod.name, mod] }]
342 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
543 def perform_initial_import
544   parser = Puppet::Parser::ParserFactory.parser
545   @parsed_code = Puppet[:code]
546   if @parsed_code != ""
547     parser.string = @parsed_code
548     parser.parse
549   else
550     file = self.manifest
551     # if the manifest file is a reference to a directory, parse and combine
552     # all .pp files in that directory
553     if file == NO_MANIFEST
554       empty_parse_result
555     elsif File.directory?(file)
556       parse_results = Puppet::FileSystem::PathPattern.absolute(File.join(file, '**/*.pp')).glob.sort.map do | file_to_parse |
557         parser.file = file_to_parse
558         parser.parse
559         end
560       # Use a parser type specific merger to concatenate the results
561       Puppet::Parser::AST::Hostclass.new('', :code => Puppet::Parser::ParserFactory.code_merger.concatenate(parse_results))
562     else
563       parser.file = file
564       parser.parse
565     end
566   end
567 rescue Puppet::ParseErrorWithIssue => detail
568   @known_resource_types.parse_failed = true
569   detail.environment = self.name
570   raise
571 rescue => detail
572   @known_resource_types.parse_failed = true
573 
574   msg = _("Could not parse for environment %{env}: %{detail}") % { env: self, detail: detail }
575   error = Puppet::Error.new(msg)
576   error.set_backtrace(detail.backtrace)
577   raise error
578 end