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 d033f3bc6..5b4866b5c 100755 --- a/spec/integration/indirector/file_metadata/file_server_spec.rb +++ b/spec/integration/indirector/file_metadata/file_server_spec.rb @@ -1,91 +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 - it "should find plugin file content in the environment specified in the request" do - path = tmpfile("file_content_with_env") - - Dir.mkdir(path) - - modpath = File.join(path, "mod") - FileUtils.mkdir_p(File.join(modpath, "lib")) - file = File.join(modpath, "lib", "file.rb") - File.open(file, "wb") { |f| f.write "1\r\n" } - - Puppet.settings[:modulepath] = "/no/such/file" - - env = Puppet::Node::Environment.create(:foo, [path]) - - result = Puppet::FileServing::Metadata.indirection.search("plugins", :environment => env, :recurse => true) - - result.should_not be_nil - result.length.should == 2 - result.map {|x| x.should be_instance_of(Puppet::FileServing::Metadata) } - result.find {|x| x.relative_path == 'file.rb' }.checksum.should == "{md5}a5ea0ad9260b1550a14cc58d2c39b03d" + 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 - it "should find file metadata in modules" do - path = tmpfile("file_content") - - Dir.mkdir(path) - - modpath = File.join(path, "mymod") - FileUtils.mkdir_p(File.join(modpath, "files")) - file = File.join(modpath, "files", "myfile") - File.open(file, "wb") { |f| f.write "1\r\n" } - - env = Puppet::Node::Environment.create(:foo, [path]) - - result = Puppet::FileServing::Metadata.indirection.find("modules/mymod/myfile", :environment => env) - - result.should_not be_nil - result.should be_instance_of(Puppet::FileServing::Metadata) - result.checksum.should == "{md5}a5ea0ad9260b1550a14cc58d2c39b03d" + 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 - it "should find file content in files when node name expansions are used" do - Puppet::FileSystem.stubs(:exist?).returns true - Puppet::FileSystem.stubs(:exist?).with(Puppet[:fileserverconfig]).returns(true) - - path = tmpfile("file_server_testing") - - Dir.mkdir(path) - subdir = File.join(path, "mynode") - Dir.mkdir(subdir) - File.open(File.join(subdir, "myfile"), "wb") { |f| f.write "1\r\n" } - - # 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(path, "%h") - - parser = stub 'parser', :changed? => false - parser.stubs(:parse).returns("one" => mount1) - - Puppet::FileServing::Configuration::Parser.stubs(:new).returns(parser) + 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) - path = File.join(path, "myfile") + # 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") - env = Puppet::Node::Environment.create(:foo, []) + parser = stub 'parser', :changed? => false + parser.stubs(:parse).returns("one" => mount1) - result = Puppet::FileServing::Metadata.indirection.find("one/myfile", :environment => env, :node => "mynode") + Puppet::FileServing::Configuration::Parser.stubs(:new).returns(parser) + env = Puppet::Node::Environment.create(:foo, []) - result.should_not be_nil - result.should be_instance_of(Puppet::FileServing::Metadata) - result.checksum.should == "{md5}a5ea0ad9260b1550a14cc58d2c39b03d" + 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/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/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