def main(alias: str, bugs: typing.List[int]): """ Iterate the list of bugs, retrieving information from Bugzilla and modifying them. Iterate the given list of bugs associated with the given update. For each bug, retrieve details from Bugzilla, comment on the bug to let watchers know about the update, and mark the bug as MODIFIED. If the bug is a security issue, mark the update as a security update. Args: update: The update that the bugs are associated with. bugs: A list of bodhi.server.models.Bug instances that we wish to act on. """ from bodhi.server.models import Bug, Update, UpdateType log.info(f'Got {len(bugs)} bugs to sync for {alias}') db_factory = util.transactional_session_maker() with db_factory() as session: update = Update.get(alias) if not update: raise BodhiException(f"Couldn't find alias {alias} in DB") for bug_id in bugs: bug = Bug.get(bug_id) # Sanity check if bug is None or bug not in update.bugs: update_bugs_ids = [b.bug_id for b in update.bugs] update.update_bugs(update_bugs_ids + [bug_id], session) # Now, after update.update_bugs, bug with bug_id should exists in DB bug = Bug.get(bug_id) log.info(f'Getting RHBZ bug {bug.bug_id}') try: rhbz_bug = bug_module.bugtracker.getbug(bug.bug_id) log.info(f'Updating our details for {bug.bug_id}') bug.update_details(rhbz_bug) log.info(f' Got title {bug.title} for {bug.bug_id}') # If you set the type of your update to 'enhancement' but you # attach a security bug, we automatically change the type of your # update to 'security'. We need to do this first, so we don't # accidentally comment on stuff that we shouldn't. if not update.type == UpdateType.security and bug.security: log.info("Setting our UpdateType to security.") update.type = UpdateType.security log.info(f'Commenting on {bug.bug_id}') comment = config['initial_bug_msg'] % ( update.alias, update.release.long_name, update.abs_url()) log.info(f'Modifying {bug.bug_id}') bug.modified(update, comment) except Exception: log.warning('Error occurred during updating single bug', exc_info=True) raise ExternalCallException
def __call__(self, message: typing.Union[UpdateEditV1, UpdateRequestTestingV1]): """ Process the given message, updating relevant bugs and test cases. Duplicate messages: if the server delivers the message multiple times, the bugs and test cases are simply re-fetched and updated, so nothing bad happens. Args: message: A message about a new or edited update. """ topic = message.topic alias = message.update.alias log.info("Updates Handler handling %s, %s" % (alias, topic)) # Go to sleep for a second to try and avoid a race condition # https://github.com/fedora-infra/bodhi/issues/458 time.sleep(1) with self.db_factory() as session: update = Update.get(alias) if not update: raise BodhiException("Couldn't find alias '%s' in DB" % alias) bugs = [] if isinstance(message, UpdateEditV1): for idx in message.new_bugs: bug = Bug.get(idx) # Sanity check if bug is None or bug not in update.bugs: update_bugs_ids = [b.bug_id for b in update.bugs] update.update_bugs(update_bugs_ids + [idx], session) # Now, after update.update_bugs, bug with idx should exists in DB bug = Bug.get(idx) bugs.append(bug) elif isinstance(message, UpdateRequestTestingV1): bugs = update.bugs else: raise NotImplementedError("Should never get here.") self.work_on_bugs(session, update, bugs) self.fetch_test_cases(session, update) if config['test_gating.required']: with self.db_factory() as session: update = Update.get(alias) update.update_test_gating_status() log.info("Updates Handler done with %s, %s" % (alias, topic))
def run(self, api_version: int, data: dict): """ Process the given message, updating relevant bugs and test cases. Duplicate messages: if the server delivers the message multiple times, the bugs and test cases are simply re-fetched and updated, so nothing bad happens. Args: api_version: API version number. data: Information about a new or edited update. """ action = data["action"] alias = data['update'].get('alias') log.info("Updates Handler handling %s, %s" % (alias, action)) # Go to sleep for a second to try and avoid a race condition # https://github.com/fedora-infra/bodhi/issues/458 time.sleep(1) with self.db_factory() as session: update = Update.get(alias) if not update: raise BodhiException("Couldn't find alias '%s' in DB" % alias) bugs = [] if action == "edit": for idx in data['new_bugs']: bug = Bug.get(idx) # Sanity check if bug is None or bug not in update.bugs: update_bugs_ids = [b.bug_id for b in update.bugs] update.update_bugs(update_bugs_ids + [idx], session) # Now, after update.update_bugs, bug with idx should exists in DB bug = Bug.get(idx) bugs.append(bug) elif action == "testing": bugs = update.bugs else: raise NotImplementedError("Should never get here.") self.work_on_bugs(session, update, bugs) self.fetch_test_cases(session, update) if config['test_gating.required']: with self.db_factory() as session: update = Update.get(alias) update.update_test_gating_status() log.info("Updates Handler done with %s, %s" % (alias, action))
def __call__(self, message: fedora_messaging.api.Message): """ Process the given message, updating relevant bugs and test cases. Args: message: A message about a new or edited update. """ msg = message.body['msg'] topic = message.topic alias = msg['update'].get('alias') log.info("Updates Handler handling %s, %s" % (alias, topic)) # Go to sleep for a second to try and avoid a race condition # https://github.com/fedora-infra/bodhi/issues/458 time.sleep(1) with self.db_factory() as session: update = Update.get(alias) if not update: raise BodhiException("Couldn't find alias '%s' in DB" % alias) bugs = [] if topic.endswith('update.edit'): for idx in msg['new_bugs']: bug = Bug.get(idx) # Sanity check if bug is None or bug not in update.bugs: update_bugs_ids = [b.bug_id for b in update.bugs] update.update_bugs(update_bugs_ids + [idx], session) # Now, after update.update_bugs, bug with idx should exists in DB bug = Bug.get(idx) bugs.append(bug) elif topic.endswith('update.request.testing'): bugs = update.bugs else: raise NotImplementedError("Should never get here.") self.work_on_bugs(session, update, bugs) self.fetch_test_cases(session, update) if config['test_gating.required']: with self.db_factory() as session: update = Update.get(alias) update.update_test_gating_status() log.info("Updates Handler done with %s, %s" % (alias, topic))
def populate(db): """ Create some data for tests to use. Args: db (sqlalchemy.orm.session.Session): The database session. """ user = User(name='guest') db.add(user) anonymous = User(name='anonymous') db.add(anonymous) provenpackager = Group(name='provenpackager') db.add(provenpackager) packager = Group(name='packager') db.add(packager) user.groups.append(packager) release = Release(name='F17', long_name='Fedora 17', id_prefix='FEDORA', version='17', dist_tag='f17', stable_tag='f17-updates', testing_tag='f17-updates-testing', candidate_tag='f17-updates-candidate', pending_signing_tag='f17-updates-signing-pending', pending_testing_tag='f17-updates-testing-pending', pending_stable_tag='f17-updates-pending', override_tag='f17-override', branch='f17', state=ReleaseState.current, create_automatic_updates=True, package_manager=PackageManager.unspecified, testing_repository=None) db.add(release) db.flush() # This mock will help us generate a consistent update alias. with mock.patch(target='uuid.uuid4', return_value='wat'): update = create_update(db, ['bodhi-2.0-1.fc17']) update.type = UpdateType.bugfix update.severity = UpdateSeverity.medium bug = Bug(bug_id=12345) db.add(bug) update.bugs.append(bug) comment = Comment(karma=1, text="wow. amaze.") db.add(comment) comment.user = user update.comments.append(comment) comment = Comment(karma=0, text="srsly. pretty good.") comment.user = anonymous db.add(comment) update.comments.append(comment) db.add(update) db.commit()
def populate(db): """ Create some data for tests to use. Args: db (sqlalchemy.orm.session.Session): The database session. """ user = User(name=u'guest') db.add(user) anonymous = User(name=u'anonymous') db.add(anonymous) provenpackager = Group(name=u'provenpackager') db.add(provenpackager) packager = Group(name=u'packager') db.add(packager) user.groups.append(packager) release = Release(name=u'F17', long_name=u'Fedora 17', id_prefix=u'FEDORA', version=u'17', dist_tag=u'f17', stable_tag=u'f17-updates', testing_tag=u'f17-updates-testing', candidate_tag=u'f17-updates-candidate', pending_signing_tag=u'f17-updates-testing-signing', pending_testing_tag=u'f17-updates-testing-pending', pending_stable_tag=u'f17-updates-pending', override_tag=u'f17-override', branch=u'f17', state=ReleaseState.current) db.add(release) db.flush() update = create_update(db, [u'bodhi-2.0-1.fc17']) update.type = UpdateType.bugfix update.severity = UpdateSeverity.medium bug = Bug(bug_id=12345) db.add(bug) update.bugs.append(bug) cve = CVE(cve_id=u"CVE-1985-0110") db.add(cve) update.cves.append(cve) comment = Comment(karma=1, text=u"wow. amaze.") db.add(comment) comment.user = user update.comments.append(comment) comment = Comment(karma=0, text=u"srsly. pretty good.", anonymous=True) comment.user = anonymous db.add(comment) update.comments.append(comment) with mock.patch(target='uuid.uuid4', return_value='wat'): update.assign_alias() db.add(update) db.commit()
def consume(self, message): """ Process the given message, updating relevant bugs and test cases. Args: message (munch.Munch): A fedmsg about a new or edited update. """ msg = message['body']['msg'] topic = message['topic'] alias = msg['update'].get('alias') log.info("Updates Handler handling %s, %s" % (alias, topic)) # Go to sleep for a second to try and avoid a race condition # https://github.com/fedora-infra/bodhi/issues/458 time.sleep(1) if not alias: log.error("Update Handler got update with no " "alias %s." % pprint.pformat(msg)) return with self.db_factory() as session: update = Update.get(alias) if not update: raise BodhiException("Couldn't find alias '%s' in DB" % alias) if topic.endswith('update.edit'): bugs = [Bug.get(idx) for idx in msg['new_bugs']] # Sanity check for bug in bugs: assert bug in update.bugs elif topic.endswith('update.request.testing'): bugs = update.bugs else: raise NotImplementedError("Should never get here.") self.work_on_bugs(session, update, bugs) self.fetch_test_cases(session, update) if config['test_gating.required']: with self.db_factory() as session: update = Update.get(alias) update.update_test_gating_status() log.info("Updates Handler done with %s, %s" % (alias, topic))
def __call__(self, message: fedora_messaging.api.Message) -> None: """Create updates from appropriately tagged builds. Args: message: The message we are processing. """ body = message.body missing = [] for mandatory in ('tag', 'build_id', 'name', 'version', 'release'): if mandatory not in body: missing.append(mandatory) if missing: log.debug( f"Received incomplete tag message. Missing: {', '.join(missing)}" ) return btag = body['tag'] bnvr = '{name}-{version}-{release}'.format(**body) koji = buildsys.get_session() kbuildinfo = koji.getBuild(bnvr) if not kbuildinfo: log.debug(f"Can't find Koji build for {bnvr}.") return if 'nvr' not in kbuildinfo: log.debug(f"Koji build info for {bnvr} doesn't contain 'nvr'.") return if 'owner_name' not in kbuildinfo: log.debug( f"Koji build info for {bnvr} doesn't contain 'owner_name'.") return if kbuildinfo['owner_name'] in config.get( 'automatic_updates_blacklist'): log.debug( f"{bnvr} owned by {kbuildinfo['owner_name']} who is listed in " "automatic_updates_blacklist, skipping.") return # some APIs want the Koji build info, some others want the same # wrapped in a larger (request?) structure rbuildinfo = { 'info': kbuildinfo, 'nvr': kbuildinfo['nvr'].rsplit('-', 2), } with self.db_factory() as dbsession: rel = dbsession.query(Release).filter_by( create_automatic_updates=True, candidate_tag=btag).first() if not rel: log.debug( f"Ignoring build being tagged into {btag!r}, no release configured for " "automatic updates for it found.") return bcls = ContentType.infer_content_class(Build, kbuildinfo) build = bcls.get(bnvr) if build and build.update: log.info( f"Build, active update for {bnvr} exists already, skipping." ) return if not build: log.debug(f"Build for {bnvr} doesn't exist yet, creating.") # Package.get_or_create() infers content type already log.debug("Getting/creating related package object.") pkg = Package.get_or_create(dbsession, rbuildinfo) log.debug("Creating build object, adding it to the DB.") build = bcls(nvr=bnvr, package=pkg, release=rel) dbsession.add(build) owner_name = kbuildinfo['owner_name'] user = User.get(owner_name) if not user: log.debug(f"Creating bodhi user for '{owner_name}'.") # Leave email, groups blank, these will be filled # in or updated when they log into Bodhi next time, see # bodhi.server.security:remember_me(). user = User(name=owner_name) dbsession.add(user) log.debug(f"Creating new update for {bnvr}.") try: changelog = build.get_changelog(lastupdate=True) except ValueError: # Often due to bot-generated builds # https://github.com/fedora-infra/bodhi/issues/4146 changelog = None except Exception: # Re-raise exception, so that the message can be re-queued raise closing_bugs = [] if changelog: log.debug("Adding changelog to update notes.") notes = f"""Automatic update for {bnvr}. ##### **Changelog** ``` {changelog} ```""" if rel.name not in config.get('bz_exclude_rels'): for b in re.finditer(config.get('bz_regex'), changelog, re.IGNORECASE): idx = int(b.group(1)) log.debug(f'Adding bug #{idx} to the update.') bug = Bug.get(idx) if bug is None: bug = Bug(bug_id=idx) dbsession.add(bug) dbsession.flush() if bug not in closing_bugs: closing_bugs.append(bug) else: notes = f"Automatic update for {bnvr}." update = Update( release=rel, builds=[build], bugs=closing_bugs, notes=notes, type=UpdateType.unspecified, stable_karma=3, unstable_karma=-3, autokarma=False, user=user, status=UpdateStatus.pending, critpath=Update.contains_critpath_component([build], rel.branch), ) # Comment on the update that it was automatically created. update.comment( dbsession, str("This update was automatically created"), author="bodhi", ) update.add_tag(update.release.pending_signing_tag) log.debug("Adding new update to the database.") dbsession.add(update) log.debug("Flushing changes to the database.") dbsession.flush() # Obsolete older updates which may be stuck in testing due to failed gating try: update.obsolete_older_updates(dbsession) except Exception as e: log.error(f'Problem obsoleting older updates: {e}') alias = update.alias buglist = [b.bug_id for b in update.bugs] # This must be run after dbsession is closed so changes are committed to db work_on_bugs_task.delay(alias, buglist)
def run(self, api_version: int, data: dict): """ Process the given message, updating relevant bugs and test cases. Duplicate messages: if the server delivers the message multiple times, the bugs and test cases are simply re-fetched and updated, so nothing bad happens. Args: api_version: API version number. data: Information about a new or edited update. """ if api_version == 1: alias = data["update"].get("alias") elif api_version == 2: try: alias = data['update_alias'] except KeyError: log.error(f"Wrong message format for the handle_update task: {data}") return else: log.error(f"The Updates Handler doesn't know how to handle api_version {api_version}. " f"Message was: {data}") return action = data["action"] log.info("Updates Handler handling %s, %s" % (alias, action)) # Go to sleep for a second to try and avoid a race condition # https://github.com/fedora-infra/bodhi/issues/458 time.sleep(1) with self.db_factory() as session: update = Update.get(alias) if not update: raise BodhiException("Couldn't find alias '%s' in DB" % alias) bugs = [] if action == "edit": # If editing a Pending update, all of whose builds are signed, for a release # which isn't composed by Bodhi (i.e. Rawhide), move it directly to Testing. if not update.release.composed_by_bodhi \ and update.status == UpdateStatus.pending \ and update.signed: log.info("Every build in the update is signed, set status to testing") update.status = UpdateStatus.testing update.date_testing = func.current_timestamp() update.request = None log.info(f"Update status of {update.display_name} has been set to testing") for idx in data['new_bugs']: bug = Bug.get(idx) # Sanity check if bug is None or bug not in update.bugs: update_bugs_ids = [b.bug_id for b in update.bugs] update.update_bugs(update_bugs_ids + [idx], session) # Now, after update.update_bugs, bug with idx should exists in DB bug = Bug.get(idx) bugs.append(bug) elif action == "testing": bugs = update.bugs else: raise NotImplementedError("Should never get here.") self.work_on_bugs(session, update, bugs) self.fetch_test_cases(session, update) if config['test_gating.required']: with self.db_factory() as session: update = Update.get(alias) update.update_test_gating_status() log.info("Updates Handler done with %s, %s" % (alias, action))