commit fba691ae268e2d90b38db4a6ee6587e6c75d8505
Author: Marc Dequenes <duck@Orfeo.duckcorp.org>
Date:   Sat Feb 21 22:22:13 2009 +0100

    [evol] backup zone before replacing, reload zone, and rollback zone if reload failed

diff --git a/lib/cyborghood/services/dns.rb b/lib/cyborghood/services/dns.rb
index 973e73c..129efd0 100644
--- a/lib/cyborghood/services/dns.rb
+++ b/lib/cyborghood/services/dns.rb
@@ -12,32 +12,28 @@ module CyborgHood
         @config = Config.instance
         @content = nil
 
+        @filename = @config.dns.master_zone_pattern.gsub("#ZONE#", @zone)
+        # TODO: should be checked at startup time
+        raise CyberError.new(:unrecoverable, "services/dns", "erroneous configuration: pattern is constant") if @filename == @config.dns.master_zone_pattern
+
         # ensure we can find the needed programs (should be handled somewhere else)
         ENV['PATH'] = (ENV['PATH'].split(":") + ["/sbin", "/usr/sbin", "/usr/local/sbin"]).uniq.join(":")
       end
 
       def read_zone
-        filename = @config.dns.master_zone_pattern.gsub("#ZONE#", @zone)
-        # TODO: should be checked at startup time
-        raise CyberError.new(:unrecoverable, "services/dns", "erroneous configuration: pattern is constant") if filename == @config.dns.master_zone_pattern
-
         begin
-          @content = File.read(filename)
+          @content = File.read(@filename)
         rescue
           raise CyberError.new(:unrecoverable, "services/dns", "zone '#{@zone}' cannot be read (nonexistent or lack of permission)")
         end
       end
 
       def serial
-        filename = @config.dns.master_zone_pattern.gsub("#ZONE#", @zone)
-        # TODO: should be checked at startup time
-        raise CyberError.new(:unrecoverable, "services/dns", "erroneous configuration: pattern is constant") if filename == @config.dns.master_zone_pattern
-
         case @config.dns.nameserver || :bind
         when :bind
           output = []
           begin
-            IO.popen("named-checkzone -i none '#{@zone}' #{filename}") do |fp|
+            IO.popen("named-checkzone -i none '#{@zone}' #{@filename}") do |fp|
               output << fp.gets.chomp! until fp.eof?
             end
           rescue
@@ -106,11 +102,28 @@ module CyborgHood
       end
 
       def write_zone_from_file(new_zone_filename)
-        filename = @config.dns.master_zone_pattern.gsub("#ZONE#", @zone)
-        # TODO: should be checked at startup time
-        raise CyberError.new(:unrecoverable, "services/dns", "erroneous configuration: pattern is constant") if filename == @config.dns.master_zone_pattern
+        # create backup
+        FileUtils.cp(@filename, backup_zone_filename())
+
+        FileUtils.cp(new_zone_filename, @filename)
+      end
+
+      def replace_zone_with_backup
+        if File.exists?(backup_zone_filename())
+          FileUtils.cp(backup_zone_filename(), @filename)
+        else
+          raise CyberError.new(:unrecoverable, "services/dns", "no zone backup file found")
+        end
+      end
+
+      def reload_zone
+        system "sudo rndc reload '#{@zone}' >/dev/null"
+      end
+
+      private
 
-        FileUtils.cp(new_zone_filename, filename)
+      def backup_zone_filename
+        @filename + ".ch-backup"
       end
     end
   end
diff --git a/postman b/postman
index 9406c9e..7ee6e5e 100755
--- a/postman
+++ b/postman
@@ -11,6 +11,7 @@ require 'cyborghood/imap'
 require 'cyborghood/mail'
 require 'cyborghood/objects'
 require 'cyborghood/services/dns'
+require 'fileutils'
 
 #Socket.gethostname
 
@@ -106,7 +107,16 @@ class CommandParser
                       if current_serial.nil? or result.serial > current_serial
                         begin
                           srv_dns.write_zone_from_file(f.path)
+                          f.close!
                           logger.info "zone changed"
+                          if srv_dns.reload_zone
+                            logger.info "zone reloaded"
+                          else
+                            logger.info "zone reload failed, replacing old content"
+                            srv_dns.replace_zone_with_backup
+                            result.message = "Internal error."
+                            return result
+                          end
                         rescue
                           logger.debug "Writing zone file failed"
                           raise
