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 -~----------~----~----~----~------~----~------~--~---
