[Pkg-puppet-devel] [SCM] Puppet packaging for Debian branch, experimental, updated. debian/2.6.8-1-844-g7ec39d5
Jesse Wolfe
jes5199 at gmail.com
Tue May 10 07:59:12 UTC 2011
The following commit has been merged in the experimental branch:
commit 4dbff92f1cc1fa47167a4b4d8cf8ed9e4011cfc4
Merge: 3c34ea62687745156990f6d97460131b75e67c56 2b50f30c703aca5c4f3e89961d64a94d886296bd
Author: Jesse Wolfe <jes5199 at gmail.com>
Date: Sat Oct 2 01:09:01 2010 -0700
Partial merge to 2.6.2rc1 : Merge commit '2b50f30' into next
Commit 2b50f30 simplified and fixed bugs in code that had already been
modified extensively by 4da88fb and 6b1dd81. This merge resolution
commit is a manual replay of the changes from 2b50f30 onto next.
Manually Resolved Conflicts:
lib/puppet/parser/type_loader.rb
spec/unit/parser/type_loader_spec.rb
diff --combined lib/puppet/parser/parser_support.rb
index b1688cd,4f3a4dd..41bebe42
--- a/lib/puppet/parser/parser_support.rb
+++ b/lib/puppet/parser/parser_support.rb
@@@ -29,9 -29,18 +29,9 @@@ class Puppet::Parser::Parse
message
end
- # Create an AST array out of all of the args
- def aryfy(*args)
- if args[0].instance_of?(AST::ASTArray)
- result = args.shift
- args.each { |arg|
- result.push arg
- }
- else
- result = ast AST::ASTArray, :children => args
- end
-
- result
+ # Create an AST array containing a single element
+ def aryfy(arg)
+ ast AST::ASTArray, :children => [arg]
end
# Create an AST object, and automatically add the file and line information if
@@@ -59,13 -68,13 +59,13 @@@
end
# Raise a Parse error.
- def error(message)
+ def error(message, options = {})
if brace = @lexer.expected
message += "; expected '%s'"
end
except = Puppet::ParseError.new(message)
- except.line = @lexer.line
- except.file = @lexer.file if @lexer.file
+ except.line = options[:line] || @lexer.line
+ except.file = options[:file] || @lexer.file
raise except
end
@@@ -94,15 -103,15 +94,15 @@@
end
def find_hostclass(namespace, name)
- known_resource_types.find_or_load(namespace, name, :hostclass)
+ known_resource_types.find_hostclass(namespace, name)
end
def find_definition(namespace, name)
- known_resource_types.find_or_load(namespace, name, :definition)
+ known_resource_types.find_definition(namespace, name)
end
def import(file)
- known_resource_types.loader.import_if_possible(file, @lexer.file)
+ known_resource_types.loader.import(file, @lexer.file)
end
def initialize(env)
@@@ -124,6 -133,26 +124,6 @@@
return ns, n
end
- # Create a new class, or merge with an existing class.
- def newclass(name, options = {})
- known_resource_types.add Puppet::Resource::Type.new(:hostclass, name, ast_context(true).merge(options))
- end
-
- # Create a new definition.
- def newdefine(name, options = {})
- known_resource_types.add Puppet::Resource::Type.new(:definition, name, ast_context(true).merge(options))
- end
-
- # Create a new node. Nodes are special, because they're stored in a global
- # table, not according to namespaces.
- def newnode(names, options = {})
- names = [names] unless names.instance_of?(Array)
- context = ast_context(true)
- names.collect do |name|
- known_resource_types.add(Puppet::Resource::Type.new(:node, name, context.merge(options)))
- end
- end
-
def on_error(token,value,stack)
if token == 0 # denotes end of file
value = 'end of file'
@@@ -145,43 -174,42 +145,43 @@@
# how should I do error handling here?
def parse(string = nil)
- return parse_ruby_file if self.file =~ /\.rb$/
- self.string = string if string
- begin
- @yydebug = false
- main = yyparse(@lexer,:scan)
- rescue Racc::ParseError => except
- error = Puppet::ParseError.new(except)
- error.line = @lexer.line
- error.file = @lexer.file
- error.set_backtrace except.backtrace
- raise error
- rescue Puppet::ParseError => except
- except.line ||= @lexer.line
- except.file ||= @lexer.file
- raise except
- rescue Puppet::Error => except
- # and this is a framework error
- except.line ||= @lexer.line
- except.file ||= @lexer.file
- raise except
- rescue Puppet::DevError => except
- except.line ||= @lexer.line
- except.file ||= @lexer.file
- raise except
- rescue => except
- error = Puppet::DevError.new(except.message)
- error.line = @lexer.line
- error.file = @lexer.file
- error.set_backtrace except.backtrace
- raise error
- end
- if main
- # Store the results as the top-level class.
- newclass("", :code => main)
+ if self.file =~ /\.rb$/
+ parse_ruby_file
+ main = nil
+ else
+ self.string = string if string
+ begin
+ @yydebug = false
+ main = yyparse(@lexer,:scan)
+ rescue Racc::ParseError => except
+ error = Puppet::ParseError.new(except)
+ error.line = @lexer.line
+ error.file = @lexer.file
+ error.set_backtrace except.backtrace
+ raise error
+ rescue Puppet::ParseError => except
+ except.line ||= @lexer.line
+ except.file ||= @lexer.file
+ raise except
+ rescue Puppet::Error => except
+ # and this is a framework error
+ except.line ||= @lexer.line
+ except.file ||= @lexer.file
+ raise except
+ rescue Puppet::DevError => except
+ except.line ||= @lexer.line
+ except.file ||= @lexer.file
+ raise except
+ rescue => except
+ error = Puppet::DevError.new(except.message)
+ error.line = @lexer.line
+ error.file = @lexer.file
+ error.set_backtrace except.backtrace
+ raise error
+ end
end
- return known_resource_types
+ # Store the results as the top-level class.
+ return Puppet::Parser::AST::Hostclass.new('', :code => main)
ensure
@lexer.clear
end
diff --combined lib/puppet/parser/type_loader.rb
index 8a183f4,bae5603..140c9f2
--- a/lib/puppet/parser/type_loader.rb
+++ b/lib/puppet/parser/type_loader.rb
@@@ -3,25 -3,56 +3,56 @@@ require 'puppet/node/environment
class Puppet::Parser::TypeLoader
include Puppet::Node::Environment::Helper
- class Helper < Hash
+ # Helper class that makes sure we don't try to import the same file
+ # more than once from either the same thread or different threads.
+ class Helper
include MonitorMixin
- def done_with(item)
- synchronize do
- delete(item)[:busy].signal if self.has_key?(item) and self[item][:loader] == Thread.current
- end
+ def initialize
+ super
+ # These hashes are indexed by filename
+ @state = {} # :doing or :done
+ @thread = {} # if :doing, thread that's doing the parsing
+ @cond_var = {} # if :doing, condition var that will be signaled when done.
end
- def owner_of(item)
- synchronize do
- if !self.has_key? item
- self[item] = { :loader => Thread.current, :busy => self.new_cond}
- :nobody
- elsif self[item][:loader] == Thread.current
- :this_thread
+
+ # Execute the supplied block exactly once per file, no matter how
+ # many threads have asked for it to run. If another thread is
+ # already executing it, wait for it to finish. If this thread is
+ # already executing it, return immediately without executing the
+ # block.
+ #
+ # Note: the reason for returning immediately if this thread is
+ # already executing the block is to handle the case of a circular
+ # import--when this happens, we attempt to recursively re-parse a
+ # file that we are already in the process of parsing. To prevent
+ # an infinite regress we need to simply do nothing when the
+ # recursive import is attempted.
+ def do_once(file)
+ need_to_execute = synchronize do
+ case @state[file]
+ when :doing
+ if @thread[file] != Thread.current
+ @cond_var[file].wait
+ end
+ false
+ when :done
+ false
else
- flag = self[item][:busy]
- flag.wait
- flag.signal
- :another_thread
+ @state[file] = :doing
+ @thread[file] = Thread.current
+ @cond_var[file] = new_cond
+ true
+ end
+ end
+ if need_to_execute
+ begin
+ yield
+ ensure
+ synchronize do
+ @state[file] = :done
+ @thread.delete(file)
+ @cond_var.delete(file).broadcast
+ end
end
end
end
@@@ -47,99 -78,71 +78,68 @@@
raise Puppet::ImportError.new("No file(s) found for import of '#{pat}'")
end
+ loaded_asts = []
files.each do |file|
unless file =~ /^#{File::SEPARATOR}/
file = File.join(dir, file)
end
- unless imported? file
- @imported[file] = true
+ @loading_helper.do_once(file) do
- parse_file(file)
+ loaded_asts << parse_file(file)
end
end
-
- modname
+ loaded_asts.inject([]) do |loaded_types, ast|
+ loaded_types + known_resource_types.import_ast(ast, modname)
+ end
end
- def imported?(file)
- @imported.has_key?(file)
- end
-
def known_resource_types
environment.known_resource_types
end
def initialize(env)
self.environment = env
- @loaded = {}
- @loading = Helper.new
-
- @imported = {}
+ @loading_helper = Helper.new
end
- def load_until(namespaces, name)
- return nil if name == "" # special-case main.
- name2files(namespaces, name).each do |filename|
- modname = begin
- import(filename)
+ # Try to load the object with the given fully qualified name.
+ def try_load_fqname(type, fqname)
+ return nil if fqname == "" # special-case main.
+ name2files(fqname).each do |filename|
- if not loaded?(filename)
- begin
- imported_types = import_if_possible(filename)
- if result = imported_types.find { |t| t.type == type and t.name == fqname }
- Puppet.debug "Automatically imported #{fqname} from #{filename} into #{environment}"
- return result
- end
- rescue Puppet::ImportError => detail
- # We couldn't load the item
- # I'm not convienced we should just drop these errors, but this
- # preserves existing behaviours.
++ begin
++ imported_types = import(filename)
++ if result = imported_types.find { |t| t.type == type and t.name == fqname }
++ Puppet.debug "Automatically imported #{fqname} from #{filename} into #{environment}"
++ return result
+ end
+ rescue Puppet::ImportError => detail
+ # We couldn't load the item
+ # I'm not convienced we should just drop these errors, but this
+ # preserves existing behaviours.
- nil
- end
- if result = yield(filename)
- Puppet.debug "Automatically imported #{name} from #{filename} into #{environment}"
- result.module_name = modname if modname and result.respond_to?(:module_name=)
- return result
end
end
- nil
- end
-
- def name2files(namespaces, name)
- return [name.sub(/^::/, '').gsub("::", File::SEPARATOR)] if name =~ /^::/
-
- result = namespaces.inject([]) do |names_to_try, namespace|
- fullname = (namespace + "::#{name}").sub(/^::/, '')
-
- # Try to load the module init file if we're a qualified name
- names_to_try << fullname.split("::")[0] if fullname.include?("::")
-
- # Then the fully qualified name
- names_to_try << fullname
- end
-
- # Otherwise try to load the bare name on its own. This
- # is appropriate if the class we're looking for is in a
- # module that's different from our namespace.
- result << name
- result.uniq.collect { |f| f.gsub("::", File::SEPARATOR) }
+ # Nothing found.
+ return nil
end
- def loaded?(name)
- @loaded.include?(name)
- end
-
def parse_file(file)
Puppet.debug("importing '#{file}' in environment #{environment}")
parser = Puppet::Parser::Parser.new(environment)
parser.file = file
- parser.parse
+ return parser.parse
+ end
+
- # Utility method factored out of load for handling thread-safety.
- # This isn't tested in the specs, because that's basically impossible.
- def import_if_possible(file, current_file = nil)
- @loaded[file] || begin
- case @loading.owner_of(file)
- when :this_thread
- nil
- when :another_thread
- import_if_possible(file,current_file)
- when :nobody
- @loaded[file] = import(file,current_file)
- end
- ensure
- @loading.done_with(file)
- end
- end
-
+ private
+
+ # Return a list of all file basenames that should be tried in order
+ # to load the object with the given fully qualified name.
+ def name2files(fqname)
+ result = []
+ ary = fqname.split("::")
+ while ary.length > 0
+ result << ary.join(File::SEPARATOR)
+ ary.pop
+ end
+ return result
end
+
end
diff --combined spec/unit/parser/type_loader_spec.rb
index 58c386d,02d543b..cfa6887
--- a/spec/unit/parser/type_loader_spec.rb
+++ b/spec/unit/parser/type_loader_spec.rb
@@@ -28,26 -28,86 +28,21 @@@ describe Puppet::Parser::TypeLoader d
describe "when loading names from namespaces" do
it "should do nothing if the name to import is an empty string" do
@loader.expects(:name2files).never
- @loader.load_until(["foo"], "") { |f| false }.should be_nil
- end
-
- it "should turn the provided namespaces and name into a list of files" do
- @loader.expects(:name2files).with(["foo"], "bar").returns []
- @loader.load_until(["foo"], "bar") { |f| false }
+ @loader.try_load_fqname(:hostclass, "") { |filename, modname| raise :should_not_occur }.should be_nil
end
it "should attempt to import each generated name" do
- @loader.expects(:name2files).returns %w{foo bar}
- @loader.expects(:import).with("foo",nil)
- @loader.expects(:import).with("bar",nil)
- @loader.load_until(["foo"], "bar") { |f| false }
- end
-
- it "should yield after each import" do
- yielded = []
- @loader.expects(:name2files).returns %w{foo bar}
- @loader.expects(:import).with("foo",nil)
- @loader.expects(:import).with("bar",nil)
- @loader.load_until(["foo"], "bar") { |f| yielded << f; false }
- yielded.should == %w{foo bar}
- end
-
- it "should stop importing when the yielded block returns true" do
- yielded = []
- @loader.expects(:name2files).returns %w{foo bar baz}
- @loader.expects(:import).with("foo",nil)
- @loader.expects(:import).with("bar",nil)
- @loader.expects(:import).with("baz",nil).never
- @loader.load_until(["foo"], "bar") { |f| true if f == "bar" }
- end
-
- it "should return the result of the block" do
- yielded = []
- @loader.expects(:name2files).returns %w{foo bar baz}
- @loader.expects(:import).with("foo",nil)
- @loader.expects(:import).with("bar",nil)
- @loader.expects(:import).with("baz",nil).never
- @loader.load_until(["foo"], "bar") { |f| 10 if f == "bar" }.should == 10
- end
-
- it "should return nil if the block never returns true" do
- @loader.expects(:name2files).returns %w{foo bar}
- @loader.expects(:import).with("foo",nil)
- @loader.expects(:import).with("bar",nil)
- @loader.load_until(["foo"], "bar") { |f| false }.should be_nil
- end
-
- it "should set the module name on any created resource types" do
- type = Puppet::Resource::Type.new(:hostclass, "mytype")
-
- Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{one}]
- @loader.stubs(:parse_file)
- @loader.load_until(["foo"], "one") { |f| type }
-
- type.module_name.should == "modname"
- end
- end
-
- describe "when mapping names to files" do
- {
- [["foo"], "::bar::baz"] => %w{bar/baz},
- [[""], "foo::bar"] => %w{foo foo/bar},
- [%w{foo}, "bar"] => %w{foo foo/bar bar},
- [%w{a b}, "bar"] => %w{a a/bar b b/bar bar},
- [%w{a::b::c}, "bar"] => %w{a a/b/c/bar bar},
- [%w{a::b}, "foo::bar"] => %w{a a/b/foo/bar foo/bar}
- }.each do |inputs, outputs|
- it "should produce #{outputs.inspect} from the #{inputs[0].inspect} namespace and #{inputs[1]} name" do
- @loader.name2files(*inputs).should == outputs
- end
+ @loader.expects(:import).with("foo/bar",nil).returns([])
+ @loader.expects(:import).with("foo",nil).returns([])
+ @loader.try_load_fqname(:hostclass, "foo::bar") { |f| false }
end
-
- it "should know when a given name has been loaded" do
- @loader.expects(:import).with("file",nil).returns([])
- @loader.try_load_fqname(:hostclass, "file") { |f| true }
- @loader.should be_loaded("file")
- end
end
describe "when importing" do
before do
Puppet::Parser::Files.stubs(:find_manifests).returns ["modname", %w{file}]
- @loader.stubs(:parse_file).returns(Puppet::Parser::AST::Hostclass.new(''))
- Puppet::Parser::Parser.any_instance.stubs(:parse)
++ Puppet::Parser::Parser.any_instance.stubs(:parse).returns(Puppet::Parser::AST::Hostclass.new(''))
+ Puppet::Parser::Parser.any_instance.stubs(:file=)
end
it "should return immediately when imports are being ignored" do
@@@ -78,26 -138,19 +73,19 @@@
it "should parse each found file" do
Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{/one}]
- @loader.expects(:parse_file).with("/one")
+ @loader.expects(:parse_file).with("/one").returns(Puppet::Parser::AST::Hostclass.new(''))
@loader.import("myfile")
end
it "should make each file qualified before attempting to parse it" do
Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{one}]
- @loader.expects(:parse_file).with("/current/one")
+ @loader.expects(:parse_file).with("/current/one").returns(Puppet::Parser::AST::Hostclass.new(''))
@loader.import("myfile", "/current/file")
end
- it "should know when a given file has been imported" do
- Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{/one}]
- @loader.import("myfile")
-
- @loader.should be_imported("/one")
- end
-
it "should not attempt to import files that have already been imported" do
Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{/one}]
- @loader.expects(:parse_file).once.returns(Puppet::Parser::AST::Hostclass.new(''))
- Puppet::Parser::Parser.any_instance.expects(:parse).once
++ Puppet::Parser::Parser.any_instance.expects(:parse).once.returns(Puppet::Parser::AST::Hostclass.new(''))
@loader.import("myfile")
# This will fail if it tries to reimport the file.
@@@ -108,7 -161,7 +96,7 @@@
describe "when parsing a file" do
before do
@parser = Puppet::Parser::Parser.new(@loader.environment)
- @parser.stubs(:parse)
+ @parser.stubs(:parse).returns(Puppet::Parser::AST::Hostclass.new(''))
@parser.stubs(:file=)
Puppet::Parser::Parser.stubs(:new).with(@loader.environment).returns @parser
end
@@@ -120,7 -173,7 +108,7 @@@
it "should assign the parser its file and parse" do
@parser.expects(:file=).with("/my/file")
- @parser.expects(:parse)
+ @parser.expects(:parse).returns(Puppet::Parser::AST::Hostclass.new(''))
@loader.parse_file("/my/file")
end
end
@@@ -132,15 -185,4 +120,15 @@@
@loader.known_resource_types.hostclass("foo").should be_instance_of(Puppet::Resource::Type)
end
+
+ describe "when deciding where to look for files" do
+ { 'foo' => ['foo'],
+ 'foo::bar' => ['foo/bar', 'foo'],
+ 'foo::bar::baz' => ['foo/bar/baz', 'foo/bar', 'foo']
+ }.each do |fqname, expected_paths|
+ it "should look for #{fqname.inspect} in #{expected_paths.inspect}" do
+ @loader.instance_eval { name2files(fqname) }.should == expected_paths
+ end
+ end
+ end
end
--
Puppet packaging for Debian
More information about the Pkg-puppet-devel
mailing list