Jesse and I are shooting for the minimal viable fix here, with the idea that a great deal of refactoring is needed but isn't appropriate at this time. The changes in this commit are:
* Index the function-holding modules by environment * Pass the evaluation environment into the function retrieving methods (which we can get from the scope) * We need to know the "current environment" when we're defining a function so we can attach it to the proper module, and this information isn't dynamically available when user-defined functions are being created (we're being called by user written code that doesn't "know" about environments) so we cheat and stash the value in Puppet::Node::Environment * Add a special *root* environment for the built in functions, and extend all scopes with it. * Index the function characteristics (name, type, docstring, etc.) by environment * Make the autoloader environment aware, so that it uses the modulepath for the specified environment rather than the default * Turn off caching of the modulepath since it potentially changes for each node Signed-off-by: Markus Roberts <[email protected]> --- lib/puppet/dsl/resource_api.rb | 4 +- lib/puppet/node/environment.rb | 12 +++++++++ lib/puppet/parser/ast/function.rb | 7 ++--- lib/puppet/parser/compiler.rb | 1 + lib/puppet/parser/functions.rb | 49 ++++++++++++++---------------------- lib/puppet/parser/scope.rb | 1 + lib/puppet/parser/type_loader.rb | 2 +- lib/puppet/util/autoload.rb | 19 ++++++-------- 8 files changed, 47 insertions(+), 48 deletions(-) diff --git a/lib/puppet/dsl/resource_api.rb b/lib/puppet/dsl/resource_api.rb index 98e5215..ffe7777 100644 --- a/lib/puppet/dsl/resource_api.rb +++ b/lib/puppet/dsl/resource_api.rb @@ -33,7 +33,7 @@ class Puppet::DSL::ResourceAPI name = map_function(name) - return call_function(name, args) if Puppet::Parser::Functions.function(name) + return call_function(name, args) if Puppet::Parser::Functions.function(name,environment) super ensure @@ -66,7 +66,7 @@ class Puppet::DSL::ResourceAPI end def call_function(name, args) - return false unless method = Puppet::Parser::Functions.function(name) + return false unless method = Puppet::Parser::Functions.function(name,environment) scope.send(method, *args) end diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index d1a126a..4363eea 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -41,6 +41,18 @@ class Puppet::Node::Environment @seen[symbol] = obj end + def self.current + @current || root + end + + def self.current=(env) + @current = new(env) + end + + def self.root + @root ||= new(:'*root*') + end + # This is only used for testing. def self.clear @seen.clear diff --git a/lib/puppet/parser/ast/function.rb b/lib/puppet/parser/ast/function.rb index 4c3a5dc..59cdc76 100644 --- a/lib/puppet/parser/ast/function.rb +++ b/lib/puppet/parser/ast/function.rb @@ -13,19 +13,19 @@ class Puppet::Parser::AST def evaluate(scope) # Make sure it's a defined function - unless @fname + unless @fname = Puppet::Parser::Functions.function(@name,scope.environment) raise Puppet::ParseError, "Unknown function %s" % @name end # Now check that it's been used correctly case @ftype when :rvalue - unless Puppet::Parser::Functions.rvalue?(@name) + unless Puppet::Parser::Functions.rvalue?(@name,scope.environment) raise Puppet::ParseError, "Function '%s' does not return a value" % @name end when :statement - if Puppet::Parser::Functions.rvalue?(@name) + if Puppet::Parser::Functions.rvalue?(@name,scope.environment) raise Puppet::ParseError, "Function '%s' must be the value of a statement" % @name @@ -48,7 +48,6 @@ class Puppet::Parser::AST super(hash) - @fname = Puppet::Parser::Functions.function(@name) # Lastly, check the parity end diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb index beba438..22e2f07 100644 --- a/lib/puppet/parser/compiler.rb +++ b/lib/puppet/parser/compiler.rb @@ -127,6 +127,7 @@ class Puppet::Parser::Compiler @environment = nil end end + Puppet::Node::Environment.current = @environment @environment end diff --git a/lib/puppet/parser/functions.rb b/lib/puppet/parser/functions.rb index 6ba3669..9cdbf12 100644 --- a/lib/puppet/parser/functions.rb +++ b/lib/puppet/parser/functions.rb @@ -6,7 +6,7 @@ require 'puppet/parser/scope' # class. module Puppet::Parser::Functions - @functions = {} + @functions = Hash.new { |h,k| h[k] = {} } @modules = {} class << self @@ -24,15 +24,17 @@ module Puppet::Parser::Functions @autoloader end + Environment = Puppet::Node::Environment + def self.environment_module(env = nil) - @module ||= Module.new + @modules[ env || Environment.current || Environment.root ] ||= Module.new end # Create a new function type. def self.newfunction(name, options = {}, &block) name = symbolize(name) - if @functions.include? name + if functions.include?(name) raise Puppet::DevError, "Function %s already defined" % name end @@ -46,10 +48,10 @@ module Puppet::Parser::Functions environment_module.send(:define_method, fname, &block) # Someday we'll support specifying an arity, but for now, nope - #...@functions[name] = {:arity => arity, :type => ftype} - @functions[name] = {:type => ftype, :name => fname} + #functions[name] = {:arity => arity, :type => ftype} + functions[name] = {:type => ftype, :name => fname} if options[:doc] - @functions[name][:doc] = options[:doc] + functions[name][:doc] = options[:doc] end end @@ -57,29 +59,25 @@ module Puppet::Parser::Functions def self.rmfunction(name) name = symbolize(name) - unless @functions.include? name + unless functions.include? name raise Puppet::DevError, "Function %s is not defined" % name end - @functions.delete(name) + functions.delete name fname = "function_" + name.to_s environment_module.send(:remove_method, fname) end # Determine if a given name is a function - def self.function(name) + def self.function(name,environment) name = symbolize(name) - unless @functions.include? name - autoloader.load(name) + unless functions.include?(name) or functions(Puppet::Node::Environment.root).include?(name) + autoloader.load(name,environment) end - if @functions.include? name - return @functions[name][:name] - else - return false - end + ( functions(Puppet::Node::Environment.root)[name] || functions[name] || {:name => false} )[:name] end def self.functiondocs @@ -87,7 +85,7 @@ module Puppet::Parser::Functions ret = "" - @functions.sort { |a,b| a[0].to_s <=> b[0].to_s }.each do |name, hash| + functions.sort { |a,b| a[0].to_s <=> b[0].to_s }.each do |name, hash| #ret += "%s\n%s\n" % [name, hash[:type]] ret += "%s\n%s\n" % [name, "-" * name.to_s.length] if hash[:doc] @@ -102,22 +100,13 @@ module Puppet::Parser::Functions return ret end - def self.functions - @functions.keys + def self.functions(env = nil) + @functions[ env || Environment.current || Environment.root ] end # Determine if a given function returns a value or not. - def self.rvalue?(name) - name = symbolize(name) - - if @functions.include? name - case @functions[name][:type] - when :statement; return false - when :rvalue; return true - end - else - return false - end + def self.rvalue?(name,env) + (functions(env)[symbolize(name)] || {})[:type] == :rvalue end # Runs a newfunction to create a function for each of the log levels diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index 991e123..a097d2a 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -505,6 +505,7 @@ class Puppet::Parser::Scope private def extend_with_functions_module + extend Puppet::Parser::Functions.environment_module(Puppet::Node::Environment.root) extend Puppet::Parser::Functions.environment_module(compiler ? environment : nil) end end diff --git a/lib/puppet/parser/type_loader.rb b/lib/puppet/parser/type_loader.rb index 3268eae..bcb7fa3 100644 --- a/lib/puppet/parser/type_loader.rb +++ b/lib/puppet/parser/type_loader.rb @@ -114,7 +114,7 @@ class Puppet::Parser::TypeLoader end def parse_file(file) - Puppet.debug("importing '#{file}'") + Puppet.debug("importing '#{file}' in environment #{environment}") parser = Puppet::Parser::Parser.new(environment) parser.file = file parser.parse diff --git a/lib/puppet/util/autoload.rb b/lib/puppet/util/autoload.rb index 7358618..4a687bf 100644 --- a/lib/puppet/util/autoload.rb +++ b/lib/puppet/util/autoload.rb @@ -73,12 +73,12 @@ class Puppet::Util::Autoload # Load a single plugin by name. We use 'load' here so we can reload a # given plugin. - def load(name) + def load(name,env=nil) return false if named_file_missing?(name) path = name.to_s + ".rb" - searchpath.each do |dir| + searchpath(env).each do |dir| file = File.join(dir, path) next unless file_exist?(file) begin @@ -130,25 +130,22 @@ class Puppet::Util::Autoload end # The list of directories to search through for loadable plugins. - # We have to hard-code the ttl because this library is used by - # so many other classes it's hard to get the load-order such that - # the defaults load before this. - cached_attr(:searchpath, :ttl => 15) do - search_directories.collect { |d| File.join(d, @path) }.find_all { |d| FileTest.directory?(d) } + def searchpath(env=nil) + search_directories(env).collect { |d| File.join(d, @path) }.find_all { |d| FileTest.directory?(d) } end - def module_directories + def module_directories(env=nil) # We have to require this late in the process because otherwise we might have # load order issues. require 'puppet/node/environment' - Puppet::Node::Environment.new.modulepath.collect do |dir| + Puppet::Node::Environment.new(env).modulepath.collect do |dir| Dir.entries(dir).reject { |f| f =~ /^\./ }.collect { |f| File.join(dir, f) } end.flatten.collect { |d| [File.join(d, "plugins"), File.join(d, "lib")] }.flatten.find_all do |d| FileTest.directory?(d) end end - def search_directories(dummy_argument=:work_arround_for_ruby_GC_bug) - [module_directories, Puppet[:libdir].split(File::PATH_SEPARATOR), $:].flatten + def search_directories(env=nil) + [module_directories(env), Puppet[:libdir].split(File::PATH_SEPARATOR), $:].flatten end end -- 1.6.4 -- You received this message because you are subscribed to the Google Groups "Puppet Developers" group. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/puppet-dev?hl=en.
