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
Commented out the :static_loader and :puppet_system_loader because of rubocop offenses with duplicated definitions of these generated methods, but keeping them here for visibility on how the loaders are stacked. attr_reader :static_loader attr_reader :puppet_system_loader
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 166 def self.catalog_loader 167 loaders = Puppet.lookup(:loaders) { nil } 168 if loaders.nil? 169 loaders = Loaders.new(Puppet.lookup(:current_environment), true) 170 Puppet.push_context(:loaders => loaders) 171 end 172 loaders.find_loader(nil) 173 end
Clears the cached static and puppet_system loaders (to enable testing)
# File lib/puppet/pops/loaders.rb 80 def self.clear 81 @@static_loader = nil 82 Puppet::Pops::Types::TypeFactory.clear 83 Model.class_variable_set(:@@pcore_ast_initialized, false) 84 Model.register_pcore_types 85 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 94 def self.find_loader(module_name) 95 loaders.find_loader(module_name) 96 end
# File lib/puppet/pops/loaders.rb 117 def self.implementation_registry 118 loaders = Puppet.lookup(:loaders) { nil } 119 loaders.nil? ? nil : loaders.implementation_registry 120 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 180 def self.loaders 181 loaders = Puppet.lookup(:loaders) { nil } 182 raise Puppet::ParseError, _("Internal Error: Puppet Context ':loaders' missing") if loaders.nil? 183 loaders 184 end
# File lib/puppet/pops/loaders.rb 26 def self.new(environment, for_agent = false, load_from_pcore = true) 27 environment.lock.synchronize do 28 obj = environment.loaders 29 if obj.nil? 30 obj = self.allocate 31 obj.send(:initialize, environment, for_agent, load_from_pcore) 32 end 33 obj 34 end 35 end
# File lib/puppet/pops/loaders.rb 37 def initialize(environment, for_agent, load_from_pcore = true) 38 # Protect against environment havoc 39 raise ArgumentError.new(_("Attempt to redefine already initialized loaders for environment")) unless environment.loaders.nil? 40 environment.loaders = self 41 @environment = environment 42 @loaders_by_name = {} 43 44 add_loader_by_name(self.class.static_loader) 45 46 # Create the set of loaders 47 # 1. Puppet, loads from the "running" puppet - i.e. bundled functions, types, extension points and extensions 48 # These cannot be cached since a loaded instance will be bound to its closure scope which holds on to 49 # a compiler and all loaded types. Subsequent request would find remains of the environment that loaded 50 # the content. PUP-4461. 51 # 52 @puppet_system_loader = create_puppet_system_loader() 53 54 # 2. Cache loader(optional) - i.e. what puppet stores on disk via pluginsync; gate behind the for_agent flag. 55 # 3. Environment loader - i.e. what is bound across the environment, may change for each setup 56 # TODO: loaders need to work when also running in an agent doing catalog application. There is no 57 # concept of environment the same way as when running as a master (except when doing apply). 58 # The creation mechanisms should probably differ between the two. 59 @private_environment_loader = 60 if for_agent 61 @puppet_cache_loader = create_puppet_cache_loader 62 create_environment_loader(environment, @puppet_cache_loader, load_from_pcore) 63 else 64 create_environment_loader(environment, @puppet_system_loader, load_from_pcore) 65 end 66 67 Pcore.init_env(@private_environment_loader) 68 69 # 4. module loaders are set up from the create_environment_loader, they register themselves 70 end
# File lib/puppet/pops/loaders.rb 131 def self.register_implementations_with_loader(obj_classes, name_authority, loader) 132 types = obj_classes.map do |obj_class| 133 type = obj_class._pcore_type 134 typed_name = Loader::TypedName.new(:type, type.name, name_authority) 135 entry = loader.loaded_entry(typed_name) 136 loader.set_entry(typed_name, type) if entry.nil? || entry.value.nil? 137 type 138 end 139 # Resolve lazy so that all types can cross reference each other 140 types.each { |type| type.resolve(loader) } 141 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 149 def self.register_runtime3_type(name, origin) 150 loaders = Puppet.lookup(:loaders) { nil } 151 return nil if loaders.nil? 152 153 rt3_loader = loaders.runtime3_type_loader 154 return nil if rt3_loader.nil? 155 156 name = name.to_s 157 caps_name = Types::TypeFormatter.singleton.capitalize_segments(name) 158 typed_name = Loader::TypedName.new(:type, name) 159 rt3_loader.set_entry(typed_name, Types::PResourceType.new(caps_name), origin) 160 nil 161 end
Register implementations using the global static loader
# File lib/puppet/pops/loaders.rb 127 def self.register_static_implementations(obj_classes) 128 register_implementations_with_loader(obj_classes, Pcore::RUNTIME_NAME_AUTHORITY, static_loader) 129 end
# File lib/puppet/pops/loaders.rb 98 def self.static_implementation_registry 99 if !class_variable_defined?(:@@static_implementation_registry) || @@static_implementation_registry.nil? 100 ir = Types::ImplementationRegistry.new 101 Types::TypeParser.type_map.values.each { |t| ir.register_implementation(t.simple_name, t.class.name) } 102 @@static_implementation_registry = ir 103 end 104 @@static_implementation_registry 105 end
# File lib/puppet/pops/loaders.rb 107 def self.static_loader 108 # The static loader can only be changed after a reboot 109 if !class_variable_defined?(:@@static_loader) || @@static_loader.nil? 110 @@static_loader = Loader::StaticLoader.new() 111 @@static_loader.register_aliases 112 Pcore.init(@@static_loader, static_implementation_registry) 113 end 114 @@static_loader 115 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 191 def [](loader_name) 192 loader = @loaders_by_name[loader_name] 193 if loader.nil? 194 # Unable to find the module private loader. Try resolving the module 195 loader = private_loader_for_module(loader_name[0..-9]) if loader_name.end_with?(' private') 196 raise Puppet::ParseError, _("Unable to find loader named '%{loader_name}'") % { loader_name: loader_name } if loader.nil? 197 end 198 loader 199 end
# File lib/puppet/pops/loaders.rb 260 def add_loader_by_name(loader) 261 name = loader.loader_name 262 if @loaders_by_name.include?(name) 263 raise Puppet::ParseError, _("Internal Error: Attempt to redefine loader named '%{name}'") % { name: name } 264 end 265 @loaders_by_name[name] = loader 266 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 208 def find_loader(module_name) 209 if module_name.nil? || EMPTY_STRING == module_name 210 # Use the public environment loader 211 public_environment_loader 212 else 213 # TODO : Later check if definition is private, and then add it to private_loader_for_module 214 # 215 loader = public_loader_for_module(module_name) 216 if loader.nil? 217 raise Puppet::ParseError, _("Internal Error: did not find public loader for module: '%{module_name}'") % { module_name: module_name } 218 end 219 loader 220 end 221 end
# File lib/puppet/pops/loaders.rb 223 def implementation_registry 224 # Environment specific implementation registry 225 @implementation_registry ||= Types::ImplementationRegistry.new(self.class.static_implementation_registry) 226 end
Add given 4.x definition to the given loader.
# File lib/puppet/pops/loaders.rb 319 def instantiate_definition(definition, loader) 320 case definition 321 when Model::PlanDefinition 322 instantiate_PlanDefinition(definition, loader) 323 when Model::FunctionDefinition 324 instantiate_FunctionDefinition(definition, loader) 325 when Model::TypeAlias 326 instantiate_TypeAlias(definition, loader) 327 when Model::TypeMapping 328 instantiate_TypeMapping(definition, loader) 329 else 330 raise Puppet::ParseError, "Internal Error: Unknown type of definition - got '#{definition.class}'" 331 end 332 end
Add 4.x definitions found in the given program to the given loader.
# File lib/puppet/pops/loaders.rb 313 def instantiate_definitions(program, loader) 314 program.definitions.each { |d| instantiate_definition(d, loader) } 315 nil 316 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 280 def load_main_manifest 281 parser = Parser::EvaluatingParser.singleton 282 parsed_code = Puppet[:code] 283 program = if parsed_code != "" 284 parser.parse_string(parsed_code, 'unknown-source-location') 285 else 286 file = @environment.manifest 287 288 # if the manifest file is a reference to a directory, parse and combine 289 # all .pp files in that directory 290 if file == Puppet::Node::Environment::NO_MANIFEST 291 nil 292 elsif File.directory?(file) 293 raise Puppet::Error, "manifest of environment '#{@environment.name}' appoints directory '#{file}'. It must be a file" 294 elsif File.exist?(file) 295 parser.parse_file(file) 296 else 297 raise Puppet::Error, "manifest of environment '#{@environment.name}' appoints '#{file}'. It does not exist" 298 end 299 end 300 instantiate_definitions(program, public_environment_loader) unless program.nil? 301 program 302 rescue Puppet::ParseErrorWithIssue => detail 303 detail.environment = @environment.name 304 raise 305 rescue => detail 306 msg = _('Could not parse for environment %{env}: %{detail}') % { env: @environment, detail: detail } 307 error = Puppet::Error.new(msg) 308 error.set_backtrace(detail.backtrace) 309 raise error 310 end
# File lib/puppet/pops/loaders.rb 249 def private_loader_for_module(module_name) 250 md = @module_resolver[module_name] || (return nil) 251 # Since there is interest in the visibility from the perspective of entities contained in the 252 # module, it must be resolved (to provide this visibility). 253 # See {#configure_loaders_for_modules} 254 unless md.resolved? 255 @module_resolver.resolve(md) 256 end 257 md.private_loader 258 end
# File lib/puppet/pops/loaders.rb 240 def public_loader_for_module(module_name) 241 md = @module_resolver[module_name] || (return nil) 242 # Note, this loader is not resolved until there is interest in the visibility of entities from the 243 # perspective of something contained in the module. (Many request may pass through a module loader 244 # without it loading anything. 245 # See {#private_loader_for_module}, and not in {#configure_loaders_for_modules} 246 md.public_loader 247 end
# File lib/puppet/pops/loaders.rb 232 def puppet_system_loader 233 @puppet_system_loader 234 end
# File lib/puppet/pops/loaders.rb 122 def register_implementations(obj_classes, name_authority) 123 self.class.register_implementations_with_loader(obj_classes, name_authority, @private_environment_loader) 124 end
# File lib/puppet/pops/loaders.rb 236 def runtime3_type_loader 237 @runtime3_type_loader 238 end
# File lib/puppet/pops/loaders.rb 228 def static_loader 229 self.class.static_loader 230 end
Private Instance Methods
# File lib/puppet/pops/loaders.rb 431 def configure_loaders_for_modules(parent_loader, environment) 432 @module_resolver = mr = ModuleResolver.new(self) 433 environment.modules.each do |puppet_module| 434 # Create data about this module 435 md = LoaderModuleData.new(puppet_module) 436 mr[puppet_module.name] = md 437 md.public_loader = Loader::ModuleLoaders.module_loader_from(parent_loader, self, md.name, md.path) 438 end 439 # NOTE: Do not resolve all modules here - this is wasteful if only a subset of modules / functions are used 440 # The resolution is triggered by asking for a module's private loader, since this means there is interest 441 # in the visibility from that perspective. 442 # If later, it is wanted that all resolutions should be made up-front (to capture errors eagerly, this 443 # can be introduced (better for production), but may be irritating in development mode. 444 end
# File lib/puppet/pops/loaders.rb 371 def create_environment_loader(environment, parent_loader, load_from_pcore = true) 372 # This defines where to start parsing/evaluating - the "initial import" (to use 3x terminology) 373 # Is either a reference to a single .pp file, or a directory of manifests. If the environment becomes 374 # a module and can hold functions, types etc. then these are available across all other modules without 375 # them declaring this dependency - it is however valuable to be able to treat it the same way 376 # bindings and other such system related configuration. 377 378 # This is further complicated by the many options available: 379 # - The environment may not have a directory, the code comes from one appointed 'manifest' (site.pp) 380 # - The environment may have a directory and also point to a 'manifest' 381 # - The code to run may be set in settings (code) 382 383 # Further complication is that there is nothing specifying what the visibility is into 384 # available modules. (3x is everyone sees everything). 385 # Puppet binder currently reads confdir/bindings - that is bad, it should be using the new environment support. 386 387 # env_conf is setup from the environment_dir value passed into Puppet::Environments::Directories.new 388 env_conf = Puppet.lookup(:environments).get_conf(environment.name) 389 env_path = env_conf.nil? || !env_conf.is_a?(Puppet::Settings::EnvironmentConf) ? nil : env_conf.path_to_env 390 391 if Puppet[:tasks] 392 loader = Loader::ModuleLoaders.environment_loader_from(parent_loader, self, env_path) 393 else 394 # Create the 3.x resource type loader 395 static_loader.runtime_3_init 396 # Create pcore resource type loader, if applicable 397 pcore_resource_type_loader = if load_from_pcore && env_path 398 Loader::ModuleLoaders.pcore_resource_type_loader_from(parent_loader, self, env_path) 399 else 400 nil 401 end 402 @runtime3_type_loader = add_loader_by_name(Loader::Runtime3TypeLoader.new(parent_loader, self, environment, pcore_resource_type_loader)) 403 404 if env_path.nil? 405 # Not a real directory environment, cannot work as a module TODO: Drop when legacy env are dropped? 406 loader = add_loader_by_name(Loader::SimpleEnvironmentLoader.new(@runtime3_type_loader, Loader::ENVIRONMENT, environment)) 407 else 408 # View the environment as a module to allow loading from it - this module is always called 'environment' 409 loader = Loader::ModuleLoaders.environment_loader_from(@runtime3_type_loader, self, env_path) 410 end 411 end 412 413 # An environment has a module path even if it has a null loader 414 configure_loaders_for_modules(loader, environment) 415 # modules should see this loader 416 @public_environment_loader = loader 417 418 # Code in the environment gets to see all modules (since there is no metadata for the environment) 419 # but since this is not given to the module loaders, they can not load global code (since they can not 420 # have prior knowledge about this 421 loader = add_loader_by_name(Loader::DependencyLoader.new(loader, Loader::ENVIRONMENT_PRIVATE, @module_resolver.all_module_loaders(), environment)) 422 423 # The module loader gets the private loader via a lazy operation to look up the module's private loader. 424 # This does not work for an environment since it is not resolved the same way. 425 # TODO: The EnvironmentLoader could be a specialized loader instead of using a ModuleLoader to do the work. 426 # This is subject to future design - an Environment may move more in the direction of a Module. 427 @public_environment_loader.private_loader = loader 428 loader 429 end
# File lib/puppet/pops/loaders.rb 367 def create_puppet_cache_loader() 368 Loader::ModuleLoaders.cached_loader_from(puppet_system_loader, self) 369 end
# File lib/puppet/pops/loaders.rb 363 def create_puppet_system_loader() 364 Loader::ModuleLoaders.system_loader_from(static_loader, self) 365 end
# File lib/puppet/pops/loaders.rb 342 def instantiate_FunctionDefinition(function_definition, loader) 343 # Instantiate Function, and store it in the loader 344 typed_name, f = Loader::PuppetFunctionInstantiator.create_from_model(function_definition, loader) 345 loader.set_entry(typed_name, f, function_definition.locator.to_uri(function_definition)) 346 nil 347 end
# File lib/puppet/pops/loaders.rb 336 def instantiate_PlanDefinition(plan_definition, loader) 337 typed_name, f = Loader::PuppetPlanInstantiator.create_from_model(plan_definition, loader) 338 loader.set_entry(typed_name, f, plan_definition.locator.to_uri(plan_definition)) 339 nil 340 end
# File lib/puppet/pops/loaders.rb 349 def instantiate_TypeAlias(type_alias, loader) 350 # Bind the type alias to the loader using the alias 351 Puppet::Pops::Loader::TypeDefinitionInstantiator.create_from_model(type_alias, loader) 352 nil 353 end
# File lib/puppet/pops/loaders.rb 355 def instantiate_TypeMapping(type_mapping, loader) 356 tf = Types::TypeParser.singleton 357 lhs = tf.interpret(type_mapping.type_expr, loader) 358 rhs = tf.interpret_any(type_mapping.mapping_expr, loader) 359 implementation_registry.register_type_mapping(lhs, rhs) 360 nil 361 end