Class: Puppet::Node::Environment

Inherits:
Object
  • Object
show all
Defined in:
lib/puppet/node/environment.rb

Overview

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 Parser::Functions.reset is called.

Direct Known Subclasses

None, Remote

Defined Under Namespace

Classes: None, Remote

Constant Summary collapse

NO_MANIFEST =
:no_manifest

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, modulepath, manifest, config_version) ⇒ Environment

Note:

new is private for historical reasons, as previously it had been overridden to return memoized objects and was replaced with create, so this will not be invoked with the normal Ruby initialization semantics.

Instantiate a new environment

Parameters:

  • name (Symbol)

    The environment name



74
75
76
77
78
79
80
81
# File 'lib/puppet/node/environment.rb', line 74

def initialize(name, modulepath, manifest, config_version)
  @lock = Puppet::Concurrent::Lock.new
  @name = name.intern
  @modulepath = self.class.expand_dirs(self.class.extralibs() + modulepath)
  @manifest = manifest == NO_MANIFEST ? manifest : Puppet::FileSystem.expand_path(manifest)

  @config_version = config_version
end

Instance Attribute Details

#config_versionObject (readonly)

Returns the value of attribute config_version.



160
161
162
# File 'lib/puppet/node/environment.rb', line 160

def config_version
  @config_version
end

#configured_pathObject

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.



174
175
176
# File 'lib/puppet/node/environment.rb', line 174

def configured_path
  @configured_path
end

#loadersObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

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



164
165
166
# File 'lib/puppet/node/environment.rb', line 164

def loaders
  @loaders
end

#lockObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Lock for compilation that needs exclusive access to the environment



168
169
170
# File 'lib/puppet/node/environment.rb', line 168

def lock
  @lock
end

#manifestObject (readonly)

Returns the value of attribute manifest.



154
155
156
# File 'lib/puppet/node/environment.rb', line 154

def manifest
  @manifest
end

#nameObject (readonly)

Returns the value of attribute name.



135
136
137
# File 'lib/puppet/node/environment.rb', line 135

def name
  @name
end

#resolved_pathObject

See :configured_path above



177
178
179
# File 'lib/puppet/node/environment.rb', line 177

def resolved_path
  @resolved_path
end

Class Method Details

.create(name, modulepath, manifest = NO_MANIFEST, config_version = nil) ⇒ Puppet::Node::Environment

Create a new environment with the given name

Parameters:

  • name (Symbol)

    the name of the environment

  • modulepath (Array<String>)

    the list of paths from which to load modules

  • manifest (String) (defaults to: NO_MANIFEST)

    the path to the manifest for the environment or the constant Puppet::Node::Environment::NO_MANIFEST if there is none.

  • config_version (String) (defaults to: nil)

    path to a script whose output will be added to report logs (optional)

Returns:



43
44
45
# File 'lib/puppet/node/environment.rb', line 43

def self.create(name, modulepath, manifest = NO_MANIFEST, config_version = nil)
  new(name, modulepath, manifest, config_version)
end

.expand_dirs(dirs) ⇒ Object

not private so it can be called in initialize



562
563
564
565
566
# File 'lib/puppet/node/environment.rb', line 562

def self.expand_dirs(dirs)
  dirs.collect do |dir|
    Puppet::FileSystem.expand_path(dir)
  end
end

.extralibsObject

not private so it can be called in tests



553
554
555
556
557
558
559
# File 'lib/puppet/node/environment.rb', line 553

def self.extralibs
  if ENV['PUPPETLIB']
    split_path(ENV.fetch('PUPPETLIB', nil))
  else
    []
  end
end

.remote(name) ⇒ Object

Note:

This does not provide access to the information of the remote

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.

environment’s modules, manifest, or anything else. It is simply a value object to pass around and use as an environment.

Parameters:

  • name (Symbol)

    The name of the remote environment



62
63
64
# File 'lib/puppet/node/environment.rb', line 62

def self.remote(name)
  Remote.create(name, [], NO_MANIFEST)
end

.split_path(path_string) ⇒ Object



535
536
537
# File 'lib/puppet/node/environment.rb', line 535

def self.split_path(path_string)
  path_string.split(File::PATH_SEPARATOR)
end

.valid_name?(name) ⇒ Boolean

Returns true if name is valid.

Parameters:

  • name (String)

    Environment name to check for valid syntax.

Returns:

  • (Boolean)

    true if name is valid



127
128
129
# File 'lib/puppet/node/environment.rb', line 127

def self.valid_name?(name)
  !!name.match(/\A\w+\Z/)
end

Instance Method Details

#==(other) ⇒ Object Also known as: eql?



539
540
541
542
543
544
# File 'lib/puppet/node/environment.rb', line 539

def ==(other)
  true if other.is_a?(Puppet::Node::Environment) &&
          name == other.name &&
          full_modulepath == other.full_modulepath &&
          manifest == other.manifest
end

#[](param) ⇒ Object

Return an environment-specific Puppet setting.

Parameters:

  • param (String, Symbol)

    The environment setting to look up

Returns:

  • (Object)

    The resolved setting value



281
282
283
# File 'lib/puppet/node/environment.rb', line 281

def [](param)
  Puppet.settings.value(param, name)
end

#check_for_reparseObject

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



498
499
500
501
502
503
504
505
# File 'lib/puppet/node/environment.rb', line 498

def check_for_reparse
  @lock.synchronize do
    if Puppet[:code] != @parsed_code || @known_resource_types.parse_failed?
      @parsed_code = nil
      @known_resource_types = nil
    end
  end
end

#configurationPuppet::Settings::EnvironmentConf

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Return the environment configuration

Returns:



242
243
244
# File 'lib/puppet/node/environment.rb', line 242

def configuration
  Puppet.lookup(:environments).get_conf(name)
end

#conflicting_manifest_settings?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

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.

Returns:

  • (Boolean)

    true if using directory environments, and Puppet is true, and this environment’s original environment.conf had a manifest setting that is not the Puppet.



222
223
224
225
226
227
# File 'lib/puppet/node/environment.rb', line 222

def conflicting_manifest_settings?
  return false unless Puppet[:disable_per_environment_manifest]

  original_manifest = configuration.raw_setting(:manifest)
  !original_manifest.nil? && !original_manifest.empty? && original_manifest != Puppet[:default_manifest]
end

#each_plugin_directory {|String| ... } ⇒ Object

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

Yields:

  • (String)

    Yields the plugin directory from each module to the block.



302
303
304
305
306
307
# File 'lib/puppet/node/environment.rb', line 302

def each_plugin_directory(&block)
  modules.map(&:plugin_directory).each do |lib|
    lib = Puppet::Util::Autoload.cleanpath(lib)
    yield lib if File.directory?(lib)
  end
end

#externalize_path(filepath) ⇒ Object

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”



194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/puppet/node/environment.rb', line 194

def externalize_path(filepath)
  paths_set        = configured_path && resolved_path
  munging_possible = paths_set && configured_path != resolved_path
  munging_desired  = munging_possible &&
                     Puppet[:report_configured_environmentpath] &&
                     filepath.to_s.start_with?(resolved_path)

  if munging_desired
    File.join(configured_path, filepath.delete_prefix(resolved_path))
  else
    filepath
  end
end

#full_modulepathArray<String>

Returns All directories in the modulepath (even if they are not present on disk).

Returns:

  • (Array<String>)

    All directories in the modulepath (even if they are not present on disk)



147
148
149
# File 'lib/puppet/node/environment.rb', line 147

def full_modulepath
  @modulepath
end

#hashObject



548
549
550
# File 'lib/puppet/node/environment.rb', line 548

def hash
  [self.class, name, full_modulepath, manifest].hash
end

#inspectObject



520
521
522
# File 'lib/puppet/node/environment.rb', line 520

def inspect
  %Q(<#{self.class}:#{object_id} @name="#{name}" @manifest="#{manifest}" @modulepath="#{full_modulepath.join(':')}" >)
end

#known_resource_typesPuppet::Resource::TypeCollection

Returns The current global TypeCollection.

Returns:



287
288
289
290
291
292
293
294
295
# File 'lib/puppet/node/environment.rb', line 287

def known_resource_types
  @lock.synchronize do
    if @known_resource_types.nil?
      @known_resource_types = Puppet::Resource::TypeCollection.new(self)
      @known_resource_types.import_ast(perform_initial_import(), '')
    end
    @known_resource_types
  end
end

#module(name) ⇒ Puppet::Module?

Locate a module instance by the module name alone.

Parameters:

  • name (String)

    The module name

Returns:



315
316
317
# File 'lib/puppet/node/environment.rb', line 315

def module(name)
  modules_by_name[name]
end

#module_by_forge_name(forge_name) ⇒ Puppet::Module?

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

Parameters:

  • forge_name (String)

    The module name

Returns:



325
326
327
328
329
330
331
# File 'lib/puppet/node/environment.rb', line 325

def module_by_forge_name(forge_name)
  _, modname = forge_name.split('/')
  found_mod = self.module(modname)
  found_mod and found_mod.forge_name == forge_name ?
    found_mod :
    nil
end

#module_requirementsHash<String, Array<Hash<String, String>>>

All module requirements for all modules in the environment modulepath

Examples:

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',
#     }
#   ]
# }
#

Returns:

  • (Hash<String, Array<Hash<String, String>>>)

    See the method example for an explanation of the return value.



451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
# File 'lib/puppet/node/environment.rb', line 451

def module_requirements
  deps = {}

  modules.each do |mod|
    next unless mod.forge_name

    deps[mod.forge_name] ||= []

    mod.dependencies and mod.dependencies.each do |mod_dep|
      dep_name = mod_dep['name'].tr('-', '/')
      (deps[dep_name] ||= []) << {
        'name' => mod.forge_name,
        'version' => mod.version,
        'version_requirement' => mod_dep['version_requirement']
      }
    end
  end

  deps.each do |mod, mod_deps|
    deps[mod] = mod_deps.sort_by { |d| d['name'] }
  end

  deps
end

#modulepathArray<String>

Returns All directories present on disk in the modulepath.

Returns:

  • (Array<String>)

    All directories present on disk in the modulepath



139
140
141
142
143
# File 'lib/puppet/node/environment.rb', line 139

def modulepath
  @modulepath.find_all do |p|
    Puppet::FileSystem.directory?(p)
  end
end

#modulesArray<Puppet::Module>

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`.

Return all modules for this environment in the order they appear in the modulepath.

Returns:



344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
# File 'lib/puppet/node/environment.rb', line 344

def modules
  if @modules.nil?
    module_references = []
    project = Puppet.lookup(:bolt_project) { nil }
    seen_modules = if project && project.load_as_module?
                     module_references << project.to_h
                     { project.name => true }
                   else
                     {}
                   end
    modulepath.each do |path|
      Puppet::FileSystem.children(path).map do |p|
        Puppet::FileSystem.basename_string(p)
      end.each do |name|
        next unless Puppet::Module.is_module_directory?(name, path)

        warn_about_mistaken_path(path, name)
        unless seen_modules[name]
          module_references << { :name => name, :path => File.join(path, name) }
          seen_modules[name] = true
        end
      end
    end

    @modules = module_references.filter_map do |reference|
      Puppet::Module.new(reference[:name], reference[:path], self)
    rescue Puppet::Module::Error => e
      Puppet.log_exception(e)
      nil
    end
  end
  @modules
end

#modules_by_pathHash<String, Array<Puppet::Module>>

Modules broken out by directory in the modulepath

Returns:

  • (Hash<String, Array<Puppet::Module>>)

    A hash whose keys are file paths, and whose values is an array of Puppet Modules for that path



405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
# File 'lib/puppet/node/environment.rb', line 405

def modules_by_path
  modules_by_path = {}
  modulepath.each do |path|
    if Puppet::FileSystem.exist?(path)
      module_names = Puppet::FileSystem.children(path).map do |p|
        Puppet::FileSystem.basename_string(p)
      end.select do |name|
        Puppet::Module.is_module_directory?(name, path)
      end
      modules_by_path[path] = module_names.sort.map do |name|
        Puppet::Module.new(name, File.join(path, name), self)
      end
    else
      modules_by_path[path] = []
    end
  end
  modules_by_path
end

#override_from_commandline(settings) ⇒ Puppet::Node::Environment

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.

Parameters:

Returns:



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/puppet/node/environment.rb', line 104

def override_from_commandline(settings)
  overrides = {}

  if settings.set_by_cli?(:modulepath)
    overrides[:modulepath] = self.class.split_path(settings.value(:modulepath))
  end

  if settings.set_by_cli?(:config_version)
    overrides[:config_version] = settings.value(:config_version)
  end

  if settings.set_by_cli?(:manifest)
    overrides[:manifest] = settings.value(:manifest)
  end

  overrides.empty? ?
    self :
    override_with(overrides)
end

#override_with(env_params) ⇒ Puppet::Node::Environment

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

Parameters:

  • env_params (Hash<{Symbol => String,Array<String>}>)

    new environment parameters (:modulepath, :manifest, :config_version)

Returns:



89
90
91
92
93
94
# File 'lib/puppet/node/environment.rb', line 89

def override_with(env_params)
  self.class.create(name,
                    env_params[:modulepath] || modulepath,
                    env_params[:manifest] || manifest,
                    env_params[:config_version] || config_version)
end

#rich_data?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

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.

Returns:

  • (Boolean)

    ‘true` if rich data is permitted.



271
272
273
# File 'lib/puppet/node/environment.rb', line 271

def rich_data?
  @rich_data = rich_data_from_env_conf.nil? ? Puppet[:rich_data] : rich_data_from_env_conf
end

#rich_data_from_env_confObject



257
258
259
260
261
262
263
264
# File 'lib/puppet/node/environment.rb', line 257

def rich_data_from_env_conf
  unless @checked_conf_for_rich_data
    environment_conf = Puppet.lookup(:environments).get_conf(name)
    @rich_data_from_conf = environment_conf&.rich_data
    @checked_conf_for_rich_data = true
  end
  @rich_data_from_conf
end

#static_catalogs?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


230
231
232
233
234
235
236
# File 'lib/puppet/node/environment.rb', line 230

def static_catalogs?
  if @static_catalogs.nil?
    environment_conf = Puppet.lookup(:environments).get_conf(name)
    @static_catalogs = (environment_conf.nil? ? Puppet[:static_catalogs] : environment_conf.static_catalogs)
  end
  @static_catalogs
end

#to_sString

Returns The stringified value of the ‘name` instance variable.

Returns:

  • (String)

    The stringified value of the ‘name` instance variable



515
516
517
# File 'lib/puppet/node/environment.rb', line 515

def to_s
  name.to_s
end

#to_symSymbol

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

Returns The ‘name` value, cast to a string, then cast to a symbol.

Returns:

  • (Symbol)

    The ‘name` value, cast to a string, then cast to a symbol.



531
532
533
# File 'lib/puppet/node/environment.rb', line 531

def to_sym
  to_s.to_sym
end

#to_yamlString

Return the name of the environment as a string interpretation of the object

Returns:

  • (String)

    The YAML interpretation of the object



509
510
511
# File 'lib/puppet/node/environment.rb', line 509

def to_yaml
  to_s.to_yaml
end

#validation_errorsArray<String>

Checks the environment and settings for any conflicts

Returns:

  • (Array<String>)

    an array of validation errors



249
250
251
252
253
254
255
# File 'lib/puppet/node/environment.rb', line 249

def validation_errors
  errors = []
  if conflicting_manifest_settings?
    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 }
  end
  errors
end

#warn_about_mistaken_path(path, name) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

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

Parameters:

  • path (String)

    The module directory containing the given directory

  • name (String)

    The directory name



390
391
392
393
394
395
396
397
# File 'lib/puppet/node/environment.rb', line 390

def warn_about_mistaken_path(path, name)
  if name == "lib"
    Puppet.debug {
      "Warning: Found directory named 'lib' in module path ('#{path}/lib'); unless you \
      are expecting to load a module named 'lib', your module path may be set incorrectly."
    }
  end
end

#with_text_domainObject

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



479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
# File 'lib/puppet/node/environment.rb', line 479

def with_text_domain
  return yield if Puppet[:disable_i18n]

  if @text_domain.nil?
    @text_domain = @name
    Puppet::GettextConfig.reset_text_domain(@text_domain)
    Puppet::ModuleTranslations.load_from_modulepath(modules)
  else
    Puppet::GettextConfig.use_text_domain(@text_domain)
  end

  yield
ensure
  # Is a noop if disable_i18n is true
  Puppet::GettextConfig.clear_text_domain
end