f85ae85f84b48a3274054a895bbc9db1fda7ccd5
[mirror/dsa-puppet.git] / 3rdparty / modules / stdlib / lib / puppet / provider / file_line / ruby.rb
1 Puppet::Type.type(:file_line).provide(:ruby) do
2   desc <<-DOC
3     @summary
4       This type allows puppet to manage small config files.
5
6     The implementation matches the full line, including whitespace at the
7     beginning and end.  If the line is not contained in the given file, Puppet
8     will append the line to the end of the file to ensure the desired state.
9     Multiple resources may be declared to manage multiple lines in the same file.
10   DOC
11   def exists?
12     found = false
13     lines_count = 0
14     lines.each do |line|
15       found = line.chomp == resource[:line]
16       if found
17         lines_count += 1
18       end
19     end
20     return found = lines_count > 0 if resource[:match].nil?
21
22     match_count = count_matches(new_match_regex)
23     found = if resource[:ensure] == :present
24               if match_count.zero?
25                 if lines_count.zero? && resource[:append_on_no_match].to_s == 'false'
26                   true # lies, but gets the job done
27                 elsif lines_count.zero? && resource[:append_on_no_match].to_s != 'false'
28                   false
29                 else
30                   true
31                 end
32               elsif resource[:replace_all_matches_not_matching_line].to_s == 'true'
33                 false # maybe lies, but knows there's still work to do
34               elsif lines_count.zero?
35                 resource[:replace].to_s == 'false'
36               else
37                 true
38               end
39             elsif match_count.zero?
40               if lines_count.zero?
41                 false
42               else
43                 true
44               end
45             elsif lines_count.zero?
46               resource[:match_for_absence].to_s == 'true'
47             else
48               true
49             end
50   end
51
52   def create
53     return if resource[:replace].to_s != 'true' && count_matches(new_match_regex) > 0
54     if resource[:match]
55       handle_create_with_match
56     elsif resource[:after]
57       handle_create_with_after
58     else
59       handle_append_line
60     end
61   end
62
63   def destroy
64     if resource[:match_for_absence].to_s == 'true' && resource[:match]
65       handle_destroy_with_match
66     else
67       handle_destroy_line
68     end
69   end
70
71   private
72
73   def lines
74     # If this type is ever used with very large files, we should
75     #  write this in a different way, using a temp
76     #  file; for now assuming that this type is only used on
77     #  small-ish config files that can fit into memory without
78     #  too much trouble.
79
80     @lines ||= File.readlines(resource[:path], :encoding => resource[:encoding])
81   rescue TypeError => _e
82     # Ruby 1.8 doesn't support open_args
83     @lines ||= File.readlines(resource[:path])
84   end
85
86   def new_after_regex
87     resource[:after] ? Regexp.new(resource[:after]) : nil
88   end
89
90   def new_match_regex
91     resource[:match] ? Regexp.new(resource[:match]) : nil
92   end
93
94   def count_matches(regex)
95     lines.select { |line|
96       if resource[:replace_all_matches_not_matching_line].to_s == 'true'
97         line.match(regex) unless line.chomp == resource[:line]
98       else
99         line.match(regex)
100       end
101     }.size
102   end
103
104   def handle_create_with_match
105     after_regex = new_after_regex
106     match_regex = new_match_regex
107     match_count = count_matches(new_match_regex)
108
109     if match_count > 1 && resource[:multiple].to_s != 'true'
110       raise Puppet::Error, "More than one line in file '#{resource[:path]}' matches pattern '#{resource[:match]}'"
111     end
112
113     File.open(resource[:path], 'w') do |fh|
114       lines.each do |line|
115         fh.puts(match_regex.match(line) ? resource[:line] : line)
116         next unless match_count.zero? && after_regex
117         if after_regex.match(line)
118           fh.puts(resource[:line])
119           match_count += 1 # Increment match_count to indicate that the new line has been inserted.
120         end
121       end
122
123       if match_count.zero?
124         fh.puts(resource[:line])
125       end
126     end
127   end
128
129   def handle_create_with_after
130     after_regex = new_after_regex
131     after_count = count_matches(after_regex)
132
133     if after_count > 1 && resource[:multiple].to_s != 'true'
134       raise Puppet::Error, "#{after_count} lines match pattern '#{resource[:after]}' in file '#{resource[:path]}'. One or no line must match the pattern."
135     end
136
137     File.open(resource[:path], 'w') do |fh|
138       lines.each do |line|
139         fh.puts(line)
140         if after_regex.match(line)
141           fh.puts(resource[:line])
142         end
143       end
144
145       if after_count.zero?
146         fh.puts(resource[:line])
147       end
148     end
149   end
150
151   def handle_destroy_with_match
152     match_regex = new_match_regex
153     match_count = count_matches(match_regex)
154     if match_count > 1 && resource[:multiple].to_s != 'true'
155       raise Puppet::Error, "More than one line in file '#{resource[:path]}' matches pattern '#{resource[:match]}'"
156     end
157
158     local_lines = lines
159     File.open(resource[:path], 'w') do |fh|
160       fh.write(local_lines.reject { |line| match_regex.match(line) }.join(''))
161     end
162   end
163
164   def handle_destroy_line
165     local_lines = lines
166     File.open(resource[:path], 'w') do |fh|
167       fh.write(local_lines.reject { |line| line.chomp == resource[:line] }.join(''))
168     end
169   end
170
171   def handle_append_line
172     local_lines = lines
173     File.open(resource[:path], 'w') do |fh|
174       local_lines.each do |line|
175         fh.puts(line)
176       end
177       fh.puts(resource[:line])
178     end
179   end
180 end