zuul/zuul/cmd/executor.py
Monty Taylor 0dbe15993a Support finger ports in finger URL
We support configuring an alternate port for finger. Make sure it makes
its way into the URL we provide if it's provided.

Change-Id: I5f511e15c031755d5c90627830ed29b80c6285fd
2017-06-29 16:20:28 -04:00

181 lines
6.0 KiB
Python
Executable File

#!/usr/bin/env python
# Copyright 2012 Hewlett-Packard Development Company, L.P.
# Copyright 2013-2014 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import argparse
import daemon
import extras
# as of python-daemon 1.6 it doesn't bundle pidlockfile anymore
# instead it depends on lockfile-0.9.1 which uses pidfile.
pid_file_module = extras.try_imports(['daemon.pidlockfile', 'daemon.pidfile'])
import logging
import os
import pwd
import socket
import sys
import signal
import tempfile
import zuul.cmd
import zuul.executor.server
from zuul.lib.config import get_default
# No zuul imports that pull in paramiko here; it must not be
# imported until after the daemonization.
# https://github.com/paramiko/paramiko/issues/59
# Similar situation with gear and statsd.
class Executor(zuul.cmd.ZuulApp):
def parse_arguments(self):
parser = argparse.ArgumentParser(description='Zuul executor.')
parser.add_argument('-c', dest='config',
help='specify the config file')
parser.add_argument('-d', dest='nodaemon', action='store_true',
help='do not run as a daemon')
parser.add_argument('--version', dest='version', action='version',
version=self._get_version(),
help='show zuul version')
parser.add_argument('--keep-jobdir', dest='keep_jobdir',
action='store_true',
help='keep local jobdirs after run completes')
parser.add_argument('command',
choices=zuul.executor.server.COMMANDS,
nargs='?')
self.args = parser.parse_args()
def send_command(self, cmd):
state_dir = get_default(self.config, 'zuul', 'state_dir',
'/var/lib/zuul', expand_user=True)
path = os.path.join(state_dir, 'executor.socket')
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.connect(path)
cmd = '%s\n' % cmd
s.sendall(cmd.encode('utf8'))
def exit_handler(self):
self.executor.stop()
self.executor.join()
def start_log_streamer(self):
pipe_read, pipe_write = os.pipe()
child_pid = os.fork()
if child_pid == 0:
os.close(pipe_write)
import zuul.lib.log_streamer
self.log.info("Starting log streamer")
streamer = zuul.lib.log_streamer.LogStreamer(
self.user, '0.0.0.0', self.finger_port, self.jobroot_dir)
# Keep running until the parent dies:
pipe_read = os.fdopen(pipe_read)
pipe_read.read()
self.log.info("Stopping log streamer")
streamer.stop()
os._exit(0)
else:
os.close(pipe_read)
self.log_streamer_pid = child_pid
def change_privs(self):
'''
Drop our privileges to the zuul user.
'''
if os.getuid() != 0:
return
pw = pwd.getpwnam(self.user)
os.setgroups([])
os.setgid(pw.pw_gid)
os.setuid(pw.pw_uid)
os.umask(0o022)
def main(self, daemon=True):
# See comment at top of file about zuul imports
self.user = get_default(self.config, 'executor', 'user', 'zuul')
if self.config.has_option('zuul', 'jobroot_dir'):
self.jobroot_dir = os.path.expanduser(
self.config.get('zuul', 'jobroot_dir'))
if not os.path.isdir(self.jobroot_dir):
print("Invalid jobroot_dir: {jobroot_dir}".format(
jobroot_dir=self.jobroot_dir))
sys.exit(1)
else:
self.jobroot_dir = tempfile.gettempdir()
self.setup_logging('executor', 'log_config')
self.log = logging.getLogger("zuul.Executor")
self.finger_port = int(
get_default(self.config, 'executor', 'finger_port',
zuul.executor.server.DEFAULT_FINGER_PORT)
)
self.start_log_streamer()
self.change_privs()
ExecutorServer = zuul.executor.server.ExecutorServer
self.executor = ExecutorServer(self.config, self.connections,
jobdir_root=self.jobroot_dir,
keep_jobdir=self.args.keep_jobdir,
log_streaming_port=self.finger_port)
self.executor.start()
signal.signal(signal.SIGUSR2, zuul.cmd.stack_dump_handler)
if daemon:
self.executor.join()
else:
while True:
try:
signal.pause()
except KeyboardInterrupt:
print("Ctrl + C: asking executor to exit nicely...\n")
self.exit_handler()
sys.exit(0)
def main():
server = Executor()
server.parse_arguments()
server.read_config()
if server.args.command in zuul.executor.server.COMMANDS:
server.send_command(server.args.command)
sys.exit(0)
server.configure_connections(source_only=True)
pid_fn = get_default(server.config, 'executor', 'pidfile',
'/var/run/zuul-executor/zuul-executor.pid',
expand_user=True)
pid = pid_file_module.TimeoutPIDLockFile(pid_fn, 10)
if server.args.nodaemon:
server.main(False)
else:
with daemon.DaemonContext(pidfile=pid):
server.main(True)
if __name__ == "__main__":
sys.path.insert(0, '.')
main()