diff --git a/lib/puppet/file_serving/mount.rb b/lib/puppet/file_serving/mount.rb index 37dd89537..79290ab81 100644 --- a/lib/puppet/file_serving/mount.rb +++ b/lib/puppet/file_serving/mount.rb @@ -1,44 +1,43 @@ # # Created by Luke Kanies on 2007-10-16. # Copyright (c) 2007. All rights reserved. require 'puppet/network/authstore' require 'puppet/util/logging' -require 'puppet/util/cacher' require 'puppet/file_serving' require 'puppet/file_serving/metadata' require 'puppet/file_serving/content' # Broker access to the filesystem, converting local URIs into metadata # or content objects. class Puppet::FileServing::Mount < Puppet::Network::AuthStore include Puppet::Util::Logging attr_reader :name def find(path, options) raise NotImplementedError end # Create our object. It must have a name. def initialize(name) unless name =~ %r{^[-\w]+$} raise ArgumentError, "Invalid mount name format '#{name}'" end @name = name super() end def search(path, options) raise NotImplementedError end def to_s "mount[#{@name}]" end # A noop. def validate end end diff --git a/lib/puppet/file_serving/mount/file.rb b/lib/puppet/file_serving/mount/file.rb index 7d622e4bf..7f5af7f52 100644 --- a/lib/puppet/file_serving/mount/file.rb +++ b/lib/puppet/file_serving/mount/file.rb @@ -1,124 +1,121 @@ -require 'puppet/util/cacher' - require 'puppet/file_serving/mount' class Puppet::FileServing::Mount::File < Puppet::FileServing::Mount - class << self - include Puppet::Util::Cacher - - cached_attr(:localmap) do - { "h" => Facter.value("hostname"), - "H" => [Facter.value("hostname"), - Facter.value("domain")].join("."), - "d" => Facter.value("domain") - } - end + def self.localmap + @localmap ||= { + "h" => Facter.value("hostname"), + "H" => [ + Facter.value("hostname"), + Facter.value("domain") + ].join("."), + "d" => Facter.value("domain") + } end def complete_path(relative_path, node) full_path = path(node) raise ArgumentError.new("Mounts without paths are not usable") unless full_path # If there's no relative path name, then we're serving the mount itself. return full_path unless relative_path file = ::File.join(full_path, relative_path) if !(FileTest.exist?(file) or FileTest.symlink?(file)) Puppet.info("File does not exist or is not accessible: #{file}") return nil end file end # Return an instance of the appropriate class. def find(short_file, request) complete_path(short_file, request.node) end # Return the path as appropriate, expanding as necessary. def path(node = nil) if expandable? return expand(@path, node) else return @path end end # Set the path. def path=(path) # FIXME: For now, just don't validate paths with replacement # patterns in them. if path =~ /%./ # Mark that we're expandable. @expandable = true else raise ArgumentError, "#{path} does not exist or is not a directory" unless FileTest.directory?(path) raise ArgumentError, "#{path} is not readable" unless FileTest.readable?(path) @expandable = false end @path = path end def search(path, request) return nil unless path = complete_path(path, request.node) [path] end # Verify our configuration is valid. This should really check to # make sure at least someone will be allowed, but, eh. def validate raise ArgumentError.new("Mounts without paths are not usable") if @path.nil? end private # Create a map for a specific node. def clientmap(node) { "h" => node.sub(/\..*$/, ""), "H" => node, "d" => node.sub(/[^.]+\./, "") # domain name } end # Replace % patterns as appropriate. def expand(path, node = nil) # This map should probably be moved into a method. map = nil if node map = clientmap(node) else Puppet.notice "No client; expanding '#{path}' with local host" # Else, use the local information map = localmap end path.gsub(/%(.)/) do |v| key = $1 if key == "%" "%" else map[key] || v end end end # Do we have any patterns in our path, yo? def expandable? if defined?(@expandable) @expandable else false end end # Cache this manufactured map, since if it's used it's likely # to get used a lot. def localmap self.class.localmap end end diff --git a/spec/unit/file_serving/mount/file_spec.rb b/spec/unit/file_serving/mount/file_spec.rb index 70c804abd..1ee004c10 100755 --- a/spec/unit/file_serving/mount/file_spec.rb +++ b/spec/unit/file_serving/mount/file_spec.rb @@ -1,195 +1,189 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/file_serving/mount/file' module FileServingMountTesting def stub_facter(hostname) Facter.stubs(:value).with("hostname").returns(hostname.sub(/\..+/, '')) Facter.stubs(:value).with("domain").returns(hostname.sub(/^[^.]+\./, '')) end end describe Puppet::FileServing::Mount::File do - it "should provide a method for clearing its cached host information" do - old = Puppet::FileServing::Mount::File.localmap - Puppet::Util::Cacher.expire - Puppet::FileServing::Mount::File.localmap.should_not equal(old) - end - it "should be invalid if it does not have a path" do lambda { Puppet::FileServing::Mount::File.new("foo").validate }.should raise_error(ArgumentError) end it "should be valid if it has a path" do FileTest.stubs(:directory?).returns true FileTest.stubs(:readable?).returns true mount = Puppet::FileServing::Mount::File.new("foo") mount.path = "/foo" lambda { mount.validate }.should_not raise_error(ArgumentError) end -end - -describe Puppet::FileServing::Mount::File, " when setting the path" do - before do - @mount = Puppet::FileServing::Mount::File.new("test") - @dir = "/this/path/does/not/exist" - end - - it "should fail if the path is not a directory" do - FileTest.expects(:directory?).returns(false) - proc { @mount.path = @dir }.should raise_error(ArgumentError) - end - - it "should fail if the path is not readable" do - FileTest.expects(:directory?).returns(true) - FileTest.expects(:readable?).returns(false) - proc { @mount.path = @dir }.should raise_error(ArgumentError) - end -end - -describe Puppet::FileServing::Mount::File, " when substituting hostnames and ip addresses into file paths" do - include FileServingMountTesting - - before do - FileTest.stubs(:directory?).returns(true) - FileTest.stubs(:readable?).returns(true) - @mount = Puppet::FileServing::Mount::File.new("test") - @host = "host.domain.com" - end - - it "should replace incidences of %h in the path with the client's short name" do - @mount.path = "/dir/%h/yay" - @mount.path(@host).should == "/dir/host/yay" - end - - it "should replace incidences of %H in the path with the client's fully qualified name" do - @mount.path = "/dir/%H/yay" - @mount.path(@host).should == "/dir/host.domain.com/yay" - end - - it "should replace incidences of %d in the path with the client's domain name" do - @mount.path = "/dir/%d/yay" - @mount.path(@host).should == "/dir/domain.com/yay" - end - - it "should perform all necessary replacements" do - @mount.path = "/%h/%d/%H" - @mount.path(@host).should == "/host/domain.com/host.domain.com" - end - - it "should use local host information if no client data is provided" do - stub_facter("myhost.mydomain.com") - @mount.path = "/%h/%d/%H" - @mount.path.should == "/myhost/mydomain.com/myhost.mydomain.com" - end - - after do - Puppet::Util::Cacher.expire - end -end - -describe Puppet::FileServing::Mount::File, "when determining the complete file path" do - include FileServingMountTesting - - before do - FileTest.stubs(:exist?).returns(true) - FileTest.stubs(:directory?).returns(true) - FileTest.stubs(:readable?).returns(true) - @mount = Puppet::FileServing::Mount::File.new("test") - @mount.path = "/mount" - stub_facter("myhost.mydomain.com") - @host = "host.domain.com" - end - - it "should return nil if the file is absent" do - FileTest.stubs(:exist?).returns(false) - @mount.complete_path("/my/path", nil).should be_nil - end - - it "should write a log message if the file is absent" do - FileTest.stubs(:exist?).returns(false) - - Puppet.expects(:info).with("File does not exist or is not accessible: /mount/my/path") - - @mount.complete_path("/my/path", nil) - end - - it "should return the file path if the file is present" do - FileTest.stubs(:exist?).with("/my/path").returns(true) - @mount.complete_path("/my/path", nil).should == "/mount/my/path" - end - - it "should treat a nil file name as the path to the mount itself" do - FileTest.stubs(:exist?).returns(true) - @mount.complete_path(nil, nil).should == "/mount" - end - - it "should use the client host name if provided in the options" do - @mount.path = "/mount/%h" - @mount.complete_path("/my/path", @host).should == "/mount/host/my/path" - end - - it "should perform replacements on the base path" do - @mount.path = "/blah/%h" - @mount.complete_path("/my/stuff", @host).should == "/blah/host/my/stuff" - end - - it "should not perform replacements on the per-file path" do - @mount.path = "/blah" - @mount.complete_path("/%h/stuff", @host).should == "/blah/%h/stuff" - end - - it "should look for files relative to its base directory" do - @mount.complete_path("/my/stuff", @host).should == "/mount/my/stuff" - end -end - -describe Puppet::FileServing::Mount::File, "when finding files" do - include FileServingMountTesting - - before do - FileTest.stubs(:exist?).returns(true) - FileTest.stubs(:directory?).returns(true) - FileTest.stubs(:readable?).returns(true) - @mount = Puppet::FileServing::Mount::File.new("test") - @mount.path = "/mount" - stub_facter("myhost.mydomain.com") - @host = "host.domain.com" - - @request = stub 'request', :node => "foo" - end - - it "should return the results of the complete file path" do - FileTest.stubs(:exist?).returns(false) - @mount.expects(:complete_path).with("/my/path", "foo").returns "eh" - @mount.find("/my/path", @request).should == "eh" - end -end - -describe Puppet::FileServing::Mount::File, "when searching for files" do - include FileServingMountTesting - - before do - FileTest.stubs(:exist?).returns(true) - FileTest.stubs(:directory?).returns(true) - FileTest.stubs(:readable?).returns(true) - @mount = Puppet::FileServing::Mount::File.new("test") - @mount.path = "/mount" - stub_facter("myhost.mydomain.com") - @host = "host.domain.com" - - @request = stub 'request', :node => "foo" - end - - it "should return the results of the complete file path as an array" do - FileTest.stubs(:exist?).returns(false) - @mount.expects(:complete_path).with("/my/path", "foo").returns "eh" - @mount.search("/my/path", @request).should == ["eh"] - end - it "should return nil if the complete path is nil" do - FileTest.stubs(:exist?).returns(false) - @mount.expects(:complete_path).with("/my/path", "foo").returns nil - @mount.search("/my/path", @request).should be_nil + describe "when setting the path" do + before do + @mount = Puppet::FileServing::Mount::File.new("test") + @dir = "/this/path/does/not/exist" + end + + it "should fail if the path is not a directory" do + FileTest.expects(:directory?).returns(false) + proc { @mount.path = @dir }.should raise_error(ArgumentError) + end + + it "should fail if the path is not readable" do + FileTest.expects(:directory?).returns(true) + FileTest.expects(:readable?).returns(false) + proc { @mount.path = @dir }.should raise_error(ArgumentError) + end + end + + describe "when substituting hostnames and ip addresses into file paths" do + include FileServingMountTesting + + before do + FileTest.stubs(:directory?).returns(true) + FileTest.stubs(:readable?).returns(true) + @mount = Puppet::FileServing::Mount::File.new("test") + @host = "host.domain.com" + end + + after :each do + Puppet::FileServing::Mount::File.instance_variable_set(:@localmap, nil) + end + + it "should replace incidences of %h in the path with the client's short name" do + @mount.path = "/dir/%h/yay" + @mount.path(@host).should == "/dir/host/yay" + end + + it "should replace incidences of %H in the path with the client's fully qualified name" do + @mount.path = "/dir/%H/yay" + @mount.path(@host).should == "/dir/host.domain.com/yay" + end + + it "should replace incidences of %d in the path with the client's domain name" do + @mount.path = "/dir/%d/yay" + @mount.path(@host).should == "/dir/domain.com/yay" + end + + it "should perform all necessary replacements" do + @mount.path = "/%h/%d/%H" + @mount.path(@host).should == "/host/domain.com/host.domain.com" + end + + it "should use local host information if no client data is provided" do + stub_facter("myhost.mydomain.com") + @mount.path = "/%h/%d/%H" + @mount.path.should == "/myhost/mydomain.com/myhost.mydomain.com" + end + end + + describe "when determining the complete file path" do + include FileServingMountTesting + + before do + FileTest.stubs(:exist?).returns(true) + FileTest.stubs(:directory?).returns(true) + FileTest.stubs(:readable?).returns(true) + @mount = Puppet::FileServing::Mount::File.new("test") + @mount.path = "/mount" + stub_facter("myhost.mydomain.com") + @host = "host.domain.com" + end + + it "should return nil if the file is absent" do + FileTest.stubs(:exist?).returns(false) + @mount.complete_path("/my/path", nil).should be_nil + end + + it "should write a log message if the file is absent" do + FileTest.stubs(:exist?).returns(false) + + Puppet.expects(:info).with("File does not exist or is not accessible: /mount/my/path") + + @mount.complete_path("/my/path", nil) + end + + it "should return the file path if the file is present" do + FileTest.stubs(:exist?).with("/my/path").returns(true) + @mount.complete_path("/my/path", nil).should == "/mount/my/path" + end + + it "should treat a nil file name as the path to the mount itself" do + FileTest.stubs(:exist?).returns(true) + @mount.complete_path(nil, nil).should == "/mount" + end + + it "should use the client host name if provided in the options" do + @mount.path = "/mount/%h" + @mount.complete_path("/my/path", @host).should == "/mount/host/my/path" + end + + it "should perform replacements on the base path" do + @mount.path = "/blah/%h" + @mount.complete_path("/my/stuff", @host).should == "/blah/host/my/stuff" + end + + it "should not perform replacements on the per-file path" do + @mount.path = "/blah" + @mount.complete_path("/%h/stuff", @host).should == "/blah/%h/stuff" + end + + it "should look for files relative to its base directory" do + @mount.complete_path("/my/stuff", @host).should == "/mount/my/stuff" + end + end + + describe "when finding files" do + include FileServingMountTesting + + before do + FileTest.stubs(:exist?).returns(true) + FileTest.stubs(:directory?).returns(true) + FileTest.stubs(:readable?).returns(true) + @mount = Puppet::FileServing::Mount::File.new("test") + @mount.path = "/mount" + stub_facter("myhost.mydomain.com") + @host = "host.domain.com" + + @request = stub 'request', :node => "foo" + end + + it "should return the results of the complete file path" do + FileTest.stubs(:exist?).returns(false) + @mount.expects(:complete_path).with("/my/path", "foo").returns "eh" + @mount.find("/my/path", @request).should == "eh" + end + end + + describe "when searching for files" do + include FileServingMountTesting + + before do + FileTest.stubs(:exist?).returns(true) + FileTest.stubs(:directory?).returns(true) + FileTest.stubs(:readable?).returns(true) + @mount = Puppet::FileServing::Mount::File.new("test") + @mount.path = "/mount" + stub_facter("myhost.mydomain.com") + @host = "host.domain.com" + + @request = stub 'request', :node => "foo" + end + + it "should return the results of the complete file path as an array" do + FileTest.stubs(:exist?).returns(false) + @mount.expects(:complete_path).with("/my/path", "foo").returns "eh" + @mount.search("/my/path", @request).should == ["eh"] + end + + it "should return nil if the complete path is nil" do + FileTest.stubs(:exist?).returns(false) + @mount.expects(:complete_path).with("/my/path", "foo").returns nil + @mount.search("/my/path", @request).should be_nil + end end end