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)
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.
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
See :configured_path above
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 43 def self.create(name, modulepath, manifest = NO_MANIFEST, config_version = nil) 44 new(name, modulepath, manifest, config_version) 45 end
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
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
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
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
# File lib/puppet/node/environment.rb 534 def self.split_path(path_string) 535 path_string.split(File::PATH_SEPARATOR) 536 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 127 def self.valid_name?(name) 128 !!name.match(/\A\w+\Z/) 129 end
Private Class Methods
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
# 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
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
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
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
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
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
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
@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
# File lib/puppet/node/environment.rb 547 def hash 548 [self.class, name, full_modulepath, manifest].hash 549 end
@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
@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
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
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
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
@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
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 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
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
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
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
# 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
@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
@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
@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
@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
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
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
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
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
@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
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