blob: d57c5640710f1186922995bcd1a538370f4579b3 [file] [log] [blame]
James E. Blair1ce97ad2012-05-29 15:38:19 -07001#!/usr/bin/env python
2# Copyright 2012 Hewlett-Packard Development Company, L.P.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
James E. Blairee743612012-05-29 14:49:32 -070016import argparse
17import ConfigParser
James E. Blaird3bbe002012-05-31 13:06:31 -070018import daemon
Antoine Musso80925f52012-09-22 21:37:45 +020019
20try:
21 import daemon.pidlockfile as pid_file_module
22 pid_file_module # workaround for pyflakes issue #13
23except:
24 # as of python-daemon 1.6 it doesn't bundle pidlockfile anymore
25 # instead it depends on lockfile-0.9.1 which uses pidfile.
26 import daemon.pidfile as pid_file_module
27
James E. Blaire9d45c32012-05-31 09:56:45 -070028import logging.config
James E. Blairee743612012-05-29 14:49:32 -070029import os
James E. Blaire9d45c32012-05-31 09:56:45 -070030import signal
James E. Blairee743612012-05-29 14:49:32 -070031
James E. Blair32663402012-06-01 10:04:18 -070032# No zuul imports here because they pull in paramiko which must not be
33# imported until after the daemonization.
34# https://github.com/paramiko/paramiko/issues/59
James E. Blairee743612012-05-29 14:49:32 -070035
James E. Blairee743612012-05-29 14:49:32 -070036
James E. Blaire9d45c32012-05-31 09:56:45 -070037class Server(object):
38 def __init__(self):
39 self.args = None
40 self.config = None
James E. Blair1e8dd892012-05-30 09:15:05 -070041
James E. Blaire9d45c32012-05-31 09:56:45 -070042 def parse_arguments(self):
43 parser = argparse.ArgumentParser(description='Project gating system.')
44 parser.add_argument('-c', dest='config',
45 help='specify the config file')
James E. Blaird3bbe002012-05-31 13:06:31 -070046 parser.add_argument('-d', dest='nodaemon', action='store_true',
47 help='do not run as a daemon')
James E. Blaire9d45c32012-05-31 09:56:45 -070048 self.args = parser.parse_args()
James E. Blairee743612012-05-29 14:49:32 -070049
James E. Blaire9d45c32012-05-31 09:56:45 -070050 def read_config(self):
51 self.config = ConfigParser.ConfigParser()
52 if self.args.config:
53 locations = [self.args.config]
54 else:
55 locations = ['/etc/zuul/zuul.conf',
56 '~/zuul.conf']
57 for fp in locations:
58 if os.path.exists(os.path.expanduser(fp)):
59 self.config.read(os.path.expanduser(fp))
60 return
61 raise Exception("Unable to locate config file in %s" % locations)
James E. Blair1e8dd892012-05-30 09:15:05 -070062
James E. Blaire9d45c32012-05-31 09:56:45 -070063 def setup_logging(self):
64 if self.config.has_option('zuul', 'log_config'):
65 fp = os.path.expanduser(self.config.get('zuul', 'log_config'))
66 if not os.path.exists(fp):
67 raise Exception("Unable to read logging config file at %s" %
68 fp)
69 logging.config.fileConfig(fp)
70 else:
71 logging.basicConfig(level=logging.DEBUG)
James E. Blairee743612012-05-29 14:49:32 -070072
James E. Blaire9d45c32012-05-31 09:56:45 -070073 def reconfigure_handler(self, signum, frame):
74 signal.signal(signal.SIGHUP, signal.SIG_IGN)
75 self.read_config()
76 self.setup_logging()
77 self.sched.reconfigure(self.config)
78 signal.signal(signal.SIGHUP, self.reconfigure_handler)
James E. Blair1e8dd892012-05-30 09:15:05 -070079
James E. Blair5d5bc2b2012-07-06 10:24:01 -070080 def exit_handler(self, signum, frame):
81 signal.signal(signal.SIGUSR1, signal.SIG_IGN)
82 self.sched.exit()
83
James E. Blaire9d45c32012-05-31 09:56:45 -070084 def main(self):
James E. Blair32663402012-06-01 10:04:18 -070085 # See comment at top of file about zuul imports
86 import zuul.scheduler
87 import zuul.launcher.jenkins
88 import zuul.trigger.gerrit
89
James E. Blaire9d45c32012-05-31 09:56:45 -070090 self.sched = zuul.scheduler.Scheduler()
James E. Blairee743612012-05-29 14:49:32 -070091
James E. Blaire9d45c32012-05-31 09:56:45 -070092 jenkins = zuul.launcher.jenkins.Jenkins(self.config, self.sched)
93 gerrit = zuul.trigger.gerrit.Gerrit(self.config, self.sched)
James E. Blair1e8dd892012-05-30 09:15:05 -070094
James E. Blaire9d45c32012-05-31 09:56:45 -070095 self.sched.setLauncher(jenkins)
96 self.sched.setTrigger(gerrit)
James E. Blairee743612012-05-29 14:49:32 -070097
James E. Blaire9d45c32012-05-31 09:56:45 -070098 self.sched.start()
99 self.sched.reconfigure(self.config)
James E. Blair5d5bc2b2012-07-06 10:24:01 -0700100 self.sched.resume()
James E. Blaire9d45c32012-05-31 09:56:45 -0700101 signal.signal(signal.SIGHUP, self.reconfigure_handler)
James E. Blair5d5bc2b2012-07-06 10:24:01 -0700102 signal.signal(signal.SIGUSR1, self.exit_handler)
James E. Blaire9d45c32012-05-31 09:56:45 -0700103 while True:
Antoine Musso6ad5d5b2012-09-24 13:25:28 +0200104 try:
105 signal.pause()
106 except KeyboardInterrupt:
107 print "Ctrl + C: asking scheduler to exit nicely...\n"
108 self.exit_handler( signal.SIGINT, None )
James E. Blairee743612012-05-29 14:49:32 -0700109
110if __name__ == '__main__':
James E. Blaire9d45c32012-05-31 09:56:45 -0700111 server = Server()
James E. Blaird3bbe002012-05-31 13:06:31 -0700112 server.parse_arguments()
113 server.read_config()
James E. Blaird3bbe002012-05-31 13:06:31 -0700114
James E. Blair5d5bc2b2012-07-06 10:24:01 -0700115 if server.config.has_option('zuul', 'state_dir'):
116 state_dir = os.path.expanduser(server.config.get('zuul', 'state_dir'))
117 else:
118 state_dir = '/var/lib/zuul'
119 test_fn = os.path.join(state_dir, 'test')
120 try:
121 f = open(test_fn, 'w')
122 f.close()
123 os.unlink(test_fn)
124 except:
125 print
126 print "Unable to write to state directory: %s" % state_dir
127 print
128 raise
129
James E. Blaird3bbe002012-05-31 13:06:31 -0700130 if server.config.has_option('zuul', 'pidfile'):
131 pid_fn = os.path.expanduser(server.config.get('zuul', 'pidfile'))
132 else:
133 pid_fn = '/var/run/zuul/zuul.pid'
Antoine Musso80925f52012-09-22 21:37:45 +0200134 pid = pid_file_module.TimeoutPIDLockFile(pid_fn, 10)
James E. Blaird3bbe002012-05-31 13:06:31 -0700135
136 if server.args.nodaemon:
James E. Blair4c58c8f2012-05-31 13:38:09 -0700137 server.setup_logging()
James E. Blaird3bbe002012-05-31 13:06:31 -0700138 server.main()
139 else:
140 with daemon.DaemonContext(pidfile=pid):
James E. Blair4c58c8f2012-05-31 13:38:09 -0700141 server.setup_logging()
James E. Blaird3bbe002012-05-31 13:06:31 -0700142 server.main()