Add optional internal gearman server.

Change-Id: Id4e6b77f5abba23f46236def28f54509c61fd247
Reviewed-on: https://review.openstack.org/30170
Reviewed-by: Monty Taylor <mordred@inaugust.com>
Reviewed-by: Clark Boylan <clark.boylan@gmail.com>
Reviewed-by: Jeremy Stanley <fungi@yuggoth.org>
Approved: Clark Boylan <clark.boylan@gmail.com>
Tested-by: Jenkins
diff --git a/zuul/cmd/server.py b/zuul/cmd/server.py
index 26e496c..e12e3f3 100755
--- a/zuul/cmd/server.py
+++ b/zuul/cmd/server.py
@@ -28,6 +28,8 @@
 import sys
 import signal
 
+import gear
+
 # No zuul imports here because they pull in paramiko which must not be
 # imported until after the daemonization.
 # https://github.com/paramiko/paramiko/issues/59
@@ -37,6 +39,7 @@
     def __init__(self):
         self.args = None
         self.config = None
+        self.gear_server_pid = None
 
     def parse_arguments(self):
         parser = argparse.ArgumentParser(description='Project gating system.')
@@ -65,9 +68,9 @@
                 return
         raise Exception("Unable to locate config file in %s" % locations)
 
-    def setup_logging(self):
-        if self.config.has_option('zuul', 'log_config'):
-            fp = os.path.expanduser(self.config.get('zuul', 'log_config'))
+    def setup_logging(self, section, parameter):
+        if self.config.has_option(section, parameter):
+            fp = os.path.expanduser(self.config.get(section, parameter))
             if not os.path.exists(fp):
                 raise Exception("Unable to read logging config file at %s" %
                                 fp)
@@ -84,8 +87,13 @@
 
     def exit_handler(self, signum, frame):
         signal.signal(signal.SIGUSR1, signal.SIG_IGN)
+        self.stop_gear_server()
         self.sched.exit()
 
+    def term_handler(self, signum, frame):
+        self.stop_gear_server()
+        os._exit(0)
+
     def test_config(self):
         # See comment at top of file about zuul imports
         import zuul.scheduler
@@ -96,6 +104,26 @@
         self.sched = zuul.scheduler.Scheduler()
         self.sched.testConfig(self.config.get('zuul', 'layout_config'))
 
+    def start_gear_server(self):
+        pipe_read, pipe_write = os.pipe()
+        child_pid = os.fork()
+        if child_pid == 0:
+            os.close(pipe_write)
+            self.setup_logging('gearman_server', 'log_config')
+            gear.Server(4730)
+            # Keep running until the parent dies:
+            pipe_read = os.fdopen(pipe_read)
+            pipe_read.read()
+            os._exit(0)
+        else:
+            os.close(pipe_read)
+            self.gear_server_pid = child_pid
+            self.gear_pipe_write = pipe_write
+
+    def stop_gear_server(self):
+        if self.gear_server_pid:
+            os.kill(self.gear_server_pid, signal.SIGKILL)
+
     def main(self):
         # See comment at top of file about zuul imports
         import zuul.scheduler
@@ -103,6 +131,12 @@
         import zuul.trigger.gerrit
         import zuul.webapp
 
+        if (self.config.has_option('gearman_server', 'start') and
+            self.config.getboolean('gearman_server', 'start')):
+            self.start_gear_server()
+
+        self.setup_logging('zuul', 'log_config')
+
         self.sched = zuul.scheduler.Scheduler()
 
         gearman = zuul.launcher.gearman.Gearman(self.config, self.sched)
@@ -119,6 +153,7 @@
 
         signal.signal(signal.SIGHUP, self.reconfigure_handler)
         signal.signal(signal.SIGUSR1, self.exit_handler)
+        signal.signal(signal.SIGTERM, self.term_handler)
         while True:
             try:
                 signal.pause()
@@ -167,11 +202,9 @@
     pid = pid_file_module.TimeoutPIDLockFile(pid_fn, 10)
 
     if server.args.nodaemon:
-        server.setup_logging()
         server.main()
     else:
         with daemon.DaemonContext(pidfile=pid):
-            server.setup_logging()
             server.main()