Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
    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))
Ejemplo n.º 3
0
    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))
Ejemplo n.º 4
0
def validate_from_tag(request: pyramid.request.Request, **kwargs: dict):
    """Check that an existing from_tag is valid and set `builds`.

    Ensure that `from_tag` is a valid Koji tag and set `builds` to latest
    builds from this tag if unset.

    Args:
        request: The current request.
        kwargs: The kwargs of the related service definition. Unused.
    """
    koji_tag = request.validated.get('from_tag')

    if koji_tag:
        koji_client = buildsys.get_session()
        taginfo = koji_client.getTag(koji_tag)

        # add all the inherited tags of a sidetag to from_tag_inherited
        if taginfo and taginfo['extra']['sidetag']:
            for tag in koji_client.getFullInheritance(koji_tag):
                request.from_tag_inherited.append(tag['name'])

        if request.validated.get('builds'):
            # Builds were specified explicitly, verify that the tag exists in Koji.
            if not taginfo:
                request.errors.add('body', 'from_tag',
                                   "The supplied from_tag doesn't exist.")
                return

            # Flag that `builds` wasn't filled from the Koji tag.
            request.validated['builds_from_tag'] = False
        else:
            # Builds weren't specified explicitly, pull the list of latest NVRs here, as it is
            # necessary for later validation of ACLs pertaining the respective components.
            try:
                request.validated['builds'] = [
                    b['nvr']
                    for b in koji_client.listTagged(koji_tag, latest=True)
                ]
            except koji.GenericError as e:
                if "invalid taginfo" in str(e).lower():
                    request.errors.add('body', 'from_tag',
                                       "The supplied from_tag doesn't exist.")
                else:
                    raise BodhiException(
                        "Encountered error while requesting tagged builds from "
                        f"Koji: '{e}'") from e
            else:  # no Koji error, request.validated['builds'] was filled
                if not request.validated['builds']:
                    request.errors.add(
                        'body', 'from_tag',
                        "The supplied from_tag doesn't contain any builds.")
                else:
                    # Flag that `builds` was filled from the Koji tag.
                    request.validated['builds_from_tag'] = True
Ejemplo n.º 5
0
    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))
Ejemplo n.º 6
0
    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))
Ejemplo n.º 7
0
def main(alias: str):
    """
    Query the wiki for test cases for each package on the given update.

    Args:
        alias: The update's builds are iterated upon to find test cases for
            them.
    """
    from bodhi.server.models import Update

    db_factory = 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 build in update.builds:
            try:
                build.update_test_cases(session)
            except ExternalCallException:
                log.warning('Error occurred during fetching testcases',
                            exc_info=True)
                raise ExternalCallException
Ejemplo n.º 8
0
def validate_from_tag(request: pyramid.request.Request, **kwargs: dict):
    """Check that an existing from_tag is valid and set `builds`.

    Ensure that `from_tag` is a valid Koji tag and set `builds` to latest
    builds from this tag if unset.

    Args:
        request: The current request.
        kwargs: The kwargs of the related service definition. Unused.
    """
    koji_tag = request.validated.get('from_tag')

    if koji_tag:
        # check if any existing updates use this side tag
        update = request.db.query(Update).filter_by(from_tag=koji_tag).first()
        if update:
            if request.validated.get('edited') == update.alias:
                # existing update found, but it is the one we are editing, so keep going
                pass
            else:
                request.errors.add('body', 'from_tag', "Update already exists using this side tag")
                # don't run any more validators
                request.validated = []
                return

        koji_client = buildsys.get_session()
        taginfo = koji_client.getTag(koji_tag)

        if not taginfo:
            request.errors.add('body', 'from_tag', "The supplied from_tag doesn't exist.")
            return

        # prevent user from creating an update if tag is not a side tag
        if not taginfo.get('extra', {}).get('sidetag'):
            request.errors.add('body', 'from_tag', "The supplied tag is not a side tag.")
            return

        # add all the inherited tags of a sidetag to from_tag_inherited
        for tag in koji_client.getFullInheritance(koji_tag):
            request.from_tag_inherited.append(tag['name'])

        if request.validated.get('builds'):
            # Builds were specified explicitly, verify that the tag exists in Koji.
            if not taginfo:
                request.errors.add('body', 'from_tag', "The supplied from_tag doesn't exist.")
                return

            # Flag that `builds` wasn't filled from the Koji tag.
            request.validated['builds_from_tag'] = False
        else:
            # Builds weren't specified explicitly, pull the list of latest NVRs here, as it is
            # necessary for later validation of ACLs pertaining the respective components.
            try:
                request.validated['builds'] = [
                    b['nvr'] for b in koji_client.listTagged(koji_tag, latest=True)
                ]
            except koji.GenericError as e:
                if "invalid taginfo" in str(e).lower():
                    request.errors.add('body', 'from_tag', "The supplied from_tag doesn't exist.")
                else:
                    raise BodhiException("Encountered error while requesting tagged builds from "
                                         f"Koji: '{e}'") from e
            else:  # no Koji error, request.validated['builds'] was filled
                if not request.validated['builds']:
                    request.errors.add('body', 'from_tag',
                                       "The supplied from_tag doesn't contain any builds.")
                else:
                    # Flag that `builds` was filled from the Koji tag.
                    request.validated['builds_from_tag'] = True
Ejemplo n.º 9
0
    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))
Ejemplo n.º 10
0
    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:
            raise BodhiException(
                f"Received incomplete tag message. Missing: {', '.join(missing)}"
            )

        btag = body['tag']
        bnvr = '{name}-{version}-{release}'.format(**body)

        koji = buildsys.get_session()

        kbuildinfo = koji.getBuild(bnvr)
        if not kbuildinfo:
            raise BodhiException(f"Can't find Koji build for {bnvr}.")

        if 'nvr' not in kbuildinfo:
            raise BodhiException(
                f"Koji build info for {bnvr} doesn't contain 'nvr'.")

        if 'owner_name' not in kbuildinfo:
            raise BodhiException(
                f"Koji build info for {bnvr} doesn't contain 'owner_name'.")

        # 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:
                log.info(
                    f"Build, active update for {bnvr} exists already, skipping."
                )
                return

            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(rbuildinfo)

            log.debug("Creating build object, adding it to the DB.")
            build = bcls(nvr=bnvr, package=pkg)
            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}.")
            update = Update(
                release=rel,
                builds=[build],
                notes=f"Automatic update for {bnvr}.",
                type=UpdateType.unspecified,
                stable_karma=3,
                unstable_karma=-3,
                user=user,
            )

            log.debug("Setting request for new update.")
            update.set_request(dbsession, UpdateRequest.testing, owner_name)

            log.debug("Adding new update to the database.")
            dbsession.add(update)

            log.debug("Committing changes to the database.")
            dbsession.commit()