Mercurial > piecrust2
comparison 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 |
comparison
equal
deleted
inserted
replaced
612:2edaefcb82cd | 613:e2e955a3bb25 |
---|---|
1 import os | 1 import os |
2 import os.path | 2 import os.path |
3 import time | 3 import time |
4 import errno | |
4 import signal | 5 import signal |
5 import logging | 6 import logging |
6 from .web import app | 7 from .web import app |
7 | 8 |
8 | 9 |
25 # Make sure CTRL+C works correctly. | 26 # Make sure CTRL+C works correctly. |
26 signal.signal(signal.SIGINT, | 27 signal.signal(signal.SIGINT, |
27 lambda *args: _shutdown_server_and_raise_sigint()) | 28 lambda *args: _shutdown_server_and_raise_sigint()) |
28 | 29 |
29 | 30 |
31 def _pid_exists(pid): | |
32 try: | |
33 os.kill(pid, 0) | |
34 except OSError as ex: | |
35 if ex.errno == errno.ESRCH: | |
36 # No such process. | |
37 return False | |
38 elif ex.errno == errno.EPERM: | |
39 # No permission, so process exists. | |
40 return True | |
41 else: | |
42 raise | |
43 else: | |
44 return True | |
45 | |
46 | |
47 def _read_pid_file(pid_file): | |
48 logger.debug("Reading PID file: %s" % pid_file) | |
49 try: | |
50 with open(pid_file, 'r') as fp: | |
51 pid_str = fp.read() | |
52 | |
53 return int(pid_str.strip()) | |
54 except Exception: | |
55 logger.error("Error reading PID file.") | |
56 raise | |
57 | |
58 | |
30 class PublishLogReader(object): | 59 class PublishLogReader(object): |
31 _pub_max_time = 10 * 60 # Don't bother about pubs older than 10mins. | 60 _pub_max_time = 10 * 60 # Don't bother about pubs older than 10mins. |
32 _poll_interval = 1 # Check the PID file every 1 seconds. | 61 _poll_interval = 1 # Check the process every 1 seconds. |
33 _ping_interval = 30 # Send a ping message every 30 seconds. | 62 _ping_interval = 30 # Send a ping message every 30 seconds. |
34 | 63 |
35 def __init__(self, pid_path, log_path): | 64 def __init__(self, pid_path, log_path): |
36 self.pid_path = pid_path | 65 self.pid_path = pid_path |
37 self.log_path = log_path | 66 self.log_path = log_path |
39 self._last_seek = -1 | 68 self._last_seek = -1 |
40 self._last_ping_time = 0 | 69 self._last_ping_time = 0 |
41 | 70 |
42 def run(self): | 71 def run(self): |
43 logger.debug("Opening publish log...") | 72 logger.debug("Opening publish log...") |
44 | 73 pid = None |
74 is_running = False | |
45 try: | 75 try: |
46 while not server_shutdown: | 76 while not server_shutdown: |
47 # PING! | 77 # PING! |
48 interval = time.time() - self._last_ping_time | 78 interval = time.time() - self._last_ping_time |
49 if interval > self._ping_interval: | 79 if interval > self._ping_interval: |
50 logger.debug("Sending ping...") | 80 logger.debug("Sending ping...") |
51 self._last_ping_time = time.time() | 81 self._last_ping_time = time.time() |
52 yield bytes("event: ping\ndata: 1\n\n", 'utf8') | 82 yield bytes("event: ping\ndata: 1\n\n", 'utf8') |
53 | 83 |
54 # Check pid file. | 84 # Check if the PID file has changed. |
85 try: | |
86 new_mtime = os.path.getmtime(self.pid_path) | |
87 except OSError: | |
88 new_mtime = 0 | |
89 | |
90 if (new_mtime > 0 and | |
91 time.time() - new_mtime > self._pub_max_time): | |
92 new_mtime = 0 | |
93 | |
94 # Re-read the PID file. | |
55 prev_mtime = self._pub_pid_mtime | 95 prev_mtime = self._pub_pid_mtime |
56 try: | 96 if new_mtime > 0 and new_mtime != prev_mtime: |
57 self._pub_pid_mtime = os.path.getmtime(self.pid_path) | 97 self._pub_pid_mtime = new_mtime |
58 if time.time() - self._pub_pid_mtime > \ | 98 pid = _read_pid_file(self.pid_path) |
59 self._pub_max_time: | 99 if pid: |
60 self._pub_pid_mtime = 0 | 100 logger.debug("Monitoring new process, PID: %d" % pid) |
61 except OSError: | 101 |
62 self._pub_pid_mtime = 0 | 102 was_running = is_running |
103 if pid: | |
104 is_running = _pid_exists(pid) | |
105 logger.debug( | |
106 "Process %d is %s" % | |
107 (pid, 'running' if is_running else 'not running')) | |
108 if not is_running: | |
109 pid = None | |
110 else: | |
111 is_running = False | |
63 | 112 |
64 # Send data. | 113 # Send data. |
65 new_data = None | 114 new_data = None |
66 if self._pub_pid_mtime > 0 or prev_mtime > 0: | 115 if is_running or was_running: |
67 if self._last_seek < 0: | 116 if self._last_seek < 0: |
68 outstr = 'event: message\ndata: Publish started.\n\n' | 117 outstr = 'event: message\ndata: Publish started.\n\n' |
69 yield bytes(outstr, 'utf8') | 118 yield bytes(outstr, 'utf8') |
70 self._last_seek = 0 | 119 self._last_seek = 0 |
71 | 120 |
74 fp.seek(self._last_seek) | 123 fp.seek(self._last_seek) |
75 new_data = fp.read() | 124 new_data = fp.read() |
76 self._last_seek = fp.tell() | 125 self._last_seek = fp.tell() |
77 except OSError: | 126 except OSError: |
78 pass | 127 pass |
79 if self._pub_pid_mtime == 0: | 128 if not is_running: |
80 self._last_seek = 0 | 129 self._last_seek = 0 |
81 | 130 |
82 if new_data: | 131 if new_data: |
83 logger.debug("SSE: %s" % outstr) | 132 logger.debug("SSE: %s" % new_data) |
84 for line in new_data.split('\n'): | 133 for line in new_data.split('\n'): |
85 outstr = 'event: message\ndata: %s\n\n' % line | 134 outstr = 'event: message\ndata: %s\n\n' % line |
86 yield bytes(outstr, 'utf8') | 135 yield bytes(outstr, 'utf8') |
87 | 136 |
88 time.sleep(self._poll_interval) | 137 time.sleep(self._poll_interval) |