From: Bart Vanbrabant <[email protected]>

Signed-off-by: James Turnbull <[email protected]>
---
 lib/puppet/parser/compiler.rb                |   29 ++++++--
 lib/puppet/parser/conflicthandler.rb         |   95 ++++++++++++++++++++++++++
 lib/puppet/parser/conflicthandlers/concat.rb |   39 +++++++++++
 lib/puppet/type.rb                           |   11 +++
 4 files changed, 167 insertions(+), 7 deletions(-)
 create mode 100644 lib/puppet/parser/conflicthandler.rb
 create mode 100644 lib/puppet/parser/conflicthandlers/concat.rb

diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb
index 968e0b9..ee0f098 100644
--- a/lib/puppet/parser/compiler.rb
+++ b/lib/puppet/parser/compiler.rb
@@ -33,13 +33,26 @@ class Puppet::Parser::Compiler
     def add_resource(scope, resource)
         @resources << resource
 
-        # Note that this will fail if the resource is not unique.
-        @catalog.add_resource(resource)
-
-        # And in the resource graph.  At some point, this might supercede
-        # the global resource table, but the table is a lot faster
-        # so it makes sense to maintain for now.
-        @catalog.add_edge(scope.resource, resource)
+        begin
+            # Note that this will fail if the resource is not unique.
+            @catalog.add_resource(resource)
+
+            # And in the resource graph.  At some point, this might supercede
+            # the global resource table, but the table is a lot faster
+            # so it makes sense to maintain for now.
+            @catalog.add_edge(scope.resource, resource)
+        rescue Puppet::Resource::Catalog::DuplicateResourceError => 
+                conflict_exception
+            # get the original resource the new one is conflicting with
+            original = @catalog.resource(resource.ref)
+
+            # Ask the conflicthandler if there is a handler defined
+            handler = Puppet::Parser::ConflictHandler.get_handler(original)
+            raise conflict_exception unless handler
+
+            # invoke the handler and ignore the duplicate resource
+            handler.add_conflict(resource)
+        end
     end
 
     # Do we use nodes found in the code, vs. the external node sources?
@@ -374,6 +387,8 @@ class Puppet::Parser::Compiler
 
             resource.finish if resource.respond_to?(:finish)
         end
+
+        Puppet::Parser::ConflictHandler.finish()
     end
 
     # Initialize the top-level scope, class, and resource.
diff --git a/lib/puppet/parser/conflicthandler.rb 
b/lib/puppet/parser/conflicthandler.rb
new file mode 100644
index 0000000..bdbb947
--- /dev/null
+++ b/lib/puppet/parser/conflicthandler.rb
@@ -0,0 +1,95 @@
+# A conflict handler
+module Puppet::Parser
+    class ConflictHandler
+       class << self
+            include Puppet::Util
+            
+            # setup autoloading for all conflicthandlers
+            def autoloader
+                unless defined? @autoloader
+                    @autoloader = Puppet::Util::Autoload.new(self,
+                        "puppet/parser/conflicthandlers",
+                        :wrap => false)
+                end
+                @autoloader
+            end
+            
+            def handlers
+                autoloader.loadall
+                
+                @handlers
+            end
+            
+            # a class method to add a new conflicthandler. The first argument 
is
+            # the name of the resource type the handler can handle. The name 
is the
+            # name of the conflicthandlers that is referenced in the 
metaparameter.
+            # The block is that actual code that handles the conflict.
+            def newconflicthandler(type, name, &block)
+                @handlers ||= {}
+                type = symbolize(type)
+
+                if handler(name, type)
+                    raise Puppet::DevError, "Conflict handler for %s already 
defined" % type
+                end
+
+                # create a new class
+                clazz = Class.new ConflictHandler
+                clazz.add_finish(&block)
+
+                # store the class
+                @handlers[name] ||= {}
+                @handlers[name][type] = clazz
+            end
+            
+            # Does a handler exist with the given name and type
+            def handler(name, type)
+                return (handlers.has_key? name and handlers[name].has_key? 
type)
+            end
+            
+            # Get the handler for the given resource. If there isn't one create
+            # one if this resource has a handler defined
+            def get_handler(resource)
+                @instances ||= {}
+                
+                if @instances.has_key? resource.ref
+                    return @instances[resource.ref]
+                else
+                    handler = resource["conflicthandler"]
+                    type = resource.type.downcase.to_sym
+                    return unless handler(handler, type)
+                    
+                    cls = @handlers[handler][type]
+                    instance = cls.new(resource)
+                    @instances[resource.ref] = instance
+
+                    return instance
+                end
+            end
+            
+            def finish
+                @instances.each do |ref, obj|
+                    obj.finish
+                end if @instances
+                
+                # clear all instances
+                @instances = {}
+            end
+            
+            # add the finish method to this class
+            def add_finish(&block)
+                send(:define_method, :finish, &block)
+            end
+        end
+
+        attr_reader :conflicts, :resource
+        def initialize(resource, &block)
+            @resource = resource
+            @conflicts = []
+        end
+
+        def add_conflict(resource)
+            @conflicts << resource
+        end
+    end
+end
+
diff --git a/lib/puppet/parser/conflicthandlers/concat.rb 
b/lib/puppet/parser/conflicthandlers/concat.rb
new file mode 100644
index 0000000..38ce12a
--- /dev/null
+++ b/lib/puppet/parser/conflicthandlers/concat.rb
@@ -0,0 +1,39 @@
+# Concatenate the content of the duplicate file to the original file
+# TODO: handle owner, group, mode, ... parameters
+class Puppet::Parser::ConflictHandler
+    self.newconflicthandler("file", "concat") do  
+        # find the content parameter in the kept resource
+        content_param = nil
+        resource.eachparam do |param|
+            if param.name == :content
+                content_param = param
+            elsif param.name == :source
+                raise ArgumentError.new("Can not concat file resources that 
have the source parameter set")
+            end
+        end
+
+        # collect all the values of the content parameter
+        values = []
+
+        values << content_param.value if content_param
+        conflicts.each do |conflict|
+            if conflict[:source]
+                raise ArgumentError.new("Can not concat file resources that 
have the source parameter set")
+            end
+
+            value = conflict[:content]
+            values << value if value
+        end
+
+        # sort them to prevent flapping
+        values.sort!
+
+        # set the value
+        if content_param
+            content_param.value = values.join
+        else
+            resource.set_parameter(:content, values.join)
+        end
+    end
+end
+
diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb
index 99ac909..4ca2c04 100644
--- a/lib/puppet/type.rb
+++ b/lib/puppet/type.rb
@@ -13,6 +13,7 @@ require 'puppet/resource/reference'
 require 'puppet/util/cacher'
 require 'puppet/file_collection/lookup'
 require 'puppet/util/tagging'
+require 'puppet/parser/conflicthandler'
 
 # see the bottom of the file for the rest of the inclusions
 
@@ -1439,6 +1440,16 @@ class Type
             This will restart the sshd service if the sshd config file 
changes.}
     end
 
+    newmetaparam(:conflicthandler) do
+        desc %{The name of the conflicthandler that is invoked if a conflicting
+            resource exists.}
+
+        validate do |handler|
+            Puppet::Parser::ConflictHandler.handler(handler, 
@resource.class.name)
+        end
+    end
+
+
     ###############################
     # All of the provider plumbing for the resource types.
     require 'puppet/provider'
-- 
1.6.0.4


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Puppet Developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/puppet-dev?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to