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
@!attribute [r] config_version
@api public @return [String] path to a script whose output will be added to report logs (optional)
Cached loaders - management of value handled by Puppet::Pops::Loaders
@api private
Lock for compilation that needs exclusive access to the environment @api private
@!attribute [r] manifest
@api public @return [String] path to the manifest file or directory.
@!attribute [r] name
@api public @return [Symbol] the human readable environment name that serves as the environment identifier
Public Class Methods
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
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
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
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
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
# File lib/puppet/node/environment.rb 495 def self.split_path(path_string) 496 path_string.split(File::PATH_SEPARATOR) 497 end
@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
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
# 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
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
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
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
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
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
@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
# File lib/puppet/node/environment.rb 508 def hash 509 [self.class, name, full_modulepath, manifest].hash 510 end
@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
@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
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
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
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
@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
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 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
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
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
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
# 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
@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
@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
@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
@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
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
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
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
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
@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
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