Esempio n. 1
0
 def eject_from_mash(self, update, reason):
     update.locked = False
     text = '%s ejected from the push because %r' % (update.title, reason)
     log.warn(text)
     update.comment(self.db, text, author=u'bodhi')
     # Remove the pending tag as well
     if update.request is UpdateRequest.stable:
         update.remove_tag(update.release.pending_stable_tag,
                           koji=buildsys.get_session())
     elif update.request is UpdateRequest.testing:
         update.remove_tag(update.release.pending_testing_tag,
                           koji=buildsys.get_session())
     update.request = None
     if update.title in self.state['updates']:
         self.state['updates'].remove(update.title)
     if update in self.updates:
         self.updates.remove(update)
     notifications.publish(
         topic="update.eject",
         msg=dict(
             repo=self.id,
             update=update,
             reason=reason,
             request=self.request,
             release=self.release,
             agent=self.agent,
         ),
         force=True,
     )
Esempio n. 2
0
 def test_publish_force(self, mock_fedmsg_publish, mock_init):
     """Assert publish with the force flag sends the message immediately."""
     notifications.publish('demo.topic', {'such': 'important'}, force=True)
     session = Session()
     self.assertEqual(dict(), session.info)
     mock_fedmsg_publish.assert_called_once_with(topic='demo.topic',
                                                 msg={'such': 'important'})
     mock_init.assert_called_once_with()
Esempio n. 3
0
    def test_publish_force(self):
        """Assert that fedora-messaging messages respect the force flag."""
        expected = api.Message(topic='bodhi.demo.topic',
                               body={u'such': 'important'})

        with fml_testing.mock_sends(expected):
            notifications.publish('demo.topic', {'such': 'important'},
                                  force=True)
Esempio n. 4
0
 def test_single_topic_one_message(self, mock_fedmsg_publish):
     """Assert a single message for a single topic is published."""
     session = Session()
     session.add(models.Package(name=u'ejabberd'))
     notifications.publish('demo.topic', {'new': 'package'})
     session.commit()
     mock_fedmsg_publish.assert_called_once_with(topic='demo.topic',
                                                 msg={'new': 'package'})
Esempio n. 5
0
 def test_repeated_commit(self, mock_fedmsg_publish):
     """Assert queued fedmsgs are cleared between commits."""
     session = Session()
     notifications.publish('demo.topic', {'new': 'package'})
     session.commit()
     session.commit()
     mock_fedmsg_publish.assert_called_once_with(topic='demo.topic',
                                                 msg={'new': 'package'})
Esempio n. 6
0
 def test_publish_force(self):
     """Assert that fedora-messaging messages respect the force flag."""
     message = compose_schemas.ComposeSyncWaitV1.from_dict({
         'agent': 'double O seven',
         'repo': 'f30'
     })
     with fml_testing.mock_sends(message):
         notifications.publish(message, force=True)
Esempio n. 7
0
 def test_publish(self):
     """Assert publish places the message inside the session info dict."""
     notifications.publish('demo.topic', {'such': 'important'})
     session = Session()
     self.assertIn('messages', session.info)
     self.assertEqual(len(session.info['messages']), 1)
     msg = session.info['messages'][0]
     self.assertEqual(msg.topic, 'bodhi.demo.topic')
     self.assertEqual(msg.body, {'such': 'important'})
Esempio n. 8
0
 def test_empty_commit(self, mock_fedmsg_publish):
     """Assert calling commit on a session with no changes still triggers fedmsgs."""
     # Ensure nothing at all is in our session
     Session.remove()
     session = Session()
     notifications.publish('demo.topic', {'new': 'package'})
     session.commit()
     mock_fedmsg_publish.assert_called_once_with(topic='demo.topic',
                                                 msg={'new': 'package'})
Esempio n. 9
0
 def test_publish(self, mock_init):
     """Assert publish places the message inside the session info dict."""
     notifications.publish('demo.topic', {'such': 'important'})
     session = Session()
     self.assertIn('fedmsg', session.info)
     self.assertEqual(session.info['fedmsg']['demo.topic'],
                      [{
                          'such': 'important'
                      }])
Esempio n. 10
0
    def work(self, msg):
        """Begin the push process.

        Here we organize & prioritize the updates, and fire off separate
        threads for each reop tag being mashed.

        If there are any security updates in the push, then those repositories
        will be executed before all others.
        """
        body = msg['body']['msg']
        resume = body.get('resume', False)
        agent = body.get('agent')
        notifications.publish(topic="mashtask.start", msg=dict(agent=agent), force=True)

        with self.db_factory() as session:
            batches = self.generate_batches(session, body['updates'])

        results = []
        # Important repos first, then normal
        last_key = None
        threads = []
        for batch in batches:
            if last_key is not None and request_order_key(batch) != last_key:
                # This means that after we submit all Stable+Security updates, we wait with kicking
                # off the next series of mashes until that finishes.
                self.log.info('All mashes for priority %s running, waiting', last_key)
                for thread in threads:
                    thread.join()
                    for result in thread.results():
                        results.append(result)

            last_key = request_order_key(batch)
            self.log.info('Now starting mashes for priority %s', last_key)

            masher = get_masher(batch['contenttype'])
            if not masher:
                self.log.error('Unsupported content type %s submitted for mashing. SKIPPING',
                               batch['contenttype'].value)
                continue

            self.log.info('Starting masher type %s for %s with %d updates',
                          masher, batch['title'], len(batch['updates']))
            thread = masher(batch['release'], batch['request'], batch['updates'], agent, self.log,
                            self.db_factory, self.mash_dir, resume)
            threads.append(thread)
            thread.start()

        self.log.info('All of the batches are running. Now waiting for the final results')
        for thread in threads:
            thread.join()
            for result in thread.results():
                results.append(result)

        self.log.info('Push complete!  Summary follows:')
        for result in results:
            self.log.info(result)
Esempio n. 11
0
    def test_commit_aborted(self, mock_fedmsg_publish):
        """Assert that when commits are aborted, messages aren't sent."""
        session = Session()
        session.add(models.Package(name=u'ejabberd'))
        session.commit()

        session.add(models.Package(name=u'ejabberd'))
        notifications.publish('demo.topic', {'new': 'package'})
        self.assertRaises(exc.IntegrityError, session.commit)
        self.assertEqual(0, mock_fedmsg_publish.call_count)
Esempio n. 12
0
 def test_single_topic_many_messages(self, mock_fedmsg_publish):
     """Assert many messages for a single topic are sent."""
     session = Session()
     notifications.publish('demo.topic', {'new': 'package'})
     notifications.publish('demo.topic', {'newer': 'packager'})
     session.commit()
     self.assertEqual(2, mock_fedmsg_publish.call_count)
     mock_fedmsg_publish.assert_any_call(topic='demo.topic',
                                         msg={'new': 'package'})
     mock_fedmsg_publish.assert_any_call(topic='demo.topic',
                                         msg={'newer': 'packager'})
Esempio n. 13
0
    def finish(self, success):
        if hasattr(self, '_pungi_conf_dir') and os.path.exists(self._pungi_conf_dir) and success:
            # Let's clean up the pungi configs we wrote
            shutil.rmtree(self._pungi_conf_dir)

        self.log.info('Thread(%s) finished.  Success: %r' % (self.id, success))
        notifications.publish(
            topic="mashtask.complete",
            msg=dict(success=success, repo=self.id, agent=self.agent, ctype=self.ctype.value),
            force=True,
        )
Esempio n. 14
0
 def test_multiple_topics(self, mock_fedmsg_publish):
     """Assert messages with different topics are sent."""
     session = Session()
     notifications.publish('demo.topic', {'new': 'package'})
     notifications.publish('other.topic', {'newer': 'packager'})
     session.commit()
     self.assertEqual(2, mock_fedmsg_publish.call_count)
     mock_fedmsg_publish.assert_any_call(topic='demo.topic',
                                         msg={'new': 'package'})
     mock_fedmsg_publish.assert_any_call(topic='other.topic',
                                         msg={'newer': 'packager'})
Esempio n. 15
0
 def send_notifications(self):
     self.log.info('Sending notifications')
     try:
         agent = os.getlogin()
     except OSError:  # this can happen when building on koji
         agent = u'masher'
     for update in self.updates:
         topic = u'update.complete.%s' % update.status
         notifications.publish(
             topic=topic,
             msg=dict(update=update, agent=agent),
             force=True,
         )
Esempio n. 16
0
    def test_publish(self):
        """Assert publish places the message inside the session info dict."""
        message = compose_schemas.ComposeSyncWaitV1.from_dict({
            'agent': 'double O seven',
            'repo': 'f30'
        })

        notifications.publish(message)

        session = Session()
        assert 'messages' in session.info
        assert len(session.info['messages']) == 1
        msg = session.info['messages'][0]
        assert msg == message
Esempio n. 17
0
    def test_publish(self):
        """Assert publish places the message inside the session info dict."""
        message = compose_schemas.ComposeSyncWaitV1.from_dict({
            'agent': 'double O seven',
            'repo': 'f30'
        })

        notifications.publish(message)

        session = Session()
        self.assertIn('messages', session.info)
        self.assertEqual(len(session.info['messages']), 1)
        msg = session.info['messages'][0]
        self.assertEqual(msg, message)
Esempio n. 18
0
    def wait_for_sync(self):
        """Block until our repomd.xml hits the master mirror"""
        self.log.info('Waiting for updates to hit the master mirror')
        notifications.publish(
            topic="mashtask.sync.wait",
            msg=dict(repo=self.id, agent=self.agent),
            force=True,
        )
        mash_path = os.path.join(self.path, 'compose', 'Everything')
        checkarch = None
        # Find the first non-source arch to check against
        for arch in os.listdir(mash_path):
            if arch == 'source':
                continue
            checkarch = arch
            break
        if not checkarch:
            raise Exception('Not found an arch to wait_for_sync with')

        repomd = os.path.join(mash_path, arch, 'os', 'repodata', 'repomd.xml')
        if not os.path.exists(repomd):
            self.log.error('Cannot find local repomd: %s', repomd)
            return

        master_repomd_url = self._get_master_repomd_url(arch)

        with open(repomd) as repomdf:
            checksum = hashlib.sha1(repomdf.read()).hexdigest()
        while True:
            try:
                self.log.info('Polling %s' % master_repomd_url)
                masterrepomd = urllib2.urlopen(master_repomd_url)
            except (urllib2.URLError, urllib2.HTTPError):
                self.log.exception('Error fetching repomd.xml')
                time.sleep(200)
                continue
            newsum = hashlib.sha1(masterrepomd.read()).hexdigest()
            if newsum == checksum:
                self.log.info("master repomd.xml matches!")
                notifications.publish(
                    topic="mashtask.sync.done",
                    msg=dict(repo=self.id, agent=self.agent),
                    force=True,
                )
                return

            self.log.debug("master repomd.xml doesn't match! %s != %s for %r",
                           checksum, newsum, self.id)
            time.sleep(200)
Esempio n. 19
0
def delete_stack(request):
    """
    Delete a stack.

    Args:
        request (pyramid.request): The current web request.
    Returns:
        dict: The dictionary {'status': 'success'}.
    """
    stack = request.validated['stack']
    notifications.publish(topic='stack.delete', msg=dict(
        stack=stack, agent=request.user.name))
    request.db.delete(stack)
    log.info('Deleted stack: %s', stack.name)
    return dict(status=u'success')
Esempio n. 20
0
 def test_publish_sqlalchemy_object(self):
     """Assert publish places the message inside the session info dict."""
     Session.remove()
     expected_msg = {
         u'some_package': {
             u'name': u'so good',
             u'type': 'base',
             u'requirements': None
         }
     }
     package = models.Package(name='so good')
     notifications.publish('demo.topic', {'some_package': package})
     session = Session()
     self.assertIn('messages', session.info)
     self.assertEqual(len(session.info['messages']), 1)
     msg = session.info['messages'][0]
     self.assertEqual(msg.body, expected_msg)
Esempio n. 21
0
 def test_publish_sqlalchemy_object(self, mock_init):
     """Assert publish places the message inside the session info dict."""
     Session.remove()
     expected_msg = {
         u'some_package': {
             u'name': u'so good',
             u'type': 'base',
             u'requirements': None,
             u'stack': None,
             u'stack_id': None,
         }
     }
     package = models.Package(name='so good')
     notifications.publish('demo.topic', {'some_package': package})
     session = Session()
     self.assertIn('fedmsg', session.info)
     self.assertEqual(session.info['fedmsg']['demo.topic'], [expected_msg])
     mock_init.assert_called_once_with()
Esempio n. 22
0
def trigger_tests(request):
    """
    Trigger tests for update.

    Args:
        request (pyramid.request): The current request.
    Returns:
        dict: A dictionary mapping the key "update" to the update.
    """
    update = request.validated['update']

    if update.status != UpdateStatus.testing:
        log.error("Can't trigger tests for update: Update is not in testing status")
        request.errors.add('body', 'request', 'Update is not in testing status')
    else:
        message = update_schemas.UpdateReadyForTestingV1.from_dict(
            message={'update': update, 'agent': 'bodhi', 're-trigger': True}
        )
        notifications.publish(message)

    return dict(update=update)
Esempio n. 23
0
def trigger_tests(request):
    """
    Trigger tests for update.

    Args:
        request (pyramid.request): The current request.
    Returns:
        dict: A dictionary mapping the key "update" to the update.
    """
    update = request.validated['update']

    if update.status != UpdateStatus.testing:
        log.error("Can't trigger tests for update: Update is not in testing status")
        request.errors.add('body', 'request', 'Update is not in testing status')
    else:
        if update.content_type == ContentType.rpm:
            message = update_schemas.UpdateReadyForTestingV1.from_dict(
                message=update._build_group_test_message()
            )
            notifications.publish(message)

    return dict(update=update)
Esempio n. 24
0
 def test_publish_off(self, mock_init):
     """Assert publish doesn't populate the info dict when publishing is off."""
     notifications.publish('demo.topic', {'such': 'important'})
     session = Session()
     self.assertEqual(dict(), session.info)
     self.assertEqual(0, mock_init.call_count)
Esempio n. 25
0
def save_stack(request):
    """
    Save a stack.

    Args:
        request (pyramid.request): The current web request.
    Returns:
        dict: A dictionary with key "stack" that indexes the newly created Stack.
    """
    data = request.validated
    db = request.db
    user = User.get(request.user.name)

    # Fetch or create the stack
    stack = Stack.get(data['name'])
    if not stack:
        stack = Stack(name=data['name'], users=[user])
        db.add(stack)
        db.flush()

    if stack.users or stack.groups:
        if user in stack.users:
            log.info('%s is an owner of the %s', user.name, stack.name)
        else:
            for group in user.groups:
                if group in stack.groups:
                    log.info('%s is a member of the %s group', user.name, stack.name)
                    break
            else:
                log.warning('%s is not an owner of the %s stack',
                            user.name, stack.name)
                log.debug('owners = %s; groups = %s', stack.users, stack.groups)
                request.errors.add('body', 'name', '%s does not have privileges'
                                   ' to modify the %s stack' % (user.name, stack.name))
                request.errors.status = HTTPForbidden.code
                return

    # Update the stack description
    desc = data['description']
    if desc:
        stack.description = desc

    # Update the stack requirements
    # If the user passed in no value at all for requirements, then use
    # the site defaults.  If, however, the user passed in the empty string, we
    # assume they mean *really*, no requirements so we leave the value null.
    reqs = data['requirements']
    if reqs is None:
        stack.requirements = config.get('site_requirements')
    elif reqs:
        stack.requirements = reqs

    stack.update_relationship('users', User, data, db)
    stack.update_relationship('groups', Group, data, db)

    # We make a special case out of packages here, since when a package is
    # added to a stack, we want to give it the same requirements as the stack
    # has. See https://github.com/fedora-infra/bodhi/issues/101
    new, same, rem = stack.update_relationship('packages', Package, data, db)
    if stack.requirements:
        additional = list(tokenize(stack.requirements))

        for name in new:
            package = Package.get(name)
            original = package.requirements
            original = [] if not original else list(tokenize(original))
            package.requirements = " ".join(list(set(original + additional)))

    log.info('Saved %s stack', data['name'])
    notifications.publish(topic='stack.save', msg=dict(
        stack=stack, agent=user.name))

    return dict(stack=stack)
Esempio n. 26
0
def approve_update(update: Update, db: Session):
    """Add a comment to an update if it is ready for stable.

    Check that the update is eligible to be pushed to stable but hasn't had comments from Bodhi to
    this effect. Add a comment stating that the update may now be pushed to stable.

    Args:
        update: an update in testing that may be ready for stable.
    """
    if not update.release.mandatory_days_in_testing and not update.autotime:
        # If this release does not have any testing requirements and is not autotime,
        # skip it
        log.info(
            f"{update.release.name} doesn't have mandatory days in testing")
        return
    # If this update was already commented, skip it
    if update.has_stable_comment:
        return
    # If updates have reached the testing threshold, say something! Keep in mind
    # that we don't care about karma here, because autokarma updates get their request set
    # to stable by the Update.comment() workflow when they hit the required threshold. Thus,
    # this function only needs to consider the time requirements because these updates have
    # not reached the karma threshold.
    if not update.meets_testing_requirements:
        return
    log.info(f'{update.alias} now meets testing requirements')
    # Only send email notification about the update reaching
    # testing approval on releases composed by bodhi
    update.comment(db,
                   str(config.get('testing_approval_msg')),
                   author='bodhi',
                   email_notification=update.release.composed_by_bodhi)
    notifications.publish(
        update_schemas.UpdateRequirementsMetStableV1.from_dict(
            dict(update=update)))
    if update.autotime and update.days_in_testing >= update.stable_days:
        log.info(f"Automatically marking {update.alias} as stable")
        # For now only rawhide update can be created using side tag
        # Do not add the release.pending_stable_tag if the update
        # was created from a side tag.
        if update.release.composed_by_bodhi:
            update.set_request(db=db,
                               action=UpdateRequest.stable,
                               username="******")
        # For updates that are not included in composes run by bodhi itself,
        # mark them as stable
        else:
            # Single and Multi build update
            conflicting_builds = update.find_conflicting_builds()
            if conflicting_builds:
                builds_str = str.join(", ", conflicting_builds)
                update.comment(
                    db, "This update cannot be pushed to stable. "
                    f"These builds {builds_str} have a more recent "
                    f"build in koji's {update.release.stable_tag} tag.",
                    author="bodhi")
                update.request = None
                if update.from_tag is not None:
                    update.status = UpdateStatus.pending
                    update.remove_tag(
                        update.release.get_testing_side_tag(update.from_tag))
                else:
                    update.status = UpdateStatus.obsolete
                    update.remove_tag(update.release.pending_testing_tag)
                    update.remove_tag(update.release.candidate_tag)
                db.commit()
                return
            update.add_tag(update.release.stable_tag)
            update.status = UpdateStatus.stable
            update.request = None
            update.pushed = True
            update.date_stable = update.date_pushed = func.current_timestamp()
            update.comment(
                db,
                "This update has been submitted for stable by bodhi",
                author=u'bodhi')
            update.modify_bugs()
            db.commit()
            # Multi build update
            if update.from_tag:
                # Merging the side tag should happen here
                pending_signing_tag = update.release.get_pending_signing_side_tag(
                    update.from_tag)
                testing_tag = update.release.get_testing_side_tag(
                    update.from_tag)
                update.remove_tag(pending_signing_tag)
                update.remove_tag(testing_tag)
                update.remove_tag(update.from_tag)
                koji = buildsys.get_session()
                koji.deleteTag(pending_signing_tag)
                koji.deleteTag(testing_tag)

            else:
                # Single build update
                update.remove_tag(update.release.pending_testing_tag)
                update.remove_tag(update.release.pending_stable_tag)
                update.remove_tag(update.release.pending_signing_tag)
                update.remove_tag(update.release.candidate_tag)
Esempio n. 27
0
def main(argv=sys.argv):
    """
    Comment on updates that are eligible to be pushed to stable.

    Queries for updates in the testing state that have a NULL request, looping over them looking for
    updates that are eligible to be pushed to stable but haven't had comments from Bodhi to this
    effect. For each such update it finds it will add a comment stating that the update may now be
    pushed to stable.

    This function is the entry point for the bodhi-approve-testing console script.

    Args:
        argv (list): A list of command line arguments. Defaults to sys.argv.
    """
    logging.basicConfig(level=logging.ERROR)

    if len(argv) != 2:
        usage(argv)

    settings = get_appsettings(argv[1])
    initialize_db(settings)
    db = Session()
    buildsys.setup_buildsystem(config)

    try:
        testing = db.query(Update).filter_by(status=UpdateStatus.testing,
                                             request=None)
        for update in testing:
            if not update.release.mandatory_days_in_testing and not update.autotime:
                # If this release does not have any testing requirements and is not autotime,
                # skip it
                print(
                    f"{update.release.name} doesn't have mandatory days in testing"
                )
                continue

            # If this update was already commented, skip it
            if update.has_stable_comment:
                continue

            # If updates have reached the testing threshold, say something! Keep in mind
            # that we don't care about karma here, because autokarma updates get their request set
            # to stable by the Update.comment() workflow when they hit the required threshold. Thus,
            # this function only needs to consider the time requirements because these updates have
            # not reached the karma threshold.
            if update.meets_testing_requirements:
                print(f'{update.alias} now meets testing requirements')
                # Only send email notification about the update reaching
                # testing approval on releases composed by bodhi
                update.comment(
                    db,
                    str(config.get('testing_approval_msg')),
                    author='bodhi',
                    email_notification=update.release.composed_by_bodhi)

                notifications.publish(
                    update_schemas.UpdateRequirementsMetStableV1.from_dict(
                        dict(update=update)))

                if update.autotime and update.days_in_testing >= update.stable_days:
                    print(f"Automatically marking {update.alias} as stable")
                    # For now only rawhide update can be created using side tag
                    # Do not add the release.pending_stable_tag if the update
                    # was created from a side tag.
                    if update.release.composed_by_bodhi:
                        update.set_request(db=db,
                                           action=UpdateRequest.stable,
                                           username="******")
                    # For updates that are not included in composes run by bodhi itself,
                    # mark them as stable
                    else:
                        # Single and Multi build update
                        conflicting_builds = update.find_conflicting_builds()
                        if conflicting_builds:
                            builds_str = str.join(", ", conflicting_builds)
                            update.comment(
                                db, "This update cannot be pushed to stable. "
                                f"These builds {builds_str} have a more recent "
                                f"build in koji's {update.release.stable_tag} tag.",
                                author="bodhi")
                            update.request = None
                            if update.from_tag is not None:
                                update.status = UpdateStatus.pending
                                update.remove_tag(
                                    update.release.get_testing_side_tag(
                                        update.from_tag))
                            else:
                                update.status = UpdateStatus.obsolete
                                update.remove_tag(
                                    update.release.pending_testing_tag)
                                update.remove_tag(update.release.candidate_tag)
                            db.commit()
                            continue

                        update.add_tag(update.release.stable_tag)
                        update.status = UpdateStatus.stable
                        update.request = None
                        update.pushed = True
                        update.date_stable = update.date_pushed = func.current_timestamp(
                        )
                        update.comment(
                            db,
                            "This update has been submitted for stable by bodhi",
                            author=u'bodhi')

                        # Multi build update
                        if update.from_tag:
                            # Merging the side tag should happen here
                            pending_signing_tag = update.release.get_pending_signing_side_tag(
                                update.from_tag)
                            testing_tag = update.release.get_testing_side_tag(
                                update.from_tag)

                            update.remove_tag(pending_signing_tag)
                            update.remove_tag(testing_tag)
                            update.remove_tag(update.from_tag)

                            koji = buildsys.get_session()
                            koji.deleteTag(pending_signing_tag)
                            koji.deleteTag(testing_tag)

                            # Removes the tag and the build target from koji.
                            koji.removeSideTag(update.from_tag)
                        else:
                            # Single build update
                            update.remove_tag(
                                update.release.pending_testing_tag)
                            update.remove_tag(
                                update.release.pending_stable_tag)
                            update.remove_tag(
                                update.release.pending_signing_tag)
                            update.remove_tag(update.release.candidate_tag)

                db.commit()

    except Exception as e:
        print(str(e))
        db.rollback()
        Session.remove()
        sys.exit(1)
Esempio n. 28
0
def main(argv=sys.argv):
    """
    Comment on updates that are eligible to be pushed to stable.

    Queries for updates in the testing state that have a NULL request, looping over them looking for
    updates that are eligible to be pushed to stable but haven't had comments from Bodhi to this
    effect. For each such update it finds it will add a comment stating that the update may now be
    pushed to stable.

    This function is the entry point for the bodhi-approve-testing console script.

    Args:
        argv (list): A list of command line arguments. Defaults to sys.argv.
    """
    if len(argv) != 2:
        usage(argv)

    settings = get_appsettings(argv[1])
    initialize_db(settings)
    db = Session()

    try:
        testing = db.query(Update).filter_by(status=UpdateStatus.testing,
                                             request=None)
        for update in testing:
            # If this release does not have any testing requirements, skip it
            if not update.release.mandatory_days_in_testing:
                print('%s doesn\'t have mandatory days in testing' %
                      update.release.name)
                continue

            # If this has already met testing requirements, skip it
            if update.met_testing_requirements:
                continue

            # Approval message when testing based on karma threshold
            if update.stable_karma not in (0, None) and update.karma >= update.stable_karma \
                    and not update.autokarma and update.meets_testing_requirements:
                print('%s now reaches stable karma threshold' % update.title)
                text = config.get('testing_approval_msg_based_on_karma')
                update.comment(db, text, author=u'bodhi')
                continue

            # If autokarma updates have reached the testing threshold, say something! Keep in mind
            # that we don't care about karma here, because autokarma updates get their request set
            # to stable by the Update.comment() workflow when they hit the required threshold. Thus,
            # this function only needs to consider the time requirements because these updates have
            # not reached the karma threshold.
            if update.meets_testing_requirements:
                print('%s now meets testing requirements' % update.title)
                text = six.text_type(
                    config.get('testing_approval_msg') %
                    update.mandatory_days_in_testing)
                update.comment(db, text, author=u'bodhi')

                notifications.publish(topic='update.requirements_met.stable',
                                      msg=dict(update=update))

        db.commit()
    except Exception as e:
        print(str(e))
        db.rollback()
        Session.remove()
        sys.exit(1)
Esempio n. 29
0
def main(argv=sys.argv):
    """
    Comment on updates that are eligible to be pushed to stable.

    Queries for updates in the testing state that have a NULL request, looping over them looking for
    updates that are eligible to be pushed to stable but haven't had comments from Bodhi to this
    effect. For each such update it finds it will add a comment stating that the update may now be
    pushed to stable.

    This function is the entry point for the bodhi-approve-testing console script.

    Args:
        argv (list): A list of command line arguments. Defaults to sys.argv.
    """
    logging.basicConfig(level=logging.ERROR)

    if len(argv) != 2:
        usage(argv)

    settings = get_appsettings(argv[1])
    initialize_db(settings)
    db = Session()

    try:
        testing = db.query(Update).filter_by(status=UpdateStatus.testing,
                                             request=None)
        for update in testing:
            if not update.release.mandatory_days_in_testing and not update.autotime:
                # If this release does not have any testing requirements and is not autotime,
                # skip it
                print(
                    f"{update.release.name} doesn't have mandatory days in testing"
                )
                continue

            # If this update was already commented, skip it
            if update.has_stable_comment:
                continue

            # If updates have reached the testing threshold, say something! Keep in mind
            # that we don't care about karma here, because autokarma updates get their request set
            # to stable by the Update.comment() workflow when they hit the required threshold. Thus,
            # this function only needs to consider the time requirements because these updates have
            # not reached the karma threshold.
            if update.meets_testing_requirements:
                print(f'{update.alias} now meets testing requirements')
                update.comment(db,
                               str(config.get('testing_approval_msg')),
                               author='bodhi')

                notifications.publish(
                    update_schemas.UpdateRequirementsMetStableV1.from_dict(
                        dict(update=update)))

                if update.autotime and update.days_in_testing >= update.stable_days:
                    print(f"Automatically marking {update.alias} as stable")
                    update.set_request(db=db,
                                       action=UpdateRequest.stable,
                                       username="******")

                db.commit()

    except Exception as e:
        print(str(e))
        db.rollback()
        Session.remove()
        sys.exit(1)
Esempio n. 30
0
    def work(self):
        self.release = self.db.query(Release)\
                              .filter_by(name=self.release).one()
        self.id = getattr(self.release, '%s_tag' % self.request.value)

        # Set our thread's "name" so it shows up nicely in the logs.
        # https://docs.python.org/2/library/threading.html#thread-objects
        self.name = self.id

        # For 'pending' branched releases, we only want to perform repo-related
        # tasks for testing updates. For stable updates, we should just add the
        # dist_tag and do everything else other than mashing/updateinfo, since
        # the nightly build-branched cron job mashes for us.
        self.skip_mash = False
        if (self.release.state is ReleaseState.pending and
                self.request is UpdateRequest.stable):
            self.skip_mash = True

        self.log.info('Running MasherThread(%s)' % self.id)
        self.init_state()

        notifications.publish(
            topic="mashtask.mashing",
            msg=dict(repo=self.id,
                     updates=self.state['updates'],
                     agent=self.agent,
                     ctype=self.ctype.value),
            force=True,
        )

        try:
            if self.resume:
                self.load_state()
            else:
                self.save_state()

            self.load_updates()
            self.verify_updates()

            if self.request is UpdateRequest.stable:
                self.perform_gating()

            self.determine_and_perform_tag_actions()

            self.update_security_bugs()

            self.expire_buildroot_overrides()
            self.remove_pending_tags()

            if not self.skip_mash:
                mash_process = self.mash()

            # Things we can do while we're mashing
            self.complete_requests()
            self.generate_testing_digest()

            if not self.skip_mash:
                uinfo = self.generate_updateinfo()

                self.wait_for_mash(mash_process)

                uinfo.insert_updateinfo(self.path)

            if not self.skip_mash:
                self.sanity_check_repo()
                self.stage_repo()

                # Wait for the repo to hit the master mirror
                self.wait_for_sync()

            # Send fedmsg notifications
            self.send_notifications()

            # Update bugzillas
            self.modify_bugs()

            # Add comments to updates
            self.status_comments()

            # Announce stable updates to the mailing list
            self.send_stable_announcements()

            # Email updates-testing digest
            self.send_testing_digest()

            self.success = True
            self.remove_state()
            self.unlock_updates()

            self.check_all_karma_thresholds()
            self.obsolete_older_updates()

        except Exception:
            self.log.exception('Exception in MasherThread(%s)' % self.id)
            self.save_state()
            raise
        finally:
            self.finish(self.success)