Please review pull request #546: (#11988) Don't overwrite symlinks in augeas provider opened by (nicklewis)

Description:

Previously, if not running with force set, we would try to write the
file in SAVE_NEWFILE mode to create a .augnew file with the
changes. We determined whether there were changes to be made based on
that file (and used it to show a diff). When it came time to actually
make the changes, we would simply move the .augnew file over the file
being managed. Unfortunately, if the file being managed were a symlink,
this would clobber it.

There was a fallback path in the case of force (or older versions of
augeas not supporting SAVE_NEWFILE) in which we would make the
changes in SAVE_OVERWRITE mode as normal. Now, the behavior is a
combination of the two; we still use SAVE_NEWFILE to determine whether
changes need to be made and to show a diff, but then remove the .augnew
file and always run again in SAVE_OVERWRITE mode to save the changes.
This effectively delegates the behavior of preserving the file, etc.
to augeas, so we don't duplicate effort or bugs.

  • Opened: Wed Feb 29 23:08:19 UTC 2012
  • Based on: puppetlabs:2.7.x (675b245647f5452796216eeb35763579c0c8973b)
  • Requested merge: nicklewis:ticket/2.7.x/11988 (dfefc23b744b68589068f1c0e92e2b7baee0cf83)

Diff follows:

diff --git a/lib/puppet/provider/augeas/augeas.rb b/lib/puppet/provider/augeas/augeas.rb
index 703a01b..303c59d 100644
--- a/lib/puppet/provider/augeas/augeas.rb
+++ b/lib/puppet/provider/augeas/augeas.rb
@@ -285,10 +285,10 @@ def need_to_run?
 
       unless force
         # If we have a verison of augeas which is at least 0.3.6 then we
-        # can make the changes now, see if changes were made, and
-        # actually do the save.
+        # can make the changes now and see if changes were made.
         if return_value and versioncmp(get_augeas_version, "0.3.6") >= 0
           debug("Will attempt to save and only run if files changed")
+          # Execute in NEWFILE mode so we can show a diff
           set_augeas_save_mode(SAVE_NEWFILE)
           do_execute_changes
           save_result = @aug.save
@@ -302,9 +302,7 @@ def need_to_run?
               if Puppet[:show_diff]
                 notice "\n" + diff(saved_file, saved_file + ".augnew")
               end
-              if resource.noop?
-                File.delete(saved_file + ".augnew")
-              end
+              File.delete(saved_file + ".augnew")
             end
             debug("Files changed, should execute")
             return_value = true
@@ -323,32 +321,16 @@ def need_to_run?
   end
 
   def execute_changes
-    # Re-connect to augeas, and re-execute the changes
-    begin
-      open_augeas
-      saved_files = @aug.match("/augeas/events/saved")
-      unless saved_files.empty?
-        saved_files.each do |key|
-          root = resource[:root].sub(/^\/$/, "")
-          saved_file = @aug.get(key).to_s.sub(/^\/files/, root)
-          if File.exists?(saved_file + ".augnew")
-            success = File.rename(saved_file + ".augnew", saved_file)
-            debug(saved_file + ".augnew moved to " + saved_file)
-            fail("Rename failed with return code #{success}") if success != 0
-          end
-        end
-      else
-        debug("No saved files, re-executing augeas")
-        set_augeas_save_mode(SAVE_OVERWRITE) if versioncmp(get_augeas_version, "0.3.6") >= 0
-        do_execute_changes
-        success = @aug.save
-        fail("Save failed with return code #{success}") if success != true
-      end
-    ensure
-      close_augeas
-    end
+    # Reload augeas, and execute the changes for real
+    aug.load
+    set_augeas_save_mode(SAVE_OVERWRITE) if versioncmp(get_augeas_version, "0.3.6") >= 0
+    do_execute_changes
+    success = @aug.save
+    fail("Save failed with return code #{success}") if success != true
 
     :executed
+  ensure
+    close_augeas
   end
 
   # Actually execute the augeas changes.
diff --git a/spec/unit/provider/augeas/augeas_spec.rb b/spec/unit/provider/augeas/augeas_spec.rb
index 52ebb34..e985398 100755
--- a/spec/unit/provider/augeas/augeas_spec.rb
+++ b/spec/unit/provider/augeas/augeas_spec.rb
@@ -4,14 +4,22 @@
 provider_class = Puppet::Type.type(:augeas).provider(:augeas)
 
 describe provider_class do
-  describe "command parsing" do
-    before do
-      @resource = stub("resource")
-      @provider = provider_class.new(@resource)
-    end
+  before(:each) do
+    @resource = Puppet::Type.type(:augeas).new(
+      :name     => "test",
+      :root     => my_fixture_dir,
+      :provider => :augeas
+    )
+    @provider = provider_class.new(@resource)
+  end
+
+  after(:each) do
+    @provider.close_augeas
+  end
 
+  describe "command parsing" do
     it "should break apart a single line into three tokens and clean up the context" do
-      @resource.stubs(:[]).returns("/context")
+      @resource[:context] = "/context"
       tokens = @provider.parse_commands("set Jar/Jar Binks")
       tokens.size.should == 1
       tokens[0].size.should == 3
@@ -21,7 +29,6 @@
     end
 
     it "should break apart a multiple line into six tokens" do
-      @resource.stubs(:[]).returns("")
       tokens = @provider.parse_commands("set /Jar/Jar Binks\nrm anakin")
       tokens.size.should == 2
       tokens[0].size.should == 3
@@ -34,7 +41,6 @@
     end
 
     it "should strip whitespace and ignore blank lines" do
-      @resource.stubs(:[]).returns("")
       tokens = @provider.parse_commands("  set /Jar/Jar Binks \t\n  \n\n  rm anakin ")
       tokens.size.should == 2
       tokens[0].size.should == 3
@@ -47,7 +53,7 @@
     end
 
     it "should handle arrays" do
-      @resource.stubs(:[]).returns("/foo/")
+      @resource[:context] = "/foo/"
       commands = ["set /Jar/Jar Binks", "rm anakin"]
       tokens = @provider.parse_commands(commands)
       tokens.size.should == 2
@@ -72,7 +78,7 @@
     #end
 
     it "should accept spaces in the value and single ticks" do
-      @resource.stubs(:[]).returns("/foo/")
+      @resource[:context] = "/foo/"
       tokens = @provider.parse_commands("set JarJar 'Binks is my copilot'")
       tokens.size.should == 1
       tokens[0].size.should == 3
@@ -82,7 +88,7 @@
     end
 
     it "should accept spaces in the value and double ticks" do
-      @resource.stubs(:[]).returns("/foo/")
+      @resource[:context] = "/foo/"
       tokens = @provider.parse_commands('set /JarJar "Binks is my copilot"')
       tokens.size.should == 1
       tokens[0].size.should == 3
@@ -92,7 +98,7 @@
     end
 
     it "should accept mixed ticks" do
-      @resource.stubs(:[]).returns("/foo/")
+      @resource[:context] = "/foo/"
       tokens = @provider.parse_commands('set JarJar "Some \'Test\'"')
       tokens.size.should == 1
       tokens[0].size.should == 3
@@ -102,59 +108,59 @@
     end
 
     it "should handle predicates with literals" do
-      @resource.stubs(:[]).returns("/foo/")
+      @resource[:context] = "/foo/"
       tokens = @provider.parse_commands("rm */*[module='pam_console.so']")
       tokens.should == [["rm", "/foo/*/*[module='pam_console.so']"]]
     end
 
     it "should handle whitespace in predicates" do
-      @resource.stubs(:[]).returns("/foo/")
+      @resource[:context] = "/foo/"
       tokens = @provider.parse_commands("ins 42 before /files/etc/hosts/*/ipaddr[ . = '127.0.0.1' ]")
       tokens.should == [["ins", "42", "before","/files/etc/hosts/*/ipaddr[ . = '127.0.0.1' ]"]]
     end
 
     it "should handle multiple predicates" do
-      @resource.stubs(:[]).returns("/foo/")
+      @resource[:context] = "/foo/"
       tokens = @provider.parse_commands("clear pam.d/*/*[module = 'system-auth'][type = 'account']")
       tokens.should == [["clear", "/foo/pam.d/*/*[module = 'system-auth'][type = 'account']"]]
     end
 
     it "should handle nested predicates" do
-      @resource.stubs(:[]).returns("/foo/")
+      @resource[:context] = "/foo/"
       args = ["clear", "/foo/pam.d/*/*[module[ ../type = 'type] = 'system-auth'][type[last()] = 'account']"]
       tokens = @provider.parse_commands(args.join(" "))
       tokens.should == [ args ]
     end
 
     it "should handle escaped doublequotes in doublequoted string" do
-      @resource.stubs(:[]).returns("/foo/")
+      @resource[:context] = "/foo/"
       tokens = @provider.parse_commands("set /foo \"''\\\"''\"")
       tokens.should == [[ "set", "/foo", "''\\\"''" ]]
     end
 
     it "should allow escaped spaces and brackets in paths" do
-      @resource.stubs(:[]).returns("/foo/")
+      @resource[:context] = "/foo/"
       args = [ "set", "/white\\ space/\\[section", "value" ]
       tokens = @provider.parse_commands(args.join(" \t "))
       tokens.should == [ args ]
     end
 
     it "should allow single quoted escaped spaces in paths" do
-      @resource.stubs(:[]).returns("/foo/")
+      @resource[:context] = "/foo/"
       args = [ "set", "'/white\\ space/key'", "value" ]
       tokens = @provider.parse_commands(args.join(" \t "))
       tokens.should == [[ "set", "/white\\ space/key", "value" ]]
     end
 
     it "should allow double quoted escaped spaces in paths" do
-      @resource.stubs(:[]).returns("/foo/")
+      @resource[:context] = "/foo/"
       args = [ "set", '"/white\\ space/key"', "value" ]
       tokens = @provider.parse_commands(args.join(" \t "))
       tokens.should == [[ "set", "/white\\ space/key", "value" ]]
     end
 
     it "should remove trailing slashes" do
-      @resource.stubs(:[]).returns("/foo/")
+      @resource[:context] = "/foo/"
       tokens = @provider.parse_commands("set foo/ bar")
       tokens.should == [[ "set", "/foo/foo", "bar" ]]
     end
@@ -162,9 +168,9 @@
 
   describe "get filters" do
     before do
-      augeas_stub = stub("augeas", :get => "value")
-      @provider = provider_class.new
-      @provider.aug= augeas_stub
+      augeas = stub("augeas", :get => "value")
+      augeas.stubs("close")
+      @provider.aug = augeas
     end
 
     it "should return false for a = nonmatch" do
@@ -190,10 +196,10 @@
 
   describe "match filters" do
     before do
-      resource = stub("resource", :[] => "")
-      augeas_stub = stub("augeas", :match => ["set", "of", "values"])
-      @provider = provider_class.new(resource)
-      @provider.aug= augeas_stub
+      augeas = stub("augeas", :match => ["set", "of", "values"])
+      augeas.stubs("close")
+      @provider = provider_class.new(@resource)
+      @provider.aug = augeas
     end
 
     it "should return true for size match" do
@@ -248,126 +254,89 @@
   end
 
   describe "need to run" do
+    before(:each) do
+      @augeas = stub("augeas")
+      @augeas.stubs("close")
+      @provider.aug = @augeas
+
+      # These tests pretend to be an earlier version so the provider doesn't
+      # attempt to make the change in the need_to_run? method
+      @provider.stubs(:get_augeas_version).returns("0.3.5")
+    end
+
     it "should handle no filters" do
-      resource = stub("resource")
-      resource.stubs(:[]).returns(false).then.returns("").then.returns("")
-      resource.stubs(:noop?).returns(false)
-      augeas_stub = stub("augeas", :match => ["set", "of", "values"])
-      augeas_stub.stubs("close")
-      provider = provider_class.new(resource)
-      provider.aug= augeas_stub
-      provider.stubs(:get_augeas_version).returns("0.3.5")
-      provider.need_to_run?.should == true
+      @augeas.stubs("match").returns(["set", "of", "values"])
+      @provider.need_to_run?.should == true
     end
 
     it "should return true when a get filter matches" do
-      resource = stub("resource")
-      resource.stubs(:[]).returns(false).then.returns("get path == value").then.returns("")
-      resource.stubs(:noop?).returns(false)
-      provider = provider_class.new(resource)
-      augeas_stub = stub("augeas", :get => "value")
-      augeas_stub.stubs("close")
-      provider.aug= augeas_stub
-      provider.stubs(:get_augeas_version).returns("0.3.5")
-      provider.need_to_run?.should == true
+      @resource[:onlyif] = "get path == value"
+      @augeas.stubs("get").returns("value")
+      @provider.need_to_run?.should == true
     end
 
     it "should return false when a get filter does not match" do
-      resource = stub("resource")
-      resource.stubs(:[]).returns(false).then.returns("get path == another value").then.returns("")
-      provider = provider_class.new(resource)
-      augeas_stub = stub("augeas", :get => "value")
-      augeas_stub.stubs("close")
-      provider.aug= augeas_stub
-      provider.stubs(:get_augeas_version).returns("0.3.5")
-      provider.need_to_run?.should == false
+      @resource[:onlyif] = "get path == another value"
+      @augeas.stubs("get").returns("value")
+      @provider.need_to_run?.should == false
     end
 
     it "should return true when a match filter matches" do
-      resource = stub("resource")
-      resource.stubs(:[]).returns(false).then.returns("match path size == 3").then.returns("")
-      resource.stubs(:noop?).returns(false)
-      provider = provider_class.new(resource)
-      augeas_stub = stub("augeas", :match => ["set", "of", "values"])
-      augeas_stub.stubs("close")
-      provider.aug= augeas_stub
-      provider.stubs(:get_augeas_version).returns("0.3.5")
-      provider.need_to_run?.should == true
+      @resource[:onlyif] = "match path size == 3"
+      @augeas.stubs("match").returns(["set", "of", "values"])
+      @provider.need_to_run?.should == true
     end
 
     it "should return false when a match filter does not match" do
-      resource = stub("resource")
-      resource.stubs(:[]).returns(false).then.returns("match path size == 2").then.returns("")
-      provider = provider_class.new(resource)
-      augeas_stub = stub("augeas", :match => ["set", "of", "values"])
-      augeas_stub.stubs("close")
-      provider.aug= augeas_stub
-      provider.stubs(:get_augeas_version).returns("0.3.5")
-      provider.need_to_run?.should == false
+      @resource[:onlyif] = "match path size == 2"
+      @augeas.stubs("match").returns(["set", "of", "values"])
+      @provider.need_to_run?.should == false
     end
 
-    #This is a copy of the last one, with setting the force to true
+    # Now setting force to true
     it "setting force should not change the above logic" do
-      resource = stub("resource")
-      resource.stubs(:[]).returns(true).then.returns("match path size == 2").then.returns("")
-      provider = provider_class.new(resource)
-      augeas_stub = stub("augeas", :match => ["set", "of", "values"])
-      augeas_stub.stubs("close")
-      provider.aug= augeas_stub
-      provider.stubs(:get_augeas_version).returns("0.3.5")
-      provider.need_to_run?.should == false
+      @resource[:force] = true
+      @resource[:onlyif] = "match path size == 2"
+      @augeas.stubs("match").returns(["set", "of", "values"])
+      @provider.need_to_run?.should == false
     end
 
     #Ticket 5211 testing
     it "should return true when a size != the provided value" do
-      resource = stub("resource")
-      resource.stubs(:[]).returns(false).then.returns("match path size != 17").then.returns("")
-      resource.stubs(:noop?).returns(false)
-      provider = provider_class.new(resource)
-      augeas_stub = stub("augeas", :match => ["set", "of", "values"])
-      augeas_stub.stubs("close")
-      provider.aug= augeas_stub
-      provider.stubs(:get_augeas_version).returns("0.3.5")
-      provider.need_to_run?.should == true
+      @resource[:onlyif] = "match path size != 17"
+      @augeas.stubs("match").returns(["set", "of", "values"])
+      @provider.need_to_run?.should == true
     end
 
     #Ticket 5211 testing
     it "should return false when a size doeas equal the provided value" do
-      resource = stub("resource")
-      resource.stubs(:[]).returns(false).then.returns("match path size != 3").then.returns("")
-      provider = provider_class.new(resource)
-      augeas_stub = stub("augeas", :match => ["set", "of", "values"])
-      augeas_stub.stubs("close")
-      provider.aug= augeas_stub
-      provider.stubs(:get_augeas_version).returns("0.3.5")
-      provider.need_to_run?.should == false
+      @resource[:onlyif] = "match path size != 3"
+      @augeas.stubs("match").returns(["set", "of", "values"])
+      @provider.need_to_run?.should == false
     end
 
     # Ticket 2728 (diff files)
-    describe "and Puppet[:show_diff] is set", :if => Puppet.features.augeas? do
-      before do
+    describe "and Puppet[:show_diff] is set" do
+      before(:each) do
         Puppet[:show_diff] = true
 
-        @resource = Puppet::Type.type(:augeas).new(:name => "test")
-        @provider = provider_class.new(@resource)
-        @augeas_stub = stub("augeas")
-        @provider.aug = @augeas_stub
-
-        @augeas_stub.stubs("get").with("/augeas/version").returns("0.10.0")
-        @augeas_stub.stubs(:set).returns(true)
-        @augeas_stub.stubs(:save).returns(true)
+        @resource[:root] = ""
+        @provider.stubs(:get_augeas_version).returns("0.10.0")
+        @augeas.stubs(:set).returns(true)
+        @augeas.stubs(:save).returns(true)
       end
 
       it "should call diff when a file is shown to have been changed" do
         file = "/etc/hosts"
+        File.stubs(:delete)
 
         @resource[:context] = "/files"
         @resource[:changes] = ["set #{file}/foo bar"]
 
-        @augeas_stub.stubs(:match).with("/augeas/events/saved").returns(["/augeas/events/saved"])
-        @augeas_stub.stubs(:get).with("/augeas/events/saved").returns(["/files#{file}"])
-        @augeas_stub.expects(:set).with("/augeas/save", "newfile")
-        @augeas_stub.expects(:close).never()
+        @augeas.stubs(:match).with("/augeas/events/saved").returns(["/augeas/events/saved"])
+        @augeas.stubs(:get).with("/augeas/events/saved").returns(["/files#{file}"])
+        @augeas.expects(:set).with("/augeas/save", "newfile")
+        @augeas.expects(:close).never()
 
         @provider.expects("diff").with("#{file}", "#{file}.augnew").returns("")
         @provider.should be_need_to_run
@@ -376,15 +345,16 @@
       it "should call diff for each file thats changed" do
         file1 = "/etc/hosts"
         file2 = "/etc/resolv.conf"
+        File.stubs(:delete)
 
         @resource[:context] = "/files"
         @resource[:changes] = ["set #{file1}/foo bar", "set #{file2}/baz biz"]
 
-        @augeas_stub.stubs(:match).with("/augeas/events/saved").returns(["/augeas/events/saved[1]", "/augeas/events/saved[2]"])
-        @augeas_stub.stubs(:get).with("/augeas/events/saved[1]").returns(["/files#{file1}"])
-        @augeas_stub.stubs(:get).with("/augeas/events/saved[2]").returns(["/files#{file2}"])
-        @augeas_stub.expects(:set).with("/augeas/save", "newfile")
-        @augeas_stub.expects(:close).never()
+        @augeas.stubs(:match).with("/augeas/events/saved").returns(["/augeas/events/saved[1]", "/augeas/events/saved[2]"])
+        @augeas.stubs(:get).with("/augeas/events/saved[1]").returns(["/files#{file1}"])
+        @augeas.stubs(:get).with("/augeas/events/saved[2]").returns(["/files#{file2}"])
+        @augeas.expects(:set).with("/augeas/save", "newfile")
+        @augeas.expects(:close).never()
 
         @provider.expects(:diff).with("#{file1}", "#{file1}.augnew").returns("")
         @provider.expects(:diff).with("#{file2}", "#{file2}.augnew").returns("")
@@ -395,15 +365,16 @@
         it "should call diff when a file is shown to have been changed" do
           root = "/tmp/foo"
           file = "/etc/hosts"
+          File.stubs(:delete)
 
           @resource[:context] = "/files"
           @resource[:changes] = ["set #{file}/foo bar"]
           @resource[:root] = root
 
-          @augeas_stub.stubs(:match).with("/augeas/events/saved").returns(["/augeas/events/saved"])
-          @augeas_stub.stubs(:get).with("/augeas/events/saved").returns(["/files#{file}"])
-          @augeas_stub.expects(:set).with("/augeas/save", "newfile")
-          @augeas_stub.expects(:close).never()
+          @augeas.stubs(:match).with("/augeas/events/saved").returns(["/augeas/events/saved"])
+          @augeas.stubs(:get).with("/augeas/events/saved").returns(["/files#{file}"])
+          @augeas.expects(:set).with("/augeas/save", "newfile")
+          @augeas.expects(:close).never()
 
           @provider.expects(:diff).with("#{root}#{file}", "#{root}#{file}.augnew").returns("")
           @provider.should be_need_to_run
@@ -416,26 +387,25 @@
         @resource[:context] = "/files"
         @resource[:changes] = ["set #{file}/foo bar"]
 
-        @augeas_stub.stubs(:match).with("/augeas/events/saved").returns([])
-        @augeas_stub.expects(:set).with("/augeas/save", "newfile")
-        @augeas_stub.expects(:get).with("/augeas/events/saved").never()
-        @augeas_stub.expects(:close)
+        @augeas.stubs(:match).with("/augeas/events/saved").returns([])
+        @augeas.expects(:set).with("/augeas/save", "newfile")
+        @augeas.expects(:get).with("/augeas/events/saved").never()
+        @augeas.expects(:close)
 
         @provider.expects(:diff).never()
         @provider.should_not be_need_to_run
       end
 
-      it "should cleanup when in noop mode" do
+      it "should cleanup the .augnew file" do
         file = "/etc/hosts"
 
-        @resource[:noop] = true
         @resource[:context] = "/files"
         @resource[:changes] = ["set #{file}/foo bar"]
 
-        @augeas_stub.stubs(:match).with("/augeas/events/saved").returns(["/augeas/events/saved"])
-        @augeas_stub.stubs(:get).with("/augeas/events/saved").returns(["/files#{file}"])
-        @augeas_stub.expects(:set).with("/augeas/save", "newfile")
-        @augeas_stub.expects(:close)
+        @augeas.stubs(:match).with("/augeas/events/saved").returns(["/augeas/events/saved"])
+        @augeas.stubs(:get).with("/augeas/events/saved").returns(["/files#{file}"])
+        @augeas.expects(:set).with("/augeas/save", "newfile")
+        @augeas.expects(:close)
 
         File.expects(:delete).with(file + ".augnew")
 
@@ -449,9 +419,9 @@
         @resource[:context] = "/files"
         @resource[:changes] = ["set #{file}/foo bar"]
 
-        @augeas_stub.stubs(:save).returns(false)
-        @augeas_stub.stubs(:match).with("/augeas/events/saved").returns([])
-        @augeas_stub.expects(:close)
+        @augeas.stubs(:save).returns(false)
+        @augeas.stubs(:match).with("/augeas/events/saved").returns([])
+        @augeas.expects(:close)
 
         @provider.expects(:diff).never()
         lambda { @provider.need_to_run? }.should raise_error
@@ -460,20 +430,18 @@
   end
 
   describe "augeas execution integration" do
-
     before do
-      @resource = stub("resource")
-      @provider = provider_class.new(@resource)
-      @augeas = stub("augeas")
-      @provider.aug= @augeas
-      @provider.stubs(:get_augeas_version).returns("0.3.5")
+      @augeas = stub("augeas", :load)
+      @augeas.stubs("close")
       @augeas.stubs(:match).with("/augeas/events/saved").returns([])
+
+      @provider.aug = @augeas
+      @provider.stubs(:get_augeas_version).returns("0.3.5")
     end
 
     it "should handle set commands" do
-      command = "set JarJar Binks"
-      context = "/some/path/"
-      @resource.expects(:[]).times(2).returns(command).then.returns(context)
+      @resource[:changes] = "set JarJar Binks"
+      @resource[:context] = "/some/path/"
       @augeas.expects(:set).with("/some/path/JarJar", "Binks").returns(true)
       @augeas.expects(:save).returns(true)
       @augeas.expects(:close)
@@ -481,9 +449,7 @@
     end
 
     it "should handle rm commands" do
-      command = "rm /Jar/Jar"
-      context = ""
-      @resource.expects(:[]).times(2).returns(command).then.returns(context)
+      @resource[:changes] = "rm /Jar/Jar"
       @augeas.expects(:rm).with("/Jar/Jar")
       @augeas.expects(:save).returns(true)
       @augeas.expects(:close)
@@ -491,9 +457,7 @@
     end
 
     it "should handle remove commands" do
-      command = "remove /Jar/Jar"
-      context = ""
-      @resource.expects(:[]).times(2).returns(command).then.returns(context)
+      @resource[:changes] = "remove /Jar/Jar"
       @augeas.expects(:rm).with("/Jar/Jar")
       @augeas.expects(:save).returns(true)
       @augeas.expects(:close)
@@ -501,20 +465,17 @@
     end
 
     it "should handle clear commands" do
-      command = "clear Jar/Jar"
-      context = "/foo/"
-      @resource.expects(:[]).times(2).returns(command).then.returns(context)
+      @resource[:changes] = "clear Jar/Jar"
+      @resource[:context] = "/foo/"
       @augeas.expects(:clear).with("/foo/Jar/Jar").returns(true)
       @augeas.expects(:save).returns(true)
       @augeas.expects(:close)
       @provider.execute_changes.should == :executed
     end
 
-
     it "should handle ins commands with before" do
-      command = "ins Binks before Jar/Jar"
-      context = "/foo"
-      @resource.expects(:[]).times(2).returns(command).then.returns(context)
+      @resource[:changes] = "ins Binks before Jar/Jar"
+      @resource[:context] = "/foo"
       @augeas.expects(:insert).with("/foo/Jar/Jar", "Binks", true)
       @augeas.expects(:save).returns(true)
       @augeas.expects(:close)
@@ -522,9 +483,8 @@
     end
 
     it "should handle ins commands with after" do
-      command = "ins Binks after /Jar/Jar"
-      context = "/foo"
-      @resource.expects(:[]).times(2).returns(command).then.returns(context)
+      @resource[:changes] = "ins Binks after /Jar/Jar"
+      @resource[:context] = "/foo"
       @augeas.expects(:insert).with("/Jar/Jar", "Binks", false)
       @augeas.expects(:save).returns(true)
       @augeas.expects(:close)
@@ -532,9 +492,7 @@
     end
 
     it "should handle ins with no context" do
-      command = "ins Binks after /Jar/Jar"
-      context = "" # this is the default
-      @resource.expects(:[]).times(2).returns(command).then.returns(context)
+      @resource[:changes] = "ins Binks after /Jar/Jar"
       @augeas.expects(:insert).with("/Jar/Jar", "Binks", false)
       @augeas.expects(:save).returns(true)
       @augeas.expects(:close)
@@ -542,9 +500,8 @@
     end
 
     it "should handle multiple commands" do
-      command = ["ins Binks after /Jar/Jar", "clear Jar/Jar"]
-      context = "/foo/"
-      @resource.expects(:[]).times(2).returns(command).then.returns(context)
+      @resource[:changes] = ["ins Binks after /Jar/Jar", "clear Jar/Jar"]
+      @resource[:context] = "/foo/"
       @augeas.expects(:insert).with("/Jar/Jar", "Binks", false)
       @augeas.expects(:clear).with("/foo/Jar/Jar").returns(true)
       @augeas.expects(:save).returns(true)
@@ -553,9 +510,8 @@
     end
 
     it "should handle defvar commands" do
-      command = "defvar myjar Jar/Jar"
-      context = "/foo/"
-      @resource.expects(:[]).times(2).returns(command).then.returns(context)
+      @resource[:changes] = "defvar myjar Jar/Jar"
+      @resource[:context] = "/foo/"
       @augeas.expects(:defvar).with("myjar", "/foo/Jar/Jar").returns(true)
       @augeas.expects(:save).returns(true)
       @augeas.expects(:close)
@@ -563,9 +519,8 @@
     end
 
     it "should pass through augeas variables without context" do
-      command = ["defvar myjar Jar/Jar","set $myjar/Binks 1"]
-      context = "/foo/"
-      @resource.expects(:[]).times(2).returns(command).then.returns(context)
+      @resource[:changes] = ["defvar myjar Jar/Jar","set $myjar/Binks 1"]
+      @resource[:context] = "/foo/"
       @augeas.expects(:defvar).with("myjar", "/foo/Jar/Jar").returns(true)
       # this is the important bit, shouldn't be /foo/$myjar/Binks
       @augeas.expects(:set).with("$myjar/Binks", "1").returns(true)
@@ -575,9 +530,8 @@
     end
 
     it "should handle defnode commands" do
-      command = "defnode newjar Jar/Jar[last()+1] Binks"
-      context = "/foo/"
-      @resource.expects(:[]).times(2).returns(command).then.returns(context)
+      @resource[:changes] = "defnode newjar Jar/Jar[last()+1] Binks"
+      @resource[:context] = "/foo/"
       @augeas.expects(:defnode).with("newjar", "/foo/Jar/Jar[last()+1]", "Binks").returns(true)
       @augeas.expects(:save).returns(true)
       @augeas.expects(:close)
@@ -585,9 +539,8 @@
     end
 
     it "should handle mv commands" do
-      command = "mv Jar/Jar Binks"
-      context = "/foo/"
-      @resource.expects(:[]).times(2).returns(command).then.returns(context)
+      @resource[:changes] = "mv Jar/Jar Binks"
+      @resource[:context] = "/foo/"
       @augeas.expects(:mv).with("/foo/Jar/Jar", "/foo/Binks").returns(true)
       @augeas.expects(:save).returns(true)
       @augeas.expects(:close)
@@ -595,9 +548,8 @@
     end
 
     it "should handle setm commands" do
-      command = ["set test[1]/Jar/Jar Foo","set test[2]/Jar/Jar Bar","setm test Jar/Jar Binks"]
-      context = "/foo/"
-      @resource.expects(:[]).times(2).returns(command).then.returns(context)
+      @resource[:changes] = ["set test[1]/Jar/Jar Foo","set test[2]/Jar/Jar Bar","setm test Jar/Jar Binks"]
+      @resource[:context] = "/foo/"
       @augeas.expects(:set).with("/foo/test[1]/Jar/Jar", "Foo").returns(true)
       @augeas.expects(:set).with("/foo/test[2]/Jar/Jar", "Bar").returns(true)
       @augeas.expects(:setm).with("/foo/test", "Jar/Jar", "Binks").returns(true)
@@ -606,4 +558,33 @@
       @provider.execute_changes.should == :executed
     end
   end
+
+  describe "when making changes", :if => Puppet.features.augeas? do
+    include PuppetSpec::Files
+
+    it "should not clobber the file if it's a symlink" do
+      Puppet::Util::Storage.stubs(:store)
+
+      link = tmpfile('link')
+      target = tmpfile('target')
+      FileUtils.touch(target)
+      FileUtils.symlink(target, link)
+
+      resource = Puppet::Type.type(:augeas).new(
+        :name => 'test',
+        :incl => link,
+        :lens => 'Sshd.lns',
+        :changes => "set PermitRootLogin no"
+      )
+
+      catalog = Puppet::Resource::Catalog.new
+      catalog.add_resource resource
+
+      catalog.apply
+
+      File.ftype(link).should == 'link'
+      File.readlink(link).should == target
+      File.read(target).should =~ /PermitRootLogin no/
+    end
+  end
 end

    

--
You received this message because you are subscribed to the Google Groups "Puppet Developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to [email protected].
For more options, visit this group at http://groups.google.com/group/puppet-dev?hl=en.

Reply via email to