Rev 5: 'apache2' can't be relied upon to ensure server is started or in file:///v/home/vila/.bazaar/plugins/local_test_server/

Vincent Ladeuil v.ladeuil+lp at free.fr
Wed May 28 10:15:09 BST 2008


At file:///v/home/vila/.bazaar/plugins/local_test_server/

------------------------------------------------------------
revno: 5
revision-id: v.ladeuil+lp at free.fr-20080528091508-y8f72o1afg1zhxsd
parent: v.ladeuil+lp at free.fr-20080528082712-cbye8jpu0qxvz1rf
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: local_test_server
timestamp: Wed 2008-05-28 11:15:08 +0200
message:
  'apache2' can't be relied upon to ensure server is started or
  stopped.
  
  * server.py:
  (Apache2): Refactored now that we got a better understanding of
  the issues.
modified:
  server.py                      server.py-20080524160639-rqhbexqatjqbwypw-1
-------------- next part --------------
=== modified file 'server.py'
--- a/server.py	2008-05-28 08:27:12 +0000
+++ b/server.py	2008-05-28 09:15:08 +0000
@@ -42,10 +42,6 @@
     def is_running(self):
         raise NotImplementedError(self.is_running)
 
-    @property
-    def pid(self):
-        raise NotImplementedError(self.pid)
-
     def get_config_value(self, name, default=None):
         return self.config.get_value(name, default)
 
@@ -66,8 +62,16 @@
             f.close()
         return content
 
+
 class Apache2(Server):
 
+    # Sometimes we need to let apache2 do its house keeping (hk) to be sure
+    # some commands are really taken into account.
+    _hk_delay = 10 # in seconds
+    # We poll at regular intervals to achieve that, for lack of a better
+    # alternative
+    _hk_interval = 10.0 # in miliseconds
+
     def __init__(self):
         super(Apache2, self).__init__(config.Apache2())
         self.port = None
@@ -83,57 +87,79 @@
             outfile.close()
         subprocess.check_call(['apache2', '-k', 'start',
                                '-f', config_filename ])
-        self._wait_for_pidfile_to_be_created()
+        if not self._wait_for_pidfile_to_be_created():
+            raise errors.BzrCommandError('apache2 cannot be atarted')
 
-    def _wait_for_pidfile_to_be_created(self):
-        # I couldn't find a a way/command/parameter to start apache2 so that
-        # pidfile file is created when the command returns. Then, if the server
-        # fails to start no pidfile is ever created. Damn if you wait, damn if
-        # you wait too long ;-)
-        started = False
-        delay = 0.001 # 10 ms
-        # total delay to wait in seconds, if you experienced a longer start
-        # time, well, increase.
-        total_delay = 2
-        for i in range(0, int(total_delay * (1  / delay))):
-            if self.is_running():
-                break
-            else:
-                time.sleep(delay)
 
     def stop(self):
         config_filename = config.get_lts_path('etc/apache2.conf')
-        pid = self.pid
+        pid = self.get_pid()
         # Tell it to stop
         subprocess.call(['apache2', '-k', 'stop',
                          '-f', config_filename ])
         # But of course, it's not enough
-        self._wait_for_pid_death(pid)
+        if not self._wait_for_pid_death(pid):
+            raise errors.BzrCommandError(
+                'apache2 cannot be stopped pid: %s' % pid)
+
+    def _hk_poll(self, verified, delay=None, interval=None):
+        """Poll for a condition to be verified until delay expired.
+
+        :param verified: A callable retuning True if the desired condition is
+            verified.
+        :param delay: How long to try (in seconds).
+        :param interval: How often to try (in miliseconds).
+
+        :return: True if the condition is verified, False if the delay expires.
+        """
+        if delay is None:
+            delay = self._hk_delay
+        if interval is None:
+            interval = self._hk_interval
+
+        interval = interval / 1000 # Convert to seconds
+        nb_polls = int(delay * (1 / interval))
+
+        for i in range(0, nb_polls):
+            if verified():
+                return True
+            time.sleep(interval)
+
+        return False
+
+    def _wait_for_pidfile_to_be_created(self):
+        """Reliably check that the server is started.
+
+        apache2 returns early when running the '-k start' command. This doesn't
+        guarantee that the server is started. We need to poll the pidfile
+        creation for that.
+        """
+        return self._hk_poll(self.is_running)
 
     def _wait_for_pid_death(self, pid):
-        # After apache2 is stopped, ensure the corresponding processes are
-        # really dead.
-        started = False
-        delay = 0.001 # 10 ms
-        # total delay to wait in seconds, if you experienced a longer start
-        # time, well, increase.
-        total_delay = 2
-        for i in range(0, int(total_delay * (1  / delay))):
+        """Reliably check that the server has stopped.
+
+        apache2 returns early when running the '-k stop' command. This doesn't
+        guarantee that the server is stopped. We need to check that the main
+        process is dead.
+        """
+        def server_is_dead():
             try:
                 os.kill(pid, 0)
             except OSError, e:
                 if e.errno == errno.ESRCH:
-                    # Good, finally it's dead
-                    break
+                    # The process doesn't exist anymore
+                    return True
                 else:
                     raise
-            time.sleep(delay)
+            return False
+
+        return self._hk_poll(server_is_dead)
 
     def is_running(self):
-        return bool(self.pid is not None)
+        return bool(self.get_pid() is not None)
 
-    @property
-    def pid(self):
+    def get_pid(self):
         try:
             content = self._get_file_content(self.get_config_value('pid_file'))
         except errors.NoSuchFile:



More information about the bazaar-commits mailing list