summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/puppet/indirector/indirection.rb96
-rwxr-xr-xspec/unit/indirector/indirection.rb927
2 files changed, 562 insertions, 461 deletions
diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb
index d47433c60..91cc42b17 100644
--- a/lib/puppet/indirector/indirection.rb
+++ b/lib/puppet/indirector/indirection.rb
@@ -1,4 +1,5 @@
require 'puppet/util/docs'
+require 'puppet/indirector/envelope'
# The class that connects functional classes with their different collection
# back-ends. Each indirection has a set of associated terminus classes,
@@ -28,8 +29,7 @@ class Puppet::Indirector::Indirection
# Find an indirected model by name. This is provided so that Terminus classes
# can specifically hook up with the indirections they are associated with.
def self.model(name)
- match = @@indirections.find { |i| i.name == name }
- return nil unless match
+ return nil unless match = @@indirections.find { |i| i.name == name }
match.model
end
@@ -65,6 +65,25 @@ class Puppet::Indirector::Indirection
@@indirections.delete(self) if @@indirections.include?(self)
end
+ # Set the time-to-live for instances created through this indirection.
+ def ttl=(value)
+ raise ArgumentError, "Indirection TTL must be an integer" unless value.is_a?(Fixnum)
+ @ttl = value
+ end
+
+ # Default to the runinterval for the ttl.
+ def ttl
+ unless defined?(@ttl)
+ @ttl = Puppet[:runinterval].to_i
+ end
+ @ttl
+ end
+
+ # Calculate the expiration date for a returned instance.
+ def expiration
+ Time.now + ttl
+ end
+
# Generate the full doc string.
def doc
text = ""
@@ -106,6 +125,12 @@ class Puppet::Indirector::Indirection
end
end
+ # Set the options that can be passed on to the terminus instances.
+ attr_reader :option_struct
+ def options=(options)
+ @option_struct = Struct.new(*options)
+ end
+
# Return the singleton terminus for this indirection.
def terminus(terminus_name = nil)
# Get the name of the terminus.
@@ -161,29 +186,41 @@ class Puppet::Indirector::Indirection
check_authorization(:find, terminus_name, ([key] + args))
# See if our instance is in the cache and up to date.
- if cache? and cache.has_most_recent?(key, terminus(terminus_name).version(key))
- Puppet.debug "Using cached %s %s" % [self.name, key]
- return cache.find(key, *args)
+ if cache? and cached = cache.find(key, *args)
+ if cached.expired?
+ Puppet.info "Cached %s %s expired at %s; not using" % [self.name, key, cached.expiration]
+ else
+ Puppet.debug "Using cached %s %s" % [self.name, key]
+ return cached
+ end
end
# Otherwise, return the result from the terminus, caching if appropriate.
if result = terminus(terminus_name).find(key, *args)
- result.version ||= Time.now.utc
+ # Include the envelope module, so we can set the expiration.
+ result.extend(Puppet::Indirector::Envelope)
+ result.expiration ||= self.expiration
if cache?
Puppet.info "Caching %s %s" % [self.name, key]
cache.save(result, *args)
end
- terminus(terminus_name).post_find(result) if terminus(terminus_name).respond_to?(:post_find)
-
return result
end
+
+ return nil
end
- def destroy(*args)
- check_authorization(:destroy, terminus_class, args)
+ def destroy(key, *args)
+ check_authorization(:destroy, terminus_class, ([key] + args))
+
+ terminus.destroy(key, *args)
+
+ if cache? and cached = cache.find(key, *args)
+ cache.destroy(key, *args)
+ end
- terminus.destroy(*args)
+ nil
end
def search(*args)
@@ -191,21 +228,22 @@ class Puppet::Indirector::Indirection
result = terminus.search(*args)
- terminus().post_search(result) if terminus().respond_to?(:post_search)
-
result
end
# these become instance methods
def save(instance, *args)
- check_authorization(:save, terminus_class, ([instance] + args))
+ if respond_to?(:select_terminus)
+ terminus_name = select_terminus(instance.name, *args)
+ else
+ terminus_name = terminus_class
+ end
- instance.version ||= Time.now.utc
- dest = cache? ? cache : terminus
- return if dest.has_most_recent?(instance.name, instance.version)
- Puppet.info "Caching %s %s" % [self.name, instance.name] if cache?
+ check_authorization(:save, terminus_name, ([instance] + args))
+
+ # If caching is enabled, save our document there, do
cache.save(instance, *args) if cache?
- terminus.save(instance, *args)
+ terminus(terminus_class).save(instance, *args)
end
def version(*args)
@@ -226,6 +264,20 @@ class Puppet::Indirector::Indirection
end
end
+ # Handle a given indirected call.
+ def prepare_call(method, arguments)
+ raise ArgumentError, "Options must be a hash" unless arguments.is_a?(Hash)
+
+ # Set any terminus options.
+ options = option_struct ? set_options(option_struct, arguments) : nil
+
+ tclass = choose_terminus(options)
+
+ check_authorization(method, tclass, options)
+
+ return terminus(tclass), options
+ end
+
# Create a new terminus instance.
def make_terminus(terminus_class)
# Load our terminus class.
@@ -234,4 +286,10 @@ class Puppet::Indirector::Indirection
end
return klass.new
end
+
+ # Create a struct instance with all of the appropriate options set
+ # from the provided hash.
+ def set_options(struct, arguments)
+ struct.new(struct.members.inject([]) { |array, param| arguments[param.to_sym]; array } )
+ end
end
diff --git a/spec/unit/indirector/indirection.rb b/spec/unit/indirector/indirection.rb
index fe456b61e..5338a4858 100755
--- a/spec/unit/indirector/indirection.rb
+++ b/spec/unit/indirector/indirection.rb
@@ -2,579 +2,622 @@
require File.dirname(__FILE__) + '/../../spec_helper'
-require 'puppet/indirector'
+require 'puppet/indirector/indirection'
+describe Puppet::Indirector::Indirection do
+ describe "when initializing" do
+ # (LAK) I've no idea how to test this, really.
+ it "should store a reference to itself before it consumes its options" do
+ proc { @indirection = Puppet::Indirector::Indirection.new(Object.new, :testingness, :not_valid_option) }.should raise_error
+ Puppet::Indirector::Indirection.instance(:testingness).should be_instance_of(Puppet::Indirector::Indirection)
+ Puppet::Indirector::Indirection.instance(:testingness).delete
+ end
+ 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
-describe Puppet::Indirector::Indirection, " when initializing" do
- # (LAK) I've no idea how to test this, really.
- it "should store a reference to itself before it consumes its options" do
- proc { @indirection = Puppet::Indirector::Indirection.new(Object.new, :testingness, :not_valid_option) }.should raise_error
- Puppet::Indirector::Indirection.instance(:testingness).should be_instance_of(Puppet::Indirector::Indirection)
- Puppet::Indirector::Indirection.instance(:testingness).delete
- end
+ it "should set the name" do
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :myind)
+ @indirection.name.should == :myind
+ end
- 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 require indirections to have unique names" do
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
+ proc { Puppet::Indirector::Indirection.new(:test) }.should raise_error(ArgumentError)
+ end
- it "should set the name" do
- @indirection = Puppet::Indirector::Indirection.new(mock('model'), :myind)
- @indirection.name.should == :myind
- end
+ it "should extend itself with any specified module" do
+ mod = Module.new
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test, :extend => mod)
+ @indirection.metaclass.included_modules.should include(mod)
+ end
- it "should require indirections to have unique names" do
- @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
- proc { Puppet::Indirector::Indirection.new(:test) }.should raise_error(ArgumentError)
- end
+ it "should allow specification of supported options" do
+ lambda { @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test, :options => [:one, :two]) }.should_not raise_error
+ end
- it "should extend itself with any specified module" do
- mod = Module.new
- @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test, :extend => mod)
- @indirection.metaclass.included_modules.should include(mod)
- end
+ it "should define a new Struct class to support the specified options" do
+ struct = mock 'struct'
+ Struct.expects(:new).with(:one, :two)
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test, :options => [:one, :two])
+ end
- after do
- @indirection.delete if defined? @indirection
+ after do
+ @indirection.delete if defined? @indirection
+ end
end
-end
-describe Puppet::Indirector::Indirection do
- before :each do
- @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
- @terminus = stub 'terminus', :has_most_recent? => false
- @indirection.stubs(:terminus).returns(@terminus)
- @indirection.stubs(:terminus_class).returns(:whatever)
- @instance = stub 'instance', :version => nil, :version= => nil, :name => "whatever"
- @name = :mything
- end
-
- describe Puppet::Indirector::Indirection, " when looking for a model instance" do
+ describe "when an instance" do
+ before :each do
+ @terminus_class = mock 'terminus_class'
+ @terminus = mock 'terminus'
+ @terminus_class.stubs(:new).returns(@terminus)
+ @cache = mock 'cache'
+ @cache_class = mock 'cache_class'
+ Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :cache_terminus).returns(@cache_class)
+ Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :test_terminus).returns(@terminus_class)
- it "should let the appropriate terminus perform the lookup" do
- @terminus.expects(:find).with(@name).returns(@instance)
- @indirection.find(@name).should == @instance
- end
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
+ @indirection.terminus_class = :test_terminus
- it "should not attempt to set a timestamp if the terminus cannot find the instance" do
- @terminus.expects(:find).with(@name).returns(nil)
- proc { @indirection.find(@name) }.should_not raise_error
+ @instance = stub 'instance', :expiration => nil, :expiration= => nil, :name => "whatever"
+ @name = :mything
end
- it "should pass the instance to the :post_find hook if there is one" do
- class << @terminus
- def post_find
- end
- end
- @terminus.expects(:post_find).with(@instance)
- @terminus.expects(:find).with(@name).returns(@instance)
- @indirection.find(@name)
+ it "should allow setting the ttl" do
+ @indirection.ttl = 300
+ @indirection.ttl.should == 300
end
- end
-
- describe Puppet::Indirector::Indirection, " when removing a model instance" do
- it "should let the appropriate terminus remove the instance" do
- @terminus.expects(:destroy).with(@name).returns(@instance)
- @indirection.destroy(@name).should == @instance
+ it "should default to the :runinterval setting, converted to an integer, for its ttl" do
+ Puppet.settings.expects(:value).returns "1800"
+ @indirection.ttl.should == 1800
end
- end
-
- describe Puppet::Indirector::Indirection, " when searching for multiple model instances" do
- it "should let the appropriate terminus find the matching instances" do
- @terminus.expects(:search).with(@name).returns(@instance)
- @indirection.search(@name).should == @instance
+ it "should calculate the current expiration by adding the TTL to the current time" do
+ @indirection.stubs(:ttl).returns(100)
+ now = Time.now
+ Time.stubs(:now).returns now
+ @indirection.expiration.should == (Time.now + 100)
end
+
+ describe "and looking for a model instance" do
+ it "should let the appropriate terminus perform the lookup" do
+ @terminus.expects(:find).with(@name).returns(@instance)
+ @indirection.find(@name).should == @instance
+ end
- it "should pass the instances to the :post_search hook if there is one" do
- class << @terminus
- def post_search
- end
+ it "should return nil if nothing is returned by the terminus" do
+ @terminus.expects(:find).with(@name).returns(nil)
+ @indirection.find(@name).should be_nil
end
- @terminus.expects(:post_search).with(@instance)
- @terminus.expects(:search).with(@name).returns(@instance)
- @indirection.search(@name)
- end
- end
- describe Puppet::Indirector::Indirection, " when storing a model instance" do
+ it "should extend any found instance with the Envelope module" do
+ @terminus.stubs(:find).returns(@instance)
- it "should let the appropriate terminus store the instance" do
- @terminus.expects(:save).with(@instance).returns(@instance)
- @indirection.save(@instance).should == @instance
- end
- end
-
- describe Puppet::Indirector::Indirection, " when handling instance versions" do
+ @instance.expects(:extend).with(Puppet::Indirector::Envelope)
+ @indirection.find(@name)
+ end
- it "should let the appropriate terminus perform the lookup" do
- @terminus.expects(:version).with(@name).returns(5)
- @indirection.version(@name).should == 5
- end
+ it "should set the expiration date on any instances without one set" do
+ # Otherwise, our stub method doesn't get used, so the tests fail.
+ @instance.stubs(:extend)
+ @terminus.stubs(:find).returns(@instance)
- it "should add versions to found instances that do not already have them" do
- @terminus.expects(:find).with(@name).returns(@instance)
- time = mock 'time'
- time.expects(:utc).returns(:mystamp)
- Time.expects(:now).returns(time)
- @instance.expects(:version=).with(:mystamp)
- @indirection.find(@name)
- end
+ @indirection.expects(:expiration).returns :yay
- it "should add versions to saved instances that do not already have them" do
- time = mock 'time'
- time.expects(:utc).returns(:mystamp)
- Time.expects(:now).returns(time)
- @instance.expects(:version=).with(:mystamp)
- @terminus.stubs(:save)
- @indirection.save(@instance)
- end
+ @instance.expects(:expiration).returns(nil)
+ @instance.expects(:expiration=).with(:yay)
- # We've already tested this, basically, but...
- it "should use the current time in UTC for versions" do
- @instance.expects(:version=).with do |time|
- time.utc?
+ @indirection.find(@name)
end
- @terminus.stubs(:save)
- @indirection.save(@instance)
- end
- end
+ it "should not override an already-set expiration date on returned instances" do
+ # Otherwise, our stub method doesn't get used, so the tests fail.
+ @instance.stubs(:extend)
+ @terminus.stubs(:find).returns(@instance)
- describe Puppet::Indirector::Indirection, " when an authorization hook is present" do
+ @indirection.expects(:expiration).never
- before do
- # So the :respond_to? turns out right.
- class << @terminus
- def authorized?
- end
+ @instance.expects(:expiration).returns(:yay)
+ @instance.expects(:expiration=).never
+
+ @indirection.find(@name)
end
- end
- it "should not check authorization if a node name is not provided" do
- @terminus.expects(:authorized?).never
- @terminus.stubs(:find)
- @indirection.find("/my/key")
- end
+ describe "when caching is enabled" do
+ before do
+ @indirection.cache_class = :cache_terminus
+ @cache_class.expects(:new).returns(@cache)
- it "should fail while finding instances if authorization returns false" do
- @terminus.expects(:authorized?).with(:find, "/my/key", :node => "mynode").returns(false)
- @terminus.stubs(:find)
- proc { @indirection.find("/my/key", :node => "mynode") }.should raise_error(ArgumentError)
- end
+ @instance.stubs(:expired?).returns false
+ end
- it "should continue finding instances if authorization returns true" do
- @terminus.expects(:authorized?).with(:find, "/my/key", :node => "mynode").returns(true)
- @terminus.stubs(:find)
- @indirection.find("/my/key", :node => "mynode")
- end
+ it "should first look in the cache for an instance" do
+ @terminus.expects(:find).never
+ @cache.expects(:find).with(@name).returns @instance
- it "should fail while saving instances if authorization returns false" do
- @terminus.expects(:authorized?).with(:save, :myinstance, :node => "mynode").returns(false)
- @terminus.stubs(:save)
- proc { @indirection.save(:myinstance, :node => "mynode") }.should raise_error(ArgumentError)
- end
+ @indirection.find(@name)
+ end
- it "should continue saving instances if authorization returns true" do
- instance = stub 'instance', :version => 1.0, :name => "eh"
- @terminus.expects(:authorized?).with(:save, instance, :node => "mynode").returns(true)
- @terminus.stubs(:save)
- @indirection.save(instance, :node => "mynode")
- end
+ it "should return the cached object if it is not expired" do
+ @instance.stubs(:expired?).returns false
- it "should fail while destroying instances if authorization returns false" do
- @terminus.expects(:authorized?).with(:destroy, "/my/key", :node => "mynode").returns(false)
- @terminus.stubs(:destroy)
- proc { @indirection.destroy("/my/key", :node => "mynode") }.should raise_error(ArgumentError)
- end
+ @cache.stubs(:find).returns @instance
+ @indirection.find(@name).should equal(@instance)
+ end
- it "should continue destroying instances if authorization returns true" do
- instance = stub 'instance', :version => 1.0, :name => "eh"
- @terminus.expects(:authorized?).with(:destroy, instance, :node => "mynode").returns(true)
- @terminus.stubs(:destroy)
- @indirection.destroy(instance, :node => "mynode")
- end
+ it "should send a debug log if it is using the cached object" do
+ Puppet.expects(:debug)
+ @cache.stubs(:find).returns @instance
- it "should fail while searching for instances if authorization returns false" do
- @terminus.expects(:authorized?).with(:search, "/my/key", :node => "mynode").returns(false)
- @terminus.stubs(:search)
- proc { @indirection.search("/my/key", :node => "mynode") }.should raise_error(ArgumentError)
- end
+ @indirection.find(@name)
+ end
- it "should continue searching for instances if authorization returns true" do
- @terminus.expects(:authorized?).with(:search, "/my/key", :node => "mynode").returns(true)
- @terminus.stubs(:search)
- @indirection.search("/my/key", :node => "mynode")
- end
- end
+ it "should not return the cached object if it is expired" do
+ @instance.stubs(:expired?).returns true
- after :each do
- @indirection.delete
- Puppet::Indirector::Indirection.clear_cache
- end
-end
+ @cache.stubs(:find).returns @instance
+ @terminus.stubs(:find).returns nil
+ @indirection.find(@name).should be_nil
+ end
+ it "should send an info log if it is using the cached object" do
+ Puppet.expects(:info)
+ @instance.stubs(:expired?).returns true
-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(mock('model'), :test)
- Puppet::Indirector::Indirection.instance(:test).should equal(@indirection)
- end
-
- it "should return nil when the named indirection has not been created" do
- Puppet::Indirector::Indirection.instance(:test).should be_nil
- end
+ @cache.stubs(:find).returns @instance
+ @terminus.stubs(:find).returns nil
+ @indirection.find(@name)
+ end
- it "should allow an indirection's model to be retrieved by name" do
- mock_model = mock('model')
- @indirection = Puppet::Indirector::Indirection.new(mock_model, :test)
- Puppet::Indirector::Indirection.model(:test).should equal(mock_model)
- end
-
- it "should return nil when no model matches the requested name" do
- Puppet::Indirector::Indirection.model(:test).should be_nil
- end
+ it "should cache any objects not retrieved from the cache" do
+ @cache.expects(:find).with(@name).returns nil
- after do
- @indirection.delete if defined? @indirection
- end
-end
+ @terminus.expects(:find).with(@name).returns(@instance)
+ @cache.expects(:save).with(@instance)
-describe Puppet::Indirector::Indirection, " when choosing the terminus class" do
- before do
- @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
- @terminus = mock 'terminus'
- @terminus_class = stub 'terminus class', :new => @terminus
- Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :default).returns(@terminus_class)
- end
+ @indirection.find(@name)
+ end
- it "should choose the default terminus class if one is specified and no specific terminus class is provided" do
- @indirection.terminus_class = :default
- @indirection.terminus_class.should equal(:default)
- end
+ it "should send an info log that the object is being cached" do
+ @cache.stubs(:find).returns nil
- it "should use the provided Puppet setting if told to do so" do
- Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :my_terminus).returns(mock("terminus_class2"))
- Puppet.settings.expects(:value).with(:my_setting).returns("my_terminus")
- @indirection.terminus_setting = :my_setting
- @indirection.terminus_class.should equal(:my_terminus)
- end
+ @terminus.stubs(:find).returns(@instance)
+ @cache.stubs(:save)
- it "should fail if the provided terminus class is not valid" do
- Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :nosuchclass).returns(nil)
- proc { @indirection.terminus_class = :nosuchclass }.should raise_error(ArgumentError)
- end
-
- it "should fail if no terminus class is picked" do
- proc { @indirection.terminus_class }.should raise_error(Puppet::DevError)
- end
+ Puppet.expects(:info)
- after do
- @indirection.delete if defined? @indirection
- end
-end
+ @indirection.find(@name)
+ end
+ end
+ end
-describe Puppet::Indirector::Indirection, " when specifying the terminus class to use" do
- before do
- @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
- @terminus = mock 'terminus'
- @terminus_class = stub 'terminus class', :new => @terminus
- end
+ describe "and storing a model instance" do
+ it "should let the appropriate terminus store the instance" do
+ @terminus.expects(:save).with(@instance).returns(@instance)
+ @indirection.save(@instance).should == @instance
+ end
- it "should allow specification of a terminus type" do
- @indirection.should respond_to(:terminus_class=)
- end
+ it "should check authorization" do
+ @indirection.expects(:check_authorization).with(:save, :test_terminus, [@instance])
+ @terminus.stubs(:save)
- it "should fail to redirect if no terminus type has been specified" do
- proc { @indirection.find("blah") }.should raise_error(Puppet::DevError)
- end
+ @indirection.save(@instance)
+ end
- it "should fail when the terminus class name is an empty string" do
- proc { @indirection.terminus_class = "" }.should raise_error(ArgumentError)
- end
+ describe "when caching is enabled" do
+ before do
+ @indirection.cache_class = :cache_terminus
+ @cache_class.expects(:new).returns(@cache)
- it "should fail when the terminus class name is nil" do
- proc { @indirection.terminus_class = nil }.should raise_error(ArgumentError)
- end
+ @instance.stubs(:expired?).returns false
+ end
- it "should fail when the specified terminus class cannot be found" do
- Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(nil)
- proc { @indirection.terminus_class = :foo }.should raise_error(ArgumentError)
- end
+ it "should save the object to the cache" do
+ @cache.expects(:save).with(@instance)
+ @terminus.stubs(:save)
+ @indirection.save(@instance)
+ end
+ end
+ end
+
+ describe "and removing a model instance" do
+ it "should delegate the instance removal to the appropriate terminus" do
+ @terminus.expects(:destroy).with(@name)
+ @indirection.destroy(@name)
+ end
- it "should select the specified terminus class if a terminus class name is provided" do
- Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(@terminus_class)
- @indirection.terminus(:foo).should equal(@terminus)
- end
+ it "should return nil" do
+ @terminus.stubs(:destroy)
+ @indirection.destroy(@name).should be_nil
+ end
- it "should use the configured terminus class if no terminus name is specified" do
- Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class)
- @indirection.terminus_class = :foo
- @indirection.terminus().should equal(@terminus)
- end
+ describe "when caching is enabled" do
+ before do
+ @indirection.cache_class = :cache_terminus
+ @cache_class.expects(:new).returns(@cache)
- after do
- @indirection.delete if defined? @indirection
- end
-end
+ @instance.stubs(:expired?).returns false
+ end
+
+ it "should destroy any found object in the cache" do
+ cached = mock 'cache'
+ @cache.expects(:find).with(@name).returns cached
+ @cache.expects(:destroy).with(@name)
+ @terminus.stubs(:destroy)
-describe Puppet::Indirector::Indirection, " when a select_terminus hook is available" do
- before do
- @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
+ @indirection.destroy(@name)
+ end
+ end
+ end
- # And provide a select_terminus hook
- @indirection.meta_def(:select_terminus) do |uri|
- :other
+ describe "and searching for multiple model instances" do
+ it "should let the appropriate terminus find the matching instances" do
+ @terminus.expects(:search).with(@name).returns(@instance)
+ @indirection.search(@name).should == @instance
+ end
end
- @terminus = mock 'terminus'
- @terminus_class = stub 'terminus class', :new => @terminus
+ describe "and an authorization hook is present" do
+ before do
+ # So the :respond_to? turns out correctly.
+ class << @terminus
+ def authorized?
+ end
+ end
+ end
- @other_terminus = mock 'other_terminus'
- @other_terminus_class = stub 'other_terminus_class', :new => @other_terminus
+ it "should not check authorization if a node name is not provided" do
+ @terminus.expects(:authorized?).never
+ @terminus.stubs(:find)
+ @indirection.find("/my/key")
+ end
- @cache_terminus = mock 'cache_terminus'
- @cache_terminus_class = stub 'cache_terminus_class', :new => @cache_terminus
+ it "should fail while finding instances if authorization returns false" do
+ @terminus.expects(:authorized?).with(:find, "/my/key", :node => "mynode").returns(false)
+ @terminus.stubs(:find)
+ proc { @indirection.find("/my/key", :node => "mynode") }.should raise_error(ArgumentError)
+ end
- Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class)
- Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :other).returns(@other_terminus_class)
- Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :cache).returns(@cache_terminus_class)
+ it "should continue finding instances if authorization returns true" do
+ @terminus.expects(:authorized?).with(:find, "/my/key", :node => "mynode").returns(true)
+ @terminus.stubs(:find)
+ @indirection.find("/my/key", :node => "mynode")
+ end
- # Set it to a default type.
- @indirection.terminus_class = :foo
+ it "should fail while saving instances if authorization returns false" do
+ @terminus.expects(:authorized?).with(:save, @instance, :node => "mynode").returns(false)
+ @terminus.stubs(:save)
+ proc { @indirection.save(@instance, :node => "mynode") }.should raise_error(ArgumentError)
+ end
- @uri = "full://url/path"
- @result = stub 'result', :version => 1.0
- end
+ it "should continue saving instances if authorization returns true" do
+ @terminus.expects(:authorized?).with(:save, @instance, :node => "mynode").returns(true)
+ @terminus.stubs(:save)
+ @indirection.save(@instance, :node => "mynode")
+ end
- it "should use the terminus name provided by passing the key to the :select_terminus hook when finding instances" do
- # Set up the expectation
- @other_terminus.expects(:find).with(@uri).returns(@result)
+ it "should fail while destroying instances if authorization returns false" do
+ @terminus.expects(:authorized?).with(:destroy, "/my/key", :node => "mynode").returns(false)
+ @terminus.stubs(:destroy)
+ proc { @indirection.destroy("/my/key", :node => "mynode") }.should raise_error(ArgumentError)
+ end
+
+ it "should continue destroying instances if authorization returns true" do
+ @terminus.expects(:authorized?).with(:destroy, @instance, :node => "mynode").returns(true)
+ @terminus.stubs(:destroy)
+ @indirection.destroy(@instance, :node => "mynode")
+ end
+
+ it "should fail while searching for instances if authorization returns false" do
+ @terminus.expects(:authorized?).with(:search, "/my/key", :node => "mynode").returns(false)
+ @terminus.stubs(:search)
+ proc { @indirection.search("/my/key", :node => "mynode") }.should raise_error(ArgumentError)
+ end
+
+ it "should continue searching for instances if authorization returns true" do
+ @terminus.expects(:authorized?).with(:search, "/my/key", :node => "mynode").returns(true)
+ @terminus.stubs(:search)
+ @indirection.search("/my/key", :node => "mynode")
+ end
+ end
- @indirection.find(@uri)
+ after :each do
+ @indirection.delete
+ Puppet::Indirector::Indirection.clear_cache
+ end
end
- it "should use the terminus name provided by passing the key to the :select_terminus hook when testing if a cached instance is up to date" do
- @indirection.cache_class = :cache
- @other_terminus.expects(:version).with(@uri).returns(2.0)
+ describe "when managing indirection instances" do
+ it "should allow an indirection to be retrieved by name" do
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
+ Puppet::Indirector::Indirection.instance(:test).should equal(@indirection)
+ end
+
+ it "should return nil when the named indirection has not been created" do
+ Puppet::Indirector::Indirection.instance(:test).should be_nil
+ end
- @cache_terminus.expects(:has_most_recent?).with(@uri, 2.0).returns(true)
- @cache_terminus.expects(:find).returns(:whatever)
+ it "should allow an indirection's model to be retrieved by name" do
+ mock_model = mock('model')
+ @indirection = Puppet::Indirector::Indirection.new(mock_model, :test)
+ Puppet::Indirector::Indirection.model(:test).should equal(mock_model)
+ end
+
+ it "should return nil when no model matches the requested name" do
+ Puppet::Indirector::Indirection.model(:test).should be_nil
+ end
- @indirection.find(@uri).should == :whatever
+ after do
+ @indirection.delete if defined? @indirection
+ end
end
- it "should pass all arguments to the :select_terminus hook" do
- @indirection.expects(:select_terminus).with(@uri, :node => "johnny").returns(:other)
- @other_terminus.stubs(:find)
+ describe "when routing to the correct the terminus class" do
+ before do
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
+ @terminus = mock 'terminus'
+ @terminus_class = stub 'terminus class', :new => @terminus
+ Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :default).returns(@terminus_class)
+ end
+
+ it "should have a method for choosing the appropriate terminus class"
- @indirection.find(@uri, :node => "johnny")
- end
+ it "should fail if no terminus class can be picked" do
+ proc { @indirection.terminus_class }.should raise_error(Puppet::DevError)
+ end
- it "should pass the original key to the terminus rather than a modified key" do
- # This is the same test as before
- @other_terminus.expects(:find).with(@uri).returns(@result)
- @indirection.find(@uri)
- end
+ it "should use the select_terminus hook if one is available"
- after do
- @indirection.delete if defined? @indirection
- end
-end
+ it "should choose the default terminus class if one is specified" do
+ @indirection.terminus_class = :default
+ @indirection.terminus_class.should equal(:default)
+ end
-describe Puppet::Indirector::Indirection, " when managing terminus instances" do
- before do
- @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)
- end
+ it "should use the provided Puppet setting if told to do so" do
+ Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :my_terminus).returns(mock("terminus_class2"))
+ Puppet.settings.expects(:value).with(:my_setting).returns("my_terminus")
+ @indirection.terminus_setting = :my_setting
+ @indirection.terminus_class.should equal(:my_terminus)
+ end
- it "should create an instance of the chosen terminus class" do
- @terminus_class.stubs(:new).returns(@terminus)
- @indirection.terminus(:foo).should equal(@terminus)
- end
+ it "should fail if the provided terminus class is not valid" do
+ Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :nosuchclass).returns(nil)
+ proc { @indirection.terminus_class = :nosuchclass }.should raise_error(ArgumentError)
+ end
- it "should allow the clearance of cached terminus instances" do
- terminus1 = mock 'terminus1'
- terminus2 = mock 'terminus2'
- @terminus_class.stubs(:new).returns(terminus1, terminus2, ArgumentError)
- @indirection.terminus(:foo).should equal(terminus1)
- @indirection.class.clear_cache
- @indirection.terminus(:foo).should equal(terminus2)
+ after do
+ @indirection.delete if defined? @indirection
+ end
end
- # Make sure it caches the terminus.
- it "should return the same terminus instance each time for a given name" do
- @terminus_class.stubs(:new).returns(@terminus)
- @indirection.terminus(:foo).should equal(@terminus)
- @indirection.terminus(:foo).should equal(@terminus)
- end
+ describe "when specifying the terminus class to use" do
+ before do
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
+ @terminus = mock 'terminus'
+ @terminus_class = stub 'terminus class', :new => @terminus
+ end
- it "should not create a terminus instance until one is actually needed" do
- Puppet::Indirector.expects(:terminus).never
- indirection = Puppet::Indirector::Indirection.new(mock('model'), :lazytest)
- end
+ it "should allow specification of a terminus type" do
+ @indirection.should respond_to(:terminus_class=)
+ end
- after do
- @indirection.delete
- Puppet::Indirector::Indirection.clear_cache
- end
-end
+ it "should fail to redirect if no terminus type has been specified" do
+ proc { @indirection.find("blah") }.should raise_error(Puppet::DevError)
+ end
-describe Puppet::Indirector::Indirection, " when deciding whether to cache" do
- before do
- @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
- @terminus = mock 'terminus'
- @terminus_class = mock 'terminus class'
- @terminus_class.stubs(:new).returns(@terminus)
- Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class)
- @indirection.terminus_class = :foo
- end
+ it "should fail when the terminus class name is an empty string" do
+ proc { @indirection.terminus_class = "" }.should raise_error(ArgumentError)
+ end
- it "should provide a method for setting the cache terminus class" do
- @indirection.should respond_to(:cache_class=)
- end
+ it "should fail when the terminus class name is nil" do
+ proc { @indirection.terminus_class = nil }.should raise_error(ArgumentError)
+ end
- it "should fail to cache if no cache type has been specified" do
- proc { @indirection.cache }.should raise_error(Puppet::DevError)
- end
+ it "should fail when the specified terminus class cannot be found" do
+ Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(nil)
+ proc { @indirection.terminus_class = :foo }.should raise_error(ArgumentError)
+ end
- it "should fail to set the cache class when the cache class name is an empty string" do
- proc { @indirection.cache_class = "" }.should raise_error(ArgumentError)
- end
+ it "should select the specified terminus class if a terminus class name is provided" do
+ Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(@terminus_class)
+ @indirection.terminus(:foo).should equal(@terminus)
+ end
- it "should fail to set the cache class when the cache class name is nil" do
- proc { @indirection.cache_class = nil }.should raise_error(ArgumentError)
- end
+ it "should use the configured terminus class if no terminus name is specified" do
+ Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class)
+ @indirection.terminus_class = :foo
+ @indirection.terminus().should equal(@terminus)
+ end
- it "should fail to set the cache class when the specified cache class cannot be found" do
- Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(nil)
- proc { @indirection.cache_class = :foo }.should raise_error(ArgumentError)
+ after do
+ @indirection.delete if defined? @indirection
+ end
end
- after do
- @indirection.delete
- Puppet::Indirector::Indirection.clear_cache
- end
-end
+ describe "when a select_terminus hook is available" do
+ before do
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
-describe Puppet::Indirector::Indirection do
- before :each do
- Puppet.settings.stubs(:value).with("test_terminus").returns("test_terminus")
- @terminus_class = mock 'terminus_class'
- @terminus = mock 'terminus'
- @terminus_class.stubs(:new).returns(@terminus)
- @cache = mock 'cache'
- @cache_class = mock 'cache_class'
- Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :cache_terminus).returns(@cache_class)
- Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :test_terminus).returns(@terminus_class)
- @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
- @indirection.terminus_class = :test_terminus
- end
+ # And provide a select_terminus hook
+ @indirection.meta_def(:select_terminus) do |uri|
+ :other
+ end
- describe Puppet::Indirector::Indirection, " when managing the cache terminus" do
+ @terminus = mock 'terminus'
+ @terminus_class = stub 'terminus class', :new => @terminus
- it "should not create a cache terminus at initialization" do
- # This is weird, because all of the code is in the setup. If we got
- # new() called on the cache class, we'd get an exception here.
- end
+ @other_terminus = mock 'other_terminus'
+ @other_terminus_class = stub 'other_terminus_class', :new => @other_terminus
- it "should reuse the cache terminus" do
- @cache_class.expects(:new).returns(@cache)
- Puppet.settings.stubs(:value).with("test_cache").returns("cache_terminus")
- @indirection.cache_class = :cache_terminus
- @indirection.cache.should equal(@cache)
- @indirection.cache.should equal(@cache)
+ @cache_terminus = mock 'cache_terminus'
+ @cache_terminus_class = stub 'cache_terminus_class', :new => @cache_terminus
+
+ Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class)
+ Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :other).returns(@other_terminus_class)
+ Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :cache).returns(@cache_terminus_class)
+
+ # Set it to a default type.
+ @indirection.terminus_class = :foo
+
+ @uri = "full://url/path"
+ @result = stub 'result', :expiration => 1, :expired? => false
end
- it "should remove the cache terminus when all other terminus instances are cleared" do
- cache2 = mock 'cache2'
- @cache_class.stubs(:new).returns(@cache, cache2)
- @indirection.cache_class = :cache_terminus
- @indirection.cache.should equal(@cache)
- @indirection.clear_cache
- @indirection.cache.should equal(cache2)
+ it "should use the terminus name provided by passing the key to the :select_terminus hook when finding instances" do
+ # Set up the expectation
+ @other_terminus.expects(:find).with(@uri).returns(@result)
+
+ @indirection.find(@uri)
end
- end
- describe Puppet::Indirector::Indirection, " when saving and using a cache" do
+ it "should pass all arguments to the :select_terminus hook" do
+ @indirection.expects(:select_terminus).with(@uri, :node => "johnny").returns(:other)
+ @other_terminus.stubs(:find)
- before do
- @indirection.cache_class = :cache_terminus
- @cache_class.expects(:new).returns(@cache)
- @name = "testing"
- @instance = stub 'instance', :version => 5, :name => @name
+ @indirection.find(@uri, :node => "johnny")
end
- it "should not update the cache or terminus if the new object is not different" do
- @cache.expects(:has_most_recent?).with(@name, 5).returns(true)
- @indirection.save(@instance)
+ it "should pass the original key to the terminus rather than a modified key" do
+ # This is the same test as before
+ @other_terminus.expects(:find).with(@uri).returns(@result)
+ @indirection.find(@uri)
end
- it "should update the original and the cache if the cached object is different" do
- @cache.expects(:has_most_recent?).with(@name, 5).returns(false)
- @terminus.expects(:save).with(@instance)
- @cache.expects(:save).with(@instance)
- @indirection.save(@instance)
+ after do
+ @indirection.delete if defined? @indirection
end
end
-
- describe Puppet::Indirector::Indirection, " when finding and using a cache" do
+ describe "when managing terminus instances" do
before do
- @indirection.cache_class = :cache_terminus
- @cache_class.expects(:new).returns(@cache)
+ @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)
end
- it "should return the cached object if the cache is up to date" do
- cached = mock 'cached object'
+ it "should create an instance of the chosen terminus class" do
+ @terminus_class.stubs(:new).returns(@terminus)
+ @indirection.terminus(:foo).should equal(@terminus)
+ end
- name = "myobject"
+ it "should allow the clearance of cached terminus instances" do
+ terminus1 = mock 'terminus1'
+ terminus2 = mock 'terminus2'
+ @terminus_class.stubs(:new).returns(terminus1, terminus2, ArgumentError)
+ @indirection.terminus(:foo).should equal(terminus1)
+ @indirection.class.clear_cache
+ @indirection.terminus(:foo).should equal(terminus2)
+ end
- @terminus.expects(:version).with(name).returns(1)
- @cache.expects(:has_most_recent?).with(name, 1).returns(true)
+ # Make sure it caches the terminus.
+ it "should return the same terminus instance each time for a given name" do
+ @terminus_class.stubs(:new).returns(@terminus)
+ @indirection.terminus(:foo).should equal(@terminus)
+ @indirection.terminus(:foo).should equal(@terminus)
+ end
- @cache.expects(:find).with(name).returns(cached)
+ it "should not create a terminus instance until one is actually needed" do
+ Puppet::Indirector.expects(:terminus).never
+ indirection = Puppet::Indirector::Indirection.new(mock('model'), :lazytest)
+ end
- @indirection.find(name).should equal(cached)
+ after do
+ @indirection.delete
+ Puppet::Indirector::Indirection.clear_cache
end
+ end
- it "should return the original object if the cache is not up to date" do
- real = stub 'real object', :version => 1
+ describe "when deciding whether to cache" do
+ before do
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
+ @terminus = mock 'terminus'
+ @terminus_class = mock 'terminus class'
+ @terminus_class.stubs(:new).returns(@terminus)
+ Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class)
+ @indirection.terminus_class = :foo
+ end
- name = "myobject"
+ it "should provide a method for setting the cache terminus class" do
+ @indirection.should respond_to(:cache_class=)
+ end
- @cache.stubs(:save)
- @cache.expects(:has_most_recent?).with(name, 1).returns(false)
- @terminus.expects(:version).with(name).returns(1)
+ it "should fail to cache if no cache type has been specified" do
+ proc { @indirection.cache }.should raise_error(Puppet::DevError)
+ end
- @terminus.expects(:find).with(name).returns(real)
+ it "should fail to set the cache class when the cache class name is an empty string" do
+ proc { @indirection.cache_class = "" }.should raise_error(ArgumentError)
+ end
- @indirection.find(name).should equal(real)
+ it "should fail to set the cache class when the cache class name is nil" do
+ proc { @indirection.cache_class = nil }.should raise_error(ArgumentError)
end
- it "should cache any newly returned objects" do
- real = stub 'real object', :version => 1
+ it "should fail to set the cache class when the specified cache class cannot be found" do
+ Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(nil)
+ proc { @indirection.cache_class = :foo }.should raise_error(ArgumentError)
+ end
- name = "myobject"
+ after do
+ @indirection.delete
+ Puppet::Indirector::Indirection.clear_cache
+ end
+ end
+
+ describe "when using a cache" do
+ before :each do
+ Puppet.settings.stubs(:value).with("test_terminus").returns("test_terminus")
+ @terminus_class = mock 'terminus_class'
+ @terminus = mock 'terminus'
+ @terminus_class.stubs(:new).returns(@terminus)
+ @cache = mock 'cache'
+ @cache_class = mock 'cache_class'
+ Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :cache_terminus).returns(@cache_class)
+ Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :test_terminus).returns(@terminus_class)
+ @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test)
+ @indirection.terminus_class = :test_terminus
+ end
- @terminus.expects(:version).with(name).returns(1)
- @cache.expects(:has_most_recent?).with(name, 1).returns(false)
+ describe "and managing the cache terminus" do
+ it "should not create a cache terminus at initialization" do
+ # This is weird, because all of the code is in the setup. If we got
+ # new() called on the cache class, we'd get an exception here.
+ end
- @terminus.expects(:find).with(name).returns(real)
- @cache.expects(:save).with(real)
+ it "should reuse the cache terminus" do
+ @cache_class.expects(:new).returns(@cache)
+ Puppet.settings.stubs(:value).with("test_cache").returns("cache_terminus")
+ @indirection.cache_class = :cache_terminus
+ @indirection.cache.should equal(@cache)
+ @indirection.cache.should equal(@cache)
+ end
- @indirection.find(name).should equal(real)
+ it "should remove the cache terminus when all other terminus instances are cleared" do
+ cache2 = mock 'cache2'
+ @cache_class.stubs(:new).returns(@cache, cache2)
+ @indirection.cache_class = :cache_terminus
+ @indirection.cache.should equal(@cache)
+ @indirection.clear_cache
+ @indirection.cache.should equal(cache2)
+ end
+ end
+
+ describe "and saving" do
+ end
+
+ describe "and finding" do
+ end
+
+ after :each do
+ @indirection.delete
+ Puppet::Indirector::Indirection.clear_cache
end
- end
-
- after :each do
- @indirection.delete
- Puppet::Indirector::Indirection.clear_cache
end
end