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