Source code for estuary_updater.handlers.freshmaker

# SPDX-License-Identifier: GPL-3.0+

from __future__ import unicode_literals, absolute_import

from estuary.models.freshmaker import FreshmakerEvent, FreshmakerBuild
from estuary.models.errata import Advisory
from estuary.utils.general import timestamp_to_datetime

from estuary_updater.handlers.base import BaseHandler
from estuary_updater import log

[docs]class FreshmakerHandler(BaseHandler): """A handler for Freshmaker-related messages."""
[docs] @staticmethod def can_handle(msg): """ Determine if the message is a Freshmaker-related message and can be handled by this handler. :param dict msg: a message to be analyzed :return: a bool based on if the handler can handle this kind of message :rtype: bool """ supported_topics = [ '/topic/VirtualTopic.eng.freshmaker.event.state.changed', '/topic/' ] return msg['topic'] in supported_topics
[docs] def handle(self, msg): """ Handle a Freshmaker message and update Neo4j if necessary. :param dict msg: a message to be processed """ topic = msg['topic'] if topic == '/topic/VirtualTopic.eng.freshmaker.event.state.changed': self.event_state_handler(msg) elif topic == '/topic/': self.build_state_handler(msg) else: raise RuntimeError('This message is unable to be handled: {0}'.format(msg))
[docs] def event_state_handler(self, msg): """ Handle a Freshmaker event state changed message and update Neo4j if necessary. :param dict msg: a message to be processed """ msg_id = msg['body']['msg']['message_id'] if msg['body']['msg'].get('dry_run'): return event_params = { 'id_': str(msg['body']['msg']['id']), 'state_name': msg['body']['msg']['state_name'], 'state_reason': msg['body']['msg']['state_reason'] } if 'time_created' in msg['body']['msg']: event_params['time_created'] = timestamp_to_datetime(msg['body']['msg']['time_created']) if 'time_done' in msg['body']['msg'] and msg['body']['msg']['time_done'] is not None: event_params['time_done'] = timestamp_to_datetime(msg['body']['msg']['time_done']) event = FreshmakerEvent.create_or_update(event_params)[0] advisory_name = msg_id.rsplit('.', 1)[-1] if advisory_name[0:4] not in ('RHSA', 'RHBA', 'RHEA'): log.warn('Unable to parse the advisory name from the Freshmaker message_id: {0}' .format(msg_id)) advisory_name = None advisory = Advisory.get_or_create({ 'id_': msg['body']['msg']['search_key'], 'advisory_name': advisory_name })[0] event.conditional_connect(event.triggered_by_advisory, advisory)
[docs] def build_state_handler(self, msg): """ Handle a Freshmaker build state changed message and update Neo4j if necessary. :param dict msg: a message to be processed """ build_info = msg['body']['msg'] event_id = msg['body']['msg']['event_id'] freshmaker_build = None build = None # build_id in Freshmaker is actually the task_id if not build_info['build_id']: log.debug('Skipping Koji build update for event {0} because build_id is not set'.format( event_id)) freshmaker_build = self.create_or_update_freshmaker_build(build_info, event_id) # Ignore Freshmaker dry run mode, indicated by a negative ID elif build_info['build_id'] < 0: log.debug('Skipping build update for event {0} because it is a dry run'.format( event_id)) else: freshmaker_build = self.create_or_update_freshmaker_build(build_info, event_id) build = self.create_or_update_build(build_info, event_id) event = FreshmakerEvent.nodes.get_or_none(id_=str(event_id)) if event: if build: event.successful_koji_builds.connect(build) if freshmaker_build: freshmaker_build.conditional_connect(freshmaker_build.event, event) else: log.warning('The Freshmaker event {0} does not exist in Neo4j'.format(event_id))
[docs] def create_or_update_freshmaker_build(self, build, event_id): """ Create or update a FreshmakerBuild. :param dict build: the build represented in Freshmaker being created or updated :param int event_id: the id of the Freshmaker event :return: the created/updated FreshmakerBuild or None if it cannot be created :rtype: FreshmakerBuild or None """ log.debug('Creating FreshmakerBuild {0}'.format(build['build_id'])) fb_params = dict( id_=build['id'], dep_on=build['dep_on'], name=build['name'], original_nvr=build['original_nvr'], rebuilt_nvr=build['rebuilt_nvr'], state_name=build['state_name'], state_reason=build['state_reason'], time_submitted=timestamp_to_datetime(build['time_submitted']), type_name=build['type_name'], ) if build['time_completed']: fb_params['time_completed'] = timestamp_to_datetime( build['time_completed']) if build['build_id']: fb_params['build_id'] = build['build_id'] return FreshmakerBuild.create_or_update(fb_params)[0]
[docs] def create_or_update_build(self, build, event_id): """ Use the Koji Task Result to create or update a ContainerKojiBuild. :param dict build: the build represented in Freshmaker being created or updated :param int event_id: the id of the Freshmaker event :return: the created/updated ContainerKojiBuild or None if it cannot be created :rtype: ContainerKojiBuild or None """ # Builds in Koji only exist when the Koji task this Freshmaker build represents completes if build['state_name'] != 'DONE': log.debug('Skipping build update for event {0} because the build is not complete yet' .format(event_id)) return None try: koji_task_result = self.koji_session.getTaskResult(build['build_id']) except Exception: log.error('Failed to get the Koji task result with ID {0}'.format(build['build_id'])) raise if not koji_task_result.get('koji_builds'): log.warn('The task result of {0} does not contain the koji_builds key'.format( build['build_id'])) return None # The ID is returned as a string so it must be cast to an int koji_build_id = int(koji_task_result['koji_builds'][0]) # It's always going to be a container build when the build comes from Freshmaker, so we can # just set force_container_label to avoid unncessary heuristic checks return self.get_or_create_build( koji_build_id, build['original_nvr'], force_container_label=True)