diff --git a/lib/puppet/file_serving/content.rb b/lib/puppet/file_serving/content.rb index 00e9dc8ce..6b48cc640 100644 --- a/lib/puppet/file_serving/content.rb +++ b/lib/puppet/file_serving/content.rb @@ -1,45 +1,43 @@ require 'puppet/indirector' require 'puppet/file_serving' require 'puppet/file_serving/base' # A class that handles retrieving file contents. # It only reads the file when its content is specifically # asked for. class Puppet::FileServing::Content < Puppet::FileServing::Base extend Puppet::Indirector indirects :file_content, :terminus_class => :selector attr_writer :content def self.supported_formats [:raw] end def self.from_raw(content) instance = new("/this/is/a/fake/path") instance.content = content instance end - # BF: we used to fetch the file content here, but this is counter-productive - # for puppetmaster streaming of file content. So collect just returns itself + # This is no longer used, but is still called by the file server implementations when interacting + # with their model abstraction. def collect(source_permissions = nil) - return if stat.ftype == "directory" - self end # Read the content of our file in. def content unless @content # This stat can raise an exception, too. raise(ArgumentError, "Cannot read the contents of links unless following links") if stat.ftype == "symlink" @content = Puppet::FileSystem.binread(full_path) end @content end def to_raw File.new(full_path, "rb") end end diff --git a/lib/puppet/file_serving/terminus_helper.rb b/lib/puppet/file_serving/terminus_helper.rb index b36ec55f8..98bfdcbd7 100644 --- a/lib/puppet/file_serving/terminus_helper.rb +++ b/lib/puppet/file_serving/terminus_helper.rb @@ -1,21 +1,32 @@ require 'puppet/file_serving' require 'puppet/file_serving/fileset' # Define some common methods for FileServing termini. module Puppet::FileServing::TerminusHelper + # Create model instance for a file in a file server. + def path2instance(request, path, options = {}) + result = model.new(path, :relative_path => options[:relative_path]) + result.checksum_type = request.options[:checksum_type] if request.options[:checksum_type] + result.links = request.options[:links] if request.options[:links] + + # :ignore_source_permissions is here pending investigation in PUP-3906. + if options[:ignore_source_permissions] + result.collect + else + result.collect(request.options[:source_permissions]) + end + result + end + # Create model instances for all files in a fileset. def path2instances(request, *paths) filesets = paths.collect do |path| # Filesets support indirector requests as an options collection Puppet::FileServing::Fileset.new(path, request) end Puppet::FileServing::Fileset.merge(*filesets).collect do |file, base_path| - inst = model.new(base_path, :relative_path => file) - inst.checksum_type = request.options[:checksum_type] if request.options[:checksum_type] - inst.links = request.options[:links] if request.options[:links] - inst.collect - inst + path2instance(request, base_path, :ignore_source_permissions => true, :relative_path => file) end end end diff --git a/lib/puppet/indirector/direct_file_server.rb b/lib/puppet/indirector/direct_file_server.rb index a043c425e..c919c759a 100644 --- a/lib/puppet/indirector/direct_file_server.rb +++ b/lib/puppet/indirector/direct_file_server.rb @@ -1,19 +1,17 @@ require 'puppet/file_serving/terminus_helper' require 'puppet/indirector/terminus' class Puppet::Indirector::DirectFileServer < Puppet::Indirector::Terminus include Puppet::FileServing::TerminusHelper def find(request) return nil unless Puppet::FileSystem.exist?(request.key) - instance = model.new(request.key) - instance.links = request.options[:links] if request.options[:links] - instance + path2instance(request, request.key) end def search(request) return nil unless Puppet::FileSystem.exist?(request.key) path2instances(request, request.key) end end diff --git a/lib/puppet/indirector/file_metadata/file.rb b/lib/puppet/indirector/file_metadata/file.rb index 78dfe5786..5cfbf87f7 100644 --- a/lib/puppet/indirector/file_metadata/file.rb +++ b/lib/puppet/indirector/file_metadata/file.rb @@ -1,22 +1,7 @@ require 'puppet/file_serving/metadata' require 'puppet/indirector/file_metadata' require 'puppet/indirector/direct_file_server' class Puppet::Indirector::FileMetadata::File < Puppet::Indirector::DirectFileServer desc "Retrieve file metadata directly from the local filesystem." - - def find(request) - return unless data = super - data.collect(request.options[:source_permissions]) - - data - end - - def search(request) - return unless result = super - - result.each { |instance| instance.collect } - - result - end end diff --git a/lib/puppet/indirector/file_server.rb b/lib/puppet/indirector/file_server.rb index 389951dc3..7c5f424f1 100644 --- a/lib/puppet/indirector/file_server.rb +++ b/lib/puppet/indirector/file_server.rb @@ -1,65 +1,51 @@ require 'puppet/file_serving/configuration' require 'puppet/file_serving/fileset' require 'puppet/file_serving/terminus_helper' require 'puppet/indirector/terminus' # Look files up using the file server. class Puppet::Indirector::FileServer < Puppet::Indirector::Terminus include Puppet::FileServing::TerminusHelper # Is the client authorized to perform this action? def authorized?(request) return false unless [:find, :search].include?(request.method) mount, file_path = configuration.split_path(request) # If we're not serving this mount, then access is denied. return false unless mount mount.allowed?(request.node, request.ip) end # Find our key using the fileserver. def find(request) mount, relative_path = configuration.split_path(request) return nil unless mount # The mount checks to see if the file exists, and returns nil # if not. return nil unless path = mount.find(relative_path, request) - result = model.new(path) - result.links = request.options[:links] if request.options[:links] - result.collect(request.options[:source_permissions]) - result + path2instance(request, path) end # Search for files. This returns an array rather than a single # file. def search(request) mount, relative_path = configuration.split_path(request) unless mount and paths = mount.search(relative_path, request) Puppet.info "Could not find filesystem info for file '#{request.key}' in environment #{request.environment}" return nil end - - filesets = paths.collect do |path| - # Filesets support indirector requests as an options collection - Puppet::FileServing::Fileset.new(path, request) - end - - Puppet::FileServing::Fileset.merge(*filesets).collect do |file, base_path| - inst = model.new(base_path, :relative_path => file) - inst.links = request.options[:links] if request.options[:links] - inst.collect - inst - end + path2instances(request, *paths) end private # Our fileserver configuration, if needed. def configuration Puppet::FileServing::Configuration.configuration end end diff --git a/spec/integration/indirector/direct_file_server_spec.rb b/spec/integration/indirector/direct_file_server_spec.rb index 9533b46bc..ab0466e01 100755 --- a/spec/integration/indirector/direct_file_server_spec.rb +++ b/spec/integration/indirector/direct_file_server_spec.rb @@ -1,68 +1,92 @@ require 'spec_helper' require 'matchers/include' require 'puppet/indirector/file_content/file' +require 'puppet/indirector/file_metadata/file' describe Puppet::Indirector::DirectFileServer, " when interacting with the filesystem and the model" do include PuppetSpec::Files before do # We just test a subclass, since it's close enough. @terminus = Puppet::Indirector::FileContent::File.new - - @filepath = make_absolute("/path/to/my/file") end it "should return an instance of the model" do - if Puppet.features.microsoft_windows? - skip("porting to Windows") - else - Puppet::FileSystem.expects(:exist?).with(@filepath).returns(true) + filepath = make_absolute("/path/to/my/file") + Puppet::FileSystem.expects(:exist?).with(filepath).returns(true) - expect(@terminus.find(@terminus.indirection.request(:find, "file://host#{@filepath}", nil))).to be_instance_of(Puppet::FileServing::Content) - end + expect(@terminus.find(@terminus.indirection.request(:find, Puppet::Util.path_to_uri(filepath).to_s, nil))).to be_instance_of(Puppet::FileServing::Content) end it "should return an instance capable of returning its content" do - if Puppet.features.microsoft_windows? - skip("porting to Windows") - else - filename = file_containing("testfile", "my content") + filename = file_containing("testfile", "my content") - instance = @terminus.find(@terminus.indirection.request(:find, "file://host#{filename}", nil)) + instance = @terminus.find(@terminus.indirection.request(:find, Puppet::Util.path_to_uri(filename).to_s, nil)) - expect(instance.content).to eq("my content") - end + expect(instance.content).to eq("my content") end end describe Puppet::Indirector::DirectFileServer, " when interacting with FileServing::Fileset and the model" do include PuppetSpec::Files include Matchers::Include matcher :file_with_content do |name, content| match do |actual| actual.full_path == name && actual.content == content end end matcher :directory_named do |name| match do |actual| actual.full_path == name end end it "should return an instance for every file in the fileset" do path = tmpdir('direct_file_server_testing') File.open(File.join(path, "one"), "w") { |f| f.print "one content" } File.open(File.join(path, "two"), "w") { |f| f.print "two content" } terminus = Puppet::Indirector::FileContent::File.new - request = terminus.indirection.request(:search, "file:///#{path}", nil, :recurse => true) + request = terminus.indirection.request(:search, Puppet::Util.path_to_uri(path).to_s, nil, :recurse => true) expect(terminus.search(request)).to include_in_any_order( file_with_content(File.join(path, "one"), "one content"), file_with_content(File.join(path, "two"), "two content"), directory_named(path)) end end + +describe Puppet::Indirector::DirectFileServer, " when interacting with filesystem metadata" do + include PuppetSpec::Files + include_context 'with supported checksum types' + + before do + @terminus = Puppet::Indirector::FileMetadata::File.new + end + + with_checksum_types("file_metadata", "testfile") do + it "should return the correct metadata" do + request = @terminus.indirection.request(:find, Puppet::Util.path_to_uri(checksum_file).to_s, nil, :checksum_type => checksum_type) + result = @terminus.find(request) + expect_correct_checksum(result, checksum_type, checksum, Puppet::FileServing::Metadata) + end + end + + with_checksum_types("direct_file_server_testing", "testfile") do + it "search of FileServing::Fileset should return the correct metadata" do + request = @terminus.indirection.request(:search, Puppet::Util.path_to_uri(env_path).to_s, nil, :recurse => true, :checksum_type => checksum_type) + result = @terminus.search(request) + + expect(result).to_not be_nil + expect(result.length).to eq(2) + file, dir = result.partition {|x| x.relative_path == 'testfile'} + expect(file.length).to eq(1) + expect(dir.length).to eq(1) + expect_correct_checksum(dir[0], 'ctime', "#{CHECKSUM_STAT_TIME}", Puppet::FileServing::Metadata) + expect_correct_checksum(file[0], checksum_type, checksum, Puppet::FileServing::Metadata) + end + end +end diff --git a/spec/integration/indirector/file_metadata/file_server_spec.rb b/spec/integration/indirector/file_metadata/file_server_spec.rb index 42cd33679..5b4866b5c 100755 --- a/spec/integration/indirector/file_metadata/file_server_spec.rb +++ b/spec/integration/indirector/file_metadata/file_server_spec.rb @@ -1,14 +1,67 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/indirector/file_metadata/file_server' require 'shared_behaviours/file_server_terminus' +require 'puppet_spec/files' + describe Puppet::Indirector::FileMetadata::FileServer, " when finding files" do it_should_behave_like "Puppet::Indirector::FileServerTerminus" + include PuppetSpec::Files + include_context 'with supported checksum types' before do @terminus = Puppet::Indirector::FileMetadata::FileServer.new @test_class = Puppet::FileServing::Metadata + Puppet::FileServing::Configuration.instance_variable_set(:@configuration, nil) + end + + describe "with a plugin environment specified in the request" do + with_checksum_types("file_content_with_env", "mod/lib/file.rb") do + it "should return the correct metadata" do + Puppet.settings[:modulepath] = "/no/such/file" + env = Puppet::Node::Environment.create(:foo, [env_path]) + result = Puppet::FileServing::Metadata.indirection.search("plugins", :environment => env, :checksum_type => checksum_type, :recurse => true) + + expect(result).to_not be_nil + expect(result.length).to eq(2) + result.map {|x| expect(x).to be_instance_of(Puppet::FileServing::Metadata)} + expect_correct_checksum(result.find {|x| x.relative_path == 'file.rb'}, checksum_type, checksum, Puppet::FileServing::Metadata) + end + end + end + + describe "in modules" do + with_checksum_types("file_content", "mymod/files/myfile") do + it "should return the correct metadata" do + env = Puppet::Node::Environment.create(:foo, [env_path]) + result = Puppet::FileServing::Metadata.indirection.find("modules/mymod/myfile", :environment => env, :checksum_type => checksum_type) + expect_correct_checksum(result, checksum_type, checksum, Puppet::FileServing::Metadata) + end + end + end + + describe "when node name expansions are used" do + with_checksum_types("file_server_testing", "mynode/myfile") do + it "should return the correct metadata" do + Puppet::FileSystem.stubs(:exist?).with(checksum_file).returns true + Puppet::FileSystem.stubs(:exist?).with(Puppet[:fileserverconfig]).returns(true) + + # Use a real mount, so the integration is a bit deeper. + mount1 = Puppet::FileServing::Configuration::Mount::File.new("one") + mount1.stubs(:allowed?).returns true + mount1.path = File.join(env_path, "%h") + + parser = stub 'parser', :changed? => false + parser.stubs(:parse).returns("one" => mount1) + + Puppet::FileServing::Configuration::Parser.stubs(:new).returns(parser) + env = Puppet::Node::Environment.create(:foo, []) + + result = Puppet::FileServing::Metadata.indirection.find("one/myfile", :environment => env, :node => "mynode", :checksum_type => checksum_type) + expect_correct_checksum(result, checksum_type, checksum, Puppet::FileServing::Metadata) + end + end end end diff --git a/spec/shared_contexts/checksum.rb b/spec/shared_contexts/checksum.rb new file mode 100644 index 000000000..3310762dd --- /dev/null +++ b/spec/shared_contexts/checksum.rb @@ -0,0 +1,48 @@ +# Shared contexts for testing against all supported checksum types. +# +# These helpers define nested rspec example groups to test code against all our +# supported checksum types. Example groups that need to be run against all +# types should use the `with_checksum_types` helper which will +# create a new example group for each types and will run the given block +# in each example group. + +CHECKSUM_STAT_TIME = Time.now +CHECKSUM_TYPES_TO_TRY = [ + ['md5', 'a7a169ac84bb863b30484d0aa03139c1'], + ['md5lite', '22b4182363e81b326e98231fde616782'], + ['sha256', '47fcae62967db2fb5cba2fc0d9cf3e6767035d763d825ecda535a7b1928b9746'], + ['sha256lite', 'fd50217a2b0286ba25121bf2297bbe6c197933992de67e4e568f19861444ecf8'], + ['ctime', "#{CHECKSUM_STAT_TIME}"], + ['mtime', "#{CHECKSUM_STAT_TIME}"] +] + +shared_context('with supported checksum types') do + + def self.with_checksum_types(path, file, &block) + def expect_correct_checksum(meta, checksum_type, checksum, type) + expect(meta).to_not be_nil + expect(meta).to be_instance_of(type) + expect(meta.checksum_type).to eq(checksum_type) + expect(meta.checksum).to eq("{#{checksum_type}}#{checksum}") + end + + CHECKSUM_TYPES_TO_TRY.each do |checksum_type, checksum| + describe("when checksum_type is #{checksum_type}") do + let(:checksum_type) { checksum_type } + let(:plaintext) { "1\r\n"*4000 } + let(:checksum) { checksum } + let(:env_path) { tmpfile(path) } + let(:checksum_file) { File.join(env_path, file) } + + before do + FileUtils.mkdir_p(File.dirname(checksum_file)) + File.open(checksum_file, "wb") { |f| f.write plaintext } + Puppet::FileSystem.stubs(:stat).returns(stub('stat', :ctime => CHECKSUM_STAT_TIME, :mtime => CHECKSUM_STAT_TIME)) + end + + instance_eval(&block) + end + end + end +end + diff --git a/spec/shared_contexts/checksums.rb b/spec/shared_contexts/digests.rb similarity index 94% rename from spec/shared_contexts/checksums.rb rename to spec/shared_contexts/digests.rb index 8770a9e09..9aefd5775 100644 --- a/spec/shared_contexts/checksums.rb +++ b/spec/shared_contexts/digests.rb @@ -1,55 +1,55 @@ # Shared contexts for testing against all supported digest algorithms. # # These helpers define nested rspec example groups to test code against all our # supported digest algorithms. Example groups that need to be run against all -# algorithms should use the `using_checksums_describe` helper which will +# algorithms should use the `with_digest_algorithms` helper which will # create a new example group for each algorithm and will run the given block # in each example group. # # For each algorithm a shared context is defined for the given algorithm that # has precomputed checksum values and paths. These contexts are included # automatically based on the rspec metadata selected with -# `using_checksums_describe`. +# `with_digest_algorithms`. DIGEST_ALGORITHMS_TO_TRY = ['md5', 'sha256'] shared_context('with supported digest algorithms', :uses_checksums => true) do def self.with_digest_algorithms(&block) DIGEST_ALGORITHMS_TO_TRY.each do |digest_algorithm| describe("when digest_algorithm is #{digest_algorithm}", :digest_algorithm => digest_algorithm) do instance_eval(&block) end end end end shared_context("when digest_algorithm is set to sha256", :digest_algorithm => 'sha256') do before { Puppet[:digest_algorithm] = 'sha256' } after { Puppet[:digest_algorithm] = nil } let(:digest_algorithm) { 'sha256' } let(:plaintext) { "my\r\ncontents" } let(:checksum) { '409a11465ed0938227128b1756c677a8480a8b84814f1963853775e15a74d4b4' } let(:bucket_dir) { '4/0/9/a/1/1/4/6/409a11465ed0938227128b1756c677a8480a8b84814f1963853775e15a74d4b4' } def digest(content) Puppet::Util::Checksums.sha256(content) end end shared_context("when digest_algorithm is set to md5", :digest_algorithm => 'md5') do before { Puppet[:digest_algorithm] = 'md5' } after { Puppet[:digest_algorithm] = nil } let(:digest_algorithm) { 'md5' } let(:plaintext) { "my\r\ncontents" } let(:checksum) { 'f0d7d4e480ad698ed56aeec8b6bd6dea' } let(:bucket_dir) { 'f/0/d/7/d/4/e/4/f0d7d4e480ad698ed56aeec8b6bd6dea' } def digest(content) Puppet::Util::Checksums.md5(content) end end diff --git a/spec/unit/file_serving/content_spec.rb b/spec/unit/file_serving/content_spec.rb index 079902b42..a1e8f6585 100755 --- a/spec/unit/file_serving/content_spec.rb +++ b/spec/unit/file_serving/content_spec.rb @@ -1,112 +1,100 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/file_serving/content' describe Puppet::FileServing::Content do let(:path) { File.expand_path('/path') } it "should be a subclass of Base" do expect(Puppet::FileServing::Content.superclass).to equal(Puppet::FileServing::Base) end it "should indirect file_content" do expect(Puppet::FileServing::Content.indirection.name).to eq(:file_content) end it "should only support the raw format" do expect(Puppet::FileServing::Content.supported_formats).to eq([:raw]) end it "should have a method for collecting its attributes" do expect(Puppet::FileServing::Content.new(path)).to respond_to(:collect) end - it "should not retrieve and store its contents when its attributes are collected if the file is a normal file" do + it "should not retrieve and store its contents when its attributes are collected" do content = Puppet::FileServing::Content.new(path) result = "foo" - Puppet::FileSystem.expects(:lstat).with(path).returns stub('stat', :ftype => "file") - File.expects(:read).with(path).never - content.collect - - expect(content.instance_variable_get("@content")).to be_nil - end - - it "should not attempt to retrieve its contents if the file is a directory" do - content = Puppet::FileServing::Content.new(path) - - result = "foo" - Puppet::FileSystem.expects(:lstat).with(path).returns stub('stat', :ftype => "directory") File.expects(:read).with(path).never content.collect expect(content.instance_variable_get("@content")).to be_nil end it "should have a method for setting its content" do content = Puppet::FileServing::Content.new(path) expect(content).to respond_to(:content=) end it "should make content available when set externally" do content = Puppet::FileServing::Content.new(path) content.content = "foo/bar" expect(content.content).to eq("foo/bar") end it "should be able to create a content instance from raw file contents" do expect(Puppet::FileServing::Content).to respond_to(:from_raw) end it "should create an instance with a fake file name and correct content when converting from raw" do instance = mock 'instance' Puppet::FileServing::Content.expects(:new).with("/this/is/a/fake/path").returns instance instance.expects(:content=).with "foo/bar" expect(Puppet::FileServing::Content.from_raw("foo/bar")).to equal(instance) end it "should return an opened File when converted to raw" do content = Puppet::FileServing::Content.new(path) File.expects(:new).with(path, "rb").returns :file expect(content.to_raw).to eq(:file) end end describe Puppet::FileServing::Content, "when returning the contents" do let(:path) { File.expand_path('/my/path') } let(:content) { Puppet::FileServing::Content.new(path, :links => :follow) } it "should fail if the file is a symlink and links are set to :manage" do content.links = :manage Puppet::FileSystem.expects(:lstat).with(path).returns stub("stat", :ftype => "symlink") expect { content.content }.to raise_error(ArgumentError) end it "should fail if a path is not set" do expect { content.content }.to raise_error(Errno::ENOENT) end it "should raise Errno::ENOENT if the file is absent" do content.path = File.expand_path("/there/is/absolutely/no/chance/that/this/path/exists") expect { content.content }.to raise_error(Errno::ENOENT) end it "should return the contents of the path if the file exists" do Puppet::FileSystem.expects(:stat).with(path).returns(stub('stat', :ftype => 'file')) Puppet::FileSystem.expects(:binread).with(path).returns(:mycontent) expect(content.content).to eq(:mycontent) end it "should cache the returned contents" do Puppet::FileSystem.expects(:stat).with(path).returns(stub('stat', :ftype => 'file')) Puppet::FileSystem.expects(:binread).with(path).returns(:mycontent) content.content # The second run would throw a failure if the content weren't being cached. content.content end end diff --git a/spec/unit/file_serving/metadata_spec.rb b/spec/unit/file_serving/metadata_spec.rb index 0d1e31f01..74d2cec02 100755 --- a/spec/unit/file_serving/metadata_spec.rb +++ b/spec/unit/file_serving/metadata_spec.rb @@ -1,462 +1,476 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/file_serving/metadata' require 'matchers/json' describe Puppet::FileServing::Metadata do let(:foobar) { File.expand_path('/foo/bar') } it "should be a subclass of Base" do expect(Puppet::FileServing::Metadata.superclass).to equal(Puppet::FileServing::Base) end it "should indirect file_metadata" do expect(Puppet::FileServing::Metadata.indirection.name).to eq(:file_metadata) end it "should have a method that triggers attribute collection" do expect(Puppet::FileServing::Metadata.new(foobar)).to respond_to(:collect) end it "should support pson serialization" do expect(Puppet::FileServing::Metadata.new(foobar)).to respond_to(:to_pson) end it "should support deserialization" do expect(Puppet::FileServing::Metadata).to respond_to(:from_data_hash) end describe "when serializing" do let(:metadata) { Puppet::FileServing::Metadata.new(foobar) } it "the data should include the path, relative_path, links, owner, group, mode, checksum, type, and destination" do expect(metadata.to_data_hash.keys.sort).to eq(%w{ path relative_path links owner group mode checksum type destination }.sort) end it "should pass the path in the hash verbatim" do expect(metadata.to_data_hash['path']).to eq(metadata.path) end it "should pass the relative_path in the hash verbatim" do expect(metadata.to_data_hash['relative_path']).to eq(metadata.relative_path) end it "should pass the links in the hash verbatim" do expect(metadata.to_data_hash['links']).to eq(metadata.links) end it "should pass the path owner in the hash verbatim" do expect(metadata.to_data_hash['owner']).to eq(metadata.owner) end it "should pass the group in the hash verbatim" do expect(metadata.to_data_hash['group']).to eq(metadata.group) end it "should pass the mode in the hash verbatim" do expect(metadata.to_data_hash['mode']).to eq(metadata.mode) end it "should pass the ftype in the hash verbatim as the 'type'" do expect(metadata.to_data_hash['type']).to eq(metadata.ftype) end it "should pass the destination verbatim" do expect(metadata.to_data_hash['destination']).to eq(metadata.destination) end it "should pass the checksum in the hash as a nested hash" do expect(metadata.to_data_hash['checksum']).to be_is_a(Hash) end it "should pass the checksum_type in the hash verbatim as the checksum's type" do expect(metadata.to_data_hash['checksum']['type']).to eq(metadata.checksum_type) end it "should pass the checksum in the hash verbatim as the checksum's value" do expect(metadata.to_data_hash['checksum']['value']).to eq(metadata.checksum) end end end describe Puppet::FileServing::Metadata, :uses_checksums => true do include JSONMatchers include PuppetSpec::Files shared_examples_for "metadata collector" do let(:metadata) do data = described_class.new(path) data.collect data end describe "when collecting attributes" do describe "when managing files" do let(:path) { tmpfile('file_serving_metadata') } + let(:time) { Time.now } before :each do FileUtils.touch(path) end describe "checksumming" do with_digest_algorithms do before :each do File.open(path, "wb") {|f| f.print(plaintext)} end it "should default to a checksum of the proper type with the file's current checksum" do expect(metadata.checksum).to eq("{#{digest_algorithm}}#{checksum}") end - it "should give a mtime checksum when checksum_type is set" do - time = Time.now - metadata.checksum_type = "mtime" - metadata.expects(:mtime_file).returns(@time) + it "should give a #{Puppet[:digest_algorithm]} when checksum_type is set" do + Puppet[:digest_algorithm] = nil + metadata.checksum_type = digest_algorithm metadata.collect - expect(metadata.checksum).to eq("{mtime}#{@time}") + expect(metadata.checksum).to eq("{#{digest_algorithm}}#{checksum}") end end + + it "should give a mtime checksum when checksum_type is set" do + metadata.checksum_type = "mtime" + metadata.expects(:mtime_file).returns(time) + metadata.collect + expect(metadata.checksum).to eq("{mtime}#{time}") + end + + it "should give a ctime checksum when checksum_type is set" do + metadata.checksum_type = "ctime" + metadata.expects(:ctime_file).returns(time) + metadata.collect + expect(metadata.checksum).to eq("{ctime}#{time}") + end end it "should validate against the schema" do expect(metadata.to_pson).to validate_against('api/schemas/file_metadata.json') end end describe "when managing directories" do let(:path) { tmpdir('file_serving_metadata_dir') } let(:time) { Time.now } before :each do metadata.expects(:ctime_file).returns(time) end it "should only use checksums of type 'ctime' for directories" do metadata.collect expect(metadata.checksum).to eq("{ctime}#{time}") end it "should only use checksums of type 'ctime' for directories even if checksum_type set" do metadata.checksum_type = "mtime" metadata.expects(:mtime_file).never metadata.collect expect(metadata.checksum).to eq("{ctime}#{time}") end it "should validate against the schema" do metadata.collect expect(metadata.to_pson).to validate_against('api/schemas/file_metadata.json') end end end end describe "WindowsStat", :if => Puppet.features.microsoft_windows? do include PuppetSpec::Files it "should return default owner, group and mode when the given path has an invalid DACL (such as a non-NTFS volume)" do invalid_error = Puppet::Util::Windows::Error.new('Invalid DACL', 1336) path = tmpfile('foo') FileUtils.touch(path) Puppet::Util::Windows::Security.stubs(:get_owner).with(path).raises(invalid_error) Puppet::Util::Windows::Security.stubs(:get_group).with(path).raises(invalid_error) Puppet::Util::Windows::Security.stubs(:get_mode).with(path).raises(invalid_error) stat = Puppet::FileSystem.stat(path) win_stat = Puppet::FileServing::Metadata::WindowsStat.new(stat, path) expect(win_stat.owner).to eq('S-1-5-32-544') expect(win_stat.group).to eq('S-1-0-0') expect(win_stat.mode).to eq(0644) end it "should still raise errors that are not the result of an 'Invalid DACL'" do invalid_error = ArgumentError.new('bar') path = tmpfile('bar') FileUtils.touch(path) Puppet::Util::Windows::Security.stubs(:get_owner).with(path, :use).raises(invalid_error) Puppet::Util::Windows::Security.stubs(:get_group).with(path, :use).raises(invalid_error) Puppet::Util::Windows::Security.stubs(:get_mode).with(path, :use).raises(invalid_error) stat = Puppet::FileSystem.stat(path) expect { Puppet::FileServing::Metadata::WindowsStat.new(stat, path, :use) }.to raise_error("Unsupported Windows source permissions option use") end end shared_examples_for "metadata collector symlinks" do let(:metadata) do data = described_class.new(path) data.collect data end describe "when collecting attributes" do describe "when managing links" do # 'path' is a link that points to 'target' let(:path) { tmpfile('file_serving_metadata_link') } let(:target) { tmpfile('file_serving_metadata_target') } let(:fmode) { Puppet::FileSystem.lstat(path).mode & 0777 } before :each do File.open(target, "wb") {|f| f.print('some content')} set_mode(0644, target) Puppet::FileSystem.symlink(target, path) end it "should read links instead of returning their checksums" do expect(metadata.destination).to eq(target) end it "should validate against the schema" do expect(metadata.to_pson).to validate_against('api/schemas/file_metadata.json') end end end describe Puppet::FileServing::Metadata, " when finding the file to use for setting attributes" do let(:path) { tmpfile('file_serving_metadata_find_file') } before :each do File.open(path, "wb") {|f| f.print('some content')} set_mode(0755, path) end it "should accept a base path to which the file should be relative" do dir = tmpdir('metadata_dir') metadata = described_class.new(dir) metadata.relative_path = 'relative_path' FileUtils.touch(metadata.full_path) metadata.collect end it "should use the set base path if one is not provided" do metadata.collect end it "should raise an exception if the file does not exist" do File.delete(path) expect { metadata.collect}.to raise_error(Errno::ENOENT) end it "should validate against the schema" do expect(metadata.to_pson).to validate_against('api/schemas/file_metadata.json') end end end describe "on POSIX systems", :if => Puppet.features.posix? do let(:owner) {10} let(:group) {20} before :each do File::Stat.any_instance.stubs(:uid).returns owner File::Stat.any_instance.stubs(:gid).returns group end describe "when collecting attributes when managing files" do let(:metadata) do data = described_class.new(path) data.collect data end let(:path) { tmpfile('file_serving_metadata') } before :each do FileUtils.touch(path) end it "should set the owner to the Process's current owner" do expect(metadata.owner).to eq(Process.euid) end it "should set the group to the Process's current group" do expect(metadata.group).to eq(Process.egid) end it "should set the mode to the default mode" do set_mode(33261, path) expect(metadata.mode).to eq(0644) end end it_should_behave_like "metadata collector" it_should_behave_like "metadata collector symlinks" def set_mode(mode, path) File.chmod(mode, path) end end describe "on Windows systems", :if => Puppet.features.microsoft_windows? do let(:owner) {'S-1-1-50'} let(:group) {'S-1-1-51'} before :each do require 'puppet/util/windows/security' Puppet::Util::Windows::Security.stubs(:get_owner).returns owner Puppet::Util::Windows::Security.stubs(:get_group).returns group end describe "when collecting attributes when managing files" do let(:metadata) do data = described_class.new(path) data.collect data end let(:path) { tmpfile('file_serving_metadata') } before :each do FileUtils.touch(path) end it "should set the owner to the Process's current owner" do expect(metadata.owner).to eq("S-1-5-32-544") end it "should set the group to the Process's current group" do expect(metadata.group).to eq("S-1-0-0") end it "should set the mode to the default mode" do set_mode(33261, path) expect(metadata.mode).to eq(0644) end end it_should_behave_like "metadata collector" it_should_behave_like "metadata collector symlinks" if Puppet.features.manages_symlinks? describe "if ACL metadata cannot be collected" do let(:path) { tmpdir('file_serving_metadata_acl') } let(:metadata) do data = described_class.new(path) data.collect data end let (:invalid_dacl_error) do Puppet::Util::Windows::Error.new('Invalid DACL', 1336) end it "should default owner" do Puppet::Util::Windows::Security.stubs(:get_owner).returns nil expect(metadata.owner).to eq('S-1-5-32-544') end it "should default group" do Puppet::Util::Windows::Security.stubs(:get_group).returns nil expect(metadata.group).to eq('S-1-0-0') end it "should default mode" do Puppet::Util::Windows::Security.stubs(:get_mode).returns nil expect(metadata.mode).to eq(0644) end describe "when the path raises an Invalid ACL error" do # these simulate the behavior of a symlink file whose target does not support ACLs it "should default owner" do Puppet::Util::Windows::Security.stubs(:get_owner).raises(invalid_dacl_error) expect(metadata.owner).to eq('S-1-5-32-544') end it "should default group" do Puppet::Util::Windows::Security.stubs(:get_group).raises(invalid_dacl_error) expect(metadata.group).to eq('S-1-0-0') end it "should default mode" do Puppet::Util::Windows::Security.stubs(:get_mode).raises(invalid_dacl_error) expect(metadata.mode).to eq(0644) end end end def set_mode(mode, path) Puppet::Util::Windows::Security.set_mode(mode, path) end end end describe Puppet::FileServing::Metadata, " when pointing to a link", :if => Puppet.features.manages_symlinks?, :uses_checksums => true do with_digest_algorithms do describe "when links are managed" do before do path = "/base/path/my/file" @file = Puppet::FileServing::Metadata.new(path, :links => :manage) stat = stub("stat", :uid => 1, :gid => 2, :ftype => "link", :mode => 0755) stub_file = stub(:readlink => "/some/other/path", :lstat => stat) Puppet::FileSystem.expects(:lstat).with(path).at_least_once.returns stat Puppet::FileSystem.expects(:readlink).with(path).at_least_once.returns "/some/other/path" @file.stubs("#{digest_algorithm}_file".intern).returns(checksum) # Remove these when :managed links are no longer checksumed. if Puppet.features.microsoft_windows? win_stat = stub('win_stat', :owner => 'snarf', :group => 'thundercats', :ftype => 'link', :mode => 0755) Puppet::FileServing::Metadata::WindowsStat.stubs(:new).returns win_stat end end it "should store the destination of the link in :destination if links are :manage" do @file.collect expect(@file.destination).to eq("/some/other/path") end pending "should not collect the checksum if links are :manage" do # We'd like this to be true, but we need to always collect the checksum because in the server/client/server round trip we lose the distintion between manage and follow. @file.collect expect(@file.checksum).to be_nil end it "should collect the checksum if links are :manage" do # see pending note above @file.collect expect(@file.checksum).to eq("{#{digest_algorithm}}#{checksum}") end end describe "when links are followed" do before do path = "/base/path/my/file" @file = Puppet::FileServing::Metadata.new(path, :links => :follow) stat = stub("stat", :uid => 1, :gid => 2, :ftype => "file", :mode => 0755) Puppet::FileSystem.expects(:stat).with(path).at_least_once.returns stat Puppet::FileSystem.expects(:readlink).never if Puppet.features.microsoft_windows? win_stat = stub('win_stat', :owner => 'snarf', :group => 'thundercats', :ftype => 'file', :mode => 0755) Puppet::FileServing::Metadata::WindowsStat.stubs(:new).returns win_stat end @file.stubs("#{digest_algorithm}_file".intern).returns(checksum) end it "should not store the destination of the link in :destination if links are :follow" do @file.collect expect(@file.destination).to be_nil end it "should collect the checksum if links are :follow" do @file.collect expect(@file.checksum).to eq("{#{digest_algorithm}}#{checksum}") end end end end diff --git a/spec/unit/file_serving/terminus_helper_spec.rb b/spec/unit/file_serving/terminus_helper_spec.rb index edaf3ff95..d274375d1 100755 --- a/spec/unit/file_serving/terminus_helper_spec.rb +++ b/spec/unit/file_serving/terminus_helper_spec.rb @@ -1,94 +1,113 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/file_serving/terminus_helper' describe Puppet::FileServing::TerminusHelper do before do @helper = Object.new @helper.extend(Puppet::FileServing::TerminusHelper) @model = mock 'model' @helper.stubs(:model).returns(@model) @request = stub 'request', :key => "url", :options => {} @fileset = stub 'fileset', :files => [], :path => "/my/file" Puppet::FileServing::Fileset.stubs(:new).with("/my/file", {}).returns(@fileset) end + it "should find a file with absolute path" do + file = stub 'file', :collect => nil + file.expects(:collect).with(nil) + @model.expects(:new).with("/my/file", {:relative_path => nil}).returns(file) + @helper.path2instance(@request, "/my/file") + end + + it "should pass through links, checksum_type, and source_permissions" do + file = stub 'file', :checksum_type= => nil, :links= => nil, :collect => nil + [[:checksum_type, :sha256], [:links, true]].each {|k, v| + file.expects(k.to_s+'=').with(v) + @request.options[k] = v + } + @request.options[:source_permissions] = :yes + file.expects(:collect).with(:yes) + @model.expects(:new).with("/my/file", {:relative_path => :file}).returns(file) + @helper.path2instance(@request, "/my/file", {:relative_path => :file}) + end + it "should use a fileset to find paths" do @fileset = stub 'fileset', :files => [], :path => "/my/files" Puppet::FileServing::Fileset.expects(:new).with { |key, options| key == "/my/file" }.returns(@fileset) @helper.path2instances(@request, "/my/file") end it "should support finding across multiple paths by merging the filesets" do first = stub 'fileset', :files => [], :path => "/first/file" Puppet::FileServing::Fileset.expects(:new).with { |path, options| path == "/first/file" }.returns(first) second = stub 'fileset', :files => [], :path => "/second/file" Puppet::FileServing::Fileset.expects(:new).with { |path, options| path == "/second/file" }.returns(second) Puppet::FileServing::Fileset.expects(:merge).with(first, second).returns({}) @helper.path2instances(@request, "/first/file", "/second/file") end it "should pass the indirection request to the Fileset at initialization" do Puppet::FileServing::Fileset.expects(:new).with { |path, options| options == @request }.returns @fileset @helper.path2instances(@request, "/my/file") end describe "when creating instances" do before do @request.stubs(:key).returns "puppet://host/mount/dir" @one = stub 'one', :links= => nil, :collect => nil @two = stub 'two', :links= => nil, :collect => nil @fileset = stub 'fileset', :files => %w{one two}, :path => "/my/file" Puppet::FileServing::Fileset.stubs(:new).returns(@fileset) end it "should set each returned instance's path to the original path" do @model.expects(:new).with { |key, options| key == "/my/file" }.returns(@one) @model.expects(:new).with { |key, options| key == "/my/file" }.returns(@two) @helper.path2instances(@request, "/my/file") end it "should set each returned instance's relative path to the file-specific path" do @model.expects(:new).with { |key, options| options[:relative_path] == "one" }.returns(@one) @model.expects(:new).with { |key, options| options[:relative_path] == "two" }.returns(@two) @helper.path2instances(@request, "/my/file") end it "should set the links value on each instance if one is provided" do @one.expects(:links=).with :manage @two.expects(:links=).with :manage @model.expects(:new).returns(@one) @model.expects(:new).returns(@two) @request.options[:links] = :manage @helper.path2instances(@request, "/my/file") end it "should set the request checksum_type if one is provided" do @one.expects(:checksum_type=).with :test @two.expects(:checksum_type=).with :test @model.expects(:new).returns(@one) @model.expects(:new).returns(@two) @request.options[:checksum_type] = :test @helper.path2instances(@request, "/my/file") end it "should collect the instance's attributes" do @one.expects(:collect) @two.expects(:collect) @model.expects(:new).returns(@one) @model.expects(:new).returns(@two) @helper.path2instances(@request, "/my/file") end end end diff --git a/spec/unit/indirector/direct_file_server_spec.rb b/spec/unit/indirector/direct_file_server_spec.rb index 00764c3b9..32cdc97cd 100755 --- a/spec/unit/indirector/direct_file_server_spec.rb +++ b/spec/unit/indirector/direct_file_server_spec.rb @@ -1,80 +1,90 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/indirector/direct_file_server' describe Puppet::Indirector::DirectFileServer do before :all 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) module Testing; end @direct_file_class = class Testing::Mytype < Puppet::Indirector::DirectFileServer self end @server = @direct_file_class.new @path = File.expand_path('/my/local') @uri = Puppet::Util.path_to_uri(@path).to_s @request = Puppet::Indirector::Request.new(:mytype, :find, @uri, nil) end describe Puppet::Indirector::DirectFileServer, "when finding a single file" do it "should return nil if the file does not exist" do Puppet::FileSystem.expects(:exist?).with(@path).returns false expect(@server.find(@request)).to be_nil end it "should return a Content instance created with the full path to the file if the file exists" do Puppet::FileSystem.expects(:exist?).with(@path).returns true - @model.expects(:new).returns(:mycontent) - expect(@server.find(@request)).to eq(:mycontent) + mycontent = stub 'content', :collect => nil + mycontent.expects(:collect) + @model.expects(:new).returns(mycontent) + expect(@server.find(@request)).to eq(mycontent) end end describe Puppet::Indirector::DirectFileServer, "when creating the instance for a single found file" do before do @data = mock 'content' @data.stubs(:collect) Puppet::FileSystem.expects(:exist?).with(@path).returns true end it "should pass the full path to the instance" do @model.expects(:new).with { |key, options| key == @path }.returns(@data) @server.find(@request) end it "should pass the :links setting on to the created Content instance if the file exists and there is a value for :links" do @model.expects(:new).returns(@data) @data.expects(:links=).with(:manage) @request.stubs(:options).returns(:links => :manage) @server.find(@request) end + + it "should set 'checksum_type' on the instances if it is set in the request options" do + @model.expects(:new).returns(@data) + @data.expects(:checksum_type=).with :checksum + + @request.stubs(:options).returns(:checksum_type => :checksum) + @server.find(@request) + end end describe Puppet::Indirector::DirectFileServer, "when searching for multiple files" do it "should return nil if the file does not exist" do Puppet::FileSystem.expects(:exist?).with(@path).returns false expect(@server.find(@request)).to be_nil end it "should use :path2instances from the terminus_helper to return instances if the file exists" do Puppet::FileSystem.expects(:exist?).with(@path).returns true @server.expects(:path2instances) @server.search(@request) end it "should pass the original request to :path2instances" do Puppet::FileSystem.expects(:exist?).with(@path).returns true @server.expects(:path2instances).with(@request, @path) @server.search(@request) end end end diff --git a/spec/unit/indirector/file_metadata/file_spec.rb b/spec/unit/indirector/file_metadata/file_spec.rb index 85b4cf3ea..55e383b55 100755 --- a/spec/unit/indirector/file_metadata/file_spec.rb +++ b/spec/unit/indirector/file_metadata/file_spec.rb @@ -1,50 +1,58 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/indirector/file_metadata/file' describe Puppet::Indirector::FileMetadata::File do it "should be registered with the file_metadata indirection" do expect(Puppet::Indirector::Terminus.terminus_class(:file_metadata, :file)).to equal(Puppet::Indirector::FileMetadata::File) end it "should be a subclass of the DirectFileServer terminus" do expect(Puppet::Indirector::FileMetadata::File.superclass).to equal(Puppet::Indirector::DirectFileServer) end describe "when creating the instance for a single found file" do before do @metadata = Puppet::Indirector::FileMetadata::File.new @path = File.expand_path('/my/local') @uri = Puppet::Util.path_to_uri(@path).to_s @data = mock 'metadata' @data.stubs(:collect) Puppet::FileSystem.expects(:exist?).with(@path).returns true @request = Puppet::Indirector::Request.new(:file_metadata, :find, @uri, nil) end it "should collect its attributes when a file is found" do @data.expects(:collect) Puppet::FileServing::Metadata.expects(:new).returns(@data) expect(@metadata.find(@request)).to eq(@data) end end describe "when searching for multiple files" do before do @metadata = Puppet::Indirector::FileMetadata::File.new @path = File.expand_path('/my/local') @uri = Puppet::Util.path_to_uri(@path).to_s @request = Puppet::Indirector::Request.new(:file_metadata, :find, @uri, nil) end it "should collect the attributes of the instances returned" do Puppet::FileSystem.expects(:exist?).with(@path).returns true - @metadata.expects(:path2instances).returns( [mock("one", :collect => nil), mock("two", :collect => nil)] ) - @metadata.search(@request) + Puppet::FileServing::Fileset.expects(:new).with(@path, @request).returns mock("fileset") + Puppet::FileServing::Fileset.expects(:merge).returns [["one", @path], ["two", @path]] + + one = mock("one", :collect => nil) + Puppet::FileServing::Metadata.expects(:new).with(@path, {:relative_path => "one"}).returns one + + two = mock("two", :collect => nil) + Puppet::FileServing::Metadata.expects(:new).with(@path, {:relative_path => "two"}).returns two + + expect(@metadata.search(@request)).to eq([one, two]) end end end diff --git a/spec/unit/indirector/file_server_spec.rb b/spec/unit/indirector/file_server_spec.rb index 8cbccdc37..167228e47 100755 --- a/spec/unit/indirector/file_server_spec.rb +++ b/spec/unit/indirector/file_server_spec.rb @@ -1,263 +1,281 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/indirector/file_server' require 'puppet/file_serving/configuration' describe Puppet::Indirector::FileServer do before :all 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) module Testing; end @file_server_class = class Testing::MyFileServer < Puppet::Indirector::FileServer self end end before :each do @file_server = @file_server_class.new @uri = "puppet://host/my/local/file" @configuration = mock 'configuration' Puppet::FileServing::Configuration.stubs(:configuration).returns(@configuration) @request = Puppet::Indirector::Request.new(:myind, :mymethod, @uri, :environment => "myenv") end describe "when finding files" do before do @mount = stub 'mount', :find => nil @instance = stub('instance', :links= => nil, :collect => nil) end it "should use the configuration to find the mount and relative path" do @configuration.expects(:split_path).with(@request) @file_server.find(@request) end it "should return nil if it cannot find the mount" do @configuration.expects(:split_path).with(@request).returns(nil, nil) expect(@file_server.find(@request)).to be_nil end it "should use the mount to find the full path" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:find).with { |key, request| key == "rel/path" } @file_server.find(@request) end it "should pass the request when finding a file" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:find).with { |key, request| request == @request } @file_server.find(@request) end it "should return nil if it cannot find a full path" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:find).with { |key, request| key == "rel/path" }.returns nil expect(@file_server.find(@request)).to be_nil end it "should create an instance with the found path" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:find).with { |key, request| key == "rel/path" }.returns "/my/file" - @model.expects(:new).with("/my/file").returns @instance + @model.expects(:new).with("/my/file", {:relative_path => nil}).returns @instance expect(@file_server.find(@request)).to equal(@instance) end it "should set 'links' on the instance if it is set in the request options" do @request.options[:links] = true @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:find).with { |key, request| key == "rel/path" }.returns "/my/file" - @model.expects(:new).with("/my/file").returns @instance + @model.expects(:new).with("/my/file", {:relative_path => nil}).returns @instance @instance.expects(:links=).with(true) expect(@file_server.find(@request)).to equal(@instance) end it "should collect the instance" do @request.options[:links] = true @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:find).with { |key, request| key == "rel/path" }.returns "/my/file" - @model.expects(:new).with("/my/file").returns @instance + @model.expects(:new).with("/my/file", {:relative_path => nil}).returns @instance @instance.expects(:collect) expect(@file_server.find(@request)).to equal(@instance) end end describe "when searching for instances" do before do @mount = stub 'mount', :search => nil @instance = stub('instance', :links= => nil, :collect => nil) end it "should use the configuration to search the mount and relative path" do @configuration.expects(:split_path).with(@request) @file_server.search(@request) end it "should return nil if it cannot search the mount" do @configuration.expects(:split_path).with(@request).returns(nil, nil) expect(@file_server.search(@request)).to be_nil end it "should use the mount to search for the full paths" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:search).with { |key, request| key == "rel/path" } @file_server.search(@request) end it "should pass the request" do @configuration.stubs(:split_path).returns([@mount, "rel/path"]) @mount.expects(:search).with { |key, request| request == @request } @file_server.search(@request) end it "should return nil if searching does not find any full paths" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:search).with { |key, request| key == "rel/path" }.returns nil expect(@file_server.search(@request)).to be_nil end it "should create a fileset with each returned path and merge them" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:search).with { |key, request| key == "rel/path" }.returns %w{/one /two} Puppet::FileSystem.stubs(:exist?).returns true one = mock 'fileset_one' Puppet::FileServing::Fileset.expects(:new).with("/one", @request).returns(one) two = mock 'fileset_two' Puppet::FileServing::Fileset.expects(:new).with("/two", @request).returns(two) Puppet::FileServing::Fileset.expects(:merge).with(one, two).returns [] @file_server.search(@request) end it "should create an instance with each path resulting from the merger of the filesets" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:search).with { |key, request| key == "rel/path" }.returns [] Puppet::FileSystem.stubs(:exist?).returns true Puppet::FileServing::Fileset.expects(:merge).returns("one" => "/one", "two" => "/two") one = stub 'one', :collect => nil @model.expects(:new).with("/one", :relative_path => "one").returns one two = stub 'two', :collect => nil @model.expects(:new).with("/two", :relative_path => "two").returns two # order can't be guaranteed result = @file_server.search(@request) expect(result).to be_include(one) expect(result).to be_include(two) expect(result.length).to eq(2) end it "should set 'links' on the instances if it is set in the request options" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:search).with { |key, request| key == "rel/path" }.returns [] Puppet::FileSystem.stubs(:exist?).returns true Puppet::FileServing::Fileset.expects(:merge).returns("one" => "/one") one = stub 'one', :collect => nil @model.expects(:new).with("/one", :relative_path => "one").returns one one.expects(:links=).with true @request.options[:links] = true @file_server.search(@request) end + it "should set 'checksum_type' on the instances if it is set in the request options" do + @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) + + @mount.expects(:search).with { |key, request| key == "rel/path" }.returns [] + + Puppet::FileSystem.stubs(:exist?).returns true + + Puppet::FileServing::Fileset.expects(:merge).returns("one" => "/one") + + one = stub 'one', :collect => nil + @model.expects(:new).with("/one", :relative_path => "one").returns one + + one.expects(:checksum_type=).with :checksum + @request.options[:checksum_type] = :checksum + + @file_server.search(@request) + end + it "should collect the instances" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:search).with { |key, options| key == "rel/path" }.returns [] Puppet::FileSystem.stubs(:exist?).returns true Puppet::FileServing::Fileset.expects(:merge).returns("one" => "/one") one = mock 'one' @model.expects(:new).with("/one", :relative_path => "one").returns one one.expects(:collect) @file_server.search(@request) end end describe "when checking authorization" do before do @request.method = :find @mount = stub 'mount' @configuration.stubs(:split_path).with(@request).returns([@mount, "rel/path"]) @request.stubs(:node).returns("mynode") @request.stubs(:ip).returns("myip") @mount.stubs(:allowed?).with("mynode", "myip").returns "something" end it "should return false when destroying" do @request.method = :destroy expect(@file_server).not_to be_authorized(@request) end it "should return false when saving" do @request.method = :save expect(@file_server).not_to be_authorized(@request) end it "should use the configuration to find the mount and relative path" do @configuration.expects(:split_path).with(@request) @file_server.authorized?(@request) end it "should return false if it cannot find the mount" do @configuration.expects(:split_path).with(@request).returns(nil, nil) expect(@file_server).not_to be_authorized(@request) end it "should return the results of asking the mount whether the node and IP are authorized" do expect(@file_server.authorized?(@request)).to eq("something") end end end