summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuke Kanies <luke@madstop.com>2008-05-15 22:14:02 -0500
committerLuke Kanies <luke@madstop.com>2008-05-15 22:14:02 -0500
commitc9757a689a2ecdcd044ca0355e1ebfa30bb1978f (patch)
tree60f1dd0ccc8dd7968c44b5e5286f492a92bf67d5
parentac79a79c8d7713068243209f782bf16077dd3d37 (diff)
downloadpuppet-c9757a689a2ecdcd044ca0355e1ebfa30bb1978f.tar.gz
puppet-c9757a689a2ecdcd044ca0355e1ebfa30bb1978f.tar.xz
puppet-c9757a689a2ecdcd044ca0355e1ebfa30bb1978f.zip
Moving the 'confine' handling to separate classes.
-rw-r--r--lib/puppet/provider/confine.rb76
-rw-r--r--lib/puppet/provider/confiner.rb41
-rwxr-xr-xspec/unit/provider/confine.rb182
-rwxr-xr-xspec/unit/provider/confiner.rb106
4 files changed, 405 insertions, 0 deletions
diff --git a/lib/puppet/provider/confine.rb b/lib/puppet/provider/confine.rb
new file mode 100644
index 000000000..e55ba02ec
--- /dev/null
+++ b/lib/puppet/provider/confine.rb
@@ -0,0 +1,76 @@
+# The class that handles testing whether our providers
+# actually work or not.
+class Puppet::Provider::Confine
+ attr_reader :test, :values, :fact
+
+ def exists?(value)
+ value and FileTest.exist?(value)
+ end
+
+ # Are we a facter comparison?
+ def facter?
+ defined?(@facter)
+ end
+
+ # Retrieve the value from facter
+ def facter_value
+ unless defined?(@facter_value) and @facter_value
+ @facter_value = Facter.value(@fact).to_s.downcase
+ end
+ @facter_value
+ end
+
+ def false?(value)
+ ! value
+ end
+
+ def initialize(test, values)
+ values = [values] unless values.is_a?(Array)
+ @values = values
+
+ if %w{exists false true}.include?(test.to_s)
+ @test = test
+ @method = @test.to_s + "?"
+ else
+ @fact = test
+ @test = :facter
+ @method = "match?"
+ end
+ end
+
+ def match?(value)
+ facter_value == value.to_s.downcase
+ end
+
+ # Collect the results of all of them.
+ def result
+ values.collect { |value| send(@method, value) }
+ end
+
+ def true?(value)
+ # Double negate, so we only get true or false.
+ ! ! value
+ end
+
+ # Test whether our confine matches.
+ def valid?
+ values.each do |value|
+ unless send(@method, value)
+ msg = case test
+ when :false: "false value"
+ when :true: "true value"
+ when :exists: "file %s does not exist" % value
+ when :facter: "facter value '%s' for '%s' not in required list '%s'" % [value, @fact, values.join(",")]
+ end
+ Puppet.debug msg
+ return false
+ end
+ end
+
+ return true
+ ensure
+ # Reset the cache. We want to cache it during a given
+ # run, but across runs.
+ @facter_value = nil
+ end
+end
diff --git a/lib/puppet/provider/confiner.rb b/lib/puppet/provider/confiner.rb
new file mode 100644
index 000000000..b88f1f01b
--- /dev/null
+++ b/lib/puppet/provider/confiner.rb
@@ -0,0 +1,41 @@
+# Manage a collection of confines, returning a boolean or
+# helpful information.
+require 'puppet/provider/confine'
+
+class Puppet::Provider::Confiner
+ def confine(hash)
+ hash.each do |test, values|
+ @confines << Puppet::Provider::Confine.new(test, values)
+ end
+ end
+
+ def initialize
+ @confines = []
+ end
+
+ # Return a hash of the whole confine set, used for the Provider
+ # reference.
+ def result
+ defaults = {
+ :false => 0,
+ :true => 0,
+ :exists => [],
+ :facter => {}
+ }
+ missing = Hash.new { |hash, key| hash[key] = defaults[key] }
+ @confines.each do |confine|
+ case confine.test
+ when :false: missing[confine.test] += confine.result.find_all { |v| v == false }.length
+ when :true: missing[confine.test] += confine.result.find_all { |v| v == true }.length
+ when :exists: confine.result.zip(confine.values).each { |val, f| missing[:exists] << f unless val }
+ when :facter: missing[:facter][confine.fact] = confine.values if confine.result.include?(false)
+ end
+ end
+
+ missing
+ end
+
+ def valid?
+ ! @confines.detect { |c| ! c.valid? }
+ end
+end
diff --git a/spec/unit/provider/confine.rb b/spec/unit/provider/confine.rb
new file mode 100755
index 000000000..0e87ccdfb
--- /dev/null
+++ b/spec/unit/provider/confine.rb
@@ -0,0 +1,182 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/provider/confine'
+
+describe Puppet::Provider::Confine do
+ it "should require a test" do
+ lambda { Puppet::Provider::Confine.new }.should raise_error(ArgumentError)
+ end
+
+ it "should require a value" do
+ lambda { Puppet::Provider::Confine.new(:exists) }.should raise_error(ArgumentError)
+ end
+
+ it "should have a test" do
+ Puppet::Provider::Confine.new(:exists, "/some/file").test.should == :exists
+ end
+
+ it "should always convert values to an array" do
+ Puppet::Provider::Confine.new(:exists, "/some/file").values.should be_instance_of(Array)
+ end
+
+ it "should have an accessor for its fact" do
+ Puppet::Provider::Confine.new(:foo, :bar).should respond_to(:fact)
+ end
+
+ describe "when testing values" do
+ before { @confine = Puppet::Provider::Confine.new("eh", "foo") }
+
+ describe "and the test is 'false'" do
+ it "should use the 'false?' method to test validity" do
+ @confine = Puppet::Provider::Confine.new(:false, "foo")
+ @confine.expects(:false?).with("foo")
+ @confine.valid?
+ end
+
+ it "should return true if the value is false" do
+ @confine.false?(false).should be_true
+ end
+
+ it "should return false if the value is not false" do
+ @confine.false?("else").should be_false
+ end
+
+ it "should log that a value is false" do
+ @confine = Puppet::Provider::Confine.new(:false, "foo")
+ Puppet.expects(:debug).with { |l| l.include?("false") }
+ @confine.valid?
+ end
+ end
+
+ describe "and the test is 'true'" do
+ it "should use the 'true?' method to test validity" do
+ @confine = Puppet::Provider::Confine.new(:true, "foo")
+ @confine.expects(:true?).with("foo")
+ @confine.valid?
+ end
+
+ it "should return true if the value is not false" do
+ @confine.true?("else").should be_true
+ end
+
+ it "should return false if the value is false" do
+ @confine.true?(nil).should be_false
+ end
+ end
+
+ describe "and the test is 'exists'" do
+ it "should use the 'exists?' method to test validity" do
+ @confine = Puppet::Provider::Confine.new(:exists, "foo")
+ @confine.expects(:exists?).with("foo")
+ @confine.valid?
+ end
+
+ it "should return false if the value is false" do
+ @confine.exists?(false).should be_false
+ end
+
+ it "should return false if the value does not point to a file" do
+ FileTest.expects(:exist?).with("/my/file").returns false
+ @confine.exists?("/my/file").should be_false
+ end
+
+ it "should return true if the value points to a file" do
+ FileTest.expects(:exist?).with("/my/file").returns true
+ @confine.exists?("/my/file").should be_true
+ end
+
+ it "should log that a value is true" do
+ @confine = Puppet::Provider::Confine.new(:true, nil)
+ Puppet.expects(:debug).with { |l| l.include?("true") }
+ @confine.valid?
+ end
+ end
+
+ describe "and the test is not 'true', 'false', or 'exists'" do
+ it "should use the 'match?' method to test validity" do
+ @confine = Puppet::Provider::Confine.new("yay", "foo")
+ @confine.expects(:match?).with("foo")
+ @confine.valid?
+ end
+
+ it "should return true if the value matches the facter value" do
+ Facter.expects(:value).returns("foo")
+
+ @confine.match?("foo").should be_true
+ end
+
+ it "should return false if the value does not match the facter value" do
+ Facter.expects(:value).returns("boo")
+
+ @confine.match?("foo").should be_false
+ end
+
+ it "should be case insensitive" do
+ Facter.expects(:value).returns("FOO")
+
+ @confine.match?("foo").should be_true
+ end
+
+ it "should not care whether the value is a string or symbol" do
+ Facter.expects(:value).returns("FOO")
+
+ @confine.match?(:foo).should be_true
+ end
+
+ it "should cache the fact during testing" do
+ Facter.expects(:value).once.returns("FOO")
+
+ @confine.match?(:foo)
+ @confine.match?(:foo)
+ end
+
+ it "should log that the fact value is not correct" do
+ @confine = Puppet::Provider::Confine.new("foo", ["bar", "bee"])
+ Facter.expects(:value).with("foo").returns "yayness"
+ Puppet.expects(:debug).with { |l| l.include?("facter") and l.include?("bar,bee") }
+ @confine.valid?
+ end
+ end
+ end
+
+ describe "when testing all values" do
+ before { @confine = Puppet::Provider::Confine.new(:true, %w{a b c}) }
+
+ it "should be invalid if any values fail" do
+ @confine.stubs(:true?).returns true
+ @confine.expects(:true?).with("b").returns false
+ @confine.should_not be_valid
+ end
+
+ it "should be valid if all values pass" do
+ @confine.stubs(:true?).returns true
+ @confine.should be_valid
+ end
+
+ it "should short-cut at the first failing value" do
+ @confine.expects(:true?).once.returns false
+ @confine.valid?
+ end
+
+ it "should remove the cached facter value" do
+ @confine = Puppet::Provider::Confine.new(:foo, :bar)
+ Facter.expects(:value).with(:foo).times(2).returns "eh"
+ @confine.valid?
+ @confine.valid?
+ end
+ end
+
+ describe "when testing the result of the values" do
+ before { @confine = Puppet::Provider::Confine.new(:true, %w{a b c d}) }
+
+ it "should return an array with the result of the test for each value" do
+ @confine.stubs(:true?).returns true
+ @confine.expects(:true?).with("b").returns false
+ @confine.expects(:true?).with("d").returns false
+
+ @confine.result.should == [true, false, true, false]
+ end
+ end
+end
diff --git a/spec/unit/provider/confiner.rb b/spec/unit/provider/confiner.rb
new file mode 100755
index 000000000..b80255c95
--- /dev/null
+++ b/spec/unit/provider/confiner.rb
@@ -0,0 +1,106 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/provider/confiner'
+
+describe Puppet::Provider::Confiner do
+ it "should be able to add confines" do
+ Puppet::Provider::Confiner.new.should respond_to(:confine)
+ end
+
+ it "should create a Confine instance for every confine call" do
+ Puppet::Provider::Confine.expects(:new).with(:foo, :bar).returns "eh"
+ Puppet::Provider::Confine.expects(:new).with(:baz, :bee).returns "eh"
+ Puppet::Provider::Confiner.new.confine :foo => :bar, :baz => :bee
+ end
+
+ it "should be valid if no confines are present" do
+ Puppet::Provider::Confiner.new.should be_valid
+ end
+
+ it "should be valid if all confines are valid" do
+ c1 = mock 'c1', :valid? => true
+ c2 = mock 'c2', :valid? => true
+
+ Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2)
+
+ confiner = Puppet::Provider::Confiner.new
+ confiner.confine :foo => :bar, :baz => :bee
+
+ confiner.should be_valid
+ end
+
+ it "should not be valid if any confines are valid" do
+ c1 = mock 'c1', :valid? => true
+ c2 = mock 'c2', :valid? => false
+
+ Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2)
+
+ confiner = Puppet::Provider::Confiner.new
+ confiner.confine :foo => :bar, :baz => :bee
+
+ confiner.should_not be_valid
+ end
+
+ describe "when providing a complete result" do
+ before do
+ @confiner = Puppet::Provider::Confiner.new
+ end
+
+ it "should return a hash" do
+ @confiner.result.should be_instance_of(Hash)
+ end
+
+ it "should return an empty hash if the confiner is valid" do
+ @confiner.result.should == {}
+ end
+
+ it "should contain the number of incorrectly false values" do
+ c1 = stub 'c1', :result => [true, false, true], :test => :true
+ c2 = stub 'c2', :result => [false, true, false], :test => :true
+
+ Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2)
+
+ confiner = Puppet::Provider::Confiner.new
+ confiner.confine :foo => :bar, :baz => :bee
+
+ confiner.result[:true].should == 3
+ end
+
+ it "should contain the number of incorrectly true values" do
+ c1 = stub 'c1', :result => [true, false, true], :test => :false
+ c2 = stub 'c2', :result => [false, true, false], :test => :false
+
+ Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2)
+
+ confiner = Puppet::Provider::Confiner.new
+ confiner.confine :foo => :bar, :baz => :bee
+
+ confiner.result[:false].should == 3
+ end
+
+ it "should contain the missing files" do
+ FileTest.stubs(:exist?).returns true
+ FileTest.expects(:exist?).with("/two").returns false
+ FileTest.expects(:exist?).with("/four").returns false
+
+ confiner = Puppet::Provider::Confiner.new
+ confiner.confine :exists => %w{/one /two}
+ confiner.confine :exists => %w{/three /four}
+
+ confiner.result[:exists].should == %w{/two /four}
+ end
+
+ it "should contain a hash of facts and the allowed values" do
+ Facter.expects(:value).with(:foo).returns "yay"
+ Facter.expects(:value).with(:bar).returns "boo"
+ confiner = Puppet::Provider::Confiner.new
+ confiner.confine :foo => "yes", :bar => "boo"
+
+ result = confiner.result
+ result[:facter][:foo].should == %w{yes}
+ result[:facter][:bar].should be_nil
+ end
+ end
+end