From eb60c1e08ed5e31ae1444d41fff4eaf5893c193f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20T=C3=B3th?= Date: Fri, 12 Jul 2019 23:19:22 +0200 Subject: [PATCH 1/5] Remove pipe auxlib --- solvable/src/pipe_io_auxlib.py | 235 --------------------------------- 1 file changed, 235 deletions(-) delete mode 100644 solvable/src/pipe_io_auxlib.py diff --git a/solvable/src/pipe_io_auxlib.py b/solvable/src/pipe_io_auxlib.py deleted file mode 100644 index 933a594..0000000 --- a/solvable/src/pipe_io_auxlib.py +++ /dev/null @@ -1,235 +0,0 @@ -from json import dumps, loads - -from tfw.crypto import KeyManager, sign_message, verify_message -from tfw.builtins import PipeIOEventHandlerBase -from tfw.builtins.pipe_io_event_handler import DEFAULT_PERMISSIONS -from tfw.networking.scope import Scope - - -class SignMessagePipeIOEventHandler(PipeIOEventHandlerBase): - """ - Signs a valid TFW message with HMAC. - Note that the running process needs root permissions in order to read - the authentication key. - When forwarding is true, it will send the signed message to the TFW - server before writing it into the output pipe. - """ - def __init__( - self, in_pipe_path, out_pipe_path, - permissions=DEFAULT_PERMISSIONS, - forwarding=True - ): - self.forwarding = forwarding - self.auth_key = KeyManager().auth_key - super().__init__(None, in_pipe_path, out_pipe_path, permissions) - - def handle_event(self, message): - pass - - def handle_pipe_event(self, message_bytes): - message = loads(message_bytes) - sign_message(self.auth_key, message) - if self.forwarding: - self.server_connector.send_message(message) - self.pipe_io.send_message(dumps(message).encode()) - - -class VerifyMessagePipeIOEventHandler(PipeIOEventHandlerBase): - """ - Verifies a signed TFW message. - This pipe also needs root permissions. Send the serialized JSON object - to the pipe, then wait for its boolean response. - """ - - def __init__(self, in_pipe_path, out_pipe_path, permissions=DEFAULT_PERMISSIONS): - self.auth_key = KeyManager().auth_key - super().__init__(None, in_pipe_path, out_pipe_path, permissions) - - def handle_event(self, message): - pass - - def handle_pipe_event(self, message_bytes): - message = loads(message_bytes) - validity = verify_message(self.auth_key, message) - self.pipe_io.send_message(str(validity).lower().encode()) - - -class BotPipeIOEventHandler(PipeIOEventHandlerBase): - """ - Sends bot messages to the frontend. - If you assign @originator, it will be the default message sender. - When you write a line to the pipe, it will be considered as a single - message and gets appended to the queue until an empty line is received, - which triggers forwarding the messages to the TFW server. - """ - - def __init__( - self, in_pipe_path, out_pipe_path, permissions=DEFAULT_PERMISSIONS, - originator='avataobot' - ): - self.queue = [] - self.originator = originator - super().__init__(None, in_pipe_path, out_pipe_path, permissions) - - def handle_event(self, message): - pass - - def handle_pipe_event(self, message_bytes): - if message_bytes == b"": - if self.queue: - self.server_connector.send_message({ - 'key': 'queueMessages', - 'data': { - 'messages': self.queue - } - }) - self.queue = [] - else: - self.queue.append({ - 'originator': self.originator, - 'message': message_bytes.decode().replace('\\n', '\n') - }) - - -class DeployPipeIOEventHandler(PipeIOEventHandlerBase): - """ - Manages deployment in the IDE. - When you receive "deploy", then you have to answer with a "true" or - "false" depending whether you are satisfied with the result or not. - The @process parameter is the name of the supervised service. - """ - - # pylint: disable=too-many-arguments - def __init__( - self, in_pipe_path, out_pipe_path, process, - permissions=DEFAULT_PERMISSIONS - ): - self.expected = False - self.process = process - - self.onsuccess = { - 'key': 'processmanager', - 'data': { - 'process_name': process, - 'command': 'restart' - } - } - self.onerror = { - 'key': 'processmanager', - 'data': { - 'process_name': process, - 'error': True - } - } - - super().__init__('processmanager', in_pipe_path, out_pipe_path, permissions) - - def handle_event(self, message): - if message == self.onsuccess: - self.expected = True - self.pipe_io.send_message(b'deploy') - - def handle_pipe_event(self, message_bytes): - if not self.expected: - raise ValueError( - f'{self.pipe_io.in_pipe}: There is nothing to deploy.' - ) - - self.expected = False - if message_bytes == b'true': - self.server_connector.send_message(self.onsuccess, scope=Scope.WEBSOCKET) - elif message_bytes == b'false': - self.server_connector.send_message(self.onerror, scope=Scope.WEBSOCKET) - else: - raise ValueError( - f'{self.pipe_io.in_pipe}: Expected "true" or "false".' - ) - - -class IdePipeIOEventHandler(PipeIOEventHandlerBase): - """ - Manipulates the content of the IDE. - You can observe a file, and when the user edits it, you will receive - the new contents where newlines are escaped as "\\n". - In order to overwrite the file, send an escaped text back to the pipe. - Since the pipe doesn't know if the file is selected initially in the IDE, - you have to provide this information by yourself with @selected, - but it will track it later on. - """ - - # pylint: disable=too-many-arguments - def __init__( - self, in_pipe_path, out_pipe_path, filename, - permissions=DEFAULT_PERMISSIONS, - selected=True - ): - self.selected = selected - self.filename = filename - super().__init__('ide', in_pipe_path, out_pipe_path, permissions) - - def handle_event(self, message): - data = message['data'] - - if data['command'] == 'select': - self.selected = data['filename'] == self.filename - elif data['command'] == 'write' and self.selected: - clean = data['content'].replace('\n', '\\n') - self.pipe_io.send_message(clean.encode()) - - def handle_pipe_event(self, message_bytes): - if not self.selected: - self.server_connector.send_message({ - 'key': 'mirror', - 'data': { - 'key': 'ide', - 'data': { - 'command': 'select', - 'filename': self.filename - } - } - }) - - self.server_connector.send_message({ - 'key': 'mirror', - 'data': { - 'key': 'ide', - 'data': { - 'command': 'write', - 'content': message_bytes.decode().replace('\\n', '\n') - } - } - }) - self.server_connector.send_message({ - 'key': 'mirror', - 'data': { - 'key': 'ide', - 'data': { - 'command': 'read' - } - } - }) - - -class FSMPipeIOEventHandler(PipeIOEventHandlerBase): - """ - Handles FSM steps. - When the FSM enters the next state, you will receive a line containing - its name. To trigger a state change, send the name of the transition to - the pipe. - """ - - def __init__(self, in_pipe_path, out_pipe_path, permissions=DEFAULT_PERMISSIONS): - super().__init__( - ['fsm', 'fsm_update'], - in_pipe_path, out_pipe_path, permissions - ) - - def handle_event(self, message): - if 'current_state' in message['data']: - self.pipe_io.send_message(message['data']['current_state'].encode()) - - def handle_pipe_event(self, message_bytes): - self.server_connector.send_message({ - 'key': '', - 'trigger': message_bytes.decode() - }) From d9e69a6327a68e59709fe691f3a78fdbf0c58ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20T=C3=B3th?= Date: Fri, 12 Jul 2019 23:26:57 +0200 Subject: [PATCH 2/5] Use new event handling model --- controller/opt/server.py | 7 ++-- solvable/src/custom_event_handlers.py | 34 +++++++++--------- solvable/src/event_handler_main.py | 50 +++++++++++++++++---------- solvable/src/pipe_io_main.py | 50 ++++----------------------- solvable/src/signal_handling.py | 11 ------ 5 files changed, 59 insertions(+), 93 deletions(-) delete mode 100644 solvable/src/signal_handling.py diff --git a/controller/opt/server.py b/controller/opt/server.py index c34f0a2..2fa5b44 100644 --- a/controller/opt/server.py +++ b/controller/opt/server.py @@ -5,11 +5,12 @@ from tornado.ioloop import IOLoop from tornado.web import RequestHandler, Application from tfw.builtins import FSMAwareEventHandler +from tfw.main import EventHandlerFactory class ControllerPostHandler(RequestHandler): - # pylint: disable=abstract-method - def initialize(self, **kwargs): # pylint: disable=arguments-differ + # pylint: disable=abstract-method,attribute-defined-outside-init,unused-argument + def initialize(self, **kwargs): self.controller = kwargs['controller'] def post(self, *args, **kwargs): @@ -26,6 +27,8 @@ class ControllerEventHandler(FSMAwareEventHandler): if __name__ == '__main__': controller = ControllerEventHandler('controller') + controller_eh = EventHandlerFactory().build(controller) + application = Application([( f'/{os.environ["SECRET"]}', ControllerPostHandler, diff --git a/solvable/src/custom_event_handlers.py b/solvable/src/custom_event_handlers.py index ca9749b..0789dba 100644 --- a/solvable/src/custom_event_handlers.py +++ b/solvable/src/custom_event_handlers.py @@ -2,25 +2,25 @@ import logging from ast import literal_eval from tfw.components import MessageSender -from tfw.builtins import TFWServerUplinkConnector -from tfw.builtins import EventHandler, FSMAwareEventHandler, TerminalCommandsEventHandler +from tfw.builtins import TerminalCommandsEventHandler +from tfw.main import TFWUplinkConnector + LOG = logging.getLogger(__name__) -class CenatorEventHandler(EventHandler): +class CenatorEventHandler: + keys = ['history.bash'] + # pylint: disable=no-self-use """ Logs commands executed in terminal to messages and invokes an additional callback function to handle special commands. !! Please remove from production code. !! """ - def __init__(self, key): - super().__init__(key) - - def handle_event(self, message): + def handle_event(self, message, server_connector): command = message['value'] LOG.debug('User executed command: "%s"', command) - MessageSender(self.server_connector).send('JOHN CENA', f'You\'ve executed "{command}"') + MessageSender(server_connector).send('JOHN CENA', f'You\'ve executed "{command}"') class TestCommandsEventHandler(TerminalCommandsEventHandler): @@ -40,7 +40,7 @@ class TestCommandsEventHandler(TerminalCommandsEventHandler): """ if not args: message_template = """'{"key": "", "data": {"command": ""}}'""" - TFWServerUplinkConnector().send_message({ + TFWUplinkConnector().send_message({ 'key': 'shell', 'data': { 'command': 'write', @@ -48,24 +48,22 @@ class TestCommandsEventHandler(TerminalCommandsEventHandler): } }) else: - TFWServerUplinkConnector().send_message(literal_eval(args[0])) + TFWUplinkConnector().send_message(literal_eval(args[0])) -class MessageFSMStepsEventHandler(FSMAwareEventHandler): +class MessageFSMStepsEventHandler: + # pylint: disable=no-self-use """ This example EventHandler is capable of detecting FSM state. !! Please remove from production code !! """ - def handle_event(self, message): - pass - - def handle_fsm_step(self, **kwargs): + def handle_event(self, message, server_connector): """ When the FSM steps this method is invoked. Receives a 'data' field from an fsm_update message as kwargs. """ - MessageSender(self.server_connector).send( + MessageSender(server_connector).send( 'FSM info', - f'FSM has stepped from state "{kwargs["last_event"]["from_state"]}" ' - f'to state "{kwargs["current_state"]}" in response to trigger "{kwargs["last_event"]["trigger"]}"' + f'FSM has stepped from state "{message["last_event"]["from_state"]}" ' + f'to state "{message["current_state"]}" in response to trigger "{message["last_event"]["trigger"]}"' ) diff --git a/solvable/src/event_handler_main.py b/solvable/src/event_handler_main.py index b31bd2d..fc8e588 100644 --- a/solvable/src/event_handler_main.py +++ b/solvable/src/event_handler_main.py @@ -5,73 +5,87 @@ from functools import partial from tornado.ioloop import IOLoop from tfw.fsm import YamlFSM +from tfw.event_handlers import FSMAwareEventHandler from tfw.builtins import IdeEventHandler, TerminalEventHandler, FrontendEventHandler from tfw.builtins import LogMonitoringEventHandler, ProcessManagingEventHandler from tfw.builtins import DirectorySnapshottingEventHandler, FSMManagingEventHandler -from tfw.config import TFWENV +from tfw.main import EventHandlerFactory, setup_signal_handlers from tfw.logging import Log, Logger, LogFormatter, VerboseLogFormatter +from tfw.config import TFWENV from tao.config import TAOENV from custom_event_handlers import MessageFSMStepsEventHandler from custom_event_handlers import CenatorEventHandler, TestCommandsEventHandler -from signal_handling import setup_signal_handlers + LOG = logging.getLogger(__name__) def main(): - # pylint: disable=unused-variable + # pylint: disable=unused-variable,too-many-locals Logger([ Log(stderr, LogFormatter(20)), Log(TFWENV.LOGFILE, VerboseLogFormatter()) ]).start() + eh_factory = EventHandlerFactory() # TFW component EventHandlers (builtins, required for their respective functionalities) fsm = FSMManagingEventHandler( # TFW FSM - key='fsm', fsm_type=partial( YamlFSM, 'test_fsm.yml', {} # jinja2 variables, use empty dict to enable jinja2 parsing without any variables ) ) + fsm_eh = eh_factory.build(fsm) + ide = IdeEventHandler( # Web IDE backend - key='ide', allowed_directories=[TFWENV.IDE_WD, TFWENV.WEBSERVICE_DIR], directory=TFWENV.IDE_WD, exclude=['*.pyc'] ) - terminal = TerminalEventHandler( # Web shell backend - key='shell' - ) - cenator = CenatorEventHandler('history.bash') # Reacts to terminal commands - commands = TestCommandsEventHandler( # Catches special commands - key='history.bash', - bashrc=f'/home/{TAOENV.USER}/.bashrc' - ) + ide_eh = eh_factory.build(ide) + + terminal = TerminalEventHandler() # Web shell backend + terminal_eh = eh_factory.build(terminal) + processmanager = ProcessManagingEventHandler( # Handles 'deploy' button clicks - key='processmanager', log_tail=2000 ) + processmanager_eh = eh_factory.build(processmanager) + logmonitor = LogMonitoringEventHandler( # Sends live logs of webservice process to frontend - key='logmonitor', process_name='webservice', log_tail=2000 ) + logmonitor_eh = eh_factory.build(logmonitor) + snapshot = DirectorySnapshottingEventHandler( # Manages filesystem snapshots of directories - key='snapshot', directories=[ TFWENV.IDE_WD, TFWENV.WEBSERVICE_DIR ] ) + snapshot_eh = eh_factory.build(snapshot) + frontend = FrontendEventHandler() # Proxies frontend API calls to frontend + frontend_eh = eh_factory.build(frontend) # Your custom event handlers - message_fsm_steps_eh = MessageFSMStepsEventHandler( - key='test' + cenator = CenatorEventHandler() + cenator_eh = eh_factory.build(cenator) + + message_fsm_steps = MessageFSMStepsEventHandler() + message_fsm_steps_eh = eh_factory.build( + message_fsm_steps, + event_handler_type=FSMAwareEventHandler ) + commands = TestCommandsEventHandler( # Catches special commands + bashrc=f'/home/{TAOENV.USER}/.bashrc' + ) + commands_eh = eh_factory.build(commands) + setup_signal_handlers() IOLoop.instance().start() diff --git a/solvable/src/pipe_io_main.py b/solvable/src/pipe_io_main.py index 27d886f..7a44dc3 100644 --- a/solvable/src/pipe_io_main.py +++ b/solvable/src/pipe_io_main.py @@ -6,14 +6,8 @@ from tornado.ioloop import IOLoop from tfw.builtins import PipeIOEventHandler from tfw.config import TFWENV from tfw.logging import Log, Logger, LogFormatter, VerboseLogFormatter +from tfw.main import EventHandlerFactory, setup_signal_handlers -from pipe_io_auxlib import ( - SignMessagePipeIOEventHandler, VerifyMessagePipeIOEventHandler, - BotPipeIOEventHandler, - DeployPipeIOEventHandler, IdePipeIOEventHandler, - FSMPipeIOEventHandler -) -from signal_handling import setup_signal_handlers LOG = logging.getLogger(__name__) @@ -25,46 +19,14 @@ def main(): Log(TFWENV.LOGFILE, VerboseLogFormatter()) ]).start() + eh_factory = EventHandlerFactory() + json_pipe = PipeIOEventHandler( - '', - '/tmp/tfw_json_send', - '/tmp/tfw_json_recv' + '/tmp/tfw_send', + '/tmp/tfw_recv' ) + json_pipe_eh = eh_factory.build(json_pipe) - sign_pipe = SignMessagePipeIOEventHandler( - '/tmp/tfw_sign_send', - '/tmp/tfw_sign_recv', - forwarding=True - ) - - verify_pipe = VerifyMessagePipeIOEventHandler( - '/tmp/tfw_verify_send', - '/tmp/tfw_verify_recv' - ) - - bot_pipe = BotPipeIOEventHandler( - '/tmp/tfw_bot_send', - '/tmp/tfw_bot_recv', - permissions=0o666 - ) - - deploy_pipe = DeployPipeIOEventHandler( - '/tmp/tfw_deploy_send', - '/tmp/tfw_deploy_recv', - 'webservice' - ) - - ide_pipe = IdePipeIOEventHandler( - '/tmp/tfw_ide_send', - '/tmp/tfw_ide_recv', - 'user_ops.py', - selected=True - ) - - fsm_pipe = FSMPipeIOEventHandler( - '/tmp/tfw_fsm_send', - '/tmp/tfw_fsm_recv' - ) setup_signal_handlers() IOLoop.instance().start() diff --git a/solvable/src/signal_handling.py b/solvable/src/signal_handling.py deleted file mode 100644 index 21294d2..0000000 --- a/solvable/src/signal_handling.py +++ /dev/null @@ -1,11 +0,0 @@ -from signal import signal, SIGTERM, SIGINT - -from tfw.builtins import EventHandler - - -def setup_signal_handlers(): - def stop(*_): - EventHandler.stop_all_instances() - exit(0) - signal(SIGTERM, stop) - signal(SIGINT, stop) From 290c301d27897f314f466a3888bf0b25915a55a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20T=C3=B3th?= Date: Mon, 15 Jul 2019 13:43:34 +0200 Subject: [PATCH 3/5] Simplify *main setup --- solvable/src/custom_event_handlers.py | 30 ++---------- solvable/src/event_handler_main.py | 70 ++++++++++++--------------- solvable/src/pipe_io_main.py | 6 +-- 3 files changed, 37 insertions(+), 69 deletions(-) diff --git a/solvable/src/custom_event_handlers.py b/solvable/src/custom_event_handlers.py index 0789dba..8c2b82e 100644 --- a/solvable/src/custom_event_handlers.py +++ b/solvable/src/custom_event_handlers.py @@ -11,33 +11,16 @@ LOG = logging.getLogger(__name__) class CenatorEventHandler: keys = ['history.bash'] - # pylint: disable=no-self-use - """ - Logs commands executed in terminal to messages and invokes an - additional callback function to handle special commands. - !! Please remove from production code. !! - """ - def handle_event(self, message, server_connector): + + def handle_event(self, message, server_connector): # pylint: disable=no-self-use command = message['value'] LOG.debug('User executed command: "%s"', command) MessageSender(server_connector).send('JOHN CENA', f'You\'ve executed "{command}"') class TestCommandsEventHandler(TerminalCommandsEventHandler): - """ - Some example commands useful for debugging. - !! Please remove from production code !! and inherit your own - class from TerminalCommands if you need to define custom - commands in your challenge. - """ - # pylint: disable=unused-argument, attribute-defined-outside-init, no-self-use + # pylint: disable=unused-argument,attribute-defined-outside-init,no-self-use def command_sendmessage(self, *args): - """ - Insert TFW message template as first argument if executed without args. - - Evaluate first argumen as a dict and send it to the frontend. - This is useful for playing around with frontend APIs. - """ if not args: message_template = """'{"key": "", "data": {"command": ""}}'""" TFWUplinkConnector().send_message({ @@ -52,12 +35,7 @@ class TestCommandsEventHandler(TerminalCommandsEventHandler): class MessageFSMStepsEventHandler: - # pylint: disable=no-self-use - """ - This example EventHandler is capable of detecting FSM state. - !! Please remove from production code !! - """ - def handle_event(self, message, server_connector): + def handle_event(self, message, server_connector): # pylint: disable=no-self-use """ When the FSM steps this method is invoked. Receives a 'data' field from an fsm_update message as kwargs. diff --git a/solvable/src/event_handler_main.py b/solvable/src/event_handler_main.py index fc8e588..de74952 100644 --- a/solvable/src/event_handler_main.py +++ b/solvable/src/event_handler_main.py @@ -22,69 +22,61 @@ LOG = logging.getLogger(__name__) def main(): - # pylint: disable=unused-variable,too-many-locals + # pylint: disable=unused-variable Logger([ Log(stderr, LogFormatter(20)), Log(TFWENV.LOGFILE, VerboseLogFormatter()) ]).start() - eh_factory = EventHandlerFactory() - # TFW component EventHandlers (builtins, required for their respective functionalities) - fsm = FSMManagingEventHandler( # TFW FSM + eh_factory = EventHandlerFactory() + # TFW builtin EventHandlers (required for their respective functionalities) + # TFW FSM + fsm_eh = eh_factory.build(FSMManagingEventHandler( fsm_type=partial( YamlFSM, 'test_fsm.yml', - {} # jinja2 variables, use empty dict to enable jinja2 parsing without any variables + {} # jinja2 variables, empty dict enables jinja2 without any variables ) - ) - fsm_eh = eh_factory.build(fsm) - - ide = IdeEventHandler( # Web IDE backend + )) + # Web IDE backend + ide_eh = eh_factory.build(IdeEventHandler( allowed_directories=[TFWENV.IDE_WD, TFWENV.WEBSERVICE_DIR], directory=TFWENV.IDE_WD, exclude=['*.pyc'] - ) - ide_eh = eh_factory.build(ide) - - terminal = TerminalEventHandler() # Web shell backend - terminal_eh = eh_factory.build(terminal) - - processmanager = ProcessManagingEventHandler( # Handles 'deploy' button clicks + )) + # Web shell backend + terminal_eh = eh_factory.build(TerminalEventHandler()) + # Handles 'deploy' button clicks + processmanager_eh = eh_factory.build(ProcessManagingEventHandler( log_tail=2000 - ) - processmanager_eh = eh_factory.build(processmanager) - - logmonitor = LogMonitoringEventHandler( # Sends live logs of webservice process to frontend + )) + # Sends live logs of webservice process to frontend + logmonitor_eh = eh_factory.build(LogMonitoringEventHandler( process_name='webservice', log_tail=2000 - ) - logmonitor_eh = eh_factory.build(logmonitor) - - snapshot = DirectorySnapshottingEventHandler( # Manages filesystem snapshots of directories + )) + # Manages filesystem snapshots of directories + snapshot_eh = eh_factory.build(DirectorySnapshottingEventHandler( directories=[ TFWENV.IDE_WD, TFWENV.WEBSERVICE_DIR ] - ) - snapshot_eh = eh_factory.build(snapshot) + )) + # Proxies frontend API calls to frontend + frontend_eh = eh_factory.build(FrontendEventHandler()) - frontend = FrontendEventHandler() # Proxies frontend API calls to frontend - frontend_eh = eh_factory.build(frontend) - - # Your custom event handlers - cenator = CenatorEventHandler() - cenator_eh = eh_factory.build(cenator) - - message_fsm_steps = MessageFSMStepsEventHandler() + # Replace these with your custom event handlers + # Echoes executed commands to messages + cenator_eh = eh_factory.build(CenatorEventHandler()) + # Echoes FSM steps message_fsm_steps_eh = eh_factory.build( - message_fsm_steps, + MessageFSMStepsEventHandler(), event_handler_type=FSMAwareEventHandler ) - - commands = TestCommandsEventHandler( # Catches special commands + # Catches special commands + commands_eh = eh_factory.build(TestCommandsEventHandler( bashrc=f'/home/{TAOENV.USER}/.bashrc' - ) - commands_eh = eh_factory.build(commands) + )) setup_signal_handlers() IOLoop.instance().start() diff --git a/solvable/src/pipe_io_main.py b/solvable/src/pipe_io_main.py index 7a44dc3..89b7008 100644 --- a/solvable/src/pipe_io_main.py +++ b/solvable/src/pipe_io_main.py @@ -21,12 +21,10 @@ def main(): eh_factory = EventHandlerFactory() - json_pipe = PipeIOEventHandler( + json_pipe_eh = eh_factory.build(PipeIOEventHandler( '/tmp/tfw_send', '/tmp/tfw_recv' - ) - json_pipe_eh = eh_factory.build(json_pipe) - + )) setup_signal_handlers() IOLoop.instance().start() From 9095a9f85a13bbcb594e31773be4637eafe73f91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20T=C3=B3th?= Date: Wed, 24 Jul 2019 15:52:38 +0200 Subject: [PATCH 4/5] Comply new package structure --- controller/Dockerfile | 4 +- controller/opt/server.py | 23 ++++----- ...m_event_handlers.py => custom_handlers.py} | 29 ++++++------ solvable/src/event_handler_main.py | 47 +++++++++++-------- solvable/src/pipe_io_main.py | 4 +- solvable/src/test_fsm.py | 6 +-- 6 files changed, 58 insertions(+), 55 deletions(-) rename solvable/src/{custom_event_handlers.py => custom_handlers.py} (57%) diff --git a/controller/Dockerfile b/controller/Dockerfile index d09b2fd..6c1afeb 100644 --- a/controller/Dockerfile +++ b/controller/Dockerfile @@ -2,8 +2,8 @@ FROM avatao/controller:debian-buster USER root ENV PYTHONPATH="/usr/local/lib" \ - TFW_PUBLISHER_PORT=7654 \ - TFW_RECEIVER_PORT=8765 \ + TFW_PUB_PORT=7654 \ + TFW_PULL_PORT=8765 \ TFW_AUTH_KEY="/tmp/tfw-auth.key" \ CONTROLLER_PORT=5555 diff --git a/controller/opt/server.py b/controller/opt/server.py index 2fa5b44..f2c1bb6 100644 --- a/controller/opt/server.py +++ b/controller/opt/server.py @@ -4,8 +4,8 @@ import json from tornado.ioloop import IOLoop from tornado.web import RequestHandler, Application -from tfw.builtins import FSMAwareEventHandler -from tfw.main import EventHandlerFactory +from tfw.event_handlers import FSMAwareEventHandler +from tfw.main import EventHandlerFactory, setup_signal_handlers class ControllerPostHandler(RequestHandler): @@ -20,23 +20,18 @@ class ControllerPostHandler(RequestHandler): })) -class ControllerEventHandler(FSMAwareEventHandler): - def handle_event(self, message): - pass - - if __name__ == '__main__': - controller = ControllerEventHandler('controller') - controller_eh = EventHandlerFactory().build(controller) + controller_eh = EventHandlerFactory().build( + lambda *_: None, + event_handler_type=FSMAwareEventHandler + ) application = Application([( f'/{os.environ["SECRET"]}', ControllerPostHandler, - {'controller': controller} + {'controller': controller_eh} )]) application.listen(os.environ['CONTROLLER_PORT']) - try: - IOLoop.instance().start() - finally: - controller.cleanup() + setup_signal_handlers() + IOLoop.instance().start() diff --git a/solvable/src/custom_event_handlers.py b/solvable/src/custom_handlers.py similarity index 57% rename from solvable/src/custom_event_handlers.py rename to solvable/src/custom_handlers.py index 8c2b82e..48c7220 100644 --- a/solvable/src/custom_event_handlers.py +++ b/solvable/src/custom_handlers.py @@ -1,15 +1,15 @@ import logging from ast import literal_eval -from tfw.components import MessageSender -from tfw.builtins import TerminalCommandsEventHandler +from tfw.components.frontend import MessageSender +from tfw.components.terminal import TerminalCommandsHandler from tfw.main import TFWUplinkConnector LOG = logging.getLogger(__name__) -class CenatorEventHandler: +class CenatorHandler: keys = ['history.bash'] def handle_event(self, message, server_connector): # pylint: disable=no-self-use @@ -18,7 +18,7 @@ class CenatorEventHandler: MessageSender(server_connector).send('JOHN CENA', f'You\'ve executed "{command}"') -class TestCommandsEventHandler(TerminalCommandsEventHandler): +class TestCommandsHandler(TerminalCommandsHandler): # pylint: disable=unused-argument,attribute-defined-outside-init,no-self-use def command_sendmessage(self, *args): if not args: @@ -34,14 +34,13 @@ class TestCommandsEventHandler(TerminalCommandsEventHandler): TFWUplinkConnector().send_message(literal_eval(args[0])) -class MessageFSMStepsEventHandler: - def handle_event(self, message, server_connector): # pylint: disable=no-self-use - """ - When the FSM steps this method is invoked. - Receives a 'data' field from an fsm_update message as kwargs. - """ - MessageSender(server_connector).send( - 'FSM info', - f'FSM has stepped from state "{message["last_event"]["from_state"]}" ' - f'to state "{message["current_state"]}" in response to trigger "{message["last_event"]["trigger"]}"' - ) +def messageFSMStepsHandler(message, server_connector): + """ + When the FSM steps this method is invoked. + Receives a 'data' field from an fsm_update message as kwargs. + """ + MessageSender(server_connector).send( + 'FSM info', + f'FSM has stepped from state "{message["last_event"]["from_state"]}" ' + f'to state "{message["current_state"]}" in response to trigger "{message["last_event"]["trigger"]}"' + ) diff --git a/solvable/src/event_handler_main.py b/solvable/src/event_handler_main.py index de74952..fe670e5 100644 --- a/solvable/src/event_handler_main.py +++ b/solvable/src/event_handler_main.py @@ -6,16 +6,17 @@ from tornado.ioloop import IOLoop from tfw.fsm import YamlFSM from tfw.event_handlers import FSMAwareEventHandler -from tfw.builtins import IdeEventHandler, TerminalEventHandler, FrontendEventHandler -from tfw.builtins import LogMonitoringEventHandler, ProcessManagingEventHandler -from tfw.builtins import DirectorySnapshottingEventHandler, FSMManagingEventHandler +from tfw.components.ide import IdeHandler +from tfw.components.terminal import TerminalHandler +from tfw.components.frontend import FrontendHandler +from tfw.components.process_management import ProcessHandler, ProcessLogHandler +from tfw.components.snapshots import SnapshotHandler +from tfw.components.fsm import FSMHandler from tfw.main import EventHandlerFactory, setup_signal_handlers from tfw.logging import Log, Logger, LogFormatter, VerboseLogFormatter -from tfw.config import TFWENV -from tao.config import TAOENV +from tfw.config import TFWENV, TAOENV -from custom_event_handlers import MessageFSMStepsEventHandler -from custom_event_handlers import CenatorEventHandler, TestCommandsEventHandler +from custom_handlers import CenatorHandler, TestCommandsHandler, messageFSMStepsHandler LOG = logging.getLogger(__name__) @@ -31,7 +32,7 @@ def main(): eh_factory = EventHandlerFactory() # TFW builtin EventHandlers (required for their respective functionalities) # TFW FSM - fsm_eh = eh_factory.build(FSMManagingEventHandler( + fsm_eh = eh_factory.build(FSMHandler( fsm_type=partial( YamlFSM, 'test_fsm.yml', @@ -39,42 +40,50 @@ def main(): ) )) # Web IDE backend - ide_eh = eh_factory.build(IdeEventHandler( + ide_eh = eh_factory.build(IdeHandler( allowed_directories=[TFWENV.IDE_WD, TFWENV.WEBSERVICE_DIR], directory=TFWENV.IDE_WD, exclude=['*.pyc'] )) # Web shell backend - terminal_eh = eh_factory.build(TerminalEventHandler()) + terminal_eh = eh_factory.build(TerminalHandler( + port=TFWENV.TERMINADO_PORT, + user=TAOENV.USER, + workind_directory=TFWENV.TERMINADO_WD, + histfile=TFWENV.HISTFILE + )) # Handles 'deploy' button clicks - processmanager_eh = eh_factory.build(ProcessManagingEventHandler( - log_tail=2000 + processmanager_eh = eh_factory.build(ProcessHandler( + supervisor_uri=TFWENV.SUPERVISOR_HTTP_URI, + log_tail=2000, )) # Sends live logs of webservice process to frontend - logmonitor_eh = eh_factory.build(LogMonitoringEventHandler( + logmonitor_eh = eh_factory.build(ProcessLogHandler( process_name='webservice', + supervisor_uri=TFWENV.SUPERVISOR_HTTP_URI, log_tail=2000 )) # Manages filesystem snapshots of directories - snapshot_eh = eh_factory.build(DirectorySnapshottingEventHandler( + snapshot_eh = eh_factory.build(SnapshotHandler( directories=[ TFWENV.IDE_WD, TFWENV.WEBSERVICE_DIR - ] + ], + snapshots_dir=TFWENV.SNAPSHOTS_DIR )) # Proxies frontend API calls to frontend - frontend_eh = eh_factory.build(FrontendEventHandler()) + frontend_eh = eh_factory.build(FrontendHandler()) # Replace these with your custom event handlers # Echoes executed commands to messages - cenator_eh = eh_factory.build(CenatorEventHandler()) + cenator_eh = eh_factory.build(CenatorHandler()) # Echoes FSM steps message_fsm_steps_eh = eh_factory.build( - MessageFSMStepsEventHandler(), + messageFSMStepsHandler, event_handler_type=FSMAwareEventHandler ) # Catches special commands - commands_eh = eh_factory.build(TestCommandsEventHandler( + commands_eh = eh_factory.build(TestCommandsHandler( bashrc=f'/home/{TAOENV.USER}/.bashrc' )) diff --git a/solvable/src/pipe_io_main.py b/solvable/src/pipe_io_main.py index 89b7008..1b6abf9 100644 --- a/solvable/src/pipe_io_main.py +++ b/solvable/src/pipe_io_main.py @@ -3,7 +3,7 @@ from sys import stderr from tornado.ioloop import IOLoop -from tfw.builtins import PipeIOEventHandler +from tfw.components.pipe_io import PipeIOHandler from tfw.config import TFWENV from tfw.logging import Log, Logger, LogFormatter, VerboseLogFormatter from tfw.main import EventHandlerFactory, setup_signal_handlers @@ -21,7 +21,7 @@ def main(): eh_factory = EventHandlerFactory() - json_pipe_eh = eh_factory.build(PipeIOEventHandler( + json_pipe_eh = eh_factory.build(PipeIOHandler( '/tmp/tfw_send', '/tmp/tfw_recv' )) diff --git a/solvable/src/test_fsm.py b/solvable/src/test_fsm.py index f4f7639..0d41590 100644 --- a/solvable/src/test_fsm.py +++ b/solvable/src/test_fsm.py @@ -3,8 +3,8 @@ from os.path import exists from tfw.fsm import LinearFSM -from tfw.components import MessageSender -from tfw.builtins import TFWServerUplinkConnector +from tfw.components.frontend import MessageSender +from tfw.main import TFWUplinkConnector class TestFSM(LinearFSM): @@ -12,7 +12,7 @@ class TestFSM(LinearFSM): def __init__(self): super().__init__(6) - self.uplink = TFWServerUplinkConnector() + self.uplink = TFWUplinkConnector() self.message_sender = MessageSender(self.uplink) self.subscribe_predicate('step_3', self.step_3_allowed) From 27cc80d36c65ad2551dd4e8c28b5469443a9f104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20T=C3=B3th?= Date: Wed, 24 Jul 2019 16:33:21 +0200 Subject: [PATCH 5/5] Remove unnecessary newlines --- solvable/src/custom_handlers.py | 1 - solvable/src/event_handler_main.py | 1 - solvable/src/pipe_io_main.py | 1 - 3 files changed, 3 deletions(-) diff --git a/solvable/src/custom_handlers.py b/solvable/src/custom_handlers.py index 48c7220..96f4597 100644 --- a/solvable/src/custom_handlers.py +++ b/solvable/src/custom_handlers.py @@ -5,7 +5,6 @@ from tfw.components.frontend import MessageSender from tfw.components.terminal import TerminalCommandsHandler from tfw.main import TFWUplinkConnector - LOG = logging.getLogger(__name__) diff --git a/solvable/src/event_handler_main.py b/solvable/src/event_handler_main.py index fe670e5..56bc57d 100644 --- a/solvable/src/event_handler_main.py +++ b/solvable/src/event_handler_main.py @@ -18,7 +18,6 @@ from tfw.config import TFWENV, TAOENV from custom_handlers import CenatorHandler, TestCommandsHandler, messageFSMStepsHandler - LOG = logging.getLogger(__name__) diff --git a/solvable/src/pipe_io_main.py b/solvable/src/pipe_io_main.py index 1b6abf9..2aab217 100644 --- a/solvable/src/pipe_io_main.py +++ b/solvable/src/pipe_io_main.py @@ -8,7 +8,6 @@ from tfw.config import TFWENV from tfw.logging import Log, Logger, LogFormatter, VerboseLogFormatter from tfw.main import EventHandlerFactory, setup_signal_handlers - LOG = logging.getLogger(__name__)