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