diff --git a/lib/puppet/parser/relationship.rb b/lib/puppet/parser/relationship.rb index 92ba4071a..ac525fcbe 100644 --- a/lib/puppet/parser/relationship.rb +++ b/lib/puppet/parser/relationship.rb @@ -1,60 +1,62 @@ class Puppet::Parser::Relationship attr_accessor :source, :target, :type PARAM_MAP = {:relationship => :before, :subscription => :notify} def arrayify(resources) case resources when Puppet::Parser::Collector resources.collected.values when Array resources else [resources] end end def evaluate(catalog) arrayify(source).each do |s| arrayify(target).each do |t| mk_relationship(s, t, catalog) end end end def initialize(source, target, type) @source, @target, @type = source, target, type end def param_name PARAM_MAP[type] || raise(ArgumentError, "Invalid relationship type #{type}") end def mk_relationship(source, target, catalog) # REVISIT: In Ruby 1.8 we applied `to_s` to source and target, rather than # `join` without an argument. In 1.9 the behaviour of Array#to_s changed, # and it gives a different representation than just concat the stringified # elements. # # This restores the behaviour, but doesn't address the underlying question # of what would happen when more than one item was passed in that array. # (Hint: this will not end well.) # # See http://projects.puppetlabs.com/issues/12076 for the ticket tracking # the fact that we should dig out the sane version of this behaviour, then # implement it - where we don't risk breaking a stable release series. # --daniel 2012-01-21 source = source.is_a?(Array) ? source.join : source.to_s target = target.is_a?(Array) ? target.join : target.to_s unless source_resource = catalog.resource(source) raise ArgumentError, "Could not find resource '#{source}' for relationship on '#{target}'" end unless target_resource = catalog.resource(target) raise ArgumentError, "Could not find resource '#{target}' for relationship from '#{source}'" end Puppet.debug "Adding relationship from #{source} to #{target} with '#{param_name}'" - source_resource[param_name] ||= [] + if source_resource[param_name].class != Array + source_resource[param_name] = [source_resource[param_name]].compact + end source_resource[param_name] << target end end diff --git a/spec/unit/parser/relationship_spec.rb b/spec/unit/parser/relationship_spec.rb index 5a49831e1..4abd01395 100755 --- a/spec/unit/parser/relationship_spec.rb +++ b/spec/unit/parser/relationship_spec.rb @@ -1,69 +1,93 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/parser/relationship' describe Puppet::Parser::Relationship do before do @source = Puppet::Resource.new(:mytype, "source") @target = Puppet::Resource.new(:mytype, "target") + @extra_resource = Puppet::Resource.new(:mytype, "extra") + @extra_resource2 = Puppet::Resource.new(:mytype, "extra2") @dep = Puppet::Parser::Relationship.new(@source, @target, :relationship) end describe "when evaluating" do before do @catalog = Puppet::Resource::Catalog.new @catalog.add_resource(@source) @catalog.add_resource(@target) + @catalog.add_resource(@extra_resource) + @catalog.add_resource(@extra_resource2) end it "should fail if the source resource cannot be found" do @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @target lambda { @dep.evaluate(@catalog) }.should raise_error(ArgumentError) end it "should fail if the target resource cannot be found" do @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @source lambda { @dep.evaluate(@catalog) }.should raise_error(ArgumentError) end it "should add the target as a 'before' value if the type is 'relationship'" do @dep.type = :relationship @dep.evaluate(@catalog) @source[:before].should be_include("Mytype[target]") end it "should add the target as a 'notify' value if the type is 'subscription'" do @dep.type = :subscription @dep.evaluate(@catalog) @source[:notify].should be_include("Mytype[target]") end it "should supplement rather than clobber existing relationship values" do @source[:before] = "File[/bar]" @dep.evaluate(@catalog) + # this test did not work before. It was appending the resources + # together as a string + (@source[:before].class == Array).should be_true @source[:before].should be_include("Mytype[target]") @source[:before].should be_include("File[/bar]") end + it "should supplement rather than clobber existing resource relationships" do + @source[:before] = @extra_resource + @dep.evaluate(@catalog) + (@source[:before].class == Array).should be_true + @source[:before].should be_include("Mytype[target]") + @source[:before].should be_include(@extra_resource) + end + + it "should supplement rather than clobber multiple existing resource relationships" do + @source[:before] = [@extra_resource, @extra_resource2] + @dep.evaluate(@catalog) + (@source[:before].class == Array).should be_true + @source[:before].should be_include("Mytype[target]") + @source[:before].should be_include(@extra_resource) + @source[:before].should be_include(@extra_resource2) + end + it "should use the collected retargets if the target is a Collector" do orig_target = @target @target = Puppet::Parser::Collector.new(stub("scope"), :file, "equery", "vquery", :virtual) @target.collected[:foo] = @target @dep.evaluate(@catalog) @source[:before].should be_include("Mytype[target]") end it "should use the collected resources if the source is a Collector" do orig_source = @source @source = Puppet::Parser::Collector.new(stub("scope"), :file, "equery", "vquery", :virtual) @source.collected[:foo] = @source @dep.evaluate(@catalog) orig_source[:before].should be_include("Mytype[target]") end end end