dev/provisioning/modules/concat/files/concatfragments.rb
changeset 28 b0b56e0f8c7f
equal deleted inserted replaced
27:a2342f26c9de 28:b0b56e0f8c7f
       
     1 #!/usr/bin/env ruby
       
     2 # Script to concat files to a config file.
       
     3 #
       
     4 # Given a directory like this:
       
     5 # /path/to/conf.d
       
     6 # |-- fragments
       
     7 # |   |-- 00_named.conf
       
     8 # |   |-- 10_domain.net
       
     9 # |   `-- zz_footer
       
    10 #
       
    11 # The script supports a test option that will build the concat file to a temp location and
       
    12 # use /usr/bin/cmp to verify if it should be run or not.  This would result in the concat happening
       
    13 # twice on each run but gives you the option to have an unless option in your execs to inhibit rebuilds.
       
    14 #
       
    15 # Without the test option and the unless combo your services that depend on the final file would end up
       
    16 # restarting on each run, or in other manifest models some changes might get missed.
       
    17 #
       
    18 # OPTIONS:
       
    19 #  -o	The file to create from the sources
       
    20 #  -d	The directory where the fragments are kept
       
    21 #  -t	Test to find out if a build is needed, basically concats the files to a temp
       
    22 #       location and compare with what's in the final location, return codes are designed
       
    23 #       for use with unless on an exec resource
       
    24 #  -w   Add a shell style comment at the top of the created file to warn users that it
       
    25 #       is generated by puppet
       
    26 #  -f   Enables the creation of empty output files when no fragments are found
       
    27 #  -n	Sort the output numerically rather than the default alpha sort
       
    28 #
       
    29 # the command:
       
    30 #
       
    31 #   concatfragments.rb -o /path/to/conffile.cfg -d /path/to/conf.d
       
    32 #
       
    33 # creates /path/to/conf.d/fragments.concat and copies the resulting
       
    34 # file to /path/to/conffile.cfg.  The files will be sorted alphabetically
       
    35 # pass the -n switch to sort numerically.
       
    36 #
       
    37 # The script does error checking on the various dirs and files to make
       
    38 # sure things don't fail.
       
    39 require 'optparse'
       
    40 require 'fileutils'
       
    41 
       
    42 settings = {
       
    43     :outfile => "",
       
    44     :workdir => "",
       
    45     :test => false,
       
    46     :force => false,
       
    47     :warn => "",
       
    48     :sortarg => "",
       
    49     :newline => false
       
    50 }
       
    51 
       
    52 OptionParser.new do |opts|
       
    53   opts.banner = "Usage: #{$0} [options]"
       
    54   opts.separator "Specific options:"
       
    55 
       
    56   opts.on("-o", "--outfile OUTFILE", String, "The file to create from the sources") do |o|
       
    57     settings[:outfile] = o
       
    58   end
       
    59 
       
    60   opts.on("-d", "--workdir WORKDIR", String, "The directory where the fragments are kept") do |d|
       
    61     settings[:workdir] = d
       
    62   end
       
    63 
       
    64   opts.on("-t", "--test", "Test to find out if a build is needed") do
       
    65     settings[:test] = true
       
    66   end
       
    67 
       
    68   opts.separator "Other options:"
       
    69   opts.on("-w", "--warn WARNMSG", String,
       
    70           "Add a shell style comment at the top of the created file to warn users that it is generated by puppet") do |w|
       
    71     settings[:warn] = w
       
    72   end
       
    73 
       
    74   opts.on("-f", "--force", "Enables the creation of empty output files when no fragments are found") do
       
    75     settings[:force] = true
       
    76   end
       
    77 
       
    78   opts.on("-n", "--sort", "Sort the output numerically rather than the default alpha sort") do
       
    79     settings[:sortarg] = "-n"
       
    80   end
       
    81 
       
    82   opts.on("-l", "--line", "Append a newline") do
       
    83     settings[:newline] = true
       
    84   end
       
    85 end.parse!
       
    86 
       
    87 # do we have -o?
       
    88 raise 'Please specify an output file with -o' unless !settings[:outfile].empty?
       
    89 
       
    90 # do we have -d?
       
    91 raise 'Please specify fragments directory with -d' unless !settings[:workdir].empty?
       
    92 
       
    93 # can we write to -o?
       
    94 if File.file?(settings[:outfile])
       
    95   if !File.writable?(settings[:outfile])
       
    96     raise "Cannot write to #{settings[:outfile]}"
       
    97   end
       
    98 else
       
    99   if !File.writable?(File.dirname(settings[:outfile]))
       
   100     raise "Cannot write to dirname #{File.dirname(settings[:outfile])} to create #{settings[:outfile]}"
       
   101   end
       
   102 end
       
   103 
       
   104 # do we have a fragments subdir inside the work dir?
       
   105 if !File.directory?(File.join(settings[:workdir], "fragments")) && !File.executable?(File.join(settings[:workdir], "fragments"))
       
   106   raise "Cannot access the fragments directory"
       
   107 end
       
   108 
       
   109 # are there actually any fragments?
       
   110 if (Dir.entries(File.join(settings[:workdir], "fragments")) - %w{ . .. }).empty?
       
   111   if !settings[:force]
       
   112     raise "The fragments directory is empty, cowardly refusing to make empty config files"
       
   113   end
       
   114 end
       
   115 
       
   116 Dir.chdir(settings[:workdir])
       
   117 
       
   118 if settings[:warn].empty?
       
   119   File.open("fragments.concat", 'w') { |f| f.write("") }
       
   120 else
       
   121   File.open("fragments.concat", 'w') { |f| f.write("#{settings[:warn]}\n") }
       
   122 end
       
   123 
       
   124 # find all the files in the fragments directory, sort them numerically and concat to fragments.concat in the working dir
       
   125 open('fragments.concat', 'a') do |f|
       
   126   fragments = Dir.entries("fragments").sort
       
   127   if settings[:sortarg] == '-n'
       
   128     fragments = fragments.sort_by { |v| v.split('_').map(&:to_i) }
       
   129   end
       
   130   fragments.each { |entry|
       
   131     if File.file?(File.join("fragments", entry))
       
   132       f << File.read(File.join("fragments", entry))
       
   133 
       
   134       # append a newline if we were asked to (invoked with -l)
       
   135       if settings[:newline]
       
   136         f << "\n"
       
   137       end
       
   138 
       
   139     end
       
   140   }
       
   141 end
       
   142 
       
   143 if !settings[:test]
       
   144   # This is a real run, copy the file to outfile
       
   145   FileUtils.cp 'fragments.concat', settings[:outfile]
       
   146 else
       
   147   # Just compare the result to outfile to help the exec decide
       
   148   if FileUtils.cmp 'fragments.concat', settings[:outfile]
       
   149     exit 0
       
   150   else
       
   151     exit 1
       
   152   end
       
   153 end