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)
def build_handler(self, msg): """ Handle a build state message and update Neo4j if necessary. :param dict msg: a message to be processed """ if not msg['body']['msg']['info']['source']: return commit_hash_pattern = re.compile(r'(?:\#)([0-9a-f]{40})$') commit_hash = re.findall(commit_hash_pattern, msg['body']['msg']['info']['source']) # Container builds and rpms have commit hashes, so we want to process them if commit_hash: commit = DistGitCommit.get_or_create({'hash_': commit_hash[0]})[0] build = self.get_or_create_build(msg['body']['msg']['info']['id']) if build.__label__ == ModuleKojiBuild.__label__: extra_json = msg['body']['msg']['info']['extra'] module_extra_info = extra_json.get('typeinfo', {}).get('module') module_build_tag_name = module_extra_info.get( 'content_koji_tag') if module_build_tag_name: try: tag_info = self.koji_session.getTag( module_build_tag_name) except Exception: log.error('Failed to get tag {0}'.format( module_build_tag_name)) raise module_build_tag = KojiTag.create_or_update({ 'id_': tag_info['id'], 'name': module_build_tag_name })[0] module_build_tag.module_builds.connect(build) _, components = self.koji_session.listTaggedRPMS( module_build_tag_name) for component in components: component_build = self.get_or_create_build(component) build.components.connect(component_build) build.conditional_connect(build.commit, commit)
def get_or_create_build(self, identifier, original_nvr=None, force_container_label=False): """ Get a Koji build from Neo4j, or create it if it does not exist in Neo4j. :param str/int identifier: an NVR (str) or build ID (int), or a dict of info from Koji API :kwarg str original_nvr: original_nvr property for the ContainerKojiBuild :kwarg bool force_container_label: when true, this skips the check to see if the build is a container and just creates the build with the ContainerKojiBuild label :rtype: KojiBuild :return: the Koji Build retrieved or created from Neo4j """ if type(identifier) is dict: build_info = identifier else: try: build_info = self.koji_session.getBuild(identifier, strict=True) except Exception: log.error( 'Failed to get brew build using the identifier {0}'.format( identifier)) raise build_params = { 'epoch': build_info['epoch'], 'id_': str(build_info['id']), 'name': build_info['package_name'], 'release': build_info['release'], 'state': build_info['state'], 'version': build_info['version'] } if build_info.get('extra'): build_params['extra'] = json.dumps(build_info['extra']) # To handle the case when a message has a null timestamp for time_key in ('completion_time', 'creation_time', 'start_time'): # Certain Koji API endpoints omit the *_ts values but have the *_time values, so that's # why the *_time values are used if build_info[time_key]: ts_format = r'%Y-%m-%d %H:%M:%S' if len(build_info[time_key].rsplit('.', 1)) == 2: # If there are microseconds, go ahead and parse that too ts_format += r'.%f' build_params[time_key] = datetime.strptime( build_info[time_key], ts_format) owner = User.create_or_update({ 'username': build_info['owner_name'], 'email': '{0}@redhat.com'.format(build_info['owner_name']) })[0] if force_container_label or self.is_container_build(build_info): if original_nvr: build_params['original_nvr'] = original_nvr build = ContainerKojiBuild.create_or_update(build_params)[0] elif self.is_module_build(build_info): module_extra_info = build_info['extra'].get('typeinfo', {}).get('module') build_params['context'] = module_extra_info.get('context') build_params['mbs_id'] = module_extra_info.get( 'module_build_service_id') build_params['module_name'] = module_extra_info.get('name') build_params['module_stream'] = module_extra_info.get('stream') build_params['module_version'] = module_extra_info.get('version') try: build = ModuleKojiBuild.create_or_update(build_params)[0] except neomodel.exceptions.ConstraintValidationFailed: # This must have errantly been created as a KojiBuild instead of a # ModuleKojiBuild, so let's fix that. build = KojiBuild.nodes.get_or_none(id_=build_params['id_']) if not build: # If there was a constraint validation failure and the build isn't just the # wrong label, then we can't recover. raise build.add_label(ModuleKojiBuild.__label__) build = ModuleKojiBuild.create_or_update(build_params)[0] else: build = KojiBuild.create_or_update(build_params)[0] build.conditional_connect(build.owner, owner) return build