diff --git a/spec/unit/indirector/catalog/static_compiler_spec.rb b/spec/unit/indirector/catalog/static_compiler_spec.rb new file mode 100644 index 000000000..ac21efc60 --- /dev/null +++ b/spec/unit/indirector/catalog/static_compiler_spec.rb @@ -0,0 +1,193 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' +require 'puppet/indirector/catalog/static_compiler' +require 'puppet/file_serving/metadata' +require 'puppet/file_serving/content' +require 'yaml' + +describe Puppet::Resource::Catalog::StaticCompiler do + before :all do + @num_file_resources = 10 + end + + let(:request) do + Puppet::Indirector::Request.new(:the_indirection_named_foo, + :find, + "the-node-named-foo", + :environment => "production") + end + + describe "#find" do + it "returns a catalog" do + subject.find(request).should be_a_kind_of(Puppet::Resource::Catalog) + end + + it "returns nil if there is no compiled catalog" do + compiler = mock('compiler indirection') + compiler.expects(:find).with(request).returns(nil) + + subject.expects(:compiler).returns(compiler) + subject.find(request).should be_nil + end + + describe "a catalog with file resources containing source parameters with puppet:// URIs" do + it "filters file resource source URI's to checksums" do + stub_the_compiler + resource_catalog = subject.find(request) + resource_catalog.resources.each do |resource| + next unless resource.type == "File" + resource[:content].should == "{md5}361fadf1c712e812d198c4cab5712a79" + resource[:source].should be_nil + end + end + + it "does not modify file resources with non-puppet:// URI's" do + uri = "/this/is/not/a/puppet/uri.txt" + stub_the_compiler(:source => uri) + resource_catalog = subject.find(request) + resource_catalog.resources.each do |resource| + next unless resource.type == "File" + resource[:content].should be_nil + resource[:source].should == uri + end + end + + it "copies the owner, group and mode from the fileserer" do + stub_the_compiler + resource_catalog = subject.find(request) + resource_catalog.resources.each do |resource| + next unless resource.type == "File" + resource[:owner].should == 0 + resource[:group].should == 0 + resource[:mode].should == 420 + end + end + end + end + + describe "(#15193) when storing content to the filebucket" do + it "explicitly uses the indirection method" do + # Do not stub the store_content method which is stubbed by default in the + # value of the stub_methods option. + stub_the_compiler(:stub_methods => false) + + # We expect the content to be retrieved from the FileServer ... + fake_content = mock('FileServer Content') + fake_content.expects(:content).returns("HELLO WORLD") + + # Mock the FileBucket to behave as if the file content does not exist. + # NOTE, we're simulating the first call returning false, indicating the + # file is not present, then all subsequent calls returning true. This + # mocked behavior is intended to replicate the real behavior of the same + # file being stored to the filebucket multiple times. + Puppet::FileBucket::File.indirection. + expects(:find).times(@num_file_resources). + returns(false).then.returns(true) + + Puppet::FileServing::Content.indirection. + expects(:find).once. + returns(fake_content) + + # Once retrived from the FileServer, we expect the file to be stored into + # the FileBucket only once. All of the file resources in the fake + # catalog have the same content. + Puppet::FileBucket::File.indirection.expects(:save).once.with do |file| + file.contents == "HELLO WORLD" + end + + # Obtain the Static Catalog + resource_catalog = subject.find(request) + + # Ensure all of the file resources were filtered + resource_catalog.resources.each do |resource| + next unless resource.type == "File" + resource[:content].should == "{md5}361fadf1c712e812d198c4cab5712a79" + resource[:source].should be_nil + end + end + end + + # Spec helper methods + + def stub_the_compiler(options = {:stub_methods => [:store_content]}) + # Build a resource catalog suitable for specifying the behavior of the + # static compiler. + compiler = mock('indirection terminus compiler') + compiler.stubs(:find).returns(build_catalog(options)) + subject.stubs(:compiler).returns(compiler) + # Mock the store content method to prevent copying the contents to the + # file bucket. + (options[:stub_methods] || []).each do |mthd| + subject.stubs(mthd) + end + end + + def build_catalog(options = {}) + options = options.dup + options[:source] ||= 'puppet:///modules/mymodule/config_file.txt' + options[:request] ||= request + + # Build a catalog suitable for the static compiler to operate on + catalog = Puppet::Resource::Catalog.new("#{options[:request].key}") + + # Mock out the fileserver, otherwise converting the catalog to a + fake_fileserver_metadata = fileserver_metadata(options) + + # Stub the call to the FileServer metadata API so we don't have to have + # a real fileserver initialized for testing. + Puppet::FileServing::Metadata. + indirection.stubs(:find). + with(options[:source].sub('puppet:///','')). + returns(fake_fileserver_metadata) + + # I want a resource that all the file resources require and another + # that requires them. + resources = Array.new + resources << Puppet::Resource.new("notify", "alpha") + resources << Puppet::Resource.new("notify", "omega") + + # Create some File resources with source parameters. + 1.upto(@num_file_resources) do |idx| + parameters = { + :ensure => 'file', + :source => options[:source], + :require => "Notify[alpha]", + :before => "Notify[omega]" + } + # The static compiler does not operate on a RAL catalog, so we're + # using Puppet::Resource to produce a resource catalog. + rsrc = Puppet::Resource.new("file", "/tmp/file_#{idx}.txt", :parameters => parameters) + rsrc.file = 'site.pp' + rsrc.line = idx + resources << rsrc + end + + resources.each do |rsrc| + catalog.add_resource(rsrc) + end + + # Return the resource catalog + catalog + end + + def fileserver_metadata(options = {}) + yaml = <