diff --git a/examples/boot_fail.py b/examples/boot_fail.py new file mode 100644 index 00000000..0a2455ed --- /dev/null +++ b/examples/boot_fail.py @@ -0,0 +1,5 @@ + +raise RuntimeError("Bad app!") + +def app(environ, start_response): + assert 1 == 2, "Shouldn't get here." diff --git a/gunicorn/arbiter.py b/gunicorn/arbiter.py index ff8e325a..fd10b1f8 100644 --- a/gunicorn/arbiter.py +++ b/gunicorn/arbiter.py @@ -14,6 +14,7 @@ import sys import time import traceback +from gunicorn.errors import HaltServer from gunicorn.pidfile import Pidfile from gunicorn.sock import create_socket from gunicorn import util @@ -168,6 +169,8 @@ class Arbiter(object): self.halt() except KeyboardInterrupt: self.halt() + except HaltServer, inst: + self.halt(reason=inst.reason, exit_status=inst.exit_status) except SystemExit: raise except Exception: @@ -259,13 +262,15 @@ class Arbiter(object): if e.errno not in [errno.EAGAIN, errno.EINTR]: raise - def halt(self): + def halt(self, reason=None, exit_status=0): """ halt arbiter """ self.stop() self.log.info("Shutting down: %s" % self.master_name) + if reason is not None: + self.log.info("Reason: %s" % reason) if self.pidfile is not None: self.pidfile.unlink() - sys.exit(0) + sys.exit(exit_status) def sleep(self): """\ @@ -386,7 +391,8 @@ class Arbiter(object): # to avoid infinite start/stop cycles. exitcode = status >> 8 if exitcode == self.WORKER_BOOT_ERROR: - raise StopIteration + reason = "Worker failed to boot." + raise HaltServer(reason, self.WORKER_BOOT_ERROR) worker = self.WORKERS.pop(wpid, None) if not worker: continue diff --git a/gunicorn/errors.py b/gunicorn/errors.py new file mode 100644 index 00000000..482b8293 --- /dev/null +++ b/gunicorn/errors.py @@ -0,0 +1,8 @@ + +class HaltServer(Exception): + def __init__(self, reason, exit_status=1): + self.reason = reason + self.exit_status = exit_status + + def __str__(self): + return "" % (self.reason, self.exit_status)