summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuke Kanies <luke@madstop.com>2007-09-22 00:16:39 -0500
committerLuke Kanies <luke@madstop.com>2007-09-22 00:16:39 -0500
commitebe7290bf0c9119e268c9037c33da515e527aa5b (patch)
tree3f59b5b3fc46c35f5ef18e0a1110381c94187692
parentb9dc6cb22f087f419b328cafa945c9604043b22f (diff)
downloadpuppet-ebe7290bf0c9119e268c9037c33da515e527aa5b.tar.gz
puppet-ebe7290bf0c9119e268c9037c33da515e527aa5b.tar.xz
puppet-ebe7290bf0c9119e268c9037c33da515e527aa5b.zip
All indirections are working, and they have all
been migrated over to the new organization. Where we would have previously had an 'ldap' node terminus at puppet/indirector/node/ldap.rb, we would not have it at puppet/indirector/ldap/node.rb, and it would be a subclass of puppet/indirector/ldap.rb. These are called terminus classes, and there are now three categories of them: The base class itself, abstract classes that provide most of the functionality (e.g., the ldap and yaml classes), and the classes themselves that implement the functionality for a given model like Node or Facts. The base terminus class handles auto-loading any of these classes from disk.
-rw-r--r--lib/puppet/defaults.rb2
-rw-r--r--lib/puppet/indirector.rb2
-rw-r--r--lib/puppet/indirector/exec.rb57
-rw-r--r--lib/puppet/indirector/exec/node.rb50
-rw-r--r--lib/puppet/indirector/indirection.rb5
-rw-r--r--lib/puppet/indirector/ldap.rb90
-rw-r--r--lib/puppet/indirector/ldap/node.rb75
-rw-r--r--lib/puppet/indirector/node/external.rb89
-rw-r--r--lib/puppet/indirector/node/ldap.rb140
-rw-r--r--lib/puppet/indirector/null.rb9
-rw-r--r--lib/puppet/indirector/null/node.rb (renamed from lib/puppet/indirector/node/none.rb)6
-rw-r--r--lib/puppet/indirector/terminus.rb8
-rwxr-xr-xspec/unit/indirector/exec.rb49
-rwxr-xr-xspec/unit/indirector/exec/node.rb62
-rwxr-xr-xspec/unit/indirector/indirection.rb20
-rwxr-xr-xspec/unit/indirector/ldap.rb147
-rwxr-xr-xspec/unit/indirector/ldap/node.rb84
-rwxr-xr-xspec/unit/indirector/node/external.rb119
-rwxr-xr-xspec/unit/indirector/node/ldap.rb243
-rwxr-xr-xspec/unit/indirector/node/none.rb32
-rwxr-xr-xspec/unit/indirector/null.rb27
-rwxr-xr-xspec/unit/indirector/null/node.rb18
-rwxr-xr-xspec/unit/indirector/terminus.rb14
23 files changed, 711 insertions, 637 deletions
diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb
index fa119bb5a..fa06c7fdb 100644
--- a/lib/puppet/defaults.rb
+++ b/lib/puppet/defaults.rb
@@ -554,7 +554,7 @@ module Puppet
setdefaults(:parser,
:typecheck => [true, "Whether to validate types during parsing."],
:paramcheck => [true, "Whether to validate parameters during parsing."],
- :node_source => ["none", "Where to look for node configuration information.
+ :node_terminus => ["null", "Where to look for node configuration information.
The default node source, ``none``, just returns a node with its facts
filled in, which is required for normal functionality.
See the `NodeSourceReference`:trac: for more information."]
diff --git a/lib/puppet/indirector.rb b/lib/puppet/indirector.rb
index b89017c9a..bd2487e33 100644
--- a/lib/puppet/indirector.rb
+++ b/lib/puppet/indirector.rb
@@ -23,7 +23,7 @@ module Puppet::Indirector
# instantiate the actual Terminus for that type and this name (:ldap, w/ args :node)
# & hook the instantiated Terminus into this class (Node: @indirection = terminus)
- @indirection = Puppet::Indirector::Indirection.new(indirection)
+ @indirection = Puppet::Indirector::Indirection.new(self, indirection)
end
module ClassMethods
diff --git a/lib/puppet/indirector/exec.rb b/lib/puppet/indirector/exec.rb
new file mode 100644
index 000000000..7e4ac8d18
--- /dev/null
+++ b/lib/puppet/indirector/exec.rb
@@ -0,0 +1,57 @@
+require 'puppet/indirector/terminus'
+require 'puppet/util'
+
+class Puppet::Indirector::Exec < Puppet::Indirector::Terminus
+ # Look for external node definitions.
+ def find(name)
+ # Run the command.
+ unless output = query(name)
+ return nil
+ end
+
+ # Translate the output to ruby.
+ return output
+ end
+
+ private
+
+ # Proxy the execution, so it's easier to test.
+ def execute(command)
+ Puppet::Util.execute(command)
+ end
+
+ # Call the external command and see if it returns our output.
+ def query(name)
+ external_command = command
+
+ # Make sure it's an arry
+ unless external_command.is_a?(Array)
+ raise Puppet::DevError, "Exec commands must be an array"
+ end
+
+ # Make sure it's fully qualified.
+ unless external_command[0][0] == File::SEPARATOR[0]
+ raise ArgumentError, "You must set the exec parameter to a fully qualified command"
+ end
+
+ # Add our name to it.
+ external_command << name
+ begin
+ output = execute(external_command)
+ rescue Puppet::ExecutionFailure => detail
+ if $?.exitstatus == 1
+ return nil
+ else
+ Puppet.err "Could not retrieve external node information for %s: %s" % [name, detail]
+ end
+ return nil
+ end
+
+ if output =~ /\A\s*\Z/ # all whitespace
+ Puppet.debug "Empty response for %s from exec %s terminus" % [name, self.name]
+ return nil
+ else
+ return output
+ end
+ end
+end
diff --git a/lib/puppet/indirector/exec/node.rb b/lib/puppet/indirector/exec/node.rb
new file mode 100644
index 000000000..033afe3f0
--- /dev/null
+++ b/lib/puppet/indirector/exec/node.rb
@@ -0,0 +1,50 @@
+require 'puppet/indirector/exec'
+
+class Puppet::Indirector::Exec::Node < Puppet::Indirector::Exec
+ desc "Call an external program to get node information."
+ include Puppet::Util
+
+ def command
+ command = Puppet[:external_nodes]
+ unless command != "none"
+ raise ArgumentError, "You must set the 'external_nodes' parameter to use the external node terminus"
+ end
+ command.split
+ end
+
+ # Look for external node definitions.
+ def find(name)
+ output = super or return nil
+
+ # Translate the output to ruby.
+ result = translate(name, output)
+
+ return create_node(name, result)
+ end
+
+ private
+
+ # Turn our outputted objects into a Puppet::Node instance.
+ def create_node(name, result)
+ node = Puppet::Node.new(name)
+ set = false
+ [:parameters, :classes].each do |param|
+ if value = result[param]
+ node.send(param.to_s + "=", value)
+ set = true
+ end
+ end
+
+ node.fact_merge
+ return node
+ end
+
+ # Translate the yaml string into Ruby objects.
+ def translate(name, output)
+ begin
+ YAML.load(output).inject({}) { |hash, data| hash[symbolize(data[0])] = data[1]; hash }
+ rescue => detail
+ raise Puppet::Error, "Could not load external node results for %s: %s" % [name, detail]
+ end
+ end
+end
diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb
index 31b9d68a5..12b8cf328 100644
--- a/lib/puppet/indirector/indirection.rb
+++ b/lib/puppet/indirector/indirection.rb
@@ -13,7 +13,7 @@ class Puppet::Indirector::Indirection
@@indirections.find { |i| i.name == name }
end
- attr_accessor :name, :termini
+ attr_accessor :name, :model
# Clear our cached list of termini.
# This is only used for testing.
@@ -26,7 +26,8 @@ class Puppet::Indirector::Indirection
@@indirections.delete(self) if @@indirections.include?(self)
end
- def initialize(name, options = {})
+ def initialize(model, name, options = {})
+ @model = model
@name = name
options.each do |name, value|
begin
diff --git a/lib/puppet/indirector/ldap.rb b/lib/puppet/indirector/ldap.rb
new file mode 100644
index 000000000..63848d10a
--- /dev/null
+++ b/lib/puppet/indirector/ldap.rb
@@ -0,0 +1,90 @@
+require 'puppet/indirector/terminus'
+
+class Puppet::Indirector::Ldap < Puppet::Indirector::Terminus
+ # Perform our ldap search and process the result.
+ def find(name)
+ # We have to use 'yield' here because the LDAP::Entry objects
+ # get destroyed outside the scope of the search, strangely.
+ ldapsearch(name) { |entry| return process(entry) }
+
+ # Return nil if we haven't found something.
+ return nil
+ end
+
+ # Process the found entry. We assume that we don't just want the
+ # ldap object.
+ def process(entry)
+ raise Puppet::DevError, "The 'process' method has not been overridden for the LDAP terminus for %s" % self.name
+ end
+
+ # Default to all attributes.
+ def search_attributes
+ nil
+ end
+
+ def search_base
+ Puppet[:ldapbase]
+ end
+
+ # The ldap search filter to use.
+ def search_filter(name)
+ raise Puppet::DevError, "No search string set for LDAP terminus for %s" % self.name
+ end
+
+ # Find the ldap node, return the class list and parent node specially,
+ # and everything else in a parameter hash.
+ def ldapsearch(node)
+ raise ArgumentError.new("You must pass a block to ldapsearch") unless block_given?
+
+ found = false
+ count = 0
+
+ begin
+ connection.search(search_base, 2, search_filter(node), search_attributes) do |entry|
+ found = true
+ yield entry
+ end
+ rescue => detail
+ if count == 0
+ # Try reconnecting to ldap if we get an exception and we haven't yet retried.
+ count += 1
+ @connection = nil
+ Puppet.warning "Retrying LDAP connection"
+ retry
+ else
+ raise Puppet::Error, "LDAP Search failed: %s" % detail
+ end
+ end
+
+ return found
+ end
+
+ private
+
+ # Create an ldap connection.
+ def connection
+ unless defined? @connection and @connection
+ unless Puppet.features.ldap?
+ raise Puppet::Error, "Could not set up LDAP Connection: Missing ruby/ldap libraries"
+ end
+ begin
+ if Puppet[:ldapssl]
+ @connection = LDAP::SSLConn.new(Puppet[:ldapserver], Puppet[:ldapport])
+ elsif Puppet[:ldaptls]
+ @connection = LDAP::SSLConn.new(
+ Puppet[:ldapserver], Puppet[:ldapport], true
+ )
+ else
+ @connection = LDAP::Conn.new(Puppet[:ldapserver], Puppet[:ldapport])
+ end
+ @connection.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
+ @connection.set_option(LDAP::LDAP_OPT_REFERRALS, LDAP::LDAP_OPT_ON)
+ @connection.simple_bind(Puppet[:ldapuser], Puppet[:ldappassword])
+ rescue => detail
+ raise Puppet::Error, "Could not connect to LDAP: %s" % detail
+ end
+ end
+
+ return @connection
+ end
+end
diff --git a/lib/puppet/indirector/ldap/node.rb b/lib/puppet/indirector/ldap/node.rb
new file mode 100644
index 000000000..187c74b94
--- /dev/null
+++ b/lib/puppet/indirector/ldap/node.rb
@@ -0,0 +1,75 @@
+require 'puppet/indirector/ldap'
+
+class Puppet::Indirector::Ldap::Node < Puppet::Indirector::Ldap
+ desc "Search in LDAP for node configuration information."
+
+ # The attributes that Puppet class information is stored in.
+ def class_attributes
+ Puppet[:ldapclassattrs].split(/\s*,\s*/)
+ end
+
+ # Look for our node in ldap.
+ def find(name)
+ unless ary = ldapsearch(name)
+ return nil
+ end
+ parent, classes, parameters = ary
+
+ while parent
+ parent, tmpclasses, tmpparams = ldapsearch(parent)
+ classes += tmpclasses if tmpclasses
+ tmpparams.each do |param, value|
+ # Specifically test for whether it's set, so false values are handled
+ # correctly.
+ parameters[param] = value unless parameters.include?(param)
+ end
+ end
+
+ node = Puppet::Node.new(name, :classes => classes, :source => "ldap", :parameters => parameters)
+ node.fact_merge
+ return node
+ end
+
+ # The parent attribute, if we have one.
+ def parent_attribute
+ if pattr = Puppet[:ldapparentattr] and ! pattr.empty?
+ pattr
+ else
+ nil
+ end
+ end
+
+ # Process the found entry. We assume that we don't just want the
+ # ldap object.
+ def process(entry)
+ raise Puppet::DevError, "The 'process' method has not been overridden for the LDAP terminus for %s" % self.name
+ 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
+end
diff --git a/lib/puppet/indirector/node/external.rb b/lib/puppet/indirector/node/external.rb
deleted file mode 100644
index 66aca6048..000000000
--- a/lib/puppet/indirector/node/external.rb
+++ /dev/null
@@ -1,89 +0,0 @@
-require 'puppet/node/facts'
-
-Puppet::Indirector.register_terminus :node, :external do
- desc "Call an external program to get node information."
-
- include Puppet::Util
-
- # Proxy the execution, so it's easier to test.
- def execute(command)
- Puppet::Util.execute(command)
- end
-
- # Look for external node definitions.
- def find(name)
- unless Puppet[:external_nodes] != "none"
- raise ArgumentError, "You must set the 'external_nodes' parameter to use the external node source"
- end
-
- unless Puppet[:external_nodes][0] == File::SEPARATOR[0]
- raise ArgumentError, "You must set the 'external_nodes' parameter to a fully qualified command"
- end
-
- # Run the command.
- unless output = query(name)
- return nil
- end
-
- # Translate the output to ruby.
- result = translate(name, output)
-
- return create_node(name, result)
- end
-
- private
-
- # Turn our outputted objects into a Puppet::Node instance.
- def create_node(name, result)
- node = Puppet::Node.new(name)
- set = false
- [:parameters, :classes].each do |param|
- if value = result[param]
- node.send(param.to_s + "=", value)
- set = true
- end
- end
-
- if set
- node.fact_merge
- return node
- else
- return nil
- end
- end
-
- # Call the external command and see if it returns our output.
- def query(name)
- # This is a very cheap way to do this, since it will break on
- # commands that have spaces in the arguments. But it's good
- # enough for most cases.
- external_node_command = Puppet[:external_nodes].split
- external_node_command << name
- begin
- output = execute(external_node_command)
- rescue Puppet::ExecutionFailure => detail
- if $?.exitstatus == 1
- return nil
- else
- Puppet.err "Could not retrieve external node information for %s: %s" % [name, detail]
- end
- return nil
- end
-
- if output =~ /\A\s*\Z/ # all whitespace
- Puppet.debug "Empty response for %s from external node source" % name
- return nil
- else
- return output
- end
- end
-
- # Translate the yaml string into Ruby objects.
- def translate(name, output)
- begin
- YAML.load(output).inject({}) { |hash, data| hash[symbolize(data[0])] = data[1]; hash }
- rescue => detail
- raise Puppet::Error, "Could not load external node results for %s: %s" % [name, detail]
- end
- end
-end
diff --git a/lib/puppet/indirector/node/ldap.rb b/lib/puppet/indirector/node/ldap.rb
deleted file mode 100644
index 230ca2467..000000000
--- a/lib/puppet/indirector/node/ldap.rb
+++ /dev/null
@@ -1,140 +0,0 @@
-Puppet::Indirector.register_terminus :node, :ldap do
- desc "Search in LDAP for node configuration information."
-
- # Look for our node in ldap.
- def find(name)
- unless ary = ldapsearch(name)
- return nil
- end
- parent, classes, parameters = ary
-
- while parent
- parent, tmpclasses, tmpparams = ldapsearch(parent)
- classes += tmpclasses if tmpclasses
- tmpparams.each do |param, value|
- # Specifically test for whether it's set, so false values are handled
- # correctly.
- parameters[param] = value unless parameters.include?(param)
- end
- end
-
- node = Puppet::Node.new(name, :classes => classes, :source => "ldap", :parameters => parameters)
- node.fact_merge
- return node
- end
-
- # Find the ldap node, return the class list and parent node specially,
- # and everything else in a parameter hash.
- def ldapsearch(node)
- filter = Puppet[:ldapstring]
- classattrs = Puppet[:ldapclassattrs].split("\s*,\s*")
- if Puppet[:ldapattrs] == "all"
- # A nil value here causes all attributes to be returned.
- search_attrs = nil
- else
- search_attrs = classattrs + Puppet[:ldapattrs].split("\s*,\s*")
- end
- pattr = nil
- if pattr = Puppet[:ldapparentattr]
- if pattr == ""
- pattr = nil
- else
- search_attrs << pattr unless search_attrs.nil?
- end
- end
-
- if filter =~ /%s/
- filter = filter.gsub(/%s/, node)
- end
-
- parent = nil
- classes = []
- parameters = nil
-
- found = false
- count = 0
-
- begin
- # We're always doing a sub here; oh well.
- ldap.search(Puppet[:ldapbase], 2, filter, search_attrs) do |entry|
- found = true
- if pattr
- if values = entry.vals(pattr)
- if values.length > 1
- raise Puppet::Error,
- "Node %s has more than one parent: %s" %
- [node, values.inspect]
- end
- unless values.empty?
- parent = values.shift
- end
- end
- end
-
- classattrs.each { |attr|
- if values = entry.vals(attr)
- values.each do |v| classes << v end
- end
- }
-
- parameters = entry.to_hash.inject({}) do |hash, ary|
- if ary[1].length == 1
- hash[ary[0]] = ary[1].shift
- else
- hash[ary[0]] = ary[1]
- end
- hash
- end
- end
- rescue => detail
- if count == 0
- # Try reconnecting to ldap
- @ldap = nil
- retry
- else
- raise Puppet::Error, "LDAP Search failed: %s" % detail
- end
- end
-
- classes.flatten!
-
- if classes.empty?
- classes = nil
- end
-
- if parent or classes or parameters
- return parent, classes, parameters
- else
- return nil
- end
- end
-
- private
-
- # Create an ldap connection.
- def ldap
- unless defined? @ldap and @ldap
- unless Puppet.features.ldap?
- raise Puppet::Error, "Could not set up LDAP Connection: Missing ruby/ldap libraries"
- end
- begin
- if Puppet[:ldapssl]
- @ldap = LDAP::SSLConn.new(Puppet[:ldapserver], Puppet[:ldapport])
- elsif Puppet[:ldaptls]
- @ldap = LDAP::SSLConn.new(
- Puppet[:ldapserver], Puppet[:ldapport], true
- )
- else
- @ldap = LDAP::Conn.new(Puppet[:ldapserver], Puppet[:ldapport])
- end
- @ldap.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
- @ldap.set_option(LDAP::LDAP_OPT_REFERRALS, LDAP::LDAP_OPT_ON)
- @ldap.simple_bind(Puppet[:ldapuser], Puppet[:ldappassword])
- rescue => detail
- raise Puppet::Error, "Could not connect to LDAP: %s" % detail
- end
- end
-
- return @ldap
- end
-end
diff --git a/lib/puppet/indirector/null.rb b/lib/puppet/indirector/null.rb
new file mode 100644
index 000000000..db2b1db1c
--- /dev/null
+++ b/lib/puppet/indirector/null.rb
@@ -0,0 +1,9 @@
+require 'puppet/indirector/terminus'
+
+# An empty terminus type, meant to just return empty objects.
+class Puppet::Indirector::Null < Puppet::Indirector::Terminus
+ # Just return nothing.
+ def find(name)
+ indirection.model.new(name)
+ end
+end
diff --git a/lib/puppet/indirector/node/none.rb b/lib/puppet/indirector/null/node.rb
index 034c0b349..eb08f5697 100644
--- a/lib/puppet/indirector/node/none.rb
+++ b/lib/puppet/indirector/null/node.rb
@@ -1,13 +1,13 @@
-require 'puppet/node/facts'
+require 'puppet/indirector/null'
-Puppet::Indirector.register_terminus :node, :none do
+class Puppet::Indirector::Null::Node < Puppet::Indirector::Null
desc "Always return an empty node object. This is the node source you should
use when you don't have some other, functional source you want to use,
as the compiler will not work without this node information."
# Just return an empty node.
def find(name)
- node = Puppet::Node.new(name)
+ node = super
node.fact_merge
node
end
diff --git a/lib/puppet/indirector/terminus.rb b/lib/puppet/indirector/terminus.rb
index f3797f471..bcff08d79 100644
--- a/lib/puppet/indirector/terminus.rb
+++ b/lib/puppet/indirector/terminus.rb
@@ -68,6 +68,10 @@ class Puppet::Indirector::Terminus
@abstract_terminus = true
end
+ def model
+ indirection.model
+ end
+
# Register a class, probably autoloaded.
def register_terminus_class(klass)
setup_instance_loading klass.terminus_type
@@ -102,6 +106,10 @@ class Puppet::Indirector::Terminus
def name
self.class.name
end
+
+ def model
+ self.class.model
+ end
def indirection
self.class.indirection
diff --git a/spec/unit/indirector/exec.rb b/spec/unit/indirector/exec.rb
new file mode 100755
index 000000000..42fbe0955
--- /dev/null
+++ b/spec/unit/indirector/exec.rb
@@ -0,0 +1,49 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/indirector/exec'
+
+describe Puppet::Indirector::Exec do
+ before do
+ @indirection = mock 'indirection'
+ Puppet::Indirector::Indirection.expects(:instance).with(:testing).returns(@indirection)
+ @exec_class = Class.new(Puppet::Indirector::Exec) do
+ def self.to_s
+ "Testing"
+ end
+
+ attr_accessor :command
+ end
+
+ @searcher = @exec_class.new
+ @searcher.command = ["/echo"]
+ end
+
+ it "should throw an exception if the command is not an array" do
+ @searcher.command = "/usr/bin/echo"
+ proc { @searcher.find("foo") }.should raise_error(Puppet::DevError)
+ end
+
+ it "should throw an exception if the command is not fully qualified" do
+ @searcher.command = ["mycommand"]
+ proc { @searcher.find("foo") }.should raise_error(ArgumentError)
+ end
+
+ it "should execute the command with the object name as the only argument" do
+ @searcher.expects(:execute).with(%w{/echo yay})
+ @searcher.find("yay")
+ end
+
+ it "should return the output of the script" do
+ @searcher.expects(:execute).with(%w{/echo yay}).returns("whatever")
+ @searcher.find("yay").should == "whatever"
+ end
+
+ it "should return nil when the command produces no output" do
+ @searcher.expects(:execute).with(%w{/echo yay}).returns(nil)
+ @searcher.find("yay").should be_nil
+ end
+
+ it "should be able to execute commands with multiple arguments"
+end
diff --git a/spec/unit/indirector/exec/node.rb b/spec/unit/indirector/exec/node.rb
new file mode 100755
index 000000000..2eaf3c12e
--- /dev/null
+++ b/spec/unit/indirector/exec/node.rb
@@ -0,0 +1,62 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/indirector/exec/node'
+
+describe Puppet::Indirector::Exec::Node, " when constructing the command to run" do
+ before do
+ @indirection = mock 'indirection'
+ Puppet.config.stubs(:value).with(:external_nodes).returns("/echo")
+ @searcher = Puppet::Indirector::Exec::Node.new
+ end
+
+ it "should use the external_node script as the command" do
+ Puppet.expects(:[]).with(:external_nodes).returns("/bin/echo")
+ @searcher.command.should == %w{/bin/echo}
+ end
+
+ it "should throw an exception if no external node command is set" do
+ Puppet.expects(:[]).with(:external_nodes).returns("none")
+ proc { @searcher.find("foo") }.should raise_error(ArgumentError)
+ end
+end
+
+describe Puppet::Indirector::Exec::Node, " when handling the results of the command" do
+ before do
+ @indirection = mock 'indirection'
+ Puppet.config.stubs(:value).with(:external_nodes).returns("/echo")
+ @searcher = Puppet::Indirector::Exec::Node.new
+ @node = stub 'node', :fact_merge => nil
+ @name = "yay"
+ Puppet::Node.expects(:new).with(@name).returns(@node)
+ @result = {}
+ # Use a local variable so the reference is usable in the execute() definition.
+ result = @result
+ @searcher.meta_def(:execute) do |command|
+ return YAML.dump(result)
+ end
+ end
+
+ it "should translate the YAML into a Node instance" do
+ # Use an empty hash
+ @searcher.find(@name).should equal(@node)
+ end
+
+ it "should set the resulting parameters as the node parameters" do
+ @result[:parameters] = {"a" => "b", "c" => "d"}
+ @node.expects(:parameters=).with "a" => "b", "c" => "d"
+ @searcher.find(@name)
+ end
+
+ it "should set the resulting classes as the node classes" do
+ @result[:classes] = %w{one two}
+ @node.expects(:classes=).with %w{one two}
+ @searcher.find(@name)
+ end
+
+ it "should merge the node's facts with its parameters" do
+ @node.expects(:fact_merge)
+ @searcher.find(@name)
+ end
+end
diff --git a/spec/unit/indirector/indirection.rb b/spec/unit/indirector/indirection.rb
index 5866f2d5f..e57f12072 100755
--- a/spec/unit/indirector/indirection.rb
+++ b/spec/unit/indirector/indirection.rb
@@ -5,13 +5,19 @@ require File.dirname(__FILE__) + '/../../spec_helper'
require 'puppet/indirector'
describe Puppet::Indirector::Indirection, " when initializing" do
+ it "should keep a reference to the indirecting model" do
+ model = mock 'model'
+ @indirection = Puppet::Indirector::Indirection.new(model, :myind)
+ @indirection.model.should equal(model)
+ end
+
it "should set the name" do
- @indirection = Puppet::Indirector::Indirection.new(:myind)
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :myind)
@indirection.name.should == :myind
end
it "should require indirections to have unique names" do
- @indirection = Puppet::Indirector::Indirection.new(:test)
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
proc { Puppet::Indirector::Indirection.new(:test) }.should raise_error(ArgumentError)
end
@@ -22,7 +28,7 @@ end
describe Puppet::Indirector::Indirection, " when managing indirection instances" do
it "should allow an indirection to be retrieved by name" do
- @indirection = Puppet::Indirector::Indirection.new(:test)
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
Puppet::Indirector::Indirection.instance(:test).should equal(@indirection)
end
@@ -37,7 +43,7 @@ end
describe Puppet::Indirector::Indirection, " when choosing terminus types" do
before do
- @indirection = Puppet::Indirector::Indirection.new(:test)
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
@terminus = mock 'terminus'
@terminus_class = stub 'terminus class', :new => @terminus
end
@@ -82,7 +88,7 @@ end
describe Puppet::Indirector::Indirection, " when managing terminus instances" do
before do
- @indirection = Puppet::Indirector::Indirection.new(:test)
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
@terminus = mock 'terminus'
@terminus_class = mock 'terminus class'
Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class)
@@ -111,7 +117,7 @@ describe Puppet::Indirector::Indirection, " when managing terminus instances" do
it "should not create a terminus instance until one is actually needed" do
Puppet::Indirector.expects(:terminus).never
- indirection = Puppet::Indirector::Indirection.new(:lazytest)
+ indirection = Puppet::Indirector::Indirection.new(mock('model'), :lazytest)
end
after do
@@ -122,7 +128,7 @@ end
describe Puppet::Indirector::Indirection do
before do
- @indirection = Puppet::Indirector::Indirection.new(:test)
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
@terminus = mock 'terminus'
@indirection.stubs(:terminus).returns(@terminus)
end
diff --git a/spec/unit/indirector/ldap.rb b/spec/unit/indirector/ldap.rb
new file mode 100755
index 000000000..a936936bc
--- /dev/null
+++ b/spec/unit/indirector/ldap.rb
@@ -0,0 +1,147 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/indirector/ldap'
+
+describe Puppet::Indirector::Ldap, " when searching ldap" do
+ before do
+ @indirection = mock 'indirection'
+ Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection)
+ @ldap_class = Class.new(Puppet::Indirector::Ldap) do
+ def self.to_s
+ "Testing"
+ end
+ end
+
+ @connection = mock 'ldap'
+
+ @searcher = @ldap_class.new
+
+ # Stub everything, and we can selectively replace with an expect as
+ # we need to for testing.
+ @searcher.stubs(:connection).returns(@connection)
+ @searcher.stubs(:search_filter).returns(:filter)
+ @searcher.stubs(:search_base).returns(:base)
+ @searcher.stubs(:process)
+ end
+
+ it "should call the ldapsearch method with the name being searched for" do
+ @searcher.expects(:ldapsearch).with("yay")
+ @searcher.find "yay"
+ end
+
+ it "should fail if no block is passed to the ldapsearch method" do
+ proc { @searcher.ldapsearch("blah") }.should raise_error(ArgumentError)
+ end
+
+ it "should use the results of the ldapbase method as the ldap search base" do
+ @searcher.stubs(:search_base).returns("mybase")
+ @connection.expects(:search).with do |*args|
+ args[0].should == "mybase"
+ true
+ end
+ @searcher.find "yay"
+ end
+
+ it "should default to the value of the :search_base setting as the result of the ldapbase method" do
+ Puppet.expects(:[]).with(:ldapbase).returns("myldapbase")
+ searcher = @ldap_class.new
+ searcher.search_base.should == "myldapbase"
+ end
+
+ it "should use the results of the :search_attributes method as the list of attributes to return" do
+ @searcher.stubs(:search_attributes).returns(:myattrs)
+ @connection.expects(:search).with do |*args|
+ args[3].should == :myattrs
+ true
+ end
+ @searcher.find "yay"
+ end
+
+ it "should use the results of the :search_filter method as the search filter" do
+ @searcher.stubs(:search_filter).with("yay").returns("yay's filter")
+ @connection.expects(:search).with do |*args|
+ args[2].should == "yay's filter"
+ true
+ end
+ @searcher.find "yay"
+ end
+
+ it "should use depth 2 when searching" do
+ @connection.expects(:search).with do |*args|
+ args[1].should == 2
+ true
+ end
+ @searcher.find "yay"
+ end
+
+ it "should call process() on the first found entry" do
+ @connection.expects(:search).yields("myresult")
+ @searcher.expects(:process).with("myresult")
+ @searcher.find "yay"
+ end
+
+ it "should reconnect and retry the search if there is a failure" do
+ run = false
+ @connection.stubs(:search).with do |*args|
+ if run
+ true
+ else
+ run = true
+ raise "failed"
+ end
+ end.yields("myresult")
+ @searcher.expects(:process).with("myresult")
+
+ @searcher.find "yay"
+ end
+
+ it "should not reconnect on failure more than once" do
+ count = 0
+ @connection.stubs(:search).with do |*args|
+ count += 1
+ raise ArgumentError, "yay"
+ end
+ proc { @searcher.find("whatever") }.should raise_error(Puppet::Error)
+ count.should == 2
+ end
+
+ it "should return true if an entry is found" do
+ @connection.expects(:search).yields("result")
+ @searcher.ldapsearch("whatever") { |r| }.should be_true
+ end
+end
+
+describe Puppet::Indirector::Ldap, " when connecting to ldap" do
+ confine "LDAP is not available" => Puppet.features.ldap?
+ confine "No LDAP test data for networks other than Luke's" => Facter.value(:domain) == "madstop.com"
+
+ it "should only create the ldap connection when asked for it the first time"
+
+ it "should throw an exception if it cannot connect to LDAP"
+
+ it "should use SSL when the :ldapssl setting is true"
+
+ it "should connect to the server specified in the :ldapserver setting"
+
+ it "should use the port specified in the :ldapport setting"
+
+ it "should use protocol version 3"
+
+ it "should follow referrals"
+
+ it "should use the user specified in the :ldapuser setting"
+
+ it "should use the password specified in the :ldappassord setting"
+
+ it "should have an ldap method that returns an LDAP connection object"
+
+ it "should fail when LDAP support is missing"
+end
+
+describe Puppet::Indirector::Ldap, " when reconnecting to ldap" do
+ confine "Not running on culain as root" => (Puppet::Util::SUIDManager.uid == 0 and Facter.value("hostname") == "culain")
+
+ it "should reconnect to ldap when connections are lost"
+end
diff --git a/spec/unit/indirector/ldap/node.rb b/spec/unit/indirector/ldap/node.rb
new file mode 100755
index 000000000..edaf77e92
--- /dev/null
+++ b/spec/unit/indirector/ldap/node.rb
@@ -0,0 +1,84 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/indirector/ldap/node'
+
+describe Puppet::Indirector::Ldap::Node, " when searching for nodes" do
+ before do
+ @searcher = Puppet::Indirector::Ldap::Node.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
+
+ 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 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 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 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
+
+describe Puppet::Indirector::Ldap::Node, " when deciding attributes to search for" do
+ before do
+ @searcher = Puppet::Indirector::Ldap::Node.new
+ 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
+
+ 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 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 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 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}
+ end
+end
diff --git a/spec/unit/indirector/node/external.rb b/spec/unit/indirector/node/external.rb
deleted file mode 100755
index c64a6f6e2..000000000
--- a/spec/unit/indirector/node/external.rb
+++ /dev/null
@@ -1,119 +0,0 @@
-#!/usr/bin/env ruby
-
-require File.dirname(__FILE__) + '/../../../spec_helper'
-
-require 'yaml'
-require 'puppet/indirector'
-
-describe Puppet::Indirector.terminus(:node, :external), " when searching for nodes" do
- require 'puppet/node'
-
- before do
- Puppet.config[:external_nodes] = "/yay/ness"
- @searcher = Puppet::Indirector.terminus(:node, :external).new
-
- # Set the searcher up so that we do not need to actually call the
- # external script.
- @searcher.meta_def(:execute) do |command|
- name = command.last.chomp
- result = {}
-
- if name =~ /a/
- result[:parameters] = {'one' => command.last + '1', 'two' => command.last + '2'}
- end
-
- if name =~ /p/
- result['classes'] = [1,2,3].collect { |n| command.last + n.to_s }
- end
-
- return YAML.dump(result)
- end
- end
-
- it "should throw an exception if the node_source is external but no external node command is set" do
- Puppet[:external_nodes] = "none"
- proc { @searcher.find("foo") }.should raise_error(ArgumentError)
- end
-
- it "should throw an exception if the external node source is not fully qualified" do
- Puppet[:external_nodes] = "mycommand"
- proc { @searcher.find("foo") }.should raise_error(ArgumentError)
- end
-
- it "should execute the command with the node name as the only argument" do
- command = [Puppet[:external_nodes], "yay"]
- @searcher.expects(:execute).with(command).returns("")
- @searcher.find("yay")
- end
-
- it "should return a node object" do
- @searcher.find("apple").should be_instance_of(Puppet::Node)
- end
-
- it "should set the node's name" do
- @searcher.find("apple").name.should == "apple"
- end
-
- # If we use a name that has a 'p' but no 'a', then our test generator
- # will return classes but no parameters.
- it "should be able to configure a node's classes" do
- node = @searcher.find("plum")
- node.classes.should == %w{plum1 plum2 plum3}
- node.parameters.should == {}
- end
-
- # If we use a name that has an 'a' but no 'p', then our test generator
- # will return parameters but no classes.
- it "should be able to configure a node's parameters" do
- node = @searcher.find("guava")
- node.classes.should == []
- node.parameters.should == {"one" => "guava1", "two" => "guava2"}
- end
-
- it "should be able to configure a node's classes and parameters" do
- node = @searcher.find("apple")
- node.classes.should == %w{apple1 apple2 apple3}
- node.parameters.should == {"one" => "apple1", "two" => "apple2"}
- end
-
- it "should merge node facts with returned parameters" do
- facts = Puppet::Node::Facts.new("apple", "three" => "four")
- Puppet::Node::Facts.expects(:find).with("apple").returns(facts)
- node = @searcher.find("apple")
- node.parameters["three"].should == "four"
- end
-
- it "should return nil when it cannot find the node" do
- @searcher.find("honeydew").should be_nil
- end
-
- # Make sure a nodesearch with arguments works
- def test_nodesearch_external_arguments
- mapper = mk_node_mapper
- Puppet[:external_nodes] = "#{mapper} -s something -p somethingelse"
- searcher = mk_searcher(:external)
- node = nil
- assert_nothing_raised do
- node = searcher.nodesearch("apple")
- end
- assert_instance_of(SimpleNode, node, "did not create node")
- end
-
- # A wrapper test, to make sure we're correctly calling the external search method.
- def test_nodesearch_external_functional
- mapper = mk_node_mapper
- searcher = mk_searcher(:external)
-
- Puppet[:external_nodes] = mapper
-
- node = nil
- assert_nothing_raised do
- node = searcher.nodesearch("apple")
- end
- assert_instance_of(SimpleNode, node, "did not create node")
- end
-
- after do
- Puppet.config.clear
- end
-end
diff --git a/spec/unit/indirector/node/ldap.rb b/spec/unit/indirector/node/ldap.rb
deleted file mode 100755
index e4b0cd7d4..000000000
--- a/spec/unit/indirector/node/ldap.rb
+++ /dev/null
@@ -1,243 +0,0 @@
-#!/usr/bin/env ruby
-
-require File.dirname(__FILE__) + '/../../../spec_helper'
-
-require 'yaml'
-require 'puppet/indirector'
-
-describe Puppet::Indirector.terminus(:node, :ldap), " when searching for nodes" do
- require 'puppet/node'
-
- def setup
- Puppet.config[:external_nodes] = "/yay/ness"
- @searcher = Puppet::Indirector.terminus(:node, :ldap).new
- nodetable = {}
- @nodetable = nodetable
- # Override the ldapsearch definition, so we don't have to actually set it up.
- @searcher.meta_def(:ldapsearch) do |name|
- nodetable[name]
- end
- end
-
- it "should return nil for hosts that cannot be found" do
- @searcher.find("foo").should be_nil
- end
-
- it "should return Puppet::Node instances" do
- @nodetable["foo"] = [nil, %w{}, {}]
- @searcher.find("foo").should be_instance_of(Puppet::Node)
- end
-
- it "should set the node name" do
- @nodetable["foo"] = [nil, %w{}, {}]
- @searcher.find("foo").name.should == "foo"
- end
-
- it "should set the classes" do
- @nodetable["foo"] = [nil, %w{one two}, {}]
- @searcher.find("foo").classes.should == %w{one two}
- end
-
- it "should set the parameters" do
- @nodetable["foo"] = [nil, %w{}, {"one" => "two"}]
- @searcher.find("foo").parameters.should == {"one" => "two"}
- end
-
- it "should set classes and parameters from the parent node" do
- @nodetable["foo"] = ["middle", %w{one two}, {"one" => "two"}]
- @nodetable["middle"] = [nil, %w{three four}, {"three" => "four"}]
- node = @searcher.find("foo")
- node.classes.sort.should == %w{one two three four}.sort
- node.parameters.should == {"one" => "two", "three" => "four"}
- end
-
- it "should prefer child parameters to parent parameters" do
- @nodetable["foo"] = ["middle", %w{}, {"one" => "two"}]
- @nodetable["middle"] = [nil, %w{}, {"one" => "four"}]
- @searcher.find("foo").parameters["one"].should == "two"
- end
-
- it "should recurse indefinitely through parent relationships" do
- @nodetable["foo"] = ["middle", %w{one two}, {"one" => "two"}]
- @nodetable["middle"] = ["top", %w{three four}, {"three" => "four"}]
- @nodetable["top"] = [nil, %w{five six}, {"five" => "six"}]
- node = @searcher.find("foo")
- node.parameters.should == {"one" => "two", "three" => "four", "five" => "six"}
- node.classes.sort.should == %w{one two three four five six}.sort
- end
-
- # This can stay in the main test suite because it doesn't actually use ldapsearch,
- # it just overrides the method so it behaves as though it were hitting ldap.
- def test_ldap_nodesearch
-
- # Make sure we get nothing for nonexistent hosts
- node = nil
- assert_nothing_raised do
- node = searcher.nodesearch("nosuchhost")
- end
-
- assert_nil(node, "Got a node for a non-existent host")
-
- # Now add a base node with some classes and parameters
- nodetable["base"] = [nil, %w{one two}, {"base" => "true"}]
-
- assert_nothing_raised do
- node = searcher.nodesearch("base")
- end
-
- assert_instance_of(SimpleNode, node, "Did not get node from ldap nodesearch")
- assert_equal("base", node.name, "node name was not set")
-
- assert_equal(%w{one two}, node.classes, "node classes were not set")
- assert_equal({"base" => "true"}, node.parameters, "node parameters were not set")
-
- # Now use a different with this as the base
- nodetable["middle"] = ["base", %w{three}, {"center" => "boo"}]
- assert_nothing_raised do
- node = searcher.nodesearch("middle")
- end
-
- assert_instance_of(SimpleNode, node, "Did not get node from ldap nodesearch")
- assert_equal("middle", node.name, "node name was not set")
-
- assert_equal(%w{one two three}.sort, node.classes.sort, "node classes were not set correctly with a parent node")
- assert_equal({"base" => "true", "center" => "boo"}, node.parameters, "node parameters were not set correctly with a parent node")
-
- # And one further, to make sure we fully recurse
- nodetable["top"] = ["middle", %w{four five}, {"master" => "far"}]
- assert_nothing_raised do
- node = searcher.nodesearch("top")
- end
-
- assert_instance_of(SimpleNode, node, "Did not get node from ldap nodesearch")
- assert_equal("top", node.name, "node name was not set")
-
- assert_equal(%w{one two three four five}.sort, node.classes.sort, "node classes were not set correctly with the top node")
- assert_equal({"base" => "true", "center" => "boo", "master" => "far"}, node.parameters, "node parameters were not set correctly with the top node")
- end
-end
-
-describe Puppet::Indirector.terminus(:node, :ldap), " when interacting with ldap" do
- confine "LDAP is not available" => Puppet.features.ldap?
- confine "No LDAP test data for networks other than Luke's" => Facter.value(:domain) == "madstop.com"
-
- def ldapconnect
-
- @ldap = LDAP::Conn.new("ldap", 389)
- @ldap.set_option( LDAP::LDAP_OPT_PROTOCOL_VERSION, 3 )
- @ldap.simple_bind("", "")
-
- return @ldap
- end
-
- def ldaphost(name)
- node = Puppet::Node.new(name)
- parent = nil
- found = false
- @ldap.search( "ou=hosts, dc=madstop, dc=com", 2,
- "(&(objectclass=puppetclient)(cn=%s))" % name
- ) do |entry|
- node.classes = entry.vals("puppetclass") || []
- node.parameters = entry.to_hash.inject({}) do |hash, ary|
- if ary[1].length == 1
- hash[ary[0]] = ary[1].shift
- else
- hash[ary[0]] = ary[1]
- end
- hash
- end
- parent = node.parameters["parentnode"]
- found = true
- end
- raise "Could not find node %s" % name unless found
-
- return node, parent
- end
-
- it "should have tests" do
- raise ArgumentError
- end
-
- def test_ldapsearch
- Puppet[:ldapbase] = "ou=hosts, dc=madstop, dc=com"
- Puppet[:ldapnodes] = true
-
- searcher = Object.new
- searcher.extend(Node.node_source(:ldap))
-
- ldapconnect()
-
- # Make sure we get nil and nil back when we search for something missing
- parent, classes, parameters = nil
- assert_nothing_raised do
- parent, classes, parameters = searcher.ldapsearch("nosuchhost")
- end
-
- assert_nil(parent, "Got a parent for a non-existent host")
- assert_nil(classes, "Got classes for a non-existent host")
-
- # Make sure we can find 'culain' in ldap
- assert_nothing_raised do
- parent, classes, parameters = searcher.ldapsearch("culain")
- end
-
- node, realparent = ldaphost("culain")
- assert_equal(realparent, parent, "did not get correct parent node from ldap")
- assert_equal(node.classes, classes, "did not get correct ldap classes from ldap")
- assert_equal(node.parameters, parameters, "did not get correct ldap parameters from ldap")
-
- # Now compare when we specify the attributes to get.
- Puppet[:ldapattrs] = "cn"
- assert_nothing_raised do
- parent, classes, parameters = searcher.ldapsearch("culain")
- end
- assert_equal(realparent, parent, "did not get correct parent node from ldap")
- assert_equal(node.classes, classes, "did not get correct ldap classes from ldap")
-
- list = %w{cn puppetclass parentnode dn}
- should = node.parameters.inject({}) { |h, a| h[a[0]] = a[1] if list.include?(a[0]); h }
- assert_equal(should, parameters, "did not get correct ldap parameters from ldap")
- end
-end
-
-describe Puppet::Indirector.terminus(:node, :ldap), " when connecting to ldap" do
- confine "Not running on culain as root" => (Puppet::Util::SUIDManager.uid == 0 and Facter.value("hostname") == "culain")
-
- it "should have tests" do
- raise ArgumentError
- end
-
- def test_ldapreconnect
- Puppet[:ldapbase] = "ou=hosts, dc=madstop, dc=com"
- Puppet[:ldapnodes] = true
-
- searcher = Object.new
- searcher.extend(Node.node_source(:ldap))
- hostname = "culain.madstop.com"
-
- # look for our host
- assert_nothing_raised {
- parent, classes = searcher.nodesearch(hostname)
- }
-
- # Now restart ldap
- system("/etc/init.d/slapd restart 2>/dev/null >/dev/null")
- sleep(1)
-
- # and look again
- assert_nothing_raised {
- parent, classes = searcher.nodesearch(hostname)
- }
-
- # Now stop ldap
- system("/etc/init.d/slapd stop 2>/dev/null >/dev/null")
- cleanup do
- system("/etc/init.d/slapd start 2>/dev/null >/dev/null")
- end
-
- # And make sure we actually fail here
- assert_raise(Puppet::Error) {
- parent, classes = searcher.nodesearch(hostname)
- }
- end
-end
diff --git a/spec/unit/indirector/node/none.rb b/spec/unit/indirector/node/none.rb
deleted file mode 100755
index 2329cdfbb..000000000
--- a/spec/unit/indirector/node/none.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env ruby
-
-require File.dirname(__FILE__) + '/../../../spec_helper'
-require 'puppet/indirector'
-require 'puppet/node/facts'
-
-describe Puppet::Indirector.terminus(:node, :none), " when searching for nodes" do
- before do
- Puppet.config[:node_source] = "none"
- @searcher = Puppet::Indirector.terminus(:node, :none).new
- end
-
- it "should create a node instance" do
- @searcher.find("yay").should be_instance_of(Puppet::Node)
- end
-
- it "should create a new node with the correct name" do
- @searcher.find("yay").name.should == "yay"
- end
-
- it "should merge the node's facts" do
- facts = Puppet::Node::Facts.new("yay", "one" => "two", "three" => "four")
- Puppet::Node::Facts.expects(:find).with("yay").returns(facts)
- node = @searcher.find("yay")
- node.parameters["one"].should == "two"
- node.parameters["three"].should == "four"
- end
-
- after do
- Puppet.config.clear
- end
-end
diff --git a/spec/unit/indirector/null.rb b/spec/unit/indirector/null.rb
new file mode 100755
index 000000000..9e1dcb07c
--- /dev/null
+++ b/spec/unit/indirector/null.rb
@@ -0,0 +1,27 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+require 'puppet/indirector/null'
+
+describe Puppet::Indirector::Null do
+ before do
+ Puppet::Indirector::Terminus.stubs(:register_terminus_class)
+ @model = mock 'model'
+ @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model
+ Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection)
+
+ @null_class = Class.new(Puppet::Indirector::Null) do
+ def self.to_s
+ "Testing"
+ end
+ end
+
+ @searcher = @null_class.new
+ end
+
+ it "should return return an instance of the indirected model" do
+ object = mock 'object'
+ @model.expects(:new).with("yay").returns object
+ @searcher.find("yay").should equal(object)
+ end
+end
diff --git a/spec/unit/indirector/null/node.rb b/spec/unit/indirector/null/node.rb
new file mode 100755
index 000000000..c589e5820
--- /dev/null
+++ b/spec/unit/indirector/null/node.rb
@@ -0,0 +1,18 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/indirector/null/node'
+
+describe Puppet::Indirector::Null::Node do
+ before do
+ @searcher = Puppet::Indirector::Null::Node.new
+ 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)
+ @searcher.find("mynode")
+ end
+end
diff --git a/spec/unit/indirector/terminus.rb b/spec/unit/indirector/terminus.rb
index 08e7e6ccb..dc86cf315 100755
--- a/spec/unit/indirector/terminus.rb
+++ b/spec/unit/indirector/terminus.rb
@@ -36,6 +36,10 @@ describe Puppet::Indirector::Terminus do
@terminus.should respond_to(:terminus_type)
end
+ it "should support a class-level model attribute" do
+ @terminus.should respond_to(:model)
+ end
+
it "should accept indirection instances as its indirection" do
indirection = stub 'indirection', :is_a? => true, :register_terminus_type => nil
proc { @terminus.indirection = indirection }.should_not raise_error
@@ -177,6 +181,11 @@ describe Puppet::Indirector::Terminus, " when creating terminus classes" do
it "should set the subclass's name to the indirection name" do
@terminus.name.should == :myindirection
end
+
+ it "should set the subclass's model to the indirection model" do
+ @indirection.expects(:model).returns :yay
+ @terminus.model.should == :yay
+ end
end
describe Puppet::Indirector::Terminus, " when a terminus instance" do
@@ -209,4 +218,9 @@ describe Puppet::Indirector::Terminus, " when a terminus instance" do
it "should set the instances's type to the abstract terminus type's name" do
@terminus.terminus_type.should == :abstract
end
+
+ it "should set the instances's model to the indirection's model" do
+ @indirection.expects(:model).returns :yay
+ @terminus.model.should == :yay
+ end
end