diff --git a/lib/puppet/indirector/file.rb b/lib/puppet/indirector/file.rb deleted file mode 100644 index b3746b792..000000000 --- a/lib/puppet/indirector/file.rb +++ /dev/null @@ -1,79 +0,0 @@ -require 'puppet/indirector/terminus' - -# Store instances as files, usually serialized using some format. -class Puppet::Indirector::File < Puppet::Indirector::Terminus - # Where do we store our data? - def data_directory - name = Puppet.run_mode.master? ? :server_datadir : :client_datadir - - File.join(Puppet.settings[name], self.class.indirection_name.to_s) - end - - def file_format(path) - path =~ /\.(\w+)$/ and return $1 - end - - def file_path(request) - File.join(data_directory, request.key + ".#{serialization_format}") - end - - def latest_path(request) - files = Dir.glob(File.join(data_directory, request.key + ".*")) - return nil if files.empty? - - # Return the newest file. - files.sort { |a, b| File.stat(b).mtime <=> File.stat(a).mtime }[0] - end - - def serialization_format - model.default_format - end - - # Remove files on disk. - def destroy(request) - begin - removed = false - Dir.glob(File.join(data_directory, request.key.to_s + ".*")).each do |file| - removed = true - File.unlink(file) - end - rescue => detail - raise Puppet::Error, "Could not remove #{request.key}: #{detail}" - end - - raise Puppet::Error, "Could not find files for #{request.key} to remove" unless removed - end - - # Return a model instance for a given file on disk. - def find(request) - return nil unless path = latest_path(request) - format = file_format(path) - - raise ArgumentError, "File format #{format} is not supported by #{self.class.indirection_name}" unless model.support_format?(format) - - begin - return model.convert_from(format, File.read(path)) - rescue => detail - raise Puppet::Error, "Could not convert path #{path} into a #{self.class.indirection_name}: #{detail}" - end - end - - # Save a new file to disk. - def save(request) - path = file_path(request) - - dir = File.dirname(path) - - raise Puppet::Error.new("Cannot save #{request.key}; parent directory #{dir} does not exist") unless File.directory?(dir) - - begin - File.open(path, "w") { |f| f.print request.instance.render(serialization_format) } - rescue => detail - raise Puppet::Error, "Could not write #{request.key}: #{detail}" % [request.key, detail] - end - end - - def path(key) - key - end -end diff --git a/spec/unit/indirector/file_spec.rb b/spec/unit/indirector/file_spec.rb deleted file mode 100755 index 86673f0e2..000000000 --- a/spec/unit/indirector/file_spec.rb +++ /dev/null @@ -1,181 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/indirector/file' - - -describe Puppet::Indirector::File do - before :each 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) - - @file_class = Class.new(Puppet::Indirector::File) do - def self.to_s - "Testing::Mytype" - end - end - - @searcher = @file_class.new - - @path = "/my/file" - @dir = "/my" - - @request = stub 'request', :key => @path - end - - describe "when finding files" do - it "should provide a method to return file contents at a specified path" do - @searcher.should respond_to(:find) - end - - it "should use the server data directory plus the indirection name if the run_mode is master" do - Puppet.run_mode.expects(:master?).returns true - Puppet.settings.expects(:value).with(:server_datadir).returns "/my/dir" - - @searcher.data_directory.should == File.join("/my/dir", "mystuff") - end - - it "should use the client data directory plus the indirection name if the run_mode is not master" do - Puppet.run_mode.expects(:master?).returns false - Puppet.settings.expects(:value).with(:client_datadir).returns "/my/dir" - - @searcher.data_directory.should == File.join("/my/dir", "mystuff") - end - - it "should use the newest file in the data directory matching the indirection key without extension" do - @searcher.expects(:data_directory).returns "/data/dir" - @request.stubs(:key).returns "foo" - Dir.expects(:glob).with("/data/dir/foo.*").returns %w{/data1.stuff /data2.stuff} - - stat1 = stub 'data1', :mtime => (Time.now - 5) - stat2 = stub 'data2', :mtime => Time.now - File.expects(:stat).with("/data1.stuff").returns stat1 - File.expects(:stat).with("/data2.stuff").returns stat2 - - @searcher.latest_path(@request).should == "/data2.stuff" - end - - it "should return nil when no files are found" do - @searcher.stubs(:latest_path).returns nil - - @searcher.find(@request).should be_nil - end - - it "should determine the file format from the file extension" do - @searcher.file_format("/data2.pson").should == "pson" - end - - it "should fail if the model does not support the file format" do - @searcher.stubs(:latest_path).returns "/my/file.pson" - - @model.expects(:support_format?).with("pson").returns false - - lambda { @searcher.find(@request) }.should raise_error(ArgumentError) - end - end - - describe "when saving files" do - before do - @content = "my content" - @file = stub 'file', :content => @content, :path => @path, :name => @path, :render => "mydata" - @request.stubs(:instance).returns @file - end - - it "should provide a method to save file contents at a specified path" do - @searcher.should respond_to(:save) - end - - it "should choose the file extension based on the default format of the model" do - @model.expects(:default_format).returns "pson" - - @searcher.serialization_format.should == "pson" - end - - it "should place the file in the data directory, named after the indirection, key, and format" do - @searcher.stubs(:data_directory).returns "/my/dir" - @searcher.stubs(:serialization_format).returns "pson" - - @request.stubs(:key).returns "foo" - @searcher.file_path(@request).should == File.join("/my/dir", "foo.pson") - end - - it "should fail intelligently if the file's parent directory does not exist" do - @searcher.stubs(:file_path).returns "/my/dir/file.pson" - @searcher.stubs(:serialization_format).returns "pson" - - @request.stubs(:key).returns "foo" - File.expects(:directory?).with(File.join("/my/dir")).returns(false) - - proc { @searcher.save(@request) }.should raise_error(Puppet::Error) - end - - it "should render the instance using the file format and print it to the file path" do - @searcher.stubs(:file_path).returns "/my/file.pson" - @searcher.stubs(:serialization_format).returns "pson" - - File.stubs(:directory?).returns true - - @request.instance.expects(:render).with("pson").returns "data" - - fh = mock 'filehandle' - File.expects(:open).with("/my/file.pson", "w").yields fh - fh.expects(:print).with("data") - - @searcher.save(@request) - end - - it "should fail intelligently if a file cannot be written" do - filehandle = mock 'file' - File.stubs(:directory?).returns(true) - File.stubs(:open).yields(filehandle) - filehandle.expects(:print).raises(ArgumentError) - - @searcher.stubs(:file_path).returns "/my/file.pson" - @model.stubs(:default_format).returns "pson" - - @instance.stubs(:render).returns "stuff" - - proc { @searcher.save(@request) }.should raise_error(Puppet::Error) - end - end - - describe "when removing files" do - it "should provide a method to remove files" do - @searcher.should respond_to(:destroy) - end - - it "should remove files in all formats found in the data directory that match the request key" do - @searcher.stubs(:data_directory).returns "/my/dir" - @request.stubs(:key).returns "me" - - Dir.expects(:glob).with(File.join("/my/dir", "me.*")).returns %w{/one /two} - - File.expects(:unlink).with("/one") - File.expects(:unlink).with("/two") - - @searcher.destroy(@request) - end - - it "should throw an exception if no file is found" do - @searcher.stubs(:data_directory).returns "/my/dir" - @request.stubs(:key).returns "me" - - Dir.expects(:glob).with(File.join("/my/dir", "me.*")).returns [] - - proc { @searcher.destroy(@request) }.should raise_error(Puppet::Error) - end - - it "should fail intelligently if a file cannot be removed" do - @searcher.stubs(:data_directory).returns "/my/dir" - @request.stubs(:key).returns "me" - - Dir.expects(:glob).with(File.join("/my/dir", "me.*")).returns %w{/one} - - File.expects(:unlink).with("/one").raises ArgumentError - - proc { @searcher.destroy(@request) }.should raise_error(Puppet::Error) - end - end -end diff --git a/spec/unit/indirector/terminus_spec.rb b/spec/unit/indirector/terminus_spec.rb index 826b9347a..63cb7cc75 100755 --- a/spec/unit/indirector/terminus_spec.rb +++ b/spec/unit/indirector/terminus_spec.rb @@ -1,245 +1,245 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/defaults' require 'puppet/indirector' -require 'puppet/indirector/file' +require 'puppet/indirector/memory' describe Puppet::Indirector::Terminus do before :each do Puppet::Indirector::Terminus.stubs(:register_terminus_class) @indirection = stub 'indirection', :name => :my_stuff, :register_terminus_type => nil Puppet::Indirector::Indirection.stubs(:instance).with(:my_stuff).returns(@indirection) @abstract_terminus = Class.new(Puppet::Indirector::Terminus) do def self.to_s "Testing::Abstract" end end @terminus_class = Class.new(@abstract_terminus) do def self.to_s "MyStuff::TermType" end end @terminus = @terminus_class.new end describe Puppet::Indirector::Terminus do it "should provide a method for setting terminus class documentation" do @terminus_class.should respond_to(:desc) end it "should support a class-level name attribute" do @terminus_class.should respond_to(:name) end it "should support a class-level indirection attribute" do @terminus_class.should respond_to(:indirection) end it "should support a class-level terminus-type attribute" do @terminus_class.should respond_to(:terminus_type) end it "should support a class-level model attribute" do @terminus_class.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_class.indirection = indirection }.should_not raise_error @terminus_class.indirection.should equal(indirection) end it "should look up indirection instances when only a name has been provided" do indirection = mock 'indirection' Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns(indirection) @terminus_class.indirection = :myind @terminus_class.indirection.should equal(indirection) end it "should fail when provided a name that does not resolve to an indirection" do Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns(nil) proc { @terminus_class.indirection = :myind }.should raise_error(ArgumentError) # It shouldn't overwrite our existing one (or, more normally, it shouldn't set # anything). @terminus_class.indirection.should equal(@indirection) end end describe Puppet::Indirector::Terminus, " when creating terminus classes" do it "should associate the subclass with an indirection based on the subclass constant" do @terminus.indirection.should equal(@indirection) end it "should set the subclass's type to the abstract terminus name" do @terminus.terminus_type.should == :abstract end it "should set the subclass's name to the indirection name" do @terminus.name.should == :term_type 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 it "should return the class's name as its name" do @terminus.name.should == :term_type end it "should return the class's indirection as its indirection" do @terminus.indirection.should equal(@indirection) end 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 end # LAK: This could reasonably be in the Indirection instances, too. It doesn't make # a whole heckuva lot of difference, except that with the instance loading in # the Terminus base class, we have to have a check to see if we're already # instance-loading a given terminus class type. describe Puppet::Indirector::Terminus, " when managing terminus classes" do it "should provide a method for registering terminus classes" do Puppet::Indirector::Terminus.should respond_to(:register_terminus_class) end it "should provide a method for returning terminus classes by name and type" do terminus = stub 'terminus_type', :name => :abstract, :indirection_name => :whatever Puppet::Indirector::Terminus.register_terminus_class(terminus) Puppet::Indirector::Terminus.terminus_class(:whatever, :abstract).should equal(terminus) end it "should set up autoloading for any terminus class types requested" do Puppet::Indirector::Terminus.expects(:instance_load).with(:test2, "puppet/indirector/test2") Puppet::Indirector::Terminus.terminus_class(:test2, :whatever) end it "should load terminus classes that are not found" do # Set up instance loading; it would normally happen automatically Puppet::Indirector::Terminus.instance_load :test1, "puppet/indirector/test1" Puppet::Indirector::Terminus.instance_loader(:test1).expects(:load).with(:yay) Puppet::Indirector::Terminus.terminus_class(:test1, :yay) end it "should fail when no indirection can be found" do Puppet::Indirector::Indirection.expects(:instance).with(:my_indirection).returns(nil) @abstract_terminus = Class.new(Puppet::Indirector::Terminus) do def self.to_s "Abstract" end end proc { @terminus = Class.new(@abstract_terminus) do def self.to_s "MyIndirection::TestType" end end }.should raise_error(ArgumentError) end it "should register the terminus class with the terminus base class" do Puppet::Indirector::Terminus.expects(:register_terminus_class).with do |type| type.indirection_name == :my_indirection and type.name == :test_terminus end @indirection = stub 'indirection', :name => :my_indirection, :register_terminus_type => nil Puppet::Indirector::Indirection.expects(:instance).with(:my_indirection).returns(@indirection) @abstract_terminus = Class.new(Puppet::Indirector::Terminus) do def self.to_s "Abstract" end end @terminus = Class.new(@abstract_terminus) do def self.to_s "MyIndirection::TestTerminus" end end end end describe Puppet::Indirector::Terminus, " when parsing class constants for indirection and terminus names" do before do @subclass = mock 'subclass' @subclass.stubs(:to_s).returns("TestInd::OneTwo") @subclass.stubs(:mark_as_abstract_terminus) Puppet::Indirector::Terminus.stubs(:register_terminus_class) end it "should fail when anonymous classes are used" do proc { Puppet::Indirector::Terminus.inherited(Class.new) }.should raise_error(Puppet::DevError) end it "should use the last term in the constant for the terminus class name" do @subclass.expects(:name=).with(:one_two) @subclass.stubs(:indirection=) Puppet::Indirector::Terminus.inherited(@subclass) end it "should convert the terminus name to a downcased symbol" do @subclass.expects(:name=).with(:one_two) @subclass.stubs(:indirection=) Puppet::Indirector::Terminus.inherited(@subclass) end it "should use the second to last term in the constant for the indirection name" do @subclass.expects(:indirection=).with(:test_ind) @subclass.stubs(:name=) @subclass.stubs(:terminus_type=) - Puppet::Indirector::File.inherited(@subclass) + Puppet::Indirector::Memory.inherited(@subclass) end it "should convert the indirection name to a downcased symbol" do @subclass.expects(:indirection=).with(:test_ind) @subclass.stubs(:name=) @subclass.stubs(:terminus_type=) - Puppet::Indirector::File.inherited(@subclass) + Puppet::Indirector::Memory.inherited(@subclass) end it "should convert camel case to lower case with underscores as word separators" do @subclass.expects(:name=).with(:one_two) @subclass.stubs(:indirection=) Puppet::Indirector::Terminus.inherited(@subclass) end end describe Puppet::Indirector::Terminus, " when creating terminus class types" do before do Puppet::Indirector::Terminus.stubs(:register_terminus_class) @subclass = Class.new(Puppet::Indirector::Terminus) do def self.to_s "Puppet::Indirector::Terminus::MyTermType" end end end it "should set the name of the abstract subclass to be its class constant" do @subclass.name.should equal(:my_term_type) end it "should mark abstract terminus types as such" do @subclass.should be_abstract_terminus end it "should not allow instances of abstract subclasses to be created" do proc { @subclass.new }.should raise_error(Puppet::DevError) end end