Beispiel #1
0
class UpdateRequestSchema(CSRFProtectedSchema, colander.MappingSchema):
    """An API schema for bodhi.server.services.updates.set_request()."""

    request = colander.SchemaNode(
        colander.String(),
        validator=colander.OneOf(list(UpdateRequest.values())),
    )
Beispiel #2
0
 def __init__(self, release, request, updates, agent,
              log, db_factory, mash_dir, resume=False):
     super(MasherThread, self).__init__()
     self.db_factory = db_factory
     self.log = log
     self.agent = agent
     self.mash_dir = mash_dir
     self.request = UpdateRequest.from_string(request)
     self.release = release
     self.resume = resume
     self.updates = set()
     self.add_tags_async = []
     self.move_tags_async = []
     self.add_tags_sync = []
     self.move_tags_sync = []
     self.testing_digest = {}
     self.path = None
     self.state = {
         'updates': updates,
         'completed_repos': []
     }
     self.success = False
     self.devnull = None
     self._startyear = None
Beispiel #3
0
class ListUpdateSchema(PaginatedSchema, SearchableSchema, Cosmetics):
    """An API schema for bodhi.server.services.updates.query_updates()."""

    alias = Builds(
        colander.Sequence(accept_scalar=True),
        location="querystring",
        missing=None,
        preparer=[util.splitter],
    )

    approved_since = colander.SchemaNode(
        colander.DateTime(),
        location="querystring",
        missing=None,
    )

    approved_before = colander.SchemaNode(
        colander.DateTime(),
        location="querystring",
        missing=None,
    )

    bugs = Bugs(
        colander.Sequence(accept_scalar=True),
        location="querystring",
        missing=None,
        preparer=[util.splitter],
    )

    builds = Builds(
        colander.Sequence(accept_scalar=True),
        location="querystring",
        missing=None,
        preparer=[util.splitter],
    )

    critpath = colander.SchemaNode(
        colander.Boolean(true_choices=('true', '1')),
        location="querystring",
        missing=None,
    )

    locked = colander.SchemaNode(
        colander.Boolean(true_choices=('true', '1')),
        location="querystring",
        missing=None,
    )

    modified_since = colander.SchemaNode(
        colander.DateTime(),
        location="querystring",
        missing=None,
    )

    modified_before = colander.SchemaNode(
        colander.DateTime(),
        location="querystring",
        missing=None,
    )

    packages = Packages(
        colander.Sequence(accept_scalar=True),
        location="querystring",
        missing=None,
        preparer=[util.splitter],
    )

    pushed = colander.SchemaNode(
        colander.Boolean(true_choices=('true', '1')),
        location="querystring",
        missing=None,
    )

    pushed_since = colander.SchemaNode(
        colander.DateTime(),
        location="querystring",
        missing=None,
    )

    pushed_before = colander.SchemaNode(
        colander.DateTime(),
        location="querystring",
        missing=None,
    )

    releases = Releases(
        colander.Sequence(accept_scalar=True),
        location="querystring",
        missing=None,
        preparer=[util.splitter],
    )

    # This singular version of the plural "releases" is purely for bodhi1
    # backwards compat (mostly for RSS feeds) - threebean
    release = colander.SchemaNode(
        colander.String(),
        location="querystring",
        missing=None,
    )

    request = colander.SchemaNode(
        colander.String(),
        location="querystring",
        missing=None,
        validator=colander.OneOf(list(UpdateRequest.values())),
    )

    severity = colander.SchemaNode(
        colander.String(),
        location="querystring",
        missing=None,
        validator=colander.OneOf(list(UpdateSeverity.values())),
    )

    status = Status(
        colander.Sequence(accept_scalar=True),
        location="querystring",
        missing=None,
        preparer=[util.splitter],
    )

    submitted_since = colander.SchemaNode(
        colander.DateTime(),
        location="querystring",
        missing=None,
    )

    submitted_before = colander.SchemaNode(
        colander.DateTime(),
        location="querystring",
        missing=None,
    )

    suggest = colander.SchemaNode(
        colander.String(),
        location="querystring",
        missing=None,
        validator=colander.OneOf(list(UpdateSuggestion.values())),
    )

    type = colander.SchemaNode(
        colander.String(),
        location="querystring",
        missing=None,
        validator=colander.OneOf(list(UpdateType.values())),
    )

    content_type = colander.SchemaNode(
        colander.String(),
        location="querystring",
        missing=None,
        validator=colander.OneOf(list(ContentType.values())),
    )

    user = Users(
        colander.Sequence(accept_scalar=True),
        location="querystring",
        missing=None,
        preparer=[util.splitter],
    )

    updateid = Builds(
        colander.Sequence(accept_scalar=True),
        location="querystring",
        missing=None,
        preparer=[util.splitter],
    )

    gating = colander.SchemaNode(
        colander.String(),
        location="querystring",
        missing=None,
        validator=colander.OneOf(list(TestGatingStatus.values())),
    )
Beispiel #4
0
class SaveUpdateSchema(CSRFProtectedSchema, colander.MappingSchema):
    """An API schema for bodhi.server.services.updates.new_update()."""

    builds = Builds(colander.Sequence(accept_scalar=True),
                    preparer=[util.splitter])

    from_tag = colander.SchemaNode(
        colander.String(),
        missing=None,
    )

    bugs = Bugs(colander.Sequence(accept_scalar=True), missing=None, preparer=[util.splitter])

    display_name = colander.SchemaNode(
        colander.String(),
        missing='',
    )
    close_bugs = colander.SchemaNode(
        colander.Boolean(),
        missing=True,
    )
    type = colander.SchemaNode(
        colander.String(),
        validator=colander.OneOf(list(UpdateType.values())),
    )
    request = colander.SchemaNode(
        colander.String(),
        validator=colander.OneOf(list(UpdateRequest.values())),
        missing='testing',
    )
    severity = colander.SchemaNode(
        colander.String(),
        validator=colander.OneOf(list(UpdateSeverity.values())),
        missing='unspecified',
    )
    notes = colander.SchemaNode(
        colander.String(),
        validator=colander.Length(min=2),
        missing_msg='A description is required for the update.'
    )
    autokarma = colander.SchemaNode(
        colander.Boolean(),
        missing=True,
    )
    stable_karma = colander.SchemaNode(
        colander.Integer(),
        validator=colander.Range(min=1),
        missing=3,
    )
    unstable_karma = colander.SchemaNode(
        colander.Integer(),
        validator=colander.Range(max=-1),
        missing=-3,
    )
    suggest = colander.SchemaNode(
        colander.String(),
        validator=colander.OneOf(list(UpdateSuggestion.values())),
        missing='unspecified',
    )
    edited = colander.SchemaNode(
        colander.String(),
        missing='',
    )
    requirements = colander.SchemaNode(
        colander.String(),
        missing=None,
    )
    require_bugs = colander.SchemaNode(
        colander.Boolean(),
        missing=True,
    )
    require_testcases = colander.SchemaNode(
        colander.Boolean(),
        missing=True,
    )
    autotime = colander.SchemaNode(
        colander.Boolean(),
        missing=True,
    )
    stable_days = colander.SchemaNode(
        colander.Integer(),
        validator=colander.Range(min=0),
        missing=0,
    )
Beispiel #5
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)
Beispiel #6
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,
        )