diff --git a/tests/base.py b/tests/base.py index 7214ebef44..542ba08a11 100644 --- a/tests/base.py +++ b/tests/base.py @@ -2505,7 +2505,8 @@ class WebProxyFixture(fixtures.Fixture): class ZuulWebFixture(fixtures.Fixture): - def __init__(self, gearman_server_port, config, info=None, zk_hosts=None): + def __init__(self, gearman_server_port, config, test_root, info=None, + zk_hosts=None): super(ZuulWebFixture, self).__init__() self.gearman_server_port = gearman_server_port self.connections = zuul.lib.connections.ConnectionRegistry() @@ -2519,6 +2520,7 @@ class ZuulWebFixture(fixtures.Fixture): else: self.info = info self.zk_hosts = zk_hosts + self.test_root = test_root def _setUp(self): # Start the web server @@ -2527,7 +2529,8 @@ class ZuulWebFixture(fixtures.Fixture): gear_server='127.0.0.1', gear_port=self.gearman_server_port, info=self.info, connections=self.connections, - zk_hosts=self.zk_hosts) + zk_hosts=self.zk_hosts, + command_socket=os.path.join(self.test_root, 'web.socket')) self.web.start() self.addCleanup(self.stop) diff --git a/tests/unit/test_github_driver.py b/tests/unit/test_github_driver.py index 803d908231..0a94db83cb 100644 --- a/tests/unit/test_github_driver.py +++ b/tests/unit/test_github_driver.py @@ -1258,7 +1258,7 @@ class TestGithubWebhook(ZuulTestCase): # Start the web server self.web = self.useFixture( ZuulWebFixture(self.gearman_server.port, - self.config)) + self.config, self.test_root)) host = '127.0.0.1' # Wait until web server is started diff --git a/tests/unit/test_pagure_driver.py b/tests/unit/test_pagure_driver.py index ffc3b6f8e6..30d1a07074 100644 --- a/tests/unit/test_pagure_driver.py +++ b/tests/unit/test_pagure_driver.py @@ -839,7 +839,7 @@ class TestPagureWebhook(ZuulTestCase): # Start the web server self.web = self.useFixture( ZuulWebFixture(self.gearman_server.port, - self.config)) + self.config, self.test_root)) host = '127.0.0.1' # Wait until web server is started diff --git a/tests/unit/test_streaming.py b/tests/unit/test_streaming.py index 2b228be966..d203c626cd 100644 --- a/tests/unit/test_streaming.py +++ b/tests/unit/test_streaming.py @@ -251,7 +251,7 @@ class TestStreaming(tests.base.AnsibleZuulTestCase): # Start the web server web = self.useFixture( ZuulWebFixture(self.gearman_server.port, - self.config)) + self.config, self.test_root)) # Start the finger streamer daemon streamer = zuul.lib.log_streamer.LogStreamer( @@ -327,7 +327,7 @@ class TestStreaming(tests.base.AnsibleZuulTestCase): # Start the web server web = self.useFixture( ZuulWebFixture(self.gearman_server.port, - self.config)) + self.config, self.test_root)) # Start the finger streamer daemon streamer = zuul.lib.log_streamer.LogStreamer( @@ -400,7 +400,7 @@ class TestStreaming(tests.base.AnsibleZuulTestCase): # Start the web server web = self.useFixture( ZuulWebFixture(self.gearman_server.port, - self.config)) + self.config, self.test_root)) # Start the finger streamer daemon streamer = zuul.lib.log_streamer.LogStreamer( diff --git a/tests/unit/test_web.py b/tests/unit/test_web.py index 500ef262a2..a0907de3cb 100644 --- a/tests/unit/test_web.py +++ b/tests/unit/test_web.py @@ -52,6 +52,7 @@ class BaseTestWeb(ZuulTestCase): ZuulWebFixture( self.gearman_server.port, self.config, + self.test_root, info=zuul.model.WebInfo.fromConfig(self.zuul_ini_config), zk_hosts=self.zk_config)) diff --git a/tests/unit/test_web_urls.py b/tests/unit/test_web_urls.py index 771b7fcb9b..7ce7f11b4e 100644 --- a/tests/unit/test_web_urls.py +++ b/tests/unit/test_web_urls.py @@ -28,7 +28,7 @@ class TestWebURLs(ZuulTestCase): super(TestWebURLs, self).setUp() self.web = self.useFixture( ZuulWebFixture(self.gearman_server.port, - self.config)) + self.config, self.test_root)) def _get(self, port, uri): url = "http://localhost:{}{}".format(port, uri) diff --git a/zuul/cmd/web.py b/zuul/cmd/web.py index 061bd19c63..9b2e270f32 100755 --- a/zuul/cmd/web.py +++ b/zuul/cmd/web.py @@ -30,6 +30,18 @@ class WebServer(zuul.cmd.ZuulDaemonApp): app_name = 'web' app_description = 'A standalone Zuul web server.' + def createParser(self): + parser = super().createParser() + parser.add_argument('command', + choices=zuul.web.COMMANDS, + nargs='?') + return parser + + def parseArguments(self, args=None): + super().parseArguments() + if self.args.command: + self.args.nodaemon = True + def exit_handler(self, signum, frame): self.web.stop() @@ -55,6 +67,10 @@ class WebServer(zuul.cmd.ZuulDaemonApp): params['ssl_cert'] = get_default(self.config, 'gearman', 'ssl_cert') params['ssl_ca'] = get_default(self.config, 'gearman', 'ssl_ca') + params['command_socket'] = get_default( + self.config, 'web', 'command_socket', + '/var/lib/zuul/web.socket') + params['connections'] = self.connections # Validate config here before we spin up the ZuulWeb object for conn_name, connection in self.connections.connections.items(): @@ -91,6 +107,10 @@ class WebServer(zuul.cmd.ZuulDaemonApp): self.log.info("Zuul Web Server stopped") def run(self): + if self.args.command in zuul.web.COMMANDS: + self.send_command(self.args.command) + sys.exit(0) + self.setup_logging('web', 'log_config') self.log = logging.getLogger("zuul.WebServer") diff --git a/zuul/web/__init__.py b/zuul/web/__init__.py index 5c86a8901b..7e46a79ed8 100755 --- a/zuul/web/__init__.py +++ b/zuul/web/__init__.py @@ -34,10 +34,13 @@ import re2 import zuul.model import zuul.rpcclient import zuul.zk +from zuul.lib import commandsocket STATIC_DIR = os.path.join(os.path.dirname(__file__), 'static') cherrypy.tools.websocket = WebSocketTool() +COMMANDS = ['stop'] + class SaveParamsTool(cherrypy.Tool): """ @@ -726,7 +729,8 @@ class ZuulWeb(object): connections=None, info=None, static_path=None, - zk_hosts=None): + zk_hosts=None, + command_socket=None): self.start_time = time.time() self.listen_address = listen_address self.listen_port = listen_port @@ -745,6 +749,11 @@ class ZuulWeb(object): self.connections = connections self.stream_manager = StreamManager() + self.command_socket = commandsocket.CommandSocket(command_socket) + self.command_map = { + 'stop': self.stop, + } + route_map = cherrypy.dispatch.RoutesDispatcher() api = ZuulWebAPI(self) route_map.connect('api', '/api', @@ -838,6 +847,14 @@ class ZuulWeb(object): self.wsplugin.subscribe() cherrypy.engine.start() + self.log.debug("Starting command processor") + self._command_running = True + self.command_socket.start() + self.command_thread = threading.Thread(target=self.runCommand, + name='command') + self.command_thread.daemon = True + self.command_thread.start() + def stop(self): self.log.debug("ZuulWeb stopping") self.rpc.shutdown() @@ -849,6 +866,18 @@ class ZuulWeb(object): self.wsplugin.unsubscribe() self.stream_manager.stop() self.zk.disconnect() + self._command_running = False + self.command_socket.stop() + self.command_thread.join() + + def runCommand(self): + while self._command_running: + try: + command = self.command_socket.get().decode('utf8') + if command != '_stop': + self.command_map[command]() + except Exception: + self.log.exception("Exception while processing command") if __name__ == "__main__":