diff foodtruck/pubutil.py @ 613:e2e955a3bb25

publish: Add publish command. * Add `shell` publisher. * Refactor admin panel's publishing backend to use that, along with the new PID file support.
author Ludovic Chabant <ludovic@chabant.com>
date Thu, 04 Feb 2016 08:05:03 -0800
parents 978d8bca9fb3
children cbb170d9c894
line wrap: on
line diff
--- a/foodtruck/pubutil.py	Thu Feb 04 08:03:52 2016 -0800
+++ b/foodtruck/pubutil.py	Thu Feb 04 08:05:03 2016 -0800
@@ -1,6 +1,7 @@
 import os
 import os.path
 import time
+import errno
 import signal
 import logging
 from .web import app
@@ -27,9 +28,37 @@
                   lambda *args: _shutdown_server_and_raise_sigint())
 
 
+def _pid_exists(pid):
+    try:
+        os.kill(pid, 0)
+    except OSError as ex:
+        if ex.errno == errno.ESRCH:
+            # No such process.
+            return False
+        elif ex.errno == errno.EPERM:
+            # No permission, so process exists.
+            return True
+        else:
+            raise
+    else:
+        return True
+
+
+def _read_pid_file(pid_file):
+    logger.debug("Reading PID file: %s" % pid_file)
+    try:
+        with open(pid_file, 'r') as fp:
+            pid_str = fp.read()
+
+        return int(pid_str.strip())
+    except Exception:
+        logger.error("Error reading PID file.")
+        raise
+
+
 class PublishLogReader(object):
     _pub_max_time = 10 * 60   # Don't bother about pubs older than 10mins.
-    _poll_interval = 1        # Check the PID file every 1 seconds.
+    _poll_interval = 1        # Check the process every 1 seconds.
     _ping_interval = 30       # Send a ping message every 30 seconds.
 
     def __init__(self, pid_path, log_path):
@@ -41,7 +70,8 @@
 
     def run(self):
         logger.debug("Opening publish log...")
-
+        pid = None
+        is_running = False
         try:
             while not server_shutdown:
                 # PING!
@@ -51,19 +81,38 @@
                     self._last_ping_time = time.time()
                     yield bytes("event: ping\ndata: 1\n\n", 'utf8')
 
-                # Check pid file.
-                prev_mtime = self._pub_pid_mtime
+                # Check if the PID file has changed.
                 try:
-                    self._pub_pid_mtime = os.path.getmtime(self.pid_path)
-                    if time.time() - self._pub_pid_mtime > \
-                            self._pub_max_time:
-                        self._pub_pid_mtime = 0
+                    new_mtime = os.path.getmtime(self.pid_path)
                 except OSError:
-                    self._pub_pid_mtime = 0
+                    new_mtime = 0
+
+                if (new_mtime > 0 and
+                        time.time() - new_mtime > self._pub_max_time):
+                    new_mtime = 0
+
+                # Re-read the PID file.
+                prev_mtime = self._pub_pid_mtime
+                if new_mtime > 0 and new_mtime != prev_mtime:
+                    self._pub_pid_mtime = new_mtime
+                    pid = _read_pid_file(self.pid_path)
+                    if pid:
+                        logger.debug("Monitoring new process, PID: %d" % pid)
+
+                was_running = is_running
+                if pid:
+                    is_running = _pid_exists(pid)
+                    logger.debug(
+                            "Process %d is %s" %
+                            (pid, 'running' if is_running else 'not running'))
+                    if not is_running:
+                        pid = None
+                else:
+                    is_running = False
 
                 # Send data.
                 new_data = None
-                if self._pub_pid_mtime > 0 or prev_mtime > 0:
+                if is_running or was_running:
                     if self._last_seek < 0:
                         outstr = 'event: message\ndata: Publish started.\n\n'
                         yield bytes(outstr, 'utf8')
@@ -76,11 +125,11 @@
                             self._last_seek = fp.tell()
                     except OSError:
                         pass
-                if self._pub_pid_mtime == 0:
+                if not is_running:
                     self._last_seek = 0
 
                 if new_data:
-                    logger.debug("SSE: %s" % outstr)
+                    logger.debug("SSE: %s" % new_data)
                     for line in new_data.split('\n'):
                         outstr = 'event: message\ndata: %s\n\n' % line
                         yield bytes(outstr, 'utf8')