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