diff --git a/README.md b/README.md index 2b6a64bd9..b6d6c0dd2 100644 --- a/README.md +++ b/README.md @@ -219,7 +219,7 @@ Values: String. ##### `match` -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. +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. Values: String containing a regex. @@ -236,7 +236,7 @@ Default value: `false`. ##### `multiple` -Specifies whether `match` and `after` can change multiple lines. If set to `false`, an exception is raised if more than one line matches. +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. Values: `true`, `false`. @@ -261,12 +261,20 @@ Value: String specifying an absolute path to the file. ##### `replace` -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. +Specifies whether the resource overwrites an existing line that matches the `match` parameter when `line` does not otherwise exist. + +If set to `false` and a line is found matching the `match` parameter, the line is not placed in the file. Boolean. Default value: `true`. +##### `replace_all_matches_not_matching_line` + +Replace all lines matched by `match` parameter, even if `line` already exists in the file. + +Default value: `false`. + ### Data types #### `Stdlib::Absolutepath` diff --git a/lib/puppet/provider/file_line/ruby.rb b/lib/puppet/provider/file_line/ruby.rb index 16f2709c3..2d188e050 100644 --- a/lib/puppet/provider/file_line/ruby.rb +++ b/lib/puppet/provider/file_line/ruby.rb @@ -12,12 +12,50 @@ def exists? found = lines_count > 0 else match_count = count_matches(new_match_regex) - if resource[:append_on_no_match].to_s == 'false' - found = true - elsif resource[:replace].to_s == 'true' - found = lines_count > 0 && lines_count == match_count + if resource[:ensure] == :present + if match_count == 0 + if lines_count == 0 + if resource[:append_on_no_match].to_s == 'false' + found = true # lies, but gets the job done + else + found = false + end + else + found = true + end + else + if resource[:replace_all_matches_not_matching_line].to_s == 'true' + found = false # maybe lies, but knows there's still work to do + else + if lines_count == 0 + if resource[:replace].to_s == 'false' + found = true + else + found = false + end + else + found = true + end + end + end else - found = match_count > 0 + if match_count == 0 + if lines_count == 0 + found = false + else + found = true + end + else + if lines_count == 0 + if resource[:match_for_absence].to_s == 'true' + found = true # found matches, not lines + else + found = false + end + else + found = true + end + end end end found @@ -68,7 +106,13 @@ def new_match_regex end def count_matches(regex) - lines.select{ |line| line.match(regex) }.size + lines.select 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.size end def handle_create_with_match() @@ -140,8 +184,9 @@ def handle_destroy_line end def handle_append_line + local_lines = lines File.open(resource[:path],'w') do |fh| - lines.each do |line| + local_lines.each do |line| fh.puts(line) end fh.puts(resource[:line]) diff --git a/lib/puppet/type/file_line.rb b/lib/puppet/type/file_line.rb index 06be5522d..42d7d5fa9 100644 --- a/lib/puppet/type/file_line.rb +++ b/lib/puppet/type/file_line.rb @@ -151,6 +151,13 @@ def retrieve defaultto true end + newparam(:replace_all_matches_not_matching_line) do + desc 'Configures the behavior of replacing all lines in a file which match the `match` parameter regular expression, regardless of whether the specified line is already present in the file.' + + newvalues(true, false) + defaultto false + end + newparam(:encoding) do desc 'For files that are not UTF-8 encoded, specify encoding such as iso-8859-1' defaultto 'UTF-8' @@ -168,6 +175,12 @@ def retrieve end validate do + if self[:replace_all_matches_not_matching_line].to_s == 'true' and self[:multiple].to_s == 'false' + raise(Puppet::Error, "multiple must be true when replace_all_matches_not_matching_line is true") + end + if self[:replace_all_matches_not_matching_line].to_s == 'true' and self[:replace].to_s == 'false' + raise(Puppet::Error, "replace must be true when replace_all_matches_not_matching_line is true") + end unless self[:line] unless (self[:ensure].to_s == 'absent') and (self[:match_for_absence].to_s == 'true') and self[:match] raise(Puppet::Error, "line is a required attribute") diff --git a/spec/unit/puppet/provider/file_line/ruby_spec.rb b/spec/unit/puppet/provider/file_line/ruby_spec.rb index 4ec9baed7..0330f1d0a 100755 --- a/spec/unit/puppet/provider/file_line/ruby_spec.rb +++ b/spec/unit/puppet/provider/file_line/ruby_spec.rb @@ -1,159 +1,370 @@ #! /usr/bin/env ruby -S rspec require 'spec_helper' -require 'tempfile' + provider_class = Puppet::Type.type(:file_line).provider(:ruby) # These tests fail on windows when run as part of the rake task. Individually they pass describe provider_class, :unless => Puppet::Util::Platform.windows? do - context "when adding" do - let :tmpfile do - tmp = Tempfile.new('tmp') - path = tmp.path - tmp.close! - path + include PuppetlabsSpec::Files + + let :tmpfile do + tmpfilename("file_line_test") + end + let :content do + '' + end + let :params do + { } + end + let :resource do + Puppet::Type::File_line.new( { + name: 'foo', + path: tmpfile, + line: 'foo', + }.merge(params)) + end + let :provider do + provider_class.new(resource) + end + before :each do + File.open(tmpfile, 'w') do |fh| + fh.write(content) end - let :resource do - Puppet::Type::File_line.new( - {:name => 'foo', :path => tmpfile, :line => 'foo'} - ) + end + + describe "line parameter" do + context "line exists" do + let(:content) { 'foo' } + it 'detects the line' do + expect(provider.exists?).to be_truthy + end end - let :provider do - provider_class.new(resource) + context "line does not exist" do + let(:content) { 'foo bar' } + it 'requests changes' do + expect(provider.exists?).to be_falsy + end + it 'appends the line' do + provider.create + expect(File.read(tmpfile).chomp).to eq("foo bar\nfoo") + end end - - it 'should detect if the line exists in the file' do - File.open(tmpfile, 'w') do |fh| - fh.write('foo') + end + describe "match parameter" do + context "does not match line" do + let(:params) { { match: '^bar' } } + context "line does not exist" do + describe "replacing" do + let(:content) { "foo bar\nbar" } + it 'requests changes' do + expect(provider.exists?).to be_falsy + end + it 'replaces the match' do + provider.create + expect(File.read(tmpfile).chomp).to eq("foo bar\nfoo") + end + end + describe "appending" do + let(:params) { super().merge({ replace: false }) } + let(:content) { "foo bar\nbar" } + it 'does not request changes' do + expect(provider.exists?).to be_truthy + end + end + end + context "line exists" do + let(:content) { "foo\nbar" } + it 'detects the line' do + expect(provider.exists?).to be_truthy + end end - expect(provider.exists?).to be_truthy end - it 'should detect if the line does not exist in the file' do - File.open(tmpfile, 'w') do |fh| - fh.write('foo1') + context "matches line" do + let(:params) { { match: '^foo' } } + context "line exists" do + let(:content) { "foo\nbar" } + it 'detects the line' do + expect(provider.exists?).to be_truthy + end + end + context "line does not exist" do + let(:content) { "foo bar\nbar" } + it 'requests changes' do + expect(provider.exists?).to be_falsy + end + it 'replaces the match' do + provider.create + expect(File.read(tmpfile).chomp).to eq("foo\nbar") + end + end + end + end + describe 'append_on_no_match' do + let(:params) { { + append_on_no_match: false, + match: '^foo1$', + } } + context 'when matching' do + let(:content) { "foo1\nbar" } + it 'requests changes' do + expect(provider.exists?).to be_falsy + end + it 'replaces the match' do + provider.create + expect(File.read(tmpfile).chomp).to eql("foo\nbar") end - expect(provider.exists?).to eql (false) end - it 'should append to an existing file when creating' do - provider.create - expect(File.read(tmpfile).chomp).to eq('foo') + context 'when not matching' do + let(:content) { "foo3\nbar" } + it 'does not affect the file' do + expect(provider.exists?).to be_truthy + end end end - context 'when using replace' do - before :each do - # TODO: these should be ported over to use the PuppetLabs spec_helper - # file fixtures once the following pull request has been merged: - # https://github.com/puppetlabs/puppetlabs-stdlib/pull/73/files - tmp = Tempfile.new('tmp') - @tmpfile = tmp.path - tmp.close! - @resource = Puppet::Type::File_line.new( - { - :name => 'foo', - :path => @tmpfile, - :line => 'foo = bar', - :match => '^foo\s*=.*$', - :replace => false, - } - ) - @provider = provider_class.new(@resource) + describe 'replace_all_matches_not_matching_line' do + context 'when replace is false' do + let(:params) { { + replace_all_matches_not_matching_line: true, + replace: false, + } } + it 'raises an error' do + expect { provider.exists? }.to raise_error(Puppet::Error, /replace must be true/) + end end - - it 'should not replace the matching line' do - File.open(@tmpfile, 'w') do |fh| - fh.write("foo1\nfoo=blah\nfoo2\nfoo3") + context 'when match matches line' do + let(:params) { { + replace_all_matches_not_matching_line: true, + match: '^foo', + multiple: true, + } } + context 'when there are more matches than lines' do + let(:content) { "foo\nfoo bar\nbar\nfoo baz" } + it 'requests changes' do + expect(provider.exists?).to be_falsy + end + it 'replaces the matches' do + provider.create + expect(File.read(tmpfile).chomp).to eql("foo\nfoo\nbar\nfoo") + end + end + context 'when there are the same matches and lines' do + let(:content) { "foo\nfoo\nbar" } + it 'does not request changes' do + expect(provider.exists?).to be_truthy + end end - expect(@provider.exists?).to be_truthy - @provider.create - expect(File.read(@tmpfile).chomp).to eql("foo1\nfoo=blah\nfoo2\nfoo3") end - - it 'should append the line if no matches are found' do - File.open(@tmpfile, 'w') do |fh| - fh.write("foo1\nfoo2") + context 'when match does not match line' do + let(:params) { { + replace_all_matches_not_matching_line: true, + match: '^bar', + multiple: true, + } } + context 'when there are more matches than lines' do + let(:content) { "foo\nfoo bar\nbar\nbar baz" } + it 'requests changes' do + expect(provider.exists?).to be_falsy + end + it 'replaces the matches' do + provider.create + expect(File.read(tmpfile).chomp).to eql("foo\nfoo bar\nfoo\nfoo") + end + end + context 'when there are the same matches and lines' do + let(:content) { "foo\nfoo\nbar\nbar baz" } + it 'requests changes' do + expect(provider.exists?).to be_falsy + end + it 'replaces the matches' do + provider.create + expect(File.read(tmpfile).chomp).to eql("foo\nfoo\nfoo\nfoo") + end + end + context 'when there are no matches' do + let(:content) { "foo\nfoo bar" } + it 'does not request changes' do + expect(provider.exists?).to be_truthy + end + end + context 'when there are no matches or lines' do + let(:content) { "foo bar" } + it 'requests changes' do + expect(provider.exists?).to be_falsy + end + it 'appends the line' do + provider.create + expect(File.read(tmpfile).chomp).to eql("foo bar\nfoo") + end end - expect(@provider.exists?).to eql (false) - @provider.create - expect(File.read(@tmpfile).chomp).to eql("foo1\nfoo2\nfoo = bar") end + end + describe 'match_for_absence' do + end + describe 'customer use cases' do + describe 'MODULES-5003' do + let(:params) { { + line: "*\thard\tcore\t0", + match: "^[ \t]*\\*[ \t]+hard[ \t]+core[ \t]+.*", + multiple: true, + } } + context 'no lines' do + let(:content) { "* hard core 90\n* hard core 10\n" } + it 'requests changes' do + expect(provider.exists?).to be_falsy + end + it 'replaces the matches' do + provider.create + expect(File.read(tmpfile).chomp).to eq("* hard core 0\n* hard core 0") + end + end + context 'one match, one line' do + let(:content) { "* hard core 90\n* hard core 0\n" } + describe 'just ensure the line exists' do + it 'does not request changes' do + expect(provider.exists?).to be_truthy + end + end + describe 'replace all matches, even when line exists' do + let(:params) { super().merge(replace_all_matches_not_matching_line: true) } + it 'requests changes' do + expect(provider.exists?).to be_falsy + end + it 'replaces the matches' do + provider.create + expect(File.read(tmpfile).chomp).to eq("* hard core 0\n* hard core 0") + end + end + end + end + describe 'MODULES-5651' do + let(:params) { { + line: 'LogLevel=notice', + match: '^#LogLevel$', + } } + context "match, no line" do + let(:content) { "#LogLevel\nstuff" } + it 'requests changes' do + expect(provider.exists?).to be_falsy + end + it 'replaces the match' do + provider.create + expect(File.read(tmpfile).chomp).to eq("LogLevel=notice\nstuff") + end + end + context "match, line" do + let(:content) { "#Loglevel\nLogLevel=notice\nstuff" } + it 'does not request changes' do + expect(provider.exists?).to be_truthy + end + end + context "no match, line" do + let(:content) { "LogLevel=notice\nstuff" } + it 'does not request changes' do + expect(provider.exists?).to be_truthy + end + end + end + end + describe "#create" do + context "when adding" do + end + context 'when replacing' do + let :params do + { + line: 'foo = bar', + match: '^foo\s*=.*$', + replace: false, + } + end + let(:content) { "foo1\nfoo=blah\nfoo2\nfoo3" } - it 'should raise an error with invalid values' do - expect { - @resource = Puppet::Type::File_line.new( - { - :name => 'foo', - :path => @tmpfile, - :line => 'foo = bar', - :match => '^foo\s*=.*$', - :replace => 'asgadga', - } - ) - }.to raise_error(Puppet::Error, /Invalid value "asgadga"\. Valid values are true, false\./) + it 'should not replace the matching line' do + expect(provider.exists?).to be_truthy + provider.create + expect(File.read(tmpfile).chomp).to eql("foo1\nfoo=blah\nfoo2\nfoo3") + end + it 'should append the line if no matches are found' do + File.open(tmpfile, 'w') do |fh| + fh.write("foo1\nfoo2") + end + expect(provider.exists?).to eql (false) + provider.create + expect(File.read(tmpfile).chomp).to eql("foo1\nfoo2\nfoo = bar") + end + it 'should raise an error with invalid values' do + expect { + @resource = Puppet::Type::File_line.new( + { + :name => 'foo', + :path => tmpfile, + :line => 'foo = bar', + :match => '^foo\s*=.*$', + :replace => 'asgadga', + } + ) + }.to raise_error(Puppet::Error, /Invalid value "asgadga"\. Valid values are true, false\./) + end end end + describe "#destroy" do + end context "when matching" do before :each do - # TODO: these should be ported over to use the PuppetLabs spec_helper - # file fixtures once the following pull request has been merged: - # https://github.com/puppetlabs/puppetlabs-stdlib/pull/73/files - tmp = Tempfile.new('tmp') - @tmpfile = tmp.path - tmp.close! @resource = Puppet::Type::File_line.new( { :name => 'foo', - :path => @tmpfile, + :path => tmpfile, :line => 'foo = bar', :match => '^foo\s*=.*$', } ) @provider = provider_class.new(@resource) end - describe 'using match' do it 'should raise an error if more than one line matches, and should not have modified the file' do - File.open(@tmpfile, 'w') do |fh| + File.open(tmpfile, 'w') do |fh| fh.write("foo1\nfoo=blah\nfoo2\nfoo=baz") end expect(@provider.exists?).to eql(false) expect { @provider.create }.to raise_error(Puppet::Error, /More than one line.*matches/) - expect(File.read(@tmpfile)).to eql("foo1\nfoo=blah\nfoo2\nfoo=baz") + expect(File.read(tmpfile)).to eql("foo1\nfoo=blah\nfoo2\nfoo=baz") end it 'should replace all lines that matches' do @resource = Puppet::Type::File_line.new( { :name => 'foo', - :path => @tmpfile, + :path => tmpfile, :line => 'foo = bar', :match => '^foo\s*=.*$', :multiple => true, } ) @provider = provider_class.new(@resource) - File.open(@tmpfile, 'w') do |fh| + File.open(tmpfile, 'w') do |fh| fh.write("foo1\nfoo=blah\nfoo2\nfoo=baz") end expect(@provider.exists?).to eql(false) @provider.create - expect(File.read(@tmpfile).chomp).to eql("foo1\nfoo = bar\nfoo2\nfoo = bar") + expect(File.read(tmpfile).chomp).to eql("foo1\nfoo = bar\nfoo2\nfoo = bar") end it 'should replace all lines that match, even when some lines are correct' do @resource = Puppet::Type::File_line.new( { :name => 'neil', - :path => @tmpfile, + :path => tmpfile, :line => "\thard\tcore\t0\n", :match => '^[ \t]hard[ \t]+core[ \t]+.*', :multiple => true, } ) @provider = provider_class.new(@resource) - File.open(@tmpfile, 'w') do |fh| + File.open(tmpfile, 'w') do |fh| fh.write("\thard\tcore\t90\n\thard\tcore\t0\n") end expect(@provider.exists?).to eql(false) @provider.create - expect(File.read(@tmpfile).chomp).to eql("\thard\tcore\t0\n\thard\tcore\t0") + expect(File.read(tmpfile).chomp).to eql("\thard\tcore\t0\n\thard\tcore\t0") end it 'should raise an error with invalid values' do @@ -161,7 +372,7 @@ @resource = Puppet::Type::File_line.new( { :name => 'foo', - :path => @tmpfile, + :path => tmpfile, :line => 'foo = bar', :match => '^foo\s*=.*$', :multiple => 'asgadga', @@ -171,184 +382,202 @@ end it 'should replace a line that matches' do - File.open(@tmpfile, 'w') do |fh| + File.open(tmpfile, 'w') do |fh| fh.write("foo1\nfoo=blah\nfoo2") end expect(@provider.exists?).to eql(false) @provider.create - expect(File.read(@tmpfile).chomp).to eql("foo1\nfoo = bar\nfoo2") + expect(File.read(tmpfile).chomp).to eql("foo1\nfoo = bar\nfoo2") end it 'should add a new line if no lines match' do - File.open(@tmpfile, 'w') do |fh| + File.open(tmpfile, 'w') do |fh| fh.write("foo1\nfoo2") end expect(@provider.exists?).to eql(false) @provider.create - expect(File.read(@tmpfile)).to eql("foo1\nfoo2\nfoo = bar\n") + expect(File.read(tmpfile)).to eql("foo1\nfoo2\nfoo = bar\n") end it 'should do nothing if the exact line already exists' do - File.open(@tmpfile, 'w') do |fh| + File.open(tmpfile, 'w') do |fh| fh.write("foo1\nfoo = bar\nfoo2") end expect(@provider.exists?).to eql(true) @provider.create - expect(File.read(@tmpfile).chomp).to eql("foo1\nfoo = bar\nfoo2") + expect(File.read(tmpfile).chomp).to eql("foo1\nfoo = bar\nfoo2") end - it 'should not add line after no matches found' do - @resource = Puppet::Type::File_line.new( - { - :name => 'foo', - :path => @tmpfile, - :line => 'inserted = line', - :match => '^foo3$', - :append_on_no_match => false, - } - ) - @provider = provider_class.new(@resource) - File.open(@tmpfile, 'w') do |fh| - fh.write("foo1\nfoo = blah\nfoo2\nfoo = baz") - end - expect(@provider.exists?).to be true - expect(File.read(@tmpfile).chomp).to eql("foo1\nfoo = blah\nfoo2\nfoo = baz") - end end - - describe 'using after' do - let :resource do - Puppet::Type::File_line.new( - { - :name => 'foo', - :path => @tmpfile, - :line => 'inserted = line', - :after => '^foo1', - } - ) - end - - let :provider do - provider_class.new(resource) - end - context 'match and after set' do - shared_context 'resource_create' do - let(:match) { '^foo2$' } - let(:after) { '^foo1$' } - let(:resource) { - Puppet::Type::File_line.new( - { - :name => 'foo', - :path => @tmpfile, - :line => 'inserted = line', - :after => after, - :match => match, - } - ) - } - end - before :each do - File.open(@tmpfile, 'w') do |fh| - fh.write("foo1\nfoo2\nfoo = baz") - end - end - describe 'inserts at match' do - include_context 'resource_create' - it { - provider.create - expect(File.read(@tmpfile).chomp).to eq("foo1\ninserted = line\nfoo = baz") - } - end - describe 'inserts a new line after when no match' do - include_context 'resource_create' do - let(:match) { '^nevergoingtomatch$' } - end - it { - provider.create - expect(File.read(@tmpfile).chomp).to eq("foo1\ninserted = line\nfoo2\nfoo = baz") - } - end - describe 'append to end of file if no match for both after and match' do - include_context 'resource_create' do - let(:match) { '^nevergoingtomatch$' } - let(:after) { '^stillneverafter' } + describe 'using match+append_on_no_match' do + context 'when there is a match' do + it 'should replace line' do + @resource = Puppet::Type::File_line.new( + { + :name => 'foo', + :path => tmpfile, + :line => 'inserted = line', + :match => '^foo3$', + :append_on_no_match => false, + } + ) + @provider = provider_class.new(@resource) + File.open(tmpfile, 'w') do |fh| + fh.write("foo1\nfoo = blah\nfoo2\nfoo = baz") end - it { - provider.create - expect(File.read(@tmpfile).chomp).to eq("foo1\nfoo2\nfoo = baz\ninserted = line") - } + expect(@provider.exists?).to be true + expect(File.read(tmpfile).chomp).to eql("foo1\nfoo = blah\nfoo2\nfoo = baz") end end - context 'with one line matching the after expression' do - before :each do - File.open(@tmpfile, 'w') do |fh| + context 'when there is no match' do + it 'should not add line after no matches found' do + @resource = Puppet::Type::File_line.new( + { + :name => 'foo', + :path => tmpfile, + :line => 'inserted = line', + :match => '^foo3$', + :append_on_no_match => false, + } + ) + @provider = provider_class.new(@resource) + File.open(tmpfile, 'w') do |fh| fh.write("foo1\nfoo = blah\nfoo2\nfoo = baz") end + expect(@provider.exists?).to be true + expect(File.read(tmpfile).chomp).to eql("foo1\nfoo = blah\nfoo2\nfoo = baz") end + end + end + end + context "when match+replace+append_on_no_match" do + end + context 'when after' do + let :resource do + Puppet::Type::File_line.new( + { + :name => 'foo', + :path => tmpfile, + :line => 'inserted = line', + :after => '^foo1', + } + ) + end - it 'inserts the specified line after the line matching the "after" expression' do + let :provider do + provider_class.new(resource) + end + context 'match and after set' do + shared_context 'resource_create' do + let(:match) { '^foo2$' } + let(:after) { '^foo1$' } + let(:resource) { + Puppet::Type::File_line.new( + { + :name => 'foo', + :path => tmpfile, + :line => 'inserted = line', + :after => after, + :match => match, + } + ) + } + end + before :each do + File.open(tmpfile, 'w') do |fh| + fh.write("foo1\nfoo2\nfoo = baz") + end + end + describe 'inserts at match' do + include_context 'resource_create' + it { provider.create - expect(File.read(@tmpfile).chomp).to eql("foo1\ninserted = line\nfoo = blah\nfoo2\nfoo = baz") + expect(File.read(tmpfile).chomp).to eq("foo1\ninserted = line\nfoo = baz") + } + end + describe 'inserts a new line after when no match' do + include_context 'resource_create' do + let(:match) { '^nevergoingtomatch$' } end + it { + provider.create + expect(File.read(tmpfile).chomp).to eq("foo1\ninserted = line\nfoo2\nfoo = baz") + } end - - context 'with multiple lines matching the after expression' do - before :each do - File.open(@tmpfile, 'w') do |fh| - fh.write("foo1\nfoo = blah\nfoo2\nfoo1\nfoo = baz") - end + describe 'append to end of file if no match for both after and match' do + include_context 'resource_create' do + let(:match) { '^nevergoingtomatch$' } + let(:after) { '^stillneverafter' } end - - it 'errors out stating "One or no line must match the pattern"' do - expect { provider.create }.to raise_error(Puppet::Error, /One or no line must match the pattern/) + it { + provider.create + expect(File.read(tmpfile).chomp).to eq("foo1\nfoo2\nfoo = baz\ninserted = line") + } + end + end + context 'with one line matching the after expression' do + before :each do + File.open(tmpfile, 'w') do |fh| + fh.write("foo1\nfoo = blah\nfoo2\nfoo = baz") end + end - it 'adds the line after all lines matching the after expression' do - @resource = Puppet::Type::File_line.new( - { - :name => 'foo', - :path => @tmpfile, - :line => 'inserted = line', - :after => '^foo1$', - :multiple => true, - } - ) - @provider = provider_class.new(@resource) - expect(@provider.exists?).to eql (false) - @provider.create - expect(File.read(@tmpfile).chomp).to eql("foo1\ninserted = line\nfoo = blah\nfoo2\nfoo1\ninserted = line\nfoo = baz") + it 'inserts the specified line after the line matching the "after" expression' do + provider.create + expect(File.read(tmpfile).chomp).to eql("foo1\ninserted = line\nfoo = blah\nfoo2\nfoo = baz") + end + end + context 'with multiple lines matching the after expression' do + before :each do + File.open(tmpfile, 'w') do |fh| + fh.write("foo1\nfoo = blah\nfoo2\nfoo1\nfoo = baz") end end - context 'with no lines matching the after expression' do - let :content do - "foo3\nfoo = blah\nfoo2\nfoo = baz\n" - end + it 'errors out stating "One or no line must match the pattern"' do + expect { provider.create }.to raise_error(Puppet::Error, /One or no line must match the pattern/) + end - before :each do - File.open(@tmpfile, 'w') do |fh| - fh.write(content) - end - end + it 'adds the line after all lines matching the after expression' do + @resource = Puppet::Type::File_line.new( + { + :name => 'foo', + :path => tmpfile, + :line => 'inserted = line', + :after => '^foo1$', + :multiple => true, + } + ) + @provider = provider_class.new(@resource) + expect(@provider.exists?).to eql (false) + @provider.create + expect(File.read(tmpfile).chomp).to eql("foo1\ninserted = line\nfoo = blah\nfoo2\nfoo1\ninserted = line\nfoo = baz") + end + end + context 'with no lines matching the after expression' do + let :content do + "foo3\nfoo = blah\nfoo2\nfoo = baz\n" + end - it 'appends the specified line to the file' do - provider.create - expect(File.read(@tmpfile)).to eq(content << resource[:line] << "\n") + before :each do + File.open(tmpfile, 'w') do |fh| + fh.write(content) end end + + it 'appends the specified line to the file' do + provider.create + expect(File.read(tmpfile)).to eq(content << resource[:line] << "\n") + end end end - context "when removing with a line" do before :each do # TODO: these should be ported over to use the PuppetLabs spec_helper # file fixtures once the following pull request has been merged: # https://github.com/puppetlabs/puppetlabs-stdlib/pull/73/files - tmp = Tempfile.new('tmp') - @tmpfile = tmp.path - tmp.close! @resource = Puppet::Type::File_line.new( { :name => 'foo', - :path => @tmpfile, + :path => tmpfile, :line => 'foo', :ensure => 'absent', } @@ -356,59 +585,49 @@ @provider = provider_class.new(@resource) end it 'should remove the line if it exists' do - File.open(@tmpfile, 'w') do |fh| + File.open(tmpfile, 'w') do |fh| fh.write("foo1\nfoo\nfoo2") end @provider.destroy - expect(File.read(@tmpfile)).to eql("foo1\nfoo2") + expect(File.read(tmpfile)).to eql("foo1\nfoo2") end - it 'should remove the line without touching the last new line' do - File.open(@tmpfile, 'w') do |fh| + File.open(tmpfile, 'w') do |fh| fh.write("foo1\nfoo\nfoo2\n") end @provider.destroy - expect(File.read(@tmpfile)).to eql("foo1\nfoo2\n") + expect(File.read(tmpfile)).to eql("foo1\nfoo2\n") end - it 'should remove any occurence of the line' do - File.open(@tmpfile, 'w') do |fh| + File.open(tmpfile, 'w') do |fh| fh.write("foo1\nfoo\nfoo2\nfoo\nfoo") end @provider.destroy - expect(File.read(@tmpfile)).to eql("foo1\nfoo2\n") + expect(File.read(tmpfile)).to eql("foo1\nfoo2\n") end - it 'example in the docs' do @resource = Puppet::Type::File_line.new( { :name => 'bashrc_proxy', :ensure => 'absent', - :path => @tmpfile, + :path => tmpfile, :line => 'export HTTP_PROXY=http://squid.puppetlabs.vm:3128', } ) @provider = provider_class.new(@resource) - File.open(@tmpfile, 'w') do |fh| + File.open(tmpfile, 'w') do |fh| fh.write("foo1\nfoo2\nexport HTTP_PROXY=http://squid.puppetlabs.vm:3128\nfoo4\n") end @provider.destroy - expect(File.read(@tmpfile)).to eql("foo1\nfoo2\nfoo4\n") + expect(File.read(tmpfile)).to eql("foo1\nfoo2\nfoo4\n") end end - context "when removing with a match" do before :each do - # TODO: these should be ported over to use the PuppetLabs spec_helper - # file fixtures once the following pull request has been merged: - # https://github.com/puppetlabs/puppetlabs-stdlib/pull/73/files - tmp = Tempfile.new('tmp') - @tmpfile = tmp.path - tmp.close! @resource = Puppet::Type::File_line.new( { :name => 'foo', - :path => @tmpfile, + :path => tmpfile, :line => 'foo2', :ensure => 'absent', :match => 'o$', @@ -419,57 +638,57 @@ end it 'should find a line to match' do - File.open(@tmpfile, 'w') do |fh| + File.open(tmpfile, 'w') do |fh| fh.write("foo1\nfoo\nfoo2") end expect(@provider.exists?).to eql (true) end it 'should remove one line if it matches' do - File.open(@tmpfile, 'w') do |fh| + File.open(tmpfile, 'w') do |fh| fh.write("foo1\nfoo\nfoo2") end @provider.destroy - expect(File.read(@tmpfile)).to eql("foo1\nfoo2") + expect(File.read(tmpfile)).to eql("foo1\nfoo2") end it 'the line parameter is actually not used at all but is silently ignored if here' do @resource = Puppet::Type::File_line.new( { :name => 'foo', - :path => @tmpfile, + :path => tmpfile, :line => 'supercalifragilisticexpialidocious', :ensure => 'absent', :match => 'o$', :match_for_absence => true, } ) - File.open(@tmpfile, 'w') do |fh| + File.open(tmpfile, 'w') do |fh| fh.write("foo1\nfoo\nfoo2") end @provider.destroy - expect(File.read(@tmpfile)).to eql("foo1\nfoo2") + expect(File.read(tmpfile)).to eql("foo1\nfoo2") end it 'and may not be here and does not need to be here' do @resource = Puppet::Type::File_line.new( { :name => 'foo', - :path => @tmpfile, + :path => tmpfile, :ensure => 'absent', :match => 'o$', :match_for_absence => true, } ) - File.open(@tmpfile, 'w') do |fh| + File.open(tmpfile, 'w') do |fh| fh.write("foo1\nfoo\nfoo2") end @provider.destroy - expect(File.read(@tmpfile)).to eql("foo1\nfoo2") + expect(File.read(tmpfile)).to eql("foo1\nfoo2") end it 'should raise an error if more than one line matches' do - File.open(@tmpfile, 'w') do |fh| + File.open(tmpfile, 'w') do |fh| fh.write("foo1\nfoo\nfoo2\nfoo\nfoo") end expect { @provider.destroy }.to raise_error(Puppet::Error, /More than one line/) @@ -479,7 +698,7 @@ @resource = Puppet::Type::File_line.new( { :name => 'foo', - :path => @tmpfile, + :path => tmpfile, :line => 'foo2', :ensure => 'absent', :match => 'o$', @@ -488,36 +707,36 @@ } ) @provider = provider_class.new(@resource) - File.open(@tmpfile, 'w') do |fh| + File.open(tmpfile, 'w') do |fh| fh.write("foo1\nfoo\nfoo2\nfoo\nfoo") end @provider.destroy - expect(File.read(@tmpfile)).to eql("foo1\nfoo2\n") + expect(File.read(tmpfile)).to eql("foo1\nfoo2\n") end it 'should ignore the match if match_for_absence is not specified' do @resource = Puppet::Type::File_line.new( { :name => 'foo', - :path => @tmpfile, + :path => tmpfile, :line => 'foo2', :ensure => 'absent', :match => 'o$', } ) @provider = provider_class.new(@resource) - File.open(@tmpfile, 'w') do |fh| + File.open(tmpfile, 'w') do |fh| fh.write("foo1\nfoo\nfoo2") end @provider.destroy - expect(File.read(@tmpfile)).to eql("foo1\nfoo\n") + expect(File.read(tmpfile)).to eql("foo1\nfoo\n") end it 'should ignore the match if match_for_absence is false' do @resource = Puppet::Type::File_line.new( { :name => 'foo', - :path => @tmpfile, + :path => tmpfile, :line => 'foo2', :ensure => 'absent', :match => 'o$', @@ -525,11 +744,11 @@ } ) @provider = provider_class.new(@resource) - File.open(@tmpfile, 'w') do |fh| + File.open(tmpfile, 'w') do |fh| fh.write("foo1\nfoo\nfoo2") end @provider.destroy - expect(File.read(@tmpfile)).to eql("foo1\nfoo\n") + expect(File.read(tmpfile)).to eql("foo1\nfoo\n") end it 'example in the docs' do @@ -537,18 +756,18 @@ { :name => 'bashrc_proxy', :ensure => 'absent', - :path => @tmpfile, + :path => tmpfile, :line => 'export HTTP_PROXY=http://squid.puppetlabs.vm:3128', :match => '^export\ HTTP_PROXY\=', :match_for_absence => true, } ) @provider = provider_class.new(@resource) - File.open(@tmpfile, 'w') do |fh| + File.open(tmpfile, 'w') do |fh| fh.write("foo1\nfoo2\nexport HTTP_PROXY=foo\nfoo4\n") end @provider.destroy - expect(File.read(@tmpfile)).to eql("foo1\nfoo2\nfoo4\n") + expect(File.read(tmpfile)).to eql("foo1\nfoo2\nfoo4\n") end it 'example in the docs showing line is redundant' do @@ -556,17 +775,17 @@ { :name => 'bashrc_proxy', :ensure => 'absent', - :path => @tmpfile, + :path => tmpfile, :match => '^export\ HTTP_PROXY\=', :match_for_absence => true, } ) @provider = provider_class.new(@resource) - File.open(@tmpfile, 'w') do |fh| + File.open(tmpfile, 'w') do |fh| fh.write("foo1\nfoo2\nexport HTTP_PROXY=foo\nfoo4\n") end @provider.destroy - expect(File.read(@tmpfile)).to eql("foo1\nfoo2\nfoo4\n") + expect(File.read(tmpfile)).to eql("foo1\nfoo2\nfoo4\n") end end end