[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