[Pkg-puppet-devel] [SCM] Puppet packaging for Debian branch, experimental, updated. debian/2.6.8-1-844-g7ec39d5

Max Martin max at puppetlabs.com
Tue May 10 08:11:17 UTC 2011


The following commit has been merged in the experimental branch:
commit 20bff91c8b8e450d913deeb1750a00a14f1b1061
Merge: e17cc651a9625576aa79af428bbaec702e216ac8 da4457be4dedaed5368bacf81a08f0429e21cd45
Author: Max Martin <max at puppetlabs.com>
Date:   Thu Apr 7 12:21:42 2011 -0700

    Merge branch '2.6.x' into next
    
    * 2.6.x:
      (maint) Indentation fixes
      (#6490) Add plugin initialization callback system to core
      Fix #4339 - Locally save the last report to $lastrunreport
      Fix #4339 - Save a last run report summary to $statedir/last_run_summary.yaml
      Fixed #3127 - removed legacy debug code
      Fixed #3127 - Fixed gem selection regex
      (#5437) Invalidate cached TypeCollection when there was an error parsing
      (#6937) Adjust formatting of recurse's desc
      (#6937) Document the recurse parameter of File type.
      (#6893) Document the cron type in the case of specials.
      (#5670) Don't trigger refresh from a failed resource
      Fixed #6554 - Missing $haveftool if/else conditional in install.rb breaking Ruby 1.9
    
    Conflicts (Manually resolved):
    	lib/puppet/application/agent.rb
    	lib/puppet/application/apply.rb
    	lib/puppet/configurer.rb
    	lib/puppet/resource/type_collection.rb
    	lib/puppet/type/file.rb
    	spec/integration/configurer_spec.rb
    	spec/unit/application/agent_spec.rb
    	spec/unit/application/apply_spec.rb
    	spec/unit/configurer_spec.rb
    	spec/unit/indirector/report/yaml_spec.rb
    	spec/unit/resource/type_collection_spec.rb
    
    Paired-with: Nick Lewis

diff --combined install.rb
index 6854363,351ba25..6bf4f55
--- a/install.rb
+++ b/install.rb
@@@ -53,6 -53,18 +53,6 @@@ rescue LoadErro
    $haverdoc = false
  end
  
 -begin
 -  if $haverdoc
 -    ronn = %x{which ronn}
 -    $haveman = true
 -  else
 -    $haveman = false
 -  end
 -rescue
 -  puts "Missing ronn; skipping man page creation"
 -  $haveman = false
 -end
 -
  PREREQS = %w{openssl facter xmlrpc/client xmlrpc/server cgi}
  MIN_FACTER_VERSION = 1.5
  
@@@ -80,8 -92,12 +80,12 @@@ def do_configs(configs, target, strip 
    Dir.mkdir(target) unless File.directory? target
    configs.each do |cf|
      ocf = File.join(InstallOptions.config_dir, cf.gsub(/#{strip}/, ''))
-     File.install(cf, ocf, 0644, true)
-   end
+     if $haveftools
+       File.install(cf, ocf, 0644, true)
+     else
+       FileUtils.install(cf, ocf, {:mode => 0644, :verbose => true})
+     end
+    end
  end
  
  def do_bins(bins, target, strip = 's?bin/')
@@@ -166,6 -182,15 +170,6 @@@ def prepare_installatio
    end
  
  
 -  if $haveman
 -    InstallOptions.man = true
 -    if $operatingsystem == "windows"
 -      InstallOptions.man  = false
 -    end
 -  else
 -    InstallOptions.man = false
 -  end
 -
    InstallOptions.tests = true
  
    ARGV.options do |opts|
@@@ -177,6 -202,9 +181,6 @@@
      opts.on('--[no-]ri', 'Prevents the creation of RI output.', 'Default off on mswin32.') do |onri|
        InstallOptions.ri = onri
      end
 -    opts.on('--[no-]man', 'Prevents the creation of man pages.', 'Default on.') do |onman|
 -    InstallOptions.man = onman
 -    end
      opts.on('--[no-]tests', 'Prevents the execution of unit tests.', 'Default on.') do |ontest|
        InstallOptions.tests = ontest
      end
@@@ -209,6 -237,7 +213,6 @@@
      end
      opts.on('--full', 'Performs a full installation. All', 'optional installation steps are run.') do |full|
        InstallOptions.rdoc    = true
 -      InstallOptions.man     = true
        InstallOptions.ri      = true
        InstallOptions.tests   = true
        InstallOptions.configs = true
@@@ -338,6 -367,33 +342,6 @@@ def build_ri(files
    end
  end
  
 -def build_man(bins, sbins)
 -  return unless $haveman
 -  begin
 -    # Locate ronn
 -    ronn = %x{which ronn}
 -    ronn.chomp!
 -    # Create puppet.conf.5 man page
 -    %x{bin/puppetdoc --reference configuration > ./man/man5/puppetconf.5.ronn}
 -    %x{#{ronn} -r ./man/man5/puppetconf.5.ronn}
 -    File.move("./man/man5/puppetconf.5", "./man/man5/puppet.conf.5")
 -    File.unlink("./man/man5/puppetconf.5.ronn")
 -
 -    # Create binary man pages
 -    binary = bins + sbins
 -    binary.each do |bin|
 -      b = bin.gsub( /(bin|sbin)\//, "")
 -      %x{#{bin} --help > ./man/man8/#{b}.8.ronn}
 -      %x{#{ronn} -r ./man/man8/#{b}.8.ronn}
 -      File.unlink("./man/man8/#{b}.8.ronn")
 -    end
 -
 -rescue SystemCallError
 -  $stderr.puts "Couldn't build man pages: " + $ERROR_INFO
 -  $stderr.puts "Continuing with install..."
 -  end
 -end
 -
  def run_tests(test_list)
      require 'test/unit/ui/console/testrunner'
      $LOAD_PATH.unshift "lib"
@@@ -431,6 -487,7 +435,6 @@@ prepare_installatio
  #run_tests(tests) if InstallOptions.tests
  #build_rdoc(rdoc) if InstallOptions.rdoc
  #build_ri(ri) if InstallOptions.ri
 -#build_man(bins, sbins) if InstallOptions.man
  do_configs(configs, InstallOptions.config_dir) if InstallOptions.configs
  do_bins(sbins, InstallOptions.sbin_dir)
  do_bins(bins, InstallOptions.bin_dir)
diff --combined lib/puppet/application.rb
index 7ef71bc,a028a15..57bd888
--- a/lib/puppet/application.rb
+++ b/lib/puppet/application.rb
@@@ -1,4 -1,5 +1,5 @@@
  require 'optparse'
+ require 'puppet/util/plugins'
  
  # This class handles all the aspects of a Puppet application/executable
  # * setting up options
@@@ -250,8 -251,7 +251,8 @@@ class Applicatio
  
    # Every app responds to --help
    option("--help", "-h") do |v|
 -    help
 +    puts help
 +    exit
    end
  
    def should_parse_config?
@@@ -298,11 -298,11 +299,11 @@@
  
    # This is the main application entry point
    def run
-     exit_on_fail("initialize") { preinit }
-     exit_on_fail("parse options") { parse_options }
+     exit_on_fail("initialize")               { hook('preinit')       { preinit } }
+     exit_on_fail("parse options")            { hook('parse_options') { parse_options } }
      exit_on_fail("parse configuration file") { Puppet.settings.parse } if should_parse_config?
-     exit_on_fail("prepare for execution") { setup }
-     exit_on_fail("run") { run_command }
+     exit_on_fail("prepare for execution")    { hook('setup')         { setup } }
+     exit_on_fail("run")                      { hook('run_command')   { run_command } }
    end
  
    def main
@@@ -386,17 -386,40 +387,24 @@@
    end
  
    def help
 -    if Puppet.features.usage?
 -      # RH:FIXME: My goodness, this is ugly.
 -      ::RDoc.const_set("PuppetSourceFile", name)
 -      #:stopdoc: # Issue #4161
 -      def (::RDoc).caller
 -        docfile = `grep -l 'Puppet::Application\\[:#{::RDoc::PuppetSourceFile}\\]' #{DOCPATTERN}`.chomp
 -        super << "#{docfile}:0"
 -      end
 -      #:startdoc:
 -      ::RDoc::usage && exit
 -    else
 -      puts "No help available unless you have RDoc::usage installed"
 -      exit
 -    end
 -  rescue Errno::ENOENT
 -    puts "No help available for puppet #{name}"
 -    exit
 +    "No help available for puppet #{name}"
    end
  
    private
  
    def exit_on_fail(message, code = 1)
-       yield
+     yield
    rescue RuntimeError, NotImplementedError => detail
-       puts detail.backtrace if Puppet[:trace]
-       $stderr.puts "Could not #{message}: #{detail}"
-       exit(code)
+     puts detail.backtrace if Puppet[:trace]
+     $stderr.puts "Could not #{message}: #{detail}"
+     exit(code)
+   end
+ 
+   def hook(step,&block)
+     Puppet::Plugins.send("before_application_#{step}",:application_object => self)
+     x = yield
+     Puppet::Plugins.send("after_application_#{step}",:application_object => self, :return_value => x)
+     x
    end
  end
  end
diff --combined lib/puppet/configurer.rb
index 72e387c,9f68ca4..cfeb73a
--- a/lib/puppet/configurer.rb
+++ b/lib/puppet/configurer.rb
@@@ -173,9 -173,7 +173,7 @@@ class Puppet::Configure
      report.finalize_report if trans
      puts report.summary if Puppet[:summarize]
      save_last_run_summary(report)
-     if Puppet[:report]
-       Puppet::Transaction::Report.indirection.save(report)
-     end
 -    report.save if Puppet[:report]
++    Puppet::Transaction::Report.indirection.save(report) if Puppet[:report]
    rescue => detail
      puts detail.backtrace if Puppet[:trace]
      Puppet.err "Could not send report: #{detail}"
@@@ -222,7 -220,7 +220,7 @@@
    def retrieve_catalog_from_cache(fact_options)
      result = nil
      @duration = thinmark do
 -      result = Puppet::Resource::Catalog.find(Puppet[:certname], fact_options.merge(:ignore_terminus => true))
 +      result = Puppet::Resource::Catalog.indirection.find(Puppet[:certname], fact_options.merge(:ignore_terminus => true))
      end
      Puppet.notice "Using cached catalog"
      result
@@@ -235,7 -233,7 +233,7 @@@
    def retrieve_new_catalog(fact_options)
      result = nil
      @duration = thinmark do
 -      result = Puppet::Resource::Catalog.find(Puppet[:certname], fact_options.merge(:ignore_cache => true))
 +      result = Puppet::Resource::Catalog.indirection.find(Puppet[:certname], fact_options.merge(:ignore_cache => true))
      end
      result
    rescue SystemExit,NoMemoryError
diff --combined lib/puppet/node/environment.rb
index d505306,807479b..dc63197
--- a/lib/puppet/node/environment.rb
+++ b/lib/puppet/node/environment.rb
@@@ -76,12 -76,12 +76,12 @@@ class Puppet::Node::Environmen
      # per environment semantics with an efficient most common cases; we almost
      # always just return our thread's known-resource types.  Only at the start
      # of a compilation (after our thread var has been set to nil) or when the
 -    # environment has changed do we delve deeper. 
 +    # environment has changed do we delve deeper.
      Thread.current[:known_resource_types] = nil if (krt = Thread.current[:known_resource_types]) && krt.environment != self
      Thread.current[:known_resource_types] ||= synchronize {
-       if @known_resource_types.nil? or @known_resource_types.stale?
+       if @known_resource_types.nil? or @known_resource_types.require_reparse?
          @known_resource_types = Puppet::Resource::TypeCollection.new(self)
 -        @known_resource_types.perform_initial_import
 +        @known_resource_types.import_ast(perform_initial_import, '')
        end
        @known_resource_types
      }
@@@ -128,7 -128,7 +128,7 @@@
      to_s.to_sym
    end
  
 -  # The only thing we care about when serializing an environment is its 
 +  # The only thing we care about when serializing an environment is its
    # identity; everything else is ephemeral and should not be stored or
    # transmitted.
    def to_zaml(z)
@@@ -147,30 -147,5 +147,32 @@@
      end
    end
  
 +  private
 +
 +  def perform_initial_import
 +    return empty_parse_result if Puppet.settings[:ignoreimport]
 +    parser = Puppet::Parser::Parser.new(self)
 +    if code = Puppet.settings.uninterpolated_value(:code, name.to_s) and code != ""
 +      parser.string = code
 +    else
 +      file = Puppet.settings.value(:manifest, name.to_s)
 +      parser.file = file
 +    end
 +    parser.parse
 +  rescue => detail
++    known_resource_types.parse_failed = true
++
 +    msg = "Could not parse for environment #{self}: #{detail}"
 +    error = Puppet::Error.new(msg)
 +    error.set_backtrace(detail.backtrace)
 +    raise error
 +  end
 +
 +  def empty_parse_result
 +    # Return an empty toplevel hostclass to use as the result of
 +    # perform_initial_import when no file was actually loaded.
 +    return Puppet::Parser::AST::Hostclass.new('')
 +  end
 +
    @root = new(:'*root*')
  end
diff --combined lib/puppet/resource/type_collection.rb
index 6ab978f,4210475..9fe7cdd
--- a/lib/puppet/resource/type_collection.rb
+++ b/lib/puppet/resource/type_collection.rb
@@@ -1,5 -1,5 +1,6 @@@
  class Puppet::Resource::TypeCollection
    attr_reader :environment
++  attr_accessor :parse_failed
  
    def clear
      @hostclasses.clear
@@@ -19,12 -19,6 +20,12 @@@
      @watched_files = {}
    end
  
 +  def import_ast(ast, modname)
 +    ast.instantiate(modname).each do |instance|
 +      add(instance)
 +    end
 +  end
 +
    def inspect
      "TypeCollection" + { :hostclasses => @hostclasses.keys, :definitions => @definitions.keys, :nodes => @nodes.keys }.inspect
    end
@@@ -102,8 -96,50 +103,8 @@@
      @definitions[munge_name(name)]
    end
  
 -  def find(namespaces, name, type)
 -    #Array("") == [] for some reason
 -    namespaces = [namespaces] unless namespaces.is_a?(Array)
 -
 -    if name =~ /^::/
 -      return send(type, name.sub(/^::/, ''))
 -    end
 -
 -    namespaces.each do |namespace|
 -      ary = namespace.split("::")
 -
 -      while ary.length > 0
 -        tmp_namespace = ary.join("::")
 -        if r = find_partially_qualified(tmp_namespace, name, type)
 -          return r
 -        end
 -
 -        # Delete the second to last object, which reduces our namespace by one.
 -        ary.pop
 -      end
 -
 -      if result = send(type, name)
 -        return result
 -      end
 -    end
 -    nil
 -  end
 -
 -  def find_or_load(namespaces, name, type)
 -    name      = name.downcase
 -    namespaces = [namespaces] unless namespaces.is_a?(Array)
 -    namespaces = namespaces.collect { |ns| ns.downcase }
 -
 -    # This could be done in the load_until, but the knowledge seems to
 -    # belong here.
 -    if r = find(namespaces, name, type)
 -      return r
 -    end
 -
 -    loader.load_until(namespaces, name) { find(namespaces, name, type) }
 -  end
 -
    def find_node(namespaces, name)
 -    find("", name, :node)
 +    @nodes[munge_name(name)]
    end
  
    def find_hostclass(namespaces, name)
@@@ -120,6 -156,28 +121,10 @@@
      end
    end
  
 -  def perform_initial_import
 -    parser = Puppet::Parser::Parser.new(environment)
 -    if code = Puppet.settings.uninterpolated_value(:code, environment.to_s) and code != ""
 -      parser.string = code
 -    else
 -      file = Puppet.settings.value(:manifest, environment.to_s)
 -      parser.file = file
 -    end
 -    parser.parse
 -  rescue => detail
 -    @parse_failed = true
 -
 -    msg = "Could not parse for environment #{environment}: #{detail}"
 -    error = Puppet::Error.new(msg)
 -    error.set_backtrace(detail.backtrace)
 -    raise error
 -  end
 -
+   def require_reparse?
+     @parse_failed || stale?
+   end
+ 
    def stale?
      @watched_files.values.detect { |file| file.changed? }
    end
@@@ -148,52 -206,8 +153,52 @@@
  
    private
  
 -  def find_partially_qualified(namespace, name, type)
 -    send(type, [namespace, name].join("::"))
 +  # Return a list of all possible fully-qualified names that might be
 +  # meant by the given name, in the context of namespaces.
 +  def resolve_namespaces(namespaces, name)
 +    name      = name.downcase
 +    if name =~ /^::/
 +      # name is explicitly fully qualified, so just return it, sans
 +      # initial "::".
 +      return [name.sub(/^::/, '')]
 +    end
 +    if name == ""
 +      # The name "" has special meaning--it always refers to a "main"
 +      # hostclass which contains all toplevel resources.
 +      return [""]
 +    end
 +
 +    namespaces = [namespaces] unless namespaces.is_a?(Array)
 +    namespaces = namespaces.collect { |ns| ns.downcase }
 +
 +    result = []
 +    namespaces.each do |namespace|
 +      ary = namespace.split("::")
 +
 +      # Search each namespace nesting in innermost-to-outermost order.
 +      while ary.length > 0
 +        result << "#{ary.join("::")}::#{name}"
 +        ary.pop
 +      end
 +
 +      # Finally, search the toplevel namespace.
 +      result << name
 +    end
 +
 +    return result.uniq
 +  end
 +
 +  # Resolve namespaces and find the given object.  Autoload it if
 +  # necessary.
 +  def find_or_load(namespaces, name, type)
 +    resolve_namespaces(namespaces, name).each do |fqname|
 +      if result = send(type, fqname) || loader.try_load_fqname(type, fqname)
 +        return result
 +      end
 +    end
 +
 +    # Nothing found.
 +    return nil
    end
  
    def munge_name(name)
diff --combined lib/puppet/transaction.rb
index 8118178,48154ad..0533273
--- a/lib/puppet/transaction.rb
+++ b/lib/puppet/transaction.rb
@@@ -4,7 -4,6 +4,7 @@@
  require 'puppet'
  require 'puppet/util/tagging'
  require 'puppet/application'
 +require 'sha1'
  
  class Puppet::Transaction
    require 'puppet/transaction/event'
@@@ -13,7 -12,7 +13,7 @@@
    require 'puppet/resource/status'
  
    attr_accessor :component, :catalog, :ignoreschedules
 -  attr_accessor :sorted_resources, :configurator
 +  attr_accessor :configurator
  
    # The report, once generated.
    attr_accessor :report
@@@ -48,7 -47,7 +48,7 @@@
    def apply(resource, ancestor = nil)
      status = resource_harness.evaluate(resource)
      add_resource_status(status)
-     event_manager.queue_events(ancestor || resource, status.events)
+     event_manager.queue_events(ancestor || resource, status.events) unless status.failed?
    rescue => detail
      resource.err "Could not evaluate: #{detail}"
    end
@@@ -58,36 -57,68 +58,36 @@@
      report.resource_statuses.values.find_all { |status| status.changed }.collect { |status| catalog.resource(status.resource) }
    end
  
 +  # Find all of the applied resources (including failed attempts).
 +  def applied_resources
 +    report.resource_statuses.values.collect { |status| catalog.resource(status.resource) }
 +  end
 +
    # Copy an important relationships from the parent to the newly-generated
    # child resource.
 -  def make_parent_child_relationship(resource, children)
 -    depthfirst = resource.depthfirst?
 -
 -    children.each do |gen_child|
 -      if depthfirst
 -        edge = [gen_child, resource]
 -      else
 -        edge = [resource, gen_child]
 -      end
 -      relationship_graph.add_vertex(gen_child)
 -
 -      unless relationship_graph.edge?(edge[1], edge[0])
 -        relationship_graph.add_edge(*edge)
 -      else
 -        resource.debug "Skipping automatic relationship to #{gen_child}"
 -      end
 +  def add_conditional_directed_dependency(parent, child, label=nil)
 +    relationship_graph.add_vertex(child)
 +    edge = parent.depthfirst? ? [child, parent] : [parent, child]
 +    if relationship_graph.edge?(*edge.reverse)
 +      parent.debug "Skipping automatic relationship to #{child}"
 +    else
 +      relationship_graph.add_edge(edge[0],edge[1],label)
      end
    end
  
 -  # See if the resource generates new resources at evaluation time.
 -  def eval_generate(resource)
 -    generate_additional_resources(resource, :eval_generate)
 -  end
 -
    # Evaluate a single resource.
    def eval_resource(resource, ancestor = nil)
      if skip?(resource)
        resource_status(resource).skipped = true
      else
 -      eval_children_and_apply_resource(resource, ancestor)
 +      resource_status(resource).scheduled = true
 +      apply(resource, ancestor)
      end
  
      # Check to see if there are any events queued for this resource
      event_manager.process_events(resource)
    end
  
 -  def eval_children_and_apply_resource(resource, ancestor = nil)
 -    resource_status(resource).scheduled = true
 -
 -    # We need to generate first regardless, because the recursive
 -    # actions sometimes change how the top resource is applied.
 -    children = eval_generate(resource)
 -
 -    if ! children.empty? and resource.depthfirst?
 -      children.each do |child|
 -        # The child will never be skipped when the parent isn't
 -        eval_resource(child, ancestor || resource)
 -      end
 -    end
 -
 -    # Perform the actual changes
 -    apply(resource, ancestor)
 -
 -    if ! children.empty? and ! resource.depthfirst?
 -      children.each do |child|
 -        eval_resource(child, ancestor || resource)
 -      end
 -    end
 -  end
 -
    # This method does all the actual work of running a transaction.  It
    # collects all of the changes, executes them, and responds to any
    # necessary events.
@@@ -100,13 -131,19 +100,13 @@@
      Puppet.info "Applying configuration version '#{catalog.version}'" if catalog.version
  
      begin
 -      @sorted_resources.each do |resource|
 -        next if stop_processing?
 +      relationship_graph.traverse do |resource|
          if resource.is_a?(Puppet::Type::Component)
            Puppet.warning "Somehow left a component in the relationship graph"
 -          next
 +        else
 +          seconds = thinmark { eval_resource(resource) }
 +          resource.info "Evaluated in %0.2f seconds" % seconds if Puppet[:evaltrace] and @catalog.host_config?
          end
 -        ret = nil
 -        seconds = thinmark do
 -          ret = eval_resource(resource)
 -        end
 -
 -        resource.info "Evaluated in %0.2f seconds" % seconds if Puppet[:evaltrace] and @catalog.host_config?
 -        ret
        end
      ensure
        # And then close the transaction log.
@@@ -140,66 -177,48 +140,66 @@@
      found_failed
    end
  
 +  def eval_generate(resource)
 +    raise Puppet::DevError,"Depthfirst resources are not supported by eval_generate" if resource.depthfirst?
 +    begin
 +      made = resource.eval_generate.uniq.reverse
 +    rescue => detail
 +      puts detail.backtrace if Puppet[:trace]
 +      resource.err "Failed to generate additional resources using 'eval_generate: #{detail}"
 +      return
 +    end
 +    made.each do |res|
 +      begin
 +        res.tag(*resource.tags)
 +        @catalog.add_resource(res)
 +        res.finish
 +      rescue Puppet::Resource::Catalog::DuplicateResourceError
 +        res.info "Duplicate generated resource; skipping"
 +      end
 +    end
 +    sentinal = Puppet::Type::Whit.new(:name => "completed_#{resource.title}", :catalog => resource.catalog)
 +    relationship_graph.adjacent(resource,:direction => :out,:type => :edges).each { |e|
 +      add_conditional_directed_dependency(sentinal, e.target, e.label)
 +      relationship_graph.remove_edge! e
 +    }
 +    default_label = Puppet::Resource::Catalog::Default_label
 +    made.each do |res|
 +      add_conditional_directed_dependency(made.find { |r| r != res && r.name == res.name[0,r.name.length]} || resource, res)
 +      add_conditional_directed_dependency(res, sentinal, default_label)
 +    end
 +    add_conditional_directed_dependency(resource, sentinal, default_label)
 +  end
 +
    # A general method for recursively generating new resources from a
    # resource.
 -  def generate_additional_resources(resource, method)
 -    return [] unless resource.respond_to?(method)
 +  def generate_additional_resources(resource)
 +    return unless resource.respond_to?(:generate)
      begin
 -      made = resource.send(method)
 +      made = resource.generate
      rescue => detail
        puts detail.backtrace if Puppet[:trace]
 -      resource.err "Failed to generate additional resources using '#{method}': #{detail}"
 +      resource.err "Failed to generate additional resources using 'generate': #{detail}"
      end
 -    return [] unless made
 +    return unless made
      made = [made] unless made.is_a?(Array)
 -    made.uniq.find_all do |res|
 +    made.uniq.each do |res|
        begin
          res.tag(*resource.tags)
 -        @catalog.add_resource(res) do |r|
 -          r.finish
 -          make_parent_child_relationship(resource, [r])
 -
 -          # Call 'generate' recursively
 -          generate_additional_resources(r, method)
 -        end
 -        true
 +        @catalog.add_resource(res)
 +        res.finish
 +        add_conditional_directed_dependency(resource, res)
 +        generate_additional_resources(res)
        rescue Puppet::Resource::Catalog::DuplicateResourceError
          res.info "Duplicate generated resource; skipping"
 -        false
        end
      end
    end
  
    # Collect any dynamically generated resources.  This method is called
    # before the transaction starts.
 -  def generate
 -    list = @catalog.vertices
 -    newlist = []
 -    while ! list.empty?
 -      list.each do |resource|
 -        newlist += generate_additional_resources(resource, :generate)
 -      end
 -      list = newlist
 -      newlist = []
 -    end
 +  def xgenerate
 +    @catalog.vertices.each { |resource| generate_additional_resources(resource) }
    end
  
    # Should we ignore tags?
@@@ -212,7 -231,7 +212,7 @@@
    def initialize(catalog)
      @catalog = catalog
  
 -    @report = Report.new("apply", catalog.version)
 +    @report = Puppet::Transaction::Report.new("apply")
  
      @event_manager = Puppet::Transaction::EventManager.new(self)
  
@@@ -245,75 -264,18 +245,75 @@@
    # Prepare to evaluate the resources in a transaction.
    def prepare
      # Now add any dynamically generated resources
 -    generate
 +    xgenerate
  
      # Then prefetch.  It's important that we generate and then prefetch,
      # so that any generated resources also get prefetched.
      prefetch
 +  end
 +
  
 -    # This will throw an error if there are cycles in the graph.
 -    @sorted_resources = relationship_graph.topsort
 +  # We want to monitor changes in the relationship graph of our
 +  # catalog but this is complicated by the fact that the catalog
 +  # both is_a graph and has_a graph, by the fact that changes to 
 +  # the structure of the object can have adverse serialization
 +  # effects, by threading issues, by order-of-initialization issues,
 +  # etc.  
 +  #
 +  # Since the proper lifetime/scope of the monitoring is a transaction
 +  # and the transaction is already commiting a mild law-of-demeter 
 +  # transgression, we cut the Gordian knot here by simply wrapping the
 +  # transaction's view of the resource graph to capture and maintain
 +  # the information we need.  Nothing outside the transaction needs
 +  # this information, and nothing outside the transaction can see it
 +  # except via the Transaction#relationship_graph
 +
 +  class Relationship_graph_wrapper
 +    attr_reader :real_graph,:transaction,:ready,:generated,:done,:unguessable_deterministic_key
 +    def initialize(real_graph,transaction)
 +      @real_graph = real_graph
 +      @transaction = transaction
 +      @ready = {}
 +      @generated = {}
 +      @done = {}
 +      @unguessable_deterministic_key = Hash.new { |h,k| h[k] = Digest::SHA1.hexdigest("NaCl, MgSO4 (salts) and then #{k.title}") }
 +      vertices.each { |v| check_if_now_ready(v) }
 +    end
 +    def method_missing(*args,&block)
 +      real_graph.send(*args,&block)
 +    end
 +    def add_vertex(v)
 +      real_graph.add_vertex(v)
 +      check_if_now_ready(v) # ?????????????????????????????????????????
 +    end
 +    def add_edge(f,t,label=nil)
 +      ready.delete(t)
 +      real_graph.add_edge(f,t,label)
 +    end
 +    def check_if_now_ready(r)
 +      ready[r] = true if direct_dependencies_of(r).all? { |r2| done[r2] }
 +    end
 +    def next_resource
 +      ready.keys.sort_by { |r0| unguessable_deterministic_key[r0] }.first
 +    end
 +    def traverse(&block)
 +      real_graph.report_cycles_in_graph
 +      while (r = next_resource) && !transaction.stop_processing?
 +        if !generated[r] && r.respond_to?(:eval_generate)
 +          transaction.eval_generate(r)
 +          generated[r] = true
 +        else
 +          ready.delete(r)
 +          yield r
 +          done[r] = true
 +          direct_dependents_of(r).each { |v| check_if_now_ready(v) }
 +        end
 +      end
 +    end
    end
  
    def relationship_graph
 -    catalog.relationship_graph
 +    @relationship_graph ||= Relationship_graph_wrapper.new(catalog.relationship_graph,self)
    end
  
    def add_resource_status(status)
diff --combined lib/puppet/type/file.rb
index 5632d41,630ebe5..715836b
--- a/lib/puppet/type/file.rb
+++ b/lib/puppet/type/file.rb
@@@ -44,7 -44,7 +44,7 @@@ Puppet::Type.newtype(:file) d
      # convert the current path in an index into the collection and the last
      # path name. The aim is to use less storage for all common paths in a hierarchy
      munge do |value|
 -      path, name = File.split(value.gsub(/\/+/,'/'))
 +      path, name = ::File.split(value.gsub(/\/+/,'/'))
        { :index => Puppet::FileCollection.collection.index(path), :name => name }
      end
  
@@@ -55,7 -55,7 +55,7 @@@
        if value[:name] == '/'
          basedir
        else
 -        File.join( basedir, value[:name] )
 +        ::File.join( basedir, value[:name] )
        end
      end
    end
@@@ -123,15 -123,16 +123,16 @@@
    newparam(:recurse) do
      desc "Whether and how deeply to do recursive
        management. Options are:
-         inf,true  => Regular style recursion on both remote and local
-                      directory structure.
-         remote    => Descends recursively into the remote directory
-                      but not the local directory. Allows copying of
-                      a few files into a directory containing many
-                      unmanaged files without scanning all the local files.
-         false     => Default of no-recursion.
-         [0-9]+    => Both, but limit recursion. Warning: this syntax
-                      is deprecated and has moved to recurselimit.
+ 
+       * `inf,true` --- Regular style recursion on both remote and local
+         directory structure.
+       * `remote` --- Descends recursively into the remote directory
+         but not the local directory. Allows copying of
+         a few files into a directory containing many
+         unmanaged files without scanning all the local files.
+       * `false` --- Default of no recursion.
+       * `[0-9]+` --- Same as true, but limit recursion. Warning: this syntax
+         has been deprecated in favor of the `recurselimit` attribute.
      "
  
      newvalues(:true, :false, :inf, :remote, /^[0-9]+$/)
@@@ -258,7 -259,7 +259,7 @@@
  
    # Autorequire any parent directories.
    autorequire(:file) do
 -    basedir = File.dirname(self[:path])
 +    basedir = ::File.dirname(self[:path])
      if basedir != self[:path]
        basedir
      else
@@@ -313,11 -314,13 +314,11 @@@
      return self.new(:name => base, :recurse => true, :recurselimit => 1, :audit => :all).recurse_local.values
    end
  
 -  @depthfirst = false
 -
    # Determine the user to write files as.
    def asuser
      if self.should(:owner) and ! self.should(:owner).is_a?(Symbol)
        writeable = Puppet::Util::SUIDManager.asuser(self.should(:owner)) {
 -        FileTest.writable?(File.dirname(self[:path]))
 +        FileTest.writable?(::File.dirname(self[:path]))
        }
  
        # If the parent directory is writeable, then we execute
@@@ -420,7 -423,7 +421,7 @@@
    # Create a new file or directory object as a child to the current
    # object.
    def newchild(path)
 -    full_path = File.join(self[:path], path)
 +    full_path = ::File.join(self[:path], path)
  
      # Add some new values to our original arguments -- these are the ones
      # set at initialization.  We specifically want to exclude any param
@@@ -472,7 -475,8 +473,7 @@@
    # be used to copy remote files, manage local files, and/or make links
    # to map to another directory.
    def recurse
 -    children = {}
 -    children = recurse_local if self[:recurse] != :remote
 +    children = (self[:recurse] == :remote) ? {} : recurse_local
  
      if self[:target]
        recurse_link(children)
@@@ -493,23 -497,27 +494,23 @@@
    # not likely to have many actual conflicts, which is good, because
    # this is a pretty inefficient implementation.
    def remove_less_specific_files(files)
 -    mypath = self[:path].split(File::Separator)
 +    mypath = self[:path].split(::File::Separator)
      other_paths = catalog.vertices.
        select  { |r| r.is_a?(self.class) and r[:path] != self[:path] }.
 -      collect { |r| r[:path].split(File::Separator) }.
 +      collect { |r| r[:path].split(::File::Separator) }.
        select  { |p| p[0,mypath.length]  == mypath }
  
      return files if other_paths.empty?
  
      files.reject { |file|
 -      path = file[:path].split(File::Separator)
 +      path = file[:path].split(::File::Separator)
        other_paths.any? { |p| path[0,p.length] == p }
        }
    end
  
    # A simple method for determining whether we should be recursing.
    def recurse?
 -    return false unless @parameters.include?(:recurse)
 -
 -    val = @parameters[:recurse].value
 -
 -    !!(val and (val == true or val == :remote))
 +    self[:recurse] == true or self[:recurse] == :remote
    end
  
    # Recurse the target of the link.
@@@ -581,10 -589,13 +582,10 @@@
    end
  
    def perform_recursion(path)
 -
 -    Puppet::FileServing::Metadata.search(
 -
 +    Puppet::FileServing::Metadata.indirection.search(
        path,
        :links => self[:links],
        :recurse => (self[:recurse] == :remote ? true : self[:recurse]),
 -
        :recurselimit => self[:recurselimit],
        :ignore => self[:ignore],
        :checksum_type => (self[:source] || self[:content]) ? self[:checksum] : :none
@@@ -612,7 -623,7 +613,7 @@@
        end
      when "link", "file"
        debug "Removing existing #{s.ftype} for replacement with #{should}"
 -      File.unlink(self[:path])
 +      ::File.unlink(self[:path])
      else
        self.fail "Could not back up files of type #{s.ftype}"
      end
@@@ -677,7 -688,7 +678,7 @@@
      path = self[:path]
  
      begin
 -      File.send(method, self[:path])
 +      ::File.send(method, self[:path])
      rescue Errno::ENOENT => error
        return nil
      rescue Errno::EACCES => error
@@@ -703,7 -714,7 +704,7 @@@
      use_temporary_file = write_temporary_file?
      if use_temporary_file
        path = "#{self[:path]}.puppettmp_#{rand(10000)}"
 -      path = "#{self[:path]}.puppettmp_#{rand(10000)}" while File.exists?(path) or File.symlink?(path)
 +      path = "#{self[:path]}.puppettmp_#{rand(10000)}" while ::File.exists?(path) or ::File.symlink?(path)
      else
        path = self[:path]
      end
@@@ -712,18 -723,18 +713,18 @@@
      umask = mode ? 000 : 022
      mode_int = mode ? mode.to_i(8) : nil
  
 -    content_checksum = Puppet::Util.withumask(umask) { File.open(path, 'w', mode_int ) { |f| write_content(f) } }
 +    content_checksum = Puppet::Util.withumask(umask) { ::File.open(path, 'w', mode_int ) { |f| write_content(f) } }
  
      # And put our new file in place
      if use_temporary_file # This is only not true when our file is empty.
        begin
          fail_if_checksum_is_wrong(path, content_checksum) if validate_checksum?
 -        File.rename(path, self[:path])
 +        ::File.rename(path, self[:path])
        rescue => detail
          fail "Could not rename temporary file #{path} to #{self[:path]}: #{detail}"
        ensure
          # Make sure the created file gets removed
 -        File.unlink(path) if FileTest.exists?(path)
 +        ::File.unlink(path) if FileTest.exists?(path)
        end
      end
  
diff --combined spec/integration/transaction_spec.rb
index ff15e59,66a049e..e608faa
--- a/spec/integration/transaction_spec.rb
+++ b/spec/integration/transaction_spec.rb
@@@ -1,6 -1,6 +1,6 @@@
  #!/usr/bin/env ruby
  
 -require File.dirname(__FILE__) + '/../spec_helper'
 +require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
  
  require 'puppet_spec/files'
  require 'puppet/transaction'
@@@ -135,33 -135,26 +135,26 @@@ describe Puppet::Transaction d
    it "should not let one failed refresh result in other refreshes failing" do
      path = tmpfile("path")
      newfile = tmpfile("file")
- 
-           file = Puppet::Type.type(:file).new(
-                 
+       file = Puppet::Type.type(:file).new(
        :path => path,
        :ensure => "file"
      )
  
-           exec1 = Puppet::Type.type(:exec).new(
-                 
+     exec1 = Puppet::Type.type(:exec).new(
        :path => ENV["PATH"],
        :command => "touch /this/cannot/possibly/exist",
        :logoutput => true,
        :refreshonly => true,
        :subscribe => file,
        :title => "one"
      )
  
-           exec2 = Puppet::Type.type(:exec).new(
-                 
+     exec2 = Puppet::Type.type(:exec).new(
        :path => ENV["PATH"],
        :command => "touch #{newfile}",
        :logoutput => true,
        :refreshonly => true,
        :subscribe => [file, exec1],
-         
        :title => "two"
      )
  
@@@ -178,22 -171,18 +171,18 @@@
  
      Puppet[:ignoreschedules] = false
  
-           file = Puppet::Type.type(:file).new(
-                 
+     file = Puppet::Type.type(:file).new(
        :name => tmpfile("file"),
-         
        :ensure => "file",
        :backup => false
      )
  
      fname = tmpfile("exec")
  
-           exec = Puppet::Type.type(:exec).new(
-                 
+     exec = Puppet::Type.type(:exec).new(
        :name => "touch #{fname}",
        :path => "/usr/bin:/bin",
        :schedule => "monthly",
-         
        :subscribe => Puppet::Resource.new("file", file.name)
      )
  
@@@ -230,29 -219,21 +219,21 @@@
  
    it "should not attempt to evaluate resources with failed dependencies" do
  
-           exec = Puppet::Type.type(:exec).new(
-                 
+     exec = Puppet::Type.type(:exec).new(
        :command => "/bin/mkdir /this/path/cannot/possibly/exit",
        :title => "mkdir"
      )
  
- 
-           file1 = Puppet::Type.type(:file).new(
-                 
+     file1 = Puppet::Type.type(:file).new(
        :title => "file1",
        :path => tmpfile("file1"),
-         
        :require => exec,
        :ensure => :file
      )
  
- 
-           file2 = Puppet::Type.type(:file).new(
-                 
+     file2 = Puppet::Type.type(:file).new(
        :title => "file2",
        :path => tmpfile("file2"),
-         
        :require => file1,
        :ensure => :file
      )
@@@ -264,6 -245,32 +245,32 @@@
      FileTest.should_not be_exists(file2[:path])
    end
  
+   it "should not trigger subscribing resources on failure" do
+     file1 = tmpfile("file1")
+     file2 = tmpfile("file2")
+ 
+     create_file1 = Puppet::Type.type(:exec).new(
+       :command => "/usr/bin/touch #{file1}"
+     )
+ 
+     exec = Puppet::Type.type(:exec).new(
+       :command => "/bin/mkdir /this/path/cannot/possibly/exit",
+       :title => "mkdir",
+       :notify => create_file1
+     )
+ 
+     create_file2 = Puppet::Type.type(:exec).new(
+       :command => "/usr/bin/touch #{file2}",
+       :subscribe => exec
+     )
+ 
+     catalog = mk_catalog(exec, create_file1, create_file2)
+     catalog.apply
+ 
+     FileTest.should_not be_exists(file1)
+     FileTest.should_not be_exists(file2)
+   end
+ 
    # #801 -- resources only checked in noop should be rescheduled immediately.
    it "should immediately reschedule noop resources" do
      Puppet::Type.type(:schedule).mkdefaultschedules
diff --combined spec/unit/configurer_spec.rb
index f8acdd0,a4b627c..d21d86e
--- a/spec/unit/configurer_spec.rb
+++ b/spec/unit/configurer_spec.rb
@@@ -3,7 -3,7 +3,7 @@@
  #  Created by Luke Kanies on 2007-11-12.
  #  Copyright (c) 2007. All rights reserved.
  
 -require File.dirname(__FILE__) + '/../spec_helper'
 +require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
  require 'puppet/configurer'
  
  describe Puppet::Configurer do
@@@ -82,7 -82,9 +82,7 @@@ describe Puppet::Configurer, "when exec
      @catalog.stubs(:apply)
      @agent.stubs(:retrieve_catalog).returns @catalog
      @agent.stubs(:save_last_run_summary)
 -
 -    Puppet::Util::Log.stubs(:newdestination)
 -    Puppet::Util::Log.stubs(:close)
 +    Puppet::Transaction::Report.indirection.stubs(:save)
    end
  
    it "should prepare for the run" do
@@@ -93,7 -95,7 +93,7 @@@
  
    it "should initialize a transaction report if one is not provided" do
      report = Puppet::Transaction::Report.new("apply")
 -    Puppet::Transaction::Report.expects(:new).returns report
 +    Puppet::Transaction::Report.expects(:new).at_least_once.returns report
  
      @agent.run
    end
@@@ -118,7 -120,6 +118,7 @@@
      report = Puppet::Transaction::Report.new("apply")
      Puppet::Transaction::Report.expects(:new).returns report
  
 +    @agent.stubs(:send_report)
      Puppet::Util::Log.expects(:newdestination).with(report)
  
      @agent.run
@@@ -211,10 -212,9 +211,10 @@@
      report = Puppet::Transaction::Report.new("apply")
      Puppet::Transaction::Report.expects(:new).returns(report)
  
 -    Puppet::Util::Log.expects(:close).with(report)
 +    report.expects(:<<).at_least_once
  
      @agent.run
 +    Puppet::Util::Log.destinations.should_not include(report)
    end
  
    it "should return the report as the result of the run" do
@@@ -226,10 -226,12 +226,12 @@@
  end
  
  describe Puppet::Configurer, "when sending a report" do
+   include PuppetSpec::Files
+ 
    before do
      Puppet.settings.stubs(:use).returns(true)
      @configurer = Puppet::Configurer.new
-     @configurer.stubs(:save_last_run_summary)
+     Puppet[:lastrunfile] = tmpfile('last_run_file')
  
      @report = Puppet::Transaction::Report.new("apply")
      @trans = stub 'transaction'
@@@ -259,14 -261,14 +261,14 @@@
    it "should save the report if reporting is enabled" do
      Puppet.settings[:report] = true
  
 -    @report.expects(:save)
 +    Puppet::Transaction::Report.indirection.expects(:save).with(@report)
      @configurer.send_report(@report, nil)
    end
  
    it "should not save the report if reporting is disabled" do
      Puppet.settings[:report] = false
  
 -    @report.expects(:save).never
 +    Puppet::Transaction::Report.indirection.expects(:save).never
      @configurer.send_report(@report, nil)
    end
  
@@@ -277,17 -279,17 +279,17 @@@
      @configurer.send_report(@report, nil)
    end
  
-   it "should not save the last run summary if reporting is disabled" do
+   it "should save the last run summary if reporting is disabled" do
      Puppet.settings[:report] = false
  
-     @configurer.expects(:save_last_run_summary).never
+     @configurer.expects(:save_last_run_summary).with(@report)
      @configurer.send_report(@report, nil)
    end
  
    it "should log but not fail if saving the report fails" do
      Puppet.settings[:report] = true
  
 -    @report.expects(:save).raises "whatever"
 +    Puppet::Transaction::Report.indirection.expects(:save).with(@report).raises "whatever"
  
      Puppet.expects(:err)
      lambda { @configurer.send_report(@report, nil) }.should_not raise_error
@@@ -344,15 -346,15 +346,15 @@@ describe Puppet::Configurer, "when retr
      end
  
      it "should first look in the cache for a catalog" do
 -      Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns @catalog
 -      Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.never
 +      Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns @catalog
 +      Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.never
  
        @agent.retrieve_catalog.should == @catalog
      end
  
      it "should compile a new catalog if none is found in the cache" do
 -      Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns nil
 -      Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns @catalog
 +      Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns nil
 +      Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns @catalog
  
        @agent.retrieve_catalog.should == @catalog
      end
@@@ -361,7 -363,7 +363,7 @@@
    describe "when not using a REST terminus for catalogs" do
      it "should not pass any facts when retrieving the catalog" do
        @agent.expects(:facts_for_uploading).never
 -      Puppet::Resource::Catalog.expects(:find).with { |name, options|
 +      Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options|
          options[:facts].nil?
        }.returns @catalog
  
@@@ -372,7 -374,7 +374,7 @@@
    describe "when using a REST terminus for catalogs" do
      it "should pass the prepared facts and the facts format as arguments when retrieving the catalog" do
        @agent.expects(:facts_for_uploading).returns(:facts => "myfacts", :facts_format => :foo)
 -      Puppet::Resource::Catalog.expects(:find).with { |name, options|
 +      Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options|
          options[:facts] == "myfacts" and options[:facts_format] == :foo
        }.returns @catalog
  
@@@ -381,7 -383,7 +383,7 @@@
    end
  
    it "should use the Catalog class to get its catalog" do
 -    Puppet::Resource::Catalog.expects(:find).returns @catalog
 +    Puppet::Resource::Catalog.indirection.expects(:find).returns @catalog
  
      @agent.retrieve_catalog
    end
@@@ -389,20 -391,20 +391,20 @@@
    it "should use its certname to retrieve the catalog" do
      Facter.stubs(:value).returns "eh"
      Puppet.settings[:certname] = "myhost.domain.com"
 -    Puppet::Resource::Catalog.expects(:find).with { |name, options| name == "myhost.domain.com" }.returns @catalog
 +    Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| name == "myhost.domain.com" }.returns @catalog
  
      @agent.retrieve_catalog
    end
  
    it "should default to returning a catalog retrieved directly from the server, skipping the cache" do
 -    Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns @catalog
 +    Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns @catalog
  
      @agent.retrieve_catalog.should == @catalog
    end
  
    it "should log and return the cached catalog when no catalog can be retrieved from the server" do
 -    Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns nil
 -    Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns @catalog
 +    Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns nil
 +    Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns @catalog
  
      Puppet.expects(:notice)
  
@@@ -410,15 -412,15 +412,15 @@@
    end
  
    it "should not look in the cache for a catalog if one is returned from the server" do
 -    Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns @catalog
 -    Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.never
 +    Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns @catalog
 +    Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_terminus] == true }.never
  
      @agent.retrieve_catalog.should == @catalog
    end
  
    it "should return the cached catalog when retrieving the remote catalog throws an exception" do
 -    Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.raises "eh"
 -    Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns @catalog
 +    Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.raises "eh"
 +    Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns @catalog
  
      @agent.retrieve_catalog.should == @catalog
    end
@@@ -426,7 -428,7 +428,7 @@@
    it "should log and return nil if no catalog can be retrieved from the server and :usecacheonfailure is disabled" do
      Puppet.stubs(:[])
      Puppet.expects(:[]).with(:usecacheonfailure).returns false
 -    Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns nil
 +    Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns nil
  
      Puppet.expects(:warning)
  
@@@ -434,21 -436,21 +436,21 @@@
    end
  
    it "should return nil if no cached catalog is available and no catalog can be retrieved from the server" do
 -    Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns nil
 -    Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns nil
 +    Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns nil
 +    Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns nil
  
      @agent.retrieve_catalog.should be_nil
    end
  
    it "should convert the catalog before returning" do
 -    Puppet::Resource::Catalog.stubs(:find).returns @catalog
 +    Puppet::Resource::Catalog.indirection.stubs(:find).returns @catalog
  
      @agent.expects(:convert_catalog).with { |cat, dur| cat == @catalog }.returns "converted catalog"
      @agent.retrieve_catalog.should == "converted catalog"
    end
  
    it "should return nil if there is an error while retrieving the catalog" do
 -    Puppet::Resource::Catalog.expects(:find).at_least_once.raises "eh"
 +    Puppet::Resource::Catalog.indirection.expects(:find).at_least_once.raises "eh"
  
      @agent.retrieve_catalog.should be_nil
    end
diff --combined spec/unit/node/environment_spec.rb
index 05527e7,6109007..d34bdb0
--- a/spec/unit/node/environment_spec.rb
+++ b/spec/unit/node/environment_spec.rb
@@@ -1,12 -1,11 +1,12 @@@
  #!/usr/bin/env ruby
  
 -require File.dirname(__FILE__) + '/../../spec_helper'
 +require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
  
  require 'puppet/node/environment'
  require 'puppet/util/execution'
  
  describe Puppet::Node::Environment do
 +  include PuppetSpec::Files
    after do
      Puppet::Node::Environment.clear
    end
@@@ -53,7 -52,7 +53,7 @@@
      before do
        @env = Puppet::Node::Environment.new("dev")
        @collection = Puppet::Resource::TypeCollection.new(@env)
 -      @collection.stubs(:perform_initial_import)
 +      @env.stubs(:perform_initial_import).returns(Puppet::Parser::AST::Hostclass.new(''))
        Thread.current[:known_resource_types] = nil
      end
  
@@@ -67,8 -66,9 +67,8 @@@
      end
  
      it "should perform the initial import when creating a new collection" do
 -      @collection.expects(:perform_initial_import)
 -      Puppet::Resource::TypeCollection.expects(:new).returns @collection
 -
 +      @env = Puppet::Node::Environment.new("dev")
 +      @env.expects(:perform_initial_import).returns(Puppet::Parser::AST::Hostclass.new(''))
        @env.known_resource_types
      end
  
@@@ -85,28 -85,29 +85,29 @@@
        @env.known_resource_types.should equal(@collection)
      end
  
-     it "should give to all threads the same collection if it didn't change" do
-       Puppet::Resource::TypeCollection.expects(:new).with(@env).returns @collection
-       @env.known_resource_types
+     it "should give to all threads using the same environment the same collection if the collection isn't stale" do
+       original_thread_type_collection = Puppet::Resource::TypeCollection.new(@env)
+       Puppet::Resource::TypeCollection.expects(:new).with(@env).returns original_thread_type_collection
+       @env.known_resource_types.should equal(original_thread_type_collection)
+ 
+       original_thread_type_collection.expects(:require_reparse?).returns(false)
+       Puppet::Resource::TypeCollection.stubs(:new).with(@env).returns @collection
  
        t = Thread.new {
-         @env.known_resource_types.should equal(@collection)
+         @env.known_resource_types.should equal(original_thread_type_collection)
        }
        t.join
      end
  
-     it "should give to new threads a new collection if it isn't stale" do
-       Puppet::Resource::TypeCollection.expects(:new).with(@env).returns @collection
-       @env.known_resource_types.expects(:stale?).returns(true)
- 
-       Puppet::Resource::TypeCollection.expects(:new).returns @collection
+     it "should generate a new TypeCollection if the current one requires reparsing" do
+       old_type_collection = @env.known_resource_types
+       old_type_collection.stubs(:require_reparse?).returns true
+       Thread.current[:known_resource_types] = nil
+       new_type_collection = @env.known_resource_types
  
-       t = Thread.new {
-         @env.known_resource_types.should equal(@collection)
-       }
-       t.join
+       new_type_collection.should be_a Puppet::Resource::TypeCollection
+       new_type_collection.should_not equal(old_type_collection)
      end
- 
    end
  
    [:modulepath, :manifestdir].each do |setting|
@@@ -274,52 -275,4 +275,61 @@@
        @helper.environment.name.should == :foo
      end
    end
 +
 +  describe "when performing initial import" do
 +    before do
-       @parser = stub 'parser'
++      @parser = Puppet::Parser::Parser.new("test")
 +      Puppet::Parser::Parser.stubs(:new).returns @parser
 +      @env = Puppet::Node::Environment.new("env")
 +    end
 +
 +    it "should set the parser's string to the 'code' setting and parse if code is available" do
 +      Puppet.settings[:code] = "my code"
 +      @parser.expects(:string=).with "my code"
 +      @parser.expects(:parse)
 +      @env.instance_eval { perform_initial_import }
 +    end
 +
 +    it "should set the parser's file to the 'manifest' setting and parse if no code is available and the manifest is available" do
 +      filename = tmpfile('myfile')
 +      File.open(filename, 'w'){|f| }
 +      Puppet.settings[:manifest] = filename
 +      @parser.expects(:file=).with filename
 +      @parser.expects(:parse)
 +      @env.instance_eval { perform_initial_import }
 +    end
 +
 +    it "should pass the manifest file to the parser even if it does not exist on disk" do
 +      filename = tmpfile('myfile')
 +      Puppet.settings[:code] = ""
 +      Puppet.settings[:manifest] = filename
 +      @parser.expects(:file=).with(filename).once
 +      @parser.expects(:parse).once
 +      @env.instance_eval { perform_initial_import }
 +    end
 +
 +    it "should fail helpfully if there is an error importing" do
 +      File.stubs(:exist?).returns true
++      @env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(@env)
 +      @parser.expects(:file=).once
 +      @parser.expects(:parse).raises ArgumentError
 +      lambda { @env.instance_eval { perform_initial_import } }.should raise_error(Puppet::Error)
 +    end
 +
 +    it "should not do anything if the ignore_import settings is set" do
 +      Puppet.settings[:ignoreimport] = true
 +      @parser.expects(:string=).never
 +      @parser.expects(:file=).never
 +      @parser.expects(:parse).never
 +      @env.instance_eval { perform_initial_import }
 +    end
++
++    it "should mark the type collection as needing a reparse when there is an error parsing" do
++      @parser.expects(:parse).raises Puppet::ParseError.new("Syntax error at ...")
++      @env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(@env)
++
++      lambda { @env.instance_eval { perform_initial_import } }.should raise_error(Puppet::Error, /Syntax error at .../)
++      @env.known_resource_types.require_reparse?.should be_true
++    end
 +  end
  end
diff --combined spec/unit/provider/package/gem_spec.rb
index 7c0d34d,32c067a..c2bd3cc
--- a/spec/unit/provider/package/gem_spec.rb
+++ b/spec/unit/provider/package/gem_spec.rb
@@@ -1,6 -1,6 +1,6 @@@
  #!/usr/bin/env ruby
  
 -require File.dirname(__FILE__) + '/../../../spec_helper'
 +require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
  
  provider_class = Puppet::Type.type(:package).provider(:gem)
  
@@@ -42,8 -42,18 +42,18 @@@ describe provider_class d
        @provider.install
      end
  
+     it "should specify that documentation should not be included" do
+       @provider.expects(:execute).with { |args| args[3] == "--no-rdoc" }.returns ""
+       @provider.install
+     end
+ 
+     it "should specify that RI should not be included" do
+       @provider.expects(:execute).with { |args| args[4] == "--no-ri" }.returns ""
+       @provider.install
+     end
+ 
      it "should specify the package name" do
-       @provider.expects(:execute).with { |args| args[3] == "myresource" }.returns ""
+       @provider.expects(:execute).with { |args| args[5] == "myresource" }.returns ""
        @provider.install
      end
  

-- 
Puppet packaging for Debian



More information about the Pkg-puppet-devel mailing list