Esempio n. 1
0
    def __init__(self, *args, **kwargs):
        """Initialize the UpdatesHandler."""
        self.db_factory = util.transactional_session_maker()

        self.handle_bugs = bool(config.get('bodhi_email'))
        if not self.handle_bugs:
            log.warning("No bodhi_email defined; not fetching bug details")
Esempio n. 2
0
def main():
    """Check build tags and sign those we missed."""
    db_factory = transactional_session_maker()
    older_than = datetime.utcnow() - timedelta(
        days=config.get('check_signed_builds_delay'))
    with db_factory() as session:
        updates = models.Update.query.filter(
            models.Update.status == models.UpdateStatus.pending).filter(
                models.Update.release_id == models.Release.id).filter(
                    models.Release.state.in_([
                        models.ReleaseState.current,
                        models.ReleaseState.pending,
                        models.ReleaseState.frozen,
                    ])).all()

        if len(updates) == 0:
            log.debug('No stuck Updates found')
            return

        kc = buildsys.get_session()
        stuck_builds = []

        for update in updates:
            # Let Bodhi have its times
            if update.date_submitted >= older_than:
                continue
            builds = update.builds
            # Clean Updates with no builds
            if len(builds) == 0:
                log.debug(f'Obsoleting empty update {update.alias}')
                update.obsolete(session)
                session.flush()
                continue
            pending_signing_tag = update.release.pending_signing_tag
            pending_testing_tag = update.release.pending_testing_tag
            for build in builds:
                if build.signed:
                    log.debug(f'{build.nvr} already marked as signed')
                    continue
                build_tags = [t['name'] for t in kc.listTags(build=build.nvr)]
                if pending_signing_tag not in build_tags and pending_testing_tag in build_tags:
                    # Our composer missed the message that the build got signed
                    log.debug(f'Changing signed status of {build.nvr}')
                    build.signed = True
                if pending_signing_tag in build_tags and pending_testing_tag not in build_tags:
                    # autosign missed the message that the build is waiting to be signed
                    log.debug(
                        f'{build.nvr} is stuck waiting to be signed, let\'s try again'
                    )
                    stuck_builds.append(build.nvr)
            session.flush()

        if stuck_builds:
            kc.multicall = True
            for b in stuck_builds:
                kc.untagBuild(pending_signing_tag, b, force=True)
            kc.multiCall()
            for b in stuck_builds:
                kc.tagBuild(pending_signing_tag, b, force=True)
            kc.multiCall()
Esempio n. 3
0
    def __init__(self, hub, *args, **kwargs):
        """
        Initialize the UpdatesHandler, subscribing it to the appropriate topics.

        Args:
            hub (moksha.hub.hub.CentralMokshaHub): The hub this handler is consuming messages from.
                It is used to look up the hub config.
        """
        initialize_db(config)
        self.db_factory = util.transactional_session_maker()

        prefix = hub.config.get('topic_prefix')
        env = hub.config.get('environment')
        self.topic = [
            prefix + '.' + env + '.bodhi.update.request.testing',
            prefix + '.' + env + '.bodhi.update.edit',
        ]

        self.handle_bugs = bool(config.get('bodhi_email'))
        if not self.handle_bugs:
            log.warning("No bodhi_email defined; not fetching bug details")
        else:
            bug_module.set_bugtracker()

        super(UpdatesHandler, self).__init__(hub, *args, **kwargs)
        log.info('Bodhi updates handler listening on:\n'
                 '%s' % pprint.pformat(self.topic))
Esempio n. 4
0
def main():
    """Check the enforced policies by Greenwave for each open update."""
    db_factory = transactional_session_maker()
    with db_factory() as session:

        updates = models.Update.query.filter(
            models.Update.status.in_(
                [models.UpdateStatus.pending, models.UpdateStatus.testing])
        ).filter(models.Update.release_id == models.Release.id).filter(
            models.Release.state.in_([
                models.ReleaseState.current,
                models.ReleaseState.pending,
                models.ReleaseState.frozen,
            ])
        ).order_by(
            # Check the older updates first so there is more time for the newer to
            # get their test results
            models.Update.id.asc())

        for update in updates:
            try:
                update.update_test_gating_status()
                session.commit()
            except Exception:
                # If there is a problem talking to Greenwave server, print the error.
                log.exception(
                    f"There was an error checking the policy for {update.alias}"
                )
                session.rollback()
Esempio n. 5
0
def main():
    """Wrap ``expire_overrides()``, catching exceptions."""
    db_factory = transactional_session_maker()
    try:
        with db_factory() as db:
            expire_overrides(db)
    except Exception:
        log.exception("There was an error expiring overrides")
Esempio n. 6
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
Esempio n. 7
0
    def __init__(self, db_factory: transactional_session_maker = None):
        """
        Initialize the CI Handler.

        Args:
            db_factory: If given, used as the db_factory for this handler. If
            None (the default), a new TransactionalSessionMaker is created and
            used.
        """
        if not db_factory:
            self.db_factory = transactional_session_maker()
        else:
            self.db_factory = db_factory
Esempio n. 8
0
    def __init__(self, hub, *args, **kwargs):
        """
        Initialize the SignedHandler, configuring its topic and database.

        Args:
            hub (moksha.hub.hub.CentralMokshaHub): The hub this handler is consuming messages from.
                It is used to look up the hub config.
        """
        initialize_db(config)
        self.db_factory = transactional_session_maker()

        prefix = hub.config.get('topic_prefix')
        env = hub.config.get('environment')
        self.topic = [prefix + '.' + env + '.buildsys.tag']

        super(SignedHandler, self).__init__(hub, *args, **kwargs)
        log.info('Bodhi signed handler listening on:\n'
                 '%s' % pprint.pformat(self.topic))
Esempio n. 9
0
def main():
    """
    Comment on updates that are eligible to be pushed to stable.

    Queries for updates in the testing state that have a NULL request, and run approve_update on
    them.
    """
    db_factory = transactional_session_maker()
    try:
        with db_factory() as db:
            testing = db.query(Update).filter_by(status=UpdateStatus.testing,
                                                 request=None)
            for update in testing:
                approve_update(update, db)
                db.commit()
    except Exception:
        log.exception("There was an error approving testing updates.")
    finally:
        db_factory._end_session()
Esempio n. 10
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
Esempio n. 11
0
    def __init__(self, hub, db_factory=None, mash_dir=config.get('mash_dir'),
                 *args, **kw):
        if not db_factory:
            config_uri = '/etc/bodhi/production.ini'
            settings = get_appsettings(config_uri)
            engine = engine_from_config(settings, 'sqlalchemy.')
            Base.metadata.create_all(engine)
            self.db_factory = transactional_session_maker()
        else:
            self.db_factory = db_factory

        buildsys.setup_buildsystem(config)
        bugs.set_bugtracker()
        self.mash_dir = mash_dir
        prefix = hub.config.get('topic_prefix')
        env = hub.config.get('environment')
        self.topic = prefix + '.' + env + '.' + hub.config.get('masher_topic')
        self.valid_signer = hub.config.get('releng_fedmsg_certname')
        if not self.valid_signer:
            log.warn('No releng_fedmsg_certname defined'
                     'Cert validation disabled')
        super(Masher, self).__init__(hub, *args, **kw)
        log.info('Bodhi masher listening on topic: %s' % self.topic)
Esempio n. 12
0
def push(username, yes, **kwargs):
    """Push builds out to the repositories."""
    resume = kwargs.pop('resume')
    resume_all = False

    initialize_db(config)
    db_factory = transactional_session_maker()
    composes = []
    with db_factory() as session:
        if not resume and session.query(Compose).count():
            if yes:
                click.echo('Existing composes detected: {}. Resuming all.'.format(
                    ', '.join([str(c) for c in session.query(Compose).all()])))
            else:
                click.confirm(
                    'Existing composes detected: {}. Do you wish to resume them all?'.format(
                        ', '.join([str(c) for c in session.query(Compose).all()])),
                    abort=True)
            resume = True
            resume_all = True

        # If we're resuming a push
        if resume:
            for compose in session.query(Compose).all():
                if len(compose.updates) == 0:
                    # Compose objects can end up with 0 updates in them if the composer ejects all
                    # the updates in a compose for some reason. Composes with no updates cannot be
                    # serialized because their content_type property uses the content_type of the
                    # first update in the Compose. Additionally, it doesn't really make sense to go
                    # forward with running an empty Compose. It makes the most sense to delete them.
                    click.echo("{} has no updates. It is being removed.".format(compose))
                    session.delete(compose)
                    continue

                if not resume_all:
                    if yes:
                        click.echo('Resuming {}.'.format(compose))
                    elif not click.confirm('Resume {}?'.format(compose)):
                        continue

                # Reset the Compose's state and error message.
                compose.state = ComposeState.requested
                compose.error_message = ''

                composes.append(compose)
        else:
            updates = []
            # Accept both comma and space separated request list
            requests = kwargs['request'].replace(',', ' ').split(' ')
            requests = [UpdateRequest.from_string(val) for val in requests]

            query = session.query(Update).filter(Update.request.in_(requests))

            if kwargs.get('builds'):
                query = query.join(Update.builds)
                query = query.filter(
                    or_(*[Build.nvr == build for build in kwargs['builds'].split(',')]))

            if kwargs.get('updates'):
                query = query.filter(
                    or_(*[Update.alias == alias for alias in kwargs['updates'].split(',')]))

            query = _filter_releases(session, query, kwargs.get('releases'))

            for update in query.all():
                # Skip unsigned updates (this checks that all builds in the update are signed)
                update_sig_status(update)

                if not update.signed:
                    click.echo(
                        f'Warning: {update.get_title()} has unsigned builds and has been skipped')
                    continue

                updates.append(update)

            composes = Compose.from_updates(updates)
            for c in composes:
                session.add(c)

            # We need to flush so the database knows about the new Compose objects, so the
            # Compose.updates relationship will work properly. This is due to us overriding the
            # primaryjoin on the relationship between Composes and Updates.
            session.flush()

            # Now we need to refresh the composes so their updates property will not be empty.
            for compose in composes:
                session.refresh(compose)

        # Now we need to sort the composes so their security property can be used to prioritize
        # security updates. The security property relies on the updates property being
        # non-empty, so this must happen after the refresh above.
        composes = sorted(composes)

        for compose in composes:
            click.echo('\n\n===== {} =====\n'.format(compose))
            for update in compose.updates:
                click.echo(update.get_title())

        if composes:
            if yes:
                click.echo('\n\nPushing {:d} updates.'.format(
                    sum([len(c.updates) for c in composes])))
            else:
                click.confirm('\n\nPush these {:d} updates?'.format(
                    sum([len(c.updates) for c in composes])), abort=True)
            click.echo('\nLocking updates...')
        else:
            click.echo('\nThere are no updates to push.')

        composes = [c.__json__(composer=True) for c in composes]

    if composes:
        click.echo('\nSending composer.start message')
        bodhi.server.notifications.publish(composer_schemas.ComposerStartV1.from_dict(dict(
            api_version=2, composes=composes, resume=resume, agent=username)),
            force=True)
Esempio n. 13
0
 def __init__(self):
     """Initialize the SignedHandler."""
     self.db_factory = transactional_session_maker()
Esempio n. 14
0
def push(username, cert_prefix, **kwargs):
    """Push builds out to the repositories."""
    resume = kwargs.pop('resume')

    lockfiles = defaultdict(list)
    locked_updates = []
    locks = '%s/MASHING-*' % config.get('mash_dir')
    for lockfile in glob.glob(locks):
        with file(lockfile) as lock:
            state = json.load(lock)
        for update in state['updates']:
            lockfiles[lockfile].append(update)
            locked_updates.append(update)

    update_titles = None

    initialize_db(config)
    db_factory = transactional_session_maker()
    with db_factory() as session:
        updates = []
        # If we're resuming a push
        if resume:
            for lockfile in lockfiles:
                if not click.confirm('Resume {}?'.format(lockfile)):
                    continue

                for update in lockfiles[lockfile]:
                    update = session.query(Update).filter(
                        Update.title == update).first()
                    updates.append(update)
        else:
            # Accept both comma and space separated request list
            requests = kwargs['request'].replace(',', ' ').split(' ')
            requests = [UpdateRequest.from_string(val) for val in requests]

            query = session.query(Update).filter(Update.request.in_(requests))

            if kwargs.get('builds'):
                query = query.join(Update.builds)
                query = query.filter(
                    or_(*[
                        Build.nvr == build
                        for build in kwargs['builds'].split(',')
                    ]))

            query = _filter_releases(session, query, kwargs.get('releases'))

            for update in query.all():
                # Skip locked updates that are in a current push
                if update.locked:
                    if update.title in locked_updates:
                        continue
                    # Warn about locked updates that aren't a part of a push and
                    # push them again.
                    else:
                        click.echo('Warning: %s is locked but not in a push' %
                                   update.title)

                # Skip unsigned updates (this checks that all builds in the update are signed)
                if not update.signed:
                    click.echo(
                        'Warning: %s has unsigned builds and has been skipped'
                        % update.title)
                    continue

                updates.append(update)

        for update in updates:
            click.echo(update.title)

        if updates:
            click.confirm('Push these {:d} updates?'.format(len(updates)),
                          abort=True)
            click.echo('\nLocking updates...')
            for update in updates:
                update.locked = True
                update.date_locked = datetime.utcnow()
        else:
            click.echo('\nThere are no updates to push.')

        update_titles = list([update.title for update in updates])

    if update_titles:
        click.echo('\nSending masher.start fedmsg')
        # Because we're a script, we want to send to the fedmsg-relay,
        # that's why we say active=True
        bodhi.server.notifications.init(active=True, cert_prefix=cert_prefix)
        bodhi.server.notifications.publish(
            topic='masher.start',
            msg=dict(
                updates=update_titles,
                resume=resume,
                agent=username,
            ),
            force=True,
        )