diff options
author | Richard Ipsum <richard.ipsum@codethink.co.uk> | 2014-02-24 18:21:33 +0000 |
---|---|---|
committer | Richard Ipsum <richard.ipsum@codethink.co.uk> | 2014-03-21 16:47:28 +0000 |
commit | 1de342b8a4cf13b295805855bfaa341bcd86277e (patch) | |
tree | 2b550a0d60532446dad50ee3ecc703a90bb6d780 /distbuild/initiator.py | |
parent | f4b503b036f76c23c4f2cb99ca6596823b323035 (diff) | |
download | morph-1de342b8a4cf13b295805855bfaa341bcd86277e.tar.gz |
Add the distbuild libs
Diffstat (limited to 'distbuild/initiator.py')
-rw-r--r-- | distbuild/initiator.py | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/distbuild/initiator.py b/distbuild/initiator.py new file mode 100644 index 00000000..9c7e2ddf --- /dev/null +++ b/distbuild/initiator.py @@ -0,0 +1,195 @@ +# distbuild/initiator.py -- state machine for the initiator +# +# Copyright (C) 2014 Codethink Limited +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.. + + +import cliapp +import logging +import random +import sys + +import distbuild + + +class _Finished(object): + + def __init__(self, msg): + self.msg = msg + + +class _Failed(object): + + def __init__(self, msg): + self.msg = msg + + +class Initiator(distbuild.StateMachine): + + def __init__(self, cm, conn, app, repo_name, ref, morphology): + distbuild.StateMachine.__init__(self, 'waiting') + self._cm = cm + self._conn = conn + self._app = app + self._repo_name = repo_name + self._ref = ref + self._morphology = morphology + self._steps = None + self._step_outputs = {} + self.debug_transitions = True + + def setup(self): + distbuild.crash_point() + + self._jm = distbuild.JsonMachine(self._conn) + self.mainloop.add_state_machine(self._jm) + logging.debug('initiator: _jm=%s' % repr(self._jm)) + + spec = [ + ('waiting', self._jm, distbuild.JsonEof, None, self._terminate), + ('waiting', self._jm, distbuild.JsonNewMessage, 'waiting', + self._handle_json_message), + ('waiting', self, _Finished, None, self._succeed), + ('waiting', self, _Failed, None, self._fail), + ] + self.add_transitions(spec) + + random_id = random.randint(0, 2**32-1) + + self._app.status( + msg='Requesting build of %(repo)s %(ref)s %(morph)s', + repo=self._repo_name, + ref=self._ref, + morph=self._morphology) + msg = distbuild.message('build-request', + id=random_id, + repo=self._repo_name, + ref=self._ref, + morphology=self._morphology + ) + self._jm.send(msg) + logging.debug('Initiator: sent to controller: %s', repr(msg)) + + def _handle_json_message(self, event_source, event): + distbuild.crash_point() + + logging.debug('Initiator: from controller: %s' % repr(event.msg)) + + handlers = { + 'build-finished': self._handle_build_finished_message, + 'build-failed': self._handle_build_failed_message, + 'build-progress': self._handle_build_progress_message, + 'build-steps': self._handle_build_steps_message, + 'step-started': self._handle_step_started_message, + 'step-output': self._handle_step_output_message, + 'step-finished': self._handle_step_finished_message, + 'step-failed': self._handle_step_failed_message, + } + + handler = handlers[event.msg['type']] + handler(event.msg) + + def _handle_build_finished_message(self, msg): + self.mainloop.queue_event(self, _Finished(msg)) + + def _handle_build_failed_message(self, msg): + self.mainloop.queue_event(self, _Failed(msg)) + + def _handle_build_progress_message(self, msg): + self._app.status(msg='Progress: %(msgtext)s', msgtext=msg['message']) + + def _handle_build_steps_message(self, msg): + self._steps = msg['steps'] + self._app.status( + msg='Build steps in total: %(steps)d', + steps=len(self._steps)) + + def _open_output(self, msg): + assert msg['step_name'] not in self._step_outputs + filename = 'build-step-%s.log' % msg['step_name'] + f = open(filename, 'a') + self._step_outputs[msg['step_name']] = f + worker = msg['worker'] + f.write('Worker: %s\n%s\n\n' % (worker, '-' * len(worker))) + + def _close_output(self, msg): + self._step_outputs[msg['step_name']].close() + del self._step_outputs[msg['step_name']] + + def _handle_step_started_message(self, msg): + self._app.status( + msg='Started building %(step_name)s on %(worker_name)s', + step_name=msg['step_name'], + worker_name=msg['worker_name']) + self._open_output(msg) + + def _handle_step_output_message(self, msg): + step_name = msg['step_name'] + if step_name in self._step_outputs: + f = self._step_outputs[step_name] + f.write(msg['stdout']) + f.write(msg['stderr']) + f.flush() + else: + logging.warning( + 'Got step-output message for unknown step: %s' % repr(msg)) + + def _handle_step_finished_message(self, msg): + step_name = msg['step_name'] + if step_name in self._step_outputs: + self._app.status( + msg='Finished building %(step_name)s', + step_name=step_name) + self._close_output(msg) + else: + logging.warning( + 'Got step-finished message for unknown step: %s' % repr(msg)) + + def _handle_step_failed_message(self, msg): + step_name = msg['step_name'] + if step_name in self._step_outputs: + self._app.status( + msg='Build failed: %(step_name)s', + step_name=step_name) + self._close_output(msg) + else: + logging.warning( + 'Got step-failed message for unknown step: %s' % repr(msg)) + + def _succeed(self, event_source, event): + self.mainloop.queue_event(self._cm, distbuild.StopConnecting()) + self._jm.close() + logging.info('Build finished OK') + + urls = event.msg['urls'] + if urls: + for url in urls: + self._app.status(msg='Artifact: %(url)s', url=url) + else: + self._app.status( + msg='Controller did not give us any artifact URLs.') + + def _fail(self, event_source, event): + self.mainloop.queue_event(self._cm, distbuild.StopConnecting()) + self._jm.close() + raise cliapp.AppException( + 'Failed to build %s %s %s: %s' % + (self._repo_name, self._ref, self._morphology, + event.msg['reason'])) + + def _terminate(self, event_source, event): + self.mainloop.queue_event(self._cm, distbuild.StopConnecting()) + self._jm.close() + |