diff --git a/files/concatfragments.rb b/files/concatfragments.rb index 73fd7f9..cb66b03 100755 --- a/files/concatfragments.rb +++ b/files/concatfragments.rb @@ -1,137 +1,138 @@ +#!/usr/bin/env ruby # Script to concat files to a config file. # # Given a directory like this: # /path/to/conf.d # |-- fragments # | |-- 00_named.conf # | |-- 10_domain.net # | `-- zz_footer # # The script supports a test option that will build the concat file to a temp location and # use /usr/bin/cmp to verify if it should be run or not. This would result in the concat happening # twice on each run but gives you the option to have an unless option in your execs to inhibit rebuilds. # # Without the test option and the unless combo your services that depend on the final file would end up # restarting on each run, or in other manifest models some changes might get missed. # # OPTIONS: # -o The file to create from the sources # -d The directory where the fragments are kept # -t Test to find out if a build is needed, basically concats the files to a temp # location and compare with what's in the final location, return codes are designed # for use with unless on an exec resource # -w Add a shell style comment at the top of the created file to warn users that it # is generated by puppet # -f Enables the creation of empty output files when no fragments are found # -n Sort the output numerically rather than the default alpha sort # # the command: # # concatfragments.rb -o /path/to/conffile.cfg -d /path/to/conf.d # # creates /path/to/conf.d/fragments.concat and copies the resulting # file to /path/to/conffile.cfg. The files will be sorted alphabetically # pass the -n switch to sort numerically. # # The script does error checking on the various dirs and files to make # sure things don't fail. require 'optparse' require 'fileutils' settings = { :outfile => "", :workdir => "", :test => false, :force => false, :warn => "", :sortarg => "" } OptionParser.new do |opts| opts.banner = "Usage: #{$0} [options]" opts.separator "Specific options:" opts.on("-o", "--outfile OUTFILE", String, "The file to create from the sources") do |o| settings[:outfile] = o end opts.on("-d", "--workdir WORKDIR", String, "The directory where the fragments are kept") do |d| settings[:workdir] = d end opts.on("-t", "--test", "Test to find out if a build is needed") do settings[:test] = true end opts.separator "Other options:" opts.on("-w", "--warn WARNMSG", String, "Add a shell style comment at the top of the created file to warn users that it is generated by puppet") do |w| settings[:warn] = w end opts.on("-f", "--force", "Enables the creation of empty output files when no fragments are found") do settings[:force] = true end opts.on("-n", "--sort", "Sort the output numerically rather than the default alpha sort") do settings[:sortarg] = "-n" end end.parse! # do we have -o? raise 'Please specify an output file with -o' unless !settings[:outfile].empty? # do we have -d? raise 'Please specify fragments directory with -d' unless !settings[:workdir].empty? # can we write to -o? if File.file?(settings[:outfile]) if !File.writable?(settings[:outfile]) raise "Cannot write to #{settings[:outfile]}" end else if !File.writable?(File.dirname(settings[:outfile])) raise "Cannot write to dirname #{File.dirname(settings[:outfile])} to create #{settings[:outfile]}" end end # do we have a fragments subdir inside the work dir? if !File.directory?(File.join(settings[:workdir], "fragments")) && !File.executable?(File.join(settings[:workdir], "fragments")) raise "Cannot access the fragments directory" end # are there actually any fragments? if (Dir.entries(File.join(settings[:workdir], "fragments")) - %w{ . .. }).empty? if !settings[:force] raise "The fragments directory is empty, cowardly refusing to make empty config files" end end Dir.chdir(settings[:workdir]) if settings[:warn].empty? File.open("fragments.concat", 'w') {|f| f.write("") } else File.open("fragments.concat", 'w') {|f| f.write("#{settings[:warn]}\n") } end # find all the files in the fragments directory, sort them numerically and concat to fragments.concat in the working dir open('fragments.concat', 'a') do |f| Dir.entries("fragments").sort.each{ |entry| if File.file?(File.join("fragments", entry)) f << File.read(File.join("fragments", entry)) end } end if !settings[:test] # This is a real run, copy the file to outfile FileUtils.cp 'fragments.concat', settings[:outfile] else # Just compare the result to outfile to help the exec decide if FileUtils.cmp 'fragments.concat', settings[:outfile] exit 0 else exit 1 end end diff --git a/manifests/setup.pp b/manifests/setup.pp index 1767400..549284e 100644 --- a/manifests/setup.pp +++ b/manifests/setup.pp @@ -1,58 +1,64 @@ # === Class: concat::setup # # Sets up the concat system. This is a private class. # # [$concatdir] # is where the fragments live and is set on the fact concat_basedir. # Since puppet should always manage files in $concatdir and they should # not be deleted ever, /tmp is not an option. # -# It also copies out the concatfragments.sh file to ${concatdir}/bin +# It also copies out the concatfragments.{sh,rb} file to ${concatdir}/bin # class concat::setup { if $caller_module_name != $module_name { warning("${name} is deprecated as a public API of the ${module_name} module and should no longer be directly included in the manifest.") } if $::concat_basedir { $concatdir = $::concat_basedir } else { fail ('$concat_basedir not defined. Try running again with pluginsync=true on the [master] and/or [main] section of your node\'s \'/etc/puppet/puppet.conf\'.') } - - # owner and mode of fragment files (on windows owner and access rights should be inherited from concatdir and not explicitly set to avoid problems) - $fragment_owner = $osfamily ? { 'windows' => undef, default => $::id } - $fragment_mode = $osfamily ? { 'windows' => undef, default => '0640' } - - $script_name = $::kernel ? { - 'windows' => 'concatfragments.rb', - default => 'concatfragments.sh' + + # owner and mode of fragment files (on windows owner and access rights should + # be inherited from concatdir and not explicitly set to avoid problems) + $fragment_owner = $::osfamily ? { 'windows' => undef, default => $::id } + $fragment_mode = $::osfamily ? { 'windows' => undef, default => '0640' } + + # PR #174 introduced changes to the concatfragments.sh script that are + # incompatible with Solaris 10 but reportedly OK on Solaris 11. As a work + # around we are enable the .rb concat script on all Solaris versions. If + # this goes smoothly, we should move towards completely eliminating the .sh + # version. + $script_name = $::osfamily? { + /(Windows|Solaris)/ => 'concatfragments.rb', + default => 'concatfragments.sh' } $script_path = "${concatdir}/bin/${script_name}" - $script_owner = $osfamily ? { 'windows' => undef, default => $::id } + $script_owner = $::osfamily ? { 'windows' => undef, default => $::id } - $script_mode = $osfamily ? { 'windows' => undef, default => '0755' } + $script_mode = $::osfamily ? { 'windows' => undef, default => '0755' } - $script_command = $::kernel ? { + $script_command = $::osfamily? { 'windows' => "ruby.exe ${script_path}", default => $script_path } File { backup => false, } file { $script_path: ensure => file, owner => $script_owner, mode => $script_mode, source => "puppet:///modules/concat/${script_name}", } file { [ $concatdir, "${concatdir}/bin" ]: ensure => directory, mode => '0755', } } diff --git a/spec/unit/classes/concat_setup_spec.rb b/spec/unit/classes/concat_setup_spec.rb index bba455a..4e83cb2 100644 --- a/spec/unit/classes/concat_setup_spec.rb +++ b/spec/unit/classes/concat_setup_spec.rb @@ -1,42 +1,84 @@ require 'spec_helper' describe 'concat::setup', :type => :class do shared_examples 'setup' do |concatdir| concatdir = '/foo' if concatdir.nil? let(:facts) {{ :concat_basedir => concatdir }} it do should contain_file("#{concatdir}/bin/concatfragments.sh").with({ :mode => '0755', :source => 'puppet:///modules/concat/concatfragments.sh', :backup => false, }) end [concatdir, "#{concatdir}/bin"].each do |file| it do should contain_file(file).with({ :ensure => 'directory', :mode => '0755', :backup => false, }) end end end context 'facts' do context 'concat_basedir =>' do context '/foo' do it_behaves_like 'setup', '/foo' end end end # facts context 'deprecated as a public class' do it 'should create a warning' do pending('rspec-puppet support for testing warning()') end end + + context "on osfamily Solaris" do + concatdir = '/foo' + let(:facts) do + { + :concat_basedir => concatdir, + :osfamily => 'Solaris', + :id => 'root', + } + end + + it do + should contain_file("#{concatdir}/bin/concatfragments.rb").with({ + :ensure => 'file', + :owner => 'root', + :mode => '0755', + :source => 'puppet:///modules/concat/concatfragments.rb', + :backup => false, + }) + end + end # on osfamily Solaris + + context "on osfamily Windows" do + concatdir = '/foo' + let(:facts) do + { + :concat_basedir => concatdir, + :osfamily => 'Windows', + :id => 'batman', + } + end + + it do + should contain_file("#{concatdir}/bin/concatfragments.rb").with({ + :ensure => 'file', + :owner => nil, + :mode => nil, + :source => 'puppet:///modules/concat/concatfragments.rb', + :backup => false, + }) + end + end # on osfamily Windows end