1 Puppet::Type.type(:file_line).provide(:ruby) do
4 This type allows puppet to manage small config files.
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.
15 found = line.chomp == resource[:line]
20 return found = lines_count > 0 if resource[:match].nil?
22 match_count = count_matches(new_match_regex)
23 found = if resource[:ensure] == :present
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'
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'
39 elsif match_count.zero?
45 elsif lines_count.zero?
46 resource[:match_for_absence].to_s == 'true'
53 return if resource[:replace].to_s != 'true' && count_matches(new_match_regex) > 0
55 handle_create_with_match
56 elsif resource[:after]
57 handle_create_with_after
64 if resource[:match_for_absence].to_s == 'true' && resource[:match]
65 handle_destroy_with_match
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
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])
87 resource[:after] ? Regexp.new(resource[:after]) : nil
91 resource[:match] ? Regexp.new(resource[:match]) : nil
94 def count_matches(regex)
96 if resource[:replace_all_matches_not_matching_line].to_s == 'true'
97 line.match(regex) unless line.chomp == resource[:line]
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)
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]}'"
113 File.open(resource[:path], 'w') do |fh|
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.
124 fh.puts(resource[:line])
129 def handle_create_with_after
130 after_regex = new_after_regex
131 after_count = count_matches(after_regex)
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."
137 File.open(resource[:path], 'w') do |fh|
140 if after_regex.match(line)
141 fh.puts(resource[:line])
146 fh.puts(resource[:line])
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]}'"
159 File.open(resource[:path], 'w') do |fh|
160 fh.write(local_lines.reject { |line| match_regex.match(line) }.join(''))
164 def handle_destroy_line
166 File.open(resource[:path], 'w') do |fh|
167 fh.write(local_lines.reject { |line| line.chomp == resource[:line] }.join(''))
171 def handle_append_line
173 File.open(resource[:path], 'w') do |fh|
174 local_lines.each do |line|
177 fh.puts(resource[:line])