class Puppet::Pops::Loaders
This is the container for all Loader
instances. Each Loader
instance has a `loader_name` by which it can be uniquely identified within this container. A Loader
can be private or public. In general, code will have access to the private loader associated with the location of the code. It will be parented by a loader that in turn have access to other public loaders that can load only such entries that have been publicly available. The split between public and private is not yet enforced in Puppet
.
The name of a private loader should always end with ' private'
Attributes
Public Class Methods
Finds a loader to use when deserializing a catalog and then subsequenlty use user defined types found in that catalog.
# File lib/puppet/pops/loaders.rb 161 def self.catalog_loader 162 loaders = Puppet.lookup(:loaders) { nil } 163 if loaders.nil? 164 loaders = Loaders.new(Puppet.lookup(:current_environment), true) 165 Puppet.push_context(:loaders => loaders) 166 end 167 loaders.find_loader(nil) 168 end
Clears the cached static and puppet_system loaders (to enable testing)
# File lib/puppet/pops/loaders.rb 75 def self.clear 76 @@static_loader = nil 77 Puppet::Pops::Types::TypeFactory.clear 78 Model.class_variable_set(:@@pcore_ast_initialized, false) 79 Model.register_pcore_types 80 end
Calls {#loaders} to obtain the {{Loaders}} instance and then uses it to find the appropriate loader for the given `module_name`, or for the environment in case `module_name` is `nil` or empty.
@param module_name [String,nil] the name of the module @return [Loader::Loader] the found loader @raise [Puppet::ParseError] if no loader can be found @api private
# File lib/puppet/pops/loaders.rb 89 def self.find_loader(module_name) 90 loaders.find_loader(module_name) 91 end
# File lib/puppet/pops/loaders.rb 112 def self.implementation_registry 113 loaders = Puppet.lookup(:loaders) { nil } 114 loaders.nil? ? nil : loaders.implementation_registry 115 end
Finds the `Loaders` instance by looking up the :loaders in the global Puppet
context
@return [Loaders] the loaders instance @raise [Puppet::ParseError] if loader has been bound to the global context @api private
# File lib/puppet/pops/loaders.rb 175 def self.loaders 176 loaders = Puppet.lookup(:loaders) { nil } 177 raise Puppet::ParseError, _("Internal Error: Puppet Context ':loaders' missing") if loaders.nil? 178 loaders 179 end
# File lib/puppet/pops/loaders.rb 21 def self.new(environment, for_agent = false, load_from_pcore = true) 22 environment.lock.synchronize do 23 obj = environment.loaders 24 if obj.nil? 25 obj = self.allocate 26 obj.send(:initialize, environment, for_agent, load_from_pcore) 27 end 28 obj 29 end 30 end
# File lib/puppet/pops/loaders.rb 32 def initialize(environment, for_agent, load_from_pcore = true) 33 # Protect against environment havoc 34 raise ArgumentError.new(_("Attempt to redefine already initialized loaders for environment")) unless environment.loaders.nil? 35 environment.loaders = self 36 @environment = environment 37 @loaders_by_name = {} 38 39 add_loader_by_name(self.class.static_loader) 40 41 # Create the set of loaders 42 # 1. Puppet, loads from the "running" puppet - i.e. bundled functions, types, extension points and extensions 43 # These cannot be cached since a loaded instance will be bound to its closure scope which holds on to 44 # a compiler and all loaded types. Subsequent request would find remains of the environment that loaded 45 # the content. PUP-4461. 46 # 47 @puppet_system_loader = create_puppet_system_loader() 48 49 # 2. Cache loader(optional) - i.e. what puppet stores on disk via pluginsync; gate behind the for_agent flag. 50 # 3. Environment loader - i.e. what is bound across the environment, may change for each setup 51 # TODO: loaders need to work when also running in an agent doing catalog application. There is no 52 # concept of environment the same way as when running as a master (except when doing apply). 53 # The creation mechanisms should probably differ between the two. 54 @private_environment_loader = 55 if for_agent 56 @puppet_cache_loader = create_puppet_cache_loader 57 create_environment_loader(environment, @puppet_cache_loader, load_from_pcore) 58 else 59 create_environment_loader(environment, @puppet_system_loader, load_from_pcore) 60 end 61 62 Pcore.init_env(@private_environment_loader) 63 64 # 4. module loaders are set up from the create_environment_loader, they register themselves 65 end
# File lib/puppet/pops/loaders.rb 126 def self.register_implementations_with_loader(obj_classes, name_authority, loader) 127 types = obj_classes.map do |obj_class| 128 type = obj_class._pcore_type 129 typed_name = Loader::TypedName.new(:type, type.name, name_authority) 130 entry = loader.loaded_entry(typed_name) 131 loader.set_entry(typed_name, type) if entry.nil? || entry.value.nil? 132 type 133 end 134 # Resolve lazy so that all types can cross reference each other 135 types.each { |type| type.resolve(loader) } 136 end
Register the given type with the Runtime3TypeLoader. The registration will not happen unless the type system has been initialized.
@param name [String,Symbol] the name of the entity being set @param origin [URI] the origin or the source where the type is defined @api private
# File lib/puppet/pops/loaders.rb 144 def self.register_runtime3_type(name, origin) 145 loaders = Puppet.lookup(:loaders) { nil } 146 return nil if loaders.nil? 147 148 rt3_loader = loaders.runtime3_type_loader 149 return nil if rt3_loader.nil? 150 151 name = name.to_s 152 caps_name = Types::TypeFormatter.singleton.capitalize_segments(name) 153 typed_name = Loader::TypedName.new(:type, name) 154 rt3_loader.set_entry(typed_name, Types::PResourceType.new(caps_name), origin) 155 nil 156 end
Register implementations using the global static loader
# File lib/puppet/pops/loaders.rb 122 def self.register_static_implementations(obj_classes) 123 register_implementations_with_loader(obj_classes, Pcore::RUNTIME_NAME_AUTHORITY, static_loader) 124 end
# File lib/puppet/pops/loaders.rb 93 def self.static_implementation_registry 94 if !class_variable_defined?(:@@static_implementation_registry) || @@static_implementation_registry.nil? 95 ir = Types::ImplementationRegistry.new 96 Types::TypeParser.type_map.values.each { |t| ir.register_implementation(t.simple_name, t.class.name) } 97 @@static_implementation_registry = ir 98 end 99 @@static_implementation_registry 100 end
# File lib/puppet/pops/loaders.rb 102 def self.static_loader 103 # The static loader can only be changed after a reboot 104 if !class_variable_defined?(:@@static_loader) || @@static_loader.nil? 105 @@static_loader = Loader::StaticLoader.new() 106 @@static_loader.register_aliases 107 Pcore.init(@@static_loader, static_implementation_registry) 108 end 109 @@static_loader 110 end
Public Instance Methods
Lookup
a loader by its unique name.
@param [String] loader_name the name of the loader to lookup @return [Loader] the found loader @raise [Puppet::ParserError] if no loader is found
# File lib/puppet/pops/loaders.rb 186 def [](loader_name) 187 loader = @loaders_by_name[loader_name] 188 if loader.nil? 189 # Unable to find the module private loader. Try resolving the module 190 loader = private_loader_for_module(loader_name[0..-9]) if loader_name.end_with?(' private') 191 raise Puppet::ParseError, _("Unable to find loader named '%{loader_name}'") % { loader_name: loader_name } if loader.nil? 192 end 193 loader 194 end
# File lib/puppet/pops/loaders.rb 255 def add_loader_by_name(loader) 256 name = loader.loader_name 257 if @loaders_by_name.include?(name) 258 raise Puppet::ParseError, _("Internal Error: Attempt to redefine loader named '%{name}'") % { name: name } 259 end 260 @loaders_by_name[name] = loader 261 end
Finds the appropriate loader for the given `module_name`, or for the environment in case `module_name` is `nil` or empty.
@param module_name [String,nil] the name of the module @return [Loader::Loader] the found loader @raise [Puppet::ParseError] if no loader can be found @api private
# File lib/puppet/pops/loaders.rb 203 def find_loader(module_name) 204 if module_name.nil? || EMPTY_STRING == module_name 205 # Use the public environment loader 206 public_environment_loader 207 else 208 # TODO : Later check if definition is private, and then add it to private_loader_for_module 209 # 210 loader = public_loader_for_module(module_name) 211 if loader.nil? 212 raise Puppet::ParseError, _("Internal Error: did not find public loader for module: '%{module_name}'") % { module_name: module_name } 213 end 214 loader 215 end 216 end
# File lib/puppet/pops/loaders.rb 218 def implementation_registry 219 # Environment specific implementation registry 220 @implementation_registry ||= Types::ImplementationRegistry.new(self.class.static_implementation_registry) 221 end
Add given 4.x definition to the given loader.
# File lib/puppet/pops/loaders.rb 314 def instantiate_definition(definition, loader) 315 case definition 316 when Model::PlanDefinition 317 instantiate_PlanDefinition(definition, loader) 318 when Model::FunctionDefinition 319 instantiate_FunctionDefinition(definition, loader) 320 when Model::TypeAlias 321 instantiate_TypeAlias(definition, loader) 322 when Model::TypeMapping 323 instantiate_TypeMapping(definition, loader) 324 else 325 raise Puppet::ParseError, "Internal Error: Unknown type of definition - got '#{definition.class}'" 326 end 327 end
Add 4.x definitions found in the given program to the given loader.
# File lib/puppet/pops/loaders.rb 308 def instantiate_definitions(program, loader) 309 program.definitions.each { |d| instantiate_definition(d, loader) } 310 nil 311 end
Load the main manifest 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 the environment's +manifest+ attribute: Puppet will try to load the environment manifest. The manifest must be a file.
@return [Model::Program] The manifest parsed into a model object
# File lib/puppet/pops/loaders.rb 275 def load_main_manifest 276 parser = Parser::EvaluatingParser.singleton 277 parsed_code = Puppet[:code] 278 program = if parsed_code != "" 279 parser.parse_string(parsed_code, 'unknown-source-location') 280 else 281 file = @environment.manifest 282 283 # if the manifest file is a reference to a directory, parse and combine 284 # all .pp files in that directory 285 if file == Puppet::Node::Environment::NO_MANIFEST 286 nil 287 elsif File.directory?(file) 288 raise Puppet::Error, "manifest of environment '#{@environment.name}' appoints directory '#{file}'. It must be a file" 289 elsif File.exist?(file) 290 parser.parse_file(file) 291 else 292 raise Puppet::Error, "manifest of environment '#{@environment.name}' appoints '#{file}'. It does not exist" 293 end 294 end 295 instantiate_definitions(program, public_environment_loader) unless program.nil? 296 program 297 rescue Puppet::ParseErrorWithIssue => detail 298 detail.environment = @environment.name 299 raise 300 rescue => detail 301 msg = _('Could not parse for environment %{env}: %{detail}') % { env: @environment, detail: detail } 302 error = Puppet::Error.new(msg) 303 error.set_backtrace(detail.backtrace) 304 raise error 305 end
# File lib/puppet/pops/loaders.rb 244 def private_loader_for_module(module_name) 245 md = @module_resolver[module_name] || (return nil) 246 # Since there is interest in the visibility from the perspective of entities contained in the 247 # module, it must be resolved (to provide this visibility). 248 # See {#configure_loaders_for_modules} 249 unless md.resolved? 250 @module_resolver.resolve(md) 251 end 252 md.private_loader 253 end
# File lib/puppet/pops/loaders.rb 235 def public_loader_for_module(module_name) 236 md = @module_resolver[module_name] || (return nil) 237 # Note, this loader is not resolved until there is interest in the visibility of entities from the 238 # perspective of something contained in the module. (Many request may pass through a module loader 239 # without it loading anything. 240 # See {#private_loader_for_module}, and not in {#configure_loaders_for_modules} 241 md.public_loader 242 end
# File lib/puppet/pops/loaders.rb 117 def register_implementations(obj_classes, name_authority) 118 self.class.register_implementations_with_loader(obj_classes, name_authority, @private_environment_loader) 119 end
# File lib/puppet/pops/loaders.rb 231 def runtime3_type_loader 232 @runtime3_type_loader 233 end
Private Instance Methods
# File lib/puppet/pops/loaders.rb 426 def configure_loaders_for_modules(parent_loader, environment) 427 @module_resolver = mr = ModuleResolver.new(self) 428 environment.modules.each do |puppet_module| 429 # Create data about this module 430 md = LoaderModuleData.new(puppet_module) 431 mr[puppet_module.name] = md 432 md.public_loader = Loader::ModuleLoaders.module_loader_from(parent_loader, self, md.name, md.path) 433 end 434 # NOTE: Do not resolve all modules here - this is wasteful if only a subset of modules / functions are used 435 # The resolution is triggered by asking for a module's private loader, since this means there is interest 436 # in the visibility from that perspective. 437 # If later, it is wanted that all resolutions should be made up-front (to capture errors eagerly, this 438 # can be introduced (better for production), but may be irritating in development mode. 439 end
# File lib/puppet/pops/loaders.rb 366 def create_environment_loader(environment, parent_loader, load_from_pcore = true) 367 # This defines where to start parsing/evaluating - the "initial import" (to use 3x terminology) 368 # Is either a reference to a single .pp file, or a directory of manifests. If the environment becomes 369 # a module and can hold functions, types etc. then these are available across all other modules without 370 # them declaring this dependency - it is however valuable to be able to treat it the same way 371 # bindings and other such system related configuration. 372 373 # This is further complicated by the many options available: 374 # - The environment may not have a directory, the code comes from one appointed 'manifest' (site.pp) 375 # - The environment may have a directory and also point to a 'manifest' 376 # - The code to run may be set in settings (code) 377 378 # Further complication is that there is nothing specifying what the visibility is into 379 # available modules. (3x is everyone sees everything). 380 # Puppet binder currently reads confdir/bindings - that is bad, it should be using the new environment support. 381 382 # env_conf is setup from the environment_dir value passed into Puppet::Environments::Directories.new 383 env_conf = Puppet.lookup(:environments).get_conf(environment.name) 384 env_path = env_conf.nil? || !env_conf.is_a?(Puppet::Settings::EnvironmentConf) ? nil : env_conf.path_to_env 385 386 if Puppet[:tasks] 387 loader = Loader::ModuleLoaders.environment_loader_from(parent_loader, self, env_path) 388 else 389 # Create the 3.x resource type loader 390 static_loader.runtime_3_init 391 # Create pcore resource type loader, if applicable 392 pcore_resource_type_loader = if load_from_pcore && env_path 393 Loader::ModuleLoaders.pcore_resource_type_loader_from(parent_loader, self, env_path) 394 else 395 nil 396 end 397 @runtime3_type_loader = add_loader_by_name(Loader::Runtime3TypeLoader.new(parent_loader, self, environment, pcore_resource_type_loader)) 398 399 if env_path.nil? 400 # Not a real directory environment, cannot work as a module TODO: Drop when legacy env are dropped? 401 loader = add_loader_by_name(Loader::SimpleEnvironmentLoader.new(@runtime3_type_loader, Loader::ENVIRONMENT, environment)) 402 else 403 # View the environment as a module to allow loading from it - this module is always called 'environment' 404 loader = Loader::ModuleLoaders.environment_loader_from(@runtime3_type_loader, self, env_path) 405 end 406 end 407 408 # An environment has a module path even if it has a null loader 409 configure_loaders_for_modules(loader, environment) 410 # modules should see this loader 411 @public_environment_loader = loader 412 413 # Code in the environment gets to see all modules (since there is no metadata for the environment) 414 # but since this is not given to the module loaders, they can not load global code (since they can not 415 # have prior knowledge about this 416 loader = add_loader_by_name(Loader::DependencyLoader.new(loader, Loader::ENVIRONMENT_PRIVATE, @module_resolver.all_module_loaders(), environment)) 417 418 # The module loader gets the private loader via a lazy operation to look up the module's private loader. 419 # This does not work for an environment since it is not resolved the same way. 420 # TODO: The EnvironmentLoader could be a specialized loader instead of using a ModuleLoader to do the work. 421 # This is subject to future design - an Environment may move more in the direction of a Module. 422 @public_environment_loader.private_loader = loader 423 loader 424 end
# File lib/puppet/pops/loaders.rb 362 def create_puppet_cache_loader() 363 Loader::ModuleLoaders.cached_loader_from(puppet_system_loader, self) 364 end
# File lib/puppet/pops/loaders.rb 358 def create_puppet_system_loader() 359 Loader::ModuleLoaders.system_loader_from(static_loader, self) 360 end
# File lib/puppet/pops/loaders.rb 337 def instantiate_FunctionDefinition(function_definition, loader) 338 # Instantiate Function, and store it in the loader 339 typed_name, f = Loader::PuppetFunctionInstantiator.create_from_model(function_definition, loader) 340 loader.set_entry(typed_name, f, function_definition.locator.to_uri(function_definition)) 341 nil 342 end
# File lib/puppet/pops/loaders.rb 331 def instantiate_PlanDefinition(plan_definition, loader) 332 typed_name, f = Loader::PuppetPlanInstantiator.create_from_model(plan_definition, loader) 333 loader.set_entry(typed_name, f, plan_definition.locator.to_uri(plan_definition)) 334 nil 335 end
# File lib/puppet/pops/loaders.rb 344 def instantiate_TypeAlias(type_alias, loader) 345 # Bind the type alias to the loader using the alias 346 Puppet::Pops::Loader::TypeDefinitionInstantiator.create_from_model(type_alias, loader) 347 nil 348 end
# File lib/puppet/pops/loaders.rb 350 def instantiate_TypeMapping(type_mapping, loader) 351 tf = Types::TypeParser.singleton 352 lhs = tf.interpret(type_mapping.type_expr, loader) 353 rhs = tf.interpret_any(type_mapping.mapping_expr, loader) 354 implementation_registry.register_type_mapping(lhs, rhs) 355 nil 356 end