|
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 |