diff --git a/data/queue.py b/data/queue.py index 71a7bedaa..dbdaadb7d 100644 --- a/data/queue.py +++ b/data/queue.py @@ -156,7 +156,7 @@ class WorkQueue(object): subpath_query = '%s/' % subpath if subpath else '' queue_prefix = '%s/%s/%s%%' % (self._queue_name, namespace, subpath_query) - QueueItem.delete().where(QueueItem.queue_name ** queue_prefix).execute() + return QueueItem.delete().where(QueueItem.queue_name ** queue_prefix).execute() def alive(self, canonical_name_list): """ diff --git a/util/disableabuser.py b/util/disableabuser.py new file mode 100644 index 000000000..b8c724b8b --- /dev/null +++ b/util/disableabuser.py @@ -0,0 +1,86 @@ +from app import tf +from data import model +from data.model import db_transaction +from data.database import QueueItem, Repository, RepositoryBuild, RepositoryBuildTrigger +from data.queue import WorkQueue +from datetime import datetime + +import argparse + +def disable_abusing_user(username, queue_name): + if not username: + raise Exception('Must enter a username') + + user = model.user.get_user(username) + if user is None: + raise Exception('Unknown user %s' % username) + + if not user.enabled: + print "NOTE: User %s is already disabled" % username + + queue_prefix = '%s/%s/%%' % (queue_name, username) + existing_queue_item_count = (QueueItem + .select() + .where(QueueItem.queue_name ** queue_prefix) + .where(QueueItem.available == 1, + QueueItem.retries_remaining > 0, + QueueItem.processing_expires > datetime.now()) + .count()) + + repository_trigger_count = (RepositoryBuildTrigger + .select() + .join(Repository) + .where(Repository.namespace_user == user) + .count()) + + print "Using queue namespace %s" % queue_name + print "User %s has email address %s" % (username, user.email) + print "User %s has %s queued builds in their namespace" % (username, existing_queue_item_count) + print "User %s has %s build triggers in their namespace" % (username, repository_trigger_count) + + confirm_msg = "Would you like to disable this user and delete their triggers and builds? [y/N]> " + letter = str(raw_input(confirm_msg)) + if letter.lower() != 'y': + print "Action canceled" + return + + # Disable the user. + with db_transaction(): + user.enabled = False + user.save() + + repositories_query = Repository.select().where(Repository.namespace_user == user) + if len(repositories_query.clone()): + builds = list(RepositoryBuild + .select() + .where(RepositoryBuild.repository << list(repositories_query))) + + triggers = list(RepositoryBuildTrigger + .select() + .where(RepositoryBuildTrigger.repository << list(repositories_query))) + + # Delete all builds for the user's repositories. + if builds: + RepositoryBuild.delete().where(RepositoryBuild.id << builds).execute() + + # Delete all build triggers for the user's repositories. + if triggers: + (RepositoryBuildTrigger + .delete() + .where(RepositoryBuildTrigger.id << triggers) + .execute()) + + # Delete all queue items for the user's namespace. + dockerfile_build_queue = WorkQueue(queue_name, tf, has_namespace=True) + count_removed = dockerfile_build_queue.delete_namespaced_items(user.username) + + info = (username, len(triggers), count_removed) + print "User %s disabled, %s triggers deleted, %s queued builds removed" % info + + +parser = argparse.ArgumentParser(description='Disables a user abusing the build system') +parser.add_argument('username', help='The username of the abuser') +parser.add_argument('queuename', help='The name of the dockerfile build queue ' + + '(e.g. `dockerfilebuild` or `dockerfilebuildstaging`)') +args = parser.parse_args() +disable_abusing_user(args.username, args.queuename)