[CalendarServer-changes] [9371] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Mon Jun 18 11:24:20 PDT 2012


Revision: 9371
          http://trac.macosforge.org/projects/calendarserver/changeset/9371
Author:   sagen at apple.com
Date:     2012-06-18 11:24:20 -0700 (Mon, 18 Jun 2012)
Log Message:
-----------
SIGHUP now restarts the service allowing *all* configuration changes to be applied.

Modified Paths:
--------------
    CalendarServer/trunk/calendarserver/tap/caldav.py
    CalendarServer/trunk/calendarserver/tap/test/test_caldav.py
    CalendarServer/trunk/twistedcaldav/test/util.py

Added Paths:
-----------
    CalendarServer/trunk/calendarserver/tap/test/reexec.tac

Modified: CalendarServer/trunk/calendarserver/tap/caldav.py
===================================================================
--- CalendarServer/trunk/calendarserver/tap/caldav.py	2012-06-18 18:20:10 UTC (rev 9370)
+++ CalendarServer/trunk/calendarserver/tap/caldav.py	2012-06-18 18:24:20 UTC (rev 9371)
@@ -22,6 +22,7 @@
 ]
 
 import os
+import signal
 import socket
 import stat
 import sys
@@ -562,7 +563,50 @@
                                env=PARENT_ENVIRONMENT)
 
 
+class ReExecService(MultiService, LoggingMixIn):
+    """
+    A MultiService which catches SIGHUP and re-exec's the process.
+    """
 
+    def __init__(self, pidfilePath, reactor=None):
+        """
+        @param pidFilePath: Absolute path to the pidfile which will need to be
+            removed
+        @type pidFilePath: C{str}
+        """
+        self.pidfilePath = pidfilePath
+        if reactor is None:
+            from twisted.internet import reactor
+        self.reactor = reactor
+        MultiService.__init__(self)
+
+    def reExec(self):
+        """
+        Removes pidfile, registers an exec to happen after shutdown, then
+        stops the reactor.
+        """
+        try:
+            self.log_info("Removing pidfile: %s" % (self.pidfilePath,))
+            os.remove(self.pidfilePath)
+        except OSError:
+            pass
+        self.reactor.addSystemEventTrigger("after", "shutdown", os.execv,
+            sys.executable, [sys.executable] + sys.argv)
+        self.reactor.stop()
+
+    def sighupHandler(self, num, frame):
+        self.log_info("SIGHUP received - restarting")
+        self.reactor.callFromThread(self.reExec)
+
+    def startService(self):
+        self.previousHandler = signal.signal(signal.SIGHUP, self.sighupHandler)
+        MultiService.startService(self)
+
+    def stopService(self):
+        signal.signal(signal.SIGHUP, self.previousHandler)
+        MultiService.stopService(self)
+
+
 class CalDAVServiceMaker (LoggingMixIn):
     implements(IPlugin, IServiceMaker)
 
@@ -637,30 +681,7 @@
                 else:
                     return "%s: %s" % (frame.f_code.co_name, frame.f_lineno)
 
-            import signal
-            def sighup_handler(num, frame):
-                self.log_info("SIGHUP received at %s" % (location(frame),))
 
-                # Reload the config file
-                try:
-                    config.reload()
-                except ConfigurationError, e:
-                    self.log_error("Invalid configuration: {0}".format(e))
-
-                # If combined service send signal to all caldavd children
-                if hasattr(service, "processMonitor"):
-                    service.processMonitor.signalAll(signal.SIGHUP, "caldav")
-
-                # FIXME: There is no memcachepool.getCachePool
-                #   Also, better option is probably to add a hook to
-                #   the config object instead of doing things here.
-                #self.log_info("Suggesting new max clients for memcache.")
-                #memcachepool.getCachePool().suggestMaxClients(
-                #    config.Memcached.MaxClients
-                #)
-
-            signal.signal(signal.SIGHUP, sighup_handler)
-
             return service
 
 
@@ -1063,6 +1084,9 @@
         """
         s = ErrorLoggingMultiService()
 
+        # Add a service to re-exec the master when it receives SIGHUP
+        ReExecService(config.PIDFile).setServiceParent(s)
+
         # Make sure no old socket files are lying around.
         self.deleteStaleSocketFiles()
 

Added: CalendarServer/trunk/calendarserver/tap/test/reexec.tac
===================================================================
--- CalendarServer/trunk/calendarserver/tap/test/reexec.tac	                        (rev 0)
+++ CalendarServer/trunk/calendarserver/tap/test/reexec.tac	2012-06-18 18:24:20 UTC (rev 9371)
@@ -0,0 +1,14 @@
+from twisted.application import service
+from calendarserver.tap.caldav import ReExecService
+
+class TestService(service.Service):
+    def startService(self):
+        print "START"
+    def stopService(self):
+        print "STOP"
+
+application = service.Application("ReExec Tester")
+reExecService = ReExecService("twistd.pid")
+reExecService.setServiceParent(application)
+testService = TestService()
+testService.setServiceParent(reExecService)

Modified: CalendarServer/trunk/calendarserver/tap/test/test_caldav.py
===================================================================
--- CalendarServer/trunk/calendarserver/tap/test/test_caldav.py	2012-06-18 18:20:10 UTC (rev 9370)
+++ CalendarServer/trunk/calendarserver/tap/test/test_caldav.py	2012-06-18 18:24:20 UTC (rev 9371)
@@ -28,11 +28,13 @@
 from twisted.python.usage import Options, UsageError
 from twisted.python.reflect import namedAny
 from twisted.python import log
+from twisted.python.procutils import which
 
 from twisted.internet.interfaces import IProcessTransport, IReactorProcess
 from twisted.internet.protocol import ServerFactory
-from twisted.internet.defer import Deferred
+from twisted.internet.defer import Deferred, inlineCallbacks
 from twisted.internet.task import Clock
+from twisted.internet import reactor
 
 from twisted.application.service import IService, IServiceCollection
 from twisted.application import internet
@@ -51,7 +53,7 @@
 from twistedcaldav.directory.calendar import DirectoryCalendarHomeProvisioningResource
 from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource
 
-from twistedcaldav.test.util import TestCase
+from twistedcaldav.test.util import TestCase, CapturingProcessProtocol
 
 from calendarserver.tap.caldav import (
     CalDAVOptions, CalDAVServiceMaker, CalDAVService, GroupOwnedUNIXServer,
@@ -1159,3 +1161,31 @@
         self.assertIn(option, commandLine)
         self.assertEquals(commandLine[commandLine.index(option) - 1], '-o')
 
+
+
+
+
+
+class ReExecServiceTests(TestCase):
+
+    @inlineCallbacks
+    def test_reExecService(self):
+        """
+        Verify that sending a HUP to the test reexec.tac causes startService
+        and stopService to be called again by counting the number of times
+        START and STOP appear in the process output.
+        """
+        tacFilePath = os.path.join(os.path.dirname(__file__), "reexec.tac")
+        twistd = which("twistd")[0]
+        deferred = Deferred()
+        proc = reactor.spawnProcess(
+            CapturingProcessProtocol(deferred, None), twistd,
+                [twistd, '-n', '-y', tacFilePath],
+                env=os.environ
+        )
+        reactor.callLater(3, proc.signalProcess, "HUP")
+        reactor.callLater(6, proc.signalProcess, "TERM")
+        output = yield deferred
+        self.assertEquals(output.count("START"), 2)
+        self.assertEquals(output.count("STOP"), 2)
+

Modified: CalendarServer/trunk/twistedcaldav/test/util.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/util.py	2012-06-18 18:20:10 UTC (rev 9370)
+++ CalendarServer/trunk/twistedcaldav/test/util.py	2012-06-18 18:24:20 UTC (rev 9371)
@@ -24,7 +24,6 @@
 from twisted.python.failure import Failure
 from twisted.internet.base import DelayedCall
 from twisted.internet.defer import succeed, fail, inlineCallbacks, returnValue
-from twisted.internet.error import ProcessDone
 from twisted.internet.protocol import ProcessProtocol
 
 from twext.python.memcacheclient import ClientFactory
@@ -670,8 +669,7 @@
         """
         The process is over, fire the Deferred with the output.
         """
-        if why.check(ProcessDone) and not self.error:
+        if why.value.exitCode == 0 and not self.error:
             self.deferred.callback(''.join(self.output))
         else:
-            self.deferred.errback(ErrorOutput(''.join(self.error)))
-
+            self.deferred.errback(ErrorOutput(repr(''.join(self.error))))
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120618/a083a173/attachment-0001.html>


More information about the calendarserver-changes mailing list