diff --git a/lib/puppet/indirector/node/active_record.rb b/lib/puppet/indirector/node/active_record.rb index 08bc7e67a..2d150bdff 100644 --- a/lib/puppet/indirector/node/active_record.rb +++ b/lib/puppet/indirector/node/active_record.rb @@ -1,13 +1,14 @@ require 'puppet/rails/host' require 'puppet/indirector/active_record' require 'puppet/node' class Puppet::Node::ActiveRecord < Puppet::Indirector::ActiveRecord use_ar_model Puppet::Rails::Host def find(request) node = super + node.environment = request.environment node.fact_merge node end end diff --git a/lib/puppet/indirector/node/ldap.rb b/lib/puppet/indirector/node/ldap.rb index 5fd738511..20045c8b5 100644 --- a/lib/puppet/indirector/node/ldap.rb +++ b/lib/puppet/indirector/node/ldap.rb @@ -1,256 +1,259 @@ require 'puppet/node' require 'puppet/indirector/ldap' class Puppet::Node::Ldap < Puppet::Indirector::Ldap desc "Search in LDAP for node configuration information. See the [LDAP Nodes](http://projects.puppetlabs.com/projects/puppet/wiki/Ldap_Nodes) page for more information. This will first search for whatever the certificate name is, then (if that name contains a `.`) for the short name, then `default`." # The attributes that Puppet class information is stored in. def class_attributes # LAK:NOTE See http://snurl.com/21zf8 [groups_google_com] x = Puppet[:ldapclassattrs].split(/\s*,\s*/) end # Separate this out so it's relatively atomic. It's tempting to call # process instead of name2hash() here, but it ends up being # difficult to test because all exceptions get caught by ldapsearch. # LAK:NOTE Unfortunately, the ldap support is too stupid to throw anything # but LDAP::ResultError, even on bad connections, so we are rough handed # with our error handling. def name2hash(name) info = nil ldapsearch(search_filter(name)) { |entry| info = entry2hash(entry) } info end # Look for our node in ldap. def find(request) names = [request.key] names << request.key.sub(/\..+/, '') if request.key.include?(".") # we assume it's an fqdn names << "default" node = nil names.each do |name| next unless info = name2hash(name) - break if node = info2node(request.key, info) + merge_parent(info) if info[:parent] + info[:environment] ||= request.environment.to_s + node = info2node(request.key, info) + break end node end # Find more than one node. LAK:NOTE This is a bit of a clumsy API, because the 'search' # method currently *requires* a key. It seems appropriate in some cases but not others, # and I don't really know how to get rid of it as a requirement but allow it when desired. def search(request) if classes = request.options[:class] classes = [classes] unless classes.is_a?(Array) filter = "(&(objectclass=puppetClient)(puppetclass=" + classes.join(")(puppetclass=") + "))" else filter = "(objectclass=puppetClient)" end infos = [] ldapsearch(filter) { |entry| infos << entry2hash(entry, request.options[:fqdn]) } return infos.collect do |info| + merge_parent(info) if info[:parent] + info[:environment] ||= request.environment.to_s info2node(info[:name], info) end end # The parent attribute, if we have one. def parent_attribute if pattr = Puppet[:ldapparentattr] and ! pattr.empty? pattr else nil end end # The attributes that Puppet will stack as array over the full # hierarchy. def stacked_attributes(dummy_argument=:work_arround_for_ruby_GC_bug) Puppet[:ldapstackedattrs].split(/\s*,\s*/) end # Convert the found entry into a simple hash. def entry2hash(entry, fqdn = false) result = {} cn = entry.dn[ /cn\s*=\s*([^,\s]+)/i,1] dcs = entry.dn.scan(/dc\s*=\s*([^,\s]+)/i) result[:name] = fqdn ? ([cn]+dcs).join('.') : cn result[:parent] = get_parent_from_entry(entry) if parent_attribute result[:classes] = get_classes_from_entry(entry) result[:stacked] = get_stacked_values_from_entry(entry) result[:parameters] = get_parameters_from_entry(entry) result[:environment] = result[:parameters]["environment"] if result[:parameters]["environment"] result[:stacked_parameters] = {} if result[:stacked] result[:stacked].each do |value| param = value.split('=', 2) result[:stacked_parameters][param[0]] = param[1] end end if result[:stacked_parameters] result[:stacked_parameters].each do |param, value| result[:parameters][param] = value unless result[:parameters].include?(param) end end result[:parameters] = convert_parameters(result[:parameters]) result end # Default to all attributes. def search_attributes ldapattrs = Puppet[:ldapattrs] # results in everything getting returned return nil if ldapattrs == "all" search_attrs = class_attributes + ldapattrs.split(/\s*,\s*/) if pattr = parent_attribute search_attrs << pattr end search_attrs end # The ldap search filter to use. def search_filter(name) filter = Puppet[:ldapstring] if filter.include? "%s" # Don't replace the string in-line, since that would hard-code our node # info. filter = filter.gsub('%s', name) end filter end private # Add our hash of ldap information to the node instance. def add_to_node(node, information) node.classes = information[:classes].uniq unless information[:classes].nil? or information[:classes].empty? node.parameters = information[:parameters] unless information[:parameters].nil? or information[:parameters].empty? node.environment = information[:environment] if information[:environment] end def convert_parameters(parameters) result = {} parameters.each do |param, value| if value.is_a?(Array) result[param] = value.collect { |v| convert(v) } else result[param] = convert(value) end end result end # Convert any values if necessary. def convert(value) case value when Integer, Fixnum, Bignum; value when "true"; true when "false"; false else value end end # Find information for our parent and merge it into the current info. def find_and_merge_parent(parent, information) parent_info = name2hash(parent) || raise(Puppet::Error.new("Could not find parent node '#{parent}'")) information[:classes] += parent_info[:classes] parent_info[:parameters].each do |param, value| # Specifically test for whether it's set, so false values are handled correctly. information[:parameters][param] = value unless information[:parameters].include?(param) end information[:environment] ||= parent_info[:environment] parent_info[:parent] end # Take a name and a hash, and return a node instance. def info2node(name, info) - merge_parent(info) if info[:parent] - node = Puppet::Node.new(name) add_to_node(node, info) node.fact_merge node end def merge_parent(info) parent_info = nil parent = info[:parent] # Preload the parent array with the node name. parents = [info[:name]] while parent raise ArgumentError, "Found loop in LDAP node parents; #{parent} appears twice" if parents.include?(parent) parents << parent parent = find_and_merge_parent(parent, info) end info end def get_classes_from_entry(entry) result = class_attributes.inject([]) do |array, attr| if values = entry.vals(attr) values.each do |v| array << v end end array end result.uniq end def get_parameters_from_entry(entry) stacked_params = stacked_attributes entry.to_hash.inject({}) do |hash, ary| unless stacked_params.include?(ary[0]) # don't add our stacked parameters to the main param list if ary[1].length == 1 hash[ary[0]] = ary[1].shift else hash[ary[0]] = ary[1] end end hash end end def get_parent_from_entry(entry) pattr = parent_attribute return nil unless values = entry.vals(pattr) if values.length > 1 raise Puppet::Error, "Node entry #{entry.dn} specifies more than one parent: #{values.inspect}" end return(values.empty? ? nil : values.shift) end def get_stacked_values_from_entry(entry) stacked_attributes.inject([]) do |result, attr| if values = entry.vals(attr) result += values end result end end end diff --git a/lib/puppet/indirector/node/plain.rb b/lib/puppet/indirector/node/plain.rb index d648cce8d..e5d9d3477 100644 --- a/lib/puppet/indirector/node/plain.rb +++ b/lib/puppet/indirector/node/plain.rb @@ -1,19 +1,20 @@ require 'puppet/node' require 'puppet/indirector/plain' class Puppet::Node::Plain < Puppet::Indirector::Plain desc "Always return an empty node object. Assumes you keep track of nodes in flat file manifests. You should use it when you don't have some other, functional source you want to use, as the compiler will not work without a valid node terminus. Note that class is responsible for merging the node's facts into the node instance before it is returned." # Just return an empty node. def find(request) node = super + node.environment = request.environment node.fact_merge node end end diff --git a/spec/unit/indirector/node/active_record_spec.rb b/spec/unit/indirector/node/active_record_spec.rb index d0e693927..ffc9511b5 100755 --- a/spec/unit/indirector/node/active_record_spec.rb +++ b/spec/unit/indirector/node/active_record_spec.rb @@ -1,37 +1,41 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/node' describe "Puppet::Node::ActiveRecord", :if => Puppet.features.rails? && Puppet.features.sqlite? do include PuppetSpec::Files + let(:nodename) { "mynode" } + let(:fact_values) { {:afact => "a value"} } + let(:facts) { Puppet::Node::Facts.new(nodename, fact_values) } + let(:environment) { Puppet::Node::Environment.new("myenv") } + let(:request) { Puppet::Indirector::Request.new(:node, :find, nodename, nil, :environment => environment) } + let(:node_indirection) { Puppet::Node::ActiveRecord.new } + before do require 'puppet/indirector/node/active_record' end it "should be a subclass of the ActiveRecord terminus class" do Puppet::Node::ActiveRecord.ancestors.should be_include(Puppet::Indirector::ActiveRecord) end it "should use Puppet::Rails::Host as its ActiveRecord model" do Puppet::Node::ActiveRecord.ar_model.should equal(Puppet::Rails::Host) end it "should call fact_merge when a node is found" do db_instance = stub 'db_instance' Puppet::Node::ActiveRecord.ar_model.expects(:find_by_name).returns db_instance - node = Puppet::Node.new("foo") + node = Puppet::Node.new(nodename) db_instance.expects(:to_puppet).returns node Puppet[:statedir] = tmpdir('active_record_tmp') Puppet[:railslog] = '$statedir/rails.log' - ar = Puppet::Node::ActiveRecord.new - - node.expects(:fact_merge) + Puppet::Node::Facts.indirection.expects(:find).with(nodename, :environment => environment).returns(facts) - request = Puppet::Indirector::Request.new(:node, :find, "what.ever", nil) - ar.find(request) + node_indirection.find(request).parameters.should include(fact_values) end end diff --git a/spec/unit/indirector/node/ldap_spec.rb b/spec/unit/indirector/node/ldap_spec.rb index 5e57db3c4..a224488de 100755 --- a/spec/unit/indirector/node/ldap_spec.rb +++ b/spec/unit/indirector/node/ldap_spec.rb @@ -1,453 +1,432 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/indirector/node/ldap' describe Puppet::Node::Ldap do - describe "when searching for a single node" do - before :each do - @searcher = Puppet::Node::Ldap.new - - @name = "mynode.domain.com" - @node = stub 'node', :name => @name, :name= => nil - @node.stub_everything + let(:nodename) { "mynode.domain.com" } + let(:node_indirection) { Puppet::Node::Ldap.new } + let(:environment) { Puppet::Node::Environment.new("myenv") } + let(:fact_values) { {:afact => "a value", "one" => "boo"} } + let(:facts) { Puppet::Node::Facts.new(nodename, fact_values) } - Puppet::Node.stubs(:new).returns(@node) + before do + Puppet::Node::Facts.indirection.stubs(:find).with(nodename, :environment => environment).returns(facts) + end - @request = stub 'request', :key => @name - end + describe "when searching for a single node" do + let(:request) { Puppet::Indirector::Request.new(:node, :find, nodename, nil, :environment => environment) } it "should convert the hostname into a search filter" do entry = stub 'entry', :dn => 'cn=mynode.domain.com,ou=hosts,dc=madstop,dc=com', :vals => %w{}, :to_hash => {} - @searcher.expects(:ldapsearch).with("(&(objectclass=puppetClient)(cn=#{@name}))").yields entry - @searcher.name2hash(@name) + node_indirection.expects(:ldapsearch).with("(&(objectclass=puppetClient)(cn=#{nodename}))").yields entry + node_indirection.name2hash(nodename) end it "should convert any found entry into a hash" do entry = stub 'entry', :dn => 'cn=mynode.domain.com,ou=hosts,dc=madstop,dc=com', :vals => %w{}, :to_hash => {} - @searcher.expects(:ldapsearch).with("(&(objectclass=puppetClient)(cn=#{@name}))").yields entry + node_indirection.expects(:ldapsearch).with("(&(objectclass=puppetClient)(cn=#{nodename}))").yields entry myhash = {"myhash" => true} - @searcher.expects(:entry2hash).with(entry).returns myhash - @searcher.name2hash(@name).should == myhash + node_indirection.expects(:entry2hash).with(entry).returns myhash + node_indirection.name2hash(nodename).should == myhash end # This heavily tests our entry2hash method, so we don't have to stub out the stupid entry information any more. describe "when an ldap entry is found" do before do @entry = stub 'entry', :dn => 'cn=mynode,ou=hosts,dc=madstop,dc=com', :vals => %w{}, :to_hash => {} - @searcher.stubs(:ldapsearch).yields @entry + node_indirection.stubs(:ldapsearch).yields @entry end it "should convert the entry to a hash" do - @searcher.entry2hash(@entry).should be_instance_of(Hash) + node_indirection.entry2hash(@entry).should be_instance_of(Hash) end it "should add the entry's common name to the hash if fqdn if false" do - @searcher.entry2hash(@entry,fqdn = false)[:name].should == "mynode" + node_indirection.entry2hash(@entry,fqdn = false)[:name].should == "mynode" end it "should add the entry's fqdn name to the hash if fqdn if true" do - @searcher.entry2hash(@entry,fqdn = true)[:name].should == "mynode.madstop.com" + node_indirection.entry2hash(@entry,fqdn = true)[:name].should == "mynode.madstop.com" end it "should add all of the entry's classes to the hash" do @entry.stubs(:vals).with("puppetclass").returns %w{one two} - @searcher.entry2hash(@entry)[:classes].should == %w{one two} + node_indirection.entry2hash(@entry)[:classes].should == %w{one two} end it "should deduplicate class values" do @entry.stubs(:to_hash).returns({}) - @searcher.stubs(:class_attributes).returns(%w{one two}) + node_indirection.stubs(:class_attributes).returns(%w{one two}) @entry.stubs(:vals).with("one").returns(%w{a b}) @entry.stubs(:vals).with("two").returns(%w{b c}) - @searcher.entry2hash(@entry)[:classes].should == %w{a b c} + node_indirection.entry2hash(@entry)[:classes].should == %w{a b c} end it "should add the entry's environment to the hash" do @entry.stubs(:to_hash).returns("environment" => %w{production}) - @searcher.entry2hash(@entry)[:environment].should == "production" + node_indirection.entry2hash(@entry)[:environment].should == "production" end it "should add all stacked parameters as parameters in the hash" do @entry.stubs(:vals).with("puppetvar").returns(%w{one=two three=four}) - result = @searcher.entry2hash(@entry) + result = node_indirection.entry2hash(@entry) result[:parameters]["one"].should == "two" result[:parameters]["three"].should == "four" end it "should not add the stacked parameter as a normal parameter" do @entry.stubs(:vals).with("puppetvar").returns(%w{one=two three=four}) @entry.stubs(:to_hash).returns("puppetvar" => %w{one=two three=four}) - @searcher.entry2hash(@entry)[:parameters]["puppetvar"].should be_nil + node_indirection.entry2hash(@entry)[:parameters]["puppetvar"].should be_nil end it "should add all other attributes as parameters in the hash" do @entry.stubs(:to_hash).returns("foo" => %w{one two}) - @searcher.entry2hash(@entry)[:parameters]["foo"].should == %w{one two} + node_indirection.entry2hash(@entry)[:parameters]["foo"].should == %w{one two} end it "should return single-value parameters as strings, not arrays" do @entry.stubs(:to_hash).returns("foo" => %w{one}) - @searcher.entry2hash(@entry)[:parameters]["foo"].should == "one" + node_indirection.entry2hash(@entry)[:parameters]["foo"].should == "one" end it "should convert 'true' values to the boolean 'true'" do @entry.stubs(:to_hash).returns({"one" => ["true"]}) - @searcher.entry2hash(@entry)[:parameters]["one"].should == true + node_indirection.entry2hash(@entry)[:parameters]["one"].should == true end it "should convert 'false' values to the boolean 'false'" do @entry.stubs(:to_hash).returns({"one" => ["false"]}) - @searcher.entry2hash(@entry)[:parameters]["one"].should == false + node_indirection.entry2hash(@entry)[:parameters]["one"].should == false end it "should convert 'true' values to the boolean 'true' inside an array" do @entry.stubs(:to_hash).returns({"one" => ["true", "other"]}) - @searcher.entry2hash(@entry)[:parameters]["one"].should == [true, "other"] + node_indirection.entry2hash(@entry)[:parameters]["one"].should == [true, "other"] end it "should convert 'false' values to the boolean 'false' inside an array" do @entry.stubs(:to_hash).returns({"one" => ["false", "other"]}) - @searcher.entry2hash(@entry)[:parameters]["one"].should == [false, "other"] + node_indirection.entry2hash(@entry)[:parameters]["one"].should == [false, "other"] end it "should add the parent's name if present" do @entry.stubs(:vals).with("parentnode").returns(%w{foo}) - @searcher.entry2hash(@entry)[:parent].should == "foo" + node_indirection.entry2hash(@entry)[:parent].should == "foo" end it "should fail if more than one parent is specified" do @entry.stubs(:vals).with("parentnode").returns(%w{foo}) - @searcher.entry2hash(@entry)[:parent].should == "foo" + node_indirection.entry2hash(@entry)[:parent].should == "foo" end end it "should search first for the provided key" do - @searcher.expects(:name2hash).with("mynode.domain.com").returns({}) - @searcher.find(@request) + node_indirection.expects(:name2hash).with("mynode.domain.com").returns({}) + node_indirection.find(request) end it "should search for the short version of the provided key if the key looks like a hostname and no results are found for the key itself" do - @searcher.expects(:name2hash).with("mynode.domain.com").returns(nil) - @searcher.expects(:name2hash).with("mynode").returns({}) - @searcher.find(@request) + node_indirection.expects(:name2hash).with("mynode.domain.com").returns(nil) + node_indirection.expects(:name2hash).with("mynode").returns({}) + node_indirection.find(request) end it "should search for default information if no information can be found for the key" do - @searcher.expects(:name2hash).with("mynode.domain.com").returns(nil) - @searcher.expects(:name2hash).with("mynode").returns(nil) - @searcher.expects(:name2hash).with("default").returns({}) - @searcher.find(@request) + node_indirection.expects(:name2hash).with("mynode.domain.com").returns(nil) + node_indirection.expects(:name2hash).with("mynode").returns(nil) + node_indirection.expects(:name2hash).with("default").returns({}) + node_indirection.find(request) end it "should return nil if no results are found in ldap" do - @searcher.stubs(:name2hash).returns nil - @searcher.find(@request).should be_nil + node_indirection.stubs(:name2hash).returns nil + node_indirection.find(request).should be_nil end it "should return a node object if results are found in ldap" do - @searcher.stubs(:name2hash).returns({}) - @searcher.find(@request).should equal(@node) + node_indirection.stubs(:name2hash).returns({}) + node_indirection.find(request).should be end describe "and node information is found in LDAP" do before do @result = {} - @searcher.stubs(:name2hash).returns @result + node_indirection.stubs(:name2hash).returns @result end it "should create the node with the correct name, even if it was found by a different name" do - @searcher.expects(:name2hash).with("mynode.domain.com").returns nil - @searcher.expects(:name2hash).with("mynode").returns @result + node_indirection.expects(:name2hash).with(nodename).returns nil + node_indirection.expects(:name2hash).with("mynode").returns @result - Puppet::Node.expects(:new).with("mynode.domain.com").returns @node - @searcher.find(@request) + node_indirection.find(request).name.should == nodename end it "should add any classes from ldap" do - @result[:classes] = %w{a b c d} - @node.expects(:classes=).with(%w{a b c d}) - @searcher.find(@request) + classes = %w{a b c d} + @result[:classes] = classes + node_indirection.find(request).classes.should == classes end it "should add all entry attributes as node parameters" do - @result[:parameters] = {"one" => "two", "three" => "four"} - @node.expects(:parameters=).with("one" => "two", "three" => "four") - @searcher.find(@request) + params = {"one" => "two", "three" => "four"} + @result[:parameters] = params + node_indirection.find(request).parameters.should include(params) end it "should set the node's environment to the environment of the results" do - @result[:environment] = "test" - @node.expects(:environment=).with("test") - @searcher.find(@request) + result_env = Puppet::Node::Environment.new("local_test") + Puppet::Node::Facts.indirection.stubs(:find).with(nodename, :environment => result_env).returns(facts) + @result[:environment] = "local_test" + node_indirection.find(request).environment.should == result_env end it "should retain false parameter values" do @result[:parameters] = {} @result[:parameters]["one"] = false - @node.expects(:parameters=).with("one" => false) - @searcher.find(@request) + node_indirection.find(request).parameters.should include({"one" => false}) end it "should merge the node's facts after the parameters from ldap are assigned" do # Make sure we've got data to start with, so the parameters are actually set. - @result[:parameters] = {} - @result[:parameters]["one"] = "yay" + params = {"one" => "yay", "two" => "hooray"} + @result[:parameters] = params - # A hackish way to enforce order. - set = false - @node.expects(:parameters=).with { |*args| set = true } - @node.expects(:fact_merge).with { |*args| raise "Facts were merged before parameters were set" unless set; true } - - @searcher.find(@request) + # Node implements its own merge so that an existing param takes + # precedence over facts. We get the same result here by merging params + # into facts + node_indirection.find(request).parameters.should == facts.values.merge(params) end describe "and a parent node is specified" do before do @entry = {:classes => [], :parameters => {}} @parent = {:classes => [], :parameters => {}} @parent_parent = {:classes => [], :parameters => {}} - @searcher.stubs(:name2hash).with(@name).returns(@entry) - @searcher.stubs(:name2hash).with('parent').returns(@parent) - @searcher.stubs(:name2hash).with('parent_parent').returns(@parent_parent) + node_indirection.stubs(:name2hash).with(nodename).returns(@entry) + node_indirection.stubs(:name2hash).with('parent').returns(@parent) + node_indirection.stubs(:name2hash).with('parent_parent').returns(@parent_parent) - @searcher.stubs(:parent_attribute).returns(:parent) + node_indirection.stubs(:parent_attribute).returns(:parent) end it "should search for the parent node" do @entry[:parent] = "parent" - @searcher.expects(:name2hash).with(@name).returns @entry - @searcher.expects(:name2hash).with('parent').returns @parent + node_indirection.expects(:name2hash).with(nodename).returns @entry + node_indirection.expects(:name2hash).with('parent').returns @parent - @searcher.find(@request) + node_indirection.find(request) end it "should fail if the parent cannot be found" do @entry[:parent] = "parent" - @searcher.expects(:name2hash).with('parent').returns nil + node_indirection.expects(:name2hash).with('parent').returns nil - proc { @searcher.find(@request) }.should raise_error(Puppet::Error) + proc { node_indirection.find(request) }.should raise_error(Puppet::Error, /Could not find parent node/) end it "should add any parent classes to the node's classes" do @entry[:parent] = "parent" @entry[:classes] = %w{a b} @parent[:classes] = %w{c d} - @node.expects(:classes=).with(%w{a b c d}) - @searcher.find(@request) + node_indirection.find(request).classes.should == %w{a b c d} end it "should add any parent parameters to the node's parameters" do @entry[:parent] = "parent" @entry[:parameters]["one"] = "two" @parent[:parameters]["three"] = "four" - @node.expects(:parameters=).with("one" => "two", "three" => "four") - @searcher.find(@request) + node_indirection.find(request).parameters.should include({"one" => "two", "three" => "four"}) end it "should prefer node parameters over parent parameters" do @entry[:parent] = "parent" @entry[:parameters]["one"] = "two" @parent[:parameters]["one"] = "three" - @node.expects(:parameters=).with("one" => "two") - @searcher.find(@request) + node_indirection.find(request).parameters.should include({"one" => "two"}) end it "should use the parent's environment if the node has none" do + env = Puppet::Node::Environment.new("parent") @entry[:parent] = "parent" @parent[:environment] = "parent" - @node.stubs(:parameters=) - @node.expects(:environment=).with("parent") - @searcher.find(@request) + Puppet::Node::Facts.indirection.stubs(:find).with(nodename, :environment => env).returns(facts) + node_indirection.find(request).environment.should == env end it "should prefer the node's environment to the parent's" do + child_env = Puppet::Node::Environment.new("child") @entry[:parent] = "parent" @entry[:environment] = "child" @parent[:environment] = "parent" - @node.stubs(:parameters=) - @node.expects(:environment=).with("child") - @searcher.find(@request) + Puppet::Node::Facts.indirection.stubs(:find).with(nodename, :environment => child_env).returns(facts) + + node_indirection.find(request).environment.should == child_env end it "should recursively look up parent information" do @entry[:parent] = "parent" @entry[:parameters]["one"] = "two" @parent[:parent] = "parent_parent" @parent[:parameters]["three"] = "four" @parent_parent[:parameters]["five"] = "six" - @node.expects(:parameters=).with("one" => "two", "three" => "four", "five" => "six") - @searcher.find(@request) + node_indirection.find(request).parameters.should include("one" => "two", "three" => "four", "five" => "six") end it "should not allow loops in parent declarations" do @entry[:parent] = "parent" - @parent[:parent] = @name - proc { @searcher.find(@request) }.should raise_error(ArgumentError) + @parent[:parent] = nodename + proc { node_indirection.find(request) }.should raise_error(ArgumentError) end end end end describe "when searching for multiple nodes" do - before :each do - @searcher = Puppet::Node::Ldap.new - @options = {} - @request = stub 'request', :key => "foo", :options => @options + let(:options) { {:environment => environment} } + let(:request) { Puppet::Indirector::Request.new(:node, :find, nodename, nil, options) } + before :each do Puppet::Node::Facts.indirection.stubs(:terminus_class).returns :yaml end it "should find all nodes if no arguments are provided" do - @searcher.expects(:ldapsearch).with("(objectclass=puppetClient)") + node_indirection.expects(:ldapsearch).with("(objectclass=puppetClient)") # LAK:NOTE The search method requires an essentially bogus key. It's # an API problem that I don't really know how to fix. - @searcher.search @request + node_indirection.search request end describe "and a class is specified" do it "should find all nodes that are members of that class" do - @searcher.expects(:ldapsearch).with("(&(objectclass=puppetClient)(puppetclass=one))") + node_indirection.expects(:ldapsearch).with("(&(objectclass=puppetClient)(puppetclass=one))") - @options[:class] = "one" - @searcher.search @request + options[:class] = "one" + node_indirection.search request end end describe "multiple classes are specified" do it "should find all nodes that are members of all classes" do - @searcher.expects(:ldapsearch).with("(&(objectclass=puppetClient)(puppetclass=one)(puppetclass=two))") - @options[:class] = %w{one two} - @searcher.search @request + node_indirection.expects(:ldapsearch).with("(&(objectclass=puppetClient)(puppetclass=one)(puppetclass=two))") + options[:class] = %w{one two} + node_indirection.search request end end it "should process each found entry" do # .yields can't be used to yield multiple values :/ - @searcher.expects(:ldapsearch).yields("one") - @searcher.expects(:entry2hash).with("one",nil).returns(:name => "foo") - @searcher.search @request + node_indirection.expects(:ldapsearch).yields("one") + node_indirection.expects(:entry2hash).with("one",nil).returns(:name => nodename) + node_indirection.search request end it "should return a node for each processed entry with the name from the entry" do - @searcher.expects(:ldapsearch).yields("whatever") - @searcher.expects(:entry2hash).with("whatever",nil).returns(:name => "foo") - result = @searcher.search(@request) + node_indirection.expects(:ldapsearch).yields("whatever") + node_indirection.expects(:entry2hash).with("whatever",nil).returns(:name => nodename) + result = node_indirection.search(request) result[0].should be_instance_of(Puppet::Node) - result[0].name.should == "foo" + result[0].name.should == nodename end it "should merge each node's facts" do - node = mock 'node' - Puppet::Node.expects(:new).with("foo").returns node - node.expects(:fact_merge) - @searcher.stubs(:ldapsearch).yields("one") - @searcher.stubs(:entry2hash).with("one",nil).returns(:name => "foo") - @searcher.search(@request) + node_indirection.stubs(:ldapsearch).yields("one") + node_indirection.stubs(:entry2hash).with("one",nil).returns(:name => nodename) + node_indirection.search(request)[0].parameters.should include(fact_values) end it "should pass the request's fqdn option to entry2hash" do - node = mock 'node' - @options[:fqdn] = :hello - Puppet::Node.stubs(:new).with("foo").returns node - node.stubs(:fact_merge) - @searcher.stubs(:ldapsearch).yields("one") - @searcher.expects(:entry2hash).with("one",:hello).returns(:name => "foo") - @searcher.search(@request) + options[:fqdn] = :hello + node_indirection.stubs(:ldapsearch).yields("one") + node_indirection.expects(:entry2hash).with("one",:hello).returns(:name => nodename) + node_indirection.search(request) end end -end - -describe Puppet::Node::Ldap, " when developing the search query" do - before do - @searcher = Puppet::Node::Ldap.new - end - - it "should return the value of the :ldapclassattrs split on commas as the class attributes" do - Puppet.stubs(:[]).with(:ldapclassattrs).returns("one,two") - @searcher.class_attributes.should == %w{one two} - end - it "should return nil as the parent attribute if the :ldapparentattr is set to an empty string" do - Puppet.stubs(:[]).with(:ldapparentattr).returns("") - @searcher.parent_attribute.should be_nil - end + describe Puppet::Node::Ldap, " when developing the search query" do + it "should return the value of the :ldapclassattrs split on commas as the class attributes" do + Puppet.stubs(:[]).with(:ldapclassattrs).returns("one,two") + node_indirection.class_attributes.should == %w{one two} + end - it "should return the value of the :ldapparentattr as the parent attribute" do - Puppet.stubs(:[]).with(:ldapparentattr).returns("pere") - @searcher.parent_attribute.should == "pere" - end + it "should return nil as the parent attribute if the :ldapparentattr is set to an empty string" do + Puppet.stubs(:[]).with(:ldapparentattr).returns("") + node_indirection.parent_attribute.should be_nil + end - it "should use the value of the :ldapstring as the search filter" do - Puppet.stubs(:[]).with(:ldapstring).returns("mystring") - @searcher.search_filter("testing").should == "mystring" - end + it "should return the value of the :ldapparentattr as the parent attribute" do + Puppet.stubs(:[]).with(:ldapparentattr).returns("pere") + node_indirection.parent_attribute.should == "pere" + end - it "should replace '%s' with the node name in the search filter if it is present" do - Puppet.stubs(:[]).with(:ldapstring).returns("my%sstring") - @searcher.search_filter("testing").should == "mytestingstring" - end + it "should use the value of the :ldapstring as the search filter" do + Puppet.stubs(:[]).with(:ldapstring).returns("mystring") + node_indirection.search_filter("testing").should == "mystring" + end - it "should not modify the global :ldapstring when replacing '%s' in the search filter" do - filter = mock 'filter' - filter.expects(:include?).with("%s").returns(true) - filter.expects(:gsub).with("%s", "testing").returns("mynewstring") - Puppet.stubs(:[]).with(:ldapstring).returns(filter) - @searcher.search_filter("testing").should == "mynewstring" - end -end + it "should replace '%s' with the node name in the search filter if it is present" do + Puppet.stubs(:[]).with(:ldapstring).returns("my%sstring") + node_indirection.search_filter("testing").should == "mytestingstring" + end -describe Puppet::Node::Ldap, " when deciding attributes to search for" do - before do - @searcher = Puppet::Node::Ldap.new + it "should not modify the global :ldapstring when replacing '%s' in the search filter" do + filter = mock 'filter' + filter.expects(:include?).with("%s").returns(true) + filter.expects(:gsub).with("%s", "testing").returns("mynewstring") + Puppet.stubs(:[]).with(:ldapstring).returns(filter) + node_indirection.search_filter("testing").should == "mynewstring" + end end - it "should use 'nil' if the :ldapattrs setting is 'all'" do - Puppet.stubs(:[]).with(:ldapattrs).returns("all") - @searcher.search_attributes.should be_nil - end + describe Puppet::Node::Ldap, " when deciding attributes to search for" do + it "should use 'nil' if the :ldapattrs setting is 'all'" do + Puppet.stubs(:[]).with(:ldapattrs).returns("all") + node_indirection.search_attributes.should be_nil + end - it "should split the value of :ldapattrs on commas and use the result as the attribute list" do - Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") - @searcher.stubs(:class_attributes).returns([]) - @searcher.stubs(:parent_attribute).returns(nil) - @searcher.search_attributes.should == %w{one two} - end + it "should split the value of :ldapattrs on commas and use the result as the attribute list" do + Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") + node_indirection.stubs(:class_attributes).returns([]) + node_indirection.stubs(:parent_attribute).returns(nil) + node_indirection.search_attributes.should == %w{one two} + end - it "should add the class attributes to the search attributes if not returning all attributes" do - Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") - @searcher.stubs(:class_attributes).returns(%w{three four}) - @searcher.stubs(:parent_attribute).returns(nil) - # Sort them so i don't have to care about return order - @searcher.search_attributes.sort.should == %w{one two three four}.sort - end + it "should add the class attributes to the search attributes if not returning all attributes" do + Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") + node_indirection.stubs(:class_attributes).returns(%w{three four}) + node_indirection.stubs(:parent_attribute).returns(nil) + # Sort them so i don't have to care about return order + node_indirection.search_attributes.sort.should == %w{one two three four}.sort + end - it "should add the parent attribute to the search attributes if not returning all attributes" do - Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") - @searcher.stubs(:class_attributes).returns([]) - @searcher.stubs(:parent_attribute).returns("parent") - @searcher.search_attributes.sort.should == %w{one two parent}.sort - end + it "should add the parent attribute to the search attributes if not returning all attributes" do + Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") + node_indirection.stubs(:class_attributes).returns([]) + node_indirection.stubs(:parent_attribute).returns("parent") + node_indirection.search_attributes.sort.should == %w{one two parent}.sort + end - it "should not add nil parent attributes to the search attributes" do - Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") - @searcher.stubs(:class_attributes).returns([]) - @searcher.stubs(:parent_attribute).returns(nil) - @searcher.search_attributes.should == %w{one two} + it "should not add nil parent attributes to the search attributes" do + Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") + node_indirection.stubs(:class_attributes).returns([]) + node_indirection.stubs(:parent_attribute).returns(nil) + node_indirection.search_attributes.should == %w{one two} + end end end diff --git a/spec/unit/indirector/node/plain_spec.rb b/spec/unit/indirector/node/plain_spec.rb index c6ba84e67..9bfb0d9cd 100755 --- a/spec/unit/indirector/node/plain_spec.rb +++ b/spec/unit/indirector/node/plain_spec.rb @@ -1,18 +1,26 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/indirector/node/plain' describe Puppet::Node::Plain do + let(:nodename) { "mynode" } + let(:fact_values) { {:afact => "a value"} } + let(:facts) { Puppet::Node::Facts.new(nodename, fact_values) } + let(:environment) { Puppet::Node::Environment.new("myenv") } + let(:request) { Puppet::Indirector::Request.new(:node, :find, nodename, nil, :environment => environment) } + let(:node_indirection) { Puppet::Node::Plain.new } + before do - @searcher = Puppet::Node::Plain.new + Puppet::Node::Facts.indirection.expects(:find).with(nodename, :environment => environment).returns(facts) + end + + it "merges facts into the node" do + node_indirection.find(request).parameters.should include(fact_values) end - it "should call node_merge() on the returned node" do - node = mock 'node' - Puppet::Node.expects(:new).with("mynode").returns(node) - node.expects(:fact_merge) - request = stub 'request', :key => "mynode" - @searcher.find(request) + it "should set the node environment from the request" do + node_indirection.find(request).environment.should == environment end + end