summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/facter/resolution.rb105
-rwxr-xr-xspec/unit/resolution.rb184
2 files changed, 217 insertions, 72 deletions
diff --git a/lib/facter/resolution.rb b/lib/facter/resolution.rb
index 35a931b..7fc1e24 100644
--- a/lib/facter/resolution.rb
+++ b/lib/facter/resolution.rb
@@ -18,54 +18,40 @@ class Facter::Resolution
# Execute a chunk of code.
def self.exec(code, interpreter = "/bin/sh")
- if interpreter == "/bin/sh"
- binary = code.split(/\s+/).shift
-
- if have_which
- path = nil
- if binary !~ /^\//
- path = %x{which #{binary} 2>/dev/null}.chomp
- if path == ""
- # we don't have the binary necessary
- return nil
- end
- else
- path = binary
- end
-
- unless FileTest.exists?(path)
- # our binary does not exist
- return nil
- end
- end
-
- out = nil
- begin
- out = %x{#{code}}.chomp
- rescue => detail
- $stderr.puts detail
- return nil
- end
- if out == ""
- return nil
+ raise ArgumentError, "non-sh interpreters are not currently supported" unless interpreter == "/bin/sh"
+ binary = code.split(/\s+/).shift
+
+ if have_which
+ path = nil
+ if binary !~ /^\//
+ path = %x{which #{binary} 2>/dev/null}.chomp
+ # we don't have the binary necessary
+ return nil if path == ""
else
- return out
+ path = binary
end
+
+ return nil unless FileTest.exists?(path)
+ end
+
+ out = nil
+ begin
+ out = %x{#{code}}.chomp
+ rescue => detail
+ $stderr.puts detail
+ return nil
+ end
+ if out == ""
+ return nil
else
- raise ArgumentError,
- "non-sh interpreters are not currently supported"
+ return out
end
end
# Add a new confine to the resolution mechanism.
- def confine(*args)
- if args[0].is_a? Hash
- args[0].each do |fact, values|
- @confines.push Facter::Confine.new(fact,*values)
- end
- else
- fact = args.shift
- @confines.push Facter::Confine.new(fact,*args)
+ def confine(confines)
+ confines.each do |fact, values|
+ @confines.push Facter::Confine.new(fact, *values)
end
end
@@ -97,56 +83,31 @@ class Facter::Resolution
# Set the name by which this parameter is known in LDAP. The default
# is just the fact name.
def setldapname(name)
- @fact.ldapname = name.to_s
+ fact.ldapname = name.to_s
end
# Is this resolution mechanism suitable on the system in question?
def suitable?
unless defined? @suitable
- @suitable = true
- if @confines.length == 0
- return true
- end
- @confines.each { |confine|
- unless confine.true?
- @suitable = false
- end
- }
+ @suitable = ! @confines.detect { |confine| ! confine.true? }
end
return @suitable
end
- # Set tags on our parent fact.
- def tag(*values)
- @fact.tag(*values)
- end
-
def to_s
return self.value()
end
# How we get a value for our resolution mechanism.
def value
- value = nil
-
if @code.is_a?(Proc)
- value = @code.call()
+ result = @code.call()
else
- unless defined? @interpreter
- @interpreter = "/bin/sh"
- end
- if @code.nil?
- $stderr.puts "Code for %s is nil" % @name
- else
- value = Facter::Resolution.exec(@code,@interpreter)
- end
- end
-
- if value == ""
- value = nil
+ result = Facter::Resolution.exec(@code,@interpreter)
end
- return value
+ return nil if result == ""
+ return result
end
end
diff --git a/spec/unit/resolution.rb b/spec/unit/resolution.rb
index 9cb0712..d4dc7e1 100755
--- a/spec/unit/resolution.rb
+++ b/spec/unit/resolution.rb
@@ -5,4 +5,188 @@ require File.dirname(__FILE__) + '/../spec_helper'
require 'facter/resolution'
describe Facter::Resolution do
+ it "should require a name" do
+ lambda { Facter::Resolution.new }.should raise_error(ArgumentError)
+ end
+
+ it "should have a name" do
+ Facter::Resolution.new("yay").name.should == "yay"
+ end
+
+ it "should have a method for setting the code" do
+ Facter::Resolution.new("yay").should respond_to(:setcode)
+ end
+
+ describe "when setting the code" do
+ before do
+ @resolve = Facter::Resolution.new("yay")
+ end
+
+ it "should default to /bin/sh as the interpreter if a string is provided" do
+ @resolve.setcode "foo"
+ @resolve.interpreter.should == "/bin/sh"
+ end
+
+ it "should set the code to any provided string" do
+ @resolve.setcode "foo"
+ @resolve.code.should == "foo"
+ end
+
+ it "should set the code to any provided block" do
+ block = lambda { }
+ @resolve.setcode(&block)
+ @resolve.code.should equal(block)
+ end
+
+ it "should prefer the string over a block" do
+ @resolve.setcode("foo") { }
+ @resolve.code.should == "foo"
+ end
+
+ it "should fail if neither a string nor block has been provided" do
+ lambda { @resolve.setcode }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "should be able to return a value" do
+ Facter::Resolution.new("yay").should respond_to(:value)
+ end
+
+ describe "when returning the value" do
+ before do
+ @resolve = Facter::Resolution.new("yay")
+ end
+
+ describe "and the code is a string" do
+ it "should return the result of executing the code with the interpreter" do
+ @resolve.setcode "/bin/foo"
+ Facter::Resolution.expects(:exec).with("/bin/foo", "/bin/sh").returns "yup"
+
+ @resolve.value.should == "yup"
+ end
+
+ it "should return nil if the value is an empty string" do
+ @resolve.setcode "/bin/foo"
+ Facter::Resolution.stubs(:exec).returns ""
+ @resolve.value.should be_nil
+ end
+ end
+
+ describe "and the code is a block" do
+ it "should return the value returned by the block" do
+ @resolve.setcode { "yayness" }
+ @resolve.value.should == "yayness"
+ end
+
+ it "should return nil if the value is an empty string" do
+ @resolve.setcode { "" }
+ @resolve.value.should be_nil
+ end
+ end
+ end
+
+ it "should return its value when converted to a string" do
+ @resolve = Facter::Resolution.new("yay")
+ @resolve.expects(:value).returns "myval"
+ @resolve.to_s.should == "myval"
+ end
+
+ it "should allow the adding of confines" do
+ Facter::Resolution.new("yay").should respond_to(:confine)
+ end
+
+ it "should provide a method for returning the number of confines" do
+ @resolve = Facter::Resolution.new("yay")
+ @resolve.confine "one" => "foo", "two" => "fee"
+ @resolve.length.should == 2
+ end
+
+ it "should return 0 confines when no confines have been added" do
+ Facter::Resolution.new("yay").length.should == 0
+ end
+
+ it "should have a method for determining if it is suitable" do
+ Facter::Resolution.new("yay").should respond_to(:suitable?)
+ end
+
+ describe "when adding confines" do
+ before do
+ @resolve = Facter::Resolution.new("yay")
+ end
+
+ it "should accept a hash of fact names and values" do
+ lambda { @resolve.confine :one => "two" }.should_not raise_error
+ end
+
+ it "should create a Confine instance for every argument in the provided hash" do
+ Facter::Confine.expects(:new).with("one", "foo")
+ Facter::Confine.expects(:new).with("two", "fee")
+
+ @resolve.confine "one" => "foo", "two" => "fee"
+ end
+
+ end
+
+ describe "when determining suitability" do
+ before do
+ @resolve = Facter::Resolution.new("yay")
+ end
+
+ it "should always be suitable if no confines have been added" do
+ @resolve.should be_suitable
+ end
+
+ it "should be unsuitable if any provided confines return false" do
+ confine1 = mock 'confine1', :true? => true
+ confine2 = mock 'confine2', :true? => false
+ Facter::Confine.expects(:new).times(2).returns(confine1).then.returns(confine2)
+ @resolve.confine :one => :two, :three => :four
+
+ @resolve.should_not be_suitable
+ end
+
+ it "should be suitable if all provided confines return true" do
+ confine1 = mock 'confine1', :true? => true
+ confine2 = mock 'confine2', :true? => true
+ Facter::Confine.expects(:new).times(2).returns(confine1).then.returns(confine2)
+ @resolve.confine :one => :two, :three => :four
+
+ @resolve.should be_suitable
+ end
+ end
+
+ it "should have a method for setting its ldap name" do
+ Facter::Resolution.new("yay").should respond_to(:setldapname)
+ end
+
+ it "should set the ldap name on the associated fact" do
+ @resolve = Facter::Resolution.new("yay")
+ fact = mock 'fact'
+ @resolve.stubs(:fact).returns fact
+
+ fact.expects(:ldapname=).with "ldapness"
+
+ @resolve.setldapname "ldapness"
+ end
+
+ it "should always convert the ldap name to a string" do
+ @resolve = Facter::Resolution.new("yay")
+ fact = mock 'fact'
+ @resolve.stubs(:fact).returns fact
+
+ fact.expects(:ldapname=).with "ldapness"
+
+ @resolve.setldapname :ldapness
+ end
+
+ it "should have a class method for executing code" do
+ Facter::Resolution.should respond_to(:exec)
+ end
+
+ # It's not possible, AFAICT, to mock %x{}, so I can't really test this bit.
+ describe "when executing code" do
+ it "should fail if any interpreter other than /bin/sh is requested" do
+ lambda { Facter::Resolution.exec("/something", "/bin/perl") }.should raise_error(ArgumentError)
+ end
+ end
end