Skip to content

Commit 9b8e8fe

Browse files
committed
(MODULES-5651) Do not append infinitely
https://tickets.puppetlabs.com/browse/MODULES-5003 gave rise to #788 which caused different behavior based on whether the line value was matched by the match regex. This resolves the behavior by adding a new parameter to control whether multiple matches should universally be replaced by the line value, or whether matches should be left alone when the line exists elsewhere in the file. This problem only affects modifying multiple lines when the line already exists.
1 parent 2339ea8 commit 9b8e8fe

File tree

4 files changed

+101
-29
lines changed

4 files changed

+101
-29
lines changed

README.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ Values: String.
219219

220220
##### `match`
221221

222-
Specifies a regular expression to compare against existing lines in the file; if a match is found, it is replaced rather than adding a new line. A regex comparison is performed against the line value, and if it does not match, an exception is raised.
222+
Specifies a regular expression to compare against existing lines in the file; if a match is found, it is replaced rather than adding a new line.
223223

224224
Values: String containing a regex.
225225

@@ -236,7 +236,7 @@ Default value: `false`.
236236

237237
##### `multiple`
238238

239-
Specifies whether `match` and `after` can change multiple lines. If set to `false`, an exception is raised if more than one line matches.
239+
Specifies whether `match` and `after` can change multiple lines. If set to `false`, allows file\_line to replace only one line and raises an error if more than one will be replaced. If set to `true`, allows file\_line to replace one or more lines.
240240

241241
Values: `true`, `false`.
242242

@@ -261,12 +261,20 @@ Value: String specifying an absolute path to the file.
261261

262262
##### `replace`
263263

264-
Specifies whether the resource overwrites an existing line that matches the `match` parameter. If set to `false` and a line is found matching the `match` parameter, the line is not placed in the file.
264+
Specifies whether the resource overwrites an existing line that matches the `match` parameter when `line` does not otherwise exist.
265+
266+
If set to `false` and a line is found matching the `match` parameter, the line is not placed in the file.
265267

266268
Boolean.
267269

268270
Default value: `true`.
269271

272+
##### `replace_all_matches_not_matching_line`
273+
274+
Replace all lines matched by `match` parameter, even if `line` already exists in the file.
275+
276+
Default value: `false`.
277+
270278
### Data types
271279

272280
#### `Stdlib::Absolutepath`

lib/puppet/provider/file_line/ruby.rb

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,58 @@
11
Puppet::Type.type(:file_line).provide(:ruby) do
22
def exists?
3-
found = false
3+
leave_it = false
44
lines_count = 0
55
lines.each do |line|
6-
found = line.chomp == resource[:line]
7-
if found
6+
leave_it = line.chomp == resource[:line]
7+
if leave_it
88
lines_count += 1
99
end
1010
end
1111
if resource[:match] == nil
12-
found = lines_count > 0
12+
leave_it = lines_count > 0
1313
else
14-
match_count = count_matches(new_match_regex)
15-
if resource[:append_on_no_match].to_s == 'false'
16-
found = true
17-
elsif resource[:replace].to_s == 'true'
18-
found = lines_count > 0 && lines_count == match_count
14+
# conditions: match, line, append, replace, replace_all_matches_not_matching_line
15+
# values int int t/f t/f t/f
16+
# cases:
17+
# - false replace, true replace_all_matches_not_matching_line => error (handled in type)
18+
# - false multiple, true replace_all_matches_not_matching_line => error (handled in type)
19+
#
20+
#
21+
#
22+
# - zero match matches, zero line matches, and false append => leave it
23+
# - zero match matches, zero line matches, and true append => append
24+
# - zero match matches, any line matches => leave it
25+
# - any match matches, zero line matches, and false replace => leave it
26+
# - any match matches, zero line matches, and true replace => replace
27+
# - any match matches, true replace_all_matches_not_matching_line => replace
28+
# - any match matches, false replace_all_matches_not_matching_line => go with old behavior
29+
if match_count == 0
30+
if lines_count == 0
31+
if resource[:append_on_no_match].to_s == 'false'
32+
leave_it = true
33+
else
34+
leave_it = false
35+
end
36+
else
37+
leave_it = true
38+
end
1939
else
20-
found = match_count > 0
40+
if resource[:replace_all_matches_not_matching_line].to_s == 'true'
41+
leave_it = false
42+
else
43+
if lines_count == 0
44+
if resource[:replace].to_s == 'false'
45+
leave_it = true
46+
else
47+
leave_it = false
48+
end
49+
else
50+
leave_it = true
51+
end
52+
end
2153
end
2254
end
23-
found
55+
leave_it
2456
end
2557

2658
def create

lib/puppet/type/file_line.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,12 @@ def retrieve
168168
end
169169

170170
validate do
171+
if self[:replace_all_matches_not_matching_line].to_s == 'true' and self[:multiple].to_s == 'false'
172+
raise(Puppet::Error, "multiple must be true when replace_all_matches_not_matching_line is true")
173+
end
174+
if self[:replace_all_matches_not_matching_line].to_s == 'true' and self[:replace].to_s == 'false'
175+
raise(Puppet::Error, "replace must be true when replace_all_matches_not_matching_line is true")
176+
end
171177
unless self[:line]
172178
unless (self[:ensure].to_s == 'absent') and (self[:match_for_absence].to_s == 'true') and self[:match]
173179
raise(Puppet::Error, "line is a required attribute")

spec/unit/puppet/provider/file_line/ruby_spec.rb

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -195,25 +195,51 @@
195195
expect(File.read(@tmpfile).chomp).to eql("foo1\nfoo = bar\nfoo2")
196196
end
197197

198-
it 'should not add line after no matches found' do
199-
@resource = Puppet::Type::File_line.new(
200-
{
201-
:name => 'foo',
202-
:path => @tmpfile,
203-
:line => 'inserted = line',
204-
:match => '^foo3$',
205-
:append_on_no_match => false,
206-
}
207-
)
208-
@provider = provider_class.new(@resource)
209-
File.open(@tmpfile, 'w') do |fh|
210-
fh.write("foo1\nfoo = blah\nfoo2\nfoo = baz")
198+
end
199+
200+
describe 'using match+append_on_no_match' do
201+
context 'when there is a match' do
202+
it 'should replace line' do
203+
@resource = Puppet::Type::File_line.new(
204+
{
205+
:name => 'foo',
206+
:path => @tmpfile,
207+
:line => 'inserted = line',
208+
:match => '^foo3$',
209+
:append_on_no_match => false,
210+
}
211+
)
212+
@provider = provider_class.new(@resource)
213+
File.open(@tmpfile, 'w') do |fh|
214+
fh.write("foo1\nfoo = blah\nfoo2\nfoo = baz")
215+
end
216+
expect(@provider.exists?).to be true
217+
expect(File.read(@tmpfile).chomp).to eql("foo1\nfoo = blah\nfoo2\nfoo = baz")
218+
end
219+
end
220+
context 'when there is no match' do
221+
it 'should not add line after no matches found' do
222+
@resource = Puppet::Type::File_line.new(
223+
{
224+
:name => 'foo',
225+
:path => @tmpfile,
226+
:line => 'inserted = line',
227+
:match => '^foo3$',
228+
:append_on_no_match => false,
229+
}
230+
)
231+
@provider = provider_class.new(@resource)
232+
File.open(@tmpfile, 'w') do |fh|
233+
fh.write("foo1\nfoo = blah\nfoo2\nfoo = baz")
234+
end
235+
expect(@provider.exists?).to be true
236+
expect(File.read(@tmpfile).chomp).to eql("foo1\nfoo = blah\nfoo2\nfoo = baz")
211237
end
212-
expect(@provider.exists?).to be true
213-
expect(File.read(@tmpfile).chomp).to eql("foo1\nfoo = blah\nfoo2\nfoo = baz")
214238
end
215239
end
216240

241+
describe 'using match+replace+append_on_no_match'
242+
217243
describe 'using after' do
218244
let :resource do
219245
Puppet::Type::File_line.new(

0 commit comments

Comments
 (0)