blob: 4f77610c4420b21993a7826f3a97bcd6a59298ca [file] [log] [blame]
James E. Blair1ce97ad2012-05-29 15:38:19 -07001# Copyright 2012 Hewlett-Packard Development Company, L.P.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
James E. Blairee743612012-05-29 14:49:32 -070015import argparse
16import ConfigParser
James E. Blaird3bbe002012-05-31 13:06:31 -070017import daemon
Antoine Musso80925f52012-09-22 21:37:45 +020018
19try:
20 import daemon.pidlockfile as pid_file_module
Monty Taylor7df9e7f2012-11-25 12:12:28 -080021 pid_file_module # workaround for pyflakes issue #13
Antoine Musso80925f52012-09-22 21:37:45 +020022except:
23 # as of python-daemon 1.6 it doesn't bundle pidlockfile anymore
24 # instead it depends on lockfile-0.9.1 which uses pidfile.
25 import daemon.pidfile as pid_file_module
26
James E. Blaire9d45c32012-05-31 09:56:45 -070027import logging.config
James E. Blairee743612012-05-29 14:49:32 -070028import os
James E. Blaire9d45c32012-05-31 09:56:45 -070029import signal
James E. Blairee743612012-05-29 14:49:32 -070030
James E. Blair32663402012-06-01 10:04:18 -070031# No zuul imports here because they pull in paramiko which must not be
32# imported until after the daemonization.
33# https://github.com/paramiko/paramiko/issues/59
James E. Blairee743612012-05-29 14:49:32 -070034
James E. Blairee743612012-05-29 14:49:32 -070035
James E. Blaire9d45c32012-05-31 09:56:45 -070036class Server(object):
37 def __init__(self):
38 self.args = None
39 self.config = None
James E. Blair1e8dd892012-05-30 09:15:05 -070040
James E. Blaire9d45c32012-05-31 09:56:45 -070041 def parse_arguments(self):
42 parser = argparse.ArgumentParser(description='Project gating system.')
43 parser.add_argument('-c', dest='config',
44 help='specify the config file')
James E. Blaird3bbe002012-05-31 13:06:31 -070045 parser.add_argument('-d', dest='nodaemon', action='store_true',
46 help='do not run as a daemon')
James E. Blaire9d45c32012-05-31 09:56:45 -070047 self.args = parser.parse_args()
James E. Blairee743612012-05-29 14:49:32 -070048
James E. Blaire9d45c32012-05-31 09:56:45 -070049 def read_config(self):
50 self.config = ConfigParser.ConfigParser()
51 if self.args.config:
52 locations = [self.args.config]
53 else:
54 locations = ['/etc/zuul/zuul.conf',
55 '~/zuul.conf']
56 for fp in locations:
57 if os.path.exists(os.path.expanduser(fp)):
58 self.config.read(os.path.expanduser(fp))
59 return
60 raise Exception("Unable to locate config file in %s" % locations)
James E. Blair1e8dd892012-05-30 09:15:05 -070061
James E. Blaire9d45c32012-05-31 09:56:45 -070062 def setup_logging(self):
63 if self.config.has_option('zuul', 'log_config'):
64 fp = os.path.expanduser(self.config.get('zuul', 'log_config'))
65 if not os.path.exists(fp):
66 raise Exception("Unable to read logging config file at %s" %
67 fp)
68 logging.config.fileConfig(fp)
69 else:
70 logging.basicConfig(level=logging.DEBUG)
James E. Blairee743612012-05-29 14:49:32 -070071
James E. Blaire9d45c32012-05-31 09:56:45 -070072 def reconfigure_handler(self, signum, frame):
73 signal.signal(signal.SIGHUP, signal.SIG_IGN)
74 self.read_config()
75 self.setup_logging()
76 self.sched.reconfigure(self.config)
77 signal.signal(signal.SIGHUP, self.reconfigure_handler)
James E. Blair1e8dd892012-05-30 09:15:05 -070078
James E. Blair5d5bc2b2012-07-06 10:24:01 -070079 def exit_handler(self, signum, frame):
80 signal.signal(signal.SIGUSR1, signal.SIG_IGN)
81 self.sched.exit()
82
James E. Blaire9d45c32012-05-31 09:56:45 -070083 def main(self):
James E. Blair32663402012-06-01 10:04:18 -070084 # See comment at top of file about zuul imports
85 import zuul.scheduler
86 import zuul.launcher.jenkins
87 import zuul.trigger.gerrit
88
James E. Blaire9d45c32012-05-31 09:56:45 -070089 self.sched = zuul.scheduler.Scheduler()
James E. Blairee743612012-05-29 14:49:32 -070090
James E. Blaire9d45c32012-05-31 09:56:45 -070091 jenkins = zuul.launcher.jenkins.Jenkins(self.config, self.sched)
92 gerrit = zuul.trigger.gerrit.Gerrit(self.config, self.sched)
James E. Blair1e8dd892012-05-30 09:15:05 -070093
James E. Blaire9d45c32012-05-31 09:56:45 -070094 self.sched.setLauncher(jenkins)
95 self.sched.setTrigger(gerrit)
James E. Blairee743612012-05-29 14:49:32 -070096
James E. Blaire9d45c32012-05-31 09:56:45 -070097 self.sched.start()
98 self.sched.reconfigure(self.config)
James E. Blair5d5bc2b2012-07-06 10:24:01 -070099 self.sched.resume()
James E. Blaire9d45c32012-05-31 09:56:45 -0700100 signal.signal(signal.SIGHUP, self.reconfigure_handler)
James E. Blair5d5bc2b2012-07-06 10:24:01 -0700101 signal.signal(signal.SIGUSR1, self.exit_handler)
James E. Blaire9d45c32012-05-31 09:56:45 -0700102 while True:
Antoine Musso6ad5d5b2012-09-24 13:25:28 +0200103 try:
104 signal.pause()
105 except KeyboardInterrupt:
106 print "Ctrl + C: asking scheduler to exit nicely...\n"
Monty Taylor7df9e7f2012-11-25 12:12:28 -0800107 self.exit_handler(signal.SIGINT, None)
James E. Blairee743612012-05-29 14:49:32 -0700108
Monty Taylor7df9e7f2012-11-25 12:12:28 -0800109
110def 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()